Skip to content

Commit 6db7cef

Browse files
committed
Update delete integration and add unit tests
1 parent 82f7fde commit 6db7cef

File tree

3 files changed

+770
-8
lines changed

3 files changed

+770
-8
lines changed

pkg/cmd/integration.go

+55-8
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,10 @@ oc plugin mobile delete integration <consuming_service_instance_id> <providing_s
236236
if len(args) != 2 {
237237
return cmd.Usage()
238238
}
239+
quiet, err := cmd.Flags().GetBool("quiet")
240+
if err != nil {
241+
return errors.Wrap(err, "failed to get quiet flag")
242+
}
239243
namespace, err := currentNamespace(cmd.Flags())
240244
if err != nil {
241245
return errors.Wrap(err, "failed to get namespace")
@@ -264,22 +268,65 @@ oc plugin mobile delete integration <consuming_service_instance_id> <providing_s
264268
if err != nil {
265269
return errors.WithStack(err)
266270
}
267-
if !redeploy {
268-
fmt.Println("you will need to redeploy your service to pick up the changes")
271+
noWait, err := cmd.PersistentFlags().GetBool("no-wait")
272+
if err != nil {
273+
return errors.WithStack(err)
274+
}
275+
if noWait && !redeploy {
276+
fmt.Sprintln(bc.Out, "you will need to redeploy your service to pick up the changes")
269277
return nil
270278
}
271-
//update the deployment with an annotation
272-
dep, err := bc.k8Client.AppsV1beta1().Deployments(namespace).Get(consumerSvcName, metav1.GetOptions{})
279+
280+
w, err := bc.scClient.ServicecatalogV1beta1().ServiceBindings(namespace).Watch(metav1.ListOptions{})
273281
if err != nil {
274-
return errors.Wrap(err, "failed to get deployment for service "+consumerSvcInstName)
282+
return errors.WithStack(err)
275283
}
276-
delete(dep.Spec.Template.Labels, providerSvcName)
277-
if _, err := bc.k8Client.AppsV1beta1().Deployments(namespace).Update(dep); err != nil {
278-
return errors.Wrap(err, "failed to update deployment for service "+consumerSvcInstName)
284+
for u := range w.ResultChan() {
285+
o := u.Object.(*v1beta1.ServiceBinding)
286+
if o.Name != objectName {
287+
continue
288+
}
289+
switch u.Type {
290+
case watch.Error:
291+
w.Stop()
292+
return errors.New("unexpected error watching service binding " + err.Error())
293+
case watch.Modified:
294+
for _, c := range o.Status.Conditions {
295+
if !quiet {
296+
fmt.Println("status: " + c.Message)
297+
}
298+
if c.Type == "Ready" && c.Status == "True" {
299+
w.Stop()
300+
}
301+
if c.Type == "Failed" {
302+
w.Stop()
303+
return errors.New("Failed to create integration: " + c.Message)
304+
}
305+
}
306+
case watch.Deleted:
307+
w.Stop()
308+
}
309+
}
310+
311+
if !quiet {
312+
fmt.Printf("Completed deletion of ServiceBinding %v\n", objectName)
313+
}
314+
315+
if redeploy {
316+
//update the deployment with an annotation
317+
dep, err := bc.k8Client.AppsV1beta1().Deployments(namespace).Get(consumerSvcName, metav1.GetOptions{})
318+
if err != nil {
319+
return errors.Wrap(err, "service "+consumerSvcInstName)
320+
}
321+
delete(dep.Spec.Template.Labels, providerSvcName)
322+
if _, err := bc.k8Client.AppsV1beta1().Deployments(namespace).Update(dep); err != nil {
323+
return errors.Wrap(err, "failed to update deployment for service "+consumerSvcInstName)
324+
}
279325
}
280326
return nil
281327
},
282328
}
329+
cmd.PersistentFlags().Bool("no-wait", false, "--no-wait will cause the command to exit immediately after a successful response instead of waiting until the binding is complete")
283330
cmd.PersistentFlags().Bool("auto-redeploy", false, "--auto-redeploy=true will cause a backing deployment to be rolled out")
284331
return cmd
285332
}

pkg/cmd/integration_test.go

