elemental-operator/controllers/managedosimage_controller_t...

395 lines
12 KiB
Go

/*
Copyright © 2022 SUSE LLC
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package controllers
import (
"encoding/json"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
elementalv1 "github.com/rancher/elemental-operator/api/v1beta1"
"github.com/rancher/elemental-operator/pkg/test"
fleetv1 "github.com/rancher/fleet/pkg/apis/fleet.cattle.io/v1alpha1"
upgradev1 "github.com/rancher/system-upgrade-controller/pkg/apis/upgrade.cattle.io/v1"
"github.com/rancher/wrangler/pkg/genericcondition"
"github.com/rancher/wrangler/pkg/name"
"gopkg.in/yaml.v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/intstr"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
)
var _ = Describe("reconcile managed os image", func() {
var r *ManagedOSImageReconciler
var managedOSImage *elementalv1.ManagedOSImage
var bundle *fleetv1.Bundle
BeforeEach(func() {
r = &ManagedOSImageReconciler{
Client: cl,
Scheme: cl.Scheme(),
}
managedOSImage = &elementalv1.ManagedOSImage{
ObjectMeta: metav1.ObjectMeta{
Name: "test-name",
Namespace: "default",
},
}
bundle = &fleetv1.Bundle{
ObjectMeta: metav1.ObjectMeta{
Name: name.SafeConcatName("mos", managedOSImage.Name),
Namespace: managedOSImage.Namespace,
},
}
})
AfterEach(func() {
Expect(test.CleanupAndWait(ctx, cl, managedOSImage, bundle)).To(Succeed())
})
It("should reconcile managed os image object", func() {
Expect(cl.Create(ctx, managedOSImage)).To(Succeed())
_, err := r.Reconcile(ctx, reconcile.Request{
NamespacedName: types.NamespacedName{
Namespace: managedOSImage.Namespace,
Name: managedOSImage.Name,
},
})
Expect(err).ToNot(HaveOccurred())
Expect(cl.Get(ctx, client.ObjectKey{
Name: managedOSImage.Name,
Namespace: managedOSImage.Namespace,
}, managedOSImage)).To(Succeed())
bundle := &fleetv1.Bundle{}
Expect(cl.Get(ctx, client.ObjectKey{
Name: name.SafeConcatName("mos", managedOSImage.Name),
Namespace: managedOSImage.Namespace,
}, bundle)).To(Succeed())
})
})
var _ = Describe("newFleetBundleResources", func() {
var r *ManagedOSImageReconciler
var managedOSImage *elementalv1.ManagedOSImage
var managedOSVersion *elementalv1.ManagedOSVersion
BeforeEach(func() {
r = &ManagedOSImageReconciler{
Client: cl,
Scheme: cl.Scheme(),
}
managedOSImage = &elementalv1.ManagedOSImage{
ObjectMeta: metav1.ObjectMeta{
Name: "test-name",
Namespace: "default",
},
}
managedOSVersion = &elementalv1.ManagedOSVersion{
ObjectMeta: metav1.ObjectMeta{
Name: "test",
Namespace: "default",
},
Spec: elementalv1.ManagedOSVersionSpec{
UpgradeContainer: &upgradev1.ContainerSpec{
Image: "foo/bar:image",
},
},
}
})
AfterEach(func() {
Expect(test.CleanupAndWait(ctx, cl, managedOSImage, managedOSVersion)).To(Succeed())
})
It("should create fleet bundle resources", func() {
bundleResources, err := r.newFleetBundleResources(ctx, managedOSImage)
Expect(err).ToNot(HaveOccurred())
Expect(bundleResources).To(HaveLen(5))
Expect(bundleResources[0].Name).To(Equal("ClusterRole--os-upgrader-test-name-87f8d805a5c0.yaml"))
Expect(bundleResources[1].Name).To(Equal("ClusterRoleBinding--os-upgrader-test-name-cc7ce4275b54.yaml"))
Expect(bundleResources[2].Name).To(Equal("ServiceAccount-cattle-system-os-upgrader-test-name-08929531f5c0.yaml"))
Expect(bundleResources[3].Name).To(Equal("Secret-cattle-system-os-upgrader-test-name-52e9d8e041f4.yaml"))
Expect(bundleResources[4].Name).To(Equal("Plan-cattle-system-os-upgrader-test-name-24d63a562894.yaml"))
})
It("should create fleet bundle when managedOSVersion exists", func() {
Expect(r.Create(ctx, managedOSVersion)).To(Succeed())
managedOSImage.Spec.ManagedOSVersionName = "test"
bundleResources, err := r.newFleetBundleResources(ctx, managedOSImage)
Expect(err).ToNot(HaveOccurred())
Expect(bundleResources).To(HaveLen(5))
plan := &upgradev1.Plan{}
Expect(yaml.Unmarshal([]byte(bundleResources[4].Content), plan)).To(Succeed())
Expect(plan.Spec.Upgrade.Image).To(Equal("foo/bar:image"))
})
It("return error of managedOSVersion doesn't exist", func() {
managedOSImage.Spec.ManagedOSVersionName = "test"
bundleResources, err := r.newFleetBundleResources(ctx, managedOSImage)
Expect(err).To(HaveOccurred())
Expect(bundleResources).To(BeNil())
})
It("return early if fleet bundle already exists", func() {
meta.SetStatusCondition(&managedOSImage.Status.Conditions, metav1.Condition{
Type: elementalv1.FleetBundleCreation,
Reason: elementalv1.FleetBundleCreateSuccessReason,
Status: metav1.ConditionTrue,
})
bundleResources, err := r.newFleetBundleResources(ctx, managedOSImage)
Expect(err).ToNot(HaveOccurred())
Expect(bundleResources).To(BeNil())
})
})
var _ = Describe("createFleetBundle", func() {
var r *ManagedOSImageReconciler
var managedOSImage *elementalv1.ManagedOSImage
var bundle *fleetv1.Bundle
BeforeEach(func() {
r = &ManagedOSImageReconciler{
Client: cl,
Scheme: cl.Scheme(),
}
managedOSImage = &elementalv1.ManagedOSImage{
ObjectMeta: metav1.ObjectMeta{
Name: "test-name",
Namespace: "default",
UID: "test",
},
Spec: elementalv1.ManagedOSImageSpec{
ClusterRolloutStrategy: &fleetv1.RolloutStrategy{
MaxUnavailable: &intstr.IntOrString{
IntVal: 3,
},
},
Targets: []elementalv1.BundleTarget{
{
Name: "test",
},
},
},
}
bundle = &fleetv1.Bundle{
ObjectMeta: metav1.ObjectMeta{
Name: name.SafeConcatName("mos", managedOSImage.Name),
Namespace: managedOSImage.Namespace,
},
}
})
AfterEach(func() {
Expect(test.CleanupAndWait(ctx, cl, managedOSImage, bundle)).To(Succeed())
})
It("should create fleet bundle resources", func() {
err := r.createFleetBundle(ctx, managedOSImage, []fleetv1.BundleResource{
{
Name: "test",
},
})
Expect(err).ToNot(HaveOccurred())
Expect(cl.Get(ctx, client.ObjectKey{
Name: name.SafeConcatName("mos", managedOSImage.Name),
Namespace: managedOSImage.Namespace,
}, bundle)).To(Succeed())
Expect(bundle.Spec.Resources).To(HaveLen(1))
Expect(bundle.Spec.Resources[0].Name).To(Equal("test"))
Expect(bundle.Spec.RolloutStrategy).ToNot(BeNil())
Expect(bundle.Spec.RolloutStrategy.MaxUnavailable.IntVal).To(Equal(int32(3)))
Expect(bundle.Spec.Targets).To(HaveLen(1))
Expect(bundle.Spec.Targets[0].Name).To(Equal("test"))
})
It("should change target when namespace is fleet-local", func() {
managedOSImage.Namespace = "fleet-local"
err := r.createFleetBundle(ctx, managedOSImage, []fleetv1.BundleResource{
{
Name: "test",
},
})
Expect(err).ToNot(HaveOccurred())
Expect(cl.Get(ctx, client.ObjectKey{
Name: name.SafeConcatName("mos", managedOSImage.Name),
Namespace: managedOSImage.Namespace,
}, bundle)).To(Succeed())
Expect(bundle.Spec.Resources).To(HaveLen(1))
Expect(bundle.Spec.Resources[0].Name).To(Equal("test"))
Expect(bundle.Spec.RolloutStrategy).ToNot(BeNil())
Expect(bundle.Spec.RolloutStrategy.MaxUnavailable.IntVal).To(Equal(int32(3)))
Expect(bundle.Spec.Targets).To(HaveLen(1))
Expect(bundle.Spec.Targets[0].ClusterName).To(Equal("local"))
})
It("return early if fleet bundle already exists", func() {
meta.SetStatusCondition(&managedOSImage.Status.Conditions, metav1.Condition{
Type: elementalv1.FleetBundleCreation,
Reason: elementalv1.FleetBundleCreateSuccessReason,
Status: metav1.ConditionTrue,
})
err := r.createFleetBundle(ctx, managedOSImage, []fleetv1.BundleResource{})
Expect(err).ToNot(HaveOccurred())
})
})
var _ = Describe("updateManagedOSImageStatus", func() {
var r *ManagedOSImageReconciler
var managedOSImage *elementalv1.ManagedOSImage
var bundle *fleetv1.Bundle
BeforeEach(func() {
r = &ManagedOSImageReconciler{
Client: cl,
Scheme: cl.Scheme(),
}
managedOSImage = &elementalv1.ManagedOSImage{
ObjectMeta: metav1.ObjectMeta{
Name: "test-name",
Namespace: "default",
},
Spec: elementalv1.ManagedOSImageSpec{
ClusterRolloutStrategy: &fleetv1.RolloutStrategy{
MaxUnavailable: &intstr.IntOrString{
IntVal: 3,
},
},
Targets: []elementalv1.BundleTarget{
{
Name: "test",
},
},
},
}
bundle = &fleetv1.Bundle{
ObjectMeta: metav1.ObjectMeta{
Name: name.SafeConcatName("mos", managedOSImage.Name),
Namespace: managedOSImage.Namespace,
},
}
})
AfterEach(func() {
Expect(test.CleanupAndWait(ctx, cl, managedOSImage, bundle)).To(Succeed())
})
It("should update manage os image status", func() {
Expect(r.Create(ctx, bundle)).To(Succeed())
patchBase := client.MergeFrom(bundle.DeepCopy())
bundle.Status = fleetv1.BundleStatus{
Conditions: []genericcondition.GenericCondition{
{
Type: "test",
Status: corev1.ConditionTrue,
Reason: "test",
Message: "test",
},
},
}
Expect(r.Status().Patch(ctx, bundle, patchBase)).To(Succeed())
err := r.updateManagedOSImageStatus(ctx, managedOSImage)
Expect(err).ToNot(HaveOccurred())
Expect(managedOSImage.Status.Conditions).To(HaveLen(1))
Expect(managedOSImage.Status.Conditions[0].Type).To(Equal("test"))
Expect(managedOSImage.Status.Conditions[0].Status).To(Equal(metav1.ConditionTrue))
Expect(managedOSImage.Status.Conditions[0].Reason).To(Equal("test"))
})
It("should update manage os image status when condition is missing fields", func() {
Expect(r.Create(ctx, bundle)).To(Succeed())
patchBase := client.MergeFrom(bundle.DeepCopy())
bundle.Status = fleetv1.BundleStatus{
Conditions: []genericcondition.GenericCondition{
{
Message: "test",
},
},
}
Expect(r.Status().Patch(ctx, bundle, patchBase)).To(Succeed())
err := r.updateManagedOSImageStatus(ctx, managedOSImage)
Expect(err).ToNot(HaveOccurred())
Expect(managedOSImage.Status.Conditions).To(HaveLen(1))
Expect(managedOSImage.Status.Conditions[0].Type).To(Equal("UnknownType"))
Expect(managedOSImage.Status.Conditions[0].Status).To(Equal(metav1.ConditionUnknown))
Expect(managedOSImage.Status.Conditions[0].Reason).To(Equal("UnknownReason"))
})
})
var _ = Describe("metadataEnv", func() {
It("should container env variables when passed as strings", func() {
envMap := map[string]runtime.RawExtension{
"upgradeImage": {Raw: []byte(`"registry.com/repository/image:v1.0"`)},
"robin": {Raw: []byte(`"batman"`)},
}
env := metadataEnv(envMap)
Expect(env).To(HaveLen(2))
Expect(env[0].Name).To(Equal("METADATA_UPGRADEIMAGE"))
Expect(env[0].Value).To(Equal("registry.com/repository/image:v1.0"))
Expect(env[1].Name).To(Equal("METADATA_ROBIN"))
Expect(env[1].Value).To(Equal("batman"))
})
It("should container env variables when passed as jsondata", func() {
jsonStruct := struct{ Foo string }{Foo: "foostruct"}
jsonData, err := json.Marshal(jsonStruct)
Expect(err).ToNot(HaveOccurred())
envMap := map[string]runtime.RawExtension{
"jsondata": {Raw: jsonData},
}
env := metadataEnv(envMap)
Expect(env).To(HaveLen(1))
Expect(env[0].Name).To(Equal("METADATA_JSONDATA"))
Expect(env[0].Value).To(Equal("{\"Foo\":\"foostruct\"}"))
})
})