diff --git a/CHANGELOG.md b/CHANGELOG.md index 718abb9f36e..2067e266e92 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ - Enables controller-runtime metrics in Helm operator projects. ([#1482](https://github.com/operator-framework/operator-sdk/pull/1482)) - New flags `--vendor` and `--skip-validation` for [`operator-sdk new`](https://github.com/operator-framework/operator-sdk/blob/master/doc/sdk-cli-reference.md#new) that direct the SDK to initialize a new project with a `vendor/` directory, and without validating project dependencies. `vendor/` is not written by default. ([#1519](https://github.com/operator-framework/operator-sdk/pull/1519)) - 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)) ### Changed diff --git a/cmd/operator-sdk/add/api.go b/cmd/operator-sdk/add/api.go index 2b11ba04965..4be4934544f 100644 --- a/cmd/operator-sdk/add/api.go +++ b/cmd/operator-sdk/add/api.go @@ -16,12 +16,16 @@ package add import ( "fmt" + "io/ioutil" + "os" + "path/filepath" "github.com/operator-framework/operator-sdk/cmd/operator-sdk/internal/genutil" "github.com/operator-framework/operator-sdk/internal/pkg/scaffold" "github.com/operator-framework/operator-sdk/internal/pkg/scaffold/input" "github.com/operator-framework/operator-sdk/internal/util/projutil" + "github.com/pkg/errors" log "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) @@ -102,8 +106,15 @@ func apiRun(cmd *cobra.Command, args []string) error { Repo: projutil.CheckAndGetProjectGoPkg(), 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 { + return errors.Wrap(err, "scaffold group file") + } + err = s.Execute(cfg, &scaffold.Types{Resource: r}, &scaffold.AddToScheme{Resource: r}, @@ -134,3 +145,26 @@ func apiRun(cmd *cobra.Command, args []string) error { 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 errors.Wrapf(err, "error getting file %s input", i.Path) + } + groupDir := filepath.Dir(i.Path) + gdInfos, err := ioutil.ReadDir(groupDir) + if err != nil && !os.IsNotExist(err) { + return errors.Wrapf(err, "error reading dir %s", groupDir) + } + 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/internal/pkg/scaffold/group.go b/internal/pkg/scaffold/group.go new file mode 100644 index 00000000000..e406451f5d2 --- /dev/null +++ b/internal/pkg/scaffold/group.go @@ -0,0 +1,47 @@ +// 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/pkg/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}} +`