-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
generate: add
packagemanifests
subcommand for new project layouts
- Loading branch information
Showing
11 changed files
with
982 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
entries: | ||
- description: add `generate packagemanifests` subcommand for new project layouts | ||
kind: addition |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
// 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 packagemanifests | ||
|
||
import ( | ||
"fmt" | ||
|
||
log "github.com/sirupsen/logrus" | ||
"github.com/spf13/cobra" | ||
"github.com/spf13/pflag" | ||
|
||
kbutil "github.com/operator-framework/operator-sdk/internal/util/kubebuilder" | ||
"github.com/operator-framework/operator-sdk/internal/util/projutil" | ||
) | ||
|
||
//nolint:maligned | ||
type packagemanifestsCmd struct { | ||
// Options to turn on different parts of packaging. | ||
kustomize bool | ||
manifests bool | ||
|
||
// Common options. | ||
operatorName string | ||
version string | ||
fromVersion string | ||
inputDir string | ||
outputDir string | ||
manifestRoot string | ||
apisDir string | ||
crdsDir string | ||
updateCRDs bool | ||
stdout bool | ||
quiet bool | ||
|
||
// Interactive options. | ||
interactiveLevel projutil.InteractiveLevel | ||
interactive bool | ||
|
||
// Package manifest options. | ||
channelName string | ||
isDefaultChannel bool | ||
} | ||
|
||
// NewCmd returns the 'packagemanifests' command configured for the new project layout. | ||
func NewCmd() *cobra.Command { | ||
c := &packagemanifestsCmd{} | ||
|
||
cmd := &cobra.Command{ | ||
Use: "packagemanifests", | ||
Short: "Generates a package manifests format", | ||
RunE: func(cmd *cobra.Command, args []string) error { | ||
if len(args) != 0 { | ||
return fmt.Errorf("command %s doesn't accept any arguments", cmd.CommandPath()) | ||
} | ||
|
||
// Check if the user has any specific preference to enable/disable | ||
// interactive prompts. Default behaviour is to disable the prompt | ||
// unless a base package does not exist. | ||
if cmd.Flags().Changed("interactive") { | ||
if c.interactive { | ||
c.interactiveLevel = projutil.InteractiveOnAll | ||
} else { | ||
c.interactiveLevel = projutil.InteractiveHardOff | ||
} | ||
} | ||
|
||
// Generate kustomize bases and manifests by default if no flags are set | ||
// so the default behavior is "do everything". | ||
fs := cmd.Flags() | ||
if !fs.Changed("kustomize") && !fs.Changed("manifests") { | ||
c.kustomize = true | ||
c.manifests = true | ||
} | ||
|
||
cfg, err := kbutil.ReadConfig() | ||
if err != nil { | ||
log.Fatal(fmt.Errorf("error reading configuration: %v", err)) | ||
} | ||
c.setCommonDefaults(cfg) | ||
|
||
if c.kustomize { | ||
if err = c.runKustomize(cfg); err != nil { | ||
log.Fatalf("Error generating package bases: %v", err) | ||
} | ||
} | ||
if c.manifests { | ||
if err = c.validateManifests(); err != nil { | ||
return fmt.Errorf("invalid command options: %v", err) | ||
} | ||
if err = c.runManifests(cfg); err != nil { | ||
log.Fatalf("Error generating package manifests: %v", err) | ||
} | ||
} | ||
|
||
return nil | ||
}, | ||
} | ||
|
||
cmd.Flags().BoolVar(&c.kustomize, "kustomize", false, "Generate kustomize bases") | ||
cmd.Flags().BoolVar(&c.manifests, "manifests", false, "Generate package manifests") | ||
cmd.Flags().BoolVar(&c.stdout, "stdout", false, "Write package to stdout") | ||
|
||
c.addCommonFlagsTo(cmd.Flags()) | ||
|
||
return cmd | ||
} | ||
|
||
func (c *packagemanifestsCmd) addCommonFlagsTo(fs *pflag.FlagSet) { | ||
fs.StringVar(&c.operatorName, "operator-name", "", "Name of the packaged operator") | ||
fs.StringVarP(&c.version, "version", "v", "", "Semantic version of the packaged operator") | ||
fs.StringVar(&c.inputDir, "input-dir", "", "Directory to read existing package manifests from. "+ | ||
"This directory is the parent of individual versioned package directories, and different from --manifest-root") | ||
fs.StringVar(&c.outputDir, "output-dir", "", "Directory in which to write package manifests") | ||
|
||
fs.StringVar(&c.manifestRoot, "manifest-root", "", "Root directory for operator manifests such as "+ | ||
"Deployments and RBAC, ex. 'deploy' or 'config'. This directory is different from that passed to --input-dir") | ||
// NB(estroz): still debating the name of this flag. For now, hide it as an | ||
// "alpha" flag so we do not have to deprecate it if we change this name. | ||
// TODO(estroz): decide on this flag's name before making 'init' default. | ||
if err := fs.MarkHidden("manifest-root"); err != nil { | ||
panic(err) | ||
} | ||
|
||
fs.StringVar(&c.apisDir, "apis-dir", "", "Root directory for API type defintions") | ||
fs.StringVar(&c.crdsDir, "crds-dir", "", "Root directory for CustomResoureDefinition manifests") | ||
fs.StringVar(&c.channelName, "channel", "", "Channel name for the generated package") | ||
fs.BoolVar(&c.isDefaultChannel, "default-channel", false, "Use the channel passed to --channel "+ | ||
"as the package manifest file's default channel") | ||
fs.BoolVar(&c.updateCRDs, "update-crds", false, "Update CustomResoureDefinition manifests "+ | ||
"in this package") | ||
fs.BoolVarP(&c.quiet, "quiet", "q", false, "Run in quiet mode") | ||
fs.BoolVar(&c.interactive, "interactive", false, "When set or no package base exists, an interactive "+ | ||
"command prompt will be presented to accept package ClusterServiceVersion metadata") | ||
} |
216 changes: 216 additions & 0 deletions
216
cmd/operator-sdk/generate/packagemanifests/packagemanifests.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,216 @@ | ||
// 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 packagemanifests | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"os" | ||
"path/filepath" | ||
|
||
"sigs.k8s.io/kubebuilder/pkg/model/config" | ||
|
||
genutil "github.com/operator-framework/operator-sdk/cmd/operator-sdk/generate/internal" | ||
gencsv "github.com/operator-framework/operator-sdk/internal/generate/clusterserviceversion" | ||
"github.com/operator-framework/operator-sdk/internal/generate/collector" | ||
genpkg "github.com/operator-framework/operator-sdk/internal/generate/packagemanifest" | ||
) | ||
|
||
// setCommonDefaults sets defaults useful to all modes of this subcommand. | ||
func (c *packagemanifestsCmd) setCommonDefaults(cfg *config.Config) { | ||
if c.operatorName == "" { | ||
c.operatorName = filepath.Base(cfg.Repo) | ||
} | ||
} | ||
|
||
// runKustomize generates kustomize package bases. | ||
func (c packagemanifestsCmd) runKustomize(cfg *config.Config) error { | ||
|
||
if !c.quiet { | ||
fmt.Println("Generating package manifests kustomize bases") | ||
} | ||
|
||
defaultDir := filepath.Join("config", "packages") | ||
if c.inputDir == "" { | ||
c.inputDir = defaultDir | ||
} | ||
if c.outputDir == "" { | ||
c.outputDir = defaultDir | ||
} | ||
if c.apisDir == "" { | ||
if cfg.MultiGroup { | ||
c.apisDir = "apis" | ||
} else { | ||
c.apisDir = "api" | ||
} | ||
} | ||
|
||
csvGen := gencsv.Generator{ | ||
OperatorName: c.operatorName, | ||
OperatorType: genutil.PluginKeyToOperatorType(cfg.Layout), | ||
} | ||
opts := []gencsv.Option{ | ||
gencsv.WithBase(c.inputDir, c.apisDir, c.interactiveLevel), | ||
gencsv.WithBaseWriter(c.outputDir), | ||
} | ||
if err := csvGen.Generate(cfg, opts...); err != nil { | ||
return fmt.Errorf("error generating ClusterServiceVersion: %v", err) | ||
} | ||
|
||
if !c.quiet { | ||
fmt.Println("Bases generated successfully in", c.outputDir) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// validateManifests validates c for package manifests generation. | ||
func (c packagemanifestsCmd) validateManifests() error { | ||
|
||
if err := genutil.ValidateVersion(c.version); err != nil { | ||
return err | ||
} | ||
|
||
if c.fromVersion != "" { | ||
return errors.New("--from-version cannot be set for PROJECT configured projects") | ||
} | ||
|
||
if !genutil.IsPipeReader() { | ||
if c.manifestRoot == "" { | ||
return errors.New("--manifest-root must be set if not reading from stdin") | ||
} | ||
if c.crdsDir == "" { | ||
return errors.New("--crd-dir must be set if not reading from stdin") | ||
} | ||
} | ||
|
||
if c.stdout { | ||
if c.outputDir != "" { | ||
return errors.New("--output-dir cannot be set if writing to stdout") | ||
} | ||
} | ||
|
||
if c.isDefaultChannel && c.channelName == "" { | ||
return fmt.Errorf("--default-channel can only be set if --channel is set") | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// runManifests generates package manifests. | ||
func (c packagemanifestsCmd) runManifests(cfg *config.Config) error { | ||
|
||
if !c.quiet && !c.stdout { | ||
fmt.Println("Generating package manifests version", c.version) | ||
} | ||
|
||
defaultDir := filepath.Join("config", "packages") | ||
if c.inputDir == "" { | ||
c.inputDir = defaultDir | ||
} | ||
if !c.stdout { | ||
if c.outputDir == "" { | ||
c.outputDir = defaultDir | ||
} | ||
} | ||
// Only regenerate API definitions once. | ||
if c.apisDir == "" && !c.kustomize { | ||
if cfg.MultiGroup { | ||
c.apisDir = "apis" | ||
} else { | ||
c.apisDir = "api" | ||
} | ||
} | ||
|
||
if err := c.generatePackageManifest(); err != nil { | ||
return err | ||
} | ||
|
||
col := &collector.Manifests{} | ||
if genutil.IsPipeReader() { | ||
if err := col.UpdateFromReader(os.Stdin); err != nil { | ||
return err | ||
} | ||
} | ||
if c.manifestRoot != "" { | ||
if err := col.UpdateFromDirs(c.manifestRoot, c.crdsDir); err != nil { | ||
return err | ||
} | ||
} | ||
|
||
csvGen := gencsv.Generator{ | ||
OperatorName: c.operatorName, | ||
OperatorType: genutil.PluginKeyToOperatorType(cfg.Layout), | ||
Version: c.version, | ||
Collector: col, | ||
} | ||
|
||
stdout := genutil.NewMultiManifestWriter(os.Stdout) | ||
opts := []gencsv.Option{ | ||
gencsv.WithBase(c.inputDir, c.apisDir, c.interactiveLevel), | ||
} | ||
if c.stdout { | ||
opts = append(opts, gencsv.WithWriter(stdout)) | ||
} else { | ||
opts = append(opts, gencsv.WithPackageWriter(c.outputDir)) | ||
} | ||
|
||
if err := csvGen.Generate(cfg, opts...); err != nil { | ||
return fmt.Errorf("error generating ClusterServiceVersion: %v", err) | ||
} | ||
|
||
if c.updateCRDs { | ||
var objs []interface{} | ||
for _, crd := range col.V1CustomResourceDefinitions { | ||
objs = append(objs, crd) | ||
} | ||
for _, crd := range col.V1beta1CustomResourceDefinitions { | ||
objs = append(objs, crd) | ||
} | ||
if c.stdout { | ||
if err := genutil.WriteObjects(stdout, objs...); err != nil { | ||
return err | ||
} | ||
} else { | ||
dir := filepath.Join(c.outputDir, c.version) | ||
if err := genutil.WriteObjectsToFiles(dir, objs...); err != nil { | ||
return err | ||
} | ||
} | ||
} | ||
|
||
if !c.quiet && !c.stdout { | ||
fmt.Println("Package manifests generated successfully in", c.outputDir) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func (c packagemanifestsCmd) generatePackageManifest() error { | ||
pkgGen := genpkg.Generator{ | ||
OperatorName: c.operatorName, | ||
Version: c.version, | ||
ChannelName: c.channelName, | ||
IsDefaultChannel: c.isDefaultChannel, | ||
} | ||
opts := []genpkg.Option{ | ||
genpkg.WithBase(c.inputDir), | ||
genpkg.WithFileWriter(c.outputDir), | ||
} | ||
if err := pkgGen.Generate(opts...); err != nil { | ||
return err | ||
} | ||
return nil | ||
} |
Oops, something went wrong.