Cleanup commands (#807)

* src: refactor commands

Commands are constructed from root,
not by using init() blocks.

Signed-off-by: Matej Vasek <mvasek@redhat.com>

* fixup the prefix issue

Signed-off-by: Matej Vasek <mvasek@redhat.com>

* fixup style

Signed-off-by: Matej Vasek <mvasek@redhat.com>

* fixup nolint:misspell

Signed-off-by: Matej Vasek <mvasek@redhat.com>
This commit is contained in:
Matej Vasek 2022-02-03 19:26:21 +01:00 committed by GitHub
parent e1095e0509
commit abd4eea0c7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 340 additions and 365 deletions

View File

@ -19,12 +19,6 @@ import (
"knative.dev/kn-plugin-func/progress" "knative.dev/kn-plugin-func/progress"
) )
func init() {
// Add to the root a new "Build" command which obtains an appropriate
// instance of fn.Client from the given client creator function.
root.AddCommand(NewBuildCmd(newBuildClient))
}
func newBuildClient(cfg buildConfig) (*fn.Client, error) { func newBuildClient(cfg buildConfig) (*fn.Client, error) {
builder := buildpacks.NewBuilder() builder := buildpacks.NewBuilder()
listener := progress.New() listener := progress.New()

View File

@ -7,15 +7,11 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
func init() { func NewCompletionCmd() *cobra.Command {
root.AddCommand(completionCmd) return &cobra.Command{
} Use: "completion <bash|zsh|fish>",
Short: "Generate completion scripts for bash, fish and zsh",
// completionCmd represents the completion command Long: `To load completion run
var completionCmd = &cobra.Command{
Use: "completion <bash|zsh|fish>",
Short: "Generate completion scripts for bash, fish and zsh",
Long: `To load completion run
For zsh: For zsh:
source <(func completion zsh) source <(func completion zsh)
@ -28,23 +24,25 @@ For bash:
source <(func completion bash) source <(func completion bash)
`, `,
ValidArgs: []string{"bash", "zsh", "fish"}, ValidArgs: []string{"bash", "zsh", "fish"},
Args: cobra.ExactArgs(1), Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) (err error) { RunE: func(cmd *cobra.Command, args []string) (err error) {
if len(args) < 1 { if len(args) < 1 {
return errors.New("missing argument") return errors.New("missing argument")
} }
switch args[0] { switch args[0] {
case "bash": case "bash":
err = root.GenBashCompletion(os.Stdout) err = cmd.Root().GenBashCompletion(os.Stdout)
case "zsh": case "zsh":
err = root.GenZshCompletion(os.Stdout) err = cmd.Root().GenZshCompletion(os.Stdout)
case "fish": case "fish":
err = root.GenFishCompletion(os.Stdout, true) err = cmd.Root().GenFishCompletion(os.Stdout, true)
default: default:
err = errors.New("unknown shell, only bash, zsh and fish are supported") err = errors.New("unknown shell, only bash, zsh and fish are supported")
} }
return err
},
}
return err
},
} }

View File

@ -43,24 +43,28 @@ func (s standardLoaderSaver) Save(f fn.Function) error {
var defaultLoaderSaver standardLoaderSaver var defaultLoaderSaver standardLoaderSaver
func init() { func NewConfigCmd() *cobra.Command {
root.AddCommand(configCmd) cmd := &cobra.Command{
setPathFlag(configCmd) Use: "config",
configCmd.AddCommand(NewConfigLabelsCmd(defaultLoaderSaver)) Short: "Configure a function",
} Long: `Configure a function
var configCmd = &cobra.Command{
Use: "config",
Short: "Configure a function",
Long: `Configure a function
Interactive propmt that allows configuration of Volume mounts, Environment Interactive propmt that allows configuration of Volume mounts, Environment
variables, and Labels for a function project present in the current directory variables, and Labels for a function project present in the current directory
or from the directory specified with --path. or from the directory specified with --path.
`, `,
SuggestFor: []string{"cfg", "cofnig"}, SuggestFor: []string{"cfg", "cofnig"},
PreRunE: bindEnv("path"), PreRunE: bindEnv("path"),
RunE: runConfigCmd, RunE: runConfigCmd,
}
setPathFlag(cmd)
cmd.AddCommand(NewConfigLabelsCmd(defaultLoaderSaver))
cmd.AddCommand(NewConfigEnvsCmd())
cmd.AddCommand(NewConfigVolumesCmd())
return cmd
} }
func runConfigCmd(cmd *cobra.Command, args []string) (err error) { func runConfigCmd(cmd *cobra.Command, args []string) (err error) {

View File

@ -14,41 +14,47 @@ import (
"knative.dev/kn-plugin-func/utils" "knative.dev/kn-plugin-func/utils"
) )
func init() { func NewConfigEnvsCmd() *cobra.Command {
setPathFlag(configEnvsCmd) cmd := &cobra.Command{
setPathFlag(configEnvsAddCmd) Use: "envs",
setPathFlag(configEnvsRemoveCmd) Short: "List and manage configured environment variable for a function",
configCmd.AddCommand(configEnvsCmd) Long: `List and manage configured environment variable for a function
configEnvsCmd.AddCommand(configEnvsAddCmd)
configEnvsCmd.AddCommand(configEnvsRemoveCmd)
}
var configEnvsCmd = &cobra.Command{
Use: "envs",
Short: "List and manage configured environment variable for a function",
Long: `List and manage configured environment variable for a function
Prints configured Environment variable for a function project present in Prints configured Environment variable for a function project present in
the current directory or from the directory specified with --path. the current directory or from the directory specified with --path.
`, `,
SuggestFor: []string{"ensv", "env"}, SuggestFor: []string{"ensv", "env"},
PreRunE: bindEnv("path"), PreRunE: bindEnv("path"),
RunE: func(cmd *cobra.Command, args []string) (err error) { RunE: func(cmd *cobra.Command, args []string) (err error) {
function, err := initConfigCommand(args, defaultLoaderSaver) function, err := initConfigCommand(args, defaultLoaderSaver)
if err != nil { if err != nil {
return
}
listEnvs(function)
return return
} },
}
listEnvs(function) configEnvsAddCmd := NewConfigEnvsAddCmd()
configEnvsRemoveCmd := NewConfigEnvsRemoveCmd()
return setPathFlag(cmd)
}, setPathFlag(configEnvsAddCmd)
setPathFlag(configEnvsRemoveCmd)
cmd.AddCommand(configEnvsAddCmd)
cmd.AddCommand(configEnvsRemoveCmd)
return cmd
} }
var configEnvsAddCmd = &cobra.Command{ func NewConfigEnvsAddCmd() *cobra.Command {
Use: "add", return &cobra.Command{
Short: "Add environment variable to the function configuration", Use: "add",
Long: `Add environment variable to the function configuration Short: "Add environment variable to the function configuration",
Long: `Add environment variable to the function configuration
Interactive prompt to add Environment variables to the function project Interactive prompt to add Environment variables to the function project
in the current directory or from the directory specified with --path. in the current directory or from the directory specified with --path.
@ -56,36 +62,41 @@ in the current directory or from the directory specified with --path.
The environment variable can be set directly from a value, The environment variable can be set directly from a value,
from an environment variable on the local machine or from Secrets and ConfigMaps. from an environment variable on the local machine or from Secrets and ConfigMaps.
`, `,
SuggestFor: []string{"ad", "create", "insert", "append"}, SuggestFor: []string{"ad", "create", "insert", "append"},
PreRunE: bindEnv("path"), PreRunE: bindEnv("path"),
RunE: func(cmd *cobra.Command, args []string) (err error) { RunE: func(cmd *cobra.Command, args []string) (err error) {
function, err := initConfigCommand(args, defaultLoaderSaver) function, err := initConfigCommand(args, defaultLoaderSaver)
if err != nil { if err != nil {
return return
} }
return runAddEnvsPrompt(cmd.Context(), function)
},
}
return runAddEnvsPrompt(cmd.Context(), function)
},
} }
var configEnvsRemoveCmd = &cobra.Command{ func NewConfigEnvsRemoveCmd() *cobra.Command {
Use: "remove", return &cobra.Command{
Short: "Remove environment variable from the function configuration", Use: "remove",
Long: `Remove environment variable from the function configuration Short: "Remove environment variable from the function configuration",
Long: `Remove environment variable from the function configuration
Interactive prompt to remove Environment variables from the function project Interactive prompt to remove Environment variables from the function project
in the current directory or from the directory specified with --path. in the current directory or from the directory specified with --path.
`, `,
SuggestFor: []string{"rm", "del", "delete", "rmeove"}, SuggestFor: []string{"rm", "del", "delete", "rmeove"},
PreRunE: bindEnv("path"), PreRunE: bindEnv("path"),
RunE: func(cmd *cobra.Command, args []string) (err error) { RunE: func(cmd *cobra.Command, args []string) (err error) {
function, err := initConfigCommand(args, defaultLoaderSaver) function, err := initConfigCommand(args, defaultLoaderSaver)
if err != nil { if err != nil {
return return
} }
return runRemoveEnvsPrompt(function)
},
}
return runRemoveEnvsPrompt(function)
},
} }
func listEnvs(f fn.Function) { func listEnvs(f fn.Function) {

View File

@ -13,75 +13,86 @@ import (
"knative.dev/kn-plugin-func/k8s" "knative.dev/kn-plugin-func/k8s"
) )
func init() { func NewConfigVolumesCmd() *cobra.Command {
setPathFlag(configVolumesCmd) cmd := &cobra.Command{
setPathFlag(configVolumesAddCmd) Use: "volumes",
setPathFlag(configVolumesRemoveCmd) Short: "List and manage configured volumes for a function",
configCmd.AddCommand(configVolumesCmd) Long: `List and manage configured volumes for a function
configVolumesCmd.AddCommand(configVolumesAddCmd)
configVolumesCmd.AddCommand(configVolumesRemoveCmd)
}
var configVolumesCmd = &cobra.Command{
Use: "volumes",
Short: "List and manage configured volumes for a function",
Long: `List and manage configured volumes for a function
Prints configured Volume mounts for a function project present in Prints configured Volume mounts for a function project present in
the current directory or from the directory specified with --path. the current directory or from the directory specified with --path.
`, `,
SuggestFor: []string{"volums", "volume", "vols"}, SuggestFor: []string{"volums", "volume", "vols"},
PreRunE: bindEnv("path"), PreRunE: bindEnv("path"),
RunE: func(cmd *cobra.Command, args []string) (err error) { RunE: func(cmd *cobra.Command, args []string) (err error) {
function, err := initConfigCommand(args, defaultLoaderSaver) function, err := initConfigCommand(args, defaultLoaderSaver)
if err != nil { if err != nil {
return
}
listVolumes(function)
return return
} },
}
listVolumes(function) configVolumesAddCmd := NewConfigVolumesAddCmd()
configVolumesRemoveCmd := NewConfigVolumesRemoveCmd()
return setPathFlag(cmd)
}, setPathFlag(configVolumesAddCmd)
setPathFlag(configVolumesRemoveCmd)
cmd.AddCommand(configVolumesAddCmd)
cmd.AddCommand(configVolumesRemoveCmd)
return cmd
} }
var configVolumesAddCmd = &cobra.Command{ func NewConfigVolumesAddCmd() *cobra.Command {
Use: "add", return &cobra.Command{
Short: "Add volume to the function configuration", Use: "add",
Long: `Add volume to the function configuration Short: "Add volume to the function configuration",
Long: `Add volume to the function configuration
Interactive prompt to add Secrets and ConfigMaps as Volume mounts to the function project Interactive prompt to add Secrets and ConfigMaps as Volume mounts to the function project
in the current directory or from the directory specified with --path. in the current directory or from the directory specified with --path.
`, `,
SuggestFor: []string{"ad", "create", "insert", "append"}, SuggestFor: []string{"ad", "create", "insert", "append"},
PreRunE: bindEnv("path"), PreRunE: bindEnv("path"),
RunE: func(cmd *cobra.Command, args []string) (err error) { RunE: func(cmd *cobra.Command, args []string) (err error) {
function, err := initConfigCommand(args, defaultLoaderSaver) function, err := initConfigCommand(args, defaultLoaderSaver)
if err != nil { if err != nil {
return return
} }
return runAddVolumesPrompt(cmd.Context(), function)
},
}
return runAddVolumesPrompt(cmd.Context(), function)
},
} }
var configVolumesRemoveCmd = &cobra.Command{ func NewConfigVolumesRemoveCmd() *cobra.Command {
Use: "remove", return &cobra.Command{
Short: "Remove volume from the function configuration", Use: "remove",
Long: `Remove volume from the function configuration Short: "Remove volume from the function configuration",
Long: `Remove volume from the function configuration
Interactive prompt to remove Volume mounts from the function project Interactive prompt to remove Volume mounts from the function project
in the current directory or from the directory specified with --path. in the current directory or from the directory specified with --path.
`, `,
SuggestFor: []string{"del", "delete", "rmeove"}, SuggestFor: []string{"del", "delete", "rmeove"},
PreRunE: bindEnv("path"), PreRunE: bindEnv("path"),
RunE: func(cmd *cobra.Command, args []string) (err error) { RunE: func(cmd *cobra.Command, args []string) (err error) {
function, err := initConfigCommand(args, defaultLoaderSaver) function, err := initConfigCommand(args, defaultLoaderSaver)
if err != nil { if err != nil {
return return
} }
return runRemoveVolumesPrompt(function)
},
}
return runRemoveVolumesPrompt(function)
},
} }
func listVolumes(f fn.Function) { func listVolumes(f fn.Function) {

View File

@ -25,12 +25,6 @@ type ErrInvalidRuntime error
// ErrInvalidTemplate indicates that the passed template was invalid. // ErrInvalidTemplate indicates that the passed template was invalid.
type ErrInvalidTemplate error type ErrInvalidTemplate error
func init() {
// Add to the root a new "Create" command which obtains an appropriate
// instance of fn.Client from the given client creator function.
root.AddCommand(NewCreateCmd(newCreateClient))
}
// createClientFn is a factory function which returns a Client suitable for // createClientFn is a factory function which returns a Client suitable for
// use with the Create command. // use with the Create command.
type createClientFn func(createConfig) *fn.Client type createClientFn func(createConfig) *fn.Client
@ -53,16 +47,16 @@ func NewCreateCmd(clientFn createClientFn) *cobra.Command {
Short: "Create a Function Project", Short: "Create a Function Project",
Long: ` Long: `
NAME NAME
{{.Prefix}}func create - Create a Function project. {{.Name}} create - Create a Function project.
SYNOPSIS SYNOPSIS
{{.Prefix}}func create [-l|--language] [-t|--template] [-r|--repository] {{.Name}} create [-l|--language] [-t|--template] [-r|--repository]
[-c|--confirm] [-v|--verbose] [path] [-c|--confirm] [-v|--verbose] [path]
DESCRIPTION DESCRIPTION
Creates a new Function project. Creates a new Function project.
$ {{.Prefix}}func create -l node -t http $ {{.Name}} create -l node -t http
Creates a Function in the current directory '.' which is written in the Creates a Function in the current directory '.' which is written in the
language/runtime 'node' and handles HTTP events. language/runtime 'node' and handles HTTP events.
@ -71,24 +65,24 @@ DESCRIPTION
the path if necessary. the path if necessary.
To complete this command interactivly, use --confirm (-c): To complete this command interactivly, use --confirm (-c):
$ {{.Prefix}}func create -c $ {{.Name}} create -c
Available Language Runtimes and Templates: Available Language Runtimes and Templates:
{{ .Options | indent 2 " " | indent 1 "\t" }} {{ .Options | indent 2 " " | indent 1 "\t" }}
To install more language runtimes and their templates see '{{.Prefix}}func repository'. To install more language runtimes and their templates see '{{.Name}} repository'.
EXAMPLES EXAMPLES
o Create a Node.js Function (the default language runtime) in the current o Create a Node.js Function (the default language runtime) in the current
directory (the default path) which handles http events (the default directory (the default path) which handles http events (the default
template). template).
$ {{.Prefix}}func create $ {{.Name}} create
o Create a Node.js Function in the directory 'myfunc'. o Create a Node.js Function in the directory 'myfunc'.
$ {{.Prefix}}func create myfunc $ {{.Name}} create myfunc
o Create a Go Function which handles CloudEvents in ./myfunc. o Create a Go Function which handles CloudEvents in ./myfunc.
$ {{.Prefix}}func create -l go -t cloudevents myfunc $ {{.Name}} create -l go -t cloudevents myfunc
`, `,
SuggestFor: []string{"vreate", "creaet", "craete", "new"}, SuggestFor: []string{"vreate", "creaet", "craete", "new"},
PreRunE: bindEnv("language", "template", "repository", "confirm"), PreRunE: bindEnv("language", "template", "repository", "confirm"),
@ -182,10 +176,10 @@ func runCreateHelp(cmd *cobra.Command, args []string, clientFn createClientFn) {
var data = struct { var data = struct {
Options string Options string
Prefix string Name string
}{ }{
Options: options, Options: options,
Prefix: pluginPrefix(), Name: cmd.Root().Name(),
} }
if err := tpl.Execute(cmd.OutOrStdout(), data); err != nil { if err := tpl.Execute(cmd.OutOrStdout(), data); err != nil {
fmt.Fprintf(cmd.ErrOrStderr(), "unable to display help text: %v", err) fmt.Fprintf(cmd.ErrOrStderr(), "unable to display help text: %v", err)

View File

@ -14,12 +14,6 @@ import (
"knative.dev/kn-plugin-func/progress" "knative.dev/kn-plugin-func/progress"
) )
func init() {
// Create a new delete command with a reference to
// a function which yields an appropriate concrete client instance.
root.AddCommand(NewDeleteCmd(newDeleteClient))
}
// newDeleteClient returns an instance of a Client using the // newDeleteClient returns an instance of a Client using the
// final config state. // final config state.
// Testing note: This method is swapped out during testing to allow // Testing note: This method is swapped out during testing to allow

View File

@ -24,10 +24,6 @@ import (
"knative.dev/kn-plugin-func/progress" "knative.dev/kn-plugin-func/progress"
) )
func init() {
root.AddCommand(NewDeployCmd(newDeployClient))
}
func newDeployClient(cfg deployConfig) (*fn.Client, error) { func newDeployClient(cfg deployConfig) (*fn.Client, error) {
listener := progress.New() listener := progress.New()
builder := buildpacks.NewBuilder() builder := buildpacks.NewBuilder()

View File

@ -2,6 +2,7 @@ package main
import ( import (
"context" "context"
"fmt"
"os" "os"
"os/signal" "os/signal"
"syscall" "syscall"
@ -28,6 +29,25 @@ func main() {
os.Exit(137) os.Exit(137)
}() }()
cmd.SetMeta(date, vers, hash) root, err := cmd.NewRootCmd(cmd.RootCommandConfig{
cmd.Execute(ctx) Name: "func",
Date: date,
Version: vers,
Hash: hash,
})
if err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
os.Exit(1)
}
if err := root.ExecuteContext(ctx); err != nil {
if ctx.Err() != nil {
os.Exit(130)
return
}
// Errors are printed to STDERR output and the process exits with code of 1.
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
os.Exit(1)
}
} }

View File

@ -15,10 +15,6 @@ import (
"knative.dev/kn-plugin-func/knative" "knative.dev/kn-plugin-func/knative"
) )
func init() {
root.AddCommand(NewInfoCmd(newInfoClient))
}
func newInfoClient(cfg infoConfig) (*fn.Client, error) { func newInfoClient(cfg infoConfig) (*fn.Client, error) {
describer, err := knative.NewDescriber(cfg.Namespace) describer, err := knative.NewDescriber(cfg.Namespace)
if err != nil { if err != nil {

View File

@ -18,10 +18,6 @@ import (
knative "knative.dev/kn-plugin-func/knative" knative "knative.dev/kn-plugin-func/knative"
) )
func init() {
root.AddCommand(NewInvokeCmd(newInvokeClient))
}
type invokeClientFn func(invokeConfig) (*fn.Client, error) type invokeClientFn func(invokeConfig) (*fn.Client, error)
func newInvokeClient(cfg invokeConfig) (*fn.Client, error) { func newInvokeClient(cfg invokeConfig) (*fn.Client, error) {
@ -44,10 +40,10 @@ func NewInvokeCmd(clientFn invokeClientFn) *cobra.Command {
Short: "Invoke a Function", Short: "Invoke a Function",
Long: ` Long: `
NAME NAME
{{.Prefix}}func invoke - Invoke a Function. {{.Name}} invoke - Invoke a Function.
SYNOPSIS SYNOPSIS
{{.Prefix}}func invoke [-t|--target] [-f|--format] {{.Name}} invoke [-t|--target] [-f|--format]
[--id] [--source] [--type] [--data] [--file] [--content-type] [--id] [--source] [--type] [--data] [--file] [--content-type]
[-s|--save] [-p|--path] [-c|--confirm] [-v|--verbose] [-s|--save] [-p|--path] [-c|--confirm] [-v|--verbose]
@ -71,54 +67,54 @@ DESCRIPTION
Invocation Target Invocation Target
The Function instance to invoke can be specified using the --target flag The Function instance to invoke can be specified using the --target flag
which accepts the values "local", "remote", or <URL>. By default the which accepts the values "local", "remote", or <URL>. By default the
local Function instance is chosen if running (see {{.Prefix}}func run). local Function instance is chosen if running (see {{.Name}} run).
To explicitly target the remote (deployed) Function: To explicitly target the remote (deployed) Function:
{{.Prefix}}func invoke --target=remote {{.Name}} invoke --target=remote
To target an arbitrary endpoint, provide a URL: To target an arbitrary endpoint, provide a URL:
{{.Prefix}}func invoke --target=https://myfunction.example.com {{.Name}} invoke --target=https://myfunction.example.com
Invocation Data Invocation Data
Providing a filename in the --file flag will base64 encode its contents Providing a filename in the --file flag will base64 encode its contents
as the "data" parameter sent to the Function. The value of --content-type as the "data" parameter sent to the Function. The value of --content-type
should be set to the type from the source file. For example, the following should be set to the type from the source file. For example, the following
would send a JPEG base64 encoded in the "data" POST parameter: would send a JPEG base64 encoded in the "data" POST parameter:
{{.Prefix}}func invoke --file=example.jpeg --content-type=image/jpeg {{.Name}} invoke --file=example.jpeg --content-type=image/jpeg
Message Format Message Format
By default Functions are sent messages which match the invocation format By default Functions are sent messages which match the invocation format
of the template they were created using; for example "http" or "cloudevent". of the template they were created using; for example "http" or "cloudevent".
To override this behavior, use the --format (-f) flag. To override this behavior, use the --format (-f) flag.
{{.Prefix}}func invoke -f=cloudevent -t=http://my-sink.my-cluster {{.Name}} invoke -f=cloudevent -t=http://my-sink.my-cluster
EXAMPLES EXAMPLES
o Invoke the default (local or remote) running Function with default values o Invoke the default (local or remote) running Function with default values
$ {{.Prefix}}func invoke $ {{.Name}} invoke
o Run the Function locally and then invoke it with a test request: o Run the Function locally and then invoke it with a test request:
(run in two terminals or by running the first in the background) (run in two terminals or by running the first in the background)
$ {{.Prefix}}func run $ {{.Name}} run
$ {{.Prefix}}func invoke $ {{.Name}} invoke
o Deploy and then invoke the remote Function: o Deploy and then invoke the remote Function:
$ {{.Prefix}}func deploy $ {{.Name}} deploy
$ {{.Prefix}}func invoke $ {{.Name}} invoke
o Invoke a remote (deployed) Function when it is already running locally: o Invoke a remote (deployed) Function when it is already running locally:
(overrides the default behavior of preferring locally running instances) (overrides the default behavior of preferring locally running instances)
$ {{.Prefix}}func invoke --target=remote $ {{.Name}} invoke --target=remote
o Specify the data to send to the Function as a flag o Specify the data to send to the Function as a flag
$ {{.Prefix}}func invoke --data="Hello World!" $ {{.Name}} invoke --data="Hello World!"
o Send a JPEG to the Function o Send a JPEG to the Function
$ {{.Prefix}}func invoke --file=example.jpeg --content-type=image/jpeg $ {{.Name}} invoke --file=example.jpeg --content-type=image/jpeg
o Invoke an arbitrary endpoint (HTTP POST) o Invoke an arbitrary endpoint (HTTP POST)
$ {{.Prefix}}func invoke --target="https://my-http-handler.example.com" $ {{.Name}} invoke --target="https://my-http-handler.example.com"
o Invoke an arbitrary endpoint (CloudEvent) o Invoke an arbitrary endpoint (CloudEvent)
$ {{.Prefix}}func invoke -f=cloudevent -t="https://my-event-broker.example.com" $ {{.Name}} invoke -f=cloudevent -t="https://my-event-broker.example.com"
`, `,
SuggestFor: []string{"emit", "emti", "send", "emit", "exec", "nivoke", "onvoke", "unvoke", "knvoke", "imvoke", "ihvoke", "ibvoke"}, SuggestFor: []string{"emit", "emti", "send", "emit", "exec", "nivoke", "onvoke", "unvoke", "knvoke", "imvoke", "ihvoke", "ibvoke"},
@ -203,9 +199,9 @@ func runInvokeHelp(cmd *cobra.Command, args []string, clientFn invokeClientFn) {
) )
var data = struct { var data = struct {
Prefix string Name string
}{ }{
Prefix: pluginPrefix(), Name: cmd.Root().Name(),
} }
if err := tpl.Execute(cmd.OutOrStdout(), data); err != nil { if err := tpl.Execute(cmd.OutOrStdout(), data); err != nil {

View File

@ -17,10 +17,6 @@ import (
"knative.dev/kn-plugin-func/knative" "knative.dev/kn-plugin-func/knative"
) )
func init() {
root.AddCommand(NewListCmd(newListClient))
}
func newListClient(cfg listConfig) (*fn.Client, error) { func newListClient(cfg listConfig) (*fn.Client, error) {
// TODO(lkingland): does an empty namespace mean all namespaces // TODO(lkingland): does an empty namespace mean all namespaces
// or the default namespace as defined in user's config? // or the default namespace as defined in user's config?

View File

@ -12,15 +12,6 @@ import (
fn "knative.dev/kn-plugin-func" fn "knative.dev/kn-plugin-func"
) )
func init() {
repositoryCmd := NewRepositoryCmd(newRepositoryClient)
repositoryCmd.AddCommand(NewRepositoryListCmd(newRepositoryClient))
repositoryCmd.AddCommand(NewRepositoryAddCmd(newRepositoryClient))
repositoryCmd.AddCommand(NewRepositoryRenameCmd(newRepositoryClient))
repositoryCmd.AddCommand(NewRepositoryRemoveCmd(newRepositoryClient))
root.AddCommand(repositoryCmd)
}
// repositoryClientFn is a function which yields both a client and the final // repositoryClientFn is a function which yields both a client and the final
// config used to instantiate. // config used to instantiate.
type repositoryClientFn func([]string) (repositoryConfig, RepositoryClient, error) type repositoryClientFn func([]string) (repositoryConfig, RepositoryClient, error)
@ -174,6 +165,12 @@ EXAMPLES
cmd.RunE = func(cmd *cobra.Command, args []string) error { cmd.RunE = func(cmd *cobra.Command, args []string) error {
return runRepository(cmd, args, clientFn) return runRepository(cmd, args, clientFn)
} }
cmd.AddCommand(NewRepositoryListCmd(newRepositoryClient))
cmd.AddCommand(NewRepositoryAddCmd(newRepositoryClient))
cmd.AddCommand(NewRepositoryRenameCmd(newRepositoryClient))
cmd.AddCommand(NewRepositoryRemoveCmd(newRepositoryClient))
return cmd return cmd
} }

View File

@ -2,7 +2,6 @@ package cmd
import ( import (
"bytes" "bytes"
"context"
"fmt" "fmt"
"os" "os"
"path/filepath" "path/filepath"
@ -29,69 +28,34 @@ var exampleTemplate = template.Must(template.New("example").Parse(`
curl $(kn service describe myfunc -o url) curl $(kn service describe myfunc -o url)
`)) `))
// The root of the command tree defines the command name, description, globally type RootCommandConfig struct {
Name string // usually `func` or `kn func`
Date string
Version string
Hash string
}
// NewRootCmd creates the root of the command tree defines the command name, description, globally
// available flags, etc. It has no action of its own, such that running the // available flags, etc. It has no action of its own, such that running the
// resultant binary with no arguments prints the help/usage text. // resultant binary with no arguments prints the help/usage text.
var root = &cobra.Command{ func NewRootCmd(config RootCommandConfig) (*cobra.Command, error) {
Use: "func", var err error
Short: "Serverless Functions",
SilenceErrors: true, // we explicitly handle errors in Execute() root := &cobra.Command{
SilenceUsage: true, // no usage dump on error Use: config.Name,
Long: `Serverless Functions Short: "Serverless Functions",
SilenceErrors: true, // we explicitly handle errors in Execute()
SilenceUsage: true, // no usage dump on error
Long: `Serverless Functions
Create, build and deploy Functions in serverless containers for multiple runtimes on Knative`, Create, build and deploy Functions in serverless containers for multiple runtimes on Knative`,
} }
func init() { root.Example, err = replaceNameInTemplate(config.Name, "example")
var err error
root.Example, err = replaceNameInTemplate("func", "example")
if err != nil { if err != nil {
root.Example = "Usage could not be loaded" root.Example = "Usage could not be loaded"
} }
}
func replaceNameInTemplate(name, template string) (string, error) {
var buffer bytes.Buffer
err := exampleTemplate.ExecuteTemplate(&buffer, template, name)
if err != nil {
return "", err
}
return buffer.String(), nil
}
// pluginPrefix returns an optional prefix for help commands based on the
// value of the FUNC_PARENT_COMMAND environment variable.
func pluginPrefix() string {
parent := os.Getenv("FUNC_PARENT_COMMAND")
if parent != "" {
return parent + " "
}
return ""
}
// NewRootCmd can be used to embed the func commands as a library, such as
// a plugin in 'kn'.
func NewRootCmd() (*cobra.Command, error) {
// TODO: have 'kn' provide this environment variable, such that this works
// generically, and works whether it is compiled in as a library or used via
// the `kn-func` binary naming convention.
os.Setenv("FUNC_PARENT_COMMAND", "kn")
// TODO: update the below to use the environment variable, and the general
// structure seen in the create command's help text.
root.Use = "kn func"
var err error
root.Example, err = replaceNameInTemplate("kn func", "example")
if err != nil {
root.Example = "Usage could not be loaded"
}
return root, err
}
// When the code is loaded into memory upon invocation, the cobra/viper packages
// are invoked to gather system context. This includes reading the configuration
// file, environment variables, and parsing the command flags.
func init() {
// read in environment variables that match // read in environment variables that match
viper.AutomaticEnv() viper.AutomaticEnv()
@ -101,9 +65,9 @@ func init() {
// which thus overrides both the default and the value read in from the // which thus overrides both the default and the value read in from the
// config file (i.e. flags always take highest precidence). // config file (i.e. flags always take highest precidence).
root.PersistentFlags().BoolVarP(&verbose, "verbose", "v", verbose, "print verbose logs") root.PersistentFlags().BoolVarP(&verbose, "verbose", "v", verbose, "print verbose logs")
err := viper.BindPFlag("verbose", root.PersistentFlags().Lookup("verbose")) err = viper.BindPFlag("verbose", root.PersistentFlags().Lookup("verbose"))
if err != nil { if err != nil {
panic(err) return nil, err
} }
// Override the --version template to match the output format from the // Override the --version template to match the output format from the
@ -112,24 +76,38 @@ func init() {
// Prefix all environment variables with "FUNC_" to avoid collisions with other apps. // Prefix all environment variables with "FUNC_" to avoid collisions with other apps.
viper.SetEnvPrefix("func") viper.SetEnvPrefix("func")
version := Version{
Date: config.Date,
Vers: config.Version,
Hash: config.Hash,
}
root.Version = version.String()
root.AddCommand(NewVersionCmd(version))
root.AddCommand(NewCreateCmd(newCreateClient))
root.AddCommand(NewConfigCmd())
root.AddCommand(NewBuildCmd(newBuildClient))
root.AddCommand(NewDeployCmd(newDeployClient))
root.AddCommand(NewDeleteCmd(newDeleteClient))
root.AddCommand(NewInfoCmd(newInfoClient))
root.AddCommand(NewListCmd(newListClient))
root.AddCommand(NewInvokeCmd(newInvokeClient))
root.AddCommand(NewRepositoryCmd(newRepositoryClient))
root.AddCommand(NewRunCmd(newRunClient))
root.AddCommand(NewCompletionCmd())
return root, nil
} }
// Execute the command tree by executing the root command, which runs func replaceNameInTemplate(name, template string) (string, error) {
// according to the context defined by: the optional config file, var buffer bytes.Buffer
// Environment Variables, command arguments and flags. err := exampleTemplate.ExecuteTemplate(&buffer, template, name)
func Execute(ctx context.Context) { if err != nil {
// Sets version to a string partially populated by compile-time flags. return "", err
root.Version = version.String()
// Execute the root of the command tree.
if err := root.ExecuteContext(ctx); err != nil {
if ctx.Err() != nil {
os.Exit(130)
return
}
// Errors are printed to STDERR output and the process exits with code of 1.
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
os.Exit(1)
} }
return buffer.String(), nil
} }
// Helpers // Helpers
@ -338,3 +316,45 @@ func setPathFlag(cmd *cobra.Command) {
func setNamespaceFlag(cmd *cobra.Command) { func setNamespaceFlag(cmd *cobra.Command) {
cmd.Flags().StringP("namespace", "n", "", "The namespace on the cluster. By default, the namespace in func.yaml is used or the currently active namespace if not set in the configuration. (Env: $FUNC_NAMESPACE)") cmd.Flags().StringP("namespace", "n", "", "The namespace on the cluster. By default, the namespace in func.yaml is used or the currently active namespace if not set in the configuration. (Env: $FUNC_NAMESPACE)")
} }
type Version struct {
// Date of compilation
Date string
// Version tag of the git commit, or 'tip' if no tag.
Vers string
// Hash of the currently active git commit on build.
Hash string
// Verbose printing enabled for the string representation.
Verbose bool
}
func (v Version) String() string {
// If 'vers' is not a semver already, then the binary was built either
// from an untagged git commit (set semver to v0.0.0), or was built
// directly from source (set semver to v0.0.0-source).
if strings.HasPrefix(v.Vers, "v") {
// Was built via make with a tagged commit
if v.Verbose {
return fmt.Sprintf("%s-%s-%s", v.Vers, v.Hash, v.Date)
} else {
return v.Vers
}
} else if v.Vers == "tip" {
// Was built via make from an untagged commit
v.Vers = "v0.0.0"
if v.Verbose {
return fmt.Sprintf("%s-%s-%s", v.Vers, v.Hash, v.Date)
} else {
return v.Vers
}
} else {
// Was likely built from source
v.Vers = "v0.0.0"
v.Hash = "source"
if v.Verbose {
return fmt.Sprintf("%s-%s", v.Vers, v.Hash)
} else {
return v.Vers
}
}
}

View File

@ -120,6 +120,11 @@ func TestRoot_mergeEnvMaps(t *testing.T) {
func TestRoot_CMDParameterized(t *testing.T) { func TestRoot_CMDParameterized(t *testing.T) {
rootConfig := RootCommandConfig{
Name: "func",
}
root, _ := NewRootCmd(rootConfig)
if root.Use != "func" { if root.Use != "func" {
t.Fatalf("default command use should be \"func\".") t.Fatalf("default command use should be \"func\".")
} }
@ -129,13 +134,17 @@ func TestRoot_CMDParameterized(t *testing.T) {
t.Fatalf("default command example should assume \"func\" as executable name. error: %v", err) t.Fatalf("default command example should assume \"func\" as executable name. error: %v", err)
} }
cmd, err := NewRootCmd() rootConfig = RootCommandConfig{
Name: "kn func",
}
cmd, err := NewRootCmd(rootConfig)
if cmd.Use != "kn func" && err != nil { if cmd.Use != "kn func" && err != nil {
t.Fatalf("plugin command use should be \"kn func\".") t.Fatalf("plugin command use should be \"kn func\".")
} }
usageExample, _ = replaceNameInTemplate("kn func", "example") usageExample, _ = replaceNameInTemplate("kn func", "example")
cmd, err = NewRootCmd() cmd, err = NewRootCmd(rootConfig)
if cmd.Example != usageExample || err != nil { if cmd.Example != usageExample || err != nil {
t.Fatalf("plugin command example should assume \"kn func\" as executable name. error: %v", err) t.Fatalf("plugin command example should assume \"kn func\" as executable name. error: %v", err)
} }

View File

@ -15,10 +15,6 @@ import (
"knative.dev/kn-plugin-func/progress" "knative.dev/kn-plugin-func/progress"
) )
func init() {
root.AddCommand(NewRunCmd(newRunClient))
}
func newRunClient(cfg runConfig) *fn.Client { func newRunClient(cfg runConfig) *fn.Client {
bc := newBuildConfig() bc := newBuildConfig()
runner := docker.NewRunner() runner := docker.NewRunner()

View File

@ -2,88 +2,25 @@ package cmd
import ( import (
"fmt" "fmt"
"strings"
"github.com/ory/viper"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
// metadata about the build process/binary etc. func NewVersionCmd(version Version) *cobra.Command {
// Not populated if building from source with go build. runVersion := func(cmd *cobra.Command, args []string) {
// Set by the `make` targets. fmt.Println(version)
var version = Version{} }
// SetMeta is called by `main` with any provided build metadata cmd := &cobra.Command{
func SetMeta(date, vers, hash string) { Use: "version",
version.Date = date // build timestamp Short: "Show the version",
version.Vers = vers // version tag Long: `Show the version
version.Hash = hash // git commit hash
}
func init() {
root.AddCommand(versionCmd)
}
var versionCmd = &cobra.Command{
Use: "version",
Short: "Show the version",
Long: `Show the version
Use the --verbose option to include the build date stamp and commit hash" Use the --verbose option to include the build date stamp and commit hash"
`, `,
SuggestFor: []string{"vers", "verison"}, SuggestFor: []string{"vers", "verison"}, //nolint:misspell
Run: runVersion, Run: runVersion,
}
func runVersion(cmd *cobra.Command, args []string) {
// update version with the value of the (global) flag 'verbose'
version.Verbose = viper.GetBool("verbose")
// version is the metadata, serialized.
fmt.Println(version)
}
// versionMetadata is set by the main package.
// When compiled from source, they remain the zero value.
// When compiled via `make`, they are initialized to the noted values.
type Version struct {
// Date of compilation
Date string
// Version tag of the git commit, or 'tip' if no tag.
Vers string
// Hash of the currently active git commit on build.
Hash string
// Verbose printing enabled for the string representation.
Verbose bool
}
func (v Version) String() string {
// If 'vers' is not a semver already, then the binary was built either
// from an untagged git commit (set semver to v0.0.0), or was built
// directly from source (set semver to v0.0.0-source).
if strings.HasPrefix(v.Vers, "v") {
// Was built via make with a tagged commit
if v.Verbose {
return fmt.Sprintf("%s-%s-%s", v.Vers, v.Hash, v.Date)
} else {
return v.Vers
}
} else if v.Vers == "tip" {
// Was built via make from an untagged commit
v.Vers = "v0.0.0"
if v.Verbose {
return fmt.Sprintf("%s-%s-%s", v.Vers, v.Hash, v.Date)
} else {
return v.Vers
}
} else {
// Was likely built from source
v.Vers = "v0.0.0"
v.Hash = "source"
if v.Verbose {
return fmt.Sprintf("%s-%s", v.Vers, v.Hash)
} else {
return v.Vers
}
} }
return cmd
} }

View File

@ -35,13 +35,19 @@ func (f *funcPlugin) Execute(args []string) error {
cancel() cancel()
}() }()
rootCmd, _ := cmd.NewRootCmd() rootConfig := cmd.RootCommandConfig{
Name: "kn func",
}
info, _ := debug.ReadBuildInfo() info, _ := debug.ReadBuildInfo()
for _, dep := range info.Deps { for _, dep := range info.Deps {
if strings.Contains(dep.Path, "knative.dev/kn-plugin-func") { if strings.Contains(dep.Path, "knative.dev/kn-plugin-func") {
cmd.SetMeta("", dep.Version, dep.Sum) rootConfig.Version, rootConfig.Hash = dep.Version, dep.Sum
} }
} }
rootCmd, _ := cmd.NewRootCmd(rootConfig)
oldArgs := os.Args oldArgs := os.Args
defer (func() { defer (func() {
os.Args = oldArgs os.Args = oldArgs