Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Upgrade to adopt readonly support #126

Merged
merged 1 commit into from
Jun 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ require (
k8s.io/client-go v0.29.4
k8s.io/component-base v0.29.3
k8s.io/klog/v2 v2.120.1
open-cluster-management.io/api v0.13.1-0.20240521030453-9d94703b9eba
open-cluster-management.io/ocm v0.13.1-0.20240606073633-61a74bb348a4
open-cluster-management.io/api v0.13.1-0.20240605083248-f9e7f50520fc
open-cluster-management.io/ocm v0.13.1-0.20240612012446-8e792c14d8f4
open-cluster-management.io/sdk-go v0.13.1-0.20240606075054-7671bb086504
)

Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1175,8 +1175,12 @@ k8s.io/utils v0.0.0-20240310230437-4693a0247e57 h1:gbqbevonBh57eILzModw6mrkbwM0g
k8s.io/utils v0.0.0-20240310230437-4693a0247e57/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
open-cluster-management.io/api v0.13.1-0.20240521030453-9d94703b9eba h1:UsXnD4/N7pxYupPgoLvTq8wO73V72vD2D2ZkDd4iws0=
open-cluster-management.io/api v0.13.1-0.20240521030453-9d94703b9eba/go.mod h1:yrNuMMpciXjXPnj2yznb6LTyrGliiTrFZAJDp/Ck3c4=
open-cluster-management.io/api v0.13.1-0.20240605083248-f9e7f50520fc h1:tcfncubZRFphYtDXBE7ApBNlSnj1RNazhW+8F01XYYg=
open-cluster-management.io/api v0.13.1-0.20240605083248-f9e7f50520fc/go.mod h1:ltijKJhDifrPH0csvCUmFt5lzaERv+BBfh6X3l83rT0=
open-cluster-management.io/ocm v0.13.1-0.20240606073633-61a74bb348a4 h1:waZV6MKK01jRUmUyLyLRLZUjCdaCvs/AJwqQPNcEHoo=
open-cluster-management.io/ocm v0.13.1-0.20240606073633-61a74bb348a4/go.mod h1:em/6OHu/z7vkwoQanhC3i3lA8bQiKuBQlik6cyk3E5o=
open-cluster-management.io/ocm v0.13.1-0.20240612012446-8e792c14d8f4 h1:Z3gwbMUZmblGTHFx6iOO1zlCJc2FJcAJsa2RASTKDqA=
open-cluster-management.io/ocm v0.13.1-0.20240612012446-8e792c14d8f4/go.mod h1:RuYCuKuVJzNxRBkSoQnxyJxyUqOyCH388DlR/QDr7rE=
open-cluster-management.io/sdk-go v0.13.1-0.20240606075054-7671bb086504 h1:65KSUUpUapTbaeMx+MuxCqXRyrR3KGtOSFuoJwoXmMA=
open-cluster-management.io/sdk-go v0.13.1-0.20240606075054-7671bb086504/go.mod h1:muWzHWsgK8IsopltwTnsBjf4DN9IcC9rF0G2uEq/Pjw=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
Expand Down
109 changes: 80 additions & 29 deletions test/e2e/pkg/resources_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,19 @@ package e2e_test

import (
"context"
"encoding/json"
"fmt"
"net/http"
"time"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/openshift-online/maestro/pkg/api/openapi"
appsv1 "k8s.io/api/apps/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

"github.com/openshift-online/maestro/pkg/api"
"github.com/openshift-online/maestro/pkg/api/openapi"
)

