Skip to content

Commit

Permalink
fix: Keep API v1beta1 in scheme
Browse files Browse the repository at this point in the history
Keeping the v1beta1 version in scheme allows the operator
to decode old objects.

Changed webhook to accept both versions.

Signed-off-by: Andrej Krejcir <[email protected]>
  • Loading branch information
akrejcir authored and kubevirt-bot committed Jun 23, 2023
1 parent b75c5bf commit 44e5baf
Show file tree
Hide file tree
Showing 11 changed files with 727 additions and 84 deletions.
14 changes: 14 additions & 0 deletions PROJECT
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,20 @@ resources:
# defaulting: true
validation: true
webhookVersion: v1
- api:
crdVersion: v1
namespaced: true
controller: true
domain: kubevirt.io
group: ssp
kind: SSP
path: kubevirt.io/ssp-operator/api/v1beta1
version: v1beta1
webhooks:
# conversion: true
# defaulting: true
validation: true
webhookVersion: v1
version: "3"
plugins:
manifests.sdk.operatorframework.io/v2: {}
Expand Down
4 changes: 2 additions & 2 deletions automation/e2e-upgrade-functests/ssp-cr-template.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# This custom resource is used by upgrade tests

apiVersion: ssp.kubevirt.io/v1beta1
apiVersion: ssp.kubevirt.io/v1beta2
kind: SSP
metadata:
name: %%_SSP_NAME_%%
Expand All @@ -15,5 +15,5 @@ spec:
replicas: 2
nodeLabeller:
placement:
nodeSelector:
nodeSelector:
"test": "test"
6 changes: 4 additions & 2 deletions internal/common/scheme.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import (
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
instancetypev1alpha2 "kubevirt.io/api/instancetype/v1alpha2"
ssp "kubevirt.io/ssp-operator/api/v1beta2"
sspv1beta1 "kubevirt.io/ssp-operator/api/v1beta1"
sspv1beta2 "kubevirt.io/ssp-operator/api/v1beta2"
)

var (
Expand All @@ -23,7 +24,8 @@ func init() {
utilruntime.Must(clientgoscheme.AddToScheme(Scheme))
utilruntime.Must(extv1.AddToScheme(Scheme))
utilruntime.Must(internalmeta.AddToScheme(Scheme))
utilruntime.Must(ssp.AddToScheme(Scheme))
utilruntime.Must(sspv1beta1.AddToScheme(Scheme))
utilruntime.Must(sspv1beta2.AddToScheme(Scheme))
utilruntime.Must(osconfv1.Install(Scheme))
utilruntime.Must(instancetypev1alpha2.AddToScheme(Scheme))
}
Expand Down
52 changes: 27 additions & 25 deletions tests/tests_suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client/config"

pipeline "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1"
ssp "kubevirt.io/ssp-operator/api/v1beta2"
sspv1beta1 "kubevirt.io/ssp-operator/api/v1beta1"
sspv1beta2 "kubevirt.io/ssp-operator/api/v1beta2"
"kubevirt.io/ssp-operator/internal/common"
vm_console_proxy "kubevirt.io/ssp-operator/internal/operands/vm-console-proxy"
)
Expand Down Expand Up @@ -78,7 +79,7 @@ type TestSuiteStrategy interface {
}

type newSspStrategy struct {
ssp *ssp.SSP
ssp *sspv1beta2.SSP
}

var _ TestSuiteStrategy = &newSspStrategy{}
Expand All @@ -102,7 +103,7 @@ func (s *newSspStrategy) Init() {
return apiClient.Create(ctx, namespaceObj)
}, env.Timeout(), time.Second).ShouldNot(HaveOccurred())

