abra/cli/app.go
decentral1se ef1591d596
WIP: app status listing using concurrency
This being my first time using goroutines, it is pretty messy but the
idea has been shown to be workable! We can concurrently look up multiple
contexts for a much faster response time especially when using multiple
servers.

Remaining TODOs are:

- [ ] Get proper status reporting (deployed/inactive/unknown)
- [ ] Error handling (especially when missing contexts)
- [ ] Refactor and tidy
2021-07-27 12:52:09 +02:00

220 lines
4.8 KiB
Go

package cli
import (
"context"
"fmt"
"sort"
"strings"
"coopcloud.tech/abra/client"
"coopcloud.tech/abra/config"
"github.com/docker/cli/cli/command/formatter"
"github.com/docker/cli/cli/command/idresolver"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/swarm"
"github.com/schultz-is/passgen"
"github.com/sirupsen/logrus"
"github.com/urfave/cli/v2"
)
var appNewCommand = &cli.Command{
Name: "new",
Flags: []cli.Flag{
ServerFlag,
DomainFlag,
AppNameFlag,
PassFlag,
SecretsFlag,
},
}
var appDeployCommand = &cli.Command{
Name: "deploy",
Flags: []cli.Flag{
UpdateFlag,
ForceFlag,
SkipVersionCheckFlag,
NoDomainPollFlag,
},
}
var appUndeployCommand = &cli.Command{
Name: "undeploy",
}
var appBackupCommand = &cli.Command{
Name: "backup",
Flags: []cli.Flag{AllFlag},
}
var appRestoreCommand = &cli.Command{
Name: "restore",
Flags: []cli.Flag{AllFlag},
ArgsUsage: "<service> [<backup file>]",
}
var appListCommand = &cli.Command{
Name: "list",
Aliases: []string{"ls"},
Flags: []cli.Flag{StatusFlag, ServerFlag, TypeFlag},
Action: func(c *cli.Context) error {
appFiles, err := config.LoadAppFiles(Server)
if err != nil {
logrus.Fatal(err)
}
apps, err := config.GetApps(appFiles)
sort.Sort(config.ByServerAndType(apps))
statuses := map[string]string{}
tableCol := []string{"Server", "Type", "Domain"}
if Status {
tableCol = append(tableCol, "Status")
statuses, err = config.GetAppStatuses(appFiles)
if err != nil {
logrus.Fatal(err)
}
}
table := createTable(tableCol)
table.SetAutoMergeCellsByColumnIndex([]int{0})
for _, app := range apps {
var tableRow []string
if app.Type == Type || Type == "" {
// If type flag is set, check for it, if not, Type == ""
tableRow = []string{app.File.Server, app.Type, app.Domain}
if Status {
stackName := strings.ReplaceAll(app.Name, ".", "_")
if status, ok := statuses[stackName]; ok {
tableRow = append(tableRow, status)
} else {
tableRow = append(tableRow, "unknown")
}
}
}
table.Append(tableRow)
}
table.Render()
return nil
},
}
var appCheckCommand = &cli.Command{
Name: "check",
}
var appCpCommand = &cli.Command{
Name: "cp",
ArgsUsage: "<src> <dst>",
}
var appConfigCommand = &cli.Command{
Name: "config",
}
var appLogsCommand = &cli.Command{
Name: "logs",
ArgsUsage: "[<service>]",
}
var appPsCommand = &cli.Command{
Name: "ps",
Action: func(c *cli.Context) error {
ctx := context.Background()
cl, err := client.NewClientWithContext(Context)
if err != nil {
logrus.Fatal(err)
}
tasks, err := cl.TaskList(ctx, types.TaskListOptions{})
if err != nil {
logrus.Fatal(err)
}
for _, task := range tasks {
resolver := idresolver.New(cl, false)
serviceName, err := resolver.Resolve(ctx, swarm.Service{}, task.ServiceID)
if err != nil {
return err
}
fmt.Printf("%#v\n", serviceName)
}
containers, err := cl.ContainerList(ctx, types.ContainerListOptions{})
if err != nil {
logrus.Fatal(err)
}
table := createTable([]string{"ID", "Image", "Command", "Created", "Status", "Ports", "Names"})
var conTable [][]string
for _, container := range containers {
conRow := []string{
shortenID(container.ID),
removeSha(container.Image),
truncate(container.Command),
humanDuration(container.Created),
container.Status,
formatter.DisplayablePorts(container.Ports),
strings.Join(container.Names, ","),
}
conTable = append(conTable, conRow)
}
table.AppendBulk(conTable)
table.Render()
return nil
},
}
var appRemoveCommand = &cli.Command{
Name: "remove",
Flags: []cli.Flag{VolumesFlag, SecretsFlag},
}
var appRunCommand = &cli.Command{
Name: "run",
Flags: []cli.Flag{
NoTTYFlag,
UserFlag,
},
ArgsUsage: "<service> <args>...",
}
var appRollbackCommand = &cli.Command{
Name: "rollback",
ArgsUsage: "[<version>]",
}
// TODO: Replicating what the bash abra does might be hard
// with the mix of subcommands and flags
var appSecretCommand = &cli.Command{
Name: "secret",
Flags: []cli.Flag{AllFlag, PassFlag},
Action: func(c *cli.Context) error {
passwords, err := passgen.GeneratePassphrases(
1,
passgen.PassphraseWordCountDefault,
rune('-'),
passgen.PassphraseCasingDefault,
passgen.WordListDefault,
)
if err != nil {
logrus.Fatal(err)
}
for _, password := range passwords {
fmt.Println(password)
}
return nil
},
}
var AppCommand = &cli.Command{
Name: "app",
Usage: "Manage your apps",
Subcommands: []*cli.Command{
appNewCommand,
appConfigCommand,
appDeployCommand,
appUndeployCommand,
appBackupCommand,
appRestoreCommand,
appRemoveCommand,
appCheckCommand,
appListCommand,
appPsCommand,
appLogsCommand,
appCpCommand,
appRunCommand,
appRollbackCommand,
appSecretCommand,
},
}