diff --git a/bundle/manifests/oadp-operator.clusterserviceversion.yaml b/bundle/manifests/oadp-operator.clusterserviceversion.yaml index 425d288a07..bf9648cfc7 100644 --- a/bundle/manifests/oadp-operator.clusterserviceversion.yaml +++ b/bundle/manifests/oadp-operator.clusterserviceversion.yaml @@ -648,6 +648,8 @@ spec: - infrastructures verbs: - get + - list + - watch - apiGroups: - cloudcredential.openshift.io resources: @@ -904,6 +906,9 @@ spec: valueFrom: fieldRef: fieldPath: metadata.annotations['olm.targetNamespaces'] + - name: RESTIC_PV_HOSTPATH + - name: FS_PV_HOSTPATH + - name: PLUGINS_HOSTPATH - name: RELATED_IMAGE_VELERO value: quay.io/konveyor/velero:latest - name: RELATED_IMAGE_VELERO_RESTORE_HELPER diff --git a/config/manager/manager.yaml b/config/manager/manager.yaml index c223590438..071d498d29 100644 --- a/config/manager/manager.yaml +++ b/config/manager/manager.yaml @@ -36,6 +36,12 @@ spec: valueFrom: fieldRef: fieldPath: metadata.namespace + - name: RESTIC_PV_HOSTPATH + value: "" + - name: FS_PV_HOSTPATH + value: "" + - name: PLUGINS_HOSTPATH + value: "" - name: RELATED_IMAGE_VELERO value: quay.io/konveyor/velero:latest - name: RELATED_IMAGE_VELERO_RESTORE_HELPER diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index cc301fd669..660e9f64c1 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -12,6 +12,8 @@ rules: - infrastructures verbs: - get + - list + - watch - apiGroups: - cloudcredential.openshift.io resources: diff --git a/controllers/bsl_test.go b/controllers/bsl_test.go index b5aac22e33..e46a5b2fb6 100644 --- a/controllers/bsl_test.go +++ b/controllers/bsl_test.go @@ -8,6 +8,7 @@ import ( "github.com/go-logr/logr" "github.com/google/go-cmp/cmp" + configv1 "github.com/openshift/api/config/v1" velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -36,6 +37,11 @@ func getSchemeForFakeClient() (*runtime.Scheme, error) { return nil, err } + err = configv1.AddToScheme((scheme.Scheme)) + if err != nil { + return nil, err + } + return scheme.Scheme, nil } diff --git a/controllers/nodeagent.go b/controllers/nodeagent.go index 9622192c6e..7ae172e575 100644 --- a/controllers/nodeagent.go +++ b/controllers/nodeagent.go @@ -7,6 +7,7 @@ import ( "reflect" "github.com/go-logr/logr" + configv1 "github.com/openshift/api/config/v1" "github.com/operator-framework/operator-lib/proxy" "github.com/vmware-tanzu/velero/pkg/install" appsv1 "k8s.io/api/apps/v1" @@ -25,14 +26,24 @@ import ( ) const ( - ResticRestoreHelperCM = "restic-restore-action-config" - FsRestoreHelperCM = "fs-restore-action-config" - HostPods = "host-pods" - HostPlugins = "host-plugins" + ResticRestoreHelperCM = "restic-restore-action-config" + FsRestoreHelperCM = "fs-restore-action-config" + HostPods = "host-pods" + HostPlugins = "host-plugins" + Cluster = "cluster" + IBMCloudPlatform = "IBMCloud" + GenericPVHostPath = "/var/lib/kubelet/pods" + IBMCloudPVHostPath = "/var/data/kubelet/pods" + GenericPluginsHostPath = "/var/lib/kubelet/plugins" + IBMCloudPluginsHostPath = "/var/data/kubelet/plugins" + ResticPVHostPathEnvVar = "RESTIC_PV_HOSTPATH" + FSPVHostPathEnvVar = "FS_PV_HOSTPATH" + PluginsHostPathEnvVar = "PLUGINS_HOSTPATH" ) var ( - fsPvHostPath string = getFsPvHostPath() + fsPvHostPath = getFsPvHostPath("") + pluginsHostPath = getPluginsHostPath("") // v1.MountPropagationHostToContainer is a const. Const cannot be pointed to. // we need to declare mountPropagationToHostContainer so that we have an address to point to @@ -47,19 +58,40 @@ var ( } ) -func getFsPvHostPath() string { - env := os.Getenv("RESTIC_PV_HOSTPATH") - envFs := os.Getenv("FS_PV_HOSTPATH") +// getFsPvHostPath returns the host path for persistent volumes based on the platform type. +func getFsPvHostPath(platformType string) string { + // Check if environment variables are set for host paths + if envFs := os.Getenv(FSPVHostPathEnvVar); envFs != "" { + return envFs + } - if env != "" { + if env := os.Getenv(ResticPVHostPathEnvVar); env != "" { return env } - if envFs != "" { - return envFs + // Return platform-specific host paths + switch platformType { + case IBMCloudPlatform: + return IBMCloudPVHostPath + default: + return GenericPVHostPath + } +} + +// getPluginsHostPath returns the host path for persistent volumes based on the platform type. +func getPluginsHostPath(platformType string) string { + // Check if environment var is set for host plugins + if env := os.Getenv(PluginsHostPathEnvVar); env != "" { + return env } - return "/var/lib/kubelet/pods" + // Return platform-specific host paths + switch platformType { + case IBMCloudPlatform: + return IBMCloudPluginsHostPath + default: + return GenericPluginsHostPath + } } func getNodeAgentObjectMeta(r *DPAReconciler) metav1.ObjectMeta { @@ -281,10 +313,22 @@ func (r *DPAReconciler) customizeNodeAgentDaemonset(dpa *oadpv1alpha1.DataProtec }, }) + // check platform type + platformType, err := r.getPlatformType() + if err != nil { + return nil, fmt.Errorf("error checking platform type: %s", err) + } // update nodeAgent host PV path for i, vol := range ds.Spec.Template.Spec.Volumes { if vol.Name == HostPods { - ds.Spec.Template.Spec.Volumes[i].HostPath.Path = getFsPvHostPath() + ds.Spec.Template.Spec.Volumes[i].HostPath.Path = getFsPvHostPath(platformType) + } + } + + // update nodeAgent plugins host path + for i, vol := range ds.Spec.Template.Spec.Volumes { + if vol.Name == HostPlugins { + ds.Spec.Template.Spec.Volumes[i].HostPath.Path = getPluginsHostPath(platformType) } } @@ -310,6 +354,13 @@ func (r *DPAReconciler) customizeNodeAgentDaemonset(dpa *oadpv1alpha1.DataProtec Name: "certs", MountPath: "/etc/ssl/certs", }) + + // update nodeAgent plugins volume mount host path + for v, volumeMount := range nodeAgentContainer.VolumeMounts { + if volumeMount.Name == HostPlugins { + nodeAgentContainer.VolumeMounts[v].MountPath = getPluginsHostPath(platformType) + } + } // append PodConfig envs to nodeAgent container if useResticConf { if dpa.Spec.Configuration.Restic.PodConfig != nil && dpa.Spec.Configuration.Restic.PodConfig.Env != nil { @@ -452,3 +503,19 @@ func (r *DPAReconciler) updateFsRestoreHelperCM(fsRestoreHelperCM *corev1.Config return nil } + +// getPlatformType fetches the cluster infrastructure object and returns the platform type. +func (r *DPAReconciler) getPlatformType() (string, error) { + infra := &configv1.Infrastructure{} + key := types.NamespacedName{Name: Cluster} + if err := r.Get(r.Context, key, infra); err != nil { + return "", err + } + + if platformStatus := infra.Status.PlatformStatus; platformStatus != nil { + if platformType := platformStatus.Type; platformType != "" { + return string(platformType), nil + } + } + return "", nil +} diff --git a/controllers/nodeagent_test.go b/controllers/nodeagent_test.go index d7584430a4..c0747e1eb3 100644 --- a/controllers/nodeagent_test.go +++ b/controllers/nodeagent_test.go @@ -12,6 +12,7 @@ import ( "github.com/google/go-cmp/cmp" "github.com/onsi/ginkgo/v2" "github.com/onsi/gomega" + configv1 "github.com/openshift/api/config/v1" "github.com/operator-framework/operator-lib/proxy" velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" appsv1 "k8s.io/api/apps/v1" @@ -43,6 +44,21 @@ var _ = ginkgo.Describe("Test ReconcileNodeAgentDaemonSet function", func() { } ) + ginkgo.BeforeEach(func() { + clusterInfraObject := &configv1.Infrastructure{ + ObjectMeta: metav1.ObjectMeta{ + Name: "cluster", + }, + Spec: configv1.InfrastructureSpec{ + PlatformSpec: configv1.PlatformSpec{ + Type: IBMCloudPlatform, + }, + }, + } + + gomega.Expect(k8sClient.Create(ctx, clusterInfraObject)).To(gomega.Succeed()) + }) + ginkgo.AfterEach(func() { os.Unsetenv(currentTestScenario.envVar.Name) @@ -72,6 +88,16 @@ var _ = ginkgo.Describe("Test ReconcileNodeAgentDaemonSet function", func() { }, } gomega.Expect(k8sClient.Delete(ctx, namespace)).To(gomega.Succeed()) + + clusterInfraObject := &configv1.Infrastructure{ + ObjectMeta: metav1.ObjectMeta{ + Name: "cluster", + }, + } + + if k8sClient.Get(ctx, client.ObjectKeyFromObject(clusterInfraObject), clusterInfraObject) == nil { + gomega.Expect(k8sClient.Delete(ctx, clusterInfraObject)).To(gomega.Succeed()) + } }) ginkgo.DescribeTable("Check if Subscription Config environment variables are passed to NodeAgent Containers", @@ -249,6 +275,13 @@ func TestDPAReconciler_buildNodeAgentDaemonset(t *testing.T) { }, }, wantErr: false, + clientObjects: []client.Object{ + &configv1.Infrastructure{ + ObjectMeta: metav1.ObjectMeta{ + Name: "cluster", + }, + }, + }, want: &appsv1.DaemonSet{ ObjectMeta: getNodeAgentObjectMeta(r), TypeMeta: metav1.TypeMeta{ @@ -288,7 +321,7 @@ func TestDPAReconciler_buildNodeAgentDaemonset(t *testing.T) { Name: HostPlugins, VolumeSource: corev1.VolumeSource{ HostPath: &corev1.HostPathVolumeSource{ - Path: "/var/lib/kubelet/plugins", + Path: pluginsHostPath, }, }, }, @@ -336,7 +369,7 @@ func TestDPAReconciler_buildNodeAgentDaemonset(t *testing.T) { }, { Name: HostPlugins, - MountPath: "/var/lib/kubelet/plugins", + MountPath: pluginsHostPath, MountPropagation: &mountPropagationToHostContainer, }, { @@ -412,6 +445,13 @@ func TestDPAReconciler_buildNodeAgentDaemonset(t *testing.T) { }, }, wantErr: false, + clientObjects: []client.Object{ + &configv1.Infrastructure{ + ObjectMeta: metav1.ObjectMeta{ + Name: "cluster", + }, + }, + }, want: &appsv1.DaemonSet{ ObjectMeta: getNodeAgentObjectMeta(r), TypeMeta: metav1.TypeMeta{ @@ -451,7 +491,7 @@ func TestDPAReconciler_buildNodeAgentDaemonset(t *testing.T) { Name: HostPlugins, VolumeSource: corev1.VolumeSource{ HostPath: &corev1.HostPathVolumeSource{ - Path: "/var/lib/kubelet/plugins", + Path: pluginsHostPath, }, }, }, @@ -499,7 +539,7 @@ func TestDPAReconciler_buildNodeAgentDaemonset(t *testing.T) { }, { Name: HostPlugins, - MountPath: "/var/lib/kubelet/plugins", + MountPath: pluginsHostPath, MountPropagation: &mountPropagationToHostContainer, }, { @@ -580,6 +620,13 @@ func TestDPAReconciler_buildNodeAgentDaemonset(t *testing.T) { }, }, wantErr: false, + clientObjects: []client.Object{ + &configv1.Infrastructure{ + ObjectMeta: metav1.ObjectMeta{ + Name: "cluster", + }, + }, + }, want: &appsv1.DaemonSet{ ObjectMeta: getNodeAgentObjectMeta(r), TypeMeta: metav1.TypeMeta{ @@ -620,7 +667,7 @@ func TestDPAReconciler_buildNodeAgentDaemonset(t *testing.T) { Name: HostPlugins, VolumeSource: corev1.VolumeSource{ HostPath: &corev1.HostPathVolumeSource{ - Path: "/var/lib/kubelet/plugins", + Path: pluginsHostPath, }, }, }, @@ -668,7 +715,7 @@ func TestDPAReconciler_buildNodeAgentDaemonset(t *testing.T) { }, { Name: HostPlugins, - MountPath: "/var/lib/kubelet/plugins", + MountPath: pluginsHostPath, MountPropagation: &mountPropagationToHostContainer, }, { @@ -745,7 +792,14 @@ func TestDPAReconciler_buildNodeAgentDaemonset(t *testing.T) { }, }, wantErr: true, - want: nil, + clientObjects: []client.Object{ + &configv1.Infrastructure{ + ObjectMeta: metav1.ObjectMeta{ + Name: "cluster", + }, + }, + }, + want: nil, }, { name: "test NodeAgent nodeselector customization via dpa", @@ -776,6 +830,13 @@ func TestDPAReconciler_buildNodeAgentDaemonset(t *testing.T) { }, }, wantErr: false, + clientObjects: []client.Object{ + &configv1.Infrastructure{ + ObjectMeta: metav1.ObjectMeta{ + Name: "cluster", + }, + }, + }, want: &appsv1.DaemonSet{ ObjectMeta: getNodeAgentObjectMeta(r), TypeMeta: metav1.TypeMeta{ @@ -817,7 +878,7 @@ func TestDPAReconciler_buildNodeAgentDaemonset(t *testing.T) { Name: HostPlugins, VolumeSource: corev1.VolumeSource{ HostPath: &corev1.HostPathVolumeSource{ - Path: "/var/lib/kubelet/plugins", + Path: pluginsHostPath, }, }, }, @@ -873,7 +934,7 @@ func TestDPAReconciler_buildNodeAgentDaemonset(t *testing.T) { }, { Name: HostPlugins, - MountPath: "/var/lib/kubelet/plugins", + MountPath: pluginsHostPath, MountPropagation: &mountPropagationToHostContainer, }, { @@ -967,6 +1028,13 @@ func TestDPAReconciler_buildNodeAgentDaemonset(t *testing.T) { }, }, wantErr: false, + clientObjects: []client.Object{ + &configv1.Infrastructure{ + ObjectMeta: metav1.ObjectMeta{ + Name: "cluster", + }, + }, + }, want: &appsv1.DaemonSet{ ObjectMeta: getNodeAgentObjectMeta(r), TypeMeta: metav1.TypeMeta{ @@ -1008,7 +1076,7 @@ func TestDPAReconciler_buildNodeAgentDaemonset(t *testing.T) { Name: HostPlugins, VolumeSource: corev1.VolumeSource{ HostPath: &corev1.HostPathVolumeSource{ - Path: "/var/lib/kubelet/plugins", + Path: pluginsHostPath, }, }, }, @@ -1064,7 +1132,7 @@ func TestDPAReconciler_buildNodeAgentDaemonset(t *testing.T) { }, { Name: HostPlugins, - MountPath: "/var/lib/kubelet/plugins", + MountPath: pluginsHostPath, MountPropagation: &mountPropagationToHostContainer, }, { @@ -1157,6 +1225,13 @@ func TestDPAReconciler_buildNodeAgentDaemonset(t *testing.T) { }, }, wantErr: false, + clientObjects: []client.Object{ + &configv1.Infrastructure{ + ObjectMeta: metav1.ObjectMeta{ + Name: "cluster", + }, + }, + }, want: &appsv1.DaemonSet{ ObjectMeta: getNodeAgentObjectMeta(r), TypeMeta: metav1.TypeMeta{ @@ -1198,7 +1273,7 @@ func TestDPAReconciler_buildNodeAgentDaemonset(t *testing.T) { Name: HostPlugins, VolumeSource: corev1.VolumeSource{ HostPath: &corev1.HostPathVolumeSource{ - Path: "/var/lib/kubelet/plugins", + Path: pluginsHostPath, }, }, }, @@ -1254,7 +1329,7 @@ func TestDPAReconciler_buildNodeAgentDaemonset(t *testing.T) { }, { Name: HostPlugins, - MountPath: "/var/lib/kubelet/plugins", + MountPath: pluginsHostPath, MountPropagation: &mountPropagationToHostContainer, }, { @@ -1346,6 +1421,13 @@ func TestDPAReconciler_buildNodeAgentDaemonset(t *testing.T) { }, }, wantErr: false, + clientObjects: []client.Object{ + &configv1.Infrastructure{ + ObjectMeta: metav1.ObjectMeta{ + Name: "cluster", + }, + }, + }, want: &appsv1.DaemonSet{ ObjectMeta: getNodeAgentObjectMeta(r), TypeMeta: metav1.TypeMeta{ @@ -1387,7 +1469,7 @@ func TestDPAReconciler_buildNodeAgentDaemonset(t *testing.T) { Name: HostPlugins, VolumeSource: corev1.VolumeSource{ HostPath: &corev1.HostPathVolumeSource{ - Path: "/var/lib/kubelet/plugins", + Path: pluginsHostPath, }, }, }, @@ -1443,7 +1525,7 @@ func TestDPAReconciler_buildNodeAgentDaemonset(t *testing.T) { }, { Name: HostPlugins, - MountPath: "/var/lib/kubelet/plugins", + MountPath: pluginsHostPath, MountPropagation: &mountPropagationToHostContainer, }, { @@ -1532,6 +1614,13 @@ func TestDPAReconciler_buildNodeAgentDaemonset(t *testing.T) { }, }, wantErr: false, + clientObjects: []client.Object{ + &configv1.Infrastructure{ + ObjectMeta: metav1.ObjectMeta{ + Name: "cluster", + }, + }, + }, want: &appsv1.DaemonSet{ ObjectMeta: getNodeAgentObjectMeta(r), TypeMeta: metav1.TypeMeta{ @@ -1573,7 +1662,7 @@ func TestDPAReconciler_buildNodeAgentDaemonset(t *testing.T) { Name: HostPlugins, VolumeSource: corev1.VolumeSource{ HostPath: &corev1.HostPathVolumeSource{ - Path: "/var/lib/kubelet/plugins", + Path: pluginsHostPath, }, }, }, @@ -1629,7 +1718,7 @@ func TestDPAReconciler_buildNodeAgentDaemonset(t *testing.T) { }, { Name: HostPlugins, - MountPath: "/var/lib/kubelet/plugins", + MountPath: pluginsHostPath, MountPropagation: &mountPropagationToHostContainer, }, { @@ -1721,6 +1810,13 @@ func TestDPAReconciler_buildNodeAgentDaemonset(t *testing.T) { }, }, wantErr: false, + clientObjects: []client.Object{ + &configv1.Infrastructure{ + ObjectMeta: metav1.ObjectMeta{ + Name: "cluster", + }, + }, + }, want: &appsv1.DaemonSet{ ObjectMeta: getNodeAgentObjectMeta(r), TypeMeta: metav1.TypeMeta{ @@ -1762,7 +1858,7 @@ func TestDPAReconciler_buildNodeAgentDaemonset(t *testing.T) { Name: HostPlugins, VolumeSource: corev1.VolumeSource{ HostPath: &corev1.HostPathVolumeSource{ - Path: "/var/lib/kubelet/plugins", + Path: pluginsHostPath, }, }, }, @@ -1818,7 +1914,7 @@ func TestDPAReconciler_buildNodeAgentDaemonset(t *testing.T) { }, { Name: HostPlugins, - MountPath: "/var/lib/kubelet/plugins", + MountPath: pluginsHostPath, MountPropagation: &mountPropagationToHostContainer, }, { @@ -1907,6 +2003,13 @@ func TestDPAReconciler_buildNodeAgentDaemonset(t *testing.T) { }, }, wantErr: false, + clientObjects: []client.Object{ + &configv1.Infrastructure{ + ObjectMeta: metav1.ObjectMeta{ + Name: "cluster", + }, + }, + }, want: &appsv1.DaemonSet{ ObjectMeta: getNodeAgentObjectMeta(r), TypeMeta: metav1.TypeMeta{ @@ -1945,7 +2048,7 @@ func TestDPAReconciler_buildNodeAgentDaemonset(t *testing.T) { Name: HostPlugins, VolumeSource: corev1.VolumeSource{ HostPath: &corev1.HostPathVolumeSource{ - Path: "/var/lib/kubelet/plugins", + Path: pluginsHostPath, }, }, }, @@ -2008,7 +2111,7 @@ func TestDPAReconciler_buildNodeAgentDaemonset(t *testing.T) { }, { Name: HostPlugins, - MountPath: "/var/lib/kubelet/plugins", + MountPath: pluginsHostPath, MountPropagation: &mountPropagationToHostContainer, }, { @@ -2105,6 +2208,11 @@ func TestDPAReconciler_buildNodeAgentDaemonset(t *testing.T) { "unsupported-bool-arg": "True", }, }, + &configv1.Infrastructure{ + ObjectMeta: metav1.ObjectMeta{ + Name: "cluster", + }, + }, }, wantErr: false, want: &appsv1.DaemonSet{ @@ -2146,7 +2254,7 @@ func TestDPAReconciler_buildNodeAgentDaemonset(t *testing.T) { Name: HostPlugins, VolumeSource: corev1.VolumeSource{ HostPath: &corev1.HostPathVolumeSource{ - Path: "/var/lib/kubelet/plugins", + Path: pluginsHostPath, }, }, }, @@ -2204,7 +2312,7 @@ func TestDPAReconciler_buildNodeAgentDaemonset(t *testing.T) { }, { Name: HostPlugins, - MountPath: "/var/lib/kubelet/plugins", + MountPath: pluginsHostPath, MountPropagation: &mountPropagationToHostContainer, }, { @@ -2301,6 +2409,11 @@ func TestDPAReconciler_buildNodeAgentDaemonset(t *testing.T) { "unsupported-bool-arg": "True", }, }, + &configv1.Infrastructure{ + ObjectMeta: metav1.ObjectMeta{ + Name: "cluster", + }, + }, }, wantErr: false, want: &appsv1.DaemonSet{ @@ -2342,7 +2455,7 @@ func TestDPAReconciler_buildNodeAgentDaemonset(t *testing.T) { Name: HostPlugins, VolumeSource: corev1.VolumeSource{ HostPath: &corev1.HostPathVolumeSource{ - Path: "/var/lib/kubelet/plugins", + Path: pluginsHostPath, }, }, }, @@ -2398,7 +2511,7 @@ func TestDPAReconciler_buildNodeAgentDaemonset(t *testing.T) { }, { Name: HostPlugins, - MountPath: "/var/lib/kubelet/plugins", + MountPath: pluginsHostPath, MountPropagation: &mountPropagationToHostContainer, }, { @@ -2485,7 +2598,14 @@ func TestDPAReconciler_buildNodeAgentDaemonset(t *testing.T) { }, }, wantErr: true, - want: nil, + clientObjects: []client.Object{ + &configv1.Infrastructure{ + ObjectMeta: metav1.ObjectMeta{ + Name: "cluster", + }, + }, + }, + want: nil, }, { name: "Valid velero and daemon set for aws as bsl", @@ -2512,6 +2632,13 @@ func TestDPAReconciler_buildNodeAgentDaemonset(t *testing.T) { }, }, wantErr: false, + clientObjects: []client.Object{ + &configv1.Infrastructure{ + ObjectMeta: metav1.ObjectMeta{ + Name: "cluster", + }, + }, + }, want: &appsv1.DaemonSet{ ObjectMeta: getNodeAgentObjectMeta(r), TypeMeta: metav1.TypeMeta{ @@ -2551,7 +2678,7 @@ func TestDPAReconciler_buildNodeAgentDaemonset(t *testing.T) { Name: HostPlugins, VolumeSource: corev1.VolumeSource{ HostPath: &corev1.HostPathVolumeSource{ - Path: "/var/lib/kubelet/plugins", + Path: pluginsHostPath, }, }, }, @@ -2607,7 +2734,7 @@ func TestDPAReconciler_buildNodeAgentDaemonset(t *testing.T) { }, { Name: HostPlugins, - MountPath: "/var/lib/kubelet/plugins", + MountPath: pluginsHostPath, MountPropagation: &mountPropagationToHostContainer, }, { @@ -2706,6 +2833,13 @@ func TestDPAReconciler_buildNodeAgentDaemonset(t *testing.T) { }, }, wantErr: false, + clientObjects: []client.Object{ + &configv1.Infrastructure{ + ObjectMeta: metav1.ObjectMeta{ + Name: "cluster", + }, + }, + }, want: &appsv1.DaemonSet{ ObjectMeta: getNodeAgentObjectMeta(r), TypeMeta: metav1.TypeMeta{ @@ -2748,7 +2882,7 @@ func TestDPAReconciler_buildNodeAgentDaemonset(t *testing.T) { Name: HostPlugins, VolumeSource: corev1.VolumeSource{ HostPath: &corev1.HostPathVolumeSource{ - Path: "/var/lib/kubelet/plugins", + Path: pluginsHostPath, }, }, }, @@ -2796,7 +2930,7 @@ func TestDPAReconciler_buildNodeAgentDaemonset(t *testing.T) { }, { Name: HostPlugins, - MountPath: "/var/lib/kubelet/plugins", + MountPath: pluginsHostPath, MountPropagation: &mountPropagationToHostContainer, }, { @@ -2898,6 +3032,13 @@ func TestDPAReconciler_buildNodeAgentDaemonset(t *testing.T) { }, }, wantErr: false, + clientObjects: []client.Object{ + &configv1.Infrastructure{ + ObjectMeta: metav1.ObjectMeta{ + Name: "cluster", + }, + }, + }, want: &appsv1.DaemonSet{ ObjectMeta: getNodeAgentObjectMeta(r), TypeMeta: metav1.TypeMeta{ @@ -2956,7 +3097,7 @@ func TestDPAReconciler_buildNodeAgentDaemonset(t *testing.T) { Name: HostPlugins, VolumeSource: corev1.VolumeSource{ HostPath: &corev1.HostPathVolumeSource{ - Path: "/var/lib/kubelet/plugins", + Path: pluginsHostPath, }, }, }, @@ -3012,7 +3153,7 @@ func TestDPAReconciler_buildNodeAgentDaemonset(t *testing.T) { }, { Name: HostPlugins, - MountPath: "/var/lib/kubelet/plugins", + MountPath: pluginsHostPath, MountPropagation: &mountPropagationToHostContainer, }, { @@ -3182,3 +3323,175 @@ func TestDPAReconciler_updateFsRestoreHelperCM(t *testing.T) { }) } } + +func TestDPAReconciler_getPlatformType(t *testing.T) { + tests := []struct { + name string + dpa *oadpv1alpha1.DataProtectionApplication + clientObjects []client.Object + want string + wantErr bool + }{ + { + name: "get IBMCloud platform type from infrastructure object", + dpa: &oadpv1alpha1.DataProtectionApplication{ + ObjectMeta: metav1.ObjectMeta{ + Name: "sample-dpa", + Namespace: "sample-ns", + }, + }, + clientObjects: []client.Object{ + &configv1.Infrastructure{ + ObjectMeta: metav1.ObjectMeta{ + Name: "cluster", + }, + Status: configv1.InfrastructureStatus{ + PlatformStatus: &configv1.PlatformStatus{ + Type: configv1.IBMCloudPlatformType, + }, + }, + }, + }, + want: IBMCloudPlatform, + wantErr: false, + }, + { + name: "get empty platform type for non existing infrastructure object", + dpa: &oadpv1alpha1.DataProtectionApplication{ + ObjectMeta: metav1.ObjectMeta{ + Name: "sample-dpa", + Namespace: "sample-ns", + }, + }, + clientObjects: []client.Object{ + &configv1.Infrastructure{}, + }, + want: "", + wantErr: true, + }, + } + for _, tt := range tests { + fakeClient, err := getFakeClientFromObjects(tt.clientObjects...) + if err != nil { + t.Errorf("error in creating fake client, likely programmer error") + } + t.Run(tt.name, func(t *testing.T) { + r := &DPAReconciler{ + Client: fakeClient, + Scheme: fakeClient.Scheme(), + Log: logr.Discard(), + Context: newContextForTest(tt.name), + NamespacedName: types.NamespacedName{ + Namespace: tt.dpa.Namespace, + Name: tt.dpa.Name, + }, + EventRecorder: record.NewFakeRecorder(10), + } + got, err := r.getPlatformType() + if (err != nil) != tt.wantErr { + t.Errorf("getPlatformType() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("getPlatformType() got = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_getFsPvHostPath(t *testing.T) { + tests := []struct { + name string + platformType string + envRestic string + envFS string + want string + }{ + { + name: "generic pv host path returned for empty platform type case", + platformType: "", + envRestic: "", + envFS: "", + want: GenericPVHostPath, + }, + { + name: "IBMCloud pv host path returned for IBMCloud platform type", + platformType: IBMCloudPlatform, + envRestic: "", + envFS: "", + want: IBMCloudPVHostPath, + }, + { + name: "empty platform type with restic env var set", + platformType: "", + envRestic: "/foo/restic/bar", + envFS: "", + want: "/foo/restic/bar", + }, + { + name: "empty platform type with fs env var set", + platformType: "", + envRestic: "", + envFS: "/foo/file-system/bar", + want: "/foo/file-system/bar", + }, + { + name: "IBMCloud platform type but env var also set, env var takes precedence", + platformType: IBMCloudPlatform, + envRestic: "", + envFS: "/foo/file-system/env/var/override", + want: "/foo/file-system/env/var/override", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Setenv(ResticPVHostPathEnvVar, tt.envRestic) + t.Setenv(FSPVHostPathEnvVar, tt.envFS) + if got := getFsPvHostPath(tt.platformType); got != tt.want { + t.Errorf("getFsPvHostPath() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_getPluginsHostPath(t *testing.T) { + tests := []struct { + name string + platformType string + env string + want string + }{ + { + name: "generic plugins host path returned for empty platform type case", + platformType: "", + env: "", + want: GenericPluginsHostPath, + }, + { + name: "IBMCloud plugins host path returned for IBMCloud platform type", + platformType: IBMCloudPlatform, + env: "", + want: IBMCloudPluginsHostPath, + }, + { + name: "empty platform type with env var set", + platformType: "", + env: "/foo/plugins/bar", + want: "/foo/plugins/bar", + }, + { + name: "IBMClout platform type and env var also set, env var takes precedence", + platformType: IBMCloudPlatform, + env: "/foo/plugins/bar", + want: "/foo/plugins/bar", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Setenv(PluginsHostPathEnvVar, tt.env) + if got := getPluginsHostPath(tt.platformType); got != tt.want { + t.Errorf("getPluginsHostPath() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/controllers/suite_test.go b/controllers/suite_test.go index d290a8b242..7266ed2342 100644 --- a/controllers/suite_test.go +++ b/controllers/suite_test.go @@ -50,7 +50,7 @@ var _ = ginkgo.BeforeSuite(func() { ginkgo.By("bootstrapping test environment") testEnv = &envtest.Environment{ - CRDDirectoryPaths: []string{filepath.Join("..", "config", "crd", "bases")}, + CRDDirectoryPaths: []string{filepath.Join("..", "config", "crd", "bases"), filepath.Join("..", "hack", "extra-crds")}, ErrorIfCRDPathMissing: true, // The BinaryAssetsDirectory is only required if you want to run the tests directly diff --git a/hack/extra-crds/config-operator_01_infrastructures-Default.crd.yaml b/hack/extra-crds/config-operator_01_infrastructures-Default.crd.yaml new file mode 100644 index 0000000000..46446f94fa --- /dev/null +++ b/hack/extra-crds/config-operator_01_infrastructures-Default.crd.yaml @@ -0,0 +1,1858 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + api-approved.openshift.io: https://github.com/openshift/api/pull/470 + include.release.openshift.io/ibm-cloud-managed: "true" + include.release.openshift.io/self-managed-high-availability: "true" + include.release.openshift.io/single-node-developer: "true" + release.openshift.io/feature-set: Default + name: infrastructures.config.openshift.io +spec: + conversion: + strategy: None + group: config.openshift.io + names: + kind: Infrastructure + listKind: InfrastructureList + plural: infrastructures + singular: infrastructure + scope: Cluster + versions: + - name: v1 + schema: + openAPIV3Schema: + description: "Infrastructure holds cluster-wide information about Infrastructure. + \ The canonical name is `cluster` \n Compatibility level 1: Stable within + a major release for a minimum of 12 months or 3 minor releases (whichever + is longer)." + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: spec holds user settable values for configuration + properties: + cloudConfig: + description: "cloudConfig is a reference to a ConfigMap containing + the cloud provider configuration file. This configuration file is + used to configure the Kubernetes cloud provider integration when + using the built-in cloud provider integration or the external cloud + controller manager. The namespace for this config map is openshift-config. + \n cloudConfig should only be consumed by the kube_cloud_config + controller. The controller is responsible for using the user configuration + in the spec for various platforms and combining that with the user + provided ConfigMap in this field to create a stitched kube cloud + config. The controller generates a ConfigMap `kube-cloud-config` + in `openshift-config-managed` namespace with the kube cloud config + is stored in `cloud.conf` key. All the clients are expected to use + the generated ConfigMap only." + properties: + key: + description: Key allows pointing to a specific key/value inside + of the configmap. This is useful for logical file references. + type: string + name: + type: string + type: object + platformSpec: + description: platformSpec holds desired information specific to the + underlying infrastructure provider. + properties: + alibabaCloud: + description: AlibabaCloud contains settings specific to the Alibaba + Cloud infrastructure provider. + type: object + aws: + description: AWS contains settings specific to the Amazon Web + Services infrastructure provider. + properties: + serviceEndpoints: + description: serviceEndpoints list contains custom endpoints + which will override default service endpoint of AWS Services. + There must be only one ServiceEndpoint for a service. + items: + description: AWSServiceEndpoint store the configuration + of a custom url to override existing defaults of AWS Services. + properties: + name: + description: name is the name of the AWS service. The + list of all the service names can be found at https://docs.aws.amazon.com/general/latest/gr/aws-service-information.html + This must be provided and cannot be empty. + pattern: ^[a-z0-9-]+$ + type: string + url: + description: url is fully qualified URI with scheme + https, that overrides the default generated endpoint + for a client. This must be provided and cannot be + empty. + pattern: ^https:// + type: string + type: object + type: array + x-kubernetes-list-type: atomic + type: object + azure: + description: Azure contains settings specific to the Azure infrastructure + provider. + type: object + baremetal: + description: BareMetal contains settings specific to the BareMetal + platform. + type: object + equinixMetal: + description: EquinixMetal contains settings specific to the Equinix + Metal infrastructure provider. + type: object + external: + description: ExternalPlatformType represents generic infrastructure + provider. Platform-specific components should be supplemented + separately. + properties: + platformName: + default: Unknown + description: PlatformName holds the arbitrary string representing + the infrastructure provider name, expected to be set at + the installation time. This field is solely for informational + and reporting purposes and is not expected to be used for + decision-making. + type: string + x-kubernetes-validations: + - message: platform name cannot be changed once set + rule: oldSelf == 'Unknown' || self == oldSelf + type: object + gcp: + description: GCP contains settings specific to the Google Cloud + Platform infrastructure provider. + type: object + ibmcloud: + description: IBMCloud contains settings specific to the IBMCloud + infrastructure provider. + type: object + kubevirt: + description: Kubevirt contains settings specific to the kubevirt + infrastructure provider. + type: object + nutanix: + description: Nutanix contains settings specific to the Nutanix + infrastructure provider. + properties: + failureDomains: + description: failureDomains configures failure domains information + for the Nutanix platform. When set, the failure domains + defined here may be used to spread Machines across prism + element clusters to improve fault tolerance of the cluster. + items: + description: NutanixFailureDomain configures failure domain + information for the Nutanix platform. + properties: + cluster: + description: cluster is to identify the cluster (the + Prism Element under management of the Prism Central), + in which the Machine's VM will be created. The cluster + identifier (uuid or name) can be obtained from the + Prism Central console or using the prism_central API. + properties: + name: + description: name is the resource name in the PC. + It cannot be empty if the type is Name. + type: string + type: + description: type is the identifier type to use + for this resource. + enum: + - UUID + - Name + type: string + uuid: + description: uuid is the UUID of the resource in + the PC. It cannot be empty if the type is UUID. + type: string + required: + - type + type: object + x-kubernetes-validations: + - message: uuid configuration is required when type + is UUID, and forbidden otherwise + rule: 'has(self.type) && self.type == ''UUID'' ? has(self.uuid) + : !has(self.uuid)' + - message: name configuration is required when type + is Name, and forbidden otherwise + rule: 'has(self.type) && self.type == ''Name'' ? has(self.name) + : !has(self.name)' + name: + description: name defines the unique name of a failure + domain. Name is required and must be at most 64 characters + in length. It must consist of only lower case alphanumeric + characters and hyphens (-). It must start and end + with an alphanumeric character. This value is arbitrary + and is used to identify the failure domain within + the platform. + maxLength: 64 + minLength: 1 + pattern: '[a-z0-9]([-a-z0-9]*[a-z0-9])?' + type: string + subnets: + description: subnets holds a list of identifiers (one + or more) of the cluster's network subnets for the + Machine's VM to connect to. The subnet identifiers + (uuid or name) can be obtained from the Prism Central + console or using the prism_central API. + items: + description: NutanixResourceIdentifier holds the identity + of a Nutanix PC resource (cluster, image, subnet, + etc.) + properties: + name: + description: name is the resource name in the + PC. It cannot be empty if the type is Name. + type: string + type: + description: type is the identifier type to use + for this resource. + enum: + - UUID + - Name + type: string + uuid: + description: uuid is the UUID of the resource + in the PC. It cannot be empty if the type is + UUID. + type: string + required: + - type + type: object + x-kubernetes-validations: + - message: uuid configuration is required when type + is UUID, and forbidden otherwise + rule: 'has(self.type) && self.type == ''UUID'' ? has(self.uuid) + : !has(self.uuid)' + - message: name configuration is required when type + is Name, and forbidden otherwise + rule: 'has(self.type) && self.type == ''Name'' ? has(self.name) + : !has(self.name)' + maxItems: 1 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + required: + - cluster + - name + - subnets + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + prismCentral: + description: prismCentral holds the endpoint address and port + to access the Nutanix Prism Central. When a cluster-wide + proxy is installed, by default, this endpoint will be accessed + via the proxy. Should you wish for communication with this + endpoint not to be proxied, please add the endpoint to the + proxy spec.noProxy list. + properties: + address: + description: address is the endpoint address (DNS name + or IP address) of the Nutanix Prism Central or Element + (cluster) + maxLength: 256 + type: string + port: + description: port is the port number to access the Nutanix + Prism Central or Element (cluster) + format: int32 + maximum: 65535 + minimum: 1 + type: integer + required: + - address + - port + type: object + prismElements: + description: prismElements holds one or more endpoint address + and port data to access the Nutanix Prism Elements (clusters) + of the Nutanix Prism Central. Currently we only support + one Prism Element (cluster) for an OpenShift cluster, where + all the Nutanix resources (VMs, subnets, volumes, etc.) + used in the OpenShift cluster are located. In the future, + we may support Nutanix resources (VMs, etc.) spread over + multiple Prism Elements (clusters) of the Prism Central. + items: + description: NutanixPrismElementEndpoint holds the name + and endpoint data for a Prism Element (cluster) + properties: + endpoint: + description: endpoint holds the endpoint address and + port data of the Prism Element (cluster). When a cluster-wide + proxy is installed, by default, this endpoint will + be accessed via the proxy. Should you wish for communication + with this endpoint not to be proxied, please add the + endpoint to the proxy spec.noProxy list. + properties: + address: + description: address is the endpoint address (DNS + name or IP address) of the Nutanix Prism Central + or Element (cluster) + maxLength: 256 + type: string + port: + description: port is the port number to access the + Nutanix Prism Central or Element (cluster) + format: int32 + maximum: 65535 + minimum: 1 + type: integer + required: + - address + - port + type: object + name: + description: name is the name of the Prism Element (cluster). + This value will correspond with the cluster field + configured on other resources (eg Machines, PVCs, + etc). + maxLength: 256 + type: string + required: + - endpoint + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + required: + - prismCentral + - prismElements + type: object + openstack: + description: OpenStack contains settings specific to the OpenStack + infrastructure provider. + type: object + ovirt: + description: Ovirt contains settings specific to the oVirt infrastructure + provider. + type: object + powervs: + description: PowerVS contains settings specific to the IBM Power + Systems Virtual Servers infrastructure provider. + properties: + serviceEndpoints: + description: serviceEndpoints is a list of custom endpoints + which will override the default service endpoints of a Power + VS service. + items: + description: PowervsServiceEndpoint stores the configuration + of a custom url to override existing defaults of PowerVS + Services. + properties: + name: + description: name is the name of the Power VS service. + Few of the services are IAM - https://cloud.ibm.com/apidocs/iam-identity-token-api + ResourceController - https://cloud.ibm.com/apidocs/resource-controller/resource-controller + Power Cloud - https://cloud.ibm.com/apidocs/power-cloud + pattern: ^[a-z0-9-]+$ + type: string + url: + description: url is fully qualified URI with scheme + https, that overrides the default generated endpoint + for a client. This must be provided and cannot be + empty. + format: uri + pattern: ^https:// + type: string + required: + - name + - url + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + type: + description: type is the underlying infrastructure provider for + the cluster. This value controls whether infrastructure automation + such as service load balancers, dynamic volume provisioning, + machine creation and deletion, and other integrations are enabled. + If None, no infrastructure automation is enabled. Allowed values + are "AWS", "Azure", "BareMetal", "GCP", "Libvirt", "OpenStack", + "VSphere", "oVirt", "KubeVirt", "EquinixMetal", "PowerVS", "AlibabaCloud", + "Nutanix" and "None". Individual components may not support + all platforms, and must handle unrecognized platforms as None + if they do not support that platform. + enum: + - "" + - AWS + - Azure + - BareMetal + - GCP + - Libvirt + - OpenStack + - None + - VSphere + - oVirt + - IBMCloud + - KubeVirt + - EquinixMetal + - PowerVS + - AlibabaCloud + - Nutanix + - External + type: string + vsphere: + description: VSphere contains settings specific to the VSphere + infrastructure provider. + properties: + failureDomains: + description: failureDomains contains the definition of region, + zone and the vCenter topology. If this is omitted failure + domains (regions and zones) will not be used. + items: + description: VSpherePlatformFailureDomainSpec holds the + region and zone failure domain and the vCenter topology + of that failure domain. + properties: + name: + description: name defines the arbitrary but unique name + of a failure domain. + maxLength: 256 + minLength: 1 + type: string + region: + description: region defines the name of a region tag + that will be attached to a vCenter datacenter. The + tag category in vCenter must be named openshift-region. + maxLength: 80 + minLength: 1 + type: string + server: + anyOf: + - format: ipv4 + - format: ipv6 + - format: hostname + description: server is the fully-qualified domain name + or the IP address of the vCenter server. --- + maxLength: 255 + minLength: 1 + type: string + topology: + description: Topology describes a given failure domain + using vSphere constructs + properties: + computeCluster: + description: computeCluster the absolute path of + the vCenter cluster in which virtual machine will + be located. The absolute path is of the form //host/. + The maximum length of the path is 2048 characters. + maxLength: 2048 + pattern: ^/.*?/host/.*? + type: string + datacenter: + description: datacenter is the name of vCenter datacenter + in which virtual machines will be located. The + maximum length of the datacenter name is 80 characters. + maxLength: 80 + type: string + datastore: + description: datastore is the absolute path of the + datastore in which the virtual machine is located. + The absolute path is of the form //datastore/ + The maximum length of the path is 2048 characters. + maxLength: 2048 + pattern: ^/.*?/datastore/.*? + type: string + folder: + description: folder is the absolute path of the + folder where virtual machines are located. The + absolute path is of the form //vm/. + The maximum length of the path is 2048 characters. + maxLength: 2048 + pattern: ^/.*?/vm/.*? + type: string + networks: + description: networks is the list of port group + network names within this failure domain. Currently, + we only support a single interface per RHCOS virtual + machine. The available networks (port groups) + can be listed using `govc ls 'network/*'` The + single interface should be the absolute path of + the form //network/. + items: + type: string + maxItems: 1 + minItems: 1 + type: array + x-kubernetes-list-type: atomic + resourcePool: + description: resourcePool is the absolute path of + the resource pool where virtual machines will + be created. The absolute path is of the form //host//Resources/. + The maximum length of the path is 2048 characters. + maxLength: 2048 + pattern: ^/.*?/host/.*?/Resources.* + type: string + template: + description: "template is the full inventory path + of the virtual machine or template that will be + cloned when creating new machines in this failure + domain. The maximum length of the path is 2048 + characters. \n When omitted, the template will + be calculated by the control plane machineset + operator based on the region and zone defined + in VSpherePlatformFailureDomainSpec. For example, + for zone=zonea, region=region1, and infrastructure + name=test, the template path would be calculated + as //vm/test-rhcos-region1-zonea." + maxLength: 2048 + minLength: 1 + pattern: ^/.*?/vm/.*? + type: string + required: + - computeCluster + - datacenter + - datastore + - networks + type: object + zone: + description: zone defines the name of a zone tag that + will be attached to a vCenter cluster. The tag category + in vCenter must be named openshift-zone. + maxLength: 80 + minLength: 1 + type: string + required: + - name + - region + - server + - topology + - zone + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + nodeNetworking: + description: nodeNetworking contains the definition of internal + and external network constraints for assigning the node's + networking. If this field is omitted, networking defaults + to the legacy address selection behavior which is to only + support a single address and return the first one found. + properties: + external: + description: external represents the network configuration + of the node that is externally routable. + properties: + excludeNetworkSubnetCidr: + description: excludeNetworkSubnetCidr IP addresses + in subnet ranges will be excluded when selecting + the IP address from the VirtualMachine's VM for + use in the status.addresses fields. --- + items: + format: cidr + type: string + type: array + x-kubernetes-list-type: atomic + network: + description: network VirtualMachine's VM Network names + that will be used to when searching for status.addresses + fields. Note that if internal.networkSubnetCIDR + and external.networkSubnetCIDR are not set, then + the vNIC associated to this network must only have + a single IP address assigned to it. The available + networks (port groups) can be listed using `govc + ls 'network/*'` + type: string + networkSubnetCidr: + description: networkSubnetCidr IP address on VirtualMachine's + network interfaces included in the fields' CIDRs + that will be used in respective status.addresses + fields. --- + items: + format: cidr + type: string + type: array + x-kubernetes-list-type: set + type: object + internal: + description: internal represents the network configuration + of the node that is routable only within the cluster. + properties: + excludeNetworkSubnetCidr: + description: excludeNetworkSubnetCidr IP addresses + in subnet ranges will be excluded when selecting + the IP address from the VirtualMachine's VM for + use in the status.addresses fields. --- + items: + format: cidr + type: string + type: array + x-kubernetes-list-type: atomic + network: + description: network VirtualMachine's VM Network names + that will be used to when searching for status.addresses + fields. Note that if internal.networkSubnetCIDR + and external.networkSubnetCIDR are not set, then + the vNIC associated to this network must only have + a single IP address assigned to it. The available + networks (port groups) can be listed using `govc + ls 'network/*'` + type: string + networkSubnetCidr: + description: networkSubnetCidr IP address on VirtualMachine's + network interfaces included in the fields' CIDRs + that will be used in respective status.addresses + fields. --- + items: + format: cidr + type: string + type: array + x-kubernetes-list-type: set + type: object + type: object + vcenters: + description: vcenters holds the connection details for services + to communicate with vCenter. Currently, only a single vCenter + is supported. --- + items: + description: VSpherePlatformVCenterSpec stores the vCenter + connection fields. This is used by the vSphere CCM. + properties: + datacenters: + description: The vCenter Datacenters in which the RHCOS + vm guests are located. This field will be used by + the Cloud Controller Manager. Each datacenter listed + here should be used within a topology. + items: + type: string + minItems: 1 + type: array + x-kubernetes-list-type: set + port: + description: port is the TCP port that will be used + to communicate to the vCenter endpoint. When omitted, + this means the user has no opinion and it is up to + the platform to choose a sensible default, which is + subject to change over time. + format: int32 + maximum: 32767 + minimum: 1 + type: integer + server: + anyOf: + - format: ipv4 + - format: ipv6 + - format: hostname + description: server is the fully-qualified domain name + or the IP address of the vCenter server. --- + maxLength: 255 + type: string + required: + - datacenters + - server + type: object + maxItems: 1 + minItems: 0 + type: array + x-kubernetes-list-type: atomic + type: object + type: object + type: object + status: + description: status holds observed values from the cluster. They may not + be overridden. + properties: + apiServerInternalURI: + description: apiServerInternalURL is a valid URI with scheme 'https', + address and optionally a port (defaulting to 443). apiServerInternalURL + can be used by components like kubelets, to contact the Kubernetes + API server using the infrastructure provider rather than Kubernetes + networking. + type: string + apiServerURL: + description: apiServerURL is a valid URI with scheme 'https', address + and optionally a port (defaulting to 443). apiServerURL can be + used by components like the web console to tell users where to find + the Kubernetes API. + type: string + controlPlaneTopology: + default: HighlyAvailable + description: controlPlaneTopology expresses the expectations for operands + that normally run on control nodes. The default is 'HighlyAvailable', + which represents the behavior operators have in a "normal" cluster. + The 'SingleReplica' mode will be used in single-node deployments + and the operators should not configure the operand for highly-available + operation The 'External' mode indicates that the control plane is + hosted externally to the cluster and that its components are not + visible within the cluster. + enum: + - HighlyAvailable + - SingleReplica + - External + type: string + cpuPartitioning: + default: None + description: cpuPartitioning expresses if CPU partitioning is a currently + enabled feature in the cluster. CPU Partitioning means that this + cluster can support partitioning workloads to specific CPU Sets. + Valid values are "None" and "AllNodes". When omitted, the default + value is "None". The default value of "None" indicates that no nodes + will be setup with CPU partitioning. The "AllNodes" value indicates + that all nodes have been setup with CPU partitioning, and can then + be further configured via the PerformanceProfile API. + enum: + - None + - AllNodes + type: string + etcdDiscoveryDomain: + description: 'etcdDiscoveryDomain is the domain used to fetch the + SRV records for discovering etcd servers and clients. For more info: + https://github.com/etcd-io/etcd/blob/329be66e8b3f9e2e6af83c123ff89297e49ebd15/Documentation/op-guide/clustering.md#dns-discovery + deprecated: as of 4.7, this field is no longer set or honored. It + will be removed in a future release.' + type: string + infrastructureName: + description: infrastructureName uniquely identifies a cluster with + a human friendly name. Once set it should not be changed. Must be + of max length 27 and must have only alphanumeric or hyphen characters. + type: string + infrastructureTopology: + default: HighlyAvailable + description: 'infrastructureTopology expresses the expectations for + infrastructure services that do not run on control plane nodes, + usually indicated by a node selector for a `role` value other than + `master`. The default is ''HighlyAvailable'', which represents the + behavior operators have in a "normal" cluster. The ''SingleReplica'' + mode will be used in single-node deployments and the operators should + not configure the operand for highly-available operation NOTE: External + topology mode is not applicable for this field.' + enum: + - HighlyAvailable + - SingleReplica + type: string + platform: + description: "platform is the underlying infrastructure provider for + the cluster. \n Deprecated: Use platformStatus.type instead." + enum: + - "" + - AWS + - Azure + - BareMetal + - GCP + - Libvirt + - OpenStack + - None + - VSphere + - oVirt + - IBMCloud + - KubeVirt + - EquinixMetal + - PowerVS + - AlibabaCloud + - Nutanix + - External + type: string + platformStatus: + description: platformStatus holds status information specific to the + underlying infrastructure provider. + properties: + alibabaCloud: + description: AlibabaCloud contains settings specific to the Alibaba + Cloud infrastructure provider. + properties: + region: + description: region specifies the region for Alibaba Cloud + resources created for the cluster. + pattern: ^[0-9A-Za-z-]+$ + type: string + resourceGroupID: + description: resourceGroupID is the ID of the resource group + for the cluster. + pattern: ^(rg-[0-9A-Za-z]+)?$ + type: string + resourceTags: + description: resourceTags is a list of additional tags to + apply to Alibaba Cloud resources created for the cluster. + items: + description: AlibabaCloudResourceTag is the set of tags + to add to apply to resources. + properties: + key: + description: key is the key of the tag. + maxLength: 128 + minLength: 1 + type: string + value: + description: value is the value of the tag. + maxLength: 128 + minLength: 1 + type: string + required: + - key + - value + type: object + maxItems: 20 + type: array + x-kubernetes-list-map-keys: + - key + x-kubernetes-list-type: map + required: + - region + type: object + aws: + description: AWS contains settings specific to the Amazon Web + Services infrastructure provider. + properties: + region: + description: region holds the default AWS region for new AWS + resources created by the cluster. + type: string + resourceTags: + description: resourceTags is a list of additional tags to + apply to AWS resources created for the cluster. See https://docs.aws.amazon.com/general/latest/gr/aws_tagging.html + for information on tagging AWS resources. AWS supports a + maximum of 50 tags per resource. OpenShift reserves 25 tags + for its use, leaving 25 tags available for the user. + items: + description: AWSResourceTag is a tag to apply to AWS resources + created for the cluster. + properties: + key: + description: key is the key of the tag + maxLength: 128 + minLength: 1 + pattern: ^[0-9A-Za-z_.:/=+-@]+$ + type: string + value: + description: value is the value of the tag. Some AWS + service do not support empty values. Since tags are + added to resources in many services, the length of + the tag value must meet the requirements of all services. + maxLength: 256 + minLength: 1 + pattern: ^[0-9A-Za-z_.:/=+-@]+$ + type: string + required: + - key + - value + type: object + maxItems: 25 + type: array + x-kubernetes-list-type: atomic + serviceEndpoints: + description: ServiceEndpoints list contains custom endpoints + which will override default service endpoint of AWS Services. + There must be only one ServiceEndpoint for a service. + items: + description: AWSServiceEndpoint store the configuration + of a custom url to override existing defaults of AWS Services. + properties: + name: + description: name is the name of the AWS service. The + list of all the service names can be found at https://docs.aws.amazon.com/general/latest/gr/aws-service-information.html + This must be provided and cannot be empty. + pattern: ^[a-z0-9-]+$ + type: string + url: + description: url is fully qualified URI with scheme + https, that overrides the default generated endpoint + for a client. This must be provided and cannot be + empty. + pattern: ^https:// + type: string + type: object + type: array + x-kubernetes-list-type: atomic + type: object + azure: + description: Azure contains settings specific to the Azure infrastructure + provider. + properties: + armEndpoint: + description: armEndpoint specifies a URL to use for resource + management in non-soverign clouds such as Azure Stack. + type: string + cloudName: + description: cloudName is the name of the Azure cloud environment + which can be used to configure the Azure SDK with the appropriate + Azure API endpoints. If empty, the value is equal to `AzurePublicCloud`. + enum: + - "" + - AzurePublicCloud + - AzureUSGovernmentCloud + - AzureChinaCloud + - AzureGermanCloud + - AzureStackCloud + type: string + networkResourceGroupName: + description: networkResourceGroupName is the Resource Group + for network resources like the Virtual Network and Subnets + used by the cluster. If empty, the value is same as ResourceGroupName. + type: string + resourceGroupName: + description: resourceGroupName is the Resource Group for new + Azure resources created for the cluster. + type: string + resourceTags: + description: resourceTags is a list of additional tags to + apply to Azure resources created for the cluster. See https://docs.microsoft.com/en-us/rest/api/resources/tags + for information on tagging Azure resources. Due to limitations + on Automation, Content Delivery Network, DNS Azure resources, + a maximum of 15 tags may be applied. OpenShift reserves + 5 tags for internal use, allowing 10 tags for user configuration. + items: + description: AzureResourceTag is a tag to apply to Azure + resources created for the cluster. + properties: + key: + description: key is the key part of the tag. A tag key + can have a maximum of 128 characters and cannot be + empty. Key must begin with a letter, end with a letter, + number or underscore, and must contain only alphanumeric + characters and the following special characters `_ + . -`. + maxLength: 128 + minLength: 1 + pattern: ^[a-zA-Z]([0-9A-Za-z_.-]*[0-9A-Za-z_])?$ + type: string + value: + description: 'value is the value part of the tag. A + tag value can have a maximum of 256 characters and + cannot be empty. Value must contain only alphanumeric + characters and the following special characters `_ + + , - . / : ; < = > ? @`.' + maxLength: 256 + minLength: 1 + pattern: ^[0-9A-Za-z_.=+-@]+$ + type: string + required: + - key + - value + type: object + maxItems: 10 + type: array + x-kubernetes-list-type: atomic + x-kubernetes-validations: + - message: resourceTags are immutable and may only be configured + during installation + rule: self.all(x, x in oldSelf) && oldSelf.all(x, x in self) + type: object + x-kubernetes-validations: + - message: resourceTags may only be configured during installation + rule: '!has(oldSelf.resourceTags) && !has(self.resourceTags) + || has(oldSelf.resourceTags) && has(self.resourceTags)' + baremetal: + description: BareMetal contains settings specific to the BareMetal + platform. + properties: + apiServerInternalIP: + description: "apiServerInternalIP is an IP address to contact + the Kubernetes API server that can be used by components + inside the cluster, like kubelets using the infrastructure + rather than Kubernetes networking. It is the IP that the + Infrastructure.status.apiServerInternalURI points to. It + is the IP for a self-hosted load balancer in front of the + API servers. \n Deprecated: Use APIServerInternalIPs instead." + type: string + apiServerInternalIPs: + description: apiServerInternalIPs are the IP addresses to + contact the Kubernetes API server that can be used by components + inside the cluster, like kubelets using the infrastructure + rather than Kubernetes networking. These are the IPs for + a self-hosted load balancer in front of the API servers. + In dual stack clusters this list contains two IPs otherwise + only one. + format: ip + items: + type: string + maxItems: 2 + type: array + x-kubernetes-list-type: set + ingressIP: + description: "ingressIP is an external IP which routes to + the default ingress controller. The IP is a suitable target + of a wildcard DNS record used to resolve default route host + names. \n Deprecated: Use IngressIPs instead." + type: string + ingressIPs: + description: ingressIPs are the external IPs which route to + the default ingress controller. The IPs are suitable targets + of a wildcard DNS record used to resolve default route host + names. In dual stack clusters this list contains two IPs + otherwise only one. + format: ip + items: + type: string + maxItems: 2 + type: array + x-kubernetes-list-type: set + loadBalancer: + default: + type: OpenShiftManagedDefault + description: loadBalancer defines how the load balancer used + by the cluster is configured. + properties: + type: + default: OpenShiftManagedDefault + description: type defines the type of load balancer used + by the cluster on BareMetal platform which can be a + user-managed or openshift-managed load balancer that + is to be used for the OpenShift API and Ingress endpoints. + When set to OpenShiftManagedDefault the static pods + in charge of API and Ingress traffic load-balancing + defined in the machine config operator will be deployed. + When set to UserManaged these static pods will not be + deployed and it is expected that the load balancer is + configured out of band by the deployer. When omitted, + this means no opinion and the platform is left to choose + a reasonable default. The default value is OpenShiftManagedDefault. + enum: + - OpenShiftManagedDefault + - UserManaged + type: string + x-kubernetes-validations: + - message: type is immutable once set + rule: oldSelf == '' || self == oldSelf + type: object + nodeDNSIP: + description: nodeDNSIP is the IP address for the internal + DNS used by the nodes. Unlike the one managed by the DNS + operator, `NodeDNSIP` provides name resolution for the nodes + themselves. There is no DNS-as-a-service for BareMetal deployments. + In order to minimize necessary changes to the datacenter + DNS, a DNS service is hosted as a static pod to serve those + hostnames to the nodes in the cluster. + type: string + type: object + equinixMetal: + description: EquinixMetal contains settings specific to the Equinix + Metal infrastructure provider. + properties: + apiServerInternalIP: + description: apiServerInternalIP is an IP address to contact + the Kubernetes API server that can be used by components + inside the cluster, like kubelets using the infrastructure + rather than Kubernetes networking. It is the IP that the + Infrastructure.status.apiServerInternalURI points to. It + is the IP for a self-hosted load balancer in front of the + API servers. + type: string + ingressIP: + description: ingressIP is an external IP which routes to the + default ingress controller. The IP is a suitable target + of a wildcard DNS record used to resolve default route host + names. + type: string + type: object + external: + description: External contains settings specific to the generic + External infrastructure provider. + properties: + cloudControllerManager: + description: cloudControllerManager contains settings specific + to the external Cloud Controller Manager (a.k.a. CCM or + CPI). When omitted, new nodes will be not tainted and no + extra initialization from the cloud controller manager is + expected. + properties: + state: + description: "state determines whether or not an external + Cloud Controller Manager is expected to be installed + within the cluster. https://kubernetes.io/docs/tasks/administer-cluster/running-cloud-controller/#running-cloud-controller-manager + \n Valid values are \"External\", \"None\" and omitted. + When set to \"External\", new nodes will be tainted + as uninitialized when created, preventing them from + running workloads until they are initialized by the + cloud controller manager. When omitted or set to \"None\", + new nodes will be not tainted and no extra initialization + from the cloud controller manager is expected." + enum: + - "" + - External + - None + type: string + x-kubernetes-validations: + - message: state is immutable once set + rule: self == oldSelf + type: object + x-kubernetes-validations: + - message: state may not be added or removed once set + rule: (has(self.state) == has(oldSelf.state)) || (!has(oldSelf.state) + && self.state != "External") + type: object + x-kubernetes-validations: + - message: cloudControllerManager may not be added or removed + once set + rule: has(self.cloudControllerManager) == has(oldSelf.cloudControllerManager) + gcp: + description: GCP contains settings specific to the Google Cloud + Platform infrastructure provider. + properties: + cloudLoadBalancerConfig: + default: + dnsType: PlatformDefault + description: cloudLoadBalancerConfig is a union that contains + the IP addresses of API, API-Int and Ingress Load Balancers + created on the cloud platform. These values would not be + populated on on-prem platforms. These Load Balancer IPs + are used to configure the in-cluster DNS instances for API, + API-Int and Ingress services. `dnsType` is expected to be + set to `ClusterHosted` when these Load Balancer IP addresses + are populated and used. + nullable: true + properties: + clusterHosted: + description: clusterHosted holds the IP addresses of API, + API-Int and Ingress Load Balancers on Cloud Platforms. + The DNS solution hosted within the cluster use these + IP addresses to provide resolution for API, API-Int + and Ingress services. + properties: + apiIntLoadBalancerIPs: + description: apiIntLoadBalancerIPs holds Load Balancer + IPs for the internal API service. These Load Balancer + IP addresses can be IPv4 and/or IPv6 addresses. + Entries in the apiIntLoadBalancerIPs must be unique. + A maximum of 16 IP addresses are permitted. + format: ip + items: + description: IP is an IP address (for example, "10.0.0.0" + or "fd00::"). + pattern: (^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$)|(^s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:)))(%.+)?s*) + type: string + maxItems: 16 + type: array + x-kubernetes-list-type: set + apiLoadBalancerIPs: + description: apiLoadBalancerIPs holds Load Balancer + IPs for the API service. These Load Balancer IP + addresses can be IPv4 and/or IPv6 addresses. Could + be empty for private clusters. Entries in the apiLoadBalancerIPs + must be unique. A maximum of 16 IP addresses are + permitted. + format: ip + items: + description: IP is an IP address (for example, "10.0.0.0" + or "fd00::"). + pattern: (^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$)|(^s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:)))(%.+)?s*) + type: string + maxItems: 16 + type: array + x-kubernetes-list-type: set + ingressLoadBalancerIPs: + description: ingressLoadBalancerIPs holds IPs for + Ingress Load Balancers. These Load Balancer IP addresses + can be IPv4 and/or IPv6 addresses. Entries in the + ingressLoadBalancerIPs must be unique. A maximum + of 16 IP addresses are permitted. + format: ip + items: + description: IP is an IP address (for example, "10.0.0.0" + or "fd00::"). + pattern: (^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$)|(^s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:)))(%.+)?s*) + type: string + maxItems: 16 + type: array + x-kubernetes-list-type: set + type: object + dnsType: + default: PlatformDefault + description: dnsType indicates the type of DNS solution + in use within the cluster. Its default value of `PlatformDefault` + indicates that the cluster's DNS is the default provided + by the cloud platform. It can be set to `ClusterHosted` + to bypass the configuration of the cloud default DNS. + In this mode, the cluster needs to provide a self-hosted + DNS solution for the cluster's installation to succeed. + The cluster's use of the cloud's Load Balancers is unaffected + by this setting. The value is immutable after it has + been set at install time. Currently, there is no way + for the customer to add additional DNS entries into + the cluster hosted DNS. Enabling this functionality + allows the user to start their own DNS solution outside + the cluster after installation is complete. The customer + would be responsible for configuring this custom DNS + solution, and it can be run in addition to the in-cluster + DNS solution. + enum: + - ClusterHosted + - PlatformDefault + type: string + x-kubernetes-validations: + - message: dnsType is immutable + rule: oldSelf == '' || self == oldSelf + type: object + x-kubernetes-validations: + - message: clusterHosted is permitted only when dnsType is + ClusterHosted + rule: 'has(self.dnsType) && self.dnsType != ''ClusterHosted'' + ? !has(self.clusterHosted) : true' + projectID: + description: resourceGroupName is the Project ID for new GCP + resources created for the cluster. + type: string + region: + description: region holds the region for new GCP resources + created for the cluster. + type: string + resourceLabels: + description: resourceLabels is a list of additional labels + to apply to GCP resources created for the cluster. See https://cloud.google.com/compute/docs/labeling-resources + for information on labeling GCP resources. GCP supports + a maximum of 64 labels per resource. OpenShift reserves + 32 labels for internal use, allowing 32 labels for user + configuration. + items: + description: GCPResourceLabel is a label to apply to GCP + resources created for the cluster. + properties: + key: + description: key is the key part of the label. A label + key can have a maximum of 63 characters and cannot + be empty. Label key must begin with a lowercase letter, + and must contain only lowercase letters, numeric characters, + and the following special characters `_-`. Label key + must not have the reserved prefixes `kubernetes-io` + and `openshift-io`. + maxLength: 63 + minLength: 1 + pattern: ^[a-z][0-9a-z_-]{0,62}$ + type: string + x-kubernetes-validations: + - message: label keys must not start with either `openshift-io` + or `kubernetes-io` + rule: '!self.startsWith(''openshift-io'') && !self.startsWith(''kubernetes-io'')' + value: + description: value is the value part of the label. A + label value can have a maximum of 63 characters and + cannot be empty. Value must contain only lowercase + letters, numeric characters, and the following special + characters `_-`. + maxLength: 63 + minLength: 1 + pattern: ^[0-9a-z_-]{1,63}$ + type: string + required: + - key + - value + type: object + maxItems: 32 + type: array + x-kubernetes-list-map-keys: + - key + x-kubernetes-list-type: map + x-kubernetes-validations: + - message: resourceLabels are immutable and may only be configured + during installation + rule: self.all(x, x in oldSelf) && oldSelf.all(x, x in self) + resourceTags: + description: resourceTags is a list of additional tags to + apply to GCP resources created for the cluster. See https://cloud.google.com/resource-manager/docs/tags/tags-overview + for information on tagging GCP resources. GCP supports a + maximum of 50 tags per resource. + items: + description: GCPResourceTag is a tag to apply to GCP resources + created for the cluster. + properties: + key: + description: key is the key part of the tag. A tag key + can have a maximum of 63 characters and cannot be + empty. Tag key must begin and end with an alphanumeric + character, and must contain only uppercase, lowercase + alphanumeric characters, and the following special + characters `._-`. + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z0-9]([0-9A-Za-z_.-]{0,61}[a-zA-Z0-9])?$ + type: string + parentID: + description: 'parentID is the ID of the hierarchical + resource where the tags are defined, e.g. at the Organization + or the Project level. To find the Organization or + Project ID refer to the following pages: https://cloud.google.com/resource-manager/docs/creating-managing-organization#retrieving_your_organization_id, + https://cloud.google.com/resource-manager/docs/creating-managing-projects#identifying_projects. + An OrganizationID must consist of decimal numbers, + and cannot have leading zeroes. A ProjectID must be + 6 to 30 characters in length, can only contain lowercase + letters, numbers, and hyphens, and must start with + a letter, and cannot end with a hyphen.' + maxLength: 32 + minLength: 1 + pattern: (^[1-9][0-9]{0,31}$)|(^[a-z][a-z0-9-]{4,28}[a-z0-9]$) + type: string + value: + description: value is the value part of the tag. A tag + value can have a maximum of 63 characters and cannot + be empty. Tag value must begin and end with an alphanumeric + character, and must contain only uppercase, lowercase + alphanumeric characters, and the following special + characters `_-.@%=+:,*#&(){}[]` and spaces. + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z0-9]([0-9A-Za-z_.@%=+:,*#&()\[\]{}\-\s]{0,61}[a-zA-Z0-9])?$ + type: string + required: + - key + - parentID + - value + type: object + maxItems: 50 + type: array + x-kubernetes-list-map-keys: + - key + x-kubernetes-list-type: map + x-kubernetes-validations: + - message: resourceTags are immutable and may only be configured + during installation + rule: self.all(x, x in oldSelf) && oldSelf.all(x, x in self) + type: object + x-kubernetes-validations: + - message: resourceLabels may only be configured during installation + rule: '!has(oldSelf.resourceLabels) && !has(self.resourceLabels) + || has(oldSelf.resourceLabels) && has(self.resourceLabels)' + - message: resourceTags may only be configured during installation + rule: '!has(oldSelf.resourceTags) && !has(self.resourceTags) + || has(oldSelf.resourceTags) && has(self.resourceTags)' + ibmcloud: + description: IBMCloud contains settings specific to the IBMCloud + infrastructure provider. + properties: + cisInstanceCRN: + description: CISInstanceCRN is the CRN of the Cloud Internet + Services instance managing the DNS zone for the cluster's + base domain + type: string + dnsInstanceCRN: + description: DNSInstanceCRN is the CRN of the DNS Services + instance managing the DNS zone for the cluster's base domain + type: string + location: + description: Location is where the cluster has been deployed + type: string + providerType: + description: ProviderType indicates the type of cluster that + was created + type: string + resourceGroupName: + description: ResourceGroupName is the Resource Group for new + IBMCloud resources created for the cluster. + type: string + serviceEndpoints: + description: serviceEndpoints is a list of custom endpoints + which will override the default service endpoints of an + IBM Cloud service. These endpoints are consumed by components + within the cluster to reach the respective IBM Cloud Services. + items: + description: IBMCloudServiceEndpoint stores the configuration + of a custom url to override existing defaults of IBM Cloud + Services. + properties: + name: + description: 'name is the name of the IBM Cloud service. + Possible values are: CIS, COS, DNSServices, GlobalSearch, + GlobalTagging, HyperProtect, IAM, KeyProtect, ResourceController, + ResourceManager, or VPC. For example, the IBM Cloud + Private IAM service could be configured with the service + `name` of `IAM` and `url` of `https://private.iam.cloud.ibm.com` + Whereas the IBM Cloud Private VPC service for US South + (Dallas) could be configured with the service `name` + of `VPC` and `url` of `https://us.south.private.iaas.cloud.ibm.com`' + enum: + - CIS + - COS + - DNSServices + - GlobalSearch + - GlobalTagging + - HyperProtect + - IAM + - KeyProtect + - ResourceController + - ResourceManager + - VPC + type: string + url: + description: url is fully qualified URI with scheme + https, that overrides the default generated endpoint + for a client. This must be provided and cannot be + empty. + type: string + x-kubernetes-validations: + - message: url must be a valid absolute URL + rule: isURL(self) + required: + - name + - url + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + kubevirt: + description: Kubevirt contains settings specific to the kubevirt + infrastructure provider. + properties: + apiServerInternalIP: + description: apiServerInternalIP is an IP address to contact + the Kubernetes API server that can be used by components + inside the cluster, like kubelets using the infrastructure + rather than Kubernetes networking. It is the IP that the + Infrastructure.status.apiServerInternalURI points to. It + is the IP for a self-hosted load balancer in front of the + API servers. + type: string + ingressIP: + description: ingressIP is an external IP which routes to the + default ingress controller. The IP is a suitable target + of a wildcard DNS record used to resolve default route host + names. + type: string + type: object + nutanix: + description: Nutanix contains settings specific to the Nutanix + infrastructure provider. + properties: + apiServerInternalIP: + description: "apiServerInternalIP is an IP address to contact + the Kubernetes API server that can be used by components + inside the cluster, like kubelets using the infrastructure + rather than Kubernetes networking. It is the IP that the + Infrastructure.status.apiServerInternalURI points to. It + is the IP for a self-hosted load balancer in front of the + API servers. \n Deprecated: Use APIServerInternalIPs instead." + type: string + apiServerInternalIPs: + description: apiServerInternalIPs are the IP addresses to + contact the Kubernetes API server that can be used by components + inside the cluster, like kubelets using the infrastructure + rather than Kubernetes networking. These are the IPs for + a self-hosted load balancer in front of the API servers. + In dual stack clusters this list contains two IPs otherwise + only one. + format: ip + items: + type: string + maxItems: 2 + type: array + x-kubernetes-list-type: set + ingressIP: + description: "ingressIP is an external IP which routes to + the default ingress controller. The IP is a suitable target + of a wildcard DNS record used to resolve default route host + names. \n Deprecated: Use IngressIPs instead." + type: string + ingressIPs: + description: ingressIPs are the external IPs which route to + the default ingress controller. The IPs are suitable targets + of a wildcard DNS record used to resolve default route host + names. In dual stack clusters this list contains two IPs + otherwise only one. + format: ip + items: + type: string + maxItems: 2 + type: array + x-kubernetes-list-type: set + loadBalancer: + default: + type: OpenShiftManagedDefault + description: loadBalancer defines how the load balancer used + by the cluster is configured. + properties: + type: + default: OpenShiftManagedDefault + description: type defines the type of load balancer used + by the cluster on Nutanix platform which can be a user-managed + or openshift-managed load balancer that is to be used + for the OpenShift API and Ingress endpoints. When set + to OpenShiftManagedDefault the static pods in charge + of API and Ingress traffic load-balancing defined in + the machine config operator will be deployed. When set + to UserManaged these static pods will not be deployed + and it is expected that the load balancer is configured + out of band by the deployer. When omitted, this means + no opinion and the platform is left to choose a reasonable + default. The default value is OpenShiftManagedDefault. + enum: + - OpenShiftManagedDefault + - UserManaged + type: string + x-kubernetes-validations: + - message: type is immutable once set + rule: oldSelf == '' || self == oldSelf + type: object + type: object + openstack: + description: OpenStack contains settings specific to the OpenStack + infrastructure provider. + properties: + apiServerInternalIP: + description: "apiServerInternalIP is an IP address to contact + the Kubernetes API server that can be used by components + inside the cluster, like kubelets using the infrastructure + rather than Kubernetes networking. It is the IP that the + Infrastructure.status.apiServerInternalURI points to. It + is the IP for a self-hosted load balancer in front of the + API servers. \n Deprecated: Use APIServerInternalIPs instead." + type: string + apiServerInternalIPs: + description: apiServerInternalIPs are the IP addresses to + contact the Kubernetes API server that can be used by components + inside the cluster, like kubelets using the infrastructure + rather than Kubernetes networking. These are the IPs for + a self-hosted load balancer in front of the API servers. + In dual stack clusters this list contains two IPs otherwise + only one. + format: ip + items: + type: string + maxItems: 2 + type: array + x-kubernetes-list-type: set + cloudName: + description: cloudName is the name of the desired OpenStack + cloud in the client configuration file (`clouds.yaml`). + type: string + ingressIP: + description: "ingressIP is an external IP which routes to + the default ingress controller. The IP is a suitable target + of a wildcard DNS record used to resolve default route host + names. \n Deprecated: Use IngressIPs instead." + type: string + ingressIPs: + description: ingressIPs are the external IPs which route to + the default ingress controller. The IPs are suitable targets + of a wildcard DNS record used to resolve default route host + names. In dual stack clusters this list contains two IPs + otherwise only one. + format: ip + items: + type: string + maxItems: 2 + type: array + x-kubernetes-list-type: set + loadBalancer: + default: + type: OpenShiftManagedDefault + description: loadBalancer defines how the load balancer used + by the cluster is configured. + properties: + type: + default: OpenShiftManagedDefault + description: type defines the type of load balancer used + by the cluster on OpenStack platform which can be a + user-managed or openshift-managed load balancer that + is to be used for the OpenShift API and Ingress endpoints. + When set to OpenShiftManagedDefault the static pods + in charge of API and Ingress traffic load-balancing + defined in the machine config operator will be deployed. + When set to UserManaged these static pods will not be + deployed and it is expected that the load balancer is + configured out of band by the deployer. When omitted, + this means no opinion and the platform is left to choose + a reasonable default. The default value is OpenShiftManagedDefault. + enum: + - OpenShiftManagedDefault + - UserManaged + type: string + x-kubernetes-validations: + - message: type is immutable once set + rule: oldSelf == '' || self == oldSelf + type: object + nodeDNSIP: + description: nodeDNSIP is the IP address for the internal + DNS used by the nodes. Unlike the one managed by the DNS + operator, `NodeDNSIP` provides name resolution for the nodes + themselves. There is no DNS-as-a-service for OpenStack deployments. + In order to minimize necessary changes to the datacenter + DNS, a DNS service is hosted as a static pod to serve those + hostnames to the nodes in the cluster. + type: string + type: object + ovirt: + description: Ovirt contains settings specific to the oVirt infrastructure + provider. + properties: + apiServerInternalIP: + description: "apiServerInternalIP is an IP address to contact + the Kubernetes API server that can be used by components + inside the cluster, like kubelets using the infrastructure + rather than Kubernetes networking. It is the IP that the + Infrastructure.status.apiServerInternalURI points to. It + is the IP for a self-hosted load balancer in front of the + API servers. \n Deprecated: Use APIServerInternalIPs instead." + type: string + apiServerInternalIPs: + description: apiServerInternalIPs are the IP addresses to + contact the Kubernetes API server that can be used by components + inside the cluster, like kubelets using the infrastructure + rather than Kubernetes networking. These are the IPs for + a self-hosted load balancer in front of the API servers. + In dual stack clusters this list contains two IPs otherwise + only one. + format: ip + items: + type: string + maxItems: 2 + type: array + x-kubernetes-list-type: set + ingressIP: + description: "ingressIP is an external IP which routes to + the default ingress controller. The IP is a suitable target + of a wildcard DNS record used to resolve default route host + names. \n Deprecated: Use IngressIPs instead." + type: string + ingressIPs: + description: ingressIPs are the external IPs which route to + the default ingress controller. The IPs are suitable targets + of a wildcard DNS record used to resolve default route host + names. In dual stack clusters this list contains two IPs + otherwise only one. + format: ip + items: + type: string + maxItems: 2 + type: array + x-kubernetes-list-type: set + loadBalancer: + default: + type: OpenShiftManagedDefault + description: loadBalancer defines how the load balancer used + by the cluster is configured. + properties: + type: + default: OpenShiftManagedDefault + description: type defines the type of load balancer used + by the cluster on Ovirt platform which can be a user-managed + or openshift-managed load balancer that is to be used + for the OpenShift API and Ingress endpoints. When set + to OpenShiftManagedDefault the static pods in charge + of API and Ingress traffic load-balancing defined in + the machine config operator will be deployed. When set + to UserManaged these static pods will not be deployed + and it is expected that the load balancer is configured + out of band by the deployer. When omitted, this means + no opinion and the platform is left to choose a reasonable + default. The default value is OpenShiftManagedDefault. + enum: + - OpenShiftManagedDefault + - UserManaged + type: string + x-kubernetes-validations: + - message: type is immutable once set + rule: oldSelf == '' || self == oldSelf + type: object + nodeDNSIP: + description: 'deprecated: as of 4.6, this field is no longer + set or honored. It will be removed in a future release.' + type: string + type: object + powervs: + description: PowerVS contains settings specific to the Power Systems + Virtual Servers infrastructure provider. + properties: + cisInstanceCRN: + description: CISInstanceCRN is the CRN of the Cloud Internet + Services instance managing the DNS zone for the cluster's + base domain + type: string + dnsInstanceCRN: + description: DNSInstanceCRN is the CRN of the DNS Services + instance managing the DNS zone for the cluster's base domain + type: string + region: + description: region holds the default Power VS region for + new Power VS resources created by the cluster. + type: string + resourceGroup: + description: 'resourceGroup is the resource group name for + new IBMCloud resources created for a cluster. The resource + group specified here will be used by cluster-image-registry-operator + to set up a COS Instance in IBMCloud for the cluster registry. + More about resource groups can be found here: https://cloud.ibm.com/docs/account?topic=account-rgs. + When omitted, the image registry operator won''t be able + to configure storage, which results in the image registry + cluster operator not being in an available state.' + maxLength: 40 + pattern: ^[a-zA-Z0-9-_ ]+$ + type: string + x-kubernetes-validations: + - message: resourceGroup is immutable once set + rule: oldSelf == '' || self == oldSelf + serviceEndpoints: + description: serviceEndpoints is a list of custom endpoints + which will override the default service endpoints of a Power + VS service. + items: + description: PowervsServiceEndpoint stores the configuration + of a custom url to override existing defaults of PowerVS + Services. + properties: + name: + description: name is the name of the Power VS service. + Few of the services are IAM - https://cloud.ibm.com/apidocs/iam-identity-token-api + ResourceController - https://cloud.ibm.com/apidocs/resource-controller/resource-controller + Power Cloud - https://cloud.ibm.com/apidocs/power-cloud + pattern: ^[a-z0-9-]+$ + type: string + url: + description: url is fully qualified URI with scheme + https, that overrides the default generated endpoint + for a client. This must be provided and cannot be + empty. + format: uri + pattern: ^https:// + type: string + required: + - name + - url + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + zone: + description: 'zone holds the default zone for the new Power + VS resources created by the cluster. Note: Currently only + single-zone OCP clusters are supported' + type: string + type: object + x-kubernetes-validations: + - message: cannot unset resourceGroup once set + rule: '!has(oldSelf.resourceGroup) || has(self.resourceGroup)' + type: + description: "type is the underlying infrastructure provider for + the cluster. This value controls whether infrastructure automation + such as service load balancers, dynamic volume provisioning, + machine creation and deletion, and other integrations are enabled. + If None, no infrastructure automation is enabled. Allowed values + are \"AWS\", \"Azure\", \"BareMetal\", \"GCP\", \"Libvirt\", + \"OpenStack\", \"VSphere\", \"oVirt\", \"EquinixMetal\", \"PowerVS\", + \"AlibabaCloud\", \"Nutanix\" and \"None\". Individual components + may not support all platforms, and must handle unrecognized + platforms as None if they do not support that platform. \n This + value will be synced with to the `status.platform` and `status.platformStatus.type`. + Currently this value cannot be changed once set." + enum: + - "" + - AWS + - Azure + - BareMetal + - GCP + - Libvirt + - OpenStack + - None + - VSphere + - oVirt + - IBMCloud + - KubeVirt + - EquinixMetal + - PowerVS + - AlibabaCloud + - Nutanix + - External + type: string + vsphere: + description: VSphere contains settings specific to the VSphere + infrastructure provider. + properties: + apiServerInternalIP: + description: "apiServerInternalIP is an IP address to contact + the Kubernetes API server that can be used by components + inside the cluster, like kubelets using the infrastructure + rather than Kubernetes networking. It is the IP that the + Infrastructure.status.apiServerInternalURI points to. It + is the IP for a self-hosted load balancer in front of the + API servers. \n Deprecated: Use APIServerInternalIPs instead." + type: string + apiServerInternalIPs: + description: apiServerInternalIPs are the IP addresses to + contact the Kubernetes API server that can be used by components + inside the cluster, like kubelets using the infrastructure + rather than Kubernetes networking. These are the IPs for + a self-hosted load balancer in front of the API servers. + In dual stack clusters this list contains two IPs otherwise + only one. + format: ip + items: + type: string + maxItems: 2 + type: array + x-kubernetes-list-type: set + ingressIP: + description: "ingressIP is an external IP which routes to + the default ingress controller. The IP is a suitable target + of a wildcard DNS record used to resolve default route host + names. \n Deprecated: Use IngressIPs instead." + type: string + ingressIPs: + description: ingressIPs are the external IPs which route to + the default ingress controller. The IPs are suitable targets + of a wildcard DNS record used to resolve default route host + names. In dual stack clusters this list contains two IPs + otherwise only one. + format: ip + items: + type: string + maxItems: 2 + type: array + x-kubernetes-list-type: set + loadBalancer: + default: + type: OpenShiftManagedDefault + description: loadBalancer defines how the load balancer used + by the cluster is configured. + properties: + type: + default: OpenShiftManagedDefault + description: type defines the type of load balancer used + by the cluster on VSphere platform which can be a user-managed + or openshift-managed load balancer that is to be used + for the OpenShift API and Ingress endpoints. When set + to OpenShiftManagedDefault the static pods in charge + of API and Ingress traffic load-balancing defined in + the machine config operator will be deployed. When set + to UserManaged these static pods will not be deployed + and it is expected that the load balancer is configured + out of band by the deployer. When omitted, this means + no opinion and the platform is left to choose a reasonable + default. The default value is OpenShiftManagedDefault. + enum: + - OpenShiftManagedDefault + - UserManaged + type: string + x-kubernetes-validations: + - message: type is immutable once set + rule: oldSelf == '' || self == oldSelf + type: object + nodeDNSIP: + description: nodeDNSIP is the IP address for the internal + DNS used by the nodes. Unlike the one managed by the DNS + operator, `NodeDNSIP` provides name resolution for the nodes + themselves. There is no DNS-as-a-service for vSphere deployments. + In order to minimize necessary changes to the datacenter + DNS, a DNS service is hosted as a static pod to serve those + hostnames to the nodes in the cluster. + type: string + type: object + type: object + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} diff --git a/main.go b/main.go index d53a8f01ab..7cd24df03f 100644 --- a/main.go +++ b/main.go @@ -23,6 +23,7 @@ import ( "fmt" "os" + configv1 "github.com/openshift/api/config/v1" routev1 "github.com/openshift/api/route/v1" security "github.com/openshift/api/security/v1" monitor "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" @@ -212,6 +213,11 @@ func main() { os.Exit(1) } + if err := configv1.AddToScheme(mgr.GetScheme()); err != nil { + setupLog.Error(err, "unable to add OpenShift configuration API to scheme") + os.Exit(1) + } + if err = (&controllers.DPAReconciler{ Client: mgr.GetClient(), Scheme: mgr.GetScheme(),