diff --git a/.travis.yml b/.travis.yml index faf4a22dfec..071b7226cb0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -51,9 +51,9 @@ x_base_steps: - make setup-k8s - export KUBECONFIG="$(kind get kubeconfig-path --name="kind")" after_success: - - echo "Build succeeded, operator was generated, memcached operator is running on $CLUSTER, and unit/integration tests pass" + - echo "Tests passed" after_failure: - - echo "Build failed, operator failed to generate, memcached operator is not running on $CLUSTER, or unit/integration tests failed" + - echo "Tests failed" - kubectl get all --all-namespaces - kubectl get events --all-namespaces --field-selector=type=Warning services: @@ -132,9 +132,9 @@ jobs: name: Subcommands on Kubernetes script: make test-subcommand - # Build and test go for legacy project layouts + # Build and test go - <<: *test - name: Go for legacy project layouts on Kubernetes + name: Go on Kubernetes before_script: - (cd / && go get github.com/mattn/goveralls) script: @@ -144,23 +144,6 @@ jobs: after_success: - $GOPATH/bin/goveralls -service=travis-ci -coverprofile=coverage.out -repotoken=$COVERALLS_TOKEN - # Build and test go for new project layouts - - name: Go e2e tests for new project layouts - before_install: - # hack/ci/check-doc-only-update.sh needs to be sourced so - # that it can properly exit the test early with success - - source hack/ci/check-doc-only-update.sh - script: - - make test-e2e-go-new - after_success: - - echo "E2E tests passed" - after_failure: - - echo "E2E tests failed" - - kubectl get all --all-namespaces - - kubectl get events --all-namespaces --field-selector=type=Warning - services: - - docker - # Build and test helm - <<: *test name: Helm on Kubernetes @@ -168,7 +151,6 @@ jobs: ## Image deploy/push stage jobs ## - # Build and deploy arm64 ansible-operator docker image - stage: deploy <<: *deploy @@ -234,38 +216,6 @@ jobs: - make image-build-helm - make image-push-helm - # Build and deploy arm64 scorecard-proxy docker image - - <<: *deploy - name: Docker image for scorecard-proxy (arm64) - arch: arm64 - script: - - make image-build-scorecard-proxy - - make image-push-scorecard-proxy - - # Build and deploy amd64 scorecard-proxy docker image - - <<: *deploy - name: Docker image for scorecard-proxy (amd64) - arch: amd64 - script: - - make image-build-scorecard-proxy - - make image-push-scorecard-proxy - - # Build and deploy ppc64le scorecard-proxy docker image - - <<: *deploy - name: Docker image for scorecard-proxy (ppc64le) - arch: ppc64le - script: - - make image-build-scorecard-proxy - - make image-push-scorecard-proxy - - # Build and deploy s390x scorecard-proxy docker image - - <<: *deploy - name: Docker image for scorecard-proxy (s390x) - arch: s390x - script: - - make image-build-scorecard-proxy - - make image-push-scorecard-proxy - # Build and deploy arm64 scorecard-test docker image - <<: *deploy name: Docker image for scorecard-test (arm64) @@ -319,12 +269,6 @@ jobs: script: - make image-push-helm-multiarch - # Build and deploy scorecard-proxy multi-arch manifest list - - <<: *manifest-deploy - name: Manifest list for scorecard-proxy - script: - - make image-push-scorecard-proxy-multiarch - # Build and deploy scorecard-test multi-arch manifest list - <<: *manifest-deploy name: Manifest list for scorecard-test diff --git a/Makefile b/Makefile index cba10692dca..3e9be6e2d5c 100644 --- a/Makefile +++ b/Makefile @@ -29,19 +29,16 @@ GO_BUILD_ARGS = \ ANSIBLE_BASE_IMAGE = quay.io/operator-framework/ansible-operator HELM_BASE_IMAGE = quay.io/operator-framework/helm-operator -SCORECARD_PROXY_BASE_IMAGE = quay.io/operator-framework/scorecard-proxy SCORECARD_TEST_BASE_IMAGE = quay.io/operator-framework/scorecard-test SCORECARD_TEST_KUTTL_BASE_IMAGE = quay.io/operator-framework/scorecard-test-kuttl ANSIBLE_IMAGE ?= $(ANSIBLE_BASE_IMAGE) HELM_IMAGE ?= $(HELM_BASE_IMAGE) -SCORECARD_PROXY_IMAGE ?= $(SCORECARD_PROXY_BASE_IMAGE) SCORECARD_TEST_IMAGE ?= $(SCORECARD_TEST_BASE_IMAGE) SCORECARD_TEST_KUTTL_IMAGE ?= $(SCORECARD_TEST_KUTTL_BASE_IMAGE) ANSIBLE_ARCHES:="amd64" "ppc64le" "s390x" "arm64" HELM_ARCHES:="amd64" "ppc64le" "s390x" "arm64" -SCORECARD_PROXY_ARCHES:="amd64" "ppc64le" "s390x" "arm64" SCORECARD_TEST_ARCHES:="amd64" "ppc64le" "s390x" "arm64" SCORECARD_TEST_KUTTL_ARCHES:="amd64" "ppc64le" "s390x" "arm64" @@ -185,9 +182,9 @@ build/%.asc: ## Create release signatures for operator-sdk release binaries image: image-build image-push ## Build and push all images -image-build: image-build-ansible image-build-helm image-build-scorecard-proxy image-build-scorecard-test image-build-scorecard-test-kuttl## Build all images +image-build: image-build-ansible image-build-helm image-build-scorecard-test image-build-scorecard-test-kuttl## Build all images -image-push: image-push-ansible image-push-helm image-push-scorecard-proxy image-push-scorecard-test ## Push all images +image-push: image-push-ansible image-push-helm image-push-scorecard-test ## Push all images # Ansible operator image scaffold/build/push. .PHONY: image-scaffold-ansible image-build-ansible image-push-ansible image-push-ansible-multiarch @@ -219,18 +216,6 @@ image-push-helm: image-push-helm-multiarch: ./hack/image/push-manifest-list.sh $(HELM_IMAGE) ${HELM_ARCHES} -# Scorecard proxy image scaffold/build/push. -.PHONY: image-build-scorecard-proxy image-push-scorecard-proxy image-push-scorecard-proxy-multiarch - -image-build-scorecard-proxy: - ./hack/image/build-scorecard-proxy-image.sh $(SCORECARD_PROXY_BASE_IMAGE):dev - -image-push-scorecard-proxy: - ./hack/image/push-image-tags.sh $(SCORECARD_PROXY_BASE_IMAGE):dev $(SCORECARD_PROXY_IMAGE)-$(shell go env GOARCH) - -image-push-scorecard-proxy-multiarch: - ./hack/image/push-manifest-list.sh $(SCORECARD_PROXY_IMAGE) ${SCORECARD_PROXY_ARCHES} - # Scorecard test image scaffold/build/push. .PHONY: image-build-scorecard-test image-push-scorecard-test image-push-scorecard-test-multiarch @@ -281,31 +266,25 @@ test-links: test-ci: test-sanity test-unit install test-subcommand test-e2e ## Run the CI test suite # Subcommand tests. -.PHONY: test-subcommand test-subcommand-local test-subcommand-scorecard test-subcommand-olm-install +.PHONY: test-subcommand test-subcommand-local test-subcommand-olm-install -test-subcommand: test-subcommand-local test-subcommand-scorecard test-subcommand-olm-install +test-subcommand: test-subcommand-local test-subcommand-olm-install ./hack/tests/subcommand-bundle.sh ./hack/tests/subcommand-generate-csv.sh test-subcommand-local: ./hack/tests/subcommand.sh -test-subcommand-scorecard: - ./hack/tests/subcommand-scorecard.sh - test-subcommand-olm-install: ./hack/tests/subcommand-olm-install.sh # E2E tests. -.PHONY: test-e2e test-e2e-go test-e2e-go-new test-e2e-ansible test-e2e-ansible-molecule test-e2e-helm +.PHONY: test-e2e test-e2e-go test-e2e-ansible test-e2e-ansible-molecule test-e2e-helm -test-e2e: test-e2e-go test-e2e-go-new test-e2e-ansible test-e2e-ansible-molecule test-e2e-helm ## Run the e2e tests +test-e2e: test-e2e-go test-e2e-ansible test-e2e-ansible-molecule test-e2e-helm ## Run the e2e tests test-e2e-go: - ./hack/tests/e2e-go.sh $(ARGS) - -test-e2e-go-new: - K8S_VERSION=$(K8S_VERSION) ./hack/tests/e2e-go-new.sh + ./hack/tests/e2e-go.sh test-e2e-ansible: image-build-ansible ./hack/tests/e2e-ansible.sh diff --git a/cmd/operator-sdk/add/api.go b/cmd/operator-sdk/add/api.go index 14bf9c68776..a34ad376437 100644 --- a/cmd/operator-sdk/add/api.go +++ b/cmd/operator-sdk/add/api.go @@ -16,7 +16,6 @@ package add import ( "fmt" - "io/ioutil" "os" "path/filepath" @@ -131,9 +130,7 @@ func apiRun(cmd *cobra.Command, args []string) error { switch operatorType { case projutil.OperatorTypeGo: - if err := doGoAPIScaffold(); err != nil { - return err - } + return fmt.Errorf("the `add api` command is not supported for Go operators") case projutil.OperatorTypeAnsible: if err := doAnsibleAPIScaffold(); err != nil { return err @@ -218,84 +215,3 @@ func doAnsibleAPIScaffold() error { } return nil } - -// TODO -// Consolidate scaffold func to be used by both "new" and "add api" commands. -func doGoAPIScaffold() error { - - // Create and validate new resource. - r, err := scaffold.NewResource(apiFlags.APIVersion, apiFlags.Kind) - if err != nil { - return err - } - - absProjectPath := projutil.MustGetwd() - - cfg := &input.Config{ - Repo: projutil.GetGoPkg(), - AbsProjectPath: absProjectPath, - } - s := &scaffold.Scaffold{} - - // Check if any package files for this API group dir exist, and if not - // scaffold a group.go to prevent erroneous gengo parse errors. - group := &scaffold.Group{Resource: r} - if err := scaffoldIfNoPkgFileExists(s, cfg, group); err != nil { - log.Fatalf("Failed to scaffold group file: %v", err) - } - - err = s.Execute(cfg, - &scaffold.Types{Resource: r}, - &scaffold.AddToScheme{Resource: r}, - &scaffold.Register{Resource: r}, - &scaffold.Doc{Resource: r}, - &scaffold.CR{Resource: r}, - ) - if err != nil { - log.Fatalf("API scaffold failed: %v", err) - } - - // update deploy/role.yaml for the given resource r. - if err := scaffold.UpdateRoleForResource(r, absProjectPath); err != nil { - log.Fatalf("Failed to update the RBAC manifest for the resource (%v, %v): (%v)", - r.APIVersion, r.Kind, err) - } - - if !apiFlags.SkipGeneration { - // Run k8s codegen for deepcopy - if err := genutil.K8sCodegen(); err != nil { - log.Fatal(err) - } - - // Generate a validation spec for the new CRD. - if err := genutil.CRDGen(apiFlags.CrdVersion); err != nil { - log.Fatal(err) - } - } - - log.Info("API generation complete.") - return nil -} - -// scaffoldIfNoPkgFileExists executes f using s and cfg if no go files -// in f's directory exist. -func scaffoldIfNoPkgFileExists(s *scaffold.Scaffold, cfg *input.Config, f input.File) error { - i, err := f.GetInput() - if err != nil { - return fmt.Errorf("error getting file %s input: %v", i.Path, err) - } - groupDir := filepath.Dir(i.Path) - gdInfos, err := ioutil.ReadDir(groupDir) - if err != nil && !os.IsNotExist(err) { - return fmt.Errorf("error reading dir %s: %v", groupDir, err) - } - if err == nil { - for _, info := range gdInfos { - if !info.IsDir() && filepath.Ext(info.Name()) == ".go" { - return nil - } - } - } - // err must be a non-existence error or no go files exist, so execute f. - return s.Execute(cfg, f) -} diff --git a/cmd/operator-sdk/add/cmd.go b/cmd/operator-sdk/add/cmd.go index d9f689e2838..51aa932978c 100644 --- a/cmd/operator-sdk/add/cmd.go +++ b/cmd/operator-sdk/add/cmd.go @@ -26,7 +26,6 @@ func NewCmd() *cobra.Command { } cmd.AddCommand(newAddAPICmd()) - cmd.AddCommand(newAddControllerCmd()) cmd.AddCommand(newAddCRDCmd()) return cmd } diff --git a/cmd/operator-sdk/add/controller.go b/cmd/operator-sdk/add/controller.go deleted file mode 100644 index 9360401ac78..00000000000 --- a/cmd/operator-sdk/add/controller.go +++ /dev/null @@ -1,122 +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 add - -import ( - "github.com/operator-framework/operator-sdk/internal/scaffold" - "github.com/operator-framework/operator-sdk/internal/scaffold/input" - "github.com/operator-framework/operator-sdk/internal/util/projutil" - - log "github.com/sirupsen/logrus" - "github.com/spf13/cobra" -) - -var ( - customAPIImport string - apiVersion string - kind string - crdVersion string -) - -func newAddControllerCmd() *cobra.Command { - controllerCmd := &cobra.Command{ - Use: "controller", - Short: "Adds a new controller pkg", - Long: ` -Add a new controller package to your operator project. - -This command creates a new controller package under pkg/controller/ that, by default, reconciles on a custom resource for the specified apiversion and kind. The controller will expect to use the custom resource type that should already be defined under pkg/apis// via the "operator-sdk add api" command. - -Note that, if the controller pkg for that Kind already exists at pkg/controller/ then the command will not overwrite and return an error. - -This command MUST be run from the project root directory.`, - - Example: ` -The following example will create a controller to manage, watch and reconcile as primary resource the from the domain . - -Example: - - $ operator-sdk add controller --api-version=app.example.com/v1 --kind=AppService - $ tree pkg/controller - pkg/controller/ - ├── add_appservice.go - ├── appservice - │ └── appservice_controller.go - └── controller.go - -The following example will create a controller to manage, watch and reconcile as a primary resource the from the domain , which is not defined in the project (external). Note that, it can be used to create controllers for any External API. - -Example: - - $ operator-sdk add controller --api-version=k8s.io.api/v1 --kind=Deployment --custom-api-import=k8s.io/api/apps - $ tree pkg/controller - pkg/controller/ - ├── add_deployment.go - ├── deployment - │ └── deployment_controller.go - └── controller.go - `, - RunE: controllerRun, - } - - controllerCmd.Flags().StringVar(&apiVersion, "api-version", "", - "Kubernetes APIVersion that has a format of $GROUP_NAME/$VERSION (e.g app.example.com/v1alpha1)") - if err := controllerCmd.MarkFlagRequired("api-version"); err != nil { - log.Fatalf("Failed to mark `api-version` flag for `add controller` subcommand as required") - } - controllerCmd.Flags().StringVar(&kind, "kind", "", - "Kubernetes resource Kind name. (e.g AppService)") - if err := controllerCmd.MarkFlagRequired("kind"); err != nil { - log.Fatalf("Failed to mark `kind` flag for `add controller` subcommand as required") - } - controllerCmd.Flags().StringVar(&customAPIImport, "custom-api-import", "", - `The External API import path of the form "host.com/repo/path[=import_identifier]" Note that import_identifier is optional. ( E.g. --custom-api-import=k8s.io/api/apps )`) - - return controllerCmd -} - -func controllerRun(cmd *cobra.Command, args []string) error { - projutil.MustInProjectRoot() - - // Only Go projects can add controllers. - if err := projutil.CheckGoProjectCmd(cmd); err != nil { - return err - } - - log.Infof("Generating controller version %s for kind %s.", apiVersion, kind) - - // Create and validate new resource - r, err := scaffold.NewResource(apiVersion, kind) - if err != nil { - log.Fatal(err) - } - - cfg := &input.Config{ - Repo: projutil.GetGoPkg(), - AbsProjectPath: projutil.MustGetwd(), - } - s := &scaffold.Scaffold{} - - err = s.Execute(cfg, - &scaffold.ControllerKind{Resource: r, CustomImport: customAPIImport}, - &scaffold.AddController{Resource: r}, - ) - if err != nil { - log.Fatalf("Controller scaffold failed: %v", err) - } - - log.Info("Controller generation complete.") - return nil -} diff --git a/cmd/operator-sdk/add/crd.go b/cmd/operator-sdk/add/crd.go index d28e54c3857..9f4628c57b1 100644 --- a/cmd/operator-sdk/add/crd.go +++ b/cmd/operator-sdk/add/crd.go @@ -20,13 +20,19 @@ import ( "path/filepath" "strings" + log "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + gencrd "github.com/operator-framework/operator-sdk/internal/generate/crd" "github.com/operator-framework/operator-sdk/internal/scaffold" "github.com/operator-framework/operator-sdk/internal/scaffold/input" "github.com/operator-framework/operator-sdk/internal/util/projutil" +) - log "github.com/sirupsen/logrus" - "github.com/spf13/cobra" +var ( + apiVersion string + kind string + crdVersion string ) // newAddCRDCmd - add crd command diff --git a/cmd/operator-sdk/cli/legacy.go b/cmd/operator-sdk/cli/legacy.go index d1c84e0c47d..fa235adcebe 100644 --- a/cmd/operator-sdk/cli/legacy.go +++ b/cmd/operator-sdk/cli/legacy.go @@ -15,6 +15,10 @@ package cli import ( + log "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + "github.com/spf13/viper" + "github.com/operator-framework/operator-sdk/cmd/operator-sdk/add" "github.com/operator-framework/operator-sdk/cmd/operator-sdk/alpha" "github.com/operator-framework/operator-sdk/cmd/operator-sdk/build" @@ -22,20 +26,13 @@ import ( "github.com/operator-framework/operator-sdk/cmd/operator-sdk/cleanup" "github.com/operator-framework/operator-sdk/cmd/operator-sdk/completion" "github.com/operator-framework/operator-sdk/cmd/operator-sdk/generate" - "github.com/operator-framework/operator-sdk/cmd/operator-sdk/migrate" "github.com/operator-framework/operator-sdk/cmd/operator-sdk/new" "github.com/operator-framework/operator-sdk/cmd/operator-sdk/olm" - "github.com/operator-framework/operator-sdk/cmd/operator-sdk/printdeps" "github.com/operator-framework/operator-sdk/cmd/operator-sdk/run" - "github.com/operator-framework/operator-sdk/cmd/operator-sdk/scorecard" "github.com/operator-framework/operator-sdk/cmd/operator-sdk/test" "github.com/operator-framework/operator-sdk/cmd/operator-sdk/version" "github.com/operator-framework/operator-sdk/internal/flags" "github.com/operator-framework/operator-sdk/internal/util/projutil" - - log "github.com/sirupsen/logrus" - "github.com/spf13/cobra" - "github.com/spf13/viper" ) func RunLegacy() error { @@ -76,41 +73,16 @@ func GetCLIRoot() *cobra.Command { cleanup.NewCmdLegacy(), completion.NewCmd(), generate.NewCmdLegacy(), - migrate.NewCmd(), new.NewCmd(), olm.NewCmd(), - printdeps.NewCmd(), run.NewCmdLegacy(), - scorecard.NewCmd(), test.NewCmd(), version.NewCmd(), ) - // The "init" cmd is is added as a hidden subcommand from the new KB CLI to provide - // a way to initialize Kubebuilder projects from the legacy CLI. - // TODO(hasbro17): This should be removed once the KB CLI is the default. - initCmd := getInitCmd() - if initCmd == nil { - log.Debug("Failed to add init cmd to CLI") - } else { - initCmd.Hidden = true - root.AddCommand(initCmd) - } - return root } -func getInitCmd() *cobra.Command { - _, root := GetPluginsCLIAndRoot() - var initCmd *cobra.Command - for _, cmd := range root.Commands() { - if cmd.Name() == "init" { - initCmd = cmd - } - } - return initCmd -} - func checkGoModulesForCmd(cmd *cobra.Command) (err error) { // Certain commands are able to be run anywhere or handle this check // differently in their CLI code. @@ -134,12 +106,10 @@ func checkGoModulesForCmd(cmd *cobra.Command) (err error) { var commandsToSkip = map[string]struct{}{ "new": struct{}{}, // Handles this logic in cmd/operator-sdk/new - "migrate": struct{}{}, // Handles this logic in cmd/operator-sdk/migrate "operator-sdk": struct{}{}, // Alias for "help" "help": struct{}{}, "completion": struct{}{}, "version": struct{}{}, - "print-deps": struct{}{}, // Does not require this logic } func skipCheckForCmd(cmd *cobra.Command) (skip bool) { diff --git a/cmd/operator-sdk/generate/cmd.go b/cmd/operator-sdk/generate/cmd.go index 12c1ff8d267..b0750b16b92 100644 --- a/cmd/operator-sdk/generate/cmd.go +++ b/cmd/operator-sdk/generate/cmd.go @@ -46,8 +46,6 @@ func NewCmd() *cobra.Command { func NewCmdLegacy() *cobra.Command { cmd := newCmd() cmd.AddCommand( - newGenerateK8SCmd(), - newGenerateCRDsCmd(), newGenerateCSVCmd(), bundle.NewCmdLegacy(), packagemanifests.NewCmdLegacy(), diff --git a/cmd/operator-sdk/generate/crds.go b/cmd/operator-sdk/generate/crds.go deleted file mode 100644 index e678ac2aa62..00000000000 --- a/cmd/operator-sdk/generate/crds.go +++ /dev/null @@ -1,77 +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 generate - -import ( - "fmt" - "os" - - log "github.com/sirupsen/logrus" - "github.com/spf13/cobra" - - gencrd "github.com/operator-framework/operator-sdk/internal/generate/crd" - "github.com/operator-framework/operator-sdk/internal/genutil" - "github.com/operator-framework/operator-sdk/internal/scaffold" - "github.com/operator-framework/operator-sdk/internal/util/projutil" -) - -var ( - crdVersion string -) - -func newGenerateCRDsCmd() *cobra.Command { - cmd := &cobra.Command{ - Use: "crds", - Short: "Generates CRDs for API's", - Long: `generate crds generates CRDs or updates them if they exist, -under deploy/crds/__crd.yaml; OpenAPI -V3 validation YAML is generated as a 'validation' object. - -Example: - - $ operator-sdk generate crds - $ tree deploy/crds - ├── deploy/crds/app.example.com_v1alpha1_appservice_cr.yaml - ├── deploy/crds/app.example.com_appservices_crd.yaml -`, - RunE: crdsFunc, - } - - cmd.Flags().StringVar(&crdVersion, "crd-version", gencrd.DefaultCRDVersion, "CRD version to generate") - return cmd -} - -func crdsFunc(cmd *cobra.Command, args []string) error { - if len(args) != 0 { - return fmt.Errorf("command %s doesn't accept any arguments", cmd.CommandPath()) - } - projutil.MustInProjectRoot() - - stat, err := os.Stat(scaffold.ApisDir) - if os.IsNotExist(err) { - log.Fatalf("Failed to generate CRDs; directory %q not found.", scaffold.ApisDir) - } else if err != nil { - log.Fatalf("Failed to generate CRDs; stat: %v", err) - } else if !stat.IsDir() { - log.Fatalf("Failed to generate CRDs; expected %q to be a directory.", scaffold.ApisDir) - } - - // Skip usage printing on error, since this command will never fail from - // improper CLI usage. - if err := genutil.CRDGen(crdVersion); err != nil { - log.Fatal(err) - } - return nil -} diff --git a/cmd/operator-sdk/generate/k8s.go b/cmd/operator-sdk/generate/k8s.go deleted file mode 100644 index f7934591b59..00000000000 --- a/cmd/operator-sdk/generate/k8s.go +++ /dev/null @@ -1,62 +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 generate - -import ( - "fmt" - - "github.com/operator-framework/operator-sdk/internal/genutil" - "github.com/operator-framework/operator-sdk/internal/util/projutil" - - log "github.com/sirupsen/logrus" - "github.com/spf13/cobra" -) - -func newGenerateK8SCmd() *cobra.Command { - return &cobra.Command{ - Use: "k8s", - Short: "Generates Kubernetes code for custom resource", - Long: `k8s generator generates code for custom resources given the API -specs in pkg/apis// directories to comply with kube-API -requirements. Go code is generated under -pkg/apis///zz_generated.deepcopy.go. -Example: - - $ operator-sdk generate k8s - $ tree pkg/apis - pkg/apis/ - └── app - └── v1alpha1 - ├── zz_generated.deepcopy.go -`, - RunE: k8sFunc, - } -} - -func k8sFunc(cmd *cobra.Command, args []string) error { - if len(args) != 0 { - return fmt.Errorf("command %s doesn't accept any arguments", cmd.CommandPath()) - } - - // Only Go projects can generate k8s deepcopy code. - if err := projutil.CheckGoProjectCmd(cmd); err != nil { - return err - } - - if err := genutil.K8sCodegen(); err != nil { - log.Fatal(err) - } - return nil -} diff --git a/cmd/operator-sdk/migrate/cmd.go b/cmd/operator-sdk/migrate/cmd.go deleted file mode 100644 index 844e06a959a..00000000000 --- a/cmd/operator-sdk/migrate/cmd.go +++ /dev/null @@ -1,195 +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 migrate - -import ( - "fmt" - "os" - "path/filepath" - - "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" - - log "github.com/sirupsen/logrus" - "github.com/spf13/cobra" -) - -var ( - headerFile string - repo string -) - -// NewCmd returns a command that will add source code to an existing non-go operator -func NewCmd() *cobra.Command { - newCmd := &cobra.Command{ - Use: "migrate", - Short: "Adds source code to an operator", - Long: `operator-sdk migrate adds a main.go source file and any associated source files` + - `for an operator that is not of the "go" type.`, - RunE: migrateRun, - Deprecated: "and will be removed prior to operator-sdk v1.0.0", - } - - newCmd.Flags().StringVar(&headerFile, "header-file", "", - "Path to file containing headers for generated Go files. Copied to hack/boilerplate.go.txt") - newCmd.Flags().StringVar(&repo, "repo", "", - "Project repository path. Used as the project's Go import path. This must be set if outside of "+ - "$GOPATH/src (e.g. github.com/example-inc/my-operator)") - - return newCmd -} - -// migrateRun determines the current operator type and runs the corresponding -// migrate function. -func migrateRun(cmd *cobra.Command, args []string) error { - projutil.MustInProjectRoot() - - if err := verifyFlags(); err != nil { - return err - } - - if repo == "" { - repo = projutil.GetGoPkg() - } - - opType := projutil.GetOperatorType() - switch opType { - case projutil.OperatorTypeAnsible: - if err := migrateAnsible(); err != nil { - log.Fatal(err) - } - case projutil.OperatorTypeHelm: - if err := migrateHelm(); err != nil { - log.Fatal(err) - } - default: - return fmt.Errorf("operator of type %s cannot be migrated", opType) - } - return nil -} - -func verifyFlags() error { - err := projutil.CheckRepo(repo) - if err != nil { - return err - } - return nil -} - -// migrateAnsible runs the migration process for an ansible-based operator -func migrateAnsible() error { - wd := projutil.MustGetwd() - - cfg := &input.Config{ - Repo: repo, - AbsProjectPath: wd, - ProjectName: filepath.Base(wd), - } - - dockerfile := ansible.DockerfileHybrid{ - Watches: true, - Roles: true, - Requirements: true, - } - _, err := os.Stat(ansible.PlaybookYamlFile) - switch { - case err == nil: - dockerfile.Playbook = true - case os.IsNotExist(err): - log.Info("No playbook was found, so not including it in the new Dockerfile") - default: - return fmt.Errorf("error trying to stat %s: %v", ansible.PlaybookYamlFile, err) - } - if err := renameDockerfile(); err != nil { - return err - } - - s := &scaffold.Scaffold{} - if headerFile != "" { - err = s.Execute(cfg, &scaffold.Boilerplate{BoilerplateSrcPath: headerFile}) - if err != nil { - return fmt.Errorf("boilerplate scaffold failed: %v", err) - } - s.BoilerplatePath = headerFile - } - - err = s.Execute(cfg, - &ansible.GoMod{}, - &scaffold.Tools{}, - &ansible.Main{}, - &dockerfile, - &ansible.Entrypoint{}, - &ansible.UserSetup{}, - ) - if err != nil { - return fmt.Errorf("migrate ansible scaffold failed: %v", err) - } - return nil -} - -// migrateHelm runs the migration process for a helm-based operator -func migrateHelm() error { - wd := projutil.MustGetwd() - - cfg := &input.Config{ - Repo: repo, - AbsProjectPath: wd, - ProjectName: filepath.Base(wd), - } - - if err := renameDockerfile(); err != nil { - return err - } - - s := &scaffold.Scaffold{} - if headerFile != "" { - err := s.Execute(cfg, &scaffold.Boilerplate{BoilerplateSrcPath: headerFile}) - if err != nil { - return fmt.Errorf("boilerplate scaffold failed: %v", err) - } - s.BoilerplatePath = headerFile - } - - err := s.Execute(cfg, - &helm.GoMod{}, - &scaffold.Tools{}, - &helm.Main{}, - &helm.DockerfileHybrid{ - Watches: true, - HelmCharts: true, - }, - &helm.Entrypoint{}, - &helm.UserSetup{}, - ) - if err != nil { - return fmt.Errorf("migrate helm scaffold failed: %v", err) - } - return nil -} - -func renameDockerfile() error { - dockerfilePath := filepath.Join(scaffold.BuildDir, scaffold.DockerfileFile) - newDockerfilePath := dockerfilePath + ".sdkold" - err := os.Rename(dockerfilePath, newDockerfilePath) - if err != nil { - return fmt.Errorf("failed to rename Dockerfile: %v", err) - } - log.Infof("Renamed Dockerfile to %s and replaced with newer version. Compare the new Dockerfile to your"+ - " old one and manually migrate any customizations", newDockerfilePath) - return nil -} diff --git a/cmd/operator-sdk/printdeps/cmd.go b/cmd/operator-sdk/printdeps/cmd.go deleted file mode 100644 index 22aedaaa022..00000000000 --- a/cmd/operator-sdk/printdeps/cmd.go +++ /dev/null @@ -1,67 +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 printdeps - -import ( - "fmt" - - "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/util/projutil" - "github.com/spf13/cobra" - - log "github.com/sirupsen/logrus" -) - -func NewCmd() *cobra.Command { - printDepsCmd := &cobra.Command{ - Use: "print-deps", - Short: "Print Golang packages and versions required to run the operator", - Long: `The operator-sdk print-deps command prints all Golang packages and versions expected -by this version of the Operator SDK. Versions for these packages should match -those in an operator's go.mod file. -`, - RunE: printDepsFunc, - } - return printDepsCmd -} - -func printDepsFunc(cmd *cobra.Command, args []string) error { - if len(args) != 0 { - return fmt.Errorf("command %s doesn't accept any arguments", cmd.CommandPath()) - } - projutil.MustInProjectRoot() - - if err := printDeps(); err != nil { - log.Fatalf("Print deps failed: %v", err) - } - return nil -} - -func printDeps() (err error) { - // Migrated Ansible and Helm projects will be of type OperatorTypeGo but - // their deps files will differ from a vanilla Go project. - switch { - case projutil.IsOperatorAnsible(): - return ansible.PrintGoMod() - case projutil.IsOperatorHelm(): - return helm.PrintGoMod() - case projutil.IsOperatorGo(): - return scaffold.PrintGoMod() - } - - return projutil.ErrUnknownOperatorType{} -} diff --git a/cmd/operator-sdk/scorecard/cmd.go b/cmd/operator-sdk/scorecard/cmd.go deleted file mode 100644 index 487110a1b72..00000000000 --- a/cmd/operator-sdk/scorecard/cmd.go +++ /dev/null @@ -1,218 +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 scorecard - -import ( - "bytes" - "fmt" - "io" - "os" - - log "github.com/sirupsen/logrus" - - "github.com/mitchellh/mapstructure" - "github.com/operator-framework/operator-sdk/internal/scorecard" - schelpers "github.com/operator-framework/operator-sdk/internal/scorecard/helpers" - "github.com/operator-framework/operator-sdk/internal/util/projutil" - "github.com/pkg/errors" - "k8s.io/apimachinery/pkg/labels" - - "github.com/spf13/cobra" - "github.com/spf13/viper" -) - -const ( - versionOpt = "version" - kubeconfigOpt = "kubeconfig" - configOpt = "config" - outputFormatOpt = "output" - selectorOpt = "selector" - bundleOpt = "bundle" - listOpt = "list" -) - -var ( - logReadWriter io.ReadWriter -) - -func NewCmd() *cobra.Command { - - c := scorecard.Config{} - - scorecardCmd := &cobra.Command{ - Use: "scorecard", - Short: "Run scorecard tests", - Long: `Runs blackbox scorecard tests on an operator -`, - //RunE: scorecard.Tests, - RunE: func(cmd *cobra.Command, args []string) error { - cmd.SilenceUsage = true - buildScorecardConfig(&c) - for idx, plugin := range c.PluginConfigs { - if err := plugin.ValidateConfig(idx); err != nil { - return fmt.Errorf("error validating plugin config: %v", err) - } - } - - if err := c.RunTests(); err != nil { - log.Fatal(err) - } - return nil - }, - } - - scorecardCmd.Flags().String(configOpt, "", fmt.Sprintf( - "config file (default is '/%s.yaml'; the config file's extension and format must be .yaml", - scorecard.DefaultConfigFile)) - scorecardCmd.Flags().String(kubeconfigOpt, "", - "Path to kubeconfig of custom resource created in cluster") - scorecardCmd.Flags().StringP(outputFormatOpt, "o", scorecard.TextOutputFormat, - fmt.Sprintf("Output format for results. Valid values: %s, %s", scorecard.TextOutputFormat, - scorecard.JSONOutputFormat)) - scorecardCmd.Flags().String(versionOpt, schelpers.DefaultScorecardVersion, - "scorecard version. Valid values: v1alpha2") - scorecardCmd.Flags().StringP(selectorOpt, "l", "", - "selector (label query) to filter tests on") - scorecardCmd.Flags().BoolP(listOpt, "L", false, - "If true, only print the test names that would be run based on selector filtering") - scorecardCmd.Flags().StringP(bundleOpt, "b", "", - "OLM bundle directory path, when specified runs bundle validation") - - if err := viper.BindPFlag(configOpt, scorecardCmd.Flags().Lookup(configOpt)); err != nil { - log.Fatalf("Unable to add config :%v", err) - } - if err := viper.BindPFlag("scorecard."+kubeconfigOpt, - scorecardCmd.Flags().Lookup(kubeconfigOpt)); err != nil { - log.Fatalf("Unable to add kubeconfig :%v", err) - } - if err := viper.BindPFlag("scorecard."+outputFormatOpt, scorecardCmd.Flags().Lookup(outputFormatOpt)); err != nil { - log.Fatalf("Unable to add output format :%v", err) - } - if err := viper.BindPFlag("scorecard."+versionOpt, scorecardCmd.Flags().Lookup(versionOpt)); err != nil { - log.Fatalf("Unable to add version :%v", err) - } - if err := viper.BindPFlag("scorecard."+selectorOpt, scorecardCmd.Flags().Lookup(selectorOpt)); err != nil { - log.Fatalf("Unable to add selector :%v", err) - } - if err := viper.BindPFlag("scorecard."+listOpt, scorecardCmd.Flags().Lookup(listOpt)); err != nil { - log.Fatalf("Unable to add list :%v", err) - } - if err := viper.BindPFlag("scorecard."+bundleOpt, scorecardCmd.Flags().Lookup(bundleOpt)); err != nil { - log.Fatalf("Unable to add bundle :%v", err) - } - - return scorecardCmd -} - -func initConfig() (*viper.Viper, error) { - // viper/cobra already has flags parsed at this point; we can check if a config file flag is set - if viper.GetString(configOpt) != "" { - // Use config file from the flag. - viper.SetConfigFile(viper.GetString(configOpt)) - } else { - viper.AddConfigPath(projutil.MustGetwd()) - // Note that viper allows other extensions as .json, or .toml file as well, however, - // these other formats are deprecated in the SDK. - // By using SetConfigName allows users to use diff extensions. - // todo(camilamacedo86): Check if we can replace this configuration and make the things easier for the future - // versions since from 0.16 we will need just support the YAML format. - viper.SetConfigName(scorecard.DefaultConfigFile) - } - - var scViper *viper.Viper - if err := viper.ReadInConfig(); err == nil { - scViper = viper.Sub("scorecard") - // this is a workaround for the fact that nested flags don't persist on viper.Sub - scViper.Set(outputFormatOpt, viper.GetString("scorecard."+outputFormatOpt)) - scViper.Set(kubeconfigOpt, viper.GetString("scorecard."+kubeconfigOpt)) - scViper.Set(versionOpt, viper.GetString("scorecard."+versionOpt)) - scViper.Set(selectorOpt, viper.GetString("scorecard."+selectorOpt)) - scViper.Set(bundleOpt, viper.GetString("scorecard."+bundleOpt)) - scViper.Set(listOpt, viper.GetString("scorecard."+listOpt)) - // configure logger output before logging anything - if !scViper.IsSet(outputFormatOpt) { - scViper.Set(outputFormatOpt, scorecard.TextOutputFormat) - } - - switch format := scViper.GetString(outputFormatOpt); format { - case scorecard.TextOutputFormat: - logReadWriter = os.Stdout - case scorecard.JSONOutputFormat: - logReadWriter = &bytes.Buffer{} - default: - return nil, fmt.Errorf("invalid output format: %s", format) - } - - scorecard.Log.SetOutput(logReadWriter) - scorecard.Log.Info("Using config file: ", viper.ConfigFileUsed()) - } else { - // The file var is used here to make clear the file.ext that is missing in the project - file := viper.ConfigFileUsed() - if len(file) < 1 { - file = scorecard.DefaultConfigFile + ".yaml" - } - return nil, fmt.Errorf("could not read config file (%v): %v\nSee %s for more information about the"+ - " scorecard config file", file, err, scorecard.ConfigDocLink()) - } - return scViper, nil -} - -func buildScorecardConfig(c *scorecard.Config) { - - scViper, err := initConfig() - if err != nil { - log.Fatalf("Unable to parse the scorecard config file: %v", err.Error()) - } - - outputFormat := scViper.GetString(outputFormatOpt) - if outputFormat != scorecard.TextOutputFormat && outputFormat != scorecard.JSONOutputFormat { - log.Fatalf("Invalid output format (%s); valid values: %s, %s", outputFormat, scorecard.TextOutputFormat, - scorecard.JSONOutputFormat) - } - - version := scViper.GetString(versionOpt) - err = schelpers.ValidateVersion(version) - if err != nil { - log.Fatalf("%v", err) - } - - c.List = scViper.GetBool(listOpt) - c.OutputFormat = scViper.GetString(outputFormatOpt) - c.Version = scViper.GetString(versionOpt) - c.Bundle = scViper.GetString(bundleOpt) - - if scViper.IsSet(kubeconfigOpt) { - c.Kubeconfig = scViper.GetString(kubeconfigOpt) - } - - c.Selector, err = labels.Parse(scViper.GetString(selectorOpt)) - if err != nil { - log.Fatalf("%v", err) - } - - c.PluginConfigs = []scorecard.PluginConfig{} - if err := scViper.UnmarshalKey("plugins", &c.PluginConfigs, - func(c *mapstructure.DecoderConfig) { c.ErrorUnused = true }); err != nil { - log.Fatalf("%v", errors.Wrap(err, "Could not load plugin configurations")) - } - - c.Plugins, err = c.GetPlugins(c.PluginConfigs) - if err != nil { - log.Fatalf("%v", err) - } - - c.LogReadWriter = logReadWriter - -} diff --git a/go.mod b/go.mod index 4e8845b5a37..78529af89a3 100644 --- a/go.mod +++ b/go.mod @@ -13,13 +13,11 @@ require ( github.com/markbates/inflect v1.0.4 github.com/mattn/go-isatty v0.0.12 github.com/mitchellh/go-homedir v1.1.0 - github.com/mitchellh/mapstructure v1.1.2 github.com/onsi/ginkgo v1.12.0 github.com/onsi/gomega v1.9.0 github.com/operator-framework/api v0.3.8 github.com/operator-framework/operator-registry v1.12.6-0.20200611222234-275301b779f8 github.com/pborman/uuid v1.2.0 - github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.5.1 github.com/prometheus/common v0.9.1 github.com/rogpeppe/go-internal v1.5.0 @@ -42,7 +40,6 @@ require ( k8s.io/apimachinery v0.18.2 k8s.io/cli-runtime v0.18.2 k8s.io/client-go v12.0.0+incompatible - k8s.io/code-generator v0.18.2 k8s.io/gengo v0.0.0-20200114144118-36b2048a9120 k8s.io/klog v1.0.0 k8s.io/kube-state-metrics v1.7.2 diff --git a/hack/generate/test-framework/gen-test-framework.sh b/hack/generate/test-framework/gen-test-framework.sh index ad8b64d4ee1..1303c1e7355 100755 --- a/hack/generate/test-framework/gen-test-framework.sh +++ b/hack/generate/test-framework/gen-test-framework.sh @@ -11,14 +11,8 @@ source hack/lib/test_lib.sh # Go inside of the mock data test project cd test/test-framework -# Ensure test-framework is up-to-date with current Go project dependencies. -echo "$(../../build/operator-sdk print-deps)" > go.mod sed -i".bak" -E -e "/github.com\/operator-framework\/operator-sdk .+/d" go.mod; rm -f go.mod.bak echo -e "\nreplace github.com/operator-framework/operator-sdk => ../../" >> go.mod go mod edit -require "github.com/operator-framework/operator-sdk@v0.0.0" go build ./... go mod tidy - -# Run gen commands -../../build/operator-sdk generate k8s -../../build/operator-sdk generate crds diff --git a/hack/image/ansible/Dockerfile b/hack/image/ansible/Dockerfile new file mode 100644 index 00000000000..cf18694a468 --- /dev/null +++ b/hack/image/ansible/Dockerfile @@ -0,0 +1,43 @@ +FROM registry.access.redhat.com/ubi8/ubi + +RUN mkdir -p /etc/ansible \ + && echo "localhost ansible_connection=local" > /etc/ansible/hosts \ + && echo '[defaults]' > /etc/ansible/ansible.cfg \ + && echo 'roles_path = /opt/ansible/roles' >> /etc/ansible/ansible.cfg \ + && echo 'library = /usr/share/ansible/openshift' >> /etc/ansible/ansible.cfg + +ENV HOME=/opt/ansible \ + USER_NAME=ansible \ + USER_UID=1001 + +# Install python dependencies +# Ensure fresh metadata rather than cached metadata in the base by running +# yum clean all && rm -rf /var/yum/cache/* first +RUN yum clean all && rm -rf /var/cache/yum/* \ + && yum -y update \ + && yum install -y libffi-devel openssl-devel python36-devel gcc python3-pip python3-setuptools \ + && pip3 install --no-cache-dir --ignore-installed ipaddress \ + ansible-runner==1.3.4 \ + ansible-runner-http==1.0.0 \ + openshift~=0.10.0 \ + ansible~=2.9 \ + jmespath \ + && yum remove -y gcc libffi-devel openssl-devel python36-devel \ + && yum clean all \ + && rm -rf /var/cache/yum + +COPY ansible-operator-dev-linux-gnu /usr/local/bin/ansible-operator + +# Ensure directory permissions are properly set +RUN echo "${USER_NAME}:x:${USER_UID}:0:${USER_NAME} user:${HOME}:/sbin/nologin" >> /etc/passwd \ + && mkdir -p ${HOME}/.ansible/tmp \ + && chown -R ${USER_UID}:0 ${HOME} \ + && chmod -R ug+rwx ${HOME} + +RUN TINIARCH=$(case $(arch) in x86_64) echo -n amd64 ;; ppc64le) echo -n ppc64el ;; aarch64) echo -n arm64 ;; *) echo -n $(arch) ;; esac) \ + && curl -L -o /tini https://github.com/krallin/tini/releases/latest/download/tini-$TINIARCH \ + && chmod +x /tini + +WORKDIR ${HOME} +USER ${USER_UID} +ENTRYPOINT ["/tini", "--", "/usr/local/bin/ansible-operator", "--watches-file=./watches.yaml"] diff --git a/hack/image/ansible/scaffold-ansible-image.go b/hack/image/ansible/scaffold-ansible-image.go deleted file mode 100644 index b5013968b57..00000000000 --- a/hack/image/ansible/scaffold-ansible-image.go +++ /dev/null @@ -1,45 +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 main - -import ( - "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/input" - "github.com/operator-framework/operator-sdk/internal/util/projutil" - - log "github.com/sirupsen/logrus" -) - -// main renders scaffolds that are required to build the ansible operator base -// image. It is intended for release engineering use only. After running this, -// you can place a binary in `build/_output/bin/ansible-operator` and then run -// `operator-sdk build`. -func main() { - cfg := &input.Config{ - AbsProjectPath: projutil.MustGetwd(), - ProjectName: "ansible-operator", - } - - s := &scaffold.Scaffold{} - err := s.Execute(cfg, - &ansible.DockerfileHybrid{}, - &ansible.Entrypoint{}, - &ansible.UserSetup{}, - ) - if err != nil { - log.Fatalf("Add scaffold failed: (%v)", err) - } -} diff --git a/hack/image/build-ansible-image.sh b/hack/image/build-ansible-image.sh index 3da45198b0c..08700d5cae1 100755 --- a/hack/image/build-ansible-image.sh +++ b/hack/image/build-ansible-image.sh @@ -8,17 +8,12 @@ source hack/lib/image_lib.sh ROOTDIR="$(pwd)" TMPDIR="$(mktemp -d)" trap_add 'rm -rf $TMPDIR' EXIT -BASEIMAGEDIR="$TMPDIR/ansible-operator" -mkdir -p "$BASEIMAGEDIR" -go build -o $BASEIMAGEDIR/scaffold-ansible-image ./hack/image/ansible/scaffold-ansible-image.go -# build operator binary and base image -pushd "$BASEIMAGEDIR" -./scaffold-ansible-image +# build the base image +pushd $TMPDIR +cp $ROOTDIR/build/ansible-operator-dev-linux-gnu . +docker build -f $ROOTDIR/hack/image/ansible/Dockerfile -t $1 . -mkdir -p build/_output/bin/ -cp $ROOTDIR/build/ansible-operator-dev-linux-gnu build/_output/bin/ansible-operator -operator-sdk build $1 # If using a kind cluster, load the image into all nodes. load_image_if_kind "$1" popd diff --git a/hack/image/build-helm-image.sh b/hack/image/build-helm-image.sh index 3fbc1cb423b..e9adb0fab6b 100755 --- a/hack/image/build-helm-image.sh +++ b/hack/image/build-helm-image.sh @@ -8,17 +8,12 @@ source hack/lib/image_lib.sh ROOTDIR="$(pwd)" TMPDIR="$(mktemp -d)" trap_add 'rm -rf $TMPDIR' EXIT -BASEIMAGEDIR="$TMPDIR/helm-operator" -mkdir -p "$BASEIMAGEDIR" -go build -o $BASEIMAGEDIR/scaffold-helm-image ./hack/image/helm/scaffold-helm-image.go -# build operator binary and base image -pushd "$BASEIMAGEDIR" -./scaffold-helm-image +# build the base image +pushd $TMPDIR +cp $ROOTDIR/build/helm-operator-dev-linux-gnu . +docker build -f $ROOTDIR/hack/image/helm/Dockerfile -t $1 . -mkdir -p build/_output/bin/ -cp $ROOTDIR/build/helm-operator-dev-linux-gnu build/_output/bin/helm-operator -operator-sdk build $1 # If using a kind cluster, load the image into all nodes. load_image_if_kind "$1" popd diff --git a/hack/image/build-scorecard-proxy-image.sh b/hack/image/build-scorecard-proxy-image.sh deleted file mode 100755 index 45aecbefc02..00000000000 --- a/hack/image/build-scorecard-proxy-image.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env bash - -set -eux - -source hack/lib/image_lib.sh - -# build operator binary and base image -WD="$(dirname "$(pwd)")" -GOOS=linux CGO_ENABLED=0 \ - go build \ - -gcflags "all=-trimpath=${WD}" \ - -asmflags "all=-trimpath=${WD}" \ - -o images/scorecard-proxy/scorecard-proxy \ - images/scorecard-proxy/cmd/proxy/main.go -pushd images/scorecard-proxy -docker build -t "$1" . -# If using a kind cluster, load the image into all nodes. -load_image_if_kind "$1" -popd diff --git a/hack/image/helm/Dockerfile b/hack/image/helm/Dockerfile new file mode 100644 index 00000000000..dda3523ae50 --- /dev/null +++ b/hack/image/helm/Dockerfile @@ -0,0 +1,12 @@ +FROM registry.access.redhat.com/ubi8/ubi-minimal:latest + +ENV HOME=/opt/helm \ + USER_NAME=helm \ + USER_UID=1001 + +RUN echo "${USER_NAME}:x:${USER_UID}:0:${USER_NAME} user:${HOME}:/sbin/nologin" >> /etc/passwd +COPY helm-operator-dev-linux-gnu /usr/local/bin/helm-operator + +WORKDIR ${HOME} +USER ${USER_UID} +ENTRYPOINT ["/usr/local/bin/helm-operator", "--watches-file=./watches.yaml"] diff --git a/hack/image/helm/scaffold-helm-image.go b/hack/image/helm/scaffold-helm-image.go deleted file mode 100644 index 100c28740e3..00000000000 --- a/hack/image/helm/scaffold-helm-image.go +++ /dev/null @@ -1,45 +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 main - -import ( - "log" - - "github.com/operator-framework/operator-sdk/internal/scaffold" - "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" -) - -// main renders scaffolds that are required to build the helm operator base -// image. It is intended for release engineering use only. After running this, -// you can place a binary in `build/_output/bin/helm-operator` and then run -// `operator-sdk build`. -func main() { - cfg := &input.Config{ - AbsProjectPath: projutil.MustGetwd(), - ProjectName: "helm-operator", - } - - s := &scaffold.Scaffold{} - err := s.Execute(cfg, - &helm.DockerfileHybrid{}, - &helm.Entrypoint{}, - &helm.UserSetup{}, - ) - if err != nil { - log.Fatalf("add scaffold failed: (%v)", err) - } -} diff --git a/hack/tests/e2e-ansible.sh b/hack/tests/e2e-ansible.sh index 4721363da6c..b4cf909ddfc 100755 --- a/hack/tests/e2e-ansible.sh +++ b/hack/tests/e2e-ansible.sh @@ -221,30 +221,5 @@ deploy_operator test_operator remove_operator -header_text "###" -header_text "### Base image testing passed" -header_text "### Now testing migrate to hybrid operator" -header_text "###" - -operator-sdk migrate --repo=github.com/example-inc/memcached-operator - -if [[ ! -e build/Dockerfile.sdkold ]]; -then - error_text "FAIL the old Dockerfile should have been renamed to Dockerfile.sdkold" - exit 1 -fi - -add_go_mod_replace "github.com/operator-framework/operator-sdk" "$ROOTDIR" -header_text "Build the project to resolve dependency versions in the modfile." -go build ./... - -operator-sdk build "$DEST_IMAGE" - -header_text "If using a kind cluster, load the image into all nodes." -load_image_if_kind "$DEST_IMAGE" - -deploy_operator -test_operator - popd popd diff --git a/hack/tests/e2e-go-new.sh b/hack/tests/e2e-go-new.sh deleted file mode 100755 index bb84e653b60..00000000000 --- a/hack/tests/e2e-go-new.sh +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/env bash - -# remove running containers on exit -function cleanup() { - kind delete cluster -} - -set -o errexit -set -o nounset -set -o pipefail - -source ./hack/lib/common.sh -source ./hack/lib/test_lib.sh - -test_dir=./test -tests=$test_dir/e2e-new - -export TRACE=1 -export GO111MODULE=on - -: ${K8S_VERSION:?"must be set"} - -prepare_staging_dir $tmp_sdk_root -fetch_tools $tmp_sdk_root -# These envtest environment variables are required for the default unit tests -# scaffolded in the test operator project. No e2e tests currently use envtest. -setup_envs $tmp_sdk_root -build_sdk $tmp_sdk_root - -# Create a cluster of version $K8S_VERSION. -kind create cluster -v 4 --retain --wait=1m \ - --config $test_dir/kind-config.yaml \ - --image=kindest/node:$K8S_VERSION - -kind export kubeconfig - -kubectl cluster-info - -docker pull gcr.io/kubebuilder/kube-rbac-proxy:v0.5.0 -kind load docker-image gcr.io/kubebuilder/kube-rbac-proxy:v0.5.0 - -trap_add cleanup EXIT -go test -v $tests diff --git a/hack/tests/e2e-go.sh b/hack/tests/e2e-go.sh index 158a446d946..69434d14070 100755 --- a/hack/tests/e2e-go.sh +++ b/hack/tests/e2e-go.sh @@ -1,16 +1,28 @@ #!/usr/bin/env bash -set -ex -source hack/lib/image_lib.sh +set -o errexit +set -o nounset +set -o pipefail -source hack/tests/scaffolding/e2e-go-scaffold.sh +source ./hack/lib/test_lib.sh +source ./hack/lib/image_lib.sh -pushd $BASEPROJECTDIR/memcached-operator -operator-sdk build $IMAGE_NAME -# If using a kind cluster, load the image into all nodes. -load_image_if_kind "$IMAGE_NAME" +test_dir=./test +tests=$test_dir/e2e -operator-sdk test local ./test/e2e -popd +export TRACE=1 +export GO111MODULE=on -go test ./test/e2e/... -root=. -globalMan=testdata/empty.yaml +prepare_staging_dir $tmp_sdk_root +fetch_tools $tmp_sdk_root +# These envtest environment variables are required for the default unit tests +# scaffolded in the test operator project. No e2e tests currently use envtest. +setup_envs $tmp_sdk_root +build_sdk $tmp_sdk_root + +kubectl cluster-info + +docker pull gcr.io/kubebuilder/kube-rbac-proxy:v0.5.0 +load_image_if_kind gcr.io/kubebuilder/kube-rbac-proxy:v0.5.0 + +go test -v $tests diff --git a/hack/tests/e2e-helm.sh b/hack/tests/e2e-helm.sh index 8703c2e0ac6..1adffd7f0f9 100755 --- a/hack/tests/e2e-helm.sh +++ b/hack/tests/e2e-helm.sh @@ -165,29 +165,5 @@ trap_add 'remove_operator' EXIT test_operator remove_operator -echo "###" -echo "### Base image testing passed" -echo "### Now testing migrate to hybrid operator" -echo "###" - -operator-sdk migrate --repo=github.com/example-inc/nginx-operator - -if [[ ! -e build/Dockerfile.sdkold ]]; -then - echo FAIL the old Dockerfile should have been renamed to Dockerfile.sdkold - exit 1 -fi - -add_go_mod_replace "github.com/operator-framework/operator-sdk" "$ROOTDIR" -# Build the project to resolve dependency versions in the modfile. -go build ./... - -operator-sdk build "$DEST_IMAGE" -# If using a kind cluster, load the image into all nodes. -load_image_if_kind "$DEST_IMAGE" - -deploy_operator -test_operator - popd popd diff --git a/hack/tests/subcommand-scorecard.sh b/hack/tests/subcommand-scorecard.sh deleted file mode 100755 index 3abdcb436bb..00000000000 --- a/hack/tests/subcommand-scorecard.sh +++ /dev/null @@ -1,96 +0,0 @@ -#!/usr/bin/env bash - -DEST_IMAGE="quay.io/example/scorecard-proxy" -CONFIG_PATH=".test-osdk-scorecard.yaml" -CONFIG_PATH_DISABLE=".osdk-scorecard-disable.yaml" -CONFIG_PATH_INVALID=".osdk-scorecard-invalid.yaml" -CONFIG_PATH_V1ALPHA2=".osdk-scorecard-v1alpha2.yaml" -CONFIG_PATH_BUNDLE=".osdk-scorecard-bundle.yaml" - -set -ex - -source ./hack/lib/common.sh - -# build scorecard-proxy image -./hack/image/build-scorecard-proxy-image.sh "$DEST_IMAGE" - -# the test framework directory has all the manifests needed to run the cluster -pushd test/test-framework - -header_text 'scorecard test to test kubeconfig flag, kubeconfig should not exist so internal plugins should fail' -if ! commandoutput="$(operator-sdk scorecard --kubeconfig=/kubeconfig 2>&1)"; then - echo $commandoutput -else - echo "test failed: expected return code 1" - exit 1 -fi - -header_text 'scorecard test to see if total score matches expected value' -if ! commandoutput="$(operator-sdk scorecard --config "$CONFIG_PATH" 2>&1)"; then - echo $commandoutput - passCount=`echo $commandoutput | grep -o "pass" | wc -l` - expectedPassCount=10 - if [ $passCount -ne $expectedPassCount ] - then - echo "expected label count $expectedLabelCount, got $labelCount" - exit 1 - fi -else - echo "test failed: expected return code 1" - exit 1 -fi - -header_text 'scorecard test to see if bundle flag works correctly' -if ! commandoutput="$(operator-sdk scorecard --config "$CONFIG_PATH_BUNDLE" 2>&1)"; then - echo $commandoutput - failCount=`echo $commandoutput | grep -o "fail" | wc -l` - expectedFailCount=2 - if [ $failCount -ne $expectedFailCount ] - then - echo "expected fail count $expectedFailCount, got $failCount" - exit 1 - fi -else - echo "test failed: expected return code 1" - exit 1 -fi - -header_text 'scorecard test to see if exit code 1 is returned on test failures' -if ! commandoutput="$(operator-sdk scorecard --config "$CONFIG_PATH_V1ALPHA2" 2>&1)"; then - echo $commandoutput - failCount=`echo $commandoutput | grep -o "fail" | wc -l` - expectedFailCount=4 - if [ $failCount -ne $expectedFailCount ] - then - echo "expected fail count $expectedFailCount, got $failCount" - exit 1 - fi -else - echo "test failed: expected return code 1" - exit 1 -fi - -header_text 'scorecard test to see if --list flag works' -commandoutput="$(operator-sdk scorecard --list --selector=suite=basic --config "$CONFIG_PATH_V1ALPHA2" 2>&1)" -labelCount=`echo $commandoutput | grep -o "Label" | wc -l` -expectedLabelCount=3 -if [ $labelCount -ne $expectedLabelCount ] -then - echo "expected label count $expectedLabelCount, got $labelCount" - exit 1 -fi - -header_text 'scorecard test to see if --selector flag works' -commandoutput="$(operator-sdk scorecard --selector=suite=basic --config "$CONFIG_PATH_V1ALPHA2" 2>&1)" -labelCount=`echo $commandoutput | grep -o "Label" | wc -l` -expectedLabelCount=6 -if [ $labelCount -ne $expectedLabelCount ] -then - echo "expected label count $expectedLabelCount, got $labelCount" - exit 1 -fi - -header_text 'scorecard test to check invalid config' -operator-sdk scorecard --config "$CONFIG_PATH_INVALID" |& grep '^.*invalid keys.*$' - -popd diff --git a/images/scorecard-proxy/Dockerfile b/images/scorecard-proxy/Dockerfile deleted file mode 100644 index f18a4478c4f..00000000000 --- a/images/scorecard-proxy/Dockerfile +++ /dev/null @@ -1,16 +0,0 @@ -# Base image -FROM registry.access.redhat.com/ubi8/ubi-minimal:latest - -ENV PROXY=/usr/local/bin/scorecard-proxy \ - USER_UID=1001 \ - USER_NAME=proxy - -# install operator binary -COPY scorecard-proxy ${PROXY} - -COPY bin /usr/local/bin -RUN /usr/local/bin/user_setup - -ENTRYPOINT ["/usr/local/bin/entrypoint"] - -USER ${USER_UID} diff --git a/images/scorecard-proxy/bin/entrypoint b/images/scorecard-proxy/bin/entrypoint deleted file mode 100755 index 780c9a59e9f..00000000000 --- a/images/scorecard-proxy/bin/entrypoint +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/sh -e - -# This is documented here: -# https://docs.openshift.com/container-platform/3.11/creating_images/guidelines.html#openshift-specific-guidelines - -if ! whoami &>/dev/null; then - if [ -w /etc/passwd ]; then - echo "${USER_NAME:-proxy}:x:$(id -u):$(id -g):${USER_NAME:-proxy} user:${HOME}:/sbin/nologin" >> /etc/passwd - fi -fi - -exec ${PROXY} $@ diff --git a/images/scorecard-proxy/bin/user_setup b/images/scorecard-proxy/bin/user_setup deleted file mode 100755 index 9cf9ea3e767..00000000000 --- a/images/scorecard-proxy/bin/user_setup +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/sh -set -x - -# ensure $HOME exists and is accessible by group 0 (we don't know what the runtime UID will be) -mkdir -p "${HOME}" -chown "${USER_UID}:0" "${HOME}" -chmod ug+rwx "${HOME}" - -# runtime user will need to be able to self-insert in /etc/passwd -chmod g+rw /etc/passwd - -# no need for this script to remain in the image after running -rm "$0" diff --git a/images/scorecard-proxy/cmd/proxy/main.go b/images/scorecard-proxy/cmd/proxy/main.go deleted file mode 100644 index e90b8be79db..00000000000 --- a/images/scorecard-proxy/cmd/proxy/main.go +++ /dev/null @@ -1,97 +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 main - -import ( - "os" - "strconv" - - proxy "github.com/operator-framework/operator-sdk/pkg/ansible/proxy" - "github.com/operator-framework/operator-sdk/pkg/ansible/proxy/controllermap" - "github.com/operator-framework/operator-sdk/pkg/k8sutil" - "github.com/operator-framework/operator-sdk/pkg/log/zap" - - log "github.com/sirupsen/logrus" - "github.com/spf13/pflag" - "sigs.k8s.io/controller-runtime/pkg/client/config" - logf "sigs.k8s.io/controller-runtime/pkg/log" - "sigs.k8s.io/controller-runtime/pkg/manager" -) - -const ( - defaultPort = 8889 -) - -func main() { - pflag.CommandLine.AddFlagSet(zap.FlagSet()) - pflag.Parse() - - logf.SetLogger(zap.Logger()) - - namespace, found := os.LookupEnv(k8sutil.WatchNamespaceEnvVar) - if found { - log.Infof("Watching %v namespace.", namespace) - } else { - log.Infof("%v environment variable not set. This operator is watching all namespaces.", - k8sutil.WatchNamespaceEnvVar) - } - - port := defaultPort - - var tmp string - tmp, found = os.LookupEnv("SCORECARD_PROXY_PORT") - if found { - i, err := strconv.Atoi(tmp) - if err != nil { - log.Fatal(err) - } - port = i - } - log.Infof("Listening on port %d.", port) - - mgr, err := manager.New(config.GetConfigOrDie(), manager.Options{ - Namespace: namespace, - }) - if err != nil { - log.Fatal(err) - } - - done := make(chan error) - cMap := controllermap.NewControllerMap() - - // start the proxy - err = proxy.Run(done, proxy.Options{ - Address: "localhost", - Port: port, - KubeConfig: mgr.GetConfig(), - RESTMapper: mgr.GetRESTMapper(), - ControllerMap: cMap, - OwnerInjection: false, - LogRequests: true, - WatchedNamespaces: []string{namespace}, - DisableCache: true, - }) - if err != nil { - log.Fatalf("error starting proxy: %v", err) - } - - // wait forever or exit on proxy crash/finish - err = <-done - if err == nil { - log.Info("Exiting") - } else { - log.Fatal(err.Error()) - } -} diff --git a/internal/genutil/genutil.go b/internal/genutil/genutil.go deleted file mode 100644 index 018dd61b0ac..00000000000 --- a/internal/genutil/genutil.go +++ /dev/null @@ -1,58 +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 genutil - -import ( - "io/ioutil" - "os" - - "github.com/operator-framework/operator-sdk/internal/scaffold" - - log "github.com/sirupsen/logrus" -) - -// generateWithHeaderFile runs f with a header file path as an argument. -// If there is no project boilerplate.go.txt file, an empty header file is -// created and its path passed as the argument. -// generateWithHeaderFile is meant to be used with Kubernetes code generators. -func generateWithHeaderFile(f func(string) error) (err error) { - i, err := (&scaffold.Boilerplate{}).GetInput() - if err != nil { - return err - } - hf := i.Path - if _, err := os.Stat(hf); os.IsNotExist(err) { - if hf, err = createEmptyTmpFile(); err != nil { - return err - } - defer func() { - if err = os.RemoveAll(hf); err != nil { - log.Error(err) - } - }() - } - return f(hf) -} - -func createEmptyTmpFile() (string, error) { - f, err := ioutil.TempFile("", "") - if err != nil { - return "", err - } - if err = f.Close(); err != nil { - return "", err - } - return f.Name(), nil -} diff --git a/internal/genutil/k8s.go b/internal/genutil/k8s.go deleted file mode 100644 index f664bc0685f..00000000000 --- a/internal/genutil/k8s.go +++ /dev/null @@ -1,113 +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 genutil - -import ( - "fmt" - "os" - "os/exec" - "path/filepath" - "strings" - - "github.com/operator-framework/operator-sdk/internal/scaffold" - "github.com/operator-framework/operator-sdk/internal/util/k8sutil" - "github.com/operator-framework/operator-sdk/internal/util/projutil" - - log "github.com/sirupsen/logrus" - generatorargs "k8s.io/code-generator/cmd/deepcopy-gen/args" - "k8s.io/gengo/examples/deepcopy-gen/generators" -) - -// K8sCodegen performs deepcopy code-generation for all custom resources under -// pkg/apis. -func K8sCodegen() error { - projutil.MustInProjectRoot() - - goEnv, err := exec.Command("go", "env", "GOROOT").CombinedOutput() - if err != nil { - return fmt.Errorf("failed to get GOROOT from go env: %w", err) - } - goRoot := strings.TrimSuffix(string(goEnv), "\n") - log.Debugf("Setting GOROOT=%s", goRoot) - if err := os.Setenv("GOROOT", goRoot); err != nil { - return fmt.Errorf("failed to set env GOROOT=%s: %w", goRoot, err) - } - - repoPkg := projutil.GetGoPkg() - - gvMap, err := k8sutil.ParseGroupSubpackages(scaffold.ApisDir) - if err != nil { - return fmt.Errorf("failed to parse group versions: %v", err) - } - gvb := &strings.Builder{} - for g, vs := range gvMap { - gvb.WriteString(fmt.Sprintf("%s:%v, ", g, vs)) - } - - log.Infof("Running deepcopy code-generation for Custom Resource group versions: [%v]\n", gvb.String()) - - apisPkg := filepath.Join(repoPkg, scaffold.ApisDir) - fqApis := k8sutil.CreateFQAPIs(apisPkg, gvMap) - f := func(a string) error { return deepcopyGen(a, fqApis) } - if err = generateWithHeaderFile(f); err != nil { - return err - } - - log.Info("Code-generation complete.") - return nil -} - -func deepcopyGen(hf string, fqApis []string) error { - wd, err := os.Getwd() - if err != nil { - return err - } - for _, api := range fqApis { - api = filepath.FromSlash(api) - // Use relative API path so the generator writes to the correct path. - apiPath := "." + string(filepath.Separator) + api[strings.Index(api, scaffold.ApisDir):] - args, cargs := generatorargs.NewDefaults() - args.InputDirs = []string{apiPath} - args.OutputPackagePath = filepath.Join(wd, apiPath) - args.OutputFileBaseName = "zz_generated.deepcopy" - args.GoHeaderFilePath = hf - cargs.BoundingDirs = []string{apiPath} - // deepcopy-gen will use the import path of an API if in $GOPATH/src, but - // if we're outside of that dir it'll use apiPath. In order to generate - // deepcopy code at the correct path in all cases, we must unset the - // base output dir, which is $GOPATH/src by default, when we're outside. - inGopathSrc, err := projutil.WdInGoPathSrc() - if err != nil { - return err - } - if !inGopathSrc { - args.OutputBase = "" - } - - if err := generatorargs.Validate(args); err != nil { - return fmt.Errorf("deepcopy-gen argument validation error: %v", err) - } - - err = args.Execute( - generators.NameSystems(), - generators.DefaultNameSystem(), - generators.Packages, - ) - if err != nil { - return fmt.Errorf("deepcopy-gen generator error: %v", err) - } - } - return nil -} diff --git a/internal/scaffold/add_controller.go b/internal/scaffold/add_controller.go deleted file mode 100644 index 814c9b5d796..00000000000 --- a/internal/scaffold/add_controller.go +++ /dev/null @@ -1,50 +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 ( - "path/filepath" - - "github.com/operator-framework/operator-sdk/internal/scaffold/input" -) - -// AddController is the input needed to generate a pkg/controller/add_.go file -type AddController struct { - input.Input - - // Resource defines the inputs for the controller's primary resource - Resource *Resource -} - -func (s *AddController) GetInput() (input.Input, error) { - if s.Path == "" { - fileName := "add_" + s.Resource.LowerKind + ".go" - s.Path = filepath.Join(ControllerDir, fileName) - } - s.TemplateBody = addControllerTemplate - return s.Input, nil -} - -const addControllerTemplate = `package controller - -import ( - "{{ .Repo }}/pkg/controller/{{ .Resource.LowerKind }}" -) - -func init() { - // AddToManagerFuncs is a list of functions to create controllers and add them to a manager. - AddToManagerFuncs = append(AddToManagerFuncs, {{ .Resource.LowerKind }}.Add) -} -` diff --git a/internal/scaffold/add_controller_test.go b/internal/scaffold/add_controller_test.go deleted file mode 100644 index 629bd4dc02a..00000000000 --- a/internal/scaffold/add_controller_test.go +++ /dev/null @@ -1,50 +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 ( - "testing" - - "github.com/operator-framework/operator-sdk/internal/util/diffutil" -) - -func TestAddController(t *testing.T) { - r, err := NewResource(appAPIVersion, appKind) - if err != nil { - t.Fatal(err) - } - s, buf := setupScaffoldAndWriter() - err = s.Execute(appConfig, &AddController{Resource: r}) - if err != nil { - t.Fatalf("Failed to execute the scaffold: (%v)", err) - } - - if addControllerExp != buf.String() { - diffs := diffutil.Diff(addControllerExp, buf.String()) - t.Fatalf("Expected vs actual differs.\n%v", diffs) - } -} - -const addControllerExp = `package controller - -import ( - "github.com/example-inc/app-operator/pkg/controller/appservice" -) - -func init() { - // AddToManagerFuncs is a list of functions to create controllers and add them to a manager. - AddToManagerFuncs = append(AddToManagerFuncs, appservice.Add) -} -` diff --git a/internal/scaffold/addtoscheme.go b/internal/scaffold/addtoscheme.go deleted file mode 100644 index 0dac8e5f20e..00000000000 --- a/internal/scaffold/addtoscheme.go +++ /dev/null @@ -1,54 +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" - "path/filepath" - "strings" - - "github.com/operator-framework/operator-sdk/internal/scaffold/input" -) - -// AddToScheme is the input needed to generate an addtoscheme__.go file -type AddToScheme struct { - input.Input - - // Resource defines the inputs for the new api - Resource *Resource -} - -func (s *AddToScheme) GetInput() (input.Input, error) { - if s.Path == "" { - fileName := fmt.Sprintf("addtoscheme_%s_%s.go", - s.Resource.GoImportGroup, - strings.ToLower(s.Resource.Version)) - s.Path = filepath.Join(ApisDir, fileName) - } - s.TemplateBody = addToSchemeTemplate - return s.Input, nil -} - -const addToSchemeTemplate = `package apis - -import ( - "{{ .Repo }}/pkg/apis/{{ .Resource.GoImportGroup}}/{{ .Resource.Version }}" -) - -func init() { - // Register the types with the Scheme so the components can map objects to GroupVersionKinds and back - AddToSchemes = append(AddToSchemes, {{ .Resource.Version }}.SchemeBuilder.AddToScheme) -} -` diff --git a/internal/scaffold/addtoscheme_test.go b/internal/scaffold/addtoscheme_test.go deleted file mode 100644 index b983ea27c34..00000000000 --- a/internal/scaffold/addtoscheme_test.go +++ /dev/null @@ -1,50 +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 ( - "testing" - - "github.com/operator-framework/operator-sdk/internal/util/diffutil" -) - -func TestAddToScheme(t *testing.T) { - r, err := NewResource(appAPIVersion, appKind) - if err != nil { - t.Fatal(err) - } - s, buf := setupScaffoldAndWriter() - err = s.Execute(appConfig, &AddToScheme{Resource: r}) - if err != nil { - t.Fatalf("Failed to execute the scaffold: (%v)", err) - } - - if addtoschemeExp != buf.String() { - diffs := diffutil.Diff(addtoschemeExp, buf.String()) - t.Fatalf("Expected vs actual differs.\n%v", diffs) - } -} - -const addtoschemeExp = `package apis - -import ( - "github.com/example-inc/app-operator/pkg/apis/app/v1alpha1" -) - -func init() { - // Register the types with the Scheme so the components can map objects to GroupVersionKinds and back - AddToSchemes = append(AddToSchemes, v1alpha1.SchemeBuilder.AddToScheme) -} -` diff --git a/internal/scaffold/ansible/dockerfilehybrid.go b/internal/scaffold/ansible/dockerfilehybrid.go deleted file mode 100644 index 7d0c4c3419f..00000000000 --- a/internal/scaffold/ansible/dockerfilehybrid.go +++ /dev/null @@ -1,108 +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 ansible - -import ( - "path/filepath" - - "github.com/operator-framework/operator-sdk/internal/scaffold" - "github.com/operator-framework/operator-sdk/internal/scaffold/input" -) - -//DockerfileHybrid - Dockerfile for a hybrid operator -type DockerfileHybrid struct { - input.Input - - // Playbook - if true, include a COPY statement for playbook.yml - Playbook bool - - // Roles - if true, include a COPY statement for the roles directory - Roles bool - - // Watches - if true, include a COPY statement for watches.yaml - Watches bool - - // Requirements - if true, include a COPY and RUN to install Ansible requirements - Requirements bool -} - -// GetInput - gets the input -func (d *DockerfileHybrid) GetInput() (input.Input, error) { - if d.Path == "" { - d.Path = filepath.Join(scaffold.BuildDir, scaffold.DockerfileFile) - } - d.TemplateBody = dockerFileHybridAnsibleTmpl - d.Delims = AnsibleDelims - return d.Input, nil -} - -const dockerFileHybridAnsibleTmpl = `FROM registry.access.redhat.com/ubi8/ubi - -RUN mkdir -p /etc/ansible \ - && echo "localhost ansible_connection=local" > /etc/ansible/hosts \ - && echo '[defaults]' > /etc/ansible/ansible.cfg \ - && echo 'roles_path = /opt/ansible/roles' >> /etc/ansible/ansible.cfg \ - && echo 'library = /usr/share/ansible/openshift' >> /etc/ansible/ansible.cfg - -ENV OPERATOR=/usr/local/bin/ansible-operator \ - USER_UID=1001 \ - USER_NAME=ansible-operator\ - HOME=/opt/ansible - -# Install python dependencies -# Ensure fresh metadata rather than cached metadata in the base by running -# yum clean all && rm -rf /var/yum/cache/* first -RUN yum clean all && rm -rf /var/cache/yum/* \ - && yum -y update \ - && yum install -y libffi-devel openssl-devel python36-devel gcc python3-pip python3-setuptools \ - && pip3 install --no-cache-dir --ignore-installed ipaddress \ - ansible-runner==1.3.4 \ - ansible-runner-http==1.0.0 \ - openshift~=0.10.0 \ - ansible~=2.9 \ - jmespath \ - && yum remove -y gcc libffi-devel openssl-devel python36-devel \ - && yum clean all \ - && rm -rf /var/cache/yum - -COPY build/_output/bin/[[.ProjectName]] ${OPERATOR} -COPY bin /usr/local/bin - -RUN /usr/local/bin/user_setup - -# Ensure directory permissions are properly set -RUN mkdir -p ${HOME}/.ansible/tmp \ - && chown -R ${USER_UID}:0 ${HOME} \ - && chmod -R ug+rwx ${HOME} - -RUN TINIARCH=$(case $(arch) in x86_64) echo -n amd64 ;; ppc64le) echo -n ppc64el ;; aarch64) echo -n arm64 ;; *) echo -n $(arch) ;; esac) \ - && curl -L -o /tini https://github.com/krallin/tini/releases/latest/download/tini-$TINIARCH \ - && chmod +x /tini - -[[- if .Requirements ]] -COPY requirements.yml ${HOME}/requirements.yml -RUN ansible-galaxy collection install -r ${HOME}/requirements.yml \ - && chmod -R ug+rwx ${HOME}/.ansible[[ end ]] -[[- if .Watches ]] -COPY watches.yaml ${HOME}/watches.yaml[[ end ]] -[[- if .Roles ]] -COPY roles/ ${HOME}/roles/[[ end ]] -[[- if .Playbook ]] -COPY playbook.yml ${HOME}/playbook.yml[[ end ]] - -ENTRYPOINT ["/tini", "--", "/usr/local/bin/entrypoint"] - -USER ${USER_UID} -` diff --git a/internal/scaffold/ansible/entrypoint.go b/internal/scaffold/ansible/entrypoint.go deleted file mode 100644 index c559a0436fa..00000000000 --- a/internal/scaffold/ansible/entrypoint.go +++ /dev/null @@ -1,41 +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 ansible - -import ( - "path/filepath" - - "github.com/operator-framework/operator-sdk/internal/scaffold/input" -) - -// Entrypoint - entrypoint script -type Entrypoint struct { - StaticInput -} - -func (e *Entrypoint) GetInput() (input.Input, error) { - if e.Path == "" { - e.Path = filepath.Join("bin", "entrypoint") - } - e.TemplateBody = entrypointTmpl - e.IsExec = true - return e.Input, nil -} - -const entrypointTmpl = `#!/bin/bash -e - -cd $HOME -exec ${OPERATOR} --watches-file=$HOME/watches.yaml $@ -` diff --git a/internal/scaffold/ansible/go_mod.go b/internal/scaffold/ansible/go_mod.go deleted file mode 100644 index b9ccfa9435f..00000000000 --- a/internal/scaffold/ansible/go_mod.go +++ /dev/null @@ -1,61 +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 ansible - -import ( - "fmt" - - "github.com/operator-framework/operator-sdk/internal/scaffold/input" - "github.com/operator-framework/operator-sdk/internal/scaffold/internal/deps" -) - -const GoModFile = "go.mod" - -// GoMod - the go.mod file for an Ansible hybrid operator. -type GoMod struct { - input.Input -} - -func (s *GoMod) GetInput() (input.Input, error) { - if s.Path == "" { - s.Path = GoModFile - } - s.TemplateBody = goModTmpl - return s.Input, nil -} - -const goModTmpl = `module {{ .Repo }} - -go 1.13 - -require ( - github.com/operator-framework/operator-sdk master - sigs.k8s.io/controller-runtime v0.6.0 -) - -replace ( - k8s.io/client-go => k8s.io/client-go v0.18.2 // Required by prometheus-operator - github.com/Azure/go-autorest => github.com/Azure/go-autorest v13.3.2+incompatible // Required by OLM -) -` - -func PrintGoMod() error { - b, err := deps.ExecGoModTmpl(goModTmpl) - if err != nil { - return err - } - fmt.Print(string(b)) - return nil -} diff --git a/internal/scaffold/ansible/main.go b/internal/scaffold/ansible/main.go deleted file mode 100644 index 1c5eb2232cb..00000000000 --- a/internal/scaffold/ansible/main.go +++ /dev/null @@ -1,62 +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 ansible - -import ( - "path/filepath" - - "github.com/operator-framework/operator-sdk/internal/scaffold/input" -) - -// Main - main source file for ansible operator -type Main struct { - StaticInput -} - -func (m *Main) GetInput() (input.Input, error) { - if m.Path == "" { - m.Path = filepath.Join("cmd", "manager", "main.go") - } - m.TemplateBody = mainTmpl - return m.Input, nil -} - -const mainTmpl = `package main - -import ( - "os" - - // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.) - _ "k8s.io/client-go/plugin/pkg/client/auth" - - aoflags "github.com/operator-framework/operator-sdk/pkg/ansible/flags" - "github.com/operator-framework/operator-sdk/pkg/ansible" - "github.com/operator-framework/operator-sdk/pkg/log/zap" - - "github.com/spf13/pflag" - logf "sigs.k8s.io/controller-runtime/pkg/log" -) - -func main() { - aflags := aoflags.AddTo(pflag.CommandLine) - pflag.Parse() - logf.SetLogger(zap.Logger()) - - if err := ansible.Run(aflags); err != nil { - logf.Log.WithName("cmd").Error(err, "") - os.Exit(1) - } -} -` diff --git a/internal/scaffold/ansible/usersetup.go b/internal/scaffold/ansible/usersetup.go deleted file mode 100644 index d75e4b8a1fb..00000000000 --- a/internal/scaffold/ansible/usersetup.go +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2019 The Operator-SDK Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package ansible - -import ( - "path/filepath" - - "github.com/operator-framework/operator-sdk/internal/scaffold/input" -) - -// UserSetup - userSetup script -type UserSetup struct { - StaticInput -} - -func (u *UserSetup) GetInput() (input.Input, error) { - if u.Path == "" { - u.Path = filepath.Join("bin", "user_setup") - } - u.TemplateBody = userSetupTmpl - u.IsExec = true - return u.Input, nil -} - -const userSetupTmpl = `#!/bin/sh -set -x - -# ensure $HOME exists and is accessible by group 0 (we don't know what the runtime UID will be) -echo "${USER_NAME}:x:${USER_UID}:0:${USER_NAME} user:${HOME}:/sbin/nologin" >> /etc/passwd -mkdir -p "${HOME}/.ansible/tmp" -chown -R "${USER_UID}:0" "${HOME}" -chmod -R ug+rwx "${HOME}" - -# no need for this script to remain in the image after running -rm "$0" -` diff --git a/internal/scaffold/apis.go b/internal/scaffold/apis.go deleted file mode 100644 index e880fd750cc..00000000000 --- a/internal/scaffold/apis.go +++ /dev/null @@ -1,50 +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 ( - "path/filepath" - - "github.com/operator-framework/operator-sdk/internal/scaffold/input" -) - -const ApisFile = "apis.go" - -type Apis struct { - input.Input -} - -func (s *Apis) GetInput() (input.Input, error) { - if s.Path == "" { - s.Path = filepath.Join(ApisDir, ApisFile) - } - s.TemplateBody = apisTmpl - return s.Input, nil -} - -const apisTmpl = `package apis - -import ( - "k8s.io/apimachinery/pkg/runtime" -) - -// AddToSchemes may be used to add all resources defined in the project to a Scheme -var AddToSchemes runtime.SchemeBuilder - -// AddToScheme adds all Resources to the Scheme -func AddToScheme(s *runtime.Scheme) error { - return AddToSchemes.AddToScheme(s) -} -` diff --git a/internal/scaffold/apis_test.go b/internal/scaffold/apis_test.go deleted file mode 100644 index 870ae158e66..00000000000 --- a/internal/scaffold/apis_test.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 scaffold - -import ( - "testing" - - "github.com/operator-framework/operator-sdk/internal/util/diffutil" -) - -func TestApis(t *testing.T) { - s, buf := setupScaffoldAndWriter() - err := s.Execute(appConfig, &Apis{}) - if err != nil { - t.Fatalf("Failed to execute the scaffold: (%v)", err) - } - - if apisExp != buf.String() { - diffs := diffutil.Diff(apisExp, buf.String()) - t.Fatalf("Expected vs actual differs.\n%v", diffs) - } -} - -const apisExp = `package apis - -import ( - "k8s.io/apimachinery/pkg/runtime" -) - -// AddToSchemes may be used to add all resources defined in the project to a Scheme -var AddToSchemes runtime.SchemeBuilder - -// AddToScheme adds all Resources to the Scheme -func AddToScheme(s *runtime.Scheme) error { - return AddToSchemes.AddToScheme(s) -} -` diff --git a/internal/scaffold/boilerplate_go_txt.go b/internal/scaffold/boilerplate_go_txt.go deleted file mode 100644 index 025e0d628de..00000000000 --- a/internal/scaffold/boilerplate_go_txt.go +++ /dev/null @@ -1,52 +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 scaffold - -import ( - "io/ioutil" - "path/filepath" - - "github.com/operator-framework/operator-sdk/internal/scaffold/input" - - "github.com/spf13/afero" -) - -const ( - BoilerplateFile = "boilerplate.go.txt" - HackDir = "hack" -) - -type Boilerplate struct { - input.Input - - // BoilerplateSrcPath is the path to a file containing boilerplate text for - // generated Go files. - BoilerplateSrcPath string -} - -func (s *Boilerplate) GetInput() (input.Input, error) { - if s.Path == "" { - s.Path = filepath.Join(HackDir, BoilerplateFile) - } - return s.Input, nil -} - -var _ CustomRenderer = &Boilerplate{} - -func (s *Boilerplate) SetFS(_ afero.Fs) {} - -func (s *Boilerplate) CustomRender() ([]byte, error) { - return ioutil.ReadFile(s.BoilerplateSrcPath) -} diff --git a/internal/scaffold/boilerplate_go_txt_test.go b/internal/scaffold/boilerplate_go_txt_test.go deleted file mode 100644 index d12caaffbac..00000000000 --- a/internal/scaffold/boilerplate_go_txt_test.go +++ /dev/null @@ -1,102 +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 scaffold - -import ( - "testing" - - "github.com/operator-framework/operator-sdk/internal/util/diffutil" - - "github.com/spf13/afero" -) - -func TestBoilerplate(t *testing.T) { - s, buf := setupScaffoldAndWriter() - s.Fs = afero.NewMemMapFs() - f, err := afero.TempFile(s.Fs, "", "") - if err != nil { - t.Fatal(err) - } - if _, err = f.Write([]byte(boilerplate)); err != nil { - t.Fatal(err) - } - if err = f.Close(); err != nil { - t.Fatal(err) - } - s.BoilerplatePath = f.Name() - err = s.Execute(appConfig, &Apis{}) - if err != nil { - t.Fatalf("Failed to execute the scaffold: (%v)", err) - } - - if boilerplateExp != buf.String() { - diffs := diffutil.Diff(boilerplateExp, buf.String()) - t.Fatalf("Expected vs actual differs.\n%v", diffs) - } -} - -func TestBoilerplateMultiline(t *testing.T) { - s, buf := setupScaffoldAndWriter() - s.Fs = afero.NewMemMapFs() - f, err := afero.TempFile(s.Fs, "", "") - if err != nil { - t.Fatal(err) - } - if _, err = f.Write([]byte(boilerplateMulti)); err != nil { - t.Fatal(err) - } - if err = f.Close(); err != nil { - t.Fatal(err) - } - s.BoilerplatePath = f.Name() - err = s.Execute(appConfig, &Apis{}) - if err != nil { - t.Fatalf("Failed to execute the scaffold: (%v)", err) - } - - if boilerplateMultiExp != buf.String() { - diffs := diffutil.Diff(boilerplateMultiExp, buf.String()) - t.Fatalf("Expected vs actual differs.\n%v", diffs) - } -} - -const ( - boilerplate = `// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean ac -// velit a lacus tempor accumsan sit amet eu velit. Mauris orci lectus, -// rutrum vitae porttitor in, interdum nec mauris. Praesent porttitor -// lectus a sem volutpat, ac fringilla magna fermentum. Donec a nibh -// a urna fringilla eleifend. Curabitur vitae lorem nulla. Ut at risus -// varius, blandit risus quis, porta tellus. Vivamus scelerisque turpis -// quis viverra rhoncus. Aenean non arcu velit. -` - boilerplateExp = boilerplate + "\n" + apisExp - boilerplateMulti = `/* -Copyright The Project 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. -*/ -` - boilerplateMultiExp = boilerplateMulti + "\n" + apisExp -) diff --git a/internal/scaffold/build_dockerfile.go b/internal/scaffold/build_dockerfile.go deleted file mode 100644 index e1768fca427..00000000000 --- a/internal/scaffold/build_dockerfile.go +++ /dev/null @@ -1,52 +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 ( - "path/filepath" - - "github.com/operator-framework/operator-sdk/internal/scaffold/input" -) - -const DockerfileFile = "Dockerfile" - -type Dockerfile struct { - input.Input -} - -func (s *Dockerfile) GetInput() (input.Input, error) { - if s.Path == "" { - s.Path = filepath.Join(BuildDir, DockerfileFile) - } - s.TemplateBody = dockerfileTmpl - return s.Input, nil -} - -const dockerfileTmpl = `FROM registry.access.redhat.com/ubi8/ubi-minimal:latest - -ENV OPERATOR=/usr/local/bin/{{.ProjectName}} \ - USER_UID=1001 \ - USER_NAME={{.ProjectName}} - -# install operator binary -COPY build/_output/bin/{{.ProjectName}} ${OPERATOR} - -COPY build/bin /usr/local/bin -RUN /usr/local/bin/user_setup - -ENTRYPOINT ["/usr/local/bin/entrypoint"] - -USER ${USER_UID} -` diff --git a/internal/scaffold/build_dockerfile_test.go b/internal/scaffold/build_dockerfile_test.go deleted file mode 100644 index 47dec3f7438..00000000000 --- a/internal/scaffold/build_dockerfile_test.go +++ /dev/null @@ -1,51 +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 ( - "testing" - - "github.com/operator-framework/operator-sdk/internal/util/diffutil" -) - -func TestDockerfile(t *testing.T) { - s, buf := setupScaffoldAndWriter() - err := s.Execute(appConfig, &Dockerfile{}) - if err != nil { - t.Fatalf("Failed to execute the scaffold: (%v)", err) - } - - if dockerfileExp != buf.String() { - diffs := diffutil.Diff(dockerfileExp, buf.String()) - t.Fatalf("Expected vs actual differs.\n%v", diffs) - } -} - -const dockerfileExp = `FROM registry.access.redhat.com/ubi8/ubi-minimal:latest - -ENV OPERATOR=/usr/local/bin/app-operator \ - USER_UID=1001 \ - USER_NAME=app-operator - -# install operator binary -COPY build/_output/bin/app-operator ${OPERATOR} - -COPY build/bin /usr/local/bin -RUN /usr/local/bin/user_setup - -ENTRYPOINT ["/usr/local/bin/entrypoint"] - -USER ${USER_UID} -` diff --git a/internal/scaffold/constants.go b/internal/scaffold/constants.go index 1a341d956ed..c6ff3eb3a9f 100644 --- a/internal/scaffold/constants.go +++ b/internal/scaffold/constants.go @@ -23,15 +23,17 @@ const ( filePathSep = string(filepath.Separator) // dirs - CmdDir = "cmd" - ManagerDir = CmdDir + filePathSep + "manager" - PkgDir = "pkg" - ApisDir = PkgDir + filePathSep + "apis" - ControllerDir = PkgDir + filePathSep + "controller" - BuildDir = "build" - BuildTestDir = BuildDir + filePathSep + "test-framework" - BuildBinDir = BuildDir + filePathSep + "_output" + filePathSep + "bin" - BuildScriptDir = BuildDir + filePathSep + "bin" - DeployDir = "deploy" - CRDsDir = DeployDir + filePathSep + "crds" + CmdDir = "cmd" + ManagerDir = CmdDir + filePathSep + "manager" + PkgDir = "pkg" + ApisDir = PkgDir + filePathSep + "apis" + ControllerDir = PkgDir + filePathSep + "controller" + BuildDir = "build" + BuildBinDir = BuildDir + filePathSep + "_output" + filePathSep + "bin" + DeployDir = "deploy" + CRDsDir = DeployDir + filePathSep + "crds" + + // files + DockerfileFile = "Dockerfile" + OperatorYamlFile = "operator.yaml" ) diff --git a/internal/scaffold/controller.go b/internal/scaffold/controller.go deleted file mode 100644 index afb1eeda62f..00000000000 --- a/internal/scaffold/controller.go +++ /dev/null @@ -1,55 +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 ( - "path/filepath" - - "github.com/operator-framework/operator-sdk/internal/scaffold/input" -) - -const ControllerFile = "controller.go" - -type Controller struct { - input.Input -} - -func (s *Controller) GetInput() (input.Input, error) { - if s.Path == "" { - s.Path = filepath.Join(ControllerDir, ControllerFile) - } - s.TemplateBody = controllerTmpl - return s.Input, nil -} - -const controllerTmpl = `package controller - -import ( - "sigs.k8s.io/controller-runtime/pkg/manager" -) - -// AddToManagerFuncs is a list of functions to add all Controllers to the Manager -var AddToManagerFuncs []func(manager.Manager) error - -// AddToManager adds all Controllers to the Manager -func AddToManager(m manager.Manager) error { - for _, f := range AddToManagerFuncs { - if err := f(m); err != nil { - return err - } - } - return nil -} -` diff --git a/internal/scaffold/controller_kind.go b/internal/scaffold/controller_kind.go deleted file mode 100644 index a8258c7f3c0..00000000000 --- a/internal/scaffold/controller_kind.go +++ /dev/null @@ -1,283 +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" - "path" - "path/filepath" - "strings" - "unicode" - - "github.com/operator-framework/operator-sdk/internal/scaffold/input" -) - -// ControllerKind is the input needed to generate a pkg/controller//_controller.go file -type ControllerKind struct { - input.Input - - // Resource defines the inputs for the controller's primary resource - Resource *Resource - // CustomImport holds the import path for a built-in or custom Kubernetes - // API that this controller reconciles, if specified by the scaffold invoker. - CustomImport string - - // The following fields will be overwritten by GetInput(). - // - // ImportMap maps all imports destined for the scaffold to their import - // identifier, if any. - ImportMap map[string]string - // GoImportIdent is the import identifier for the API reconciled by this - // controller. - GoImportIdent string -} - -func (s *ControllerKind) GetInput() (input.Input, error) { - if s.Path == "" { - fileName := s.Resource.LowerKind + "_controller.go" - s.Path = filepath.Join(ControllerDir, s.Resource.LowerKind, fileName) - } - // Error if this file exists. - s.IfExistsAction = input.Error - s.TemplateBody = controllerKindTemplate - - // Set imports. - if err := s.setImports(); err != nil { - return input.Input{}, err - } - return s.Input, nil -} - -func (s *ControllerKind) setImports() (err error) { - s.ImportMap = controllerKindImports - importPath := "" - if s.CustomImport != "" { - importPath, s.GoImportIdent, err = getCustomAPIImportPathAndIdent(s.CustomImport) - if err != nil { - return err - } - } else { - importPath = path.Join(s.Repo, "pkg", "apis", s.Resource.GoImportGroup, s.Resource.Version) - s.GoImportIdent = s.Resource.GoImportGroup + s.Resource.Version - } - // Import identifiers must be unique within a file. - for p, id := range s.ImportMap { - if s.GoImportIdent == id && importPath != p { - // Append "api" to the conflicting import identifier. - s.GoImportIdent = s.GoImportIdent + "api" - break - } - } - s.ImportMap[importPath] = s.GoImportIdent - return nil -} - -func getCustomAPIImportPathAndIdent(m string) (p string, id string, err error) { - sm := strings.Split(m, "=") - for i, e := range sm { - if i == 0 { - p = strings.TrimSpace(e) - } else if i == 1 { - id = strings.TrimSpace(e) - } - } - if p == "" { - return "", "", fmt.Errorf(`custom import "%s" path is empty`, m) - } - if id == "" { - if len(sm) == 2 { - return "", "", - fmt.Errorf(`custom import "%s" identifier is empty, remove "=" from passed string`, m) - } - sp := strings.Split(p, "/") - if len(sp) > 1 { - id = sp[len(sp)-2] + sp[len(sp)-1] - } else { - id = sp[0] - } - id = strings.ToLower(id) - } - idb := &strings.Builder{} - // By definition, all package identifiers must be comprised of "_", unicode - // digits, and/or letters. - for _, r := range id { - if unicode.IsDigit(r) || unicode.IsLetter(r) || r == '_' { - if _, err := idb.WriteRune(r); err != nil { - return "", "", err - } - } - } - return p, idb.String(), nil -} - -var controllerKindImports = map[string]string{ - "k8s.io/api/core/v1": "corev1", - "k8s.io/apimachinery/pkg/api/errors": "", - "k8s.io/apimachinery/pkg/apis/meta/v1": "metav1", - "k8s.io/apimachinery/pkg/runtime": "", - "k8s.io/apimachinery/pkg/types": "", - "sigs.k8s.io/controller-runtime/pkg/client": "", - "sigs.k8s.io/controller-runtime/pkg/controller": "", - "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil": "", - "sigs.k8s.io/controller-runtime/pkg/handler": "", - "sigs.k8s.io/controller-runtime/pkg/manager": "", - "sigs.k8s.io/controller-runtime/pkg/reconcile": "", - "sigs.k8s.io/controller-runtime/pkg/log": "logf", - "sigs.k8s.io/controller-runtime/pkg/source": "", -} - -const controllerKindTemplate = `package {{ .Resource.LowerKind }} - -import ( - "context" - - {{range $p, $i := .ImportMap -}} - {{$i}} "{{$p}}" - {{end}} -) - -var log = logf.Log.WithName("controller_{{ .Resource.LowerKind }}") - -/** -* USER ACTION REQUIRED: This is a scaffold file intended for the user to modify with their own Controller -* business logic. Delete these comments after modifying this file.* - */ - -// Add creates a new {{ .Resource.Kind }} Controller and adds it to the Manager. The Manager will set fields on the Controller -// and Start it when the Manager is Started. -func Add(mgr manager.Manager) error { - return add(mgr, newReconciler(mgr)) -} - -// newReconciler returns a new reconcile.Reconciler -func newReconciler(mgr manager.Manager) reconcile.Reconciler { - return &Reconcile{{ .Resource.Kind }}{client: mgr.GetClient(), scheme: mgr.GetScheme()} -} - -// add adds a new Controller to mgr with r as the reconcile.Reconciler -func add(mgr manager.Manager, r reconcile.Reconciler) error { - // Create a new controller - c, err := controller.New("{{ .Resource.LowerKind }}-controller", mgr, controller.Options{Reconciler: r}) - if err != nil { - return err - } - - // Watch for changes to primary resource {{ .Resource.Kind }} - err = c.Watch(&source.Kind{Type: &{{ .GoImportIdent }}.{{ .Resource.Kind }}{}}, &handler.EnqueueRequestForObject{}) - if err != nil { - return err - } - - // TODO(user): Modify this to be the types you create that are owned by the primary resource - // Watch for changes to secondary resource Pods and requeue the owner {{ .Resource.Kind }} - err = c.Watch(&source.Kind{Type: &corev1.Pod{}}, &handler.EnqueueRequestForOwner{ - IsController: true, - OwnerType: &{{ .GoImportIdent }}.{{ .Resource.Kind }}{}, - }) - if err != nil { - return err - } - - return nil -} - -// blank assignment to verify that Reconcile{{ .Resource.Kind }} implements reconcile.Reconciler -var _ reconcile.Reconciler = &Reconcile{{ .Resource.Kind }}{} - -// Reconcile{{ .Resource.Kind }} reconciles a {{ .Resource.Kind }} object -type Reconcile{{ .Resource.Kind }} struct { - // This client, initialized using mgr.Client() above, is a split client - // that reads objects from the cache and writes to the apiserver - client client.Client - scheme *runtime.Scheme -} - -// Reconcile reads that state of the cluster for a {{ .Resource.Kind }} object and makes changes based on the state read -// and what is in the {{ .Resource.Kind }}.Spec -// TODO(user): Modify this Reconcile function to implement your Controller logic. This example creates -// a Pod as an example -// Note: -// The Controller will requeue the Request to be processed again if the returned error is non-nil or -// Result.Requeue is true, otherwise upon completion it will remove the work from the queue. -func (r *Reconcile{{ .Resource.Kind }}) Reconcile(request reconcile.Request) (reconcile.Result, error) { - reqLogger := log.WithValues("Request.Namespace", request.Namespace, "Request.Name", request.Name) - reqLogger.Info("Reconciling {{ .Resource.Kind }}") - - // Fetch the {{ .Resource.Kind }} instance - instance := &{{ .GoImportIdent }}.{{ .Resource.Kind }}{} - err := r.client.Get(context.TODO(), request.NamespacedName, instance) - if err != nil { - if errors.IsNotFound(err) { - // Request object not found, could have been deleted after reconcile request. - // Owned objects are automatically garbage collected. For additional cleanup logic use finalizers. - // Return and don't requeue - return reconcile.Result{}, nil - } - // Error reading the object - requeue the request. - return reconcile.Result{}, err - } - - // Define a new Pod object - pod := newPodForCR(instance) - - // Set {{ .Resource.Kind }} instance as the owner and controller - if err := controllerutil.SetControllerReference(instance, pod, r.scheme); err != nil { - return reconcile.Result{}, err - } - - // Check if this Pod already exists - found := &corev1.Pod{} - err = r.client.Get(context.TODO(), types.NamespacedName{Name: pod.Name, Namespace: pod.Namespace}, found) - if err != nil && errors.IsNotFound(err) { - reqLogger.Info("Creating a new Pod", "Pod.Namespace", pod.Namespace, "Pod.Name", pod.Name) - err = r.client.Create(context.TODO(), pod) - if err != nil { - return reconcile.Result{}, err - } - - // Pod created successfully - don't requeue - return reconcile.Result{}, nil - } else if err != nil { - return reconcile.Result{}, err - } - - // Pod already exists - don't requeue - reqLogger.Info("Skip reconcile: Pod already exists", "Pod.Namespace", found.Namespace, "Pod.Name", found.Name) - return reconcile.Result{}, nil -} - -// newPodForCR returns a busybox pod with the same name/namespace as the cr -func newPodForCR(cr *{{ .GoImportIdent }}.{{ .Resource.Kind }}) *corev1.Pod { - labels := map[string]string{ - "app": cr.Name, - } - return &corev1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Name: cr.Name + "-pod", - Namespace: cr.Namespace, - Labels: labels, - }, - Spec: corev1.PodSpec{ - Containers: []corev1.Container{ - { - Name: "busybox", - Image: "busybox", - Command: []string{"sleep", "3600"}, - }, - }, - }, - } -} -` diff --git a/internal/scaffold/controller_kind_test.go b/internal/scaffold/controller_kind_test.go deleted file mode 100644 index eea397f818d..00000000000 --- a/internal/scaffold/controller_kind_test.go +++ /dev/null @@ -1,239 +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 ( - "testing" - - "github.com/operator-framework/operator-sdk/internal/util/diffutil" -) - -func TestControllerKind(t *testing.T) { - r, err := NewResource(appAPIVersion, appKind) - if err != nil { - t.Fatal(err) - } - s, buf := setupScaffoldAndWriter() - err = s.Execute(appConfig, &ControllerKind{Resource: r}) - if err != nil { - t.Fatalf("Failed to execute the scaffold: (%v)", err) - } - - if controllerKindExp != buf.String() { - diffs := diffutil.Diff(controllerKindExp, buf.String()) - t.Fatalf("Expected vs actual differs.\n%v", diffs) - } -} - -const controllerKindExp = `package appservice - -import ( - "context" - - appv1alpha1 "github.com/example-inc/app-operator/pkg/apis/app/v1alpha1" - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/controller" - "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" - "sigs.k8s.io/controller-runtime/pkg/handler" - logf "sigs.k8s.io/controller-runtime/pkg/log" - "sigs.k8s.io/controller-runtime/pkg/manager" - "sigs.k8s.io/controller-runtime/pkg/reconcile" - "sigs.k8s.io/controller-runtime/pkg/source" -) - -var log = logf.Log.WithName("controller_appservice") - -/** -* USER ACTION REQUIRED: This is a scaffold file intended for the user to modify with their own Controller -* business logic. Delete these comments after modifying this file.* - */ - -// Add creates a new AppService Controller and adds it to the Manager. The Manager will set fields on the Controller -// and Start it when the Manager is Started. -func Add(mgr manager.Manager) error { - return add(mgr, newReconciler(mgr)) -} - -// newReconciler returns a new reconcile.Reconciler -func newReconciler(mgr manager.Manager) reconcile.Reconciler { - return &ReconcileAppService{client: mgr.GetClient(), scheme: mgr.GetScheme()} -} - -// add adds a new Controller to mgr with r as the reconcile.Reconciler -func add(mgr manager.Manager, r reconcile.Reconciler) error { - // Create a new controller - c, err := controller.New("appservice-controller", mgr, controller.Options{Reconciler: r}) - if err != nil { - return err - } - - // Watch for changes to primary resource AppService - err = c.Watch(&source.Kind{Type: &appv1alpha1.AppService{}}, &handler.EnqueueRequestForObject{}) - if err != nil { - return err - } - - // TODO(user): Modify this to be the types you create that are owned by the primary resource - // Watch for changes to secondary resource Pods and requeue the owner AppService - err = c.Watch(&source.Kind{Type: &corev1.Pod{}}, &handler.EnqueueRequestForOwner{ - IsController: true, - OwnerType: &appv1alpha1.AppService{}, - }) - if err != nil { - return err - } - - return nil -} - -// blank assignment to verify that ReconcileAppService implements reconcile.Reconciler -var _ reconcile.Reconciler = &ReconcileAppService{} - -// ReconcileAppService reconciles a AppService object -type ReconcileAppService struct { - // This client, initialized using mgr.Client() above, is a split client - // that reads objects from the cache and writes to the apiserver - client client.Client - scheme *runtime.Scheme -} - -// Reconcile reads that state of the cluster for a AppService object and makes changes based on the state read -// and what is in the AppService.Spec -// TODO(user): Modify this Reconcile function to implement your Controller logic. This example creates -// a Pod as an example -// Note: -// The Controller will requeue the Request to be processed again if the returned error is non-nil or -// Result.Requeue is true, otherwise upon completion it will remove the work from the queue. -func (r *ReconcileAppService) Reconcile(request reconcile.Request) (reconcile.Result, error) { - reqLogger := log.WithValues("Request.Namespace", request.Namespace, "Request.Name", request.Name) - reqLogger.Info("Reconciling AppService") - - // Fetch the AppService instance - instance := &appv1alpha1.AppService{} - err := r.client.Get(context.TODO(), request.NamespacedName, instance) - if err != nil { - if errors.IsNotFound(err) { - // Request object not found, could have been deleted after reconcile request. - // Owned objects are automatically garbage collected. For additional cleanup logic use finalizers. - // Return and don't requeue - return reconcile.Result{}, nil - } - // Error reading the object - requeue the request. - return reconcile.Result{}, err - } - - // Define a new Pod object - pod := newPodForCR(instance) - - // Set AppService instance as the owner and controller - if err := controllerutil.SetControllerReference(instance, pod, r.scheme); err != nil { - return reconcile.Result{}, err - } - - // Check if this Pod already exists - found := &corev1.Pod{} - err = r.client.Get(context.TODO(), types.NamespacedName{Name: pod.Name, Namespace: pod.Namespace}, found) - if err != nil && errors.IsNotFound(err) { - reqLogger.Info("Creating a new Pod", "Pod.Namespace", pod.Namespace, "Pod.Name", pod.Name) - err = r.client.Create(context.TODO(), pod) - if err != nil { - return reconcile.Result{}, err - } - - // Pod created successfully - don't requeue - return reconcile.Result{}, nil - } else if err != nil { - return reconcile.Result{}, err - } - - // Pod already exists - don't requeue - reqLogger.Info("Skip reconcile: Pod already exists", "Pod.Namespace", found.Namespace, "Pod.Name", found.Name) - return reconcile.Result{}, nil -} - -// newPodForCR returns a busybox pod with the same name/namespace as the cr -func newPodForCR(cr *appv1alpha1.AppService) *corev1.Pod { - labels := map[string]string{ - "app": cr.Name, - } - return &corev1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Name: cr.Name + "-pod", - Namespace: cr.Namespace, - Labels: labels, - }, - Spec: corev1.PodSpec{ - Containers: []corev1.Container{ - { - Name: "busybox", - Image: "busybox", - Command: []string{"sleep", "3600"}, - }, - }, - }, - } -} -` - -func TestGetCustomAPIImportPathAndIdentifier(t *testing.T) { - cases := []struct { - inputImport, wantImportPath, wantImportIdent string - wantErr bool - }{ - {"", "", "", true}, - {"=rbacv1", "", "", true}, - {"k8s.io/api/rbac-2/v1", "k8s.io/api/rbac-2/v1", - "rbac2v1", false}, - {"k8s.io/api/rbac/v1=rbacv1", "k8s.io/api/rbac/v1", - "rbacv1", false}, - {"k8s.io/api/rbac/v1=rbac-v1", "k8s.io/api/rbac/v1", - "rbacv1", false}, - {"k8s.io/api/rb_ac/v1", "k8s.io/api/rb_ac/v1", - "rb_acv1", false}, - {"k8s.io/api/rbac/v1=rbaC_v1", "k8s.io/api/rbac/v1", - "rbaC_v1", false}, - {"k8s.io/api/rbac/v1=", "", "", true}, - {"k8s.io/api/rbac/v1=rbacv1=", "k8s.io/api/rbac/v1", - "rbacv1", false}, - {"k8s.io/api/rbac/v1=Rbacv1", "k8s.io/api/rbac/v1", - "Rbacv1", false}, - } - - for _, c := range cases { - gotPath, gotIdent, err := getCustomAPIImportPathAndIdent(c.inputImport) - if err != nil && !c.wantErr { - t.Errorf(`wanted import path "%s" and identifier "%s" from "%s", got error %v`, c.wantImportPath, - c.wantImportIdent, c.inputImport, err) - continue - } - if err == nil && c.wantErr { - t.Errorf(`wanted error from "%s", got import path "%s" and identifier "%s"`, c.inputImport, - c.wantImportPath, c.wantImportIdent) - continue - } - if gotPath != c.wantImportPath { - t.Errorf(`wanted import path "%s" from "%s", got "%s"`, c.wantImportPath, c.inputImport, gotPath) - } - if gotIdent != c.wantImportIdent { - t.Errorf(`wanted import identifier "%s" from "%s", got "%s"`, c.wantImportIdent, - c.inputImport, gotIdent) - } - } -} diff --git a/internal/scaffold/controller_test.go b/internal/scaffold/controller_test.go deleted file mode 100644 index 544246be71f..00000000000 --- a/internal/scaffold/controller_test.go +++ /dev/null @@ -1,54 +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 ( - "testing" - - "github.com/operator-framework/operator-sdk/internal/util/diffutil" -) - -func TestController(t *testing.T) { - s, buf := setupScaffoldAndWriter() - err := s.Execute(appConfig, &Controller{}) - if err != nil { - t.Fatalf("Failed to execute the scaffold: (%v)", err) - } - - if controllerExp != buf.String() { - diffs := diffutil.Diff(controllerKindExp, buf.String()) - t.Fatalf("Expected vs actual differs.\n%v", diffs) - } -} - -const controllerExp = `package controller - -import ( - "sigs.k8s.io/controller-runtime/pkg/manager" -) - -// AddToManagerFuncs is a list of functions to add all Controllers to the Manager -var AddToManagerFuncs []func(manager.Manager) error - -// AddToManager adds all Controllers to the Manager -func AddToManager(m manager.Manager) error { - for _, f := range AddToManagerFuncs { - if err := f(m); err != nil { - return err - } - } - return nil -} -` diff --git a/internal/scaffold/doc.go b/internal/scaffold/doc.go deleted file mode 100644 index a5abe08457f..00000000000 --- a/internal/scaffold/doc.go +++ /dev/null @@ -1,50 +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 ( - "path/filepath" - "strings" - - "github.com/operator-framework/operator-sdk/internal/scaffold/input" -) - -const DocFile = "doc.go" - -// Doc is the input needed to generate a pkg/apis///doc.go file -type Doc struct { - input.Input - - // Resource defines the inputs for the new doc file - Resource *Resource -} - -func (s *Doc) GetInput() (input.Input, error) { - if s.Path == "" { - s.Path = filepath.Join(ApisDir, - s.Resource.GoImportGroup, - strings.ToLower(s.Resource.Version), - DocFile) - } - s.IfExistsAction = input.Skip - s.TemplateBody = docTemplate - return s.Input, nil -} - -const docTemplate = `// Package {{.Resource.Version}} contains API Schema definitions for the {{ .Resource.Group }} {{.Resource.Version}} API group -// +k8s:deepcopy-gen=package,register -// +groupName={{ .Resource.FullGroup }} -package {{.Resource.Version}} -` diff --git a/internal/scaffold/doc_test.go b/internal/scaffold/doc_test.go deleted file mode 100644 index 10e43edbbd0..00000000000 --- a/internal/scaffold/doc_test.go +++ /dev/null @@ -1,44 +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 ( - "testing" - - "github.com/operator-framework/operator-sdk/internal/util/diffutil" -) - -func TestDoc(t *testing.T) { - r, err := NewResource(appAPIVersion, appKind) - if err != nil { - t.Fatal(err) - } - s, buf := setupScaffoldAndWriter() - err = s.Execute(appConfig, &Doc{Resource: r}) - if err != nil { - t.Fatalf("Failed to execute the scaffold: (%v)", err) - } - - if docExp != buf.String() { - diffs := diffutil.Diff(docExp, buf.String()) - t.Fatalf("Expected vs actual differs.\n%v", diffs) - } -} - -const docExp = `// Package v1alpha1 contains API Schema definitions for the app v1alpha1 API group -// +k8s:deepcopy-gen=package,register -// +groupName=app.example.com -package v1alpha1 -` diff --git a/internal/scaffold/entrypoint.go b/internal/scaffold/entrypoint.go deleted file mode 100644 index 24080b72599..00000000000 --- a/internal/scaffold/entrypoint.go +++ /dev/null @@ -1,42 +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 scaffold - -import ( - "path/filepath" - - "github.com/operator-framework/operator-sdk/internal/scaffold/input" -) - -const EntrypointFile = "entrypoint" - -// Entrypoint - entrypoint script -type Entrypoint struct { - input.Input -} - -func (e *Entrypoint) GetInput() (input.Input, error) { - if e.Path == "" { - e.Path = filepath.Join(BuildScriptDir, EntrypointFile) - } - e.TemplateBody = entrypointTmpl - e.IsExec = true - return e.Input, nil -} - -const entrypointTmpl = `#!/bin/sh -e - -exec ${OPERATOR} $@ -` diff --git a/internal/scaffold/entrypoint_test.go b/internal/scaffold/entrypoint_test.go deleted file mode 100644 index 31b5fa3975f..00000000000 --- a/internal/scaffold/entrypoint_test.go +++ /dev/null @@ -1,39 +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 scaffold - -import ( - "testing" - - "github.com/operator-framework/operator-sdk/internal/util/diffutil" -) - -func TestEntrypointTest(t *testing.T) { - s, buf := setupScaffoldAndWriter() - err := s.Execute(appConfig, &Entrypoint{}) - if err != nil { - t.Fatalf("Failed to execute the scaffold: (%v)", err) - } - - if entrypointExp != buf.String() { - diffs := diffutil.Diff(entrypointExp, buf.String()) - t.Fatalf("Expected vs actual differs.\n%v", diffs) - } -} - -const entrypointExp = `#!/bin/sh -e - -exec ${OPERATOR} $@ -` diff --git a/internal/scaffold/gitignore.go b/internal/scaffold/gitignore.go deleted file mode 100644 index a53d52b5ba1..00000000000 --- a/internal/scaffold/gitignore.go +++ /dev/null @@ -1,112 +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 ( - "github.com/operator-framework/operator-sdk/internal/scaffold/input" -) - -const GitignoreFile = ".gitignore" - -type Gitignore struct { - input.Input -} - -func (s *Gitignore) GetInput() (input.Input, error) { - if s.Path == "" { - s.Path = GitignoreFile - } - s.TemplateBody = gitignoreTmpl - return s.Input, nil -} - -const gitignoreTmpl = `# Temporary Build Files -build/_output -build/_test -# Created by https://www.gitignore.io/api/go,vim,emacs,visualstudiocode -### Emacs ### -# -*- mode: gitignore; -*- -*~ -\#*\# -/.emacs.desktop -/.emacs.desktop.lock -*.elc -auto-save-list -tramp -.\#* -# Org-mode -.org-id-locations -*_archive -# flymake-mode -*_flymake.* -# eshell files -/eshell/history -/eshell/lastdir -# elpa packages -/elpa/ -# reftex files -*.rel -# AUCTeX auto folder -/auto/ -# cask packages -.cask/ -dist/ -# Flycheck -flycheck_*.el -# server auth directory -/server/ -# projectiles files -.projectile -projectile-bookmarks.eld -# directory configuration -.dir-locals.el -# saveplace -places -# url cache -url/cache/ -# cedet -ede-projects.el -# smex -smex-items -# company-statistics -company-statistics-cache.el -# anaconda-mode -anaconda-mode/ -### Go ### -# Binaries for programs and plugins -*.exe -*.exe~ -*.dll -*.so -*.dylib -# Test binary, build with 'go test -c' -*.test -# Output of the go coverage tool, specifically when used with LiteIDE -*.out -### Vim ### -# swap -.sw[a-p] -.*.sw[a-p] -# session -Session.vim -# temporary -.netrwhist -# auto-generated tag files -tags -### VisualStudioCode ### -.vscode/* -.history -# End of https://www.gitignore.io/api/go,vim,emacs,visualstudiocode -` diff --git a/internal/scaffold/gitignore_test.go b/internal/scaffold/gitignore_test.go deleted file mode 100644 index 3de48a90910..00000000000 --- a/internal/scaffold/gitignore_test.go +++ /dev/null @@ -1,113 +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 ( - "testing" - - "github.com/operator-framework/operator-sdk/internal/util/diffutil" -) - -func TestGitignore(t *testing.T) { - s, buf := setupScaffoldAndWriter() - err := s.Execute(appConfig, &Gitignore{}) - if err != nil { - t.Fatalf("Failed to execute the scaffold: (%v)", err) - } - - if gitignoreExp != buf.String() { - diffs := diffutil.Diff(gitignoreExp, buf.String()) - t.Fatalf("Expected vs actual differs.\n%v", diffs) - } -} - -const gitignoreExp = `# Temporary Build Files -build/_output -build/_test -# Created by https://www.gitignore.io/api/go,vim,emacs,visualstudiocode -### Emacs ### -# -*- mode: gitignore; -*- -*~ -\#*\# -/.emacs.desktop -/.emacs.desktop.lock -*.elc -auto-save-list -tramp -.\#* -# Org-mode -.org-id-locations -*_archive -# flymake-mode -*_flymake.* -# eshell files -/eshell/history -/eshell/lastdir -# elpa packages -/elpa/ -# reftex files -*.rel -# AUCTeX auto folder -/auto/ -# cask packages -.cask/ -dist/ -# Flycheck -flycheck_*.el -# server auth directory -/server/ -# projectiles files -.projectile -projectile-bookmarks.eld -# directory configuration -.dir-locals.el -# saveplace -places -# url cache -url/cache/ -# cedet -ede-projects.el -# smex -smex-items -# company-statistics -company-statistics-cache.el -# anaconda-mode -anaconda-mode/ -### Go ### -# Binaries for programs and plugins -*.exe -*.exe~ -*.dll -*.so -*.dylib -# Test binary, build with 'go test -c' -*.test -# Output of the go coverage tool, specifically when used with LiteIDE -*.out -### Vim ### -# swap -.sw[a-p] -.*.sw[a-p] -# session -Session.vim -# temporary -.netrwhist -# auto-generated tag files -tags -### VisualStudioCode ### -.vscode/* -.history -# End of https://www.gitignore.io/api/go,vim,emacs,visualstudiocode -` diff --git a/internal/scaffold/go_mod.go b/internal/scaffold/go_mod.go deleted file mode 100644 index 35948c8d31f..00000000000 --- a/internal/scaffold/go_mod.go +++ /dev/null @@ -1,60 +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 scaffold - -import ( - "fmt" - - "github.com/operator-framework/operator-sdk/internal/scaffold/input" - "github.com/operator-framework/operator-sdk/internal/scaffold/internal/deps" -) - -const GoModFile = "go.mod" - -type GoMod struct { - input.Input -} - -func (s *GoMod) GetInput() (input.Input, error) { - if s.Path == "" { - s.Path = GoModFile - } - s.TemplateBody = goModTmpl - return s.Input, nil -} - -const goModTmpl = `module {{ .Repo }} - -go 1.13 - -require ( - github.com/operator-framework/operator-sdk master - sigs.k8s.io/controller-runtime v0.6.0 -) - -replace ( - k8s.io/client-go => k8s.io/client-go v0.18.2 // Required by prometheus-operator - github.com/Azure/go-autorest => github.com/Azure/go-autorest v13.3.2+incompatible // Required by OLM -) -` - -func PrintGoMod() error { - b, err := deps.ExecGoModTmpl(goModTmpl) - if err != nil { - return err - } - fmt.Print(string(b)) - return nil -} diff --git a/internal/scaffold/go_mod_test.go b/internal/scaffold/go_mod_test.go deleted file mode 100644 index 6a380095cbb..00000000000 --- a/internal/scaffold/go_mod_test.go +++ /dev/null @@ -1,62 +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 scaffold - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestGoModGetInput(t *testing.T) { - - testCases := []struct { - name string - path string - expPath string - expTemplateBody string - }{ - { - name: "empty path should default", - path: "", - expPath: "go.mod", - expTemplateBody: goModTmpl, - }, - { - name: "path should remain the same", - path: "mygo.mod", - expPath: "mygo.mod", - expTemplateBody: goModTmpl, - }, - } - - for _, tc := range testCases { - testobj := GoMod{} - testobj.Path = tc.path - - t.Run(tc.name, func(t *testing.T) { - input, err := testobj.GetInput() - if err != nil { - t.Fatal("GetInput() should not error out") - } - - assert.NotNil(t, input) - assert.Equal(t, tc.expPath, testobj.Path) - assert.Equal(t, tc.expTemplateBody, testobj.TemplateBody) - }) - } -} - -// Needs a way to gest out the PrintGoMod method diff --git a/internal/scaffold/group.go b/internal/scaffold/group.go deleted file mode 100644 index eaf0e26feef..00000000000 --- a/internal/scaffold/group.go +++ /dev/null @@ -1,47 +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 scaffold - -import ( - "path/filepath" - - "github.com/operator-framework/operator-sdk/internal/scaffold/input" -) - -const GroupFile = "group.go" - -type Group struct { - input.Input - - Resource *Resource -} - -var _ input.File = &Group{} - -func (s *Group) GetInput() (input.Input, error) { - if s.Path == "" { - s.Path = filepath.Join(ApisDir, s.Resource.GoImportGroup, GroupFile) - } - s.TemplateBody = groupTmpl - return s.Input, nil -} - -const groupTmpl = `// Package {{.Resource.GoImportGroup}} contains {{.Resource.GoImportGroup}} API versions. -// -// This file ensures Go source parsers acknowledge the {{.Resource.GoImportGroup}} package -// and any child packages. It can be removed if any other Go source files are -// added to this package. -package {{.Resource.GoImportGroup}} -` diff --git a/internal/scaffold/group_test.go b/internal/scaffold/group_test.go deleted file mode 100644 index 090f33ee1ca..00000000000 --- a/internal/scaffold/group_test.go +++ /dev/null @@ -1,74 +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 scaffold - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestGroupGetInput(t *testing.T) { - - testCases := []struct { - name string - path string - resource *Resource - expPath string - expTemplateBody string - }{ - { - name: "empty path should default", - path: "", - resource: &Resource{}, - expPath: "pkg/apis/group.go", - expTemplateBody: groupTmpl, - }, - { - name: "empty path should use resource value", - path: "", - resource: &Resource{ - GoImportGroup: "zeusville", - }, - expPath: "pkg/apis/zeusville/group.go", - expTemplateBody: groupTmpl, - }, - { - name: "path should remain the same", - path: "mygroup.file", - expPath: "mygroup.file", - expTemplateBody: groupTmpl, - }, - } - - for _, tc := range testCases { - testobj := Group{} - testobj.Path = tc.path - testobj.Resource = tc.resource - - t.Run(tc.name, func(t *testing.T) { - input, err := testobj.GetInput() - if err != nil { - t.Fatal("GetInput() should not error out") - } - - assert.NotNil(t, input) - assert.Equal(t, tc.expPath, testobj.Path) - assert.Equal(t, tc.expTemplateBody, testobj.TemplateBody) - }) - } -} - -// Needs a way to gest out the PrintGoMod method diff --git a/internal/scaffold/helm/dockerfilehybrid.go b/internal/scaffold/helm/dockerfilehybrid.go deleted file mode 100644 index c283e2424fd..00000000000 --- a/internal/scaffold/helm/dockerfilehybrid.go +++ /dev/null @@ -1,65 +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 ( - "path/filepath" - - "github.com/operator-framework/operator-sdk/internal/scaffold" - "github.com/operator-framework/operator-sdk/internal/scaffold/input" -) - -//DockerfileHybrid - Dockerfile for a hybrid operator -type DockerfileHybrid struct { - input.Input - - // HelmCharts - if true, include a COPY statement for the helm-charts directory - HelmCharts bool - - // Watches - if true, include a COPY statement for watches.yaml - Watches bool -} - -// GetInput - gets the input -func (d *DockerfileHybrid) GetInput() (input.Input, error) { - if d.Path == "" { - d.Path = filepath.Join(scaffold.BuildDir, scaffold.DockerfileFile) - } - d.TemplateBody = dockerFileHybridHelmTmpl - return d.Input, nil -} - -const dockerFileHybridHelmTmpl = `FROM registry.access.redhat.com/ubi8/ubi-minimal:latest - -ENV OPERATOR=/usr/local/bin/helm-operator \ - USER_UID=1001 \ - USER_NAME=helm \ - HOME=/opt/helm - -{{- if .Watches }} -COPY watches.yaml ${HOME}/watches.yaml{{ end }} - -# install operator binary -COPY build/_output/bin/{{.ProjectName}} ${OPERATOR} - -COPY bin /usr/local/bin -RUN /usr/local/bin/user_setup - -{{- if .HelmCharts }} -COPY helm-charts/ ${HOME}/helm-charts/{{ end }} - -ENTRYPOINT ["/usr/local/bin/entrypoint"] - -USER ${USER_UID}` diff --git a/internal/scaffold/helm/entrypoint.go b/internal/scaffold/helm/entrypoint.go deleted file mode 100644 index 07388939625..00000000000 --- a/internal/scaffold/helm/entrypoint.go +++ /dev/null @@ -1,41 +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 ( - "path/filepath" - - "github.com/operator-framework/operator-sdk/internal/scaffold/input" -) - -// Entrypoint - entrypoint script -type Entrypoint struct { - input.Input -} - -func (e *Entrypoint) GetInput() (input.Input, error) { - if e.Path == "" { - e.Path = filepath.Join("bin", "entrypoint") - } - e.TemplateBody = entrypointTmpl - e.IsExec = true - return e.Input, nil -} - -const entrypointTmpl = `#!/bin/sh -e - -cd $HOME -exec ${OPERATOR} --watches-file=$HOME/watches.yaml $@ -` diff --git a/internal/scaffold/helm/go_mod.go b/internal/scaffold/helm/go_mod.go deleted file mode 100644 index dee9cd50dc0..00000000000 --- a/internal/scaffold/helm/go_mod.go +++ /dev/null @@ -1,61 +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" - - "github.com/operator-framework/operator-sdk/internal/scaffold/input" - "github.com/operator-framework/operator-sdk/internal/scaffold/internal/deps" -) - -const GoModFile = "go.mod" - -// GoMod - the go.mod file for a Helm hybrid operator. -type GoMod struct { - input.Input -} - -func (s *GoMod) GetInput() (input.Input, error) { - if s.Path == "" { - s.Path = GoModFile - } - s.TemplateBody = goModTmpl - return s.Input, nil -} - -const goModTmpl = `module {{ .Repo }} - -go 1.13 - -require ( - github.com/operator-framework/operator-sdk master - sigs.k8s.io/controller-runtime v0.6.0 -) - -replace ( - k8s.io/client-go => k8s.io/client-go v0.18.2 // Required by prometheus-operator - github.com/Azure/go-autorest => github.com/Azure/go-autorest v13.3.2+incompatible // Required by OLM -) -` - -func PrintGoMod() error { - b, err := deps.ExecGoModTmpl(goModTmpl) - if err != nil { - return err - } - fmt.Print(string(b)) - return nil -} diff --git a/internal/scaffold/helm/main.go b/internal/scaffold/helm/main.go deleted file mode 100644 index 89c20216d58..00000000000 --- a/internal/scaffold/helm/main.go +++ /dev/null @@ -1,62 +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 ( - "path/filepath" - - "github.com/operator-framework/operator-sdk/internal/scaffold/input" -) - -// Main - main source file for helm operator -type Main struct { - input.Input -} - -func (m *Main) GetInput() (input.Input, error) { - if m.Path == "" { - m.Path = filepath.Join("cmd", "manager", "main.go") - } - m.TemplateBody = mainTmpl - return m.Input, nil -} - -const mainTmpl = `package main - -import ( - "os" - - // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.) - _ "k8s.io/client-go/plugin/pkg/client/auth" - - hoflags "github.com/operator-framework/operator-sdk/pkg/helm/flags" - "github.com/operator-framework/operator-sdk/pkg/helm" - "github.com/operator-framework/operator-sdk/pkg/log/zap" - - "github.com/spf13/pflag" - logf "sigs.k8s.io/controller-runtime/pkg/log" -) - -func main() { - hflags := hoflags.AddTo(pflag.CommandLine) - pflag.Parse() - logf.SetLogger(zap.Logger()) - - if err := helm.Run(hflags); err != nil { - logf.Log.WithName("cmd").Error(err, "") - os.Exit(1) - } -} -` diff --git a/internal/scaffold/helm/usersetup.go b/internal/scaffold/helm/usersetup.go deleted file mode 100644 index db3eacf0593..00000000000 --- a/internal/scaffold/helm/usersetup.go +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2019 The Operator-SDK Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package helm - -import ( - "path/filepath" - - "github.com/operator-framework/operator-sdk/internal/scaffold/input" -) - -// UserSetup - userSetup script -type UserSetup struct { - input.Input -} - -func (u *UserSetup) GetInput() (input.Input, error) { - if u.Path == "" { - u.Path = filepath.Join("bin", "user_setup") - } - u.TemplateBody = userSetupTmpl - u.IsExec = true - return u.Input, nil -} - -const userSetupTmpl = `#!/bin/sh -set -x - -# ensure $HOME exists and is accessible by group 0 (we don't know what the runtime UID will be) -echo "${USER_NAME}:x:${USER_UID}:0:${USER_NAME} user:${HOME}:/sbin/nologin" >> /etc/passwd -mkdir -p "${HOME}" -chown "${USER_UID}:0" "${HOME}" -chmod ug+rwx "${HOME}" - -# no need for this script to remain in the image after running -rm "$0" -` diff --git a/internal/scaffold/operator.go b/internal/scaffold/operator.go deleted file mode 100644 index fffa295e629..00000000000 --- a/internal/scaffold/operator.go +++ /dev/null @@ -1,70 +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 ( - "path/filepath" - - "github.com/operator-framework/operator-sdk/internal/scaffold/input" -) - -const OperatorYamlFile = "operator.yaml" - -type Operator struct { - input.Input -} - -func (s *Operator) GetInput() (input.Input, error) { - if s.Path == "" { - s.Path = filepath.Join(DeployDir, 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 - command: - - {{.ProjectName}} - 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/operator_test.go b/internal/scaffold/operator_test.go deleted file mode 100644 index fc11bcdba12..00000000000 --- a/internal/scaffold/operator_test.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 scaffold - -import ( - "testing" - - "github.com/operator-framework/operator-sdk/internal/util/diffutil" -) - -func TestOperator(t *testing.T) { - s, buf := setupScaffoldAndWriter() - err := s.Execute(appConfig, &Operator{}) - if err != nil { - t.Fatalf("Failed to execute the scaffold: (%v)", err) - } - - if operatorExp != buf.String() { - diffs := diffutil.Diff(operatorExp, buf.String()) - t.Fatalf("Expected vs actual differs.\n%v", diffs) - } -} - -const operatorExp = `apiVersion: apps/v1 -kind: Deployment -metadata: - name: app-operator -spec: - replicas: 1 - selector: - matchLabels: - name: app-operator - template: - metadata: - labels: - name: app-operator - spec: - serviceAccountName: app-operator - containers: - - name: app-operator - # Replace this with the built image name - image: REPLACE_IMAGE - command: - - app-operator - imagePullPolicy: Always - env: - - name: WATCH_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: OPERATOR_NAME - value: "app-operator" -` diff --git a/internal/scaffold/register.go b/internal/scaffold/register.go deleted file mode 100644 index bd593004b38..00000000000 --- a/internal/scaffold/register.go +++ /dev/null @@ -1,66 +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 ( - "path/filepath" - "strings" - - "github.com/operator-framework/operator-sdk/internal/scaffold/input" -) - -const RegisterFile = "register.go" - -// Register is the input needed to generate a pkg/apis///register.go file -type Register struct { - input.Input - - // Resource defines the inputs for the new custom resource definition - Resource *Resource -} - -func (s *Register) GetInput() (input.Input, error) { - if s.Path == "" { - s.Path = filepath.Join(ApisDir, - s.Resource.GoImportGroup, - strings.ToLower(s.Resource.Version), - RegisterFile) - } - // Do not overwrite this file if it exists. - s.IfExistsAction = input.Skip - s.TemplateBody = registerTemplate - return s.Input, nil -} - -const registerTemplate = `// NOTE: Boilerplate only. Ignore this file. - -// Package {{.Resource.Version}} contains API Schema definitions for the {{ .Resource.Group }} {{.Resource.Version}} API group -// +k8s:deepcopy-gen=package,register -// +groupName={{ .Resource.FullGroup }} -package {{.Resource.Version}} - -import ( - "k8s.io/apimachinery/pkg/runtime/schema" - "sigs.k8s.io/controller-runtime/pkg/scheme" -) - -var ( - // SchemeGroupVersion is group version used to register these objects - SchemeGroupVersion = schema.GroupVersion{Group: "{{ .Resource.FullGroup }}", Version: "{{ .Resource.Version }}"} - - // SchemeBuilder is used to add go types to the GroupVersionKind scheme - SchemeBuilder = &scheme.Builder{GroupVersion: SchemeGroupVersion} -) -` diff --git a/internal/scaffold/register_test.go b/internal/scaffold/register_test.go deleted file mode 100644 index f92c1efb937..00000000000 --- a/internal/scaffold/register_test.go +++ /dev/null @@ -1,59 +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 ( - "testing" - - "github.com/operator-framework/operator-sdk/internal/util/diffutil" -) - -func TestRegister(t *testing.T) { - r, err := NewResource(appAPIVersion, appKind) - if err != nil { - t.Fatal(err) - } - s, buf := setupScaffoldAndWriter() - err = s.Execute(appConfig, &Register{Resource: r}) - if err != nil { - t.Fatalf("Failed to execute the scaffold: (%v)", err) - } - - if registerExp != buf.String() { - diffs := diffutil.Diff(registerExp, buf.String()) - t.Fatalf("Expected vs actual differs.\n%v", diffs) - } -} - -const registerExp = `// NOTE: Boilerplate only. Ignore this file. - -// Package v1alpha1 contains API Schema definitions for the app v1alpha1 API group -// +k8s:deepcopy-gen=package,register -// +groupName=app.example.com -package v1alpha1 - -import ( - "k8s.io/apimachinery/pkg/runtime/schema" - "sigs.k8s.io/controller-runtime/pkg/scheme" -) - -var ( - // SchemeGroupVersion is group version used to register these objects - SchemeGroupVersion = schema.GroupVersion{Group: "app.example.com", Version: "v1alpha1"} - - // SchemeBuilder is used to add go types to the GroupVersionKind scheme - SchemeBuilder = &scheme.Builder{GroupVersion: SchemeGroupVersion} -) -` diff --git a/internal/scaffold/scaffold.go b/internal/scaffold/scaffold.go index d9a486fe7c4..5563fdd045d 100644 --- a/internal/scaffold/scaffold.go +++ b/internal/scaffold/scaffold.go @@ -19,20 +19,18 @@ package scaffold import ( "bytes" "fmt" - "go/parser" - "go/token" "io" "os" "path/filepath" "strings" "text/template" - "github.com/operator-framework/operator-sdk/internal/scaffold/input" - "github.com/operator-framework/operator-sdk/internal/util/fileutil" - log "github.com/sirupsen/logrus" "github.com/spf13/afero" "golang.org/x/tools/imports" + + "github.com/operator-framework/operator-sdk/internal/scaffold/input" + "github.com/operator-framework/operator-sdk/internal/util/fileutil" ) // Scaffold writes Templates to scaffold new files @@ -47,11 +45,6 @@ type Scaffold struct { Fs afero.Fs // GetWriter returns a writer for writing scaffold files. GetWriter func(path string, mode os.FileMode) (io.Writer, error) - // BoilerplatePath is the path to a file containing Go boilerplate text. - BoilerplatePath string - - // boilerplateBytes are bytes of Go boilerplate text. - boilerplateBytes []byte } func (s *Scaffold) setFieldsAndValidate(t input.File) error { @@ -80,61 +73,6 @@ func (s *Scaffold) configure(cfg *input.Config) { s.ProjectName = cfg.ProjectName } -func validateBoilerplateBytes(b []byte) error { - // Append a 'package main' so we can parse the file. - fset := token.NewFileSet() - f, err := parser.ParseFile(fset, "", append([]byte("package main\n"), b...), parser.ParseComments) - if err != nil { - return fmt.Errorf("parse boilerplate comments: %v", err) - } - if len(f.Comments) == 0 { - return fmt.Errorf("boilerplate does not contain comments") - } - var cb []byte - for _, cg := range f.Comments { - for _, c := range cg.List { - cb = append(cb, []byte(strings.TrimSpace(c.Text)+"\n")...) - } - } - var tb []byte - tb, cb = bytes.TrimSpace(b), bytes.TrimSpace(cb) - if !bytes.Equal(tb, cb) { - return fmt.Errorf(`boilerplate contains text other than comments:\n"%s"\n`, tb) - } - return nil -} - -func wrapBoilerplateErr(err error, bp string) error { - return fmt.Errorf("boilerplate file %s: %v", bp, err) -} - -func (s *Scaffold) setBoilerplate() (err error) { - // If we've already set boilerplate bytes, don't overwrite them. - if len(s.boilerplateBytes) == 0 { - bp := s.BoilerplatePath - if bp == "" { - i, err := (&Boilerplate{}).GetInput() - if err != nil { - return wrapBoilerplateErr(err, i.Path) - } - if _, err := s.Fs.Stat(i.Path); err == nil { - bp = i.Path - } - } - if bp != "" { - b, err := afero.ReadFile(s.Fs, bp) - if err != nil { - return wrapBoilerplateErr(err, bp) - } - if err = validateBoilerplateBytes(b); err != nil { - return wrapBoilerplateErr(err, bp) - } - s.boilerplateBytes = append(bytes.TrimSpace(b), '\n', '\n') - } - } - return nil -} - // Execute executes scaffolding the Files func (s *Scaffold) Execute(cfg *input.Config, files ...input.File) error { if s.Fs == nil { @@ -144,11 +82,6 @@ func (s *Scaffold) Execute(cfg *input.Config, files ...input.File) error { s.GetWriter = fileutil.NewFileWriterFS(s.Fs).WriteCloser } - // Generate boilerplate file first so new Go files get headers. - if err := s.setBoilerplate(); err != nil { - return err - } - // Configure s using common fields from cfg. s.configure(cfg) @@ -252,11 +185,6 @@ func (s *Scaffold) doRender(i input.Input, e input.File, absPath string) error { } } - if isGoFile(absPath) && len(s.boilerplateBytes) != 0 { - if _, err = f.Write(s.boilerplateBytes); err != nil { - return err - } - } _, err = f.Write(b) log.Infoln("Created", i.Path) return err diff --git a/internal/scaffold/tools.go b/internal/scaffold/tools.go deleted file mode 100644 index 3a82652a98d..00000000000 --- a/internal/scaffold/tools.go +++ /dev/null @@ -1,40 +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 scaffold - -import ( - "github.com/operator-framework/operator-sdk/internal/scaffold/input" -) - -const ToolsFile = "tools.go" - -type Tools struct { - input.Input -} - -func (s *Tools) GetInput() (input.Input, error) { - if s.Path == "" { - s.Path = ToolsFile - } - s.TemplateBody = toolsTmpl - return s.Input, nil -} - -const toolsTmpl = `// +build tools - -// Place any runtime dependencies as imports in this file. -// Go modules will be forced to download and install them. -package tools -` diff --git a/internal/scaffold/tools_test.go b/internal/scaffold/tools_test.go deleted file mode 100644 index fab65b48817..00000000000 --- a/internal/scaffold/tools_test.go +++ /dev/null @@ -1,62 +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 scaffold - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestToolsGetInput(t *testing.T) { - - testCases := []struct { - name string - path string - expPath string - expTemplateBody string - }{ - { - name: "empty path should default", - path: "", - expPath: "tools.go", - expTemplateBody: toolsTmpl, - }, - { - name: "path should remain the same", - path: "mytools.go", - expPath: "mytools.go", - expTemplateBody: toolsTmpl, - }, - } - - for _, tc := range testCases { - testobj := Tools{} - testobj.Path = tc.path - - t.Run(tc.name, func(t *testing.T) { - input, err := testobj.GetInput() - if err != nil { - t.Fatal("GetInput() should not error out") - } - - assert.NotNil(t, input) - assert.Equal(t, tc.expPath, testobj.Path) - assert.Equal(t, tc.expTemplateBody, testobj.TemplateBody) - }) - } -} - -// Needs a way to gest out the PrintGoMod method diff --git a/internal/scaffold/types.go b/internal/scaffold/types.go deleted file mode 100644 index e6b0b325c64..00000000000 --- a/internal/scaffold/types.go +++ /dev/null @@ -1,93 +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 ( - "path/filepath" - "strings" - - "github.com/operator-framework/operator-sdk/internal/scaffold/input" -) - -// Types is the input needed to generate a pkg/apis///_types.go file -type Types struct { - input.Input - - // Resource defines the inputs for the new types file - Resource *Resource -} - -func (s *Types) GetInput() (input.Input, error) { - if s.Path == "" { - s.Path = filepath.Join(ApisDir, - s.Resource.GoImportGroup, - strings.ToLower(s.Resource.Version), - s.Resource.LowerKind+"_types.go") - } - // Error if this file exists. - s.IfExistsAction = input.Error - s.TemplateBody = typesTemplate - return s.Input, nil -} - -const typesTemplate = `package {{ .Resource.Version }} - -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! -// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. - -// {{.Resource.Kind}}Spec defines the desired state of {{.Resource.Kind}} -type {{.Resource.Kind}}Spec struct { - // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster - // Important: Run "operator-sdk generate k8s" to regenerate code after modifying this file - // Add custom validation using kubebuilder tags: https://book-v1.book.kubebuilder.io/beyond_basics/generating_crd.html -} - -// {{.Resource.Kind}}Status defines the observed state of {{.Resource.Kind}} -type {{.Resource.Kind}}Status struct { - // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster - // Important: Run "operator-sdk generate k8s" to regenerate code after modifying this file - // Add custom validation using kubebuilder tags: https://book-v1.book.kubebuilder.io/beyond_basics/generating_crd.html -} - -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -// {{.Resource.Kind}} is the Schema for the {{ .Resource.Resource }} API -// +kubebuilder:subresource:status -// +kubebuilder:resource:path={{.Resource.Resource}},scope=Namespaced -type {{.Resource.Kind}} struct { - metav1.TypeMeta ` + "`" + `json:",inline"` + "`" + ` - metav1.ObjectMeta ` + "`" + `json:"metadata,omitempty"` + "`" + ` - - Spec {{.Resource.Kind}}Spec ` + "`" + `json:"spec,omitempty"` + "`" + ` - Status {{.Resource.Kind}}Status ` + "`" + `json:"status,omitempty"` + "`" + ` -} - -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -// {{.Resource.Kind}}List contains a list of {{.Resource.Kind}} -type {{.Resource.Kind}}List struct { - metav1.TypeMeta ` + "`" + `json:",inline"` + "`" + ` - metav1.ListMeta ` + "`" + `json:"metadata,omitempty"` + "`" + ` - Items []{{ .Resource.Kind }} ` + "`" + `json:"items"` + "`" + ` -} - -func init() { - SchemeBuilder.Register(&{{.Resource.Kind}}{}, &{{.Resource.Kind}}List{}) -} -` diff --git a/internal/scaffold/types_test.go b/internal/scaffold/types_test.go deleted file mode 100644 index f5298ff3792..00000000000 --- a/internal/scaffold/types_test.go +++ /dev/null @@ -1,88 +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 ( - "testing" - - "github.com/operator-framework/operator-sdk/internal/util/diffutil" -) - -func TestTypes(t *testing.T) { - r, err := NewResource(appAPIVersion, appKind) - if err != nil { - t.Fatal(err) - } - s, buf := setupScaffoldAndWriter() - err = s.Execute(appConfig, &Types{Resource: r}) - if err != nil { - t.Fatalf("Failed to execute the scaffold: (%v)", err) - } - - if typesExp != buf.String() { - diffs := diffutil.Diff(typesExp, buf.String()) - t.Fatalf("Expected vs actual differs.\n%v", diffs) - } -} - -const typesExp = `package v1alpha1 - -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! -// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. - -// AppServiceSpec defines the desired state of AppService -type AppServiceSpec struct { - // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster - // Important: Run "operator-sdk generate k8s" to regenerate code after modifying this file - // Add custom validation using kubebuilder tags: https://book-v1.book.kubebuilder.io/beyond_basics/generating_crd.html -} - -// AppServiceStatus defines the observed state of AppService -type AppServiceStatus struct { - // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster - // Important: Run "operator-sdk generate k8s" to regenerate code after modifying this file - // Add custom validation using kubebuilder tags: https://book-v1.book.kubebuilder.io/beyond_basics/generating_crd.html -} - -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -// AppService is the Schema for the appservices API -// +kubebuilder:subresource:status -// +kubebuilder:resource:path=appservices,scope=Namespaced -type AppService struct { - metav1.TypeMeta ` + "`" + `json:",inline"` + "`" + ` - metav1.ObjectMeta ` + "`" + `json:"metadata,omitempty"` + "`" + ` - - Spec AppServiceSpec ` + "`" + `json:"spec,omitempty"` + "`" + ` - Status AppServiceStatus ` + "`" + `json:"status,omitempty"` + "`" + ` -} - -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -// AppServiceList contains a list of AppService -type AppServiceList struct { - metav1.TypeMeta ` + "`" + `json:",inline"` + "`" + ` - metav1.ListMeta ` + "`" + `json:"metadata,omitempty"` + "`" + ` - Items []AppService ` + "`" + `json:"items"` + "`" + ` -} - -func init() { - SchemeBuilder.Register(&AppService{}, &AppServiceList{}) -} -` diff --git a/internal/scorecard/config.go b/internal/scorecard/config.go deleted file mode 100644 index f1a0a3127d8..00000000000 --- a/internal/scorecard/config.go +++ /dev/null @@ -1,46 +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 scorecard - -import ( - "fmt" - - "sigs.k8s.io/yaml" -) - -// ValidateConfig takes a config for a plugin and returns a nil error if valid or an error -// explaining why the config is invalid -func (config PluginConfig) ValidateConfig(idx int) error { - // find plugin config type - pluginType := "" - if config.Basic != nil { - pluginType = "basic" - } - if config.Olm != nil { - if pluginType != "" { - return fmt.Errorf("plugin config can only contain one of: basic, olm") - } - pluginType = "olm" - } - if pluginType == "" { - marshalledConfig, err := yaml.Marshal(config) - if err != nil { - return fmt.Errorf("plugin #%d has a missing or incorrect type", idx) - } - return fmt.Errorf("plugin #%d has a missing or incorrect type. Invalid plugin config: %s", - idx, marshalledConfig) - } - return nil -} diff --git a/internal/scorecard/helpers/test_definitions.go b/internal/scorecard/helpers/test_definitions.go deleted file mode 100644 index 0ae8f16b9dd..00000000000 --- a/internal/scorecard/helpers/test_definitions.go +++ /dev/null @@ -1,55 +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 schelpers - -import ( - "context" - - scapiv1alpha2 "github.com/operator-framework/operator-sdk/pkg/apis/scorecard/v1alpha2" -) - -// Test provides methods for running scorecard tests -type Test interface { - GetName() string - GetDescription() string - GetLabels() map[string]string - Run(context.Context) *TestResult -} - -// TestResult contains a test's points, suggestions, and errors -type TestResult struct { - State scapiv1alpha2.State - Test Test - Suggestions []string - Errors []error - Log string - CRName string -} - -// TestInfo contains information about the scorecard test -type TestInfo struct { - Name string - Description string - Labels map[string]string -} - -// GetName return the test name -func (i TestInfo) GetName() string { return i.Name } - -// GetDescription returns the test description -func (i TestInfo) GetDescription() string { return i.Description } - -// GetLabels returns the labels for this test -func (i TestInfo) GetLabels() map[string]string { return i.Labels } diff --git a/internal/scorecard/helpers/versions.go b/internal/scorecard/helpers/versions.go deleted file mode 100644 index 35be36212d2..00000000000 --- a/internal/scorecard/helpers/versions.go +++ /dev/null @@ -1,38 +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 schelpers - -import ( - "fmt" - "strings" -) - -const v1alpha2 = "v1alpha2" - -const DefaultScorecardVersion = v1alpha2 -const LatestScorecardVersion = v1alpha2 - -var ScorecardVersions = []string{v1alpha2} - -func ValidateVersion(version string) error { - for _, a := range ScorecardVersions { - if a == version { - return nil - } - } - return fmt.Errorf("invalid scorecard version (%s); valid values: %s", version, - strings.Join(ScorecardVersions, ", ")) - -} diff --git a/internal/scorecard/helpers/versions_test.go b/internal/scorecard/helpers/versions_test.go deleted file mode 100644 index c1518d0dce5..00000000000 --- a/internal/scorecard/helpers/versions_test.go +++ /dev/null @@ -1,43 +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 schelpers - -import ( - "testing" -) - -func TestValidateVersions(t *testing.T) { - cases := []struct { - name string - version string - result []string - wantError bool - }{ - {"empty", "", nil, true}, - {"invalidVersion", "invalidVersion", nil, true}, - {"v1alpha2", v1alpha2, nil, false}, - } - - for _, c := range cases { - t.Run(c.name, func(t *testing.T) { - err := ValidateVersion(c.version) - if err != nil && !c.wantError { - t.Errorf("Wanted result %+q, got error: %v", c.result, err) - } else if err == nil && c.wantError { - t.Errorf("Wanted error, got nil") - } - }) - } -} diff --git a/internal/scorecard/interfaces.go b/internal/scorecard/interfaces.go deleted file mode 100644 index 2399766cd0b..00000000000 --- a/internal/scorecard/interfaces.go +++ /dev/null @@ -1,101 +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 scorecard - -import ( - "bytes" - "fmt" - "strings" - - "github.com/operator-framework/operator-sdk/internal/scaffold" - scplugins "github.com/operator-framework/operator-sdk/internal/scorecard/plugins" - scapiv1alpha2 "github.com/operator-framework/operator-sdk/pkg/apis/scorecard/v1alpha2" - "github.com/operator-framework/operator-sdk/version" - v1 "k8s.io/api/core/v1" -) - -type Plugin interface { - List() scapiv1alpha2.ScorecardOutput - Run() scapiv1alpha2.ScorecardOutput -} - -type basicOrOLMPlugin struct { - pluginType scplugins.PluginType - config scplugins.BasicAndOLMPluginConfig -} - -func (p basicOrOLMPlugin) List() scapiv1alpha2.ScorecardOutput { - res, err := scplugins.ListInternalPlugin(p.pluginType, p.config) - if err != nil { - Log.Errorf("%v", err) - return scapiv1alpha2.ScorecardOutput{} - } - return res -} - -func (p basicOrOLMPlugin) Run() scapiv1alpha2.ScorecardOutput { - pluginLogs := &bytes.Buffer{} - res, err := scplugins.RunInternalPlugin(p.pluginType, p.config, pluginLogs) - if err != nil { - var name string - if p.pluginType == scplugins.BasicOperator { - name = "Basic Tests" - } else if p.pluginType == scplugins.OLMIntegration { - name = "OLM Integration" - } - logs := fmt.Sprintf("%s:\nLogs: %s", err, pluginLogs.String()) - // output error to main logger as well for human-readable output - Log.Errorf("Plugin `%s` failed with error (%v)", name, err) - return failedPlugin(name, logs) - } - stderrString := pluginLogs.String() - if len(stderrString) != 0 { - Log.Warn(stderrString) - } - return res -} - -// setConfigDefaults sets certain config fields to default values if they are not set -func setConfigDefaults(config *scplugins.BasicAndOLMPluginConfig, kubeconfig string) { - if config.InitTimeout == 0 { - config.InitTimeout = 60 - } - if config.ProxyImage == "" { - config.ProxyImage = fmt.Sprintf("quay.io/operator-framework/scorecard-proxy:%s", - strings.TrimSuffix(version.Version, "+git")) - } - if config.ProxyPort == 0 { - config.ProxyPort = 8889 - } - if config.ProxyPullPolicy == "" { - config.ProxyPullPolicy = v1.PullAlways - } - if config.CRDsDir == "" { - config.CRDsDir = scaffold.CRDsDir - } - if config.Kubeconfig == "" { - config.Kubeconfig = kubeconfig - } -} - -func failedPlugin(name, log string) scapiv1alpha2.ScorecardOutput { - return scapiv1alpha2.ScorecardOutput{ - Results: []scapiv1alpha2.ScorecardTestResult{{ - Name: name, - Errors: []string{"plugin error"}, - Log: log, - }}, - } -} diff --git a/internal/scorecard/plugins/basic_tests.go b/internal/scorecard/plugins/basic_tests.go deleted file mode 100644 index ac07d05219d..00000000000 --- a/internal/scorecard/plugins/basic_tests.go +++ /dev/null @@ -1,173 +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 scplugins - -import ( - "context" - "encoding/json" - "fmt" - "net/http" - "strings" - - schelpers "github.com/operator-framework/operator-sdk/internal/scorecard/helpers" - scapiv1alpha2 "github.com/operator-framework/operator-sdk/pkg/apis/scorecard/v1alpha2" - - v1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -// BasicTestConfig contains all variables required by the BasicTest tests -type BasicTestConfig struct { - Client client.Client - CR *unstructured.Unstructured - ProxyPod *v1.Pod -} - -// Test Defintions - -// CheckSpecTest is a scorecard test that verifies that the CR has a spec block -type CheckSpecTest struct { - schelpers.TestInfo - BasicTestConfig -} - -// NewCheckSpecTest returns a new CheckSpecTest object -func NewCheckSpecTest(conf BasicTestConfig) *CheckSpecTest { - return &CheckSpecTest{ - BasicTestConfig: conf, - TestInfo: schelpers.TestInfo{ - Name: "Spec Block Exists", - Description: "Custom Resource has a Spec Block", - Labels: map[string]string{necessityKey: requiredNecessity, suiteKey: basicSuiteName, - testKey: getStructShortName(CheckSpecTest{})}, - }, - } -} - -// CheckStatusTest is a scorecard test that verifies that the CR has a status block -type CheckStatusTest struct { - schelpers.TestInfo - BasicTestConfig -} - -// NewCheckStatusTest returns a new CheckStatusTest object -func NewCheckStatusTest(conf BasicTestConfig) *CheckStatusTest { - return &CheckStatusTest{ - BasicTestConfig: conf, - TestInfo: schelpers.TestInfo{ - Name: "Status Block Exists", - Description: "Custom Resource has a Status Block", - Labels: map[string]string{necessityKey: requiredNecessity, suiteKey: basicSuiteName, - testKey: getStructShortName(CheckStatusTest{})}, - }, - } -} - -// WritingIntoCRsHasEffectTest is a scorecard test that verifies that the operator is making PUT and/or POST -// requests to the API server -type WritingIntoCRsHasEffectTest struct { - schelpers.TestInfo - BasicTestConfig -} - -// NewWritingIntoCRsHasEffectTest returns a new WritingIntoCRsHasEffectTest object -func NewWritingIntoCRsHasEffectTest(conf BasicTestConfig) *WritingIntoCRsHasEffectTest { - return &WritingIntoCRsHasEffectTest{ - BasicTestConfig: conf, - TestInfo: schelpers.TestInfo{ - Name: "Writing into CRs has an effect", - Description: "A CR sends PUT/POST requests to the API server to modify resources in" + - " response to spec block changes", - Labels: map[string]string{necessityKey: requiredNecessity, suiteKey: basicSuiteName, - testKey: getStructShortName(WritingIntoCRsHasEffectTest{})}, - }, - } -} - -// Test Implementations - -// Run - implements Test interface -func (t *CheckSpecTest) Run(ctx context.Context) *schelpers.TestResult { - res := &schelpers.TestResult{Test: t, CRName: t.CR.GetName(), State: scapiv1alpha2.PassState} - - err := t.Client.Get(ctx, types.NamespacedName{Namespace: t.CR.GetNamespace(), Name: t.CR.GetName()}, t.CR) - if err != nil { - res.Errors = append(res.Errors, fmt.Errorf("error getting custom resource: %v", err)) - res.State = scapiv1alpha2.ErrorState - return res - } - - if t.CR.Object["spec"] == nil { - res.Suggestions = append(res.Suggestions, "Add a 'spec' field to your Custom Resource") - res.State = scapiv1alpha2.FailState - return res - } - return res -} - -// Run - implements Test interface -func (t *CheckStatusTest) Run(ctx context.Context) *schelpers.TestResult { - res := &schelpers.TestResult{Test: t, CRName: t.CR.GetName(), State: scapiv1alpha2.PassState} - - err := t.Client.Get(ctx, types.NamespacedName{Namespace: t.CR.GetNamespace(), Name: t.CR.GetName()}, t.CR) - if err != nil { - res.Errors = append(res.Errors, fmt.Errorf("error getting custom resource: %v", err)) - res.State = scapiv1alpha2.ErrorState - return res - } - if t.CR.Object["status"] == nil { - res.Suggestions = append(res.Suggestions, "Add a 'status' field to your Custom Resource") - return res - } - return res -} - -// Run - implements Test interface -func (t *WritingIntoCRsHasEffectTest) Run(ctx context.Context) *schelpers.TestResult { - res := &schelpers.TestResult{Test: t, CRName: t.CR.GetName(), State: scapiv1alpha2.PassState} - - logs, err := getProxyLogs(t.ProxyPod) - if err != nil { - res.Errors = append(res.Errors, fmt.Errorf("error getting proxy logs: %v", err)) - res.State = scapiv1alpha2.FailState - return res - } - - var writes bool - msgMap := make(map[string]interface{}) - for _, msg := range strings.Split(logs, "\n") { - if err := json.Unmarshal([]byte(msg), &msgMap); err != nil { - continue - } - method, ok := msgMap["method"].(string) - if !ok { - continue - } - - if method == http.MethodPut || method == http.MethodPost || method == http.MethodPatch { - writes = true - break - } - } - - if !writes { - res.Suggestions = append(res.Suggestions, "The operator should write into objects to update state."+ - "No PUT, PATCH, or POST requests from the operator were recorded by the scorecard.") - res.State = scapiv1alpha2.FailState - } - return res -} diff --git a/internal/scorecard/plugins/config.go b/internal/scorecard/plugins/config.go deleted file mode 100644 index d08b04e6dd4..00000000000 --- a/internal/scorecard/plugins/config.go +++ /dev/null @@ -1,61 +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 scplugins - -import ( - "errors" - "fmt" - - v1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/labels" -) - -type BasicAndOLMPluginConfig struct { - Namespace string `mapstructure:"namespace"` - Kubeconfig string `mapstructure:"kubeconfig"` - InitTimeout int `mapstructure:"init-timeout"` - NamespacedManifest string `mapstructure:"namespaced-manifest"` - GlobalManifest string `mapstructure:"global-manifest"` - CRManifest []string `mapstructure:"cr-manifest"` - CSVManifest string `mapstructure:"csv-path"` - ProxyImage string `mapstructure:"proxy-image"` - ProxyPort int `mapstructure:"proxy-port"` - ProxyPullPolicy v1.PullPolicy `mapstructure:"proxy-pull-policy"` - CRDsDir string `mapstructure:"crds-dir"` - DeployDir string `mapstructure:"deploy-dir"` - Bundle string `mapstructure:"bundle"` - Selector labels.Selector `mapstructure:"selector"` - Version string `mapstructure:"version"` - ListOpt bool `mapstructure:"list"` - OLMDeployed bool `mapstructure:"olm-deployed"` -} - -func validateScorecardPluginFlags(config BasicAndOLMPluginConfig, pluginType PluginType) error { - if !config.OLMDeployed && len(config.CRManifest) == 0 { - return errors.New("cr-manifest config option must be set") - } - if pluginType == OLMIntegration && config.CSVManifest == "" { - return fmt.Errorf("csv-path must be set if olm-tests is enabled") - } - if config.OLMDeployed && config.CSVManifest == "" { - return fmt.Errorf("csv-path must be set if olm-deployed is enabled") - } - pullPolicy := config.ProxyPullPolicy - if pullPolicy != v1.PullAlways && pullPolicy != v1.PullNever && pullPolicy != v1.PullIfNotPresent { - return fmt.Errorf("invalid proxy pull policy: (%s); valid values: %s, %s, %s", pullPolicy, - v1.PullAlways, v1.PullNever, v1.PullIfNotPresent) - } - return nil -} diff --git a/internal/scorecard/plugins/labels.go b/internal/scorecard/plugins/labels.go deleted file mode 100644 index d97045ed061..00000000000 --- a/internal/scorecard/plugins/labels.go +++ /dev/null @@ -1,24 +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 scplugins - -const ( - necessityKey = "necessity" - requiredNecessity = "required" - suiteKey = "suite" - basicSuiteName = "basic" - olmSuiteName = "olm" - testKey = "test" -) diff --git a/internal/scorecard/plugins/labels_test.go b/internal/scorecard/plugins/labels_test.go deleted file mode 100644 index 33fbd262d18..00000000000 --- a/internal/scorecard/plugins/labels_test.go +++ /dev/null @@ -1,120 +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 scplugins - -import ( - "testing" - - schelpers "github.com/operator-framework/operator-sdk/internal/scorecard/helpers" - "k8s.io/apimachinery/pkg/labels" -) - -func TestBasicShortNames(t *testing.T) { - specBlockExists := getStructShortName(CheckSpecTest{}) - statusBlockExists := getStructShortName(CheckStatusTest{}) - writeIntoCR := getStructShortName(WritingIntoCRsHasEffectTest{}) - - cases := []struct { - selectorValue string - testsSelected int - wantError bool - }{ - {"test=" + specBlockExists, 1, false}, - {"test in (" + specBlockExists + ")", 1, false}, - {"test in (" + specBlockExists + "," + writeIntoCR + ")", 2, false}, - {"test=" + statusBlockExists, 1, false}, - {"test=" + writeIntoCR, 1, false}, - {"testXwriteintocr", 0, false}, - {"test X writeintocr", 0, true}, - } - - for _, c := range cases { - t.Run(c.selectorValue, func(t *testing.T) { - var selector labels.Selector - selector, err := labels.Parse(c.selectorValue) - if err != nil && c.wantError { - t.Logf("Wanted error and got error : %v", err) - return - } else if err != nil && !c.wantError { - t.Errorf("Wanted result but got error: %v", err) - return - } - - tests := make([]schelpers.Test, 0) - - conf := BasicTestConfig{} - tests = append(tests, NewCheckSpecTest(conf)) - tests = append(tests, NewCheckStatusTest(conf)) - tests = append(tests, NewWritingIntoCRsHasEffectTest(conf)) - - tests = applySelector(tests, selector) - testsSelected := len(tests) - if testsSelected != c.testsSelected { - t.Errorf("Wanted testsSelected %d, got: %d", c.testsSelected, testsSelected) - } - }) - - } -} - -func TestOLMShortNames(t *testing.T) { - bundleValidation := getStructShortName(BundleValidationTest{}) - crdValidationSection := getStructShortName(CRDsHaveValidationTest{}) - crdHasResources := getStructShortName(CRDsHaveResourcesTest{}) - specDescriptors := getStructShortName(SpecDescriptorsTest{}) - statusDescriptors := getStructShortName(StatusDescriptorsTest{}) - - cases := []struct { - selectorValue string - testsSelected int - wantError bool - }{ - {"test=" + bundleValidation, 1, false}, - {"test=" + crdValidationSection, 1, false}, - {"test=" + crdHasResources, 1, false}, - {"test=" + specDescriptors, 1, false}, - {"test=" + statusDescriptors, 1, false}, - {"testXstatusdescriptors", 0, false}, - {"test X statusdescriptors", 0, true}, - } - - for _, c := range cases { - t.Run(c.selectorValue, func(t *testing.T) { - var selector labels.Selector - selector, err := labels.Parse(c.selectorValue) - if err != nil && c.wantError { - t.Logf("Wanted error and got error : %v", err) - return - } - if err != nil && !c.wantError { - t.Errorf("Wanted result but got error: %v", err) - return - } - tests := make([]schelpers.Test, 0) - conf := OLMTestConfig{} - tests = append(tests, NewBundleValidationTest(conf)) - tests = append(tests, NewCRDsHaveValidationTest(conf)) - tests = append(tests, NewCRDsHaveResourcesTest(conf)) - tests = append(tests, NewSpecDescriptorsTest(conf)) - tests = append(tests, NewStatusDescriptorsTest(conf)) - tests = applySelector(tests, selector) - testsSelected := len(tests) - if testsSelected != c.testsSelected { - t.Errorf("Wanted testsSelected %d, got: %d", c.testsSelected, testsSelected) - } - }) - - } -} diff --git a/internal/scorecard/plugins/olm_tests.go b/internal/scorecard/plugins/olm_tests.go deleted file mode 100644 index dea6be17013..00000000000 --- a/internal/scorecard/plugins/olm_tests.go +++ /dev/null @@ -1,547 +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 scplugins - -import ( - "bytes" - "context" - "encoding/json" - "errors" - "fmt" - "strings" - - apimanifests "github.com/operator-framework/api/pkg/manifests" - apivalidation "github.com/operator-framework/api/pkg/validation" - schelpers "github.com/operator-framework/operator-sdk/internal/scorecard/helpers" - "github.com/operator-framework/operator-sdk/internal/util/k8sutil" - scapiv1alpha2 "github.com/operator-framework/operator-sdk/pkg/apis/scorecard/v1alpha2" - "github.com/sirupsen/logrus" - - olmapiv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1" - v1 "k8s.io/api/core/v1" - apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" - apiextv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -const ( - statusDescriptor string = "status" - specDescriptor string = "spec" -) - -// OLMTestConfig contains all variables required by the OLMTest tests -type OLMTestConfig struct { - Client client.Client - CR *unstructured.Unstructured - CSV *olmapiv1alpha1.ClusterServiceVersion - CRDsDir string - ProxyPod *v1.Pod - Bundle string -} - -// Test Defintions - -// BundleValidationTest is a scorecard test that validates a bundle -type BundleValidationTest struct { - schelpers.TestInfo - OLMTestConfig -} - -// NewBundleValidationTest returns a new BundleValidationTest object -func NewBundleValidationTest(conf OLMTestConfig) *BundleValidationTest { - return &BundleValidationTest{ - OLMTestConfig: conf, - TestInfo: schelpers.TestInfo{ - Name: "Bundle Validation Test", - Description: "Validates bundle contents", - Labels: map[string]string{necessityKey: requiredNecessity, suiteKey: olmSuiteName, - testKey: getStructShortName(BundleValidationTest{})}, - }, - } -} - -// CRDsHaveValidationTest is a scorecard test that verifies that all CRDs have a validation section -type CRDsHaveValidationTest struct { - schelpers.TestInfo - OLMTestConfig -} - -// NewCRDsHaveValidationTest returns a new CRDsHaveValidationTest object -func NewCRDsHaveValidationTest(conf OLMTestConfig) *CRDsHaveValidationTest { - return &CRDsHaveValidationTest{ - OLMTestConfig: conf, - TestInfo: schelpers.TestInfo{ - Name: "Provided APIs have validation", - Description: "All CRDs have an OpenAPI validation subsection", - Labels: map[string]string{necessityKey: requiredNecessity, suiteKey: olmSuiteName, - testKey: getStructShortName(CRDsHaveValidationTest{})}, - }, - } -} - -// CRDsHaveResourcesTest is a scorecard test that verifies that the CSV lists used resources in its owned CRDs section -type CRDsHaveResourcesTest struct { - schelpers.TestInfo - OLMTestConfig -} - -// NewCRDsHaveResourcesTest returns a new CRDsHaveResourcesTest object -func NewCRDsHaveResourcesTest(conf OLMTestConfig) *CRDsHaveResourcesTest { - return &CRDsHaveResourcesTest{ - OLMTestConfig: conf, - TestInfo: schelpers.TestInfo{ - Name: "Owned CRDs have resources listed", - Description: "All Owned CRDs contain a resources subsection", - Labels: map[string]string{necessityKey: requiredNecessity, suiteKey: olmSuiteName, - testKey: getStructShortName(CRDsHaveResourcesTest{})}, - }, - } -} - -// SpecDescriptorsTest is a scorecard test that verifies that all spec fields have descriptors -type SpecDescriptorsTest struct { - schelpers.TestInfo - OLMTestConfig -} - -// NewSpecDescriptorsTest returns a new SpecDescriptorsTest object -func NewSpecDescriptorsTest(conf OLMTestConfig) *SpecDescriptorsTest { - return &SpecDescriptorsTest{ - OLMTestConfig: conf, - TestInfo: schelpers.TestInfo{ - Name: "Spec fields with descriptors", - Description: "All spec fields have matching descriptors in the CSV", - Labels: map[string]string{necessityKey: requiredNecessity, suiteKey: olmSuiteName, - testKey: getStructShortName(SpecDescriptorsTest{})}, - }, - } -} - -// StatusDescriptorsTest is a scorecard test that verifies that all status fields have descriptors -type StatusDescriptorsTest struct { - schelpers.TestInfo - OLMTestConfig -} - -// NewStatusDescriptorsTest returns a new StatusDescriptorsTest object -func NewStatusDescriptorsTest(conf OLMTestConfig) *StatusDescriptorsTest { - return &StatusDescriptorsTest{ - OLMTestConfig: conf, - TestInfo: schelpers.TestInfo{ - Name: "Status fields with descriptors", - Description: "All status fields have matching descriptors in the CSV", - Labels: map[string]string{necessityKey: requiredNecessity, suiteKey: olmSuiteName, - testKey: getStructShortName(StatusDescriptorsTest{})}, - }, - } -} - -func matchKind(kind1, kind2 string) bool { - singularKind1, err := restMapper.ResourceSingularizer(kind1) - if err != nil { - singularKind1 = kind1 - log.Warningf("could not find singular version of %s", kind1) - } - singularKind2, err := restMapper.ResourceSingularizer(kind2) - if err != nil { - singularKind2 = kind2 - log.Warningf("could not find singular version of %s", kind2) - } - return strings.EqualFold(singularKind1, singularKind2) -} - -// Test Implentations - -// Run - implements Test interface -func (t *BundleValidationTest) Run(ctx context.Context) *schelpers.TestResult { - res := &schelpers.TestResult{Test: t, CRName: t.CR.GetName(), State: scapiv1alpha2.PassState} - - if t.OLMTestConfig.Bundle == "" { - res.Errors = append(res.Errors, - errors.New("unable to find the OLM 'bundle' directory which is required for this test")) - res.State = scapiv1alpha2.ErrorState - return res - } - - // Get the validation API log because it contains - // validation output that we include into the scorecard test output. - validationLogOutput := new(bytes.Buffer) - origOutput := logrus.StandardLogger().Out - logrus.SetOutput(validationLogOutput) - defer logrus.SetOutput(origOutput) - - bundle, err := apimanifests.GetBundleFromDir(t.OLMTestConfig.Bundle) - if err != nil { - res.Errors = append(res.Errors, err) - res.State = scapiv1alpha2.ErrorState - return res - } - - objs := []interface{}{bundle, bundle.CSV} - for _, crd := range bundle.V1CRDs { - objs = append(objs, crd) - } - for _, crd := range bundle.V1beta1CRDs { - objs = append(objs, crd) - } - validationResults := apivalidation.DefaultBundleValidators.Validate(objs...) - for _, result := range validationResults { - for _, e := range result.Errors { - res.Errors = append(res.Errors, &e) - res.State = scapiv1alpha2.FailState - } - - for _, w := range result.Warnings { - res.Suggestions = append(res.Suggestions, w.Error()) - } - } - - res.Log = validationLogOutput.String() - - return res -} - -// Run - implements Test interface -func (t *CRDsHaveValidationTest) Run(ctx context.Context) *schelpers.TestResult { - res := &schelpers.TestResult{Test: t, CRName: t.CR.GetName(), State: scapiv1alpha2.PassState} - v1crds, v1beta1crds, err := k8sutil.GetCustomResourceDefinitions(t.CRDsDir) - if err != nil { - res.Errors = append(res.Errors, fmt.Errorf("failed to get CRDs in %s directory: %v", t.CRDsDir, err)) - res.State = scapiv1alpha2.ErrorState - return res - } - err = t.Client.Get(ctx, types.NamespacedName{Namespace: t.CR.GetNamespace(), Name: t.CR.GetName()}, t.CR) - if err != nil { - res.Errors = append(res.Errors, err) - res.State = scapiv1alpha2.ErrorState - return res - } - - // check if the CRD matches the testing CR - gvk := t.CR.GroupVersionKind() - for _, crd := range v1crds { - for _, ver := range crd.Spec.Versions { - // Only check the validation block if the CRD and CR have the same Kind and Version - if strings.EqualFold(gvk.Version, ver.Name) && matchKind(gvk.Kind, crd.Spec.Names.Kind) { - checkV1CRDVersion(res, t.CR, ver.Schema) - } - } - } - for _, crd := range v1beta1crds { - if len(crd.Spec.Versions) == 0 { - // Only check the validation block if the CRD and CR have the same Kind and Version - if strings.EqualFold(gvk.Version, crd.Spec.Version) && matchKind(gvk.Kind, crd.Spec.Names.Kind) { - checkV1beta1CRDVersion(res, t.CR, crd.Spec.Validation) - } - } else { - for _, ver := range crd.Spec.Versions { - // Only check the validation block if the CRD and CR have the same Kind and Version - if strings.EqualFold(gvk.Version, ver.Name) && matchKind(gvk.Kind, crd.Spec.Names.Kind) { - checkV1beta1CRDVersion(res, t.CR, ver.Schema) - } - } - } - } - return res -} - -//nolint:dupl -func checkV1CRDVersion(res *schelpers.TestResult, cr *unstructured.Unstructured, - val *apiextv1.CustomResourceValidation) { - - gvk := cr.GroupVersionKind() - if val == nil { - res.Suggestions = append(res.Suggestions, fmt.Sprintf("Add CRD validation for %s/%s", gvk.Kind, gvk.Version)) - return - } - failed := false - if cr.Object["spec"] != nil { - spec := cr.Object["spec"].(map[string]interface{}) - for key := range spec { - if _, ok := val.OpenAPIV3Schema.Properties["spec"].Properties[key]; !ok { - failed = true - res.Suggestions = append(res.Suggestions, - fmt.Sprintf("Add CRD validation for spec field `%s` in %s/%s", key, gvk.Kind, gvk.Version)) - } - } - } - if cr.Object["status"] != nil { - status := cr.Object["status"].(map[string]interface{}) - for key := range status { - if _, ok := val.OpenAPIV3Schema.Properties["status"].Properties[key]; !ok { - failed = true - res.Suggestions = append(res.Suggestions, - fmt.Sprintf("Add CRD validation for status field `%s` in %s/%s", key, gvk.Kind, gvk.Version)) - } - } - } - - if failed { - res.State = scapiv1alpha2.FailState - } -} - -//nolint:dupl -func checkV1beta1CRDVersion(res *schelpers.TestResult, cr *unstructured.Unstructured, - val *apiextv1beta1.CustomResourceValidation) { - - gvk := cr.GroupVersionKind() - if val == nil { - res.Suggestions = append(res.Suggestions, fmt.Sprintf("Add CRD validation for %s/%s", gvk.Kind, gvk.Version)) - return - } - failed := false - if cr.Object["spec"] != nil { - spec := cr.Object["spec"].(map[string]interface{}) - for key := range spec { - if _, ok := val.OpenAPIV3Schema.Properties["spec"].Properties[key]; !ok { - failed = true - res.Suggestions = append(res.Suggestions, - fmt.Sprintf("Add CRD validation for spec field `%s` in %s/%s", key, gvk.Kind, gvk.Version)) - } - } - } - if cr.Object["status"] != nil { - status := cr.Object["status"].(map[string]interface{}) - for key := range status { - if _, ok := val.OpenAPIV3Schema.Properties["status"].Properties[key]; !ok { - failed = true - res.Suggestions = append(res.Suggestions, - fmt.Sprintf("Add CRD validation for status field `%s` in %s/%s", key, gvk.Kind, gvk.Version)) - } - } - } - - if failed { - res.State = scapiv1alpha2.FailState - } -} - -// Run - implements Test interface -func (t *CRDsHaveResourcesTest) Run(ctx context.Context) *schelpers.TestResult { - res := &schelpers.TestResult{Test: t, CRName: t.CR.GetName(), State: scapiv1alpha2.PassState} - - var missingResources []string - for _, crd := range t.CSV.Spec.CustomResourceDefinitions.Owned { - gvk := t.CR.GroupVersionKind() - if strings.EqualFold(crd.Version, gvk.Version) && matchKind(gvk.Kind, crd.Kind) { - resources, err := getUsedResources(t.ProxyPod) - if err != nil { - log.Warningf("getUsedResource failed: %v", err) - } - for _, resource := range resources { - foundResource := false - for _, listedResource := range crd.Resources { - if matchKind(resource.Kind, listedResource.Kind) && - strings.EqualFold(resource.Version, listedResource.Version) { - foundResource = true - break - } - } - if !foundResource { - missingResources = append(missingResources, fmt.Sprintf("%s/%s", - resource.Kind, resource.Version)) - } - } - } - } - if len(missingResources) > 0 { - res.Suggestions = append(res.Suggestions, fmt.Sprintf("If it would be helpful to an end-user to"+ - " understand or troubleshoot your CR, consider adding resources %v to the resources section for owned"+ - " CRD %s", missingResources, t.CR.GroupVersionKind().Kind)) - res.State = scapiv1alpha2.FailState - } - - return res -} - -func getUsedResources(proxyPod *v1.Pod) ([]schema.GroupVersionKind, error) { - const api = "api" - const apis = "apis" - logs, err := getProxyLogs(proxyPod) - if err != nil { - return nil, err - } - resources := map[schema.GroupVersionKind]bool{} - for _, line := range strings.Split(logs, "\n") { - logMap := make(map[string]interface{}) - err := json.Unmarshal([]byte(line), &logMap) - if err != nil { - // it is very common to get "unexpected end of JSON input", so we'll leave this at the debug level - log.Debugf("could not unmarshal line: %v", err) - continue - } - /* - There are 6 formats a resource uri can have: - Cluster-Scoped: - Collection: /apis/GROUP/VERSION/KIND - Individual: /apis/GROUP/VERSION/KIND/NAME - Core: /api/v1/KIND - Core Individual: /api/v1/KIND/NAME - - Namespaces: - All Namespaces: /apis/GROUP/VERSION/KIND (same as cluster collection) - Collection in Namespace: /apis/GROUP/VERSION/namespaces/NAMESPACE/KIND - Individual: /apis/GROUP/VERSION/namespaces/NAMESPACE/KIND/NAME - Core: /api/v1/namespaces/NAMESPACE/KIND - Core Indiviual: /api/v1/namespaces/NAMESPACE/KIND/NAME - - These urls are also often appended with options, which are denoted by the '?' symbol - */ - if msg, ok := logMap["msg"].(string); !ok || msg != "Request Info" { - continue - } - uri, ok := logMap["uri"].(string) - if !ok { - log.Warn("URI type is not string") - continue - } - removedOptions := strings.Split(uri, "?")[0] - splitURI := strings.Split(removedOptions, "/") - // first string is empty string "" - if len(splitURI) < 2 { - log.Warnf("Invalid URI: \"%s\"", uri) - continue - } - splitURI = splitURI[1:] - switch len(splitURI) { - case 3: - if splitURI[0] == api { - resources[schema.GroupVersionKind{Version: splitURI[1], Kind: splitURI[2]}] = true - break - } - if splitURI[0] == apis { - // this situation happens when the client enumerates the available resources of the server - // Example: "/apis/apps/v1?timeout=32s" - break - } - log.Warnf("Invalid URI: \"%s\"", uri) - case 4: - if splitURI[0] == api { - resources[schema.GroupVersionKind{Version: splitURI[1], Kind: splitURI[2]}] = true - break - } - if splitURI[0] == apis { - resources[schema.GroupVersionKind{Group: splitURI[1], Version: splitURI[2], Kind: splitURI[3]}] = true - break - } - log.Warnf("Invalid URI: \"%s\"", uri) - case 5: - if splitURI[0] == api { - resources[schema.GroupVersionKind{Version: splitURI[1], Kind: splitURI[4]}] = true - break - } - if splitURI[0] == apis { - resources[schema.GroupVersionKind{Group: splitURI[1], Version: splitURI[2], Kind: splitURI[3]}] = true - break - } - log.Warnf("Invalid URI: \"%s\"", uri) - case 6, 7: - if splitURI[0] == api { - resources[schema.GroupVersionKind{Version: splitURI[1], Kind: splitURI[4]}] = true - break - } - if splitURI[0] == apis { - resources[schema.GroupVersionKind{Group: splitURI[1], Version: splitURI[2], Kind: splitURI[5]}] = true - break - } - log.Warnf("Invalid URI: \"%s\"", uri) - } - } - var resourcesArr []schema.GroupVersionKind - for gvk := range resources { - resourcesArr = append(resourcesArr, gvk) - } - return resourcesArr, nil -} - -// Run - implements Test interface -func (t *StatusDescriptorsTest) Run(ctx context.Context) *schelpers.TestResult { - res := &schelpers.TestResult{Test: t, CRName: t.CR.GetName(), State: scapiv1alpha2.PassState} - - err := t.Client.Get(ctx, types.NamespacedName{Namespace: t.CR.GetNamespace(), Name: t.CR.GetName()}, t.CR) - if err != nil { - res.Errors = append(res.Errors, err) - res.State = scapiv1alpha2.ErrorState - return res - } - - return checkOwnedCSVDescriptors(t.CR, t.CSV, statusDescriptor, res) -} - -// Run - implements Test interface -func (t *SpecDescriptorsTest) Run(ctx context.Context) *schelpers.TestResult { - res := &schelpers.TestResult{Test: t, CRName: t.CR.GetName(), State: scapiv1alpha2.PassState} - err := t.Client.Get(ctx, types.NamespacedName{Namespace: t.CR.GetNamespace(), Name: t.CR.GetName()}, t.CR) - if err != nil { - res.Errors = append(res.Errors, err) - res.State = scapiv1alpha2.ErrorState - return res - } - - return checkOwnedCSVDescriptors(t.CR, t.CSV, specDescriptor, res) -} - -func checkOwnedCSVDescriptors(cr *unstructured.Unstructured, csv *olmapiv1alpha1.ClusterServiceVersion, - descriptor string, res *schelpers.TestResult) *schelpers.TestResult { - if cr.Object[descriptor] == nil { - res.State = scapiv1alpha2.FailState - return res - } - block := cr.Object[descriptor].(map[string]interface{}) - - var crd *olmapiv1alpha1.CRDDescription - for _, owned := range csv.Spec.CustomResourceDefinitions.Owned { - if owned.Kind == cr.GetKind() { - crd = &owned - break - } - } - - if crd == nil { - res.State = scapiv1alpha2.FailState - return res - } - - if descriptor == statusDescriptor { - for key := range block { - for _, statDesc := range crd.StatusDescriptors { - if statDesc.Path == key { - delete(block, key) - break - } - } - } - } - if descriptor == specDescriptor { - for key := range block { - for _, specDesc := range crd.SpecDescriptors { - if specDesc.Path == key { - delete(block, key) - break - } - } - } - } - - for key := range block { - res.Suggestions = append(res.Suggestions, fmt.Sprintf("Add a %s descriptor for %s", descriptor, key)) - res.State = scapiv1alpha2.FailState - } - return res -} diff --git a/internal/scorecard/plugins/plugin_runner.go b/internal/scorecard/plugins/plugin_runner.go deleted file mode 100644 index e0b275363b1..00000000000 --- a/internal/scorecard/plugins/plugin_runner.go +++ /dev/null @@ -1,510 +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 scplugins - -import ( - "bytes" - "context" - "encoding/json" - "errors" - "fmt" - "io" - "io/ioutil" - "os" - "path/filepath" - "reflect" - "strings" - "time" - - "github.com/operator-framework/api/pkg/validation" - "github.com/operator-framework/operator-sdk/internal/scaffold" - schelpers "github.com/operator-framework/operator-sdk/internal/scorecard/helpers" - internalk8sutil "github.com/operator-framework/operator-sdk/internal/util/k8sutil" - scapiv1alpha2 "github.com/operator-framework/operator-sdk/pkg/apis/scorecard/v1alpha2" - - olmapiv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1" - "github.com/sirupsen/logrus" - v1 "k8s.io/api/core/v1" - extscheme "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/scheme" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/runtime/serializer" - cached "k8s.io/client-go/discovery/cached" - "k8s.io/client-go/kubernetes" - cgoscheme "k8s.io/client-go/kubernetes/scheme" - "k8s.io/client-go/rest" - "k8s.io/client-go/restmapper" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/yaml" -) - -type PluginType int - -const ( - BasicOperator PluginType = 0 - OLMIntegration PluginType = 1 -) - -var ( - kubeconfig *rest.Config - dynamicDecoder runtime.Decoder - runtimeClient client.Client - restMapper *restmapper.DeferredDiscoveryRESTMapper - deploymentName string - proxyPodGlobal *v1.Pod - cleanupFns []cleanupFn -) - -const ( - scorecardContainerName = "scorecard-proxy" -) - -var log *logrus.Logger - -func RunInternalPlugin(pluginType PluginType, config BasicAndOLMPluginConfig, - logFile io.Writer) (scapiv1alpha2.ScorecardOutput, error) { - - // use stderr for logging not related to a single suite - log = logrus.New() - log.SetFormatter(&logrus.TextFormatter{DisableColors: true}) - log.SetOutput(logFile) - - if err := validateScorecardPluginFlags(config, pluginType); err != nil { - return scapiv1alpha2.ScorecardOutput{}, err - } - defer func() { - if err := cleanupScorecard(); err != nil { - log.SetOutput(logFile) - log.Errorf("Failed to cleanup resources: (%v)", err) - } - }() - - var tmpNamespaceVar string - var err error - kubeconfig, tmpNamespaceVar, err = internalk8sutil.GetKubeconfigAndNamespace(config.Kubeconfig) - if err != nil { - return scapiv1alpha2.ScorecardOutput{}, fmt.Errorf("failed to build the kubeconfig: %v", err) - } - - if config.Namespace == "" { - config.Namespace = tmpNamespaceVar - } - - if err := setupRuntimeClient(); err != nil { - return scapiv1alpha2.ScorecardOutput{}, err - } - - csv := &olmapiv1alpha1.ClusterServiceVersion{} - if pluginType == OLMIntegration || config.OLMDeployed { - err := getCSV(config.CSVManifest, csv) - if err != nil { - return scapiv1alpha2.ScorecardOutput{}, err - } - } - - // Extract operator manifests from the CSV if olm-deployed is set. - if config.OLMDeployed { - // Get deploymentName from the deployment manifest within the CSV. - deploymentName, err = getDeploymentName(csv.Spec.InstallStrategy) - if err != nil { - return scapiv1alpha2.ScorecardOutput{}, err - } - // Get the proxy pod, which should have been created with the CSV. - proxyPodGlobal, err = getPodFromDeployment(deploymentName, config.Namespace) - if err != nil { - return scapiv1alpha2.ScorecardOutput{}, err - } - - config.CRManifest, err = getCRFromCSV(config.CRManifest, csv.ObjectMeta.Annotations["alm-examples"], - csv.GetName()) - if err != nil { - return scapiv1alpha2.ScorecardOutput{}, err - } - - } else { - // If no namespaced manifest path is given, combine - // deploy/{service_account,role.yaml,role_binding,operator}.yaml. - if config.NamespacedManifest == "" { - file, err := internalk8sutil.GenerateCombinedNamespacedManifest(scaffold.DeployDir) - if err != nil { - return scapiv1alpha2.ScorecardOutput{}, err - } - config.NamespacedManifest = file.Name() - defer func() { - err := os.Remove(config.NamespacedManifest) - if err != nil { - log.Errorf("Could not delete temporary namespace manifest file: (%v)", err) - } - config.NamespacedManifest = "" - }() - } - // If no global manifest is given, combine all CRD's in the given CRD's dir. - if config.GlobalManifest == "" { - if config.CRDsDir == "" { - config.CRDsDir = filepath.Join(scaffold.DeployDir, "crds") - } - gMan, err := internalk8sutil.GenerateCombinedGlobalManifest(config.CRDsDir) - if err != nil { - return scapiv1alpha2.ScorecardOutput{}, err - } - config.GlobalManifest = gMan.Name() - defer func() { - err := os.Remove(config.GlobalManifest) - if err != nil { - log.Errorf("Could not delete global manifest file: (%v)", err) - } - config.GlobalManifest = "" - }() - } - } - - err = duplicateCRCheck(config.CRManifest) - if err != nil { - return scapiv1alpha2.ScorecardOutput{}, err - } - - testResults := make([]schelpers.TestResult, 0) - for _, cr := range config.CRManifest { - crTestResults, _, err := runTests(csv, pluginType, config, cr, logFile) - if err != nil { - return scapiv1alpha2.ScorecardOutput{}, err - } - testResults = append(testResults, crTestResults...) - } - - output := scapiv1alpha2.NewScorecardOutput() - output.Log = "" - - for _, tr := range testResults { - output.Results = append(output.Results, testResultToScorecardTestResult(tr)) - } - - return *output, nil -} - -func ListInternalPlugin(pluginType PluginType, config BasicAndOLMPluginConfig) (scapiv1alpha2.ScorecardOutput, error) { - testResults := make([]schelpers.TestResult, 0) - tests := make([]schelpers.Test, 0) - - switch pluginType { - case BasicOperator: - conf := BasicTestConfig{} - tests = append(tests, NewCheckSpecTest(conf)) - tests = append(tests, NewCheckStatusTest(conf)) - tests = append(tests, NewWritingIntoCRsHasEffectTest(conf)) - case OLMIntegration: - conf := OLMTestConfig{} - tests = append(tests, NewBundleValidationTest(conf)) - tests = append(tests, NewCRDsHaveValidationTest(conf)) - tests = append(tests, NewCRDsHaveResourcesTest(conf)) - tests = append(tests, NewSpecDescriptorsTest(conf)) - tests = append(tests, NewStatusDescriptorsTest(conf)) - } - - tests = applySelector(tests, config.Selector) - - for i := 0; i < len(tests); i++ { - result := schelpers.TestResult{} - result.State = scapiv1alpha2.PassState - result.Test = tests[i] - result.Suggestions = make([]string, 0) - result.Errors = make([]error, 0) - testResults = append(testResults, result) - } - - output := scapiv1alpha2.NewScorecardOutput() - output.Log = "" - - for _, tr := range testResults { - output.Results = append(output.Results, testResultToScorecardTestResult(tr)) - } - - return *output, nil -} - -func getStructShortName(obj interface{}) string { - t := reflect.TypeOf(obj) - return strings.ToLower(t.Name()) -} - -func setupRuntimeClient() error { - scheme := runtime.NewScheme() - // scheme for client go - if err := cgoscheme.AddToScheme(scheme); err != nil { - return fmt.Errorf("failed to add client-go scheme to client: (%v)", err) - } - // api extensions scheme (CRDs) - if err := extscheme.AddToScheme(scheme); err != nil { - return fmt.Errorf("failed to add failed to add extensions api scheme to client: (%v)", err) - } - // olm api (CSVs) - if err := olmapiv1alpha1.AddToScheme(scheme); err != nil { - return fmt.Errorf("failed to add failed to add oml api scheme (CSVs) to client: (%v)", err) - } - dynamicDecoder = serializer.NewCodecFactory(scheme).UniversalDeserializer() - // if a user creates a new CRD, we need to be able to reset the rest mapper - // temporary kubeclient to get a cached discovery - kubeclient, err := kubernetes.NewForConfig(kubeconfig) - if err != nil { - return fmt.Errorf("failed to get a kubeclient: %v", err) - } - cachedDiscoveryClient := cached.NewMemCacheClient(kubeclient.Discovery()) - restMapper = restmapper.NewDeferredDiscoveryRESTMapper(cachedDiscoveryClient) - restMapper.Reset() - runtimeClient, _ = client.New(kubeconfig, client.Options{Scheme: scheme, Mapper: restMapper}) - return nil -} - -func getCSV(csvManifest string, csv *olmapiv1alpha1.ClusterServiceVersion) error { - yamlSpec, err := ioutil.ReadFile(csvManifest) - if err != nil { - return fmt.Errorf("failed to read csv: %v", err) - } - if err = yaml.Unmarshal(yamlSpec, csv); err != nil { - return fmt.Errorf("error getting ClusterServiceVersion: %v", err) - } - - csvValidator := validation.ClusterServiceVersionValidator - results := csvValidator.Validate(csv) - for _, r := range results { - if len(r.Errors) > 0 { - var errorMsgs strings.Builder - for _, e := range r.Errors { - errorMsgs.WriteString(fmt.Sprintf("%s\n", e.Error())) - } - return fmt.Errorf("error validating ClusterServiceVersion: %s", errorMsgs.String()) - } - for _, w := range r.Warnings { - log.Warnf("CSV validation warning: type [%s] %s", w.Type, w.Detail) - } - } - - return nil -} - -func getDeploymentName(strategy olmapiv1alpha1.NamedInstallStrategy) (string, error) { - if len(strategy.StrategySpec.DeploymentSpecs) == 0 { - return "", errors.New("no deployment specs in CSV") - } - return strategy.StrategySpec.DeploymentSpecs[0].Name, nil -} - -func getCRFromCSV(currentCRMans []string, crJSONStr string, csvName string) ([]string, error) { - finalCR := make([]string, 0) - logCRMsg := false - if crMans := currentCRMans; len(crMans) == 0 { - // Create a temporary CR manifest from metadata if one is not provided. - if crJSONStr != "" { - var crs []interface{} - if err := json.Unmarshal([]byte(crJSONStr), &crs); err != nil { - return finalCR, fmt.Errorf("metadata.annotations['alm-examples'] in CSV %s"+ - "incorrectly formatted: %v", csvName, err) - } - if len(crs) == 0 { - return finalCR, fmt.Errorf("no CRs found in metadata.annotations['alm-examples']"+ - " in CSV %s and cr-manifest config option not set", csvName) - } - // TODO: run scorecard against all CR's in CSV. - cr := crs[0] - logCRMsg = len(crs) > 1 - crJSONBytes, err := json.Marshal(cr) - if err != nil { - return finalCR, err - } - crYAMLBytes, err := yaml.JSONToYAML(crJSONBytes) - if err != nil { - return finalCR, err - } - crFile, err := ioutil.TempFile("", "*.cr.yaml") - if err != nil { - return finalCR, err - } - if _, err := crFile.Write(crYAMLBytes); err != nil { - return finalCR, err - } - finalCR = []string{crFile.Name()} - defer func() { - for _, f := range finalCR { - if err := os.Remove(f); err != nil { - log.Errorf("Could not delete temporary CR manifest file: (%v)", err) - } - } - }() - } else { - return finalCR, errors.New( - "cr-manifest config option must be set if CSV has no metadata.annotations['alm-examples']") - } - } else { - // TODO: run scorecard against all CR's in CSV. - finalCR = []string{crMans[0]} - logCRMsg = len(crMans) > 1 - } - // Let users know that only the first CR is being tested. - if logCRMsg { - log.Infof("The scorecard does not support testing multiple CR's at once when run with --olm-deployed."+ - " Testing the first CR %s", finalCR[0]) - } - return finalCR, nil -} - -// Check if there are duplicate CRs -func duplicateCRCheck(crs []string) error { - gvks := []schema.GroupVersionKind{} - for _, cr := range crs { - file, err := ioutil.ReadFile(cr) - if err != nil { - return fmt.Errorf("failed to read file: %s", cr) - } - newGVKs, err := getGVKs(file) - if err != nil { - return fmt.Errorf("could not get GVKs for resource(s) in file: %s, due to error: (%v)", cr, err) - } - gvks = append(gvks, newGVKs...) - } - dupMap := make(map[schema.GroupVersionKind]bool) - for _, gvk := range gvks { - if _, ok := dupMap[gvk]; ok { - log.Warnf("Duplicate gvks in CR list detected (%s); results may be inaccurate", gvk) - } - dupMap[gvk] = true - } - return nil -} - -func runTests(csv *olmapiv1alpha1.ClusterServiceVersion, pluginType PluginType, cfg BasicAndOLMPluginConfig, - cr string, logFile io.Writer) ([]schelpers.TestResult, string, error) { - testResults := make([]schelpers.TestResult, 0) - - logReadWriter := &bytes.Buffer{} - log.SetOutput(logReadWriter) - log.Printf("Running for cr: %s", cr) - - if !cfg.OLMDeployed { - if err := createFromYAMLFile(cfg, cfg.GlobalManifest); err != nil { - return testResults, "", fmt.Errorf("failed to create global resources: %v", err) - } - if err := createFromYAMLFile(cfg, cfg.NamespacedManifest); err != nil { - return testResults, "", fmt.Errorf("failed to create namespaced resources: %v", err) - } - } - - if err := createFromYAMLFile(cfg, cr); err != nil { - return testResults, "", fmt.Errorf("failed to create cr resource: %v", err) - } - - obj, err := yamlToUnstructured(cfg.Namespace, cr) - if err != nil { - return testResults, "", fmt.Errorf("failed to decode custom resource manifest into object: %s", err) - } - - if err := waitUntilCRStatusExists(time.Second*time.Duration(cfg.InitTimeout), obj); err != nil { - return testResults, "", fmt.Errorf("failed waiting to check if CR status exists: %v", err) - } - - tests := make([]schelpers.Test, 0) - - switch pluginType { - case BasicOperator: - conf := BasicTestConfig{ - Client: runtimeClient, - CR: obj, - ProxyPod: proxyPodGlobal, - } - - tests = append(tests, NewCheckSpecTest(conf)) - tests = append(tests, NewCheckStatusTest(conf)) - tests = append(tests, NewWritingIntoCRsHasEffectTest(conf)) - - case OLMIntegration: - conf := OLMTestConfig{ - Client: runtimeClient, - CR: obj, - CSV: csv, - CRDsDir: cfg.CRDsDir, - ProxyPod: proxyPodGlobal, - Bundle: cfg.Bundle, - } - - tests = append(tests, NewBundleValidationTest(conf)) - tests = append(tests, NewCRDsHaveValidationTest(conf)) - tests = append(tests, NewCRDsHaveResourcesTest(conf)) - tests = append(tests, NewSpecDescriptorsTest(conf)) - tests = append(tests, NewStatusDescriptorsTest(conf)) - - } - - tests = applySelector(tests, cfg.Selector) - - for _, test := range tests { - testResults = append(testResults, *test.Run(context.TODO())) - } - - var testResultsLog string - logs, err := ioutil.ReadAll(logReadWriter) - if err != nil { - testResultsLog = fmt.Sprintf("failed to read log buffer: %v", err) - } else { - testResultsLog = string(logs) - } - - // change logging back to main log - log.SetOutput(logFile) - // set up clean environment for every CR - if err := cleanupScorecard(); err != nil { - log.Errorf("Failed to cleanup resources: (%v)", err) - } - // reset cleanup functions - cleanupFns = []cleanupFn{} - // clear name of operator deployment - deploymentName = "" - - return testResults, testResultsLog, nil -} - -// applySelector apply label selectors removing tests that do not match -func applySelector(tests []schelpers.Test, selector labels.Selector) []schelpers.Test { - for i := 0; i < len(tests); i++ { - t := tests[i] - if !selector.Matches(labels.Set(t.GetLabels())) { - // Remove the test - tests = append(tests[:i], tests[i+1:]...) - i-- - } - } - return tests -} - -// testResultToScorecardTestResult is a helper function for converting from the TestResult type -// to the ScorecardTestResult type -func testResultToScorecardTestResult(tr schelpers.TestResult) scapiv1alpha2.ScorecardTestResult { - sctr := scapiv1alpha2.ScorecardTestResult{} - sctr.State = tr.State - sctr.Name = tr.Test.GetName() - sctr.Description = tr.Test.GetDescription() - sctr.Log = tr.Log - sctr.CRName = tr.CRName - sctr.Suggestions = tr.Suggestions - if sctr.Suggestions == nil { - sctr.Suggestions = []string{} - } - stringErrors := []string{} - for _, err := range tr.Errors { - stringErrors = append(stringErrors, err.Error()) - } - sctr.Errors = stringErrors - sctr.Labels = tr.Test.GetLabels() - return sctr -} diff --git a/internal/scorecard/plugins/resource_handler.go b/internal/scorecard/plugins/resource_handler.go deleted file mode 100644 index dec24de0d6a..00000000000 --- a/internal/scorecard/plugins/resource_handler.go +++ /dev/null @@ -1,423 +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 scplugins - -import ( - "bytes" - "context" - "encoding/json" - "fmt" - "io/ioutil" - "os" - "strconv" - "time" - - internalk8sutil "github.com/operator-framework/operator-sdk/internal/util/k8sutil" - proxyConf "github.com/operator-framework/operator-sdk/pkg/ansible/proxy/kubeconfig" - "github.com/operator-framework/operator-sdk/pkg/k8sutil" - - appsv1 "k8s.io/api/apps/v1" - v1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/errors" - apierrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/types" - "k8s.io/apimachinery/pkg/util/wait" - "k8s.io/client-go/kubernetes" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/yaml" -) - -type cleanupFn func() error - -// waitUntilCRStatusExists waits until the status block of the CR currently being tested exists. If the timeout -// is reached, it simply continues and assumes there is no status block -func waitUntilCRStatusExists(timeout time.Duration, cr *unstructured.Unstructured) error { - err := wait.Poll(time.Second*1, timeout, func() (bool, error) { - err := runtimeClient.Get(context.TODO(), types.NamespacedName{Namespace: cr.GetNamespace(), - Name: cr.GetName()}, cr) - if err != nil { - return false, fmt.Errorf("error getting custom resource: %v", err) - } - if cr.Object["status"] != nil { - return true, nil - } - return false, nil - }) - if err != nil && err != wait.ErrWaitTimeout { - return err - } - return nil -} - -// yamlToUnstructured decodes a yaml file into an unstructured object -func yamlToUnstructured(namespace, yamlPath string) (*unstructured.Unstructured, error) { - yamlFile, err := ioutil.ReadFile(yamlPath) - if err != nil { - return nil, fmt.Errorf("failed to read file %s: %v", yamlPath, err) - } - if bytes.Contains(yamlFile, []byte("\n---\n")) { - return nil, fmt.Errorf("custom resource manifest cannot have more than 1 resource") - } - obj := &unstructured.Unstructured{} - jsonSpec, err := yaml.YAMLToJSON(yamlFile) - if err != nil { - return nil, fmt.Errorf("could not convert yaml file to json: %v", err) - } - if err := obj.UnmarshalJSON(jsonSpec); err != nil { - return nil, fmt.Errorf("failed to unmarshal custom resource manifest to unstructured: %v", err) - } - // set the namespace - obj.SetNamespace(namespace) - return obj, nil -} - -// createFromYAMLFile will take a path to a YAML file and create the resource. If it finds a -// deployment, it will add the scorecard proxy as a container in the deployments podspec. -func createFromYAMLFile(cfg BasicAndOLMPluginConfig, yamlPath string) error { - yamlSpecs, err := ioutil.ReadFile(yamlPath) - if err != nil { - return fmt.Errorf("failed to read file %s: %v", yamlPath, err) - } - scanner := internalk8sutil.NewYAMLScanner(bytes.NewBuffer(yamlSpecs)) - for scanner.Scan() { - obj := &unstructured.Unstructured{} - jsonSpec, err := yaml.YAMLToJSON(scanner.Bytes()) - if err != nil { - return fmt.Errorf("could not convert yaml file to json: %v", err) - } - if err := obj.UnmarshalJSON(jsonSpec); err != nil { - return fmt.Errorf("could not unmarshal resource spec: %v", err) - } - obj.SetNamespace(cfg.Namespace) - - // dirty hack to merge scorecard proxy into operator deployment; lots of serialization and deserialization - if obj.GetKind() == "Deployment" { - // TODO: support multiple deployments - if deploymentName != "" { - return fmt.Errorf("scorecard currently does not support multiple deployments in the manifests") - } - dep, err := unstructuredToDeployment(obj) - if err != nil { - return fmt.Errorf("failed to convert object to deployment: %v", err) - } - deploymentName = dep.GetName() - err = createKubeconfigSecret(cfg.Namespace, cfg.InitTimeout, cfg.ProxyPort) - if err != nil { - return fmt.Errorf("failed to create kubeconfig secret for scorecard-proxy: %v", err) - } - addMountKubeconfigSecret(dep) - addProxyContainer(dep, cfg.ProxyImage, cfg.ProxyPullPolicy, cfg.ProxyPort) - // go back to unstructured to create - obj, err = deploymentToUnstructured(dep) - if err != nil { - return fmt.Errorf("failed to convert deployment to unstructured: %v", err) - } - } - err = runtimeClient.Create(context.TODO(), obj) - if errors.IsAlreadyExists(err) { - fmt.Printf("already exists, %s, not creating.", yamlPath) - } else if err != nil { - _, restErr := restMapper.RESTMappings(obj.GetObjectKind().GroupVersionKind().GroupKind()) - if restErr == nil { - return err - } - // don't store error, as only error will be timeout. Error from runtime client will be easier for - // the user to understand than the timeout error, so just use that if we fail - _ = wait.PollImmediate(time.Second*1, time.Second*10, func() (bool, error) { - restMapper.Reset() - _, err := restMapper.RESTMappings(obj.GetObjectKind().GroupVersionKind().GroupKind()) - if err != nil { - return false, nil - } - return true, nil - }) - err = runtimeClient.Create(context.TODO(), obj) - if err != nil { - return err - } - } - addResourceCleanup(obj, types.NamespacedName{Namespace: obj.GetNamespace(), Name: obj.GetName()}, cfg.InitTimeout) - if obj.GetKind() == "Deployment" { - proxyPodGlobal, err = getPodFromDeployment(deploymentName, cfg.Namespace) - if err != nil { - return err - } - } - } - if err := scanner.Err(); err != nil { - return fmt.Errorf("failed to scan %s: %v", yamlPath, err) - } - - return nil -} - -// getPodFromDeployment returns a deployment depName's pod in namespace. -func getPodFromDeployment(depName, namespace string) (pod *v1.Pod, err error) { - dep := &appsv1.Deployment{} - err = runtimeClient.Get(context.TODO(), types.NamespacedName{Namespace: namespace, Name: depName}, dep) - if err != nil { - return nil, fmt.Errorf("failed to get newly created deployment: %v", err) - } - - // In some cases, the pod from the old deployment will be picked up - // instead of the new one. - err = wait.PollImmediate(time.Second*1, time.Second*60, func() (bool, error) { - pods := &v1.PodList{} - err = runtimeClient.List(context.TODO(), pods, client.InNamespace(namespace), - client.MatchingLabels(dep.Spec.Selector.MatchLabels)) - if err != nil { - return false, fmt.Errorf("failed to get list of pods in deployment: %v", err) - } - - // Make sure the pod exist. - if len(pods.Items) == 0 { - return false, nil - } - - // There should only be 1 pod per deployment. - if len(pods.Items) > 1 { - log.Debug("Operator deployment has more than 1 pod") - return false, nil - } - - // If the pod has a deletion timestamp, it is the old pod; wait for - // pod with no deletion timestamp - if pods.Items[0].GetDeletionTimestamp() != nil { - return false, nil - } - - // Make sure all containers are Running - for _, containerStatus := range pods.Items[0].Status.ContainerStatuses { - if containerStatus.State.Running == nil { - return false, nil - } - } - - pod = &pods.Items[0] - return true, nil - }) - if err != nil { - return nil, fmt.Errorf("failed to get proxyPod: %v", err) - } - return pod, nil -} - -// createKubeconfigSecret creates the secret that will be mounted in the operator's container and contains -// the kubeconfig for communicating with the proxy -func createKubeconfigSecret(namespace string, initTimeout int, proxyPort int) error { - kubeconfigMap := make(map[string][]byte) - proxyURL := fmt.Sprintf("http://localhost:%d", proxyPort) - kc, err := proxyConf.Create(metav1.OwnerReference{Name: "scorecard"}, proxyURL, namespace) - if err != nil { - return err - } - defer func() { - if err := os.Remove(kc.Name()); err != nil { - log.Errorf("Failed to delete generated kubeconfig file: (%v)", err) - } - }() - kc, err = os.Open(kc.Name()) - if err != nil { - return err - } - kcBytes, err := ioutil.ReadAll(kc) - if err != nil { - return err - } - kubeconfigMap["kubeconfig"] = kcBytes - kubeconfigSecret := &v1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "scorecard-kubeconfig", - Namespace: namespace, - }, - Data: kubeconfigMap, - } - err = runtimeClient.Create(context.TODO(), kubeconfigSecret) - if err != nil { - return err - } - addResourceCleanup(kubeconfigSecret, types.NamespacedName{Namespace: kubeconfigSecret.GetNamespace(), - Name: kubeconfigSecret.GetName()}, initTimeout) - return nil -} - -// addMountKubeconfigSecret creates the volume mount for the kubeconfig secret -func addMountKubeconfigSecret(dep *appsv1.Deployment) { - // create mount for secret - dep.Spec.Template.Spec.Volumes = append(dep.Spec.Template.Spec.Volumes, v1.Volume{ - Name: "scorecard-kubeconfig", - VolumeSource: v1.VolumeSource{Secret: &v1.SecretVolumeSource{ - SecretName: "scorecard-kubeconfig", - Items: []v1.KeyToPath{{ - Key: "kubeconfig", - Path: "config", - }}, - }, - }, - }) - for index := range dep.Spec.Template.Spec.Containers { - // mount the volume - dep.Spec.Template.Spec.Containers[index].VolumeMounts = - append(dep.Spec.Template.Spec.Containers[index].VolumeMounts, v1.VolumeMount{ - Name: "scorecard-kubeconfig", - MountPath: "/scorecard-secret", - }) - // specify the path via KUBECONFIG env var - dep.Spec.Template.Spec.Containers[index].Env = append(dep.Spec.Template.Spec.Containers[index].Env, v1.EnvVar{ - Name: "KUBECONFIG", - Value: "/scorecard-secret/config", - }) - } -} - -// addProxyContainer adds the container spec for the scorecard-proxy to the deployment's podspec -func addProxyContainer(dep *appsv1.Deployment, proxyImage string, pullPolicy v1.PullPolicy, proxyPort int) { - dep.Spec.Template.Spec.Containers = append(dep.Spec.Template.Spec.Containers, v1.Container{ - Name: scorecardContainerName, - Image: proxyImage, - ImagePullPolicy: pullPolicy, - Command: []string{"scorecard-proxy"}, - Env: []v1.EnvVar{ - { - Name: k8sutil.WatchNamespaceEnvVar, - ValueFrom: &v1.EnvVarSource{ - FieldRef: &v1.ObjectFieldSelector{FieldPath: "metadata.namespace"}}, - }, { - Name: "SCORECARD_PROXY_PORT", - Value: strconv.Itoa(proxyPort), - }}, - }) -} - -// unstructuredToDeployment converts an unstructured object to a deployment -func unstructuredToDeployment(obj *unstructured.Unstructured) (*appsv1.Deployment, error) { - jsonByte, err := obj.MarshalJSON() - if err != nil { - return nil, fmt.Errorf("failed to convert deployment to json: %v", err) - } - depObj, _, err := dynamicDecoder.Decode(jsonByte, nil, nil) - if err != nil { - return nil, fmt.Errorf("failed to decode deployment object: %v", err) - } - switch o := depObj.(type) { - case *appsv1.Deployment: - return o, nil - default: - return nil, fmt.Errorf("conversion of runtime object to deployment failed (resulting runtime object" + - " not deployment type)") - } -} - -// deploymentToUnstructured converts a deployment to an unstructured object -func deploymentToUnstructured(dep *appsv1.Deployment) (*unstructured.Unstructured, error) { - jsonByte, err := json.Marshal(dep) - if err != nil { - return nil, fmt.Errorf("failed to remarshal deployment: %v", err) - } - obj := &unstructured.Unstructured{} - err = obj.UnmarshalJSON(jsonByte) - if err != nil { - return nil, fmt.Errorf("failed to unmarshal updated deployment: %v", err) - } - return obj, nil -} - -// cleanupScorecard runs all cleanup functions in reverse order -func cleanupScorecard() error { - failed := false - for i := len(cleanupFns) - 1; i >= 0; i-- { - err := cleanupFns[i]() - if err != nil { - failed = true - log.Printf("a cleanup function failed with error: %v\n", err) - } - } - if failed { - return fmt.Errorf("a cleanup function failed; see stdout for more details") - } - return nil -} - -// addResourceCleanup adds a cleanup function for the specified runtime object -func addResourceCleanup(obj runtime.Object, key types.NamespacedName, initTimeout int) { - cleanupFns = append(cleanupFns, func() error { - // make a copy of the object because the client changes it - objCopy := obj.DeepCopyObject() - err := runtimeClient.Delete(context.TODO(), obj) - if err != nil && !apierrors.IsNotFound(err) { - return err - } - err = wait.PollImmediate(time.Second*1, time.Second*time.Duration(initTimeout), func() (bool, error) { - err = runtimeClient.Get(context.TODO(), key, objCopy) - if err != nil { - if apierrors.IsNotFound(err) { - return true, nil - } - return false, fmt.Errorf("error encountered during deletion of resource type %v with"+ - " namespace/name (%+v): %v", objCopy.GetObjectKind().GroupVersionKind().Kind, key, err) - } - return false, nil - }) - if err != nil { - return fmt.Errorf("cleanup function failed: %v", err) - } - return nil - }) -} - -func getProxyLogs(proxyPod *v1.Pod) (string, error) { - // need a standard kubeclient for pod logs - kubeclient, err := kubernetes.NewForConfig(kubeconfig) - if err != nil { - return "", fmt.Errorf("failed to create kubeclient: %v", err) - } - logOpts := &v1.PodLogOptions{Container: scorecardContainerName} - req := kubeclient.CoreV1().Pods(proxyPod.GetNamespace()).GetLogs(proxyPod.GetName(), logOpts) - readCloser, err := req.Stream(context.TODO()) - if err != nil { - return "", fmt.Errorf("failed to get logs: %v", err) - } - defer readCloser.Close() - buf := new(bytes.Buffer) - _, err = buf.ReadFrom(readCloser) - if err != nil { - return "", fmt.Errorf("test failed and failed to read pod logs: %v", err) - } - return buf.String(), nil -} - -func getGVKs(yamlFile []byte) ([]schema.GroupVersionKind, error) { - var gvks []schema.GroupVersionKind - - scanner := internalk8sutil.NewYAMLScanner(bytes.NewBuffer(yamlFile)) - for scanner.Scan() { - yamlSpec := scanner.Bytes() - - obj := &unstructured.Unstructured{} - jsonSpec, err := yaml.YAMLToJSON(yamlSpec) - if err != nil { - return nil, fmt.Errorf("could not convert yaml file to json: %v", err) - } - if err := obj.UnmarshalJSON(jsonSpec); err != nil { - return nil, fmt.Errorf("failed to unmarshal object spec: %v", err) - } - gvks = append(gvks, obj.GroupVersionKind()) - } - return gvks, nil -} diff --git a/internal/scorecard/plugins/runner_test.go b/internal/scorecard/plugins/runner_test.go deleted file mode 100644 index 103701bbe26..00000000000 --- a/internal/scorecard/plugins/runner_test.go +++ /dev/null @@ -1,146 +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 scplugins - -import ( - "bytes" - "fmt" - "io/ioutil" - "os" - "strings" - "testing" - - "github.com/sirupsen/logrus" -) - -const ( - cr0 = ` -apiVersion: cache.example.com/v1alpha1 -kind: Memcached -metadata: - name: example-memcached -spec: - size: 3 -status: - nodes: -` - cr1 = ` -apiVersion: cache.example.com/v1alpha2 -kind: Memcached -metadata: - name: example-memcached -spec: - size: 3 -status: - nodes: -` - badCR = ` - apiVersion: cache.example.com/v1alpha2 - k ind: Memcached -ame tadata: - name: example-memcached -sp ec: - size: 3 -s tatus : - no des: -` -) - -func TestDuplicateCR(t *testing.T) { - - duplicateLogMessage := "Duplicate gvks in CR list detected" - log = logrus.New() - var logBuffer bytes.Buffer - log.SetOutput(&logBuffer) - - var err error - var cr0File *os.File - var cr1File *os.File - var badcrFile *os.File - - if cr0File, err = createCRFile(cr0); err != nil { - t.Fatal(err) - return - } - if cr1File, err = createCRFile(cr1); err != nil { - t.Fatal(err) - return - } - if badcrFile, err = createCRFile(badCR); err != nil { - t.Fatal(err) - return - } - defer os.Remove(cr0File.Name()) - defer os.Remove(cr1File.Name()) - defer os.Remove(badcrFile.Name()) - - cases := []struct { - testDescription string - crA string - crB string - wantError bool - wantLog bool - }{ - {"duplicate CRs", cr0File.Name(), cr0File.Name(), false, true}, - {"unique CRs", cr0File.Name(), cr1File.Name(), false, false}, - {"bad CRs", badcrFile.Name(), cr1File.Name(), true, false}, - } - - for _, c := range cases { - t.Run(c.testDescription, func(t *testing.T) { - crs := []string{c.crA, c.crB} - err := duplicateCRCheck(crs) - if err == nil && !c.wantError && c.wantLog { - logContents := logBuffer.String() - if strings.Contains(logContents, duplicateLogMessage) { - t.Logf("Wanted log and got log : %s", logBuffer.String()) - } else { - t.Errorf("Wanted log to contain %s but not found\n", duplicateLogMessage) - return - } - } - - if err != nil && c.wantError { - t.Logf("Wanted error and got error : %v", err) - return - } - - if err != nil && !c.wantError { - t.Errorf("Wanted result but got error: %v", err) - return - } - - }) - - } -} - -func createCRFile(contents string) (*os.File, error) { - tmpFile0, err := ioutil.TempFile(os.TempDir(), "runnerTest-") - if err != nil { - return nil, err - } - - fmt.Println("Created file: " + tmpFile0.Name()) - - if _, err = tmpFile0.Write([]byte(contents)); err != nil { - return nil, err - } - - if err := tmpFile0.Close(); err != nil { - return nil, err - } - return tmpFile0, nil -} diff --git a/internal/scorecard/scorecard.go b/internal/scorecard/scorecard.go deleted file mode 100644 index 7cea251b918..00000000000 --- a/internal/scorecard/scorecard.go +++ /dev/null @@ -1,113 +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 scorecard - -import ( - "io" - "os" - - scplugins "github.com/operator-framework/operator-sdk/internal/scorecard/plugins" - scapiv1alpha2 "github.com/operator-framework/operator-sdk/pkg/apis/scorecard/v1alpha2" - - "github.com/sirupsen/logrus" - "k8s.io/apimachinery/pkg/labels" -) - -const DefaultConfigFile = ".osdk-scorecard" - -const ( - JSONOutputFormat = "json" - TextOutputFormat = "text" -) - -var ( - Log = logrus.New() -) - -type Config struct { - List bool - OutputFormat string - Version string - Selector labels.Selector - Bundle string - Kubeconfig string - Plugins []Plugin - PluginConfigs []PluginConfig - LogReadWriter io.ReadWriter -} - -type PluginConfig struct { - Basic *scplugins.BasicAndOLMPluginConfig `mapstructure:"basic,omitempty"` - Olm *scplugins.BasicAndOLMPluginConfig `mapstructure:"olm,omitempty"` -} - -func (s Config) GetPlugins(configs []PluginConfig) ([]Plugin, error) { - - // Add plugins from config - var plugins []Plugin - - for _, plugin := range configs { - var newPlugin Plugin - if plugin.Basic != nil { - pluginConfig := plugin.Basic - pluginConfig.Version = s.Version - pluginConfig.Selector = s.Selector - pluginConfig.ListOpt = s.List - pluginConfig.Bundle = s.Bundle - setConfigDefaults(pluginConfig, s.Kubeconfig) - newPlugin = basicOrOLMPlugin{pluginType: scplugins.BasicOperator, config: *pluginConfig} - } else if plugin.Olm != nil { - pluginConfig := plugin.Olm - pluginConfig.Version = s.Version - pluginConfig.Selector = s.Selector - pluginConfig.ListOpt = s.List - pluginConfig.Bundle = s.Bundle - setConfigDefaults(pluginConfig, s.Kubeconfig) - newPlugin = basicOrOLMPlugin{pluginType: scplugins.OLMIntegration, config: *pluginConfig} - } - plugins = append(plugins, newPlugin) - } - return plugins, nil -} - -func (s Config) RunTests() error { - - var pluginOutputs []scapiv1alpha2.ScorecardOutput - for _, plugin := range s.Plugins { - if s.List { - pluginOutputs = append(pluginOutputs, plugin.List()) - } else { - pluginOutputs = append(pluginOutputs, plugin.Run()) - } - } - - if err := s.printPluginOutputs(pluginOutputs); err != nil { - return err - } - - for _, scorecardOutput := range pluginOutputs { - for _, result := range scorecardOutput.Results { - if result.State != scapiv1alpha2.PassState { - os.Exit(1) - } - } - } - - return nil -} - -func ConfigDocLink() string { - return "https://sdk.operatorframework.io/docs/scorecard/" -} diff --git a/internal/scorecard/scorecard_output.go b/internal/scorecard/scorecard_output.go deleted file mode 100644 index d793c72404f..00000000000 --- a/internal/scorecard/scorecard_output.go +++ /dev/null @@ -1,75 +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 scorecard - -import ( - "encoding/json" - "fmt" - "io/ioutil" - - scapiv1alpha2 "github.com/operator-framework/operator-sdk/pkg/apis/scorecard/v1alpha2" -) - -func (cfg Config) printPluginOutputs(pluginOutputs []scapiv1alpha2.ScorecardOutput) error { - - var scorecardOutput scapiv1alpha2.ScorecardOutput - var err error - scorecardOutput, err = cfg.combinePluginOutput(pluginOutputs) - if err != nil { - return err - } - - if cfg.List { - for i := 0; i < len(scorecardOutput.Results); i++ { - scorecardOutput.Results[i].State = scapiv1alpha2.NotRunState - } - } - - switch format := cfg.OutputFormat; format { - case TextOutputFormat: - output, err := scorecardOutput.MarshalText() - if err != nil { - return err - } - fmt.Printf("%s\n", output) - case JSONOutputFormat: - bytes, err := json.MarshalIndent(scorecardOutput, "", " ") - if err != nil { - return err - } - fmt.Printf("%s\n", string(bytes)) - } - - return nil -} - -func (cfg Config) combinePluginOutput(pluginOutputs []scapiv1alpha2. - ScorecardOutput) (scapiv1alpha2.ScorecardOutput, error) { - output := scapiv1alpha2.ScorecardOutput{} - output.Results = make([]scapiv1alpha2.ScorecardTestResult, 0) - for _, v := range pluginOutputs { - output.Results = append(output.Results, v.Results...) - } - - if cfg.OutputFormat == JSONOutputFormat { - log, err := ioutil.ReadAll(cfg.LogReadWriter) - if err != nil { - return output, fmt.Errorf("failed to read log buffer: %v", err) - } - output.Log = string(log) - } - - return output, nil -} diff --git a/internal/util/projutil/project_util.go b/internal/util/projutil/project_util.go index 418320e3ae3..d793da0ebc4 100644 --- a/internal/util/projutil/project_util.go +++ b/internal/util/projutil/project_util.go @@ -25,7 +25,6 @@ import ( homedir "github.com/mitchellh/go-homedir" "github.com/rogpeppe/go-internal/modfile" log "github.com/sirupsen/logrus" - "github.com/spf13/cobra" kbutil "github.com/operator-framework/operator-sdk/internal/util/kubebuilder" ) @@ -105,14 +104,6 @@ func CheckProjectRoot() error { return nil } -func CheckGoProjectCmd(cmd *cobra.Command) error { - if IsOperatorGo() { - return nil - } - return fmt.Errorf("'%s' can only be run for Go operators; %s or %s do not exist", - cmd.CommandPath(), managerMainFile, mainFile) -} - func MustGetwd() string { wd, err := os.Getwd() if err != nil { @@ -243,16 +234,6 @@ func IsOperatorHelm() bool { return (err == nil && stat.IsDir()) || os.IsExist(err) } -// MustGetGopath gets GOPATH and ensures it is set and non-empty. If GOPATH -// is not set or empty, MustGetGopath exits. -func MustGetGopath() string { - gopath, ok := os.LookupEnv(GoPathEnv) - if !ok || len(gopath) == 0 { - log.Fatal("GOPATH env not set") - } - return gopath -} - // MustSetWdGopath sets GOPATH to the first element of the path list in // currentGopath that prefixes the wd, then returns the set path. // If GOPATH cannot be set, MustSetWdGopath exits. @@ -292,21 +273,6 @@ func SetGoVerbose() error { return nil } -// CheckRepo ensures dependency manager type and repo are being used in combination -// correctly, as different dependency managers have different Go environment -// requirements. -func CheckRepo(repo string) error { - inGopathSrc, err := WdInGoPathSrc() - if err != nil { - return err - } - if !inGopathSrc && repo == "" { - return fmt.Errorf(`flag --repo must be set if the working directory is not in $GOPATH/src. - See "operator-sdk new -h"`) - } - return nil -} - // CheckGoModules ensures that go modules are enabled. func CheckGoModules() error { goModOn, err := GoModOn() diff --git a/release.sh b/release.sh index 576531bc40b..b52a53d18d9 100755 --- a/release.sh +++ b/release.sh @@ -38,25 +38,6 @@ if [[ "$VER" != "$CURR_VER" ]]; then exit 1 fi -GO_GOMOD="internal/scaffold/go_mod.go" -ANS_GOMOD="internal/scaffold/ansible/go_mod.go" -HELM_GOMOD="internal/scaffold/helm/go_mod.go" -CURR_VER_GO_GOMOD="$(sed -E -n -r 's|.*operator-sdk ([^ \t\n]+).*|\1|p' "$GO_GOMOD" | tail -1 | tr -d ' \t\n')" -if [[ "$VER" != "$CURR_VER_GO_GOMOD" ]]; then - echo "go.mod 'replace' entry version is not set correctly in $GO_GOMOD" - exit 1 -fi -CURR_VER_ANS_GOMOD="$(sed -E -n -r 's|.*operator-sdk ([^ \t\n]+).*|\1|p' "$ANS_GOMOD" | tail -1 | tr -d ' \t\n')" -if [[ "$VER" != "$CURR_VER_ANS_GOMOD" ]]; then - echo "go.mod 'replace' entry version is not set correctly in $ANS_GOMOD" - exit 1 -fi -CURR_VER_HELM_GOMOD="$(sed -E -n -r 's|.*operator-sdk ([^ \t\n]+).*|\1|p' "$HELM_GOMOD" | tail -1 | tr -d ' \t\n')" -if [[ "$VER" != "$CURR_VER_HELM_GOMOD" ]]; then - echo "go.mod 'replace' entry version is not set correctly in $HELM_GOMOD" - exit 1 -fi - INSTALL_GUIDE_FILE="website/content/en/docs/install-operator-sdk.md" CURR_VER_INSTALL_GUIDE_FILE="$(sed -nr 's/.*RELEASE_VERSION=(.+)/\1/p' "$INSTALL_GUIDE_FILE" | tr -d ' \t\n')" if [[ "$VER" != "$CURR_VER_INSTALL_GUIDE_FILE" ]]; then diff --git a/test/e2e/_incluster-test-code/main_test.go b/test/e2e/_incluster-test-code/main_test.go deleted file mode 100644 index ff28005b27a..00000000000 --- a/test/e2e/_incluster-test-code/main_test.go +++ /dev/null @@ -1,37 +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 e2e - -import ( - "flag" - "testing" - - f "github.com/operator-framework/operator-sdk/pkg/test" - - // This import tests double-registration of the --kubebuilder flag: - // https://github.com/kubernetes-sigs/controller-runtime/issues/878 - _ "sigs.k8s.io/controller-runtime/pkg/client/config" -) - -type testArgs struct { - e2eImageName *string -} - -var args = &testArgs{} - -func TestMain(m *testing.M) { - args.e2eImageName = flag.String("image", "", "operator image name : used to push the image, defaults to none (builds image to local docker repo)") - f.MainEntry(m) -} diff --git a/test/e2e/_incluster-test-code/memcached_test.go b/test/e2e/_incluster-test-code/memcached_test.go deleted file mode 100644 index 643a1fab02d..00000000000 --- a/test/e2e/_incluster-test-code/memcached_test.go +++ /dev/null @@ -1,476 +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 e2e - -import ( - "bytes" - goctx "context" - "fmt" - "io" - "io/ioutil" - "os" - "os/exec" - "testing" - "time" - - apis "github.com/example-inc/memcached-operator/pkg/apis" - operator "github.com/example-inc/memcached-operator/pkg/apis/cache/v1alpha1" - - framework "github.com/operator-framework/operator-sdk/pkg/test" - "github.com/operator-framework/operator-sdk/pkg/test/e2eutil" - dto "github.com/prometheus/client_model/go" - "github.com/prometheus/common/expfmt" - "github.com/prometheus/prometheus/util/promlint" - v1 "k8s.io/api/core/v1" - apierrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" - "k8s.io/apimachinery/pkg/util/wait" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/rest" - "k8s.io/client-go/util/retry" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -const ( - retryInterval = time.Second * 5 - timeout = time.Second * 120 - cleanupRetryInterval = time.Second * 1 - cleanupTimeout = time.Second * 10 - operatorName = "memcached-operator" -) - -func TestMemcached(t *testing.T) { - memcachedList := &operator.MemcachedList{} - err := framework.AddToFrameworkScheme(apis.AddToScheme, memcachedList) - if err != nil { - t.Fatalf("Failed to add custom resource scheme to framework: %v", err) - } - // run subtests - t.Run("memcached-group", func(t *testing.T) { - t.Run("Cluster", MemcachedCluster) - t.Run("Local", MemcachedLocal) - }) -} - -func memcachedLeaderTest(t *testing.T, f *framework.Framework, ctx *framework.Context) error { - operatorNamespace, err := ctx.GetOperatorNamespace() - if err != nil { - return err - } - - err = e2eutil.WaitForOperatorDeployment(t, f.KubeClient, operatorNamespace, operatorName, 2, retryInterval, timeout) - if err != nil { - return err - } - - label := map[string]string{"name": operatorName} - - leader, err := verifyLeader(t, operatorNamespace, f, label) - if err != nil { - return err - } - - // delete the leader's pod so a new leader will get elected - err = f.Client.Delete(goctx.TODO(), leader) - if err != nil { - return err - } - - err = e2eutil.WaitForDeletion(t, f.Client.Client, leader, retryInterval, timeout) - if err != nil { - return err - } - - err = e2eutil.WaitForOperatorDeployment(t, f.KubeClient, operatorNamespace, operatorName, 2, retryInterval, timeout) - if err != nil { - return err - } - - newLeader, err := verifyLeader(t, operatorNamespace, f, label) - if err != nil { - return err - } - if newLeader.Name == leader.Name { - return fmt.Errorf("leader pod name did not change across pod delete") - } - - return nil -} - -func verifyLeader(t *testing.T, namespace string, f *framework.Framework, labels map[string]string) (*v1.Pod, error) { - // get configmap, which is the lock - lockName := "memcached-operator-lock" - lock := v1.ConfigMap{} - err := wait.Poll(retryInterval, timeout, func() (done bool, err error) { - err = f.Client.Get(goctx.TODO(), types.NamespacedName{Name: lockName, Namespace: namespace}, &lock) - if err != nil { - if apierrors.IsNotFound(err) { - t.Logf("Waiting for availability of leader lock configmap %s\n", lockName) - return false, nil - } - return false, err - } - return true, nil - }) - if err != nil { - return nil, fmt.Errorf("error getting leader lock configmap: %v", err) - } - t.Logf("Found leader lock configmap %s\n", lockName) - - owners := lock.GetOwnerReferences() - if len(owners) != 1 { - return nil, fmt.Errorf("leader lock has %d owner refs, expected 1", len(owners)) - } - owner := owners[0] - - // get operator pods - pods := &v1.PodList{} - opts := []client.ListOption{ - client.InNamespace(namespace), - client.MatchingLabels(labels), - client.MatchingFields{"status.phase": "Running"}, - } - err = f.Client.List(goctx.TODO(), pods, opts...) - if err != nil { - return nil, err - } - if len(pods.Items) != 2 { - return nil, fmt.Errorf("expected 2 pods, found %d", len(pods.Items)) - } - - // find and return the leader - for _, pod := range pods.Items { - if pod.Name == owner.Name { - return &pod, nil - } - } - return nil, fmt.Errorf("did not find operator pod that was referenced by configmap") -} - -func memcachedScaleTest(t *testing.T, f *framework.Framework, ctx *framework.Context, fromReplicas, toReplicas int) error { - name := "example-memcached" - watchNamespace, err := ctx.GetWatchNamespace() - if err != nil { - return fmt.Errorf("could not get watch namespace: %v", err) - } - key := types.NamespacedName{Name: name, Namespace: watchNamespace} - // create memcached custom resource - exampleMemcached := &operator.Memcached{ - ObjectMeta: metav1.ObjectMeta{ - Name: key.Name, - Namespace: key.Namespace, - }, - Spec: operator.MemcachedSpec{ - Size: int32(fromReplicas), - }, - } - // use Context's create helper to create the object and add a cleanup function for the new object - err = f.Client.Create(goctx.TODO(), exampleMemcached, &framework.CleanupOptions{TestContext: ctx, Timeout: cleanupTimeout, RetryInterval: cleanupRetryInterval}) - if err != nil { - return fmt.Errorf("could no create CR: %v", err) - } - // wait for example-memcached to reach `fromReplicas` replicas - err = e2eutil.WaitForDeployment(t, f.KubeClient, key.Namespace, key.Name, fromReplicas, retryInterval, timeout) - if err != nil { - return fmt.Errorf("failed waiting for %d deployment/%s replicas: %v", fromReplicas, key.Name, err) - } - - err = retry.RetryOnConflict(retry.DefaultBackoff, func() error { - err = f.Client.Get(goctx.TODO(), key, exampleMemcached) - if err != nil { - return fmt.Errorf("could not get memcached CR %q: %v", key, err) - } - // update memcached CR size to `toReplicas` replicas - exampleMemcached.Spec.Size = int32(toReplicas) - t.Logf("Attempting memcached CR %q update, resourceVersion: %s", key, exampleMemcached.GetResourceVersion()) - return f.Client.Update(goctx.TODO(), exampleMemcached) - }) - if err != nil { - return fmt.Errorf("could not update memcached CR %q: %v", key, err) - } - - // wait for example-memcached to reach `toReplicas` replicas - if err := e2eutil.WaitForDeployment(t, f.KubeClient, key.Namespace, key.Name, toReplicas, retryInterval, timeout); err != nil { - return fmt.Errorf("failed waiting for %d deployment/%s replicas: %v", toReplicas, key.Name, err) - } - return nil -} - -func MemcachedLocal(t *testing.T) { - // get global framework variables - ctx := framework.NewContext(t) - defer ctx.Cleanup() - watchNamespace, err := ctx.GetWatchNamespace() - if err != nil { - t.Fatal(err) - } - cmd := exec.Command("operator-sdk", "run", "local", - "--watch-namespace="+watchNamespace) - stderr, err := os.Create("stderr.txt") - if err != nil { - t.Fatalf("Failed to create stderr.txt: %v", err) - } - cmd.Stderr = stderr - defer func() { - if err := stderr.Close(); err != nil { - t.Errorf("Failed to close stderr: (%v)", err) - } - }() - - err = cmd.Start() - if err != nil { - t.Fatalf("Error: %v", err) - } - ctx.AddCleanupFn(func() error { return cmd.Process.Signal(os.Interrupt) }) - - // wait for operator to start (may take a minute to compile the command...) - err = wait.Poll(time.Second*5, time.Second*100, func() (done bool, err error) { - file, err := ioutil.ReadFile("stderr.txt") - if err != nil { - return false, err - } - if len(file) == 0 { - return false, nil - } - return true, nil - }) - if err != nil { - t.Fatalf("Local operator not ready after 100 seconds: %v\n", err) - } - - if err = memcachedScaleTest(t, framework.Global, ctx, 3, 4); err != nil { - file, fileErr := ioutil.ReadFile("stderr.txt") - if fileErr != nil { - t.Logf("Failed to read operator logs after test failure: %v", fileErr) - } else { - t.Logf("Operator Logs: %s", string(file)) - } - t.Fatal(err) - } -} - -func MemcachedCluster(t *testing.T) { - // get global framework variables - ctx := framework.NewContext(t) - defer ctx.Cleanup() - - err := ctx.InitializeClusterResources(&framework.CleanupOptions{TestContext: ctx, Timeout: cleanupTimeout, RetryInterval: cleanupRetryInterval}) - if err != nil { - t.Fatalf("Failed to initialize cluster resources: %v", err) - } - t.Log("Initialized cluster resources") - operatorNamespace, err := ctx.GetOperatorNamespace() - if err != nil { - t.Fatal(err) - } - // get global framework variables - f := framework.Global - // wait for memcached-operator to be ready - if err := e2eutil.WaitForOperatorDeployment(t, f.KubeClient, operatorNamespace, operatorName, 2, retryInterval, timeout); err != nil { - t.Fatal(err) - } - - if err := memcachedLeaderTest(t, f, ctx); err != nil { - t.Error(err) - } - t.Log("Completed leader test") - - if err := memcachedScaleTest(t, f, ctx, 3, 4); err != nil { - t.Error(err) - } - t.Log("Completed scale test") - - if err := memcachedMetricsTest(t, f, ctx); err != nil { - t.Error(err) - } - t.Log("Completed memcached metrics test") - - if err := memcachedOperatorMetricsTest(t, f, ctx); err != nil { - t.Error(err) - } - t.Log("Completed memcached custom resource metrics test") -} - -func memcachedMetricsTest(t *testing.T, f *framework.Framework, ctx *framework.Context) error { - namespace, err := ctx.GetOperatorNamespace() - if err != nil { - return err - } - - // Make sure metrics Service exists - s := v1.Service{} - err = f.Client.Get(goctx.TODO(), types.NamespacedName{Name: fmt.Sprintf("%s-metrics", operatorName), Namespace: namespace}, &s) - if err != nil { - return fmt.Errorf("could not get metrics Service: %v", err) - } - if len(s.Spec.Selector) == 0 { - return fmt.Errorf("no labels found in metrics Service") - } - - // TODO(lili): Make port a constant in internal/scaffold/cmd.go. - response, err := getMetrics(t, f, s.Spec.Selector, namespace, "8383") - if err != nil { - return fmt.Errorf("failed to get metrics: %v", err) - } - // Make sure metrics are present - if len(response) == 0 { - return fmt.Errorf("metrics body is empty") - } - - // Perform prometheus metrics lint checks - l := promlint.New(bytes.NewReader(response)) - problems, err := l.Lint() - if err != nil { - return fmt.Errorf("failed to lint metrics: %v", err) - } - - // TODO(joelanford): Change to 0, when we upgrade from kubernetes-1.15. - // This is fixed in kubernetes-1.16+. - if len(problems) > 1 { - return fmt.Errorf("found problems with metrics: %#+v", problems) - } - - return nil -} - -func memcachedOperatorMetricsTest(t *testing.T, f *framework.Framework, ctx *framework.Context) error { - namespace, err := ctx.GetOperatorNamespace() - if err != nil { - return err - } - - // TODO(lili): Make port a constant in internal/scaffold/cmd.go. - response, err := getMetrics(t, f, map[string]string{"name": operatorName}, namespace, "8686") - if err != nil { - return fmt.Errorf("failed to get metrics: %v", err) - } - // Make sure metrics are present - if len(response) == 0 { - return fmt.Errorf("metrics body is empty") - } - - // Perform prometheus metrics lint checks - l := promlint.New(bytes.NewReader(response)) - problems, err := l.Lint() - if err != nil { - return fmt.Errorf("failed to lint metrics: %v", err) - } - if len(problems) > 0 { - return fmt.Errorf("found problems with metrics: %#+v", problems) - } - - // Make sure the metrics are the way we expect them. - d := expfmt.NewDecoder(bytes.NewReader(response), expfmt.FmtText) - var mf dto.MetricFamily - for { - if err := d.Decode(&mf); err != nil { - if err == io.EOF { - break - } - - return err - } - - /* - Metric: - # HELP memcached_info Information about the Memcached operator replica. - # TYPE memcached_info gauge - memcached_info{namespace="memcached-memcached-group-cluster-1553683239",memcached="example-memcached"} 1 - */ - if mf.GetName() != "memcached_info" { - return fmt.Errorf("metric name was incorrect: expected %s, got %s", "memcached_info", mf.GetName()) - } - if mf.GetType() != dto.MetricType_GAUGE { - return fmt.Errorf("metric type was incorrect: expected %v, got %v", dto.MetricType_GAUGE, mf.GetType()) - } - - mlabels := mf.Metric[0].GetLabel() - if mlabels[0].GetName() != "namespace" { - return fmt.Errorf("metric label name was incorrect: expected %s, got %s", "namespace", mlabels[0].GetName()) - } - if mlabels[0].GetValue() != namespace { - return fmt.Errorf("metric label value was incorrect: expected %s, got %s", namespace, mlabels[0].GetValue()) - } - if mlabels[1].GetName() != "memcached" { - return fmt.Errorf("metric label name was incorrect: expected %s, got %s", "memcached", mlabels[1].GetName()) - } - if mlabels[1].GetValue() != "example-memcached" { - return fmt.Errorf("metric label value was incorrect: expected %s, got %s", "example-memcached", mlabels[1].GetValue()) - } - - if mf.Metric[0].GetGauge().GetValue() != float64(1) { - return fmt.Errorf("metric counter was incorrect: expected %f, got %f", float64(1), mf.Metric[0].GetGauge().GetValue()) - } - } - - return nil -} - -func getMetrics(t *testing.T, f *framework.Framework, labels map[string]string, ns, port string) ([]byte, error) { - // Get operator pod - pods := &v1.PodList{} - opts := []client.ListOption{ - client.InNamespace(ns), - client.MatchingLabels(labels), - client.MatchingFields{"status.phase": "Running"}, - } - err := f.Client.List(goctx.TODO(), pods, opts...) - if err != nil { - return nil, fmt.Errorf("failed to get pods: %v", err) - } - - podName := "" - numPods := len(pods.Items) - // TODO(lili): Remove below logic when we enable exposing metrics in all pods. - if numPods == 0 { - podName = pods.Items[0].Name - } else if numPods > 1 { - // If we got more than one pod, get leader pod name. - leader, err := verifyLeader(t, ns, f, labels) - if err != nil { - return nil, err - } - podName = leader.Name - } else { - return nil, fmt.Errorf("failed to get operator pod: could not select any pods with selector %v", labels) - } - // Pod name must be there, otherwise we cannot read metrics data via pod proxy. - if podName == "" { - return nil, fmt.Errorf("failed to get pod name") - } - - // Get metrics data - request := proxyViaPod(f.KubeClient, ns, podName, port, "/metrics") - response, err := request.DoRaw(goctx.TODO()) - if err != nil { - return nil, fmt.Errorf("failed to get response from metrics: %v", err) - } - - return response, nil - -} - -func proxyViaPod(kubeClient kubernetes.Interface, namespace, podName, podPortName, path string) *rest.Request { - return kubeClient. - CoreV1(). - RESTClient(). - Get(). - Namespace(namespace). - Resource("pods"). - SubResource("proxy"). - Name(fmt.Sprintf("%s:%s", podName, podPortName)). - Suffix(path) -} diff --git a/test/e2e-new/e2e_suite.go b/test/e2e/e2e_suite.go similarity index 96% rename from test/e2e-new/e2e_suite.go rename to test/e2e/e2e_suite.go index af46fe26de2..36b36819c56 100644 --- a/test/e2e-new/e2e_suite.go +++ b/test/e2e/e2e_suite.go @@ -99,10 +99,15 @@ var _ = Describe("operator-sdk", func() { err = tc.Make("docker-build", "IMG="+tc.ImageName) Expect(err).Should(Succeed()) - By("loading the operator image into the test cluster") - err = tc.LoadImageToKindCluster() + kubectx, err := tc.Kubectl.Command("config", "current-context") Expect(err).Should(Succeed()) + if strings.Contains(kubectx, "kind") { + By("loading the operator image into the test cluster") + err = tc.LoadImageToKindCluster() + Expect(err).Should(Succeed()) + } + By("deploying the controller manager") err = tc.Make("deploy", "IMG="+tc.ImageName) Expect(err).Should(Succeed()) diff --git a/test/e2e-new/e2e_test.go b/test/e2e/e2e_test.go similarity index 100% rename from test/e2e-new/e2e_test.go rename to test/e2e/e2e_test.go diff --git a/test/e2e/main_test.go b/test/e2e/main_test.go deleted file mode 100644 index 210d9062941..00000000000 --- a/test/e2e/main_test.go +++ /dev/null @@ -1,25 +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 e2e - -import ( - "testing" - - f "github.com/operator-framework/operator-sdk/pkg/test" -) - -func TestMain(m *testing.M) { - f.MainEntry(m) -} diff --git a/test/e2e/testdata/ca-csr.json b/test/e2e/testdata/ca-csr.json deleted file mode 100644 index 6f13ed8d334..00000000000 --- a/test/e2e/testdata/ca-csr.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "key": { - "algo": "rsa", - "size": 2048 - }, - "names": [ - { - "L": "San Francisco", - "ST": "California", - "C": "USA" - } - ], - "CN": "ca", - "ca": { - "expiry": "87600h" - } - } diff --git a/test/e2e/testdata/ca.crt b/test/e2e/testdata/ca.crt deleted file mode 100644 index 015172df5dc..00000000000 --- a/test/e2e/testdata/ca.crt +++ /dev/null @@ -1,21 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDhDCCAmygAwIBAgIUJCpEclRVqfgYKUIyrk6m46BqH0EwDQYJKoZIhvcNAQEL -BQAwSDEMMAoGA1UEBhMDVVNBMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQH -Ew1TYW4gRnJhbmNpc2NvMQswCQYDVQQDEwJjYTAeFw0xODA4MzAyMDEyMDBaFw0y -ODA4MjcyMDEyMDBaMEgxDDAKBgNVBAYTA1VTQTETMBEGA1UECBMKQ2FsaWZvcm5p -YTEWMBQGA1UEBxMNU2FuIEZyYW5jaXNjbzELMAkGA1UEAxMCY2EwggEiMA0GCSqG -SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDwSnTyS56xvTRBNbkNNoYBfOJNGzSNCtF/ -u2NrYUQW9YOCCceuhl8q4g8H6/+HTV9RlRiTAY7DSFSZUK/C3x4uFhY1emXjzgqT -Z19FKEKwkVhqo7XSkGrqb37U0vgdO2eyGWqVt2gS50wNimo4Z3HcfsziDtqFZpxb -9ZuCiWmGpnkx+NuH9Qq2LBHSdOHI+HWw7E/91ZAaTmW/QA9W9HvxKNC9pmFFBRtd -WDjGvHsTmpgPZ2Pce/jcJ6SAaO82KXM0ksW4LmaK4OTUPN+c3KODA/gSau77DNNG -bnjQ6CkyKKOfInGDVhpG57p+LuIbt724bCxmNRkvjYwrr+dEL4SpAgMBAAGjZjBk -MA4GA1UdDwEB/wQEAwIBBjASBgNVHRMBAf8ECDAGAQH/AgECMB0GA1UdDgQWBBSN -6FinnI4qg4IeK9PGzzufD6usjDAfBgNVHSMEGDAWgBSN6FinnI4qg4IeK9PGzzuf -D6usjDANBgkqhkiG9w0BAQsFAAOCAQEAy6sn3xyNcQ/HzD1Ii7p4toc5qbgDONnq -9YkbIoNFxFj8DTQ86r6jcj94PnylIhBe1I1k70tVVal7nM+4wnNaTktAfiQN/mYJ -RyvMTXN6+Vsl93AeBMo7DIGElz2sL2EkOTct1QmTr7hr/3u4SfBvppFnxYqJKiI3 -GX3V0kV1iuAllyHR787hkWq28LQ3WXtqnybAR23SMVtQNrHw1t1ia5eStK4Gbfl/ -FN/BNwkV6i8Q/5B2obCUJWpzt4UqB4hXv3TmhYCeA8ddq7LYjjil11Ed21o95QyK -FooF2jEmda+zivmB/XKC+5+DfL00x0G1QqbME6ilGkpRx2gDFg03cg== ------END CERTIFICATE----- diff --git a/test/e2e/testdata/ca.csr b/test/e2e/testdata/ca.csr deleted file mode 100644 index 0a1c4abe607..00000000000 --- a/test/e2e/testdata/ca.csr +++ /dev/null @@ -1,16 +0,0 @@ ------BEGIN CERTIFICATE REQUEST----- -MIICjTCCAXUCAQAwSDEMMAoGA1UEBhMDVVNBMRMwEQYDVQQIEwpDYWxpZm9ybmlh -MRYwFAYDVQQHEw1TYW4gRnJhbmNpc2NvMQswCQYDVQQDEwJjYTCCASIwDQYJKoZI -hvcNAQEBBQADggEPADCCAQoCggEBAPBKdPJLnrG9NEE1uQ02hgF84k0bNI0K0X+7 -Y2thRBb1g4IJx66GXyriDwfr/4dNX1GVGJMBjsNIVJlQr8LfHi4WFjV6ZePOCpNn -X0UoQrCRWGqjtdKQaupvftTS+B07Z7IZapW3aBLnTA2Kajhncdx+zOIO2oVmnFv1 -m4KJaYameTH424f1CrYsEdJ04cj4dbDsT/3VkBpOZb9AD1b0e/Eo0L2mYUUFG11Y -OMa8exOamA9nY9x7+NwnpIBo7zYpczSSxbguZorg5NQ835zco4MD+BJq7vsM00Zu -eNDoKTIoo58icYNWGkbnun4u4hu3vbhsLGY1GS+NjCuv50QvhKkCAwEAAaAAMA0G -CSqGSIb3DQEBCwUAA4IBAQDDqdtWrOmptqpQNDG6nt1EW6KLwSPhZBx+wwGVpPtb -cXVSvjQkmIzK0G22XtDnIIix+D65hvFIPyVvKYVhDm5LhRvcguyRAV2SkrDlhBka -tZG03yvvUE4hbdWjJtk7CAUoKOZ1Sl47zdf0Rn1b/LZd9gQ6Ew08YRfdZ9VLQdOm -j/o6owGqIpU/dCaMZZ/8jzccmqt6QkiTSlyrA2ws38S0wcEsILp8vppLQc056Qiw -AcqwwUw1jp8omqozPCKir12gNjkWxLnZ56ka1PwUJr0cj8QYUqHC0q/xl2eNw2lU -W8qZn+2N43F3KvJ5BPUbuu+69G8EQhhMKHq69G6WSGhQ ------END CERTIFICATE REQUEST----- diff --git a/test/e2e/testdata/ca.key b/test/e2e/testdata/ca.key deleted file mode 100644 index d170af1699e..00000000000 --- a/test/e2e/testdata/ca.key +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEpAIBAAKCAQEA8Ep08kuesb00QTW5DTaGAXziTRs0jQrRf7tja2FEFvWDggnH -roZfKuIPB+v/h01fUZUYkwGOw0hUmVCvwt8eLhYWNXpl484Kk2dfRShCsJFYaqO1 -0pBq6m9+1NL4HTtnshlqlbdoEudMDYpqOGdx3H7M4g7ahWacW/WbgolphqZ5Mfjb -h/UKtiwR0nThyPh1sOxP/dWQGk5lv0APVvR78SjQvaZhRQUbXVg4xrx7E5qYD2dj -3Hv43CekgGjvNilzNJLFuC5miuDk1DzfnNyjgwP4Emru+wzTRm540OgpMiijnyJx -g1YaRue6fi7iG7e9uGwsZjUZL42MK6/nRC+EqQIDAQABAoIBAAbaSL2ENJFjEPNv -IcjjriyqsBV82iHPlivrXyl3y6ZP+CEkQEKU6G/jpIQYUeA876P2+Y1vtO+Sx37b -0zdef5DW5mk+BVvay2hqwUfKnyRD8N6RrqTDo5jt9xMAtTy4LfvhR63fXiNz3zJf -qSnUoWWlZBhqTgcR5xGkTnwJiS3i0Vk9/xU1zNL7OZpSSSuefXuq2qQtG+Gayrps -KAp1essnKnNRyiQH2yOn3pXs5Mj+ytWCV7C9eL1TAg0ZSIjuV+S/CNpCHsVLv5p6 -yj/CWjzV7NOhsdHoo5t1FsALhQaov0bb6hv96ZbTwkPoegr1SLQnYx63kOo//AmK -uCgZgxECgYEA/qmFoBlRNm2AJ/vRp0t6JoERF9LhsUWSYRmcSho2xG5fLwzSxCjR -YOxFEdZl3hzkH/rhY7+Vg6rOH5rhjL4hBKnrCkZcC88J7WCTI4mJDwTP/iViahAg -RVEvJ6T51qI8N1wojumuQIhbUmVcWiZA49QRC/5DqIgZ1lRDWycqTeUCgYEA8Y2b -mCI5zvjPci/1WBbPqA9ZDdbi4MmvO/RG+ik4RZnN7poxg2JU/Ta5RAFB1UY21bo8 -JTiL2pRqLaGLi0HPEJEaS6K5a+9oK5tUtmJp6CgpnNiNmALUyaoXbg3lrv5ajo74 -G94AZEIha0r9ReECvtk/sdsK1IgZaBtCIJ2Lj3UCgYEAjo+4DngdzqpeJAQEyfKm -3wdB2mRjlCmuWE1OAO3L2wsundg/5TA0hl2+DM5JGJ5z1rNLmduWh68G1QqPWYrW -URYOTiI1RScSF6EIvcwwvgejqFKlVVrRtfxMuZTRiCYqL5OX4OlQcy/ib63ulUj0 -6pW9NUmR9ra6QBHL4yt5s0ECgYEAsAJpX/+AdAnkzuWXNqrYgTM9xtHP28/aOiuT -FHG4qS6bWcNNVNjv6NpZQO5RlCBnkHD1poF/lrQSclGGJuC7Cu1QZdCan8WA+FVk -8sjfNuUc/UbmVd+qQZAJJo5F0K9SORKAQ34Odv+g7ldkGekNYRdYTDa5u4e4S52h -H7bsnIkCgYAJZRkDWjhPhWX7hv9HcLcyW/yOe9tEys/2UoYBr+j3X0eraKR1+EUr -Xli+yuPBbRo5+bEzAXI/gbZGu3J6vZm0J9qHc1ybqLvcTcOweTBU7xs4aeJsSK5O -w0BnNOc/05hLf2Ow02ZlZBELMXpp1Y187+FKEmkwxTrgaGB3rZanVA== ------END RSA PRIVATE KEY----- diff --git a/test/e2e/testdata/empty.yaml b/test/e2e/testdata/empty.yaml deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/test/e2e/testdata/gencert.json b/test/e2e/testdata/gencert.json deleted file mode 100644 index dcf31054818..00000000000 --- a/test/e2e/testdata/gencert.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "signing": { - "default": { - "usages": [ - "signing", - "key encipherment", - "server auth", - "client auth" - ], - "expiry": "87600h" - } - } -} diff --git a/test/e2e/testdata/gencert.sh b/test/e2e/testdata/gencert.sh deleted file mode 100755 index c81225618da..00000000000 --- a/test/e2e/testdata/gencert.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash - -if ! [[ "$0" =~ "./gencert.sh" ]]; then - echo "must be run from 'testdata'" - exit 255 -fi - -if ! which cfssl; then - echo "cfssl is not installed" - exit 255 -fi - -cfssl gencert --initca=true ./ca-csr.json | cfssljson --bare ca - -mv ca.pem ca.crt -mv ca-key.pem ca.key -if which openssl >/dev/null; then - openssl x509 -in ca.crt -noout -text -fi diff --git a/test/e2e/tls_util_test.go b/test/e2e/tls_util_test.go deleted file mode 100644 index f0e9b68544c..00000000000 --- a/test/e2e/tls_util_test.go +++ /dev/null @@ -1,345 +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 e2e - -import ( - "context" - "io/ioutil" - "reflect" - "testing" - - v1 "k8s.io/api/core/v1" - apiErrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - - framework "github.com/operator-framework/operator-sdk/pkg/test" - tlsutil "github.com/operator-framework/operator-sdk/pkg/tls" -) - -var ( - // TLS test variables. - crKind = "Pod" - crName = "example-pod" - certName = "app-cert" - caConfigMapAndSecretName = tlsutil.ToCASecretAndConfigMapName(crKind, crName) - appSecretName = tlsutil.ToAppSecretName(crKind, crName, certName) - - caConfigMap *v1.ConfigMap - caSecret *v1.Secret - appSecret *v1.Secret - - ccfg *tlsutil.CertConfig -) - -// setup test variables. -func init() { - caCertBytes, err := ioutil.ReadFile("./testdata/ca.crt") - if err != nil { - panic(err) - } - caConfigMap = &v1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Name: caConfigMapAndSecretName, - }, - Data: map[string]string{tlsutil.TLSCACertKey: string(caCertBytes)}, - } - - caKeyBytes, err := ioutil.ReadFile("./testdata/ca.key") - if err != nil { - panic(err) - } - caSecret = &v1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: caConfigMapAndSecretName, - }, - Data: map[string][]byte{tlsutil.TLSPrivateCAKeyKey: caKeyBytes}, - } - - appSecret = &v1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: appSecretName, - }, - } - - ccfg = &tlsutil.CertConfig{ - CertName: certName, - } -} - -// TestBothAppAndCATLSAssetsExist ensures that when both application -// and CA TLS assets exist in the k8s cluster for a given cr, -// the GenerateCert() simply returns those to the caller. -func TestBothAppAndCATLSAssetsExist(t *testing.T) { - ctx := framework.NewContext(t) - defer ctx.Cleanup() - namespace, err := ctx.GetOperatorNamespace() - if err != nil { - t.Fatal(err) - } - - f := framework.Global - createOpts := metav1.CreateOptions{} - appSecret, err := f.KubeClient.CoreV1().Secrets(namespace).Create(context.TODO(), appSecret, createOpts) - if err != nil { - t.Fatal(err) - } - - caConfigMap, err := f.KubeClient.CoreV1().ConfigMaps(namespace).Create(context.TODO(), caConfigMap, createOpts) - if err != nil { - t.Fatal(err) - } - - caSecret, err := f.KubeClient.CoreV1().Secrets(namespace).Create(context.TODO(), caSecret, createOpts) - if err != nil { - t.Fatal(err) - } - - cg := tlsutil.NewSDKCertGenerator(f.KubeClient) - actualAppSecret, actualCaConfigMap, actualCaSecret, err := cg.GenerateCert(newDummyCR(namespace), nil, ccfg) - if err != nil { - t.Fatal(err) - } - - if !reflect.DeepEqual(appSecret, actualAppSecret) { - t.Fatalf("Expect %+v, but got %+v", appSecret, actualAppSecret) - } - if !reflect.DeepEqual(caConfigMap, actualCaConfigMap) { - t.Fatalf("Expect %+v, but got %+v", caConfigMap, actualCaConfigMap) - } - if !reflect.DeepEqual(caSecret, actualCaSecret) { - t.Fatalf("Expect %+v, but got %+v", caSecret, actualCaSecret) - } -} - -// TestOnlyAppSecretExist tests a case where the application TLS asset exists but its -// correspoding CA asset doesn't. In this case, CertGenerator can't genereate a new CA because -// it won't verify the existing application TLS cert. Therefore, CertGenerator can't proceed -// and returns an error to the caller. -func TestOnlyAppSecretExist(t *testing.T) { - ctx := framework.NewContext(t) - defer ctx.Cleanup() - namespace, err := ctx.GetOperatorNamespace() - if err != nil { - t.Fatal(err) - } - - f := framework.Global - _, err = f.KubeClient.CoreV1().Secrets(namespace).Create(context.TODO(), appSecret, metav1.CreateOptions{}) - if err != nil { - t.Fatal(err) - } - - cg := tlsutil.NewSDKCertGenerator(f.KubeClient) - _, _, _, err = cg.GenerateCert(newDummyCR(namespace), nil, ccfg) - if err == nil { - t.Fatal("Expect error, but got none") - } - if err != tlsutil.ErrCANotFound { - t.Fatalf("Expect %v, but got %v", tlsutil.ErrCANotFound.Error(), err.Error()) - } -} - -// TestOnlyCAExist tests the case where only the CA exists in the cluster; -// GenerateCert can retrieve the CA and uses it to create a new application secret. -func TestOnlyCAExist(t *testing.T) { - ctx := framework.NewContext(t) - defer ctx.Cleanup() - namespace, err := ctx.GetOperatorNamespace() - if err != nil { - t.Fatal(err) - } - - f := framework.Global - _, err = f.KubeClient.CoreV1().ConfigMaps(namespace).Create(context.TODO(), caConfigMap, metav1.CreateOptions{}) - if err != nil { - t.Fatal(err) - } - _, err = f.KubeClient.CoreV1().Secrets(namespace).Create(context.TODO(), caSecret, metav1.CreateOptions{}) - if err != nil { - t.Fatal(err) - } - - cg := tlsutil.NewSDKCertGenerator(f.KubeClient) - appSecret, _, _, err := cg.GenerateCert(newDummyCR(namespace), newAppSvc(namespace), ccfg) - if err != nil { - t.Fatal(err) - } - - verifyAppSecret(t, appSecret, namespace) -} - -// TestNoneOfCaAndAppSecretExist ensures that when none of the CA and Application TLS assets -// exist, GenerateCert() creates both and put them into the k8s cluster. -func TestNoneOfCaAndAppSecretExist(t *testing.T) { - ctx := framework.NewContext(t) - defer ctx.Cleanup() - namespace, err := ctx.GetOperatorNamespace() - if err != nil { - t.Fatal(err) - } - - f := framework.Global - cg := tlsutil.NewSDKCertGenerator(f.KubeClient) - appSecret, caConfigMap, caSecret, err := cg.GenerateCert(newDummyCR(namespace), newAppSvc(namespace), ccfg) - if err != nil { - t.Fatal(err) - } - - verifyAppSecret(t, appSecret, namespace) - verifyCaConfigMap(t, caConfigMap, namespace) - verifyCASecret(t, caSecret, namespace) -} - -// TestCustomCA ensures that if a user provides a custom Key and Cert and the CA and Application TLS assets -// do not exist, the GenerateCert method can use the custom CA to generate the TLS assest. -func TestCustomCA(t *testing.T) { - ctx := framework.NewContext(t) - defer ctx.Cleanup() - namespace, err := ctx.GetOperatorNamespace() - if err != nil { - t.Fatal(err) - } - - f := framework.Global - cg := tlsutil.NewSDKCertGenerator(f.KubeClient) - - customConfig := &tlsutil.CertConfig{ - CertName: certName, - CAKey: "testdata/ca.key", - CACert: "testdata/ca.crt", - } - appSecret, _, _, err := cg.GenerateCert(newDummyCR(namespace), newAppSvc(namespace), customConfig) - if err != nil { - t.Fatal(err) - } - - verifyAppSecret(t, appSecret, namespace) - - // ensure caConfigMap does not exist in k8s cluster. - _, err = f.KubeClient.CoreV1().Secrets(namespace).Get(context.TODO(), caConfigMapAndSecretName, metav1.GetOptions{}) - if !apiErrors.IsNotFound(err) { - t.Fatal(err) - } - - // ensure caConfigMap does not exist in k8s cluster. - _, err = f.KubeClient.CoreV1().Secrets(namespace).Get(context.TODO(), caConfigMapAndSecretName, metav1.GetOptions{}) - if !apiErrors.IsNotFound(err) { - t.Fatal(err) - } -} - -func verifyCASecret(t *testing.T, caSecret *v1.Secret, namespace string) { - // check if caConfigMap has the correct fields. - if caConfigMapAndSecretName != caSecret.Name { - t.Fatalf("Expect the ca config name %v, but got %v", caConfigMapAndSecretName, caConfigMap.Name) - } - if namespace != caSecret.Namespace { - t.Fatalf("Expect the ca config namespace %v, but got %v", namespace, appSecret.Namespace) - } - if _, ok := caSecret.Data[tlsutil.TLSPrivateCAKeyKey]; !ok { - t.Fatalf("Expect the ca config to have the data field %v, but got none", tlsutil.TLSPrivateCAKeyKey) - } - - // check if caConfigMap exists in k8s cluster. - caSecretFromCluster, err := framework.Global.KubeClient.CoreV1().Secrets(namespace).Get(context.TODO(), - caConfigMapAndSecretName, - metav1.GetOptions{}) - if err != nil { - t.Fatal(err) - } - // check if caSecret returned from GenerateCert is the same as the one that exists in the k8s. - if !reflect.DeepEqual(caSecret, caSecretFromCluster) { - t.Fatalf("Expect %+v, but got %+v", caSecret, caSecretFromCluster) - } -} - -func verifyCaConfigMap(t *testing.T, caConfigMap *v1.ConfigMap, namespace string) { - // check if caConfigMap has the correct fields. - if caConfigMapAndSecretName != caConfigMap.Name { - t.Fatalf("Expect the ca config name %v, but got %v", caConfigMapAndSecretName, caConfigMap.Name) - } - if namespace != caConfigMap.Namespace { - t.Fatalf("Expect the ca config namespace %v, but got %v", namespace, appSecret.Namespace) - } - if _, ok := caConfigMap.Data[tlsutil.TLSCACertKey]; !ok { - t.Fatalf("Expect the ca config to have the data field %v, but got none", tlsutil.TLSCACertKey) - } - - // check if caConfigMap exists in k8s cluster. - caConfigMapFromCluster, err := - framework.Global.KubeClient.CoreV1().ConfigMaps(namespace). - Get(context.TODO(), caConfigMapAndSecretName, metav1.GetOptions{}) - if err != nil { - t.Fatal(err) - } - // check if caConfigMap returned from GenerateCert is the same as the one that exists in the k8s. - if !reflect.DeepEqual(caConfigMap, caConfigMapFromCluster) { - t.Fatalf("Expect %+v, but got %+v", caConfigMap, caConfigMapFromCluster) - } -} - -func verifyAppSecret(t *testing.T, appSecret *v1.Secret, namespace string) { - // check if appSecret has the correct fields. - if appSecretName != appSecret.Name { - t.Fatalf("Expect the secret name %v, but got %v", appSecretName, appSecret.Name) - } - if namespace != appSecret.Namespace { - t.Fatalf("Expect the secret namespace %v, but got %v", namespace, appSecret.Namespace) - } - if v1.SecretTypeTLS != appSecret.Type { - t.Fatalf("Expect the secret type %v, but got %v", v1.SecretTypeTLS, appSecret.Type) - } - if _, ok := appSecret.Data[v1.TLSCertKey]; !ok { - t.Fatalf("Expect the secret to have the data field %v, but got none", v1.TLSCertKey) - } - if _, ok := appSecret.Data[v1.TLSPrivateKeyKey]; !ok { - t.Fatalf("Expect the secret to have the data field %v, but got none", v1.TLSPrivateKeyKey) - } - - // check if appSecret exists in k8s cluster. - appSecretFromCluster, err := framework.Global.KubeClient.CoreV1().Secrets(namespace).Get(context.TODO(), - appSecretName, - metav1.GetOptions{}) - if err != nil { - t.Fatal(err) - } - // check if appSecret returned from GenerateCert is the same as the one that exists in the k8s. - if !reflect.DeepEqual(appSecret, appSecretFromCluster) { - t.Fatalf("Expect %+v, but got %+v", appSecret, appSecretFromCluster) - } -} - -// newDummyCR returns a dummy runtime object for the CR input of GenerateCert(). -func newDummyCR(namespace string) runtime.Object { - return &v1.Pod{ - TypeMeta: metav1.TypeMeta{ - Kind: crKind, - }, - ObjectMeta: metav1.ObjectMeta{ - Name: crName, - Namespace: namespace, - }, - } -} - -func newAppSvc(namespace string) *v1.Service { - return &v1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: "app-service", - Namespace: namespace, - }, - } -} diff --git a/test/kind-config.yaml b/test/kind-config.yaml deleted file mode 100644 index f4bf78cb4a4..00000000000 --- a/test/kind-config.yaml +++ /dev/null @@ -1,7 +0,0 @@ -kind: Cluster -apiVersion: kind.x-k8s.io/v1alpha4 -nodes: - - role: control-plane - - role: worker - - role: worker - - role: worker diff --git a/website/content/en/docs/cli/operator-sdk.md b/website/content/en/docs/cli/operator-sdk.md index c83d1ef48da..ce775fb2a4e 100644 --- a/website/content/en/docs/cli/operator-sdk.md +++ b/website/content/en/docs/cli/operator-sdk.md @@ -26,9 +26,7 @@ An SDK for building operators with ease * [operator-sdk generate](../operator-sdk_generate) - Invokes a specific generator * [operator-sdk new](../operator-sdk_new) - Creates a new operator application * [operator-sdk olm](../operator-sdk_olm) - Manage the Operator Lifecycle Manager installation in your cluster -* [operator-sdk print-deps](../operator-sdk_print-deps) - Print Golang packages and versions required to run the operator * [operator-sdk run](../operator-sdk_run) - Run an Operator in a variety of environments -* [operator-sdk scorecard](../operator-sdk_scorecard) - Run scorecard tests * [operator-sdk test](../operator-sdk_test) - Tests the operator * [operator-sdk version](../operator-sdk_version) - Prints the version of operator-sdk diff --git a/website/content/en/docs/cli/operator-sdk_add.md b/website/content/en/docs/cli/operator-sdk_add.md index ad34da93f53..4201b3e3aed 100644 --- a/website/content/en/docs/cli/operator-sdk_add.md +++ b/website/content/en/docs/cli/operator-sdk_add.md @@ -19,5 +19,4 @@ Adds a controller or resource to the project * [operator-sdk](../operator-sdk) - An SDK for building operators with ease * [operator-sdk add api](../operator-sdk_add_api) - Adds a new api definition under pkg/apis -* [operator-sdk add controller](../operator-sdk_add_controller) - Adds a new controller pkg diff --git a/website/content/en/docs/cli/operator-sdk_add_controller.md b/website/content/en/docs/cli/operator-sdk_add_controller.md deleted file mode 100644 index 1c8a9d97cb4..00000000000 --- a/website/content/en/docs/cli/operator-sdk_add_controller.md +++ /dev/null @@ -1,65 +0,0 @@ ---- -title: "operator-sdk add controller" ---- -## operator-sdk add controller - -Adds a new controller pkg - -### Synopsis - - -Add a new controller package to your operator project. - -This command creates a new controller package under pkg/controller/ that, by default, reconciles on a custom resource for the specified apiversion and kind. The controller will expect to use the custom resource type that should already be defined under pkg/apis// via the "operator-sdk add api" command. - -Note that, if the controller pkg for that Kind already exists at pkg/controller/ then the command will not overwrite and return an error. - -This command MUST be run from the project root directory. - -``` -operator-sdk add controller [flags] -``` - -### Examples - -``` - -The following example will create a controller to manage, watch and reconcile as primary resource the from the domain . - -Example: - - $ operator-sdk add controller --api-version=app.example.com/v1 --kind=AppService - $ tree pkg/controller - pkg/controller/ - ├── add_appservice.go - ├── appservice - │ └── appservice_controller.go - └── controller.go - -The following example will create a controller to manage, watch and reconcile as a primary resource the from the domain , which is not defined in the project (external). Note that, it can be used to create controllers for any External API. - -Example: - - $ operator-sdk add controller --api-version=k8s.io.api/v1 --kind=Deployment --custom-api-import=k8s.io/api/apps - $ tree pkg/controller - pkg/controller/ - ├── add_deployment.go - ├── deployment - │ └── deployment_controller.go - └── controller.go - -``` - -### Options - -``` - --api-version string Kubernetes APIVersion that has a format of $GROUP_NAME/$VERSION (e.g app.example.com/v1alpha1) - --custom-api-import string The External API import path of the form "host.com/repo/path[=import_identifier]" Note that import_identifier is optional. ( E.g. --custom-api-import=k8s.io/api/apps ) - -h, --help help for controller - --kind string Kubernetes resource Kind name. (e.g AppService) -``` - -### SEE ALSO - -* [operator-sdk add](../operator-sdk_add) - Adds a controller or resource to the project - diff --git a/website/content/en/docs/cli/operator-sdk_generate.md b/website/content/en/docs/cli/operator-sdk_generate.md index 624ddbfe0c2..aa1849ce355 100644 --- a/website/content/en/docs/cli/operator-sdk_generate.md +++ b/website/content/en/docs/cli/operator-sdk_generate.md @@ -20,7 +20,5 @@ code or manifests. * [operator-sdk](../operator-sdk) - An SDK for building operators with ease * [operator-sdk generate bundle](../operator-sdk_generate_bundle) - Generates bundle data for the operator -* [operator-sdk generate crds](../operator-sdk_generate_crds) - Generates CRDs for API's -* [operator-sdk generate k8s](../operator-sdk_generate_k8s) - Generates Kubernetes code for custom resource * [operator-sdk generate packagemanifests](../operator-sdk_generate_packagemanifests) - Generates a package manifests format diff --git a/website/content/en/docs/cli/operator-sdk_generate_crds.md b/website/content/en/docs/cli/operator-sdk_generate_crds.md deleted file mode 100644 index ebc7fc3f36d..00000000000 --- a/website/content/en/docs/cli/operator-sdk_generate_crds.md +++ /dev/null @@ -1,36 +0,0 @@ ---- -title: "operator-sdk generate crds" ---- -## operator-sdk generate crds - -Generates CRDs for API's - -### Synopsis - -generate crds generates CRDs or updates them if they exist, -under deploy/crds/__crd.yaml; OpenAPI -V3 validation YAML is generated as a 'validation' object. - -Example: - - $ operator-sdk generate crds - $ tree deploy/crds - ├── deploy/crds/app.example.com_v1alpha1_appservice_cr.yaml - ├── deploy/crds/app.example.com_appservices_crd.yaml - - -``` -operator-sdk generate crds [flags] -``` - -### Options - -``` - --crd-version string CRD version to generate (default "v1") - -h, --help help for crds -``` - -### SEE ALSO - -* [operator-sdk generate](../operator-sdk_generate) - Invokes a specific generator - diff --git a/website/content/en/docs/cli/operator-sdk_generate_k8s.md b/website/content/en/docs/cli/operator-sdk_generate_k8s.md deleted file mode 100644 index 1c99696c1cd..00000000000 --- a/website/content/en/docs/cli/operator-sdk_generate_k8s.md +++ /dev/null @@ -1,37 +0,0 @@ ---- -title: "operator-sdk generate k8s" ---- -## operator-sdk generate k8s - -Generates Kubernetes code for custom resource - -### Synopsis - -k8s generator generates code for custom resources given the API -specs in pkg/apis// directories to comply with kube-API -requirements. Go code is generated under -pkg/apis///zz_generated.deepcopy.go. -Example: - - $ operator-sdk generate k8s - $ tree pkg/apis - pkg/apis/ - └── app - └── v1alpha1 - ├── zz_generated.deepcopy.go - - -``` -operator-sdk generate k8s [flags] -``` - -### Options - -``` - -h, --help help for k8s -``` - -### SEE ALSO - -* [operator-sdk generate](../operator-sdk_generate) - Invokes a specific generator - diff --git a/website/content/en/docs/cli/operator-sdk_print-deps.md b/website/content/en/docs/cli/operator-sdk_print-deps.md deleted file mode 100644 index 472efc3c8b2..00000000000 --- a/website/content/en/docs/cli/operator-sdk_print-deps.md +++ /dev/null @@ -1,28 +0,0 @@ ---- -title: "operator-sdk print-deps" ---- -## operator-sdk print-deps - -Print Golang packages and versions required to run the operator - -### Synopsis - -The operator-sdk print-deps command prints all Golang packages and versions expected -by this version of the Operator SDK. Versions for these packages should match -those in an operator's go.mod file. - - -``` -operator-sdk print-deps [flags] -``` - -### Options - -``` - -h, --help help for print-deps -``` - -### SEE ALSO - -* [operator-sdk](../operator-sdk) - An SDK for building operators with ease - diff --git a/website/content/en/docs/cli/operator-sdk_scorecard.md b/website/content/en/docs/cli/operator-sdk_scorecard.md deleted file mode 100644 index 8755027221e..00000000000 --- a/website/content/en/docs/cli/operator-sdk_scorecard.md +++ /dev/null @@ -1,33 +0,0 @@ ---- -title: "operator-sdk scorecard" ---- -## operator-sdk scorecard - -Run scorecard tests - -### Synopsis - -Runs blackbox scorecard tests on an operator - - -``` -operator-sdk scorecard [flags] -``` - -### Options - -``` - -b, --bundle string OLM bundle directory path, when specified runs bundle validation - --config string config file (default is '/.osdk-scorecard.yaml'; the config file's extension and format must be .yaml - -h, --help help for scorecard - --kubeconfig string Path to kubeconfig of custom resource created in cluster - -L, --list If true, only print the test names that would be run based on selector filtering - -o, --output string Output format for results. Valid values: text, json (default "text") - -l, --selector string selector (label query) to filter tests on - --version string scorecard version. Valid values: v1alpha2 (default "v1alpha2") -``` - -### SEE ALSO - -* [operator-sdk](../operator-sdk) - An SDK for building operators with ease -