Skip to content

Commit

Permalink
Merge pull request #61 from philbrookes/AG-2014
Browse files Browse the repository at this point in the history
Update delete integration and add unit tests
  • Loading branch information
philbrookes authored Feb 1, 2018
2 parents 82f7fde + 6db7cef commit 1dc0545
Show file tree
Hide file tree
Showing 3 changed files with 770 additions and 8 deletions.
63 changes: 55 additions & 8 deletions pkg/cmd/integration.go
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,10 @@ oc plugin mobile delete integration <consuming_service_instance_id> <providing_s
if len(args) != 2 {
return cmd.Usage()
}
quiet, err := cmd.Flags().GetBool("quiet")
if err != nil {
return errors.Wrap(err, "failed to get quiet flag")
}
namespace, err := currentNamespace(cmd.Flags())
if err != nil {
return errors.Wrap(err, "failed to get namespace")
Expand Down Expand Up @@ -264,22 +268,65 @@ oc plugin mobile delete integration <consuming_service_instance_id> <providing_s
if err != nil {
return errors.WithStack(err)
}
if !redeploy {
fmt.Println("you will need to redeploy your service to pick up the changes")
noWait, err := cmd.PersistentFlags().GetBool("no-wait")
if err != nil {
return errors.WithStack(err)
}
if noWait && !redeploy {
fmt.Sprintln(bc.Out, "you will need to redeploy your service to pick up the changes")
return nil
}
//update the deployment with an annotation
dep, err := bc.k8Client.AppsV1beta1().Deployments(namespace).Get(consumerSvcName, metav1.GetOptions{})

w, err := bc.scClient.ServicecatalogV1beta1().ServiceBindings(namespace).Watch(metav1.ListOptions{})
if err != nil {
return errors.Wrap(err, "failed to get deployment for service "+consumerSvcInstName)
return errors.WithStack(err)
}
delete(dep.Spec.Template.Labels, providerSvcName)
if _, err := bc.k8Client.AppsV1beta1().Deployments(namespace).Update(dep); err != nil {
return errors.Wrap(err, "failed to update deployment for service "+consumerSvcInstName)
for u := range w.ResultChan() {
o := u.Object.(*v1beta1.ServiceBinding)
if o.Name != objectName {
continue
}
switch u.Type {
case watch.Error:
w.Stop()
return errors.New("unexpected error watching service binding " + err.Error())
case watch.Modified:
for _, c := range o.Status.Conditions {
if !quiet {
fmt.Println("status: " + c.Message)
}
if c.Type == "Ready" && c.Status == "True" {
w.Stop()
}
if c.Type == "Failed" {
w.Stop()
return errors.New("Failed to create integration: " + c.Message)
}
}
case watch.Deleted:
w.Stop()
}
}

if !quiet {
fmt.Printf("Completed deletion of ServiceBinding %v\n", objectName)
}

if redeploy {
//update the deployment with an annotation
dep, err := bc.k8Client.AppsV1beta1().Deployments(namespace).Get(consumerSvcName, metav1.GetOptions{})
if err != nil {
return errors.Wrap(err, "service "+consumerSvcInstName)
}
delete(dep.Spec.Template.Labels, providerSvcName)
if _, err := bc.k8Client.AppsV1beta1().Deployments(namespace).Update(dep); err != nil {
return errors.Wrap(err, "failed to update deployment for service "+consumerSvcInstName)
}
}
return nil
},
}
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")
cmd.PersistentFlags().Bool("auto-redeploy", false, "--auto-redeploy=true will cause a backing deployment to be rolled out")
return cmd
}
Expand Down
238 changes: 238 additions & 0 deletions pkg/cmd/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -318,3 +318,241 @@ func TestIntegrationCmd_ListIntegrationCmd(t *testing.T) {
})
}
}

