cmd

package
v0.0.42 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 28, 2022 License: MIT Imports: 23 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	AddressFlag = &cli.StringFlag{
		Name:  "address",
		Value: api.DefaultAddress,
		Usage: "Nullstone API Address",
	}
	ApiKeyFlag = &cli.StringFlag{
		Name:  "api-key",
		Value: "",
		Usage: "Nullstone API Key",
	}
)
View Source
var AppSourceFlag = &cli.StringFlag{
	Name: "source",
	Usage: `The source artifact to push.
       app/container: This is the docker image to push. This follows the same syntax as 'docker push NAME[:TAG]'.
       app/serverless: This is a .zip archive to push.`,
	Required: true,
}
View Source
var AppVersionFlag = &cli.StringFlag{
	Name: "version",
	Usage: `Push/Deploy the artifact with this version.
       If not specified, will retrieve short sha from your latest commit.
       app/container: If specified, will push the docker image with version as the image tag. Otherwise, uses source tag.
       app/serverless: This is required to upload the artifact.`,
}
View Source
var Apps = &cli.Command{
	Name:      "apps",
	Usage:     "View and modify applications",
	UsageText: "nullstone apps [subcommand]",
	Subcommands: []*cli.Command{
		AppsList,
	},
}
View Source
var AppsList = &cli.Command{
	Name:      "list",
	Usage:     "List applications",
	UsageText: "nullstone apps list",
	Flags: []cli.Flag{
		&cli.BoolFlag{
			Name:    "detail",
			Aliases: []string{"d"},
		},
	},
	Action: func(c *cli.Context) error {
		return ProfileAction(c, func(cfg api.Config) error {
			client := api.Client{Config: cfg}
			allApps, err := client.Apps().List()
			if err != nil {
				return fmt.Errorf("error listing applications: %w", err)
			}

			finder := NsFinder{Config: cfg}

			if c.IsSet("detail") {
				appDetails := make([]string, len(allApps)+1)
				appDetails[0] = "ID|Name|Reference|Category|Type|Module|Stack|Framework"
				for i, app := range allApps {
					var appCategory types.CategoryName
					var appType string
					if appModule, err := finder.GetAppModule(app); err == nil {
						appCategory = appModule.Category
						appType = appModule.Type
					}
					appDetails[i+1] = fmt.Sprintf("%d|%s|%s|%s|%s|%s|%s|%s", app.Id, app.Name, app.Reference, appCategory, appType, app.ModuleSource, app.StackName, app.Framework)
				}
				fmt.Println(columnize.Format(appDetails, columnize.DefaultConfig()))
			} else {
				for _, app := range allApps {
					fmt.Println(app.Name)
				}
			}

			return nil
		})
	},
}
View Source
var Configure = &cli.Command{
	Name: "configure",
	Flags: []cli.Flag{
		AddressFlag,
		ApiKeyFlag,
	},
	Action: func(c *cli.Context) error {
		apiKey := c.String(ApiKeyFlag.Name)
		if apiKey == "" {
			fmt.Print("Enter API Key: ")
			rawApiKey, err := terminal.ReadPassword(int(syscall.Stdin))
			if err != nil {
				return fmt.Errorf("error reading password: %w", err)
			}
			fmt.Println()
			apiKey = string(rawApiKey)
		}

		profile := config.Profile{
			Name:    GetProfile(c),
			Address: c.String(AddressFlag.Name),
			ApiKey:  apiKey,
		}
		if err := profile.Save(); err != nil {
			return fmt.Errorf("error configuring profile: %w", err)
		}
		fmt.Fprintln(os.Stderr, "nullstone configured successfully!")
		return nil
	},
}
View Source
var Deploy = func(providers app.Providers) *cli.Command {
	return &cli.Command{
		Name:      "deploy",
		Usage:     "Deploy application",
		UsageText: "nullstone deploy [options] <app-name> <env-name>",
		Flags: []cli.Flag{
			StackFlag,
			AppVersionFlag,
		},
		Action: func(c *cli.Context) error {
			return AppEnvAction(c, providers, func(ctx context.Context, cfg api.Config, provider app.Provider, details app.Details) error {
				userConfig := map[string]string{
					"version": DetectAppVersion(c),
				}
				return provider.Deploy(cfg, details, userConfig)
			})
		},
	}
}
View Source
var Envs = &cli.Command{
	Name:      "envs",
	Usage:     "View and modify environments",
	UsageText: "nullstone envs [subcommand]",
	Subcommands: []*cli.Command{
		EnvsList,
	},
}
View Source
var EnvsList = &cli.Command{
	Name:      "list",
	Usage:     "List environments",
	UsageText: "nullstone envs list <stack-name>",
	Flags: []cli.Flag{
		&cli.BoolFlag{
			Name:    "detail",
			Aliases: []string{"d"},
		},
	},
	Action: func(c *cli.Context) error {
		return ProfileAction(c, func(cfg api.Config) error {
			if c.NArg() != 1 {
				cli.ShowCommandHelp(c, c.Command.Name)
				return fmt.Errorf("stack-name is required to list environments")
			}
			stackName := c.Args().Get(0)

			finder := NsFinder{Config: cfg}
			stack, err := finder.FindStack(stackName)
			if err != nil {
				return fmt.Errorf("error retrieving stack: %w", err)
			} else if stack == nil {
				return fmt.Errorf("stack %s does not exist", stackName)
			}

			client := api.Client{Config: cfg}
			envs, err := client.Environments().List(stack.Id)
			if err != nil {
				return fmt.Errorf("error listing environments: %w", err)
			}
			sort.SliceStable(envs, func(i, j int) bool {
				return envs[i].PipelineOrder < envs[i].PipelineOrder
			})

			if c.IsSet("detail") {
				envDetails := make([]string, len(envs)+1)
				envDetails[0] = "ID|Name"
				for i, env := range envs {
					envDetails[i+1] = fmt.Sprintf("%d|%s", env.Id, env.Name)
				}
				fmt.Println(columnize.Format(envDetails, columnize.DefaultConfig()))
			} else {
				for _, env := range envs {
					fmt.Println(env.Name)
				}
			}

			return nil
		})
	},
}
View Source
var ErrInvalidModuleSource = errors.New("invalid module source")
View Source
var (
	ErrMissingOrg = errors.New("An organization has not been configured with this profile. See 'nullstone set-org -h' for more details.")
)
View Source
var Exec = func(providers app.Providers) *cli.Command {
	return &cli.Command{
		Name:      "exec",
		Usage:     "Execute command on running service. Defaults command to '/bin/sh' which acts as opening a shell to the running container.",
		UsageText: "nullstone exec [options] <app-name> <env-name> [command]",
		Flags: []cli.Flag{
			StackFlag,
			TaskFlag,
		},
		Action: func(c *cli.Context) error {
			return AppEnvAction(c, providers, func(ctx context.Context, cfg api.Config, provider app.Provider, details app.Details) error {
				userConfig := map[string]string{
					"task": c.String("task"),
					"cmd":  "/bin/sh",
				}
				if c.Args().Len() > 2 {
					userConfig["cmd"] = c.Args().Get(2)
				}
				return provider.Exec(cfg, details, userConfig)
			})
		},
	}
}
View Source
var Launch = func(providers app.Providers, logProviders app_logs.Providers) *cli.Command {
	return &cli.Command{
		Name:      "launch",
		Usage:     "Launch application (push, deploy, log)",
		UsageText: "nullstone launch [options] <app-name> <env-name>",
		Flags: []cli.Flag{
			StackFlag,
			AppSourceFlag,
			AppVersionFlag,
		},
		Action: func(c *cli.Context) error {
			return AppEnvAction(c, providers, func(ctx context.Context, cfg api.Config, provider app.Provider, details app.Details) error {
				logger := log.New(os.Stderr, "", 0)

				userConfig := map[string]string{
					"source":  c.String("source"),
					"version": DetectAppVersion(c),
				}

				logger.Println("Pushing app artifact...")
				if err := provider.Push(cfg, details, userConfig); err != nil {
					return fmt.Errorf("error pushing artifact: %w", err)
				}
				logger.Println()

				logger.Println("Deploying application...")
				if err := provider.Deploy(cfg, details, userConfig); err != nil {
					return fmt.Errorf("error deploying app: %w", err)
				}
				logger.Println()

				logger.Println("Tailing application logs...")
				logProvider, err := logProviders.Identify(provider.DefaultLogProvider(), cfg, details)
				if err != nil {
					return err
				}
				now := time.Now()
				return logProvider.Stream(ctx, cfg, details, config.LogStreamOptions{StartTime: &now, Out: os.Stdout})
			})
		},
	}
}

