Documentation ¶
Index ¶
- Variables
- func AppEnvAction(c *cli.Context, providers app.Providers, fn AppEnvActionFn) error
- func BlockEnvAction(c *cli.Context, fn BlockEnvActionFn) error
- func CancellableAction(fn func(ctx context.Context) error) error
- func DetectAppVersion(c *cli.Context) string
- func GetApp(c *cli.Context) string
- func GetEnvironment(c *cli.Context) string
- func GetOrg(c *cli.Context, profile config.Profile) string
- func GetProfile(c *cli.Context) string
- func ParseAppEnv(c *cli.Context, isEnvRequired bool, fn ParseAppEnvFn) error
- func ParseBlockEnv(c *cli.Context, fn ParseAppEnvFn) error
- func ProfileAction(c *cli.Context, fn ProfileFn) error
- func SetupProfileCmd(c *cli.Context) (*config.Profile, api.Config, error)
- func WatchAction(ctx context.Context, watchInterval time.Duration, fn func(w io.Writer) error) error
- type AppEnvActionFn
- type AppWorkspaceInfo
- type BlockEnvActionFn
- type ErrMultipleAppsFound
- type NsFinder
- func (f NsFinder) FindAppAndStack(appName, stackName string) (*types.Application, *types.Stack, error)
- func (f NsFinder) FindAppDetails(appName, stackName, envName string) (app.Details, error)
- func (f NsFinder) FindStack(stackName string) (*types.Stack, error)
- func (f NsFinder) GetEnv(stackId int64, envName string) (*types.Environment, error)
- type NsStatus
- type ParseAppEnvFn
- type ParseBlockEnvFn
- type ProfileFn
- type TableBuffer
Constants ¶
This section is empty.
Variables ¶
var ( AddressFlag = &cli.StringFlag{ Name: "address", Value: api.DefaultAddress, Usage: "Nullstone API Address", } ApiKeyFlag = &cli.StringFlag{ Name: "api-key", Value: "", Usage: "Nullstone API Key", } )
var AppFlag = &cli.StringFlag{ Name: "app", Usage: "Set the application name.", EnvVars: []string{"NULLSTONE_APP"}, }
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, }
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.`,
}
var Apps = &cli.Command{ Name: "apps", Usage: "View and modify applications", UsageText: "nullstone apps [subcommand]", Subcommands: []*cli.Command{ AppsList, }, }
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) } 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 := find.Module(cfg, app.ModuleSource); 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 }) }, }
var BlockFlag = &cli.StringFlag{ Name: "block", Usage: "Set the block name.", EnvVars: []string{"NULLSTONE_BLOCK", "NULLSTONE_APP"}, Required: true, }
var Blocks = &cli.Command{ Name: "blocks", Usage: "View and modify blocks", UsageText: "nullstone blocks [subcommand]", Subcommands: []*cli.Command{ BlocksList, BlocksNew, }, }
var BlocksList = &cli.Command{ Name: "list", Usage: "List blocks", UsageText: "nullstone blocks list --stack=<stack>", Flags: []cli.Flag{ StackRequiredFlag, &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} stackName := c.String(StackRequiredFlag.Name) stack, err := client.StacksByName().Get(stackName) if err != nil { return fmt.Errorf("error looking for stack %q: %w", stackName, err) } else if stack == nil { return fmt.Errorf("stack %q does not exist in organization %q", stackName, cfg.OrgName) } allBlocks, err := client.Blocks().List(stack.Id) if err != nil { return fmt.Errorf("error listing blocks: %w", err) } if c.IsSet("detail") { appDetails := make([]string, len(allBlocks)+1) appDetails[0] = "ID|Type|Name|Reference|Category|Module Type|Module|Stack" for i, block := range allBlocks { var blockCategory types.CategoryName var blockType string if blockModule, err := find.Module(cfg, block.ModuleSource); err == nil { blockCategory = blockModule.Category blockType = blockModule.Type } appDetails[i+1] = fmt.Sprintf("%d|%s|%s|%s|%s|%s|%s|%s", block.Id, block.Type, block.Name, block.Reference, blockCategory, blockType, block.ModuleSource, block.StackName) } fmt.Println(columnize.Format(appDetails, columnize.DefaultConfig())) } else { for _, block := range allBlocks { fmt.Println(block.Name) } } return nil }) }, }
var BlocksNew = &cli.Command{ Name: "new", Usage: "Create block", UsageText: "nullstone blocks new --name=<name> --stack=<stack> --module=<module> [--connection=<connection>...]", Flags: []cli.Flag{ StackRequiredFlag, &cli.StringFlag{ Name: "name", Required: true, }, &cli.StringFlag{ Name: "module", Usage: `Specify the unique name of the module to use for this block. Example: nullstone/aws-network`, Required: true, }, &cli.StringSliceFlag{ Name: "connection", Usage: "Map the connection name on the module to the block name in the stack. Example: --connection network=network0", }, }, Action: func(c *cli.Context) error { return ProfileAction(c, func(cfg api.Config) error { client := api.Client{Config: cfg} stackName := c.String(StackRequiredFlag.Name) stack, err := client.StacksByName().Get(stackName) if err != nil { return fmt.Errorf("error looking for stack %q: %w", stackName, err) } else if stack == nil { return fmt.Errorf("stack %q does not exist in organization %q", stackName, cfg.OrgName) } name := c.String("name") moduleSource := c.String("module") if !strings.Contains(moduleSource, "/") { moduleSource = fmt.Sprintf("%s/%s", cfg.OrgName, moduleSource) } connectionSlice := c.StringSlice("connection") module, err := find.Module(cfg, moduleSource) if err != nil { return err } sort.Sort(sort.Reverse(module.Versions)) var latestModuleVersion *types.ModuleVersion if len(module.Versions) > 0 { latestModuleVersion = &module.Versions[0] } connections, parentBlocks, err := mapConnectionsToTargets(cfg, stack, connectionSlice) if err != nil { return err } if err := validateConnections(latestModuleVersion, connections); err != nil { return err } block := &types.Block{ OrgName: cfg.OrgName, StackId: stack.Id, Type: blockTypeFromModuleCategory(module.Category), Name: name, ModuleSource: moduleSource, ModuleSourceVersion: "latest", Connections: connections, ParentBlocks: parentBlocks, } if strings.HasPrefix(string(module.Category), "app/") { app := &types.Application{ Block: *block, Repo: "", Framework: "other", } if newApp, err := client.Apps().Create(stack.Id, app); err != nil { return err } else if newApp != nil { fmt.Printf("created %s app\n", newApp.Name) } else { fmt.Println("unable to create app") } } else { if newBlock, err := client.Blocks().Create(stack.Id, block); err != nil { return err } else if newBlock != nil { fmt.Printf("created %q block\n", newBlock.Name) } else { fmt.Println("unable to create block") } } return nil }) }, }
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 }, }
var Deploy = func(providers app.Providers) *cli.Command { return &cli.Command{ Name: "deploy", Usage: "Deploy application", UsageText: "nullstone deploy [--stack=<stack-name>] --app=<app-name> --env=<env-name> [options]", Flags: []cli.Flag{ StackFlag, AppFlag, OldEnvFlag, 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) }) }, } }
var EnvFlag = &cli.StringFlag{ Name: "env", Usage: `Set the environment name.`, EnvVars: []string{"NULLSTONE_ENV"}, Required: true, }
var EnvOptionalFlag = &cli.StringFlag{ Name: "env", Usage: `Set the environment name.`, EnvVars: []string{"NULLSTONE_ENV"}, Required: false, }
var Envs = &cli.Command{ Name: "envs", Usage: "View and modify environments", UsageText: "nullstone envs [subcommand]", Subcommands: []*cli.Command{ EnvsList, EnvsNew, }, }
var EnvsList = &cli.Command{ Name: "list", Usage: "List environments", UsageText: "nullstone envs list --stack=<stack-name>", Flags: []cli.Flag{ StackRequiredFlag, &cli.BoolFlag{ Name: "detail", Aliases: []string{"d"}, }, }, Action: func(c *cli.Context) error { return ProfileAction(c, func(cfg api.Config) error { stackName := c.String(StackRequiredFlag.Name) 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 }) }, }
var EnvsNew = &cli.Command{ Name: "new", Usage: "Create new environment", UsageText: "nullstone envs new --name=<name> --stack=<stack> --provider=<provider>", Flags: []cli.Flag{ &cli.StringFlag{Name: "name", Required: true}, StackFlag, &cli.StringFlag{Name: "provider", Required: true}, &cli.StringFlag{Name: "region"}, &cli.StringFlag{Name: "zone"}, }, Action: func(c *cli.Context) error { return ProfileAction(c, func(cfg api.Config) error { client := api.Client{Config: cfg} name := c.String("name") stackName := c.String("stack") providerName := c.String("provider") stack, err := client.StacksByName().Get(stackName) if err != nil { return fmt.Errorf("error looking for stack %q: %w", stackName, err) } else if stack == nil { return fmt.Errorf("stack %q does not exist", stackName) } provider, err := client.Providers().Get(providerName) if err != nil { return fmt.Errorf("error looking for provider %q: %w", providerName, err) } else if provider == nil { return fmt.Errorf("provider %q does not exist", providerName) } pc := types.ProviderConfig{} switch provider.ProviderType { case "aws": pc.Aws = &types.AwsProviderConfig{ ProviderName: provider.Name, Region: c.String("region"), } if pc.Aws.Region == "" { pc.Aws.Region = awsDefaultRegion } case "gcp": pc.Gcp = &types.GcpProviderConfig{ ProviderName: provider.Name, Region: c.String("region"), Zone: c.String("zone"), } if pc.Gcp.Region == "" || pc.Gcp.Zone == "" { pc.Gcp.Region = gcpDefaultRegion pc.Gcp.Zone = gcpDefaultZone } default: return fmt.Errorf("CLI does not support provider type %q yet", provider.ProviderType) } env, err := client.Environments().Create(stack.Id, &types.Environment{ Name: name, ProviderConfig: pc, }) if err != nil { return fmt.Errorf("error creating stack: %w", err) } fmt.Printf("created %q environment\n", env.Name) return nil }) }, }
var (
ErrMissingOrg = errors.New("An organization has not been configured with this profile. See 'nullstone set-org -h' for more details.")
)
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 [--stack=<stack-name>] --app=<app-name> --env=<env-name> [options] [command]", Flags: []cli.Flag{ StackFlag, AppFlag, EnvFlag, TaskFlag, }, Action: func(c *cli.Context) error { userConfig := map[string]string{ "task": c.String("task"), "cmd": "/bin/sh", } if c.Args().Len() >= 1 { userConfig["cmd"] = c.Args().Get(c.Args().Len() - 1) } return AppEnvAction(c, providers, func(ctx context.Context, cfg api.Config, provider app.Provider, details app.Details) error { return provider.Exec(ctx, cfg, details, userConfig) }) }, } }
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 [--stack=<stack-name>] --app=<app-name> --env=<env-name> [options]", Flags: []cli.Flag{ StackFlag, AppFlag, OldEnvFlag, 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
var Logs = func(providers app.Providers, logProviders app_logs.Providers) *cli.Command { return &cli.Command{ Name: "logs", Usage: "Emit application logs", UsageText: "nullstone logs [--stack=<stack-name>] --app=<app-name> --env=<env-name> [options]", Flags: []cli.Flag{ StackFlag, AppFlag, OldEnvFlag, &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 { 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") } } return AppEnvAction(c, providers, func(ctx context.Context, cfg api.Config, provider app.Provider, details app.Details) error { logProvider, err := logProviders.Identify(provider.DefaultLogProvider(), cfg, details) if err != nil { return err } return logProvider.Stream(ctx, cfg, details, logStreamOptions) }) }, } }
var Modules = &cli.Command{ Name: "modules", Usage: "View and modify modules", UsageText: "nullstone modules [subcommand]", Subcommands: []*cli.Command{ ModulesGenerate, ModulesNew, ModulesPublish, ModulesPackage, }, }
var ModulesGenerate = &cli.Command{ Name: "generate", Usage: "Generate new module manifest (and optionally register)", UsageText: "nullstone modules generate [--register]", Flags: []cli.Flag{ &cli.BoolFlag{Name: "register"}, }, Action: func(c *cli.Context) error { return ProfileAction(c, func(cfg api.Config) error { existing, _ := modules.ManifestFromFile(moduleManifestFilename) survey := &moduleSurvey{} manifest, err := survey.Ask(cfg, existing) if err != nil { return err } if err := manifest.WriteManifestToFile(moduleManifestFilename); err != nil { return err } fmt.Printf("generated module manifest file to %s\n", moduleManifestFilename) if err := modules.Generate(manifest); err != nil { return err } fmt.Printf("generated base Terraform\n") if c.IsSet("register") { module, err := modules.Register(cfg, manifest) if err != nil { return err } fmt.Printf("registered %s/%s\n", module.OrgName, module.Name) } return nil }) }, }
var ModulesNew = &cli.Command{ Name: "new", Usage: "Create new module from .nullstone/module.yml", UsageText: "nullstone modules new", Flags: []cli.Flag{}, Action: func(c *cli.Context) error { return ProfileAction(c, func(cfg api.Config) error { manifest, err := modules.ManifestFromFile(moduleManifestFilename) if err != nil { return err } module, err := modules.Register(cfg, manifest) if err != nil { return err } fmt.Printf("registered %s/%s\n", module.OrgName, module.Name) return nil }) }, }
var ModulesPackage = &cli.Command{ Name: "package", Usage: "Package a module", UsageText: "nullstone modules package", Flags: []cli.Flag{}, Action: func(c *cli.Context) error { manifest, err := modules.ManifestFromFile(moduleManifestFilename) if err != nil { return err } tarballFilename, err := modules.Package(manifest, "") if err == nil { fmt.Printf("created module package %q\n", tarballFilename) } return err }, }
var ModulesPublish = &cli.Command{ Name: "publish", Usage: "Package and publish new version of a module", UsageText: "nullstone modules publish --version=<version>", Flags: []cli.Flag{ &cli.StringFlag{ Name: "version", Aliases: []string{"v"}, Usage: "Specify a semver version for the module", Required: true, }, }, Action: func(c *cli.Context) error { return ProfileAction(c, func(cfg api.Config) error { version := c.String("version") if !strings.HasPrefix(version, "v") { version = "v" + version } if isValid := semver.IsValid(version); !isValid { return fmt.Errorf("version %q is not a valid semver", version) } manifest, err := modules.ManifestFromFile(moduleManifestFilename) if err != nil { return err } tarballFilename, err := modules.Package(manifest, version) if err != nil { return err } fmt.Printf("created module package %q\n", tarballFilename) tarball, err := os.Open(tarballFilename) if err != nil { return err } defer tarball.Close() client := api.Client{Config: cfg} if err := client.Org(manifest.OrgName).ModuleVersions().Create(manifest.Name, version, tarball); err != nil { return err } fmt.Printf("published %s/%s@%s\n", manifest.OrgName, manifest.Name, version) return nil }) }, }
var OldEnvFlag = &cli.StringFlag{ Name: "env", Usage: `Set the environment name.`, EnvVars: []string{"NULLSTONE_ENV"}, }
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
var Outputs = func() *cli.Command { return &cli.Command{ Name: "outputs", Usage: "Retrieve outputs", UsageText: "nullstone outputs [--stack=<stack-name>] --block=<block-name> --env=<env-name> [options]", Flags: []cli.Flag{ StackFlag, BlockFlag, EnvFlag, }, Action: func(c *cli.Context) error { return BlockEnvAction(c, func(ctx context.Context, cfg api.Config, stack types.Stack, block types.Block, env types.Environment) error { client := api.Client{Config: cfg} outputs, err := client.WorkspaceOutputs().GetLatest(stack.Id, block.Id, env.Id) if err != nil { return err } else if outputs == nil { outputs = &types.Outputs{} } stripped := map[string]interface{}{} for key, output := range *outputs { stripped[key] = output.Value } encoder := json.NewEncoder(os.Stdout) encoder.SetIndent("", " ") encoder.Encode(stripped) return nil }) }, } }
Outputs command retrieves outputs from a workspace (block+env)
var ProfileFlag = &cli.StringFlag{ Name: "profile", EnvVars: []string{"NULLSTONE_PROFILE"}, Value: "default", Usage: "Name of profile", }
var Push = func(providers app.Providers) *cli.Command { return &cli.Command{ Name: "push", Usage: "Push artifact", UsageText: "nullstone push [--stack=<stack-name>] --app=<app-name> --env=<env-name> [options]", Flags: []cli.Flag{ StackFlag, AppFlag, OldEnvFlag, 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
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 }, }
var StackFlag = &cli.StringFlag{ Name: "stack", Usage: `Set the stack name that owns the app/block. This is only required if multiple apps/blocks have the same name.`, EnvVars: []string{"NULLSTONE_STACK"}, }
var StackRequiredFlag = &cli.StringFlag{ Name: "stack", Usage: `Set the stack name that owns the app/block. This is only required if multiple apps/blocks have the same name.`, EnvVars: []string{"NULLSTONE_STACK"}, Required: true, }
var Stacks = &cli.Command{ Name: "stacks", Usage: "View and modify stacks", UsageText: "nullstone stacks [subcommand]", Subcommands: []*cli.Command{ StacksList, StacksNew, }, }
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 }) }, }
var StacksNew = &cli.Command{ Name: "new", Usage: "Create new stack", UsageText: "nullstone stacks new --name=<name> --description=<description>", Flags: []cli.Flag{ &cli.StringFlag{Name: "name", Required: true}, &cli.StringFlag{Name: "description", Required: true}, }, Action: func(c *cli.Context) error { return ProfileAction(c, func(cfg api.Config) error { client := api.Client{Config: cfg} name := c.String("name") description := c.String("description") stack, err := client.Stacks().Create(&types.Stack{ Name: name, Description: description, }) if err != nil { return fmt.Errorf("error creating stack: %w", err) } fmt.Printf("created %q stack\n", stack.Name) return nil }) }, }
var Status = func(providers app.Providers) *cli.Command { return &cli.Command{ Name: "status", Usage: "Application Status", UsageText: "nullstone status [--stack=<stack-name>] --app=<app-name> [--env=<env-name>] [options]", Flags: []cli.Flag{ StackFlag, AppFlag, EnvOptionalFlag, AppVersionFlag, &cli.BoolFlag{ Name: "watch", Aliases: []string{"w"}, }, }, Action: func(c *cli.Context) error { watchInterval := -1 * time.Second if c.IsSet("watch") { watchInterval = defaultWatchInterval } return ProfileAction(c, func(cfg api.Config) error { return ParseAppEnv(c, false, func(stackName, appName, envName string) error { return CancellableAction(func(ctx context.Context) error { if envName == "" { return appStatus(ctx, cfg, providers, watchInterval, stackName, appName) } else { return appEnvStatus(ctx, cfg, providers, watchInterval, stackName, appName, envName) } }) }) }) }, } }
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.`,
}
var Workspaces = &cli.Command{ Name: "workspaces", Usage: "View and modify workspaces", UsageText: "nullstone workspaces [subcommand]", Subcommands: []*cli.Command{ WorkspacesSelect, }, }
var WorkspacesSelect = &cli.Command{ Name: "select", Usage: "Select workspace", UsageText: "nullstone workspaces select [--stack=<stack>] --block=<block> --env=<env>", Flags: []cli.Flag{ StackFlag, &cli.StringFlag{ Name: "block", Required: true, }, &cli.StringFlag{ Name: "env", Required: true, }, }, Action: func(c *cli.Context) error { return ProfileAction(c, func(cfg api.Config) error { if !tfconfig.IsCredsConfigured(cfg) { if err := tfconfig.ConfigCreds(cfg); err != nil { fmt.Printf("Warning: unable to configure Terraform-based credentials with Nullstone servers: %s\n", err) } else { fmt.Println("Configured Terraform-based credentials with Nullstone servers.") } } client := api.Client{Config: cfg} stackName := c.String("stack") blockName := c.String("block") envName := c.String("env") sbe, err := find.StackBlockEnvByName(cfg, stackName, blockName, envName) if err != nil { return err } targetWorkspace := workspaces.Manifest{ OrgName: cfg.OrgName, StackId: sbe.Stack.Id, StackName: sbe.Stack.Name, BlockId: sbe.Block.Id, BlockName: sbe.Block.Name, BlockRef: sbe.Block.Reference, EnvId: sbe.Env.Id, EnvName: sbe.Env.Name, Connections: workspaces.ManifestConnections{}, } workspace, err := client.Workspaces().Get(targetWorkspace.StackId, targetWorkspace.BlockId, targetWorkspace.EnvId) if err != nil { return err } else if workspace == nil { return fmt.Errorf("could not find workspace (stack=%s, block=%s, env=%s)", stackName, blockName, envName) } targetWorkspace.WorkspaceUid = workspace.Uid.String() runConfig, err := workspaces.GetRunConfig(cfg, targetWorkspace) if err != nil { return fmt.Errorf("could not retreive current workspace configuration: %w", err) } manualConnections, err := surveyMissingConnections(cfg, targetWorkspace.StackName, runConfig) if err != nil { return err } for name, conn := range manualConnections { targetWorkspace.Connections[name] = workspaces.ManifestConnectionTarget{ StackId: conn.Reference.StackId, BlockId: conn.Reference.BlockId, BlockName: conn.Reference.BlockName, EnvId: conn.Reference.EnvId, } } return CancellableAction(func(ctx context.Context) error { return workspaces.Select(ctx, cfg, targetWorkspace, runConfig) }) }) }, }
Functions ¶
func AppEnvAction ¶ added in v0.0.26
func AppEnvAction(c *cli.Context, providers app.Providers, fn AppEnvActionFn) error
func BlockEnvAction ¶ added in v0.0.43
func BlockEnvAction(c *cli.Context, fn BlockEnvActionFn) error
func CancellableAction ¶ added in v0.0.26
func DetectAppVersion ¶ added in v0.0.28
func DetectAppVersion(c *cli.Context) string
func GetEnvironment ¶ added in v0.0.43
func GetEnvironment(c *cli.Context) string
func GetProfile ¶ added in v0.0.4
func GetProfile(c *cli.Context) string
func ParseAppEnv ¶ added in v0.0.43
func ParseAppEnv(c *cli.Context, isEnvRequired bool, fn ParseAppEnvFn) error
func ParseBlockEnv ¶ added in v0.0.43
func ParseBlockEnv(c *cli.Context, fn ParseAppEnvFn) error
func ProfileAction ¶ added in v0.0.26
func SetupProfileCmd ¶ added in v0.0.7
Types ¶
type AppEnvActionFn ¶ added in v0.0.26
type AppWorkspaceInfo ¶ added in v0.0.26
type BlockEnvActionFn ¶ added in v0.0.43
type ErrMultipleAppsFound ¶ added in v0.0.11
func (ErrMultipleAppsFound) Error ¶ added in v0.0.11
func (e ErrMultipleAppsFound) Error() string
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 (NsFinder) FindAppDetails ¶ added in v0.0.26
FindAppDetails retrieves the app, env, and workspace stackName is optional -- If multiple apps are found, this will return an 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 ParseAppEnvFn ¶ added in v0.0.43
type ParseBlockEnvFn ¶ added in v0.0.43
type TableBuffer ¶ added in v0.0.26
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
Source Files ¶
- app_env_action.go
- app_env_context.go
- apps.go
- block_env_action.go
- block_env_context.go
- blocks.go
- cancellable_action.go
- configure.go
- deploy.go
- envs.go
- exec.go
- flag_app.go
- flag_app_source.go
- flag_app_version.go
- flag_block.go
- flag_env.go
- flag_org.go
- flag_profile.go
- flag_stack.go
- flag_task.go
- get_commit_sha.go
- launch.go
- logs.go
- module_survey.go
- modules.go
- ns_finder.go
- ns_status.go
- outputs.go
- profile_action.go
- push.go
- set_org.go
- setup_profile_cmd.go
- stacks.go
- status.go
- table_buffer.go
- watch_action.go
- workspaces.go