func TestIntegrationCmd_DeleteIntegrationCmd(t *testing.T) {
var defaultServiceBinding = &v1beta1.ServiceBinding{
ObjectMeta: metav1.ObjectMeta{
Name: "keycloak-fh-sync-server",
},
Status: v1beta1.ServiceBindingStatus{
Conditions: []v1beta1.ServiceBindingCondition{
{
Status: v1beta1.ConditionStatus("True"),
Type: v1beta1.ServiceBindingConditionType("Ready"),
},
},
},
}

cases := []struct {
Name string
SvcCatalogClient func() (versioned.Interface, *watch.FakeWatcher, []runtime.Object)
K8Client func() kubernetes.Interface
ExpectError bool
ExpectUsage bool
ValidateErr func(t *testing.T, err error)
Args []string
Flags []string
}{
{
Name: "test returns usage if missing arguments",
SvcCatalogClient: func() (versioned.Interface, *watch.FakeWatcher, []runtime.Object) {
fake := &scFake.Clientset{}
return fake, nil, nil
},
K8Client: func() kubernetes.Interface {
return &kFake.Clientset{}
},
ExpectError: false,
ExpectUsage: true,
Args: []string{},
Flags: []string{},
},
{
Name: "test returns error if flags not set",
SvcCatalogClient: func() (versioned.Interface, *watch.FakeWatcher, []runtime.Object) {
fake := &scFake.Clientset{}
return fake, nil, nil
},
K8Client: func() kubernetes.Interface {
return &kFake.Clientset{}
},
ExpectError: true,
ValidateErr: func(t *testing.T, err error) {
expectedErr := "failed to get namespace: no namespace present. Cannot continue. Please set the --namespace flag or the KUBECTL_PLUGINS_CURRENT_NAMESPACE env var"
if err.Error() != expectedErr {
t.Fatalf("expected error to be '%s' but got '%v'", expectedErr, err)
}
},
Args: []string{"keycloak", "fh-sync-server"},
Flags: []string{},
},
{
Name: "returns error when deployment cannot be found",
SvcCatalogClient: func() (versioned.Interface, *watch.FakeWatcher, []runtime.Object) {
fake := &scFake.Clientset{}
fakeWatch := watch.NewFake()
fake.AddWatchReactor("servicebindings", ktesting.DefaultWatchReactor(fakeWatch, nil))
fake.AddReactor("get", "serviceinstances", func(action ktesting.Action) (handled bool, ret runtime.Object, err error) {
return true, &v1beta1.ServiceInstance{
ObjectMeta: metav1.ObjectMeta{
Name: "keycloak",
Labels: map[string]string{
"serviceName": "keycloak",
},
},
}, nil
})
fake.AddReactor("get", "serviceinstances", func(action ktesting.Action) (handled bool, ret runtime.Object, err error) {
return true, &v1beta1.ServiceInstance{
ObjectMeta: metav1.ObjectMeta{
Name: "fh-sync-server",
Labels: map[string]string{
"serviceName": "fh-sync-server",
},
},
}, nil
})
fake.AddReactor("delete", "podpreset", func(action ktesting.Action) (handled bool, ret runtime.Object, err error) {
return true, nil, nil
})
fake.AddReactor("delete", "serviceinstances", func(action ktesting.Action) (handled bool, ret runtime.Object, err error) {
return true, nil, nil
})
return fake, fakeWatch, []runtime.Object{
defaultServiceBinding,
}
},
K8Client: func() kubernetes.Interface {
fake := &kFake.Clientset{}
fake.AddReactor("get", "deployments", func(action ktesting.Action) (handled bool, ret runtime.Object, err error) {
return true, nil, errors.New("failed to get deployment")
})
return fake
},
ExpectError: true,
ValidateErr: func(t *testing.T, err error) {
expectedErr := "service keycloak: failed to get deployment"
if err.Error() != expectedErr {
t.Fatalf("expected error to be '%s' but got '%v'", expectedErr, err)
}
},
Args: []string{"keycloak", "fh-sync-server"},
Flags: []string{"--namespace=test", "--auto-redeploy=true", "--no-wait=true"},
},
{
Name: "returns error when deployment cannot be updated",
SvcCatalogClient: func() (versioned.Interface, *watch.FakeWatcher, []runtime.Object) {
fakeWatch := watch.NewFake()
fake := &scFake.Clientset{}
fake.AddWatchReactor("servicebindings", ktesting.DefaultWatchReactor(fakeWatch, nil))

fake.AddReactor("get", "serviceinstances", func(action ktesting.Action) (handled bool, ret runtime.Object, err error) {
return true, &v1beta1.ServiceInstance{
ObjectMeta: metav1.ObjectMeta{
Name: "keycloak",
},
}, nil
})
return fake, fakeWatch, []runtime.Object{
defaultServiceBinding,
}
},
K8Client: func() kubernetes.Interface {
fake := &kFake.Clientset{}
fake.AddReactor("get", "deployments", func(action ktesting.Action) (handled bool, ret runtime.Object, err error) {
return true, &kbeta.Deployment{
Spec: kbeta.DeploymentSpec{
Template: corev1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{},
},
},
},
}, nil
})
fake.AddReactor("update", "deployments", func(action ktesting.Action) (handled bool, ret runtime.Object, err error) {
return true, nil, errors.New("failed to update deployment")
})
return fake
},
ExpectError: true,
ValidateErr: func(t *testing.T, err error) {
expectedErr := "failed to update deployment for service keycloak: failed to update deployment"
if err.Error() != expectedErr {
t.Fatalf("expected error to be '%s' but got '%v'", expectedErr, err)
}
},
Args: []string{"keycloak", "fh-sync-server"},
Flags: []string{"--namespace=test", "--auto-redeploy=true", "--no-wait=true"},
},
{
Name: "should pass when serviceinstances exist and auto-redeploy is set",
SvcCatalogClient: func() (versioned.Interface, *watch.FakeWatcher, []runtime.Object) {
fake := &scFake.Clientset{}
fakeWatch := watch.NewFake()
fake.AddWatchReactor("servicebindings", ktesting.DefaultWatchReactor(fakeWatch, nil))
fake.AddReactor("get", "serviceinstances", func(action ktesting.Action) (handled bool, ret runtime.Object, err error) {
return true, &v1beta1.ServiceInstance{
ObjectMeta: metav1.ObjectMeta{
Name: "keycloak",
},
}, nil
})
fake.AddReactor("get", "serviceinstances", func(action ktesting.Action) (handled bool, ret runtime.Object, err error) {
return true, &v1beta1.ServiceInstance{
ObjectMeta: metav1.ObjectMeta{
Name: "fh-sync-server",
},
}, nil
})
return fake, fakeWatch, []runtime.Object{
defaultServiceBinding,
}
},
K8Client: func() kubernetes.Interface {
fake := &kFake.Clientset{}
fake.AddReactor("get", "deployments", func(action ktesting.Action) (handled bool, ret runtime.Object, err error) {
return true, &kbeta.Deployment{
Spec: kbeta.DeploymentSpec{
Template: corev1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{},
},
},
},
}, nil
})
return fake
},
Args: []string{"keycloak", "fh-sync-server"},
Flags: []string{"--namespace=test", "--auto-redeploy=true", "--no-wait=true"},
},
}

