From 3aadbb4f768871124b8b91ac616c7bc6a42552b8 Mon Sep 17 00:00:00 2001 From: kaczyns Date: Tue, 16 Jun 2020 16:24:47 -0400 Subject: [PATCH] rename serving->devfile-registry, CRD list/map changes --- Makefile | 6 +- build/Dockerfile | 2 +- .../main.go | 464 +++++++++--------- .../0.1/devfile-registry-controller.yaml} | 54 +- config/samples/full.yaml | 4 +- config/versions.yaml | 8 +- deploy/crds/kabanero.io_kabaneros_crd.yaml | 62 ++- deploy/crds/kabanero.io_stacks_crd.yaml | 36 +- pkg/apis/kabanero/v1alpha1/kabanero_types.go | 3 +- pkg/apis/kabanero/v1alpha2/kabanero_types.go | 33 +- pkg/apis/kabanero/v1alpha2/stack_types.go | 22 +- .../v1alpha2/zz_generated.deepcopy.go | 34 +- .../controller.go | 2 +- .../devfile_registry_controller.go} | 338 ++++++------- .../{serving => devfileregistry}/index.go | 2 +- .../{serving => devfileregistry}/register.go | 2 +- .../{serving.go => devfile-registry.go} | 279 ++++++----- .../kabaneroplatform_controller.go | 6 +- 18 files changed, 727 insertions(+), 630 deletions(-) rename cmd/{serving => devfile-registry-controller}/main.go (95%) rename config/orchestrations/{serving/0.1/serving.yaml => devfile-registry-controller/0.1/devfile-registry-controller.yaml} (66%) rename pkg/controller/{serving => devfileregistry}/controller.go (94%) rename pkg/controller/{serving/serving_controller.go => devfileregistry/devfile_registry_controller.go} (96%) rename pkg/controller/{serving => devfileregistry}/index.go (98%) rename pkg/controller/{serving => devfileregistry}/register.go (87%) rename pkg/controller/kabaneroplatform/{serving.go => devfile-registry.go} (55%) diff --git a/Makefile b/Makefile index 52fbc5fe..156430fe 100755 --- a/Makefile +++ b/Makefile @@ -72,7 +72,7 @@ build: generate GO111MODULE=on go install ./cmd/manager GO111MODULE=on go install ./cmd/manager/stack GO111MODULE=on go install ./cmd/admission-webhook - GO111MODULE=on go install ./cmd/serving + GO111MODULE=on go install ./cmd/devfile-registry-controller build-image: generate # These commands were taken from operator-sdk 0.8.1. The sdk did not let us @@ -82,7 +82,7 @@ build-image: generate GO111MODULE=on CGO_ENABLED=0 GOOS=linux GOARCH=$(ARCH) go build -o build/_output/bin/kabanero-operator -gcflags "all=-trimpath=$(GOPATH)" -asmflags "all=-trimpath=$(GOPATH)" -ldflags "-X main.GitTag=$(TRAVIS_TAG) -X main.GitCommit=$(TRAVIS_COMMIT) -X main.GitRepoSlug=$(TRAVIS_REPO_SLUG) -X main.BuildDate=`date -u +%Y%m%d.%H%M%S`" github.com/kabanero-io/kabanero-operator/cmd/manager GO111MODULE=on CGO_ENABLED=0 GOOS=linux GOARCH=$(ARCH) go build -o build/_output/bin/kabanero-operator-stack-controller -gcflags "all=-trimpath=$(GOPATH)" -asmflags "all=-trimpath=$(GOPATH)" -ldflags "-X main.GitTag=$(TRAVIS_TAG) -X main.GitCommit=$(TRAVIS_COMMIT) -X main.GitRepoSlug=$(TRAVIS_REPO_SLUG) -X main.BuildDate=`date -u +%Y%m%d.%H%M%S`" github.com/kabanero-io/kabanero-operator/cmd/manager/stack GO111MODULE=on CGO_ENABLED=0 GOOS=linux GOARCH=$(ARCH) go build -o build/_output/bin/admission-webhook -gcflags "all=-trimpath=$(GOPATH)" -asmflags "all=-trimpath=$(GOPATH)" -ldflags "-X main.GitTag=$(TRAVIS_TAG) -X main.GitCommit=$(TRAVIS_COMMIT) -X main.GitRepoSlug=$(TRAVIS_REPO_SLUG) -X main.BuildDate=`date -u +%Y%m%d.%H%M%S`" github.com/kabanero-io/kabanero-operator/cmd/admission-webhook - GO111MODULE=on CGO_ENABLED=0 GOOS=linux GOARCH=$(ARCH) go build -o build/_output/bin/serving -gcflags "all=-trimpath=$(GOPATH)" -asmflags "all=-trimpath=$(GOPATH)" -ldflags "-X main.GitTag=$(TRAVIS_TAG) -X main.GitCommit=$(TRAVIS_COMMIT) -X main.GitRepoSlug=$(TRAVIS_REPO_SLUG) -X main.BuildDate=`date -u +%Y%m%d.%H%M%S`" github.com/kabanero-io/kabanero-operator/cmd/serving + GO111MODULE=on CGO_ENABLED=0 GOOS=linux GOARCH=$(ARCH) go build -o build/_output/bin/devfile-registry-controller -gcflags "all=-trimpath=$(GOPATH)" -asmflags "all=-trimpath=$(GOPATH)" -ldflags "-X main.GitTag=$(TRAVIS_TAG) -X main.GitCommit=$(TRAVIS_COMMIT) -X main.GitRepoSlug=$(TRAVIS_REPO_SLUG) -X main.BuildDate=`date -u +%Y%m%d.%H%M%S`" github.com/kabanero-io/kabanero-operator/cmd/devfile-registry-controller docker build -f build/Dockerfile -t $(IMAGE) . @@ -197,7 +197,7 @@ ifndef GITHUB_TOKEN endif mkdir -p build/bin curl -L https://github.com/mitchellh/golicense/releases/download/v0.2.0/golicense_0.2.0_$(detected_OS)_x86_64.tar.gz | tar -C build/bin -xzf - golicense - build/bin/golicense -plain ./license-rules.json build/_output/bin/admission-webhook build/_output/bin/kabanero-operator build/_output/bin/kabanero-operator-stack-controller build/_output/bin/serving | sort > 3RD_PARTY || true + build/bin/golicense -plain ./license-rules.json build/_output/bin/admission-webhook build/_output/bin/kabanero-operator build/_output/bin/kabanero-operator-stack-controller build/_output/bin/devfile-registry-controller | sort > 3RD_PARTY || true rm build/bin/golicense # Integration Tests diff --git a/build/Dockerfile b/build/Dockerfile index ada9dd91..66d9f329 100644 --- a/build/Dockerfile +++ b/build/Dockerfile @@ -17,7 +17,7 @@ ENV OPERATOR=/usr/local/bin/kabanero-operator \ COPY build/_output/bin/kabanero-operator ${OPERATOR} COPY build/_output/bin/kabanero-operator-stack-controller /usr/local/bin/kabanero-operator-stack-controller COPY build/_output/bin/admission-webhook /usr/local/bin/admission-webhook -COPY build/_output/bin/serving /usr/local/bin/serving +COPY build/_output/bin/devfile-registry-controller /usr/local/bin/devfile-registry-controller RUN mkdir /devfiles && chmod +777 /devfiles diff --git a/cmd/serving/main.go b/cmd/devfile-registry-controller/main.go similarity index 95% rename from cmd/serving/main.go rename to cmd/devfile-registry-controller/main.go index e7cdffb0..86b4086d 100644 --- a/cmd/serving/main.go +++ b/cmd/devfile-registry-controller/main.go @@ -1,232 +1,232 @@ -package main - -import ( - "context" - "errors" - "flag" - "fmt" - "os" - // "runtime" - "strings" - "net/http" - - // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.) - _ "k8s.io/client-go/plugin/pkg/client/auth" - "k8s.io/client-go/rest" - - "github.com/kabanero-io/kabanero-operator/pkg/apis" - "github.com/kabanero-io/kabanero-operator/pkg/controller/serving" - - "github.com/operator-framework/operator-sdk/pkg/k8sutil" - kubemetrics "github.com/operator-framework/operator-sdk/pkg/kube-metrics" - "github.com/operator-framework/operator-sdk/pkg/leader" - "github.com/operator-framework/operator-sdk/pkg/log/zap" - "github.com/operator-framework/operator-sdk/pkg/metrics" - // sdkVersion "github.com/operator-framework/operator-sdk/version" - "github.com/spf13/pflag" - v1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/util/intstr" - "sigs.k8s.io/controller-runtime/pkg/cache" - "sigs.k8s.io/controller-runtime/pkg/client/config" - logf "sigs.k8s.io/controller-runtime/pkg/log" - "sigs.k8s.io/controller-runtime/pkg/manager" - "sigs.k8s.io/controller-runtime/pkg/manager/signals" -) - -// Change below variables to serve metrics on different host or port. -var ( - metricsHost = "0.0.0.0" - metricsPort int32 = 8383 - operatorMetricsPort int32 = 8686 -) -var log = logf.Log.WithName("cmd") - -func main() { - // Add the zap logger flag set to the CLI. The flag set must - // be added before calling pflag.Parse(). - pflag.CommandLine.AddFlagSet(zap.FlagSet()) - - // Add flags registered by imported packages (e.g. glog and - // controller-runtime) - pflag.CommandLine.AddGoFlagSet(flag.CommandLine) - - pflag.Parse() - - // Use a zap logr.Logger implementation. If none of the zap - // flags are configured (or if the zap flag set is not being - // used), this defaults to a production zap logger. - // - // The logger instantiated here can be changed to any logger - // implementing the logr.Logger interface. This logger will - // be propagated through the whole operator, generating - // uniform and structured logs. - logf.SetLogger(zap.Logger()) - - namespace, err := k8sutil.GetWatchNamespace() - if err != nil { - log.Error(err, "Failed to get watch namespace") - os.Exit(1) - } - - // Get a config to talk to the apiserver - cfg, err := config.GetConfig() - if err != nil { - log.Error(err, "") - os.Exit(1) - } - - ctx := context.TODO() - // Become the leader before proceeding - err = leader.Become(ctx, "serving-lock") - if err != nil { - log.Error(err, "") - os.Exit(1) - } - - // Set default manager options - options := manager.Options{ - Namespace: namespace, - MetricsBindAddress: fmt.Sprintf("%s:%d", metricsHost, metricsPort), - } - - // Add support for MultiNamespace set in WATCH_NAMESPACE (e.g ns1,ns2) - // Note that this is not intended to be used for excluding namespaces, this is better done via a Predicate - // Also note that you may face performance issues when using this with a high number of namespaces. - // More Info: https://godoc.org/github.com/kubernetes-sigs/controller-runtime/pkg/cache#MultiNamespacedCacheBuilder - if strings.Contains(namespace, ",") { - options.Namespace = "" - options.NewCache = cache.MultiNamespacedCacheBuilder(strings.Split(namespace, ",")) - } - - // Create a new manager to provide shared dependencies and start components - mgr, err := manager.New(cfg, options) - if err != nil { - log.Error(err, "") - os.Exit(1) - } - - log.Info("Registering Components.") - - // Setup Scheme for all resources - if err := apis.AddToScheme(mgr.GetScheme()); err != nil { - log.Error(err, "") - os.Exit(1) - } - - // Setup all Controllers - if err := serving.AddToManager(mgr); err != nil { - log.Error(err, "") - os.Exit(1) - } - - // Add the Metrics Service - addMetrics(ctx, cfg) - - //Start HTTTPS & Cmd - errs := Run(mgr) - - // Run until channel receives error - select { - case err := <-errs: - log.Error(err, "Manager exited non-zero") - os.Exit(1) - } - -} - -func Run(mgr manager.Manager) chan error { - - errs := make(chan error) - - // Start serving devfiles index - go func() { - fs := http.FileServer(http.Dir("/devfiles")) - http.Handle("/", fs) - log.Info("Starting Devfile registry on port :8443...") - if err := http.ListenAndServeTLS(":8443","/tmp/serving-certs/tls.crt","/tmp/serving-certs/tls.key",nil); err != nil { - errs <- err - } - }() - - // Start the Cmd - go func() { - log.Info("Starting the Cmd.") - if err := mgr.Start(signals.SetupSignalHandler()); err != nil { - errs <- err - } - }() - - return errs -} - - - -// addMetrics will create the Services and Service Monitors to allow the operator export the metrics by using -// the Prometheus operator -func addMetrics(ctx context.Context, cfg *rest.Config) { - // Get the namespace the operator is currently deployed in. - operatorNs, err := k8sutil.GetOperatorNamespace() - if err != nil { - if errors.Is(err, k8sutil.ErrRunLocal) { - log.Info("Skipping CR metrics server creation; not running in a cluster.") - return - } - } - - if err := serveCRMetrics(cfg, operatorNs); err != nil { - log.Info("Could not generate and serve custom resource metrics", "error", err.Error()) - } - - // Add to the below struct any other metrics ports you want to expose. - servicePorts := []v1.ServicePort{ - {Port: metricsPort, Name: metrics.OperatorPortName, Protocol: v1.ProtocolTCP, TargetPort: intstr.IntOrString{Type: intstr.Int, IntVal: metricsPort}}, - {Port: operatorMetricsPort, Name: metrics.CRPortName, Protocol: v1.ProtocolTCP, TargetPort: intstr.IntOrString{Type: intstr.Int, IntVal: operatorMetricsPort}}, - } - - // Create Service object to expose the metrics port(s). - service, err := metrics.CreateMetricsService(ctx, cfg, servicePorts) - if err != nil { - log.Info("Could not create metrics Service", "error", err.Error()) - } - - // CreateServiceMonitors will automatically create the prometheus-operator ServiceMonitor resources - // necessary to configure Prometheus to scrape metrics from this operator. - services := []*v1.Service{service} - - // The ServiceMonitor is created in the same namespace where the operator is deployed - _, err = metrics.CreateServiceMonitors(cfg, operatorNs, services) - if err != nil { - log.Info("Could not create ServiceMonitor object", "error", err.Error()) - // If this operator is deployed to a cluster without the prometheus-operator running, it will return - // ErrServiceMonitorNotPresent, which can be used to safely skip ServiceMonitor creation. - if err == metrics.ErrServiceMonitorNotPresent { - log.Info("Install prometheus-operator in your cluster to create ServiceMonitor objects", "error", err.Error()) - } - } -} - -// serveCRMetrics gets the Operator/CustomResource GVKs and generates metrics based on those types. -// It serves those metrics on "http://metricsHost:operatorMetricsPort". -func serveCRMetrics(cfg *rest.Config, operatorNs string) error { - // The function below returns a list of filtered operator/CR specific GVKs. For more control, override the GVK list below - // with your own custom logic. Note that if you are adding third party API schemas, probably you will need to - // customize this implementation to avoid permissions issues. - filteredGVK, err := k8sutil.GetGVKsFromAddToScheme(apis.AddToScheme) - if err != nil { - return err - } - - // The metrics will be generated from the namespaces which are returned here. - // NOTE that passing nil or an empty list of namespaces in GenerateAndServeCRMetrics will result in an error. - ns, err := kubemetrics.GetNamespacesForMetrics(operatorNs) - if err != nil { - return err - } - - // Generate and serve custom resource specific metrics. - err = kubemetrics.GenerateAndServeCRMetrics(cfg, ns, filteredGVK, metricsHost, operatorMetricsPort) - if err != nil { - return err - } - return nil -} +package main + +import ( + "context" + "errors" + "flag" + "fmt" + "os" + // "runtime" + "strings" + "net/http" + + // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.) + _ "k8s.io/client-go/plugin/pkg/client/auth" + "k8s.io/client-go/rest" + + "github.com/kabanero-io/kabanero-operator/pkg/apis" + "github.com/kabanero-io/kabanero-operator/pkg/controller/devfileregistry" + + "github.com/operator-framework/operator-sdk/pkg/k8sutil" + kubemetrics "github.com/operator-framework/operator-sdk/pkg/kube-metrics" + "github.com/operator-framework/operator-sdk/pkg/leader" + "github.com/operator-framework/operator-sdk/pkg/log/zap" + "github.com/operator-framework/operator-sdk/pkg/metrics" + // sdkVersion "github.com/operator-framework/operator-sdk/version" + "github.com/spf13/pflag" + v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/util/intstr" + "sigs.k8s.io/controller-runtime/pkg/cache" + "sigs.k8s.io/controller-runtime/pkg/client/config" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/manager" + "sigs.k8s.io/controller-runtime/pkg/manager/signals" +) + +// Change below variables to serve metrics on different host or port. +var ( + metricsHost = "0.0.0.0" + metricsPort int32 = 8383 + operatorMetricsPort int32 = 8686 +) +var log = logf.Log.WithName("cmd") + +func main() { + // Add the zap logger flag set to the CLI. The flag set must + // be added before calling pflag.Parse(). + pflag.CommandLine.AddFlagSet(zap.FlagSet()) + + // Add flags registered by imported packages (e.g. glog and + // controller-runtime) + pflag.CommandLine.AddGoFlagSet(flag.CommandLine) + + pflag.Parse() + + // Use a zap logr.Logger implementation. If none of the zap + // flags are configured (or if the zap flag set is not being + // used), this defaults to a production zap logger. + // + // The logger instantiated here can be changed to any logger + // implementing the logr.Logger interface. This logger will + // be propagated through the whole operator, generating + // uniform and structured logs. + logf.SetLogger(zap.Logger()) + + namespace, err := k8sutil.GetWatchNamespace() + if err != nil { + log.Error(err, "Failed to get watch namespace") + os.Exit(1) + } + + // Get a config to talk to the apiserver + cfg, err := config.GetConfig() + if err != nil { + log.Error(err, "") + os.Exit(1) + } + + ctx := context.TODO() + // Become the leader before proceeding + err = leader.Become(ctx, "devfile-registry-lock") + if err != nil { + log.Error(err, "") + os.Exit(1) + } + + // Set default manager options + options := manager.Options{ + Namespace: namespace, + MetricsBindAddress: fmt.Sprintf("%s:%d", metricsHost, metricsPort), + } + + // Add support for MultiNamespace set in WATCH_NAMESPACE (e.g ns1,ns2) + // Note that this is not intended to be used for excluding namespaces, this is better done via a Predicate + // Also note that you may face performance issues when using this with a high number of namespaces. + // More Info: https://godoc.org/github.com/kubernetes-sigs/controller-runtime/pkg/cache#MultiNamespacedCacheBuilder + if strings.Contains(namespace, ",") { + options.Namespace = "" + options.NewCache = cache.MultiNamespacedCacheBuilder(strings.Split(namespace, ",")) + } + + // Create a new manager to provide shared dependencies and start components + mgr, err := manager.New(cfg, options) + if err != nil { + log.Error(err, "") + os.Exit(1) + } + + log.Info("Registering Components.") + + // Setup Scheme for all resources + if err := apis.AddToScheme(mgr.GetScheme()); err != nil { + log.Error(err, "") + os.Exit(1) + } + + // Setup all Controllers + if err := devfileregistry.AddToManager(mgr); err != nil { + log.Error(err, "") + os.Exit(1) + } + + // Add the Metrics Service + addMetrics(ctx, cfg) + + //Start HTTTPS & Cmd + errs := Run(mgr) + + // Run until channel receives error + select { + case err := <-errs: + log.Error(err, "Manager exited non-zero") + os.Exit(1) + } + +} + +func Run(mgr manager.Manager) chan error { + + errs := make(chan error) + + // Start serving devfiles index + go func() { + fs := http.FileServer(http.Dir("/devfiles")) + http.Handle("/", fs) + log.Info("Starting Devfile registry on port :8443...") + if err := http.ListenAndServeTLS(":8443","/tmp/serving-certs/tls.crt","/tmp/serving-certs/tls.key",nil); err != nil { + errs <- err + } + }() + + // Start the Cmd + go func() { + log.Info("Starting the Cmd.") + if err := mgr.Start(signals.SetupSignalHandler()); err != nil { + errs <- err + } + }() + + return errs +} + + + +// addMetrics will create the Services and Service Monitors to allow the operator export the metrics by using +// the Prometheus operator +func addMetrics(ctx context.Context, cfg *rest.Config) { + // Get the namespace the operator is currently deployed in. + operatorNs, err := k8sutil.GetOperatorNamespace() + if err != nil { + if errors.Is(err, k8sutil.ErrRunLocal) { + log.Info("Skipping CR metrics server creation; not running in a cluster.") + return + } + } + + if err := serveCRMetrics(cfg, operatorNs); err != nil { + log.Info("Could not generate and serve custom resource metrics", "error", err.Error()) + } + + // Add to the below struct any other metrics ports you want to expose. + servicePorts := []v1.ServicePort{ + {Port: metricsPort, Name: metrics.OperatorPortName, Protocol: v1.ProtocolTCP, TargetPort: intstr.IntOrString{Type: intstr.Int, IntVal: metricsPort}}, + {Port: operatorMetricsPort, Name: metrics.CRPortName, Protocol: v1.ProtocolTCP, TargetPort: intstr.IntOrString{Type: intstr.Int, IntVal: operatorMetricsPort}}, + } + + // Create Service object to expose the metrics port(s). + service, err := metrics.CreateMetricsService(ctx, cfg, servicePorts) + if err != nil { + log.Info("Could not create metrics Service", "error", err.Error()) + } + + // CreateServiceMonitors will automatically create the prometheus-operator ServiceMonitor resources + // necessary to configure Prometheus to scrape metrics from this operator. + services := []*v1.Service{service} + + // The ServiceMonitor is created in the same namespace where the operator is deployed + _, err = metrics.CreateServiceMonitors(cfg, operatorNs, services) + if err != nil { + log.Info("Could not create ServiceMonitor object", "error", err.Error()) + // If this operator is deployed to a cluster without the prometheus-operator running, it will return + // ErrServiceMonitorNotPresent, which can be used to safely skip ServiceMonitor creation. + if err == metrics.ErrServiceMonitorNotPresent { + log.Info("Install prometheus-operator in your cluster to create ServiceMonitor objects", "error", err.Error()) + } + } +} + +// serveCRMetrics gets the Operator/CustomResource GVKs and generates metrics based on those types. +// It serves those metrics on "http://metricsHost:operatorMetricsPort". +func serveCRMetrics(cfg *rest.Config, operatorNs string) error { + // The function below returns a list of filtered operator/CR specific GVKs. For more control, override the GVK list below + // with your own custom logic. Note that if you are adding third party API schemas, probably you will need to + // customize this implementation to avoid permissions issues. + filteredGVK, err := k8sutil.GetGVKsFromAddToScheme(apis.AddToScheme) + if err != nil { + return err + } + + // The metrics will be generated from the namespaces which are returned here. + // NOTE that passing nil or an empty list of namespaces in GenerateAndServeCRMetrics will result in an error. + ns, err := kubemetrics.GetNamespacesForMetrics(operatorNs) + if err != nil { + return err + } + + // Generate and serve custom resource specific metrics. + err = kubemetrics.GenerateAndServeCRMetrics(cfg, ns, filteredGVK, metricsHost, operatorMetricsPort) + if err != nil { + return err + } + return nil +} diff --git a/config/orchestrations/serving/0.1/serving.yaml b/config/orchestrations/devfile-registry-controller/0.1/devfile-registry-controller.yaml similarity index 66% rename from config/orchestrations/serving/0.1/serving.yaml rename to config/orchestrations/devfile-registry-controller/0.1/devfile-registry-controller.yaml index ddba8738..31bb0ad6 100644 --- a/config/orchestrations/serving/0.1/serving.yaml +++ b/config/orchestrations/devfile-registry-controller/0.1/devfile-registry-controller.yaml @@ -1,19 +1,19 @@ apiVersion: v1 kind: Service metadata: - name: kabanero-operator-serving + name: kabanero-operator-devfile-registry annotations: - service.beta.openshift.io/serving-cert-secret-name: kabanero-operator-serving-cert + service.beta.openshift.io/serving-cert-secret-name: kabanero-operator-devfile-registry-cert labels: - app.kubernetes.io/name: kabanero-operator-serving + app.kubernetes.io/name: kabanero-operator-devfile-registry app.kubernetes.io/instance: {{ .instance }} app.kubernetes.io/version: {{ .version }} - app.kubernetes.io/component: serving + app.kubernetes.io/component: devfile-registry app.kubernetes.io/part-of: kabanero app.kubernetes.io/managed-by: kabanero-operator spec: selector: - name: kabanero-operator-serving + name: kabanero-operator-devfile-registry ports: - protocol: TCP port: 443 @@ -23,7 +23,7 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: creationTimestamp: null - name: kabanero-operator-serving + name: kabanero-operator-devfile-registry rules: - apiGroups: - "" @@ -91,28 +91,28 @@ rules: apiVersion: v1 kind: ServiceAccount metadata: - name: kabanero-operator-serving + name: kabanero-operator-devfile-registry --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: - name: kabanero-operator-serving + name: kabanero-operator-devfile-registry subjects: - kind: ServiceAccount - name: kabanero-operator-serving + name: kabanero-operator-devfile-registry roleRef: kind: Role - name: kabanero-operator-serving + name: kabanero-operator-devfile-registry apiGroup: rbac.authorization.k8s.io --- apiVersion: route.openshift.io/v1 kind: Route metadata: - name: kabanero-operator-serving + name: kabanero-operator-devfile-registry spec: to: kind: Service - name: kabanero-operator-serving + name: kabanero-operator-devfile-registry tls: termination: reencrypt insecureEdgeTerminationPolicy: Redirect @@ -120,38 +120,38 @@ spec: apiVersion: apps/v1 kind: Deployment metadata: - name: kabanero-operator-serving + name: kabanero-operator-devfile-registry labels: - name: kabanero-operator-serving - app.kubernetes.io/name: kabanero-operator-serving + name: kabanero-operator-devfile-registry + app.kubernetes.io/name: kabanero-operator-devfile-registry app.kubernetes.io/instance: {{ .instance }} app.kubernetes.io/version: {{ .version }} - app.kubernetes.io/component: serving + app.kubernetes.io/component: devfile-registry app.kubernetes.io/part-of: kabanero app.kubernetes.io/managed-by: kabanero-operator spec: replicas: 1 selector: matchLabels: - name: kabanero-operator-serving + name: kabanero-operator-devfile-registry template: metadata: labels: - name: kabanero-operator-serving - app.kubernetes.io/name: kabanero-operator-serving + name: kabanero-operator-devfile-registry + app.kubernetes.io/name: kabanero-operator-devfile-registry app.kubernetes.io/instance: {{ .instance }} app.kubernetes.io/version: {{ .version }} - app.kubernetes.io/component: serving + app.kubernetes.io/component: devfile-registry app.kubernetes.io/part-of: kabanero app.kubernetes.io/managed-by: kabanero-operator spec: - serviceAccount: kabanero-operator-serving + serviceAccount: kabanero-operator-devfile-registry containers: - - name: kabanero-operator-serving + - name: kabanero-operator-devfile-registry image: {{ .image }} imagePullPolicy: Always command: - - /usr/local/bin/serving + - /usr/local/bin/devfile-registry-controller env: - name: WATCH_NAMESPACE valueFrom: @@ -162,12 +162,12 @@ spec: fieldRef: fieldPath: metadata.name - name: OPERATOR_NAME - value: "kabanero-operator-serving" + value: "kabanero-operator-devfile-registry" volumeMounts: - mountPath: /tmp/serving-certs - name: kabanero-operator-serving-cert + name: kabanero-operator-devfile-registry-cert readOnly: true volumes: - - name: kabanero-operator-serving-cert + - name: kabanero-operator-devfile-registry-cert secret: - secretName: kabanero-operator-serving-cert + secretName: kabanero-operator-devfile-registry-cert diff --git a/config/samples/full.yaml b/config/samples/full.yaml index 3ecf9110..6fd9298f 100644 --- a/config/samples/full.yaml +++ b/config/samples/full.yaml @@ -67,9 +67,9 @@ spec: # Overrides the image uri image: kabanero/kabanero-operator:TRAVIS_TAG - serving: + devfileRegistry: # Overrides the setting for version on this component - version: "0.9.0" + version: "0.10.0" # Overrides the image as a separate repository or tag repository: kabanero/kabanero-operator diff --git a/config/versions.yaml b/config/versions.yaml index c46f0b88..cf8b8929 100644 --- a/config/versions.yaml +++ b/config/versions.yaml @@ -22,6 +22,7 @@ kabanero: admission-webhook: "0.10.0" sso: "7.3.2" codeready-workspaces: "0.10.0" + devfile-registry-controller: "0.10.0" - version: "0.9.1" related-versions: @@ -44,7 +45,6 @@ kabanero: admission-webhook: "0.9.0" sso: "7.3.2" codeready-workspaces: "0.9.0" - serving: "0.9.0" - version: "0.8.0" related-versions: @@ -304,9 +304,9 @@ related-software: - version: "7.3.2" orchestrations: "orchestrations/sso/0.1" - serving: - - version: "0.9.0" - orchestrations: "orchestrations/serving/0.1" + devfile-registry-controller: + - version: "0.10.0" + orchestrations: "orchestrations/devfile-registry-controller/0.1" identifiers: repository: "FROM_POD" tag: "FROM_POD" diff --git a/deploy/crds/kabanero.io_kabaneros_crd.yaml b/deploy/crds/kabanero.io_kabaneros_crd.yaml index 9c5796e5..bf34d8e0 100644 --- a/deploy/crds/kabanero.io_kabaneros_crd.yaml +++ b/deploy/crds/kabanero.io_kabaneros_crd.yaml @@ -135,7 +135,9 @@ spec: type: string type: object type: array - x-kubernetes-list-type: set + x-kubernetes-list-map-keys: + - url + x-kubernetes-list-type: map type: object events: properties: @@ -466,6 +468,17 @@ spec: version: type: string type: object + devfileRegistry: + properties: + image: + type: string + repository: + type: string + tag: + type: string + version: + type: string + type: object events: properties: enable: @@ -534,7 +547,10 @@ spec: type: string type: object type: array - x-kubernetes-list-type: set + x-kubernetes-list-map-keys: + - id + - sha256 + x-kubernetes-list-type: map type: object governancePolicy: description: GovernancePolicyConfig defines customization entries @@ -558,17 +574,6 @@ spec: version: type: string type: object - serving: - properties: - image: - type: string - repository: - type: string - tag: - type: string - version: - type: string - type: object sso: properties: adminSecretName: @@ -632,7 +637,10 @@ spec: type: string type: object type: array - x-kubernetes-list-type: set + x-kubernetes-list-map-keys: + - id + - sha256 + x-kubernetes-list-type: map repositories: items: description: RepositoryConfig defines customization entries @@ -703,10 +711,15 @@ spec: type: string type: object type: array - x-kubernetes-list-type: set + x-kubernetes-list-map-keys: + - id + - sha256 + x-kubernetes-list-type: map type: object type: array - x-kubernetes-list-type: set + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map skipRegistryCertVerification: type: boolean type: object @@ -752,7 +765,10 @@ spec: type: string type: object type: array - x-kubernetes-list-type: set + x-kubernetes-list-map-keys: + - id + - sha256 + x-kubernetes-list-type: map version: type: string type: object @@ -883,7 +899,13 @@ spec: type: string type: object type: array - x-kubernetes-list-type: set + x-kubernetes-list-map-keys: + - assetName + - namespace + - group + - version + - kind + x-kubernetes-list-type: map digest: type: string gitRelease: @@ -908,6 +930,10 @@ spec: type: string type: object type: array + x-kubernetes-list-map-keys: + - name + - digest + x-kubernetes-list-type: map ready: type: string type: object diff --git a/deploy/crds/kabanero.io_stacks_crd.yaml b/deploy/crds/kabanero.io_stacks_crd.yaml index 867df3e8..8b2dd02b 100644 --- a/deploy/crds/kabanero.io_stacks_crd.yaml +++ b/deploy/crds/kabanero.io_stacks_crd.yaml @@ -63,7 +63,10 @@ spec: type: string type: object type: array - x-kubernetes-list-type: set + x-kubernetes-list-map-keys: + - id + - image + x-kubernetes-list-type: map metafile: type: string pipelines: @@ -103,7 +106,10 @@ spec: type: string type: object type: array - x-kubernetes-list-type: set + x-kubernetes-list-map-keys: + - id + - sha256 + x-kubernetes-list-type: map skipCertVerification: type: boolean skipRegistryCertVerification: @@ -112,7 +118,9 @@ spec: type: string type: object type: array - x-kubernetes-list-type: set + x-kubernetes-list-map-keys: + - version + x-kubernetes-list-type: map type: object status: description: StackStatus defines the observed state of a stack @@ -146,7 +154,10 @@ spec: type: string type: object type: array - x-kubernetes-list-type: set + x-kubernetes-list-map-keys: + - id + - image + x-kubernetes-list-type: map location: type: string pipelines: @@ -177,7 +188,13 @@ spec: type: string type: object type: array - x-kubernetes-list-type: set + x-kubernetes-list-map-keys: + - assetName + - namespace + - group + - version + - kind + x-kubernetes-list-type: map digest: type: string gitRelease: @@ -202,7 +219,10 @@ spec: type: string type: object type: array - x-kubernetes-list-type: set + x-kubernetes-list-map-keys: + - name + - digest + x-kubernetes-list-type: map status: type: string statusMessage: @@ -211,7 +231,9 @@ spec: type: string type: object type: array - x-kubernetes-list-type: set + x-kubernetes-list-map-keys: + - version + x-kubernetes-list-type: map type: object type: object version: v1alpha2 diff --git a/pkg/apis/kabanero/v1alpha1/kabanero_types.go b/pkg/apis/kabanero/v1alpha1/kabanero_types.go index 583f75ab..f73ca5fd 100644 --- a/pkg/apis/kabanero/v1alpha1/kabanero_types.go +++ b/pkg/apis/kabanero/v1alpha1/kabanero_types.go @@ -43,7 +43,8 @@ type KabaneroSpec struct { // InstanceCollectionConfig defines the customization entries for a set of collections. type InstanceCollectionConfig struct { - // +listType=set + // +listType=map + // +listMapKey=url Repositories []RepositoryConfig `json:"repositories,omitempty"` } diff --git a/pkg/apis/kabanero/v1alpha2/kabanero_types.go b/pkg/apis/kabanero/v1alpha2/kabanero_types.go index c0fb11fc..226254b3 100644 --- a/pkg/apis/kabanero/v1alpha2/kabanero_types.go +++ b/pkg/apis/kabanero/v1alpha2/kabanero_types.go @@ -28,7 +28,9 @@ type KabaneroSpec struct { Stacks InstanceStackConfig `json:"stacks,omitempty"` - // +listType=set + // +listType=map + // +listMapKey=id + // +listMapKey=sha256 Triggers []TriggerSpec `json:"triggers,omitempty"` CliServices KabaneroCliServicesCustomizationSpec `json:"cliServices,omitempty"` @@ -45,7 +47,7 @@ type KabaneroSpec struct { AdmissionControllerWebhook AdmissionControllerWebhookCustomizationSpec `json:"admissionControllerWebhook,omitempty"` - Serving ServingSpec `json:"serving,omitempty"` + DevfileRegistry DevfileRegistrySpec `json:"devfileRegistry,omitempty"` Sso SsoCustomizationSpec `json:"sso,omitempty"` @@ -53,7 +55,9 @@ type KabaneroSpec struct { } type GitopsSpec struct { - // +listType=set + // +listType=map + // +listMapKey=id + // +listMapKey=sha256 Pipelines []PipelineSpec `json:"pipelines,omitempty"` } @@ -73,10 +77,13 @@ func (gs GitopsSpec) GetPipelines() []PipelineSpec { type InstanceStackConfig struct { SkipRegistryCertVerification bool `json:"skipRegistryCertVerification,omitempty"` - // +listType=set + // +listType=map + // +listMapKey=name Repositories []RepositoryConfig `json:"repositories,omitempty"` - // +listType=set + // +listType=map + // +listMapKey=id + // +listMapKey=sha256 Pipelines []PipelineSpec `json:"pipelines,omitempty"` } @@ -121,7 +128,9 @@ type GovernancePolicyConfig struct { // RepositoryConfig defines customization entries for a stack. type RepositoryConfig struct { Name string `json:"name,omitempty"` - // +listType=set + // +listType=map + // +listMapKey=id + // +listMapKey=sha256 Pipelines []PipelineSpec `json:"pipelines,omitempty"` Https HttpsProtocolFile `json:"https,omitempty"` GitRelease GitReleaseSpec `json:"gitRelease,omitempty"` @@ -234,7 +243,7 @@ type AdmissionControllerWebhookCustomizationSpec struct { Tag string `json:"tag,omitempty"` } -type ServingSpec struct { +type DevfileRegistrySpec struct { Version string `json:"version,omitempty"` Image string `json:"image,omitempty"` Repository string `json:"repository,omitempty"` @@ -311,12 +320,20 @@ type PipelineStatus struct { Url string `json:"url,omitempty"` GitRelease GitReleaseInfo `json:"gitRelease,omitempty"` Digest string `json:"digest,omitempty"` - // +listType=set + // +listType=map + // +listMapKey=assetName + // +listMapKey=namespace + // +listMapKey=group + // +listMapKey=version + // +listMapKey=kind ActiveAssets []RepositoryAssetStatus `json:"activeAssets,omitempty"` } // The status of the gitops pipelines type GitopsStatus struct { + // +listType=map + // +listMapKey=name + // +listMapKey=digest Pipelines []PipelineStatus `json:"pipelines,omitempty"` Ready string `json:"ready,omitempty"` Message string `json:"message,omitempty"` diff --git a/pkg/apis/kabanero/v1alpha2/stack_types.go b/pkg/apis/kabanero/v1alpha2/stack_types.go index 7b84edd5..cc28d28b 100644 --- a/pkg/apis/kabanero/v1alpha2/stack_types.go +++ b/pkg/apis/kabanero/v1alpha2/stack_types.go @@ -39,7 +39,8 @@ const ( // +k8s:openapi-gen=true type StackSpec struct { Name string `json:"name,omitempty"` - // +listType=set + // +listType=map + // +listMapKey=version Versions []StackVersion `json:"versions,omitempty"` } @@ -55,12 +56,16 @@ func (s StackSpec) GetVersions() []ComponentSpecVersion { type StackVersion struct { SkipRegistryCertVerification bool `json:"skipRegistryCertVerification,omitempty"` - // +listType=set + // +listType=map + // +listMapKey=id + // +listMapKey=sha256 Pipelines []PipelineSpec `json:"pipelines,omitempty"` Version string `json:"version,omitempty"` DesiredState string `json:"desiredState,omitempty"` SkipCertVerification bool `json:"skipCertVerification,omitempty"` - // +listType=set + // +listType=map + // +listMapKey=id + // +listMapKey=image Images []Image `json:"images,omitempty"` Devfile string `json:"devfile,omitempty"` Metafile string `json:"metafile,omitempty"` @@ -112,7 +117,8 @@ type RepositoryAssetStatus struct { // +k8s:openapi-gen=true type StackStatus struct { StatusMessage string `json:"statusMessage,omitempty"` - // +listType=set + // +listType=map + // +listMapKey=version Versions []StackVersionStatus `json:"versions,omitempty"` Summary string `json:"summary,omitempty"` } @@ -129,11 +135,15 @@ func (s StackStatus) GetVersions() []ComponentStatusVersion { type StackVersionStatus struct { Version string `json:"version,omitempty"` Location string `json:"location,omitempty"` - // +listType=set + // +listType=map + // +listMapKey=name + // +listMapKey=digest Pipelines []PipelineStatus `json:"pipelines,omitempty"` Status string `json:"status,omitempty"` StatusMessage string `json:"statusMessage,omitempty"` - // +listType=set + // +listType=map + // +listMapKey=id + // +listMapKey=image Images []ImageStatus `json:"images,omitempty"` } diff --git a/pkg/apis/kabanero/v1alpha2/zz_generated.deepcopy.go b/pkg/apis/kabanero/v1alpha2/zz_generated.deepcopy.go index e0f059d0..6428cfa9 100644 --- a/pkg/apis/kabanero/v1alpha2/zz_generated.deepcopy.go +++ b/pkg/apis/kabanero/v1alpha2/zz_generated.deepcopy.go @@ -246,6 +246,22 @@ func (in *CollectionControllerStatus) DeepCopy() *CollectionControllerStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DevfileRegistrySpec) DeepCopyInto(out *DevfileRegistrySpec) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DevfileRegistrySpec. +func (in *DevfileRegistrySpec) DeepCopy() *DevfileRegistrySpec { + if in == nil { + return nil + } + out := new(DevfileRegistrySpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *EventsCustomizationSpec) DeepCopyInto(out *EventsCustomizationSpec) { *out = *in @@ -647,7 +663,7 @@ func (in *KabaneroSpec) DeepCopyInto(out *KabaneroSpec) { out.CollectionController = in.CollectionController out.StackController = in.StackController out.AdmissionControllerWebhook = in.AdmissionControllerWebhook - out.Serving = in.Serving + out.DevfileRegistry = in.DevfileRegistry out.Sso = in.Sso in.Gitops.DeepCopyInto(&out.Gitops) return @@ -848,22 +864,6 @@ func (in *ServerlessStatus) DeepCopy() *ServerlessStatus { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ServingSpec) DeepCopyInto(out *ServingSpec) { - *out = *in - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServingSpec. -func (in *ServingSpec) DeepCopy() *ServingSpec { - if in == nil { - return nil - } - out := new(ServingSpec) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *SsoCustomizationSpec) DeepCopyInto(out *SsoCustomizationSpec) { *out = *in diff --git a/pkg/controller/serving/controller.go b/pkg/controller/devfileregistry/controller.go similarity index 94% rename from pkg/controller/serving/controller.go rename to pkg/controller/devfileregistry/controller.go index 24bcc503..82bda823 100644 --- a/pkg/controller/serving/controller.go +++ b/pkg/controller/devfileregistry/controller.go @@ -1,4 +1,4 @@ -package serving +package devfileregistry import ( "sigs.k8s.io/controller-runtime/pkg/manager" diff --git a/pkg/controller/serving/serving_controller.go b/pkg/controller/devfileregistry/devfile_registry_controller.go similarity index 96% rename from pkg/controller/serving/serving_controller.go rename to pkg/controller/devfileregistry/devfile_registry_controller.go index 7247e30d..9253f696 100644 --- a/pkg/controller/serving/serving_controller.go +++ b/pkg/controller/devfileregistry/devfile_registry_controller.go @@ -1,169 +1,169 @@ -package serving - -import ( - "context" - "fmt" - "os" - - kabanerov1alpha2 "github.com/kabanero-io/kabanero-operator/pkg/apis/kabanero/v1alpha2" - // corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/errors" - // metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - // "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/controller" - // "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" - "sigs.k8s.io/controller-runtime/pkg/handler" - logf "sigs.k8s.io/controller-runtime/pkg/log" - "sigs.k8s.io/controller-runtime/pkg/manager" - "sigs.k8s.io/controller-runtime/pkg/reconcile" - "sigs.k8s.io/controller-runtime/pkg/source" - - // Prefer to import index func when it's moved to a pkg - // d2index "github.com/odo-devfiles/registry/tools/cmd/index" - "encoding/json" -) - -var log = logf.Log.WithName("controller_stack") - -// Add creates a new Stack Controller and adds it to the Manager. The Manager will set fields on the Controller -// and Start it when the Manager is Started. -func Add(mgr manager.Manager) error { - return add(mgr, newReconciler(mgr)) -} - -// newReconciler returns a new reconcile.Reconciler -func newReconciler(mgr manager.Manager) reconcile.Reconciler { - return &ReconcileStack{client: mgr.GetClient(), scheme: mgr.GetScheme()} -} - -// add adds a new Controller to mgr with r as the reconcile.Reconciler -func add(mgr manager.Manager, r reconcile.Reconciler) error { - // Create a new controller - c, err := controller.New("stack-controller", mgr, controller.Options{Reconciler: r}) - if err != nil { - return err - } - - // Watch for changes to primary resource Stack - err = c.Watch(&source.Kind{Type: &kabanerov1alpha2.Stack{}}, &handler.EnqueueRequestForObject{}) - if err != nil { - return err - } - - return nil -} - -// blank assignment to verify that ReconcileStack implements reconcile.Reconciler -var _ reconcile.Reconciler = &ReconcileStack{} - -// ReconcileStack reconciles a Stack object -type ReconcileStack struct { - // This client, initialized using mgr.Client() above, is a split client - // that reads objects from the cache and writes to the apiserver - client client.Client - scheme *runtime.Scheme -} - -// Reconcile reads that state of the cluster for a Stack object and makes changes based on the state read -// and what is in the Stack.Spec -// TODO(user): Modify this Reconcile function to implement your Controller logic. This example creates -// a Pod as an example -// Note: -// The Controller will requeue the Request to be processed again if the returned error is non-nil or -// Result.Requeue is true, otherwise upon completion it will remove the work from the queue. -func (r *ReconcileStack) Reconcile(request reconcile.Request) (reconcile.Result, error) { - reqLogger := log.WithValues("Request.Namespace", request.Namespace, "Request.Name", request.Name) - reqLogger.Info("Reconciling Stack") - - // Fetch the Stack instance - instance := &kabanerov1alpha2.Stack{} - err := r.client.Get(context.TODO(), request.NamespacedName, instance) - if err != nil { - if errors.IsNotFound(err) { - // Request object not found, could have been deleted after reconcile request. - // Owned objects are automatically garbage collected. For additional cleanup logic use finalizers. - // Return and don't requeue - return reconcile.Result{}, nil - } - // Error reading the object - requeue the request. - return reconcile.Result{}, err - } - - basePath := "/devfiles" - stackPath := fmt.Sprintf("%v/%v", basePath, instance.ObjectMeta.Name) - os.Mkdir(basePath, os.ModePerm) - - // Clean all files for this stack: a version may be removed or Stack deletion - os.RemoveAll(stackPath) - - beingDeleted := !instance.DeletionTimestamp.IsZero() - - if !beingDeleted { - // Copy the yaml from the Stack to local devfile registry if it exists - for _, version := range instance.Spec.Versions { - if (len(version.Devfile) !=0) && (len(version.Metafile) !=0) { - versionPath := fmt.Sprintf("%v/%v", stackPath, version.Version) - devfile := fmt.Sprintf("%v/devfile.yaml", versionPath) - metafile := fmt.Sprintf("%v/meta.yaml", versionPath) - - os.MkdirAll(versionPath, os.ModePerm) - - f, err := os.Create(devfile) - if err != nil { - reqLogger.Error(err, fmt.Sprintf("Error creating devfile %v", devfile)) - } - defer f.Close() - _, err = f.WriteString(version.Devfile) - if err != nil { - reqLogger.Error(err, fmt.Sprintf("Error writing devfile %v", devfile)) - } - f.Sync() - - f, err = os.Create(metafile) - if err != nil { - reqLogger.Error(err, fmt.Sprintf("Error creating metafile %v", metafile)) - } - defer f.Close() - _, err = f.WriteString(version.Metafile) - if err != nil { - reqLogger.Error(err, fmt.Sprintf("Error writing metafile %v", metafile)) - } - f.Sync() - } - } - } - - // Regenerate the index - indexfile := fmt.Sprintf("%v/index.json", basePath) - index, err := genIndex(basePath) - if err != nil { - reqLogger.Error(err, "Error generating devfile index") - return reconcile.Result{}, err - } - var b []byte - - if len(index) !=0 { - b, err = json.MarshalIndent(index, "", " ") - if err != nil { - reqLogger.Error(err, "Error during marshal index") - return reconcile.Result{}, err - } - } - - f, err := os.Create(indexfile) - if err != nil { - reqLogger.Error(err, fmt.Sprintf("Error creating indexfile %v", indexfile)) - return reconcile.Result{}, err - } - defer f.Close() - _, err = f.Write(b) - if err != nil { - reqLogger.Error(err, fmt.Sprintf("Error writing indexfile %v", indexfile)) - return reconcile.Result{}, err - } - f.Sync() - - return reconcile.Result{}, nil -} +package devfileregistry + +import ( + "context" + "fmt" + "os" + + kabanerov1alpha2 "github.com/kabanero-io/kabanero-operator/pkg/apis/kabanero/v1alpha2" + // corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" + // metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + // "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller" + // "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + "sigs.k8s.io/controller-runtime/pkg/handler" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/manager" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + "sigs.k8s.io/controller-runtime/pkg/source" + + // Prefer to import index func when it's moved to a pkg + // d2index "github.com/odo-devfiles/registry/tools/cmd/index" + "encoding/json" +) + +var log = logf.Log.WithName("controller_stack") + +// Add creates a new Stack Controller and adds it to the Manager. The Manager will set fields on the Controller +// and Start it when the Manager is Started. +func Add(mgr manager.Manager) error { + return add(mgr, newReconciler(mgr)) +} + +// newReconciler returns a new reconcile.Reconciler +func newReconciler(mgr manager.Manager) reconcile.Reconciler { + return &ReconcileStack{client: mgr.GetClient(), scheme: mgr.GetScheme()} +} + +// add adds a new Controller to mgr with r as the reconcile.Reconciler +func add(mgr manager.Manager, r reconcile.Reconciler) error { + // Create a new controller + c, err := controller.New("stack-controller", mgr, controller.Options{Reconciler: r}) + if err != nil { + return err + } + + // Watch for changes to primary resource Stack + err = c.Watch(&source.Kind{Type: &kabanerov1alpha2.Stack{}}, &handler.EnqueueRequestForObject{}) + if err != nil { + return err + } + + return nil +} + +// blank assignment to verify that ReconcileStack implements reconcile.Reconciler +var _ reconcile.Reconciler = &ReconcileStack{} + +// ReconcileStack reconciles a Stack object +type ReconcileStack struct { + // This client, initialized using mgr.Client() above, is a split client + // that reads objects from the cache and writes to the apiserver + client client.Client + scheme *runtime.Scheme +} + +// Reconcile reads that state of the cluster for a Stack object and makes changes based on the state read +// and what is in the Stack.Spec +// TODO(user): Modify this Reconcile function to implement your Controller logic. This example creates +// a Pod as an example +// Note: +// The Controller will requeue the Request to be processed again if the returned error is non-nil or +// Result.Requeue is true, otherwise upon completion it will remove the work from the queue. +func (r *ReconcileStack) Reconcile(request reconcile.Request) (reconcile.Result, error) { + reqLogger := log.WithValues("Request.Namespace", request.Namespace, "Request.Name", request.Name) + reqLogger.Info("Reconciling Stack") + + // Fetch the Stack instance + instance := &kabanerov1alpha2.Stack{} + err := r.client.Get(context.TODO(), request.NamespacedName, instance) + if err != nil { + if errors.IsNotFound(err) { + // Request object not found, could have been deleted after reconcile request. + // Owned objects are automatically garbage collected. For additional cleanup logic use finalizers. + // Return and don't requeue + return reconcile.Result{}, nil + } + // Error reading the object - requeue the request. + return reconcile.Result{}, err + } + + basePath := "/devfiles" + stackPath := fmt.Sprintf("%v/%v", basePath, instance.ObjectMeta.Name) + os.Mkdir(basePath, os.ModePerm) + + // Clean all files for this stack: a version may be removed or Stack deletion + os.RemoveAll(stackPath) + + beingDeleted := !instance.DeletionTimestamp.IsZero() + + if !beingDeleted { + // Copy the yaml from the Stack to local devfile registry if it exists + for _, version := range instance.Spec.Versions { + if (len(version.Devfile) !=0) && (len(version.Metafile) !=0) { + versionPath := fmt.Sprintf("%v/%v", stackPath, version.Version) + devfile := fmt.Sprintf("%v/devfile.yaml", versionPath) + metafile := fmt.Sprintf("%v/meta.yaml", versionPath) + + os.MkdirAll(versionPath, os.ModePerm) + + f, err := os.Create(devfile) + if err != nil { + reqLogger.Error(err, fmt.Sprintf("Error creating devfile %v", devfile)) + } + defer f.Close() + _, err = f.WriteString(version.Devfile) + if err != nil { + reqLogger.Error(err, fmt.Sprintf("Error writing devfile %v", devfile)) + } + f.Sync() + + f, err = os.Create(metafile) + if err != nil { + reqLogger.Error(err, fmt.Sprintf("Error creating metafile %v", metafile)) + } + defer f.Close() + _, err = f.WriteString(version.Metafile) + if err != nil { + reqLogger.Error(err, fmt.Sprintf("Error writing metafile %v", metafile)) + } + f.Sync() + } + } + } + + // Regenerate the index + indexfile := fmt.Sprintf("%v/index.json", basePath) + index, err := genIndex(basePath) + if err != nil { + reqLogger.Error(err, "Error generating devfile index") + return reconcile.Result{}, err + } + var b []byte + + if len(index) !=0 { + b, err = json.MarshalIndent(index, "", " ") + if err != nil { + reqLogger.Error(err, "Error during marshal index") + return reconcile.Result{}, err + } + } + + f, err := os.Create(indexfile) + if err != nil { + reqLogger.Error(err, fmt.Sprintf("Error creating indexfile %v", indexfile)) + return reconcile.Result{}, err + } + defer f.Close() + _, err = f.Write(b) + if err != nil { + reqLogger.Error(err, fmt.Sprintf("Error writing indexfile %v", indexfile)) + return reconcile.Result{}, err + } + f.Sync() + + return reconcile.Result{}, nil +} diff --git a/pkg/controller/serving/index.go b/pkg/controller/devfileregistry/index.go similarity index 98% rename from pkg/controller/serving/index.go rename to pkg/controller/devfileregistry/index.go index c92d4c81..8d9ef3b0 100644 --- a/pkg/controller/serving/index.go +++ b/pkg/controller/devfileregistry/index.go @@ -1,4 +1,4 @@ -package serving +package devfileregistry import ( //"encoding/json" diff --git a/pkg/controller/serving/register.go b/pkg/controller/devfileregistry/register.go similarity index 87% rename from pkg/controller/serving/register.go rename to pkg/controller/devfileregistry/register.go index 81ad5b97..8c875069 100644 --- a/pkg/controller/serving/register.go +++ b/pkg/controller/devfileregistry/register.go @@ -1,4 +1,4 @@ -package serving +package devfileregistry func init() { // AddToManagerFuncs is a list of functions to create controllers and add them to a manager. diff --git a/pkg/controller/kabaneroplatform/serving.go b/pkg/controller/kabaneroplatform/devfile-registry.go similarity index 55% rename from pkg/controller/kabaneroplatform/serving.go rename to pkg/controller/kabaneroplatform/devfile-registry.go index d560bcba..c01e6b0d 100644 --- a/pkg/controller/kabaneroplatform/serving.go +++ b/pkg/controller/kabaneroplatform/devfile-registry.go @@ -1,129 +1,150 @@ -package kabaneroplatform - -import ( - "context" - "github.com/go-logr/logr" - kabanerov1alpha2 "github.com/kabanero-io/kabanero-operator/pkg/apis/kabanero/v1alpha2" - - corev1 "k8s.io/api/core/v1" - - mf "github.com/manifestival/manifestival" - mfc "github.com/manifestival/controller-runtime-client" - "k8s.io/apimachinery/pkg/api/errors" - "sigs.k8s.io/controller-runtime/pkg/client" - "strings" -) - -func reconcileServing(ctx context.Context, k *kabanerov1alpha2.Kabanero, c client.Client, reqLogger logr.Logger) error { - - // Figure out what version of the orchestration we are going to use. - rev, err := resolveSoftwareRevision(k, "serving", k.Spec.Serving.Version) - if err != nil { - return err - } - - templateContext := rev.Identifiers - - image, err := imageUriWithOverrides(k.Spec.Serving.Repository, k.Spec.Serving.Tag, k.Spec.Serving.Image, rev) - if err != nil { - return err - } - - templateContext["image"] = image - templateContext["instance"] = k.ObjectMeta.UID - templateContext["version"] = rev.Version - - f, err := rev.OpenOrchestration("serving.yaml") - if err != nil { - return err - } - - s, err := renderOrchestration(f, templateContext) - if err != nil { - return err - } - - mOrig, err := mf.ManifestFrom(mf.Reader(strings.NewReader(s)), mf.UseClient(mfc.NewClient(c)), mf.UseLogger(reqLogger.WithName("manifestival"))) - if err != nil { - return err - } - - transforms := []mf.Transformer{ - mf.InjectOwner(k), - mf.InjectNamespace(k.GetNamespace()), - } - - m, err := mOrig.Transform(transforms...) - if err != nil { - return err - } - - err = m.Apply() - if err != nil { - return err - } - - return nil -} - - -func cleanupServing(k *kabanerov1alpha2.Kabanero, c client.Client, reqLogger logr.Logger) error { - - rev, err := resolveSoftwareRevision(k, "serving", k.Spec.Serving.Version) - if err != nil { - return err - } - - //The context which will be used to render any templates - templateContext := rev.Identifiers - - // TODO - image, err := imageUriWithOverrides(k.Spec.Serving.Repository, k.Spec.Serving.Tag, k.Spec.Serving.Image, rev) - if err != nil { - return err - } - templateContext["image"] = image - templateContext["instance"] = k.ObjectMeta.UID - templateContext["version"] = rev.Version - - f, err := rev.OpenOrchestration("serving.yaml") - if err != nil { - return err - } - - s, err := renderOrchestration(f, templateContext) - if err != nil { - return err - } - - mOrig, err := mf.ManifestFrom(mf.Reader(strings.NewReader(s)), mf.UseClient(mfc.NewClient(c)), mf.UseLogger(reqLogger.WithName("manifestival"))) - if err != nil { - return err - } - - transforms := []mf.Transformer{mf.InjectNamespace(k.GetNamespace())} - m, err := mOrig.Transform(transforms...) - if err != nil { - return err - } - - // Manifestival ignores the "NotFound" error for us. - err = m.Delete() - if err != nil { - return err - } - - - // Now, clean up the things that the controller-runtime created on - // our behalf. - secretInstance := &corev1.Secret{} - secretInstance.Name = "kabanero-operator-serving-cert" - secretInstance.Namespace = k.GetNamespace() - err = c.Delete(context.TODO(), secretInstance) - - if (err != nil) && (errors.IsNotFound(err) == false) { - return err - } - - return nil -} +package kabaneroplatform + +import ( + "context" + "github.com/go-logr/logr" + kabanerov1alpha2 "github.com/kabanero-io/kabanero-operator/pkg/apis/kabanero/v1alpha2" + "github.com/kabanero-io/kabanero-operator/pkg/versioning" + + corev1 "k8s.io/api/core/v1" + + mf "github.com/manifestival/manifestival" + mfc "github.com/manifestival/controller-runtime-client" + "k8s.io/apimachinery/pkg/api/errors" + "sigs.k8s.io/controller-runtime/pkg/client" + "strings" +) + +func reconcileDevfileRegistry(ctx context.Context, k *kabanerov1alpha2.Kabanero, c client.Client, reqLogger logr.Logger) error { + + // Figure out what version of the orchestration we are going to use. If this version doesn't have a devfile + // registry, then make sure the devfile registry is stopped. + rev, err := resolveSoftwareRevision(k, "devfile-registry-controller", k.Spec.DevfileRegistry.Version) + if err != nil { + // We'll need to re-evaluate this down the road, but for now assume the 0.10.0 version of the devfile + // registry controller might be installed, so lets try to remove it. + rev, err2 := resolveSoftwareRevision(k, "devfile-registry-controller", "0.10.0") + if err2 != nil { + return err + } + + err = cleanupDevfileRegistryForRevision(rev, k, c, reqLogger) + if err != nil { + return err + } + + return nil + } + + templateContext := rev.Identifiers + + image, err := imageUriWithOverrides(k.Spec.DevfileRegistry.Repository, k.Spec.DevfileRegistry.Tag, k.Spec.DevfileRegistry.Image, rev) + if err != nil { + return err + } + + templateContext["image"] = image + templateContext["instance"] = k.ObjectMeta.UID + templateContext["version"] = rev.Version + + f, err := rev.OpenOrchestration("devfile-registry-controller.yaml") + if err != nil { + return err + } + + s, err := renderOrchestration(f, templateContext) + if err != nil { + return err + } + + mOrig, err := mf.ManifestFrom(mf.Reader(strings.NewReader(s)), mf.UseClient(mfc.NewClient(c)), mf.UseLogger(reqLogger.WithName("manifestival"))) + if err != nil { + return err + } + + transforms := []mf.Transformer{ + mf.InjectOwner(k), + mf.InjectNamespace(k.GetNamespace()), + } + + m, err := mOrig.Transform(transforms...) + if err != nil { + return err + } + + err = m.Apply() + if err != nil { + return err + } + + return nil +} + + +func cleanupDevfileRegistry(k *kabanerov1alpha2.Kabanero, c client.Client, reqLogger logr.Logger) error { + + rev, err := resolveSoftwareRevision(k, "devfile-registry-controller", k.Spec.DevfileRegistry.Version) + if err != nil { + // It may be that this version of kabanero doesn't have a devfile registry, so just exit. + reqLogger.Error(err, "Error encountered when cleanup up the devfile registry") + return nil + } + + return cleanupDevfileRegistryForRevision(rev, k, c, reqLogger) +} + +func cleanupDevfileRegistryForRevision(rev versioning.SoftwareRevision, k *kabanerov1alpha2.Kabanero, c client.Client, reqLogger logr.Logger) error { + + //The context which will be used to render any templates + templateContext := rev.Identifiers + + // TODO + image, err := imageUriWithOverrides(k.Spec.DevfileRegistry.Repository, k.Spec.DevfileRegistry.Tag, k.Spec.DevfileRegistry.Image, rev) + if err != nil { + return err + } + templateContext["image"] = image + templateContext["instance"] = k.ObjectMeta.UID + templateContext["version"] = rev.Version + + f, err := rev.OpenOrchestration("devfile-registry-controller.yaml") + if err != nil { + return err + } + + s, err := renderOrchestration(f, templateContext) + if err != nil { + return err + } + + mOrig, err := mf.ManifestFrom(mf.Reader(strings.NewReader(s)), mf.UseClient(mfc.NewClient(c)), mf.UseLogger(reqLogger.WithName("manifestival"))) + if err != nil { + return err + } + + transforms := []mf.Transformer{mf.InjectNamespace(k.GetNamespace())} + m, err := mOrig.Transform(transforms...) + if err != nil { + return err + } + + // Manifestival ignores the "NotFound" error for us. + err = m.Delete() + if err != nil { + return err + } + + + // Now, clean up the things that the controller-runtime created on + // our behalf. + secretInstance := &corev1.Secret{} + secretInstance.Name = "kabanero-operator-devfile-registry-cert" + secretInstance.Namespace = k.GetNamespace() + err = c.Delete(context.TODO(), secretInstance) + + if (err != nil) && (errors.IsNotFound(err) == false) { + return err + } + + return nil +} diff --git a/pkg/controller/kabaneroplatform/kabaneroplatform_controller.go b/pkg/controller/kabaneroplatform/kabaneroplatform_controller.go index f7f3eae8..a17bdd64 100644 --- a/pkg/controller/kabaneroplatform/kabaneroplatform_controller.go +++ b/pkg/controller/kabaneroplatform/kabaneroplatform_controller.go @@ -56,7 +56,7 @@ var reconcileFuncs = []reconcileFuncType{ {name: "sso", function: reconcileSso}, {name: "gitops", function: reconcileGitopsPipelines}, {name: "target namespaces", function: reconcileTargetNamespaces}, - {name: "serving", function: reconcileServing}, + {name: "devfile registry controller", function: reconcileDevfileRegistry}, } // Add creates a new Kabanero Controller and adds it to the Manager. The Manager will set fields on the Controller @@ -503,8 +503,8 @@ func cleanup(ctx context.Context, k *kabanerov1alpha2.Kabanero, client client.Cl return err } - // Cleanup the Serving and their cross-namespace objects - err = cleanupServing(k, client, reqLogger) + // Cleanup the Devfile registry controller and its cross-namespace objects + err = cleanupDevfileRegistry(k, client, reqLogger) if err != nil { return err }