diff --git a/pkg/api/v1/openapi_generated.go b/pkg/api/v1/openapi_generated.go index eecbe0626ed6..c8dafa5fd0e4 100644 --- a/pkg/api/v1/openapi_generated.go +++ b/pkg/api/v1/openapi_generated.go @@ -1677,6 +1677,20 @@ func schema_kubevirt_pkg_api_v1_KubeVirtSpec(ref common.ReferenceCallback) commo Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ Properties: map[string]spec.Schema{ + "imageTag": { + SchemaProps: spec.SchemaProps{ + Description: "The image tag to use for the continer images installed. Defaults to the same tag as the operator's container image.", + Type: []string{"string"}, + Format: "", + }, + }, + "imageRegistry": { + SchemaProps: spec.SchemaProps{ + Description: "The image registry to pull the container images from Defaults to the same registry the operator's container image is pulled from.", + Type: []string{"string"}, + Format: "", + }, + }, "imagePullPolicy": { SchemaProps: spec.SchemaProps{ Description: "The ImagePullPolicy to use.", diff --git a/pkg/api/v1/types.go b/pkg/api/v1/types.go index 928ef11419f9..6bd0d95addd9 100644 --- a/pkg/api/v1/types.go +++ b/pkg/api/v1/types.go @@ -1050,6 +1050,13 @@ func (kl *KubeVirtList) GetListMeta() meta.List { // --- // +k8s:openapi-gen=true type KubeVirtSpec struct { + // The image tag to use for the continer images installed. + // Defaults to the same tag as the operator's container image. + ImageTag string `json:"imageTag,omitempty"` + // The image registry to pull the container images from + // Defaults to the same registry the operator's container image is pulled from. + ImageRegistry string `json:"imageRegistry,omitempty"` + // The ImagePullPolicy to use. ImagePullPolicy k8sv1.PullPolicy `json:"imagePullPolicy,omitempty" valid:"required"` } diff --git a/pkg/api/v1/types_swagger_generated.go b/pkg/api/v1/types_swagger_generated.go index 108ba7d585c6..07ee1051de90 100644 --- a/pkg/api/v1/types_swagger_generated.go +++ b/pkg/api/v1/types_swagger_generated.go @@ -248,6 +248,8 @@ func (KubeVirtList) SwaggerDoc() map[string]string { func (KubeVirtSpec) SwaggerDoc() map[string]string { return map[string]string{ + "imageTag": "The image tag to use for the continer images installed.\nDefaults to the same tag as the operator's container image.", + "imageRegistry": "The image registry to pull the container images from\nDefaults to the same registry the operator's container image is pulled from.", "imagePullPolicy": "The ImagePullPolicy to use.", } } diff --git a/pkg/virt-operator/kubevirt.go b/pkg/virt-operator/kubevirt.go index 10a89cd7df86..5256ba0ce3db 100644 --- a/pkg/virt-operator/kubevirt.go +++ b/pkg/virt-operator/kubevirt.go @@ -454,7 +454,10 @@ func (c *KubeVirtController) execute(key string) error { return syncError } -func (c *KubeVirtController) generateInstallStrategyJob(kv *v1.KubeVirt, imageTag string, imageRegistry string) *batchv1.Job { +func (c *KubeVirtController) generateInstallStrategyJob(kv *v1.KubeVirt) *batchv1.Job { + + imageTag := c.getImageTag(kv) + imageRegistry := c.getImageRegistry(kv) pullPolicy := k8sv1.PullIfNotPresent if string(kv.Spec.ImagePullPolicy) != "" { @@ -559,6 +562,20 @@ func (c *KubeVirtController) deleteAllInstallStrategy() error { return nil } +func (c *KubeVirtController) getImageTag(kv *v1.KubeVirt) string { + if kv.Spec.ImageTag == "" { + return c.config.ImageTag + } + return kv.Spec.ImageTag +} + +func (c *KubeVirtController) getImageRegistry(kv *v1.KubeVirt) string { + if kv.Spec.ImageRegistry == "" { + return c.config.ImageRegistry + } + return kv.Spec.ImageRegistry +} + // Loads install strategies into memory, and generates jobs to // create install strategies that don't exist yet. func (c *KubeVirtController) loadInstallStrategy(kv *v1.KubeVirt) (*installstrategy.InstallStrategy, bool, error) { @@ -569,19 +586,19 @@ func (c *KubeVirtController) loadInstallStrategy(kv *v1.KubeVirt) (*installstrat } // 1. see if we already loaded the install strategy - strategy, ok := c.getInstallStrategyFromMap(c.config.ImageTag) + strategy, ok := c.getInstallStrategyFromMap(c.getImageTag(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.config.ImageTag) + strategy, err = installstrategy.LoadInstallStrategyFromCache(c.stores, kv.Namespace, c.getImageTag(kv)) if err == nil { c.installStrategyMutex.Lock() defer c.installStrategyMutex.Unlock() - c.installStrategyMap[c.config.ImageTag] = strategy - log.Log.Infof("Loaded install strategy for kubevirt version %s into cache", c.config.ImageTag) + c.installStrategyMap[c.getImageTag(kv)] = strategy + log.Log.Infof("Loaded install strategy for kubevirt version %s into cache", c.getImageTag(kv)) return strategy, false, nil } @@ -589,7 +606,7 @@ func (c *KubeVirtController) loadInstallStrategy(kv *v1.KubeVirt) (*installstrat // 3. See if we have a pending job in flight for this install strategy. batch := c.clientset.BatchV1() - job := c.generateInstallStrategyJob(kv, c.config.ImageTag, c.config.ImageRegistry) + job := c.generateInstallStrategyJob(kv) obj, exists, _ := c.stores.InstallStrategyJobCache.Get(job) if exists { @@ -599,7 +616,7 @@ func (c *KubeVirtController) loadInstallStrategy(kv *v1.KubeVirt) (*installstrat // job completed but we don't have a install strategy still // delete the job and we'll re-execute it once it is removed. - log.Log.Object(cachedJob).Errorf("Job failed to create install strategy for version %s", c.config.ImageTag) + log.Log.Object(cachedJob).Errorf("Job failed to create install strategy for version %s", c.getImageTag(kv)) if cachedJob.DeletionTimestamp == nil { // Just in case there's an issue causing the job to fail @@ -634,7 +651,7 @@ func (c *KubeVirtController) loadInstallStrategy(kv *v1.KubeVirt) (*installstrat return nil, true, err } - log.Log.Object(cachedJob).Errorf("Deleting job for install strategy version %s because configmap was not generated", c.config.ImageTag) + log.Log.Object(cachedJob).Errorf("Deleting job for install strategy version %s because configmap was not generated", c.getImageTag(kv)) } // waiting on deleted job to disappear before re-creating it. return nil, true, err @@ -652,7 +669,7 @@ func (c *KubeVirtController) loadInstallStrategy(kv *v1.KubeVirt) (*installstrat c.kubeVirtExpectations.InstallStrategyJob.LowerExpectations(kvkey, 1, 0) return nil, true, err } - log.Log.Infof("Created job to generate install strategy configmap for version %s", c.config.ImageTag) + log.Log.Infof("Created job to generate install strategy configmap for version %s", c.getImageTag(kv)) // pending is true here because we're waiting on the job // to generate the install strategy @@ -664,9 +681,9 @@ func (c *KubeVirtController) syncDeployment(kv *v1.KubeVirt) error { logger.Infof("Handling deployment") // Set versions... - if kv.Status.OperatorVersion == "" { - util.SetVersions(kv, c.config) - } + util.SetOperatorVersion(kv) + // record the version we're targetting to install + kv.Status.TargetKubeVirtVersion = c.getImageTag(kv) // Set phase to deploying kv.Status.Phase = v1.KubeVirtPhaseDeploying @@ -718,6 +735,9 @@ func (c *KubeVirtController) syncDeployment(kv *v1.KubeVirt) error { if objectsAdded == 0 { + // record the version just installed + kv.Status.ObservedKubeVirtVersion = c.getImageTag(kv) + // add Created condition util.UpdateCondition(kv, v1.KubeVirtConditionCreated, k8sv1.ConditionTrue, ConditionReasonDeploymentCreated, "All resources were created.") logger.Info("All KubeVirt resources created") diff --git a/pkg/virt-operator/kubevirt_test.go b/pkg/virt-operator/kubevirt_test.go index 4a464d15608f..b37520f46d77 100644 --- a/pkg/virt-operator/kubevirt_test.go +++ b/pkg/virt-operator/kubevirt_test.go @@ -51,6 +51,7 @@ import ( "kubevirt.io/kubevirt/pkg/kubecli" "kubevirt.io/kubevirt/pkg/log" "kubevirt.io/kubevirt/pkg/testutils" + "kubevirt.io/kubevirt/pkg/version" "kubevirt.io/kubevirt/pkg/virt-operator/creation/components" "kubevirt.io/kubevirt/pkg/virt-operator/creation/rbac" "kubevirt.io/kubevirt/pkg/virt-operator/install-strategy" @@ -725,7 +726,9 @@ var _ = Describe("KubeVirt Operator", func() { Message: "All components are ready.", }, }, - OperatorVersion: "v9.9.9", + OperatorVersion: version.Get().String(), + TargetKubeVirtVersion: "v9.9.9", + ObservedKubeVirtVersion: "v9.9.9", }, } @@ -819,7 +822,7 @@ var _ = Describe("KubeVirt Operator", func() { Status: v1.KubeVirtStatus{}, } - job := controller.generateInstallStrategyJob(kv, "v9.9.9", "somerepository") + job := controller.generateInstallStrategyJob(kv) // will only create a new job after 10 seconds has passed. // this is just a simple mechanism to prevent spin loops @@ -850,7 +853,7 @@ var _ = Describe("KubeVirt Operator", func() { Status: v1.KubeVirtStatus{}, } - job := controller.generateInstallStrategyJob(kv, "v9.9.9", "somerepository") + job := controller.generateInstallStrategyJob(kv) job.Status.CompletionTime = now() @@ -876,7 +879,7 @@ var _ = Describe("KubeVirt Operator", func() { addKubeVirt(kv) addInstallStrategy() - job := controller.generateInstallStrategyJob(kv, "v9.9.9", "somerepository") + job := controller.generateInstallStrategyJob(kv) job.Status.CompletionTime = now() addInstallStrategyJob(job) diff --git a/pkg/virt-operator/util/client.go b/pkg/virt-operator/util/client.go index 0364a8215c25..1e88d92f4cc7 100644 --- a/pkg/virt-operator/util/client.go +++ b/pkg/virt-operator/util/client.go @@ -20,14 +20,12 @@ package util import ( - "fmt" "time" secv1 "github.com/openshift/api/security/v1" k8sv1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/tools/cache" virtv1 "kubevirt.io/kubevirt/pkg/api/v1" "kubevirt.io/kubevirt/pkg/kubecli" @@ -116,83 +114,8 @@ func hasFinalizer(kv *virtv1.KubeVirt) bool { return false } -func SetVersions(kv *virtv1.KubeVirt, config KubeVirtDeploymentConfig) { - +func SetOperatorVersion(kv *virtv1.KubeVirt) { kv.Status.OperatorVersion = version.Get().String() - - // Note: for now we just set targetKubeVirtVersion and observedKubeVirtVersion to the tag of the operator image - // In future this needs some more work... - kv.Status.TargetKubeVirtVersion = config.ImageTag - kv.Status.ObservedKubeVirtVersion = config.ImageTag - -} - -func UpdateScc(clientset kubecli.KubevirtClient, sccStore cache.Store, kv *virtv1.KubeVirt, add bool) error { - - privSccObj, exists, err := sccStore.GetByKey("privileged") - if !exists { - return nil - } else if err != nil { - return err - } - - privScc, ok := privSccObj.(*secv1.SecurityContextConstraints) - if !ok { - return fmt.Errorf("couldn't cast object to SecurityContextConstraints: %+v", privSccObj) - } - privSccCopy := privScc.DeepCopy() - - var kubeVirtAccounts []string - prefix := "system:serviceaccount" - kubeVirtAccounts = append(kubeVirtAccounts, fmt.Sprintf("%s:%s:%s", prefix, kv.Namespace, "kubevirt-handler")) - kubeVirtAccounts = append(kubeVirtAccounts, fmt.Sprintf("%s:%s:%s", prefix, kv.Namespace, "kubevirt-apiserver")) - kubeVirtAccounts = append(kubeVirtAccounts, fmt.Sprintf("%s:%s:%s", prefix, kv.Namespace, "kubevirt-controller")) - - modified := false - users := privSccCopy.Users - for _, acc := range kubeVirtAccounts { - if add { - if !contains(users, acc) { - users = append(users, acc) - modified = true - } - } else { - removed := false - users, removed = remove(users, acc) - modified = modified || removed - } - } - if modified { - privSccCopy.Users = users - _, err = clientset.SecClient().SecurityContextConstraints().Update(privSccCopy) - if err != nil { - return fmt.Errorf("unable to update scc: %v", err) - } - } - - return nil -} - -func contains(users []string, user string) bool { - for _, u := range users { - if u == user { - return true - } - } - return false -} - -func remove(users []string, user string) ([]string, bool) { - var newUsers []string - modified := false - for _, u := range users { - if u != user { - newUsers = append(newUsers, u) - } else { - modified = true - } - } - return newUsers, modified } func IsOnOpenshift(clientset kubecli.KubevirtClient) (bool, error) {