package artifacts import ( "context" "errors" "fmt" "strings" "gitea.maximumdirect.net/eric/narratio/internal/adapters/storage" "gitea.maximumdirect.net/eric/narratio/internal/manifest" ) var ( ErrCurrentRunPointerMissing = errors.New("current run pointer missing") ErrCurrentManifestMissing = errors.New("current manifest missing") ) type CurrentRunPointerMissingError struct { Key string } func (e *CurrentRunPointerMissingError) Error() string { return fmt.Sprintf("%s: %q", ErrCurrentRunPointerMissing, e.Key) } func (e *CurrentRunPointerMissingError) Unwrap() error { return ErrCurrentRunPointerMissing } type CurrentManifestMissingError struct { Key string } func (e *CurrentManifestMissingError) Error() string { return fmt.Sprintf("%s: %q", ErrCurrentManifestMissing, e.Key) } func (e *CurrentManifestMissingError) Unwrap() error { return ErrCurrentManifestMissing } type CurrentState struct { SessionPrefix string CurrentRunIDKey string CurrentManifestKey string CurrentPointerKey string RunID string Manifest *manifest.Manifest ManifestData []byte Commit *RemoteCommitManifest Pointer *CurrentCommitPointer } type CurrentStateValidation struct { ExpectedCampaign string ExpectedSessionID string ExpectedRunID string ValidateRunID bool } func LoadCurrentState( ctx context.Context, store storage.ObjectStore, sessionPrefix string, validation CurrentStateValidation, ) (*CurrentState, error) { prefix := strings.TrimSpace(sessionPrefix) if prefix == "" { return nil, fmt.Errorf("session prefix is required") } if store == nil { return nil, fmt.Errorf("object store is required") } pointerKey := S3CurrentCommitPointerKey(prefix) pointerExists, err := store.Exists(ctx, pointerKey) if err != nil { return nil, fmt.Errorf("check current commit pointer %q: %w", pointerKey, err) } if pointerExists { return loadCommittedCurrentState(ctx, store, prefix, pointerKey, validation) } return loadLegacyCurrentState(ctx, store, prefix, validation) } func ValidateCurrentStateIdentity(state *CurrentState, validation CurrentStateValidation) error { if state == nil || state.Manifest == nil { return fmt.Errorf("current state with manifest is required") } expectedSessionID := strings.TrimSpace(validation.ExpectedSessionID) expectedCampaign := strings.TrimSpace(validation.ExpectedCampaign) expectedRunID := strings.TrimSpace(validation.ExpectedRunID) manifestSessionID := strings.TrimSpace(state.Manifest.SessionID) manifestCampaign := strings.TrimSpace(state.Manifest.Campaign) manifestRunID := strings.TrimSpace(state.Manifest.RunID) if manifestCampaign != "" { if err := ValidateSessionIdentity(manifestCampaign, manifestSessionID); err != nil { return fmt.Errorf("current manifest contains unsafe legacy identities; migrate remote state before use: %w", err) } } if manifestRunID != "" { if err := ValidateRunIdentity(manifestRunID); err != nil { return fmt.Errorf("current manifest contains an unsafe legacy run id; migrate remote state before use: %w", err) } } if expectedSessionID != "" && manifestSessionID != expectedSessionID { return fmt.Errorf( "current manifest session_id %q does not match expected session_id %q", manifestSessionID, expectedSessionID, ) } if expectedCampaign != "" { if manifestCampaign == "" { return fmt.Errorf("current manifest campaign is required") } if manifestCampaign != expectedCampaign { return fmt.Errorf( "current manifest campaign %q does not match expected campaign %q", manifestCampaign, expectedCampaign, ) } } if expectedRunID == "" && validation.ValidateRunID { expectedRunID = strings.TrimSpace(state.RunID) } if expectedRunID != "" { if manifestRunID == "" { return fmt.Errorf("current manifest run_id is required") } if manifestRunID != expectedRunID { return fmt.Errorf( "current run pointer %q references run %q but current manifest run_id is %q", state.CurrentRunIDKey, expectedRunID, manifestRunID, ) } } return nil }