Launch command performs push, deploy, and logs

View Source
var Logs = func(providers app.Providers, logProviders app_logs.Providers) *cli.Command {
	return &cli.Command{
		Name:      "logs",
		Usage:     "Emit application logs",
		UsageText: "nullstone logs [options] <app-name> <env-name>",
		Flags: []cli.Flag{
			StackFlag,
			&cli.DurationFlag{
				Name:        "start-time",
				Aliases:     []string{"s"},
				DefaultText: "0s",
				Usage: `
       Emit log events that occur after the specified start-time. 
       This is a golang duration relative to the time the command is issued.
       Examples: '5s' (5 seconds ago), '1m' (1 minute ago), '24h' (24 hours ago)
      `,
			},
			&cli.DurationFlag{
				Name:    "end-time",
				Aliases: []string{"e"},
				Usage: `
       Emit log events that occur before the specified end-time. 
       This is a golang duration relative to the time the command is issued.
       Examples: '5s' (5 seconds ago), '1m' (1 minute ago), '24h' (24 hours ago)
      `,
			},
			&cli.DurationFlag{
				Name:        "interval",
				DefaultText: "1s",
				Usage: `Set --interval to a golang duration to control how often to pull new log events.
       This will do nothing unless --tail is set.
      `,
			},
			&cli.BoolFlag{
				Name:    "tail",
				Aliases: []string{"t"},
				Usage: `Set tail to watch log events and emit as they are reported.
       Use --interval to control how often to query log events.
       This is off by default, command will exit as soon as current log events are emitted.`,
			},
		},
		Action: func(c *cli.Context) error {
			return AppEnvAction(c, providers, func(ctx context.Context, cfg api.Config, provider app.Provider, details app.Details) error {
				logStreamOptions := config.LogStreamOptions{
					WatchInterval: -1 * time.Second,
					Out:           os.Stdout,
				}
				if c.IsSet("start-time") {
					absoluteTime := time.Now().Add(-c.Duration("start-time"))
					logStreamOptions.StartTime = &absoluteTime
				}
				if c.IsSet("end-time") {
					absoluteTime := time.Now().Add(-c.Duration("end-time"))
					logStreamOptions.EndTime = &absoluteTime
				}
				if c.IsSet("tail") {
					logStreamOptions.WatchInterval = time.Duration(0)
					if c.IsSet("interval") {
						logStreamOptions.WatchInterval = c.Duration("interval")
					}
				}

				logProvider, err := logProviders.Identify(provider.DefaultLogProvider(), cfg, details)
				if err != nil {
					return err
				}
				return logProvider.Stream(ctx, cfg, details, logStreamOptions)
			})
		},
	}
}
View Source
var OrgFlag = &cli.StringFlag{
	Name:    "org",
	EnvVars: []string{"NULLSTONE_ORG"},
	Usage:   `Nullstone organization name used to contextualize API calls. If this flag is not specified, the nullstone CLI will use ~/.nullstone/<profile>/org file.`,
}

