diff --git a/api/v1beta1/hostedcluster_types.go b/api/v1beta1/hostedcluster_types.go index 474b09ac186..27b52b90d4e 100644 --- a/api/v1beta1/hostedcluster_types.go +++ b/api/v1beta1/hostedcluster_types.go @@ -133,6 +133,14 @@ const ( // SilenceClusterAlertsLabel is a label that can be used by consumers to indicate // alerts from a cluster can be silenced or ignored SilenceClusterAlertsLabel = "hypershift.openshift.io/silence-cluster-alerts" + + // InfraIDLabel is a label that indicates the hosted cluster's infra id + // that the resource is associated with. + InfraIDLabel = "hypershift.openshift.io/infra-id" + + // NodePoolNameLabel is a label that indicates the name of the node pool + // a resource is associated with + NodePoolNameLabel = "hypershift.openshift.io/nodepool-name" ) // HostedClusterSpec is the desired behavior of a HostedCluster. @@ -683,6 +691,14 @@ type KubevirtPlatformSpec struct { // +optional // +immutable BaseDomainPassthrough *bool `json:"baseDomainPassthrough,omitempty"` + + // GenerateID is used to uniquely apply a name postfix to resources associated with + // kubevirt infrastructure resources + // +kubebuilder:validation:Optional + // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="Value is immutable" + // +kubebuilder:validation:MaxLength=11 + // +optional + GenerateID string `json:"generateID,omitempty"` } // AgentPlatformSpec specifies configuration for agent-based installations. diff --git a/cmd/install/assets/hypershift-operator/hypershift.openshift.io_hostedclusters.yaml b/cmd/install/assets/hypershift-operator/hypershift.openshift.io_hostedclusters.yaml index f874b45d65e..f28fd5f2477 100644 --- a/cmd/install/assets/hypershift-operator/hypershift.openshift.io_hostedclusters.yaml +++ b/cmd/install/assets/hypershift-operator/hypershift.openshift.io_hostedclusters.yaml @@ -6260,6 +6260,14 @@ spec: Cluster: guest.apps.mgmt-cluster.example.com Apps: *.apps.guest.apps.mgmt-cluster.example.com \n This is possible using OCP wildcard routes" type: boolean + generateID: + description: GenerateID is used to uniquely apply a name postfix + to resources associated with kubevirt infrastructure resources + maxLength: 11 + type: string + x-kubernetes-validations: + - message: Value is immutable + rule: self == oldSelf type: object powervs: description: PowerVS specifies configuration for clusters running diff --git a/cmd/install/assets/hypershift-operator/hypershift.openshift.io_hostedcontrolplanes.yaml b/cmd/install/assets/hypershift-operator/hypershift.openshift.io_hostedcontrolplanes.yaml index 5daead1606b..478aa9458b8 100644 --- a/cmd/install/assets/hypershift-operator/hypershift.openshift.io_hostedcontrolplanes.yaml +++ b/cmd/install/assets/hypershift-operator/hypershift.openshift.io_hostedcontrolplanes.yaml @@ -6249,6 +6249,14 @@ spec: Cluster: guest.apps.mgmt-cluster.example.com Apps: *.apps.guest.apps.mgmt-cluster.example.com \n This is possible using OCP wildcard routes" type: boolean + generateID: + description: GenerateID is used to uniquely apply a name postfix + to resources associated with kubevirt infrastructure resources + maxLength: 11 + type: string + x-kubernetes-validations: + - message: Value is immutable + rule: self == oldSelf type: object powervs: description: PowerVS specifies configuration for clusters running diff --git a/control-plane-operator/controllers/hostedcontrolplane/cloud/kubevirt/providerconfig.go b/control-plane-operator/controllers/hostedcontrolplane/cloud/kubevirt/providerconfig.go index 8012b8a89a1..929cdd64ce7 100644 --- a/control-plane-operator/controllers/hostedcontrolplane/cloud/kubevirt/providerconfig.go +++ b/control-plane-operator/controllers/hostedcontrolplane/cloud/kubevirt/providerconfig.go @@ -1,6 +1,7 @@ package kubevirt import ( + hyperv1 "github.com/openshift/hypershift/api/v1beta1" "gopkg.in/yaml.v2" ) @@ -15,6 +16,7 @@ type CloudConfig struct { LoadBalancer LoadBalancerConfig `yaml:"loadBalancer"` InstancesV2 InstancesV2Config `yaml:"instancesV2"` Namespace string `yaml:"namespace"` + InfraLabels map[string]string `yaml:"infraLabels"` } type LoadBalancerConfig struct { @@ -39,7 +41,7 @@ func (c *CloudConfig) serialize() (string, error) { return string(out), nil } -func cloudConfig(namespace string) CloudConfig { +func cloudConfig(hcp *hyperv1.HostedControlPlane) CloudConfig { return CloudConfig{ LoadBalancer: LoadBalancerConfig{ Enabled: true, @@ -48,6 +50,9 @@ func cloudConfig(namespace string) CloudConfig { Enabled: true, ZoneAndRegionEnabled: false, }, - Namespace: namespace, + Namespace: hcp.Namespace, + InfraLabels: map[string]string{ + hyperv1.InfraIDLabel: hcp.Spec.InfraID, + }, } } diff --git a/control-plane-operator/controllers/hostedcontrolplane/cloud/kubevirt/reconcile.go b/control-plane-operator/controllers/hostedcontrolplane/cloud/kubevirt/reconcile.go index ec119151d2a..ba050da94ce 100644 --- a/control-plane-operator/controllers/hostedcontrolplane/cloud/kubevirt/reconcile.go +++ b/control-plane-operator/controllers/hostedcontrolplane/cloud/kubevirt/reconcile.go @@ -16,7 +16,7 @@ import ( ) func ReconcileCloudConfig(cm *corev1.ConfigMap, hcp *hyperv1.HostedControlPlane) error { - cfg := cloudConfig(hcp.Namespace) + cfg := cloudConfig(hcp) serializedCfg, err := cfg.serialize() if err != nil { return fmt.Errorf("failed to serialize cloudconfig: %w", err) diff --git a/control-plane-operator/controllers/hostedcontrolplane/csi/kubevirt/kubevirt.go b/control-plane-operator/controllers/hostedcontrolplane/csi/kubevirt/kubevirt.go index ca270a996c8..301b605f4fb 100644 --- a/control-plane-operator/controllers/hostedcontrolplane/csi/kubevirt/kubevirt.go +++ b/control-plane-operator/controllers/hostedcontrolplane/csi/kubevirt/kubevirt.go @@ -121,10 +121,10 @@ func getContentsOrDie(file string) []byte { return b } -func reconcileInfraConfigMap(cm *corev1.ConfigMap) error { +func reconcileInfraConfigMap(cm *corev1.ConfigMap, infraID string) error { cm.Data = map[string]string{ "infraClusterNamespace": cm.Namespace, - "infraClusterLabels": "", + "infraClusterLabels": fmt.Sprintf("%s=%s", hyperv1.InfraIDLabel, infraID), } return nil } @@ -420,7 +420,7 @@ func ReconcileInfra(client crclient.Client, hcp *hyperv1.HostedControlPlane, ctx infraConfigMap := manifests.KubevirtCSIDriverInfraConfigMap(infraNamespace) _, err = createOrUpdate(ctx, client, infraConfigMap, func() error { - return reconcileInfraConfigMap(infraConfigMap) + return reconcileInfraConfigMap(infraConfigMap, hcp.Spec.InfraID) }) if err != nil { return err diff --git a/control-plane-operator/hostedclusterconfigoperator/controllers/resources/ingress/reconcile.go b/control-plane-operator/hostedclusterconfigoperator/controllers/resources/ingress/reconcile.go index f9e642ff375..434f5470469 100644 --- a/control-plane-operator/hostedclusterconfigoperator/controllers/resources/ingress/reconcile.go +++ b/control-plane-operator/hostedclusterconfigoperator/controllers/resources/ingress/reconcile.go @@ -112,6 +112,9 @@ func ReconcileDefaultIngressPassthroughService(service *corev1.Service, defaultN return fmt.Errorf("unable to detect default ingress NodePort https port") } + if service.Labels == nil { + service.Labels = map[string]string{} + } service.Spec.Ports = []corev1.ServicePort{ { Name: "https-443", @@ -121,10 +124,13 @@ func ReconcileDefaultIngressPassthroughService(service *corev1.Service, defaultN }, } service.Spec.Selector = map[string]string{ - "kubevirt.io": "virt-launcher", + "kubevirt.io": "virt-launcher", + hyperv1.InfraIDLabel: hcp.Spec.InfraID, } service.Spec.Type = corev1.ServiceTypeClusterIP + service.Labels[hyperv1.InfraIDLabel] = hcp.Spec.InfraID + ownerRef.ApplyTo(service) return nil @@ -133,6 +139,9 @@ func ReconcileDefaultIngressPassthroughService(service *corev1.Service, defaultN func ReconcileDefaultIngressPassthroughRoute(route *routev1.Route, cpService *corev1.Service, hcp *hyperv1.HostedControlPlane) error { ownerRef := config.OwnerRefFrom(hcp) + if route.Labels == nil { + route.Labels = map[string]string{} + } route.Spec.WildcardPolicy = routev1.WildcardPolicySubdomain route.Spec.Host = fmt.Sprintf("https.apps.%s.%s", hcp.Name, hcp.Spec.DNS.BaseDomain) route.Spec.TLS = &routev1.TLSConfig{ @@ -142,6 +151,7 @@ func ReconcileDefaultIngressPassthroughRoute(route *routev1.Route, cpService *co Kind: "Service", Name: cpService.Name, } + route.Labels[hyperv1.InfraIDLabel] = hcp.Spec.InfraID ownerRef.ApplyTo(route) diff --git a/control-plane-operator/hostedclusterconfigoperator/controllers/resources/manifests/ingress.go b/control-plane-operator/hostedclusterconfigoperator/controllers/resources/manifests/ingress.go index 30a9490af37..13aec3a4ce7 100644 --- a/control-plane-operator/hostedclusterconfigoperator/controllers/resources/manifests/ingress.go +++ b/control-plane-operator/hostedclusterconfigoperator/controllers/resources/manifests/ingress.go @@ -35,19 +35,21 @@ func IngressDefaultIngressNodePortService() *corev1.Service { } } +const IngressDefaultIngressPassthroughServiceName = "default-ingress-passthrough-service" + func IngressDefaultIngressPassthroughService(namespace string) *corev1.Service { return &corev1.Service{ ObjectMeta: metav1.ObjectMeta{ - Name: "default-ingress-passthrough-service", Namespace: namespace, }, } } +const IngressDefaultIngressPassthroughRouteName = "default-ingress-passthrough-route" + func IngressDefaultIngressPassthroughRoute(namespace string) *routev1.Route { return &routev1.Route{ ObjectMeta: metav1.ObjectMeta{ - Name: "default-ingress-passthrough-route", Namespace: namespace, }, } diff --git a/control-plane-operator/hostedclusterconfigoperator/controllers/resources/resources.go b/control-plane-operator/hostedclusterconfigoperator/controllers/resources/resources.go index 67cda892bae..09cd685a5da 100644 --- a/control-plane-operator/hostedclusterconfigoperator/controllers/resources/resources.go +++ b/control-plane-operator/hostedclusterconfigoperator/controllers/resources/resources.go @@ -775,9 +775,17 @@ func (r *reconciler) reconcileIngressController(ctx context.Context, hcp *hyperv // Manifests for infra/mgmt cluster passthrough service cpService := manifests.IngressDefaultIngressPassthroughService(hcpNamespace) + cpService.Name = fmt.Sprintf("%s-%s", + manifests.IngressDefaultIngressPassthroughServiceName, + hcp.Spec.Platform.Kubevirt.GenerateID) + // Manifests for infra/mgmt cluster passthrough routes cpPassthroughRoute := manifests.IngressDefaultIngressPassthroughRoute(hcpNamespace) + cpPassthroughRoute.Name = fmt.Sprintf("%s-%s", + manifests.IngressDefaultIngressPassthroughRouteName, + hcp.Spec.Platform.Kubevirt.GenerateID) + if _, err := r.CreateOrUpdate(ctx, r.cpClient, cpService, func() error { return ingress.ReconcileDefaultIngressPassthroughService(cpService, defaultIngressNodePortService, hcp) }); err != nil { diff --git a/docs/content/reference/api.md b/docs/content/reference/api.md index 956c3fddc7b..aa4290d3f5c 100644 --- a/docs/content/reference/api.md +++ b/docs/content/reference/api.md @@ -4944,6 +4944,19 @@ Apps: *.apps.guest.apps.mgmt-cluster.example.com

This is possible using OCP wildcard routes

+ + +generateID
+ +string + + + +(Optional) +

GenerateID is used to uniquely apply a name postfix to resources associated with +kubevirt infrastructure resources

+ + ###KubevirtRootVolume { #hypershift.openshift.io/v1beta1.KubevirtRootVolume } diff --git a/hypershift-operator/controllers/hostedcluster/hostedcluster_controller.go b/hypershift-operator/controllers/hostedcluster/hostedcluster_controller.go index 52c99fb8501..a67dd97a773 100644 --- a/hypershift-operator/controllers/hostedcluster/hostedcluster_controller.go +++ b/hypershift-operator/controllers/hostedcluster/hostedcluster_controller.go @@ -81,6 +81,7 @@ import ( "k8s.io/apimachinery/pkg/types" utilerrors "k8s.io/apimachinery/pkg/util/errors" "k8s.io/apimachinery/pkg/util/intstr" + utilrand "k8s.io/apimachinery/pkg/util/rand" "k8s.io/apimachinery/pkg/util/sets" "k8s.io/client-go/util/workqueue" "k8s.io/utils/clock" @@ -4331,7 +4332,11 @@ func (r *HostedClusterReconciler) serviceAccountSigningKeyBytes(ctx context.Cont func (r *HostedClusterReconciler) reconcileKubevirtPlatformDefaultSettings(ctx context.Context, hc *hyperv1.HostedCluster) error { if hc.Spec.Platform.Kubevirt == nil { - return nil + hc.Spec.Platform.Kubevirt = &hyperv1.KubevirtPlatformSpec{} + } + + if hc.Spec.Platform.Kubevirt.GenerateID == "" { + hc.Spec.Platform.Kubevirt.GenerateID = utilrand.String(10) } // auto generate the basedomain by retrieving the default ingress *.apps dns. diff --git a/hypershift-operator/controllers/nodepool/kubevirt/kubevirt.go b/hypershift-operator/controllers/nodepool/kubevirt/kubevirt.go index 7adba237c60..5596f9e9f4b 100644 --- a/hypershift-operator/controllers/nodepool/kubevirt/kubevirt.go +++ b/hypershift-operator/controllers/nodepool/kubevirt/kubevirt.go @@ -201,8 +201,9 @@ func virtualMachineTemplateBase(image string, kvPlatform *hyperv1.KubevirtNodePo return template } -func MachineTemplateSpec(image string, nodePool *hyperv1.NodePool) *capikubevirt.KubevirtMachineTemplateSpec { - nodePoolNameLabelKey := "hypershift.kubevirt.io/node-pool-name" +func MachineTemplateSpec(image string, nodePool *hyperv1.NodePool, hc *hyperv1.HostedCluster) *capikubevirt.KubevirtMachineTemplateSpec { + nodePoolNameLabelKey := hyperv1.NodePoolNameLabel + infraIDLabelKey := hyperv1.InfraIDLabel vmTemplate := virtualMachineTemplateBase(image, nodePool.Spec.Platform.Kubevirt) @@ -236,7 +237,10 @@ func MachineTemplateSpec(image string, nodePool *hyperv1.NodePool) *capikubevirt } vmTemplate.Spec.Template.ObjectMeta.Labels[nodePoolNameLabelKey] = nodePool.Name + vmTemplate.Spec.Template.ObjectMeta.Labels[infraIDLabelKey] = hc.Spec.InfraID + vmTemplate.ObjectMeta.Labels[nodePoolNameLabelKey] = nodePool.Name + vmTemplate.ObjectMeta.Labels[infraIDLabelKey] = hc.Spec.InfraID return &capikubevirt.KubevirtMachineTemplateSpec{ Template: capikubevirt.KubevirtMachineTemplateResource{ diff --git a/hypershift-operator/controllers/nodepool/kubevirt/kubevirt_test.go b/hypershift-operator/controllers/nodepool/kubevirt/kubevirt_test.go index 57f7bb31132..d0abd5fccc4 100644 --- a/hypershift-operator/controllers/nodepool/kubevirt/kubevirt_test.go +++ b/hypershift-operator/controllers/nodepool/kubevirt/kubevirt_test.go @@ -19,6 +19,7 @@ func TestKubevirtMachineTemplate(t *testing.T) { testCases := []struct { name string nodePool *hyperv1.NodePool + hc *hyperv1.HostedCluster expected *capikubevirt.KubevirtMachineTemplateSpec }{ { @@ -40,7 +41,11 @@ func TestKubevirtMachineTemplate(t *testing.T) { Release: hyperv1.Release{}, }, }, - + hc: &hyperv1.HostedCluster{ + Spec: hyperv1.HostedClusterSpec{ + InfraID: "1234", + }, + }, expected: &capikubevirt.KubevirtMachineTemplateSpec{ Template: capikubevirt.KubevirtMachineTemplateResource{ Spec: capikubevirt.KubevirtMachineSpec{ @@ -57,7 +62,7 @@ func TestKubevirtMachineTemplate(t *testing.T) { err := PlatformValidation(tc.nodePool) g.Expect(err).ToNot(HaveOccurred()) - result := MachineTemplateSpec("", tc.nodePool) + result := MachineTemplateSpec("", tc.nodePool, tc.hc) if !equality.Semantic.DeepEqual(tc.expected, result) { t.Errorf(cmp.Diff(tc.expected, result)) } @@ -95,13 +100,15 @@ func generateNodeTemplate(memory string, cpu uint32, image string, volumeSize st runAlways := kubevirtv1.RunStrategyAlways guestQuantity := apiresource.MustParse(memory) volumeSizeQuantity := apiresource.MustParse(volumeSize) - nodePoolNameLabelKey := "hypershift.kubevirt.io/node-pool-name" + nodePoolNameLabelKey := hyperv1.NodePoolNameLabel + infraIDLabelKey := hyperv1.InfraIDLabel pullMethod := v1beta1.RegistryPullNode return &capikubevirt.VirtualMachineTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ Labels: map[string]string{ nodePoolNameLabelKey: "my-pool", + infraIDLabelKey: "1234", }, }, Spec: kubevirtv1.VirtualMachineSpec{ @@ -133,6 +140,7 @@ func generateNodeTemplate(memory string, cpu uint32, image string, volumeSize st ObjectMeta: metav1.ObjectMeta{ Labels: map[string]string{ nodePoolNameLabelKey: "my-pool", + infraIDLabelKey: "1234", }, }, Spec: kubevirtv1.VirtualMachineInstanceSpec{ diff --git a/hypershift-operator/controllers/nodepool/nodepool_controller.go b/hypershift-operator/controllers/nodepool/nodepool_controller.go index d5628b07864..b3ca1cbabfc 100644 --- a/hypershift-operator/controllers/nodepool/nodepool_controller.go +++ b/hypershift-operator/controllers/nodepool/nodepool_controller.go @@ -2238,7 +2238,7 @@ func machineTemplateBuilders(hcluster *hyperv1.HostedCluster, nodePool *hyperv1. } case hyperv1.KubevirtPlatform: template = &capikubevirt.KubevirtMachineTemplate{} - machineTemplateSpec = kubevirt.MachineTemplateSpec(kubevirtBootImage, nodePool) + machineTemplateSpec = kubevirt.MachineTemplateSpec(kubevirtBootImage, nodePool, hcluster) mutateTemplate = func(object client.Object) error { o, _ := object.(*capikubevirt.KubevirtMachineTemplate) o.Spec = *machineTemplateSpec.(*capikubevirt.KubevirtMachineTemplateSpec)