github.com/wmuizelaar/kpt@v0.0.0-20221018115725-bd564717b2ed/commands/live/status/cmdstatus_test.go (about) 1 // Copyright 2022 Google LLC 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package status 16 17 import ( 18 "bytes" 19 "context" 20 "path/filepath" 21 "strings" 22 "testing" 23 "time" 24 25 "github.com/GoogleContainerTools/kpt/internal/printer/fake" 26 "github.com/GoogleContainerTools/kpt/internal/testutil" 27 kptfilev1 "github.com/GoogleContainerTools/kpt/pkg/api/kptfile/v1" 28 "github.com/GoogleContainerTools/kpt/pkg/kptfile/kptfileutil" 29 "github.com/stretchr/testify/assert" 30 "k8s.io/apimachinery/pkg/runtime/schema" 31 cmdtesting "k8s.io/kubectl/pkg/cmd/testing" 32 cmdutil "k8s.io/kubectl/pkg/cmd/util" 33 "sigs.k8s.io/cli-utils/pkg/apply/poller" 34 "sigs.k8s.io/cli-utils/pkg/inventory" 35 "sigs.k8s.io/cli-utils/pkg/kstatus/polling" 36 pollevent "sigs.k8s.io/cli-utils/pkg/kstatus/polling/event" 37 "sigs.k8s.io/cli-utils/pkg/kstatus/status" 38 "sigs.k8s.io/cli-utils/pkg/object" 39 ) 40 41 var ( 42 depObject = object.ObjMetadata{ 43 Name: "foo", 44 Namespace: "default", 45 GroupKind: schema.GroupKind{ 46 Group: "apps", 47 Kind: "Deployment", 48 }, 49 } 50 51 stsObject = object.ObjMetadata{ 52 Name: "bar", 53 Namespace: "default", 54 GroupKind: schema.GroupKind{ 55 Group: "apps", 56 Kind: "StatefulSet", 57 }, 58 } 59 ) 60 61 func TestStatusCommand(t *testing.T) { 62 testCases := map[string]struct { 63 pollUntil string 64 printer string 65 timeout time.Duration 66 kptfileInv *kptfilev1.Inventory 67 inventory []object.ObjMetadata 68 events []pollevent.Event 69 expectedErrMsg string 70 expectedOutput string 71 }{ 72 "no inventory template": { 73 kptfileInv: nil, 74 expectedErrMsg: "no ResourceGroup object was provided within the stream or package", 75 }, 76 "invalid value for pollUntil": { 77 pollUntil: "doesNotExist", 78 expectedErrMsg: "pollUntil must be one of known,current,deleted,forever", 79 }, 80 "no inventory in live state": { 81 kptfileInv: &kptfilev1.Inventory{ 82 Name: "foo", 83 Namespace: "default", 84 InventoryID: "test", 85 }, 86 expectedOutput: "no resources found in the inventory\n", 87 }, 88 "wait for all known": { 89 pollUntil: "known", 90 printer: "events", 91 kptfileInv: &kptfilev1.Inventory{ 92 Name: "foo", 93 Namespace: "default", 94 InventoryID: "test", 95 }, 96 inventory: []object.ObjMetadata{ 97 depObject, 98 stsObject, 99 }, 100 events: []pollevent.Event{ 101 { 102 Type: pollevent.ResourceUpdateEvent, 103 Resource: &pollevent.ResourceStatus{ 104 Identifier: depObject, 105 Status: status.InProgressStatus, 106 Message: "inProgress", 107 }, 108 }, 109 { 110 Type: pollevent.ResourceUpdateEvent, 111 Resource: &pollevent.ResourceStatus{ 112 Identifier: stsObject, 113 Status: status.CurrentStatus, 114 Message: "current", 115 }, 116 }, 117 }, 118 expectedOutput: ` 119 foo/deployment.apps/default/foo is InProgress: inProgress 120 foo/statefulset.apps/default/bar is Current: current 121 `, 122 }, 123 "wait for all current": { 124 pollUntil: "current", 125 printer: "events", 126 kptfileInv: &kptfilev1.Inventory{ 127 Name: "foo", 128 Namespace: "default", 129 InventoryID: "test", 130 }, 131 inventory: []object.ObjMetadata{ 132 depObject, 133 stsObject, 134 }, 135 events: []pollevent.Event{ 136 { 137 Type: pollevent.ResourceUpdateEvent, 138 Resource: &pollevent.ResourceStatus{ 139 Identifier: depObject, 140 Status: status.InProgressStatus, 141 Message: "inProgress", 142 }, 143 }, 144 { 145 Type: pollevent.ResourceUpdateEvent, 146 Resource: &pollevent.ResourceStatus{ 147 Identifier: stsObject, 148 Status: status.InProgressStatus, 149 Message: "inProgress", 150 }, 151 }, 152 { 153 Type: pollevent.ResourceUpdateEvent, 154 Resource: &pollevent.ResourceStatus{ 155 Identifier: stsObject, 156 Status: status.CurrentStatus, 157 Message: "current", 158 }, 159 }, 160 { 161 Type: pollevent.ResourceUpdateEvent, 162 Resource: &pollevent.ResourceStatus{ 163 Identifier: depObject, 164 Status: status.CurrentStatus, 165 Message: "current", 166 }, 167 }, 168 }, 169 expectedOutput: ` 170 foo/deployment.apps/default/foo is InProgress: inProgress 171 foo/statefulset.apps/default/bar is InProgress: inProgress 172 foo/statefulset.apps/default/bar is Current: current 173 foo/deployment.apps/default/foo is Current: current 174 `, 175 }, 176 "wait for all deleted": { 177 pollUntil: "deleted", 178 printer: "events", 179 kptfileInv: &kptfilev1.Inventory{ 180 Name: "foo", 181 Namespace: "default", 182 InventoryID: "test", 183 }, 184 inventory: []object.ObjMetadata{ 185 depObject, 186 stsObject, 187 }, 188 events: []pollevent.Event{ 189 { 190 Type: pollevent.ResourceUpdateEvent, 191 Resource: &pollevent.ResourceStatus{ 192 Identifier: stsObject, 193 Status: status.NotFoundStatus, 194 Message: "notFound", 195 }, 196 }, 197 { 198 Type: pollevent.ResourceUpdateEvent, 199 Resource: &pollevent.ResourceStatus{ 200 Identifier: depObject, 201 Status: status.NotFoundStatus, 202 Message: "notFound", 203 }, 204 }, 205 }, 206 expectedOutput: ` 207 foo/statefulset.apps/default/bar is NotFound: notFound 208 foo/deployment.apps/default/foo is NotFound: notFound 209 `, 210 }, 211 "forever with timeout": { 212 pollUntil: "forever", 213 printer: "events", 214 timeout: 2 * time.Second, 215 kptfileInv: &kptfilev1.Inventory{ 216 Name: "foo", 217 Namespace: "default", 218 InventoryID: "test", 219 }, 220 inventory: []object.ObjMetadata{ 221 depObject, 222 stsObject, 223 }, 224 events: []pollevent.Event{ 225 { 226 Type: pollevent.ResourceUpdateEvent, 227 Resource: &pollevent.ResourceStatus{ 228 Identifier: stsObject, 229 Status: status.InProgressStatus, 230 Message: "inProgress", 231 }, 232 }, 233 { 234 Type: pollevent.ResourceUpdateEvent, 235 Resource: &pollevent.ResourceStatus{ 236 Identifier: depObject, 237 Status: status.InProgressStatus, 238 Message: "inProgress", 239 }, 240 }, 241 }, 242 expectedOutput: ` 243 foo/statefulset.apps/default/bar is InProgress: inProgress 244 foo/deployment.apps/default/foo is InProgress: inProgress 245 `, 246 }, 247 } 248 249 for tn, tc := range testCases { 250 t.Run(tn, func(t *testing.T) { 251 tf := cmdtesting.NewTestFactory().WithNamespace("namespace") 252 defer tf.Cleanup() 253 254 w, clean := testutil.SetupWorkspace(t) 255 defer clean() 256 kf := kptfileutil.DefaultKptfile(filepath.Base(w.WorkspaceDirectory)) 257 kf.Inventory = tc.kptfileInv 258 testutil.AddKptfileToWorkspace(t, w, kf) 259 260 revert := testutil.Chdir(t, w.WorkspaceDirectory) 261 defer revert() 262 263 var outBuf bytes.Buffer 264 ctx := fake.CtxWithPrinter(&outBuf, &outBuf) 265 invFactory := inventory.FakeClientFactory(tc.inventory) 266 loader := NewFakeLoader(ctx, tf, tc.inventory) 267 runner := NewRunner(ctx, tf, invFactory, loader) 268 runner.PollerFactoryFunc = func(c cmdutil.Factory) (poller.Poller, error) { 269 return &fakePoller{tc.events}, nil 270 } 271 272 args := []string{} 273 if tc.pollUntil != "" { 274 args = append(args, []string{ 275 "--poll-until", tc.pollUntil, 276 }...) 277 } 278 if tc.timeout != time.Duration(0) { 279 args = append(args, []string{ 280 "--timeout", tc.timeout.String(), 281 }...) 282 } 283 runner.Command.SetArgs(args) 284 runner.Command.SetOut(&outBuf) 285 err := runner.Command.Execute() 286 287 if tc.expectedErrMsg != "" { 288 if !assert.Error(t, err) { 289 t.FailNow() 290 } 291 assert.Contains(t, err.Error(), tc.expectedErrMsg) 292 return 293 } 294 295 assert.NoError(t, err) 296 assert.Equal(t, strings.TrimSpace(tc.expectedOutput), strings.TrimSpace(outBuf.String())) 297 }) 298 } 299 } 300 301 type fakePoller struct { 302 events []pollevent.Event 303 } 304 305 func (f *fakePoller) Poll(ctx context.Context, _ object.ObjMetadataSet, 306 _ polling.PollOptions) <-chan pollevent.Event { 307 eventChannel := make(chan pollevent.Event) 308 go func() { 309 defer close(eventChannel) 310 for _, e := range f.events { 311 eventChannel <- e 312 } 313 <-ctx.Done() 314 }() 315 return eventChannel 316 }