diff --git a/.ci-operator.yaml b/.ci-operator.yaml index 1a05e606..df7900c6 100644 --- a/.ci-operator.yaml +++ b/.ci-operator.yaml @@ -1,4 +1,4 @@ build_root_image: name: tools namespace: openstack-k8s-operators - tag: ci-build-root-golang-1.24-sdk-1.31 + tag: ci-build-root-golang-1.24-sdk-1.41.1 diff --git a/.github/workflows/force-bump-pr-manual.yaml b/.github/workflows/force-bump-pr-manual.yaml index fccd8c70..74b4d92e 100644 --- a/.github/workflows/force-bump-pr-manual.yaml +++ b/.github/workflows/force-bump-pr-manual.yaml @@ -9,6 +9,6 @@ jobs: with: operator_name: ironic branch_name: ${{ github.ref_name }} - custom_image: quay.io/openstack-k8s-operators/openstack-k8s-operators-ci-build-tools:golang-1.24-sdk-1.31 + custom_image: quay.io/openstack-k8s-operators/openstack-k8s-operators-ci-build-tools:golang-1.24-sdk-1.41.1 secrets: FORCE_BUMP_PULL_REQUEST_PAT: ${{ secrets.FORCE_BUMP_PULL_REQUEST_PAT }} diff --git a/.github/workflows/force-bump-pr-scheduled.yaml b/.github/workflows/force-bump-pr-scheduled.yaml index 035a49f0..374b67ce 100644 --- a/.github/workflows/force-bump-pr-scheduled.yaml +++ b/.github/workflows/force-bump-pr-scheduled.yaml @@ -10,6 +10,6 @@ jobs: uses: openstack-k8s-operators/openstack-k8s-operators-ci/.github/workflows/force-bump-branches.yaml@main with: operator_name: ironic - custom_image: quay.io/openstack-k8s-operators/openstack-k8s-operators-ci-build-tools:golang-1.24-sdk-1.31 + custom_image: quay.io/openstack-k8s-operators/openstack-k8s-operators-ci-build-tools:golang-1.24-sdk-1.41.1 secrets: FORCE_BUMP_PULL_REQUEST_PAT: ${{ secrets.FORCE_BUMP_PULL_REQUEST_PAT }} diff --git a/Dockerfile b/Dockerfile index 06bbaa20..8cac532e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -26,7 +26,7 @@ RUN mkdir -p ${DEST_ROOT}/usr/local/bin/ RUN if [ ! -f $CACHITO_ENV_FILE ]; then go mod download ; fi # Build manager -RUN if [ -f $CACHITO_ENV_FILE ] ; then source $CACHITO_ENV_FILE ; fi ; env ${GO_BUILD_EXTRA_ENV_ARGS} go build ${GO_BUILD_EXTRA_ARGS} -a -o ${DEST_ROOT}/manager main.go +RUN if [ -f $CACHITO_ENV_FILE ] ; then source $CACHITO_ENV_FILE ; fi ; env ${GO_BUILD_EXTRA_ENV_ARGS} go build ${GO_BUILD_EXTRA_ARGS} -a -o ${DEST_ROOT}/manager cmd/main.go RUN cp -r templates ${DEST_ROOT}/templates diff --git a/Makefile b/Makefile index 3e3791ad..567b7fc6 100644 --- a/Makefile +++ b/Makefile @@ -48,7 +48,7 @@ endif # Set the Operator SDK version to use. By default, what is installed on the system is used. # This is useful for CI or a project to utilize a specific version of the operator-sdk toolkit. -OPERATOR_SDK_VERSION ?= v1.31.0 +OPERATOR_SDK_VERSION ?= v1.41.1 # Image URL to use all building/pushing image targets IMG ?= controller:latest @@ -142,7 +142,7 @@ unit-test:## Run unit test .PHONY: build build: generate fmt vet ## Build manager binary. - go build -o bin/manager main.go + go build -o bin/manager cmd/main.go .PHONY: run run: export METRICS_PORT?=8080 @@ -151,7 +151,7 @@ run: export PPROF_PORT?=8082 run: export ENABLE_WEBHOOKS?=false run: manifests generate fmt vet ## Run a controller from your host. /bin/bash hack/clean_local_webhook.sh - go run ./main.go -metrics-bind-address ":$(METRICS_PORT)" -health-probe-bind-address ":$(HEALTH_PORT)" -pprof-bind-address ":$(PPROF_PORT)" + go run ./cmd/main.go -metrics-bind-address ":$(METRICS_PORT)" -health-probe-bind-address ":$(HEALTH_PORT)" -pprof-bind-address ":$(PPROF_PORT)" .PHONY: docker-build docker-build: test ## Build docker image with the manager. @@ -198,7 +198,7 @@ ENVTEST ?= $(LOCALBIN)/setup-envtest GINKGO ?= $(LOCALBIN)/ginkgo ## Tool Versions -KUSTOMIZE_VERSION ?= v3.8.7 +KUSTOMIZE_VERSION ?= v5.6.0 CONTROLLER_TOOLS_VERSION ?= v0.18.0 KUSTOMIZE_INSTALL_SCRIPT ?= "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh" diff --git a/PROJECT b/PROJECT index cea7fa5b..5f0a534d 100644 --- a/PROJECT +++ b/PROJECT @@ -1,6 +1,6 @@ domain: openstack.org layout: -- go.kubebuilder.io/v3 +- go.kubebuilder.io/v4 plugins: manifests.sdk.operatorframework.io/v2: {} scorecard.sdk.operatorframework.io/v2: {} @@ -38,4 +38,22 @@ resources: kind: IronicConductor path: github.com/openstack-k8s-operators/ironic-operator/api/v1beta1 version: v1beta1 +- api: + crdVersion: v1 + namespaced: true + controller: true + domain: openstack.org + group: ironic + kind: IronicInspector + path: github.com/openstack-k8s-operators/ironic-operator/api/v1beta1 + version: v1beta1 +- api: + crdVersion: v1 + namespaced: true + controller: true + domain: openstack.org + group: ironic + kind: IronicNeutronAgent + path: github.com/openstack-k8s-operators/ironic-operator/api/v1beta1 + version: v1beta1 version: "3" diff --git a/api/v1beta1/ironic_webhook.go b/api/v1beta1/ironic_webhook.go index 473ab658..d66836c6 100644 --- a/api/v1beta1/ironic_webhook.go +++ b/api/v1beta1/ironic_webhook.go @@ -30,7 +30,6 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/util/validation/field" k8snet "k8s.io/utils/net" - ctrl "sigs.k8s.io/controller-runtime" logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/webhook" "sigs.k8s.io/controller-runtime/pkg/webhook/admission" @@ -63,13 +62,6 @@ func SetupIronicImageDefaults(images IronicImages) { ironiclog.Info("Ironic defaults initialized", "images", imageDefaults) } -// SetupWebhookWithManager sets up the webhook with the Manager -func (r *Ironic) SetupWebhookWithManager(mgr ctrl.Manager) error { - return ctrl.NewWebhookManagedBy(mgr). - For(r). - Complete() -} - //+kubebuilder:webhook:path=/validate-ironic-openstack-org-v1beta1-ironic,mutating=false,failurePolicy=fail,sideEffects=None,groups=ironic.openstack.org,resources=ironics,verbs=create;update,versions=v1beta1,name=vironic.kb.io,admissionReviewVersions=v1 //+kubebuilder:webhook:path=/mutate-ironic-openstack-org-v1beta1-ironic,mutating=true,failurePolicy=fail,sideEffects=None,groups=ironic.openstack.org,resources=ironics,verbs=create;update,versions=v1beta1,name=mironic.kb.io,admissionReviewVersions=v1 diff --git a/cmd/main.go b/cmd/main.go new file mode 100644 index 00000000..68e94e56 --- /dev/null +++ b/cmd/main.go @@ -0,0 +1,323 @@ +/* +Copyright 2025. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package main implements the ironic-operator controller manager. +package main + +import ( + "crypto/tls" + "flag" + "os" + "path/filepath" + + // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.) + // to ensure that exec-entrypoint and run can make use of them. + _ "k8s.io/client-go/plugin/pkg/client/auth" + + "k8s.io/apimachinery/pkg/runtime" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" + clientgoscheme "k8s.io/client-go/kubernetes/scheme" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/certwatcher" + "sigs.k8s.io/controller-runtime/pkg/healthz" + "sigs.k8s.io/controller-runtime/pkg/log/zap" + "sigs.k8s.io/controller-runtime/pkg/metrics/filters" + metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" + "sigs.k8s.io/controller-runtime/pkg/webhook" + + "github.com/openstack-k8s-operators/ironic-operator/internal/controller" + webhookv1beta1 "github.com/openstack-k8s-operators/ironic-operator/internal/webhook/v1beta1" + + // +kubebuilder:scaffold:imports + "context" + + networkv1 "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/apis/k8s.cni.cncf.io/v1" + routev1 "github.com/openshift/api/route/v1" + rabbitmqv1 "github.com/openstack-k8s-operators/infra-operator/apis/rabbitmq/v1beta1" + topologyv1 "github.com/openstack-k8s-operators/infra-operator/apis/topology/v1beta1" + ironicv1 "github.com/openstack-k8s-operators/ironic-operator/api/v1beta1" + keystonev1beta1 "github.com/openstack-k8s-operators/keystone-operator/api/v1beta1" + mariadbv1beta1 "github.com/openstack-k8s-operators/mariadb-operator/api/v1beta1" + "k8s.io/client-go/kubernetes" + "sigs.k8s.io/controller-runtime/pkg/client/config" +) + +var ( + scheme = runtime.NewScheme() + setupLog = ctrl.Log.WithName("setup") +) + +func init() { + utilruntime.Must(clientgoscheme.AddToScheme(scheme)) + + utilruntime.Must(ironicv1.AddToScheme(scheme)) + utilruntime.Must(mariadbv1beta1.AddToScheme(scheme)) + utilruntime.Must(rabbitmqv1.AddToScheme(scheme)) + utilruntime.Must(routev1.AddToScheme(scheme)) + utilruntime.Must(networkv1.AddToScheme(scheme)) + utilruntime.Must(topologyv1.AddToScheme(scheme)) + err := keystonev1beta1.AddToScheme(scheme) + if err != nil { + setupLog.Info("Adding keystone schema failed, only standalone deployments are available") + } + //+kubebuilder:scaffold:scheme +} + +// nolint:gocyclo +func main() { + var metricsAddr string + var metricsCertPath, metricsCertName, metricsCertKey string + var webhookCertPath, webhookCertName, webhookCertKey string + var enableLeaderElection bool + var probeAddr string + var secureMetrics bool + var enableHTTP2 bool + var tlsOpts []func(*tls.Config) + flag.StringVar(&metricsAddr, "metrics-bind-address", "0", "The address the metrics endpoint binds to. "+ + "Use :8443 for HTTPS or :8080 for HTTP, or leave as 0 to disable the metrics service.") + flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.") + flag.BoolVar(&enableLeaderElection, "leader-elect", false, + "Enable leader election for controller manager. "+ + "Enabling this will ensure there is only one active controller manager.") + flag.BoolVar(&secureMetrics, "metrics-secure", true, + "If set, the metrics endpoint is served securely via HTTPS. Use --metrics-secure=false to use HTTP instead.") + flag.StringVar(&webhookCertPath, "webhook-cert-path", "", "The directory that contains the webhook certificate.") + flag.StringVar(&webhookCertName, "webhook-cert-name", "tls.crt", "The name of the webhook certificate file.") + flag.StringVar(&webhookCertKey, "webhook-cert-key", "tls.key", "The name of the webhook key file.") + flag.StringVar(&metricsCertPath, "metrics-cert-path", "", + "The directory that contains the metrics server certificate.") + flag.StringVar(&metricsCertName, "metrics-cert-name", "tls.crt", "The name of the metrics server certificate file.") + flag.StringVar(&metricsCertKey, "metrics-cert-key", "tls.key", "The name of the metrics server key file.") + flag.BoolVar(&enableHTTP2, "enable-http2", false, + "If set, HTTP/2 will be enabled for the metrics and webhook servers") + opts := zap.Options{ + Development: true, + } + opts.BindFlags(flag.CommandLine) + flag.Parse() + + ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts))) + + // if the enable-http2 flag is false (the default), http/2 should be disabled + // due to its vulnerabilities. More specifically, disabling http/2 will + // prevent from being vulnerable to the HTTP/2 Stream Cancellation and + // Rapid Reset CVEs. For more information see: + // - https://github.com/advisories/GHSA-qppj-fm5r-hxr3 + // - https://github.com/advisories/GHSA-4374-p667-p6c8 + disableHTTP2 := func(c *tls.Config) { + setupLog.Info("disabling http/2") + c.NextProtos = []string{"http/1.1"} + } + + if !enableHTTP2 { + tlsOpts = append(tlsOpts, disableHTTP2) + } + + // Create watchers for metrics and webhooks certificates + var metricsCertWatcher, webhookCertWatcher *certwatcher.CertWatcher + + // Initial webhook TLS options + webhookTLSOpts := tlsOpts + + if len(webhookCertPath) > 0 { + setupLog.Info("Initializing webhook certificate watcher using provided certificates", + "webhook-cert-path", webhookCertPath, "webhook-cert-name", webhookCertName, "webhook-cert-key", webhookCertKey) + + var err error + webhookCertWatcher, err = certwatcher.New( + filepath.Join(webhookCertPath, webhookCertName), + filepath.Join(webhookCertPath, webhookCertKey), + ) + if err != nil { + setupLog.Error(err, "Failed to initialize webhook certificate watcher") + os.Exit(1) + } + + webhookTLSOpts = append(webhookTLSOpts, func(config *tls.Config) { + config.GetCertificate = webhookCertWatcher.GetCertificate + }) + } + + webhookServer := webhook.NewServer(webhook.Options{ + TLSOpts: webhookTLSOpts, + }) + + // Metrics endpoint is enabled in 'config/default/kustomization.yaml'. The Metrics options configure the server. + // More info: + // - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.21.0/pkg/metrics/server + // - https://book.kubebuilder.io/reference/metrics.html + metricsServerOptions := metricsserver.Options{ + BindAddress: metricsAddr, + SecureServing: secureMetrics, + TLSOpts: tlsOpts, + } + + if secureMetrics { + // FilterProvider is used to protect the metrics endpoint with authn/authz. + // These configurations ensure that only authorized users and service accounts + // can access the metrics endpoint. The RBAC are configured in 'config/rbac/kustomization.yaml'. More info: + // https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.21.0/pkg/metrics/filters#WithAuthenticationAndAuthorization + metricsServerOptions.FilterProvider = filters.WithAuthenticationAndAuthorization + } + + // If the certificate is not specified, controller-runtime will automatically + // generate self-signed certificates for the metrics server. While convenient for development and testing, + // this setup is not recommended for production. + // + // TODO(user): If you enable certManager, uncomment the following lines: + // - [METRICS-WITH-CERTS] at config/default/kustomization.yaml to generate and use certificates + // managed by cert-manager for the metrics server. + // - [PROMETHEUS-WITH-CERTS] at config/prometheus/kustomization.yaml for TLS certification. + if len(metricsCertPath) > 0 { + setupLog.Info("Initializing metrics certificate watcher using provided certificates", + "metrics-cert-path", metricsCertPath, "metrics-cert-name", metricsCertName, "metrics-cert-key", metricsCertKey) + + var err error + metricsCertWatcher, err = certwatcher.New( + filepath.Join(metricsCertPath, metricsCertName), + filepath.Join(metricsCertPath, metricsCertKey), + ) + if err != nil { + setupLog.Error(err, "to initialize metrics certificate watcher", "error", err) + os.Exit(1) + } + + metricsServerOptions.TLSOpts = append(metricsServerOptions.TLSOpts, func(config *tls.Config) { + config.GetCertificate = metricsCertWatcher.GetCertificate + }) + } + + mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{ + Scheme: scheme, + Metrics: metricsServerOptions, + WebhookServer: webhookServer, + HealthProbeBindAddress: probeAddr, + LeaderElection: enableLeaderElection, + LeaderElectionID: "f92b5c2d.openstack.org", + // LeaderElectionReleaseOnCancel defines if the leader should step down voluntarily + // when the Manager ends. This requires the binary to immediately end when the + // Manager is stopped, otherwise, this setting is unsafe. Setting this significantly + // speeds up voluntary leader transitions as the new leader don't have to wait + // LeaseDuration time first. + // + // In the default scaffold provided, the program ends immediately after + // the manager stops, so would be fine to enable this option. However, + // if you are doing or is intended to do any operation such as perform cleanups + // after the manager stops then its usage might be unsafe. + // LeaderElectionReleaseOnCancel: true, + }) + + cfg, err := config.GetConfig() + if err != nil { + setupLog.Error(err, "") + os.Exit(1) + } + kclient, err := kubernetes.NewForConfig(cfg) + if err != nil { + setupLog.Error(err, "") + os.Exit(1) + } + if err != nil { + setupLog.Error(err, "unable to start manager") + os.Exit(1) + } + + if err := (&controller.IronicReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + Kclient: kclient, + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "Ironic") + os.Exit(1) + } + if err := (&controller.IronicAPIReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + Kclient: kclient, + }).SetupWithManager(context.Background(), mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "IronicAPI") + os.Exit(1) + } + if err := (&controller.IronicConductorReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + Kclient: kclient, + }).SetupWithManager(context.Background(), mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "IronicConductor") + os.Exit(1) + } + if err := (&controller.IronicInspectorReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + Kclient: kclient, + }).SetupWithManager(context.Background(), mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "IronicInspector") + os.Exit(1) + } + if err := (&controller.IronicNeutronAgentReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + Kclient: kclient, + }).SetupWithManager(context.Background(), mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "IronicNeutronAgent") + os.Exit(1) + } + + ironicv1.SetupDefaults() + + // nolint:goconst + checker := healthz.Ping + if os.Getenv("ENABLE_WEBHOOKS") != "false" { + if err := webhookv1beta1.SetupIronicWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "Ironic") + os.Exit(1) + } + + checker = mgr.GetWebhookServer().StartedChecker() + } + // +kubebuilder:scaffold:builder + + if metricsCertWatcher != nil { + setupLog.Info("Adding metrics certificate watcher to manager") + if err := mgr.Add(metricsCertWatcher); err != nil { + setupLog.Error(err, "unable to add metrics certificate watcher to manager") + os.Exit(1) + } + } + + if webhookCertWatcher != nil { + setupLog.Info("Adding webhook certificate watcher to manager") + if err := mgr.Add(webhookCertWatcher); err != nil { + setupLog.Error(err, "unable to add webhook certificate watcher to manager") + os.Exit(1) + } + } + + if err := mgr.AddHealthzCheck("healthz", checker); err != nil { + setupLog.Error(err, "unable to set up health check") + os.Exit(1) + } + if err := mgr.AddReadyzCheck("readyz", checker); err != nil { + setupLog.Error(err, "unable to set up ready check") + os.Exit(1) + } + + setupLog.Info("starting manager") + if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil { + setupLog.Error(err, "problem running manager") + os.Exit(1) + } +} diff --git a/config/certmanager/certificate-metrics.yaml b/config/certmanager/certificate-metrics.yaml new file mode 100644 index 00000000..c28972a3 --- /dev/null +++ b/config/certmanager/certificate-metrics.yaml @@ -0,0 +1,20 @@ +# The following manifests contain a self-signed issuer CR and a metrics certificate CR. +# More document can be found at https://docs.cert-manager.io +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + labels: + app.kubernetes.io/name: ironic-operator + app.kubernetes.io/managed-by: kustomize + name: metrics-certs # this name should match the one appeared in kustomizeconfig.yaml + namespace: system +spec: + dnsNames: + # SERVICE_NAME and SERVICE_NAMESPACE will be substituted by kustomize + # replacements in the config/default/kustomization.yaml file. + - SERVICE_NAME.SERVICE_NAMESPACE.svc + - SERVICE_NAME.SERVICE_NAMESPACE.svc.cluster.local + issuerRef: + kind: Issuer + name: selfsigned-issuer + secretName: metrics-server-cert diff --git a/config/certmanager/certificate-webhook.yaml b/config/certmanager/certificate-webhook.yaml new file mode 100644 index 00000000..a0a107aa --- /dev/null +++ b/config/certmanager/certificate-webhook.yaml @@ -0,0 +1,20 @@ +# The following manifests contain a self-signed issuer CR and a certificate CR. +# More document can be found at https://docs.cert-manager.io +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + labels: + app.kubernetes.io/name: ironic-operator + app.kubernetes.io/managed-by: kustomize + name: serving-cert # this name should match the one appeared in kustomizeconfig.yaml + namespace: system +spec: + # SERVICE_NAME and SERVICE_NAMESPACE will be substituted by kustomize + # replacements in the config/default/kustomization.yaml file. + dnsNames: + - SERVICE_NAME.SERVICE_NAMESPACE.svc + - SERVICE_NAME.SERVICE_NAMESPACE.svc.cluster.local + issuerRef: + kind: Issuer + name: selfsigned-issuer + secretName: webhook-server-cert diff --git a/config/certmanager/certificate.yaml b/config/certmanager/certificate.yaml deleted file mode 100644 index b7e5330f..00000000 --- a/config/certmanager/certificate.yaml +++ /dev/null @@ -1,39 +0,0 @@ -# The following manifests contain a self-signed issuer CR and a certificate CR. -# More document can be found at https://docs.cert-manager.io -# WARNING: Targets CertManager v1.0. Check https://cert-manager.io/docs/installation/upgrading/ for breaking changes. -apiVersion: cert-manager.io/v1 -kind: Issuer -metadata: - labels: - app.kubernetes.io/name: issuer - app.kubernetes.io/instance: selfsigned-issuer - app.kubernetes.io/component: certificate - app.kubernetes.io/created-by: ironic-operator - app.kubernetes.io/part-of: ironic-operator - app.kubernetes.io/managed-by: kustomize - name: selfsigned-issuer - namespace: system -spec: - selfSigned: {} ---- -apiVersion: cert-manager.io/v1 -kind: Certificate -metadata: - labels: - app.kubernetes.io/name: certificate - app.kubernetes.io/instance: serving-cert - app.kubernetes.io/component: certificate - app.kubernetes.io/created-by: ironic-operator - app.kubernetes.io/part-of: ironic-operator - app.kubernetes.io/managed-by: kustomize - name: serving-cert # this name should match the one appeared in kustomizeconfig.yaml - namespace: system -spec: - # $(SERVICE_NAME) and $(SERVICE_NAMESPACE) will be substituted by kustomize - dnsNames: - - $(SERVICE_NAME).$(SERVICE_NAMESPACE).svc - - $(SERVICE_NAME).$(SERVICE_NAMESPACE).svc.cluster.local - issuerRef: - kind: Issuer - name: selfsigned-issuer - secretName: webhook-server-cert # this secret will not be prefixed, since it's not managed by kustomize diff --git a/config/certmanager/issuer.yaml b/config/certmanager/issuer.yaml new file mode 100644 index 00000000..dbf23637 --- /dev/null +++ b/config/certmanager/issuer.yaml @@ -0,0 +1,13 @@ +# The following manifest contains a self-signed issuer CR. +# More information can be found at https://docs.cert-manager.io +# WARNING: Targets CertManager v1.0. Check https://cert-manager.io/docs/installation/upgrading/ for breaking changes. +apiVersion: cert-manager.io/v1 +kind: Issuer +metadata: + labels: + app.kubernetes.io/name: ironic-operator + app.kubernetes.io/managed-by: kustomize + name: selfsigned-issuer + namespace: system +spec: + selfSigned: {} diff --git a/config/certmanager/kustomization.yaml b/config/certmanager/kustomization.yaml index bebea5a5..fcb7498e 100644 --- a/config/certmanager/kustomization.yaml +++ b/config/certmanager/kustomization.yaml @@ -1,5 +1,7 @@ resources: -- certificate.yaml +- issuer.yaml +- certificate-webhook.yaml +- certificate-metrics.yaml configurations: - kustomizeconfig.yaml diff --git a/config/certmanager/kustomizeconfig.yaml b/config/certmanager/kustomizeconfig.yaml index e631f777..cf6f89e8 100644 --- a/config/certmanager/kustomizeconfig.yaml +++ b/config/certmanager/kustomizeconfig.yaml @@ -1,4 +1,4 @@ -# This configuration is for teaching kustomize how to update name ref and var substitution +# This configuration is for teaching kustomize how to update name ref substitution nameReference: - kind: Issuer group: cert-manager.io @@ -6,11 +6,3 @@ nameReference: - kind: Certificate group: cert-manager.io path: spec/issuerRef/name - -varReference: -- kind: Certificate - group: cert-manager.io - path: spec/commonName -- kind: Certificate - group: cert-manager.io - path: spec/dnsNames diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml index 78d0b1e5..db88f552 100644 --- a/config/crd/kustomization.yaml +++ b/config/crd/kustomization.yaml @@ -7,27 +7,14 @@ resources: - bases/ironic.openstack.org_ironicconductors.yaml - bases/ironic.openstack.org_ironicinspectors.yaml - bases/ironic.openstack.org_ironicneutronagents.yaml -#+kubebuilder:scaffold:crdkustomizeresource +# +kubebuilder:scaffold:crdkustomizeresource patches: # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix. # patches here are for enabling the conversion webhook for each CRD -#- path: patches/webhook_in_ironics.yaml -#- path: patches/webhook_in_ironicapis.yaml -#- path: patches/webhook_in_ironicconductors.yaml -#- path: patches/webhook_in_ironiinspectors.yaml -#- path: patches/webhook_in_ironicneutronagents.yaml -#+kubebuilder:scaffold:crdkustomizewebhookpatch - -# [CERTMANAGER] To enable cert-manager, uncomment all the sections with [CERTMANAGER] prefix. -# patches here are for enabling the CA injection for each CRD -- path: patches/cainjection_in_ironics.yaml -#- path: patches/cainjection_in_ironicapis.yaml -#- path: patches/cainjection_in_ironicconductors.yaml -#- path: patches/cainjection_in_ironicinspectors.yaml -#- path: patches/cainjection_in_ironicneutronagents.yaml -#+kubebuilder:scaffold:crdkustomizecainjectionpatch +# +kubebuilder:scaffold:crdkustomizewebhookpatch +# [WEBHOOK] To enable webhook, uncomment the following section # the following config is for teaching kustomize how to do kustomization for CRDs. -configurations: -- kustomizeconfig.yaml +#configurations: +#- kustomizeconfig.yaml diff --git a/config/crd/patches/cainjection_in_ironicapis.yaml b/config/crd/patches/cainjection_in_ironicapis.yaml deleted file mode 100644 index aafb2108..00000000 --- a/config/crd/patches/cainjection_in_ironicapis.yaml +++ /dev/null @@ -1,7 +0,0 @@ -# The following patch adds a directive for certmanager to inject CA into the CRD -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) - name: ironicapis.ironic.openstack.org diff --git a/config/crd/patches/cainjection_in_ironicconductors.yaml b/config/crd/patches/cainjection_in_ironicconductors.yaml deleted file mode 100644 index 1cd5ae52..00000000 --- a/config/crd/patches/cainjection_in_ironicconductors.yaml +++ /dev/null @@ -1,7 +0,0 @@ -# The following patch adds a directive for certmanager to inject CA into the CRD -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) - name: ironicconductorss.ironic.openstack.org diff --git a/config/crd/patches/cainjection_in_ironicinspectors.yaml b/config/crd/patches/cainjection_in_ironicinspectors.yaml deleted file mode 100644 index 932c7abd..00000000 --- a/config/crd/patches/cainjection_in_ironicinspectors.yaml +++ /dev/null @@ -1,7 +0,0 @@ -# The following patch adds a directive for certmanager to inject CA into the CRD -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) - name: ironicinspectorss.ironic.openstack.org diff --git a/config/crd/patches/cainjection_in_ironicneutronagents.yaml b/config/crd/patches/cainjection_in_ironicneutronagents.yaml deleted file mode 100644 index 0019d94d..00000000 --- a/config/crd/patches/cainjection_in_ironicneutronagents.yaml +++ /dev/null @@ -1,7 +0,0 @@ -# The following patch adds a directive for certmanager to inject CA into the CRD -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) - name: ironicneutronagents.ironic.openstack.org diff --git a/config/crd/patches/cainjection_in_ironics.yaml b/config/crd/patches/cainjection_in_ironics.yaml deleted file mode 100644 index 0aaadabd..00000000 --- a/config/crd/patches/cainjection_in_ironics.yaml +++ /dev/null @@ -1,7 +0,0 @@ -# The following patch adds a directive for certmanager to inject CA into the CRD -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) - name: ironics.ironic.openstack.org diff --git a/config/crd/patches/webhook_in_ironicapis.yaml b/config/crd/patches/webhook_in_ironicapis.yaml deleted file mode 100644 index c70be809..00000000 --- a/config/crd/patches/webhook_in_ironicapis.yaml +++ /dev/null @@ -1,16 +0,0 @@ -# The following patch enables a conversion webhook for the CRD -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - name: ironicapis.ironic.openstack.org -spec: - conversion: - strategy: Webhook - webhook: - clientConfig: - service: - namespace: system - name: webhook-service - path: /convert - conversionReviewVersions: - - v1 diff --git a/config/crd/patches/webhook_in_ironicconductors.yaml b/config/crd/patches/webhook_in_ironicconductors.yaml deleted file mode 100644 index 0c89898f..00000000 --- a/config/crd/patches/webhook_in_ironicconductors.yaml +++ /dev/null @@ -1,16 +0,0 @@ -# The following patch enables a conversion webhook for the CRD -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - name: ironicconductors.ironic.openstack.org -spec: - conversion: - strategy: Webhook - webhook: - clientConfig: - service: - namespace: system - name: webhook-service - path: /convert - conversionReviewVersions: - - v1 diff --git a/config/crd/patches/webhook_in_ironicinspectors.yaml b/config/crd/patches/webhook_in_ironicinspectors.yaml deleted file mode 100644 index 8db347b7..00000000 --- a/config/crd/patches/webhook_in_ironicinspectors.yaml +++ /dev/null @@ -1,16 +0,0 @@ -# The following patch enables a conversion webhook for the CRD -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - name: ironicinspectorss.ironic.openstack.org -spec: - conversion: - strategy: Webhook - webhook: - clientConfig: - service: - namespace: system - name: webhook-service - path: /convert - conversionReviewVersions: - - v1 diff --git a/config/crd/patches/webhook_in_ironicneutronagents.yaml b/config/crd/patches/webhook_in_ironicneutronagents.yaml deleted file mode 100644 index 5bf2298a..00000000 --- a/config/crd/patches/webhook_in_ironicneutronagents.yaml +++ /dev/null @@ -1,16 +0,0 @@ -# The following patch enables a conversion webhook for the CRD -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - name: ironicneutronagents.ironic.openstack.org -spec: - conversion: - strategy: Webhook - webhook: - clientConfig: - service: - namespace: system - name: webhook-service - path: /convert - conversionReviewVersions: - - v1 diff --git a/config/crd/patches/webhook_in_ironics.yaml b/config/crd/patches/webhook_in_ironics.yaml deleted file mode 100644 index 658305d6..00000000 --- a/config/crd/patches/webhook_in_ironics.yaml +++ /dev/null @@ -1,16 +0,0 @@ -# The following patch enables a conversion webhook for the CRD -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - name: ironics.ironic.openstack.org -spec: - conversion: - strategy: Webhook - webhook: - clientConfig: - service: - namespace: system - name: webhook-service - path: /convert - conversionReviewVersions: - - v1 diff --git a/config/default/cert_metrics_manager_patch.yaml b/config/default/cert_metrics_manager_patch.yaml new file mode 100644 index 00000000..d9750155 --- /dev/null +++ b/config/default/cert_metrics_manager_patch.yaml @@ -0,0 +1,30 @@ +# This patch adds the args, volumes, and ports to allow the manager to use the metrics-server certs. + +# Add the volumeMount for the metrics-server certs +- op: add + path: /spec/template/spec/containers/0/volumeMounts/- + value: + mountPath: /tmp/k8s-metrics-server/metrics-certs + name: metrics-certs + readOnly: true + +# Add the --metrics-cert-path argument for the metrics server +- op: add + path: /spec/template/spec/containers/0/args/- + value: --metrics-cert-path=/tmp/k8s-metrics-server/metrics-certs + +# Add the metrics-server certs volume configuration +- op: add + path: /spec/template/spec/volumes/- + value: + name: metrics-certs + secret: + secretName: metrics-server-cert + optional: false + items: + - key: ca.crt + path: ca.crt + - key: tls.crt + path: tls.crt + - key: tls.key + path: tls.key diff --git a/config/default/kustomization.yaml b/config/default/kustomization.yaml index b36eb91f..1d6e410b 100644 --- a/config/default/kustomization.yaml +++ b/config/default/kustomization.yaml @@ -11,7 +11,6 @@ namePrefix: ironic-operator- # Labels to add to all resources and selectors. #labels: #- includeSelectors: true -# includeTemplates: true # pairs: # someName: someValue @@ -23,59 +22,215 @@ resources: # crd/kustomization.yaml - ../webhook # [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. 'WEBHOOK' components are required. -- ../certmanager +#- ../certmanager # [PROMETHEUS] To enable prometheus monitor, uncomment all sections with 'PROMETHEUS'. #- ../prometheus +# [METRICS] Expose the controller manager metrics service. +- metrics_service.yaml +# [NETWORK POLICY] Protect the /metrics endpoint and Webhook Server with NetworkPolicy. +# Only Pod(s) running a namespace labeled with 'metrics: enabled' will be able to gather the metrics. +# Only CR(s) which requires webhooks and are applied on namespaces labeled with 'webhooks: enabled' will +# be able to communicate with the Webhook Server. +#- ../network-policy +# Uncomment the patches line if you enable Metrics patches: -# Protect the /metrics endpoint by putting it behind auth. -# If you want your controller-manager to expose the /metrics -# endpoint w/o any authn/z, please comment the following line. -- path: manager_auth_proxy_patch.yaml +# Injects our custom images (ENV variable settings) +- path: manager_default_images.yaml +# [METRICS] The following patch will enable the metrics endpoint using HTTPS and the port :8443. +# More info: https://book.kubebuilder.io/reference/metrics +- path: manager_metrics_patch.yaml + target: + kind: Deployment -# Mount the controller config file for loading manager configurations -# through a ComponentConfig type -#- path: manager_config_patch.yaml +# Uncomment the patches line if you enable Metrics and CertManager +# [METRICS-WITH-CERTS] To enable metrics protected with certManager, uncomment the following line. +# This patch will protect the metrics with certManager self-signed certs. +#- path: cert_metrics_manager_patch.yaml +# target: +# kind: Deployment # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in # crd/kustomization.yaml - path: manager_webhook_patch.yaml + target: + kind: Deployment -# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. -# Uncomment 'CERTMANAGER' sections in crd/kustomization.yaml to enable the CA injection in the admission webhooks. -# 'CERTMANAGER' needs to be enabled to use ca injection -- path: mutatingwebhookcainjection_patch.yaml -- path: validatingwebhookcainjection_patch.yaml - -# Injects our custom images (ENV variable settings) -- path: manager_default_images.yaml - -# the following config is for teaching kustomize how to do var substitution -vars: [] # [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER' prefix. -#- name: CERTIFICATE_NAMESPACE # namespace of the certificate CR -# objref: -# kind: Certificate -# group: cert-manager.io -# version: v1 -# name: serving-cert # this name should match the one in certificate.yaml -# fieldref: -# fieldpath: metadata.namespace -#- name: CERTIFICATE_NAME -# objref: -# kind: Certificate -# group: cert-manager.io -# version: v1 -# name: serving-cert # this name should match the one in certificate.yaml -#- name: SERVICE_NAMESPACE # namespace of the service -# objref: -# kind: Service -# version: v1 -# name: webhook-service -# fieldref: -# fieldpath: metadata.namespace -#- name: SERVICE_NAME -# objref: -# kind: Service -# version: v1 -# name: webhook-service +# Uncomment the following replacements to add the cert-manager CA injection annotations +#replacements: +# - source: # Uncomment the following block to enable certificates for metrics +# kind: Service +# version: v1 +# name: controller-manager-metrics-service +# fieldPath: metadata.name +# targets: +# - select: +# kind: Certificate +# group: cert-manager.io +# version: v1 +# name: metrics-certs +# fieldPaths: +# - spec.dnsNames.0 +# - spec.dnsNames.1 +# options: +# delimiter: '.' +# index: 0 +# create: true +# - select: # Uncomment the following to set the Service name for TLS config in Prometheus ServiceMonitor +# kind: ServiceMonitor +# group: monitoring.coreos.com +# version: v1 +# name: controller-manager-metrics-monitor +# fieldPaths: +# - spec.endpoints.0.tlsConfig.serverName +# options: +# delimiter: '.' +# index: 0 +# create: true +# +# - source: +# kind: Service +# version: v1 +# name: controller-manager-metrics-service +# fieldPath: metadata.namespace +# targets: +# - select: +# kind: Certificate +# group: cert-manager.io +# version: v1 +# name: metrics-certs +# fieldPaths: +# - spec.dnsNames.0 +# - spec.dnsNames.1 +# options: +# delimiter: '.' +# index: 1 +# create: true +# - select: # Uncomment the following to set the Service namespace for TLS in Prometheus ServiceMonitor +# kind: ServiceMonitor +# group: monitoring.coreos.com +# version: v1 +# name: controller-manager-metrics-monitor +# fieldPaths: +# - spec.endpoints.0.tlsConfig.serverName +# options: +# delimiter: '.' +# index: 1 +# create: true +# +# - source: # Uncomment the following block if you have any webhook +# kind: Service +# version: v1 +# name: webhook-service +# fieldPath: .metadata.name # Name of the service +# targets: +# - select: +# kind: Certificate +# group: cert-manager.io +# version: v1 +# name: serving-cert +# fieldPaths: +# - .spec.dnsNames.0 +# - .spec.dnsNames.1 +# options: +# delimiter: '.' +# index: 0 +# create: true +# - source: +# kind: Service +# version: v1 +# name: webhook-service +# fieldPath: .metadata.namespace # Namespace of the service +# targets: +# - select: +# kind: Certificate +# group: cert-manager.io +# version: v1 +# name: serving-cert +# fieldPaths: +# - .spec.dnsNames.0 +# - .spec.dnsNames.1 +# options: +# delimiter: '.' +# index: 1 +# create: true +# +# - source: # Uncomment the following block if you have a ValidatingWebhook (--programmatic-validation) +# kind: Certificate +# group: cert-manager.io +# version: v1 +# name: serving-cert # This name should match the one in certificate.yaml +# fieldPath: .metadata.namespace # Namespace of the certificate CR +# targets: +# - select: +# kind: ValidatingWebhookConfiguration +# fieldPaths: +# - .metadata.annotations.[cert-manager.io/inject-ca-from] +# options: +# delimiter: '/' +# index: 0 +# create: true +# - source: +# kind: Certificate +# group: cert-manager.io +# version: v1 +# name: serving-cert +# fieldPath: .metadata.name +# targets: +# - select: +# kind: ValidatingWebhookConfiguration +# fieldPaths: +# - .metadata.annotations.[cert-manager.io/inject-ca-from] +# options: +# delimiter: '/' +# index: 1 +# create: true +# +# - source: # Uncomment the following block if you have a DefaultingWebhook (--defaulting ) +# kind: Certificate +# group: cert-manager.io +# version: v1 +# name: serving-cert +# fieldPath: .metadata.namespace # Namespace of the certificate CR +# targets: +# - select: +# kind: MutatingWebhookConfiguration +# fieldPaths: +# - .metadata.annotations.[cert-manager.io/inject-ca-from] +# options: +# delimiter: '/' +# index: 0 +# create: true +# - source: +# kind: Certificate +# group: cert-manager.io +# version: v1 +# name: serving-cert +# fieldPath: .metadata.name +# targets: +# - select: +# kind: MutatingWebhookConfiguration +# fieldPaths: +# - .metadata.annotations.[cert-manager.io/inject-ca-from] +# options: +# delimiter: '/' +# index: 1 +# create: true +# +# - source: # Uncomment the following block if you have a ConversionWebhook (--conversion) +# kind: Certificate +# group: cert-manager.io +# version: v1 +# name: serving-cert +# fieldPath: .metadata.namespace # Namespace of the certificate CR +# targets: # Do not remove or uncomment the following scaffold marker; required to generate code for target CRD. +# +kubebuilder:scaffold:crdkustomizecainjectionns +# - source: +# kind: Certificate +# group: cert-manager.io +# version: v1 +# name: serving-cert +# fieldPath: .metadata.name +# targets: # Do not remove or uncomment the following scaffold marker; required to generate code for target CRD. +# +kubebuilder:scaffold:crdkustomizecainjectionname diff --git a/config/default/manager_auth_proxy_patch.yaml b/config/default/manager_auth_proxy_patch.yaml deleted file mode 100644 index 0f3d583a..00000000 --- a/config/default/manager_auth_proxy_patch.yaml +++ /dev/null @@ -1,40 +0,0 @@ -# This patch inject a sidecar container which is a HTTP proxy for the -# controller manager, it performs RBAC authorization against the Kubernetes API using SubjectAccessReviews. -apiVersion: apps/v1 -kind: Deployment -metadata: - name: controller-manager - namespace: system -spec: - template: - spec: - containers: - - name: kube-rbac-proxy - securityContext: - allowPrivilegeEscalation: false - # TODO(user): uncomment for common cases that do not require escalating privileges - # capabilities: - # drop: - # - "ALL" - image: quay.io/openstack-k8s-operators/kube-rbac-proxy:v0.16.0 - args: - - "--secure-listen-address=0.0.0.0:8443" - - "--upstream=http://127.0.0.1:8080/" - - "--logtostderr=true" - - "--v=0" - ports: - - containerPort: 8443 - protocol: TCP - name: https - resources: - limits: - cpu: 500m - memory: 128Mi - requests: - cpu: 5m - memory: 64Mi - - name: manager - args: - - "--health-probe-bind-address=:8081" - - "--metrics-bind-address=127.0.0.1:8080" - - "--leader-elect" diff --git a/config/default/manager_config_patch.yaml b/config/default/manager_config_patch.yaml deleted file mode 100644 index 6c400155..00000000 --- a/config/default/manager_config_patch.yaml +++ /dev/null @@ -1,20 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: controller-manager - namespace: system -spec: - template: - spec: - containers: - - name: manager - args: - - "--config=controller_manager_config.yaml" - volumeMounts: - - name: manager-config - mountPath: /controller_manager_config.yaml - subPath: controller_manager_config.yaml - volumes: - - name: manager-config - configMap: - name: manager-config diff --git a/config/default/manager_metrics_patch.yaml b/config/default/manager_metrics_patch.yaml new file mode 100644 index 00000000..2aaef653 --- /dev/null +++ b/config/default/manager_metrics_patch.yaml @@ -0,0 +1,4 @@ +# This patch adds the args to allow exposing the metrics endpoint using HTTPS +- op: add + path: /spec/template/spec/containers/0/args/0 + value: --metrics-bind-address=:8443 diff --git a/config/default/manager_webhook_patch.yaml b/config/default/manager_webhook_patch.yaml index 738de350..963c8a4c 100644 --- a/config/default/manager_webhook_patch.yaml +++ b/config/default/manager_webhook_patch.yaml @@ -1,23 +1,31 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: controller-manager - namespace: system -spec: - template: - spec: - containers: - - name: manager - ports: - - containerPort: 9443 - name: webhook-server - protocol: TCP - volumeMounts: - - mountPath: /tmp/k8s-webhook-server/serving-certs - name: cert - readOnly: true - volumes: - - name: cert - secret: - defaultMode: 420 - secretName: webhook-server-cert +# This patch ensures the webhook certificates are properly mounted in the manager container. +# It configures the necessary arguments, volumes, volume mounts, and container ports. + +# Add the --webhook-cert-path argument for configuring the webhook certificate path +- op: add + path: /spec/template/spec/containers/0/args/- + value: --webhook-cert-path=/tmp/k8s-webhook-server/serving-certs + +# Add the volumeMount for the webhook certificates +- op: add + path: /spec/template/spec/containers/0/volumeMounts/- + value: + mountPath: /tmp/k8s-webhook-server/serving-certs + name: webhook-certs + readOnly: true + +# Add the port configuration for the webhook server +- op: add + path: /spec/template/spec/containers/0/ports/- + value: + containerPort: 9443 + name: webhook-server + protocol: TCP + +# Add the volume configuration for the webhook certificates +- op: add + path: /spec/template/spec/volumes/- + value: + name: webhook-certs + secret: + secretName: webhook-server-cert diff --git a/config/rbac/auth_proxy_service.yaml b/config/default/metrics_service.yaml similarity index 54% rename from config/rbac/auth_proxy_service.yaml rename to config/default/metrics_service.yaml index 4e6b2aa8..04fe3615 100644 --- a/config/rbac/auth_proxy_service.yaml +++ b/config/default/metrics_service.yaml @@ -3,6 +3,8 @@ kind: Service metadata: labels: control-plane: controller-manager + app.kubernetes.io/name: ironic-operator + app.kubernetes.io/managed-by: kustomize name: controller-manager-metrics-service namespace: system spec: @@ -10,6 +12,7 @@ spec: - name: https port: 8443 protocol: TCP - targetPort: https + targetPort: 8443 selector: - openstack.org/operator-name: ironic + control-plane: controller-manager + app.kubernetes.io/name: ironic-operator diff --git a/config/default/mutatingwebhookcainjection_patch.yaml b/config/default/mutatingwebhookcainjection_patch.yaml deleted file mode 100644 index e33ee259..00000000 --- a/config/default/mutatingwebhookcainjection_patch.yaml +++ /dev/null @@ -1,15 +0,0 @@ -# This patch add annotation to admission webhook config and -# the variables $(CERTIFICATE_NAMESPACE) and $(CERTIFICATE_NAME) will be substituted by kustomize. -apiVersion: admissionregistration.k8s.io/v1 -kind: MutatingWebhookConfiguration -metadata: - labels: - app.kubernetes.io/name: mutatingwebhookconfiguration - app.kubernetes.io/instance: mutating-webhook-configuration - app.kubernetes.io/component: webhook - app.kubernetes.io/created-by: ironic-operator - app.kubernetes.io/part-of: ironic-operator - app.kubernetes.io/managed-by: kustomize - name: mutating-webhook-configuration - annotations: - cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) diff --git a/config/default/validatingwebhookcainjection_patch.yaml b/config/default/validatingwebhookcainjection_patch.yaml deleted file mode 100644 index 1b7eec55..00000000 --- a/config/default/validatingwebhookcainjection_patch.yaml +++ /dev/null @@ -1,15 +0,0 @@ -# This patch add annotation to admission webhook config and -# the variables $(CERTIFICATE_NAMESPACE) and $(CERTIFICATE_NAME) will be substituted by kustomize. -apiVersion: admissionregistration.k8s.io/v1 -kind: ValidatingWebhookConfiguration -metadata: - labels: - app.kubernetes.io/name: validatingwebhookconfiguration - app.kubernetes.io/instance: validating-webhook-configuration - app.kubernetes.io/component: webhook - app.kubernetes.io/created-by: ironic-operator - app.kubernetes.io/part-of: ironic-operator - app.kubernetes.io/managed-by: kustomize - name: validating-webhook-configuration - annotations: - cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) diff --git a/config/manager/controller_manager_config.yaml b/config/manager/controller_manager_config.yaml deleted file mode 100644 index 4eead011..00000000 --- a/config/manager/controller_manager_config.yaml +++ /dev/null @@ -1,11 +0,0 @@ -apiVersion: controller-runtime.sigs.k8s.io/v1alpha1 -kind: ControllerManagerConfig -health: - healthProbeBindAddress: :8081 -metrics: - bindAddress: 127.0.0.1:8080 -webhook: - port: 9443 -leaderElection: - leaderElect: true - resourceName: f92b5c2d.openstack.org diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index 5e793dd1..841a9120 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -1,16 +1,8 @@ resources: - manager.yaml - -generatorOptions: - disableNameSuffixHash: true - -configMapGenerator: -- files: - - controller_manager_config.yaml - name: manager-config apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization images: - name: controller - newName: controller - newTag: latest + newName: quay.io/andrewbays/ironic-operator + newTag: v0.0.1 diff --git a/config/manager/manager.yaml b/config/manager/manager.yaml index a564824a..1c6b79f7 100644 --- a/config/manager/manager.yaml +++ b/config/manager/manager.yaml @@ -1,3 +1,11 @@ +apiVersion: v1 +kind: Namespace +metadata: + labels: + control-plane: controller-manager + app.kubernetes.io/name: ironic-operator + app.kubernetes.io/managed-by: kustomize + name: system --- apiVersion: apps/v1 kind: Deployment @@ -6,11 +14,13 @@ metadata: namespace: system labels: control-plane: controller-manager - openstack.org/operator-name: ironic + app.kubernetes.io/name: ironic-operator + app.kubernetes.io/managed-by: kustomize spec: selector: matchLabels: - openstack.org/operator-name: ironic + control-plane: controller-manager + app.kubernetes.io/name: ironic-operator replicas: 1 template: metadata: @@ -18,30 +28,49 @@ spec: kubectl.kubernetes.io/default-container: manager labels: control-plane: controller-manager - openstack.org/operator-name: ironic + app.kubernetes.io/name: ironic-operator spec: + # TODO(user): Uncomment the following code to configure the nodeAffinity expression + # according to the platforms which are supported by your solution. + # It is considered best practice to support multiple architectures. You can + # build your manager image using the makefile target docker-buildx. + # affinity: + # nodeAffinity: + # requiredDuringSchedulingIgnoredDuringExecution: + # nodeSelectorTerms: + # - matchExpressions: + # - key: kubernetes.io/arch + # operator: In + # values: + # - amd64 + # - arm64 + # - ppc64le + # - s390x + # - key: kubernetes.io/os + # operator: In + # values: + # - linux securityContext: + # Projects are configured by default to adhere to the "restricted" Pod Security Standards. + # This ensures that deployments meet the highest security requirements for Kubernetes. + # For more details, see: https://kubernetes.io/docs/concepts/security/pod-security-standards/#restricted runAsNonRoot: true - # TODO(user): For common cases that do not require escalating privileges - # it is recommended to ensure that all your Pods/Containers are restrictive. - # More info: https://kubernetes.io/docs/concepts/security/pod-security-standards/#restricted - # Please uncomment the following code if your project does NOT have to work on old Kubernetes - # versions < 1.19 or on vendors versions which do NOT support this field by default (i.e. Openshift < 4.11 ). - # seccompProfile: - # type: RuntimeDefault + seccompProfile: + type: RuntimeDefault containers: - command: - /manager args: - - --leader-elect + - --leader-elect + - --health-probe-bind-address=:8081 image: controller:latest name: manager + ports: [] securityContext: allowPrivilegeEscalation: false - # TODO(user): uncomment for common cases that do not require escalating privileges - # capabilities: - # drop: - # - "ALL" + capabilities: + drop: + - "ALL" livenessProbe: httpGet: path: /healthz @@ -59,9 +88,11 @@ spec: resources: limits: cpu: 500m - memory: 256Mi + memory: 128Mi requests: cpu: 10m - memory: 128Mi + memory: 64Mi + volumeMounts: [] + volumes: [] serviceAccountName: controller-manager terminationGracePeriodSeconds: 10 diff --git a/config/manifests/bases/ironic-operator.clusterserviceversion.yaml b/config/manifests/bases/ironic-operator.clusterserviceversion.yaml index 547fcb79..559c10e3 100644 --- a/config/manifests/bases/ironic-operator.clusterserviceversion.yaml +++ b/config/manifests/bases/ironic-operator.clusterserviceversion.yaml @@ -19,17 +19,6 @@ spec: apiservicedefinitions: {} customresourcedefinitions: owned: - - description: IronicInspector is the Schema for the ironicinspectors - displayName: Ironic Inspector - kind: IronicInspector - name: ironicinspectors.ironic.openstack.org - version: v1beta1 - - description: IronicNeutronAgent is the Schema for the ML2 networking-baremetal's - agent - displayName: Ironic Neutron Agent - kind: IronicNeutronAgent - name: ironicneutronagents.ironic.openstack.org - version: v1beta1 - description: IronicAPI is the Schema for the ironicapis API displayName: Ironic API kind: IronicAPI @@ -48,6 +37,25 @@ spec: displayName: TLS path: tls version: v1beta1 + - description: IronicInspector is the Schema for the IronicInspector + displayName: Ironic Inspector + kind: IronicInspector + name: ironicinspectors.ironic.openstack.org + specDescriptors: + - description: TLS - Parameters related to the TLS + displayName: TLS + path: tls + version: v1beta1 + - description: IronicNeutronAgent is the Schema for the ML2 baremetal - ironic-neutron-agent + agents + displayName: Ironic Neutron Agent + kind: IronicNeutronAgent + name: ironicneutronagents.ironic.openstack.org + specDescriptors: + - description: TLS - Parameters related to the TLS + displayName: TLS + path: tls + version: v1beta1 - description: Ironic is the Schema for the ironics API displayName: Ironic kind: Ironic diff --git a/config/manifests/kustomization.yaml b/config/manifests/kustomization.yaml index 3e723d45..a0c2c642 100644 --- a/config/manifests/kustomization.yaml +++ b/config/manifests/kustomization.yaml @@ -20,7 +20,8 @@ resources: # # Remove the manager container's "cert" volumeMount, since OLM will create and mount a set of certs. # # Update the indices in this path if adding or removing containers/volumeMounts in the manager's Deployment. # - op: remove -# path: /spec/template/spec/containers/1/volumeMounts/0 + +# path: /spec/template/spec/containers/0/volumeMounts/0 # # Remove the "cert" volume, since OLM will create and mount a set of certs. # # Update the indices in this path if adding or removing volumes in the manager's Deployment. # - op: remove diff --git a/config/network-policy/allow-metrics-traffic.yaml b/config/network-policy/allow-metrics-traffic.yaml new file mode 100644 index 00000000..a18037cf --- /dev/null +++ b/config/network-policy/allow-metrics-traffic.yaml @@ -0,0 +1,27 @@ +# This NetworkPolicy allows ingress traffic +# with Pods running on namespaces labeled with 'metrics: enabled'. Only Pods on those +# namespaces are able to gather data from the metrics endpoint. +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + labels: + app.kubernetes.io/name: ironic-operator + app.kubernetes.io/managed-by: kustomize + name: allow-metrics-traffic + namespace: system +spec: + podSelector: + matchLabels: + control-plane: controller-manager + app.kubernetes.io/name: ironic-operator + policyTypes: + - Ingress + ingress: + # This allows ingress traffic from any namespace with the label metrics: enabled + - from: + - namespaceSelector: + matchLabels: + metrics: enabled # Only from namespaces with this label + ports: + - port: 8443 + protocol: TCP diff --git a/config/network-policy/allow-webhook-traffic.yaml b/config/network-policy/allow-webhook-traffic.yaml new file mode 100644 index 00000000..38037b79 --- /dev/null +++ b/config/network-policy/allow-webhook-traffic.yaml @@ -0,0 +1,27 @@ +# This NetworkPolicy allows ingress traffic to your webhook server running +# as part of the controller-manager from specific namespaces and pods. CR(s) which uses webhooks +# will only work when applied in namespaces labeled with 'webhook: enabled' +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + labels: + app.kubernetes.io/name: ironic-operator + app.kubernetes.io/managed-by: kustomize + name: allow-webhook-traffic + namespace: system +spec: + podSelector: + matchLabels: + control-plane: controller-manager + app.kubernetes.io/name: ironic-operator + policyTypes: + - Ingress + ingress: + # This allows ingress traffic from any namespace with the label webhook: enabled + - from: + - namespaceSelector: + matchLabels: + webhook: enabled # Only from namespaces with this label + ports: + - port: 443 + protocol: TCP diff --git a/config/network-policy/kustomization.yaml b/config/network-policy/kustomization.yaml new file mode 100644 index 00000000..0872bee1 --- /dev/null +++ b/config/network-policy/kustomization.yaml @@ -0,0 +1,3 @@ +resources: +- allow-webhook-traffic.yaml +- allow-metrics-traffic.yaml diff --git a/config/prometheus/kustomization.yaml b/config/prometheus/kustomization.yaml index ed137168..fdc5481b 100644 --- a/config/prometheus/kustomization.yaml +++ b/config/prometheus/kustomization.yaml @@ -1,2 +1,11 @@ resources: - monitor.yaml + +# [PROMETHEUS-WITH-CERTS] The following patch configures the ServiceMonitor in ../prometheus +# to securely reference certificates created and managed by cert-manager. +# Additionally, ensure that you uncomment the [METRICS WITH CERTMANAGER] patch under config/default/kustomization.yaml +# to mount the "metrics-server-cert" secret in the Manager Deployment. +#patches: +# - path: monitor_tls_patch.yaml +# target: +# kind: ServiceMonitor diff --git a/config/prometheus/monitor.yaml b/config/prometheus/monitor.yaml index 1d4fbc29..6a740030 100644 --- a/config/prometheus/monitor.yaml +++ b/config/prometheus/monitor.yaml @@ -1,20 +1,27 @@ - # Prometheus Monitor Service (Metrics) apiVersion: monitoring.coreos.com/v1 kind: ServiceMonitor metadata: labels: control-plane: controller-manager + app.kubernetes.io/name: ironic-operator + app.kubernetes.io/managed-by: kustomize name: controller-manager-metrics-monitor namespace: system spec: endpoints: - path: /metrics - port: https + port: https # Ensure this is the name of the port that exposes HTTPS metrics scheme: https bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token tlsConfig: + # TODO(user): The option insecureSkipVerify: true is not recommended for production since it disables + # certificate verification, exposing the system to potential man-in-the-middle attacks. + # For production environments, it is recommended to use cert-manager for automatic TLS certificate management. + # To apply this configuration, enable cert-manager and use the patch located at config/prometheus/servicemonitor_tls_patch.yaml, + # which securely references the certificate from the 'metrics-server-cert' secret. insecureSkipVerify: true selector: matchLabels: - openstack.org/operator-name: ironic + control-plane: controller-manager + app.kubernetes.io/name: ironic-operator diff --git a/config/prometheus/monitor_tls_patch.yaml b/config/prometheus/monitor_tls_patch.yaml new file mode 100644 index 00000000..5bf84ce0 --- /dev/null +++ b/config/prometheus/monitor_tls_patch.yaml @@ -0,0 +1,19 @@ +# Patch for Prometheus ServiceMonitor to enable secure TLS configuration +# using certificates managed by cert-manager +- op: replace + path: /spec/endpoints/0/tlsConfig + value: + # SERVICE_NAME and SERVICE_NAMESPACE will be substituted by kustomize + serverName: SERVICE_NAME.SERVICE_NAMESPACE.svc + insecureSkipVerify: false + ca: + secret: + name: metrics-server-cert + key: ca.crt + cert: + secret: + name: metrics-server-cert + key: tls.crt + keySecret: + name: metrics-server-cert + key: tls.key diff --git a/config/rbac/ironic_admin_role.yaml b/config/rbac/ironic_admin_role.yaml new file mode 100644 index 00000000..5e66ccd9 --- /dev/null +++ b/config/rbac/ironic_admin_role.yaml @@ -0,0 +1,27 @@ +# This rule is not used by the project ironic-operator itself. +# It is provided to allow the cluster admin to help manage permissions for users. +# +# Grants full permissions ('*') over ironic.openstack.org. +# This role is intended for users authorized to modify roles and bindings within the cluster, +# enabling them to delegate specific permissions to other users or groups as needed. + +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: ironic-operator + app.kubernetes.io/managed-by: kustomize + name: ironic-admin-role +rules: +- apiGroups: + - ironic.openstack.org + resources: + - ironics + verbs: + - '*' +- apiGroups: + - ironic.openstack.org + resources: + - ironics/status + verbs: + - get diff --git a/config/rbac/ironic_editor_role.yaml b/config/rbac/ironic_editor_role.yaml index ab375627..bb5673ca 100644 --- a/config/rbac/ironic_editor_role.yaml +++ b/config/rbac/ironic_editor_role.yaml @@ -1,7 +1,16 @@ -# permissions for end users to edit ironics. +# This rule is not used by the project ironic-operator itself. +# It is provided to allow the cluster admin to help manage permissions for users. +# +# Grants permissions to create, update, and delete resources within the ironic.openstack.org. +# This role is intended for users who need to manage these resources +# but should not control RBAC or manage permissions for others. + apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: + labels: + app.kubernetes.io/name: ironic-operator + app.kubernetes.io/managed-by: kustomize name: ironic-editor-role rules: - apiGroups: diff --git a/config/rbac/ironic_viewer_role.yaml b/config/rbac/ironic_viewer_role.yaml index 0d574c68..5e76b1b5 100644 --- a/config/rbac/ironic_viewer_role.yaml +++ b/config/rbac/ironic_viewer_role.yaml @@ -1,7 +1,16 @@ -# permissions for end users to view ironics. +# This rule is not used by the project ironic-operator itself. +# It is provided to allow the cluster admin to help manage permissions for users. +# +# Grants read-only access to ironic.openstack.org resources. +# This role is intended for users who need visibility into these resources +# without permissions to modify them. It is ideal for monitoring purposes and limited-access viewing. + apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: + labels: + app.kubernetes.io/name: ironic-operator + app.kubernetes.io/managed-by: kustomize name: ironic-viewer-role rules: - apiGroups: diff --git a/config/rbac/ironicapi_admin_role.yaml b/config/rbac/ironicapi_admin_role.yaml new file mode 100644 index 00000000..50158592 --- /dev/null +++ b/config/rbac/ironicapi_admin_role.yaml @@ -0,0 +1,27 @@ +# This rule is not used by the project ironic-operator itself. +# It is provided to allow the cluster admin to help manage permissions for users. +# +# Grants full permissions ('*') over ironic.openstack.org. +# This role is intended for users authorized to modify roles and bindings within the cluster, +# enabling them to delegate specific permissions to other users or groups as needed. + +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: ironic-operator + app.kubernetes.io/managed-by: kustomize + name: ironicapi-admin-role +rules: +- apiGroups: + - ironic.openstack.org + resources: + - ironicapis + verbs: + - '*' +- apiGroups: + - ironic.openstack.org + resources: + - ironicapis/status + verbs: + - get diff --git a/config/rbac/ironicapi_editor_role.yaml b/config/rbac/ironicapi_editor_role.yaml index 8590edaa..f042604d 100644 --- a/config/rbac/ironicapi_editor_role.yaml +++ b/config/rbac/ironicapi_editor_role.yaml @@ -1,7 +1,16 @@ -# permissions for end users to edit ironicapis. +# This rule is not used by the project ironic-operator itself. +# It is provided to allow the cluster admin to help manage permissions for users. +# +# Grants permissions to create, update, and delete resources within the ironic.openstack.org. +# This role is intended for users who need to manage these resources +# but should not control RBAC or manage permissions for others. + apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: + labels: + app.kubernetes.io/name: ironic-operator + app.kubernetes.io/managed-by: kustomize name: ironicapi-editor-role rules: - apiGroups: diff --git a/config/rbac/ironicapi_viewer_role.yaml b/config/rbac/ironicapi_viewer_role.yaml index 00b88102..c355f538 100644 --- a/config/rbac/ironicapi_viewer_role.yaml +++ b/config/rbac/ironicapi_viewer_role.yaml @@ -1,7 +1,16 @@ -# permissions for end users to view ironicapis. +# This rule is not used by the project ironic-operator itself. +# It is provided to allow the cluster admin to help manage permissions for users. +# +# Grants read-only access to ironic.openstack.org resources. +# This role is intended for users who need visibility into these resources +# without permissions to modify them. It is ideal for monitoring purposes and limited-access viewing. + apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: + labels: + app.kubernetes.io/name: ironic-operator + app.kubernetes.io/managed-by: kustomize name: ironicapi-viewer-role rules: - apiGroups: diff --git a/config/rbac/ironicconductor_admin_role.yaml b/config/rbac/ironicconductor_admin_role.yaml new file mode 100644 index 00000000..ddeda1a4 --- /dev/null +++ b/config/rbac/ironicconductor_admin_role.yaml @@ -0,0 +1,27 @@ +# This rule is not used by the project ironic-operator itself. +# It is provided to allow the cluster admin to help manage permissions for users. +# +# Grants full permissions ('*') over ironic.openstack.org. +# This role is intended for users authorized to modify roles and bindings within the cluster, +# enabling them to delegate specific permissions to other users or groups as needed. + +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: ironic-operator + app.kubernetes.io/managed-by: kustomize + name: ironicconductor-admin-role +rules: +- apiGroups: + - ironic.openstack.org + resources: + - ironicconductors + verbs: + - '*' +- apiGroups: + - ironic.openstack.org + resources: + - ironicconductors/status + verbs: + - get diff --git a/config/rbac/ironicconductor_editor_role.yaml b/config/rbac/ironicconductor_editor_role.yaml index 47a78cb6..c29b2418 100644 --- a/config/rbac/ironicconductor_editor_role.yaml +++ b/config/rbac/ironicconductor_editor_role.yaml @@ -1,7 +1,16 @@ -# permissions for end users to edit ironicconductors. +# This rule is not used by the project ironic-operator itself. +# It is provided to allow the cluster admin to help manage permissions for users. +# +# Grants permissions to create, update, and delete resources within the ironic.openstack.org. +# This role is intended for users who need to manage these resources +# but should not control RBAC or manage permissions for others. + apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: + labels: + app.kubernetes.io/name: ironic-operator + app.kubernetes.io/managed-by: kustomize name: ironicconductor-editor-role rules: - apiGroups: diff --git a/config/rbac/ironicconductor_viewer_role.yaml b/config/rbac/ironicconductor_viewer_role.yaml index 58a5c87b..f55a05f2 100644 --- a/config/rbac/ironicconductor_viewer_role.yaml +++ b/config/rbac/ironicconductor_viewer_role.yaml @@ -1,7 +1,16 @@ -# permissions for end users to view ironicapis. +# This rule is not used by the project ironic-operator itself. +# It is provided to allow the cluster admin to help manage permissions for users. +# +# Grants read-only access to ironic.openstack.org resources. +# This role is intended for users who need visibility into these resources +# without permissions to modify them. It is ideal for monitoring purposes and limited-access viewing. + apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: + labels: + app.kubernetes.io/name: ironic-operator + app.kubernetes.io/managed-by: kustomize name: ironicconductor-viewer-role rules: - apiGroups: diff --git a/config/rbac/ironicinspector_admin_role.yaml b/config/rbac/ironicinspector_admin_role.yaml new file mode 100644 index 00000000..5f2139c5 --- /dev/null +++ b/config/rbac/ironicinspector_admin_role.yaml @@ -0,0 +1,27 @@ +# This rule is not used by the project ironic-operator itself. +# It is provided to allow the cluster admin to help manage permissions for users. +# +# Grants full permissions ('*') over ironic.openstack.org. +# This role is intended for users authorized to modify roles and bindings within the cluster, +# enabling them to delegate specific permissions to other users or groups as needed. + +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: ironic-operator + app.kubernetes.io/managed-by: kustomize + name: ironicinspector-admin-role +rules: +- apiGroups: + - ironic.openstack.org + resources: + - ironicinspectors + verbs: + - '*' +- apiGroups: + - ironic.openstack.org + resources: + - ironicinspectors/status + verbs: + - get diff --git a/config/rbac/ironicinspector_editor_role.yaml b/config/rbac/ironicinspector_editor_role.yaml new file mode 100644 index 00000000..415321e2 --- /dev/null +++ b/config/rbac/ironicinspector_editor_role.yaml @@ -0,0 +1,33 @@ +# This rule is not used by the project ironic-operator itself. +# It is provided to allow the cluster admin to help manage permissions for users. +# +# Grants permissions to create, update, and delete resources within the ironic.openstack.org. +# This role is intended for users who need to manage these resources +# but should not control RBAC or manage permissions for others. + +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: ironic-operator + app.kubernetes.io/managed-by: kustomize + name: ironicinspector-editor-role +rules: +- apiGroups: + - ironic.openstack.org + resources: + - ironicinspectors + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - ironic.openstack.org + resources: + - ironicinspectors/status + verbs: + - get diff --git a/config/rbac/ironicinspector_viewer_role.yaml b/config/rbac/ironicinspector_viewer_role.yaml new file mode 100644 index 00000000..138e76bc --- /dev/null +++ b/config/rbac/ironicinspector_viewer_role.yaml @@ -0,0 +1,29 @@ +# This rule is not used by the project ironic-operator itself. +# It is provided to allow the cluster admin to help manage permissions for users. +# +# Grants read-only access to ironic.openstack.org resources. +# This role is intended for users who need visibility into these resources +# without permissions to modify them. It is ideal for monitoring purposes and limited-access viewing. + +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: ironic-operator + app.kubernetes.io/managed-by: kustomize + name: ironicinspector-viewer-role +rules: +- apiGroups: + - ironic.openstack.org + resources: + - ironicinspectors + verbs: + - get + - list + - watch +- apiGroups: + - ironic.openstack.org + resources: + - ironicinspectors/status + verbs: + - get diff --git a/config/rbac/ironicneutronagent_admin_role.yaml b/config/rbac/ironicneutronagent_admin_role.yaml new file mode 100644 index 00000000..e6155397 --- /dev/null +++ b/config/rbac/ironicneutronagent_admin_role.yaml @@ -0,0 +1,27 @@ +# This rule is not used by the project ironic-operator itself. +# It is provided to allow the cluster admin to help manage permissions for users. +# +# Grants full permissions ('*') over ironic.openstack.org. +# This role is intended for users authorized to modify roles and bindings within the cluster, +# enabling them to delegate specific permissions to other users or groups as needed. + +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: ironic-operator + app.kubernetes.io/managed-by: kustomize + name: ironicneutronagent-admin-role +rules: +- apiGroups: + - ironic.openstack.org + resources: + - ironicneutronagents + verbs: + - '*' +- apiGroups: + - ironic.openstack.org + resources: + - ironicneutronagents/status + verbs: + - get diff --git a/config/rbac/ironicneutronagent_editor_role.yaml b/config/rbac/ironicneutronagent_editor_role.yaml new file mode 100644 index 00000000..0367dcc1 --- /dev/null +++ b/config/rbac/ironicneutronagent_editor_role.yaml @@ -0,0 +1,33 @@ +# This rule is not used by the project ironic-operator itself. +# It is provided to allow the cluster admin to help manage permissions for users. +# +# Grants permissions to create, update, and delete resources within the ironic.openstack.org. +# This role is intended for users who need to manage these resources +# but should not control RBAC or manage permissions for others. + +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: ironic-operator + app.kubernetes.io/managed-by: kustomize + name: ironicneutronagent-editor-role +rules: +- apiGroups: + - ironic.openstack.org + resources: + - ironicneutronagents + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - ironic.openstack.org + resources: + - ironicneutronagents/status + verbs: + - get diff --git a/config/rbac/ironicneutronagent_viewer_role.yaml b/config/rbac/ironicneutronagent_viewer_role.yaml new file mode 100644 index 00000000..88f96fe2 --- /dev/null +++ b/config/rbac/ironicneutronagent_viewer_role.yaml @@ -0,0 +1,29 @@ +# This rule is not used by the project ironic-operator itself. +# It is provided to allow the cluster admin to help manage permissions for users. +# +# Grants read-only access to ironic.openstack.org resources. +# This role is intended for users who need visibility into these resources +# without permissions to modify them. It is ideal for monitoring purposes and limited-access viewing. + +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: ironic-operator + app.kubernetes.io/managed-by: kustomize + name: ironicneutronagent-viewer-role +rules: +- apiGroups: + - ironic.openstack.org + resources: + - ironicneutronagents + verbs: + - get + - list + - watch +- apiGroups: + - ironic.openstack.org + resources: + - ironicneutronagents/status + verbs: + - get diff --git a/config/rbac/kustomization.yaml b/config/rbac/kustomization.yaml index 731832a6..f8d0b0f9 100644 --- a/config/rbac/kustomization.yaml +++ b/config/rbac/kustomization.yaml @@ -9,10 +9,31 @@ resources: - role_binding.yaml - leader_election_role.yaml - leader_election_role_binding.yaml -# Comment the following 4 lines if you want to disable -# the auth proxy (https://github.com/brancz/kube-rbac-proxy) -# which protects your /metrics endpoint. -- auth_proxy_service.yaml -- auth_proxy_role.yaml -- auth_proxy_role_binding.yaml -- auth_proxy_client_clusterrole.yaml +# The following RBAC configurations are used to protect +# the metrics endpoint with authn/authz. These configurations +# ensure that only authorized users and service accounts +# can access the metrics endpoint. Comment the following +# permissions if you want to disable this protection. +# More info: https://book.kubebuilder.io/reference/metrics.html +- metrics_auth_role.yaml +- metrics_auth_role_binding.yaml +- metrics_reader_role.yaml +# For each CRD, "Admin", "Editor" and "Viewer" roles are scaffolded by +# default, aiding admins in cluster management. Those roles are +# not used by the ironic-operator itself. You can comment the following lines +# if you do not want those helpers be installed with your Project. +- ironicneutronagent_admin_role.yaml +- ironicneutronagent_editor_role.yaml +- ironicneutronagent_viewer_role.yaml +- ironicinspector_admin_role.yaml +- ironicinspector_editor_role.yaml +- ironicinspector_viewer_role.yaml +- ironicconductor_admin_role.yaml +- ironicconductor_editor_role.yaml +- ironicconductor_viewer_role.yaml +- ironicapi_admin_role.yaml +- ironicapi_editor_role.yaml +- ironicapi_viewer_role.yaml +- ironic_admin_role.yaml +- ironic_editor_role.yaml +- ironic_viewer_role.yaml diff --git a/config/rbac/leader_election_role.yaml b/config/rbac/leader_election_role.yaml index 4190ec80..18f5e1d4 100644 --- a/config/rbac/leader_election_role.yaml +++ b/config/rbac/leader_election_role.yaml @@ -2,6 +2,9 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: + labels: + app.kubernetes.io/name: ironic-operator + app.kubernetes.io/managed-by: kustomize name: leader-election-role rules: - apiGroups: diff --git a/config/rbac/leader_election_role_binding.yaml b/config/rbac/leader_election_role_binding.yaml index 1d1321ed..e34eb8d0 100644 --- a/config/rbac/leader_election_role_binding.yaml +++ b/config/rbac/leader_election_role_binding.yaml @@ -1,6 +1,9 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: + labels: + app.kubernetes.io/name: ironic-operator + app.kubernetes.io/managed-by: kustomize name: leader-election-rolebinding roleRef: apiGroup: rbac.authorization.k8s.io diff --git a/config/rbac/auth_proxy_role.yaml b/config/rbac/metrics_auth_role.yaml similarity index 90% rename from config/rbac/auth_proxy_role.yaml rename to config/rbac/metrics_auth_role.yaml index 80e1857c..32d2e4ec 100644 --- a/config/rbac/auth_proxy_role.yaml +++ b/config/rbac/metrics_auth_role.yaml @@ -1,7 +1,7 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: - name: proxy-role + name: metrics-auth-role rules: - apiGroups: - authentication.k8s.io diff --git a/config/rbac/auth_proxy_role_binding.yaml b/config/rbac/metrics_auth_role_binding.yaml similarity index 79% rename from config/rbac/auth_proxy_role_binding.yaml rename to config/rbac/metrics_auth_role_binding.yaml index ec7acc0a..e775d67f 100644 --- a/config/rbac/auth_proxy_role_binding.yaml +++ b/config/rbac/metrics_auth_role_binding.yaml @@ -1,11 +1,11 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: - name: proxy-rolebinding + name: metrics-auth-rolebinding roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole - name: proxy-role + name: metrics-auth-role subjects: - kind: ServiceAccount name: controller-manager diff --git a/config/rbac/auth_proxy_client_clusterrole.yaml b/config/rbac/metrics_reader_role.yaml similarity index 100% rename from config/rbac/auth_proxy_client_clusterrole.yaml rename to config/rbac/metrics_reader_role.yaml diff --git a/config/rbac/role_binding.yaml b/config/rbac/role_binding.yaml index 2070ede4..05856519 100644 --- a/config/rbac/role_binding.yaml +++ b/config/rbac/role_binding.yaml @@ -1,6 +1,9 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: + labels: + app.kubernetes.io/name: ironic-operator + app.kubernetes.io/managed-by: kustomize name: manager-rolebinding roleRef: apiGroup: rbac.authorization.k8s.io diff --git a/config/rbac/service_account.yaml b/config/rbac/service_account.yaml index 7cd6025b..f625e91b 100644 --- a/config/rbac/service_account.yaml +++ b/config/rbac/service_account.yaml @@ -1,5 +1,8 @@ apiVersion: v1 kind: ServiceAccount metadata: + labels: + app.kubernetes.io/name: ironic-operator + app.kubernetes.io/managed-by: kustomize name: controller-manager namespace: system diff --git a/config/scorecard/kustomization.yaml b/config/scorecard/kustomization.yaml index a9a84a85..54e8aa50 100644 --- a/config/scorecard/kustomization.yaml +++ b/config/scorecard/kustomization.yaml @@ -1,16 +1,18 @@ resources: - bases/config.yaml +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization patches: - path: patches/basic.config.yaml target: group: scorecard.operatorframework.io - version: v1alpha3 kind: Configuration name: config + version: v1alpha3 - path: patches/olm.config.yaml target: group: scorecard.operatorframework.io - version: v1alpha3 kind: Configuration name: config -#+kubebuilder:scaffold:patches + version: v1alpha3 +# +kubebuilder:scaffold:patches diff --git a/config/scorecard/patches/basic.config.yaml b/config/scorecard/patches/basic.config.yaml index 4a6c8167..8237b70d 100644 --- a/config/scorecard/patches/basic.config.yaml +++ b/config/scorecard/patches/basic.config.yaml @@ -4,7 +4,7 @@ entrypoint: - scorecard-test - basic-check-spec - image: quay.io/operator-framework/scorecard-test:v1.22.2 + image: quay.io/operator-framework/scorecard-test:v1.41.1 labels: suite: basic test: basic-check-spec-test diff --git a/config/scorecard/patches/olm.config.yaml b/config/scorecard/patches/olm.config.yaml index c342410a..416660a7 100644 --- a/config/scorecard/patches/olm.config.yaml +++ b/config/scorecard/patches/olm.config.yaml @@ -4,7 +4,7 @@ entrypoint: - scorecard-test - olm-bundle-validation - image: quay.io/operator-framework/scorecard-test:v1.22.2 + image: quay.io/operator-framework/scorecard-test:v1.41.1 labels: suite: olm test: olm-bundle-validation-test @@ -14,7 +14,7 @@ entrypoint: - scorecard-test - olm-crds-have-validation - image: quay.io/operator-framework/scorecard-test:v1.22.2 + image: quay.io/operator-framework/scorecard-test:v1.41.1 labels: suite: olm test: olm-crds-have-validation-test @@ -24,7 +24,7 @@ entrypoint: - scorecard-test - olm-crds-have-resources - image: quay.io/operator-framework/scorecard-test:v1.22.2 + image: quay.io/operator-framework/scorecard-test:v1.41.1 labels: suite: olm test: olm-crds-have-resources-test @@ -34,7 +34,7 @@ entrypoint: - scorecard-test - olm-spec-descriptors - image: quay.io/operator-framework/scorecard-test:v1.22.2 + image: quay.io/operator-framework/scorecard-test:v1.41.1 labels: suite: olm test: olm-spec-descriptors-test @@ -44,7 +44,7 @@ entrypoint: - scorecard-test - olm-status-descriptors - image: quay.io/operator-framework/scorecard-test:v1.22.2 + image: quay.io/operator-framework/scorecard-test:v1.41.1 labels: suite: olm test: olm-status-descriptors-test diff --git a/config/webhook/kustomizeconfig.yaml b/config/webhook/kustomizeconfig.yaml index f06d78e2..206316e5 100644 --- a/config/webhook/kustomizeconfig.yaml +++ b/config/webhook/kustomizeconfig.yaml @@ -1,25 +1,22 @@ -# the following config is for teaching kustomize where to look at when substituting vars. +# the following config is for teaching kustomize where to look at when substituting nameReference. # It requires kustomize v2.1.0 or newer to work properly. nameReference: - kind: Service version: v1 fieldSpecs: - - kind: ValidatingWebhookConfiguration + - kind: MutatingWebhookConfiguration group: admissionregistration.k8s.io path: webhooks/clientConfig/service/name - - kind: MutatingWebhookConfiguration + - kind: ValidatingWebhookConfiguration group: admissionregistration.k8s.io path: webhooks/clientConfig/service/name namespace: -- kind: ValidatingWebhookConfiguration +- kind: MutatingWebhookConfiguration group: admissionregistration.k8s.io path: webhooks/clientConfig/service/namespace create: true -- kind: MutatingWebhookConfiguration +- kind: ValidatingWebhookConfiguration group: admissionregistration.k8s.io path: webhooks/clientConfig/service/namespace create: true - -varReference: -- path: metadata/annotations diff --git a/config/webhook/manifests.yaml b/config/webhook/manifests.yaml index 003c1a1c..067b620a 100644 --- a/config/webhook/manifests.yaml +++ b/config/webhook/manifests.yaml @@ -4,6 +4,26 @@ kind: MutatingWebhookConfiguration metadata: name: mutating-webhook-configuration webhooks: +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /mutate-ironic-openstack-org-v1beta1-ironic + failurePolicy: Fail + name: mironic-v1beta1.kb.io + rules: + - apiGroups: + - ironic.openstack.org + apiVersions: + - v1beta1 + operations: + - CREATE + - UPDATE + resources: + - ironics + sideEffects: None - admissionReviewVersions: - v1 clientConfig: @@ -30,6 +50,26 @@ kind: ValidatingWebhookConfiguration metadata: name: validating-webhook-configuration webhooks: +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /validate-ironic-openstack-org-v1beta1-ironic + failurePolicy: Fail + name: vironic-v1beta1.kb.io + rules: + - apiGroups: + - ironic.openstack.org + apiVersions: + - v1beta1 + operations: + - CREATE + - UPDATE + resources: + - ironics + sideEffects: None - admissionReviewVersions: - v1 clientConfig: diff --git a/config/webhook/service.yaml b/config/webhook/service.yaml index 2c039bdf..41c5e5f8 100644 --- a/config/webhook/service.yaml +++ b/config/webhook/service.yaml @@ -2,11 +2,7 @@ apiVersion: v1 kind: Service metadata: labels: - app.kubernetes.io/name: service - app.kubernetes.io/instance: webhook-service - app.kubernetes.io/component: webhook - app.kubernetes.io/created-by: ironic-operator - app.kubernetes.io/part-of: ironic-operator + app.kubernetes.io/name: ironic-operator app.kubernetes.io/managed-by: kustomize name: webhook-service namespace: system @@ -16,4 +12,5 @@ spec: protocol: TCP targetPort: 9443 selector: - openstack.org/operator-name: ironic + control-plane: controller-manager + app.kubernetes.io/name: ironic-operator diff --git a/go.mod b/go.mod index 61116980..94096f79 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.24.4 require ( github.com/go-logr/logr v1.4.3 + github.com/google/uuid v1.6.0 github.com/k8snetworkplumbingwg/network-attachment-definition-client v1.7.7 github.com/onsi/ginkgo/v2 v2.27.1 github.com/onsi/gomega v1.38.2 @@ -12,8 +13,10 @@ require ( github.com/openstack-k8s-operators/ironic-operator/api v0.0.0-00010101000000-000000000000 github.com/openstack-k8s-operators/keystone-operator/api v0.6.1-0.20251027074845-ed8154b20ad1 github.com/openstack-k8s-operators/lib-common/modules/common v0.6.1-0.20251027074416-ab5c045dbe00 - github.com/openstack-k8s-operators/lib-common/modules/test v0.6.1-0.20251027074416-ab5c045dbe00 + github.com/openstack-k8s-operators/lib-common/modules/test v0.6.1-0.20250929092825-4c2402451077 github.com/openstack-k8s-operators/mariadb-operator/api v0.6.1-0.20251015110425-ad0381ce8cd4 + go.uber.org/zap v1.27.0 + gopkg.in/yaml.v3 v3.0.1 k8s.io/api v0.31.13 k8s.io/apimachinery v0.31.13 k8s.io/client-go v0.31.13 @@ -21,21 +24,21 @@ require ( sigs.k8s.io/controller-runtime v0.19.7 ) -require ( - github.com/google/uuid v1.6.0 - go.uber.org/zap v1.27.0 - gopkg.in/yaml.v3 v3.0.1 -) - require ( github.com/Masterminds/semver/v3 v3.4.0 // indirect + github.com/antlr4-go/antlr/v4 v4.13.0 // indirect + github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect github.com/beorn7/perks v1.0.1 // indirect + github.com/blang/semver/v4 v4.0.0 // indirect + github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/emicklei/go-restful/v3 v3.12.2 // indirect github.com/evanphx/json-patch/v5 v5.9.11 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/fxamacker/cbor/v2 v2.9.0 // indirect + github.com/go-logr/stdr v1.2.2 // indirect github.com/go-logr/zapr v1.3.0 // indirect github.com/go-openapi/jsonpointer v0.21.1 // indirect github.com/go-openapi/jsonreference v0.21.0 // indirect @@ -44,12 +47,15 @@ require ( github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.4 // indirect + github.com/google/cel-go v0.20.1 // indirect github.com/google/gnostic-models v0.7.0 // indirect github.com/google/go-cmp v0.7.0 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 // indirect github.com/gophercloud/gophercloud/v2 v2.8.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect github.com/imdario/mergo v0.3.16 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/mailru/easyjson v0.9.0 // indirect @@ -64,8 +70,19 @@ require ( github.com/prometheus/common v0.65.0 // indirect github.com/prometheus/procfs v0.16.1 // indirect github.com/rabbitmq/cluster-operator/v2 v2.16.0 // indirect + github.com/spf13/cobra v1.9.1 // indirect github.com/spf13/pflag v1.0.7 // indirect + github.com/stoewer/go-strcase v1.2.0 // indirect github.com/x448/float16 v0.8.4 // indirect + go.opentelemetry.io/auto/sdk v1.1.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 // indirect + go.opentelemetry.io/otel v1.34.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0 // indirect + go.opentelemetry.io/otel/metric v1.34.0 // indirect + go.opentelemetry.io/otel/sdk v1.34.0 // indirect + go.opentelemetry.io/otel/trace v1.34.0 // indirect + go.opentelemetry.io/proto/otlp v1.3.1 // indirect go.uber.org/automaxprocs v1.6.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.yaml.in/yaml/v2 v2.4.2 // indirect @@ -81,11 +98,18 @@ require ( golang.org/x/time v0.12.0 // indirect golang.org/x/tools v0.36.0 // indirect gomodules.xyz/jsonpatch/v2 v2.5.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f // indirect + google.golang.org/grpc v1.71.1 // indirect google.golang.org/protobuf v1.36.7 // indirect gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect k8s.io/apiextensions-apiserver v0.33.2 // indirect + k8s.io/apiserver v0.33.2 // indirect + k8s.io/component-base v0.33.2 // indirect k8s.io/klog/v2 v2.130.1 // indirect k8s.io/kube-openapi v0.0.0-20250902184714-7fc278399c7f // indirect + sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.30.3 // indirect sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect sigs.k8s.io/randfill v1.0.0 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.7.0 // indirect diff --git a/go.sum b/go.sum index 0b7b3c32..63a535da 100644 --- a/go.sum +++ b/go.sum @@ -1,9 +1,18 @@ github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0= github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= +github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI= +github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g= +github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= +github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= +github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= @@ -14,6 +23,8 @@ github.com/evanphx/json-patch v5.9.11+incompatible h1:ixHHqfcGvxhWkniF1tWxBHA0yb github.com/evanphx/json-patch v5.9.11+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch/v5 v5.9.11 h1:/8HVnzMq13/3x9TPvjG08wUGqBTmZBsCWzjTM0wiaDU= github.com/evanphx/json-patch/v5 v5.9.11/go.mod h1:3j+LviiESTElxA4p3EMKAB9HXj3/XEtnUf6OZxqIQTM= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM= @@ -24,8 +35,11 @@ github.com/gkampitakis/go-diff v1.3.2 h1:Qyn0J9XJSDTgnsgHRdz9Zp24RaJeKMUHg2+PDZZ github.com/gkampitakis/go-diff v1.3.2/go.mod h1:LLgOrpqleQe26cte8s36HTWcTmMEur6OPYerdAAS9tk= github.com/gkampitakis/go-snaps v0.5.15 h1:amyJrvM1D33cPHwVrjo9jQxX8g/7E2wYdZ+01KS3zGE= github.com/gkampitakis/go-snaps v0.5.15/go.mod h1:HNpx/9GoKisdhw9AFOBT1N7DBs9DiHo/hGheFGBZ+mc= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg= github.com/go-openapi/jsonpointer v0.21.1 h1:whnzv/pNXtK2FbX/W9yJfRmE2gsmkfahjMKB0fZvcic= @@ -44,6 +58,8 @@ github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/cel-go v0.20.1 h1:nDx9r8S3L4pE61eDdt8igGj8rf5kjYR3ILxWIpWNi84= +github.com/google/cel-go v0.20.1/go.mod h1:kWcIzTsPX0zmQ+H3TirHstLLf9ep5QTsZBN9u4dOYLg= github.com/google/gnostic-models v0.7.0 h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnLmJEJxo= github.com/google/gnostic-models v0.7.0/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= @@ -58,8 +74,12 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gophercloud/gophercloud/v2 v2.8.0 h1:of2+8tT6+FbEYHfYC8GBu8TXJNsXYSNm9KuvpX7Neqo= github.com/gophercloud/gophercloud/v2 v2.8.0/go.mod h1:Ki/ILhYZr/5EPebrPL9Ej+tUg4lqx71/YH2JWVeU+Qk= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k= github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/joshdk/go-junit v1.0.0 h1:S86cUKIdwBHWwA6xCmFlf3RTLfVXYQfvanM5Uh+K6GE= @@ -108,8 +128,8 @@ github.com/openstack-k8s-operators/lib-common/modules/openstack v0.6.1-0.2025102 github.com/openstack-k8s-operators/lib-common/modules/openstack v0.6.1-0.20251021145236-2b84ec9fd9bb/go.mod h1:yf13jWb60XV26eA7A8o86ZCXNWBLNK9dPkTSWFaTPCw= github.com/openstack-k8s-operators/lib-common/modules/storage v0.6.1-0.20250929092825-4c2402451077 h1:9tpPDBV2RLXMDgt13ec8XR2OatFriItseqg+Oyvx9GA= github.com/openstack-k8s-operators/lib-common/modules/storage v0.6.1-0.20250929092825-4c2402451077/go.mod h1:JPQHkExlxeT6MU3DNJgXXJJG0NMQHlZwxxfbYRaP3eg= -github.com/openstack-k8s-operators/lib-common/modules/test v0.6.1-0.20251027074416-ab5c045dbe00 h1:2pRRZgTqfYPAUM11AqeDbxKlgIvaLCzP0OGMfW9xAiE= -github.com/openstack-k8s-operators/lib-common/modules/test v0.6.1-0.20251027074416-ab5c045dbe00/go.mod h1:BGPgBOz+Af5UqBMLeizN2OsKdXzOXQBDkilVpwi443E= +github.com/openstack-k8s-operators/lib-common/modules/test v0.6.1-0.20250929092825-4c2402451077 h1:h11tW/Ntg9OiKCnKVAMgR+ka12597ai31OeAD1FGa4s= +github.com/openstack-k8s-operators/lib-common/modules/test v0.6.1-0.20250929092825-4c2402451077/go.mod h1:tWZFuXyOZZI+h4uAwaBqyRcvpN7f+PGTHYRDV9VltOk= github.com/openstack-k8s-operators/mariadb-operator/api v0.6.1-0.20251015110425-ad0381ce8cd4 h1:4qDSDLX7HpCIdnlUExyPc3DkyCq+73PLPb99FVj1CZk= github.com/openstack-k8s-operators/mariadb-operator/api v0.6.1-0.20251015110425-ad0381ce8cd4/go.mod h1:lOZNSKG7MMkhMjL7OQXKscy+dH2mxs3HPD+oj4wVytA= github.com/openstack-k8s-operators/rabbitmq-cluster-operator/v2 v2.6.1-0.20250929174222-a0d328fa4dec h1:saovr368HPAKHN0aRPh8h8n9s9dn3d8Frmfua0UYRlc= @@ -131,10 +151,17 @@ github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzM github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= +github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= +github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M= github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stoewer/go-strcase v1.2.0 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ainaU= +github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= @@ -149,6 +176,26 @@ github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 h1:yd02MEjBdJkG3uabWP9apV+OuWRIXGDuJEUJbOHmCFU= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0/go.mod h1:umTcuxiv1n/s/S6/c2AT/g2CQ7u5C59sHDNmfSwgz7Q= +go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY= +go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 h1:3Q/xZUyC1BBkualc9ROb4G8qkH90LXEIICcs5zv1OYY= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0/go.mod h1:s75jGIWA9OfCMzF0xr+ZgfrB5FEbbV7UuYo32ahUiFI= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0 h1:qFffATk0X+HD+f1Z8lswGiOQYKHRlzfmdJm0wEaVrFA= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0/go.mod h1:MOiCmryaYtc+V0Ei+Tx9o5S1ZjA7kzLucuVuyzBZloQ= +go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ= +go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE= +go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A= +go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU= +go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk= +go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w= +go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k= +go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE= +go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= +go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= @@ -208,6 +255,12 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gomodules.xyz/jsonpatch/v2 v2.5.0 h1:JELs8RLM12qJGXU4u/TO3V25KW8GreMKl9pdkk14RM0= gomodules.xyz/jsonpatch/v2 v2.5.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= +google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422 h1:GVIKPyP/kLIyVOgOnTwFOrvQaQUzOzGMCxgFUOEmm24= +google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422/go.mod h1:b6h1vNKhxaSoEI+5jc3PJUCustfli/mRab7295pY7rw= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f h1:OxYkA3wjPsZyBylwymxSHa7ViiW1Sml4ToBrncvFehI= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:+2Yz8+CLJbIfL9z73EW45avw8Lmge3xVElCP9zEKi50= +google.golang.org/grpc v1.71.1 h1:ffsFWr7ygTUscGPI0KKK6TLrGz0476KUvvsbqWK0rPI= +google.golang.org/grpc v1.71.1/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec= google.golang.org/protobuf v1.36.7 h1:IgrO7UwFQGJdRNXH/sQux4R1Dj1WAKcLElzeeRaXV2A= google.golang.org/protobuf v1.36.7/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -217,6 +270,9 @@ gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSP gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= k8s.io/api v0.31.13 h1:sco9Cq2pY4Ysv9qZiWzcR97MmA/35nwYQ/VCTzOcWmc= @@ -225,14 +281,20 @@ k8s.io/apiextensions-apiserver v0.31.13 h1:8xtWKVpV/YbYX0UX2k6w+cgxfxKhX0UNGuo/V k8s.io/apiextensions-apiserver v0.31.13/go.mod h1:zxpMLWXBxnJqKUIruJ+ulP+Xlfe5lPZPxq1z0cLwA2U= k8s.io/apimachinery v0.31.13 h1:rkG0EiBkBkEzURo/8dKGx/oBF202Z2LqHuSD8Cm3bG4= k8s.io/apimachinery v0.31.13/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= +k8s.io/apiserver v0.31.13 h1:Ke9/X2m3vHSgsminpAbUxULDNMbvAfjrRX73Gqx6CZc= +k8s.io/apiserver v0.31.13/go.mod h1:5nBPhL2g7am/CS+/OI5A6+olEbo0C7tQ8QNDODLd+WY= k8s.io/client-go v0.31.13 h1:Q0LG51uFbzNd9fzIj5ilA0Sm1wUholHvDaNwVKzqdCA= k8s.io/client-go v0.31.13/go.mod h1:UB4yTzQeRAv+vULOKp2jdqA5LSwV55bvc3RQ5tM48LM= +k8s.io/component-base v0.31.13 h1:/uVLq7yHk9azReqeCFAZSr/8NXydzpz7yDZ6p/yiwBQ= +k8s.io/component-base v0.31.13/go.mod h1:uMXtKNyDqeNdZYL6SRCr9wB6FutL9pOlQmkK2dRVAKQ= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/kube-openapi v0.0.0-20250627150254-e9823e99808e h1:UGI9rv1A2cV87NhXr4s+AUBxIuoo/SME/IyJ3b6KztE= k8s.io/kube-openapi v0.0.0-20250627150254-e9823e99808e/go.mod h1:GLOk5B+hDbRROvt0X2+hqX64v/zO3vXN7J78OUmBSKw= k8s.io/utils v0.0.0-20250820121507-0af2bda4dd1d h1:wAhiDyZ4Tdtt7e46e9M5ZSAJ/MnPGPs+Ki1gHw4w1R0= k8s.io/utils v0.0.0-20250820121507-0af2bda4dd1d/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.30.3 h1:2770sDpzrjjsAtVhSeUFseziht227YAWYHLGNM8QPwY= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.30.3/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw= sigs.k8s.io/controller-runtime v0.19.7 h1:DLABZfMr20A+AwCZOHhcbcu+TqBXnJZaVBri9K3EO48= sigs.k8s.io/controller-runtime v0.19.7/go.mod h1:iRmWllt8IlaLjvTTDLhRBXIEtkCK6hwVBJJsYS9Ajf4= sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE= diff --git a/controllers/funcs.go b/internal/controller/funcs.go similarity index 97% rename from controllers/funcs.go rename to internal/controller/funcs.go index 62392341..d1936a80 100644 --- a/controllers/funcs.go +++ b/internal/controller/funcs.go @@ -14,8 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ -// Package controllers contains shared utility functions for ironic-operator controllers. -package controllers +// Package controller contains shared utility functions for ironic-operator controllers. +package controller import ( "context" diff --git a/controllers/ironic_controller.go b/internal/controller/ironic_controller.go similarity index 99% rename from controllers/ironic_controller.go rename to internal/controller/ironic_controller.go index 6198099f..f187ef03 100644 --- a/controllers/ironic_controller.go +++ b/internal/controller/ironic_controller.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package controllers +package controller import ( "context" @@ -24,7 +24,7 @@ import ( "time" "github.com/go-logr/logr" - ironic "github.com/openstack-k8s-operators/ironic-operator/pkg/ironic" + ironic "github.com/openstack-k8s-operators/ironic-operator/internal/ironic" common "github.com/openstack-k8s-operators/lib-common/modules/common" condition "github.com/openstack-k8s-operators/lib-common/modules/common/condition" diff --git a/internal/controller/ironic_controller_test.go b/internal/controller/ironic_controller_test.go new file mode 100644 index 00000000..6a2f9663 --- /dev/null +++ b/internal/controller/ironic_controller_test.go @@ -0,0 +1,84 @@ +/* +Copyright 2025. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package controller + +import ( + "context" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + ironicv1beta1 "github.com/openstack-k8s-operators/ironic-operator/api/v1beta1" +) + +var _ = Describe("Ironic Controller", func() { + Context("When reconciling a resource", func() { + const resourceName = "test-resource" + + ctx := context.Background() + + typeNamespacedName := types.NamespacedName{ + Name: resourceName, + Namespace: "default", // TODO(user):Modify as needed + } + ironic := &ironicv1beta1.Ironic{} + + BeforeEach(func() { + By("creating the custom resource for the Kind Ironic") + err := k8sClient.Get(ctx, typeNamespacedName, ironic) + if err != nil && errors.IsNotFound(err) { + resource := &ironicv1beta1.Ironic{ + ObjectMeta: metav1.ObjectMeta{ + Name: resourceName, + Namespace: "default", + }, + // TODO(user): Specify other spec details if needed. + } + Expect(k8sClient.Create(ctx, resource)).To(Succeed()) + } + }) + + AfterEach(func() { + // TODO(user): Cleanup logic after each test, like removing the resource instance. + resource := &ironicv1beta1.Ironic{} + err := k8sClient.Get(ctx, typeNamespacedName, resource) + Expect(err).NotTo(HaveOccurred()) + + By("Cleanup the specific resource instance Ironic") + Expect(k8sClient.Delete(ctx, resource)).To(Succeed()) + }) + It("should successfully reconcile the resource", func() { + By("Reconciling the created resource") + controllerReconciler := &IronicReconciler{ + Client: k8sClient, + Scheme: k8sClient.Scheme(), + } + + _, err := controllerReconciler.Reconcile(ctx, reconcile.Request{ + NamespacedName: typeNamespacedName, + }) + Expect(err).NotTo(HaveOccurred()) + // TODO(user): Add more specific assertions depending on your controller's reconciliation logic. + // Example: If you expect a certain status condition after reconciliation, verify it here. + }) + }) +}) diff --git a/controllers/ironicapi_controller.go b/internal/controller/ironicapi_controller.go similarity index 99% rename from controllers/ironicapi_controller.go rename to internal/controller/ironicapi_controller.go index 18f59778..df9a8419 100644 --- a/controllers/ironicapi_controller.go +++ b/internal/controller/ironicapi_controller.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package controllers +package controller import ( "context" @@ -44,8 +44,8 @@ import ( networkv1 "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/apis/k8s.cni.cncf.io/v1" topologyv1 "github.com/openstack-k8s-operators/infra-operator/apis/topology/v1beta1" ironicv1 "github.com/openstack-k8s-operators/ironic-operator/api/v1beta1" - ironic "github.com/openstack-k8s-operators/ironic-operator/pkg/ironic" - ironicapi "github.com/openstack-k8s-operators/ironic-operator/pkg/ironicapi" + ironic "github.com/openstack-k8s-operators/ironic-operator/internal/ironic" + ironicapi "github.com/openstack-k8s-operators/ironic-operator/internal/ironicapi" keystonev1 "github.com/openstack-k8s-operators/keystone-operator/api/v1beta1" "github.com/openstack-k8s-operators/lib-common/modules/common" "github.com/openstack-k8s-operators/lib-common/modules/common/condition" @@ -62,7 +62,7 @@ import ( "github.com/openstack-k8s-operators/lib-common/modules/common/util" mariadbv1 "github.com/openstack-k8s-operators/mariadb-operator/api/v1beta1" - keystone "github.com/openstack-k8s-operators/ironic-operator/pkg/keystone" + keystone "github.com/openstack-k8s-operators/ironic-operator/internal/keystone" ) // IronicAPIReconciler reconciles a IronicAPI object diff --git a/internal/controller/ironicapi_controller_test.go b/internal/controller/ironicapi_controller_test.go new file mode 100644 index 00000000..df26cde9 --- /dev/null +++ b/internal/controller/ironicapi_controller_test.go @@ -0,0 +1,84 @@ +/* +Copyright 2025. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package controller + +import ( + "context" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + ironicv1beta1 "github.com/openstack-k8s-operators/ironic-operator/api/v1beta1" +) + +var _ = Describe("IronicAPI Controller", func() { + Context("When reconciling a resource", func() { + const resourceName = "test-resource" + + ctx := context.Background() + + typeNamespacedName := types.NamespacedName{ + Name: resourceName, + Namespace: "default", // TODO(user):Modify as needed + } + ironicapi := &ironicv1beta1.IronicAPI{} + + BeforeEach(func() { + By("creating the custom resource for the Kind IronicAPI") + err := k8sClient.Get(ctx, typeNamespacedName, ironicapi) + if err != nil && errors.IsNotFound(err) { + resource := &ironicv1beta1.IronicAPI{ + ObjectMeta: metav1.ObjectMeta{ + Name: resourceName, + Namespace: "default", + }, + // TODO(user): Specify other spec details if needed. + } + Expect(k8sClient.Create(ctx, resource)).To(Succeed()) + } + }) + + AfterEach(func() { + // TODO(user): Cleanup logic after each test, like removing the resource instance. + resource := &ironicv1beta1.IronicAPI{} + err := k8sClient.Get(ctx, typeNamespacedName, resource) + Expect(err).NotTo(HaveOccurred()) + + By("Cleanup the specific resource instance IronicAPI") + Expect(k8sClient.Delete(ctx, resource)).To(Succeed()) + }) + It("should successfully reconcile the resource", func() { + By("Reconciling the created resource") + controllerReconciler := &IronicAPIReconciler{ + Client: k8sClient, + Scheme: k8sClient.Scheme(), + } + + _, err := controllerReconciler.Reconcile(ctx, reconcile.Request{ + NamespacedName: typeNamespacedName, + }) + Expect(err).NotTo(HaveOccurred()) + // TODO(user): Add more specific assertions depending on your controller's reconciliation logic. + // Example: If you expect a certain status condition after reconciliation, verify it here. + }) + }) +}) diff --git a/controllers/ironicconductor_controller.go b/internal/controller/ironicconductor_controller.go similarity index 99% rename from controllers/ironicconductor_controller.go rename to internal/controller/ironicconductor_controller.go index aec30cda..1fe444b3 100644 --- a/controllers/ironicconductor_controller.go +++ b/internal/controller/ironicconductor_controller.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package controllers +package controller import ( "context" @@ -46,8 +46,8 @@ import ( topologyv1 "github.com/openstack-k8s-operators/infra-operator/apis/topology/v1beta1" ironicv1 "github.com/openstack-k8s-operators/ironic-operator/api/v1beta1" - ironic "github.com/openstack-k8s-operators/ironic-operator/pkg/ironic" - ironicconductor "github.com/openstack-k8s-operators/ironic-operator/pkg/ironicconductor" + ironic "github.com/openstack-k8s-operators/ironic-operator/internal/ironic" + ironicconductor "github.com/openstack-k8s-operators/ironic-operator/internal/ironicconductor" "github.com/openstack-k8s-operators/lib-common/modules/common" "github.com/openstack-k8s-operators/lib-common/modules/common/condition" "github.com/openstack-k8s-operators/lib-common/modules/common/endpoint" diff --git a/internal/controller/ironicconductor_controller_test.go b/internal/controller/ironicconductor_controller_test.go new file mode 100644 index 00000000..511533c2 --- /dev/null +++ b/internal/controller/ironicconductor_controller_test.go @@ -0,0 +1,84 @@ +/* +Copyright 2025. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package controller + +import ( + "context" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + ironicv1beta1 "github.com/openstack-k8s-operators/ironic-operator/api/v1beta1" +) + +var _ = Describe("IronicConductor Controller", func() { + Context("When reconciling a resource", func() { + const resourceName = "test-resource" + + ctx := context.Background() + + typeNamespacedName := types.NamespacedName{ + Name: resourceName, + Namespace: "default", // TODO(user):Modify as needed + } + ironicconductor := &ironicv1beta1.IronicConductor{} + + BeforeEach(func() { + By("creating the custom resource for the Kind IronicConductor") + err := k8sClient.Get(ctx, typeNamespacedName, ironicconductor) + if err != nil && errors.IsNotFound(err) { + resource := &ironicv1beta1.IronicConductor{ + ObjectMeta: metav1.ObjectMeta{ + Name: resourceName, + Namespace: "default", + }, + // TODO(user): Specify other spec details if needed. + } + Expect(k8sClient.Create(ctx, resource)).To(Succeed()) + } + }) + + AfterEach(func() { + // TODO(user): Cleanup logic after each test, like removing the resource instance. + resource := &ironicv1beta1.IronicConductor{} + err := k8sClient.Get(ctx, typeNamespacedName, resource) + Expect(err).NotTo(HaveOccurred()) + + By("Cleanup the specific resource instance IronicConductor") + Expect(k8sClient.Delete(ctx, resource)).To(Succeed()) + }) + It("should successfully reconcile the resource", func() { + By("Reconciling the created resource") + controllerReconciler := &IronicConductorReconciler{ + Client: k8sClient, + Scheme: k8sClient.Scheme(), + } + + _, err := controllerReconciler.Reconcile(ctx, reconcile.Request{ + NamespacedName: typeNamespacedName, + }) + Expect(err).NotTo(HaveOccurred()) + // TODO(user): Add more specific assertions depending on your controller's reconciliation logic. + // Example: If you expect a certain status condition after reconciliation, verify it here. + }) + }) +}) diff --git a/controllers/ironicinspector_controller.go b/internal/controller/ironicinspector_controller.go similarity index 99% rename from controllers/ironicinspector_controller.go rename to internal/controller/ironicinspector_controller.go index ea060f06..81127198 100644 --- a/controllers/ironicinspector_controller.go +++ b/internal/controller/ironicinspector_controller.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package controllers +package controller import ( "context" @@ -24,8 +24,8 @@ import ( "github.com/go-logr/logr" networkv1 "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/apis/k8s.cni.cncf.io/v1" - ironic "github.com/openstack-k8s-operators/ironic-operator/pkg/ironic" - ironicinspector "github.com/openstack-k8s-operators/ironic-operator/pkg/ironicinspector" + ironic "github.com/openstack-k8s-operators/ironic-operator/internal/ironic" + ironicinspector "github.com/openstack-k8s-operators/ironic-operator/internal/ironicinspector" mariadbv1 "github.com/openstack-k8s-operators/mariadb-operator/api/v1beta1" common "github.com/openstack-k8s-operators/lib-common/modules/common" @@ -67,7 +67,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/log" topologyv1 "github.com/openstack-k8s-operators/infra-operator/apis/topology/v1beta1" - keystone "github.com/openstack-k8s-operators/ironic-operator/pkg/keystone" + keystone "github.com/openstack-k8s-operators/ironic-operator/internal/keystone" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" rbacv1 "k8s.io/api/rbac/v1" diff --git a/internal/controller/ironicinspector_controller_test.go b/internal/controller/ironicinspector_controller_test.go new file mode 100644 index 00000000..cd8409b4 --- /dev/null +++ b/internal/controller/ironicinspector_controller_test.go @@ -0,0 +1,84 @@ +/* +Copyright 2025. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package controller + +import ( + "context" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + ironicv1beta1 "github.com/openstack-k8s-operators/ironic-operator/api/v1beta1" +) + +var _ = Describe("IronicInspector Controller", func() { + Context("When reconciling a resource", func() { + const resourceName = "test-resource" + + ctx := context.Background() + + typeNamespacedName := types.NamespacedName{ + Name: resourceName, + Namespace: "default", // TODO(user):Modify as needed + } + ironicinspector := &ironicv1beta1.IronicInspector{} + + BeforeEach(func() { + By("creating the custom resource for the Kind IronicInspector") + err := k8sClient.Get(ctx, typeNamespacedName, ironicinspector) + if err != nil && errors.IsNotFound(err) { + resource := &ironicv1beta1.IronicInspector{ + ObjectMeta: metav1.ObjectMeta{ + Name: resourceName, + Namespace: "default", + }, + // TODO(user): Specify other spec details if needed. + } + Expect(k8sClient.Create(ctx, resource)).To(Succeed()) + } + }) + + AfterEach(func() { + // TODO(user): Cleanup logic after each test, like removing the resource instance. + resource := &ironicv1beta1.IronicInspector{} + err := k8sClient.Get(ctx, typeNamespacedName, resource) + Expect(err).NotTo(HaveOccurred()) + + By("Cleanup the specific resource instance IronicInspector") + Expect(k8sClient.Delete(ctx, resource)).To(Succeed()) + }) + It("should successfully reconcile the resource", func() { + By("Reconciling the created resource") + controllerReconciler := &IronicInspectorReconciler{ + Client: k8sClient, + Scheme: k8sClient.Scheme(), + } + + _, err := controllerReconciler.Reconcile(ctx, reconcile.Request{ + NamespacedName: typeNamespacedName, + }) + Expect(err).NotTo(HaveOccurred()) + // TODO(user): Add more specific assertions depending on your controller's reconciliation logic. + // Example: If you expect a certain status condition after reconciliation, verify it here. + }) + }) +}) diff --git a/controllers/ironicneutronagent_controller.go b/internal/controller/ironicneutronagent_controller.go similarity index 99% rename from controllers/ironicneutronagent_controller.go rename to internal/controller/ironicneutronagent_controller.go index 86f90570..f5181fbd 100644 --- a/controllers/ironicneutronagent_controller.go +++ b/internal/controller/ironicneutronagent_controller.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package controllers +package controller import ( "context" @@ -43,8 +43,8 @@ import ( "github.com/go-logr/logr" rabbitmqv1 "github.com/openstack-k8s-operators/infra-operator/apis/rabbitmq/v1beta1" ironicv1 "github.com/openstack-k8s-operators/ironic-operator/api/v1beta1" - ironic "github.com/openstack-k8s-operators/ironic-operator/pkg/ironic" - "github.com/openstack-k8s-operators/ironic-operator/pkg/ironicneutronagent" + ironic "github.com/openstack-k8s-operators/ironic-operator/internal/ironic" + "github.com/openstack-k8s-operators/ironic-operator/internal/ironicneutronagent" keystonev1 "github.com/openstack-k8s-operators/keystone-operator/api/v1beta1" endpoint "github.com/openstack-k8s-operators/lib-common/modules/common/endpoint" "github.com/openstack-k8s-operators/lib-common/modules/common/tls" diff --git a/internal/controller/ironicneutronagent_controller_test.go b/internal/controller/ironicneutronagent_controller_test.go new file mode 100644 index 00000000..c31dd3ac --- /dev/null +++ b/internal/controller/ironicneutronagent_controller_test.go @@ -0,0 +1,84 @@ +/* +Copyright 2025. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package controller + +import ( + "context" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + ironicv1beta1 "github.com/openstack-k8s-operators/ironic-operator/api/v1beta1" +) + +var _ = Describe("IronicNeutronAgent Controller", func() { + Context("When reconciling a resource", func() { + const resourceName = "test-resource" + + ctx := context.Background() + + typeNamespacedName := types.NamespacedName{ + Name: resourceName, + Namespace: "default", // TODO(user):Modify as needed + } + ironicneutronagent := &ironicv1beta1.IronicNeutronAgent{} + + BeforeEach(func() { + By("creating the custom resource for the Kind IronicNeutronAgent") + err := k8sClient.Get(ctx, typeNamespacedName, ironicneutronagent) + if err != nil && errors.IsNotFound(err) { + resource := &ironicv1beta1.IronicNeutronAgent{ + ObjectMeta: metav1.ObjectMeta{ + Name: resourceName, + Namespace: "default", + }, + // TODO(user): Specify other spec details if needed. + } + Expect(k8sClient.Create(ctx, resource)).To(Succeed()) + } + }) + + AfterEach(func() { + // TODO(user): Cleanup logic after each test, like removing the resource instance. + resource := &ironicv1beta1.IronicNeutronAgent{} + err := k8sClient.Get(ctx, typeNamespacedName, resource) + Expect(err).NotTo(HaveOccurred()) + + By("Cleanup the specific resource instance IronicNeutronAgent") + Expect(k8sClient.Delete(ctx, resource)).To(Succeed()) + }) + It("should successfully reconcile the resource", func() { + By("Reconciling the created resource") + controllerReconciler := &IronicNeutronAgentReconciler{ + Client: k8sClient, + Scheme: k8sClient.Scheme(), + } + + _, err := controllerReconciler.Reconcile(ctx, reconcile.Request{ + NamespacedName: typeNamespacedName, + }) + Expect(err).NotTo(HaveOccurred()) + // TODO(user): Add more specific assertions depending on your controller's reconciliation logic. + // Example: If you expect a certain status condition after reconciliation, verify it here. + }) + }) +}) diff --git a/internal/controller/suite_test.go b/internal/controller/suite_test.go new file mode 100644 index 00000000..3deb324f --- /dev/null +++ b/internal/controller/suite_test.go @@ -0,0 +1,116 @@ +/* +Copyright 2025. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package controller + +import ( + "context" + "os" + "path/filepath" + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/rest" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/envtest" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/log/zap" + + ironicv1beta1 "github.com/openstack-k8s-operators/ironic-operator/api/v1beta1" + // +kubebuilder:scaffold:imports +) + +// These tests use Ginkgo (BDD-style Go testing framework). Refer to +// http://onsi.github.io/ginkgo/ to learn more about Ginkgo. + +var ( + ctx context.Context + cancel context.CancelFunc + testEnv *envtest.Environment + cfg *rest.Config + k8sClient client.Client +) + +func TestControllers(t *testing.T) { + RegisterFailHandler(Fail) + + RunSpecs(t, "Controller Suite") +} + +var _ = BeforeSuite(func() { + logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true))) + + ctx, cancel = context.WithCancel(context.TODO()) + + var err error + err = ironicv1beta1.AddToScheme(scheme.Scheme) + Expect(err).NotTo(HaveOccurred()) + + // +kubebuilder:scaffold:scheme + + By("bootstrapping test environment") + testEnv = &envtest.Environment{ + CRDDirectoryPaths: []string{filepath.Join("..", "..", "config", "crd", "bases")}, + ErrorIfCRDPathMissing: true, + } + + // Retrieve the first found binary directory to allow running tests from IDEs + if getFirstFoundEnvTestBinaryDir() != "" { + testEnv.BinaryAssetsDirectory = getFirstFoundEnvTestBinaryDir() + } + + // cfg is defined in this file globally. + cfg, err = testEnv.Start() + Expect(err).NotTo(HaveOccurred()) + Expect(cfg).NotTo(BeNil()) + + k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}) + Expect(err).NotTo(HaveOccurred()) + Expect(k8sClient).NotTo(BeNil()) +}) + +var _ = AfterSuite(func() { + By("tearing down the test environment") + cancel() + err := testEnv.Stop() + Expect(err).NotTo(HaveOccurred()) +}) + +// getFirstFoundEnvTestBinaryDir locates the first binary in the specified path. +// ENVTEST-based tests depend on specific binaries, usually located in paths set by +// controller-runtime. When running tests directly (e.g., via an IDE) without using +// Makefile targets, the 'BinaryAssetsDirectory' must be explicitly configured. +// +// This function streamlines the process by finding the required binaries, similar to +// setting the 'KUBEBUILDER_ASSETS' environment variable. To ensure the binaries are +// properly set up, run 'make setup-envtest' beforehand. +func getFirstFoundEnvTestBinaryDir() string { + basePath := filepath.Join("..", "..", "bin", "k8s") + entries, err := os.ReadDir(basePath) + if err != nil { + logf.Log.Error(err, "Failed to read directory", "path", basePath) + return "" + } + for _, entry := range entries { + if entry.IsDir() { + return filepath.Join(basePath, entry.Name()) + } + } + return "" +} diff --git a/pkg/ironic/const.go b/internal/ironic/const.go similarity index 100% rename from pkg/ironic/const.go rename to internal/ironic/const.go diff --git a/pkg/ironic/dbsync.go b/internal/ironic/dbsync.go similarity index 100% rename from pkg/ironic/dbsync.go rename to internal/ironic/dbsync.go diff --git a/pkg/ironic/funcs.go b/internal/ironic/funcs.go similarity index 100% rename from pkg/ironic/funcs.go rename to internal/ironic/funcs.go diff --git a/pkg/ironic/initcontainer.go b/internal/ironic/initcontainer.go similarity index 100% rename from pkg/ironic/initcontainer.go rename to internal/ironic/initcontainer.go diff --git a/pkg/ironic/volumes.go b/internal/ironic/volumes.go similarity index 100% rename from pkg/ironic/volumes.go rename to internal/ironic/volumes.go diff --git a/pkg/ironicapi/deployment.go b/internal/ironicapi/deployment.go similarity index 98% rename from pkg/ironicapi/deployment.go rename to internal/ironicapi/deployment.go index d727e881..c9bf7f0a 100644 --- a/pkg/ironicapi/deployment.go +++ b/internal/ironicapi/deployment.go @@ -21,7 +21,7 @@ import ( topologyv1 "github.com/openstack-k8s-operators/infra-operator/apis/topology/v1beta1" ironicv1 "github.com/openstack-k8s-operators/ironic-operator/api/v1beta1" - ironic "github.com/openstack-k8s-operators/ironic-operator/pkg/ironic" + ironic "github.com/openstack-k8s-operators/ironic-operator/internal/ironic" common "github.com/openstack-k8s-operators/lib-common/modules/common" affinity "github.com/openstack-k8s-operators/lib-common/modules/common/affinity" env "github.com/openstack-k8s-operators/lib-common/modules/common/env" diff --git a/pkg/ironicapi/volumes.go b/internal/ironicapi/volumes.go similarity index 97% rename from pkg/ironicapi/volumes.go rename to internal/ironicapi/volumes.go index 13193804..4af66503 100644 --- a/pkg/ironicapi/volumes.go +++ b/internal/ironicapi/volumes.go @@ -5,7 +5,7 @@ import ( "fmt" ironicv1 "github.com/openstack-k8s-operators/ironic-operator/api/v1beta1" - "github.com/openstack-k8s-operators/ironic-operator/pkg/ironic" + "github.com/openstack-k8s-operators/ironic-operator/internal/ironic" corev1 "k8s.io/api/core/v1" "sigs.k8s.io/controller-runtime/pkg/log" ) diff --git a/pkg/ironicconductor/const.go b/internal/ironicconductor/const.go similarity index 100% rename from pkg/ironicconductor/const.go rename to internal/ironicconductor/const.go diff --git a/pkg/ironicconductor/pod.go b/internal/ironicconductor/pod.go similarity index 100% rename from pkg/ironicconductor/pod.go rename to internal/ironicconductor/pod.go diff --git a/pkg/ironicconductor/service.go b/internal/ironicconductor/service.go similarity index 96% rename from pkg/ironicconductor/service.go rename to internal/ironicconductor/service.go index c1e1f33c..0427e961 100644 --- a/pkg/ironicconductor/service.go +++ b/internal/ironicconductor/service.go @@ -3,7 +3,7 @@ package ironicconductor import ( routev1 "github.com/openshift/api/route/v1" ironicv1 "github.com/openstack-k8s-operators/ironic-operator/api/v1beta1" - ironic "github.com/openstack-k8s-operators/ironic-operator/pkg/ironic" + ironic "github.com/openstack-k8s-operators/ironic-operator/internal/ironic" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" intstr "k8s.io/apimachinery/pkg/util/intstr" diff --git a/pkg/ironicconductor/statefulset.go b/internal/ironicconductor/statefulset.go similarity index 99% rename from pkg/ironicconductor/statefulset.go rename to internal/ironicconductor/statefulset.go index 55ba8ee0..c205c65b 100644 --- a/pkg/ironicconductor/statefulset.go +++ b/internal/ironicconductor/statefulset.go @@ -22,7 +22,7 @@ import ( topologyv1 "github.com/openstack-k8s-operators/infra-operator/apis/topology/v1beta1" ironicv1 "github.com/openstack-k8s-operators/ironic-operator/api/v1beta1" - ironic "github.com/openstack-k8s-operators/ironic-operator/pkg/ironic" + ironic "github.com/openstack-k8s-operators/ironic-operator/internal/ironic" common "github.com/openstack-k8s-operators/lib-common/modules/common" affinity "github.com/openstack-k8s-operators/lib-common/modules/common/affinity" env "github.com/openstack-k8s-operators/lib-common/modules/common/env" diff --git a/pkg/ironicconductor/volumes.go b/internal/ironicconductor/volumes.go similarity index 97% rename from pkg/ironicconductor/volumes.go rename to internal/ironicconductor/volumes.go index f44cf78c..886197e2 100644 --- a/pkg/ironicconductor/volumes.go +++ b/internal/ironicconductor/volumes.go @@ -5,7 +5,7 @@ import ( "fmt" ironicv1 "github.com/openstack-k8s-operators/ironic-operator/api/v1beta1" - "github.com/openstack-k8s-operators/ironic-operator/pkg/ironic" + "github.com/openstack-k8s-operators/ironic-operator/internal/ironic" corev1 "k8s.io/api/core/v1" "sigs.k8s.io/controller-runtime/pkg/log" ) diff --git a/pkg/ironicinspector/const.go b/internal/ironicinspector/const.go similarity index 100% rename from pkg/ironicinspector/const.go rename to internal/ironicinspector/const.go diff --git a/pkg/ironicinspector/dbsync.go b/internal/ironicinspector/dbsync.go similarity index 97% rename from pkg/ironicinspector/dbsync.go rename to internal/ironicinspector/dbsync.go index 7d672824..85e5df71 100644 --- a/pkg/ironicinspector/dbsync.go +++ b/internal/ironicinspector/dbsync.go @@ -17,7 +17,7 @@ package ironicinspector import ( ironicv1 "github.com/openstack-k8s-operators/ironic-operator/api/v1beta1" - ironic "github.com/openstack-k8s-operators/ironic-operator/pkg/ironic" + ironic "github.com/openstack-k8s-operators/ironic-operator/internal/ironic" "github.com/openstack-k8s-operators/lib-common/modules/common/env" batchv1 "k8s.io/api/batch/v1" diff --git a/pkg/ironicinspector/initcontainer.go b/internal/ironicinspector/initcontainer.go similarity index 100% rename from pkg/ironicinspector/initcontainer.go rename to internal/ironicinspector/initcontainer.go diff --git a/pkg/ironicinspector/pod.go b/internal/ironicinspector/pod.go similarity index 100% rename from pkg/ironicinspector/pod.go rename to internal/ironicinspector/pod.go diff --git a/pkg/ironicinspector/service.go b/internal/ironicinspector/service.go similarity index 95% rename from pkg/ironicinspector/service.go rename to internal/ironicinspector/service.go index 7730c0ca..166a52d9 100644 --- a/pkg/ironicinspector/service.go +++ b/internal/ironicinspector/service.go @@ -3,7 +3,7 @@ package ironicinspector import ( routev1 "github.com/openshift/api/route/v1" ironicv1 "github.com/openstack-k8s-operators/ironic-operator/api/v1beta1" - ironic "github.com/openstack-k8s-operators/ironic-operator/pkg/ironic" + ironic "github.com/openstack-k8s-operators/ironic-operator/internal/ironic" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" intstr "k8s.io/apimachinery/pkg/util/intstr" diff --git a/pkg/ironicinspector/statefulset.go b/internal/ironicinspector/statefulset.go similarity index 99% rename from pkg/ironicinspector/statefulset.go rename to internal/ironicinspector/statefulset.go index 6cef02b6..dea373f5 100644 --- a/pkg/ironicinspector/statefulset.go +++ b/internal/ironicinspector/statefulset.go @@ -20,7 +20,7 @@ import ( topologyv1 "github.com/openstack-k8s-operators/infra-operator/apis/topology/v1beta1" ironicv1 "github.com/openstack-k8s-operators/ironic-operator/api/v1beta1" - ironic "github.com/openstack-k8s-operators/ironic-operator/pkg/ironic" + ironic "github.com/openstack-k8s-operators/ironic-operator/internal/ironic" common "github.com/openstack-k8s-operators/lib-common/modules/common" affinity "github.com/openstack-k8s-operators/lib-common/modules/common/affinity" env "github.com/openstack-k8s-operators/lib-common/modules/common/env" diff --git a/pkg/ironicinspector/volumes.go b/internal/ironicinspector/volumes.go similarity index 100% rename from pkg/ironicinspector/volumes.go rename to internal/ironicinspector/volumes.go diff --git a/pkg/ironicneutronagent/const.go b/internal/ironicneutronagent/const.go similarity index 100% rename from pkg/ironicneutronagent/const.go rename to internal/ironicneutronagent/const.go diff --git a/pkg/ironicneutronagent/deployment.go b/internal/ironicneutronagent/deployment.go similarity index 100% rename from pkg/ironicneutronagent/deployment.go rename to internal/ironicneutronagent/deployment.go diff --git a/pkg/ironicneutronagent/volumes.go b/internal/ironicneutronagent/volumes.go similarity index 100% rename from pkg/ironicneutronagent/volumes.go rename to internal/ironicneutronagent/volumes.go diff --git a/pkg/keystone/keystone_registration.go b/internal/keystone/keystone_registration.go similarity index 100% rename from pkg/keystone/keystone_registration.go rename to internal/keystone/keystone_registration.go diff --git a/internal/webhook/v1beta1/ironic_webhook.go b/internal/webhook/v1beta1/ironic_webhook.go new file mode 100644 index 00000000..803d1da0 --- /dev/null +++ b/internal/webhook/v1beta1/ironic_webhook.go @@ -0,0 +1,131 @@ +/* +Copyright 2025. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package v1beta1 contains webhook implementations for ironic-operator v1beta1 API +package v1beta1 + +import ( + "context" + "errors" + "fmt" + + "k8s.io/apimachinery/pkg/runtime" + ctrl "sigs.k8s.io/controller-runtime" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/webhook" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" + + ironicv1beta1 "github.com/openstack-k8s-operators/ironic-operator/api/v1beta1" +) + +// nolint:unused +// log is for logging in this package. +var ironiclog = logf.Log.WithName("ironic-resource") + +// Static errors for webhook validation +var ( + errExpectedIronicObject = errors.New("expected an Ironic object") +) + +// SetupIronicWebhookWithManager registers the webhook for Ironic in the manager. +func SetupIronicWebhookWithManager(mgr ctrl.Manager) error { + return ctrl.NewWebhookManagedBy(mgr).For(&ironicv1beta1.Ironic{}). + WithValidator(&IronicCustomValidator{}). + WithDefaulter(&IronicCustomDefaulter{}). + Complete() +} + +// TODO(user): EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! + +// +kubebuilder:webhook:path=/mutate-ironic-openstack-org-v1beta1-ironic,mutating=true,failurePolicy=fail,sideEffects=None,groups=ironic.openstack.org,resources=ironics,verbs=create;update,versions=v1beta1,name=mironic-v1beta1.kb.io,admissionReviewVersions=v1 + +// IronicCustomDefaulter struct is responsible for setting default values on the custom resource of the +// Kind Ironic when those are created or updated. +// +// NOTE: The +kubebuilder:object:generate=false marker prevents controller-gen from generating DeepCopy methods, +// as it is used only for temporary operations and does not need to be deeply copied. +type IronicCustomDefaulter struct { + // TODO(user): Add more fields as needed for defaulting +} + +var _ webhook.CustomDefaulter = &IronicCustomDefaulter{} + +// Default implements webhook.CustomDefaulter so a webhook will be registered for the Kind Ironic. +func (d *IronicCustomDefaulter) Default(_ context.Context, obj runtime.Object) error { + ironic, ok := obj.(*ironicv1beta1.Ironic) + + if !ok { + return fmt.Errorf("%w but got %T", errExpectedIronicObject, obj) + } + ironiclog.Info("Defaulting for Ironic", "name", ironic.GetName()) + + // Call the defaulting logic from the API package + ironic.Default() + + return nil +} + +// TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation. +// NOTE: The 'path' attribute must follow a specific pattern and should not be modified directly here. +// Modifying the path for an invalid path can cause API server errors; failing to locate the webhook. +// +kubebuilder:webhook:path=/validate-ironic-openstack-org-v1beta1-ironic,mutating=false,failurePolicy=fail,sideEffects=None,groups=ironic.openstack.org,resources=ironics,verbs=create;update,versions=v1beta1,name=vironic-v1beta1.kb.io,admissionReviewVersions=v1 + +// IronicCustomValidator struct is responsible for validating the Ironic resource +// when it is created, updated, or deleted. +// +// NOTE: The +kubebuilder:object:generate=false marker prevents controller-gen from generating DeepCopy methods, +// as this struct is used only for temporary operations and does not need to be deeply copied. +type IronicCustomValidator struct { + // TODO(user): Add more fields as needed for validation +} + +var _ webhook.CustomValidator = &IronicCustomValidator{} + +// ValidateCreate implements webhook.CustomValidator so a webhook will be registered for the type Ironic. +func (v *IronicCustomValidator) ValidateCreate(_ context.Context, obj runtime.Object) (admission.Warnings, error) { + ironic, ok := obj.(*ironicv1beta1.Ironic) + if !ok { + return nil, fmt.Errorf("%w but got %T", errExpectedIronicObject, obj) + } + ironiclog.Info("Validation for Ironic upon creation", "name", ironic.GetName()) + + // Call the validation logic from the API package + return ironic.ValidateCreate() +} + +// ValidateUpdate implements webhook.CustomValidator so a webhook will be registered for the type Ironic. +func (v *IronicCustomValidator) ValidateUpdate(_ context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) { + ironic, ok := newObj.(*ironicv1beta1.Ironic) + if !ok { + return nil, fmt.Errorf("%w for the newObj but got %T", errExpectedIronicObject, newObj) + } + ironiclog.Info("Validation for Ironic upon update", "name", ironic.GetName()) + + // Call the validation logic from the API package + return ironic.ValidateUpdate(oldObj) +} + +// ValidateDelete implements webhook.CustomValidator so a webhook will be registered for the type Ironic. +func (v *IronicCustomValidator) ValidateDelete(_ context.Context, obj runtime.Object) (admission.Warnings, error) { + ironic, ok := obj.(*ironicv1beta1.Ironic) + if !ok { + return nil, fmt.Errorf("%w but got %T", errExpectedIronicObject, obj) + } + ironiclog.Info("Validation for Ironic upon deletion", "name", ironic.GetName()) + + // Call the validation logic from the API package + return ironic.ValidateDelete() +} diff --git a/internal/webhook/v1beta1/ironic_webhook_test.go b/internal/webhook/v1beta1/ironic_webhook_test.go new file mode 100644 index 00000000..1f04051c --- /dev/null +++ b/internal/webhook/v1beta1/ironic_webhook_test.go @@ -0,0 +1,87 @@ +/* +Copyright 2025. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1beta1 + +import ( + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + ironicv1beta1 "github.com/openstack-k8s-operators/ironic-operator/api/v1beta1" + // TODO (user): Add any additional imports if needed +) + +var _ = Describe("Ironic Webhook", func() { + var ( + obj *ironicv1beta1.Ironic + oldObj *ironicv1beta1.Ironic + validator IronicCustomValidator + defaulter IronicCustomDefaulter + ) + + BeforeEach(func() { + obj = &ironicv1beta1.Ironic{} + oldObj = &ironicv1beta1.Ironic{} + validator = IronicCustomValidator{} + Expect(validator).NotTo(BeNil(), "Expected validator to be initialized") + defaulter = IronicCustomDefaulter{} + Expect(defaulter).NotTo(BeNil(), "Expected defaulter to be initialized") + Expect(oldObj).NotTo(BeNil(), "Expected oldObj to be initialized") + Expect(obj).NotTo(BeNil(), "Expected obj to be initialized") + // TODO (user): Add any setup logic common to all tests + }) + + AfterEach(func() { + // TODO (user): Add any teardown logic common to all tests + }) + + Context("When creating Ironic under Defaulting Webhook", func() { + // TODO (user): Add logic for defaulting webhooks + // Example: + // It("Should apply defaults when a required field is empty", func() { + // By("simulating a scenario where defaults should be applied") + // obj.SomeFieldWithDefault = "" + // By("calling the Default method to apply defaults") + // defaulter.Default(ctx, obj) + // By("checking that the default values are set") + // Expect(obj.SomeFieldWithDefault).To(Equal("default_value")) + // }) + }) + + Context("When creating or updating Ironic under Validating Webhook", func() { + // TODO (user): Add logic for validating webhooks + // Example: + // It("Should deny creation if a required field is missing", func() { + // By("simulating an invalid creation scenario") + // obj.SomeRequiredField = "" + // Expect(validator.ValidateCreate(ctx, obj)).Error().To(HaveOccurred()) + // }) + // + // It("Should admit creation if all required fields are present", func() { + // By("simulating an invalid creation scenario") + // obj.SomeRequiredField = "valid_value" + // Expect(validator.ValidateCreate(ctx, obj)).To(BeNil()) + // }) + // + // It("Should validate updates correctly", func() { + // By("simulating a valid update scenario") + // oldObj.SomeRequiredField = "updated_value" + // obj.SomeRequiredField = "updated_value" + // Expect(validator.ValidateUpdate(ctx, oldObj, obj)).To(BeNil()) + // }) + }) + +}) diff --git a/api/v1beta1/webhook_suite_test.go b/internal/webhook/v1beta1/webhook_suite_test.go similarity index 57% rename from api/v1beta1/webhook_suite_test.go rename to internal/webhook/v1beta1/webhook_suite_test.go index 37e8e42a..52ff3ae2 100644 --- a/api/v1beta1/webhook_suite_test.go +++ b/internal/webhook/v1beta1/webhook_suite_test.go @@ -1,9 +1,12 @@ /* -Copyright 2022. +Copyright 2025. + Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -18,6 +21,7 @@ import ( "crypto/tls" "fmt" "net" + "os" "path/filepath" "testing" "time" @@ -25,29 +29,29 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - admissionv1beta1 "k8s.io/api/admission/v1beta1" - //+kubebuilder:scaffold:imports - "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/kubernetes/scheme" "k8s.io/client-go/rest" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/envtest" logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/log/zap" + metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" "sigs.k8s.io/controller-runtime/pkg/webhook" - metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" + ironicv1beta1 "github.com/openstack-k8s-operators/ironic-operator/api/v1beta1" + // +kubebuilder:scaffold:imports ) // These tests use Ginkgo (BDD-style Go testing framework). Refer to // http://onsi.github.io/ginkgo/ to learn more about Ginkgo. var ( - cfg *rest.Config - k8sClient client.Client - testEnv *envtest.Environment ctx context.Context cancel context.CancelFunc + k8sClient client.Client + cfg *rest.Config + testEnv *envtest.Environment ) func TestAPIs(t *testing.T) { @@ -61,55 +65,54 @@ var _ = BeforeSuite(func() { ctx, cancel = context.WithCancel(context.TODO()) + var err error + err = ironicv1beta1.AddToScheme(scheme.Scheme) + Expect(err).NotTo(HaveOccurred()) + + // +kubebuilder:scaffold:scheme + By("bootstrapping test environment") testEnv = &envtest.Environment{ - CRDDirectoryPaths: []string{filepath.Join("..", "..", "config", "crd", "bases")}, + CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "config", "crd", "bases")}, ErrorIfCRDPathMissing: false, + WebhookInstallOptions: envtest.WebhookInstallOptions{ - Paths: []string{filepath.Join("..", "..", "config", "webhook")}, + Paths: []string{filepath.Join("..", "..", "..", "config", "webhook")}, }, } - var err error + // Retrieve the first found binary directory to allow running tests from IDEs + if getFirstFoundEnvTestBinaryDir() != "" { + testEnv.BinaryAssetsDirectory = getFirstFoundEnvTestBinaryDir() + } + // cfg is defined in this file globally. cfg, err = testEnv.Start() Expect(err).NotTo(HaveOccurred()) Expect(cfg).NotTo(BeNil()) - scheme := runtime.NewScheme() - err = AddToScheme(scheme) - Expect(err).NotTo(HaveOccurred()) - - err = admissionv1beta1.AddToScheme(scheme) - Expect(err).NotTo(HaveOccurred()) - - //+kubebuilder:scaffold:scheme - - k8sClient, err = client.New(cfg, client.Options{Scheme: scheme}) + k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}) Expect(err).NotTo(HaveOccurred()) Expect(k8sClient).NotTo(BeNil()) - // start webhook server using Manager + // start webhook server using Manager. webhookInstallOptions := &testEnv.WebhookInstallOptions mgr, err := ctrl.NewManager(cfg, ctrl.Options{ - Scheme: scheme, - Metrics: metricsserver.Options{ - BindAddress: "0", - }, - WebhookServer: webhook.NewServer( - webhook.Options{ - Host: webhookInstallOptions.LocalServingHost, - Port: webhookInstallOptions.LocalServingPort, - CertDir: webhookInstallOptions.LocalServingCertDir, - }), + Scheme: scheme.Scheme, + WebhookServer: webhook.NewServer(webhook.Options{ + Host: webhookInstallOptions.LocalServingHost, + Port: webhookInstallOptions.LocalServingPort, + CertDir: webhookInstallOptions.LocalServingCertDir, + }), LeaderElection: false, + Metrics: metricsserver.Options{BindAddress: "0"}, }) Expect(err).NotTo(HaveOccurred()) - err = (&Ironic{}).SetupWebhookWithManager(mgr) + err = SetupIronicWebhookWithManager(mgr) Expect(err).NotTo(HaveOccurred()) - //+kubebuilder:scaffold:webhook + // +kubebuilder:scaffold:webhook go func() { defer GinkgoRecover() @@ -117,7 +120,7 @@ var _ = BeforeSuite(func() { Expect(err).NotTo(HaveOccurred()) }() - // wait for the webhook server to get ready + // wait for the webhook server to get ready. dialer := &net.Dialer{Timeout: time.Second} addrPort := fmt.Sprintf("%s:%d", webhookInstallOptions.LocalServingHost, webhookInstallOptions.LocalServingPort) Eventually(func() error { @@ -125,14 +128,37 @@ var _ = BeforeSuite(func() { if err != nil { return err } - conn.Close() - return nil + + return conn.Close() }).Should(Succeed()) }) var _ = AfterSuite(func() { - cancel() By("tearing down the test environment") + cancel() err := testEnv.Stop() Expect(err).NotTo(HaveOccurred()) }) + +// getFirstFoundEnvTestBinaryDir locates the first binary in the specified path. +// ENVTEST-based tests depend on specific binaries, usually located in paths set by +// controller-runtime. When running tests directly (e.g., via an IDE) without using +// Makefile targets, the 'BinaryAssetsDirectory' must be explicitly configured. +// +// This function streamlines the process by finding the required binaries, similar to +// setting the 'KUBEBUILDER_ASSETS' environment variable. To ensure the binaries are +// properly set up, run 'make setup-envtest' beforehand. +func getFirstFoundEnvTestBinaryDir() string { + basePath := filepath.Join("..", "..", "..", "bin", "k8s") + entries, err := os.ReadDir(basePath) + if err != nil { + logf.Log.Error(err, "Failed to read directory", "path", basePath) + return "" + } + for _, entry := range entries { + if entry.IsDir() { + return filepath.Join(basePath, entry.Name()) + } + } + return "" +} diff --git a/main.go b/main.go deleted file mode 100644 index b4bd0ba0..00000000 --- a/main.go +++ /dev/null @@ -1,217 +0,0 @@ -/* -Copyright 2022 Red Hat Inc. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Package main implements the ironic-operator controller manager. -package main - -import ( - "context" - "crypto/tls" - "flag" - "os" - "strings" - - // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.) - // to ensure that exec-entrypoint and run can make use of them. - "k8s.io/client-go/kubernetes" - _ "k8s.io/client-go/plugin/pkg/client/auth" - - routev1 "github.com/openshift/api/route/v1" - "k8s.io/apimachinery/pkg/runtime" - utilruntime "k8s.io/apimachinery/pkg/util/runtime" - clientgoscheme "k8s.io/client-go/kubernetes/scheme" - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/client/config" - "sigs.k8s.io/controller-runtime/pkg/healthz" - "sigs.k8s.io/controller-runtime/pkg/log/zap" - "sigs.k8s.io/controller-runtime/pkg/webhook" - - metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" - - networkv1 "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/apis/k8s.cni.cncf.io/v1" - rabbitmqv1 "github.com/openstack-k8s-operators/infra-operator/apis/rabbitmq/v1beta1" - topologyv1 "github.com/openstack-k8s-operators/infra-operator/apis/topology/v1beta1" - ironicv1 "github.com/openstack-k8s-operators/ironic-operator/api/v1beta1" - "github.com/openstack-k8s-operators/ironic-operator/controllers" - keystonev1beta1 "github.com/openstack-k8s-operators/keystone-operator/api/v1beta1" - mariadbv1beta1 "github.com/openstack-k8s-operators/mariadb-operator/api/v1beta1" - - "github.com/openstack-k8s-operators/lib-common/modules/common/operator" - //+kubebuilder:scaffold:imports -) - -var ( - scheme = runtime.NewScheme() - setupLog = ctrl.Log.WithName("setup") -) - -func init() { - utilruntime.Must(clientgoscheme.AddToScheme(scheme)) - - utilruntime.Must(ironicv1.AddToScheme(scheme)) - utilruntime.Must(mariadbv1beta1.AddToScheme(scheme)) - utilruntime.Must(rabbitmqv1.AddToScheme(scheme)) - utilruntime.Must(routev1.AddToScheme(scheme)) - utilruntime.Must(networkv1.AddToScheme(scheme)) - utilruntime.Must(topologyv1.AddToScheme(scheme)) - err := keystonev1beta1.AddToScheme(scheme) - if err != nil { - setupLog.Info("Adding keystone schema failed, only standalone deployments are available") - } - //+kubebuilder:scaffold:scheme -} - -func main() { - var metricsAddr string - var enableLeaderElection bool - var probeAddr string - var pprofBindAddress string - var webhookPort int - var enableHTTP2 bool - flag.BoolVar(&enableHTTP2, "enable-http2", enableHTTP2, "If HTTP/2 should be enabled for the metrics and webhook servers.") - flag.StringVar(&metricsAddr, "metrics-bind-address", ":8080", "The address the metric endpoint binds to.") - flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.") - flag.StringVar(&pprofBindAddress, "pprof-bind-address", "", "The address the pprof endpoint binds to. Set to empty to disable pprof.") - flag.IntVar(&webhookPort, "webhook-bind-address", 9443, "The port the webhook server binds to.") - flag.BoolVar(&enableLeaderElection, "leader-elect", false, - "Enable leader election for controller manager. "+ - "Enabling this will ensure there is only one active controller manager.") - opts := zap.Options{ - Development: true, - } - opts.BindFlags(flag.CommandLine) - flag.Parse() - - ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts))) - - disableHTTP2 := func(c *tls.Config) { - if enableHTTP2 { - return - } - c.NextProtos = []string{"http/1.1"} - } - - options := ctrl.Options{ - Scheme: scheme, - Metrics: metricsserver.Options{ - BindAddress: metricsAddr, - }, - HealthProbeBindAddress: probeAddr, - LeaderElection: enableLeaderElection, - LeaderElectionID: "f92b5c2d.openstack.org", - PprofBindAddress: pprofBindAddress, - WebhookServer: webhook.NewServer( - webhook.Options{ - Port: webhookPort, - TLSOpts: []func(config *tls.Config){disableHTTP2}, - }), - } - - err := operator.SetManagerOptions(&options, setupLog) - if err != nil { - setupLog.Error(err, "unable to set manager options") - os.Exit(1) - } - - mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), options) - if err != nil { - setupLog.Error(err, "unable to start manager") - os.Exit(1) - } - - cfg, err := config.GetConfig() - if err != nil { - setupLog.Error(err, "") - os.Exit(1) - } - kclient, err := kubernetes.NewForConfig(cfg) - if err != nil { - setupLog.Error(err, "") - os.Exit(1) - } - - if err = (&controllers.IronicReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), - Kclient: kclient, - }).SetupWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", "Ironic") - os.Exit(1) - } - if err = (&controllers.IronicConductorReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), - Kclient: kclient, - }).SetupWithManager(context.Background(), mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", "IronicConductor") - os.Exit(1) - } - if err = (&controllers.IronicAPIReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), - Kclient: kclient, - }).SetupWithManager(context.Background(), mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", "IronicAPI") - os.Exit(1) - } - if err = (&controllers.IronicInspectorReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), - Kclient: kclient, - }).SetupWithManager(context.Background(), mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", "IronicInspector") - os.Exit(1) - } - if err = (&controllers.IronicNeutronAgentReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), - Kclient: kclient, - }).SetupWithManager(context.Background(), mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", "IronicNeutronAgent") - os.Exit(1) - } - - // Acquire environmental defaults and initialize operator defaults with them - ironicv1.SetupDefaults() - - // Setup webhook if requested - checker := healthz.Ping - if strings.ToLower(os.Getenv("ENABLE_WEBHOOKS")) != "false" { - - if err = (&ironicv1.Ironic{}).SetupWebhookWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create webhook", "webhook", "Ironic") - os.Exit(1) - } - checker = mgr.GetWebhookServer().StartedChecker() - } - - //+kubebuilder:scaffold:builder - - if err := mgr.AddHealthzCheck("healthz", checker); err != nil { - setupLog.Error(err, "unable to set up health check") - os.Exit(1) - } - if err := mgr.AddReadyzCheck("readyz", checker); err != nil { - setupLog.Error(err, "unable to set up ready check") - os.Exit(1) - } - - setupLog.Info("starting manager") - if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil { - setupLog.Error(err, "problem running manager") - os.Exit(1) - } -} diff --git a/tests/functional/base_test.go b/tests/functional/base_test.go index 8c408331..b21421c2 100644 --- a/tests/functional/base_test.go +++ b/tests/functional/base_test.go @@ -23,8 +23,8 @@ import ( . "github.com/onsi/gomega" //revive:disable:dot-imports ironicv1 "github.com/openstack-k8s-operators/ironic-operator/api/v1beta1" - ironic_pkg "github.com/openstack-k8s-operators/ironic-operator/pkg/ironic" - ironic_inspector_pkg "github.com/openstack-k8s-operators/ironic-operator/pkg/ironicinspector" + ironic_pkg "github.com/openstack-k8s-operators/ironic-operator/internal/ironic" + ironic_inspector_pkg "github.com/openstack-k8s-operators/ironic-operator/internal/ironicinspector" condition "github.com/openstack-k8s-operators/lib-common/modules/common/condition" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" diff --git a/tests/functional/ironicapi_controller_test.go b/tests/functional/ironicapi_controller_test.go index ad3bef45..e4d77e5b 100644 --- a/tests/functional/ironicapi_controller_test.go +++ b/tests/functional/ironicapi_controller_test.go @@ -25,7 +25,7 @@ import ( . "github.com/openstack-k8s-operators/lib-common/modules/common/test/helpers" ironicv1 "github.com/openstack-k8s-operators/ironic-operator/api/v1beta1" - "github.com/openstack-k8s-operators/ironic-operator/pkg/ironic" + "github.com/openstack-k8s-operators/ironic-operator/internal/ironic" "github.com/openstack-k8s-operators/lib-common/modules/common/condition" mariadbv1 "github.com/openstack-k8s-operators/mariadb-operator/api/v1beta1" corev1 "k8s.io/api/core/v1" diff --git a/tests/functional/ironicconductor_controller_test.go b/tests/functional/ironicconductor_controller_test.go index b78ad60b..dd1b77ca 100644 --- a/tests/functional/ironicconductor_controller_test.go +++ b/tests/functional/ironicconductor_controller_test.go @@ -26,7 +26,7 @@ import ( routev1 "github.com/openshift/api/route/v1" ironicv1 "github.com/openstack-k8s-operators/ironic-operator/api/v1beta1" - "github.com/openstack-k8s-operators/ironic-operator/pkg/ironic" + "github.com/openstack-k8s-operators/ironic-operator/internal/ironic" "github.com/openstack-k8s-operators/lib-common/modules/common/condition" mariadbv1 "github.com/openstack-k8s-operators/mariadb-operator/api/v1beta1" corev1 "k8s.io/api/core/v1" diff --git a/tests/functional/suite_test.go b/tests/functional/suite_test.go index 0e416d95..46bbcb87 100644 --- a/tests/functional/suite_test.go +++ b/tests/functional/suite_test.go @@ -45,7 +45,8 @@ import ( rabbitmqv1 "github.com/openstack-k8s-operators/infra-operator/apis/rabbitmq/v1beta1" ironicv1 "github.com/openstack-k8s-operators/ironic-operator/api/v1beta1" - "github.com/openstack-k8s-operators/ironic-operator/controllers" + "github.com/openstack-k8s-operators/ironic-operator/internal/controller" + webhookv1beta1 "github.com/openstack-k8s-operators/ironic-operator/internal/webhook/v1beta1" keystonev1 "github.com/openstack-k8s-operators/keystone-operator/api/v1beta1" mariadbv1 "github.com/openstack-k8s-operators/mariadb-operator/api/v1beta1" @@ -185,38 +186,38 @@ var _ = BeforeSuite(func() { kclient, err := kubernetes.NewForConfig(cfg) Expect(err).ToNot(HaveOccurred(), "failed to create kclient") - err = (&ironicv1.Ironic{}).SetupWebhookWithManager(k8sManager) + err = webhookv1beta1.SetupIronicWebhookWithManager(k8sManager) Expect(err).NotTo(HaveOccurred()) - err = (&controllers.IronicNeutronAgentReconciler{ + err = (&controller.IronicNeutronAgentReconciler{ Client: k8sManager.GetClient(), Scheme: k8sManager.GetScheme(), Kclient: kclient, }).SetupWithManager(context.Background(), k8sManager) Expect(err).ToNot(HaveOccurred()) - err = (&controllers.IronicInspectorReconciler{ + err = (&controller.IronicInspectorReconciler{ Client: k8sManager.GetClient(), Scheme: k8sManager.GetScheme(), Kclient: kclient, }).SetupWithManager(context.Background(), k8sManager) Expect(err).ToNot(HaveOccurred()) - err = (&controllers.IronicConductorReconciler{ + err = (&controller.IronicConductorReconciler{ Client: k8sManager.GetClient(), Scheme: k8sManager.GetScheme(), Kclient: kclient, }).SetupWithManager(context.Background(), k8sManager) Expect(err).ToNot(HaveOccurred()) - err = (&controllers.IronicAPIReconciler{ + err = (&controller.IronicAPIReconciler{ Client: k8sManager.GetClient(), Scheme: k8sManager.GetScheme(), Kclient: kclient, }).SetupWithManager(context.Background(), k8sManager) Expect(err).ToNot(HaveOccurred()) - err = (&controllers.IronicReconciler{ + err = (&controller.IronicReconciler{ Client: k8sManager.GetClient(), Scheme: k8sManager.GetScheme(), Kclient: kclient, diff --git a/tests/kuttl/common/assert-endpoints.yaml b/tests/kuttl/common/assert-endpoints.yaml index 7e426cf1..6d856a4e 100644 --- a/tests/kuttl/common/assert-endpoints.yaml +++ b/tests/kuttl/common/assert-endpoints.yaml @@ -84,7 +84,7 @@ commands: - script: | set -euxo pipefail oc config set-context --current --namespace=$NAMESPACE - tupleTemplate='{{ range (index .spec.template.spec.containers 1).env }}{{ .name }}{{ "#" }}{{ .value}}{{"\n"}}{{ end }}' + tupleTemplate='{{ range (index .spec.template.spec.containers 0).env }}{{ .name }}{{ "#" }}{{ .value}}{{"\n"}}{{ end }}' imageTuples=$(oc get -n openstack-operators deployment ironic-operator-controller-manager -o go-template="$tupleTemplate") for ITEM in $(echo $imageTuples); do # it is an image diff --git a/zuul.d/projects.yaml b/zuul.d/projects.yaml index 90bb9d15..aacc1f13 100644 --- a/zuul.d/projects.yaml +++ b/zuul.d/projects.yaml @@ -3,3 +3,8 @@ name: openstack-k8s-operators/ironic-operator templates: - podified-ironic-operator-pipeline + github-check: + jobs: + - openstack-k8s-operators-content-provider: + vars: + cifmw_install_yamls_sdk_version: v1.41.1