From a5ae3e326aa6cad1ce8693b27b0a5cdf1d0e5c98 Mon Sep 17 00:00:00 2001 From: varshaprasad96 Date: Tue, 30 Jun 2020 09:12:41 -0700 Subject: [PATCH 1/4] [ansible] generate binary to run ansible operator This PR intends to create an independent binary using exec-entrypoint command for ansible operators. --- hack/image/ansible/baseimage/baseimage.go | 17 + hack/image/build-ansible-image.sh | 6 +- internal/scaffold/ansible/dockerfilehybrid.go | 2 +- internal/scaffold/ansible/entrypoint.go | 2 +- pkg/ansible/image/base_image_build.go | 354 ++++++++++++++++++ 5 files changed, 377 insertions(+), 4 deletions(-) create mode 100644 hack/image/ansible/baseimage/baseimage.go create mode 100644 pkg/ansible/image/base_image_build.go diff --git a/hack/image/ansible/baseimage/baseimage.go b/hack/image/ansible/baseimage/baseimage.go new file mode 100644 index 0000000000..6a149ee332 --- /dev/null +++ b/hack/image/ansible/baseimage/baseimage.go @@ -0,0 +1,17 @@ +package main + +import ( + ansibleImage "github.com/operator-framework/operator-sdk/pkg/ansible/image" + "github.com/operator-framework/operator-sdk/pkg/log/zap" + log "github.com/sirupsen/logrus" + logf "sigs.k8s.io/controller-runtime/pkg/log" +) + +func main() { + logf.SetLogger(zap.Logger()) + + err := ansibleImage.RunAnsibleOperator() + if err != nil { + log.Error(err, "error running ansible operator binary") + } +} diff --git a/hack/image/build-ansible-image.sh b/hack/image/build-ansible-image.sh index 596ddc7ba4..58bc36d1a5 100755 --- a/hack/image/build-ansible-image.sh +++ b/hack/image/build-ansible-image.sh @@ -12,13 +12,15 @@ BASEIMAGEDIR="$TMPDIR/ansible-operator" mkdir -p "$BASEIMAGEDIR" go build -o $BASEIMAGEDIR/scaffold-ansible-image ./hack/image/ansible/scaffold-ansible-image.go +# build binary for specific target platform (for purposes of base image only) +env GOOS=linux GOARCH=amd64 go build -o $BASEIMAGEDIR/base-image ./hack/image/ansible/baseimage/baseimage.go + # build operator binary and base image pushd "$BASEIMAGEDIR" ./scaffold-ansible-image mkdir -p build/_output/bin/ -cp $ROOTDIR/build/operator-sdk-dev-linux-gnu build/_output/bin/ansible-operator -operator-sdk build $1 +mv $BASEIMAGEDIR/base-image build/_output/bin # If using a kind cluster, load the image into all nodes. load_image_if_kind "$1" popd diff --git a/internal/scaffold/ansible/dockerfilehybrid.go b/internal/scaffold/ansible/dockerfilehybrid.go index df490902ed..5e8a54f279 100644 --- a/internal/scaffold/ansible/dockerfilehybrid.go +++ b/internal/scaffold/ansible/dockerfilehybrid.go @@ -78,8 +78,8 @@ RUN yum clean all && rm -rf /var/cache/yum/* \ && yum clean all \ && rm -rf /var/cache/yum -COPY build/_output/bin/[[.ProjectName]] ${OPERATOR} COPY bin /usr/local/bin +ADD build/_output/bin/base-image ${HOME}/ RUN /usr/local/bin/user_setup diff --git a/internal/scaffold/ansible/entrypoint.go b/internal/scaffold/ansible/entrypoint.go index 49a5ea0b9e..8689f0ace8 100644 --- a/internal/scaffold/ansible/entrypoint.go +++ b/internal/scaffold/ansible/entrypoint.go @@ -37,5 +37,5 @@ func (e *Entrypoint) GetInput() (input.Input, error) { const entrypointTmpl = `#!/bin/bash -e cd $HOME -exec ${OPERATOR} exec-entrypoint ansible --watches-file=$HOME/watches.yaml $@ +./base-image --watches-file=$HOME/watches.yaml ` diff --git a/pkg/ansible/image/base_image_build.go b/pkg/ansible/image/base_image_build.go new file mode 100644 index 0000000000..803c4e3d97 --- /dev/null +++ b/pkg/ansible/image/base_image_build.go @@ -0,0 +1,354 @@ +// Copyright 2020 The Operator-SDK Authors +// +// 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 image + +import ( + "context" + "errors" + "flag" + "fmt" + "os" + "runtime" + "strconv" + "strings" + "time" + + "github.com/operator-framework/operator-sdk/pkg/ansible/controller" + "github.com/operator-framework/operator-sdk/pkg/ansible/flags" + aoflags "github.com/operator-framework/operator-sdk/pkg/ansible/flags" + "github.com/operator-framework/operator-sdk/pkg/ansible/proxy" + "github.com/operator-framework/operator-sdk/pkg/ansible/proxy/controllermap" + "github.com/operator-framework/operator-sdk/pkg/ansible/runner" + "github.com/operator-framework/operator-sdk/pkg/ansible/watches" + "github.com/operator-framework/operator-sdk/pkg/k8sutil" + kubemetrics "github.com/operator-framework/operator-sdk/pkg/kube-metrics" + "github.com/operator-framework/operator-sdk/pkg/leader" + "github.com/operator-framework/operator-sdk/pkg/metrics" + sdkVersion "github.com/operator-framework/operator-sdk/version" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/util/intstr" + "k8s.io/client-go/rest" + "sigs.k8s.io/controller-runtime/pkg/cache" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/config" + "sigs.k8s.io/controller-runtime/pkg/healthz" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/manager" + "sigs.k8s.io/controller-runtime/pkg/manager/signals" +) + +var ( + metricsHost = "0.0.0.0" + metricsPort int32 = 8383 + operatorMetricsPort int32 = 8686 + healthProbePort int32 = 6789 + log = logf.Log.WithName("ansible-operator") +) + +func printVersion() { + log.Info(fmt.Sprintf("Go Version: %s", runtime.Version())) + log.Info(fmt.Sprintf("Go OS/Arch: %s/%s", runtime.GOOS, runtime.GOARCH)) + log.Info(fmt.Sprintf("Version of operator-sdk: %v", sdkVersion.Version)) +} + +// RunAnsibleOperator sets the default flags for base image and starts the ansible operator. +func RunAnsibleOperator() error { + flags := setDefaultFlags() + + err := runAnsibleOperator(flags) + if err != nil { + return err + } + return nil +} + +// SetDefaultFlags fills the operator flags with default value. These are not user-facing. +// For building base images, only `--watches-file` flag is required. The rest are available +// and can be changed if required in future while building base image. +func setDefaultFlags() *aoflags.AnsibleOperatorFlags { + aof := &flags.AnsibleOperatorFlags{} + + flag.BoolVar(&aof.InjectOwnerRef, "inject-owner-ref", true, "The ansible operator will "+ + "inject owner references unless this flag is false") + flag.IntVar(&aof.MaxWorkers, "max-workers", 1, "Maximum number of workers to use. "+ + "Overridden by environment variable.") + flag.IntVar(&aof.AnsibleVerbosity, "ansible-verbosity", 2, "Ansible verbosity. "+ + "Overridden by environment variable.") + flag.StringVar(&aof.AnsibleRolesPath, "ansible-roles-path", "", "Ansible Roles Path. "+ + "If unset, roles are assumed to be in {{CWD}}/roles.") + flag.StringVar(&aof.AnsibleCollectionsPath, "ansible-collections-path", "", + `Path to installed Ansible Collections. If set, collections should be + located in {{value}}/ansible_collections/. If unset, collections are + assumed to be in ~/.ansible/collections or + /usr/share/ansible/collections.`) + flag.DurationVar(&aof.ReconcilePeriod, "reconcile-period", time.Minute, "Default reconcile"+ + "period for controllers") + flag.StringVar(&aof.WatchesFile, "watches-file", "watches.yaml", "watches file location") + + flag.Parse() + + if err := setAnsibleEnvVars(aof); err != nil { + log.Error(err, "Error setting env variables") + } + + return aof +} + +func setAnsibleEnvVars(flags *aoflags.AnsibleOperatorFlags) error { + if flags != nil { + if len(flags.AnsibleRolesPath) > 0 { + if err := os.Setenv(aoflags.AnsibleRolesPathEnvVar, flags.AnsibleRolesPath); err != nil { + return fmt.Errorf("failed to set %s environment variable: (%v)", aoflags.AnsibleRolesPathEnvVar, err) + } + log.Info("Set the value %v for environment variable %v.", + flags.AnsibleRolesPath, aoflags.AnsibleRolesPathEnvVar) + } + if len(flags.AnsibleCollectionsPath) > 0 { + if err := os.Setenv(aoflags.AnsibleCollectionsPathEnvVar, flags.AnsibleCollectionsPath); err != nil { + return fmt.Errorf("failed to set %s environment variable: (%v)", aoflags.AnsibleCollectionsPathEnvVar, err) + } + log.Info("Set the value %v for environment variable %v.", + flags.AnsibleCollectionsPath, aoflags.AnsibleCollectionsPathEnvVar) + } + } + return nil +} + +func runAnsibleOperator(flags *aoflags.AnsibleOperatorFlags) error { + printVersion() + + cfg, err := config.GetConfig() + if err != nil { + log.Error(err, "Failed to get config.") + return err + } + + // Set default manager options + // TODO: probably should expose the host & port as an environment variables + options := manager.Options{ + HealthProbeBindAddress: fmt.Sprintf("%s:%d", metricsHost, healthProbePort), + MetricsBindAddress: fmt.Sprintf("%s:%d", metricsHost, metricsPort), + NewClient: func(cache cache.Cache, config *rest.Config, options client.Options) (client.Client, error) { + c, err := client.New(config, options) + if err != nil { + return nil, err + } + return &client.DelegatingClient{ + Reader: cache, + Writer: c, + StatusClient: c, + }, nil + }, + } + namespace, found := os.LookupEnv(k8sutil.WatchNamespaceEnvVar) + log = log.WithValues("Namespace", namespace) + if found { + if namespace == metav1.NamespaceAll { + log.Info("Watching all namespaces.") + options.Namespace = metav1.NamespaceAll + } else { + if strings.Contains(namespace, ",") { + log.Info("Watching multiple namespaces.") + options.NewCache = cache.MultiNamespacedCacheBuilder(strings.Split(namespace, ",")) + } else { + log.Info("Watching single namespace.") + options.Namespace = namespace + } + } + } else { + log.Info(fmt.Sprintf("%v environment variable not set. Watching all namespaces.", + k8sutil.WatchNamespaceEnvVar)) + options.Namespace = metav1.NamespaceAll + } + + // Create a new manager to provide shared dependencies and start components + mgr, err := manager.New(cfg, options) + if err != nil { + log.Error(err, "Failed to create a new manager.") + return err + } + + var gvks []schema.GroupVersionKind + cMap := controllermap.NewControllerMap() + flags.WatchesFile = "watches.yaml" + watches, err := watches.Load(flags.WatchesFile, flags.MaxWorkers, flags.AnsibleVerbosity) + if err != nil { + log.Error(err, "Failed to load watches.") + return err + } + fmt.Printf("loaded watches") + for _, w := range watches { + runner, err := runner.New(w) + if err != nil { + log.Error(err, "Failed to create runner") + return err + } + + ctr := controller.Add(mgr, controller.Options{ + GVK: w.GroupVersionKind, + Runner: runner, + ManageStatus: w.ManageStatus, + AnsibleDebugLogs: getAnsibleDebugLog(), + MaxWorkers: w.MaxWorkers, + ReconcilePeriod: w.ReconcilePeriod, + }) + if ctr == nil { + return fmt.Errorf("failed to add controller for GVK %v", w.GroupVersionKind.String()) + } + + cMap.Store(w.GroupVersionKind, &controllermap.Contents{Controller: *ctr, + WatchDependentResources: w.WatchDependentResources, + WatchClusterScopedResources: w.WatchClusterScopedResources, + OwnerWatchMap: controllermap.NewWatchMap(), + AnnotationWatchMap: controllermap.NewWatchMap(), + }, w.Blacklist) + gvks = append(gvks, w.GroupVersionKind) + } + operatorName, err := k8sutil.GetOperatorName() + if err != nil { + log.Error(err, "Failed to get the operator name") + return err + } + // Become the leader before proceeding + err = leader.Become(context.TODO(), operatorName+"-lock") + if err != nil { + log.Error(err, "Failed to become leader.") + return err + } + addMetrics(context.TODO(), cfg, gvks) + err = mgr.AddHealthzCheck("ping", healthz.Ping) + if err != nil { + log.Error(err, "Failed to add Healthz check.") + } + done := make(chan error) + + // start the proxy + err = proxy.Run(done, proxy.Options{ + Address: "localhost", + Port: 8888, + KubeConfig: mgr.GetConfig(), + Cache: mgr.GetCache(), + RESTMapper: mgr.GetRESTMapper(), + ControllerMap: cMap, + OwnerInjection: flags.InjectOwnerRef, + WatchedNamespaces: []string{namespace}, + }) + + if err != nil { + log.Error(err, "Error starting proxy.") + return err + } + + // start the operator + go func() { + done <- mgr.Start(signals.SetupSignalHandler()) + }() + + // wait for either to finish + err = <-done + if err != nil { + log.Error(err, "Proxy or operator exited with error.") + os.Exit(1) + } + log.Info("Exiting.") + return nil + +} + +// addMetrics will create the Services and Service Monitors to allow the operator export the metrics by using +// the Prometheus operator +func addMetrics(ctx context.Context, cfg *rest.Config, gvks []schema.GroupVersionKind) { + // Get the namespace the operator is currently deployed in. + operatorNs, err := k8sutil.GetOperatorNamespace() + if err != nil { + if errors.Is(err, k8sutil.ErrRunLocal) { + log.Info("Skipping CR metrics server creation; not running in a cluster.") + return + } + } + + if err := serveCRMetrics(cfg, operatorNs, gvks); err != nil { + log.Info("Could not generate and serve custom resource metrics", "error", err.Error()) + } + + // Add to the below struct any other metrics ports you want to expose. + servicePorts := []v1.ServicePort{ + {Port: metricsPort, Name: metrics.OperatorPortName, Protocol: v1.ProtocolTCP, + TargetPort: intstr.IntOrString{Type: intstr.Int, IntVal: metricsPort}}, + {Port: operatorMetricsPort, Name: metrics.CRPortName, Protocol: v1.ProtocolTCP, + TargetPort: intstr.IntOrString{Type: intstr.Int, IntVal: operatorMetricsPort}}, + } + + // Create Service object to expose the metrics port(s). + service, err := metrics.CreateMetricsService(ctx, cfg, servicePorts) + if err != nil { + log.Info("Could not create metrics Service", "error", err.Error()) + return + } + + // CreateServiceMonitors will automatically create the prometheus-operator ServiceMonitor resources + // necessary to configure Prometheus to scrape metrics from this operator. + services := []*v1.Service{service} + + // The ServiceMonitor is created in the same namespace where the operator is deployed + _, err = metrics.CreateServiceMonitors(cfg, operatorNs, services) + if err != nil { + log.Info("Could not create ServiceMonitor object", "error", err.Error()) + // If this operator is deployed to a cluster without the prometheus-operator running, it will return + // ErrServiceMonitorNotPresent, which can be used to safely skip ServiceMonitor creation. + if err == metrics.ErrServiceMonitorNotPresent { + log.Info("Install prometheus-operator in your cluster to create ServiceMonitor objects", "error", err.Error()) + } + } +} + +// serveCRMetrics takes GVKs retrieved from watches and generates metrics based on those types. +// It serves those metrics on "http://metricsHost:operatorMetricsPort". +func serveCRMetrics(cfg *rest.Config, operatorNs string, gvks []schema.GroupVersionKind) error { + // The metrics will be generated from the namespaces which are returned here. + // NOTE that passing nil or an empty list of namespaces in GenerateAndServeCRMetrics will result in an error. + ns, err := kubemetrics.GetNamespacesForMetrics(operatorNs) + if err != nil { + return err + } + + // Generate and serve custom resource specific metrics. + err = kubemetrics.GenerateAndServeCRMetrics(cfg, ns, gvks, metricsHost, operatorMetricsPort) + if err != nil { + return err + } + return nil +} + +// getAnsibleDebugLog return the value from the ANSIBLE_DEBUG_LOGS it order to +// print the full Ansible logs +func getAnsibleDebugLog() bool { + const envVar = "ANSIBLE_DEBUG_LOGS" + val := false + if envVal, ok := os.LookupEnv(envVar); ok { + if i, err := strconv.ParseBool(envVal); err != nil { + log.Info("Could not parse environment variable as an boolean; using default value", + "envVar", envVar, "default", val) + } else { + val = i + } + } else if !ok { + log.Info("Environment variable not set; using default value", "envVar", envVar, + envVar, val) + } + return val +} From 16e4b357212f6eb7f12a62b8e90b623f1d049cec Mon Sep 17 00:00:00 2001 From: varshaprasad96 Date: Wed, 1 Jul 2020 17:26:03 -0700 Subject: [PATCH 2/4] [helm] generate binary to run helm operator --- hack/image/ansible/baseimage/baseimage.go | 14 ++ hack/image/build-ansible-image.sh | 1 + hack/image/build-helm-image.sh | 5 +- hack/image/helm/baseimage/baseimage.go | 31 +++ internal/scaffold/helm/dockerfilehybrid.go | 3 +- internal/scaffold/helm/entrypoint.go | 2 +- pkg/ansible/image/base_image_build.go | 4 +- pkg/helm/image/base_image_build.go | 252 +++++++++++++++++++++ 8 files changed, 306 insertions(+), 6 deletions(-) create mode 100644 hack/image/helm/baseimage/baseimage.go create mode 100644 pkg/helm/image/base_image_build.go diff --git a/hack/image/ansible/baseimage/baseimage.go b/hack/image/ansible/baseimage/baseimage.go index 6a149ee332..3b9b5caa71 100644 --- a/hack/image/ansible/baseimage/baseimage.go +++ b/hack/image/ansible/baseimage/baseimage.go @@ -1,3 +1,17 @@ +// Copyright 2019 The Operator-SDK Authors +// +// 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 import ( diff --git a/hack/image/build-ansible-image.sh b/hack/image/build-ansible-image.sh index 58bc36d1a5..bd0912ff1d 100755 --- a/hack/image/build-ansible-image.sh +++ b/hack/image/build-ansible-image.sh @@ -21,6 +21,7 @@ pushd "$BASEIMAGEDIR" mkdir -p build/_output/bin/ mv $BASEIMAGEDIR/base-image build/_output/bin +operator-sdk build $1 # If using a kind cluster, load the image into all nodes. load_image_if_kind "$1" popd diff --git a/hack/image/build-helm-image.sh b/hack/image/build-helm-image.sh index 52e55be75d..3f30d246ab 100755 --- a/hack/image/build-helm-image.sh +++ b/hack/image/build-helm-image.sh @@ -12,12 +12,15 @@ BASEIMAGEDIR="$TMPDIR/helm-operator" mkdir -p "$BASEIMAGEDIR" go build -o $BASEIMAGEDIR/scaffold-helm-image ./hack/image/helm/scaffold-helm-image.go +# build binary for specific target platform (for purposes of base image only) +env GOOS=linux GOARCH=amd64 go build -o $BASEIMAGEDIR/base-image ./hack/image/helm/baseimage/baseimage.go + # build operator binary and base image pushd "$BASEIMAGEDIR" ./scaffold-helm-image mkdir -p build/_output/bin/ -cp $ROOTDIR/build/operator-sdk-dev-linux-gnu build/_output/bin/helm-operator +mv $BASEIMAGEDIR/base-image build/_output/bin operator-sdk build $1 # If using a kind cluster, load the image into all nodes. load_image_if_kind "$1" diff --git a/hack/image/helm/baseimage/baseimage.go b/hack/image/helm/baseimage/baseimage.go new file mode 100644 index 0000000000..095433210b --- /dev/null +++ b/hack/image/helm/baseimage/baseimage.go @@ -0,0 +1,31 @@ +// Copyright 2019 The Operator-SDK Authors +// +// 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 + +import ( + helmImage "github.com/operator-framework/operator-sdk/pkg/helm/image" + "github.com/operator-framework/operator-sdk/pkg/log/zap" + log "github.com/sirupsen/logrus" + logf "sigs.k8s.io/controller-runtime/pkg/log" +) + +func main() { + logf.SetLogger(zap.Logger()) + + err := helmImage.RunHelmOperator() + if err != nil { + log.Error(err, "error running helm operator binary") + } +} diff --git a/internal/scaffold/helm/dockerfilehybrid.go b/internal/scaffold/helm/dockerfilehybrid.go index c283e2424f..0a6b519465 100644 --- a/internal/scaffold/helm/dockerfilehybrid.go +++ b/internal/scaffold/helm/dockerfilehybrid.go @@ -51,8 +51,7 @@ ENV OPERATOR=/usr/local/bin/helm-operator \ {{- if .Watches }} COPY watches.yaml ${HOME}/watches.yaml{{ end }} -# install operator binary -COPY build/_output/bin/{{.ProjectName}} ${OPERATOR} +ADD build/_output/bin/base-image ${HOME}/ COPY bin /usr/local/bin RUN /usr/local/bin/user_setup diff --git a/internal/scaffold/helm/entrypoint.go b/internal/scaffold/helm/entrypoint.go index 3d04c95b64..10d6c07518 100644 --- a/internal/scaffold/helm/entrypoint.go +++ b/internal/scaffold/helm/entrypoint.go @@ -37,5 +37,5 @@ func (e *Entrypoint) GetInput() (input.Input, error) { const entrypointTmpl = `#!/bin/sh -e cd $HOME -exec ${OPERATOR} exec-entrypoint helm --watches-file=$HOME/watches.yaml $@ +./base-image --watches-file=$HOME/watches.yaml ` diff --git a/pkg/ansible/image/base_image_build.go b/pkg/ansible/image/base_image_build.go index 803c4e3d97..acd490a1b0 100644 --- a/pkg/ansible/image/base_image_build.go +++ b/pkg/ansible/image/base_image_build.go @@ -76,7 +76,7 @@ func RunAnsibleOperator() error { return nil } -// SetDefaultFlags fills the operator flags with default value. These are not user-facing. +// setDefaultFlags fills the operator flags with default value. These are not user-facing. // For building base images, only `--watches-file` flag is required. The rest are available // and can be changed if required in future while building base image. func setDefaultFlags() *aoflags.AnsibleOperatorFlags { @@ -97,7 +97,7 @@ func setDefaultFlags() *aoflags.AnsibleOperatorFlags { /usr/share/ansible/collections.`) flag.DurationVar(&aof.ReconcilePeriod, "reconcile-period", time.Minute, "Default reconcile"+ "period for controllers") - flag.StringVar(&aof.WatchesFile, "watches-file", "watches.yaml", "watches file location") + flag.StringVar(&aof.WatchesFile, "watches-file", "watches.yaml", "Path to the watches file to use") flag.Parse() diff --git a/pkg/helm/image/base_image_build.go b/pkg/helm/image/base_image_build.go new file mode 100644 index 0000000000..10ac036241 --- /dev/null +++ b/pkg/helm/image/base_image_build.go @@ -0,0 +1,252 @@ +// Copyright 2020 The Operator-SDK Authors +// +// 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 image + +import ( + "context" + "errors" + "flag" + "fmt" + "os" + "runtime" + "strings" + "time" + + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/util/intstr" + "k8s.io/client-go/rest" + "sigs.k8s.io/controller-runtime/pkg/cache" + crclient "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/config" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/manager" + "sigs.k8s.io/controller-runtime/pkg/manager/signals" + + "github.com/operator-framework/operator-sdk/pkg/helm/controller" + hoflags "github.com/operator-framework/operator-sdk/pkg/helm/flags" + "github.com/operator-framework/operator-sdk/pkg/helm/release" + "github.com/operator-framework/operator-sdk/pkg/helm/watches" + "github.com/operator-framework/operator-sdk/pkg/k8sutil" + kubemetrics "github.com/operator-framework/operator-sdk/pkg/kube-metrics" + "github.com/operator-framework/operator-sdk/pkg/leader" + "github.com/operator-framework/operator-sdk/pkg/metrics" + sdkVersion "github.com/operator-framework/operator-sdk/version" +) + +var ( + metricsHost = "0.0.0.0" + metricsPort int32 = 8383 + operatorMetricsPort int32 = 8686 +) + +var log = logf.Log.WithName("helm-operator") + +func printVersion() { + log.Info(fmt.Sprintf("Go Version: %s", runtime.Version())) + log.Info(fmt.Sprintf("Go OS/Arch: %s/%s", runtime.GOOS, runtime.GOARCH)) + log.Info(fmt.Sprintf("Version of operator-sdk: %v", sdkVersion.Version)) +} + +// setDefaultFlags fills the operator flags with default value. These are not user-facing. +// For building base images, only `--watches-file` flag is required. The rest are available +// and can be changed if required in future while building base image. +func setDefaultFlags() *hoflags.HelmOperatorFlags { + hof := &hoflags.HelmOperatorFlags{} + flag.IntVar(&hof.MaxWorkers, "max-workers", 1, "Maximum number of workers to use.") + flag.DurationVar(&hof.ReconcilePeriod, "reconcile-period", time.Minute, + "Default reconcile period for controllers") + flag.StringVar(&hof.WatchesFile, "watches-file", "watches.yaml", "Path to the watches file to use") + flag.Parse() + + return hof +} + +// RunHelmOperator sets the default flags for base image and starts the ansible operator. +func RunHelmOperator() error { + flags := setDefaultFlags() + + err := runHelmOperator(flags) + if err != nil { + return err + } + return nil +} + +func runHelmOperator(flags *hoflags.HelmOperatorFlags) error { + printVersion() + + cfg, err := config.GetConfig() + if err != nil { + log.Error(err, "Failed to get config.") + return err + } + + // Set default manager options + options := manager.Options{ + MetricsBindAddress: fmt.Sprintf("%s:%d", metricsHost, metricsPort), + NewClient: func(cache cache.Cache, config *rest.Config, options crclient.Options) (crclient.Client, error) { + c, err := crclient.New(config, options) + if err != nil { + return nil, err + } + return &crclient.DelegatingClient{ + Reader: cache, + Writer: c, + StatusClient: c, + }, nil + }, + } + + namespace, found := os.LookupEnv(k8sutil.WatchNamespaceEnvVar) + log = log.WithValues("Namespace", namespace) + if found { + if namespace == metav1.NamespaceAll { + log.Info("Watching all namespaces.") + options.Namespace = metav1.NamespaceAll + } else { + if strings.Contains(namespace, ",") { + log.Info("Watching multiple namespaces.") + options.NewCache = cache.MultiNamespacedCacheBuilder(strings.Split(namespace, ",")) + } else { + log.Info("Watching single namespace.") + options.Namespace = namespace + } + } + } else { + log.Info(fmt.Sprintf("%v environment variable not set. Watching all namespaces.", + k8sutil.WatchNamespaceEnvVar)) + options.Namespace = metav1.NamespaceAll + } + + mgr, err := manager.New(cfg, options) + if err != nil { + log.Error(err, "Failed to create a new manager.") + return err + } + + ws, err := watches.Load(flags.WatchesFile) + if err != nil { + log.Error(err, "Failed to create new manager factories.") + return err + } + var gvks []schema.GroupVersionKind + for _, w := range ws { + // Register the controller with the factory. + err := controller.Add(mgr, controller.WatchOptions{ + Namespace: namespace, + GVK: w.GroupVersionKind, + ManagerFactory: release.NewManagerFactory(mgr, w.ChartDir), + ReconcilePeriod: flags.ReconcilePeriod, + WatchDependentResources: *w.WatchDependentResources, + OverrideValues: w.OverrideValues, + MaxWorkers: flags.MaxWorkers, + }) + if err != nil { + log.Error(err, "Failed to add manager factory to controller.") + return err + } + gvks = append(gvks, w.GroupVersionKind) + } + + operatorName, err := k8sutil.GetOperatorName() + if err != nil { + log.Error(err, "Failed to get operator name") + return err + } + + ctx := context.TODO() + + // Become the leader before proceeding + err = leader.Become(ctx, operatorName+"-lock") + if err != nil { + log.Error(err, "Failed to become leader.") + return err + } + + addMetrics(context.TODO(), cfg, gvks) + + // Start the Cmd + if err = mgr.Start(signals.SetupSignalHandler()); err != nil { + log.Error(err, "Manager exited non-zero.") + os.Exit(1) + } + return nil +} + +// addMetrics will create the Services and Service Monitors to allow the operator export the metrics by using +// the Prometheus operator +func addMetrics(ctx context.Context, cfg *rest.Config, gvks []schema.GroupVersionKind) { + // Get the namespace the operator is currently deployed in. + operatorNs, err := k8sutil.GetOperatorNamespace() + if err != nil { + if errors.Is(err, k8sutil.ErrRunLocal) { + log.Info("Skipping CR metrics server creation; not running in a cluster.") + return + } + } + + if err := serveCRMetrics(cfg, operatorNs, gvks); err != nil { + log.Info("Could not generate and serve custom resource metrics", "error", err.Error()) + } + + // Add to the below struct any other metrics ports you want to expose. + servicePorts := []v1.ServicePort{ + {Port: metricsPort, Name: metrics.OperatorPortName, Protocol: v1.ProtocolTCP, + TargetPort: intstr.IntOrString{Type: intstr.Int, IntVal: metricsPort}}, + {Port: operatorMetricsPort, Name: metrics.CRPortName, Protocol: v1.ProtocolTCP, + TargetPort: intstr.IntOrString{Type: intstr.Int, IntVal: operatorMetricsPort}}, + } + + // Create Service object to expose the metrics port(s). + service, err := metrics.CreateMetricsService(ctx, cfg, servicePorts) + if err != nil { + log.Info("Could not create metrics Service", "error", err.Error()) + } + + // CreateServiceMonitors will automatically create the prometheus-operator ServiceMonitor resources + // necessary to configure Prometheus to scrape metrics from this operator. + services := []*v1.Service{service} + + // The ServiceMonitor is created in the same namespace where the operator is deployed + _, err = metrics.CreateServiceMonitors(cfg, operatorNs, services) + if err != nil { + log.Info("Could not create ServiceMonitor object", "error", err.Error()) + // If this operator is deployed to a cluster without the prometheus-operator running, it will return + // ErrServiceMonitorNotPresent, which can be used to safely skip ServiceMonitor creation. + if err == metrics.ErrServiceMonitorNotPresent { + log.Info("Install prometheus-operator in your cluster to create ServiceMonitor objects", "error", err.Error()) + } + } +} + +// serveCRMetrics gets the Operator/CustomResource GVKs and generates metrics based on those types. +// It serves those metrics on "http://metricsHost:operatorMetricsPort". +func serveCRMetrics(cfg *rest.Config, operatorNs string, gvks []schema.GroupVersionKind) error { + // The metrics will be generated from the namespaces which are returned here. + // NOTE that passing nil or an empty list of namespaces in GenerateAndServeCRMetrics will result in an error. + ns, err := kubemetrics.GetNamespacesForMetrics(operatorNs) + if err != nil { + return err + } + + // Generate and serve custom resource specific metrics. + err = kubemetrics.GenerateAndServeCRMetrics(cfg, ns, gvks, metricsHost, operatorMetricsPort) + if err != nil { + return err + } + return nil +} From 8825cd0245bf38b290394d56de405602ab5a58fd Mon Sep 17 00:00:00 2001 From: varshaprasad96 Date: Thu, 2 Jul 2020 09:46:02 -0700 Subject: [PATCH 3/4] [ansible/helm] make changes to e2e tests --- .../ansible-operator/main.go | 16 +- .../baseimage.go => cmd/helm-operator/main.go | 16 +- hack/image/build-ansible-image.sh | 4 +- hack/image/build-helm-image.sh | 4 +- internal/scaffold/ansible/dockerfilehybrid.go | 2 +- internal/scaffold/ansible/entrypoint.go | 2 +- internal/scaffold/helm/dockerfilehybrid.go | 3 +- internal/scaffold/helm/entrypoint.go | 2 +- pkg/ansible/image/base_image_build.go | 354 ------------------ pkg/helm/image/base_image_build.go | 252 ------------- 10 files changed, 29 insertions(+), 626 deletions(-) rename hack/image/helm/baseimage/baseimage.go => cmd/ansible-operator/main.go (70%) rename hack/image/ansible/baseimage/baseimage.go => cmd/helm-operator/main.go (71%) delete mode 100644 pkg/ansible/image/base_image_build.go delete mode 100644 pkg/helm/image/base_image_build.go diff --git a/hack/image/helm/baseimage/baseimage.go b/cmd/ansible-operator/main.go similarity index 70% rename from hack/image/helm/baseimage/baseimage.go rename to cmd/ansible-operator/main.go index 095433210b..101d180974 100644 --- a/hack/image/helm/baseimage/baseimage.go +++ b/cmd/ansible-operator/main.go @@ -1,4 +1,4 @@ -// Copyright 2019 The Operator-SDK Authors +// Copyright 2020 The Operator-SDK Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -15,17 +15,21 @@ package main import ( - helmImage "github.com/operator-framework/operator-sdk/pkg/helm/image" - "github.com/operator-framework/operator-sdk/pkg/log/zap" log "github.com/sirupsen/logrus" + "github.com/spf13/pflag" logf "sigs.k8s.io/controller-runtime/pkg/log" + + "github.com/operator-framework/operator-sdk/pkg/ansible" + aoflags "github.com/operator-framework/operator-sdk/pkg/ansible/flags" + "github.com/operator-framework/operator-sdk/pkg/log/zap" ) func main() { + flags := aoflags.AddTo(pflag.CommandLine) + pflag.Parse() logf.SetLogger(zap.Logger()) - err := helmImage.RunHelmOperator() - if err != nil { - log.Error(err, "error running helm operator binary") + if err := ansible.Run(flags); err != nil { + log.Fatal(err) } } diff --git a/hack/image/ansible/baseimage/baseimage.go b/cmd/helm-operator/main.go similarity index 71% rename from hack/image/ansible/baseimage/baseimage.go rename to cmd/helm-operator/main.go index 3b9b5caa71..f76ba5d911 100644 --- a/hack/image/ansible/baseimage/baseimage.go +++ b/cmd/helm-operator/main.go @@ -1,4 +1,4 @@ -// Copyright 2019 The Operator-SDK Authors +// Copyright 2020 The Operator-SDK Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -15,17 +15,21 @@ package main import ( - ansibleImage "github.com/operator-framework/operator-sdk/pkg/ansible/image" - "github.com/operator-framework/operator-sdk/pkg/log/zap" log "github.com/sirupsen/logrus" + "github.com/spf13/pflag" logf "sigs.k8s.io/controller-runtime/pkg/log" + + "github.com/operator-framework/operator-sdk/pkg/helm" + hoflags "github.com/operator-framework/operator-sdk/pkg/helm/flags" + "github.com/operator-framework/operator-sdk/pkg/log/zap" ) func main() { + flags := hoflags.AddTo(pflag.CommandLine) + pflag.Parse() logf.SetLogger(zap.Logger()) - err := ansibleImage.RunAnsibleOperator() - if err != nil { - log.Error(err, "error running ansible operator binary") + if err := helm.Run(flags); err != nil { + log.Fatal(err) } } diff --git a/hack/image/build-ansible-image.sh b/hack/image/build-ansible-image.sh index bd0912ff1d..6588bbe146 100755 --- a/hack/image/build-ansible-image.sh +++ b/hack/image/build-ansible-image.sh @@ -13,14 +13,14 @@ mkdir -p "$BASEIMAGEDIR" go build -o $BASEIMAGEDIR/scaffold-ansible-image ./hack/image/ansible/scaffold-ansible-image.go # build binary for specific target platform (for purposes of base image only) -env GOOS=linux GOARCH=amd64 go build -o $BASEIMAGEDIR/base-image ./hack/image/ansible/baseimage/baseimage.go +env GOOS=linux GOARCH=amd64 go build -o $BASEIMAGEDIR/ansible-operator-dev-linux-gnu ./cmd/ansible-operator/main.go # build operator binary and base image pushd "$BASEIMAGEDIR" ./scaffold-ansible-image mkdir -p build/_output/bin/ -mv $BASEIMAGEDIR/base-image build/_output/bin +cp $BASEIMAGEDIR/ansible-operator-dev-linux-gnu build/_output/bin/ansible-operator operator-sdk build $1 # If using a kind cluster, load the image into all nodes. load_image_if_kind "$1" diff --git a/hack/image/build-helm-image.sh b/hack/image/build-helm-image.sh index 3f30d246ab..dcf1f79341 100755 --- a/hack/image/build-helm-image.sh +++ b/hack/image/build-helm-image.sh @@ -13,14 +13,14 @@ mkdir -p "$BASEIMAGEDIR" go build -o $BASEIMAGEDIR/scaffold-helm-image ./hack/image/helm/scaffold-helm-image.go # build binary for specific target platform (for purposes of base image only) -env GOOS=linux GOARCH=amd64 go build -o $BASEIMAGEDIR/base-image ./hack/image/helm/baseimage/baseimage.go +env GOOS=linux GOARCH=amd64 go build -o $BASEIMAGEDIR/helm-operator-dev-linux-gnu ./cmd/helm-operator/main.go # build operator binary and base image pushd "$BASEIMAGEDIR" ./scaffold-helm-image mkdir -p build/_output/bin/ -mv $BASEIMAGEDIR/base-image build/_output/bin +cp $BASEIMAGEDIR/helm-operator-dev-linux-gnu build/_output/bin/helm-operator operator-sdk build $1 # If using a kind cluster, load the image into all nodes. load_image_if_kind "$1" diff --git a/internal/scaffold/ansible/dockerfilehybrid.go b/internal/scaffold/ansible/dockerfilehybrid.go index 5e8a54f279..df490902ed 100644 --- a/internal/scaffold/ansible/dockerfilehybrid.go +++ b/internal/scaffold/ansible/dockerfilehybrid.go @@ -78,8 +78,8 @@ RUN yum clean all && rm -rf /var/cache/yum/* \ && yum clean all \ && rm -rf /var/cache/yum +COPY build/_output/bin/[[.ProjectName]] ${OPERATOR} COPY bin /usr/local/bin -ADD build/_output/bin/base-image ${HOME}/ RUN /usr/local/bin/user_setup diff --git a/internal/scaffold/ansible/entrypoint.go b/internal/scaffold/ansible/entrypoint.go index 8689f0ace8..c559a0436f 100644 --- a/internal/scaffold/ansible/entrypoint.go +++ b/internal/scaffold/ansible/entrypoint.go @@ -37,5 +37,5 @@ func (e *Entrypoint) GetInput() (input.Input, error) { const entrypointTmpl = `#!/bin/bash -e cd $HOME -./base-image --watches-file=$HOME/watches.yaml +exec ${OPERATOR} --watches-file=$HOME/watches.yaml $@ ` diff --git a/internal/scaffold/helm/dockerfilehybrid.go b/internal/scaffold/helm/dockerfilehybrid.go index 0a6b519465..c283e2424f 100644 --- a/internal/scaffold/helm/dockerfilehybrid.go +++ b/internal/scaffold/helm/dockerfilehybrid.go @@ -51,7 +51,8 @@ ENV OPERATOR=/usr/local/bin/helm-operator \ {{- if .Watches }} COPY watches.yaml ${HOME}/watches.yaml{{ end }} -ADD build/_output/bin/base-image ${HOME}/ +# install operator binary +COPY build/_output/bin/{{.ProjectName}} ${OPERATOR} COPY bin /usr/local/bin RUN /usr/local/bin/user_setup diff --git a/internal/scaffold/helm/entrypoint.go b/internal/scaffold/helm/entrypoint.go index 10d6c07518..0738893962 100644 --- a/internal/scaffold/helm/entrypoint.go +++ b/internal/scaffold/helm/entrypoint.go @@ -37,5 +37,5 @@ func (e *Entrypoint) GetInput() (input.Input, error) { const entrypointTmpl = `#!/bin/sh -e cd $HOME -./base-image --watches-file=$HOME/watches.yaml +exec ${OPERATOR} --watches-file=$HOME/watches.yaml $@ ` diff --git a/pkg/ansible/image/base_image_build.go b/pkg/ansible/image/base_image_build.go deleted file mode 100644 index acd490a1b0..0000000000 --- a/pkg/ansible/image/base_image_build.go +++ /dev/null @@ -1,354 +0,0 @@ -// Copyright 2020 The Operator-SDK Authors -// -// 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 image - -import ( - "context" - "errors" - "flag" - "fmt" - "os" - "runtime" - "strconv" - "strings" - "time" - - "github.com/operator-framework/operator-sdk/pkg/ansible/controller" - "github.com/operator-framework/operator-sdk/pkg/ansible/flags" - aoflags "github.com/operator-framework/operator-sdk/pkg/ansible/flags" - "github.com/operator-framework/operator-sdk/pkg/ansible/proxy" - "github.com/operator-framework/operator-sdk/pkg/ansible/proxy/controllermap" - "github.com/operator-framework/operator-sdk/pkg/ansible/runner" - "github.com/operator-framework/operator-sdk/pkg/ansible/watches" - "github.com/operator-framework/operator-sdk/pkg/k8sutil" - kubemetrics "github.com/operator-framework/operator-sdk/pkg/kube-metrics" - "github.com/operator-framework/operator-sdk/pkg/leader" - "github.com/operator-framework/operator-sdk/pkg/metrics" - sdkVersion "github.com/operator-framework/operator-sdk/version" - v1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/util/intstr" - "k8s.io/client-go/rest" - "sigs.k8s.io/controller-runtime/pkg/cache" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/client/config" - "sigs.k8s.io/controller-runtime/pkg/healthz" - logf "sigs.k8s.io/controller-runtime/pkg/log" - "sigs.k8s.io/controller-runtime/pkg/manager" - "sigs.k8s.io/controller-runtime/pkg/manager/signals" -) - -var ( - metricsHost = "0.0.0.0" - metricsPort int32 = 8383 - operatorMetricsPort int32 = 8686 - healthProbePort int32 = 6789 - log = logf.Log.WithName("ansible-operator") -) - -func printVersion() { - log.Info(fmt.Sprintf("Go Version: %s", runtime.Version())) - log.Info(fmt.Sprintf("Go OS/Arch: %s/%s", runtime.GOOS, runtime.GOARCH)) - log.Info(fmt.Sprintf("Version of operator-sdk: %v", sdkVersion.Version)) -} - -// RunAnsibleOperator sets the default flags for base image and starts the ansible operator. -func RunAnsibleOperator() error { - flags := setDefaultFlags() - - err := runAnsibleOperator(flags) - if err != nil { - return err - } - return nil -} - -// setDefaultFlags fills the operator flags with default value. These are not user-facing. -// For building base images, only `--watches-file` flag is required. The rest are available -// and can be changed if required in future while building base image. -func setDefaultFlags() *aoflags.AnsibleOperatorFlags { - aof := &flags.AnsibleOperatorFlags{} - - flag.BoolVar(&aof.InjectOwnerRef, "inject-owner-ref", true, "The ansible operator will "+ - "inject owner references unless this flag is false") - flag.IntVar(&aof.MaxWorkers, "max-workers", 1, "Maximum number of workers to use. "+ - "Overridden by environment variable.") - flag.IntVar(&aof.AnsibleVerbosity, "ansible-verbosity", 2, "Ansible verbosity. "+ - "Overridden by environment variable.") - flag.StringVar(&aof.AnsibleRolesPath, "ansible-roles-path", "", "Ansible Roles Path. "+ - "If unset, roles are assumed to be in {{CWD}}/roles.") - flag.StringVar(&aof.AnsibleCollectionsPath, "ansible-collections-path", "", - `Path to installed Ansible Collections. If set, collections should be - located in {{value}}/ansible_collections/. If unset, collections are - assumed to be in ~/.ansible/collections or - /usr/share/ansible/collections.`) - flag.DurationVar(&aof.ReconcilePeriod, "reconcile-period", time.Minute, "Default reconcile"+ - "period for controllers") - flag.StringVar(&aof.WatchesFile, "watches-file", "watches.yaml", "Path to the watches file to use") - - flag.Parse() - - if err := setAnsibleEnvVars(aof); err != nil { - log.Error(err, "Error setting env variables") - } - - return aof -} - -func setAnsibleEnvVars(flags *aoflags.AnsibleOperatorFlags) error { - if flags != nil { - if len(flags.AnsibleRolesPath) > 0 { - if err := os.Setenv(aoflags.AnsibleRolesPathEnvVar, flags.AnsibleRolesPath); err != nil { - return fmt.Errorf("failed to set %s environment variable: (%v)", aoflags.AnsibleRolesPathEnvVar, err) - } - log.Info("Set the value %v for environment variable %v.", - flags.AnsibleRolesPath, aoflags.AnsibleRolesPathEnvVar) - } - if len(flags.AnsibleCollectionsPath) > 0 { - if err := os.Setenv(aoflags.AnsibleCollectionsPathEnvVar, flags.AnsibleCollectionsPath); err != nil { - return fmt.Errorf("failed to set %s environment variable: (%v)", aoflags.AnsibleCollectionsPathEnvVar, err) - } - log.Info("Set the value %v for environment variable %v.", - flags.AnsibleCollectionsPath, aoflags.AnsibleCollectionsPathEnvVar) - } - } - return nil -} - -func runAnsibleOperator(flags *aoflags.AnsibleOperatorFlags) error { - printVersion() - - cfg, err := config.GetConfig() - if err != nil { - log.Error(err, "Failed to get config.") - return err - } - - // Set default manager options - // TODO: probably should expose the host & port as an environment variables - options := manager.Options{ - HealthProbeBindAddress: fmt.Sprintf("%s:%d", metricsHost, healthProbePort), - MetricsBindAddress: fmt.Sprintf("%s:%d", metricsHost, metricsPort), - NewClient: func(cache cache.Cache, config *rest.Config, options client.Options) (client.Client, error) { - c, err := client.New(config, options) - if err != nil { - return nil, err - } - return &client.DelegatingClient{ - Reader: cache, - Writer: c, - StatusClient: c, - }, nil - }, - } - namespace, found := os.LookupEnv(k8sutil.WatchNamespaceEnvVar) - log = log.WithValues("Namespace", namespace) - if found { - if namespace == metav1.NamespaceAll { - log.Info("Watching all namespaces.") - options.Namespace = metav1.NamespaceAll - } else { - if strings.Contains(namespace, ",") { - log.Info("Watching multiple namespaces.") - options.NewCache = cache.MultiNamespacedCacheBuilder(strings.Split(namespace, ",")) - } else { - log.Info("Watching single namespace.") - options.Namespace = namespace - } - } - } else { - log.Info(fmt.Sprintf("%v environment variable not set. Watching all namespaces.", - k8sutil.WatchNamespaceEnvVar)) - options.Namespace = metav1.NamespaceAll - } - - // Create a new manager to provide shared dependencies and start components - mgr, err := manager.New(cfg, options) - if err != nil { - log.Error(err, "Failed to create a new manager.") - return err - } - - var gvks []schema.GroupVersionKind - cMap := controllermap.NewControllerMap() - flags.WatchesFile = "watches.yaml" - watches, err := watches.Load(flags.WatchesFile, flags.MaxWorkers, flags.AnsibleVerbosity) - if err != nil { - log.Error(err, "Failed to load watches.") - return err - } - fmt.Printf("loaded watches") - for _, w := range watches { - runner, err := runner.New(w) - if err != nil { - log.Error(err, "Failed to create runner") - return err - } - - ctr := controller.Add(mgr, controller.Options{ - GVK: w.GroupVersionKind, - Runner: runner, - ManageStatus: w.ManageStatus, - AnsibleDebugLogs: getAnsibleDebugLog(), - MaxWorkers: w.MaxWorkers, - ReconcilePeriod: w.ReconcilePeriod, - }) - if ctr == nil { - return fmt.Errorf("failed to add controller for GVK %v", w.GroupVersionKind.String()) - } - - cMap.Store(w.GroupVersionKind, &controllermap.Contents{Controller: *ctr, - WatchDependentResources: w.WatchDependentResources, - WatchClusterScopedResources: w.WatchClusterScopedResources, - OwnerWatchMap: controllermap.NewWatchMap(), - AnnotationWatchMap: controllermap.NewWatchMap(), - }, w.Blacklist) - gvks = append(gvks, w.GroupVersionKind) - } - operatorName, err := k8sutil.GetOperatorName() - if err != nil { - log.Error(err, "Failed to get the operator name") - return err - } - // Become the leader before proceeding - err = leader.Become(context.TODO(), operatorName+"-lock") - if err != nil { - log.Error(err, "Failed to become leader.") - return err - } - addMetrics(context.TODO(), cfg, gvks) - err = mgr.AddHealthzCheck("ping", healthz.Ping) - if err != nil { - log.Error(err, "Failed to add Healthz check.") - } - done := make(chan error) - - // start the proxy - err = proxy.Run(done, proxy.Options{ - Address: "localhost", - Port: 8888, - KubeConfig: mgr.GetConfig(), - Cache: mgr.GetCache(), - RESTMapper: mgr.GetRESTMapper(), - ControllerMap: cMap, - OwnerInjection: flags.InjectOwnerRef, - WatchedNamespaces: []string{namespace}, - }) - - if err != nil { - log.Error(err, "Error starting proxy.") - return err - } - - // start the operator - go func() { - done <- mgr.Start(signals.SetupSignalHandler()) - }() - - // wait for either to finish - err = <-done - if err != nil { - log.Error(err, "Proxy or operator exited with error.") - os.Exit(1) - } - log.Info("Exiting.") - return nil - -} - -// addMetrics will create the Services and Service Monitors to allow the operator export the metrics by using -// the Prometheus operator -func addMetrics(ctx context.Context, cfg *rest.Config, gvks []schema.GroupVersionKind) { - // Get the namespace the operator is currently deployed in. - operatorNs, err := k8sutil.GetOperatorNamespace() - if err != nil { - if errors.Is(err, k8sutil.ErrRunLocal) { - log.Info("Skipping CR metrics server creation; not running in a cluster.") - return - } - } - - if err := serveCRMetrics(cfg, operatorNs, gvks); err != nil { - log.Info("Could not generate and serve custom resource metrics", "error", err.Error()) - } - - // Add to the below struct any other metrics ports you want to expose. - servicePorts := []v1.ServicePort{ - {Port: metricsPort, Name: metrics.OperatorPortName, Protocol: v1.ProtocolTCP, - TargetPort: intstr.IntOrString{Type: intstr.Int, IntVal: metricsPort}}, - {Port: operatorMetricsPort, Name: metrics.CRPortName, Protocol: v1.ProtocolTCP, - TargetPort: intstr.IntOrString{Type: intstr.Int, IntVal: operatorMetricsPort}}, - } - - // Create Service object to expose the metrics port(s). - service, err := metrics.CreateMetricsService(ctx, cfg, servicePorts) - if err != nil { - log.Info("Could not create metrics Service", "error", err.Error()) - return - } - - // CreateServiceMonitors will automatically create the prometheus-operator ServiceMonitor resources - // necessary to configure Prometheus to scrape metrics from this operator. - services := []*v1.Service{service} - - // The ServiceMonitor is created in the same namespace where the operator is deployed - _, err = metrics.CreateServiceMonitors(cfg, operatorNs, services) - if err != nil { - log.Info("Could not create ServiceMonitor object", "error", err.Error()) - // If this operator is deployed to a cluster without the prometheus-operator running, it will return - // ErrServiceMonitorNotPresent, which can be used to safely skip ServiceMonitor creation. - if err == metrics.ErrServiceMonitorNotPresent { - log.Info("Install prometheus-operator in your cluster to create ServiceMonitor objects", "error", err.Error()) - } - } -} - -// serveCRMetrics takes GVKs retrieved from watches and generates metrics based on those types. -// It serves those metrics on "http://metricsHost:operatorMetricsPort". -func serveCRMetrics(cfg *rest.Config, operatorNs string, gvks []schema.GroupVersionKind) error { - // The metrics will be generated from the namespaces which are returned here. - // NOTE that passing nil or an empty list of namespaces in GenerateAndServeCRMetrics will result in an error. - ns, err := kubemetrics.GetNamespacesForMetrics(operatorNs) - if err != nil { - return err - } - - // Generate and serve custom resource specific metrics. - err = kubemetrics.GenerateAndServeCRMetrics(cfg, ns, gvks, metricsHost, operatorMetricsPort) - if err != nil { - return err - } - return nil -} - -// getAnsibleDebugLog return the value from the ANSIBLE_DEBUG_LOGS it order to -// print the full Ansible logs -func getAnsibleDebugLog() bool { - const envVar = "ANSIBLE_DEBUG_LOGS" - val := false - if envVal, ok := os.LookupEnv(envVar); ok { - if i, err := strconv.ParseBool(envVal); err != nil { - log.Info("Could not parse environment variable as an boolean; using default value", - "envVar", envVar, "default", val) - } else { - val = i - } - } else if !ok { - log.Info("Environment variable not set; using default value", "envVar", envVar, - envVar, val) - } - return val -} diff --git a/pkg/helm/image/base_image_build.go b/pkg/helm/image/base_image_build.go deleted file mode 100644 index 10ac036241..0000000000 --- a/pkg/helm/image/base_image_build.go +++ /dev/null @@ -1,252 +0,0 @@ -// Copyright 2020 The Operator-SDK Authors -// -// 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 image - -import ( - "context" - "errors" - "flag" - "fmt" - "os" - "runtime" - "strings" - "time" - - v1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/util/intstr" - "k8s.io/client-go/rest" - "sigs.k8s.io/controller-runtime/pkg/cache" - crclient "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/client/config" - logf "sigs.k8s.io/controller-runtime/pkg/log" - "sigs.k8s.io/controller-runtime/pkg/manager" - "sigs.k8s.io/controller-runtime/pkg/manager/signals" - - "github.com/operator-framework/operator-sdk/pkg/helm/controller" - hoflags "github.com/operator-framework/operator-sdk/pkg/helm/flags" - "github.com/operator-framework/operator-sdk/pkg/helm/release" - "github.com/operator-framework/operator-sdk/pkg/helm/watches" - "github.com/operator-framework/operator-sdk/pkg/k8sutil" - kubemetrics "github.com/operator-framework/operator-sdk/pkg/kube-metrics" - "github.com/operator-framework/operator-sdk/pkg/leader" - "github.com/operator-framework/operator-sdk/pkg/metrics" - sdkVersion "github.com/operator-framework/operator-sdk/version" -) - -var ( - metricsHost = "0.0.0.0" - metricsPort int32 = 8383 - operatorMetricsPort int32 = 8686 -) - -var log = logf.Log.WithName("helm-operator") - -func printVersion() { - log.Info(fmt.Sprintf("Go Version: %s", runtime.Version())) - log.Info(fmt.Sprintf("Go OS/Arch: %s/%s", runtime.GOOS, runtime.GOARCH)) - log.Info(fmt.Sprintf("Version of operator-sdk: %v", sdkVersion.Version)) -} - -// setDefaultFlags fills the operator flags with default value. These are not user-facing. -// For building base images, only `--watches-file` flag is required. The rest are available -// and can be changed if required in future while building base image. -func setDefaultFlags() *hoflags.HelmOperatorFlags { - hof := &hoflags.HelmOperatorFlags{} - flag.IntVar(&hof.MaxWorkers, "max-workers", 1, "Maximum number of workers to use.") - flag.DurationVar(&hof.ReconcilePeriod, "reconcile-period", time.Minute, - "Default reconcile period for controllers") - flag.StringVar(&hof.WatchesFile, "watches-file", "watches.yaml", "Path to the watches file to use") - flag.Parse() - - return hof -} - -// RunHelmOperator sets the default flags for base image and starts the ansible operator. -func RunHelmOperator() error { - flags := setDefaultFlags() - - err := runHelmOperator(flags) - if err != nil { - return err - } - return nil -} - -func runHelmOperator(flags *hoflags.HelmOperatorFlags) error { - printVersion() - - cfg, err := config.GetConfig() - if err != nil { - log.Error(err, "Failed to get config.") - return err - } - - // Set default manager options - options := manager.Options{ - MetricsBindAddress: fmt.Sprintf("%s:%d", metricsHost, metricsPort), - NewClient: func(cache cache.Cache, config *rest.Config, options crclient.Options) (crclient.Client, error) { - c, err := crclient.New(config, options) - if err != nil { - return nil, err - } - return &crclient.DelegatingClient{ - Reader: cache, - Writer: c, - StatusClient: c, - }, nil - }, - } - - namespace, found := os.LookupEnv(k8sutil.WatchNamespaceEnvVar) - log = log.WithValues("Namespace", namespace) - if found { - if namespace == metav1.NamespaceAll { - log.Info("Watching all namespaces.") - options.Namespace = metav1.NamespaceAll - } else { - if strings.Contains(namespace, ",") { - log.Info("Watching multiple namespaces.") - options.NewCache = cache.MultiNamespacedCacheBuilder(strings.Split(namespace, ",")) - } else { - log.Info("Watching single namespace.") - options.Namespace = namespace - } - } - } else { - log.Info(fmt.Sprintf("%v environment variable not set. Watching all namespaces.", - k8sutil.WatchNamespaceEnvVar)) - options.Namespace = metav1.NamespaceAll - } - - mgr, err := manager.New(cfg, options) - if err != nil { - log.Error(err, "Failed to create a new manager.") - return err - } - - ws, err := watches.Load(flags.WatchesFile) - if err != nil { - log.Error(err, "Failed to create new manager factories.") - return err - } - var gvks []schema.GroupVersionKind - for _, w := range ws { - // Register the controller with the factory. - err := controller.Add(mgr, controller.WatchOptions{ - Namespace: namespace, - GVK: w.GroupVersionKind, - ManagerFactory: release.NewManagerFactory(mgr, w.ChartDir), - ReconcilePeriod: flags.ReconcilePeriod, - WatchDependentResources: *w.WatchDependentResources, - OverrideValues: w.OverrideValues, - MaxWorkers: flags.MaxWorkers, - }) - if err != nil { - log.Error(err, "Failed to add manager factory to controller.") - return err - } - gvks = append(gvks, w.GroupVersionKind) - } - - operatorName, err := k8sutil.GetOperatorName() - if err != nil { - log.Error(err, "Failed to get operator name") - return err - } - - ctx := context.TODO() - - // Become the leader before proceeding - err = leader.Become(ctx, operatorName+"-lock") - if err != nil { - log.Error(err, "Failed to become leader.") - return err - } - - addMetrics(context.TODO(), cfg, gvks) - - // Start the Cmd - if err = mgr.Start(signals.SetupSignalHandler()); err != nil { - log.Error(err, "Manager exited non-zero.") - os.Exit(1) - } - return nil -} - -// addMetrics will create the Services and Service Monitors to allow the operator export the metrics by using -// the Prometheus operator -func addMetrics(ctx context.Context, cfg *rest.Config, gvks []schema.GroupVersionKind) { - // Get the namespace the operator is currently deployed in. - operatorNs, err := k8sutil.GetOperatorNamespace() - if err != nil { - if errors.Is(err, k8sutil.ErrRunLocal) { - log.Info("Skipping CR metrics server creation; not running in a cluster.") - return - } - } - - if err := serveCRMetrics(cfg, operatorNs, gvks); err != nil { - log.Info("Could not generate and serve custom resource metrics", "error", err.Error()) - } - - // Add to the below struct any other metrics ports you want to expose. - servicePorts := []v1.ServicePort{ - {Port: metricsPort, Name: metrics.OperatorPortName, Protocol: v1.ProtocolTCP, - TargetPort: intstr.IntOrString{Type: intstr.Int, IntVal: metricsPort}}, - {Port: operatorMetricsPort, Name: metrics.CRPortName, Protocol: v1.ProtocolTCP, - TargetPort: intstr.IntOrString{Type: intstr.Int, IntVal: operatorMetricsPort}}, - } - - // Create Service object to expose the metrics port(s). - service, err := metrics.CreateMetricsService(ctx, cfg, servicePorts) - if err != nil { - log.Info("Could not create metrics Service", "error", err.Error()) - } - - // CreateServiceMonitors will automatically create the prometheus-operator ServiceMonitor resources - // necessary to configure Prometheus to scrape metrics from this operator. - services := []*v1.Service{service} - - // The ServiceMonitor is created in the same namespace where the operator is deployed - _, err = metrics.CreateServiceMonitors(cfg, operatorNs, services) - if err != nil { - log.Info("Could not create ServiceMonitor object", "error", err.Error()) - // If this operator is deployed to a cluster without the prometheus-operator running, it will return - // ErrServiceMonitorNotPresent, which can be used to safely skip ServiceMonitor creation. - if err == metrics.ErrServiceMonitorNotPresent { - log.Info("Install prometheus-operator in your cluster to create ServiceMonitor objects", "error", err.Error()) - } - } -} - -// serveCRMetrics gets the Operator/CustomResource GVKs and generates metrics based on those types. -// It serves those metrics on "http://metricsHost:operatorMetricsPort". -func serveCRMetrics(cfg *rest.Config, operatorNs string, gvks []schema.GroupVersionKind) error { - // The metrics will be generated from the namespaces which are returned here. - // NOTE that passing nil or an empty list of namespaces in GenerateAndServeCRMetrics will result in an error. - ns, err := kubemetrics.GetNamespacesForMetrics(operatorNs) - if err != nil { - return err - } - - // Generate and serve custom resource specific metrics. - err = kubemetrics.GenerateAndServeCRMetrics(cfg, ns, gvks, metricsHost, operatorMetricsPort) - if err != nil { - return err - } - return nil -} From 2a30cce9f5203b10817ef8da8ed165c9db8b9c71 Mon Sep 17 00:00:00 2001 From: varshaprasad96 Date: Tue, 7 Jul 2020 22:39:19 -0700 Subject: [PATCH 4/4] [ansible/helm] remove exec-entrypoint and add changelog fragment --- cmd/operator-sdk/cli/legacy.go | 2 - cmd/operator-sdk/execentrypoint/ansible.go | 74 ---------------------- cmd/operator-sdk/execentrypoint/cmd.go | 36 ----------- cmd/operator-sdk/execentrypoint/helm.go | 48 -------------- 4 files changed, 160 deletions(-) delete mode 100644 cmd/operator-sdk/execentrypoint/ansible.go delete mode 100644 cmd/operator-sdk/execentrypoint/cmd.go delete mode 100644 cmd/operator-sdk/execentrypoint/helm.go diff --git a/cmd/operator-sdk/cli/legacy.go b/cmd/operator-sdk/cli/legacy.go index 61e37af5dd..d1c84e0c47 100644 --- a/cmd/operator-sdk/cli/legacy.go +++ b/cmd/operator-sdk/cli/legacy.go @@ -21,7 +21,6 @@ import ( "github.com/operator-framework/operator-sdk/cmd/operator-sdk/bundle" "github.com/operator-framework/operator-sdk/cmd/operator-sdk/cleanup" "github.com/operator-framework/operator-sdk/cmd/operator-sdk/completion" - "github.com/operator-framework/operator-sdk/cmd/operator-sdk/execentrypoint" "github.com/operator-framework/operator-sdk/cmd/operator-sdk/generate" "github.com/operator-framework/operator-sdk/cmd/operator-sdk/migrate" "github.com/operator-framework/operator-sdk/cmd/operator-sdk/new" @@ -76,7 +75,6 @@ func GetCLIRoot() *cobra.Command { bundle.NewCmdLegacy(), cleanup.NewCmdLegacy(), completion.NewCmd(), - execentrypoint.NewCmd(), generate.NewCmdLegacy(), migrate.NewCmd(), new.NewCmd(), diff --git a/cmd/operator-sdk/execentrypoint/ansible.go b/cmd/operator-sdk/execentrypoint/ansible.go deleted file mode 100644 index 6d6e1300d6..0000000000 --- a/cmd/operator-sdk/execentrypoint/ansible.go +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright 2019 The Operator-SDK Authors -// -// 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 execentrypoint - -import ( - "fmt" - "os" - - log "github.com/sirupsen/logrus" - "github.com/spf13/cobra" - logf "sigs.k8s.io/controller-runtime/pkg/log" - - "github.com/operator-framework/operator-sdk/pkg/ansible" - aoflags "github.com/operator-framework/operator-sdk/pkg/ansible/flags" - "github.com/operator-framework/operator-sdk/pkg/log/zap" -) - -// newRunAnsibleCmd returns a command that will run an ansible operator. -func newRunAnsibleCmd() *cobra.Command { - var flags *aoflags.AnsibleOperatorFlags - runAnsibleCmd := &cobra.Command{ - Use: "ansible", - Short: "Runs as an ansible operator", - Long: `Runs as an ansible operator. This is intended to be used when running -in a Pod inside a cluster. Developers wanting to run their operator locally -should use 'run local' instead.`, - RunE: func(cmd *cobra.Command, args []string) error { - logf.SetLogger(zap.Logger()) - if err := setAnsibleEnvVars(flags); err != nil { - log.Fatal(err) - } - if err := ansible.Run(flags); err != nil { - log.Fatal(err) - } - return nil - }, - } - flags = aoflags.AddTo(runAnsibleCmd.Flags()) - - return runAnsibleCmd -} - -// setAnsibleEnvVars will set Ansible-defined environment variables from CLI flags. -func setAnsibleEnvVars(flags *aoflags.AnsibleOperatorFlags) error { - if flags != nil { - if len(flags.AnsibleRolesPath) > 0 { - if err := os.Setenv(aoflags.AnsibleRolesPathEnvVar, flags.AnsibleRolesPath); err != nil { - return fmt.Errorf("failed to set %s environment variable: (%v)", aoflags.AnsibleRolesPathEnvVar, err) - } - log.Infof("Set the value %v for environment variable %v.", - flags.AnsibleRolesPath, aoflags.AnsibleRolesPathEnvVar) - } - if len(flags.AnsibleCollectionsPath) > 0 { - if err := os.Setenv(aoflags.AnsibleCollectionsPathEnvVar, flags.AnsibleCollectionsPath); err != nil { - return fmt.Errorf("failed to set %s environment variable: (%v)", aoflags.AnsibleCollectionsPathEnvVar, err) - } - log.Infof("Set the value %v for environment variable %v.", - flags.AnsibleCollectionsPath, aoflags.AnsibleCollectionsPathEnvVar) - } - } - return nil -} diff --git a/cmd/operator-sdk/execentrypoint/cmd.go b/cmd/operator-sdk/execentrypoint/cmd.go deleted file mode 100644 index 9785cdd392..0000000000 --- a/cmd/operator-sdk/execentrypoint/cmd.go +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2019 The Operator-SDK Authors -// -// 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 execentrypoint - -import "github.com/spf13/cobra" - -// NewCmd returns a command that contains subcommands to run specific -// operator types. -func NewCmd() *cobra.Command { - cmd := &cobra.Command{ - Use: "exec-entrypoint", - Short: "Runs a generic operator", - Long: `Runs a generic operator. This is intended to be used when running -in a Pod inside a cluster. Developers wanting to run their operator locally -should use 'run local' instead.`, - Hidden: true, - } - - cmd.AddCommand( - newRunAnsibleCmd(), - newRunHelmCmd(), - ) - return cmd -} diff --git a/cmd/operator-sdk/execentrypoint/helm.go b/cmd/operator-sdk/execentrypoint/helm.go deleted file mode 100644 index 27ee9fc396..0000000000 --- a/cmd/operator-sdk/execentrypoint/helm.go +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2019 The Operator-SDK Authors -// -// 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 execentrypoint - -import ( - "github.com/operator-framework/operator-sdk/pkg/helm" - hoflags "github.com/operator-framework/operator-sdk/pkg/helm/flags" - "github.com/operator-framework/operator-sdk/pkg/log/zap" - - log "github.com/sirupsen/logrus" - "github.com/spf13/cobra" - logf "sigs.k8s.io/controller-runtime/pkg/log" -) - -// newRunHelmCmd returns a command that will run a helm operator. -func newRunHelmCmd() *cobra.Command { - var flags *hoflags.HelmOperatorFlags - runHelmCmd := &cobra.Command{ - Use: "helm", - Short: "Runs as a helm operator", - Long: `Runs as a helm operator. This is intended to be used when running -in a Pod inside a cluster. Developers wanting to run their operator locally -should use 'run local' instead.`, - RunE: func(cmd *cobra.Command, args []string) error { - logf.SetLogger(zap.Logger()) - - if err := helm.Run(flags); err != nil { - log.Fatal(err) - } - return nil - }, - } - flags = hoflags.AddTo(runHelmCmd.Flags()) - - return runHelmCmd -}