diff --git a/Makefile b/Makefile index 944c8d11..6dd4c8a0 100644 --- a/Makefile +++ b/Makefile @@ -102,7 +102,8 @@ deploy: manifests kustomize ## Deploy controller to the K8s cluster specified in cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG} && \ $(KUSTOMIZE) edit set image deployment-guard=$(IMG) cd config/default && $(KUSTOMIZE) edit set image kube-rbac-proxy=$(RBAC_PROXY_IMG) - cd config/console && $(KUSTOMIZE) edit set image ocs-client-operator-console=$(OCS_CLIENT_CONSOLE_IMG) + cd config/console && $(KUSTOMIZE) edit set image ocs-client-operator-console=$(OCS_CLIENT_CONSOLE_IMG) && \ + $(KUSTOMIZE) edit set image deployment-guard=$(IMG) $(KUSTOMIZE) build config/default | sed "s|STATUS_REPORTER_IMAGE_VALUE|$(IMG)|g" | awk '{print}' | kubectl apply -f - remove: ## Remove controller from the K8s cluster specified in ~/.kube/config. @@ -122,6 +123,7 @@ bundle: manifests kustomize operator-sdk yq ## Generate bundle manifests and met cd config/manager && $(KUSTOMIZE) edit set image controller=$(IMG) && \ $(KUSTOMIZE) edit set image deployment-guard=$(IMG) cd config/console && $(KUSTOMIZE) edit set image ocs-client-operator-console=$(OCS_CLIENT_CONSOLE_IMG) && \ + $(KUSTOMIZE) edit set image deployment-guard=$(IMG) && \ $(KUSTOMIZE) edit set nameprefix $(OPERATOR_NAMEPREFIX) cd config/default && \ $(KUSTOMIZE) edit set image kube-rbac-proxy=$(RBAC_PROXY_IMG) && \ diff --git a/bundle/manifests/ocs-client-operator.clusterserviceversion.yaml b/bundle/manifests/ocs-client-operator.clusterserviceversion.yaml index dce4af3c..c2e62b9f 100644 --- a/bundle/manifests/ocs-client-operator.clusterserviceversion.yaml +++ b/bundle/manifests/ocs-client-operator.clusterserviceversion.yaml @@ -7,7 +7,7 @@ metadata: categories: Storage console.openshift.io/plugins: '["odf-client-console"]' containerImage: quay.io/ocs-dev/ocs-client-operator:latest - createdAt: "2024-07-29T12:58:14Z" + createdAt: "2024-07-30T11:51:59Z" description: OpenShift Data Foundation client operator enables consumption of storage services from a remote centralized OpenShift Data Foundation provider cluster. @@ -237,6 +237,13 @@ spec: - get - patch - update + - apiGroups: + - ocs.openshift.io + resources: + - storageclusters + verbs: + - get + - list - apiGroups: - operators.coreos.com resources: @@ -823,14 +830,27 @@ spec: name: ocs-client-operator-console-nginx-log - mountPath: /var/lib/nginx/tmp name: ocs-client-operator-console-nginx-tmp + initContainers: + - command: + - /deployment-guard + env: + - name: OPERATOR_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + image: quay.io/ocs-dev/ocs-client-operator:latest + name: deployment-guard + resources: {} securityContext: runAsNonRoot: true + serviceAccountName: ocs-client-operator-controller-manager volumes: - name: ocs-client-operator-console-serving-cert secret: secretName: ocs-client-operator-console-serving-cert - configMap: name: ocs-client-operator-console-nginx-conf + optional: true name: ocs-client-operator-console-nginx-conf - emptyDir: {} name: ocs-client-operator-console-nginx-log diff --git a/config/console/console_init.yaml b/config/console/console_init.yaml index e1ee15a0..bbb274d0 100644 --- a/config/console/console_init.yaml +++ b/config/console/console_init.yaml @@ -11,6 +11,16 @@ spec: labels: app.kubernetes.io/name: ocs-client-operator-console spec: + initContainers: + - name: deployment-guard + image: deployment-guard:latest + command: + - /deployment-guard + env: + - name: OPERATOR_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace containers: - name: ocs-client-operator-console image: ocs-client-operator-console:latest @@ -54,9 +64,11 @@ spec: - name: ocs-client-operator-console-nginx-conf configMap: name: ocs-client-operator-console-nginx-conf + optional: true - name: ocs-client-operator-console-nginx-log emptyDir: {} - name: ocs-client-operator-console-nginx-tmp emptyDir: {} securityContext: runAsNonRoot: true + serviceAccountName: ocs-client-operator-controller-manager diff --git a/config/console/kustomization.yaml b/config/console/kustomization.yaml index a70695ea..3c57a2fe 100644 --- a/config/console/kustomization.yaml +++ b/config/console/kustomization.yaml @@ -9,6 +9,9 @@ resources: apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization images: +- name: deployment-guard + newName: quay.io/ocs-dev/ocs-client-operator + newTag: latest - name: ocs-client-operator-console newName: quay.io/ocs-dev/ocs-client-console newTag: latest diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index cc5837f8..d952a963 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -194,6 +194,13 @@ rules: - get - patch - update +- apiGroups: + - ocs.openshift.io + resources: + - storageclusters + verbs: + - get + - list - apiGroups: - operators.coreos.com resources: diff --git a/internal/controller/operatorconfigmap_controller.go b/internal/controller/operatorconfigmap_controller.go index 528f8f8d..51dca1d7 100644 --- a/internal/controller/operatorconfigmap_controller.go +++ b/internal/controller/operatorconfigmap_controller.go @@ -177,6 +177,7 @@ func (c *OperatorConfigMapReconciler) SetupWithManager(mgr ctrl.Manager) error { //+kubebuilder:rbac:groups=console.openshift.io,resources=consoleplugins,verbs=* //+kubebuilder:rbac:groups=operators.coreos.com,resources=subscriptions,verbs=get;list;watch;update //+kubebuilder:rbac:groups=admissionregistration.k8s.io,resources=validatingwebhookconfigurations,verbs=get;list;update;create;watch;delete +//+kubebuilder:rbac:groups=ocs.openshift.io,resources=storageclusters,verbs=get;list // For more details, check Reconcile and its Result here: // - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.8.3/pkg/reconcile diff --git a/service/deployment-guard/main.go b/service/deployment-guard/main.go index 38dd16da..22f75d77 100644 --- a/service/deployment-guard/main.go +++ b/service/deployment-guard/main.go @@ -1,3 +1,98 @@ package main -func main() {} +import ( + "context" + "os" + "time" + + "github.com/red-hat-storage/ocs-client-operator/pkg/utils" + + extv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/klog/v2" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/config" +) + +func main() { + // validations + operatorNamespace := os.Getenv(utils.OperatorNamespaceEnvVar) + if operatorNamespace == "" { + klog.Exitf("%s env var is empty", utils.OperatorNamespaceEnvVar) + } + + // creation of kube client + scheme := runtime.NewScheme() + cfg, err := config.GetConfig() + if err != nil { + klog.Exitf("Failed to get config: %v", err) + } + cl, err := client.New(cfg, client.Options{Scheme: scheme}) + if err != nil { + klog.Exitf("Failed to create controller runtime client: %v", err) + } + ctx := context.Background() + + storageClusterCRD := &metav1.PartialObjectMetadata{} + storageClusterCRD.SetGroupVersionKind( + extv1.SchemeGroupVersion.WithKind("CustomResourceDefinition"), + ) + storageClusterCRD.Name = "storageclusters.ocs.openshift.io" + + // delay exponentially from half a sec and cap at 2 minutes + delayFunc := wait.Backoff{ + Duration: 500 * time.Millisecond, + Factor: 2, + Jitter: 0.1, + Steps: 10, + Cap: 2 * time.Minute, + }.DelayFunc() + + for !allowOperatorToRun(ctx, cl, operatorNamespace) { + time.Sleep(delayFunc()) + } + +} + +func allowOperatorToRun(ctx context.Context, cl client.Client, namespace string) bool { + // verify presence of StorageCluster CRD + storageClusterCRD := &metav1.PartialObjectMetadata{} + storageClusterCRD.SetGroupVersionKind( + extv1.SchemeGroupVersion.WithKind("CustomResourceDefinition"), + ) + storageClusterCRD.Name = "storageclusters.ocs.openshift.io" + if err := cl.Get(ctx, client.ObjectKeyFromObject(storageClusterCRD), storageClusterCRD); client.IgnoreNotFound(err) != nil { + klog.Warning("Failed to find presence of StorageCluster CRD") + return false + } + + if storageClusterCRD.UID != "" { + // StorageCluster CRD exists, wait till StorageCluster CR is configured in Provider mode + storageClusters := &metav1.PartialObjectMetadataList{} + storageClusters.SetGroupVersionKind( + schema.GroupVersionKind{ + Group: "ocs.openshift.io", + Version: "v1", + Kind: "StorageCluster", + }, + ) + if err := cl.List(ctx, storageClusters, client.InNamespace(namespace), client.Limit(1)); err != nil { + klog.Warning("Failed to list StorageCluster CR") + return false + } + if len(storageClusters.Items) < 1 { + klog.Info("StorageCluster CR does not exist") + return false + } + klog.Info("Checking for StorageCluster configuration be 'Provider' mode") + if storageClusters.Items[0].GetAnnotations()["ocs.openshift.io/deployment-mode"] != "provider" { + return false + } + } + + klog.Info("Condition met to allow operator to run") + return true +}