OrgFlag defines a flag that the CLI uses

to contextualize API calls by that organization within Nullstone

The organization takes the following precedence:

`--org` flag
`NULLSTONE_ORG` env var
`~/.nullstone/<profile>/org` file
View Source
var ProfileFlag = &cli.StringFlag{
	Name:    "profile",
	EnvVars: []string{"NULLSTONE_PROFILE"},
	Value:   "default",
	Usage:   "Name of profile",
}
View Source
var Push = func(providers app.Providers) *cli.Command {
	return &cli.Command{
		Name:      "push",
		Usage:     "Push artifact",
		UsageText: "nullstone push [options] <app-name> <env-name>",
		Flags: []cli.Flag{
			StackFlag,
			AppSourceFlag,
			AppVersionFlag,
		},
		Action: func(c *cli.Context) error {
			return AppEnvAction(c, providers, func(ctx context.Context, cfg api.Config, provider app.Provider, details app.Details) error {
				userConfig := map[string]string{
					"source":  c.String("source"),
					"version": DetectAppVersion(c),
				}
				return provider.Push(cfg, details, userConfig)
			})
		},
	}
}

Push command performs a docker push to an authenticated image registry configured against an app/container

View Source
var SetOrg = &cli.Command{
	Name:  "set-org",
	Usage: "Set the organization for the CLI",
	UsageText: `Most Nullstone CLI commands require a configured nullstone organization to operate.
   This command will set the organization for the current profile.
   If you wish to set the organization per command, use the global --org flag instead.`,
	Flags: []cli.Flag{},
	Action: func(c *cli.Context) error {
		profile, err := config.LoadProfile(GetProfile(c))
		if err != nil {
			return err
		}

		if c.NArg() != 1 {
			return cli.ShowCommandHelp(c, "set-org")
		}

		orgName := c.Args().Get(0)
		if err := profile.SaveOrg(orgName); err != nil {
			return err
		}
		fmt.Fprintf(os.Stderr, "Organization set to %s for %s profile\n", orgName, profile.Name)
		return nil
	},
}
View Source
var StackFlag = &cli.StringFlag{
	Name: "stack",
	Usage: `The stack name where the app resides.
       This is only required if multiple apps have the same 'app-name'.`,
}
View Source
var Stacks = &cli.Command{
	Name:      "stacks",
	Usage:     "View and modify stacks",
	UsageText: "nullstone stacks [subcommand]",
	Subcommands: []*cli.Command{
		StacksList,
	},
}
View Source
var StacksList = &cli.Command{
	Name:      "list",
	Usage:     "List stacks",
	UsageText: "nullstone stacks list",
	Flags: []cli.Flag{
		&cli.BoolFlag{
			Name:    "detail",
			Aliases: []string{"d"},
		},
	},
	Action: func(c *cli.Context) error {
		return ProfileAction(c, func(cfg api.Config) error {
			client := api.Client{Config: cfg}
			allStacks, err := client.Stacks().List()
			if err != nil {
				return fmt.Errorf("error listing stacks: %w", err)
			}

			if c.IsSet("detail") {
				stackDetails := make([]string, len(allStacks)+1)
				stackDetails[0] = "ID|Name|Description"
				for i, stack := range allStacks {
					stackDetails[i+1] = fmt.Sprintf("%d|%s|%s", stack.Id, stack.Name, stack.Description)
				}
				fmt.Println(columnize.Format(stackDetails, columnize.DefaultConfig()))
			} else {
				for _, stack := range allStacks {
					fmt.Println(stack.Name)
				}
			}

			return nil
		})
	},
}
View Source
var Status = func(providers app.Providers) *cli.Command {
	return &cli.Command{
		Name:      "status",
		Usage:     "Application Status",
		UsageText: "nullstone status [options] <app-name> [<env-name>]",
		Flags: []cli.Flag{
			StackFlag,
			AppVersionFlag,
			&cli.BoolFlag{
				Name:    "watch",
				Aliases: []string{"w"},
			},
		},
		Action: func(c *cli.Context) error {
			return ProfileAction(c, func(cfg api.Config) error {
				return CancellableAction(func(ctx context.Context) error {
					var appName, envName string
					stackName := c.String("stack-name")

					watchInterval := -1 * time.Second
					if c.IsSet("watch") {
						watchInterval = defaultWatchInterval
					}

					switch c.NArg() {
					case 1:
						appName = c.Args().Get(0)
						return appStatus(ctx, cfg, providers, watchInterval, stackName, appName)
					case 2:
						appName = c.Args().Get(0)
						envName = c.Args().Get(1)
						return appEnvStatus(ctx, cfg, providers, watchInterval, stackName, appName, envName)
					default:
						cli.ShowCommandHelp(c, c.Command.Name)
						return fmt.Errorf("invalid usage")
					}
				})
			})
		},
	}
}
View Source
var TaskFlag = &cli.StringFlag{
	Name: "task",
	Usage: `Optionally, specify the task/replica to execute the command against.
       If not specified, this will connect to a random task/replica.
       If using Kubernetes, this will select which replica of the pod to connect.
       If using ECS, this will select which task of the service to connect.`,
}

