diff --git a/pkg/api/v1/openapi_generated.go b/pkg/api/v1/openapi_generated.go index c8dafa5fd0e4..52b8f73e4ac7 100644 --- a/pkg/api/v1/openapi_generated.go +++ b/pkg/api/v1/openapi_generated.go @@ -1741,12 +1741,24 @@ func schema_kubevirt_pkg_api_v1_KubeVirtStatus(ref common.ReferenceCallback) com Format: "", }, }, + "targetKubeVirtRegistry": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, "observedKubeVirtVersion": { SchemaProps: spec.SchemaProps{ Type: []string{"string"}, Format: "", }, }, + "observedKubeVirtRegistry": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, }, }, }, diff --git a/pkg/api/v1/types.go b/pkg/api/v1/types.go index 6bd0d95addd9..925d5e6df30e 100644 --- a/pkg/api/v1/types.go +++ b/pkg/api/v1/types.go @@ -447,8 +447,13 @@ const ( // This label will be set on all resources created by the operator ManagedByLabel = "app.kubernetes.io/managed-by" ManagedByLabelOperatorValue = "kubevirt-operator" - // This label represents the kubevirt version for an install strategy configmap. - InstallStrategyVersionLabel = "kubevirt.io/install-strategy-version" + // This annotation represents the kubevirt version for an install strategy configmap. + InstallStrategyVersionAnnotation = "kubevirt.io/install-strategy-version" + // This annotation represents the kubevirt registry used for an install strategy configmap. + InstallStrategyRegistryAnnotation = "kubevirt.io/install-strategy-registry" + + // This label indicates the object is a part of the install strategy retrieval process. + InstallStrategyLabel = "kubevirt.io/install-strategy" VirtualMachineInstanceFinalizer string = "foregroundDeleteVirtualMachine" CPUManager string = "cpumanager" @@ -1065,11 +1070,13 @@ type KubeVirtSpec struct { // --- // +k8s:openapi-gen=true type KubeVirtStatus struct { - Phase KubeVirtPhase `json:"phase,omitempty"` - Conditions []KubeVirtCondition `json:"conditions,omitempty" optional:"true"` - OperatorVersion string `json:"operatorVersion,omitempty" optional:"true"` - TargetKubeVirtVersion string `json:"targetKubeVirtVersion,omitempty" optional:"true"` - ObservedKubeVirtVersion string `json:"observedKubeVirtVersion,omitempty" optional:"true"` + Phase KubeVirtPhase `json:"phase,omitempty"` + Conditions []KubeVirtCondition `json:"conditions,omitempty" optional:"true"` + OperatorVersion string `json:"operatorVersion,omitempty" optional:"true"` + TargetKubeVirtVersion string `json:"targetKubeVirtVersion,omitempty" optional:"true"` + TargetKubeVirtRegistry string `json:"targetKubeVirtRegistry,omitempty" optional:"true"` + ObservedKubeVirtVersion string `json:"observedKubeVirtVersion,omitempty" optional:"true"` + ObservedKubeVirtRegistry string `json:"observedKubeVirtRegistry,omitempty" optional:"true"` } // KubeVirtPhase is a label for the phase of a KubeVirt deployment at the current time. diff --git a/pkg/controller/virtinformers.go b/pkg/controller/virtinformers.go index 481918aabb3a..554182e47b46 100644 --- a/pkg/controller/virtinformers.go +++ b/pkg/controller/virtinformers.go @@ -429,7 +429,7 @@ func (f *kubeInformerFactory) DummyOperatorSCC() cache.SharedIndexInformer { func (f *kubeInformerFactory) OperatorInstallStrategyConfigMaps() cache.SharedIndexInformer { return f.getInformer("installStrategyConfigMapInformer", func() cache.SharedIndexInformer { - labelSelector, err := labels.Parse(kubev1.InstallStrategyVersionLabel) + labelSelector, err := labels.Parse(kubev1.InstallStrategyLabel) if err != nil { panic(err) } @@ -441,7 +441,7 @@ func (f *kubeInformerFactory) OperatorInstallStrategyConfigMaps() cache.SharedIn func (f *kubeInformerFactory) OperatorInstallStrategyJob() cache.SharedIndexInformer { return f.getInformer("installStrategyJobsInformer", func() cache.SharedIndexInformer { - labelSelector, err := labels.Parse(kubev1.InstallStrategyVersionLabel) + labelSelector, err := labels.Parse(kubev1.InstallStrategyLabel) if err != nil { panic(err) } diff --git a/pkg/virt-operator/install-strategy/strategy.go b/pkg/virt-operator/install-strategy/strategy.go index 000d835cd475..e66785ce13fb 100644 --- a/pkg/virt-operator/install-strategy/strategy.go +++ b/pkg/virt-operator/install-strategy/strategy.go @@ -79,10 +79,6 @@ type InstallStrategy struct { customSccPrivileges []*customSccPrivilegedAccounts } -func generateConfigMapName(imageTag string) string { - return fmt.Sprintf("kubevirt-install-strategy-%s", imageTag) -} - func NewInstallStrategyConfigMap(namespace string, imageTag string, imageRegistry string) (*corev1.ConfigMap, error) { strategy, err := GenerateCurrentInstallStrategy( @@ -97,11 +93,15 @@ func NewInstallStrategyConfigMap(namespace string, imageTag string, imageRegistr configMap := &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ - Name: generateConfigMapName(imageTag), - Namespace: namespace, + GenerateName: "kubevirt-install-strategy-", + Namespace: namespace, Labels: map[string]string{ - v1.ManagedByLabel: v1.ManagedByLabelOperatorValue, - v1.InstallStrategyVersionLabel: imageTag, + v1.ManagedByLabel: v1.ManagedByLabelOperatorValue, + v1.InstallStrategyLabel: "", + }, + Annotations: map[string]string{ + v1.InstallStrategyVersionAnnotation: imageTag, + v1.InstallStrategyRegistryAnnotation: imageRegistry, }, }, Data: map[string]string{ @@ -267,21 +267,42 @@ func GenerateCurrentInstallStrategy(namespace string, return strategy, nil } -func LoadInstallStrategyFromCache(stores util.Stores, namespace string, imageTag string) (*InstallStrategy, error) { +func LoadInstallStrategyFromCache(stores util.Stores, namespace string, imageTag string, imageRegistry string) (*InstallStrategy, error) { + var configMap *corev1.ConfigMap + var matchingConfigMaps []*corev1.ConfigMap - configMap := &corev1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Name: generateConfigMapName(imageTag), - Namespace: namespace, - }, + for _, obj := range stores.InstallStrategyConfigMapCache.List() { + config, ok := obj.(*corev1.ConfigMap) + if !ok { + continue + } + if config.ObjectMeta.Annotations == nil { + continue + } + + version, _ := config.ObjectMeta.Annotations[v1.InstallStrategyVersionAnnotation] + registry, _ := config.ObjectMeta.Annotations[v1.InstallStrategyRegistryAnnotation] + if version == imageTag && registry == imageRegistry { + matchingConfigMaps = append(matchingConfigMaps, config) + } + } + + if len(matchingConfigMaps) == 0 { + return nil, fmt.Errorf("no install strategy configmap found for version %s with registry %s", imageTag, imageRegistry) } - obj, exists, _ := stores.InstallStrategyConfigMapCache.Get(configMap) - if !exists { - return nil, fmt.Errorf("no install strategy configmap found for version %s", imageTag) + // choose the most recent configmap if multiple match. + mostRecentTime := metav1.Time{} + for _, config := range matchingConfigMaps { + if configMap == nil { + configMap = config + mostRecentTime = config.ObjectMeta.CreationTimestamp + } else if mostRecentTime.Before(&config.ObjectMeta.CreationTimestamp) { + configMap = config + mostRecentTime = config.ObjectMeta.CreationTimestamp + } } - configMap = obj.(*corev1.ConfigMap) data, ok := configMap.Data["manifests"] if !ok { return nil, fmt.Errorf("install strategy configmap %s does not contain 'manifests' key", configMap.Name) diff --git a/pkg/virt-operator/kubevirt.go b/pkg/virt-operator/kubevirt.go index 5256ba0ce3db..6b75b7600e92 100644 --- a/pkg/virt-operator/kubevirt.go +++ b/pkg/virt-operator/kubevirt.go @@ -473,9 +473,13 @@ func (c *KubeVirtController) generateInstallStrategyJob(kv *v1.KubeVirt) *batchv Namespace: kv.Namespace, Name: fmt.Sprintf("virt-install-strategy-job-%s", imageTag), Labels: map[string]string{ - v1.AppLabel: "", - v1.ManagedByLabel: v1.ManagedByLabelOperatorValue, - v1.InstallStrategyVersionLabel: imageTag, + v1.AppLabel: "", + v1.ManagedByLabel: v1.ManagedByLabelOperatorValue, + v1.InstallStrategyLabel: "", + }, + Annotations: map[string]string{ + v1.InstallStrategyVersionAnnotation: imageTag, + v1.InstallStrategyRegistryAnnotation: imageRegistry, }, }, Spec: batchv1.JobSpec{ @@ -535,14 +539,22 @@ func (c *KubeVirtController) garbageCollectInstallStrategyJobs() error { return nil } -func (c *KubeVirtController) getInstallStrategyFromMap(version string) (*installstrategy.InstallStrategy, bool) { +func (c *KubeVirtController) getInstallStrategyFromMap(version string, registry string) (*installstrategy.InstallStrategy, bool) { c.installStrategyMutex.Lock() defer c.installStrategyMutex.Unlock() - strategy, ok := c.installStrategyMap[version] + strategy, ok := c.installStrategyMap[fmt.Sprintf("%s/%s", registry, version)] return strategy, ok } +func (c *KubeVirtController) cacheInstallStrategyInMap(strategy *installstrategy.InstallStrategy, version string, registry string) { + + c.installStrategyMutex.Lock() + defer c.installStrategyMutex.Unlock() + c.installStrategyMap[fmt.Sprintf("%s/%s", registry, version)] = strategy + +} + func (c *KubeVirtController) deleteAllInstallStrategy() error { for _, obj := range c.stores.InstallStrategyConfigMapCache.List() { @@ -586,18 +598,16 @@ func (c *KubeVirtController) loadInstallStrategy(kv *v1.KubeVirt) (*installstrat } // 1. see if we already loaded the install strategy - strategy, ok := c.getInstallStrategyFromMap(c.getImageTag(kv)) + strategy, ok := c.getInstallStrategyFromMap(c.getImageTag(kv), c.getImageRegistry(kv)) if ok { // we already loaded this strategy into memory return strategy, false, nil } // 2. look for install strategy config map in cache. - strategy, err = installstrategy.LoadInstallStrategyFromCache(c.stores, kv.Namespace, c.getImageTag(kv)) + strategy, err = installstrategy.LoadInstallStrategyFromCache(c.stores, kv.Namespace, c.getImageTag(kv), c.getImageRegistry(kv)) if err == nil { - c.installStrategyMutex.Lock() - defer c.installStrategyMutex.Unlock() - c.installStrategyMap[c.getImageTag(kv)] = strategy + c.cacheInstallStrategyInMap(strategy, c.getImageTag(kv), c.getImageRegistry(kv)) log.Log.Infof("Loaded install strategy for kubevirt version %s into cache", c.getImageTag(kv)) return strategy, false, nil } @@ -684,6 +694,7 @@ func (c *KubeVirtController) syncDeployment(kv *v1.KubeVirt) error { util.SetOperatorVersion(kv) // record the version we're targetting to install kv.Status.TargetKubeVirtVersion = c.getImageTag(kv) + kv.Status.TargetKubeVirtRegistry = c.getImageRegistry(kv) // Set phase to deploying kv.Status.Phase = v1.KubeVirtPhaseDeploying @@ -737,6 +748,7 @@ func (c *KubeVirtController) syncDeployment(kv *v1.KubeVirt) error { // record the version just installed kv.Status.ObservedKubeVirtVersion = c.getImageTag(kv) + kv.Status.ObservedKubeVirtRegistry = c.getImageRegistry(kv) // add Created condition util.UpdateCondition(kv, v1.KubeVirtConditionCreated, k8sv1.ConditionTrue, ConditionReasonDeploymentCreated, "All resources were created.") diff --git a/pkg/virt-operator/kubevirt_test.go b/pkg/virt-operator/kubevirt_test.go index 133b367deb89..f04286447c27 100644 --- a/pkg/virt-operator/kubevirt_test.go +++ b/pkg/virt-operator/kubevirt_test.go @@ -37,10 +37,8 @@ import ( rbacv1 "k8s.io/api/rbac/v1" extv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" extclientfake "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/fake" - "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/client-go/kubernetes/fake" "k8s.io/client-go/testing" "k8s.io/client-go/tools/cache" @@ -353,8 +351,8 @@ var _ = Describe("KubeVirt Operator", func() { } addAll := func() { - repository := "somerepository" - version := "v9.9.9" + repository := defaultRegistry + version := defaultImageTag pullPolicy := "IfNotPresent" imagePullPolicy := k8sv1.PullPolicy(pullPolicy) verbosity := "2" @@ -779,9 +777,11 @@ var _ = Describe("KubeVirt Operator", func() { Message: "All components are ready.", }, }, - OperatorVersion: version.Get().String(), - TargetKubeVirtVersion: "v9.9.9", - ObservedKubeVirtVersion: "v9.9.9", + OperatorVersion: version.Get().String(), + TargetKubeVirtVersion: defaultImageTag, + TargetKubeVirtRegistry: defaultRegistry, + ObservedKubeVirtVersion: defaultImageTag, + ObservedKubeVirtRegistry: defaultRegistry, }, } @@ -1052,43 +1052,24 @@ var _ = Describe("KubeVirt Operator", func() { Expect(ok).To(BeTrue()) configMap := create.GetObject().(*k8sv1.ConfigMap) - Expect(configMap.Name).To(Equal("kubevirt-install-strategy-v9.9.9")) + Expect(configMap.GenerateName).To(Equal("kubevirt-install-strategy-")) - _, ok = configMap.Data["manifests"] + version, ok := configMap.ObjectMeta.Annotations[v1.InstallStrategyVersionAnnotation] Expect(ok).To(BeTrue()) - return true, create.GetObject(), nil - }) - - // This generates and posts the install strategy config map - installstrategy.DumpInstallStrategyToConfigMap(virtClient) - }, 15) - - It("should update an existing install strategy config map", func(done Done) { - defer close(done) - - kubeClient.Fake.PrependReactor("create", "configmaps", func(action testing.Action) (handled bool, obj runtime.Object, err error) { - create, ok := action.(testing.CreateAction) - Expect(ok).To(BeTrue()) + Expect(version).To(Equal(defaultImageTag)) - configMap := create.GetObject().(*k8sv1.ConfigMap) - Expect(configMap.Name).To(Equal("kubevirt-install-strategy-v9.9.9")) - return true, nil, errors.NewAlreadyExists(schema.GroupResource{}, configMap.Name) - }) - kubeClient.Fake.PrependReactor("update", "configmaps", func(action testing.Action) (handled bool, obj runtime.Object, err error) { - update, ok := action.(testing.UpdateAction) + registry, ok := configMap.ObjectMeta.Annotations[v1.InstallStrategyRegistryAnnotation] + Expect(registry).To(Equal(defaultRegistry)) Expect(ok).To(BeTrue()) - configMap := update.GetObject().(*k8sv1.ConfigMap) - Expect(configMap.Name).To(Equal("kubevirt-install-strategy-v9.9.9")) - _, ok = configMap.Data["manifests"] Expect(ok).To(BeTrue()) - return true, update.GetObject(), nil + return true, create.GetObject(), nil }) - // This should update an already existing install strategy + // This generates and posts the install strategy config map installstrategy.DumpInstallStrategyToConfigMap(virtClient) }, 15) })