diff --git a/CHANGELOG.md b/CHANGELOG.md index 61047fb3c18..46b743582e2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,11 +9,13 @@ - Generating and serving info metrics about each custom resource. By default these metrics are exposed on port 8686. ([#1277](https://github.com/operator-framework/operator-sdk/pull/1277)) - Scaffold a `pkg/apis//group.go` package file to avoid `go/build` errors when running Kubernetes code generators. ([#1401](https://github.com/operator-framework/operator-sdk/pull/1401)) - Adds a new extra variable containing the unmodified CR spec for ansible based operators. [#1563](https://github.com/operator-framework/operator-sdk/pull/1563) +- New flag `--repo` for subcommands [`new`](https://github.com/operator-framework/operator-sdk/blob/master/doc/sdk-cli-reference.md#new) and [`migrate`](https://github.com/operator-framework/operator-sdk/blob/master/doc/sdk-cli-reference.md#migrate) specifies the repository path to be used in Go source files generated by the SDK. This flag can only be used with [Go modules](https://github.com/golang/go/wiki/Modules). ([#1475](https://github.com/operator-framework/operator-sdk/pull/1475)) ### Changed - Remove TypeMeta declaration from the implementation of the objects [#1462](https://github.com/operator-framework/operator-sdk/pull/1462/) - Relaxed API version format check when parsing `pkg/apis` in code generators. API dir structures can now be of the format `pkg/apis//`, where `` was previously required to be in the Kubernetes version format, ex. `v1alpha1`. ([#1525](https://github.com/operator-framework/operator-sdk/pull/1525)) +- The SDK and operator projects will work outside of `$GOPATH/src` when using [Go modules](https://github.com/golang/go/wiki/Modules). ([#1475](https://github.com/operator-framework/operator-sdk/pull/1475)) ### Deprecated diff --git a/README.md b/README.md index 2e9e10e1edf..b0306008ce7 100644 --- a/README.md +++ b/README.md @@ -57,9 +57,9 @@ Follow the steps in the [installation guide][install_guide] to learn how to inst ```sh # Create an app-operator project that defines the App CR. -$ mkdir -p $GOPATH/src/github.com/example-inc/ +$ mkdir -p $HOME/projects/example-inc/ # Create a new app-operator project -$ cd $GOPATH/src/github.com/example-inc/ +$ cd $HOME/projects/example-inc/ $ export GO111MODULE=on $ operator-sdk new app-operator $ cd app-operator diff --git a/cmd/operator-sdk/add/api.go b/cmd/operator-sdk/add/api.go index 4be4934544f..d9b4aa290a8 100644 --- a/cmd/operator-sdk/add/api.go +++ b/cmd/operator-sdk/add/api.go @@ -103,7 +103,7 @@ func apiRun(cmd *cobra.Command, args []string) error { absProjectPath := projutil.MustGetwd() cfg := &input.Config{ - Repo: projutil.CheckAndGetProjectGoPkg(), + Repo: projutil.GetGoPkg(), AbsProjectPath: absProjectPath, } s := &scaffold.Scaffold{} diff --git a/cmd/operator-sdk/add/controller.go b/cmd/operator-sdk/add/controller.go index ddb2dc2ca94..89c5258fec1 100644 --- a/cmd/operator-sdk/add/controller.go +++ b/cmd/operator-sdk/add/controller.go @@ -81,7 +81,7 @@ func controllerRun(cmd *cobra.Command, args []string) error { } cfg := &input.Config{ - Repo: projutil.CheckAndGetProjectGoPkg(), + Repo: projutil.GetGoPkg(), AbsProjectPath: projutil.MustGetwd(), } s := &scaffold.Scaffold{} diff --git a/cmd/operator-sdk/add/crd.go b/cmd/operator-sdk/add/crd.go index aa090f55bcb..3b5b6678bd8 100644 --- a/cmd/operator-sdk/add/crd.go +++ b/cmd/operator-sdk/add/crd.go @@ -118,7 +118,7 @@ func verifyCRDFlags() error { return nil } -// verifyCRDDeployPath checks if the path /deploy sub-directory is exists, and that is rooted under $GOPATH +// verifyCRDDeployPath checks if the /deploy directory exists. func verifyCRDDeployPath() error { wd, err := os.Getwd() if err != nil { diff --git a/cmd/operator-sdk/build/cmd.go b/cmd/operator-sdk/build/cmd.go index af4f42f3443..5eb64c82508 100644 --- a/cmd/operator-sdk/build/cmd.go +++ b/cmd/operator-sdk/build/cmd.go @@ -93,17 +93,16 @@ func buildFunc(cmd *cobra.Command, args []string) error { goBuildEnv = append(goBuildEnv, "CGO_ENABLED=0") } - trimPath := os.ExpandEnv("all=-trimpath=${GOPATH}") - goTrimFlags := []string{"-gcflags", trimPath, "-asmflags", trimPath} absProjectPath := projutil.MustGetwd() projectName := filepath.Base(absProjectPath) // Don't need to build Go code if a non-Go Operator. if projutil.IsOperatorGo() { + trimPath := fmt.Sprintf("all=-trimpath=%s", filepath.Dir(absProjectPath)) opts := projutil.GoCmdOptions{ BinName: filepath.Join(absProjectPath, scaffold.BuildBinDir, projectName), - PackagePath: path.Join(projutil.CheckAndGetProjectGoPkg(), filepath.ToSlash(scaffold.ManagerDir)), - Args: goTrimFlags, + PackagePath: path.Join(projutil.GetGoPkg(), filepath.ToSlash(scaffold.ManagerDir)), + Args: []string{"-gcflags", trimPath, "-asmflags", trimPath}, Env: goBuildEnv, GoMod: projutil.IsDepManagerGoMod(), } diff --git a/cmd/operator-sdk/internal/genutil/k8s.go b/cmd/operator-sdk/internal/genutil/k8s.go index b324232dd52..496aeff9dc9 100644 --- a/cmd/operator-sdk/internal/genutil/k8s.go +++ b/cmd/operator-sdk/internal/genutil/k8s.go @@ -35,7 +35,7 @@ import ( func K8sCodegen() error { projutil.MustInProjectRoot() - repoPkg := projutil.CheckAndGetProjectGoPkg() + repoPkg := projutil.GetGoPkg() gvMap, err := parseGroupVersions() if err != nil { diff --git a/cmd/operator-sdk/internal/genutil/openapi.go b/cmd/operator-sdk/internal/genutil/openapi.go index 9ea40be9363..887f1b04529 100644 --- a/cmd/operator-sdk/internal/genutil/openapi.go +++ b/cmd/operator-sdk/internal/genutil/openapi.go @@ -38,7 +38,7 @@ func OpenAPIGen() error { projutil.MustInProjectRoot() absProjectPath := projutil.MustGetwd() - repoPkg := projutil.CheckAndGetProjectGoPkg() + repoPkg := projutil.GetGoPkg() gvMap, err := parseGroupVersions() if err != nil { diff --git a/cmd/operator-sdk/main.go b/cmd/operator-sdk/main.go index e68a3ac9f95..7eeaa35f631 100644 --- a/cmd/operator-sdk/main.go +++ b/cmd/operator-sdk/main.go @@ -15,6 +15,7 @@ package main import ( + "fmt" "os" // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.) @@ -48,14 +49,15 @@ func main() { Short: "An SDK for building operators with ease", PersistentPreRun: func(cmd *cobra.Command, args []string) { if viper.GetBool(flags.VerboseOpt) { - err := projutil.SetGoVerbose() - if err != nil { - log.Errorf("Could not set GOFLAGS: (%v)", err) - return + if err := projutil.SetGoVerbose(); err != nil { + log.Fatalf("Could not set GOFLAGS: (%v)", err) } log.SetLevel(log.DebugLevel) log.Debug("Debug logging is set") } + if err := checkDepManagerForCmd(cmd); err != nil { + log.Fatal(err) + } }, } @@ -82,3 +84,77 @@ func main() { os.Exit(1) } } + +func checkDepManagerForCmd(cmd *cobra.Command) (err error) { + // Certain commands are able to be run anywhere or handle this check + // differently in their CLI code. + if skipCheckForCmd(cmd) { + return nil + } + // Do not perform this check if the project is non-Go, as they will not have + // a (Go) dep manager. + if !projutil.IsOperatorGo() { + return nil + } + // Do not perform a dep manager check if the working directory is not in + // the project root, as some sub-commands might not require project root. + // Individual subcommands will perform this check as needed. + if err := projutil.CheckProjectRoot(); err != nil { + return nil + } + + dm, err := projutil.GetDepManagerType() + if err != nil { + return err + } + return checkDepManager(dm) +} + +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{}{}, +} + +func skipCheckForCmd(cmd *cobra.Command) (skip bool) { + if _, ok := commandsToSkip[cmd.Name()]; ok { + return true + } + cmd.VisitParents(func(pc *cobra.Command) { + if _, ok := commandsToSkip[pc.Name()]; ok { + // The bare "operator-sdk" command will be checked above. + if pc.Name() != "operator-sdk" { + skip = true + } + } + }) + return skip +} + +func checkDepManager(dm projutil.DepManagerType) error { + switch dm { + case projutil.DepManagerGoMod: + goModOn, err := projutil.GoModOn() + if err != nil { + return err + } + if !goModOn { + return fmt.Errorf(`dependency manager "modules" requires working directory to be in $GOPATH/src` + + ` and GO111MODULE=on, or outside of $GOPATH/src and GO111MODULE="on", "auto", or unset`) + } + case projutil.DepManagerDep: + inGopathSrc, err := projutil.WdInGoPathSrc() + if err != nil { + return err + } + if !inGopathSrc { + return fmt.Errorf(`dependency manager "dep" requires working directory to be in $GOPATH/src`) + } + default: + return projutil.ErrInvalidDepManager(dm) + } + return nil +} diff --git a/cmd/operator-sdk/migrate/cmd.go b/cmd/operator-sdk/migrate/cmd.go index 18efade6605..a9387128610 100644 --- a/cmd/operator-sdk/migrate/cmd.go +++ b/cmd/operator-sdk/migrate/cmd.go @@ -33,6 +33,7 @@ import ( var ( depManager string headerFile string + repo string ) // NewCmd returns a command that will add source code to an existing non-go operator @@ -46,6 +47,7 @@ func NewCmd() *cobra.Command { newCmd.Flags().StringVar(&depManager, "dep-manager", "modules", `Dependency manager the new project will use (choices: "dep", "modules")`) 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 with Go modules, and cannot be set if --dep-manager=dep") return newCmd } @@ -55,7 +57,13 @@ func NewCmd() *cobra.Command { func migrateRun(cmd *cobra.Command, args []string) error { projutil.MustInProjectRoot() - _ = projutil.CheckAndGetProjectGoPkg() + if err := verifyFlags(); err != nil { + return err + } + + if repo == "" { + repo = projutil.GetGoPkg() + } opType := projutil.GetOperatorType() switch opType { @@ -67,12 +75,20 @@ func migrateRun(cmd *cobra.Command, args []string) error { return fmt.Errorf("operator of type %s cannot be migrated", opType) } +func verifyFlags() error { + err := projutil.CheckDepManagerWithRepo(projutil.DepManagerType(depManager), 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: projutil.CheckAndGetProjectGoPkg(), + Repo: repo, AbsProjectPath: wd, ProjectName: filepath.Base(wd), } @@ -126,7 +142,7 @@ func migrateHelm() error { wd := projutil.MustGetwd() cfg := &input.Config{ - Repo: projutil.CheckAndGetProjectGoPkg(), + Repo: repo, AbsProjectPath: wd, ProjectName: filepath.Base(wd), } @@ -180,9 +196,6 @@ func scaffoldHelmDepManager(s *scaffold.Scaffold, cfg *input.Config) error { case projutil.DepManagerDep: files = append(files, &helm.GopkgToml{}) case projutil.DepManagerGoMod: - if err := goModCheck(); err != nil { - return err - } files = append(files, &helm.GoMod{}, &scaffold.Tools{}) default: return projutil.ErrInvalidDepManager(depManager) @@ -196,21 +209,9 @@ func scaffoldAnsibleDepManager(s *scaffold.Scaffold, cfg *input.Config) error { case projutil.DepManagerDep: files = append(files, &ansible.GopkgToml{}) case projutil.DepManagerGoMod: - if err := goModCheck(); err != nil { - return err - } files = append(files, &ansible.GoMod{}, &scaffold.Tools{}) default: return projutil.ErrInvalidDepManager(depManager) } return s.Execute(cfg, files...) } - -func goModCheck() error { - goModOn, err := projutil.GoModOn() - if err == nil && !goModOn { - log.Fatal(`Dependency manager "modules" has been selected but go modules are not active. ` + - `Activate modules then run "operator-sdk migrate".`) - } - return err -} diff --git a/cmd/operator-sdk/new/cmd.go b/cmd/operator-sdk/new/cmd.go index 30e2d8a6596..890c5d9b56f 100644 --- a/cmd/operator-sdk/new/cmd.go +++ b/cmd/operator-sdk/new/cmd.go @@ -46,10 +46,10 @@ generates a default directory layout based on the input . is the project name of the new operator. (e.g app-operator) For example: - $ mkdir $GOPATH/src/github.com/example.com/ - $ cd $GOPATH/src/github.com/example.com/ + $ mkdir $HOME/projects/example.com/ + $ cd $HOME/projects/example.com/ $ operator-sdk new app-operator -generates a skeletal app-operator application in $GOPATH/src/github.com/example.com/app-operator. +generates a skeletal app-operator application in $HOME/projects/example.com/app-operator. `, RunE: newFunc, } @@ -58,6 +58,7 @@ generates a skeletal app-operator application in $GOPATH/src/github.com/example. newCmd.Flags().StringVar(&kind, "kind", "", "Kubernetes CustomResourceDefintion kind. (e.g AppService) - used with \"ansible\" or \"helm\" types") newCmd.Flags().StringVar(&operatorType, "type", "go", "Type of operator to initialize (choices: \"go\", \"ansible\" or \"helm\")") newCmd.Flags().StringVar(&depManager, "dep-manager", "modules", `Dependency manager the new project will use (choices: "dep", "modules")`) + newCmd.Flags().StringVar(&repo, "repo", "", "Project repository path for Go operators. Used as the project's Go import path. This must be set if outside of $GOPATH/src with Go modules, and cannot be set if --dep-manager=dep") newCmd.Flags().BoolVar(&skipGit, "skip-git-init", false, "Do not init the directory as a git repository") newCmd.Flags().StringVar(&headerFile, "header-file", "", "Path to file containing headers for generated Go files. Copied to hack/boilerplate.go.txt") newCmd.Flags().BoolVar(&makeVendor, "vendor", false, "Use a vendor directory for dependencies. This flag only applies when --dep-manager=modules (the default)") @@ -78,6 +79,7 @@ var ( projectName string depManager string headerFile string + repo string skipGit bool makeVendor bool skipValidation bool @@ -101,6 +103,9 @@ func newFunc(cmd *cobra.Command, args []string) error { switch operatorType { case projutil.OperatorTypeGo: + if repo == "" { + repo = path.Join(projutil.GetGoPkg(), projectName) + } if err := doGoScaffold(); err != nil { return err } @@ -160,7 +165,7 @@ func mustBeNewProject() { func doGoScaffold() error { cfg := &input.Config{ - Repo: path.Join(projutil.CheckAndGetProjectGoPkg(), projectName), + Repo: repo, AbsProjectPath: filepath.Join(projutil.MustGetwd(), projectName), ProjectName: projectName, } @@ -182,8 +187,8 @@ func doGoScaffold() error { if goModOn, merr := projutil.GoModOn(); merr != nil { return merr } else if !goModOn { - log.Fatalf(`Dependency manager "%s" has been selected but go modules are not active. `+ - `Activate modules then run "operator-sdk new %s".`, m, projectName) + return errors.New(`dependency manager "modules" requires working directory to be in $GOPATH/src` + + ` and GO111MODULE=on, or outside of $GOPATH/src and GO111MODULE="on", "auto", or unset`) } err = s.Execute(cfg, &scaffold.GoMod{}, &scaffold.Tools{}) default: @@ -383,9 +388,15 @@ func verifyFlags() error { if len(apiVersion) != 0 || len(kind) != 0 { return fmt.Errorf("operators of type Go do not use --api-version or --kind") } - if !makeVendor && projutil.DepManagerType(depManager) == projutil.DepManagerDep { + + dm := projutil.DepManagerType(depManager) + if !makeVendor && dm == projutil.DepManagerDep { log.Warnf("--dep-manager=dep requires a vendor directory; ignoring --vendor=false") } + err := projutil.CheckDepManagerWithRepo(dm, repo) + if err != nil { + return err + } } // --api-version and --kind are required with --type=ansible and --type=helm, with one exception. @@ -407,6 +418,7 @@ func verifyFlags() error { return fmt.Errorf("value of --api-version has wrong format (%v); format must be $GROUP_NAME/$VERSION (e.g app.example.com/v1alpha1)", apiVersion) } } + return nil } @@ -419,7 +431,7 @@ func execProjCmd(cmd string, args ...string) error { func getDeps() error { switch m := projutil.DepManagerType(depManager); m { case projutil.DepManagerDep: - log.Info("Running dep ensure ...") + log.Info("Running dep ensure") if err := execProjCmd("dep", "ensure", "-v"); err != nil { return err } @@ -427,6 +439,7 @@ func getDeps() error { // Only when a user requests a vendor directory be created should // "go mod vendor" be run during project initialization. if makeVendor { + log.Info("Running go mod vendor") opts := projutil.GoCmdOptions{ Args: []string{"-v"}, Dir: filepath.Join(projutil.MustGetwd(), projectName), @@ -449,7 +462,7 @@ func initGit() error { if skipGit { return nil } - log.Info("Run git init ...") + log.Info("Running git init") if err := execProjCmd("git", "init"); err != nil { return err } diff --git a/cmd/operator-sdk/olmcatalog/gen-csv.go b/cmd/operator-sdk/olmcatalog/gen-csv.go index 720f60d5232..24fb69c66ce 100644 --- a/cmd/operator-sdk/olmcatalog/gen-csv.go +++ b/cmd/operator-sdk/olmcatalog/gen-csv.go @@ -77,7 +77,7 @@ func genCSVFunc(cmd *cobra.Command, args []string) error { ProjectName: filepath.Base(absProjectPath), } if projutil.IsOperatorGo() { - cfg.Repo = projutil.CheckAndGetProjectGoPkg() + cfg.Repo = projutil.GetGoPkg() } log.Infof("Generating CSV manifest version %s", csvVersion) diff --git a/cmd/operator-sdk/up/local.go b/cmd/operator-sdk/up/local.go index 3178330bcec..235c8151e11 100644 --- a/cmd/operator-sdk/up/local.go +++ b/cmd/operator-sdk/up/local.go @@ -203,7 +203,7 @@ func buildLocal(outputBinName string) error { } opts := projutil.GoCmdOptions{ BinName: outputBinName, - PackagePath: path.Join(projutil.CheckAndGetProjectGoPkg(), filepath.ToSlash(scaffold.ManagerDir)), + PackagePath: path.Join(projutil.GetGoPkg(), filepath.ToSlash(scaffold.ManagerDir)), Args: args, GoMod: projutil.IsDepManagerGoMod(), } diff --git a/doc/dev/testing/running-the-tests.md b/doc/dev/testing/running-the-tests.md index bfca7c6c5d0..f931d7b4a49 100644 --- a/doc/dev/testing/running-the-tests.md +++ b/doc/dev/testing/running-the-tests.md @@ -117,8 +117,8 @@ during the go tests can cause these cleanups to fail (the ansible and helm E2E t always clean up correctly). For example, if a segfault occurs or a user kills the testing process, the cleanup functions for the go tests will not run. To manually clean up a test: -1. Delete the CRD (`kubectl delete -f $GOPATH/src/github.com/example-inc/memcached-operator/deploy/crds/cache_v1alpha1_memcached_crd.yaml`). -2. Delete the created project in `$GOPATH/src/github.com/example-inc/memcached-operator` +1. Delete the CRD (`kubectl delete -f $HOME/projects/example.com/memcached-operator/deploy/crds/cache_v1alpha1_memcached_crd.yaml`). +2. Delete the created project in `$HOME/projects/example.com/memcached-operator` 3. Delete the namespaces that the tests run in, which also deletes any resources created within the namespaces. The namespaces start with `memcached-memcached-group` or `main` and are appended with a unix timestamp (seconds since Jan 1 1970). The kubectl command can be used to delete namespaces: `kubectl delete namespace $NAMESPACE`. [travis]: ./travis-build.md diff --git a/doc/sdk-cli-reference.md b/doc/sdk-cli-reference.md index 01b6e852a9b..f6ad43b0a05 100644 --- a/doc/sdk-cli-reference.md +++ b/doc/sdk-cli-reference.md @@ -136,10 +136,6 @@ Currently only runs `deepcopy-gen` to generate the required `DeepCopy()` functio **Note**: This command must be run every time the api (spec and status) for a custom resource type is updated. -### Flags - -* `--header-file` string - Path to file containing headers for generated files (optional). - #### Example ```console @@ -228,6 +224,8 @@ you will need to rename it before running migrate or manually add it to your Doc #### Flags * `--dep-manager` string - Dependency manager the migrated project will use (choices: "dep", "modules") (default "modules") +* `--header-file` string - Path to file containing headers for generated Go files. Copied to hack/boilerplate.go.txt +* `--repo` string - Project repository path for Go operators. Used as the project's Go import path. This must be set if outside of `$GOPATH/src` with Go modules, and cannot be set if `--dep-manager=dep` ### Example @@ -261,7 +259,9 @@ Scaffolds a new operator project. * `--helm-chart` string - Initialize helm operator with existing helm chart (``, `/`, or local path) * `--helm-chart-repo` string - Chart repository URL for the requested helm chart * `--helm-chart-version` string - Specific version of the helm chart (default is latest version) +* `--header-file` string - Path to file containing headers for generated Go files. Copied to hack/boilerplate.go.txt * `--dep-manager` string - Dependency manager the new project will use (choices: "dep", "modules") (default "modules") +* `--repo` string - Project repository path for Go operators. Used as the project's Go import path. This must be set if outside of `$GOPATH/src` with Go modules, and cannot be set if `--dep-manager=dep` * `--skip-git-init` - Do not init the directory as a git repository * `--vendor` - Use a vendor directory for dependencies. This flag only applies when `--dep-manager=modules` (the default) * `--skip-validation` - Do not validate the resulting project's structure and dependencies. (Only used for --type go) @@ -272,8 +272,8 @@ Scaffolds a new operator project. #### Go project ```console -$ mkdir $GOPATH/src/github.com/example.com/ -$ cd $GOPATH/src/github.com/example.com/ +$ mkdir $HOME/projects/example.com/ +$ cd $HOME/projects/example.com/ $ operator-sdk new app-operator ``` @@ -330,7 +330,6 @@ Adds the API definition for a new custom resource under `pkg/apis` and generates * `--api-version` string - CRD APIVersion in the format `$GROUP_NAME/$VERSION` (e.g app.example.com/v1alpha1) * `--kind` string - CRD Kind. (e.g AppService) -* `--header-file` string - Path to file containing headers for generated files (optional). #### Example diff --git a/doc/user-guide.md b/doc/user-guide.md index 3a93fc772d9..220b8eadbcb 100644 --- a/doc/user-guide.md +++ b/doc/user-guide.md @@ -23,8 +23,8 @@ Follow the steps in the [installation guide][install_guide] to learn how to inst Use the CLI to create a new memcached-operator project: ```sh -$ mkdir -p $GOPATH/src/github.com/example-inc/ -$ cd $GOPATH/src/github.com/example-inc/ +$ mkdir -p $HOME/projects/example.com/ +$ cd $HOME/projects/example.com/ $ operator-sdk new memcached-operator $ cd memcached-operator ``` @@ -37,7 +37,7 @@ By default, `operator-sdk new` generates a `go.mod` file to be used with [Go mod ##### Go modules -If using go modules (the default dependency manager) in your project, ensure you activate module support before using the SDK. From the [go modules Wiki][go_mod_wiki]: +If using Go modules (the default dependency manager) in your project, ensure you activate module support before using the SDK. From the [Go modules Wiki][go_mod_wiki]: > You can activate module support in one of two ways: > - Invoke the go command in a directory outside of the $GOPATH/src tree, with a valid go.mod file in the current directory or any parent of it and the environment variable GO111MODULE unset (or explicitly set to auto). @@ -202,7 +202,7 @@ Once this is done, there are two ways to run the operator: **Note**: `operator-sdk build` invokes `docker build` by default, and optionally `buildah bud`. If using `buildah`, skip to the `operator-sdk build` invocation instructions below. If using `docker`, make sure your docker daemon is running and that you can run the docker client without sudo. You can check if this is the case by running `docker version`, which should complete without errors. Follow instructions for your OS/distribution on how to start the docker daemon and configure your access permissions, if needed. -**Note**: If using go modules and a `vendor/` directory, run +**Note**: If using Go modules and a `vendor/` directory, run ```sh $ go mod vendor ``` diff --git a/hack/image/build-ansible-image.sh b/hack/image/build-ansible-image.sh index d134245ff46..1e9813e4877 100755 --- a/hack/image/build-ansible-image.sh +++ b/hack/image/build-ansible-image.sh @@ -5,7 +5,7 @@ set -eux source hack/lib/test_lib.sh ROOTDIR="$(pwd)" -GOTMP="$(mktemp -d -p $GOPATH/src)" +GOTMP="$(mktemp -d)" trap_add 'rm -rf $GOTMP' EXIT BASEIMAGEDIR="$GOTMP/ansible-operator" mkdir -p "$BASEIMAGEDIR" diff --git a/hack/image/build-helm-image.sh b/hack/image/build-helm-image.sh index 80781618a12..81ad05d6b84 100755 --- a/hack/image/build-helm-image.sh +++ b/hack/image/build-helm-image.sh @@ -5,7 +5,7 @@ set -eux source hack/lib/test_lib.sh ROOTDIR="$(pwd)" -GOTMP="$(mktemp -d -p $GOPATH/src)" +GOTMP="$(mktemp -d)" trap_add 'rm -rf $GOTMP' EXIT BASEIMAGEDIR="$GOTMP/helm-operator" mkdir -p "$BASEIMAGEDIR" diff --git a/hack/image/build-scorecard-proxy-image.sh b/hack/image/build-scorecard-proxy-image.sh index 7f9f0cd70e7..860bfba0baa 100755 --- a/hack/image/build-scorecard-proxy-image.sh +++ b/hack/image/build-scorecard-proxy-image.sh @@ -3,7 +3,13 @@ set -eux # build operator binary and base image -GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -gcflags "all=-trimpath=${GOPATH}" -asmflags "all=-trimpath=${GOPATH}" -o images/scorecard-proxy/scorecard-proxy images/scorecard-proxy/cmd/proxy/main.go +WD="$(dirname "$(pwd)")" +GOOS=linux GOARCH=amd64 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" . popd diff --git a/hack/tests/e2e-ansible-molecule.sh b/hack/tests/e2e-ansible-molecule.sh index 32bebb949a5..6f600f6cf5e 100755 --- a/hack/tests/e2e-ansible-molecule.sh +++ b/hack/tests/e2e-ansible-molecule.sh @@ -5,7 +5,7 @@ source hack/lib/test_lib.sh set -eux ROOTDIR="$(pwd)" -GOTMP="$(mktemp -d -p $GOPATH/src)" +GOTMP="$(mktemp -d)" trap_add 'rm -rf $GOTMP' EXIT # Needs to be from source until 2.20 comes out pip install --user git+https://github.com/ansible/molecule.git @@ -27,7 +27,11 @@ remove_prereqs() { } pushd "$GOTMP" -operator-sdk new memcached-operator --api-version=ansible.example.com/v1alpha1 --kind=Memcached --type=ansible --generate-playbook +operator-sdk new memcached-operator \ + --api-version=ansible.example.com/v1alpha1 \ + --kind=Memcached \ + --type=ansible \ + --generate-playbook cp "$ROOTDIR/test/ansible-memcached/tasks.yml" memcached-operator/roles/memcached/tasks/main.yml cp "$ROOTDIR/test/ansible-memcached/defaults.yml" memcached-operator/roles/memcached/defaults/main.yml cp "$ROOTDIR/test/ansible-memcached/asserts.yml" memcached-operator/molecule/default/asserts.yml diff --git a/hack/tests/e2e-ansible.sh b/hack/tests/e2e-ansible.sh index 905fb967a19..a34bf32ba30 100755 --- a/hack/tests/e2e-ansible.sh +++ b/hack/tests/e2e-ansible.sh @@ -9,7 +9,7 @@ go test -count=1 ./pkg/ansible/proxy/... DEST_IMAGE="quay.io/example/memcached-operator:v0.0.2" ROOTDIR="$(pwd)" -GOTMP="$(mktemp -d -p $GOPATH/src)" +GOTMP="$(mktemp -d)" trap_add 'rm -rf $GOTMP' EXIT deploy_operator() { @@ -97,7 +97,10 @@ if which oc 2>/dev/null; then oc project default; fi # create and build the operator pushd "$GOTMP" -operator-sdk new memcached-operator --api-version=ansible.example.com/v1alpha1 --kind=Memcached --type=ansible +operator-sdk new memcached-operator \ + --api-version=ansible.example.com/v1alpha1 \ + --kind=Memcached \ + --type=ansible cp "$ROOTDIR/test/ansible-memcached/tasks.yml" memcached-operator/roles/memcached/tasks/main.yml cp "$ROOTDIR/test/ansible-memcached/defaults.yml" memcached-operator/roles/memcached/defaults/main.yml cp -a "$ROOTDIR/test/ansible-memcached/memfin" memcached-operator/roles/ @@ -126,7 +129,7 @@ echo "### Now testing migrate to hybrid operator" echo "###" export GO111MODULE=on -operator-sdk migrate +operator-sdk migrate --repo=github.com/example-inc/memcached-operator if [[ ! -e build/Dockerfile.sdkold ]]; then @@ -149,7 +152,7 @@ go build ./... # Use the local operator-sdk directory as the repo. To make the go toolchain # happy, the directory needs a `go.mod` file that specifies the module name, # so we need this temporary hack until we update the SDK repo itself to use -# go modules. +# Go modules. echo "module ${SDK_REPO}" > "${ROOTDIR}/go.mod" trap_add "rm ${ROOTDIR}/go.mod" EXIT go mod edit -replace="${SDK_REPO}=$ROOTDIR" diff --git a/hack/tests/e2e-helm.sh b/hack/tests/e2e-helm.sh index a07c6f32f63..9b4dacaf468 100755 --- a/hack/tests/e2e-helm.sh +++ b/hack/tests/e2e-helm.sh @@ -6,7 +6,7 @@ set -eux DEST_IMAGE="quay.io/example/nginx-operator:v0.0.2" ROOTDIR="$(pwd)" -GOTMP="$(mktemp -d -p $GOPATH/src)" +GOTMP="$(mktemp -d)" trap_add 'rm -rf $GOTMP' EXIT deploy_operator() { @@ -108,7 +108,11 @@ fi # create and build the operator pushd "$GOTMP" -log=$(operator-sdk new nginx-operator --api-version=helm.example.com/v1alpha1 --kind=Nginx --type=helm 2>&1) +log=$(operator-sdk new nginx-operator \ + --api-version=helm.example.com/v1alpha1 \ + --kind=Nginx \ + --type=helm \ + 2>&1) echo $log if echo $log | grep -q "failed to generate RBAC rules"; then echo FAIL expected successful generation of RBAC rules @@ -134,7 +138,7 @@ echo "### Now testing migrate to hybrid operator" echo "###" export GO111MODULE=on -operator-sdk migrate +operator-sdk migrate --repo=github.com/example-inc/nginx-operator if [[ ! -e build/Dockerfile.sdkold ]]; then @@ -157,7 +161,7 @@ go build ./... # Use the local operator-sdk directory as the repo. To make the go toolchain # happy, the directory needs a `go.mod` file that specifies the module name, # so we need this temporary hack until we update the SDK repo itself to use -# go modules. +# Go modules. echo "module ${SDK_REPO}" > "${ROOTDIR}/go.mod" trap_add "rm ${ROOTDIR}/go.mod" EXIT go mod edit -replace="${SDK_REPO}=$ROOTDIR" diff --git a/internal/pkg/scaffold/internal/deps/print_go_mod.go b/internal/pkg/scaffold/internal/deps/print_go_mod.go index be659f7b548..35bac923cdd 100644 --- a/internal/pkg/scaffold/internal/deps/print_go_mod.go +++ b/internal/pkg/scaffold/internal/deps/print_go_mod.go @@ -28,7 +28,7 @@ import ( func ExecGoModTmpl(tmpl string) ([]byte, error) { projutil.MustInProjectRoot() - repo := projutil.CheckAndGetProjectGoPkg() + repo := projutil.GetGoPkg() t, err := template.New("").Parse(tmpl) if err != nil { return nil, fmt.Errorf("failed to parse go mod template: (%v)", err) diff --git a/internal/util/projutil/exec.go b/internal/util/projutil/exec.go index fc9b27f250f..bb386aa0c9e 100644 --- a/internal/util/projutil/exec.go +++ b/internal/util/projutil/exec.go @@ -51,7 +51,7 @@ type GoCmdOptions struct { Dir string // GoMod determines whether to set the "-mod=vendor" flag. // If true and ./vendor/ exists, "go {cmd}" will use vendored modules. - // If false, "go {cmd}" will not use go modules. This is the default. + // If false, "go {cmd}" will not use Go modules. This is the default. // This applies to build, clean, get, install, list, run, and test. GoMod bool } @@ -157,7 +157,7 @@ func (opts GoCmdOptions) setCmdFields(c *exec.Cmd) { // the environment variable GO111MODULE unset (or explicitly set to auto). // - Invoke the go command with GO111MODULE=on environment variable set. // -// GoModOn returns true if go modules are on in one of the above two ways. +// GoModOn returns true if Go modules are on in one of the above two ways. func GoModOn() (bool, error) { v, ok := os.LookupEnv(GoModEnv) if v == "off" { diff --git a/internal/util/projutil/project_util.go b/internal/util/projutil/project_util.go index 74040deb1fa..6a5d523b46d 100644 --- a/internal/util/projutil/project_util.go +++ b/internal/util/projutil/project_util.go @@ -79,7 +79,7 @@ const ( type ErrInvalidDepManager string func (e ErrInvalidDepManager) Error() string { - return fmt.Sprintf(`"%s" is not a valid dep manager; dep manager must be one of ["%v", "%v"]`, e, DepManagerDep, DepManagerGoMod) + return fmt.Sprintf(`"%s" is not a valid dep manager; dep manager must be one of ["%v", "%v"]`, string(e), DepManagerDep, DepManagerGoMod) } var ErrNoDepManager = fmt.Errorf(`no valid dependency manager file found; dep manager must be one of ["%v", "%v"]`, DepManagerDep, DepManagerGoMod) @@ -148,23 +148,17 @@ func getHomeDir() (string, error) { return homedir.Expand(hd) } -// CheckAndGetProjectGoPkg checks if this project's repository path is rooted -// under $GOPATH and returns the current directory's import path, -// e.g: "github.com/example-inc/app-operator" -func CheckAndGetProjectGoPkg() string { - gopath := MustSetWdGopath(MustGetGopath()) - return parseGoPkg(gopath) -} - // GetGoPkg returns the current directory's import path by parsing it from // wd if this project's repository path is rooted under $GOPATH/src, or -// from go.mod the project uses go modules to manage dependencies. +// from go.mod the project uses Go modules to manage dependencies. // // Example: "github.com/example-inc/app-operator" func GetGoPkg() string { // Default to reading from go.mod, as it should usually have the (correct) // package path, and no further processing need be done on it if so. - if _, err := os.Stat(goModFile); err == nil { + if _, err := os.Stat(goModFile); err != nil && !os.IsNotExist(err) { + log.Fatalf("Failed to read go.mod: %v", err) + } else if err == nil { b, err := ioutil.ReadFile(goModFile) if err != nil { log.Fatalf("Read go.mod: %v", err) @@ -283,3 +277,32 @@ func SetGoVerbose() error { } return nil } + +// CheckDepManagerWithRepo ensures dependency manager type and repo are being used in combination +// correctly, as different dependency managers have different Go environment +// requirements. +func CheckDepManagerWithRepo(dm DepManagerType, repo string) error { + inGopathSrc, err := WdInGoPathSrc() + if err != nil { + return err + } + switch dm { + case DepManagerDep: + // dep assumes the project's path under $GOPATH/src is the project's + // repo path. + if repo != "" { + return fmt.Errorf(`The flag --repo cannot be set with dependency manager "dep", as dep always infers the repo path`) + } + if !inGopathSrc { + return fmt.Errorf(`dependency manager "dep" requires working directory to be in $GOPATH/src`) + } + case DepManagerGoMod: + if !inGopathSrc && repo == "" { + return fmt.Errorf(`dependency manager "modules" requires the flag --repo to be set if the working directory is not in $GOPATH/src. See "operator-sdk new -h"`) + } + default: + return ErrInvalidDepManager(dm) + } + + return nil +} diff --git a/test/e2e/memcached_test.go b/test/e2e/memcached_test.go index 47bc039188f..67066d22ffe 100644 --- a/test/e2e/memcached_test.go +++ b/test/e2e/memcached_test.go @@ -31,7 +31,6 @@ import ( "github.com/operator-framework/operator-sdk/internal/pkg/scaffold" "github.com/operator-framework/operator-sdk/internal/util/fileutil" - "github.com/operator-framework/operator-sdk/internal/util/projutil" "github.com/operator-framework/operator-sdk/internal/util/yamlutil" framework "github.com/operator-framework/operator-sdk/pkg/test" "github.com/operator-framework/operator-sdk/pkg/test/e2eutil" @@ -57,17 +56,15 @@ const ( timeout = time.Second * 120 cleanupRetryInterval = time.Second * 1 cleanupTimeout = time.Second * 10 + sdkRepo = "github.com/operator-framework/operator-sdk" operatorName = "memcached-operator" + testRepo = "github.com/example-inc/" + operatorName ) func TestMemcached(t *testing.T) { // get global framework variables ctx := framework.NewTestCtx(t) defer ctx.Cleanup() - gopath, ok := os.LookupEnv(projutil.GoPathEnv) - if !ok || gopath == "" { - t.Fatalf("$GOPATH not set") - } sdkTestE2EDir, err := os.Getwd() if err != nil { t.Fatal(err) @@ -88,7 +85,7 @@ func TestMemcached(t *testing.T) { } // Setup - absProjectPath, err := ioutil.TempDir(filepath.Join(gopath, "src"), "tmp.") + absProjectPath, err := ioutil.TempDir("", "tmp.") if err != nil { t.Fatal(err) } @@ -105,6 +102,7 @@ func TestMemcached(t *testing.T) { cmdOut, err := exec.Command("operator-sdk", "new", operatorName, + "--repo", testRepo, "--skip-validation").CombinedOutput() if err != nil { t.Fatalf("Error: %v\nCommand Output: %s\n", err, string(cmdOut)) @@ -114,7 +112,6 @@ func TestMemcached(t *testing.T) { t.Fatalf("Failed to change to %s directory: (%v)", operatorName, err) } - sdkRepo := "github.com/operator-framework/operator-sdk" replace := getGoModReplace(t, localSDKPath) if replace.repo != sdkRepo { if replace.isLocal { @@ -197,13 +194,9 @@ func TestMemcached(t *testing.T) { if err := os.MkdirAll(filepath.Dir(dst), fileutil.DefaultDirFileMode); err != nil { t.Fatalf("Could not create template destination directory: %s", err) } - srcTmpl, err := ioutil.ReadFile(src) + cmdOut, err = exec.Command("cp", src, dst).CombinedOutput() if err != nil { - t.Fatalf("Could not read template from %s: %s", src, err) - } - dstData := strings.Replace(string(srcTmpl), "github.com/example-inc", filepath.Base(absProjectPath), -1) - if err := ioutil.WriteFile(dst, []byte(dstData), fileutil.DefaultFileMode); err != nil { - t.Fatalf("Could not write template output to %s: %s", dst, err) + t.Fatalf("Error: %v\nCommand Output: %s\n", err, string(cmdOut)) } }