diff --git a/.github/workflows/apidiff.yml b/.github/workflows/apidiff.yml index 052c2bf08ee..e62ca838a69 100644 --- a/.github/workflows/apidiff.yml +++ b/.github/workflows/apidiff.yml @@ -21,7 +21,7 @@ jobs: with: go-version: '1.17' - name: Execute go-apidiff - uses: joelanford/go-apidiff@v0.1.0 + uses: joelanford/go-apidiff@v0.2.0 with: compare-imports: true print-compatible: true diff --git a/pkg/plugins/golang/declarative/v1/api.go b/pkg/plugins/golang/declarative/v1/api.go index dbf07edfb6c..73a33e28eb2 100644 --- a/pkg/plugins/golang/declarative/v1/api.go +++ b/pkg/plugins/golang/declarative/v1/api.go @@ -19,16 +19,13 @@ package v1 import ( "errors" "fmt" - "path/filepath" - - "github.com/spf13/afero" "sigs.k8s.io/kubebuilder/v3/pkg/config" "sigs.k8s.io/kubebuilder/v3/pkg/machinery" "sigs.k8s.io/kubebuilder/v3/pkg/model/resource" "sigs.k8s.io/kubebuilder/v3/pkg/plugin" "sigs.k8s.io/kubebuilder/v3/pkg/plugin/util" - "sigs.k8s.io/kubebuilder/v3/pkg/plugins/golang/declarative/v1/internal/templates" + "sigs.k8s.io/kubebuilder/v3/pkg/plugins/golang/declarative/v1/scaffolds" goPluginV2 "sigs.k8s.io/kubebuilder/v3/pkg/plugins/golang/v2" ) @@ -36,8 +33,6 @@ const ( // kbDeclarativePattern is the sigs.k8s.io/kubebuilder-declarative-pattern version kbDeclarativePatternForV2 = "v0.0.0-20200522144838-848d48e5b073" kbDeclarativePatternForV3 = "fea7e5cc701290589ec20ef4d9c0629d08b5307d" - - exampleManifestVersion = "0.0.1" ) var _ plugin.CreateAPISubcommand = &createAPISubcommand{} @@ -97,27 +92,17 @@ func (p *createAPISubcommand) InjectResource(res *resource.Resource) error { func (p *createAPISubcommand) Scaffold(fs machinery.Filesystem) error { fmt.Println("updating scaffold with declarative pattern...") - // Load the boilerplate - bp, err := afero.ReadFile(fs.FS, filepath.Join("hack", "boilerplate.go.txt")) + scaffolder := scaffolds.NewAPIScaffolder(p.config, *p.resource) + scaffolder.InjectFS(fs) + err := scaffolder.Scaffold() if err != nil { - return fmt.Errorf("error updating scaffold: unable to load boilerplate: %w", err) + return err } - boilerplate := string(bp) - - // Initialize the machinery.Scaffold that will write the files to disk - scaffold := machinery.NewScaffold(fs, - machinery.WithConfig(p.config), - machinery.WithBoilerplate(boilerplate), - machinery.WithResource(p.resource), - ) - - if err := scaffold.Execute( - &templates.Types{}, - &templates.Controller{}, - &templates.Channel{ManifestVersion: exampleManifestVersion}, - &templates.Manifest{ManifestVersion: exampleManifestVersion}, - ); err != nil { - return fmt.Errorf("error updating scaffold: %w", err) + + // Update Dockerfile + err = updateDockerfile() + if err != nil { + return err } // Track the resources following a declarative approach diff --git a/pkg/plugins/golang/declarative/v1/init.go b/pkg/plugins/golang/declarative/v1/init.go new file mode 100644 index 00000000000..5b6156561d2 --- /dev/null +++ b/pkg/plugins/golang/declarative/v1/init.go @@ -0,0 +1,88 @@ +/* +Copyright 2021 The Kubernetes 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 v1 + +import ( + "fmt" + "io/ioutil" + "path/filepath" + "strings" + + "sigs.k8s.io/kubebuilder/v3/pkg/config" + "sigs.k8s.io/kubebuilder/v3/pkg/machinery" + "sigs.k8s.io/kubebuilder/v3/pkg/plugin" + "sigs.k8s.io/kubebuilder/v3/pkg/plugin/util" +) + +var _ plugin.InitSubcommand = &initSubcommand{} + +type initSubcommand struct { + config config.Config +} + +func (p *initSubcommand) InjectConfig(c config.Config) error { + p.config = c + + return nil +} + +func (p *initSubcommand) Scaffold(fs machinery.Filesystem) error { + err := updateDockerfile() + if err != nil { + return err + } + return nil +} + +// updateDockerfile will add channels staging required for declarative plugin +func updateDockerfile() error { + fmt.Println("updating Dockerfile to add channels/ directory in the image") + managerFile := filepath.Join("Dockerfile") + + // nolint:lll + err := insertCodeIfDoesNotExist(managerFile, + "COPY controllers/ controllers/", + "\n# https://github.com/kubernetes-sigs/kubebuilder-declarative-pattern/blob/master/docs/addon/walkthrough/README.md#adding-a-manifest\n# Stage channels and make readable\nCOPY channels/ /channels/\nRUN chmod -R a+rx /channels/") + if err != nil { + return err + } + + err = insertCodeIfDoesNotExist(managerFile, + "COPY --from=builder /workspace/manager .", + "\n# copy channels\nCOPY --from=builder /channels /channels\n") + if err != nil { + return err + } + return nil +} + +// insertCodeIfDoesNotExist insert code if it does not already exists +func insertCodeIfDoesNotExist(filename, target, code string) error { + // false positive + // nolint:gosec + contents, err := ioutil.ReadFile(filename) + if err != nil { + return err + } + + idx := strings.Index(string(contents), code) + if idx != -1 { + return nil + } + + return util.InsertCode(filename, target, code) +} diff --git a/pkg/plugins/golang/declarative/v1/plugin.go b/pkg/plugins/golang/declarative/v1/plugin.go index 9a433e20129..9c8111b179f 100644 --- a/pkg/plugins/golang/declarative/v1/plugin.go +++ b/pkg/plugins/golang/declarative/v1/plugin.go @@ -37,6 +37,7 @@ var _ plugin.CreateAPI = Plugin{} // Plugin implements the plugin.Full interface type Plugin struct { + initSubcommand createAPISubcommand } @@ -49,6 +50,9 @@ func (Plugin) Version() plugin.Version { return pluginVersion } // SupportedProjectVersions returns an array with all project versions supported by the plugin func (Plugin) SupportedProjectVersions() []config.Version { return supportedProjectVersions } +// GetInitSubcommand will return the subcommand which is responsible for initializing and common scaffolding +func (p Plugin) GetInitSubcommand() plugin.InitSubcommand { return &p.initSubcommand } + // GetCreateAPISubcommand will return the subcommand which is responsible for scaffolding apis func (p Plugin) GetCreateAPISubcommand() plugin.CreateAPISubcommand { return &p.createAPISubcommand } diff --git a/pkg/plugins/golang/declarative/v1/scaffolds/api.go b/pkg/plugins/golang/declarative/v1/scaffolds/api.go new file mode 100644 index 00000000000..af0ab1907ed --- /dev/null +++ b/pkg/plugins/golang/declarative/v1/scaffolds/api.go @@ -0,0 +1,83 @@ +/* +Copyright 2022 The Kubernetes 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 scaffolds + +import ( + "fmt" + "path/filepath" + + "github.com/spf13/afero" + "sigs.k8s.io/kubebuilder/v3/pkg/config" + "sigs.k8s.io/kubebuilder/v3/pkg/machinery" + "sigs.k8s.io/kubebuilder/v3/pkg/model/resource" + "sigs.k8s.io/kubebuilder/v3/pkg/plugins" + "sigs.k8s.io/kubebuilder/v3/pkg/plugins/golang/declarative/v1/scaffolds/internal/templates" +) + +const ( + exampleManifestVersion = "0.0.1" +) + +var _ plugins.Scaffolder = &apiScaffolder{} + +type apiScaffolder struct { + config config.Config + resource resource.Resource + + // fs is the filesystem that will be used by the scaffolder + fs machinery.Filesystem +} + +// NewAPIScaffolder returns a new Scaffolder for declarative +func NewAPIScaffolder(config config.Config, res resource.Resource) plugins.Scaffolder { + return &apiScaffolder{ + config: config, + resource: res, + } +} + +// InjectFS implements cmdutil.Scaffolder +func (s *apiScaffolder) InjectFS(fs machinery.Filesystem) { + s.fs = fs +} + +// Scaffold implements cmdutil.Scaffolder +func (s *apiScaffolder) Scaffold() error { + // Load the boilerplate + boilerplate, err := afero.ReadFile(s.fs.FS, filepath.Join("hack", "boilerplate.go.txt")) + if err != nil { + return fmt.Errorf("error updating scaffold: unable to load boilerplate: %w", err) + } + + // Initialize the machinery.Scaffold that will write the files to disk + scaffold := machinery.NewScaffold(s.fs, + machinery.WithConfig(s.config), + machinery.WithBoilerplate(string(boilerplate)), + machinery.WithResource(&s.resource), + ) + + err = scaffold.Execute( + &templates.Types{}, + &templates.Controller{}, + &templates.Channel{ManifestVersion: exampleManifestVersion}, + &templates.Manifest{ManifestVersion: exampleManifestVersion}, + ) + if err != nil { + return fmt.Errorf("error updating scaffold: %w", err) + } + return nil +} diff --git a/pkg/plugins/golang/declarative/v1/internal/templates/channel.go b/pkg/plugins/golang/declarative/v1/scaffolds/internal/templates/channel.go similarity index 100% rename from pkg/plugins/golang/declarative/v1/internal/templates/channel.go rename to pkg/plugins/golang/declarative/v1/scaffolds/internal/templates/channel.go diff --git a/pkg/plugins/golang/declarative/v1/internal/templates/controller.go b/pkg/plugins/golang/declarative/v1/scaffolds/internal/templates/controller.go similarity index 100% rename from pkg/plugins/golang/declarative/v1/internal/templates/controller.go rename to pkg/plugins/golang/declarative/v1/scaffolds/internal/templates/controller.go diff --git a/pkg/plugins/golang/declarative/v1/internal/templates/manifest.go b/pkg/plugins/golang/declarative/v1/scaffolds/internal/templates/manifest.go similarity index 100% rename from pkg/plugins/golang/declarative/v1/internal/templates/manifest.go rename to pkg/plugins/golang/declarative/v1/scaffolds/internal/templates/manifest.go diff --git a/pkg/plugins/golang/declarative/v1/internal/templates/types.go b/pkg/plugins/golang/declarative/v1/scaffolds/internal/templates/types.go similarity index 100% rename from pkg/plugins/golang/declarative/v1/internal/templates/types.go rename to pkg/plugins/golang/declarative/v1/scaffolds/internal/templates/types.go diff --git a/pkg/plugins/golang/v2/scaffolds/internal/templates/dockerfile.go b/pkg/plugins/golang/v2/scaffolds/internal/templates/dockerfile.go index 38e4c47167c..04046f1b691 100644 --- a/pkg/plugins/golang/v2/scaffolds/internal/templates/dockerfile.go +++ b/pkg/plugins/golang/v2/scaffolds/internal/templates/dockerfile.go @@ -55,7 +55,7 @@ COPY api/ api/ COPY controllers/ controllers/ # Build -RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GO111MODULE=on go build -a -o manager main.go +RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -o manager main.go # Use distroless as minimal base image to package the manager binary # Refer to https://github.com/GoogleContainerTools/distroless for more details diff --git a/testdata/project-v2-addon/Dockerfile b/testdata/project-v2-addon/Dockerfile index 74eb9d7412f..0ee0267aec4 100644 --- a/testdata/project-v2-addon/Dockerfile +++ b/testdata/project-v2-addon/Dockerfile @@ -13,15 +13,22 @@ RUN go mod download COPY main.go main.go COPY api/ api/ COPY controllers/ controllers/ +# https://github.com/kubernetes-sigs/kubebuilder-declarative-pattern/blob/master/docs/addon/walkthrough/README.md#adding-a-manifest +# Stage channels and make readable +COPY channels/ /channels/ +RUN chmod -R a+rx /channels/ # Build -RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GO111MODULE=on go build -a -o manager main.go +RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -o manager main.go # Use distroless as minimal base image to package the manager binary # Refer to https://github.com/GoogleContainerTools/distroless for more details FROM gcr.io/distroless/static:nonroot WORKDIR / COPY --from=builder /workspace/manager . +# copy channels +COPY --from=builder /channels /channels + USER nonroot:nonroot ENTRYPOINT ["/manager"] diff --git a/testdata/project-v2-multigroup/Dockerfile b/testdata/project-v2-multigroup/Dockerfile index a765127fb4b..1a932038872 100644 --- a/testdata/project-v2-multigroup/Dockerfile +++ b/testdata/project-v2-multigroup/Dockerfile @@ -15,7 +15,7 @@ COPY apis/ apis/ COPY controllers/ controllers/ # Build -RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GO111MODULE=on go build -a -o manager main.go +RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -o manager main.go # Use distroless as minimal base image to package the manager binary # Refer to https://github.com/GoogleContainerTools/distroless for more details diff --git a/testdata/project-v2/Dockerfile b/testdata/project-v2/Dockerfile index 74eb9d7412f..f23bcb9d310 100644 --- a/testdata/project-v2/Dockerfile +++ b/testdata/project-v2/Dockerfile @@ -15,7 +15,7 @@ COPY api/ api/ COPY controllers/ controllers/ # Build -RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GO111MODULE=on go build -a -o manager main.go +RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -o manager main.go # Use distroless as minimal base image to package the manager binary # Refer to https://github.com/GoogleContainerTools/distroless for more details diff --git a/testdata/project-v3-addon/Dockerfile b/testdata/project-v3-addon/Dockerfile index 456533d4c2d..3a486ab6e30 100644 --- a/testdata/project-v3-addon/Dockerfile +++ b/testdata/project-v3-addon/Dockerfile @@ -13,6 +13,10 @@ RUN go mod download COPY main.go main.go COPY api/ api/ COPY controllers/ controllers/ +# https://github.com/kubernetes-sigs/kubebuilder-declarative-pattern/blob/master/docs/addon/walkthrough/README.md#adding-a-manifest +# Stage channels and make readable +COPY channels/ /channels/ +RUN chmod -R a+rx /channels/ # Build RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -o manager main.go @@ -22,6 +26,9 @@ RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -o manager main.go FROM gcr.io/distroless/static:nonroot WORKDIR / COPY --from=builder /workspace/manager . +# copy channels +COPY --from=builder /channels /channels + USER 65532:65532 ENTRYPOINT ["/manager"]