Skip to content

internal/generator/olm-catalog: implement CSV generator #2407

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 11 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
- Added the [`cleanup`](./doc/cli/operator-sdk_cleanup.md) subcommand and [`run --olm`](./doc/cli/operator-sdk_run.md) 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](https://github.com/operator-framework/operator-sdk/pull/2402), [#2441](https://github.com/operator-framework/operator-sdk/pull/2441))
- Added [`bundle create`](./doc/cli/operator-sdk_bundle_create.md) 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), [#2438](https://github.com/operator-framework/operator-sdk/pull/2438))
- Added [`bundle validate`](./doc/cli/operator-sdk_bundle_validate.md) which validates [operator bundle images](https://github.com/openshift/enhancements/blob/ec2cf96/enhancements/olm/operator-registry.md). ([#2411](https://github.com/operator-framework/operator-sdk/pull/2411))
- Added the [`generate csv --include`](doc/cli/operator-sdk_generate_csv.md#options) option to include files as input to the CSV generator in lieu of a config. ([#2249](https://github.com/operator-framework/operator-sdk/pull/2249))

### Changed

Expand All @@ -18,6 +19,8 @@

### Removed

- **Breaking Change:** Removed CSV configuration file support in favor of including files as input to the generator using [`generate csv --include`](doc/cli/operator-sdk_generate_csv.md#options), defaulting to the `deploy/` directory. ([#2249](https://github.com/operator-framework/operator-sdk/pull/2249))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- **Breaking Change:** Removed CSV configuration file support in favor of including files as input to the generator using [`generate csv --include`](doc/cli/operator-sdk_generate_csv.md#options), defaulting to the `deploy/` directory. ([#2249](https://github.com/operator-framework/operator-sdk/pull/2249))
- **Breaking Change:** Removed CSV configuration file support (Defaults to deploy/olm-catalog/csv-config.yaml) in favor of including files as input to the generator using [`generate csv --include`](doc/cli/operator-sdk_generate_csv.md#options), defaulting to the `deploy/` directory. ([#2249](https://github.com/operator-framework/operator-sdk/pull/2249))


### Bug Fixes

- Fixed a regression in the helm-operator that caused all releases to be deployed in the same namespace that the operator was deployed in, regardless of which namespace the CR was created in. Now release resources are created in the same namespace as the CR. ([#2414](https://github.com/operator-framework/operator-sdk/pull/2414))
Expand Down
6 changes: 3 additions & 3 deletions cmd/operator-sdk/bundle/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import (
"path/filepath"
"strings"

catalog "github.com/operator-framework/operator-sdk/internal/scaffold/olm-catalog"
gencatalog "github.com/operator-framework/operator-sdk/internal/generate/olm-catalog"
"github.com/operator-framework/operator-sdk/internal/util/projutil"

"github.com/operator-framework/operator-registry/pkg/lib/bundle"
Expand Down Expand Up @@ -108,8 +108,8 @@ $ operator-sdk bundle create \
// 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)
if _, err := os.Stat(gencatalog.OLMCatalogDir); err == nil || os.IsExist(err) {
defaultDir = filepath.Join(gencatalog.OLMCatalogDir, projectName)
}
defaultChannels := []string{"stable"}

Expand Down
4 changes: 2 additions & 2 deletions cmd/operator-sdk/cleanup/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import (
"errors"
"path/filepath"

olmcatalog "github.com/operator-framework/operator-sdk/internal/generate/olm-catalog"
gencatalog "github.com/operator-framework/operator-sdk/internal/generate/olm-catalog"
olmoperator "github.com/operator-framework/operator-sdk/internal/olm/operator"
"github.com/operator-framework/operator-sdk/internal/util/projutil"

Expand Down Expand Up @@ -63,7 +63,7 @@ func NewCmd() *cobra.Command {
c.olmArgs.OperatorNamespace = c.namespace
if c.olmArgs.ManifestsDir == "" {
operatorName := filepath.Base(projutil.MustGetwd())
c.olmArgs.ManifestsDir = filepath.Join(olmcatalog.OLMCatalogDir, operatorName)
c.olmArgs.ManifestsDir = filepath.Join(gencatalog.OLMCatalogDir, operatorName)
}
if err := c.olmArgs.Cleanup(); err != nil {
log.Fatalf("Failed to clean up operator using OLM: %v", err)
Expand Down
137 changes: 69 additions & 68 deletions cmd/operator-sdk/generate/csv.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@ package generate
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"

"github.com/operator-framework/operator-sdk/internal/generate/gen"
gencatalog "github.com/operator-framework/operator-sdk/internal/generate/olm-catalog"
"github.com/operator-framework/operator-sdk/internal/scaffold"
"github.com/operator-framework/operator-sdk/internal/scaffold/input"
catalog "github.com/operator-framework/operator-sdk/internal/scaffold/olm-catalog"
"github.com/operator-framework/operator-sdk/internal/util/fileutil"
"github.com/operator-framework/operator-sdk/internal/util/k8sutil"
"github.com/operator-framework/operator-sdk/internal/util/projutil"
Expand All @@ -37,8 +37,8 @@ type csvCmd struct {
csvVersion string
csvChannel string
fromVersion string
csvConfigPath string
operatorName string
includePaths []string
updateCRDs bool
defaultChannel bool
}
Expand All @@ -53,9 +53,7 @@ for the operator. This file is used to publish the operator to the OLM Catalog.

A CSV semantic version is supplied via the --csv-version flag. If your operator
has already generated a CSV manifest you want to use as a base, supply its
version to --from-version. Otherwise the SDK will scaffold a new CSV manifest.

Configure CSV generation by writing a config file 'deploy/olm-catalog/csv-config.yaml`,
version to --from-version. Otherwise the SDK will scaffold a new CSV manifest.`,
RunE: func(cmd *cobra.Command, args []string) error {
// The CSV generator assumes that the deploy and pkg directories are
// present at runtime, so this command must be run in a project's root.
Expand All @@ -74,76 +72,63 @@ Configure CSV generation by writing a config file 'deploy/olm-catalog/csv-config
},
}

cmd.Flags().StringVar(&c.csvVersion, "csv-version", "", "Semantic version of the CSV")
cmd.Flags().StringVar(&c.csvVersion, "csv-version", "",
"Semantic version of the CSV")
if err := cmd.MarkFlagRequired("csv-version"); err != nil {
log.Fatalf("Failed to mark `csv-version` flag for `generate csv` subcommand as required: %v", err)
}
cmd.Flags().StringVar(&c.fromVersion, "from-version", "", "Semantic version of an existing CSV to use as a base")
cmd.Flags().StringVar(&c.csvConfigPath, "csv-config", "", "Path to CSV config file. Defaults to deploy/olm-catalog/csv-config.yaml")
cmd.Flags().BoolVar(&c.updateCRDs, "update-crds", false, "Update CRD manifests in deploy/{operator-name}/{csv-version} the using latest API's")
cmd.Flags().StringVar(&c.operatorName, "operator-name", "", "Operator name to use while generating CSV")
cmd.Flags().StringVar(&c.csvChannel, "csv-channel", "", "Channel the CSV should be registered under in the package manifest")
cmd.Flags().BoolVar(&c.defaultChannel, "default-channel", false, "Use the channel passed to --csv-channel as the package manifests' default channel. Only valid when --csv-channel is set")
cmd.Flags().StringVar(&c.fromVersion, "from-version", "",
"Semantic version of an existing CSV to use as a base")
cmd.Flags().StringSliceVar(&c.includePaths, "include", []string{scaffold.DeployDir},
"Paths to include in CSV generation, ex. \"deploy/prod,deploy/test\". "+
"If this flag is set and you want to enable default behavior, "+
"you must include \"deploy/\" in the argument list")
cmd.Flags().BoolVar(&c.updateCRDs, "update-crds", false,
"Update CRD manifests in deploy/{operator-name}/{csv-version} the using latest API's")
cmd.Flags().StringVar(&c.operatorName, "operator-name", "",
"Operator name to use while generating CSV")
cmd.Flags().StringVar(&c.csvChannel, "csv-channel", "",
"Channel the CSV should be registered under in the package manifest")
cmd.Flags().BoolVar(&c.defaultChannel, "default-channel", false,
"Use the channel passed to --csv-channel as the package manifests' default channel. "+
"Only valid when --csv-channel is set")

return cmd
}

func (c csvCmd) run() error {

absProjectPath := projutil.MustGetwd()
cfg := &input.Config{
AbsProjectPath: absProjectPath,
ProjectName: filepath.Base(absProjectPath),
}
if projutil.IsOperatorGo() {
cfg.Repo = projutil.GetGoPkg()
}

log.Infof("Generating CSV manifest version %s", c.csvVersion)

csvCfg, err := catalog.GetCSVConfig(c.csvConfigPath)
if err != nil {
return err
}
if c.operatorName == "" {
// Use config operator name if not set by CLI, i.e. prefer CLI value over
// config value.
if c.operatorName = csvCfg.OperatorName; c.operatorName == "" {
// Default to using project name if both are empty.
c.operatorName = filepath.Base(absProjectPath)
}
c.operatorName = filepath.Base(projutil.MustGetwd())
}

s := &scaffold.Scaffold{}
csv := &catalog.CSV{
CSVVersion: c.csvVersion,
FromVersion: c.fromVersion,
ConfigFilePath: c.csvConfigPath,
OperatorName: c.operatorName,
}
err = s.Execute(cfg, csv)
if err != nil {
return fmt.Errorf("catalog scaffold failed: %v", err)
cfg := gen.Config{
OperatorName: c.operatorName,
Filters: gen.MakeFilters(c.includePaths...),
}

gcfg := gen.Config{
OperatorName: c.operatorName,
OutputDir: filepath.Join(gencatalog.OLMCatalogDir, c.operatorName),
csv := gencatalog.NewCSV(cfg, c.csvVersion, c.fromVersion)
if err := csv.Generate(); err != nil {
return fmt.Errorf("error generating CSV: %v", err)
}
pkg := gencatalog.NewPackageManifest(gcfg, c.csvVersion, c.csvChannel, c.defaultChannel)
pkg := gencatalog.NewPackageManifest(cfg, c.csvVersion, c.csvChannel, c.defaultChannel)
if err := pkg.Generate(); err != nil {
return fmt.Errorf("error generating package manifest: %v", err)
}

// Write CRD's to the new or updated CSV package dir.
if c.updateCRDs {
input, err := csv.GetInput()
crdManifestSet, err := findCRDs(c.includePaths...)
if err != nil {
return err
}
err = writeCRDsToDir(csvCfg.CRDCRPaths, filepath.Dir(input.Path))
if err != nil {
return err
bundleDir := filepath.Join(gencatalog.OLMCatalogDir, strings.ToLower(c.operatorName), c.csvVersion)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@estroz I might have missed this from previous PRs but if the CSV generator inputs are meant to be configurable i.e change g.inputs[DeployDirKey] to point to another location deploy/production/ or in my case config for Kubebuilder's layout, then updating the CRD manifest to that new location would fail.
While the CSV and package manifests are written out to the new location, OLMCatalogDir is currently fixed to always be rooted at the deploy/ directory so it will try to update the CRDs there.

Is there any way we can stop using that constant, and instead use the generator's input for DeployDirKey?

Not sure if any other cmds that need to have a configurable "deploy" directory are using this constant as well. That could be another thing to watch out for.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Going to reconsider how inputs/filters are set up.

for name, b := range crdManifestSet {
path := filepath.Join(bundleDir, name)
if err = ioutil.WriteFile(path, b, fileutil.DefaultFileMode); err != nil {
return err
}
}
}

Expand Down Expand Up @@ -183,25 +168,41 @@ func validateVersion(version string) error {
return nil
}

func writeCRDsToDir(crdPaths []string, toDir string) error {
for _, p := range crdPaths {
b, err := ioutil.ReadFile(p)
if err != nil {
return err
}
typeMeta, err := k8sutil.GetTypeMetaFromBytes(b)
// findCRDs searches directories and files in paths for CRD manifest paths,
// returning a map of paths to file contents.
func findCRDs(paths ...string) (map[string][]byte, error) {
crdFileSet := map[string][]byte{}
for _, path := range paths {
info, err := os.Stat(path)
if err != nil {
return err
}
if typeMeta.Kind != "CustomResourceDefinition" {
continue
return nil, err
}

path := filepath.Join(toDir, filepath.Base(p))
err = ioutil.WriteFile(path, b, fileutil.DefaultFileMode)
if err != nil {
return err
if info.IsDir() {
subsetPaths, err := k8sutil.GetCRDManifestPaths(path)
if err != nil {
return nil, err
}
for _, crdPath := range subsetPaths {
b, err := ioutil.ReadFile(crdPath)
if err != nil {
return nil, err
}
crdFileSet[filepath.Base(crdPath)] = b
}
} else {
b, err := ioutil.ReadFile(path)
if err != nil {
return nil, err
}
typeMeta, err := k8sutil.GetTypeMetaFromBytes(b)
if err != nil {
log.Infof("Skipping non-manifest file %s", path)
continue
}
if typeMeta.Kind == "CustomResourceDefinition" {
crdFileSet[filepath.Base(path)] = b
}
}
}
return nil
return crdFileSet, nil
}
4 changes: 2 additions & 2 deletions cmd/operator-sdk/run/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ import (
"fmt"
"path/filepath"

gencatalog "github.com/operator-framework/operator-sdk/internal/generate/olm-catalog"
olmoperator "github.com/operator-framework/operator-sdk/internal/olm/operator"
olmcatalog "github.com/operator-framework/operator-sdk/internal/scaffold/olm-catalog"
k8sinternal "github.com/operator-framework/operator-sdk/internal/util/k8sutil"
"github.com/operator-framework/operator-sdk/internal/util/projutil"
aoflags "github.com/operator-framework/operator-sdk/pkg/ansible/flags"
Expand Down Expand Up @@ -68,7 +68,7 @@ func NewCmd() *cobra.Command {
c.olmArgs.OperatorNamespace = c.namespace
if c.olmArgs.ManifestsDir == "" {
operatorName := filepath.Base(projutil.MustGetwd())
c.olmArgs.ManifestsDir = filepath.Join(olmcatalog.OLMCatalogDir, operatorName)
c.olmArgs.ManifestsDir = filepath.Join(gencatalog.OLMCatalogDir, operatorName)
}
if err := c.olmArgs.Run(); err != nil {
log.Fatalf("Failed to run operator using OLM: %v", err)
Expand Down
4 changes: 1 addition & 3 deletions doc/cli/operator-sdk_generate_csv.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ A CSV semantic version is supplied via the --csv-version flag. If your operator
has already generated a CSV manifest you want to use as a base, supply its
version to --from-version. Otherwise the SDK will scaffold a new CSV manifest.

Configure CSV generation by writing a config file 'deploy/olm-catalog/csv-config.yaml

```
operator-sdk generate csv [flags]
```
Expand All @@ -21,11 +19,11 @@ operator-sdk generate csv [flags]

```
--csv-channel string Channel the CSV should be registered under in the package manifest
--csv-config string Path to CSV config file. Defaults to deploy/olm-catalog/csv-config.yaml
--csv-version string Semantic version of the CSV
--default-channel Use the channel passed to --csv-channel as the package manifests' default channel. Only valid when --csv-channel is set
--from-version string Semantic version of an existing CSV to use as a base
-h, --help help for csv
--include strings Paths to include in CSV generation, ex. "deploy/prod,deploy/test". If this flag is set and you want to enable default behavior, you must include "deploy/" in the argument list (default [deploy])
--operator-name string Operator name to use while generating CSV
--update-crds Update CRD manifests in deploy/{operator-name}/{csv-version} the using latest API's
```
Expand Down
28 changes: 4 additions & 24 deletions doc/user/olm-catalog/generating-a-csv.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,40 +13,20 @@ This document describes how to manage the following lifecycle for your Operator
Operator SDK projects have an expected [project layout][doc-project-layout]. In particular, a few manifests are expected to be present in the `deploy` directory:

* Roles: `role.yaml`
* ClusterRoles: `cluster_role.yaml`
* Deployments: `operator.yaml`
* Custom Resources (CR's): `crds/<full group>_<version>_<kind>_cr.yaml`
* Custom Resource Definitions (CRD's): `crds/<full group>_<resource>_crd.yaml`.
* CustomResourceDefinitions (CRD's): `crds/<full group>_<resource>_crd.yaml`.

`generate csv` reads these files and adds their data to a CSV in an alternate form.

The following example config containing default values should be copied and written to `deploy/olm-catalog/csv-config.yaml`:

```yaml
crd-cr-paths:
- deploy/crds
operator-path: deploy/operator.yaml
role-paths:
- deploy/role.yaml
```

Explanation of all config fields:

- `crd-cr-paths`: list of strings - a list of CRD and CR manifest file/directory paths. Defaults to `[deploy/crds]`.
- `operator-path`: string - the operator `Deployment` manifest file path. Defaults to `deploy/operator.yaml`.
- `role-paths`: list of strings - Role and ClusterRole manifest file paths. Defaults to `[deploy/role.yaml]`.
- `operator-name`: string - the name used to create the CSV and manifest file names. Defaults to the project's name.

**Note**: The [design doc][doc-csv-design] has outdated field information which should not be referenced.

Fields in this config file can be modified to point towards alternate manifest locations, and passed to `generate csv --csv-config=<path>` to configure CSV generation. For example, if I have one set of production CR/CRD manifests under `deploy/crds/production`, and a set of test manifests under `deploy/crds/test`, and I only want to include production manifests in my CSV, I can set `crd-cr-paths: [deploy/crds/production]`. `generate csv` will then ignore `deploy/crds/test` when getting CR/CRD data.
`generate csv` extracts manifests from files in `deploy/` by default that match the kinds above and adds them to the CSV. If your manifest files are not in `deploy/`, you can use the `--include=[list of paths]` option to instruct the command to extract manifests from files at those paths, ex. `--include="deploy/prod,deploy/test"`. Setting `--include` overrides default behavior; if you still want default behavior, you must append `deploy/` to the list of paths passed to `--include`.

## Versioning

CSV's are versioned in path, file name, and in their `metadata.name` field. For example, running `operator-sdk generate csv --csv-version 0.0.1` will generate a CSV at `deploy/olm-catalog/<operator-name>/0.0.1/<operator-name>.v0.0.1.clusterserviceversion.yaml`. A versioned directory such as `deploy/olm-catalog/<operator-name>/0.0.1` is known as a [*bundle*][doc-bundle]. Versions allow the OLM to upgrade or downgrade your Operator at runtime, i.e. in a cluster. A valid semantic version is required.

`generate csv` allows you to upgrade your CSV using the `--from-version` flag. If you have an existing CSV with version `0.0.1` and want to write a new version `0.0.2`, you can run `operator-sdk generate csv --csv-version 0.0.2 --from-version 0.0.1`. This will write a new CSV manifest to `deploy/olm-catalog/<operator-name>/0.0.2/<operator-name>.v0.0.2.clusterserviceversion.yaml` containing user-defined data from `0.0.1` and any modifications you've made to `roles.yaml`, `operator.yaml`, CR's, or CRD's.

The SDK can manage CRD's in your Operator bundle as well. You can pass the `--update-crds` flag to `generate csv` to add or update your CRD's in your bundle by copying manifests pointed to by `crd-cr-paths` in your config. CRD's in a bundle are not updated by default.
The SDK can manage CRD's in your Operator bundle as well. You can pass the `--update-crds` flag to `generate csv` to add or update your CRD's in your bundle by copying manifests in `deploy/crds` to your bundle. CRD's in a bundle are not updated by default.

## First Generation

Expand Down
2 changes: 1 addition & 1 deletion internal/generate/crd/crd.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import (
"sort"
"strings"

gen "github.com/operator-framework/operator-sdk/internal/generate/gen"
"github.com/operator-framework/operator-sdk/internal/generate/gen"
"github.com/operator-framework/operator-sdk/internal/scaffold"
"github.com/operator-framework/operator-sdk/internal/util/fileutil"
"github.com/operator-framework/operator-sdk/internal/util/k8sutil"
Expand Down
2 changes: 1 addition & 1 deletion internal/generate/crd/crd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import (
"testing"
"time"

gen "github.com/operator-framework/operator-sdk/internal/generate/gen"
"github.com/operator-framework/operator-sdk/internal/generate/gen"
"github.com/operator-framework/operator-sdk/internal/scaffold"

"github.com/stretchr/testify/assert"
Expand Down
Loading