newSsp := &ssp.SSP{
newSsp := &sspv1beta2.SSP{
ObjectMeta: metav1.ObjectMeta{
Name: s.GetName(),
Namespace: s.GetNamespace(),
Expand All @@ -118,20 +119,20 @@ func (s *newSspStrategy) Init() {
vm_console_proxy.VmConsoleProxyNamespaceAnnotation: s.GetVmConsoleProxyNamespace(),
},
},
Spec: ssp.SSPSpec{
TemplateValidator: &ssp.TemplateValidator{
Spec: sspv1beta2.SSPSpec{
TemplateValidator: &sspv1beta2.TemplateValidator{
Replicas: pointer.Int32(int32(s.GetValidatorReplicas())),
},
CommonTemplates: ssp.CommonTemplates{
CommonTemplates: sspv1beta2.CommonTemplates{
Namespace: s.GetTemplatesNamespace(),
},
TektonPipelines: &ssp.TektonPipelines{
TektonPipelines: &sspv1beta2.TektonPipelines{
Namespace: s.GetNamespace(),
},
TektonTasks: &ssp.TektonTasks{
TektonTasks: &sspv1beta2.TektonTasks{
Namespace: s.GetNamespace(),
},
FeatureGates: &ssp.FeatureGates{
FeatureGates: &sspv1beta2.FeatureGates{
DeployTektonTaskResources: false,
},
},
Expand All @@ -154,7 +155,7 @@ func (s *newSspStrategy) Cleanup() {
waitForDeletion(client.ObjectKey{
Name: s.GetName(),
Namespace: s.GetNamespace(),
}, &ssp.SSP{})
}, &sspv1beta2.SSP{})
}

err1 := apiClient.Delete(ctx, &v1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: s.GetNamespace()}})
Expand Down Expand Up @@ -245,13 +246,13 @@ type existingSspStrategy struct {
Name string
Namespace string

ssp *ssp.SSP
ssp *sspv1beta2.SSP
}

var _ TestSuiteStrategy = &existingSspStrategy{}

func (s *existingSspStrategy) Init() {
existingSsp := &ssp.SSP{}
existingSsp := &sspv1beta2.SSP{}
err := apiClient.Get(ctx, client.ObjectKey{Name: s.Name, Namespace: s.Namespace}, existingSsp)
Expect(err).ToNot(HaveOccurred())

Expand All @@ -273,11 +274,11 @@ func (s *existingSspStrategy) Init() {
if existingSsp.Spec.TemplateValidator != nil && existingSsp.Spec.TemplateValidator.Replicas != nil {
newReplicasCount = *existingSsp.Spec.TemplateValidator.Replicas + int32(1)
}
updateSsp(func(foundSsp *ssp.SSP) {
updateSsp(func(foundSsp *sspv1beta2.SSP) {
if existingSsp.Spec.TemplateValidator != nil {
foundSsp.Spec.TemplateValidator.Replicas = &newReplicasCount
} else {
foundSsp.Spec.TemplateValidator = &ssp.TemplateValidator{
foundSsp.Spec.TemplateValidator = &sspv1beta2.TemplateValidator{
Replicas: &newReplicasCount,
}
}
Expand Down Expand Up @@ -449,7 +450,8 @@ func expectSuccessOrNotFound(err error) {
}

func setupApiClient() {
Expect(ssp.AddToScheme(testScheme)).ToNot(HaveOccurred())
Expect(sspv1beta1.AddToScheme(testScheme)).ToNot(HaveOccurred())
Expect(sspv1beta2.AddToScheme(testScheme)).ToNot(HaveOccurred())
Expect(promv1.AddToScheme(testScheme)).ToNot(HaveOccurred())
Expect(templatev1.Install(testScheme)).ToNot(HaveOccurred())
Expect(secv1.Install(testScheme)).ToNot(HaveOccurred())
Expand All @@ -476,7 +478,7 @@ func setupApiClient() {
}

func createSspListerWatcher(cfg *rest.Config) cache.ListerWatcher {
sspGvk, err := apiutil.GVKForObject(&ssp.SSP{}, testScheme)
sspGvk, err := apiutil.GVKForObject(&sspv1beta2.SSP{}, testScheme)
Expect(err).ToNot(HaveOccurred())

restClient, err := apiutil.RESTClientForGVK(sspGvk, false, cfg, serializer.NewCodecFactory(testScheme))
Expand All @@ -485,9 +487,9 @@ func createSspListerWatcher(cfg *rest.Config) cache.ListerWatcher {
return cache.NewListWatchFromClient(restClient, "ssps", strategy.GetNamespace(), fields.Everything())
}

func getSsp() *ssp.SSP {
func getSsp() *sspv1beta2.SSP {
key := client.ObjectKey{Name: strategy.GetName(), Namespace: strategy.GetNamespace()}
foundSsp := &ssp.SSP{}
foundSsp := &sspv1beta2.SSP{}
Expect(apiClient.Get(ctx, key, foundSsp)).ToNot(HaveOccurred())
return foundSsp
}
Expand Down Expand Up @@ -522,10 +524,10 @@ func waitForDeletion(key client.ObjectKey, obj client.Object) {
}, env.Timeout(), time.Second).Should(BeTrue())
}

func waitForSspDeletionIfNeeded(sspObj *ssp.SSP) {
func waitForSspDeletionIfNeeded(sspObj *sspv1beta2.SSP) {
key := client.ObjectKey{Name: sspObj.Name, Namespace: sspObj.Namespace}
Eventually(func() error {
foundSsp := &ssp.SSP{}
foundSsp := &sspv1beta2.SSP{}
err := apiClient.Get(ctx, key, foundSsp)
if errors.IsNotFound(err) {
return nil
Expand All @@ -546,13 +548,13 @@ func validateDeploymentExists() {
env.SspDeploymentName(), env.SspDeploymentNamespace()))
}

func createOrUpdateSsp(sspObj *ssp.SSP) {
func createOrUpdateSsp(sspObj *sspv1beta2.SSP) {
key := client.ObjectKey{
Name: sspObj.Name,
Namespace: sspObj.Namespace,
}
Eventually(func() error {
foundSsp := &ssp.SSP{}
foundSsp := &sspv1beta2.SSP{}
err := apiClient.Get(ctx, key, foundSsp)
if err == nil {
isEqual := reflect.DeepEqual(foundSsp.Spec, sspObj.Spec) &&
Expand All @@ -567,7 +569,7 @@ func createOrUpdateSsp(sspObj *ssp.SSP) {
return apiClient.Update(ctx, foundSsp)
}
if errors.IsNotFound(err) {
newSsp := &ssp.SSP{
newSsp := &sspv1beta2.SSP{
ObjectMeta: metav1.ObjectMeta{
Name: sspObj.Name,
Namespace: sspObj.Namespace,
Expand All @@ -583,15 +585,15 @@ func createOrUpdateSsp(sspObj *ssp.SSP) {
}

func triggerReconciliation() {
updateSsp(func(foundSsp *ssp.SSP) {
updateSsp(func(foundSsp *sspv1beta2.SSP) {
if foundSsp.GetAnnotations() == nil {
foundSsp.SetAnnotations(map[string]string{})
}

foundSsp.GetAnnotations()["forceReconciliation"] = ""
})

updateSsp(func(foundSsp *ssp.SSP) {
updateSsp(func(foundSsp *sspv1beta2.SSP) {
delete(foundSsp.GetAnnotations(), "forceReconciliation")
})

Expand Down
37 changes: 27 additions & 10 deletions tests/webhook_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@ import (
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
sspv1beta1 "kubevirt.io/ssp-operator/api/v1beta1"
"sigs.k8s.io/controller-runtime/pkg/client"

"kubevirt.io/controller-lifecycle-operator-sdk/api"
ssp "kubevirt.io/ssp-operator/api/v1beta2"
sspv1beta2 "kubevirt.io/ssp-operator/api/v1beta2"
)

// Placement API tests variables
Expand Down Expand Up @@ -91,15 +92,15 @@ var _ = Describe("Validation webhook", func() {

Context("removed existing SSP CR", func() {
var (
newSsp *ssp.SSP
newSsp *sspv1beta2.SSP
)

BeforeEach(func() {
strategy.SkipSspUpdateTestsIfNeeded()

foundSsp := getSsp()
Expect(apiClient.Delete(ctx, foundSsp)).ToNot(HaveOccurred())
waitForDeletion(client.ObjectKey{Name: foundSsp.GetName(), Namespace: foundSsp.GetNamespace()}, &ssp.SSP{})
waitForDeletion(client.ObjectKey{Name: foundSsp.GetName(), Namespace: foundSsp.GetNamespace()}, &sspv1beta2.SSP{})

foundSsp.ObjectMeta = v1.ObjectMeta{
Name: foundSsp.GetName(),
Expand Down Expand Up @@ -129,7 +130,7 @@ var _ = Describe("Validation webhook", func() {

Context("Placement API validation", func() {
It("[test_id:5988]should succeed with valid template-validator placement fields", func() {
newSsp.Spec.TemplateValidator = &ssp.TemplateValidator{
newSsp.Spec.TemplateValidator = &sspv1beta2.TemplateValidator{
Placement: &placementAPIValidationValidPlacement,
}

Expand All @@ -138,7 +139,7 @@ var _ = Describe("Validation webhook", func() {
})

It("[test_id:5987]should fail with invalid template-validator placement fields", func() {
newSsp.Spec.TemplateValidator = &ssp.TemplateValidator{
newSsp.Spec.TemplateValidator = &sspv1beta2.TemplateValidator{
Placement: &placementAPIValidationInvalidPlacement,
}

Expand All @@ -148,18 +149,34 @@ var _ = Describe("Validation webhook", func() {
})

It("[test_id:TODO] should fail when DataImportCronTemplate does not have a name", func() {
newSsp.Spec.CommonTemplates.DataImportCronTemplates = []ssp.DataImportCronTemplate{{
newSsp.Spec.CommonTemplates.DataImportCronTemplates = []sspv1beta2.DataImportCronTemplate{{
ObjectMeta: metav1.ObjectMeta{Name: ""},
}}
err := apiClient.Create(ctx, newSsp, client.DryRunAll)
Expect(err).To(MatchError(ContainSubstring("missing name in DataImportCronTemplate")))
})

It("[test_id:TODO] should accept v1beta1 SSP object", func() {
ssp := &sspv1beta1.SSP{
ObjectMeta: metav1.ObjectMeta{
Name: newSsp.GetName(),
Namespace: newSsp.GetNamespace(),
},
Spec: sspv1beta1.SSPSpec{
CommonTemplates: sspv1beta1.CommonTemplates{
Namespace: newSsp.Spec.CommonTemplates.Namespace,
},
},
}

Expect(apiClient.Create(ctx, ssp, client.DryRunAll)).To(Succeed())
})
})
})

Context("update", func() {
var (
foundSsp *ssp.SSP
foundSsp *sspv1beta2.SSP
)

BeforeEach(func() {
Expand All @@ -174,7 +191,7 @@ var _ = Describe("Validation webhook", func() {
It("[test_id:5990]should succeed with valid template-validator placement fields", func() {
Eventually(func() error {
foundSsp = getSsp()
foundSsp.Spec.TemplateValidator = &ssp.TemplateValidator{
foundSsp.Spec.TemplateValidator = &sspv1beta2.TemplateValidator{
Placement: &placementAPIValidationValidPlacement,
}
return apiClient.Update(ctx, foundSsp, client.DryRunAll)
Expand All @@ -184,7 +201,7 @@ var _ = Describe("Validation webhook", func() {
It("[test_id:5989]should fail with invalid template-validator placement fields", func() {
Eventually(func() v1.StatusReason {
foundSsp = getSsp()
foundSsp.Spec.TemplateValidator = &ssp.TemplateValidator{
foundSsp.Spec.TemplateValidator = &sspv1beta2.TemplateValidator{
Placement: &placementAPIValidationInvalidPlacement,
}
err := apiClient.Update(ctx, foundSsp, client.DryRunAll)
Expand All @@ -196,7 +213,7 @@ var _ = Describe("Validation webhook", func() {
It("[test_id:TODO] should fail when DataImportCronTemplate does not have a name", func() {
Eventually(func() error {
foundSsp = getSsp()
foundSsp.Spec.CommonTemplates.DataImportCronTemplates = []ssp.DataImportCronTemplate{{
foundSsp.Spec.CommonTemplates.DataImportCronTemplates = []sspv1beta2.DataImportCronTemplate{{
ObjectMeta: metav1.ObjectMeta{Name: ""},
}}
return apiClient.Update(ctx, foundSsp, client.DryRunAll)
Expand Down
36 changes: 36 additions & 0 deletions vendor/kubevirt.io/ssp-operator/api/v1beta1/groupversion_info.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 44e5baf

Please sign in to comment.