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
7 changes: 4 additions & 3 deletions cmd/operator-sdk/cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"github.com/operator-framework/operator-sdk/cmd/operator-sdk/bundle"
"github.com/operator-framework/operator-sdk/cmd/operator-sdk/completion"
"github.com/operator-framework/operator-sdk/cmd/operator-sdk/generate"
"github.com/operator-framework/operator-sdk/cmd/operator-sdk/new"
"github.com/operator-framework/operator-sdk/cmd/operator-sdk/olm"
"github.com/operator-framework/operator-sdk/cmd/operator-sdk/version"
"github.com/operator-framework/operator-sdk/internal/flags"
Expand All @@ -33,9 +34,9 @@ import (
)

var commands = []*cobra.Command{
// Once the KB CLI is made the default, add the "new" command as a way to
// scaffold the legacy project layout and mark "new" as deprecated
// new.NewCmd()
// The "new" cmd provides a way to scaffold Helm/Ansible projects
// from the new CLI.
new.NewCmd(),
Comment thread
hasbro17 marked this conversation as resolved.

alpha.NewCmd(),
build.NewCmd(),
Copy link
Copy Markdown
Contributor

@camilamacedo86 camilamacedo86 Jun 16, 2020

Choose a reason for hiding this comment

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

The operator-sdk create api is not showing when we use the help. Not a blocker too. We can do it in a follow up too.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

That's a plugin issue right @estroz
You have to be inside an initialized project for that help to show up for the correct plugin version I believe.

$ operator-sdk create api -h
Scaffold a Kubernetes API.

For project-specific information, run this command in the root directory of a
project.

Note: unable to find configuration file, project must be initialized

Usage:
  operator-sdk create api [flags]

Flags:
  -h, --help   help for api

And inside a project:

$ operator-sdk create api -h
Scaffold a Kubernetes API by creating a Resource definition and / or a Controller.

create resource will prompt the user for if it should scaffold the Resource and / or Controller.  To only
scaffold a Controller for an existing Resource, select "n" for Resource.  To only define
the schema for a Resource without writing a Controller, select "n" for Controller.

After the scaffold is written, api will run make on the project.

Usage:
  operator-sdk create api [flags]

Examples:
  # Create a frigates API with Group: ship, Version: v1beta1 and Kind: Frigate
  operator-sdk create api --group ship --version v1beta1 --kind Frigate

...

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

You need to set --project-version to output help for the default plugin. However I do see create listed in the available subcommands when I run operator-sdk -h regardless of where the binary is.

Copy link
Copy Markdown
Contributor

@camilamacedo86 camilamacedo86 Jun 17, 2020

Choose a reason for hiding this comment

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

I saw: create Scaffold a Kubernetes API or webhook 👍

Expand Down
35 changes: 27 additions & 8 deletions cmd/operator-sdk/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,32 +15,51 @@
package main

import (
"os"

// Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.)
// to ensure that `exec-entrypoint` and `run` can make use of them.
_ "k8s.io/client-go/plugin/pkg/client/auth"

"github.com/operator-framework/operator-sdk/cmd/operator-sdk/cli"
kbutil "github.com/operator-framework/operator-sdk/internal/util/kubebuilder"
"github.com/operator-framework/operator-sdk/internal/util/projutil"

log "github.com/sirupsen/logrus"
)

func main() {
// Use the new KB CLI only when running inside an existing Kubebuilder project with a PROJECT file.
// The default legacy CLI provides the init cmd to initialize
// a Kubebuilder project as a way to opt into the new KB CLI.
// TODO: Make the new KB CLI the default, once the integration is complete
// and deprecate "operator-sdk new" from the old CLI.
// Use the new KB CLI when running inside a Kubebuilder project with an existing PROJECT file.
if kbutil.HasProjectFile() {
if err := cli.Run(); err != nil {
log.Fatal(err)
}
return
}

if err := cli.RunLegacy(); err != nil {
os.Exit(1)
// Use the legacy CLI if inside of a Go/Helm/Ansible legacy project
operatorType := projutil.GetOperatorType()
switch operatorType {
case projutil.OperatorTypeGo, projutil.OperatorTypeHelm, projutil.OperatorTypeAnsible:
// Deprecation warning for Go projects
// TODO/Discuss: UX wise, is displaying this notice on every command that runs
// in the legacy Go projects too loud.
if operatorType == projutil.OperatorTypeGo {
depMsg := "Operator SDK has a new CLI and project layout that is aligned with Kubebuilder.\n" +
"See `operator-sdk init -h` and the following doc on how to scaffold a new project:\n" +
"https://sdk.operatorframework.io/docs/golang/new/quickstart/\n" +
"To migrate existing projects to the new layout see:\n" +
"https://sdk.operatorframework.io/docs/golang/new/migration/project_migration_guide/\n"
projutil.PrintDeprecationWarning(depMsg)
}
if err := cli.RunLegacy(); err != nil {
log.Fatal(err)
}
return
}

// Run the KB CLI when not running in either legacy or new projects
// The new CLI still supports "operator-sdk new --type=Ansible/Helm"
if err := cli.Run(); err != nil {
log.Fatal(err)
}
}
135 changes: 8 additions & 127 deletions cmd/operator-sdk/new/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import (
"fmt"
"os"
"os/exec"
"path"
"path/filepath"
"strings"

Expand Down Expand Up @@ -53,9 +52,6 @@ generates a default directory layout based on the input <project-name>.
$ mkdir $HOME/projects/example.com/
$ cd $HOME/projects/example.com/

# Go project
$ operator-sdk new app-operator

# Ansible project
$ operator-sdk new app-operator --type=ansible \
--api-version=app.example.com/v1alpha1 \
Expand Down Expand Up @@ -95,18 +91,14 @@ generates a default directory layout based on the input <project-name>.
`,
RunE: newFunc,
}
newCmd.Flags().StringVar(&operatorType, "type", "go",
"Type of operator to initialize (choices: \"go\", \"ansible\" or \"helm\")")
newCmd.Flags().StringVar(&repo, "repo", "",
"Project repository path for Go operators. Used as the project's Go import path. This must be set if "+
"outside of $GOPATH/src (e.g. github.com/example-inc/my-operator)")

newCmd.Flags().StringVar(&operatorType, "type", "",
"Type of operator to initialize (choices: \"ansible\" or \"helm\")")
if err := newCmd.MarkFlagRequired("type"); err != nil {
log.Fatalf("Failed to mark `type` flag for `new` subcommand as required")
}
newCmd.Flags().BoolVar(&gitInit, "git-init", false,
"Initialize the project directory as a git repository (default false)")
newCmd.Flags().StringVar(&headerFile, "header-file", "",
"Path to file containing headers for generated Go files. Copied to hack/boilerplate.go.txt")
newCmd.Flags().BoolVar(&makeVendor, "vendor", false, "Use a vendor directory for dependencies")
newCmd.Flags().BoolVar(&skipValidation, "skip-validation", false,
"Do not validate the resulting project's structure and dependencies. (Only used for --type go)")
newCmd.Flags().BoolVar(&generatePlaybook, "generate-playbook", false,
"Generate a playbook skeleton. (Only used for --type ansible)")

Expand All @@ -120,11 +112,7 @@ var (
apiFlags apiflags.APIFlags
operatorType string
projectName string
headerFile string
repo string
gitInit bool
makeVendor bool
skipValidation bool
generatePlaybook bool
)

Expand All @@ -140,22 +128,6 @@ func newFunc(cmd *cobra.Command, args []string) error {
log.Infof("Creating new %s operator '%s'.", strings.Title(operatorType), projectName)

switch operatorType {
case projutil.OperatorTypeGo:
if repo == "" {
repo = path.Join(projutil.GetGoPkg(), projectName)
}
if err := doGoScaffold(); err != nil {
log.Fatal(err)
}
if err := getDeps(); err != nil {
log.Fatal(err)
}
if !skipValidation {
if err := validateProject(); err != nil {
log.Fatal(err)
}
}

case projutil.OperatorTypeAnsible:
if err := doAnsibleScaffold(); err != nil {
log.Fatal(err)
Expand Down Expand Up @@ -229,48 +201,6 @@ func mustBeNewProject() {
}
}

func doGoScaffold() error {
cfg := &input.Config{
Repo: repo,
AbsProjectPath: filepath.Join(projutil.MustGetwd(), projectName),
ProjectName: projectName,
}
s := &scaffold.Scaffold{}

if headerFile != "" {
err := s.Execute(cfg, &scaffold.Boilerplate{BoilerplateSrcPath: headerFile})
if err != nil {
return fmt.Errorf("boilerplate scaffold failed: %v", err)
}
s.BoilerplatePath = headerFile
}

if err := projutil.CheckGoModules(); err != nil {
return err
}

err := s.Execute(cfg,
&scaffold.GoMod{},
&scaffold.Tools{},
&scaffold.Cmd{},
&scaffold.Dockerfile{},
&scaffold.Entrypoint{},
&scaffold.UserSetup{},
&scaffold.ServiceAccount{},
&scaffold.Role{},
&scaffold.RoleBinding{},
&scaffold.Operator{},
&scaffold.Apis{},
&scaffold.Controller{},
&scaffold.Version{},
&scaffold.Gitignore{},
)
if err != nil {
return fmt.Errorf("new Go scaffold failed: %v", err)
}
return nil
}

func doAnsibleScaffold() error {
cfg := &input.Config{
AbsProjectPath: filepath.Join(projutil.MustGetwd(), projectName),
Expand Down Expand Up @@ -365,23 +295,13 @@ func doAnsibleScaffold() error {
}

func verifyFlags() error {
if operatorType != projutil.OperatorTypeGo && operatorType != projutil.OperatorTypeAnsible && operatorType !=
projutil.OperatorTypeHelm {
return fmt.Errorf("value of --type can only be `go`, `ansible`, or `helm`: %v",
if operatorType != projutil.OperatorTypeAnsible && operatorType != projutil.OperatorTypeHelm {
return fmt.Errorf("value of --type can only be `ansible`, or `helm`: %v",
projutil.ErrUnknownOperatorType{Type: operatorType})
}
if operatorType != projutil.OperatorTypeAnsible && generatePlaybook {
return fmt.Errorf("value of --generate-playbook can only be used with --type `ansible`")
}
if operatorType == projutil.OperatorTypeGo {
if len(apiFlags.APIVersion) != 0 || len(apiFlags.Kind) != 0 {
return fmt.Errorf("operators of type Go do not use --api-version or --kind")
}

if err := projutil.CheckRepo(repo); err != nil {
return err
}
}
if err := apiFlags.VerifyCommonFlags(operatorType); err != nil {
return err
}
Expand All @@ -395,26 +315,6 @@ func execProjCmd(cmd string, args ...string) error {
return projutil.ExecCmd(dc)
}

func getDeps() error {

// Only when a user requests a vendor directory be created should
// "go mod vendor" be run during project initialization.
if !makeVendor {
return nil
}

log.Info("Running go mod vendor")
opts := projutil.GoCmdOptions{
Args: []string{"-v"},
Dir: filepath.Join(projutil.MustGetwd(), projectName),
}
if err := projutil.GoCmd("mod vendor", opts); err != nil {
return err
}
log.Info("Done getting dependencies")
return nil
}

func initGit() error {
log.Info("Running git init")
if err := execProjCmd("git", "init"); err != nil {
Expand All @@ -423,22 +323,3 @@ func initGit() error {
log.Info("Run git init done")
return nil
}

func validateProject() error {
log.Info("Validating project")
// Run "go build ./..." to make sure all packages can be built
// correctly. From "go help build":
//
// When compiling multiple packages or a single non-main package,
// build compiles the packages but discards the resulting object,
// serving only as a check that the packages can be built.
opts := projutil.GoCmdOptions{
PackagePath: "./...",
Dir: filepath.Join(projutil.MustGetwd(), projectName),
}
if err := projutil.GoBuild(opts); err != nil {
return err
}
log.Info("Project validation successful.")
return nil
}
48 changes: 30 additions & 18 deletions hack/generate/cli-doc/gen-cli-doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"strings"

log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/spf13/cobra/doc"

"github.com/operator-framework/operator-sdk/cmd/operator-sdk/cli"
Expand All @@ -33,30 +34,31 @@ title: "%s"
`

func main() {
// TODO: Generate CLI docs for plugins CLI at doc/cli/kubebuilder
root := cli.GetCLIRoot()
root.DisableAutoGenTag = true

filePrepender := func(filename string) string {
name := filepath.Base(filename)
base := strings.TrimSuffix(name, path.Ext(name))
return fmt.Sprintf(fmTemplate, strings.Replace(base, "_", " ", -1))
}
linkHandler := func(name string) string {
base := strings.TrimSuffix(name, path.Ext(name))
return "../" + base
currentDir, err := os.Getwd()
if err != nil {
log.Fatalf("Failed to get current directory: %v", err)
}

legacyDocPath := filepath.Join(currentDir, "website", "content", "en", "docs", "cli")
legacyRoot := cli.GetCLIRoot()
legacyRoot.DisableAutoGenTag = true
recreateDocDir(legacyRoot, legacyDocPath)

newDocPath := filepath.Join(currentDir, "website", "content", "en", "docs", "new-cli")
_, newRoot := cli.GetPluginsCLIAndRoot()
newRoot.DisableAutoGenTag = true
recreateDocDir(newRoot, newDocPath)
}

// recreateDocDir removes and recreates the CLI doc directory for rootCmd
// at docPath to ensure that stale files (e.g. from renamed or removed CLI subcommands)
// are removed.
func recreateDocDir(rootCmd *cobra.Command, docPath string) {
currentDir, err := os.Getwd()
if err != nil {
log.Fatalf("Failed to get current directory: %v", err)
}

docPath := filepath.Join(currentDir, "website", "content", "en", "docs", "cli")

// Remove and recreate the CLI doc directory to ensure that
// stale files (e.g. from renamed or removed CLI subcommands)
// are removed.
if err := os.Rename(docPath+"/_index.md", currentDir+"/tmp_index.md"); err != nil {
log.Fatalf("Failed to move existing index: %v", err)
}
Expand All @@ -67,7 +69,17 @@ func main() {
log.Fatalf("Failed to re-create docs directory: %v", err)
}

err = doc.GenMarkdownTreeCustom(root, docPath, filePrepender, linkHandler)
filePrepender := func(filename string) string {
name := filepath.Base(filename)
base := strings.TrimSuffix(name, path.Ext(name))
return fmt.Sprintf(fmTemplate, strings.Replace(base, "_", " ", -1))
}
linkHandler := func(name string) string {
base := strings.TrimSuffix(name, path.Ext(name))
return "../" + base
}

err = doc.GenMarkdownTreeCustom(rootCmd, docPath, filePrepender, linkHandler)
if err != nil {
log.Fatalf("Failed to generate documentation: %v", err)
}
Expand Down
21 changes: 21 additions & 0 deletions hack/tests/scaffolding/e2e-go-scaffold.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,24 @@ set -ex

source hack/lib/test_lib.sh

function download_old_sdk_binary() {
VERSION="v0.18.1"
echo "Getting SDK $VERSION release binary..."
BIN_URL=""
OS=$(go env GOOS)
if [ "$OS" = "darwin" ]; then
BIN_URL="https://github.com/operator-framework/operator-sdk/releases/download/$VERSION/operator-sdk-$VERSION-x86_64-apple-darwin"
elif [ "$OS" = "linux" ]; then
BIN_URL="https://github.com/operator-framework/operator-sdk/releases/download/$VERSION/operator-sdk-$VERSION-x86_64-linux-gnu"
else
echo "Failed to get SDK $VERSION release binary for $OS"
exit 1
fi

curl -o operator-sdk-old -L $BIN_URL
Comment thread
hasbro17 marked this conversation as resolved.
chmod +x ./operator-sdk-old
}

ROOTDIR="$(pwd)"
BASEPROJECTDIR="$(mktemp -d)"
IMAGE_NAME="quay.io/example/memcached-operator:v0.0.1"
Expand Down Expand Up @@ -34,5 +52,8 @@ set -- "${ORIG_ARGS[@]}"
go build -o $BASEPROJECTDIR/scaffold-memcached $ROOTDIR/hack/tests/scaffolding/scaffold-memcached.go

pushd "$BASEPROJECTDIR"
# scaffold-memcached uses "operator-sdk-old new ..." to scaffold the legacy project
# since "new" is not present in the new CLI
download_old_sdk_binary
Comment thread
joelanford marked this conversation as resolved.
./scaffold-memcached --local-repo $ROOTDIR --image-name=$IMAGE_NAME --local-image $@
popd
Loading