github.com/splunk/dan1-qbec@v0.7.3/internal/commands/apply_test.go (about) 1 /* 2 Copyright 2019 Splunk Inc. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package commands 18 19 import ( 20 "regexp" 21 "testing" 22 23 "github.com/splunk/qbec/internal/model" 24 "github.com/splunk/qbec/internal/remote" 25 "github.com/splunk/qbec/internal/rollout" 26 "github.com/stretchr/testify/assert" 27 "github.com/stretchr/testify/require" 28 ) 29 30 func TestNsWrap(t *testing.T) { 31 obj := model.NewK8sObject(map[string]interface{}{ 32 "kind": "foo", 33 "apiversion": "apps/v1", 34 "metadata": map[string]interface{}{ 35 "namespace": "foo", 36 "name": "foo", 37 }, 38 }) 39 w := nsWrap{K8sMeta: obj, ns: "bar"} 40 a := assert.New(t) 41 a.Equal("foo", w.GetNamespace()) 42 a.Equal("foo", w.GetName()) 43 obj = model.NewK8sObject(map[string]interface{}{ 44 "kind": "foo", 45 "apiversion": "apps/v1", 46 "metadata": map[string]interface{}{ 47 "name": "foo", 48 }, 49 }) 50 w = nsWrap{K8sMeta: obj, ns: "bar"} 51 a.Equal("bar", w.GetNamespace()) 52 a.Equal("foo", w.GetName()) 53 } 54 55 func TestApplyBasic(t *testing.T) { 56 s := newScaffold(t) 57 defer s.reset() 58 applyWaitFn = func(objects []model.K8sMeta, wp rollout.WatchProvider, opts rollout.WaitOptions) (finalErr error) { 59 return nil 60 } 61 first := true 62 var captured remote.SyncOptions 63 s.client.syncFunc = func(obj model.K8sLocalObject, opts remote.SyncOptions) (*remote.SyncResult, error) { 64 if first { 65 first = false 66 captured = opts 67 } 68 switch { 69 case obj.GetName() == "svc2-cm": 70 return &remote.SyncResult{Type: remote.SyncUpdated, Details: "data updated"}, nil 71 case obj.GetName() == "svc2-secret": 72 return &remote.SyncResult{Type: remote.SyncCreated, Details: "some yaml"}, nil 73 case obj.GetName() == "svc2-deploy": 74 return &remote.SyncResult{Type: remote.SyncObjectsIdentical, Details: "sync skipped"}, nil 75 case obj.GetName() == "": 76 return &remote.SyncResult{Type: remote.SyncCreated, GeneratedName: obj.GetGenerateName() + "1234", Details: "created"}, nil 77 default: 78 return &remote.SyncResult{Type: remote.SyncObjectsIdentical, Details: "sync skipped"}, nil 79 } 80 } 81 s.client.listFunc = stdLister 82 s.client.deleteFunc = func(obj model.K8sMeta, dryRun bool) (*remote.SyncResult, error) { 83 return &remote.SyncResult{Type: remote.SyncDeleted}, nil 84 } 85 err := s.executeCommand("apply", "dev", "--wait") 86 require.Nil(t, err) 87 stats := s.outputStats() 88 a := assert.New(t) 89 a.EqualValues(remote.SyncOptions{}, captured) 90 a.True(stats["same"].(float64) > 0) 91 a.EqualValues(8, stats["same"]) 92 a.EqualValues([]interface{}{"Secret:bar-system:svc2-secret", "Job::tj-1234"}, stats["created"]) 93 a.EqualValues([]interface{}{"ConfigMap:bar-system:svc2-cm"}, stats["updated"]) 94 a.EqualValues([]interface{}{"Deployment:bar-system:svc2-previous-deploy"}, stats["deleted"]) 95 s.assertErrorLineMatch(regexp.MustCompile(`sync ConfigMap:bar-system:svc2-cm`)) 96 } 97 98 func TestApplyFlags(t *testing.T) { 99 s := newScaffold(t) 100 defer s.reset() 101 first := true 102 var captured remote.SyncOptions 103 s.client.syncFunc = func(obj model.K8sLocalObject, opts remote.SyncOptions) (*remote.SyncResult, error) { 104 if first { 105 first = false 106 captured = opts 107 } 108 switch { 109 case obj.GetName() == "svc2-cm": 110 return &remote.SyncResult{Type: remote.SyncUpdated, Details: "data updated"}, nil 111 case obj.GetName() == "svc2-secret": 112 return &remote.SyncResult{Type: remote.SyncSkip, Details: "create skipped"}, nil 113 default: 114 return &remote.SyncResult{Type: remote.SyncObjectsIdentical, Details: "sync skipped"}, nil 115 } 116 } 117 err := s.executeCommand("apply", "dev", "-S", "-n", "--skip-create", "--gc=false") 118 require.Nil(t, err) 119 stats := s.outputStats() 120 a := assert.New(t) 121 a.EqualValues(remote.SyncOptions{DryRun: true, ShowSecrets: true, DisableCreate: true}, captured) 122 a.EqualValues(nil, stats["created"]) 123 a.EqualValues([]interface{}{"Secret:bar-system:svc2-secret"}, stats["skipped"]) 124 a.EqualValues([]interface{}{"ConfigMap:bar-system:svc2-cm"}, stats["updated"]) 125 s.assertErrorLineMatch(regexp.MustCompile(`\[dry-run\] sync ConfigMap:bar-system:svc2-cm`)) 126 s.assertErrorLineMatch(regexp.MustCompile(`\*\* dry-run mode, nothing was actually changed \*\*`)) 127 } 128 129 func TestApplyNegative(t *testing.T) { 130 tests := []struct { 131 name string 132 args []string 133 asserter func(s *scaffold, err error) 134 }{ 135 { 136 name: "no env", 137 args: []string{"apply"}, 138 asserter: func(s *scaffold, err error) { 139 a := assert.New(s.t) 140 a.True(isUsageError(err)) 141 a.Equal("exactly one environment required", err.Error()) 142 }, 143 }, 144 { 145 name: "2 envs", 146 args: []string{"apply", "dev", "prod"}, 147 asserter: func(s *scaffold, err error) { 148 a := assert.New(s.t) 149 a.True(isUsageError(err)) 150 a.Equal("exactly one environment required", err.Error()) 151 }, 152 }, 153 { 154 name: "bad env", 155 args: []string{"apply", "foo"}, 156 asserter: func(s *scaffold, err error) { 157 a := assert.New(s.t) 158 a.False(isUsageError(err)) 159 a.Equal("invalid environment \"foo\"", err.Error()) 160 }, 161 }, 162 { 163 name: "baseline env", 164 args: []string{"apply", "_"}, 165 asserter: func(s *scaffold, err error) { 166 a := assert.New(s.t) 167 a.True(isUsageError(err)) 168 a.Equal("cannot apply baseline environment, use a real environment", err.Error()) 169 }, 170 }, 171 { 172 name: "c and C", 173 args: []string{"apply", "dev", "-c", "cluster-objects", "-C", "service2"}, 174 asserter: func(s *scaffold, err error) { 175 a := assert.New(s.t) 176 a.True(isUsageError(err)) 177 a.Equal(`cannot include as well as exclude components, specify one or the other`, err.Error()) 178 }, 179 }, 180 { 181 name: "k and K", 182 args: []string{"apply", "dev", "-k", "namespace", "-K", "secret"}, 183 asserter: func(s *scaffold, err error) { 184 a := assert.New(s.t) 185 a.True(isUsageError(err)) 186 a.Equal(`cannot include as well as exclude kinds, specify one or the other`, err.Error()) 187 }, 188 }, 189 } 190 for _, test := range tests { 191 t.Run(test.name, func(t *testing.T) { 192 s := newScaffold(t) 193 defer s.reset() 194 err := s.executeCommand(test.args...) 195 require.NotNil(t, err) 196 test.asserter(s, err) 197 }) 198 } 199 200 }