Blame vendor/github.com/vmware/govmomi/govc/cli/command.go

Packit 63bb0d
/*
Packit 63bb0d
Copyright (c) 2014-2016 VMware, Inc. All Rights Reserved.
Packit 63bb0d
Packit 63bb0d
Licensed under the Apache License, Version 2.0 (the "License");
Packit 63bb0d
you may not use this file except in compliance with the License.
Packit 63bb0d
You may obtain a copy of the License at
Packit 63bb0d
Packit 63bb0d
    http://www.apache.org/licenses/LICENSE-2.0
Packit 63bb0d
Packit 63bb0d
Unless required by applicable law or agreed to in writing, software
Packit 63bb0d
distributed under the License is distributed on an "AS IS" BASIS,
Packit 63bb0d
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Packit 63bb0d
See the License for the specific language governing permissions and
Packit 63bb0d
limitations under the License.
Packit 63bb0d
*/
Packit 63bb0d
Packit 63bb0d
package cli
Packit 63bb0d
Packit 63bb0d
import (
Packit 63bb0d
	"context"
Packit 63bb0d
	"flag"
Packit 63bb0d
	"fmt"
Packit 63bb0d
	"io"
Packit 63bb0d
	"io/ioutil"
Packit 63bb0d
	"os"
Packit 63bb0d
	"sort"
Packit 63bb0d
	"strings"
Packit 63bb0d
	"text/tabwriter"
Packit 63bb0d
Packit 63bb0d
	"github.com/vmware/govmomi/vim25/types"
Packit 63bb0d
)
Packit 63bb0d
Packit 63bb0d
type HasFlags interface {
Packit 63bb0d
	// Register may be called more than once and should be idempotent.
Packit 63bb0d
	Register(ctx context.Context, f *flag.FlagSet)
Packit 63bb0d
Packit 63bb0d
	// Process may be called more than once and should be idempotent.
Packit 63bb0d
	Process(ctx context.Context) error
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
type Command interface {
Packit 63bb0d
	HasFlags
Packit 63bb0d
Packit 63bb0d
	Run(ctx context.Context, f *flag.FlagSet) error
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
func generalHelp(w io.Writer, filter string) {
Packit 63bb0d
	var cmds, matches []string
Packit 63bb0d
	for name := range commands {
Packit 63bb0d
		cmds = append(cmds, name)
Packit 63bb0d
Packit 63bb0d
		if filter != "" && strings.Contains(name, filter) {
Packit 63bb0d
			matches = append(matches, name)
Packit 63bb0d
		}
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	if len(matches) == 0 {
Packit 63bb0d
		fmt.Fprintf(w, "Usage of %s:\n", os.Args[0])
Packit 63bb0d
	} else {
Packit 63bb0d
		fmt.Fprintf(w, "%s: command '%s' not found, did you mean:\n", os.Args[0], filter)
Packit 63bb0d
		cmds = matches
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	sort.Strings(cmds)
Packit 63bb0d
	for _, name := range cmds {
Packit 63bb0d
		fmt.Fprintf(w, "  %s\n", name)
Packit 63bb0d
	}
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
func commandHelp(w io.Writer, name string, cmd Command, f *flag.FlagSet) {
Packit 63bb0d
	type HasUsage interface {
Packit 63bb0d
		Usage() string
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	fmt.Fprintf(w, "Usage: %s %s [OPTIONS]", os.Args[0], name)
Packit 63bb0d
	if u, ok := cmd.(HasUsage); ok {
Packit 63bb0d
		fmt.Fprintf(w, " %s", u.Usage())
Packit 63bb0d
	}
Packit 63bb0d
	fmt.Fprintf(w, "\n")
Packit 63bb0d
Packit 63bb0d
	type HasDescription interface {
Packit 63bb0d
		Description() string
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	if u, ok := cmd.(HasDescription); ok {
Packit 63bb0d
		fmt.Fprintf(w, "\n%s\n", u.Description())
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	n := 0
Packit 63bb0d
	f.VisitAll(func(_ *flag.Flag) {
Packit 63bb0d
		n += 1
Packit 63bb0d
	})
Packit 63bb0d
Packit 63bb0d
	if n > 0 {
Packit 63bb0d
		fmt.Fprintf(w, "\nOptions:\n")
Packit 63bb0d
		tw := tabwriter.NewWriter(w, 2, 0, 2, ' ', 0)
Packit 63bb0d
		f.VisitAll(func(f *flag.Flag) {
Packit 63bb0d
			fmt.Fprintf(tw, "\t-%s=%s\t%s\n", f.Name, f.DefValue, f.Usage)
Packit 63bb0d
		})
Packit 63bb0d
		tw.Flush()
Packit 63bb0d
	}
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
func clientLogout(ctx context.Context, cmd Command) error {
Packit 63bb0d
	type logout interface {
Packit 63bb0d
		Logout(context.Context) error
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	if l, ok := cmd.(logout); ok {
Packit 63bb0d
		return l.Logout(ctx)
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	return nil
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
func Run(args []string) int {
Packit 63bb0d
	hw := os.Stderr
Packit 63bb0d
	rc := 1
Packit 63bb0d
	hwrc := func(arg string) {
Packit 63bb0d
		if arg == "-h" {
Packit 63bb0d
			hw = os.Stdout
Packit 63bb0d
			rc = 0
Packit 63bb0d
		}
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	var err error
Packit 63bb0d
Packit 63bb0d
	if len(args) == 0 {
Packit 63bb0d
		generalHelp(hw, "")
Packit 63bb0d
		return rc
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	// Look up real command name in aliases table.
Packit 63bb0d
	name, ok := aliases[args[0]]
Packit 63bb0d
	if !ok {
Packit 63bb0d
		name = args[0]
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	cmd, ok := commands[name]
Packit 63bb0d
	if !ok {
Packit 63bb0d
		hwrc(name)
Packit 63bb0d
		generalHelp(hw, name)
Packit 63bb0d
		return rc
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	fs := flag.NewFlagSet("", flag.ContinueOnError)
Packit 63bb0d
	fs.SetOutput(ioutil.Discard)
Packit 63bb0d
Packit 63bb0d
	ctx := context.Background()
Packit 63bb0d
Packit 63bb0d
	if id := os.Getenv("GOVC_OPERATION_ID"); id != "" {
Packit 63bb0d
		ctx = context.WithValue(ctx, types.ID{}, id)
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	cmd.Register(ctx, fs)
Packit 63bb0d
Packit 63bb0d
	if err = fs.Parse(args[1:]); err != nil {
Packit 63bb0d
		goto error
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	if err = cmd.Process(ctx); err != nil {
Packit 63bb0d
		goto error
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	if err = cmd.Run(ctx, fs); err != nil {
Packit 63bb0d
		goto error
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	if err = clientLogout(ctx, cmd); err != nil {
Packit 63bb0d
		goto error
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	return 0
Packit 63bb0d
Packit 63bb0d
error:
Packit 63bb0d
	if err == flag.ErrHelp {
Packit 63bb0d
		if len(args) == 2 {
Packit 63bb0d
			hwrc(args[1])
Packit 63bb0d
		}
Packit 63bb0d
		commandHelp(hw, args[0], cmd, fs)
Packit 63bb0d
	} else {
Packit 63bb0d
		if x, ok := err.(interface{ ExitCode() int }); ok {
Packit 63bb0d
			// propagate exit code, e.g. from guest.run
Packit 63bb0d
			rc = x.ExitCode()
Packit 63bb0d
		} else {
Packit 63bb0d
			w, ok := cmd.(interface{ WriteError(error) bool })
Packit 63bb0d
			if ok {
Packit 63bb0d
				ok = w.WriteError(err)
Packit 63bb0d
			}
Packit 63bb0d
			if !ok {
Packit 63bb0d
				fmt.Fprintf(os.Stderr, "%s: %s\n", os.Args[0], err)
Packit 63bb0d
			}
Packit 63bb0d
		}
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	_ = clientLogout(ctx, cmd)
Packit 63bb0d
Packit 63bb0d
	return rc
Packit 63bb0d
}