diff --git a/changelog/fragments/remove-legacy-helm.yaml b/changelog/fragments/remove-legacy-helm.yaml new file mode 100644 index 00000000000..893feb33cad --- /dev/null +++ b/changelog/fragments/remove-legacy-helm.yaml @@ -0,0 +1,18 @@ +# entries is a list of entries to include in +# release notes and/or the migration guide +entries: + - description: > + The Helm legacy layout is no longer supported. + + kind: "removal" + + # Is this a breaking change? + breaking: true + + # Migration can be defined to automatically add a section to + # the migration guide. This is required for breaking changes. + migration: + header: Migrate your Helm project to the new layout + body: > + TBD + diff --git a/cmd/operator-sdk/add/api.go b/cmd/operator-sdk/add/api.go index a34ad376437..68ee4d48fde 100644 --- a/cmd/operator-sdk/add/api.go +++ b/cmd/operator-sdk/add/api.go @@ -26,7 +26,6 @@ import ( "github.com/operator-framework/operator-sdk/internal/genutil" "github.com/operator-framework/operator-sdk/internal/scaffold" "github.com/operator-framework/operator-sdk/internal/scaffold/ansible" - "github.com/operator-framework/operator-sdk/internal/scaffold/helm" "github.com/operator-framework/operator-sdk/internal/scaffold/input" "github.com/operator-framework/operator-sdk/internal/util/projutil" ) @@ -54,12 +53,6 @@ For Ansible-based operators: - Creates resource folder under /roles. - watches.yaml is updated with new resource. - deploy/role.yaml will be updated with apiGroup for new API. - -For Helm-based operators: - - Creates resource folder under /helm-charts. - - watches.yaml is updated with new resource. - - deploy/role.yaml will be updated to reflact new rules for the incoming API. - CRD's are generated, or updated if they exist for a particular group + version + kind, under deploy/crds/__crd.yaml; OpenAPI V3 validation YAML is generated as a 'validation' object.`, @@ -71,38 +64,6 @@ is generated as a 'validation' object.`, $ operator-sdk add api \ --api-version=app.example.com/v1alpha1 \ --kind=AppService - -# Helm Example: - $ operator-sdk add api \ - --api-version=app.example.com/v1alpha1 \ - --kind=AppService - - $ operator-sdk add api \ - --api-version=app.example.com/v1alpha1 \ - --kind=AppService - --helm-chart=myrepo/app - - $ operator-sdk add api \ - --helm-chart=myrepo/app - - $ operator-sdk add api \ - --helm-chart=myrepo/app \ - --helm-chart-version=1.2.3 - - $ operator-sdk add api \ - --helm-chart=app \ - --helm-chart-repo=https://charts.mycompany.com/ - - $ operator-sdk add api \ - --helm-chart=app \ - --helm-chart-repo=https://charts.mycompany.com/ \ - --helm-chart-version=1.2.3 - - $ operator-sdk add api \ - --helm-chart=/path/to/local/chart-directories/app/ - - $ operator-sdk add api \ - --helm-chart=/path/to/local/chart-archives/app-1.2.3.tgz `, RunE: apiRun, } @@ -131,28 +92,12 @@ func apiRun(cmd *cobra.Command, args []string) error { switch operatorType { case projutil.OperatorTypeGo: return fmt.Errorf("the `add api` command is not supported for Go operators") + case projutil.OperatorTypeHelm: + return fmt.Errorf("the `add api` command is not supported for Helm operators") case projutil.OperatorTypeAnsible: if err := doAnsibleAPIScaffold(); err != nil { return err } - case projutil.OperatorTypeHelm: - absProjectPath := projutil.MustGetwd() - projectName := filepath.Base(absProjectPath) - cfg := input.Config{ - AbsProjectPath: absProjectPath, - ProjectName: projectName, - } - createOpts := helm.CreateChartOptions{ - ResourceAPIVersion: apiFlags.APIVersion, - ResourceKind: apiFlags.Kind, - Chart: apiFlags.HelmChartRef, - Version: apiFlags.HelmChartVersion, - Repo: apiFlags.HelmChartRepo, - CRDVersion: apiFlags.CrdVersion, - } - if err := helm.API(cfg, createOpts); err != nil { - return err - } } log.Info("API generation complete.") return nil diff --git a/cmd/operator-sdk/new/cmd.go b/cmd/operator-sdk/new/cmd.go index d4397c0d712..ff19d77b34f 100644 --- a/cmd/operator-sdk/new/cmd.go +++ b/cmd/operator-sdk/new/cmd.go @@ -28,7 +28,6 @@ import ( "github.com/operator-framework/operator-sdk/internal/genutil" "github.com/operator-framework/operator-sdk/internal/scaffold" "github.com/operator-framework/operator-sdk/internal/scaffold/ansible" - "github.com/operator-framework/operator-sdk/internal/scaffold/helm" "github.com/operator-framework/operator-sdk/internal/scaffold/input" "github.com/operator-framework/operator-sdk/internal/util/projutil" ) @@ -56,46 +55,14 @@ generates a default directory layout based on the input . $ operator-sdk new app-operator --type=ansible \ --api-version=app.example.com/v1alpha1 \ --kind=AppService - - # Helm project - $ operator-sdk new app-operator --type=helm \ - --api-version=app.example.com/v1alpha1 \ - --kind=AppService - - $ operator-sdk new app-operator --type=helm \ - --api-version=app.example.com/v1alpha1 \ - --kind=AppService \ - --helm-chart=myrepo/app - - $ operator-sdk new app-operator --type=helm \ - --helm-chart=myrepo/app - - $ operator-sdk new app-operator --type=helm \ - --helm-chart=myrepo/app \ - --helm-chart-version=1.2.3 - - $ operator-sdk new app-operator --type=helm \ - --helm-chart=app \ - --helm-chart-repo=https://charts.mycompany.com/ - - $ operator-sdk new app-operator --type=helm \ - --helm-chart=app \ - --helm-chart-repo=https://charts.mycompany.com/ \ - --helm-chart-version=1.2.3 - - $ operator-sdk new app-operator --type=helm \ - --helm-chart=/path/to/local/chart-directories/app/ - - $ operator-sdk new app-operator --type=helm \ - --helm-chart=/path/to/local/chart-archives/app-1.2.3.tgz `, RunE: newFunc, } - newCmd.Flags().StringVar(&operatorType, "type", "", - "Type of operator to initialize (choices: \"ansible\" or \"helm\")") - if err := newCmd.MarkFlagRequired("type"); err != nil { - log.Fatalf("Failed to mark `type` flag for `new` subcommand as required") + newCmd.Flags().StringVar(&operatorType, "type", "ansible", + "Type of operator to initialize (choices: \"ansible\")") + if err := newCmd.Flags().MarkHidden("type"); err != nil { + log.Fatalf("Failed to mark `type` flag for `new` subcommand as hidden") } // todo(camilamacedo86): remove before 1.0.0 @@ -140,35 +107,6 @@ func newFunc(cmd *cobra.Command, args []string) error { if err := doAnsibleScaffold(); err != nil { log.Fatal(err) } - case projutil.OperatorTypeHelm: - // create the project dir - err := os.MkdirAll(projectName, 0755) - if err != nil { - log.Fatal(err) - } - // go inside of the project dir - err = os.Chdir(filepath.Join(projutil.MustGetwd(), projectName)) - if err != nil { - log.Fatal(err) - } - - cfg := input.Config{ - AbsProjectPath: filepath.Join(projutil.MustGetwd()), - ProjectName: projectName, - } - - createOpts := helm.CreateChartOptions{ - ResourceAPIVersion: apiFlags.APIVersion, - ResourceKind: apiFlags.Kind, - Chart: apiFlags.HelmChartRef, - Version: apiFlags.HelmChartVersion, - Repo: apiFlags.HelmChartRepo, - CRDVersion: apiFlags.CrdVersion, - } - - if err := helm.Init(cfg, createOpts); err != nil { - log.Fatal(err) - } } //todo: remove before 1.0.0 if gitInit { @@ -303,17 +241,12 @@ func doAnsibleScaffold() error { } func verifyFlags() error { - if operatorType != projutil.OperatorTypeAnsible && operatorType != projutil.OperatorTypeHelm { - return fmt.Errorf("value of --type can only be `ansible`, or `helm`: %v", - projutil.ErrUnknownOperatorType{Type: operatorType}) - } - if operatorType != projutil.OperatorTypeAnsible && generatePlaybook { + if generatePlaybook { return fmt.Errorf("value of --generate-playbook can only be used with --type `ansible`") } if err := apiFlags.VerifyCommonFlags(operatorType); err != nil { return err } - return nil } diff --git a/go.mod b/go.mod index 3e6b8d7ded3..aa29cb42455 100644 --- a/go.mod +++ b/go.mod @@ -19,7 +19,6 @@ require ( github.com/operator-framework/api v0.3.8 github.com/operator-framework/operator-registry v1.12.6-0.20200611222234-275301b779f8 github.com/prometheus/client_golang v1.5.1 - github.com/prometheus/common v0.9.1 github.com/rogpeppe/go-internal v1.5.0 github.com/sergi/go-diff v1.0.0 github.com/sirupsen/logrus v1.5.0 diff --git a/internal/flags/apiflags/flags.go b/internal/flags/apiflags/flags.go index 7be4d5c2ca0..6bc1ef9568d 100644 --- a/internal/flags/apiflags/flags.go +++ b/internal/flags/apiflags/flags.go @@ -21,17 +21,13 @@ import ( "github.com/spf13/pflag" gencrd "github.com/operator-framework/operator-sdk/internal/generate/crd" - "github.com/operator-framework/operator-sdk/internal/util/projutil" ) type APIFlags struct { - SkipGeneration bool - APIVersion string - Kind string - CrdVersion string - HelmChartRef string - HelmChartVersion string - HelmChartRepo string + SkipGeneration bool + APIVersion string + Kind string + CrdVersion string } // AddTo - Add the reconcile period and watches file flags to the the flagset @@ -49,49 +45,23 @@ func (f *APIFlags) AddTo(flagSet *pflag.FlagSet) { flagSet.StringVar(&f.CrdVersion, "crd-version", gencrd.DefaultCRDVersion, "CRD version to generate") - flagSet.StringVar(&f.HelmChartRef, "helm-chart", "", - "Initialize helm operator with existing helm chart (, /, or local path). Valid only for --type helm") - - flagSet.StringVar(&f.HelmChartVersion, "helm-chart-version", "", - "Specific version of the helm chart (default is latest version). Valid only for --type helm") - - flagSet.StringVar(&f.HelmChartRepo, "helm-chart-repo", "", - "Chart repository URL for the requested helm chart, Valid only for --type helm") - } // VerifyCommonFlags func is used to verify flags common to both "new" and "add api" commands. func (f *APIFlags) VerifyCommonFlags(operatorType string) error { - - if len(f.HelmChartRef) != 0 { - if operatorType != projutil.OperatorTypeHelm { - return fmt.Errorf("value of --helm-chart can only be used with --type=helm") - } - } else if len(f.HelmChartRepo) != 0 { - return fmt.Errorf("value of --helm-chart-repo can only be used with --type=helm and --helm-chart") - } else if len(f.HelmChartVersion) != 0 { - return fmt.Errorf("value of --helm-chart-version can only be used with --type=helm and --helm-chart") + if len(f.APIVersion) == 0 { + return fmt.Errorf("value of --api-version must not have empty value") } - - // --api-version and --kind are required with --type=ansible, --type=helm , with one exception. - // If --type=helm and --helm-chart is set, --api-version and --kind are optional. If left unset, - // sane defaults are used when the specified helm chart is created. - if (operatorType == projutil.OperatorTypeAnsible || operatorType == projutil.OperatorTypeHelm) && - len(f.HelmChartRef) == 0 { - if len(f.APIVersion) == 0 { - return fmt.Errorf("value of --api-version must not have empty value") - } - if len(f.Kind) == 0 { - return fmt.Errorf("value of --kind must not have empty value") - } - kindFirstLetter := string(f.Kind[0]) - if kindFirstLetter != strings.ToUpper(kindFirstLetter) { - return fmt.Errorf("value of --kind must start with an uppercase letter") - } - if strings.Count(f.APIVersion, "/") != 1 { - return fmt.Errorf("value of --api-version has wrong format (%v);"+ - " format must be $GROUP_NAME/$VERSION (e.g app.example.com/v1alpha1)", f.APIVersion) - } + if len(f.Kind) == 0 { + return fmt.Errorf("value of --kind must not have empty value") + } + kindFirstLetter := string(f.Kind[0]) + if kindFirstLetter != strings.ToUpper(kindFirstLetter) { + return fmt.Errorf("value of --kind must start with an uppercase letter") + } + if strings.Count(f.APIVersion, "/") != 1 { + return fmt.Errorf("value of --api-version has wrong format (%v);"+ + " format must be $GROUP_NAME/$VERSION (e.g app.example.com/v1alpha1)", f.APIVersion) } return nil } diff --git a/internal/flags/apiflags/flags_test.go b/internal/flags/apiflags/flags_test.go index 9b5f771bb3d..b24b9ccd31c 100644 --- a/internal/flags/apiflags/flags_test.go +++ b/internal/flags/apiflags/flags_test.go @@ -32,13 +32,10 @@ func TestAddTo(t *testing.T) { // Populate FlagSet name: "Populate FlagSet", apiFlags: APIFlags{ - APIVersion: "app.example.com/v1alpha1", - Kind: "AppService", - SkipGeneration: false, - CrdVersion: "v1", - HelmChartRef: "stable/app", - HelmChartVersion: "1.0.0", - HelmChartRepo: "https://charts.mycompany.com/", + APIVersion: "app.example.com/v1alpha1", + Kind: "AppService", + SkipGeneration: false, + CrdVersion: "v1", }, validate: func(apiFlags APIFlags, flagSet *pflag.FlagSet) error { val, err := flagSet.GetString("api-version") @@ -73,22 +70,6 @@ func TestAddTo(t *testing.T) { return fmt.Errorf("crdVersion does not match") } - val, err = flagSet.GetString("helm-chart-repo") - if err != nil { - return err - } - if apiFlags.HelmChartRepo != val { - return fmt.Errorf("helmChartRepo does not match") - } - - val, err = flagSet.GetString("helm-chart-version") - if err != nil { - return err - } - if apiFlags.HelmChartVersion != val { - return fmt.Errorf("helmChartVersion does not match") - } - return nil }, }, @@ -125,36 +106,6 @@ func TestVerifyCommonFlags(t *testing.T) { operatorType: "go", expError: "", }, - { - // Invalid Go API Flags - name: "Invalid Go API Flags-HelmChartRef", - apiFlags: APIFlags{ - APIVersion: "app.example.com/v1alpha1", - HelmChartRef: "stable/repo", - }, - operatorType: "go", - expError: "value of --helm-chart can only be used with --type=helm", - }, - { - // Invalid Go API Flags - name: "Invalid Go API Flags-HelmChartRepo", - apiFlags: APIFlags{ - APIVersion: "app.example.com/v1alpha1", - HelmChartRepo: "https://charts.mycompany.com/", - }, - operatorType: "go", - expError: "value of --helm-chart-repo can only be used with --type=helm and --helm-chart", - }, - { - // Invalid Go API Flags - name: "Invalid Go API Flags-HelmChartVersion", - apiFlags: APIFlags{ - APIVersion: "app.example.com/v1alpha1", - HelmChartVersion: "1.2.0", - }, - operatorType: "go", - expError: "value of --helm-chart-version can only be used with --type=helm and --helm-chart", - }, { // Valid Ansible API Flags name: "Valid Ansible API Flags", @@ -194,69 +145,6 @@ func TestVerifyCommonFlags(t *testing.T) { operatorType: "ansible", expError: "value of --api-version must not have empty value", }, - { - // Invalid Ansible API Flags - name: "Invalid Ansible API Flags-HelmChartVersion is used", - apiFlags: APIFlags{ - APIVersion: "app.example.com/v1alpha1", - Kind: "App", - HelmChartVersion: "1.2.0", - }, - operatorType: "ansible", - expError: "value of --helm-chart-version can only be used with --type=helm and --helm-chart", - }, - { - // Invalid Ansible API Flags - name: "Invalid Ansible API Flags-HelmChartRepo is used", - apiFlags: APIFlags{ - APIVersion: "app.example.com/v1alpha1", - Kind: "App", - HelmChartRepo: "https://charts.mycompany.com/", - }, - operatorType: "ansible", - expError: "value of --helm-chart-repo can only be used with --type=helm and --helm-chart", - }, - { - // Valid HELM API Flags - name: "Valid HELM API Flags", - apiFlags: APIFlags{ - APIVersion: "app.example.com/v1alpha1", - Kind: "App", - SkipGeneration: true, - }, - operatorType: "helm", - expError: "", - }, - { - // Valid HELM API Flags - name: "Valid HELM API Flags-Helmchart used", - apiFlags: APIFlags{ - HelmChartRef: "stable/repo", - }, - operatorType: "helm", - expError: "", - }, - { - // Valid HELM API Flags - name: "Valid HELM API Flags-Helm specific flags", - apiFlags: APIFlags{ - HelmChartRef: "stable/repo", - HelmChartRepo: "https://charts.mycompany.com/", - HelmChartVersion: "1.2.0", - }, - operatorType: "helm", - expError: "", - }, - { - // Invalid HELM API Flags - name: "Invalid HELM API Flags-no HelmChartRef provided", - apiFlags: APIFlags{ - HelmChartRepo: "https://charts.mycompany.com/", - HelmChartVersion: "1.2.0", - }, - operatorType: "helm", - expError: "value of --helm-chart-repo can only be used with --type=helm and --helm-chart", - }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { diff --git a/internal/scaffold/helm/api.go b/internal/scaffold/helm/api.go deleted file mode 100644 index b9b06b0f715..00000000000 --- a/internal/scaffold/helm/api.go +++ /dev/null @@ -1,85 +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 helm - -import ( - "fmt" - "path/filepath" - - "github.com/prometheus/common/log" - "k8s.io/client-go/discovery" - "sigs.k8s.io/controller-runtime/pkg/client/config" - "sigs.k8s.io/yaml" - - "github.com/operator-framework/operator-sdk/internal/genutil" - "github.com/operator-framework/operator-sdk/internal/scaffold" - "github.com/operator-framework/operator-sdk/internal/scaffold/input" - "github.com/operator-framework/operator-sdk/pkg/helm/watches" -) - -// TODO -// Consolidate scaffold func to be used by both "new" and "add api" commands. -func API(cfg input.Config, createOpts CreateChartOptions) error { - - r, chart, err := CreateChart(cfg.AbsProjectPath, createOpts) - if err != nil { - return fmt.Errorf("failed to create helm chart: %v", err) - } - - valuesPath := filepath.Join("", HelmChartsDir, chart.Name(), "values.yaml") - - rawValues, err := yaml.Marshal(chart.Values) - if err != nil { - return fmt.Errorf("failed to get raw chart values: %v", err) - } - crSpec := fmt.Sprintf("# Default values copied from %s\n\n%s", valuesPath, rawValues) - - // update watch.yaml for the given resource. - watchesFile := filepath.Join(cfg.AbsProjectPath, watches.WatchesFile) - if err := watches.UpdateForResource(watchesFile, r, chart.Name()); err != nil { - return fmt.Errorf("failed to update watches.yaml: %w", err) - } - - s := &scaffold.Scaffold{} - err = s.Execute(&cfg, - &scaffold.CR{ - Resource: r, - Spec: crSpec, - }, - ) - if err != nil { - log.Fatalf("API scaffold failed: %v", err) - } - if err = genutil.GenerateCRDNonGo("", *r, createOpts.CRDVersion); err != nil { - return err - } - - roleScaffold := DefaultRoleScaffold - - if k8sCfg, err := config.GetConfig(); err != nil { - log.Warnf("Using default RBAC rules: failed to get Kubernetes config: %s", err) - } else if dc, err := discovery.NewDiscoveryClientForConfig(k8sCfg); err != nil { - log.Warnf("Using default RBAC rules: failed to create Kubernetes discovery client: %s", err) - } else { - roleScaffold = GenerateRoleScaffold(dc, chart) - } - - if err = scaffold.MergeRoleForResource(r, cfg.AbsProjectPath, roleScaffold); err != nil { - return fmt.Errorf("failed to merge rules in the RBAC manifest for resource (%v, %v): %v", - r.APIVersion, r.Kind, err) - } - - return nil -} diff --git a/internal/scaffold/helm/chart.go b/internal/scaffold/helm/chart.go deleted file mode 100644 index 23b4b892b1f..00000000000 --- a/internal/scaffold/helm/chart.go +++ /dev/null @@ -1,274 +0,0 @@ -// Copyright 2018 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 helm - -import ( - "bytes" - "fmt" - "io/ioutil" - "os" - "path/filepath" - - "github.com/iancoleman/strcase" - log "github.com/sirupsen/logrus" - "helm.sh/helm/v3/pkg/chart" - "helm.sh/helm/v3/pkg/chart/loader" - "helm.sh/helm/v3/pkg/chartutil" - "helm.sh/helm/v3/pkg/cli" - "helm.sh/helm/v3/pkg/downloader" - "helm.sh/helm/v3/pkg/getter" - "helm.sh/helm/v3/pkg/repo" - - "github.com/operator-framework/operator-sdk/internal/scaffold" -) - -const ( - - // HelmChartsDir is the relative directory within an SDK project where Helm - // charts are stored. - HelmChartsDir string = "helm-charts" - - // DefaultAPIVersion is the Kubernetes CRD API Version used for fetched - // charts when the --api-version flag is not specified - DefaultAPIVersion string = "helm.operator-sdk/v1alpha1" -) - -// CreateChartOptions is used to configure how a Helm chart is scaffolded -// for a new Helm operator project. -type CreateChartOptions struct { - // ResourceAPIVersion defines the Kubernetes GroupVersion to be associated - // with the created chart. - ResourceAPIVersion string - - // ResourceKind defines the Kubernetes Kind to be associated with the - // created chart. - ResourceKind string - - // Chart is a chart reference for a local or remote chart. - Chart string - - // Repo is a URL to a custom chart repository. - Repo string - - // Version is the version of the chart to fetch. - Version string - - // CRDVersion is the version of CRD.API which will be used to generate the CRDS - CRDVersion string -} - -// CreateChart scaffolds a new helm chart for the project rooted in projectDir -// based on the passed opts. -// -// It returns a scaffold.Resource that can be used by the caller to create -// other related files. opts.ResourceAPIVersion and opts.ResourceKind are -// used to create the resource and must be specified if opts.Chart is empty. -// -// If opts.Chart is not empty, opts.ResourceAPIVersion and opts.Kind can be -// left unset: opts.ResourceAPIVersion defaults to "charts.helm.k8s.io/v1alpha1" -// and opts.ResourceKind is deduced from the specified opts.Chart. -// -// CreateChart also returns a chart.Chart that references the newly created -// chart. -// -// If opts.Chart is empty, CreateChart scaffolds the default chart from helm's -// default template. -// -// If opts.Chart is a local file, CreateChart verifies that it is a valid helm -// chart archive and unpacks it into the project's helm charts directory. -// -// If opts.Chart is a local directory, CreateChart verifies that it is a valid -// helm chart directory and copies it into the project's helm charts directory. -// -// For any other value of opts.Chart, CreateChart attempts to fetch the helm chart -// from a remote repository. -// -// If opts.Repo is not specified, the following chart reference formats are supported: -// -// - /: Fetch the helm chart named chartName from the helm -// chart repository named repoName, as specified in the -// $HELM_HOME/repositories/repositories.yaml file. -// -// - : Fetch the helm chart archive at the specified URL. -// -// If opts.Repo is specified, only one chart reference format is supported: -// -// - : Fetch the helm chart named chartName in the helm chart repository -// specified by opts.Repo -// -// If opts.Version is not set, CreateChart will fetch the latest available version of -// the helm chart. Otherwise, CreateChart will fetch the specified version. -// opts.Version is not used when opts.Chart itself refers to a specific version, for -// example when it is a local path or a URL. -// -// CreateChart returns an error if an error occurs creating the scaffold.Resource or -// creating the chart. -func CreateChart(projectDir string, opts CreateChartOptions) (*scaffold.Resource, *chart.Chart, error) { - chartsDir := filepath.Join(projectDir, HelmChartsDir) - err := os.MkdirAll(chartsDir, 0755) - if err != nil { - return nil, nil, fmt.Errorf("failed to create helm-charts directory: %v", err) - } - - var ( - r *scaffold.Resource - c *chart.Chart - ) - - // If we don't have a helm chart reference, scaffold the default chart - // from Helm's default template. Otherwise, fetch it. - if len(opts.Chart) == 0 { - r, c, err = scaffoldChart(chartsDir, opts.ResourceAPIVersion, opts.ResourceKind) - if err != nil { - return nil, nil, fmt.Errorf("failed to scaffold default chart: %v", err) - } - } else { - r, c, err = fetchChart(chartsDir, opts) - if err != nil { - return nil, nil, fmt.Errorf("failed to fetch chart: %v", err) - } - } - - relChartPath := filepath.Join(HelmChartsDir, c.Name()) - absChartPath := filepath.Join(projectDir, relChartPath) - if err := fetchChartDependencies(absChartPath); err != nil { - return nil, nil, fmt.Errorf("failed to fetch chart dependencies: %v", err) - } - - // Reload chart in case dependencies changed - c, err = loader.Load(absChartPath) - if err != nil { - return nil, nil, fmt.Errorf("failed to load chart: %v", err) - } - - log.Infof("Created %s", relChartPath) - return r, c, nil -} - -func scaffoldChart(destDir, apiVersion, kind string) (*scaffold.Resource, *chart.Chart, error) { - r, err := scaffold.NewResource(apiVersion, kind) - if err != nil { - return nil, nil, err - } - - chartPath, err := chartutil.Create(r.LowerKind, destDir) - if err != nil { - return nil, nil, err - } - - chart, err := loader.Load(chartPath) - if err != nil { - return nil, nil, err - } - return r, chart, nil -} - -func fetchChart(destDir string, opts CreateChartOptions) (*scaffold.Resource, *chart.Chart, error) { - var ( - chart *chart.Chart - err error - ) - - if _, err = os.Stat(opts.Chart); err == nil { - chart, err = createChartFromDisk(destDir, opts.Chart) - } else { - chart, err = createChartFromRemote(destDir, opts) - } - if err != nil { - return nil, nil, err - } - - chartName := chart.Name() - if len(opts.ResourceAPIVersion) == 0 { - opts.ResourceAPIVersion = DefaultAPIVersion - } - if len(opts.ResourceKind) == 0 { - opts.ResourceKind = strcase.ToCamel(chartName) - } - - r, err := scaffold.NewResource(opts.ResourceAPIVersion, opts.ResourceKind) - if err != nil { - return nil, nil, err - } - return r, chart, nil -} - -func createChartFromDisk(destDir, source string) (*chart.Chart, error) { - chart, err := loader.Load(source) - if err != nil { - return nil, err - } - - // Save it into our project's helm-charts directory. - if err := chartutil.SaveDir(chart, destDir); err != nil { - return nil, err - } - return chart, nil -} - -func createChartFromRemote(destDir string, opts CreateChartOptions) (*chart.Chart, error) { - settings := cli.New() - getters := getter.All(settings) - c := downloader.ChartDownloader{ - Out: os.Stderr, - Getters: getters, - RepositoryConfig: settings.RepositoryConfig, - RepositoryCache: settings.RepositoryCache, - } - - if opts.Repo != "" { - chartURL, err := repo.FindChartInRepoURL(opts.Repo, opts.Chart, opts.Version, "", "", "", getters) - if err != nil { - return nil, err - } - opts.Chart = chartURL - } - - tmpDir, err := ioutil.TempDir("", "osdk-helm-chart") - if err != nil { - return nil, err - } - defer func() { - if err := os.RemoveAll(tmpDir); err != nil { - log.Errorf("Failed to remove temporary directory %s: %s", tmpDir, err) - } - }() - - chartArchive, _, err := c.DownloadTo(opts.Chart, opts.Version, tmpDir) - if err != nil { - return nil, err - } - - return createChartFromDisk(destDir, chartArchive) -} - -func fetchChartDependencies(chartPath string) error { - settings := cli.New() - getters := getter.All(settings) - - out := &bytes.Buffer{} - man := &downloader.Manager{ - Out: out, - ChartPath: chartPath, - Getters: getters, - RepositoryConfig: settings.RepositoryConfig, - RepositoryCache: settings.RepositoryCache, - } - if err := man.Build(); err != nil { - fmt.Println(out.String()) - return err - } - return nil -} diff --git a/internal/scaffold/helm/chart_test.go b/internal/scaffold/helm/chart_test.go deleted file mode 100644 index 5c00b4db4ba..00000000000 --- a/internal/scaffold/helm/chart_test.go +++ /dev/null @@ -1,253 +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 helm_test - -import ( - "fmt" - "os" - "path/filepath" - "testing" - - "github.com/operator-framework/operator-sdk/internal/scaffold" - "github.com/operator-framework/operator-sdk/internal/scaffold/helm" - - "github.com/stretchr/testify/assert" - "helm.sh/helm/v3/pkg/chart/loader" - "helm.sh/helm/v3/pkg/repo/repotest" -) - -func TestCreateChart(t *testing.T) { - srv, err := repotest.NewTempServer("testdata/testcharts/*.tgz") - if err != nil { - t.Fatalf("Failed to create new temp server: %s", err) - } - defer srv.Stop() - - if err := srv.LinkIndices(); err != nil { - t.Fatalf("Failed to link server indices: %s", err) - } - - const ( - chartName = "test-chart" - latestVersion = "1.2.3" - previousVersion = "1.2.0" - nonExistentVersion = "0.0.1" - customAPIVersion = "example.com/v1" - customKind = "MyApp" - customExpectName = "myapp" - expectDerivedKind = "TestChart" - ) - - testCases := []createChartTestCase{ - { - name: "from scaffold no apiVersion", - expectErr: true, - }, - { - name: "from scaffold no kind", - expectErr: true, - }, - { - name: "version without helm chart", - helmChartVersion: latestVersion, - expectErr: true, - }, - { - name: "repo without helm chart", - helmChartRepo: srv.URL(), - expectErr: true, - }, - { - name: "non-existent version", - helmChart: "test/" + chartName, - helmChartVersion: nonExistentVersion, - expectErr: true, - }, - { - name: "from scaffold with apiVersion and kind", - apiVersion: customAPIVersion, - kind: customKind, - expectResource: mustNewResource(t, customAPIVersion, customKind), - expectChartName: customExpectName, - expectChartVersion: "0.1.0", - }, - { - name: "from directory", - helmChart: filepath.Join(".", "testdata", "testcharts", chartName), - expectResource: mustNewResource(t, helm.DefaultAPIVersion, expectDerivedKind), - expectChartName: chartName, - expectChartVersion: latestVersion, - }, - { - name: "from archive", - helmChart: filepath.Join(".", "testdata", "testcharts", fmt.Sprintf("%s-%s.tgz", chartName, latestVersion)), - expectResource: mustNewResource(t, helm.DefaultAPIVersion, expectDerivedKind), - expectChartName: chartName, - expectChartVersion: latestVersion, - }, - { - name: "from url", - helmChart: fmt.Sprintf("%s/%s-%s.tgz", srv.URL(), chartName, latestVersion), - expectResource: mustNewResource(t, helm.DefaultAPIVersion, expectDerivedKind), - expectChartName: chartName, - expectChartVersion: latestVersion, - }, - { - name: "from repo and name implicit latest", - helmChart: "test/" + chartName, - expectResource: mustNewResource(t, helm.DefaultAPIVersion, expectDerivedKind), - expectChartName: chartName, - expectChartVersion: latestVersion, - }, - { - name: "from repo and name implicit latest with apiVersion", - helmChart: "test/" + chartName, - apiVersion: customAPIVersion, - expectResource: mustNewResource(t, customAPIVersion, expectDerivedKind), - expectChartName: chartName, - expectChartVersion: latestVersion, - }, - { - name: "from repo and name implicit latest with kind", - helmChart: "test/" + chartName, - kind: customKind, - expectResource: mustNewResource(t, helm.DefaultAPIVersion, customKind), - expectChartName: chartName, - expectChartVersion: latestVersion, - }, - { - name: "from repo and name implicit latest with apiVersion and kind", - helmChart: "test/" + chartName, - apiVersion: customAPIVersion, - kind: customKind, - expectResource: mustNewResource(t, customAPIVersion, customKind), - expectChartName: chartName, - expectChartVersion: latestVersion, - }, - { - name: "from repo and name explicit latest", - helmChart: "test/" + chartName, - helmChartVersion: latestVersion, - expectResource: mustNewResource(t, helm.DefaultAPIVersion, expectDerivedKind), - expectChartName: chartName, - expectChartVersion: latestVersion, - }, - { - name: "from repo and name explicit previous", - helmChart: "test/" + chartName, - helmChartVersion: previousVersion, - expectResource: mustNewResource(t, helm.DefaultAPIVersion, expectDerivedKind), - expectChartName: chartName, - expectChartVersion: previousVersion, - }, - { - name: "from name and repo url implicit latest", - helmChart: chartName, - helmChartRepo: srv.URL(), - expectResource: mustNewResource(t, helm.DefaultAPIVersion, expectDerivedKind), - expectChartName: chartName, - expectChartVersion: latestVersion, - }, - { - name: "from name and repo url explicit latest", - helmChart: chartName, - helmChartRepo: srv.URL(), - helmChartVersion: latestVersion, - expectResource: mustNewResource(t, helm.DefaultAPIVersion, expectDerivedKind), - expectChartName: chartName, - expectChartVersion: latestVersion, - }, - { - name: "from name and repo url explicit previous", - helmChart: chartName, - helmChartRepo: srv.URL(), - helmChartVersion: previousVersion, - expectResource: mustNewResource(t, helm.DefaultAPIVersion, expectDerivedKind), - expectChartName: chartName, - expectChartVersion: previousVersion, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - runTestCase(t, srv.Root(), tc) - }) - } -} - -type createChartTestCase struct { - name string - - apiVersion string - kind string - helmChart string - helmChartVersion string - helmChartRepo string - - expectResource *scaffold.Resource - expectChartName string - expectChartVersion string - expectErr bool -} - -func mustNewResource(t *testing.T, apiVersion, kind string) *scaffold.Resource { - r, err := scaffold.NewResource(apiVersion, kind) - if err != nil { - t.Fatalf("Could not create resource for apiVersion=%s kind=%s: %s", apiVersion, kind, err) - } - return r -} - -func runTestCase(t *testing.T, testDir string, tc createChartTestCase) { - outputDir := filepath.Join(testDir, "output") - assert.NoError(t, os.Mkdir(outputDir, 0755)) - defer os.RemoveAll(outputDir) - - os.Setenv("XDG_CONFIG_HOME", filepath.Join(testDir, ".config")) - os.Setenv("XDG_CACHE_HOME", filepath.Join(testDir, ".cache")) - os.Setenv("HELM_REPOSITORY_CONFIG", filepath.Join(testDir, "repositories.yaml")) - os.Setenv("HELM_REPOSITORY_CACHE", filepath.Join(testDir)) - defer os.Unsetenv("XDG_CONFIG_HOME") - defer os.Unsetenv("XDG_CACHE_HOME") - defer os.Unsetenv("HELM_REPOSITORY_CONFIG") - defer os.Unsetenv("HELM_REPOSITORY_CACHE") - - opts := helm.CreateChartOptions{ - ResourceAPIVersion: tc.apiVersion, - ResourceKind: tc.kind, - Chart: tc.helmChart, - Version: tc.helmChartVersion, - Repo: tc.helmChartRepo, - } - resource, chart, err := helm.CreateChart(outputDir, opts) - if tc.expectErr { - assert.Error(t, err) - return - } - - if !assert.NoError(t, err) { - return - } - assert.Equal(t, tc.expectResource, resource) - assert.Equal(t, tc.expectChartName, chart.Name()) - assert.Equal(t, tc.expectChartVersion, chart.Metadata.Version) - - loadedChart, err := loader.Load(filepath.Join(outputDir, helm.HelmChartsDir, chart.Name())) - if err != nil { - t.Fatalf("Could not load chart from expected location: %s", err) - } - - assert.Equal(t, loadedChart, chart) -} diff --git a/internal/scaffold/helm/dockerfile.go b/internal/scaffold/helm/dockerfile.go deleted file mode 100644 index a77368ae317..00000000000 --- a/internal/scaffold/helm/dockerfile.go +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2018 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 helm - -import ( - "path/filepath" - "strings" - - "github.com/operator-framework/operator-sdk/internal/scaffold" - "github.com/operator-framework/operator-sdk/internal/scaffold/input" - "github.com/operator-framework/operator-sdk/version" -) - -// Dockerfile specifies the Helm Dockerfile scaffold -type Dockerfile struct { - input.Input - - HelmChartsDir string - ImageTag string -} - -// GetInput gets the scaffold execution input -func (d *Dockerfile) GetInput() (input.Input, error) { - if d.Path == "" { - d.Path = filepath.Join(scaffold.BuildDir, scaffold.DockerfileFile) - } - d.HelmChartsDir = HelmChartsDir - d.ImageTag = strings.TrimSuffix(version.Version, "+git") - d.TemplateBody = dockerFileHelmTmpl - return d.Input, nil -} - -const dockerFileHelmTmpl = `FROM quay.io/operator-framework/helm-operator:{{.ImageTag}} - -COPY watches.yaml ${HOME}/watches.yaml -COPY {{.HelmChartsDir}}/ ${HOME}/{{.HelmChartsDir}}/ -` diff --git a/internal/scaffold/helm/init.go b/internal/scaffold/helm/init.go deleted file mode 100644 index c5c4a3e6ba5..00000000000 --- a/internal/scaffold/helm/init.go +++ /dev/null @@ -1,90 +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 helm - -import ( - "fmt" - "path/filepath" - - "github.com/prometheus/common/log" - "k8s.io/client-go/discovery" - "sigs.k8s.io/controller-runtime/pkg/client/config" - "sigs.k8s.io/yaml" - - "github.com/operator-framework/operator-sdk/internal/genutil" - "github.com/operator-framework/operator-sdk/internal/scaffold" - "github.com/operator-framework/operator-sdk/internal/scaffold/input" - "github.com/operator-framework/operator-sdk/pkg/helm/watches" -) - -// todo: refactory it to work in the kb layout/design -// Init will perform the helm Scaffold to init a project -func Init(cfg input.Config, createOpts CreateChartOptions) error { - - resource, chart, err := CreateChart(cfg.AbsProjectPath, createOpts) - if err != nil { - return fmt.Errorf("failed to create helm chart: %v", err) - } - - valuesPath := filepath.Join("", HelmChartsDir, chart.Name(), "values.yaml") - - rawValues, err := yaml.Marshal(chart.Values) - if err != nil { - return fmt.Errorf("failed to get raw chart values: %v", err) - } - crSpec := fmt.Sprintf("# Default values copied from %s\n\n%s", valuesPath, rawValues) - - roleScaffold := DefaultRoleScaffold - if k8sCfg, err := config.GetConfig(); err != nil { - log.Warnf("Using default RBAC rules: failed to get Kubernetes config: %s", err) - } else if dc, err := discovery.NewDiscoveryClientForConfig(k8sCfg); err != nil { - log.Warnf("Using default RBAC rules: failed to create Kubernetes discovery client: %s", err) - } else { - roleScaffold = GenerateRoleScaffold(dc, chart) - } - - // update watch.yaml for the given resource. - watchesFile := filepath.Join(cfg.AbsProjectPath, watches.WatchesFile) - if err := watches.UpdateForResource(watchesFile, resource, chart.Name()); err != nil { - return fmt.Errorf("failed to create watches.yaml: %w", err) - } - - s := &scaffold.Scaffold{} - err = s.Execute(&cfg, - &Dockerfile{}, - &scaffold.ServiceAccount{}, - &roleScaffold, - &scaffold.RoleBinding{IsClusterScoped: roleScaffold.IsClusterScoped}, - &Operator{}, - &scaffold.CR{ - Resource: resource, - Spec: crSpec, - }, - ) - if err != nil { - return fmt.Errorf("new helm scaffold failed: %v", err) - } - - // nolint:staticcheck - if err = genutil.GenerateCRDNonGo("", *resource, createOpts.CRDVersion); err != nil { - return err - } - - if err := scaffold.UpdateRoleForResource(resource, cfg.AbsProjectPath); err != nil { - return fmt.Errorf("failed to update the RBAC manifest for resource (%v, %v): %v", - resource.APIVersion, resource.Kind, err) - } - return nil -} diff --git a/internal/scaffold/helm/operator.go b/internal/scaffold/helm/operator.go deleted file mode 100644 index 8c04fc5c07c..00000000000 --- a/internal/scaffold/helm/operator.go +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright 2018 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 helm - -import ( - "path/filepath" - - "github.com/operator-framework/operator-sdk/internal/scaffold" - "github.com/operator-framework/operator-sdk/internal/scaffold/input" -) - -// Operator specifies the Helm operator.yaml manifest scaffold -type Operator struct { - input.Input -} - -// GetInput gets the scaffold execution input -func (s *Operator) GetInput() (input.Input, error) { - if s.Path == "" { - s.Path = filepath.Join(scaffold.DeployDir, scaffold.OperatorYamlFile) - } - s.TemplateBody = operatorTemplate - return s.Input, nil -} - -const operatorTemplate = `apiVersion: apps/v1 -kind: Deployment -metadata: - name: {{.ProjectName}} -spec: - replicas: 1 - selector: - matchLabels: - name: {{.ProjectName}} - template: - metadata: - labels: - name: {{.ProjectName}} - spec: - serviceAccountName: {{.ProjectName}} - containers: - - name: {{.ProjectName}} - # Replace this with the built image name - image: REPLACE_IMAGE - imagePullPolicy: Always - env: - - name: WATCH_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: OPERATOR_NAME - value: "{{.ProjectName}}" -` diff --git a/internal/scaffold/helm/role.go b/internal/scaffold/helm/role.go deleted file mode 100644 index b13a59b7bf1..00000000000 --- a/internal/scaffold/helm/role.go +++ /dev/null @@ -1,226 +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 helm - -import ( - "fmt" - "path/filepath" - "sort" - "strings" - - "github.com/operator-framework/operator-sdk/internal/scaffold" - - log "github.com/sirupsen/logrus" - "helm.sh/helm/v3/pkg/action" - "helm.sh/helm/v3/pkg/chart" - "helm.sh/helm/v3/pkg/chartutil" - "helm.sh/helm/v3/pkg/releaseutil" - rbacv1 "k8s.io/api/rbac/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "sigs.k8s.io/yaml" -) - -// roleDiscoveryInterface is an interface that contains just the discovery -// methods needed by the Helm role scaffold generator. Requiring just this -// interface simplifies testing. -type roleDiscoveryInterface interface { - ServerGroupsAndResources() ([]*metav1.APIGroup, []*metav1.APIResourceList, error) -} - -var DefaultRoleScaffold = scaffold.Role{ - IsClusterScoped: false, - SkipDefaultRules: false, - CustomRules: []rbacv1.PolicyRule{ - // We need this rule so tiller can read namespaces to ensure they exist - { - APIGroups: []string{""}, - Resources: []string{"namespaces"}, - Verbs: []string{"get"}, - }, - - // We need this rule for leader election and release state storage to work - { - APIGroups: []string{""}, - Resources: []string{"configmaps", "secrets"}, - Verbs: []string{rbacv1.VerbAll}, - }, - - // We need this rule for creating Kubernetes events - { - APIGroups: []string{""}, - Resources: []string{"events"}, - Verbs: []string{"create"}, - }, - }, -} - -// GenerateRoleScaffold generates a role scaffold from the provided helm chart. It -// renders a release manifest using the chart's default values and uses the Kubernetes -// discovery API to lookup each resource in the resulting manifest. -// The role scaffold will have IsClusterScoped=true if the chart lists cluster scoped resources -func GenerateRoleScaffold(dc roleDiscoveryInterface, chart *chart.Chart) scaffold.Role { - log.Info("Generating RBAC rules") - - roleScaffold := DefaultRoleScaffold - clusterResourceRules, namespacedResourceRules, err := generateRoleRules(dc, chart) - if err != nil { - log.Warnf("Using default RBAC rules: failed to generate RBAC rules: %s", err) - return roleScaffold - } - - roleScaffold.SkipDefaultRules = true - - // Use a ClusterRole if cluster scoped resources are listed in the chart - if len(clusterResourceRules) > 0 { - log.Info("Scaffolding ClusterRole and ClusterRolebinding for cluster scoped resources in the helm chart") - roleScaffold.IsClusterScoped = true - } - roleScaffold.CustomRules = append(roleScaffold.CustomRules, append(clusterResourceRules, - namespacedResourceRules...)...) - - log.Warn("The RBAC rules generated in deploy/role.yaml are based on the chart's default manifest." + - " Some rules may be missing for resources that are only enabled with custom values, and" + - " some existing rules may be overly broad. Double check the rules generated in deploy/role.yaml" + - " to ensure they meet the operator's permission requirements.") - - return roleScaffold -} - -func generateRoleRules(dc roleDiscoveryInterface, chart *chart.Chart) ([]rbacv1.PolicyRule, - []rbacv1.PolicyRule, error) { - _, serverResources, err := dc.ServerGroupsAndResources() - if err != nil { - return nil, nil, fmt.Errorf("failed to get server resources: %v", err) - } - - manifests, err := getDefaultManifests(chart) - if err != nil { - return nil, nil, fmt.Errorf("failed to get default manifest: %v", err) - } - - // Use maps of sets of resources, keyed by their group. This helps us - // de-duplicate resources within a group as we traverse the manifests. - clusterGroups := map[string]map[string]struct{}{} - namespacedGroups := map[string]map[string]struct{}{} - - for _, m := range manifests { - name := m.Name - content := strings.TrimSpace(m.Content) - - // Ignore NOTES.txt, helper manifests, and empty manifests. - b := filepath.Base(name) - if b == "NOTES.txt" { - continue - } - if strings.HasPrefix(b, "_") { - continue - } - if content == "" || content == "---" { - continue - } - - // Extract the gvk from the template - resource := unstructured.Unstructured{} - err := yaml.Unmarshal([]byte(content), &resource) - if err != nil { - log.Warnf("Skipping rule generation for %s. Failed to parse manifest: %s", name, err) - continue - } - groupVersion := resource.GetAPIVersion() - group := resource.GroupVersionKind().Group - kind := resource.GroupVersionKind().Kind - - // If we don't have the group or the kind, we won't be able to - // create a valid role rule, log a warning and continue. - if groupVersion == "" { - log.Warnf("Skipping rule generation for %s. Failed to determine resource apiVersion.", name) - continue - } - if kind == "" { - log.Warnf("Skipping rule generation for %s. Failed to determine resource kind.", name) - continue - } - - if resourceName, namespaced, ok := getResource(serverResources, groupVersion, kind); ok { - if !namespaced { - if clusterGroups[group] == nil { - clusterGroups[group] = map[string]struct{}{} - } - clusterGroups[group][resourceName] = struct{}{} - } else { - if namespacedGroups[group] == nil { - namespacedGroups[group] = map[string]struct{}{} - } - namespacedGroups[group][resourceName] = struct{}{} - } - } else { - log.Warnf("Skipping rule generation for %s. Failed to determine resource scope for %s.", - name, resource.GroupVersionKind()) - continue - } - } - - // convert map[string]map[string]struct{} to []rbacv1.PolicyRule - clusterRules := buildRulesFromGroups(clusterGroups) - namespacedRules := buildRulesFromGroups(namespacedGroups) - - return clusterRules, namespacedRules, nil -} - -func getDefaultManifests(c *chart.Chart) ([]releaseutil.Manifest, error) { - install := action.NewInstall(&action.Configuration{}) - install.DryRun = true - install.ReleaseName = "RELEASE-NAME" - install.Replace = true - install.ClientOnly = true - rel, err := install.Run(c, nil) - if err != nil { - return nil, fmt.Errorf("failed to render chart templates: %v", err) - } - _, manifests, err := releaseutil.SortManifests(releaseutil.SplitManifests(rel.Manifest), - chartutil.DefaultVersionSet, releaseutil.InstallOrder) - return manifests, err -} - -func getResource(namespacedResourceList []*metav1.APIResourceList, groupVersion, kind string) (string, bool, bool) { - for _, apiResourceList := range namespacedResourceList { - if apiResourceList.GroupVersion == groupVersion { - for _, apiResource := range apiResourceList.APIResources { - if apiResource.Kind == kind { - return apiResource.Name, apiResource.Namespaced, true - } - } - } - } - return "", false, false -} - -func buildRulesFromGroups(groups map[string]map[string]struct{}) []rbacv1.PolicyRule { - rules := []rbacv1.PolicyRule{} - for group, resourceNames := range groups { - resources := []string{} - for resource := range resourceNames { - resources = append(resources, resource) - } - sort.Strings(resources) - rules = append(rules, rbacv1.PolicyRule{ - APIGroups: []string{group}, - Resources: resources, - Verbs: []string{rbacv1.VerbAll}, - }) - } - return rules -} diff --git a/internal/scaffold/helm/role_test.go b/internal/scaffold/helm/role_test.go deleted file mode 100644 index 548dc1d50a3..00000000000 --- a/internal/scaffold/helm/role_test.go +++ /dev/null @@ -1,212 +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 helm_test - -import ( - "errors" - "fmt" - "testing" - - "github.com/operator-framework/operator-sdk/internal/scaffold/helm" - - "github.com/stretchr/testify/assert" - "helm.sh/helm/v3/pkg/chart" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -func TestGenerateRoleScaffold(t *testing.T) { - validDiscoveryClient := &mockRoleDiscoveryClient{ - serverGroupsAndResources: func() ([]*metav1.APIGroup, []*metav1.APIResourceList, error) { - return simpleGroupList(), simpleResourcesList(), nil - }, - } - - brokenDiscoveryClient := &mockRoleDiscoveryClient{ - serverGroupsAndResources: func() ([]*metav1.APIGroup, []*metav1.APIResourceList, error) { - return nil, nil, errors.New("no server resources") - }, - } - - testCases := []roleScaffoldTestCase{ - { - name: "fallback to default with unparsable template", - chart: failChart(), - expectSkipDefaultRules: false, - expectIsClusterScoped: false, - expectLenCustomRules: 3, - }, - { - name: "skip rule for unknown API", - chart: unknownAPIChart(), - expectSkipDefaultRules: true, - expectIsClusterScoped: false, - expectLenCustomRules: 4, - }, - { - name: "namespaced manifest", - chart: namespacedChart(), - expectSkipDefaultRules: true, - expectIsClusterScoped: false, - expectLenCustomRules: 4, - }, - { - name: "cluster scoped manifest", - chart: clusterScopedChart(), - expectSkipDefaultRules: true, - expectIsClusterScoped: true, - expectLenCustomRules: 5, - }, - } - - for _, tc := range testCases { - t.Run(fmt.Sprintf("%s with valid discovery client", tc.name), func(t *testing.T) { - roleScaffold := helm.GenerateRoleScaffold(validDiscoveryClient, tc.chart) - assert.Equal(t, tc.expectSkipDefaultRules, roleScaffold.SkipDefaultRules) - assert.Equal(t, tc.expectLenCustomRules, len(roleScaffold.CustomRules)) - assert.Equal(t, tc.expectIsClusterScoped, roleScaffold.IsClusterScoped) - }) - - t.Run(fmt.Sprintf("%s with broken discovery client", tc.name), func(t *testing.T) { - roleScaffold := helm.GenerateRoleScaffold(brokenDiscoveryClient, tc.chart) - assert.Equal(t, false, roleScaffold.SkipDefaultRules) - assert.Equal(t, 3, len(roleScaffold.CustomRules)) - assert.Equal(t, false, roleScaffold.IsClusterScoped) - }) - } -} - -type mockRoleDiscoveryClient struct { - serverGroupsAndResources func() ([]*metav1.APIGroup, []*metav1.APIResourceList, error) -} - -func (dc *mockRoleDiscoveryClient) ServerGroupsAndResources() ([]*metav1.APIGroup, []*metav1.APIResourceList, error) { - return dc.serverGroupsAndResources() -} - -func simpleGroupList() []*metav1.APIGroup { - return []*metav1.APIGroup{ - { - Name: "example", - }, - { - Name: "example2", - }, - } -} - -func simpleResourcesList() []*metav1.APIResourceList { - return []*metav1.APIResourceList{ - { - GroupVersion: "v1", - APIResources: []metav1.APIResource{ - { - Name: "namespaces", - Kind: "Namespace", - Namespaced: false, - }, - { - Name: "pods", - Kind: "Pod", - Namespaced: true, - }, - }, - }, - } -} - -type roleScaffoldTestCase struct { - name string - chart *chart.Chart - expectSkipDefaultRules bool - expectIsClusterScoped bool - expectLenCustomRules int -} - -func failChart() *chart.Chart { - return &chart.Chart{ - Metadata: &chart.Metadata{ - Name: "broken", - }, - Templates: []*chart.File{ - {Name: "broken1.yaml", Data: []byte(`invalid {{ template`)}, - }, - } -} - -func unknownAPIChart() *chart.Chart { - return &chart.Chart{ - Metadata: &chart.Metadata{ - Name: "unknown", - }, - Templates: []*chart.File{ - {Name: "unknown1.yaml", Data: testUnknownData("unknown1")}, - {Name: "pod1.yaml", Data: testPodData("pod1")}, - }, - } -} - -func namespacedChart() *chart.Chart { - return &chart.Chart{ - Metadata: &chart.Metadata{ - Name: "namespaced", - }, - Templates: []*chart.File{ - {Name: "pod1.yaml", Data: testPodData("pod1")}, - {Name: "pod2.yaml", Data: testPodData("pod2")}, - }, - } -} - -func clusterScopedChart() *chart.Chart { - return &chart.Chart{ - Metadata: &chart.Metadata{ - Name: "clusterscoped", - }, - Templates: []*chart.File{ - {Name: "pod1.yaml", Data: testPodData("pod1")}, - {Name: "pod2.yaml", Data: testPodData("pod2")}, - {Name: "ns1.yaml", Data: testNamespaceData("ns1")}, - {Name: "ns2.yaml", Data: testNamespaceData("ns2")}, - }, - } -} - -func testUnknownData(name string) []byte { - return []byte(fmt.Sprintf(`apiVersion: my-test-unknown.unknown.com/v1alpha1 -kind: UnknownKind -metadata: - name: %s`, name), - ) -} - -func testPodData(name string) []byte { - return []byte(fmt.Sprintf(`apiVersion: v1 -kind: Pod -metadata: - name: %s -spec: - containers: - - name: test - image: test`, name), - ) -} - -func testNamespaceData(name string) []byte { - return []byte(fmt.Sprintf(`apiVersion: v1 -kind: Namespace -metadata: - name: %s`, name), - ) -} diff --git a/internal/scaffold/helm/testdata/testcharts/test-chart-1.2.0.tgz b/internal/scaffold/helm/testdata/testcharts/test-chart-1.2.0.tgz deleted file mode 100644 index c32376b22ec..00000000000 Binary files a/internal/scaffold/helm/testdata/testcharts/test-chart-1.2.0.tgz and /dev/null differ diff --git a/internal/scaffold/helm/testdata/testcharts/test-chart-1.2.3.tgz b/internal/scaffold/helm/testdata/testcharts/test-chart-1.2.3.tgz deleted file mode 100644 index 9e5b271349c..00000000000 Binary files a/internal/scaffold/helm/testdata/testcharts/test-chart-1.2.3.tgz and /dev/null differ diff --git a/internal/scaffold/helm/testdata/testcharts/test-chart/.helmignore b/internal/scaffold/helm/testdata/testcharts/test-chart/.helmignore deleted file mode 100644 index 50af0317254..00000000000 --- a/internal/scaffold/helm/testdata/testcharts/test-chart/.helmignore +++ /dev/null @@ -1,22 +0,0 @@ -# Patterns to ignore when building packages. -# This supports shell glob matching, relative path matching, and -# negation (prefixed with !). Only one pattern per line. -.DS_Store -# Common VCS dirs -.git/ -.gitignore -.bzr/ -.bzrignore -.hg/ -.hgignore -.svn/ -# Common backup files -*.swp -*.bak -*.tmp -*~ -# Various IDEs -.project -.idea/ -*.tmproj -.vscode/ diff --git a/internal/scaffold/helm/testdata/testcharts/test-chart/Chart.yaml b/internal/scaffold/helm/testdata/testcharts/test-chart/Chart.yaml deleted file mode 100644 index bbe61e499db..00000000000 --- a/internal/scaffold/helm/testdata/testcharts/test-chart/Chart.yaml +++ /dev/null @@ -1,21 +0,0 @@ -apiVersion: v2 -name: test-chart -description: A Helm chart for Kubernetes - -# A chart can be either an 'application' or a 'library' chart. -# -# Application charts are a collection of templates that can be packaged into versioned archives -# to be deployed. -# -# Library charts provide useful utilities or functions for the chart developer. They're included as -# a dependency of application charts to inject those utilities and functions into the rendering -# pipeline. Library charts do not define any templates and therefore cannot be deployed. -type: application - -# This is the chart version. This version number should be incremented each time you make changes -# to the chart and its templates, including the app version. -version: 1.2.3 - -# This is the version number of the application being deployed. This version number should be -# incremented each time you make changes to the application. -appVersion: 1.16.0 diff --git a/internal/scaffold/helm/testdata/testcharts/test-chart/templates/NOTES.txt b/internal/scaffold/helm/testdata/testcharts/test-chart/templates/NOTES.txt deleted file mode 100644 index 8fee581ed83..00000000000 --- a/internal/scaffold/helm/testdata/testcharts/test-chart/templates/NOTES.txt +++ /dev/null @@ -1,21 +0,0 @@ -1. Get the application URL by running these commands: -{{- if .Values.ingress.enabled }} -{{- range $host := .Values.ingress.hosts }} - {{- range .paths }} - http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ . }} - {{- end }} -{{- end }} -{{- else if contains "NodePort" .Values.service.type }} - export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "test-chart.fullname" . }}) - export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") - echo http://$NODE_IP:$NODE_PORT -{{- else if contains "LoadBalancer" .Values.service.type }} - NOTE: It may take a few minutes for the LoadBalancer IP to be available. - You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "test-chart.fullname" . }}' - export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "test-chart.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") - echo http://$SERVICE_IP:{{ .Values.service.port }} -{{- else if contains "ClusterIP" .Values.service.type }} - export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "test-chart.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") - echo "Visit http://127.0.0.1:8080 to use your application" - kubectl port-forward $POD_NAME 8080:80 -{{- end }} diff --git a/internal/scaffold/helm/testdata/testcharts/test-chart/templates/_helpers.tpl b/internal/scaffold/helm/testdata/testcharts/test-chart/templates/_helpers.tpl deleted file mode 100644 index b43406aead9..00000000000 --- a/internal/scaffold/helm/testdata/testcharts/test-chart/templates/_helpers.tpl +++ /dev/null @@ -1,63 +0,0 @@ -{{/* vim: set filetype=mustache: */}} -{{/* -Expand the name of the chart. -*/}} -{{- define "test-chart.name" -}} -{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} -{{- end -}} - -{{/* -Create a default fully qualified app name. -We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). -If release name contains chart name it will be used as a full name. -*/}} -{{- define "test-chart.fullname" -}} -{{- if .Values.fullnameOverride -}} -{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} -{{- else -}} -{{- $name := default .Chart.Name .Values.nameOverride -}} -{{- if contains $name .Release.Name -}} -{{- .Release.Name | trunc 63 | trimSuffix "-" -}} -{{- else -}} -{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} -{{- end -}} -{{- end -}} -{{- end -}} - -{{/* -Create chart name and version as used by the chart label. -*/}} -{{- define "test-chart.chart" -}} -{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} -{{- end -}} - -{{/* -Common labels -*/}} -{{- define "test-chart.labels" -}} -helm.sh/chart: {{ include "test-chart.chart" . }} -{{ include "test-chart.selectorLabels" . }} -{{- if .Chart.AppVersion }} -app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} -{{- end }} -app.kubernetes.io/managed-by: {{ .Release.Service }} -{{- end -}} - -{{/* -Selector labels -*/}} -{{- define "test-chart.selectorLabels" -}} -app.kubernetes.io/name: {{ include "test-chart.name" . }} -app.kubernetes.io/instance: {{ .Release.Name }} -{{- end -}} - -{{/* -Create the name of the service account to use -*/}} -{{- define "test-chart.serviceAccountName" -}} -{{- if .Values.serviceAccount.create -}} - {{ default (include "test-chart.fullname" .) .Values.serviceAccount.name }} -{{- else -}} - {{ default "default" .Values.serviceAccount.name }} -{{- end -}} -{{- end -}} diff --git a/internal/scaffold/helm/testdata/testcharts/test-chart/templates/deployment.yaml b/internal/scaffold/helm/testdata/testcharts/test-chart/templates/deployment.yaml deleted file mode 100644 index e581bd0aefb..00000000000 --- a/internal/scaffold/helm/testdata/testcharts/test-chart/templates/deployment.yaml +++ /dev/null @@ -1,55 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: {{ include "test-chart.fullname" . }} - labels: - {{- include "test-chart.labels" . | nindent 4 }} -spec: - replicas: {{ .Values.replicaCount }} - selector: - matchLabels: - {{- include "test-chart.selectorLabels" . | nindent 6 }} - template: - metadata: - labels: - {{- include "test-chart.selectorLabels" . | nindent 8 }} - spec: - {{- with .Values.imagePullSecrets }} - imagePullSecrets: - {{- toYaml . | nindent 8 }} - {{- end }} - serviceAccountName: {{ include "test-chart.serviceAccountName" . }} - securityContext: - {{- toYaml .Values.podSecurityContext | nindent 8 }} - containers: - - name: {{ .Chart.Name }} - securityContext: - {{- toYaml .Values.securityContext | nindent 12 }} - image: "{{ .Values.image.repository }}:{{ .Chart.AppVersion }}" - imagePullPolicy: {{ .Values.image.pullPolicy }} - ports: - - name: http - containerPort: 80 - protocol: TCP - livenessProbe: - httpGet: - path: / - port: http - readinessProbe: - httpGet: - path: / - port: http - resources: - {{- toYaml .Values.resources | nindent 12 }} - {{- with .Values.nodeSelector }} - nodeSelector: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.affinity }} - affinity: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.tolerations }} - tolerations: - {{- toYaml . | nindent 8 }} - {{- end }} diff --git a/internal/scaffold/helm/testdata/testcharts/test-chart/templates/ingress.yaml b/internal/scaffold/helm/testdata/testcharts/test-chart/templates/ingress.yaml deleted file mode 100644 index d7d9f64e554..00000000000 --- a/internal/scaffold/helm/testdata/testcharts/test-chart/templates/ingress.yaml +++ /dev/null @@ -1,37 +0,0 @@ -{{- if .Values.ingress.enabled -}} -{{- $fullName := include "test-chart.fullname" . -}} -{{- $svcPort := .Values.service.port -}} -apiVersion: networking.k8s.io/v1beta1 -kind: Ingress -metadata: - name: {{ $fullName }} - labels: - {{- include "test-chart.labels" . | nindent 4 }} - {{- with .Values.ingress.annotations }} - annotations: - {{- toYaml . | nindent 4 }} - {{- end }} -spec: -{{- if .Values.ingress.tls }} - tls: - {{- range .Values.ingress.tls }} - - hosts: - {{- range .hosts }} - - {{ . | quote }} - {{- end }} - secretName: {{ .secretName }} - {{- end }} -{{- end }} - rules: - {{- range .Values.ingress.hosts }} - - host: {{ .host | quote }} - http: - paths: - {{- range .paths }} - - path: {{ . }} - backend: - serviceName: {{ $fullName }} - servicePort: {{ $svcPort }} - {{- end }} - {{- end }} -{{- end }} diff --git a/internal/scaffold/helm/testdata/testcharts/test-chart/templates/service.yaml b/internal/scaffold/helm/testdata/testcharts/test-chart/templates/service.yaml deleted file mode 100644 index b8d5d516250..00000000000 --- a/internal/scaffold/helm/testdata/testcharts/test-chart/templates/service.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: {{ include "test-chart.fullname" . }} - labels: - {{- include "test-chart.labels" . | nindent 4 }} -spec: - type: {{ .Values.service.type }} - ports: - - port: {{ .Values.service.port }} - targetPort: http - protocol: TCP - name: http - selector: - {{- include "test-chart.selectorLabels" . | nindent 4 }} diff --git a/internal/scaffold/helm/testdata/testcharts/test-chart/templates/serviceaccount.yaml b/internal/scaffold/helm/testdata/testcharts/test-chart/templates/serviceaccount.yaml deleted file mode 100644 index 65903e18795..00000000000 --- a/internal/scaffold/helm/testdata/testcharts/test-chart/templates/serviceaccount.yaml +++ /dev/null @@ -1,8 +0,0 @@ -{{- if .Values.serviceAccount.create -}} -apiVersion: v1 -kind: ServiceAccount -metadata: - name: {{ include "test-chart.serviceAccountName" . }} - labels: -{{ include "test-chart.labels" . | nindent 4 }} -{{- end -}} diff --git a/internal/scaffold/helm/testdata/testcharts/test-chart/templates/tests/test-connection.yaml b/internal/scaffold/helm/testdata/testcharts/test-chart/templates/tests/test-connection.yaml deleted file mode 100644 index 3a7fdeb4e8a..00000000000 --- a/internal/scaffold/helm/testdata/testcharts/test-chart/templates/tests/test-connection.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - name: "{{ include "test-chart.fullname" . }}-test-connection" - labels: -{{ include "test-chart.labels" . | nindent 4 }} - annotations: - "helm.sh/hook": test-success -spec: - containers: - - name: wget - image: busybox - command: ['wget'] - args: ['{{ include "test-chart.fullname" . }}:{{ .Values.service.port }}'] - restartPolicy: Never diff --git a/internal/scaffold/helm/testdata/testcharts/test-chart/values.yaml b/internal/scaffold/helm/testdata/testcharts/test-chart/values.yaml deleted file mode 100644 index 5df5f37d626..00000000000 --- a/internal/scaffold/helm/testdata/testcharts/test-chart/values.yaml +++ /dev/null @@ -1,66 +0,0 @@ -# Default values for test-chart. -# This is a YAML-formatted file. -# Declare variables to be passed into your templates. - -replicaCount: 1 - -image: - repository: nginx - pullPolicy: IfNotPresent - -imagePullSecrets: [] -nameOverride: "" -fullnameOverride: "" - -serviceAccount: - # Specifies whether a service account should be created - create: true - # The name of the service account to use. - # If not set and create is true, a name is generated using the fullname template - name: - -podSecurityContext: {} - # fsGroup: 2000 - -securityContext: {} - # capabilities: - # drop: - # - ALL - # readOnlyRootFilesystem: true - # runAsNonRoot: true - # runAsUser: 1000 - -service: - type: ClusterIP - port: 80 - -ingress: - enabled: false - annotations: {} - # kubernetes.io/ingress.class: nginx - # kubernetes.io/tls-acme: "true" - hosts: - - host: chart-example.local - paths: [] - tls: [] - # - secretName: chart-example-tls - # hosts: - # - chart-example.local - -resources: {} - # We usually recommend not to specify default resources and to leave this as a conscious - # choice for the user. This also increases chances charts run on environments with little - # resources, such as Minikube. If you do want to specify resources, uncomment the following - # lines, adjust them as necessary, and remove the curly braces after 'resources:'. - # limits: - # cpu: 100m - # memory: 128Mi - # requests: - # cpu: 100m - # memory: 128Mi - -nodeSelector: {} - -tolerations: [] - -affinity: {} diff --git a/internal/scaffold/role_test.go b/internal/scaffold/role_test.go deleted file mode 100644 index befa9803c7b..00000000000 --- a/internal/scaffold/role_test.go +++ /dev/null @@ -1,1086 +0,0 @@ -// Copyright 2018 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 scaffold - -import ( - "fmt" - "io/ioutil" - "os" - "testing" - - "github.com/operator-framework/operator-sdk/internal/util/diffutil" - "github.com/operator-framework/operator-sdk/internal/util/fileutil" - log "github.com/sirupsen/logrus" - "github.com/stretchr/testify/assert" - - rbacv1 "k8s.io/api/rbac/v1" -) - -func TestRole(t *testing.T) { - s, buf := setupScaffoldAndWriter() - err := s.Execute(appConfig, &Role{}) - if err != nil { - t.Fatalf("Failed to execute the scaffold: (%v)", err) - } - - if roleExp != buf.String() { - diffs := diffutil.Diff(roleExp, buf.String()) - t.Fatalf("Expected vs actual differs.\n%v", diffs) - } -} - -func TestRoleClusterScoped(t *testing.T) { - s, buf := setupScaffoldAndWriter() - err := s.Execute(appConfig, &Role{IsClusterScoped: true}) - if err != nil { - t.Fatalf("Failed to execute the scaffold: (%v)", err) - } - - if clusterroleExp != buf.String() { - diffs := diffutil.Diff(clusterroleExp, buf.String()) - t.Fatalf("Expected vs actual differs.\n%v", diffs) - } -} - -func TestRoleCustomRules(t *testing.T) { - s, buf := setupScaffoldAndWriter() - err := s.Execute(appConfig, &Role{ - SkipDefaultRules: true, - CustomRules: []rbacv1.PolicyRule{ - { - APIGroups: []string{"policy"}, - Resources: []string{"poddisruptionbudgets"}, - Verbs: []string{ - "create", - "delete", - "get", - "list", - "patch", - "update", - "watch", - }, - }, - { - APIGroups: []string{"rbac.authorization.k8s.io"}, - Resources: []string{"roles", "rolebindings"}, - Verbs: []string{"get", "list", "watch"}, - }, - }}) - if err != nil { - t.Fatalf("Failed to execute the scaffold: (%v)", err) - } - - if roleCustomRulesExp != buf.String() { - diffs := diffutil.Diff(roleCustomRulesExp, buf.String()) - t.Fatalf("Expected vs actual differs.\n%v", diffs) - } -} - -func TestMergeRoleForResource(t *testing.T) { - clusterRoleFilePath1 := "./testdata/testroles/valid_clusterrole" - clusterRoleFile1 := clusterRoleFilePath1 + "/deploy/role.yaml" - if err := ioutil.WriteFile(clusterRoleFile1, []byte(clusterRole), fileutil.DefaultFileMode); err != nil { - fmt.Printf("failed to instantiate %v: %v", clusterRoleFile1, err) - } - defer remove(clusterRoleFile1) - - roleFilePath1 := "./testdata/testroles/valid_role1" - absRoleFile1 := roleFilePath1 + "/deploy/role.yaml" - if err := ioutil.WriteFile(absRoleFile1, []byte(roleFile1), fileutil.DefaultFileMode); err != nil { - fmt.Printf("failed to instantiate %v: %v", absRoleFile1, err) - } - defer remove(absRoleFile1) - - roleFilePath2 := "./testdata/testroles/valid_role2" - absRoleFile2 := roleFilePath2 + "/deploy/role.yaml" - if err := ioutil.WriteFile(absRoleFile2, []byte(roleFile2), fileutil.DefaultFileMode); err != nil { - fmt.Printf("failed to instantiate %v: %v", absRoleFile2, err) - } - defer remove(absRoleFile2) - - roleFilePath3 := "./testdata/testroles/valid_role3" - absRoleFile3 := roleFilePath3 + "/deploy/role.yaml" - if err := ioutil.WriteFile(absRoleFile3, []byte(roleFile3), fileutil.DefaultFileMode); err != nil { - fmt.Printf("failed to instantiate %v: %v", absRoleFile3, err) - } - defer remove(absRoleFile3) - - roleFilePath4 := "./testdata/testroles/valid_role4" - absRoleFile4 := roleFilePath4 + "/deploy/role.yaml" - if err := ioutil.WriteFile(absRoleFile4, []byte(roleFile3), fileutil.DefaultFileMode); err != nil { - fmt.Printf("failed to instantiate %v: %v", absRoleFile4, err) - } - defer remove(absRoleFile4) - - roleFilePath5 := "./testdata/testroles/valid_role5" - absRoleFile5 := roleFilePath5 + "/deploy/role.yaml" - if err := ioutil.WriteFile(absRoleFile5, []byte(roleFile3), fileutil.DefaultFileMode); err != nil { - fmt.Printf("failed to instantiate %v: %v", absRoleFile5, err) - } - defer remove(absRoleFile5) - - roleFilePath6 := "./testdata/testroles/valid_role6" - absRoleFile6 := roleFilePath6 + "/deploy/role.yaml" - if err := ioutil.WriteFile(absRoleFile6, []byte(roleFile3), fileutil.DefaultFileMode); err != nil { - fmt.Printf("failed to instantiate %v: %v", absRoleFile6, err) - } - defer remove(absRoleFile6) - - testCases := []struct { - name string - absProjectPath string - r *Resource - roleScaffold *Role - expError error - existingRole string - mergedRole string - }{ - { - name: "Valid Basic ClusterRole-1", - absProjectPath: clusterRoleFilePath1, - mergedRole: clusterRoleFilePath1 + "/mergedRole.yaml", - r: &Resource{ - APIVersion: "charts.helm.k8s.io/v1alpha1", - Kind: "Memcached", - FullGroup: "charts.helm.k8s.io", - LowerKind: "memcached", - Resource: "memcacheds", - }, - roleScaffold: &Role{ - IsClusterScoped: false, - SkipDefaultRules: true, - CustomRules: []rbacv1.PolicyRule{ - { - APIGroups: []string{""}, - Resources: []string{"namespaces"}, - Verbs: []string{"get"}, - }, - { - APIGroups: []string{""}, - Resources: []string{"configmaps", "secrets"}, - Verbs: []string{"*"}, - }, - { - APIGroups: []string{""}, - Resources: []string{"events"}, - Verbs: []string{"create"}, - }, - { - APIGroups: []string{""}, - Resources: []string{"services"}, - Verbs: []string{"*"}, - }, - { - APIGroups: []string{"apps"}, - Resources: []string{"statefulsets"}, - Verbs: []string{"*"}, - }, - }, - }, - }, - { - name: "Valid Basic CustomRules-1", - absProjectPath: roleFilePath1, - mergedRole: roleFilePath1 + "/mergedRole.yaml", - r: &Resource{ - APIVersion: "charts.helm.k8s.io/v1alpha1", - Kind: "Memcached", - FullGroup: "charts.helm.k8s.io", - LowerKind: "memcached", - Resource: "memcacheds", - }, - roleScaffold: &Role{ - IsClusterScoped: false, - SkipDefaultRules: true, - CustomRules: []rbacv1.PolicyRule{ - { - APIGroups: []string{""}, - Resources: []string{"namespaces"}, - Verbs: []string{"get"}, - }, - { - APIGroups: []string{""}, - Resources: []string{"configmaps", "secrets"}, - Verbs: []string{"*"}, - }, - { - APIGroups: []string{""}, - Resources: []string{"events"}, - Verbs: []string{"create"}, - }, - { - APIGroups: []string{""}, - Resources: []string{"services"}, - Verbs: []string{"*"}, - }, - { - APIGroups: []string{"apps"}, - Resources: []string{"statefulsets"}, - Verbs: []string{"*"}, - }, - }, - }, - }, - - { - name: "Valid With Options CustomRules-2", - absProjectPath: roleFilePath2, - mergedRole: roleFilePath2 + "/mergedRole.yaml", - r: &Resource{ - APIVersion: "cache.example.com/v1alpha1", - Kind: "Mykind", - FullGroup: "cache.example.com", - LowerKind: "mykind", - Resource: "mykinds", - }, - roleScaffold: &Role{ - IsClusterScoped: false, - SkipDefaultRules: true, - CustomRules: []rbacv1.PolicyRule{ - { - APIGroups: []string{""}, - Resources: []string{"namespaces"}, - Verbs: []string{"get"}, - }, - { - APIGroups: []string{""}, - Resources: []string{"configmaps", "secrets"}, - Verbs: []string{"*"}, - }, - { - APIGroups: []string{""}, - Resources: []string{"events"}, - Verbs: []string{"create"}, - }, - { - APIGroups: []string{""}, - Resources: []string{"serviceaccounts", "services"}, - Verbs: []string{"*"}, - }, - { - APIGroups: []string{"apps"}, - Resources: []string{"deployments"}, - ResourceNames: []string{"helm-demo"}, - NonResourceURLs: []string{"/demos"}, - Verbs: []string{"*"}, - }, - { - APIGroups: []string{"apps"}, - Resources: []string{"deamonsets"}, - Verbs: []string{"delete"}, - ResourceNames: []string{"helm-demo"}, - NonResourceURLs: []string{"/demos", "/helm"}, - }, - }, - }, - }, - { - name: "Valid and differing APIGroups in CustomRules-3", - absProjectPath: roleFilePath3, - mergedRole: roleFilePath3 + "/mergedRole.yaml", - r: &Resource{ - APIVersion: "cache.example.com/v1alpha1", - Kind: "Mykind", - FullGroup: "cache.example.com", - LowerKind: "mykind", - Resource: "mykinds", - }, - roleScaffold: &Role{ - IsClusterScoped: false, - SkipDefaultRules: true, - CustomRules: []rbacv1.PolicyRule{ - { - APIGroups: []string{""}, - Resources: []string{"namespaces"}, - Verbs: []string{"get"}, - }, - { - APIGroups: []string{""}, - Resources: []string{"configmaps", "secrets"}, - Verbs: []string{"*"}, - }, - { - APIGroups: []string{""}, - Resources: []string{"events"}, - Verbs: []string{"create"}, - }, - { - APIGroups: []string{""}, - Resources: []string{"serviceaccounts", "services"}, - Verbs: []string{"*"}, - }, - // Testing vars which differ only in APIGroups with existing role.yaml - { - APIGroups: []string{"apps"}, - Resources: []string{"replicasets", "deployments"}, - ResourceNames: []string{"helm-demo", "sample"}, - NonResourceURLs: []string{"/demos", "/helm"}, - Verbs: []string{"create", "get"}, - }, - }, - }, - }, - { - name: "Valid and differing ResourceNames in CustomRules-4", - absProjectPath: roleFilePath4, - mergedRole: roleFilePath4 + "/mergedRole.yaml", - r: &Resource{ - APIVersion: "cache.example.com/v1alpha1", - Kind: "Mykind", - FullGroup: "cache.example.com", - LowerKind: "mykind", - Resource: "mykinds", - }, - roleScaffold: &Role{ - IsClusterScoped: false, - SkipDefaultRules: true, - CustomRules: []rbacv1.PolicyRule{ - { - APIGroups: []string{""}, - Resources: []string{"namespaces"}, - Verbs: []string{"get"}, - }, - { - APIGroups: []string{""}, - Resources: []string{"configmaps", "secrets"}, - Verbs: []string{"*"}, - }, - { - APIGroups: []string{""}, - Resources: []string{"events"}, - Verbs: []string{"create"}, - }, - { - APIGroups: []string{""}, - Resources: []string{"serviceaccounts", "services"}, - Verbs: []string{"*"}, - }, - // Testing vars which differ only in ResourceNames with existing role.yaml - { - APIGroups: []string{"apps", "samples"}, - Resources: []string{"replicasets", "deployments"}, - ResourceNames: []string{"helm-demo"}, - NonResourceURLs: []string{"/demos", "/helm"}, - Verbs: []string{"create", "get"}, - }, - }, - }, - }, - { - name: "Valid and differing NonResourceURLs in CustomRule-5", - absProjectPath: roleFilePath5, - mergedRole: roleFilePath5 + "/mergedRole.yaml", - r: &Resource{ - APIVersion: "cache.example.com/v1alpha1", - Kind: "Mykind", - FullGroup: "cache.example.com", - LowerKind: "mykind", - Resource: "mykinds", - }, - roleScaffold: &Role{ - IsClusterScoped: false, - SkipDefaultRules: true, - CustomRules: []rbacv1.PolicyRule{ - { - APIGroups: []string{""}, - Resources: []string{"namespaces"}, - Verbs: []string{"get"}, - }, - { - APIGroups: []string{""}, - Resources: []string{"configmaps", "secrets"}, - Verbs: []string{"*"}, - }, - { - APIGroups: []string{""}, - Resources: []string{"events"}, - Verbs: []string{"create"}, - }, - { - APIGroups: []string{""}, - Resources: []string{"serviceaccounts", "services"}, - Verbs: []string{"*"}, - }, - // Testing vars which differ only in NonResourceURLs with existing role.yaml - { - APIGroups: []string{"apps", "samples"}, - Resources: []string{"replicasets", "deployments"}, - ResourceNames: []string{"helm-demo", "sample"}, - NonResourceURLs: []string{"/demos"}, - Verbs: []string{"create", "get"}, - }, - }, - }, - }, - { - name: "Valid and differing Verbs in CustomRules-6", - absProjectPath: roleFilePath6, - mergedRole: roleFilePath6 + "/mergedRole.yaml", - r: &Resource{ - APIVersion: "cache.example.com/v1alpha1", - Kind: "Mykind", - FullGroup: "cache.example.com", - LowerKind: "mykind", - Resource: "mykinds", - }, - roleScaffold: &Role{ - IsClusterScoped: false, - SkipDefaultRules: true, - CustomRules: []rbacv1.PolicyRule{ - { - APIGroups: []string{""}, - Resources: []string{"namespaces"}, - Verbs: []string{"get"}, - }, - { - APIGroups: []string{""}, - Resources: []string{"configmaps", "secrets"}, - Verbs: []string{"*"}, - }, - { - APIGroups: []string{""}, - Resources: []string{"events"}, - Verbs: []string{"create"}, - }, - { - APIGroups: []string{""}, - Resources: []string{"serviceaccounts", "services"}, - Verbs: []string{"*"}, - }, - // Testing vars which differ only in Verbs with existing role.yaml - { - APIGroups: []string{"apps", "samples"}, - Resources: []string{"replicasets", "deployments"}, - ResourceNames: []string{"helm-demo", "sample"}, - NonResourceURLs: []string{"/demos", "/helm"}, - Verbs: []string{"create"}, - }, - }, - }, - }, - { - name: "Invalid ClusterRole", - absProjectPath: roleFilePath1, - r: &Resource{ - APIVersion: "charts.helm.k8s.io/v1alpha1", - Kind: "Nginxingress", - FullGroup: "charts.helm.k8s.io", - LowerKind: "nginx-ingress", - Resource: "nginxingresses", - }, - roleScaffold: &Role{ - IsClusterScoped: true, - SkipDefaultRules: true, - CustomRules: []rbacv1.PolicyRule{ - { - APIGroups: []string{""}, - Resources: []string{"namespaces"}, - Verbs: []string{"get"}, - }, - { - APIGroups: []string{""}, - Resources: []string{"configmaps", "secrets"}, - Verbs: []string{"*"}, - }, - }, - }, - }, - { - name: "Empty CustomRules", - absProjectPath: "./testdata/testroles/invalid_role", - mergedRole: "", - r: &Resource{ - APIVersion: "cache.example.com/v1alpha1", - Kind: "Mykind", - FullGroup: "app.example.com", - }, - roleScaffold: &Role{ - IsClusterScoped: false, - SkipDefaultRules: true, - CustomRules: []rbacv1.PolicyRule{}, - }, - }, - { - name: "Empty role.yaml file", - absProjectPath: "./testdata/testroles/invalid_role", - mergedRole: "", - r: &Resource{ - APIVersion: "cache.example.com/v1alpha1", - Kind: "Mykind", - FullGroup: "app.example.com", - }, - roleScaffold: &Role{ - IsClusterScoped: false, - SkipDefaultRules: true, - CustomRules: []rbacv1.PolicyRule{ - { - APIGroups: []string{""}, - Resources: []string{"namespaces"}, - Verbs: []string{"get"}, - }, - }, - }, - }, - } - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - actualErr := MergeRoleForResource(tc.r, tc.absProjectPath, *tc.roleScaffold) - absFilePath := tc.absProjectPath + "/deploy/role.yaml" - actualMergedRoleYAML, err := ioutil.ReadFile(absFilePath) - if err != nil { - fmt.Printf("failed to read actualMergedrole %v: %v", absFilePath, err) - } - expectedMergedRoleYAML, err := ioutil.ReadFile(tc.mergedRole) - if err != nil { - fmt.Printf("failed to read expectedMergedrole %v: %v", tc.mergedRole, err) - } - - if actualErr != nil { - assert.NotNil(t, actualErr) - } else { - assert.Equal(t, string(expectedMergedRoleYAML), string(actualMergedRoleYAML)) - } - }) - } -} - -// remove removes path from disk. Used in defer statements. -func remove(path string) { - if err := os.RemoveAll(path); err != nil { - log.Fatal(err) - } -} - -const clusterRole = `apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - creationTimestamp: null - name: helm-demo -rules: -- apiGroups: - - "" - resources: - - namespaces - verbs: - - get -- apiGroups: - - "" - resources: - - configmaps - - secrets - verbs: - - '*' -- apiGroups: - - "" - resources: - - events - verbs: - - create -- apiGroups: - - apps - resources: - - deployments - verbs: - - '*' -- apiGroups: - - "" - resources: - - persistentvolumeclaims - - secrets - - services - verbs: - - '*' -- apiGroups: - - monitoring.coreos.com - resources: - - servicemonitors - verbs: - - get - - create -- apiGroups: - - apps - resourceNames: - - helm-demo - resources: - - deployments/finalizers - verbs: - - update -- apiGroups: - - "" - resources: - - pods - verbs: - - get -- apiGroups: - - apps - resources: - - replicasets - - deployments - verbs: - - get -- apiGroups: - - charts.helm.k8s.io - resources: - - '*' - verbs: - - create - - delete - - get - - list - - patch - - update - - watch - -` -const roleFile3 = `apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - creationTimestamp: null - name: helm-demo -rules: -- apiGroups: - - "" - resources: - - namespaces - verbs: - - get -- apiGroups: - - "" - resources: - - configmaps - - secrets - verbs: - - '*' -- apiGroups: - - "" - resources: - - events - verbs: - - create -- apiGroups: - - "" - resources: - - services - verbs: - - '*' -- apiGroups: - - apps - resources: - - statefulsets - verbs: - - '*' -- apiGroups: - - monitoring.coreos.com - resources: - - servicemonitors - verbs: - - get - - create -- apiGroups: - - apps - resourceNames: - - helm-demo - resources: - - deployments/finalizers - verbs: - - update -- apiGroups: - - "" - resources: - - pods - verbs: - - get -- apiGroups: - - apps - - samples - nonResourceURLs: - - /demos - - /helm - resourceNames: - - helm-demo - - sample - resources: - - replicasets - - deployments - verbs: - - create - - get -- apiGroups: - - charts.helm.k8s.io - resources: - - '*' - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -` - -const roleFile1 = `apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - creationTimestamp: null - name: helm-demo -rules: -- apiGroups: - - "" - resources: - - namespaces - verbs: - - get -- apiGroups: - - "" - resources: - - configmaps - - secrets - verbs: - - '*' -- apiGroups: - - "" - resources: - - events - verbs: - - create -- apiGroups: - - apps - resources: - - deployments - verbs: - - '*' -- apiGroups: - - "" - resources: - - persistentvolumeclaims - - secrets - - services - verbs: - - '*' -- apiGroups: - - monitoring.coreos.com - resources: - - servicemonitors - verbs: - - get - - create -- apiGroups: - - apps - resourceNames: - - helm-demo - resources: - - deployments/finalizers - verbs: - - update -- apiGroups: - - "" - resources: - - pods - verbs: - - get -- apiGroups: - - apps - resources: - - replicasets - - deployments - verbs: - - get -- apiGroups: - - charts.helm.k8s.io - resources: - - '*' - verbs: - - create - - delete - - get - - list - - patch - - update - - watch - -` -const roleFile2 = `apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - creationTimestamp: null - name: helm-demo -rules: -- apiGroups: - - "" - resources: - - namespaces - verbs: - - get -- apiGroups: - - "" - resources: - - configmaps - - secrets - verbs: - - '*' -- apiGroups: - - "" - resources: - - events - verbs: - - create -- apiGroups: - - "" - resources: - - services - verbs: - - '*' -- apiGroups: - - apps - resources: - - statefulsets - verbs: - - '*' -- apiGroups: - - monitoring.coreos.com - resources: - - servicemonitors - verbs: - - get - - create -- apiGroups: - - apps - resourceNames: - - helm-demo - resources: - - deployments/finalizers - verbs: - - update -- apiGroups: - - "" - resources: - - pods - verbs: - - get -- apiGroups: - - apps - resources: - - replicasets - - deployments - verbs: - - get -- apiGroups: - - charts.helm.k8s.io - resources: - - '*' - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -` - -const roleExp = `kind: Role -apiVersion: rbac.authorization.k8s.io/v1 -metadata: - name: app-operator -rules: -- apiGroups: - - "" - resources: - - pods - - services - - services/finalizers - - endpoints - - persistentvolumeclaims - - events - - configmaps - - secrets - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - apps - resources: - - deployments - - daemonsets - - replicasets - - statefulsets - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - monitoring.coreos.com - resources: - - servicemonitors - verbs: - - "get" - - "create" -- apiGroups: - - apps - resources: - - deployments/finalizers - resourceNames: - - app-operator - verbs: - - "update" -- apiGroups: - - "" - resources: - - pods - verbs: - - get -- apiGroups: - - apps - resources: - - replicasets - - deployments - verbs: - - get -` - -const clusterroleExp = `kind: ClusterRole -apiVersion: rbac.authorization.k8s.io/v1 -metadata: - name: app-operator -rules: -- apiGroups: - - "" - resources: - - pods - - services - - services/finalizers - - endpoints - - persistentvolumeclaims - - events - - configmaps - - secrets - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - apps - resources: - - deployments - - daemonsets - - replicasets - - statefulsets - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - monitoring.coreos.com - resources: - - servicemonitors - verbs: - - "get" - - "create" -- apiGroups: - - apps - resources: - - deployments/finalizers - resourceNames: - - app-operator - verbs: - - "update" -- apiGroups: - - "" - resources: - - pods - verbs: - - get -- apiGroups: - - apps - resources: - - replicasets - - deployments - verbs: - - get -` - -const roleCustomRulesExp = `kind: Role -apiVersion: rbac.authorization.k8s.io/v1 -metadata: - name: app-operator -rules: -- verbs: - - "create" - - "delete" - - "get" - - "list" - - "patch" - - "update" - - "watch" - apiGroups: - - "policy" - resources: - - "poddisruptionbudgets" -- verbs: - - "get" - - "list" - - "watch" - apiGroups: - - "rbac.authorization.k8s.io" - resources: - - "roles" - - "rolebindings" -- apiGroups: - - monitoring.coreos.com - resources: - - servicemonitors - verbs: - - "get" - - "create" -- apiGroups: - - apps - resources: - - deployments/finalizers - resourceNames: - - app-operator - verbs: - - "update" -- apiGroups: - - "" - resources: - - pods - verbs: - - get -- apiGroups: - - apps - resources: - - replicasets - - deployments - verbs: - - get -` diff --git a/internal/scaffold/testdata/testroles/invalid_role/deploy/role.yaml b/internal/scaffold/testdata/testroles/invalid_role/deploy/role.yaml deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/internal/scaffold/testdata/testroles/valid_clusterrole/mergedRole.yaml b/internal/scaffold/testdata/testroles/valid_clusterrole/mergedRole.yaml deleted file mode 100644 index 40b981c66a5..00000000000 --- a/internal/scaffold/testdata/testroles/valid_clusterrole/mergedRole.yaml +++ /dev/null @@ -1,82 +0,0 @@ -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - creationTimestamp: null - name: helm-demo -rules: -- apiGroups: - - "" - resources: - - namespaces - verbs: - - get -- apiGroups: - - "" - resources: - - configmaps - - secrets - - services - verbs: - - '*' -- apiGroups: - - "" - resources: - - events - verbs: - - create -- apiGroups: - - apps - resources: - - deployments - - statefulsets - verbs: - - '*' -- apiGroups: - - "" - resources: - - persistentvolumeclaims - - secrets - - services - verbs: - - '*' -- apiGroups: - - monitoring.coreos.com - resources: - - servicemonitors - verbs: - - get - - create -- apiGroups: - - apps - resourceNames: - - helm-demo - resources: - - deployments/finalizers - verbs: - - update -- apiGroups: - - "" - resources: - - pods - verbs: - - get -- apiGroups: - - apps - resources: - - replicasets - - deployments - verbs: - - get -- apiGroups: - - charts.helm.k8s.io - resources: - - '*' - - memcacheds - verbs: - - create - - delete - - get - - list - - patch - - update - - watch diff --git a/internal/scaffold/testdata/testroles/valid_role1/mergedRole.yaml b/internal/scaffold/testdata/testroles/valid_role1/mergedRole.yaml deleted file mode 100644 index 1f877177f0c..00000000000 --- a/internal/scaffold/testdata/testroles/valid_role1/mergedRole.yaml +++ /dev/null @@ -1,82 +0,0 @@ -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - creationTimestamp: null - name: helm-demo -rules: -- apiGroups: - - "" - resources: - - namespaces - verbs: - - get -- apiGroups: - - "" - resources: - - configmaps - - secrets - - services - verbs: - - '*' -- apiGroups: - - "" - resources: - - events - verbs: - - create -- apiGroups: - - apps - resources: - - deployments - - statefulsets - verbs: - - '*' -- apiGroups: - - "" - resources: - - persistentvolumeclaims - - secrets - - services - verbs: - - '*' -- apiGroups: - - monitoring.coreos.com - resources: - - servicemonitors - verbs: - - get - - create -- apiGroups: - - apps - resourceNames: - - helm-demo - resources: - - deployments/finalizers - verbs: - - update -- apiGroups: - - "" - resources: - - pods - verbs: - - get -- apiGroups: - - apps - resources: - - replicasets - - deployments - verbs: - - get -- apiGroups: - - charts.helm.k8s.io - resources: - - '*' - - memcacheds - verbs: - - create - - delete - - get - - list - - patch - - update - - watch diff --git a/internal/scaffold/testdata/testroles/valid_role2/mergedRole.yaml b/internal/scaffold/testdata/testroles/valid_role2/mergedRole.yaml deleted file mode 100644 index 705d15ae875..00000000000 --- a/internal/scaffold/testdata/testroles/valid_role2/mergedRole.yaml +++ /dev/null @@ -1,112 +0,0 @@ -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - creationTimestamp: null - name: helm-demo -rules: -- apiGroups: - - "" - resources: - - namespaces - verbs: - - get -- apiGroups: - - "" - resources: - - configmaps - - secrets - - serviceaccounts - - services - verbs: - - '*' -- apiGroups: - - "" - resources: - - events - verbs: - - create -- apiGroups: - - "" - resources: - - services - verbs: - - '*' -- apiGroups: - - apps - resources: - - statefulsets - verbs: - - '*' -- apiGroups: - - monitoring.coreos.com - resources: - - servicemonitors - verbs: - - create - - get -- apiGroups: - - apps - resourceNames: - - helm-demo - resources: - - deployments/finalizers - verbs: - - update -- apiGroups: - - "" - resources: - - pods - verbs: - - get -- apiGroups: - - apps - resources: - - replicasets - - deployments - verbs: - - get -- apiGroups: - - charts.helm.k8s.io - resources: - - '*' - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - apps - nonResourceURLs: - - /demos - resourceNames: - - helm-demo - resources: - - deployments - verbs: - - '*' -- apiGroups: - - apps - nonResourceURLs: - - /demos - - /helm - resourceNames: - - helm-demo - resources: - - deamonsets - verbs: - - delete -- apiGroups: - - cache.example.com - resources: - - '*' - verbs: - - create - - delete - - get - - list - - patch - - update - - watch diff --git a/internal/scaffold/testdata/testroles/valid_role3/mergedRole.yaml b/internal/scaffold/testdata/testroles/valid_role3/mergedRole.yaml deleted file mode 100644 index ebe1cb57bd4..00000000000 --- a/internal/scaffold/testdata/testroles/valid_role3/mergedRole.yaml +++ /dev/null @@ -1,113 +0,0 @@ -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - creationTimestamp: null - name: helm-demo -rules: -- apiGroups: - - "" - resources: - - namespaces - verbs: - - get -- apiGroups: - - "" - resources: - - configmaps - - secrets - - serviceaccounts - - services - verbs: - - '*' -- apiGroups: - - "" - resources: - - events - verbs: - - create -- apiGroups: - - "" - resources: - - services - verbs: - - '*' -- apiGroups: - - apps - resources: - - statefulsets - verbs: - - '*' -- apiGroups: - - monitoring.coreos.com - resources: - - servicemonitors - verbs: - - create - - get -- apiGroups: - - apps - resourceNames: - - helm-demo - resources: - - deployments/finalizers - verbs: - - update -- apiGroups: - - "" - resources: - - pods - verbs: - - get -- apiGroups: - - apps - - samples - nonResourceURLs: - - /demos - - /helm - resourceNames: - - helm-demo - - sample - resources: - - replicasets - - deployments - verbs: - - create - - get -- apiGroups: - - charts.helm.k8s.io - resources: - - '*' - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - apps - nonResourceURLs: - - /demos - - /helm - resourceNames: - - helm-demo - - sample - resources: - - replicasets - - deployments - verbs: - - create - - get -- apiGroups: - - cache.example.com - resources: - - '*' - verbs: - - create - - delete - - get - - list - - patch - - update - - watch diff --git a/internal/scaffold/testdata/testroles/valid_role4/mergedRole.yaml b/internal/scaffold/testdata/testroles/valid_role4/mergedRole.yaml deleted file mode 100644 index c0629d8a2a8..00000000000 --- a/internal/scaffold/testdata/testroles/valid_role4/mergedRole.yaml +++ /dev/null @@ -1,113 +0,0 @@ -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - creationTimestamp: null - name: helm-demo -rules: -- apiGroups: - - "" - resources: - - namespaces - verbs: - - get -- apiGroups: - - "" - resources: - - configmaps - - secrets - - serviceaccounts - - services - verbs: - - '*' -- apiGroups: - - "" - resources: - - events - verbs: - - create -- apiGroups: - - "" - resources: - - services - verbs: - - '*' -- apiGroups: - - apps - resources: - - statefulsets - verbs: - - '*' -- apiGroups: - - monitoring.coreos.com - resources: - - servicemonitors - verbs: - - create - - get -- apiGroups: - - apps - resourceNames: - - helm-demo - resources: - - deployments/finalizers - verbs: - - update -- apiGroups: - - "" - resources: - - pods - verbs: - - get -- apiGroups: - - apps - - samples - nonResourceURLs: - - /demos - - /helm - resourceNames: - - helm-demo - - sample - resources: - - replicasets - - deployments - verbs: - - create - - get -- apiGroups: - - charts.helm.k8s.io - resources: - - '*' - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - apps - - samples - nonResourceURLs: - - /demos - - /helm - resourceNames: - - helm-demo - resources: - - replicasets - - deployments - verbs: - - create - - get -- apiGroups: - - cache.example.com - resources: - - '*' - verbs: - - create - - delete - - get - - list - - patch - - update - - watch diff --git a/internal/scaffold/testdata/testroles/valid_role5/mergedRole.yaml b/internal/scaffold/testdata/testroles/valid_role5/mergedRole.yaml deleted file mode 100644 index a1ac9f0fccb..00000000000 --- a/internal/scaffold/testdata/testroles/valid_role5/mergedRole.yaml +++ /dev/null @@ -1,113 +0,0 @@ -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - creationTimestamp: null - name: helm-demo -rules: -- apiGroups: - - "" - resources: - - namespaces - verbs: - - get -- apiGroups: - - "" - resources: - - configmaps - - secrets - - serviceaccounts - - services - verbs: - - '*' -- apiGroups: - - "" - resources: - - events - verbs: - - create -- apiGroups: - - "" - resources: - - services - verbs: - - '*' -- apiGroups: - - apps - resources: - - statefulsets - verbs: - - '*' -- apiGroups: - - monitoring.coreos.com - resources: - - servicemonitors - verbs: - - create - - get -- apiGroups: - - apps - resourceNames: - - helm-demo - resources: - - deployments/finalizers - verbs: - - update -- apiGroups: - - "" - resources: - - pods - verbs: - - get -- apiGroups: - - apps - - samples - nonResourceURLs: - - /demos - - /helm - resourceNames: - - helm-demo - - sample - resources: - - replicasets - - deployments - verbs: - - create - - get -- apiGroups: - - charts.helm.k8s.io - resources: - - '*' - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - apps - - samples - nonResourceURLs: - - /demos - resourceNames: - - helm-demo - - sample - resources: - - replicasets - - deployments - verbs: - - create - - get -- apiGroups: - - cache.example.com - resources: - - '*' - verbs: - - create - - delete - - get - - list - - patch - - update - - watch diff --git a/internal/scaffold/testdata/testroles/valid_role6/mergedRole.yaml b/internal/scaffold/testdata/testroles/valid_role6/mergedRole.yaml deleted file mode 100644 index d4809876bae..00000000000 --- a/internal/scaffold/testdata/testroles/valid_role6/mergedRole.yaml +++ /dev/null @@ -1,113 +0,0 @@ -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - creationTimestamp: null - name: helm-demo -rules: -- apiGroups: - - "" - resources: - - namespaces - verbs: - - get -- apiGroups: - - "" - resources: - - configmaps - - secrets - - serviceaccounts - - services - verbs: - - '*' -- apiGroups: - - "" - resources: - - events - verbs: - - create -- apiGroups: - - "" - resources: - - services - verbs: - - '*' -- apiGroups: - - apps - resources: - - statefulsets - verbs: - - '*' -- apiGroups: - - monitoring.coreos.com - resources: - - servicemonitors - verbs: - - create - - get -- apiGroups: - - apps - resourceNames: - - helm-demo - resources: - - deployments/finalizers - verbs: - - update -- apiGroups: - - "" - resources: - - pods - verbs: - - get -- apiGroups: - - apps - - samples - nonResourceURLs: - - /demos - - /helm - resourceNames: - - helm-demo - - sample - resources: - - replicasets - - deployments - verbs: - - create - - get -- apiGroups: - - charts.helm.k8s.io - resources: - - '*' - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - apps - - samples - nonResourceURLs: - - /demos - - /helm - resourceNames: - - helm-demo - - sample - resources: - - replicasets - - deployments - verbs: - - create -- apiGroups: - - cache.example.com - resources: - - '*' - verbs: - - create - - delete - - get - - list - - patch - - update - - watch diff --git a/internal/util/projutil/project_util.go b/internal/util/projutil/project_util.go index d4845a404ec..fe3457a07dc 100644 --- a/internal/util/projutil/project_util.go +++ b/internal/util/projutil/project_util.go @@ -42,7 +42,6 @@ const ( rolesDir = "roles" requirementsFile = "requirements.yml" moleculeDir = "molecule" - helmChartsDir = "helm-charts" goModFile = "go.mod" defaultPermission = 0644 @@ -262,9 +261,7 @@ func IsOperatorAnsible() bool { } // IsOperatorHelm returns true when the layout field in PROJECT file has the Helm prefix key. -// NOTE: For the legacy, returns true when the project contains the helm-charts directory. func IsOperatorHelm() bool { - // If the project has the new layout we will check the type in the config file if kbutil.HasProjectFile() { cfg, err := kbutil.ReadConfig() if err != nil { @@ -272,9 +269,7 @@ func IsOperatorHelm() bool { } return PluginKeyToOperatorType(cfg.Layout) == OperatorTypeHelm } - // todo(camilamacedo86): remove when the legacy layout is no longer supported - stat, err := os.Stat(helmChartsDir) - return (err == nil && stat.IsDir()) || os.IsExist(err) + return false } // MustSetWdGopath sets GOPATH to the first element of the path list in diff --git a/website/content/en/docs/cli/operator-sdk_add_api.md b/website/content/en/docs/cli/operator-sdk_add_api.md index 650be29cf65..863e28c18a9 100644 --- a/website/content/en/docs/cli/operator-sdk_add_api.md +++ b/website/content/en/docs/cli/operator-sdk_add_api.md @@ -24,12 +24,7 @@ For Ansible-based operators: - Creates resource folder under /roles. - watches.yaml is updated with new resource. - deploy/role.yaml will be updated with apiGroup for new API. - -For Helm-based operators: - - Creates resource folder under /helm-charts. - - watches.yaml is updated with new resource. - - deploy/role.yaml will be updated to reflact new rules for the incoming API. - + CRD's are generated, or updated if they exist for a particular group + version + kind, under deploy/crds/__crd.yaml; OpenAPI V3 validation YAML is generated as a 'validation' object. @@ -50,51 +45,16 @@ operator-sdk add api [flags] --api-version=app.example.com/v1alpha1 \ --kind=AppService -# Helm Example: - $ operator-sdk add api \ - --api-version=app.example.com/v1alpha1 \ - --kind=AppService - - $ operator-sdk add api \ - --api-version=app.example.com/v1alpha1 \ - --kind=AppService - --helm-chart=myrepo/app - - $ operator-sdk add api \ - --helm-chart=myrepo/app - - $ operator-sdk add api \ - --helm-chart=myrepo/app \ - --helm-chart-version=1.2.3 - - $ operator-sdk add api \ - --helm-chart=app \ - --helm-chart-repo=https://charts.mycompany.com/ - - $ operator-sdk add api \ - --helm-chart=app \ - --helm-chart-repo=https://charts.mycompany.com/ \ - --helm-chart-version=1.2.3 - - $ operator-sdk add api \ - --helm-chart=/path/to/local/chart-directories/app/ - - $ operator-sdk add api \ - --helm-chart=/path/to/local/chart-archives/app-1.2.3.tgz - ``` ### Options ``` - --api-version string Kubernetes apiVersion and has a format of $GROUP_NAME/$VERSION (e.g app.example.com/v1alpha1) - --crd-version string CRD version to generate (default "v1") - --helm-chart string Initialize helm operator with existing helm chart (, /, or local path). Valid only for --type helm - --helm-chart-repo string Chart repository URL for the requested helm chart, Valid only for --type helm - --helm-chart-version string Specific version of the helm chart (default is latest version). Valid only for --type helm - -h, --help help for api - --kind string Kubernetes resource Kind name. (e.g AppService) - --skip-generation Skip generation of deepcopy and OpenAPI code and OpenAPI CRD specs + --api-version string Kubernetes apiVersion and has a format of $GROUP_NAME/$VERSION (e.g app.example.com/v1alpha1) + --crd-version string CRD version to generate (default "v1") + -h, --help help for api + --kind string Kubernetes resource Kind name. (e.g AppService) + --skip-generation Skip generation of deepcopy and OpenAPI code and OpenAPI CRD specs ``` ### SEE ALSO diff --git a/website/content/en/docs/cli/operator-sdk_new.md b/website/content/en/docs/cli/operator-sdk_new.md index 70cda0ab7ac..f660d3746b2 100644 --- a/website/content/en/docs/cli/operator-sdk_new.md +++ b/website/content/en/docs/cli/operator-sdk_new.md @@ -29,53 +29,17 @@ operator-sdk new [flags] --api-version=app.example.com/v1alpha1 \ --kind=AppService - # Helm project - $ operator-sdk new app-operator --type=helm \ - --api-version=app.example.com/v1alpha1 \ - --kind=AppService - - $ operator-sdk new app-operator --type=helm \ - --api-version=app.example.com/v1alpha1 \ - --kind=AppService \ - --helm-chart=myrepo/app - - $ operator-sdk new app-operator --type=helm \ - --helm-chart=myrepo/app - - $ operator-sdk new app-operator --type=helm \ - --helm-chart=myrepo/app \ - --helm-chart-version=1.2.3 - - $ operator-sdk new app-operator --type=helm \ - --helm-chart=app \ - --helm-chart-repo=https://charts.mycompany.com/ - - $ operator-sdk new app-operator --type=helm \ - --helm-chart=app \ - --helm-chart-repo=https://charts.mycompany.com/ \ - --helm-chart-version=1.2.3 - - $ operator-sdk new app-operator --type=helm \ - --helm-chart=/path/to/local/chart-directories/app/ - - $ operator-sdk new app-operator --type=helm \ - --helm-chart=/path/to/local/chart-archives/app-1.2.3.tgz - ``` ### Options ``` - --api-version string Kubernetes apiVersion and has a format of $GROUP_NAME/$VERSION (e.g app.example.com/v1alpha1) - --crd-version string CRD version to generate (default "v1") - --generate-playbook Generate a playbook skeleton. (Only used for --type ansible) - --helm-chart string Initialize helm operator with existing helm chart (, /, or local path). Valid only for --type helm - --helm-chart-repo string Chart repository URL for the requested helm chart, Valid only for --type helm - --helm-chart-version string Specific version of the helm chart (default is latest version). Valid only for --type helm - -h, --help help for new - --kind string Kubernetes resource Kind name. (e.g AppService) - --skip-generation Skip generation of deepcopy and OpenAPI code and OpenAPI CRD specs - --type string Type of operator to initialize (choices: "ansible" or "helm") + --api-version string Kubernetes apiVersion and has a format of $GROUP_NAME/$VERSION (e.g app.example.com/v1alpha1) + --crd-version string CRD version to generate (default "v1") + --generate-playbook Generate a playbook skeleton. (Only used for --type ansible) + -h, --help help for new + --kind string Kubernetes resource Kind name. (e.g AppService) + --skip-generation Skip generation of deepcopy and OpenAPI code and OpenAPI CRD specs ``` ### SEE ALSO diff --git a/website/content/en/docs/new-cli/operator-sdk_new.md b/website/content/en/docs/new-cli/operator-sdk_new.md index 00d7dd00e8c..7e0c2ec3a14 100644 --- a/website/content/en/docs/new-cli/operator-sdk_new.md +++ b/website/content/en/docs/new-cli/operator-sdk_new.md @@ -29,53 +29,17 @@ operator-sdk new [flags] --api-version=app.example.com/v1alpha1 \ --kind=AppService - # Helm project - $ operator-sdk new app-operator --type=helm \ - --api-version=app.example.com/v1alpha1 \ - --kind=AppService - - $ operator-sdk new app-operator --type=helm \ - --api-version=app.example.com/v1alpha1 \ - --kind=AppService \ - --helm-chart=myrepo/app - - $ operator-sdk new app-operator --type=helm \ - --helm-chart=myrepo/app - - $ operator-sdk new app-operator --type=helm \ - --helm-chart=myrepo/app \ - --helm-chart-version=1.2.3 - - $ operator-sdk new app-operator --type=helm \ - --helm-chart=app \ - --helm-chart-repo=https://charts.mycompany.com/ - - $ operator-sdk new app-operator --type=helm \ - --helm-chart=app \ - --helm-chart-repo=https://charts.mycompany.com/ \ - --helm-chart-version=1.2.3 - - $ operator-sdk new app-operator --type=helm \ - --helm-chart=/path/to/local/chart-directories/app/ - - $ operator-sdk new app-operator --type=helm \ - --helm-chart=/path/to/local/chart-archives/app-1.2.3.tgz - ``` ### Options ``` - --api-version string Kubernetes apiVersion and has a format of $GROUP_NAME/$VERSION (e.g app.example.com/v1alpha1) - --crd-version string CRD version to generate (default "v1") - --generate-playbook Generate a playbook skeleton. (Only used for --type ansible) - --helm-chart string Initialize helm operator with existing helm chart (, /, or local path). Valid only for --type helm - --helm-chart-repo string Chart repository URL for the requested helm chart, Valid only for --type helm - --helm-chart-version string Specific version of the helm chart (default is latest version). Valid only for --type helm - -h, --help help for new - --kind string Kubernetes resource Kind name. (e.g AppService) - --skip-generation Skip generation of deepcopy and OpenAPI code and OpenAPI CRD specs - --type string Type of operator to initialize (choices: "ansible" or "helm") + --api-version string Kubernetes apiVersion and has a format of $GROUP_NAME/$VERSION (e.g app.example.com/v1alpha1) + --crd-version string CRD version to generate (default "v1") + --generate-playbook Generate a playbook skeleton. (Only used for --type ansible) + -h, --help help for new + --kind string Kubernetes resource Kind name. (e.g AppService) + --skip-generation Skip generation of deepcopy and OpenAPI code and OpenAPI CRD specs ``` ### Options inherited from parent commands