feat!: change `describe` command to `info` (#474)

The describe command conflicts sematically with kubectl describe.
This commit changes the command name to `info`.

Fixes: https://github.com/knative-sandbox/kn-plugin-func/issues/337

Signed-off-by: Lance Ball <lball@redhat.com>
This commit is contained in:
Lance Ball 2021-08-16 00:43:54 -04:00 committed by GitHub
parent c8875938d7
commit 10a07578e9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 66 additions and 67 deletions

View File

@ -126,10 +126,10 @@ type ProgressListener interface {
// Describer of Functions' remote deployed aspect. // Describer of Functions' remote deployed aspect.
type Describer interface { type Describer interface {
// Describe the running state of the service as reported by the underlyng platform. // Describe the running state of the service as reported by the underlyng platform.
Describe(ctx context.Context, name string) (description Description, err error) Describe(ctx context.Context, name string) (description Info, err error)
} }
type Description struct { type Info struct {
Name string `json:"name" yaml:"name"` Name string `json:"name" yaml:"name"`
Image string `json:"image" yaml:"image"` Image string `json:"image" yaml:"image"`
Namespace string `json:"namespace" yaml:"namespace"` Namespace string `json:"namespace" yaml:"namespace"`
@ -549,9 +549,9 @@ func (c *Client) List(ctx context.Context) ([]ListItem, error) {
return c.lister.List(ctx) return c.lister.List(ctx)
} }
// Describe a Function. Name takes precidence. If no name is provided, // Info for a Function. Name takes precidence. If no name is provided,
// the Function defined at root is used. // the Function defined at root is used.
func (c *Client) Describe(ctx context.Context, name, root string) (d Description, err error) { func (c *Client) Info(ctx context.Context, name, root string) (d Info, err error) {
go func() { go func() {
<-ctx.Done() <-ctx.Done()
c.progressListener.Stopping() c.progressListener.Stopping()

View File

@ -122,9 +122,9 @@ func runEmit(cmd *cobra.Command, _ []string, clientFn emitClientFn) (err error)
// Otherwise the value of Sink is used verbatim if defined. // Otherwise the value of Sink is used verbatim if defined.
func endpoint(ctx context.Context, cfg emitConfig) (url string, err error) { func endpoint(ctx context.Context, cfg emitConfig) (url string, err error) {
var ( var (
f fn.Function f fn.Function
d fn.Describer d fn.Describer
desc fn.Description i fn.Info
) )
// If the special value "local" was requested, // If the special value "local" was requested,
@ -151,18 +151,18 @@ func endpoint(ctx context.Context, cfg emitConfig) (url string, err error) {
} }
// Get the current state of the function. // Get the current state of the function.
if desc, err = d.Describe(ctx, f.Name); err != nil { if i, err = d.Describe(ctx, f.Name); err != nil {
return return
} }
// Probably wise to be defensive here: // Probably wise to be defensive here:
if len(desc.Routes) == 0 { if len(i.Routes) == 0 {
err = errors.New("function has no active routes") err = errors.New("function has no active routes")
return return
} }
// The first route should be the destination. // The first route should be the destination.
return desc.Routes[0], nil return i.Routes[0], nil
} }
type emitConfig struct { type emitConfig struct {

View File

@ -16,10 +16,10 @@ import (
) )
func init() { func init() {
root.AddCommand(NewDescribeCmd(newDescribeClient)) root.AddCommand(NewInfoCmd(newInfoClient))
} }
func newDescribeClient(cfg describeConfig) (*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 {
return nil, err return nil, err
@ -33,11 +33,11 @@ func newDescribeClient(cfg describeConfig) (*fn.Client, error) {
), nil ), nil
} }
type describeClientFn func(describeConfig) (*fn.Client, error) type infoClientFn func(infoConfig) (*fn.Client, error)
func NewDescribeCmd(clientFn describeClientFn) *cobra.Command { func NewInfoCmd(clientFn infoClientFn) *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "describe <name>", Use: "info <name>",
Short: "Show details of a function", Short: "Show details of a function",
Long: `Show details of a function Long: `Show details of a function
@ -46,12 +46,12 @@ the current directory or from the directory specified with --path.
`, `,
Example: ` Example: `
# Show the details of a function as declared in the local func.yaml # Show the details of a function as declared in the local func.yaml
kn func describe kn func info
# Show the details of the function in the myotherfunc directory with yaml output # Show the details of the function in the myotherfunc directory with yaml output
kn func describe --output yaml --path myotherfunc kn func info --output yaml --path myotherfunc
`, `,
SuggestFor: []string{"desc", "get"}, SuggestFor: []string{"ifno", "describe", "fino", "get"},
ValidArgsFunction: CompleteFunctionList, ValidArgsFunction: CompleteFunctionList,
PreRunE: bindEnv("namespace", "output", "path"), PreRunE: bindEnv("namespace", "output", "path"),
} }
@ -65,14 +65,14 @@ kn func describe --output yaml --path myotherfunc
} }
cmd.RunE = func(cmd *cobra.Command, args []string) error { cmd.RunE = func(cmd *cobra.Command, args []string) error {
return runDescribe(cmd, args, clientFn) return runInfo(cmd, args, clientFn)
} }
return cmd return cmd
} }
func runDescribe(cmd *cobra.Command, args []string, clientFn describeClientFn) (err error) { func runInfo(cmd *cobra.Command, args []string, clientFn infoClientFn) (err error) {
config := newDescribeConfig(args) config := newInfoConfig(args)
function, err := fn.NewFunction(config.Path) function, err := fn.NewFunction(config.Path)
if err != nil { if err != nil {
@ -91,20 +91,20 @@ func runDescribe(cmd *cobra.Command, args []string, clientFn describeClientFn) (
} }
// Get the description // Get the description
d, err := client.Describe(cmd.Context(), config.Name, config.Path) d, err := client.Info(cmd.Context(), config.Name, config.Path)
if err != nil { if err != nil {
return return
} }
d.Image = function.Image d.Image = function.Image
write(os.Stdout, description(d), config.Output) write(os.Stdout, info(d), config.Output)
return return
} }
// CLI Configuration (parameters) // CLI Configuration (parameters)
// ------------------------------ // ------------------------------
type describeConfig struct { type infoConfig struct {
Name string Name string
Namespace string Namespace string
Output string Output string
@ -112,12 +112,12 @@ type describeConfig struct {
Verbose bool Verbose bool
} }
func newDescribeConfig(args []string) describeConfig { func newInfoConfig(args []string) infoConfig {
var name string var name string
if len(args) > 0 { if len(args) > 0 {
name = args[0] name = args[0]
} }
return describeConfig{ return infoConfig{
Name: deriveName(name, viper.GetString("path")), Name: deriveName(name, viper.GetString("path")),
Namespace: viper.GetString("namespace"), Namespace: viper.GetString("namespace"),
Output: viper.GetString("output"), Output: viper.GetString("output"),
@ -129,62 +129,62 @@ func newDescribeConfig(args []string) describeConfig {
// Output Formatting (serializers) // Output Formatting (serializers)
// ------------------------------- // -------------------------------
type description fn.Description type info fn.Info
func (d description) Human(w io.Writer) error { func (i info) Human(w io.Writer) error {
fmt.Fprintln(w, "Function name:") fmt.Fprintln(w, "Function name:")
fmt.Fprintf(w, " %v\n", d.Name) fmt.Fprintf(w, " %v\n", i.Name)
fmt.Fprintln(w, "Function is built in image:") fmt.Fprintln(w, "Function is built in image:")
fmt.Fprintf(w, " %v\n", d.Image) fmt.Fprintf(w, " %v\n", i.Image)
fmt.Fprintln(w, "Function is deployed in namespace:") fmt.Fprintln(w, "Function is deployed in namespace:")
fmt.Fprintf(w, " %v\n", d.Namespace) fmt.Fprintf(w, " %v\n", i.Namespace)
fmt.Fprintln(w, "Routes:") fmt.Fprintln(w, "Routes:")
for _, route := range d.Routes { for _, route := range i.Routes {
fmt.Fprintf(w, " %v\n", route) fmt.Fprintf(w, " %v\n", route)
} }
if len(d.Subscriptions) > 0 { if len(i.Subscriptions) > 0 {
fmt.Fprintln(w, "Subscriptions (Source, Type, Broker):") fmt.Fprintln(w, "Subscriptions (Source, Type, Broker):")
for _, s := range d.Subscriptions { for _, s := range i.Subscriptions {
fmt.Fprintf(w, " %v %v %v\n", s.Source, s.Type, s.Broker) fmt.Fprintf(w, " %v %v %v\n", s.Source, s.Type, s.Broker)
} }
} }
return nil return nil
} }
func (d description) Plain(w io.Writer) error { func (i info) Plain(w io.Writer) error {
fmt.Fprintf(w, "Name %v\n", d.Name) fmt.Fprintf(w, "Name %v\n", i.Name)
fmt.Fprintf(w, "Image %v\n", d.Image) fmt.Fprintf(w, "Image %v\n", i.Image)
fmt.Fprintf(w, "Namespace %v\n", d.Namespace) fmt.Fprintf(w, "Namespace %v\n", i.Namespace)
for _, route := range d.Routes { for _, route := range i.Routes {
fmt.Fprintf(w, "Route %v\n", route) fmt.Fprintf(w, "Route %v\n", route)
} }
if len(d.Subscriptions) > 0 { if len(i.Subscriptions) > 0 {
for _, s := range d.Subscriptions { for _, s := range i.Subscriptions {
fmt.Fprintf(w, "Subscription %v %v %v\n", s.Source, s.Type, s.Broker) fmt.Fprintf(w, "Subscription %v %v %v\n", s.Source, s.Type, s.Broker)
} }
} }
return nil return nil
} }
func (d description) JSON(w io.Writer) error { func (i info) JSON(w io.Writer) error {
return json.NewEncoder(w).Encode(d) return json.NewEncoder(w).Encode(i)
} }
func (d description) XML(w io.Writer) error { func (i info) XML(w io.Writer) error {
return xml.NewEncoder(w).Encode(d) return xml.NewEncoder(w).Encode(i)
} }
func (d description) YAML(w io.Writer) error { func (i info) YAML(w io.Writer) error {
return yaml.NewEncoder(w).Encode(d) return yaml.NewEncoder(w).Encode(i)
} }
func (d description) URL(w io.Writer) error { func (i info) URL(w io.Writer) error {
if len(d.Routes) > 0 { if len(i.Routes) > 0 {
fmt.Fprintf(w, "%s\n", d.Routes[0]) fmt.Fprintf(w, "%s\n", i.Routes[0])
} }
return nil return nil
} }

View File

@ -79,20 +79,20 @@ When run as a `kn` plugin.
kn func deploy [-n <namespace> -p <path> -i <image> -r <registry> -b=true|false] kn func deploy [-n <namespace> -p <path> -i <image> -r <registry> -b=true|false]
``` ```
## `describe` ## `info`
Prints the name, route and any event subscriptions for a deployed Function. The user may also specify the name of the function to describe. The namespace defaults to the value in `func.yaml` or the namespace currently active in the user's Kubernetes configuration. The namespace may be specified on the command line, and if so this will overwrite the value in `func.yaml`. Prints the name, route and any event subscriptions for a deployed Function. The user may also specify the name of the function to describe. The namespace defaults to the value in `func.yaml` or the namespace currently active in the user's Kubernetes configuration. The namespace may be specified on the command line, and if so this will overwrite the value in `func.yaml`.
Similar `kn` command: `kn service describe NAME [flags]`. This flag provides a lot of nice information not available in `func describe`, such as revisions, age, annotations and labels. This command should be renamed to make it distinct from `kn` - e.g. `func status`. Similar `kn` command: `kn service describe NAME [flags]`. This flag provides a lot of nice information not available in `func info`, such as revisions, age, annotations and labels.
```console ```console
func describe [-o <output> -n <namespace> -p <path>] func info [-o <output> -n <namespace> -p <path>]
``` ```
When run as a `kn` plugin. When run as a `kn` plugin.
```console ```console
kn func describe [-o <output> -n <namespace> -p <path>] kn func info [-o <output> -n <namespace> -p <path>]
``` ```
## `list` ## `list`

View File

@ -31,7 +31,7 @@ func NewDescriber(namespaceOverride string) (describer *Describer, err error) {
// restricts to label-syntax, which is thus escaped. Therefore as a knative (kube) implementation // restricts to label-syntax, which is thus escaped. Therefore as a knative (kube) implementation
// detal proper full names have to be escaped on the way in and unescaped on the way out. ex: // detal proper full names have to be escaped on the way in and unescaped on the way out. ex:
// www.example-site.com -> www-example--site-com // www.example-site.com -> www-example--site-com
func (d *Describer) Describe(ctx context.Context, name string) (description fn.Description, err error) { func (d *Describer) Describe(ctx context.Context, name string) (description fn.Info, err error) {
servingClient, err := NewServingClient(d.namespace) servingClient, err := NewServingClient(d.namespace)
if err != nil { if err != nil {

View File

@ -5,10 +5,10 @@ import (
"testing" "testing"
) )
// Describe runs `func describe' command basic test. // Info runs `func info' command basic test.
func Describe(t *testing.T, knFunc *TestShellCmdRunner, project *FunctionTestProject) { func Info(t *testing.T, knFunc *TestShellCmdRunner, project *FunctionTestProject) {
result := knFunc.Exec("describe", "--path", project.ProjectPath, "--output", "plain") result := knFunc.Exec("info", "--path", project.ProjectPath, "--output", "plain")
if result.Error != nil { if result.Error != nil {
t.Fail() t.Fail()
} }
@ -17,18 +17,18 @@ func Describe(t *testing.T, knFunc *TestShellCmdRunner, project *FunctionTestPro
// In case we have the route stored (i.e. by deploy command tested earlier) // In case we have the route stored (i.e. by deploy command tested earlier)
// we compare just to verify they match // we compare just to verify they match
// otherwise we take advantage and capture the route from the output // otherwise we take advantage and capture the route from the output
routeFromDescribe := "" routeFromInfo := ""
matches := regexp.MustCompile("Route (http.*)").FindStringSubmatch(result.Stdout) matches := regexp.MustCompile("Route (http.*)").FindStringSubmatch(result.Stdout)
if len(matches) > 1 { if len(matches) > 1 {
routeFromDescribe = matches[1] routeFromInfo = matches[1]
} }
if routeFromDescribe == "" { if routeFromInfo == "" {
t.Fatal("Function Route not present on output") t.Fatal("Function Route not present on output")
} }
if project.FunctionURL != "" && project.FunctionURL != routeFromDescribe { if project.FunctionURL != "" && project.FunctionURL != routeFromInfo {
t.Fatalf("Expected Route %v but found %v", project.FunctionURL, routeFromDescribe) t.Fatalf("Expected Route %v but found %v", project.FunctionURL, routeFromInfo)
} }
project.FunctionURL = routeFromDescribe project.FunctionURL = routeFromInfo
} }

View File

@ -21,7 +21,7 @@ func TestHttpFunction(t *testing.T) {
Deploy(t, knFunc, &project) Deploy(t, knFunc, &project)
defer Delete(t, knFunc, &project) defer Delete(t, knFunc, &project)
ReadyCheck(t, knFunc, project) ReadyCheck(t, knFunc, project)
Describe(t, knFunc, &project) Info(t, knFunc, &project)
DefaultFunctionHttpTest(t, knFunc, project) DefaultFunctionHttpTest(t, knFunc, project)
Update(t, knFunc, &project) Update(t, knFunc, &project)
NewRevisionFunctionHttpTest(t, knFunc, project) NewRevisionFunctionHttpTest(t, knFunc, project)
@ -40,8 +40,7 @@ func TestCloudEventsFunction(t *testing.T) {
Deploy(t, knFunc, &project) Deploy(t, knFunc, &project)
defer Delete(t, knFunc, &project) defer Delete(t, knFunc, &project)
ReadyCheck(t, knFunc, project) ReadyCheck(t, knFunc, project)
Describe(t, knFunc, &project) Info(t, knFunc, &project)
DefaultFunctionEventsTest(t, knFunc, project) DefaultFunctionEventsTest(t, knFunc, project)
} }