+238
Original file line numberDiff line numberDiff line change
@@ -318,3 +318,241 @@ func TestIntegrationCmd_ListIntegrationCmd(t *testing.T) {
318318
})
319319
}
320320
}
321+
322+
func TestIntegrationCmd_DeleteIntegrationCmd(t *testing.T) {
323+
var defaultServiceBinding = &v1beta1.ServiceBinding{
324+
ObjectMeta: metav1.ObjectMeta{
325+
Name: "keycloak-fh-sync-server",
326+
},
327+
Status: v1beta1.ServiceBindingStatus{
328+
Conditions: []v1beta1.ServiceBindingCondition{
329+
{
330+
Status: v1beta1.ConditionStatus("True"),
331+
Type: v1beta1.ServiceBindingConditionType("Ready"),
332+
},
333+
},
334+
},
335+
}
336+
337+
cases := []struct {
338+
Name string
339+
SvcCatalogClient func() (versioned.Interface, *watch.FakeWatcher, []runtime.Object)
340+
K8Client func() kubernetes.Interface
341+
ExpectError bool
342+
ExpectUsage bool
343+
ValidateErr func(t *testing.T, err error)
344+
Args []string
345+
Flags []string
346+
}{
347+
{
348+
Name: "test returns usage if missing arguments",
349+
SvcCatalogClient: func() (versioned.Interface, *watch.FakeWatcher, []runtime.Object) {
350+
fake := &scFake.Clientset{}
351+
return fake, nil, nil
352+
},
353+
K8Client: func() kubernetes.Interface {
354+
return &kFake.Clientset{}
355+
},
356+
ExpectError: false,
357+
ExpectUsage: true,
358+
Args: []string{},
359+
Flags: []string{},
360+
},
361+
{
362+
Name: "test returns error if flags not set",
363+
SvcCatalogClient: func() (versioned.Interface, *watch.FakeWatcher, []runtime.Object) {
364+
fake := &scFake.Clientset{}
365+
return fake, nil, nil
366+
},
367+
K8Client: func() kubernetes.Interface {
368+
return &kFake.Clientset{}
369+
},
370+
ExpectError: true,
371+
ValidateErr: func(t *testing.T, err error) {
372+
expectedErr := "failed to get namespace: no namespace present. Cannot continue. Please set the --namespace flag or the KUBECTL_PLUGINS_CURRENT_NAMESPACE env var"
373+
if err.Error() != expectedErr {
374+
t.Fatalf("expected error to be '%s' but got '%v'", expectedErr, err)
375+
}
376+
},
377+
Args: []string{"keycloak", "fh-sync-server"},
378+
Flags: []string{},
379+
},
380+
{
381+
Name: "returns error when deployment cannot be found",
382+
SvcCatalogClient: func() (versioned.Interface, *watch.FakeWatcher, []runtime.Object) {
383+
fake := &scFake.Clientset{}
384+
fakeWatch := watch.NewFake()
385+
fake.AddWatchReactor("servicebindings", ktesting.DefaultWatchReactor(fakeWatch, nil))
386+
fake.AddReactor("get", "serviceinstances", func(action ktesting.Action) (handled bool, ret runtime.Object, err error) {
387+
return true, &v1beta1.ServiceInstance{
388+
ObjectMeta: metav1.ObjectMeta{
389+
Name: "keycloak",
390+
Labels: map[string]string{
391+
"serviceName": "keycloak",
392+
},
393+
},
394+
}, nil
395+
})
396+
fake.AddReactor("get", "serviceinstances", func(action ktesting.Action) (handled bool, ret runtime.Object, err error) {
397+
return true, &v1beta1.ServiceInstance{
398+
ObjectMeta: metav1.ObjectMeta{
399+
Name: "fh-sync-server",
400+
Labels: map[string]string{
401+
"serviceName": "fh-sync-server",
402+
},
403+
},
404+
}, nil
405+
})
406+
fake.AddReactor("delete", "podpreset", func(action ktesting.Action) (handled bool, ret runtime.Object, err error) {
407+
return true, nil, nil
408+
})
409+
fake.AddReactor("delete", "serviceinstances", func(action ktesting.Action) (handled bool, ret runtime.Object, err error) {
410+
return true, nil, nil
411+
})
412+
return fake, fakeWatch, []runtime.Object{
413+
defaultServiceBinding,
414+
}
415+
},
416+
K8Client: func() kubernetes.Interface {
417+
fake := &kFake.Clientset{}
418+
fake.AddReactor("get", "deployments", func(action ktesting.Action) (handled bool, ret runtime.Object, err error) {
419+
return true, nil, errors.New("failed to get deployment")
420+
})
421+
return fake
422+
},
423+
ExpectError: true,
424+
ValidateErr: func(t *testing.T, err error) {
425+
expectedErr := "service keycloak: failed to get deployment"
426+
if err.Error() != expectedErr {
427+
t.Fatalf("expected error to be '%s' but got '%v'", expectedErr, err)
428+
}
429+
},
430+
Args: []string{"keycloak", "fh-sync-server"},
431+
Flags: []string{"--namespace=test", "--auto-redeploy=true", "--no-wait=true"},
432+
},
433+
{
434+
Name: "returns error when deployment cannot be updated",
435+
SvcCatalogClient: func() (versioned.Interface, *watch.FakeWatcher, []runtime.Object) {
436+
fakeWatch := watch.NewFake()
437+
fake := &scFake.Clientset{}
438+
fake.AddWatchReactor("servicebindings", ktesting.DefaultWatchReactor(fakeWatch, nil))
439+
440+
fake.AddReactor("get", "serviceinstances", func(action ktesting.Action) (handled bool, ret runtime.Object, err error) {
441+
return true, &v1beta1.ServiceInstance{
442+
ObjectMeta: metav1.ObjectMeta{
443+
Name: "keycloak",
444+
},
445+
}, nil
446+
})
447+
return fake, fakeWatch, []runtime.Object{
448+
defaultServiceBinding,
449+
}
450+
},
451+
K8Client: func() kubernetes.Interface {
452+
fake := &kFake.Clientset{}
453+
fake.AddReactor("get", "deployments", func(action ktesting.Action) (handled bool, ret runtime.Object, err error) {
454+
return true, &kbeta.Deployment{
455+
Spec: kbeta.DeploymentSpec{
456+
Template: corev1.PodTemplateSpec{
457+
ObjectMeta: metav1.ObjectMeta{
458+
Labels: map[string]string{},
459+
},
460+
},
461+
},
462+
}, nil
463+
})
464+
fake.AddReactor("update", "deployments", func(action ktesting.Action) (handled bool, ret runtime.Object, err error) {
465+
return true, nil, errors.New("failed to update deployment")
466+
})
467+
return fake
468+
},
469+
ExpectError: true,
470+
ValidateErr: func(t *testing.T, err error) {
471+
expectedErr := "failed to update deployment for service keycloak: failed to update deployment"
472+
if err.Error() != expectedErr {
473+
t.Fatalf("expected error to be '%s' but got '%v'", expectedErr, err)
474+
}
475+
},
476+
Args: []string{"keycloak", "fh-sync-server"},
477+
Flags: []string{"--namespace=test", "--auto-redeploy=true", "--no-wait=true"},
478+
},
479+
{
480+
Name: "should pass when serviceinstances exist and auto-redeploy is set",
481+
SvcCatalogClient: func() (versioned.Interface, *watch.FakeWatcher, []runtime.Object) {
482+
fake := &scFake.Clientset{}
483+
fakeWatch := watch.NewFake()
484+
fake.AddWatchReactor("servicebindings", ktesting.DefaultWatchReactor(fakeWatch, nil))
485+
fake.AddReactor("get", "serviceinstances", func(action ktesting.Action) (handled bool, ret runtime.Object, err error) {
486+
return true, &v1beta1.ServiceInstance{
487+
ObjectMeta: metav1.ObjectMeta{
488+
Name: "keycloak",
489+
},
490+
}, nil
491+
})
492+
fake.AddReactor("get", "serviceinstances", func(action ktesting.Action) (handled bool, ret runtime.Object, err error) {
493+
return true, &v1beta1.ServiceInstance{
494+
ObjectMeta: metav1.ObjectMeta{
495+
Name: "fh-sync-server",
496+
},
497+
}, nil
498+
})
499+
return fake, fakeWatch, []runtime.Object{
500+
defaultServiceBinding,
501+
}
502+
},
503+
K8Client: func() kubernetes.Interface {
504+
fake := &kFake.Clientset{}
505+
fake.AddReactor("get", "deployments", func(action ktesting.Action) (handled bool, ret runtime.Object, err error) {
506+
return true, &kbeta.Deployment{
507+
Spec: kbeta.DeploymentSpec{
508+
Template: corev1.PodTemplateSpec{
509+
ObjectMeta: metav1.ObjectMeta{
510+
Labels: map[string]string{},
511+
},
512+
},
513+
},
514+
}, nil
515+
})
516+
return fake
517+
},
518+
Args: []string{"keycloak", "fh-sync-server"},
519+
Flags: []string{"--namespace=test", "--auto-redeploy=true", "--no-wait=true"},
520+
},
521+
}
522+
523+
for _, tc := range cases {
524+
t.Run(tc.Name, func(t *testing.T) {
525+
root := cmd.NewRootCmd()
526+
var out bytes.Buffer
527+
scClient, fakeWatch, updates := tc.SvcCatalogClient()
528+
if fakeWatch != nil {
529+
go func() {
530+
for _, u := range updates {
531+
fakeWatch.Modify(u)
532+
}
533+
}()
534+
}
535+
integrationCmd := cmd.NewIntegrationCmd(scClient, tc.K8Client(), &out)
536+
deleteCmd := integrationCmd.DeleteIntegrationCmd()
537+
deleteCmd.SetOutput(&out)
538+
root.AddCommand(deleteCmd)
539+
if err := deleteCmd.ParseFlags(tc.Flags); err != nil {
540+
t.Fatal("failed to parse command flags", err)
541+
}
542+
err := deleteCmd.RunE(deleteCmd, tc.Args)
543+
544+
if err != nil && !tc.ExpectError {
545+
t.Fatal("did not expect an error but gone one:", err)
546+
}
547+
if err == nil && tc.ExpectError {
548+
t.Fatal("expected an error but got none")
549+
}
550+
if tc.ExpectUsage && out.String() != deleteCmd.UsageString() {
551+
t.Fatalf("Expected error to be '%s' but got '%v'", deleteCmd.UsageString(), err)
552+
}
553+
if tc.ValidateErr != nil {
554+
tc.ValidateErr(t, err)
555+
}
556+
})
557+
}
558+
}

0 commit comments

Comments
 (0)