142 lines
4.7 KiB
Go
142 lines
4.7 KiB
Go
package app
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
|
|
"gitea.maximumdirect.net/eric/narratio/internal/adapters/storage"
|
|
"gitea.maximumdirect.net/eric/narratio/internal/artifacts"
|
|
"gitea.maximumdirect.net/eric/narratio/internal/config"
|
|
)
|
|
|
|
type pipelineCampaignConfig struct {
|
|
PipelinePath string
|
|
CampaignPath string
|
|
Pipeline *config.PipelineConfig
|
|
Campaign *config.CampaignConfig
|
|
}
|
|
|
|
func loadCommandConfig(ctx context.Context, pipelineFlag, campaignFlag, campaignFileFlag, sessionFlag string, sessionOpts config.SessionLoadOptions) (*config.Config, error) {
|
|
base, err := loadPipelineCampaignConfig(pipelineFlag, campaignFlag, campaignFileFlag)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if explicitSession := strings.TrimSpace(sessionFlag); explicitSession != "" {
|
|
return config.LoadWithSessionOptions(base.PipelinePath, base.CampaignPath, explicitSession, sessionOpts)
|
|
}
|
|
|
|
discoveredSession, err := discoverSessionConfigPathWithCandidates(config.DefaultSessionConfigSearchPaths)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if discoveredSession.Path != "" {
|
|
return config.LoadWithSessionOptions(base.PipelinePath, base.CampaignPath, discoveredSession.Path, sessionOpts)
|
|
}
|
|
|
|
sessionID := strings.TrimSpace(sessionOpts.SessionID)
|
|
if sessionID == "" {
|
|
return nil, missingSessionConfigError(discoveredSession.Searched, "remote session loading requires a session_id")
|
|
}
|
|
|
|
sessionPrefix := artifacts.S3SessionPrefix(base.Pipeline.Storage.S3.RootPrefix, config.CampaignID(base.Campaign), sessionID)
|
|
remoteKey := artifacts.S3SessionConfigKey(sessionPrefix)
|
|
partialCfg := &config.Config{
|
|
Pipeline: base.Pipeline,
|
|
Campaign: base.Campaign,
|
|
PipelinePath: base.PipelinePath,
|
|
CampaignPath: base.CampaignPath,
|
|
}
|
|
store, err := newCommandObjectStore(ctx, partialCfg, nil)
|
|
if err != nil {
|
|
return nil, missingSessionConfigError(discoveredSession.Searched, fmt.Sprintf("remote session %q unavailable: %v", remoteKey, err))
|
|
}
|
|
|
|
sessionInfo, err := findRemoteSessionConfig(ctx, store, sessionPrefix, remoteKey)
|
|
if err != nil {
|
|
return nil, missingSessionConfigError(discoveredSession.Searched, err.Error())
|
|
}
|
|
sessionTempPath, err := storage.DownloadObjectToTemp(ctx, store, remoteKey, "narratio-session-*.yml")
|
|
if err != nil {
|
|
return nil, missingSessionConfigError(discoveredSession.Searched, fmt.Sprintf("remote session %q download failed: %v", remoteKey, err))
|
|
}
|
|
sessionBytes, err := os.ReadFile(sessionTempPath)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("read downloaded remote session %q: %w", sessionTempPath, err)
|
|
}
|
|
sessionCfg, err := config.LoadSessionBytesWithOptions("s3://"+s3BucketName(base.Pipeline)+"/"+remoteKey, sessionBytes, sessionOpts)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return config.Resolve(
|
|
base.PipelinePath,
|
|
base.Pipeline,
|
|
base.CampaignPath,
|
|
base.Campaign,
|
|
sessionTempPath,
|
|
sessionCfg,
|
|
config.SessionSource{
|
|
Source: "session_config.s3",
|
|
LocalPath: sessionTempPath,
|
|
S3Bucket: s3BucketName(base.Pipeline),
|
|
S3Key: remoteKey,
|
|
S3Size: sessionInfo.Size,
|
|
S3ETag: sessionInfo.ETag,
|
|
SpoolPath: sessionTempPath,
|
|
},
|
|
)
|
|
}
|
|
|
|
func loadPipelineCampaignConfig(pipelineFlag, campaignFlag, campaignFileFlag string) (*pipelineCampaignConfig, error) {
|
|
resolvedPipelinePath, err := resolvePipelineConfigPath(pipelineFlag)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
pipelineCfg, err := config.LoadPipeline(resolvedPipelinePath)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
resolvedCampaignPath, err := resolveCampaignConfigPath(pipelineCfg, campaignFlag, campaignFileFlag)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
campaignCfg, err := config.LoadCampaign(resolvedCampaignPath)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if selectedID := strings.TrimSpace(campaignFlag); selectedID != "" && strings.TrimSpace(campaignFileFlag) == "" {
|
|
if got := config.CampaignID(campaignCfg); got != selectedID {
|
|
return nil, fmt.Errorf("campaign config %q invalid: campaign_id %q does not match selected campaign %q", resolvedCampaignPath, got, selectedID)
|
|
}
|
|
}
|
|
return &pipelineCampaignConfig{
|
|
PipelinePath: resolvedPipelinePath,
|
|
CampaignPath: resolvedCampaignPath,
|
|
Pipeline: pipelineCfg,
|
|
Campaign: campaignCfg,
|
|
}, nil
|
|
}
|
|
|
|
func findRemoteSessionConfig(ctx context.Context, store storage.ObjectStore, sessionPrefix, remoteKey string) (storage.ObjectInfo, error) {
|
|
objects, err := store.List(ctx, sessionPrefix)
|
|
if err != nil {
|
|
return storage.ObjectInfo{}, fmt.Errorf("remote session %q list failed: %w", remoteKey, err)
|
|
}
|
|
for _, obj := range objects {
|
|
if obj.Key == remoteKey {
|
|
return obj, nil
|
|
}
|
|
}
|
|
return storage.ObjectInfo{}, fmt.Errorf("remote session %q not found", remoteKey)
|
|
}
|
|
|
|
func s3BucketName(cfg *config.PipelineConfig) string {
|
|
if cfg == nil || cfg.Storage.S3 == nil {
|
|
return ""
|
|
}
|
|
return strings.TrimSpace(cfg.Storage.S3.Bucket)
|
|
}
|