Functions

func AppEnvAction added in v0.0.26

func AppEnvAction(c *cli.Context, providers app.Providers, fn AppEnvActionFn) error

func CancellableAction added in v0.0.26

func CancellableAction(fn func(ctx context.Context) error) error

func DetectAppVersion added in v0.0.28

func DetectAppVersion(c *cli.Context) string

func GetOrg added in v0.0.4

func GetOrg(c *cli.Context, profile config.Profile) string

func GetProfile added in v0.0.4

func GetProfile(c *cli.Context) string

func ProfileAction added in v0.0.26

func ProfileAction(c *cli.Context, fn ProfileFn) error

func SetupProfileCmd added in v0.0.7

func SetupProfileCmd(c *cli.Context) (*config.Profile, api.Config, error)

func WatchAction added in v0.0.26

func WatchAction(ctx context.Context, watchInterval time.Duration, fn func(w io.Writer) error) error

Types

type AppEnvActionFn added in v0.0.26

type AppEnvActionFn func(ctx context.Context, cfg api.Config, provider app.Provider, details app.Details) error

type AppWorkspaceInfo added in v0.0.26

type AppWorkspaceInfo struct {
	AppDetails app.Details
	Status     string
	Version    string
}

type ErrMultipleAppsFound added in v0.0.11

