diff --git a/pkg/filetree/loader_test.go b/pkg/filetree/loader_test.go index 184147688..929d5a0b3 100644 --- a/pkg/filetree/loader_test.go +++ b/pkg/filetree/loader_test.go @@ -76,7 +76,7 @@ func TestAferoLoader(t *testing.T) { testResources[resource.Key.(string)] = resource.Value.(string) } - mockState.EXPECT().TryLoad().Return(state.VersionedState{ + mockState.EXPECT().TryLoad().Return(state.State{ V1: &state.V1{ Kustomize: &state.Kustomize{ Overlays: map[string]state.Overlay{ diff --git a/pkg/lifecycle/daemon/headless/daemon_test.go b/pkg/lifecycle/daemon/headless/daemon_test.go index d702cf483..b4ceb636c 100644 --- a/pkg/lifecycle/daemon/headless/daemon_test.go +++ b/pkg/lifecycle/daemon/headless/daemon_test.go @@ -44,7 +44,7 @@ func TestHeadlessDaemon(t *testing.T) { }, { Name: "one group one item, not required, no value", - State: []byte(`{"alpha": ""}`), + State: []byte(`{"v1":{"config":{"alpha":""}}}`), Release: &api.Release{ Spec: api.Spec{ Config: api.Config{ @@ -68,7 +68,7 @@ func TestHeadlessDaemon(t *testing.T) { }, { Name: "one group one item, required, no value", - State: []byte(`{"alpha": ""}`), + State: []byte(`{"v1":{"config":{"alpha":""}}}`), Release: &api.Release{ Spec: api.Spec{ Config: api.Config{ @@ -188,7 +188,7 @@ func TestHeadlessDaemon(t *testing.T) { }, { Name: "one group one item, required, value, not hidden", - State: []byte(`{"alpha": "100"}`), + State: []byte(`{"v1":{"config":{"alpha": "100"}}}`), Release: &api.Release{ Spec: api.Spec{ Config: api.Config{ @@ -212,7 +212,7 @@ func TestHeadlessDaemon(t *testing.T) { }, { Name: "one group one item, not required, no value, not hidden", - State: []byte(`{"alpha": ""}`), + State: []byte(`{"v1":{"config":{"alpha": ""}}}`), Release: &api.Release{ Spec: api.Spec{ Config: api.Config{ @@ -260,7 +260,7 @@ func TestHeadlessDaemon(t *testing.T) { }, { Name: "one group two items, neither required, neither present", - State: []byte(`{"alpha": "", "beta": ""}`), + State: []byte(`{"v1":{"config":{"alpha": "", "beta": ""}}}`), Release: &api.Release{ Spec: api.Spec{ Config: api.Config{ @@ -291,7 +291,7 @@ func TestHeadlessDaemon(t *testing.T) { }, { Name: "one group two items, both required, neither present", - State: []byte(`{"alpha": "", "beta": ""}`), + State: []byte(`{"v1":{"config":{"alpha": "", "beta": ""}}}`), Release: &api.Release{ Spec: api.Spec{ Config: api.Config{ @@ -322,7 +322,7 @@ func TestHeadlessDaemon(t *testing.T) { }, { Name: "one group two items, both required, one present", - State: []byte(`{"alpha":"", "beta": ""}`), + State: []byte(`{"v1":{"config":{"alpha":"", "beta": ""}}}`), Release: &api.Release{ Spec: api.Spec{ Config: api.Config{ @@ -384,7 +384,7 @@ func TestHeadlessDaemon(t *testing.T) { }, { Name: "beta value resolves to alpha value", - State: []byte(`{"alpha": "101"}`), + State: []byte(`{"v1":{"config":{"alpha": "101"}}}`), Release: &api.Release{ Spec: api.Spec{ Config: api.Config{ @@ -415,7 +415,7 @@ func TestHeadlessDaemon(t *testing.T) { }, { Name: "beta value resolves to alpha value when wrong beta value is presented", - State: []byte(`{"alpha": "101", "beta":"abc"}`), + State: []byte(`{"v1":{"config":{"alpha": "101", "beta":"abc"}}}`), Release: &api.Release{ Spec: api.Spec{ Config: api.Config{ @@ -446,7 +446,7 @@ func TestHeadlessDaemon(t *testing.T) { }, { Name: "charlie value resolves to beta value resolves to alpha value", - State: []byte(`{"alpha": "100"}`), + State: []byte(`{"v1":{"config":{"alpha":"100"}}}`), Release: &api.Release{ Spec: api.Spec{ Config: api.Config{ diff --git a/pkg/lifecycle/daemon/routes_navcycle.go b/pkg/lifecycle/daemon/routes_navcycle.go index 34a177c4c..2666a26c1 100644 --- a/pkg/lifecycle/daemon/routes_navcycle.go +++ b/pkg/lifecycle/daemon/routes_navcycle.go @@ -156,7 +156,8 @@ func (d *NavcycleRoutes) getRequiredButIncompleteStepFor(requires []string) (str if err != nil { return "", errors.Wrap(err, "load state") } - if currentState.Versioned().V1.Lifecycle != nil && + if currentState.Versioned().V1 != nil && + currentState.Versioned().V1.Lifecycle != nil && currentState.Versioned().V1.Lifecycle.StepsCompleted != nil { stepsCompleted = currentState.Versioned().V1.Lifecycle.StepsCompleted debug.Log("event", "steps.notEmpty", "completed", fmt.Sprintf("%v", stepsCompleted)) diff --git a/pkg/lifecycle/daemon/routes_navcycle_completestep.go b/pkg/lifecycle/daemon/routes_navcycle_completestep.go index 66578fbdd..dbc1d467f 100644 --- a/pkg/lifecycle/daemon/routes_navcycle_completestep.go +++ b/pkg/lifecycle/daemon/routes_navcycle_completestep.go @@ -72,7 +72,7 @@ func (d *NavcycleRoutes) handleAsync(errChan chan error, debug log.Logger, step return } - _, err := d.StateManager.StateUpdate(func(currentState state.VersionedState) (state.VersionedState, error) { + _, err := d.StateManager.StateUpdate(func(currentState state.State) (state.State, error) { return currentState.WithCompletedStep(step), nil }) if err != nil { diff --git a/pkg/lifecycle/daemon/routes_navcycle_completestep_test.go b/pkg/lifecycle/daemon/routes_navcycle_completestep_test.go index 6f1076e2d..d0918a0c8 100644 --- a/pkg/lifecycle/daemon/routes_navcycle_completestep_test.go +++ b/pkg/lifecycle/daemon/routes_navcycle_completestep_test.go @@ -116,7 +116,7 @@ func TestV2CompleteStep(t *testing.T) { ExpectState: &matchers.Is{ Describe: "saved state has step foo completed", Test: func(v interface{}) bool { - if versioned, ok := v.(state2.VersionedState); ok { + if versioned, ok := v.(state2.State); ok { _, ok := versioned.V1.Lifecycle.StepsCompleted["foo"] return ok } @@ -168,7 +168,7 @@ func TestV2CompleteStep(t *testing.T) { ExpectState: &matchers.Is{ Describe: "saved state has step foo completed and bar uncompleted", Test: func(v interface{}) bool { - if versioned, ok := v.(state2.VersionedState); ok { + if versioned, ok := v.(state2.State); ok { _, fooOk := versioned.V1.Lifecycle.StepsCompleted["foo"] _, barOk := versioned.V1.Lifecycle.StepsCompleted["bar"] return fooOk && !barOk @@ -197,7 +197,7 @@ func TestV2CompleteStep(t *testing.T) { ExpectState: &matchers.Is{ Describe: "saved state has step foo and bar completed", Test: func(v interface{}) bool { - if versioned, ok := v.(state2.VersionedState); ok { + if versioned, ok := v.(state2.State); ok { _, fooOk := versioned.V1.Lifecycle.StepsCompleted["foo"] _, barOk := versioned.V1.Lifecycle.StepsCompleted["bar"] return fooOk && barOk @@ -226,7 +226,7 @@ func TestV2CompleteStep(t *testing.T) { ExpectState: &matchers.Is{ Describe: "saved state has step foo completed and step bar invalidated", Test: func(v interface{}) bool { - if versioned, ok := v.(state2.VersionedState); ok { + if versioned, ok := v.(state2.State); ok { _, fooOk := versioned.V1.Lifecycle.StepsCompleted["foo"] _, barOk := versioned.V1.Lifecycle.StepsCompleted["bar"] return fooOk && !barOk @@ -299,7 +299,7 @@ func TestV2CompleteStep(t *testing.T) { ExpectState: &matchers.Is{ Describe: "saved state has step make-the-things completed", Test: func(v interface{}) bool { - if versioned, ok := v.(state2.VersionedState); ok { + if versioned, ok := v.(state2.State); ok { _, ok := versioned.V1.Lifecycle.StepsCompleted["make-the-things"] return ok } diff --git a/pkg/lifecycle/daemon/routes_navcycle_getstep_test.go b/pkg/lifecycle/daemon/routes_navcycle_getstep_test.go index 77db7b103..818f80740 100644 --- a/pkg/lifecycle/daemon/routes_navcycle_getstep_test.go +++ b/pkg/lifecycle/daemon/routes_navcycle_getstep_test.go @@ -227,7 +227,7 @@ func TestV2GetStep(t *testing.T) { StepProgress: progressmap, } - fakeState.EXPECT().TryLoad().Return(state2.VersionedState{ + fakeState.EXPECT().TryLoad().Return(state2.State{ V1: &state2.V1{ Lifecycle: test.State, }, @@ -442,7 +442,7 @@ func TestHydrateStep(t *testing.T) { }, }, }, - state: state2.V0{}, + state: state2.State{V1: &state2.V1{}}, want: &daemontypes.StepResponse{ CurrentStep: daemontypes.Step{ Source: api.Step{ @@ -492,7 +492,7 @@ func TestHydrateStep(t *testing.T) { }, Spec: api.Spec{}, }, - state: state2.VersionedState{ + state: state2.State{ V1: &state2.V1{ HelmValues: "fake: values", ReleaseName: "fake-releasename", @@ -563,7 +563,7 @@ func TestHydrateStep(t *testing.T) { mockFs := afero.Afero{Fs: afero.NewMemMapFs()} mockState := state.NewMockManager(mc) - if test.state != nil { + if !test.state.IsEmpty() { mockState.EXPECT().TryLoad().Return(test.state, nil) mockState.EXPECT().TryLoad().Return(test.state, nil) } @@ -636,7 +636,7 @@ func TestHydrateTemplatedKustomizeStep(t *testing.T) { }, }, }, - state: state2.V0{}, + state: state2.State{V1: &state2.V1{}}, want: &daemontypes.StepResponse{ CurrentStep: daemontypes.Step{ Source: api.Step{ @@ -696,7 +696,7 @@ func TestHydrateTemplatedKustomizeStep(t *testing.T) { progressmap := &daemontypes.ProgressMap{} mockFs := afero.Afero{Fs: afero.NewMemMapFs()} mockState := state.NewMockManager(mc) - if test.state != nil { + if !test.state.IsEmpty() { mockState.EXPECT().TryLoad().Return(test.state, nil) mockState.EXPECT().TryLoad().Return(test.state, nil) mockState.EXPECT().TryLoad().Return(test.state, nil) diff --git a/pkg/lifecycle/daemon/routes_navcycle_kustomize_test.go b/pkg/lifecycle/daemon/routes_navcycle_kustomize_test.go index 26b3c5dda..0d45fde2f 100644 --- a/pkg/lifecycle/daemon/routes_navcycle_kustomize_test.go +++ b/pkg/lifecycle/daemon/routes_navcycle_kustomize_test.go @@ -147,7 +147,7 @@ func TestV2KustomizeSaveFile(t *testing.T) { StepProgress: progressmap, } - fakeState.EXPECT().TryLoad().Return(state.VersionedState{ + fakeState.EXPECT().TryLoad().Return(state.State{ V1: &test.InState, }, nil).AnyTimes() @@ -269,7 +269,7 @@ func TestV2KustomizeDeleteFile(t *testing.T) { StepProgress: progressmap, } - fakeState.EXPECT().TryLoad().Return(state.VersionedState{ + fakeState.EXPECT().TryLoad().Return(state.State{ V1: &test.InState, }, nil).AnyTimes() diff --git a/pkg/lifecycle/kustomize/kustomizer_test.go b/pkg/lifecycle/kustomize/kustomizer_test.go index f8302ef0a..3f4b96ef4 100644 --- a/pkg/lifecycle/kustomize/kustomizer_test.go +++ b/pkg/lifecycle/kustomize/kustomizer_test.go @@ -329,7 +329,7 @@ resources: mockDaemon := daemon2.NewMockDaemon(mc) mockState := state2.NewMockManager(mc) - mockState.EXPECT().TryLoad().Return(state.VersionedState{ + mockState.EXPECT().TryLoad().Return(state.State{ V1: &state.V1{ Kustomize: &state.Kustomize{ Overlays: map[string]state.Overlay{ @@ -508,7 +508,7 @@ resources: BasePath: constants.KustomizeBasePath, }) mockDaemon.EXPECT().KustomizeSavedChan().Return(saveChan) - mockState.EXPECT().TryLoad().Return(state.VersionedState{V1: &state.V1{ + mockState.EXPECT().TryLoad().Return(state.State{V1: &state.V1{ Kustomize: test.kustomize, }}, nil).Times(2) diff --git a/pkg/lifecycle/kustomize/patch.go b/pkg/lifecycle/kustomize/patch.go index 0926af8df..0d2cddaa5 100644 --- a/pkg/lifecycle/kustomize/patch.go +++ b/pkg/lifecycle/kustomize/patch.go @@ -79,7 +79,7 @@ func (l *Kustomizer) generateTillerPatches(step api.Kustomize) error { if err != nil { return errors.Wrap(err, "load state") } - if state != nil && state.CurrentKustomize() != nil { + if state.V1 != nil && state.CurrentKustomize() != nil { excludedBases = state.CurrentKustomize().Ship().ExcludedBases } diff --git a/pkg/lifecycle/kustomize/patch_test.go b/pkg/lifecycle/kustomize/patch_test.go index 67238b8f4..02fc7699c 100644 --- a/pkg/lifecycle/kustomize/patch_test.go +++ b/pkg/lifecycle/kustomize/patch_test.go @@ -173,7 +173,7 @@ metadata: stateManager := state.NewManager(log.NewNopLogger(), mockFs, viper.New()) - err := stateManager.Save(state.VersionedState{V1: &state.V1{Kustomize: &state.Kustomize{}}}) + err := stateManager.Save(state.State{V1: &state.V1{Kustomize: &state.Kustomize{}}}) req.NoError(err) l := &Kustomizer{ diff --git a/pkg/lifecycle/kustomize/pre_kustomize_test.go b/pkg/lifecycle/kustomize/pre_kustomize_test.go index 039338b7a..8c96180a7 100644 --- a/pkg/lifecycle/kustomize/pre_kustomize_test.go +++ b/pkg/lifecycle/kustomize/pre_kustomize_test.go @@ -392,7 +392,7 @@ spec: req.NoError(err) actualLists := make([]util.List, 0) - if currentState.Versioned().V1.Metadata != nil { + if currentState.V1 != nil && currentState.V1.Metadata != nil { actualLists = currentState.Versioned().V1.Metadata.Lists } diff --git a/pkg/lifecycle/render/helm/template_test.go b/pkg/lifecycle/render/helm/template_test.go index da02cf65d..bdeb83e97 100644 --- a/pkg/lifecycle/render/helm/template_test.go +++ b/pkg/lifecycle/render/helm/template_test.go @@ -38,7 +38,7 @@ func TestLocalTemplater(t *testing.T) { expectedChannelName string expectHelmOpts *matchers.Is ontemplate func(req *require.Assertions, mockFs afero.Afero) func(chartRoot string, args []string) error - state *state2.VersionedState + state *state2.State requirements *chartutil.Requirements repoAdd []string namespace string @@ -130,7 +130,7 @@ func TestLocalTemplater(t *testing.T) { name: "helm template with namespace in state", describe: "template uses namespace from state", expectError: "", - state: &state2.VersionedState{ + state: &state2.State{ V1: &state2.V1{ Namespace: "test-namespace", }, @@ -172,7 +172,7 @@ func TestLocalTemplater(t *testing.T) { } if test.state == nil { - mockState.EXPECT().TryLoad().Return(state2.VersionedState{ + mockState.EXPECT().TryLoad().Return(state2.State{ V1: &state2.V1{ HelmValues: "we fake", ReleaseName: channelName, @@ -787,7 +787,7 @@ something: maybe err := mockFs.WriteFile(tt.defaultValuesPath, []byte(tt.defaultValuesContent), 0755) req.NoError(err) - mockState.EXPECT().TryLoad().Return(state2.VersionedState{V1: &state2.V1{}}, nil) + mockState.EXPECT().TryLoad().Return(state2.State{V1: &state2.V1{}}, nil) f := &LocalTemplater{ Logger: &logger.TestLogger{T: t}, FS: mockFs, diff --git a/pkg/lifecycle/render/noconfig_test.go b/pkg/lifecycle/render/noconfig_test.go index 4008ee66d..2858f7be5 100644 --- a/pkg/lifecycle/render/noconfig_test.go +++ b/pkg/lifecycle/render/noconfig_test.go @@ -26,7 +26,7 @@ func TestRenderNoConfig(t *testing.T) { tests := loadTestCases(t, filepath.Join("test-cases", "render-inline.yaml")) - for _, test := range tests[:1] { + for _, test := range tests { t.Run(test.Name, func(t *testing.T) { mc := gomock.NewController(t) @@ -57,10 +57,15 @@ func TestRenderNoConfig(t *testing.T) { func() { defer mc.Finish() - mockState.EXPECT().TryLoad().Return(state.V0(test.ViperConfig), nil) + mockState.EXPECT().TryLoad().Return(state.State{V1: &state.V1{Config: test.ViperConfig}}, nil) + + expectedConfig := test.ViperConfig + if expectedConfig == nil { + expectedConfig = make(map[string]interface{}) + } p.EXPECT(). - Build("testdir", test.Spec.Assets.V1, test.Spec.Config.V1, gomock.Any(), test.ViperConfig). + Build("testdir", test.Spec.Assets.V1, test.Spec.Config.V1, gomock.Any(), expectedConfig). Return(planner.Plan{}, nil) p.EXPECT(). diff --git a/pkg/lifecycle/terraform/state_test.go b/pkg/lifecycle/terraform/state_test.go index 672f62a17..ec6b231dd 100644 --- a/pkg/lifecycle/terraform/state_test.go +++ b/pkg/lifecycle/terraform/state_test.go @@ -21,8 +21,8 @@ func TestPersistState(t *testing.T) { tests := []struct { name string state string - instate state.VersionedState - outstate state.VersionedState + instate state.State + outstate state.State }{ { name: "post-delete state, mostly empty", @@ -43,10 +43,10 @@ func TestPersistState(t *testing.T) { ] } `, - instate: state.VersionedState{ + instate: state.State{ V1: &state.V1{}, }, - outstate: state.VersionedState{ + outstate: state.State{ V1: &state.V1{ Terraform: &state.Terraform{ RawState: `{ @@ -98,10 +98,10 @@ func TestPersistState(t *testing.T) { err := mockFs.WriteFile("installer/terraform.tfstate", []byte(test.state), 0644) req.NoError(err) - statemanager.EXPECT().TryLoad().Return(&test.instate, nil) + statemanager.EXPECT().TryLoad().Return(test.instate, nil) statemanager.EXPECT().Save(&matchers.Is{ Test: func(v interface{}) bool { - vstate := v.(state.VersionedState) + vstate := v.(state.State) diff := deep.Equal(*vstate.V1.Terraform, *test.outstate.V1.Terraform) t.Log(strings.Join(diff, "\n")) return len(diff) == 0 @@ -121,7 +121,7 @@ func TestPersistState(t *testing.T) { func TestRestoreState(t *testing.T) { tests := []struct { name string - instate state.VersionedState + instate state.State expectFile string }{ { @@ -143,7 +143,7 @@ func TestRestoreState(t *testing.T) { ] } `, - instate: state.VersionedState{ + instate: state.State{ V1: &state.V1{ Terraform: &state.Terraform{ RawState: `{ @@ -192,7 +192,7 @@ func TestRestoreState(t *testing.T) { mockFs := afero.Afero{Fs: afero.NewMemMapFs()} statemanager := state2.NewMockManager(mc) - statemanager.EXPECT().TryLoad().Return(&test.instate, nil) + statemanager.EXPECT().TryLoad().Return(test.instate, nil) err := restoreState(debug, mockFs, statemanager, "installer") req.NoError(err) diff --git a/pkg/lifecycle/terraform/when_test.go b/pkg/lifecycle/terraform/when_test.go index 3f44e1a41..0d1127945 100644 --- a/pkg/lifecycle/terraform/when_test.go +++ b/pkg/lifecycle/terraform/when_test.go @@ -14,56 +14,56 @@ import ( func TestEvaluateWhen(t *testing.T) { tests := []struct { name string - State state.VersionedState + State state.State when string release api.Release want bool }{ { name: "no when", - State: state.VersionedState{V1: &state.V1{}}, + State: state.State{V1: &state.V1{}}, when: "", release: api.Release{Metadata: api.ReleaseMetadata{}}, want: true, }, { name: "true when", - State: state.VersionedState{V1: &state.V1{}}, + State: state.State{V1: &state.V1{}}, when: "true", release: api.Release{Metadata: api.ReleaseMetadata{}}, want: true, }, { name: "false when", - State: state.VersionedState{V1: &state.V1{}}, + State: state.State{V1: &state.V1{}}, when: "false", release: api.Release{Metadata: api.ReleaseMetadata{}}, want: false, }, { name: "trivial template when true", - State: state.VersionedState{V1: &state.V1{}}, + State: state.State{V1: &state.V1{}}, when: "{{repl eq 1 1}}", release: api.Release{Metadata: api.ReleaseMetadata{}}, want: true, }, { name: "trivial template when false", - State: state.VersionedState{V1: &state.V1{}}, + State: state.State{V1: &state.V1{}}, when: "{{repl eq 1 2}}", release: api.Release{Metadata: api.ReleaseMetadata{}}, want: false, }, { name: "configOption template when true", - State: state.VersionedState{V1: &state.V1{Config: map[string]interface{}{"theOption": "hello_world"}}}, + State: state.State{V1: &state.V1{Config: map[string]interface{}{"theOption": "hello_world"}}}, when: `{{repl ConfigOptionEquals "theOption" "hello_world"}}`, release: api.Release{Metadata: api.ReleaseMetadata{}}, want: true, }, { name: "configOption template when false", - State: state.VersionedState{V1: &state.V1{Config: map[string]interface{}{"theOption": "hello_world"}}}, + State: state.State{V1: &state.V1{Config: map[string]interface{}{"theOption": "hello_world"}}}, when: `{{repl ConfigOptionEquals "theOption" "something else"}}`, release: api.Release{Metadata: api.ReleaseMetadata{}}, want: false, diff --git a/pkg/ship/kustomize.go b/pkg/ship/kustomize.go index e0a3986eb..9bcf2fe6d 100644 --- a/pkg/ship/kustomize.go +++ b/pkg/ship/kustomize.go @@ -32,7 +32,7 @@ func (s *Ship) stateFileExists(ctx context.Context) bool { debug.Log("event", "tryLoad.fail") return false } - _, noExistingState := existingState.(state.Empty) + noExistingState := existingState.Versioned().V1 == nil return !noExistingState } @@ -56,25 +56,23 @@ func (s *Ship) Init(ctx context.Context) error { } existingState, _ := s.State.TryLoad() - if existingState != nil { - if !existingState.IsEmpty() { - debug.Log("event", "existing.state") + if !existingState.IsEmpty() { + debug.Log("event", "existing.state") - if s.Viper.GetString("state-from") != "file" { - debug.Log("event", "existing.state", "state-from", "not file") - return warnings.WarnCannotRemoveState - } + if s.Viper.GetString("state-from") != "file" { + debug.Log("event", "existing.state", "state-from", "not file") + return warnings.WarnCannotRemoveState + } - if removeExistingState { - if err := s.promptToRemoveState(); err != nil { - debug.Log("event", "state.remove.prompt.fail") - return err - } - } else { - s.UI.Info("Preserving current state") - if !s.upstreamMatchesExisting(existingState) { - return errors.New(fmt.Sprintf("Upstream %s does not match upstream from state %s", s.Viper.GetString("upstream"), existingState.Upstream())) - } + if removeExistingState { + if err := s.promptToRemoveState(); err != nil { + debug.Log("event", "state.remove.prompt.fail") + return err + } + } else { + s.UI.Info("Preserving current state") + if !s.upstreamMatchesExisting(existingState) { + return errors.New(fmt.Sprintf("Upstream %s does not match upstream from state %s", s.Viper.GetString("upstream"), existingState.Upstream())) } } } diff --git a/pkg/ship/unfork.go b/pkg/ship/unfork.go index 55e32d217..9ac9eed8d 100644 --- a/pkg/ship/unfork.go +++ b/pkg/ship/unfork.go @@ -23,19 +23,17 @@ func (s *Ship) Unfork(ctx context.Context) error { defer s.Shutdown(cancelFunc) existingState, _ := s.State.TryLoad() - if existingState != nil { - if !existingState.IsEmpty() { - debug.Log("event", "existing.state") + if !existingState.IsEmpty() { + debug.Log("event", "existing.state") - if s.Viper.GetString("state-from") != "file" { - debug.Log("event", "existing.state", "state-from", "not file") - return warnings.WarnCannotRemoveState - } + if s.Viper.GetString("state-from") != "file" { + debug.Log("event", "existing.state", "state-from", "not file") + return warnings.WarnCannotRemoveState + } - if err := s.promptToRemoveState(); err != nil { - debug.Log("event", "state.remove.prompt.fail") - return err - } + if err := s.promptToRemoveState(); err != nil { + debug.Log("event", "state.remove.prompt.fail") + return err } } diff --git a/pkg/ship/update.go b/pkg/ship/update.go index ffa7992bf..320a7681b 100644 --- a/pkg/ship/update.go +++ b/pkg/ship/update.go @@ -8,7 +8,6 @@ import ( "github.com/pkg/errors" "github.com/replicatedhq/ship/pkg/constants" "github.com/replicatedhq/ship/pkg/lifecycle/daemon/daemontypes" - "github.com/replicatedhq/ship/pkg/state" ) func (s *Ship) UpdateAndMaybeExit(ctx context.Context) error { @@ -38,7 +37,7 @@ func (s *Ship) Update(ctx context.Context) error { uiPrintableStatePath = constants.StatePath } - if _, noExistingState := existingState.(state.Empty); noExistingState { + if existingState.Versioned().V1 == nil { debug.Log("event", "state.missing") return errors.Errorf(`No state file found at %s please run "ship init"`, uiPrintableStatePath) } diff --git a/pkg/ship/watch.go b/pkg/ship/watch.go index fad47e4dd..68875572b 100644 --- a/pkg/ship/watch.go +++ b/pkg/ship/watch.go @@ -9,7 +9,6 @@ import ( "github.com/go-kit/kit/log/level" "github.com/pkg/errors" "github.com/replicatedhq/ship/pkg/constants" - "github.com/replicatedhq/ship/pkg/state" ) func (s *Ship) WatchAndExit(ctx context.Context) error { @@ -37,7 +36,7 @@ func (s *Ship) Watch(ctx context.Context) error { uiPrintableStatePath = constants.StatePath } - if _, noExistingState := existingState.(state.Empty); noExistingState { + if existingState.Versioned().V1 == nil { debug.Log("event", "state.missing") return errors.Errorf(`No state found at %s, please run "ship init"`, uiPrintableStatePath) } diff --git a/pkg/specs/interface_test.go b/pkg/specs/interface_test.go index a2d5519b3..a60c6d43c 100644 --- a/pkg/specs/interface_test.go +++ b/pkg/specs/interface_test.go @@ -102,7 +102,7 @@ icon: https://kfbr.392/x5.png }, "helm").After(inOrder) inOrder = mockUi.EXPECT().Info("Looking for ship.yaml ...").After(inOrder) inOrder = mockUi.EXPECT().Info("ship.yaml not found in upstream, generating default lifecycle for application ...").After(inOrder) - inOrder = mockState.EXPECT().TryLoad().Return(state2.VersionedState{}, nil).After(inOrder) + inOrder = mockState.EXPECT().TryLoad().Return(state2.State{}, nil).After(inOrder) mockState.EXPECT().SerializeReleaseName("i-know-what-the-x5-is").After(inOrder) }, @@ -205,7 +205,7 @@ icon: https://kfbr.392/x5.png inOrder = mockState.EXPECT().SerializeContentSHA("abcdef1234567890").After(inOrder) inOrder = mockUi.EXPECT().Info("Looking for ship.yaml ...").After(inOrder) inOrder = mockUi.EXPECT().Info("ship.yaml not found in upstream, generating default lifecycle for application ...").After(inOrder) - inOrder = mockState.EXPECT().TryLoad().Return(state2.VersionedState{}, nil).After(inOrder) + inOrder = mockState.EXPECT().TryLoad().Return(state2.State{}, nil).After(inOrder) mockState.EXPECT().SerializeReleaseName("ship").After(inOrder) }, expectRelease: &api.Release{ diff --git a/pkg/specs/upstream_test.go b/pkg/specs/upstream_test.go index ca125802a..57837bc60 100644 --- a/pkg/specs/upstream_test.go +++ b/pkg/specs/upstream_test.go @@ -37,7 +37,7 @@ func TestResolver_MaybeResolveVersionedUpstream(t *testing.T) { { name: "versioned upstream has an update available", upstream: "github.com/o/r/tree/_latest_", - currentState: &state.VersionedState{ + currentState: state.State{ V1: &state.V1{ Metadata: &state.Metadata{ Version: "1.1.0", @@ -49,7 +49,7 @@ func TestResolver_MaybeResolveVersionedUpstream(t *testing.T) { { name: "version upstream is above latest", upstream: "github.com/o/r/tree/_latest_", - currentState: &state.VersionedState{ + currentState: state.State{ V1: &state.V1{ Metadata: &state.Metadata{ Version: "1.2.1", @@ -62,7 +62,7 @@ func TestResolver_MaybeResolveVersionedUpstream(t *testing.T) { { name: "commit sha upstream", upstream: "github.com/o/r/tree/d3eed9a347ad02f0b79e3f92330878f88953cf64/path", - currentState: &state.VersionedState{ + currentState: state.State{ V1: &state.V1{ Metadata: &state.Metadata{ Version: "1.2.0", @@ -74,7 +74,7 @@ func TestResolver_MaybeResolveVersionedUpstream(t *testing.T) { { name: "ref upstream", upstream: "github.com/o/r/tree/abcedfg/path", - currentState: &state.VersionedState{ + currentState: state.State{ V1: &state.V1{ Metadata: &state.Metadata{ Version: "1.2.0", @@ -86,7 +86,7 @@ func TestResolver_MaybeResolveVersionedUpstream(t *testing.T) { { name: "versioned upstream with no latest release", upstream: "github.com/a/b/tree/_latest_", - currentState: &state.VersionedState{ + currentState: state.State{ V1: &state.V1{ Metadata: &state.Metadata{ Version: "1.2.0", @@ -99,7 +99,7 @@ func TestResolver_MaybeResolveVersionedUpstream(t *testing.T) { { name: "versioned upstream with no version in state", upstream: "github.com/o/r/tree/_latest_", - currentState: &state.VersionedState{ + currentState: state.State{ V1: &state.V1{ Metadata: &state.Metadata{}, }, @@ -109,7 +109,7 @@ func TestResolver_MaybeResolveVersionedUpstream(t *testing.T) { { name: "ref upstream with no version in state", upstream: "github.com/o/r/tree/abranch", - currentState: &state.VersionedState{ + currentState: state.State{ V1: &state.V1{ Metadata: &state.Metadata{}, }, @@ -119,7 +119,7 @@ func TestResolver_MaybeResolveVersionedUpstream(t *testing.T) { { name: "not a github url", upstream: "notgithub.com/o/r/tree/_latest_", - currentState: &state.VersionedState{ + currentState: state.State{ V1: &state.V1{ Metadata: &state.Metadata{}, }, diff --git a/pkg/state/manager.go b/pkg/state/manager.go index 8ef300fdb..5ce9a60fe 100644 --- a/pkg/state/manager.go +++ b/pkg/state/manager.go @@ -42,7 +42,7 @@ type Manager interface { SerializeListsMetadata(util.List) error ClearListsMetadata() error SerializeUpstreamContents(contents *UpstreamContents) error - Save(v VersionedState) error + Save(v State) error ResetLifecycle() error AddCert(name string, newCert util.CertType) error @@ -61,11 +61,11 @@ type MManager struct { StateRWMut sync.RWMutex } -func (m *MManager) Save(v VersionedState) error { +func (m *MManager) Save(v State) error { debug := level.Debug(log.With(m.Logger, "method", "SerializeShipMetadata")) debug.Log("event", "safeStateUpdate") - _, err := m.StateUpdate(func(state VersionedState) (VersionedState, error) { + _, err := m.StateUpdate(func(state State) (State, error) { state = v return state, nil }) @@ -84,7 +84,7 @@ func NewManager( } } -type Update func(VersionedState) (VersionedState, error) +type Update func(State) (State, error) // applies the provided updater to the current state. Returns the new state and err func (m *MManager) StateUpdate(updater Update) (State, error) { @@ -93,12 +93,16 @@ func (m *MManager) StateUpdate(updater Update) (State, error) { currentState, err := m.TryLoad() if err != nil { - return nil, errors.Wrap(err, "tryLoad in safe updater") + return State{}, errors.Wrap(err, "tryLoad in safe updater") + } + + if currentState.V1 == nil { + currentState.V1 = &V1{} } updatedState, err := updater(currentState.Versioned()) if err != nil { - return nil, errors.Wrap(err, "run state update function in safe updater") + return State{}, errors.Wrap(err, "run state update function in safe updater") } return updatedState, errors.Wrap(m.serializeAndWriteState(updatedState), "write state in safe updater") @@ -109,7 +113,7 @@ func (m *MManager) SerializeShipMetadata(metadata api.ShipAppMetadata, applicati debug := level.Debug(log.With(m.Logger, "method", "SerializeShipMetadata")) debug.Log("event", "safeStateUpdate") - _, err := m.StateUpdate(func(state VersionedState) (VersionedState, error) { + _, err := m.StateUpdate(func(state State) (State, error) { state.V1.Metadata = &Metadata{ ApplicationType: applicationType, ReleaseNotes: metadata.ReleaseNotes, @@ -127,7 +131,7 @@ func (m *MManager) SerializeAppMetadata(metadata api.ReleaseMetadata) error { debug := level.Debug(log.With(m.Logger, "method", "SerializeAppMetadata")) debug.Log("event", "safeStateUpdate") - _, err := m.StateUpdate(func(state VersionedState) (VersionedState, error) { + _, err := m.StateUpdate(func(state State) (State, error) { if state.V1.Metadata == nil { state.V1.Metadata = &Metadata{} } @@ -155,7 +159,7 @@ func (m *MManager) SerializeUpstream(upstream string) error { debug := level.Debug(log.With(m.Logger, "method", "SerializeUpstream")) debug.Log("event", "safeStateUpdate") - _, err := m.StateUpdate(func(state VersionedState) (VersionedState, error) { + _, err := m.StateUpdate(func(state State) (State, error) { state.V1.Upstream = upstream return state, nil }) @@ -167,7 +171,7 @@ func (m *MManager) SerializeContentSHA(contentSHA string) error { debug := level.Debug(log.With(m.Logger, "method", "SerializeContentSHA")) debug.Log("event", "safeStateUpdate") - _, err := m.StateUpdate(func(state VersionedState) (VersionedState, error) { + _, err := m.StateUpdate(func(state State) (State, error) { state.V1.ContentSHA = contentSHA return state, nil }) @@ -179,7 +183,7 @@ func (m *MManager) SerializeHelmValues(values string, defaults string) error { debug := level.Debug(log.With(m.Logger, "method", "serializeHelmValues")) debug.Log("event", "safeStateUpdate") - _, err := m.StateUpdate(func(state VersionedState) (VersionedState, error) { + _, err := m.StateUpdate(func(state State) (State, error) { state.V1.HelmValues = values state.V1.HelmValuesDefaults = defaults return state, nil @@ -192,7 +196,7 @@ func (m *MManager) SerializeReleaseName(name string) error { debug := level.Debug(log.With(m.Logger, "method", "serializeReleaseName")) debug.Log("event", "safeStateUpdate") - _, err := m.StateUpdate(func(state VersionedState) (VersionedState, error) { + _, err := m.StateUpdate(func(state State) (State, error) { state.V1.ReleaseName = name return state, nil }) @@ -204,7 +208,7 @@ func (m *MManager) SerializeNamespace(namespace string) error { debug := level.Debug(log.With(m.Logger, "method", "serializeNamespace")) debug.Log("event", "safeStateUpdate") - _, err := m.StateUpdate(func(state VersionedState) (VersionedState, error) { + _, err := m.StateUpdate(func(state State) (State, error) { state.V1.Namespace = namespace return state, nil }) @@ -216,7 +220,7 @@ func (m *MManager) SerializeConfig(assets []api.Asset, meta api.ReleaseMetadata, debug := level.Debug(log.With(m.Logger, "method", "serializeConfig")) debug.Log("event", "safeStateUpdate") - _, err := m.StateUpdate(func(state VersionedState) (VersionedState, error) { + _, err := m.StateUpdate(func(state State) (State, error) { state.V1.Config = templateContext return state, nil }) @@ -227,7 +231,7 @@ func (m *MManager) SerializeListsMetadata(list util.List) error { debug := level.Debug(log.With(m.Logger, "method", "serializeListMetadata")) debug.Log("event", "safeStateUpdate") - _, err := m.StateUpdate(func(state VersionedState) (VersionedState, error) { + _, err := m.StateUpdate(func(state State) (State, error) { if state.V1.Metadata == nil { state.V1.Metadata = &Metadata{} } @@ -241,7 +245,7 @@ func (m *MManager) ClearListsMetadata() error { debug := level.Debug(log.With(m.Logger, "method", "clearListMetadata")) debug.Log("event", "safeStateUpdate") - _, err := m.StateUpdate(func(state VersionedState) (VersionedState, error) { + _, err := m.StateUpdate(func(state State) (State, error) { if state.V1.Metadata == nil { return state, nil } @@ -257,7 +261,7 @@ func (m *MManager) SerializeUpstreamContents(contents *UpstreamContents) error { debug := level.Debug(log.With(m.Logger, "method", "serializeUpstreamContents")) debug.Log("event", "safeStateUpdate") - _, err := m.StateUpdate(func(state VersionedState) (VersionedState, error) { + _, err := m.StateUpdate(func(state State) (State, error) { state.V1.UpstreamContents = contents return state, nil @@ -283,7 +287,7 @@ func (m *MManager) TryLoad() (State, error) { return m.tryLoadFromSecret() default: err := fmt.Errorf("unsupported state-from value: %q", stateFrom) - return nil, errors.Wrap(err, "try load state") + return State{}, errors.Wrap(err, "try load state") } } @@ -293,7 +297,7 @@ func (m *MManager) ResetLifecycle() error { debug := level.Debug(log.With(m.Logger, "method", "ResetLifecycle")) debug.Log("event", "safeStateUpdate") - _, err := m.StateUpdate(func(state VersionedState) (VersionedState, error) { + _, err := m.StateUpdate(func(state State) (State, error) { state.V1.Lifecycle = nil return state, nil @@ -306,47 +310,46 @@ func (m *MManager) ResetLifecycle() error { func (m *MManager) tryLoadFromSecret() (State, error) { config, err := rest.InClusterConfig() if err != nil { - return nil, errors.Wrap(err, "get in cluster config") + return State{}, errors.Wrap(err, "get in cluster config") } clientset, err := kubernetes.NewForConfig(config) if err != nil { - return nil, errors.Wrap(err, "get kubernetes client") + return State{}, errors.Wrap(err, "get kubernetes client") } ns := m.V.GetString("secret-namespace") if ns == "" { - return nil, errors.New("secret-namespace is not set") + return State{}, errors.New("secret-namespace is not set") } secretName := m.V.GetString("secret-name") if secretName == "" { - return nil, errors.New("secret-name is not set") + return State{}, errors.New("secret-name is not set") } secretKey := m.V.GetString("secret-key") if secretKey == "" { - return nil, errors.New("secret-key is not set") + return State{}, errors.New("secret-key is not set") } secret, err := clientset.CoreV1().Secrets(ns).Get(secretName, metav1.GetOptions{}) if err != nil { - return nil, errors.Wrap(err, "get secret") + return State{}, errors.Wrap(err, "get secret") } serialized, ok := secret.Data[secretKey] if !ok { err := fmt.Errorf("key %q not found in secret %q", secretKey, secretName) - return nil, errors.Wrap(err, "get state from secret") + return State{}, errors.Wrap(err, "get state from secret") } // An empty secret should be treated as empty state if len(strings.TrimSpace(string(serialized))) == 0 { - return Empty{}, nil + return State{}, nil } - // HACK -- try to deserialize it as VersionedState, otherwise, assume its a raw map of config values - var state VersionedState + var state State if err := json.Unmarshal(serialized, &state); err != nil { - return nil, errors.Wrap(err, "unmarshal state") + return State{}, errors.Wrap(err, "unmarshal state") } level.Debug(m.Logger).Log( @@ -356,56 +359,35 @@ func (m *MManager) tryLoadFromSecret() (State, error) { "value", fmt.Sprintf("%+v", state), ) - if state.V1 != nil { - level.Debug(m.Logger).Log("event", "state.resolve", "type", "versioned") - return state, nil - } - - var mapState map[string]interface{} - if err := json.Unmarshal(serialized, &mapState); err != nil { - return nil, errors.Wrap(err, "unmarshal state") - } - - level.Debug(m.Logger).Log("event", "state.resolve", "type", "raw") - return V0(mapState), nil + level.Debug(m.Logger).Log("event", "state.resolve", "type", "versioned") + return state, nil } func (m *MManager) tryLoadFromFile() (State, error) { if _, err := m.FS.Stat(constants.StatePath); os.IsNotExist(err) { level.Debug(m.Logger).Log("msg", "no saved state exists", "path", constants.StatePath) - return Empty{}, nil + return State{}, nil } serialized, err := m.FS.ReadFile(constants.StatePath) if err != nil { - return nil, errors.Wrap(err, "read state file") + return State{}, errors.Wrap(err, "read state file") } - // HACK -- try to deserialize it as VersionedState, otherwise, assume its a raw map of config values - var state VersionedState + var state State if err := json.Unmarshal(serialized, &state); err != nil { - return nil, errors.Wrap(err, "unmarshal state") - } - - if state.V1 != nil { - level.Debug(m.Logger).Log("event", "state.resolve", "type", "versioned") - return state, nil - } - - var mapState map[string]interface{} - if err := json.Unmarshal(serialized, &mapState); err != nil { - return nil, errors.Wrap(err, "unmarshal state") + return State{}, errors.Wrap(err, "unmarshal state") } - level.Debug(m.Logger).Log("event", "state.resolve", "type", "raw") - return V0(mapState), nil + level.Debug(m.Logger).Log("event", "state.resolve", "type", "versioned") + return state, nil } func (m *MManager) SaveKustomize(kustomize *Kustomize) error { debug := level.Debug(log.With(m.Logger, "method", "SaveKustomize")) debug.Log("event", "safeStateUpdate") - _, err := m.StateUpdate(func(state VersionedState) (VersionedState, error) { + _, err := m.StateUpdate(func(state State) (State, error) { state.V1.Kustomize = kustomize return state, nil @@ -428,7 +410,7 @@ func (m *MManager) RemoveStateFile() error { return nil } -func (m *MManager) serializeAndWriteState(state VersionedState) error { +func (m *MManager) serializeAndWriteState(state State) error { m.StateRWMut.Lock() defer m.StateRWMut.Unlock() debug := level.Debug(log.With(m.Logger, "method", "serializeAndWriteState")) @@ -452,7 +434,7 @@ func (m *MManager) serializeAndWriteState(state VersionedState) error { } } -func (m *MManager) serializeAndWriteStateFile(state VersionedState) error { +func (m *MManager) serializeAndWriteStateFile(state State) error { serialized, err := json.MarshalIndent(state, "", " ") if err != nil { @@ -472,7 +454,7 @@ func (m *MManager) serializeAndWriteStateFile(state VersionedState) error { return nil } -func (m *MManager) serializeAndWriteStateSecret(state VersionedState) error { +func (m *MManager) serializeAndWriteStateSecret(state State) error { serialized, err := json.MarshalIndent(state, "", " ") if err != nil { return errors.Wrap(err, "serialize state") @@ -510,7 +492,7 @@ func (m *MManager) AddCert(name string, newCert util.CertType) error { debug := level.Debug(log.With(m.Logger, "method", "SaveKustomize")) debug.Log("event", "safeStateUpdate") - _, err := m.StateUpdate(func(state VersionedState) (VersionedState, error) { + _, err := m.StateUpdate(func(state State) (State, error) { if state.V1.Certs == nil { state.V1.Certs = make(map[string]util.CertType) @@ -528,7 +510,7 @@ func (m *MManager) AddCA(name string, newCA util.CAType) error { debug := level.Debug(log.With(m.Logger, "method", "SaveKustomize")) debug.Log("event", "safeStateUpdate") - _, err := m.StateUpdate(func(state VersionedState) (VersionedState, error) { + _, err := m.StateUpdate(func(state State) (State, error) { if state.V1.CAs == nil { state.V1.CAs = make(map[string]util.CAType) diff --git a/pkg/state/manager_test.go b/pkg/state/manager_test.go index e7c38c510..53eaf417a 100644 --- a/pkg/state/manager_test.go +++ b/pkg/state/manager_test.go @@ -50,13 +50,6 @@ func TestLoadConfig(t *testing.T) { contents: `{}`, expectConfig: make(map[string]interface{}), }, - { - name: "v0 single item", - contents: `{"foo": "bar"}`, - expectConfig: map[string]interface{}{ - "foo": "bar", - }, - }, { name: "v1 single item", contents: `{"v1": {"config": {"foo": "bar"}}}`, @@ -177,16 +170,16 @@ func TestMManager_SerializeChartURL(t *testing.T) { name string URL string wantErr bool - before VersionedState - expected VersionedState + before State + expected State }{ { name: "basic test", URL: "abc123", - before: VersionedState{ + before: State{ V1: &V1{}, }, - expected: VersionedState{ + expected: State{ V1: &V1{ Upstream: "abc123", }, @@ -195,12 +188,12 @@ func TestMManager_SerializeChartURL(t *testing.T) { { name: "no wipe", URL: "abc123", - before: VersionedState{ + before: State{ V1: &V1{ ChartRepoURL: "abc123_", }, }, - expected: VersionedState{ + expected: State{ V1: &V1{ Upstream: "abc123", ChartRepoURL: "abc123_", @@ -210,12 +203,12 @@ func TestMManager_SerializeChartURL(t *testing.T) { { name: "no wipe, but still override", URL: "xyz789", - before: VersionedState{ + before: State{ V1: &V1{ ChartURL: "abc123", }, }, - expected: VersionedState{ + expected: State{ V1: &V1{ Upstream: "xyz789", }, @@ -254,16 +247,16 @@ func TestMManager_SerializeContentSHA(t *testing.T) { name string ContentSHA string wantErr bool - before VersionedState - expected VersionedState + before State + expected State }{ { name: "basic test", ContentSHA: "abc123", - before: VersionedState{ + before: State{ V1: &V1{}, }, - expected: VersionedState{ + expected: State{ V1: &V1{ ContentSHA: "abc123", }, @@ -272,12 +265,12 @@ func TestMManager_SerializeContentSHA(t *testing.T) { { name: "no wipe", ContentSHA: "abc123", - before: VersionedState{ + before: State{ V1: &V1{ ChartRepoURL: "abc123_", }, }, - expected: VersionedState{ + expected: State{ V1: &V1{ ContentSHA: "abc123", ChartRepoURL: "abc123_", @@ -287,12 +280,12 @@ func TestMManager_SerializeContentSHA(t *testing.T) { { name: "no wipe, but still override", ContentSHA: "xyz789", - before: VersionedState{ + before: State{ V1: &V1{ ContentSHA: "abc123", }, }, - expected: VersionedState{ + expected: State{ V1: &V1{ ContentSHA: "xyz789", }, @@ -332,16 +325,16 @@ func TestMManager_SerializeHelmValues(t *testing.T) { HelmValues string HelmDefaults string // is discarded by the function wantErr bool - before VersionedState - expected VersionedState + before State + expected State }{ { name: "basic test", HelmValues: "abc123", - before: VersionedState{ + before: State{ V1: &V1{}, }, - expected: VersionedState{ + expected: State{ V1: &V1{ HelmValues: "abc123", }, @@ -350,12 +343,12 @@ func TestMManager_SerializeHelmValues(t *testing.T) { { name: "no wipe", HelmValues: "abc123", - before: VersionedState{ + before: State{ V1: &V1{ ChartRepoURL: "abc123_", }, }, - expected: VersionedState{ + expected: State{ V1: &V1{ HelmValues: "abc123", ChartRepoURL: "abc123_", @@ -365,12 +358,12 @@ func TestMManager_SerializeHelmValues(t *testing.T) { { name: "no wipe, but still override", HelmValues: "xyz789", - before: VersionedState{ + before: State{ V1: &V1{ HelmValues: "abc123", }, }, - expected: VersionedState{ + expected: State{ V1: &V1{ HelmValues: "xyz789", }, @@ -409,8 +402,8 @@ func TestMManager_SerializeShipMetadata(t *testing.T) { name string Metadata api.ShipAppMetadata wantErr bool - before VersionedState - expected VersionedState + before State + expected State }{ { name: "basic test", @@ -419,10 +412,10 @@ func TestMManager_SerializeShipMetadata(t *testing.T) { Icon: "test icon", Name: "test name", }, - before: VersionedState{ + before: State{ V1: &V1{}, }, - expected: VersionedState{ + expected: State{ V1: &V1{ Metadata: &Metadata{ ApplicationType: "mock application type", @@ -465,12 +458,12 @@ func TestMManager_SerializeShipMetadata(t *testing.T) { func TestMManager_ResetLifecycle(t *testing.T) { tests := []struct { name string - before VersionedState - expected VersionedState + before State + expected State }{ { name: "basic test", - before: VersionedState{ + before: State{ V1: &V1{ Lifecycle: &Lifeycle{ StepsCompleted: map[string]interface{}{ @@ -481,7 +474,7 @@ func TestMManager_ResetLifecycle(t *testing.T) { }, }, }, - expected: VersionedState{ + expected: State{ V1: &V1{ Lifecycle: nil, }, @@ -515,7 +508,7 @@ func TestMManager_ParallelUpdates(t *testing.T) { tests := []struct { name string runners []func(*MManager, *require.Assertions, *sync.WaitGroup) - validator func(VersionedState, *require.Assertions) + validator func(State, *require.Assertions) }{ { name: "lists", @@ -529,7 +522,7 @@ func TestMManager_ParallelUpdates(t *testing.T) { group.Done() }, }, - validator: func(state VersionedState, req *require.Assertions) { + validator: func(state State, req *require.Assertions) { req.Len(state.V1.Metadata.Lists, 20) }, }, @@ -552,7 +545,7 @@ func TestMManager_ParallelUpdates(t *testing.T) { group.Done() }, }, - validator: func(state VersionedState, req *require.Assertions) { + validator: func(state State, req *require.Assertions) { req.Len(state.V1.Metadata.Lists, 0) }, }, @@ -573,7 +566,7 @@ func TestMManager_ParallelUpdates(t *testing.T) { group.Done() }, }, - validator: func(state VersionedState, req *require.Assertions) { + validator: func(state State, req *require.Assertions) { req.Len(state.V1.Metadata.Lists, 20) req.Equal("tested", state.V1.Metadata.Version) }, @@ -600,7 +593,7 @@ func TestMManager_ParallelUpdates(t *testing.T) { group.Done() }, }, - validator: func(state VersionedState, req *require.Assertions) { + validator: func(state State, req *require.Assertions) { req.Len(state.V1.Metadata.Lists, 20) req.Equal("testedName", state.CurrentReleaseName()) req.Equal("testedNS", state.CurrentNamespace()) @@ -622,7 +615,7 @@ func TestMManager_ParallelUpdates(t *testing.T) { func(m *MManager, req *require.Assertions, group *sync.WaitGroup) { // append the integers 1-200 to the upstream for i := 1; i <= 200; i++ { - _, err := m.StateUpdate(func(state VersionedState) (VersionedState, error) { + _, err := m.StateUpdate(func(state State) (State, error) { state.V1.Upstream += fmt.Sprintf(" a:%d ", i) return state, nil }) @@ -634,7 +627,7 @@ func TestMManager_ParallelUpdates(t *testing.T) { func(m *MManager, req *require.Assertions, group *sync.WaitGroup) { // append the integers 1-200 to the upstream for i := 1; i <= 200; i++ { - _, err := m.StateUpdate(func(state VersionedState) (VersionedState, error) { + _, err := m.StateUpdate(func(state State) (State, error) { state.V1.Upstream += fmt.Sprintf(" b:%d ", i) return state, nil }) @@ -646,7 +639,7 @@ func TestMManager_ParallelUpdates(t *testing.T) { func(m *MManager, req *require.Assertions, group *sync.WaitGroup) { // append the integers 1-200 to the upstream for i := 1; i <= 200; i++ { - _, err := m.StateUpdate(func(state VersionedState) (VersionedState, error) { + _, err := m.StateUpdate(func(state State) (State, error) { state.V1.Upstream += fmt.Sprintf(" c:%d ", i) return state, nil }) @@ -658,7 +651,7 @@ func TestMManager_ParallelUpdates(t *testing.T) { func(m *MManager, req *require.Assertions, group *sync.WaitGroup) { // append the integers 1-200 to the upstream for i := 1; i <= 200; i++ { - _, err := m.StateUpdate(func(state VersionedState) (VersionedState, error) { + _, err := m.StateUpdate(func(state State) (State, error) { state.V1.Upstream += fmt.Sprintf(" d:%d ", i) return state, nil }) @@ -670,7 +663,7 @@ func TestMManager_ParallelUpdates(t *testing.T) { func(m *MManager, req *require.Assertions, group *sync.WaitGroup) { // append the integers 1-200 to the upstream for i := 1; i <= 200; i++ { - _, err := m.StateUpdate(func(state VersionedState) (VersionedState, error) { + _, err := m.StateUpdate(func(state State) (State, error) { state.V1.Upstream += fmt.Sprintf(" e:%d ", i) return state, nil }) @@ -679,7 +672,7 @@ func TestMManager_ParallelUpdates(t *testing.T) { group.Done() }, }, - validator: func(state VersionedState, req *require.Assertions) { + validator: func(state State, req *require.Assertions) { req.Len(state.V1.Metadata.Lists, 20) totalUpstream := state.Upstream() @@ -739,7 +732,7 @@ func TestMManager_ParallelUpdates(t *testing.T) { group.Done() }, }, - validator: func(state VersionedState, req *require.Assertions) { + validator: func(state State, req *require.Assertions) { totalCAs := state.CurrentCAs() for _, str := range []string{"a", "b"} { for i := 1; i <= 100; i++ { @@ -766,7 +759,7 @@ func TestMManager_ParallelUpdates(t *testing.T) { V: viper.New(), } - initialState := VersionedState{V1: &V1{Lifecycle: nil}} + initialState := State{V1: &V1{Lifecycle: nil}} group := sync.WaitGroup{} @@ -793,19 +786,19 @@ func TestMManager_AddCA(t *testing.T) { caName string newCA util.CAType wantErr bool - before VersionedState - expected VersionedState + before State + expected State }{ { name: "basic test", caName: "aCA", newCA: util.CAType{Cert: "aCert", Key: "aKey"}, - before: VersionedState{ + before: State{ V1: &V1{ Upstream: "abc123", }, }, - expected: VersionedState{ + expected: State{ V1: &V1{ Upstream: "abc123", CAs: map[string]util.CAType{ @@ -818,7 +811,7 @@ func TestMManager_AddCA(t *testing.T) { name: "add to existing", caName: "bCA", newCA: util.CAType{Cert: "bCert", Key: "bKey"}, - before: VersionedState{ + before: State{ V1: &V1{ Upstream: "abc123", CAs: map[string]util.CAType{ @@ -826,7 +819,7 @@ func TestMManager_AddCA(t *testing.T) { }, }, }, - expected: VersionedState{ + expected: State{ V1: &V1{ Upstream: "abc123", CAs: map[string]util.CAType{ @@ -841,7 +834,7 @@ func TestMManager_AddCA(t *testing.T) { wantErr: true, caName: "aCA", newCA: util.CAType{Cert: "aCert", Key: "aKey"}, - before: VersionedState{ + before: State{ V1: &V1{ Upstream: "abc123", CAs: map[string]util.CAType{ @@ -849,7 +842,7 @@ func TestMManager_AddCA(t *testing.T) { }, }, }, - expected: VersionedState{ + expected: State{ V1: &V1{ Upstream: "abc123", CAs: map[string]util.CAType{ @@ -892,19 +885,19 @@ func TestMManager_AddCert(t *testing.T) { certName string newCert util.CertType wantErr bool - before VersionedState - expected VersionedState + before State + expected State }{ { name: "basic test", certName: "aCert", newCert: util.CertType{Cert: "aCert", Key: "aKey"}, - before: VersionedState{ + before: State{ V1: &V1{ Upstream: "abc123", }, }, - expected: VersionedState{ + expected: State{ V1: &V1{ Upstream: "abc123", Certs: map[string]util.CertType{ @@ -917,7 +910,7 @@ func TestMManager_AddCert(t *testing.T) { name: "add to existing", certName: "bCert", newCert: util.CertType{Cert: "bCert", Key: "bKey"}, - before: VersionedState{ + before: State{ V1: &V1{ Upstream: "abc123", Certs: map[string]util.CertType{ @@ -925,7 +918,7 @@ func TestMManager_AddCert(t *testing.T) { }, }, }, - expected: VersionedState{ + expected: State{ V1: &V1{ Upstream: "abc123", Certs: map[string]util.CertType{ @@ -940,7 +933,7 @@ func TestMManager_AddCert(t *testing.T) { wantErr: true, certName: "aCert", newCert: util.CertType{Cert: "aCert", Key: "aKey"}, - before: VersionedState{ + before: State{ V1: &V1{ Upstream: "abc123", Certs: map[string]util.CertType{ @@ -948,7 +941,7 @@ func TestMManager_AddCert(t *testing.T) { }, }, }, - expected: VersionedState{ + expected: State{ V1: &V1{ Upstream: "abc123", Certs: map[string]util.CertType{ diff --git a/pkg/state/models.go b/pkg/state/models.go index bfdf74fa0..abc87a783 100644 --- a/pkg/state/models.go +++ b/pkg/state/models.go @@ -6,72 +6,18 @@ import ( "time" "github.com/hashicorp/terraform/terraform" + "github.com/replicatedhq/ship/pkg/api" "github.com/replicatedhq/ship/pkg/util" + "github.com/replicatedhq/ship/pkg/version" ) -// now that we have Versioned(), we probably don't need nearly so broad an interface here -type State interface { - CurrentConfig() map[string]interface{} - CurrentKustomize() *Kustomize - CurrentKustomizeOverlay(filename string) (string, bool) - CurrentHelmValues() string - CurrentHelmValuesDefaults() string - CurrentReleaseName() string - CurrentNamespace() string - Upstream() string - UpstreamContents() *UpstreamContents - Versioned() VersionedState - IsEmpty() bool - CurrentCAs() map[string]util.CAType - CurrentCerts() map[string]util.CertType - ReleaseMetadata() *api.ReleaseMetadata -} - -var _ State = VersionedState{} -var _ State = Empty{} -var _ State = V0{} - -type Empty struct{} - -func (Empty) CurrentKustomize() *Kustomize { return nil } -func (Empty) CurrentKustomizeOverlay(string) (string, bool) { return "", false } -func (Empty) CurrentConfig() map[string]interface{} { return make(map[string]interface{}) } -func (Empty) CurrentHelmValues() string { return "" } -func (Empty) CurrentHelmValuesDefaults() string { return "" } -func (Empty) CurrentReleaseName() string { return "" } -func (Empty) CurrentNamespace() string { return "" } -func (Empty) CurrentCAs() map[string]util.CAType { return nil } -func (Empty) CurrentCerts() map[string]util.CertType { return nil } -func (Empty) ReleaseMetadata() *api.ReleaseMetadata { return nil } -func (Empty) UpstreamContents() *UpstreamContents { return nil } -func (Empty) Upstream() string { return "" } -func (Empty) Versioned() VersionedState { return VersionedState{V1: &V1{}} } -func (Empty) IsEmpty() bool { return true } - -type V0 map[string]interface{} - -func (v V0) CurrentConfig() map[string]interface{} { return v } -func (v V0) CurrentKustomize() *Kustomize { return nil } -func (v V0) CurrentKustomizeOverlay(string) (string, bool) { return "", false } -func (v V0) CurrentHelmValues() string { return "" } -func (v V0) CurrentHelmValuesDefaults() string { return "" } -func (v V0) CurrentReleaseName() string { return "" } -func (v V0) CurrentNamespace() string { return "" } -func (v V0) CurrentCAs() map[string]util.CAType { return nil } -func (v V0) CurrentCerts() map[string]util.CertType { return nil } -func (v V0) ReleaseMetadata() *api.ReleaseMetadata { return nil } -func (v V0) UpstreamContents() *UpstreamContents { return nil } -func (v V0) Upstream() string { return "" } -func (v V0) Versioned() VersionedState { return VersionedState{V1: &V1{Config: v}} } -func (v V0) IsEmpty() bool { return false } - -type VersionedState struct { +type State struct { V1 *V1 `json:"v1,omitempty" yaml:"v1,omitempty" hcl:"v1,omitempty"` } -func (v VersionedState) IsEmpty() bool { - return false +func (v State) IsEmpty() bool { + return v.V1 == nil } type V1 struct { @@ -85,6 +31,7 @@ type V1 struct { Upstream string `json:"upstream,omitempty" yaml:"upstream,omitempty" hcl:"upstream,omitempty"` Metadata *Metadata `json:"metadata,omitempty" yaml:"metadata,omitempty" hcl:"metadata,omitempty"` UpstreamContents *UpstreamContents `json:"upstreamContents,omitempty" yaml:"upstreamContents,omitempty" hcl:"upstreamContents,omitempty"` + ShipVersion *version.Build `json:"shipVersion,omitempty" yaml:"shipVersion,omitempty" hcl:"shipVersion,omitempty"` //deprecated in favor of upstream ChartURL string `json:"chartURL,omitempty" yaml:"chartURL,omitempty" hcl:"chartURL,omitempty"` @@ -189,14 +136,14 @@ func (k *Kustomize) Ship() Overlay { return NewOverlay() } -func (v VersionedState) CurrentKustomize() *Kustomize { +func (v State) CurrentKustomize() *Kustomize { if v.V1 != nil { return v.V1.Kustomize } return nil } -func (v VersionedState) CurrentKustomizeOverlay(filename string) (contents string, isResource bool) { +func (v State) CurrentKustomizeOverlay(filename string) (contents string, isResource bool) { if v.V1.Kustomize == nil { return } @@ -231,42 +178,42 @@ type Terraform struct { State *terraform.State `json:"state,omitempty" yaml:"state,omitempty" hcl:"state,omitempty"` } -func (v VersionedState) CurrentConfig() map[string]interface{} { +func (v State) CurrentConfig() map[string]interface{} { if v.V1 != nil && v.V1.Config != nil { return v.V1.Config } return make(map[string]interface{}) } -func (v VersionedState) CurrentHelmValues() string { +func (v State) CurrentHelmValues() string { if v.V1 != nil { return v.V1.HelmValues } return "" } -func (v VersionedState) CurrentHelmValuesDefaults() string { +func (v State) CurrentHelmValuesDefaults() string { if v.V1 != nil { return v.V1.HelmValuesDefaults } return "" } -func (v VersionedState) CurrentReleaseName() string { +func (v State) CurrentReleaseName() string { if v.V1 != nil { return v.V1.ReleaseName } return "" } -func (v VersionedState) CurrentNamespace() string { +func (v State) CurrentNamespace() string { if v.V1 != nil { return v.V1.Namespace } return "" } -func (v VersionedState) Upstream() string { +func (v State) Upstream() string { if v.V1 != nil { if v.V1.Upstream != "" { return v.V1.Upstream @@ -276,16 +223,19 @@ func (v VersionedState) Upstream() string { return "" } -func (v VersionedState) Versioned() VersionedState { +func (v State) Versioned() State { + if v.V1 == nil { + v.V1 = &V1{} + } return v } -func (v VersionedState) WithCompletedStep(step api.Step) VersionedState { +func (v State) WithCompletedStep(step api.Step) State { v.V1.Lifecycle = v.V1.Lifecycle.WithCompletedStep(step) return v } -func (v VersionedState) migrateDeprecatedFields() VersionedState { +func (v State) migrateDeprecatedFields() State { if v.V1 != nil { v.V1.Upstream = v.Upstream() v.V1.ChartURL = "" @@ -293,21 +243,21 @@ func (v VersionedState) migrateDeprecatedFields() VersionedState { return v } -func (v VersionedState) CurrentCAs() map[string]util.CAType { +func (v State) CurrentCAs() map[string]util.CAType { if v.V1 != nil { return v.V1.CAs } return nil } -func (v VersionedState) CurrentCerts() map[string]util.CertType { +func (v State) CurrentCerts() map[string]util.CertType { if v.V1 != nil { return v.V1.Certs } return nil } -func (v VersionedState) UpstreamContents() *UpstreamContents { +func (v State) UpstreamContents() *UpstreamContents { if v.V1 != nil { if v.V1.UpstreamContents != nil { return v.V1.UpstreamContents @@ -400,7 +350,7 @@ func (r *ShipRelease) githubContents() []api.GithubContent { return result } -func (v VersionedState) ReleaseMetadata() *api.ReleaseMetadata { +func (v State) ReleaseMetadata() *api.ReleaseMetadata { if v.V1 != nil { if v.V1.UpstreamContents != nil { baseMeta := v.V1.UpstreamContents.AppRelease.ToReleaseMeta() diff --git a/pkg/test-mocks/state/manager_mock.go b/pkg/test-mocks/state/manager_mock.go index 984d31060..677ea1204 100644 --- a/pkg/test-mocks/state/manager_mock.go +++ b/pkg/test-mocks/state/manager_mock.go @@ -97,7 +97,7 @@ func (mr *MockManagerMockRecorder) ResetLifecycle() *gomock.Call { } // Save mocks base method -func (m *MockManager) Save(arg0 state.VersionedState) error { +func (m *MockManager) Save(arg0 state.State) error { ret := m.ctrl.Call(m, "Save", arg0) ret0, _ := ret[0].(error) return ret0