Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ConsolePlugin resource #879

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
133 changes: 133 additions & 0 deletions bundle/manifests/kuadrant-console-plugin.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
apiVersion: v1
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The source of truth of the bundle content (99% of it actually), is hosted in ${PROJECT}/config folder. It should be there

kind: Namespace
metadata:
name: kuadrant-console
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: kuadrant-console
namespace: kuadrant-console
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this namespace is quite arbitrary. I would try to make it configurable at operator build time (or runtime.. let's discuss about this). One candidate for the default value would be the namespace where the operator is installed. But it is ok as well to be in 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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to be parametrized as a variable at operator build time, same like Wasm-shim image. Kuadrant console plugin is a new dep for the kuadrant operator.

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: '/'
14 changes: 13 additions & 1 deletion bundle/manifests/kuadrant-operator.clusterserviceversion.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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:
Expand Down
12 changes: 12 additions & 0 deletions config/rbac/role.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
78 changes: 78 additions & 0 deletions controllers/kuadrant_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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"
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -167,6 +179,17 @@ func (r *KuadrantReconciler) Reconcile(eventCtx context.Context, req ctrl.Reques
return statusResult, nil
}

if r.isConsolePluginAvailable() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implementing this on the kuadrant controller makes sense as long as the deployment has something to do with the Kuadrant CR. If not, I would suggest a new controller dedicated to this.

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 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am afraid, this is not gonna work

I see four approaches:

  • The kuadrant operator binary has embedded the manifests. (we use this approach for the grafana dashboards on the 3scale operator)
  • The kuadrant operator reads from a configmap
  • The kuadrant operator has the manifests available in the docker image, so the container can read the file from local filesystem as you try there.
  • The kuadrant operator creates deployment, service, nginx configmap... So all those resources are encoded in golang code. Pretty much like the limitador operator does with limitador.

Each approach has pro's & con's. Maybe a combination of some of them. IDK. I would go first with the fourth approach. But let's discuss.

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
}
Expand Down Expand Up @@ -479,3 +502,58 @@ func (r *KuadrantReconciler) SetupWithManager(mgr ctrl.Manager) error {
Owns(&authorinov1beta1.Authorino{}).
Complete(r)
}

func (r *KuadrantReconciler) isConsolePluginAvailable() bool {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is already implemented in kuadrantgatewayapi.IsCRDInstalled
I patched it and the outcome is:

diff --git a/controllers/kuadrant_controller.go b/controllers/kuadrant_controller.go
index b7c89dd..7d7589f 100644
--- a/controllers/kuadrant_controller.go
+++ b/controllers/kuadrant_controller.go
@@ -177,7 +177,13 @@ func (r *KuadrantReconciler) Reconcile(eventCtx context.Context, req ctrl.Reques
                return statusResult, nil
        }
 
-       if r.isConsolePluginAvailable() {
+       consolePluginGVK := consolev1.GroupVersion.WithKind("ConsolePlugin")
+       isConsolePluginAvailable, err := kuadrantgatewayapi.IsCRDInstalled(r.RestMapper, consolePluginGVK.Group, consolePluginGVK.Kind, consolePluginGVK.Version)
+       if err != nil {
+               return ctrl.Result{}, err
+       }
+
+       if 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")
@@ -501,17 +507,6 @@ func (r *KuadrantReconciler) SetupWithManager(mgr ctrl.Manager) error {
                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)

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)
}
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -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=
Expand Down
Loading