for _, tc := range cases {
t.Run(tc.Name, func(t *testing.T) {
root := cmd.NewRootCmd()
var out bytes.Buffer
scClient, fakeWatch, updates := tc.SvcCatalogClient()
if fakeWatch != nil {
go func() {
for _, u := range updates {
fakeWatch.Modify(u)
}
}()
}
integrationCmd := cmd.NewIntegrationCmd(scClient, tc.K8Client(), &out)
deleteCmd := integrationCmd.DeleteIntegrationCmd()
deleteCmd.SetOutput(&out)
root.AddCommand(deleteCmd)
if err := deleteCmd.ParseFlags(tc.Flags); err != nil {
t.Fatal("failed to parse command flags", err)
}
err := deleteCmd.RunE(deleteCmd, tc.Args)

if err != nil && !tc.ExpectError {
t.Fatal("did not expect an error but gone one:", err)
}
if err == nil && tc.ExpectError {
t.Fatal("expected an error but got none")
}
if tc.ExpectUsage && out.String() != deleteCmd.UsageString() {
t.Fatalf("Expected error to be '%s' but got '%v'", deleteCmd.UsageString(), err)
}
if tc.ValidateErr != nil {
tc.ValidateErr(t, err)
}
})
}
}
Loading

0 comments on commit 1dc0545

Please sign in to comment.