diff --git a/bundle/manifests/kuadrant-console-plugin.yaml b/bundle/manifests/kuadrant-console-plugin.yaml new file mode 100644 index 000000000..7b8c489db --- /dev/null +++ b/bundle/manifests/kuadrant-console-plugin.yaml @@ -0,0 +1,133 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: kuadrant-console +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: kuadrant-console + namespace: kuadrant-console + labels: + app: kuadrant-console + app.kubernetes.io/component: kuadrant-console + app.kubernetes.io/instance: kuadrant-console + app.kubernetes.io/name: kuadrant-console + app.kubernetes.io/part-of: kuadrant-console + app.openshift.io/runtime-namespace: kuadrant-console +spec: + replicas: 1 + selector: + matchLabels: + app: kuadrant-console + template: + metadata: + labels: + app: kuadrant-console + app.kubernetes.io/component: kuadrant-console + app.kubernetes.io/instance: kuadrant-console + app.kubernetes.io/name: kuadrant-console + app.kubernetes.io/part-of: kuadrant-console + spec: + containers: + - name: kuadrant-console + image: quay.io/kuadrant/console-plugin:latest + ports: + - containerPort: 9443 + protocol: TCP + imagePullPolicy: Always + volumeMounts: + - name: plugin-serving-cert + readOnly: true + mountPath: /var/serving-cert + - name: nginx-conf + readOnly: true + mountPath: /etc/nginx/nginx.conf + subPath: nginx.conf + volumes: + - name: plugin-serving-cert + secret: + secretName: plugin-serving-cert + defaultMode: 420 + - name: nginx-conf + configMap: + name: nginx-conf + defaultMode: 420 + restartPolicy: Always + dnsPolicy: ClusterFirst + strategy: + type: RollingUpdate + rollingUpdate: + maxUnavailable: 25% + maxSurge: 25% +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: nginx-conf + namespace: kuadrant-console + labels: + app: kuadrant-console + app.kubernetes.io/component: kuadrant-console + app.kubernetes.io/instance: kuadrant-console + app.kubernetes.io/name: kuadrant-console + app.kubernetes.io/part-of: kuadrant-console +data: + nginx.conf: | + error_log /dev/stdout; + events {} + http { + access_log /dev/stdout; + include /etc/nginx/mime.types; + default_type application/octet-stream; + keepalive_timeout 65; + + server { + listen 9443 ssl; + listen [::]:9443 ssl; + ssl_certificate /var/serving-cert/tls.crt; + ssl_certificate_key /var/serving-cert/tls.key; + + add_header oauth_token "$http_Authorization"; + + location / { + root /usr/share/nginx/html; + } + } + } +--- +apiVersion: v1 +kind: Service +metadata: + annotations: + service.alpha.openshift.io/serving-cert-secret-name: plugin-serving-cert + name: kuadrant-console + namespace: kuadrant-console + labels: + app: kuadrant-console + app.kubernetes.io/component: kuadrant-console + app.kubernetes.io/instance: kuadrant-console + app.kubernetes.io/name: kuadrant-console + app.kubernetes.io/part-of: kuadrant-console +spec: + ports: + - name: 9443-tcp + protocol: TCP + port: 9443 + targetPort: 9443 + selector: + app: kuadrant-console + type: ClusterIP + sessionAffinity: None +--- +apiVersion: console.openshift.io/v1alpha1 +kind: ConsolePlugin +metadata: + name: kuadrant-console +spec: + displayName: 'Kuadrant Console Plugin' + service: + name: kuadrant-console + namespace: kuadrant-console + port: 9443 + basePath: '/' diff --git a/bundle/manifests/kuadrant-operator.clusterserviceversion.yaml b/bundle/manifests/kuadrant-operator.clusterserviceversion.yaml index 81d5ead52..9b7e89985 100644 --- a/bundle/manifests/kuadrant-operator.clusterserviceversion.yaml +++ b/bundle/manifests/kuadrant-operator.clusterserviceversion.yaml @@ -106,7 +106,7 @@ metadata: capabilities: Basic Install categories: Integration & Delivery containerImage: quay.io/kuadrant/kuadrant-operator:latest - createdAt: "2024-09-17T13:54:51Z" + createdAt: "2024-09-25T08:58:08Z" operators.operatorframework.io/builder: operator-sdk-v1.32.0 operators.operatorframework.io/project_layout: go.kubebuilder.io/v3 repository: https://github.com/Kuadrant/kuadrant-operator @@ -235,6 +235,18 @@ spec: - get - list - watch + - apiGroups: + - console.openshift.io + resources: + - consoleplugins + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - coordination.k8s.io resources: diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 602db0f6d..2b2b0c1ef 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -83,6 +83,18 @@ rules: - get - list - watch +- apiGroups: + - console.openshift.io + resources: + - consoleplugins + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - coordination.k8s.io resources: diff --git a/controllers/kuadrant_controller.go b/controllers/kuadrant_controller.go index ca1c9059b..a6ae15a74 100644 --- a/controllers/kuadrant_controller.go +++ b/controllers/kuadrant_controller.go @@ -19,6 +19,11 @@ package controllers import ( "context" "encoding/json" + "errors" + "io" + "os" + "path/filepath" + "strings" "github.com/go-logr/logr" authorinov1beta1 "github.com/kuadrant/authorino-operator/api/v1beta1" @@ -28,12 +33,16 @@ import ( apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/util/yaml" "k8s.io/utils/env" istiov1alpha1 "maistra.io/istio-operator/api/v1alpha1" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + consolev1 "github.com/openshift/api/console/v1" + maistrav1 "github.com/kuadrant/kuadrant-operator/api/external/maistra/v1" maistrav2 "github.com/kuadrant/kuadrant-operator/api/external/maistra/v2" kuadrantv1beta1 "github.com/kuadrant/kuadrant-operator/api/v1beta1" @@ -95,6 +104,9 @@ type KuadrantReconciler struct { //+kubebuilder:rbac:groups=gateway.networking.k8s.io,resources=httproutes,verbs=get;list;watch;update;patch //+kubebuilder:rbac:groups=gateway.networking.k8s.io,resources=httproutes/status,verbs=get;update;patch +// ConsolePlugin perms +//+kubebuilder:rbac:groups=console.openshift.io,resources=consoleplugins,verbs=get;list;watch;create;update;patch;delete + // Reconcile is part of the main kubernetes reconciliation loop which aims to // move the current state of the cluster closer to the desired state. // For more details, check Reconcile and its Result here: @@ -167,6 +179,17 @@ func (r *KuadrantReconciler) Reconcile(eventCtx context.Context, req ctrl.Reques return statusResult, nil } + if r.isConsolePluginAvailable() { + logger.Info("ConsolePlugin API is available, proceeding to apply manifest") + if err := r.applyManifest(ctx, "bundle/manifests/kuadrant-console-plugin.yaml", r.Client()); err != nil { + logger.Error(err, "failed to apply manifest") + return ctrl.Result{}, err + } + logger.Info("Manifest applied successfully") + } else { + logger.Info("ConsolePlugin API is not available, skipping creation") + } + logger.Info("successfully reconciled") return ctrl.Result{}, nil } @@ -479,3 +502,58 @@ func (r *KuadrantReconciler) SetupWithManager(mgr ctrl.Manager) error { Owns(&authorinov1beta1.Authorino{}). Complete(r) } + +func (r *KuadrantReconciler) isConsolePluginAvailable() bool { + gvk := consolev1.GroupVersion.WithKind("ConsolePlugin") + mapping, err := r.RestMapper.RESTMapping(gvk.GroupKind(), gvk.Version) + if err != nil || mapping == nil { + log.Log.Info("ConsolePlugin API is not available") + return false + } + log.Log.Info("ConsolePlugin API is available") + return true +} + +func (r *KuadrantReconciler) applyManifest(ctx context.Context, filePath string, k8sClient client.Client) error { + logger := logr.FromContextOrDiscard(ctx) + + yamlFile, err := os.ReadFile(filepath.Clean(filePath)) + if err != nil { + logger.Error(err, "Failed to read YAML file", "file", filePath) + return err + } + + decoder := yaml.NewYAMLOrJSONDecoder(strings.NewReader(string(yamlFile)), 4096) + + for { + obj := &unstructured.Unstructured{} + + if err := decoder.Decode(obj); err != nil { + if errors.Is(err, io.EOF) { + break + } + logger.Error(err, "Failed to decode YAML resource") + return err + } + + logger.Info("Applying resource", "kind", obj.GetKind(), "name", obj.GetName()) + if err := r.applyResource(ctx, obj, k8sClient); err != nil { + logger.Error(err, "Failed to apply resource", "kind", obj.GetKind(), "name", obj.GetName()) + return err + } + } + + return nil +} + +func (r *KuadrantReconciler) applyResource(ctx context.Context, obj *unstructured.Unstructured, k8sClient client.Client) error { + existing := obj.DeepCopy() + err := k8sClient.Get(ctx, client.ObjectKey{Name: obj.GetName(), Namespace: obj.GetNamespace()}, existing) + if apierrors.IsNotFound(err) { + return k8sClient.Create(ctx, obj) + } else if err != nil { + return err + } + obj.SetResourceVersion(existing.GetResourceVersion()) + return k8sClient.Update(ctx, obj) +} diff --git a/go.mod b/go.mod index 41a95aef0..3fdbaf828 100644 --- a/go.mod +++ b/go.mod @@ -123,6 +123,7 @@ require ( github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.0 // indirect + github.com/openshift/api v0.0.0-20240924220842-3c700b6cb32b // indirect github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/pkg/errors v0.9.1 // indirect github.com/prometheus/client_model v0.6.1 // indirect diff --git a/go.sum b/go.sum index 9308c8244..237902bcb 100644 --- a/go.sum +++ b/go.sum @@ -337,6 +337,8 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8 github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= +github.com/openshift/api v0.0.0-20240924220842-3c700b6cb32b h1:3CDA+4Ed9JWKNs3czWoq1DcI2rjWMShIpoIiPFey11o= +github.com/openshift/api v0.0.0-20240924220842-3c700b6cb32b/go.mod h1:OOh6Qopf21pSzqNVCB5gomomBXb8o5sGKZxG2KNpaXM= github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI=