Skip to content
Merged
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/fragments/generate-packagemanifests-legacy.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
entries:
- description: add `generate packagemanifests` subcommand for legacy project layouts
kind: addition
1 change: 1 addition & 0 deletions cmd/operator-sdk/generate/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ func NewCmdLegacy() *cobra.Command {
newGenerateCRDsCmd(),
newGenerateCSVCmd(),
bundle.NewCmdLegacy(),
packagemanifests.NewCmdLegacy(),
)
return cmd
}
42 changes: 42 additions & 0 deletions cmd/operator-sdk/generate/packagemanifests/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,48 @@ func NewCmd() *cobra.Command {
return cmd
}

// NewCmdLegacy returns the 'packagemanifests' command configured for the legacy project layout.
func NewCmdLegacy() *cobra.Command {
c := &packagemanifestsCmd{}

cmd := &cobra.Command{
Use: "packagemanifests",
Short: "Generates a package manifests format",
Long: longHelp,
Example: examplesLegacy,
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
}
}

c.setCommonDefaultsLegacy()

if err := c.validateManifestsLegacy(); err != nil {
return fmt.Errorf("invalid command options: %v", err)
}
if err := c.runManifestsLegacy(); err != nil {
log.Fatalf("Error generating package manifests: %v", err)
}

return nil
},
}

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")
Expand Down
143 changes: 143 additions & 0 deletions cmd/operator-sdk/generate/packagemanifests/packagemanifests_legacy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
// 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"
"path/filepath"

log "github.com/sirupsen/logrus"

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"
"github.com/operator-framework/operator-sdk/internal/util/projutil"
)

const examplesLegacy = `
# Create the package manifest file and a new package:
$ operator-sdk generate packagemanifests --version 0.0.1
INFO[0000] Generating package manifests version 0.0.1

Display name for the operator (required):
> memcached-operator
...

# After running the above commands, you should see:
$ tree deploy/olm-catalog
deploy/olm-catalog
└── memcached-operator
├── 0.0.1
│ ├── cache.example.com_memcacheds_crd.yaml
│ └── memcached-operator.clusterserviceversion.yaml
└── memacached-operator.package.yaml
`

// setCommonDefaultsLegacy sets defaults useful to all modes of this subcommand for legacy project layouts.
func (c *packagemanifestsCmd) setCommonDefaultsLegacy() {
if c.operatorName == "" {
c.operatorName = filepath.Base(projutil.MustGetwd())
}

if c.apisDir == "" {
c.apisDir = filepath.Join("pkg", "apis")
}
if c.deployDir == "" {
c.deployDir = "deploy"
}
if c.crdsDir == "" {
c.crdsDir = filepath.Join(c.deployDir, "crds")
}

defaultBundleDir := filepath.Join(c.deployDir, "olm-catalog", c.operatorName)
if c.inputDir == "" {
c.inputDir = defaultBundleDir
}
if c.outputDir == "" {
c.outputDir = defaultBundleDir
}
}

// validateManifestsLegacy validates c for package manifests generation for legacy project layouts.
func (c packagemanifestsCmd) validateManifestsLegacy() error {

if err := genutil.ValidateVersion(c.version); err != nil {
return err
}
if c.fromVersion != "" {
if err := genutil.ValidateVersion(c.fromVersion); err != nil {
return err
}
}

if c.isDefaultChannel && c.channelName == "" {
return fmt.Errorf("--default-channel can only be set if --channel is set")
}

return nil
}

// runManifestsLegacy generates package manifests for legacy project layouts.
func (c packagemanifestsCmd) runManifestsLegacy() error {

if !c.quiet {
log.Infoln("Generating package manifests version", c.version)
}
packageDir := filepath.Join(c.outputDir, c.version)

if err := c.generatePackageManifest(); err != nil {
return err
}

col := &collector.Manifests{}
if err := col.UpdateFromDirs(c.deployDir, c.crdsDir); err != nil {
return err
}

csvGen := gencsv.Generator{
OperatorName: c.operatorName,
OperatorType: projutil.GetOperatorType(),
Version: c.version,
FromVersion: c.fromVersion,
Collector: col,
}

opts := []gencsv.LegacyOption{
gencsv.WithPackageBase(c.inputDir, c.apisDir, c.interactiveLevel),
gencsv.LegacyOption(gencsv.WithPackageWriter(c.outputDir)),
}
if err := csvGen.GenerateLegacy(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 err := genutil.WriteObjectsToFilesLegacy(packageDir, objs...); err != nil {
return err
}
}

if !c.quiet {
log.Infoln("Package manifests generated successfully in", c.outputDir)
}

return nil
}
49 changes: 35 additions & 14 deletions hack/tests/subcommand-generate-csv.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,30 +14,25 @@ OPERATOR_VERSION="0.0.4"
OPERATOR_BUNDLE_ROOT_DIR="deploy/olm-catalog/${OPERATOR_NAME}"
DEFAULT_BUNDLE_DIR="${OPERATOR_BUNDLE_ROOT_DIR}/${OPERATOR_VERSION}"
OUTPUT_DIR="foo"
OUTPUT_BUNDLE_DIR="${OUTPUT_DIR}/olm-catalog/${OPERATOR_NAME}/${OPERATOR_VERSION}"

function csv_file_for_dir_legacy() {
echo "${1}/${OPERATOR_NAME}.v${OPERATOR_VERSION}.clusterserviceversion.yaml"
}
OUTPUT_BUNDLE_ROOT_DIR="${OUTPUT_DIR}/olm-catalog/${OPERATOR_NAME}"
OUTPUT_BUNDLE_DIR="${OUTPUT_BUNDLE_ROOT_DIR}/${OPERATOR_VERSION}"

function check_csv_file_legacy() {
check_file "$1" "$(csv_file_for_dir_legacy "$2")" $3
}

function csv_file_for_dir() {
echo "${1}/${OPERATOR_NAME}.clusterserviceversion.yaml"
check_file "$1" "${2}/${OPERATOR_NAME}.v${OPERATOR_VERSION}.clusterserviceversion.yaml" $3
}

function check_csv_file() {
check_file "$1" "$(csv_file_for_dir "$2")" $3
check_file "$1" "${2}/${OPERATOR_NAME}.clusterserviceversion.yaml" $3
}

function crd_files_for_dir() {
echo "${1}/cache.example.com_memcacheds_crd.yaml ${1}/cache.example.com_memcachedrs_crd.yaml"
function check_package_file() {
check_file "$1" "${2}/${OPERATOR_NAME}.package.yaml" $3
}

function check_crd_files() {
for file in $(crd_files_for_dir "$2"); do check_file "$1" "$file" $3; done
local memcacheds_crd_file="${2}/cache.example.com_memcacheds_crd.yaml"
local memcachedrs_crd_file="${2}/cache.example.com_memcachedrs_crd.yaml"
for file in $memcacheds_crd_file $memcachedrs_crd_file; do check_file "$1" "$file" $3; done
}

function generate_csv() {
Expand All @@ -48,6 +43,10 @@ function generate_bundle() {
echo_run operator-sdk generate bundle --operator-name $OPERATOR_NAME --interactive=false $@
}

function generate_packagemanifests() {
echo_run operator-sdk generate packagemanifests --operator-name $OPERATOR_NAME --interactive=false $@
}

pushd "$TEST_DIR" > /dev/null
trap_add "git clean -dfxq $TEST_DIR" EXIT
trap_add "popd > /dev/null" EXIT
Expand Down Expand Up @@ -136,3 +135,25 @@ check_file "$TEST_NAME" "bundle.Dockerfile" 1
cleanup_case

header_text "All 'operator-sdk generate bundle' subcommand tests passed."

header_text "Running 'operator-sdk generate packagemanifests' subcommand tests in $TEST_DIR."

TEST_NAME="generate with version $OPERATOR_VERSION"
header_text "$TEST_NAME"
generate_packagemanifests --version $OPERATOR_VERSION
check_dir "$TEST_NAME" "$DEFAULT_BUNDLE_DIR" 1
check_package_file "$TEST_NAME" "$OPERATOR_BUNDLE_ROOT_DIR" 1
check_csv_file "$TEST_NAME" "$DEFAULT_BUNDLE_DIR" 1
check_crd_files "$TEST_NAME" "$DEFAULT_BUNDLE_DIR" 1
cleanup_case

TEST_NAME="generate with version $OPERATOR_VERSION and output-dir"
header_text "$TEST_NAME"
generate_packagemanifests --version $OPERATOR_VERSION --output-dir "$OUTPUT_DIR"
check_dir "$TEST_NAME" "${OUTPUT_DIR}/${OPERATOR_VERSION}" 1
check_package_file "$TEST_NAME" "$OUTPUT_DIR" 1
check_csv_file "$TEST_NAME" "${OUTPUT_DIR}/${OPERATOR_VERSION}" 1
check_crd_files "$TEST_NAME" "${OUTPUT_DIR}/${OPERATOR_VERSION}" 1
cleanup_case

header_text "All 'operator-sdk generate packagemanifests' subcommand tests passed."
18 changes: 18 additions & 0 deletions internal/generate/clusterserviceversion/clusterserviceversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,14 @@ func WithBundleBase(inputDir, apisDir string, ilvl projutil.InteractiveLevel) Le
}
}

// WithPackageBase sets a Generator's base CSV to a legacy-style package base.
func WithPackageBase(inputDir, apisDir string, ilvl projutil.InteractiveLevel) LegacyOption {
return func(g *Generator) error {
g.getBase = g.makePackageBaseGetterLegacy(inputDir, apisDir, ilvl)
return nil
}
}

// GenerateLegacy configures the generator with opts then runs it. Used for
// generating files for legacy project layouts.
func (g *Generator) GenerateLegacy(opts ...LegacyOption) (err error) {
Expand Down Expand Up @@ -275,6 +283,16 @@ func (g Generator) makeBundleBaseGetterLegacy(inputDir, apisDir string, ilvl pro
return g.makeBaseGetterLegacy(basePath, apisDir, requiresInteraction(basePath, ilvl))
}

// makePackageBaseGetterLegacy returns a function that gets a package base
// for legacy project layouts.
func (g Generator) makePackageBaseGetterLegacy(inputDir, apisDir string, ilvl projutil.InteractiveLevel) getBaseFunc {
basePath := filepath.Join(inputDir, g.Version, makeCSVFileName(g.OperatorName))
if genutil.IsNotExist(basePath) {
basePath = ""
}
return g.makeBaseGetterLegacy(basePath, apisDir, requiresInteraction(basePath, ilvl))
}

// makeBaseGetterLegacy returns a function that gets a base from inputDir.
// apisDir is used by getBaseFunc to populate base fields. This method should
// be used when creating LegacyOptions.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,22 @@ var _ = Describe("Generating a ClusterServiceVersion", func() {
Expect(outputFile).To(BeAnExistingFile())
Expect(string(readFileHelper(outputFile))).To(MatchYAML(newCSVStr))
})
It("should write a ClusterServiceVersion manifest as a legacy package file", func() {
g = Generator{
OperatorName: operatorName,
OperatorType: operatorType,
Version: version,
Collector: col,
}
opts := []LegacyOption{
WithPackageBase(csvBasesDir, goAPIsDir, projutil.InteractiveHardOff),
LegacyOption(WithPackageWriter(tmp)),
}
Expect(g.GenerateLegacy(opts...)).Should(Succeed())
outputFile := filepath.Join(tmp, g.Version, makeCSVFileName(operatorName))
Expect(outputFile).To(BeAnExistingFile())
Expect(string(readFileHelper(outputFile))).To(MatchYAML(newCSVStr))
})
})

Context("with incorrect Options", func() {
Expand Down
1 change: 1 addition & 0 deletions website/content/en/docs/cli/operator-sdk_generate.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,5 @@ code or manifests.
* [operator-sdk generate crds](../operator-sdk_generate_crds) - Generates CRDs for API's
* [operator-sdk generate csv](../operator-sdk_generate_csv) - Generates a ClusterServiceVersion YAML file for the operator
* [operator-sdk generate k8s](../operator-sdk_generate_k8s) - Generates Kubernetes code for custom resource
* [operator-sdk generate packagemanifests](../operator-sdk_generate_packagemanifests) - Generates a package manifests format

Loading