type ErrMultipleAppsFound struct {
	AppName    string
	StackNames []string
}

func (ErrMultipleAppsFound) Error added in v0.0.11

func (e ErrMultipleAppsFound) Error() string

type ModuleSource added in v0.0.23

type ModuleSource struct {
	Host       string
	OrgName    string
	ModuleName string
}

func ParseSource added in v0.0.23

func ParseSource(source string) (*ModuleSource, error)

type NsFinder added in v0.0.11

type NsFinder struct {
	Config api.Config
}

NsFinder is an object that provides a consistent querying approach for nullstone objects through the CLI It provides nice error messages that are tailored for the user flow of CLI commands

func (NsFinder) FindAppAndStack added in v0.0.26

func (f NsFinder) FindAppAndStack(appName, stackName string) (*types.Application, *types.Stack, error)

func (NsFinder) FindAppDetails added in v0.0.26

func (f NsFinder) FindAppDetails(appName, stackName, envName string) (app.Details, error)

FindAppDetails retrieves the app, env, and workspace stackName is optional -- If multiple apps are found, this will return an error

func (NsFinder) FindStack added in v0.0.26

func (f NsFinder) FindStack(stackName string) (*types.Stack, error)

func (NsFinder) GetAppModule added in v0.0.23

func (f NsFinder) GetAppModule(app types.Application) (*types.Module, error)

func (NsFinder) GetEnv added in v0.0.20

func (f NsFinder) GetEnv(stackId int64, envName string) (*types.Environment, error)

type NsStatus added in v0.0.26

type NsStatus struct {
	Config api.Config
}

func (NsStatus) GetAppWorkspaceInfo added in v0.0.26

func (s NsStatus) GetAppWorkspaceInfo(application *types.Application, env *types.Environment) (AppWorkspaceInfo, error)

type ProfileFn added in v0.0.26

type ProfileFn func(cfg api.Config) error

type TableBuffer added in v0.0.26

type TableBuffer struct {
	Fields   []string
	HasField map[string]bool
	Data     []map[string]interface{}
}

TableBuffer builds a table of data to display on the terminal The TableBuffer guarantees safe merging of rows with potentially different field names Example: If a user is migrating an app from container to serverless,

it's possible that the infrastructure has not fully propagated

func (*TableBuffer) AddFields added in v0.0.26

func (b *TableBuffer) AddFields(fields ...string)

func (*TableBuffer) AddRow added in v0.0.26

func (b *TableBuffer) AddRow(data map[string]interface{})

func (*TableBuffer) String added in v0.0.26

func (b *TableBuffer) String() string

func (*TableBuffer) Values added in v0.0.26

func (b *TableBuffer) Values() [][]string

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL