143 lines
4.0 KiB
Go
143 lines
4.0 KiB
Go
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
|
|
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
|
|
}
|