diff --git a/CHANGELOG.md b/CHANGELOG.md index 87ff815eda..7d6b13f1db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ### Added - Added [`run`](./doc/cli/operator-sdk_alpha_run.md) and [`cleanup`](./doc/cli/operator-sdk_alpha_cleanup.md) subcommands (under the `alpha` subcommand) to manage deployment/deletion of operators. These commands currently interact with OLM via an in-cluster registry-server created using an operator's on-disk manifests and managed by `operator-sdk`. ([#2402](ttps://github.com/operator-framework/operator-sdk/pull/2402)) +- Added [`bundle build`](./doc/cli/operator-sdk_alpha_bundle_build.md) (under the `alpha` subcommand) which builds, and optionally generates metadata for, [operator bundle images](https://github.com/openshift/enhancements/blob/ec2cf96/enhancements/olm/operator-registry.md). ([#2076](https://github.com/operator-framework/operator-sdk/pull/2076)) ### Changed diff --git a/cmd/operator-sdk/alpha/bundle/build.go b/cmd/operator-sdk/alpha/bundle/build.go new file mode 100644 index 0000000000..7a7b78a566 --- /dev/null +++ b/cmd/operator-sdk/alpha/bundle/build.go @@ -0,0 +1,129 @@ +// 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 bundle + +import ( + "errors" + "fmt" + "os" + "path/filepath" + "strings" + + catalog "github.com/operator-framework/operator-sdk/internal/scaffold/olm-catalog" + "github.com/operator-framework/operator-sdk/internal/util/projutil" + + "github.com/operator-framework/operator-registry/pkg/lib/bundle" + log "github.com/sirupsen/logrus" + "github.com/spf13/cobra" +) + +// newBundleBuildCmd returns a command that will build operator bundle image. +func newBundleBuildCmd() *cobra.Command { + c := bundleCmd{} + cmd := &cobra.Command{ + Use: "build", + Short: "Build an operator bundle image", + Long: `The 'operator-sdk bundle build' command will build an operator +bundle image containing operator metadata and manifests, tagged with the +provided image tag. + +To write metadata and a bundle image Dockerfile to disk, set '--generate-only=true'. +Bundle metadata will be generated in /metadata, and the Dockerfile +in . This flag is useful if you want to build an operator's +bundle image manually or modify metadata before building an image. + +More information on operator bundle images and metadata: +https://github.com/openshift/enhancements/blob/master/enhancements/olm/operator-bundle.md#docker + +NOTE: bundle images are not runnable.`, + Example: `The following invocation will build a test-operator bundle image using Docker. +This image will contain manifests for package channels 'stable' and 'beta': + +$ operator-sdk bundle build quay.io/example/test-operator:v0.1.0 \ + --directory ./deploy/olm-catalog/test-operator \ + --package test-operator \ + --channels stable,beta \ + --default-channel stable + +Assuming your operator has the same name as your operator and the only channel +is 'stable', the above command can be abbreviated to: + +$ operator-sdk bundle build quay.io/example/test-operator:v0.1.0 + +The following invocation will generate test-operator bundle metadata and +Dockerfile without building the image: + +$ operator-sdk bundle build \ + --generate-only \ + --directory ./deploy/olm-catalog/test-operator \ + --package test-operator \ + --channels stable,beta \ + --default-channel stable`, + RunE: func(cmd *cobra.Command, args []string) error { + channels := strings.Join(c.channels, ",") + if c.generateOnly { + if len(args) != 0 { + return fmt.Errorf("command %s does not accept any arguments", cmd.CommandPath()) + } + err := bundle.GenerateFunc(c.directory, c.packageName, channels, + c.defaultChannel, true) + if err != nil { + log.Fatalf("Error generating bundle image files: %v", err) + } + return nil + } + // An image tag is required for build only. + if len(args) != 1 { + return errors.New("a bundle image tag is a required argument, ex. example.com/test-operator:v0.1.0") + } + c.imageTag = args[0] + // Clean up transient metadata and Dockerfile once the image is built, + // as they are no longer needed. + for _, cleanup := range c.cleanupFuncs() { + defer cleanup() + } + // Build but never overwrite existing metadata/Dockerfile. + err := bundle.BuildFunc(c.directory, c.imageTag, c.imageBuilder, + c.packageName, channels, c.defaultChannel, false) + if err != nil { + log.Fatalf("Error building bundle image: %v", err) + } + return nil + }, + } + + // Set up default values. + projectName := filepath.Base(projutil.MustGetwd()) + defaultDir := "" + if _, err := os.Stat(catalog.OLMCatalogDir); err == nil || os.IsExist(err) { + defaultDir = filepath.Join(catalog.OLMCatalogDir, projectName) + } + defaultChannels := []string{"stable"} + + cmd.Flags().StringVarP(&c.directory, "directory", "d", defaultDir, + "The directory where bundle manifests are located") + cmd.Flags().StringVarP(&c.packageName, "package", "p", projectName, + "The name of the package that bundle image belongs to. Set if package name differs from project name") + cmd.Flags().StringSliceVarP(&c.channels, "channels", "c", defaultChannels, + "The list of channels that bundle image belongs to") + cmd.Flags().BoolVarP(&c.generateOnly, "generate-only", "g", false, + "Generate metadata and a Dockerfile on disk without building the bundle image") + cmd.Flags().StringVarP(&c.imageBuilder, "image-builder", "b", "docker", + "Tool to build container images. One of: [docker, podman, buildah]") + cmd.Flags().StringVarP(&c.defaultChannel, "default-channel", "e", "", + "The default channel for the bundle image") + + return cmd +} diff --git a/cmd/operator-sdk/alpha/bundle/cmd.go b/cmd/operator-sdk/alpha/bundle/cmd.go new file mode 100644 index 0000000000..b6ace140a1 --- /dev/null +++ b/cmd/operator-sdk/alpha/bundle/cmd.go @@ -0,0 +1,74 @@ +// 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 bundle + +import ( + "os" + "path/filepath" + + "github.com/operator-framework/operator-registry/pkg/lib/bundle" + "github.com/spf13/cobra" +) + +func NewCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "bundle", + Short: "Work with operator bundle metadata and bundle images", + Long: `Generate operator bundle metadata and build operator bundle images, which +are used to manage operators in the Operator Lifecycle Manager. + +More information on operator bundle images and metadata: +https://github.com/openshift/enhancements/blob/master/enhancements/olm/operator-bundle.md#docker`, + } + + cmd.AddCommand(newBundleBuildCmd()) + return cmd +} + +type bundleCmd struct { + directory string + packageName string + imageTag string + imageBuilder string + defaultChannel string + channels []string + generateOnly bool +} + +// cleanupFuncs returns a set of general funcs to clean up after a bundle +// subcommand. +func (c bundleCmd) cleanupFuncs() (fs []func()) { + metaDir := filepath.Join(c.directory, bundle.MetadataDir) + dockerFile := filepath.Join(c.directory, bundle.DockerFile) + metaExists := isExist(metaDir) + dockerFileExists := isExist(dockerFile) + fs = append(fs, + func() { + if !metaExists { + _ = os.RemoveAll(metaDir) + } + }, + func() { + if !dockerFileExists { + _ = os.RemoveAll(dockerFile) + } + }) + return fs +} + +func isExist(path string) bool { + _, err := os.Stat(path) + return os.IsExist(err) +} diff --git a/cmd/operator-sdk/alpha/cmd.go b/cmd/operator-sdk/alpha/cmd.go index de359bb4b6..2b62a87009 100644 --- a/cmd/operator-sdk/alpha/cmd.go +++ b/cmd/operator-sdk/alpha/cmd.go @@ -15,6 +15,7 @@ package alpha import ( + "github.com/operator-framework/operator-sdk/cmd/operator-sdk/alpha/bundle" "github.com/operator-framework/operator-sdk/cmd/operator-sdk/alpha/cleanup" "github.com/operator-framework/operator-sdk/cmd/operator-sdk/alpha/kubebuilder" "github.com/operator-framework/operator-sdk/cmd/operator-sdk/alpha/olm" @@ -34,6 +35,7 @@ func NewCmd() *cobra.Command { kubebuilder.NewCmd(), run.NewCmd(), cleanup.NewCmd(), + bundle.NewCmd(), ) return cmd } diff --git a/doc/cli/operator-sdk_alpha.md b/doc/cli/operator-sdk_alpha.md index eccadab732..868f8d7e0c 100644 --- a/doc/cli/operator-sdk_alpha.md +++ b/doc/cli/operator-sdk_alpha.md @@ -15,6 +15,7 @@ Run an alpha subcommand ### SEE ALSO * [operator-sdk](operator-sdk.md) - An SDK for building operators with ease +* [operator-sdk alpha bundle](operator-sdk_alpha_bundle.md) - Work with operator bundle metadata and bundle images * [operator-sdk alpha cleanup](operator-sdk_alpha_cleanup.md) - Delete and clean up after a running Operator * [operator-sdk alpha olm](operator-sdk_alpha_olm.md) - Manage the Operator Lifecycle Manager installation in your cluster * [operator-sdk alpha run](operator-sdk_alpha_run.md) - Run an Operator in a variety of environments diff --git a/doc/cli/operator-sdk_alpha_bundle.md b/doc/cli/operator-sdk_alpha_bundle.md new file mode 100644 index 0000000000..95708d84fe --- /dev/null +++ b/doc/cli/operator-sdk_alpha_bundle.md @@ -0,0 +1,23 @@ +## operator-sdk alpha bundle + +Work with operator bundle metadata and bundle images + +### Synopsis + +Generate operator bundle metadata and build operator bundle images, which +are used to manage operators in the Operator Lifecycle Manager. + +More information on operator bundle images and metadata: +https://github.com/openshift/enhancements/blob/master/enhancements/olm/operator-bundle.md#docker + +### Options + +``` + -h, --help help for bundle +``` + +### SEE ALSO + +* [operator-sdk alpha](operator-sdk_alpha.md) - Run an alpha subcommand +* [operator-sdk alpha bundle build](operator-sdk_alpha_bundle_build.md) - Build an operator bundle image + diff --git a/doc/cli/operator-sdk_alpha_bundle_build.md b/doc/cli/operator-sdk_alpha_bundle_build.md new file mode 100644 index 0000000000..52c88e1a00 --- /dev/null +++ b/doc/cli/operator-sdk_alpha_bundle_build.md @@ -0,0 +1,68 @@ +## operator-sdk alpha bundle build + +Build an operator bundle image + +### Synopsis + +The 'operator-sdk bundle build' command will build an operator +bundle image containing operator metadata and manifests, tagged with the +provided image tag. + +To write metadata and a bundle image Dockerfile to disk, set '--generate-only=true'. +Bundle metadata will be generated in /metadata, and the Dockerfile +in . This flag is useful if you want to build an operator's +bundle image manually or modify metadata before building an image. + +More information on operator bundle images and metadata: +https://github.com/openshift/enhancements/blob/master/enhancements/olm/operator-bundle.md#docker + +NOTE: bundle images are not runnable. + +``` +operator-sdk alpha bundle build [flags] +``` + +### Examples + +``` +The following invocation will build a test-operator bundle image using Docker. +This image will contain manifests for package channels 'stable' and 'beta': + +$ operator-sdk bundle build quay.io/example/test-operator:v0.1.0 \ + --directory ./deploy/olm-catalog/test-operator \ + --package test-operator \ + --channels stable,beta \ + --default-channel stable + +Assuming your operator has the same name as your operator and the only channel +is 'stable', the above command can be abbreviated to: + +$ operator-sdk bundle build quay.io/example/test-operator:v0.1.0 + +The following invocation will generate test-operator bundle metadata and +Dockerfile without building the image: + +$ operator-sdk bundle build \ + --generate-only \ + --directory ./deploy/olm-catalog/test-operator \ + --package test-operator \ + --channels stable,beta \ + --default-channel stable +``` + +### Options + +``` + -c, --channels strings The list of channels that bundle image belongs to (default [stable]) + -e, --default-channel string The default channel for the bundle image + -d, --directory string The directory where bundle manifests are located + -g, --generate-only Generate metadata and a Dockerfile on disk without building the bundle image + -h, --help help for build + -b, --image-builder string Tool to build container images. One of: [docker, podman, buildah] (default "docker") + -p, --package string The name of the package that bundle image belongs to. Set if package name differs from project name (default "operator-sdk") +``` + +### SEE ALSO + +* [operator-sdk alpha bundle](operator-sdk_alpha_bundle.md) - Work with operator bundle metadata and bundle images +