diff --git a/docs/syntax.md b/docs/syntax.md index 32e888c6..9292d127 100644 --- a/docs/syntax.md +++ b/docs/syntax.md @@ -12,12 +12,12 @@ Below you will find the step syntax next to the name of the method it utilizes. - ` [I] store [the] current time as ` kdt.KubeClientSet.SetTimestamp ### Unstructured Resources -- ` [I] (create|submit|delete|update) [the] resource ` kdt.KubeClientSet.ResourceOperation -- ` [I] (create|submit|delete|update) [the] resource in [the] namespace` kdt.KubeClientSet.ResourceOperationInNamespace -- ` [I] (create|submit|delete|update) [the] resources in ` kdt.KubeClientSet.ResourcesOperation -- ` [I] (create|submit|delete|update) [the] resources in in [the] namespace` kdt.KubeClientSet.ResourcesOperationInNamespace -- ` [I] (create|submit|delete|update) [the] resource , the operation should (succeed|fail)` kdt.KubeClientSet.ResourceOperationWithResult -- ` [I] (create|submit|delete|update) [the] resource in [the] namespace, the operation should (succeed|fail)` kdt.KubeClientSet.ResourceOperationWithResultInNamespace +- ` [I] (create|submit|delete|update|upsert) [the] resource ` kdt.KubeClientSet.ResourceOperation +- ` [I] (create|submit|delete|update|upsert) [the] resource in [the] namespace` kdt.KubeClientSet.ResourceOperationInNamespace +- ` [I] (create|submit|delete|update|upsert) [the] resources in ` kdt.KubeClientSet.ResourcesOperation +- ` [I] (create|submit|delete|update|upsert) [the] resources in in [the] namespace` kdt.KubeClientSet.ResourcesOperationInNamespace +- ` [I] (create|submit|delete|update|upsert) [the] resource , the operation should (succeed|fail)` kdt.KubeClientSet.ResourceOperationWithResult +- ` [I] (create|submit|delete|update|upsert) [the] resource in [the] namespace, the operation should (succeed|fail)` kdt.KubeClientSet.ResourceOperationWithResultInNamespace - ` [the] resource should be (created|deleted)` kdt.KubeClientSet.ResourceShouldBe - ` [the] resource [should] converge to selector ` kdt.KubeClientSet.ResourceShouldConvergeToSelector - ` [the] resource condition should be ` kdt.KubeClientSet.ResourceConditionShouldBe diff --git a/kubedog.go b/kubedog.go index 5592c229..101c75e3 100644 --- a/kubedog.go +++ b/kubedog.go @@ -45,12 +45,12 @@ func (kdt *Test) SetScenario(scenario *godog.ScenarioContext) { kdt.scenario.Step(`^(?:the )?Kubernetes cluster should be (created|deleted|upgraded)$`, kdt.KubeClientSet.KubernetesClusterShouldBe) kdt.scenario.Step(`^(?:I )?store (?:the )?current time as ([^"]*)$`, kdt.KubeClientSet.SetTimestamp) //syntax-generation:title-1:Unstructured Resources - kdt.scenario.Step(`^(?:I )?(create|submit|delete|update) (?:the )?resource (\S+)$`, kdt.KubeClientSet.ResourceOperation) - kdt.scenario.Step(`^(?:I )?(create|submit|delete|update) (?:the )?resource (\S+) in (?:the )?([^"]*) namespace$`, kdt.KubeClientSet.ResourceOperationInNamespace) - kdt.scenario.Step(`^(?:I )?(create|submit|delete|update) (?:the )?resources in (\S+)$`, kdt.KubeClientSet.ResourcesOperation) - kdt.scenario.Step(`^(?:I )?(create|submit|delete|update) (?:the )?resources in (\S+) in (?:the )?([^"]*) namespace$`, kdt.KubeClientSet.ResourcesOperationInNamespace) - kdt.scenario.Step(`^(?:I )?(create|submit|delete|update) (?:the )?resource (\S+), the operation should (succeed|fail)$`, kdt.KubeClientSet.ResourceOperationWithResult) - kdt.scenario.Step(`^(?:I )?(create|submit|delete|update) (?:the )?resource (\S+) in (?:the )?([^"]*) namespace, the operation should (succeed|fail)$`, kdt.KubeClientSet.ResourceOperationWithResultInNamespace) + kdt.scenario.Step(`^(?:I )?(create|submit|delete|update|upsert) (?:the )?resource (\S+)$`, kdt.KubeClientSet.ResourceOperation) + kdt.scenario.Step(`^(?:I )?(create|submit|delete|update|upsert) (?:the )?resource (\S+) in (?:the )?([^"]*) namespace$`, kdt.KubeClientSet.ResourceOperationInNamespace) + kdt.scenario.Step(`^(?:I )?(create|submit|delete|update|upsert) (?:the )?resources in (\S+)$`, kdt.KubeClientSet.ResourcesOperation) + kdt.scenario.Step(`^(?:I )?(create|submit|delete|update|upsert) (?:the )?resources in (\S+) in (?:the )?([^"]*) namespace$`, kdt.KubeClientSet.ResourcesOperationInNamespace) + kdt.scenario.Step(`^(?:I )?(create|submit|delete|update|upsert) (?:the )?resource (\S+), the operation should (succeed|fail)$`, kdt.KubeClientSet.ResourceOperationWithResult) + kdt.scenario.Step(`^(?:I )?(create|submit|delete|update|upsert) (?:the )?resource (\S+) in (?:the )?([^"]*) namespace, the operation should (succeed|fail)$`, kdt.KubeClientSet.ResourceOperationWithResultInNamespace) kdt.scenario.Step(`^(?:the )?resource ([^"]*) should be (created|deleted)$`, kdt.KubeClientSet.ResourceShouldBe) kdt.scenario.Step(`^(?:the )?resource ([^"]*) (?:should )?converge to selector (\S+)$`, kdt.KubeClientSet.ResourceShouldConvergeToSelector) kdt.scenario.Step(`^(?:the )?resource ([^"]*) condition ([^"]*) should be ([^"]*)$`, kdt.KubeClientSet.ResourceConditionShouldBe) diff --git a/pkg/kube/common/common.go b/pkg/kube/common/common.go index 636762c9..d532941f 100644 --- a/pkg/kube/common/common.go +++ b/pkg/kube/common/common.go @@ -26,6 +26,7 @@ const ( OperationSubmit = "submit" OperationUpdate = "update" OperationDelete = "delete" + OperationUpsert = "upsert" StateCreated = "created" StateDeleted = "deleted" diff --git a/pkg/kube/unstructured/unstructured.go b/pkg/kube/unstructured/unstructured.go index 672cde0a..38250b80 100644 --- a/pkg/kube/unstructured/unstructured.go +++ b/pkg/kube/unstructured/unstructured.go @@ -91,6 +91,30 @@ func ResourceOperationInNamespace(dynamicClient dynamic.Interface, resource unst unstruct.SetResourceVersion(currentResourceVersion.DeepCopy().GetResourceVersion()) + _, err = dynamicClient.Resource(gvr.Resource).Namespace(namespace).Update(context.Background(), unstruct, metav1.UpdateOptions{}) + if err != nil { + return err + } + log.Infof("%s %s has been updated in namespace %s", unstruct.GetKind(), unstruct.GetName(), namespace) + case common.OperationUpsert: + currentResourceVersion, err := dynamicClient.Resource(gvr.Resource).Namespace(namespace).Get(context.Background(), unstruct.GetName(), metav1.GetOptions{}) + if err != nil { + if kerrors.IsNotFound(err) { + _, err = dynamicClient.Resource(gvr.Resource).Namespace(namespace).Create(context.Background(), unstruct, metav1.CreateOptions{}) + if err != nil { + return err + } + + log.Infof("%s %s has been created in namespace %s", unstruct.GetKind(), unstruct.GetName(), namespace) + + return nil + } else { + return err + } + } + + unstruct.SetResourceVersion(currentResourceVersion.DeepCopy().GetResourceVersion()) + _, err = dynamicClient.Resource(gvr.Resource).Namespace(namespace).Update(context.Background(), unstruct, metav1.UpdateOptions{}) if err != nil { return err diff --git a/pkg/kube/unstructured/unstructured_test.go b/pkg/kube/unstructured/unstructured_test.go index c50001a2..e58275fd 100644 --- a/pkg/kube/unstructured/unstructured_test.go +++ b/pkg/kube/unstructured/unstructured_test.go @@ -372,6 +372,39 @@ func TestResourceOperationInNamespace(t *testing.T) { } } +func TestResourceOperationInNamespaceUpsertOperation(t *testing.T) { + dynScheme := runtime.NewScheme() + fakeDynamicClient := fakeDynamic.NewSimpleDynamicClient(dynScheme) + + deploymentFromYaml, err := resourceFromYaml("../../test/templates/resource-without-namespace.yaml") + if err != nil { + t.Errorf(err.Error()) + } + + deploymentUnstructured := unstructuredResource{ + GVR: &meta.RESTMapping{}, + Resource: deploymentFromYaml, + } + + if err := ResourceOperationInNamespace(fakeDynamicClient, deploymentUnstructured, "upsert", "test-namespace"); err != nil { + t.Errorf("ResourceOperationInNamespace() error = %v", err) + } + + deploymentFromYamlUpdate, err := resourceFromYaml("../../test/templates/resource-without-namespace-update.yaml") + if err != nil { + t.Errorf(err.Error()) + } + + deploymentUnstructuredUpdate := unstructuredResource{ + GVR: &meta.RESTMapping{}, + Resource: deploymentFromYamlUpdate, + } + + if err := ResourceOperationInNamespace(fakeDynamicClient, deploymentUnstructuredUpdate, "upsert", "test-namespace"); err != nil { + t.Errorf("ResourceOperationInNamespace() error = %v", err) + } +} + func resourcePath(resourceFileName string) string { return filepath.Join("../../../test/templates", resourceFileName) }