var _ = Describe("Resources", Ordered, Label("e2e-tests-resources"), func() {
Expand Down Expand Up @@ -82,6 +86,7 @@ var _ = Describe("Resources", Ordered, Label("e2e-tests-resources"), func() {
It("post the nginx resource to the maestro api", func() {
var resp *http.Response
var err error
res.DeleteOption = map[string]interface{}{"propagationPolicy": "Orphan"}
resource, resp, err = apiClient.DefaultApi.ApiMaestroV1ResourcesPost(context.Background()).Resource(res).Execute()
Expect(err).ShouldNot(HaveOccurred())
Expect(resp.StatusCode).To(Equal(http.StatusCreated))
Expand All @@ -96,44 +101,22 @@ var _ = Describe("Resources", Ordered, Label("e2e-tests-resources"), func() {
}, 1*time.Minute, 1*time.Second).ShouldNot(HaveOccurred())
})

It("patch the nginx resource with orphan delete option", func() {

patchedResource, resp, err := apiClient.DefaultApi.ApiMaestroV1ResourcesIdPatch(context.Background(), *resource.Id).
ResourcePatchRequest(openapi.ResourcePatchRequest{Version: resource.Version, Manifest: res.Manifest, DeleteOption: map[string]interface{}{"propagationPolicy": "Orphan"}}).Execute()
Expect(err).ShouldNot(HaveOccurred())
Expect(resp.StatusCode).To(Equal(http.StatusOK))
Expect(*patchedResource.Version).To(Equal(*resource.Version + 1))

})

It("delete the nginx resource from the maestro api", func() {

resp, err := apiClient.DefaultApi.ApiMaestroV1ResourcesIdDelete(context.Background(), *resource.Id).Execute()
Expect(err).ShouldNot(HaveOccurred())
Expect(resp.StatusCode).To(Equal(http.StatusNoContent))

retry := 0
Eventually(func() error {
Consistently(func() error {
// Attempt to retrieve the "nginx" deployment in the "default" namespace
_, err := kubeClient.AppsV1().Deployments("default").Get(context.Background(), "nginx", metav1.GetOptions{})

// If an error occurs
if err != nil {
// Return any other errors directly
return err
}

// Increment the retry counter
retry++

// If the retry count reaches 30, consider it successful
if retry == 30 {
return nil
}

// Otherwise, indicate that another retry is needed
return fmt.Errorf("need to retry again")
}, 1*time.Minute, 1*time.Second).ShouldNot(HaveOccurred())
return nil
}, 30*time.Second, 1*time.Second).ShouldNot(HaveOccurred())
})

It("delete the nginx deployment", func() {
Expand All @@ -154,7 +137,7 @@ var _ = Describe("Resources", Ordered, Label("e2e-tests-resources"), func() {

})

Context("Resource Update Strategy Tests", func() {
Context("Resource CreateOnly UpdateStrategy Tests", func() {

It("post the nginx resource to the maestro api with createOnly updateStrategy", func() {
res := helper.NewAPIResource(consumer_name, 1)
Expand Down Expand Up @@ -184,7 +167,7 @@ var _ = Describe("Resources", Ordered, Label("e2e-tests-resources"), func() {
Expect(resp.StatusCode).To(Equal(http.StatusOK))
Expect(*patchedResource.Version).To(Equal(*resource.Version + 1))

Eventually(func() error {
Consistently(func() error {
deploy, err := kubeClient.AppsV1().Deployments("default").Get(context.Background(), "nginx", metav1.GetOptions{})
if err != nil {
return err
Expand All @@ -193,7 +176,7 @@ var _ = Describe("Resources", Ordered, Label("e2e-tests-resources"), func() {
return nil
}
return fmt.Errorf("replicas is not 1")
}, 1*time.Minute, 1*time.Second).ShouldNot(HaveOccurred())
}, 30*time.Second, 1*time.Second).ShouldNot(HaveOccurred())
})

It("delete the nginx resource", func() {
Expand All @@ -215,4 +198,72 @@ var _ = Describe("Resources", Ordered, Label("e2e-tests-resources"), func() {
})
})

Context("Resource ReadOnly UpdateStrategy Tests", func() {

It("create a sample deployment in the target cluster", func() {
nginxDeploy := &appsv1.Deployment{}
json.Unmarshal(helper.GetTestNginxJSON(1), nginxDeploy)
_, err := kubeClient.AppsV1().Deployments("default").Create(context.Background(), nginxDeploy, metav1.CreateOptions{})
Expect(err).ShouldNot(HaveOccurred())
})

It("post the resource to the maestro api with readonly updateStrategy", func() {
res := helper.NewReadOnlyAPIResource(consumer_name)
var resp *http.Response
var err error
resource, resp, err = apiClient.DefaultApi.ApiMaestroV1ResourcesPost(context.Background()).Resource(res).Execute()
Expect(err).ShouldNot(HaveOccurred())
Expect(resp.StatusCode).To(Equal(http.StatusCreated))
Expect(*resource.Id).ShouldNot(BeEmpty())
})

It("get the resource status back", func() {
Eventually(func() error {
res, _, err := apiClient.DefaultApi.ApiMaestroV1ResourcesIdGet(context.Background(), *resource.Id).Execute()
if err != nil {
return err
}

statusJSON, err := json.Marshal(res.Status)
if err != nil {
return err
}

resourceStatus := &api.ResourceStatus{}
err = json.Unmarshal(statusJSON, resourceStatus)
if err != nil {
return err
}

if resourceStatus.ContentStatus != nil {
conditions := resourceStatus.ContentStatus["conditions"].([]interface{})
if len(conditions) > 0 {
return nil
}
}
return fmt.Errorf("contentStatus should be empty")
}, 1*time.Minute, 1*time.Second).ShouldNot(HaveOccurred())
})

It("delete the readonly resource", func() {
resp, err := apiClient.DefaultApi.ApiMaestroV1ResourcesIdDelete(context.Background(), *resource.Id).Execute()
Expect(err).ShouldNot(HaveOccurred())
Expect(resp.StatusCode).To(Equal(http.StatusNoContent))

err = kubeClient.AppsV1().Deployments("default").Delete(context.Background(), "nginx", metav1.DeleteOptions{})
Expect(err).ShouldNot(HaveOccurred())

Eventually(func() error {
_, err := kubeClient.AppsV1().Deployments("default").Get(context.Background(), "nginx", metav1.GetOptions{})
if err != nil {
if errors.IsNotFound(err) {
return nil
}
return err
}
return fmt.Errorf("nginx deployment still exists")
}, 1*time.Minute, 1*time.Second).ShouldNot(HaveOccurred())
})
})

})
30 changes: 30 additions & 0 deletions test/factories.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,20 @@ var testManifestJSON = `
}
`

var testReadOnlyManifestJSON = `
{
"apiVersion": "apps/v1",
"kind": "Deployment",
"metadata": {
"name": "nginx",
"namespace": "default"
},
"update_strategy": {
"type": "ReadOnly"
}
}
`

func (helper *Helper) NewAPIResource(consumerName string, replicas int) openapi.Resource {
testManifest := map[string]interface{}{}
if err := json.Unmarshal([]byte(fmt.Sprintf(testManifestJSON, replicas)), &testManifest); err != nil {
Expand All @@ -64,6 +78,22 @@ func (helper *Helper) NewAPIResource(consumerName string, replicas int) openapi.
}
}

func (helper *Helper) GetTestNginxJSON(replicas int) []byte {
return []byte(fmt.Sprintf(testManifestJSON, replicas))
}

func (helper *Helper) NewReadOnlyAPIResource(consumerName string) openapi.Resource {
testManifest := map[string]interface{}{}
if err := json.Unmarshal([]byte(fmt.Sprint(testReadOnlyManifestJSON)), &testManifest); err != nil {
helper.T.Errorf("error unmarshalling test manifest: %q", err)
}

return openapi.Resource{
Manifest: testManifest,
ConsumerName: &consumerName,
}
}

func (helper *Helper) NewResource(consumerName string, replicas int) *api.Resource {
testResource := helper.NewAPIResource(consumerName, replicas)
testPayload, err := api.EncodeManifest(testResource.Manifest, testResource.DeleteOption, testResource.UpdateStrategy)
Expand Down
Loading