1
0
mirror of https://github.com/smallstep/cli.git synced 2026-10-24 11:19:19 +03:00
Files
step-ca-cli/command/command.go

213 lines
4.4 KiB
Go

package command
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"reflect"
"strings"
"github.com/pkg/errors"
"github.com/smallstep/cli/config"
"github.com/smallstep/cli/usage"
"github.com/urfave/cli"
)
// IgnoreEnvVar is a value added to a flag EnvVar to avoid the use of
// environment variables or configuration files.
const IgnoreEnvVar = "STEP_IGNORE_ENV_VAR"
var cmds []cli.Command
var currentContext *cli.Context
func init() {
os.Unsetenv(IgnoreEnvVar)
cmds = []cli.Command{
usage.HelpCommand(),
}
}
// Register adds the given command to the global list of commands.
// It sets recursively the command Flags environment variables.
func Register(c cli.Command) {
setEnvVar(&c)
cmds = append(cmds, c)
}
// Retrieve returns all commands
func Retrieve() []cli.Command {
return cmds
}
// ActionFunc returns a cli.ActionFunc that stores the context.
func ActionFunc(fn cli.ActionFunc) cli.ActionFunc {
return func(ctx *cli.Context) error {
currentContext = ctx
return fn(ctx)
}
}
// IsForce returns if the force flag was passed
func IsForce() bool {
return currentContext != nil && currentContext.Bool("force")
}
// getConfigVars load the defaults.json file and sets the flags if they are not
// already set or the EnvVar is set to IgnoreEnvVar.
//
// TODO(mariano): right now it only supports parameters at first level.
func getConfigVars(ctx *cli.Context) error {
configFile := ctx.GlobalString("config")
if configFile == "" {
configFile = filepath.Join(config.StepPath(), "config", "defaults.json")
}
b, err := ioutil.ReadFile(configFile)
if err != nil {
return nil
}
m := make(map[string]interface{})
if err := json.Unmarshal(b, &m); err != nil {
return errors.Wrapf(err, "error parsing %s", configFile)
}
flags := make(map[string]cli.Flag)
for _, f := range ctx.Command.Flags {
name := strings.Split(f.GetName(), ",")[0]
flags[name] = f
}
for _, name := range ctx.FlagNames() {
if ctx.IsSet(name) {
continue
}
// Skip if EnvVar == IgnoreEnvVar
if f, ok := flags[name]; ok {
if getFlagEnvVar(f) == IgnoreEnvVar {
continue
}
}
if v, ok := m[name]; ok {
ctx.Set(name, fmt.Sprintf("%v", v))
}
}
return nil
}
// getEnvVar generates the environment variable for the given flag name.
func getEnvVar(name string) string {
parts := strings.Split(name, ",")
name = strings.TrimSpace(parts[0])
name = strings.Replace(name, "-", "_", -1)
return "STEP_" + strings.ToUpper(name)
}
// getFlagEnvVar returns the value of the EnvVar field of a flag.
func getFlagEnvVar(f cli.Flag) string {
v := reflect.ValueOf(f)
if v.Kind() == reflect.Ptr {
v = v.Elem()
}
if v.Kind() == reflect.Struct {
envVar := v.FieldByName("EnvVar")
if envVar.IsValid() {
return envVar.String()
}
}
return ""
}
// setEnvVar sets the the EnvVar element to each flag recursively.
func setEnvVar(c *cli.Command) {
if c == nil {
return
}
// Enable getting the flags from a json file
if c.Before == nil && c.Action != nil {
c.Before = getConfigVars
}
// Enable getting the flags from environment variables
for i := range c.Flags {
envVar := getEnvVar(c.Flags[i].GetName())
switch f := c.Flags[i].(type) {
case cli.BoolFlag:
if f.EnvVar == "" {
f.EnvVar = envVar
c.Flags[i] = f
}
case cli.BoolTFlag:
if f.EnvVar == "" {
f.EnvVar = envVar
c.Flags[i] = f
}
case cli.DurationFlag:
if f.EnvVar == "" {
f.EnvVar = envVar
c.Flags[i] = f
}
case cli.Float64Flag:
if f.EnvVar == "" {
f.EnvVar = envVar
c.Flags[i] = f
}
case cli.GenericFlag:
if f.EnvVar == "" {
f.EnvVar = envVar
c.Flags[i] = f
}
case cli.Int64Flag:
if f.EnvVar == "" {
f.EnvVar = envVar
c.Flags[i] = f
}
case cli.IntFlag:
if f.EnvVar == "" {
f.EnvVar = envVar
c.Flags[i] = f
}
case cli.IntSliceFlag:
if f.EnvVar == "" {
f.EnvVar = envVar
c.Flags[i] = f
}
case cli.Int64SliceFlag:
if f.EnvVar == "" {
f.EnvVar = envVar
c.Flags[i] = f
}
case cli.StringFlag:
if f.EnvVar == "" {
f.EnvVar = envVar
c.Flags[i] = f
}
case cli.StringSliceFlag:
if f.EnvVar == "" {
f.EnvVar = envVar
c.Flags[i] = f
}
case cli.Uint64Flag:
if f.EnvVar == "" {
f.EnvVar = envVar
c.Flags[i] = f
}
case cli.UintFlag:
if f.EnvVar == "" {
f.EnvVar = envVar
c.Flags[i] = f
}
}
}
for i := range c.Subcommands {
setEnvVar(&c.Subcommands[i])
}
}