diff --git a/cli/app/backup.go b/cli/app/backup.go index 6b9454d7..60514178 100644 --- a/cli/app/backup.go +++ b/cli/app/backup.go @@ -66,13 +66,10 @@ var appBackupCommand = &cli.Command{ sourceAndExec := fmt.Sprintf("%s; %s", sourceCmd, execCmd) cmd := exec.Command("bash", "-c", sourceAndExec) - output, err := cmd.Output() - if err != nil { + if err := internal.RunCmd(cmd); err != nil { logrus.Fatal(err) } - fmt.Print(string(output)) - return nil }, BashComplete: func(c *cli.Context) { diff --git a/cli/app/check.go b/cli/app/check.go index ba4a3457..06b6e05c 100644 --- a/cli/app/check.go +++ b/cli/app/check.go @@ -45,7 +45,7 @@ var appCheckCommand = &cli.Command{ logrus.Fatalf("%s is missing %s", app.Path, missingEnvVars) } - logrus.Info("All necessary environment variables defined") + logrus.Infof("all necessary environment variables defined for '%s'", app.Name) return nil }, diff --git a/cli/app/cp.go b/cli/app/cp.go index 48bd808c..72a29562 100644 --- a/cli/app/cp.go +++ b/cli/app/cp.go @@ -6,6 +6,7 @@ import ( "os" "strings" + "coopcloud.tech/abra/cli/formatter" "coopcloud.tech/abra/cli/internal" "coopcloud.tech/abra/pkg/client" "coopcloud.tech/abra/pkg/config" @@ -55,11 +56,13 @@ var appCpCommand = &cli.Command{ service = parsedSrc[0] srcPath = parsedSrc[1] dstPath = dst + logrus.Debugf("assuming transfer is coming FROM the container") } else if len(parsedDst) == 2 { service = parsedDst[0] dstPath = parsedDst[1] srcPath = src isToContainer = true // + logrus.Debugf("assuming transfer is going TO the container") } appFiles, err := config.LoadAppFiles("") @@ -90,6 +93,8 @@ var appCpCommand = &cli.Command{ } container := containers[0] + logrus.Debugf("retrieved '%s' as target container on '%s'", formatter.ShortenID(container.ID), app.Server) + if isToContainer { if _, err := os.Stat(srcPath); err != nil { logrus.Fatalf("'%s' does not exist?", srcPath) diff --git a/cli/app/logs.go b/cli/app/logs.go index e099cec8..ae71cc61 100644 --- a/cli/app/logs.go +++ b/cli/app/logs.go @@ -73,8 +73,10 @@ var appLogsCommand = &cli.Command{ serviceName := c.Args().Get(1) if serviceName == "" { + logrus.Debug("tailing logs for all app services") stackLogs(app.StackName(), cl) } + logrus.Debugf("tailing logs for '%s'", serviceName) service := fmt.Sprintf("%s_%s", app.StackName(), serviceName) filters := filters.NewArgs() diff --git a/cli/app/new.go b/cli/app/new.go index 0f99a42c..556e3fbb 100644 --- a/cli/app/new.go +++ b/cli/app/new.go @@ -197,6 +197,7 @@ func action(c *cli.Context) error { if len(sanitisedAppName) > 45 { logrus.Fatalf("'%s' cannot be longer than 45 characters", sanitisedAppName) } + logrus.Debugf("'%s' sanitised as '%s' for new app", newAppName, sanitisedAppName) if err := config.CopyAppEnvSample(recipe.Name, newAppName, newAppServer); err != nil { logrus.Fatal(err) diff --git a/cli/app/ps.go b/cli/app/ps.go index d840802d..39db3e8f 100644 --- a/cli/app/ps.go +++ b/cli/app/ps.go @@ -37,7 +37,7 @@ var appPsCommand = &cli.Command{ logrus.Fatal(err) } - tableCol := []string{"ID", "Image", "Command", "Created", "Status", "Ports", "Names"} + tableCol := []string{"id", "image", "command", "created", "status", "ports", "names"} table := abraFormatter.CreateTable(tableCol) for _, container := range containers { @@ -48,7 +48,7 @@ var appPsCommand = &cli.Command{ abraFormatter.HumanDuration(container.Created), container.Status, formatter.DisplayablePorts(container.Ports), - strings.Join(container.Names, ","), + strings.Join(container.Names, ", "), } table.Append(tableRow) } diff --git a/cli/app/remove.go b/cli/app/remove.go index 30accbc7..900614ce 100644 --- a/cli/app/remove.go +++ b/cli/app/remove.go @@ -39,13 +39,13 @@ var appRemoveCommand = &cli.Command{ if !internal.Force { response := false prompt := &survey.Confirm{ - Message: fmt.Sprintf("About to delete %s, are you sure", app.Name), + Message: fmt.Sprintf("about to delete %s, are you sure?", app.Name), } if err := survey.AskOne(prompt, &response); err != nil { logrus.Fatal(err) } if !response { - logrus.Fatal("User aborted app removal") + logrus.Fatal("user aborted app removal") } } @@ -89,7 +89,7 @@ var appRemoveCommand = &cli.Command{ var secretNamesToRemove []string if !internal.Force { secretsPrompt := &survey.MultiSelect{ - Message: "Which secrets do you want to remove?", + Message: "which secrets do you want to remove?", Options: secretNames, Default: secretNames, } @@ -103,10 +103,10 @@ var appRemoveCommand = &cli.Command{ if err != nil { logrus.Fatal(err) } - logrus.Info(fmt.Sprintf("Secret: %s removed", name)) + logrus.Info(fmt.Sprintf("secret: %s removed", name)) } } else { - logrus.Info("No secrets to remove") + logrus.Info("no secrets to remove") } volumeListOKBody, err := cl.VolumeList(ctx, fs) @@ -125,7 +125,7 @@ var appRemoveCommand = &cli.Command{ var removeVols []string if !internal.Force { volumesPrompt := &survey.MultiSelect{ - Message: "Which volumes do you want to remove?", + Message: "which volumes do you want to remove?", Options: vols, Default: vols, } @@ -138,20 +138,20 @@ var appRemoveCommand = &cli.Command{ if err != nil { logrus.Fatal(err) } - logrus.Info(fmt.Sprintf("Volume %s removed", vol)) + logrus.Info(fmt.Sprintf("volume %s removed", vol)) } } else { - logrus.Info("No volumes were removed") + logrus.Info("no volumes were removed") } } else { - logrus.Info("No volumes to remove") + logrus.Info("no volumes to remove") } err = os.Remove(app.Path) if err != nil { logrus.Fatal(err) } - logrus.Info(fmt.Sprintf("File: %s removed", app.Path)) + logrus.Info(fmt.Sprintf("file: %s removed", app.Path)) return nil }, diff --git a/cli/app/restore.go b/cli/app/restore.go index af15658c..d4fd7661 100644 --- a/cli/app/restore.go +++ b/cli/app/restore.go @@ -70,13 +70,10 @@ var appRestoreCommand = &cli.Command{ sourceAndExec := fmt.Sprintf("%s; %s", sourceCmd, execCmd) cmd := exec.Command("bash", "-c", sourceAndExec) - output, err := cmd.Output() - if err != nil { + if err := internal.RunCmd(cmd); err != nil { logrus.Fatal(err) } - fmt.Print(string(output)) - return nil }, } diff --git a/cli/app/rollback.go b/cli/app/rollback.go index ec7edab7..abdc7712 100644 --- a/cli/app/rollback.go +++ b/cli/app/rollback.go @@ -75,6 +75,8 @@ var appRollbackCommand = &cli.Command{ // display table of existing state and expected state and prompt // run the deployment with this target version! + logrus.Fatal("command not implemented yet, coming soon TM") + return nil }, } diff --git a/cli/app/secret.go b/cli/app/secret.go index 8200b674..0d131af9 100644 --- a/cli/app/secret.go +++ b/cli/app/secret.go @@ -83,13 +83,13 @@ var appSecretGenerateCommand = &cli.Command{ os.Exit(1) } - tableCol := []string{"Name", "Value"} + tableCol := []string{"name", "value"} table := abraFormatter.CreateTable(tableCol) for name, val := range secretVals { table.Append([]string{name, val}) } table.Render() - logrus.Warn("these secrets are not shown again, please take note of them *now*") + logrus.Warn("generated secrets are not shown again, please take note of them *now*") return nil }, diff --git a/cli/app/version.go b/cli/app/version.go index e5bd916e..4b1d8214 100644 --- a/cli/app/version.go +++ b/cli/app/version.go @@ -25,6 +25,7 @@ func getImagePath(image string) (string, error) { if strings.Contains(path, "library") { path = strings.Split(path, "/")[1] } + logrus.Debugf("parsed '%s' from '%s'", path, image) return path, nil } @@ -53,7 +54,7 @@ var appVersionCommand = &cli.Command{ }(app.Server, label) } - tableCol := []string{"Name", "Image", "Version", "Digest"} + tableCol := []string{"name", "image", "version", "digest"} table := abraFormatter.CreateTable(tableCol) statuses := make(map[string]stack.StackStatus) diff --git a/cli/app/volume.go b/cli/app/volume.go index cb562ef2..fe281df8 100644 --- a/cli/app/volume.go +++ b/cli/app/volume.go @@ -15,7 +15,7 @@ import ( var appVolumeListCommand = &cli.Command{ Name: "list", - Usage: "list volumes associated with an app", + Usage: "List volumes associated with an app", Aliases: []string{"ls"}, Action: func(c *cli.Context) error { app := internal.ValidateApp(c) @@ -26,7 +26,7 @@ var appVolumeListCommand = &cli.Command{ logrus.Fatal(err) } - table := abraFormatter.CreateTable([]string{"DRIVER", "VOLUME NAME"}) + table := abraFormatter.CreateTable([]string{"driver", "volume name"}) var volTable [][]string for _, volume := range volumeList { volRow := []string{ @@ -45,7 +45,7 @@ var appVolumeListCommand = &cli.Command{ var appVolumeRemoveCommand = &cli.Command{ Name: "remove", - Usage: "remove volume(s) associated with an app", + Usage: "Remove volume(s) associated with an app", Aliases: []string{"rm"}, Flags: []cli.Flag{ internal.ForceFlag, @@ -63,7 +63,7 @@ var appVolumeRemoveCommand = &cli.Command{ var volumesToRemove []string if !internal.Force { volumesPrompt := &survey.MultiSelect{ - Message: "Which volumes do you want to remove?", + Message: "which volumes do you want to remove?", Options: volumeNames, Default: volumeNames, } @@ -79,7 +79,7 @@ var appVolumeRemoveCommand = &cli.Command{ logrus.Fatal(err) } - logrus.Info("Volumes removed successfully.") + logrus.Info("volumes removed successfully") return nil }, diff --git a/cli/cli.go b/cli/cli.go index a2750d2d..53edc88b 100644 --- a/cli/cli.go +++ b/cli/cli.go @@ -68,8 +68,18 @@ func RunApp(version, commit string) { }, }, } + app.EnableBashCompletion = true + app.Before = func(c *cli.Context) error { + if Debug { + logrus.SetLevel(logrus.DebugLevel) + } + return nil + } + + logrus.Debugf("Flying abra version '%s', commit '%s', enjoy the ride", version, commit) + if err := app.Run(os.Args); err != nil { logrus.Fatal(err) } diff --git a/cli/formatter/formatter.go b/cli/formatter/formatter.go index 3e233b2c..68ac0285 100644 --- a/cli/formatter/formatter.go +++ b/cli/formatter/formatter.go @@ -31,6 +31,7 @@ func HumanDuration(timestamp int64) string { return units.HumanDuration(now.Sub(date)) + " ago" } +// CreateTable prepares a table layout for output. func CreateTable(columns []string) *tablewriter.Table { table := tablewriter.NewWriter(os.Stdout) table.SetHeader(columns) diff --git a/cli/internal/command.go b/cli/internal/command.go index 70924c7e..e3d69fd6 100644 --- a/cli/internal/command.go +++ b/cli/internal/command.go @@ -6,6 +6,7 @@ import ( "os/exec" ) +// RunCmd runs a shell command and streams stdout/stderr in real-time. func RunCmd(cmd *exec.Cmd) error { r, err := cmd.StdoutPipe() if err != nil { diff --git a/cli/internal/validate.go b/cli/internal/validate.go index 091fd57e..2a26fce5 100644 --- a/cli/internal/validate.go +++ b/cli/internal/validate.go @@ -23,6 +23,8 @@ func ValidateRecipe(c *cli.Context) recipe.Recipe { logrus.Fatal(err) } + logrus.Debugf("validated '%s' as recipe argument", recipeName) + return recipe } @@ -39,6 +41,8 @@ func ValidateApp(c *cli.Context) config.App { logrus.Fatal(err) } + logrus.Debugf("validated '%s' as app argument", appName) + return app } @@ -50,5 +54,7 @@ func ValidateDomain(c *cli.Context) string { ShowSubcommandHelpAndError(c, errors.New("no domain provided")) } + logrus.Debugf("validated '%s' as domain argument", domainName) + return domainName } diff --git a/cli/recipe/lint.go b/cli/recipe/lint.go index 4fe0ec0a..01215410 100644 --- a/cli/recipe/lint.go +++ b/cli/recipe/lint.go @@ -77,16 +77,16 @@ var recipeLintCommand = &cli.Command{ } } - tableCol := []string{"Rule", "Satisfied"} + tableCol := []string{"rule", "satisfied"} table := formatter.CreateTable(tableCol) - table.Append([]string{"Compose files have the expected version", strconv.FormatBool(expectedVersion)}) - table.Append([]string{"Environment configuration is provided", strconv.FormatBool(envSampleProvided)}) - table.Append([]string{"Recipe contains a service named 'app'", strconv.FormatBool(serviceNamedApp)}) - table.Append([]string{"Traefik routing enabled on at least one service", strconv.FormatBool(traefikEnabled)}) - table.Append([]string{"All services have a healthcheck enabled", strconv.FormatBool(healthChecksForAllServices)}) - table.Append([]string{"All images are using a tag", strconv.FormatBool(allImagesTagged)}) - table.Append([]string{"No usage of unstable 'latest' tags", strconv.FormatBool(noUnstableTags)}) - table.Append([]string{"All tags are using a semver-like format", strconv.FormatBool(semverLikeTags)}) + table.Append([]string{"compose files have the expected version", strconv.FormatBool(expectedVersion)}) + table.Append([]string{"environment configuration is provided", strconv.FormatBool(envSampleProvided)}) + table.Append([]string{"recipe contains a service named 'app'", strconv.FormatBool(serviceNamedApp)}) + table.Append([]string{"traefik routing enabled on at least one service", strconv.FormatBool(traefikEnabled)}) + table.Append([]string{"all services have a healthcheck enabled", strconv.FormatBool(healthChecksForAllServices)}) + table.Append([]string{"all images are using a tag", strconv.FormatBool(allImagesTagged)}) + table.Append([]string{"no usage of unstable 'latest' tags", strconv.FormatBool(noUnstableTags)}) + table.Append([]string{"all tags are using a semver-like format", strconv.FormatBool(semverLikeTags)}) table.Render() return nil diff --git a/cli/recipe/list.go b/cli/recipe/list.go index 919c3db1..1b8ddab6 100644 --- a/cli/recipe/list.go +++ b/cli/recipe/list.go @@ -23,7 +23,7 @@ var recipeListCommand = &cli.Command{ recipes := catl.Flatten() sort.Sort(catalogue.ByRecipeName(recipes)) - tableCol := []string{"Name", "Category", "Status"} + tableCol := []string{"name", "category", "status"} table := formatter.CreateTable(tableCol) for _, recipe := range recipes { diff --git a/cli/recipe/new.go b/cli/recipe/new.go index ea02e6e2..f487e574 100644 --- a/cli/recipe/new.go +++ b/cli/recipe/new.go @@ -8,7 +8,7 @@ import ( "coopcloud.tech/abra/cli/internal" "coopcloud.tech/abra/pkg/config" - "github.com/go-git/go-git/v5" + "coopcloud.tech/abra/pkg/git" "github.com/sirupsen/logrus" "github.com/urfave/cli/v2" ) @@ -28,10 +28,8 @@ var recipeNewCommand = &cli.Command{ } url := fmt.Sprintf("%s/example.git", config.REPOS_BASE_URL) - _, err := git.PlainClone(directory, false, &git.CloneOptions{URL: url, Tags: git.AllTags}) - if err != nil { - logrus.Fatal(err) - return nil + if err := git.Clone(directory, url); err != nil { + return err } gitRepo := path.Join(config.APPS_DIR, recipe.Name, ".git") @@ -39,6 +37,7 @@ var recipeNewCommand = &cli.Command{ logrus.Fatal(err) return nil } + logrus.Debugf("removed git repo in '%s'", gitRepo) toParse := []string{ path.Join(config.APPS_DIR, recipe.Name, "README.md"), @@ -71,7 +70,7 @@ var recipeNewCommand = &cli.Command{ } logrus.Infof( - "New recipe '%s' created in %s, happy hacking!\n", + "new recipe '%s' created in %s, happy hacking!\n", recipe.Name, path.Join(config.APPS_DIR, recipe.Name), ) diff --git a/cli/recipe/sync.go b/cli/recipe/sync.go index 99d259f7..1e23e3ef 100644 --- a/cli/recipe/sync.go +++ b/cli/recipe/sync.go @@ -36,7 +36,7 @@ the versioning metadata of up-and-running containers are. } if !hasAppService { - logrus.Fatal(fmt.Sprintf("No 'app' service defined in '%s', cannot proceed", recipe.Name)) + logrus.Fatal(fmt.Sprintf("no 'app' service defined in '%s'", recipe.Name)) } for _, service := range recipe.Config.Services { @@ -44,17 +44,20 @@ the versioning metadata of up-and-running containers are. if err != nil { logrus.Fatal(err) } + logrus.Debugf("detected image '%s' for service '%s'", img, service.Name) digest, err := client.GetTagDigest(img) if err != nil { logrus.Fatal(err) } + logrus.Debugf("retrieved digest '%s' for '%s'", digest, img) tag := img.(reference.NamedTagged).Tag() label := fmt.Sprintf("coop-cloud.${STACK_NAME}.%s.version=%s-%s", service.Name, tag, digest) if err := recipe.UpdateLabel(service.Name, label); err != nil { logrus.Fatal(err) } + logrus.Debugf("added label '%s' to service '%s'", label, service.Name) } return nil diff --git a/cli/recipe/upgrade.go b/cli/recipe/upgrade.go index 2200684f..aad91f0e 100644 --- a/cli/recipe/upgrade.go +++ b/cli/recipe/upgrade.go @@ -40,6 +40,7 @@ This is step 1 of upgrading a recipe. Step 2 is running "abra recipe sync if err != nil { logrus.Fatal(err) } + logrus.Debugf("read '%s' from the recipe catalogue for '%s'", catlVersions, service.Name) img, err := reference.ParseNormalizedNamed(service.Image) if err != nil { @@ -51,6 +52,7 @@ This is step 1 of upgrading a recipe. Step 2 is running "abra recipe sync if err != nil { logrus.Fatal(err) } + logrus.Debugf("retrieved '%s' from remote registry for '%s'", regVersions, image) if strings.Contains(image, "library") { // ParseNormalizedNamed prepends 'library' to images like nginx:, @@ -61,6 +63,7 @@ This is step 1 of upgrading a recipe. Step 2 is running "abra recipe sync semverLikeTag := true if !tagcmp.IsParsable(img.(reference.NamedTagged).Tag()) { + logrus.Debugf("'%s' not considered semver-like", img.(reference.NamedTagged).Tag()) semverLikeTag = false } @@ -68,6 +71,7 @@ This is step 1 of upgrading a recipe. Step 2 is running "abra recipe sync if err != nil && semverLikeTag { logrus.Fatal(err) } + logrus.Debugf("parsed '%s' for '%s'", tag, service.Name) var compatible []tagcmp.Tag for _, regVersion := range regVersions { @@ -81,10 +85,12 @@ This is step 1 of upgrading a recipe. Step 2 is running "abra recipe sync } } + logrus.Debugf("detected potential upgradable tags '%s' for '%s'", compatible, service.Name) + sort.Sort(tagcmp.ByTagDesc(compatible)) if len(compatible) == 0 && semverLikeTag { - logrus.Info(fmt.Sprintf("No new versions available for '%s', '%s' is the latest", image, tag)) + logrus.Info(fmt.Sprintf("no new versions available for '%s', '%s' is the latest", image, tag)) continue // skip on to the next tag and don't update any compose files } @@ -101,11 +107,13 @@ This is step 1 of upgrading a recipe. Step 2 is running "abra recipe sync } } - msg := fmt.Sprintf("Which tag would you like to upgrade to? (service: %s, tag: %s)", service.Name, tag) + logrus.Debugf("detected compatible upgradable tags '%s' for '%s'", compatibleStrings, service.Name) + + msg := fmt.Sprintf("upgrade to which tag? (service: %s, tag: %s)", service.Name, tag) if !tagcmp.IsParsable(img.(reference.NamedTagged).Tag()) { tag := img.(reference.NamedTagged).Tag() - logrus.Warning(fmt.Sprintf("Unable to determine versioning semantics of '%s', listing all tags...", tag)) - msg = fmt.Sprintf("Which tag would you like to upgrade to? (service: %s, tag: %s)", service.Name, tag) + logrus.Warning(fmt.Sprintf("unable to determine versioning semantics of '%s', listing all tags", tag)) + msg = fmt.Sprintf("upgrade to which tag? (service: %s, tag: %s)", service.Name, tag) compatibleStrings = []string{} for _, regVersion := range regVersions { compatibleStrings = append(compatibleStrings, regVersion.Name) @@ -124,6 +132,7 @@ This is step 1 of upgrading a recipe. Step 2 is running "abra recipe sync if err := recipe.UpdateTag(image, upgradeTag); err != nil { logrus.Fatal(err) } + logrus.Debugf("tag updated from '%s' to '%s' for '%s'", image, upgradeTag, recipe.Name) } return nil diff --git a/cli/server/add.go b/cli/server/add.go index 77bca81f..c2652e4f 100644 --- a/cli/server/add.go +++ b/cli/server/add.go @@ -62,10 +62,12 @@ All communication between Abra and the server will use this SSH connection. for _, context := range contexts { if context.Name == domainName { - logrus.Fatalf("Server at '%s' already exists?", domainName) + logrus.Fatalf("server at '%s' already exists?", domainName) } } + logrus.Debugf("creating context with domain '%s', username '%s' and port '%s'", domainName, username, port) + if err := client.CreateContext(domainName, username, port); err != nil { logrus.Fatal(err) } @@ -77,11 +79,12 @@ All communication between Abra and the server will use this SSH connection. } if _, err := cl.Info(ctx); err != nil { - logrus.Fatalf("Unable to make a connection to '%s'?", domainName) + logrus.Fatalf("unable to make a connection to '%s'?", domainName) logrus.Debug(err) } - logrus.Infof("Server at '%s' has been added", domainName) + logrus.Debugf("remote connection to '%s' is definitely up", domainName) + logrus.Infof("server at '%s' has been added", domainName) return nil }, diff --git a/cli/server/init.go b/cli/server/init.go index 413bd535..99c43dae 100644 --- a/cli/server/init.go +++ b/cli/server/init.go @@ -21,7 +21,7 @@ var serverInitCommand = &cli.Command{ HideHelp: true, ArgsUsage: "", Description: ` -Initialise swarm mode on the target . +Initialise swarm mode on the target . This initialisation explicitly chooses the "single host swarm" mode which uses the default IPv4 address as the advertising address. This can be re-configured @@ -45,6 +45,7 @@ later for more advanced use cases. return d.DialContext(ctx, "udp", "95.216.24.230:53") }, } + logrus.Debugf("created DNS resolver via 95.216.24.230") ctx := context.Background() ips, err := resolver.LookupIPAddr(ctx, domainName) @@ -64,11 +65,13 @@ later for more advanced use cases. if _, err := cl.SwarmInit(ctx, initReq); err != nil { return err } + logrus.Debugf("initialised swarm on '%s'", domainName) netOpts := types.NetworkCreate{Driver: "overlay", Scope: "swarm"} if _, err := cl.NetworkCreate(ctx, "proxy", netOpts); err != nil { return err } + logrus.Debug("swarm overlay network 'proxy' created") return nil }, diff --git a/cli/server/list.go b/cli/server/list.go index f25526bc..c810dbcc 100644 --- a/cli/server/list.go +++ b/cli/server/list.go @@ -22,6 +22,7 @@ var serverListCommand = &cli.Command{ if err != nil { logrus.Fatal(err) } + tableColumns := []string{"Name", "Connection"} table := formatter.CreateTable(tableColumns) defer table.Render() @@ -30,8 +31,8 @@ var serverListCommand = &cli.Command{ if err != nil { logrus.Fatal(err) } - for _, serverName := range serverNames { + for _, serverName := range serverNames { var row []string for _, ctx := range contexts { endpoint, err := client.GetContextEndpoint(ctx) @@ -47,9 +48,8 @@ var serverListCommand = &cli.Command{ row = []string{serverName, "UNKNOWN"} } table.Append(row) - } - return nil + return nil }, } diff --git a/cli/server/new.go b/cli/server/new.go index 152501c7..384ca645 100644 --- a/cli/server/new.go +++ b/cli/server/new.go @@ -79,12 +79,14 @@ environment variable or otherwise passing the "--env/-e" flag. } if hetznerCloudAPIToken == "" { - logrus.Fatal("Hetzner Cloud API token is missing, cannot continue") + logrus.Fatal("Hetzner Cloud API token is missing") } ctx := context.Background() client := hcloud.NewClient(hcloud.WithToken(hetznerCloudAPIToken)) + logrus.Debugf("successfully created hetzner cloud API client") + var sshKeys []*hcloud.SSHKey for _, sshKey := range c.StringSlice("ssh-keys") { sshKey, _, err := client.SSHKey.GetByName(ctx, sshKey) @@ -106,13 +108,17 @@ environment variable or otherwise passing the "--env/-e" flag. logrus.Fatal(err) } + logrus.Debugf("new server '%s' created", name) + tableColumns := []string{"Name", "IPv4", "Root Password"} table := formatter.CreateTable(tableColumns) + if len(sshKeys) > 0 { table.Append([]string{name, res.Server.PublicNet.IPv4.IP.String(), "N/A (using SSH keys)"}) } else { table.Append([]string{name, res.Server.PublicNet.IPv4.IP.String(), res.RootPassword}) } + table.Render() return nil @@ -182,13 +188,14 @@ environment variable or otherwise passing the "--env/-e" flag. } if capsulAPIToken == "" { - logrus.Fatal("Capsul API token is missing, cannot continue") + logrus.Fatal("Capsul API token is missing") } - // yep, the response time is quite slow, something to fix Capsul side + // yep, the response time is quite slow, something to fix on the Capsul side client := &http.Client{Timeout: 20 * time.Second} capsulCreateURL := fmt.Sprintf("https://%s/api/capsul/create", capsulInstance) + logrus.Debugf("using '%s' as capsul create url", capsulCreateURL) values := map[string]string{ "name": name, "size": capsulType, @@ -230,6 +237,7 @@ environment variable or otherwise passing the "--env/-e" flag. if err := json.NewDecoder(res.Body).Decode(&resp); err != nil { logrus.Fatal(err) } + logrus.Debugf("capsul created with ID: '%s'", resp.ID) tableColumns := []string{"Name", "ID"} table := formatter.CreateTable(tableColumns) diff --git a/cli/server/remove.go b/cli/server/remove.go index 4d18cd66..3e0149d8 100644 --- a/cli/server/remove.go +++ b/cli/server/remove.go @@ -23,7 +23,7 @@ internal bookkeeping so that it is not managed any more. logrus.Fatal(err) } - logrus.Infof("Server at '%s' has been forgotten", domainName) + logrus.Infof("server at '%s' has been forgotten", domainName) return nil }, diff --git a/cli/upgrade.go b/cli/upgrade.go index c43b0e45..a7f89743 100644 --- a/cli/upgrade.go +++ b/cli/upgrade.go @@ -14,6 +14,7 @@ var UpgradeCommand = &cli.Command{ Usage: "Upgrade abra", Action: func(c *cli.Context) error { cmd := exec.Command("bash", "-c", "curl -s https://install.abra.coopcloud.tech | bash") + logrus.Debugf("attempting to run '%s'", cmd) if err := internal.RunCmd(cmd); err != nil { logrus.Fatal(err) } diff --git a/pkg/app/app.go b/pkg/app/app.go index c995d16a..e9036bae 100644 --- a/pkg/app/app.go +++ b/pkg/app/app.go @@ -8,6 +8,7 @@ import ( "coopcloud.tech/abra/pkg/client/stack" "coopcloud.tech/abra/pkg/config" apiclient "github.com/docker/docker/client" + "github.com/sirupsen/logrus" ) // Get retrieves an app @@ -22,6 +23,8 @@ func Get(appName string) (config.App, error) { return config.App{}, err } + logrus.Debugf("retrieved '%s' for '%s'", app, appName) + return app, nil } @@ -51,6 +54,14 @@ func DeployedVersions(ctx context.Context, cl *apiclient.Client, app config.App) } } + deployed := len(services) > 0 + + if deployed { + logrus.Debugf("detected '%s' as deployed versions of '%s'", appSpec, app.Name) + } else { + logrus.Debugf("detected '%s' as not deployed", app.Name) + } + return appSpec, len(services) > 0, nil } @@ -58,11 +69,17 @@ func DeployedVersions(ctx context.Context, cl *apiclient.Client, app config.App) func ParseVersionLabel(label string) (string, string) { // versions may look like v4.2-abcd or v4.2-alpine-abcd idx := strings.LastIndex(label, "-") - return label[:idx], label[idx+1:] + version := label[:idx] + digest := label[idx+1:] + logrus.Debugf("parsed '%s' as version from '%s'", version, label) + logrus.Debugf("parsed '%s' as digest from '%s'", digest, label) + return version, digest } // ParseVersionName parses a $STACK_NAME_$SERVICE_NAME service label. func ParseServiceName(label string) string { idx := strings.LastIndex(label, "_") - return label[idx+1:] + serviceName := label[idx+1:] + logrus.Debugf("parsed '%s' as service name from '%s'", serviceName, label) + return serviceName } diff --git a/pkg/catalogue/catalogue.go b/pkg/catalogue/catalogue.go index 585dd5f5..97ab4db4 100644 --- a/pkg/catalogue/catalogue.go +++ b/pkg/catalogue/catalogue.go @@ -15,6 +15,7 @@ import ( "coopcloud.tech/abra/pkg/config" "coopcloud.tech/abra/pkg/recipe" "coopcloud.tech/abra/pkg/web" + "github.com/sirupsen/logrus" ) // RecipeCatalogueURL is the only current recipe catalogue available. @@ -76,6 +77,8 @@ func (r RecipeMeta) LatestVersion() string { version = tag } + logrus.Debugf("choosing '%s' as latest version of '%s'", version, r.Name) + return version } @@ -88,9 +91,11 @@ type RecipeCatalogue map[Name]RecipeMeta // Flatten converts AppCatalogue to slice func (r RecipeCatalogue) Flatten() []RecipeMeta { recipes := make([]RecipeMeta, 0, len(r)) + for name := range r { recipes = append(recipes, r[name]) } + return recipes } @@ -117,9 +122,11 @@ func recipeCatalogueFSIsLatest() (bool, error) { if err != nil { return false, err } + info, err := os.Stat(config.APPS_JSON) if err != nil { if os.IsNotExist(err) { + logrus.Debugf("no recipe catalogue found in file system cache") return false, nil } return false, err @@ -129,9 +136,12 @@ func recipeCatalogueFSIsLatest() (bool, error) { remoteModifiedTime := parsed.Unix() if localModifiedTime < remoteModifiedTime { + logrus.Debug("file system cached recipe catalogue is out-of-date") return false, nil } + logrus.Debug("file system cached recipe catalogue is up-to-date") + return true, nil } @@ -145,12 +155,14 @@ func ReadRecipeCatalogue() (RecipeCatalogue, error) { } if !recipeFSIsLatest { + logrus.Debugf("reading recipe catalogue from web to get latest") if err := readRecipeCatalogueWeb(&recipes); err != nil { return nil, err } return recipes, nil } + logrus.Debugf("reading recipe catalogue from file system cache to get latest") if err := readRecipeCatalogueFS(&recipes); err != nil { return nil, err } @@ -164,9 +176,13 @@ func readRecipeCatalogueFS(target interface{}) error { if err != nil { return err } + if err := json.Unmarshal(recipesJSONFS, &target); err != nil { return err } + + logrus.Debugf("read recipe catalogue from file system cache in '%s'", config.APPS_JSON) + return nil } @@ -185,6 +201,8 @@ func readRecipeCatalogueWeb(target interface{}) error { return err } + logrus.Debugf("read recipe catalogue from web at '%s'", RecipeCatalogueURL) + return nil } @@ -211,6 +229,8 @@ func VersionsOfService(recipe, serviceName string) ([]string, error) { } } + logrus.Debugf("detected versions '%s' for '%s'", strings.Join(versions, ", "), recipe) + return versions, nil } @@ -226,9 +246,12 @@ func GetRecipeMeta(recipeName string) (RecipeMeta, error) { err := fmt.Errorf("recipe '%s' does not exist?", recipeName) return RecipeMeta{}, err } + if err := recipe.EnsureExists(recipeName); err != nil { return RecipeMeta{}, err } + logrus.Debugf("recipe metadata retrieved for '%s'", recipeName) + return recipeMeta, nil } diff --git a/pkg/client/client.go b/pkg/client/client.go index b25aeb44..6708a731 100644 --- a/pkg/client/client.go +++ b/pkg/client/client.go @@ -48,5 +48,7 @@ func New(contextName string) (*client.Client, error) { logrus.Fatalf("unable to create Docker client: %s", err) } + logrus.Debugf("created client for '%s'", contextName) + return cl, nil } diff --git a/pkg/client/context.go b/pkg/client/context.go index cebd3732..5422f876 100644 --- a/pkg/client/context.go +++ b/pkg/client/context.go @@ -10,6 +10,7 @@ import ( "github.com/docker/cli/cli/context/docker" contextStore "github.com/docker/cli/cli/context/store" "github.com/moby/term" + "github.com/sirupsen/logrus" ) type Context = contextStore.Metadata @@ -26,6 +27,7 @@ func CreateContext(contextName string, user string, port string) error { if err := createContext(contextName, host); err != nil { return err } + logrus.Debugf("created the '%s' context", contextName) return nil } @@ -36,13 +38,16 @@ func createContext(name string, host string) error { Endpoints: make(map[string]interface{}), Name: name, } + contextTLSData := contextStore.ContextTLSData{ Endpoints: make(map[string]contextStore.EndpointTLSData), } + dockerEP, dockerTLS, err := getDockerEndpointMetadataAndTLS(host) if err != nil { return err } + contextMetadata.Endpoints[docker.DockerEndpoint] = dockerEP if dockerTLS != nil { contextTLSData.Endpoints[docker.DockerEndpoint] = *dockerTLS @@ -51,9 +56,11 @@ func createContext(name string, host string) error { if err := s.CreateOrUpdate(contextMetadata); err != nil { return err } + if err := s.ResetTLSMaterial(name, &contextTLSData); err != nil { return err } + return nil } @@ -61,6 +68,7 @@ func DeleteContext(name string) error { if name == "default" { return errors.New("context 'default' cannot be removed") } + if _, err := GetContext(name); err != nil { return err } @@ -81,6 +89,7 @@ func GetContext(contextName string) (contextStore.Metadata, error) { if err != nil { return contextStore.Metadata{}, err } + return ctx, nil } diff --git a/pkg/compose/compose.go b/pkg/compose/compose.go index 9c71c6be..0ad60a09 100644 --- a/pkg/compose/compose.go +++ b/pkg/compose/compose.go @@ -20,6 +20,8 @@ func UpdateTag(pattern, image, tag string) error { return err } + logrus.Debugf("considering '%s' config(s) for tag update", strings.Join(composeFiles, ", ")) + for _, composeFile := range composeFiles { opts := stack.Deploy{Composefiles: []string{composeFile}} emptyEnv := make(map[string]string) @@ -35,7 +37,7 @@ func UpdateTag(pattern, image, tag string) error { img, _ := reference.ParseNormalizedNamed(service.Image) if err != nil { - logrus.Fatal(err) + return err } composeImage := reference.Path(img) @@ -47,16 +49,20 @@ func UpdateTag(pattern, image, tag string) error { } composeTag := img.(reference.NamedTagged).Tag() + logrus.Debugf("parsed '%s' from '%s'", composeTag, service.Image) + if image == composeImage { bytes, err := ioutil.ReadFile(composeFile) if err != nil { - logrus.Fatal(err) + return err } old := fmt.Sprintf("%s:%s", composeImage, composeTag) new := fmt.Sprintf("%s:%s", composeImage, tag) replacedBytes := strings.Replace(string(bytes), old, new, -1) + logrus.Debugf("updating '%s' to '%s' in '%s'", old, new, compose.Filename) + if err := ioutil.WriteFile(compose.Filename, []byte(replacedBytes), 0644); err != nil { return err } @@ -74,6 +80,8 @@ func UpdateLabel(pattern, serviceName, label string) error { return err } + logrus.Debugf("considering '%s' config(s) for label update", strings.Join(composeFiles, ", ")) + for _, composeFile := range composeFiles { opts := stack.Deploy{Composefiles: []string{composeFile}} emptyEnv := make(map[string]string) @@ -104,6 +112,9 @@ func UpdateLabel(pattern, serviceName, label string) error { old := fmt.Sprintf("coop-cloud.${STACK_NAME}.%s.version=%s", service.Name, value) replacedBytes := strings.Replace(string(bytes), old, label, -1) + + logrus.Debugf("updating '%s' to '%s' in '%s'", old, label, compose.Filename) + if err := ioutil.WriteFile(compose.Filename, []byte(replacedBytes), 0644); err != nil { return err } diff --git a/pkg/config/app.go b/pkg/config/app.go index 3d6b65ed..9bdd1321 100644 --- a/pkg/config/app.go +++ b/pkg/config/app.go @@ -13,6 +13,7 @@ import ( loader "coopcloud.tech/abra/pkg/client/stack" stack "coopcloud.tech/abra/pkg/client/stack" composetypes "github.com/docker/cli/cli/compose/types" + "github.com/sirupsen/logrus" ) // Type aliases to make code hints easier to understand @@ -93,10 +94,14 @@ func readAppEnvFile(appFile AppFile, name AppName) (App, error) { if err != nil { return App{}, fmt.Errorf("env file for '%s' couldn't be read: %s", name, err.Error()) } + + logrus.Debugf("read env '%s' from '%s'", env, appFile.Path) + app, err := newApp(env, name, appFile) if err != nil { return App{}, fmt.Errorf("env file for '%s' has issues: %s", name, err.Error()) } + return app, nil } @@ -108,6 +113,7 @@ func newApp(env AppEnv, name string, appFile AppFile) (App, error) { if !ok { return App{}, errors.New("missing TYPE variable") } + return App{ Name: name, Domain: domain, @@ -131,6 +137,9 @@ func LoadAppFiles(servers ...string) (AppFiles, error) { } } } + + logrus.Debugf("collecting metadata from '%v' servers: '%s'", len(servers), servers) + for _, server := range servers { serverDir := path.Join(ABRA_SERVER_FOLDER, server) files, err := getAllFilesInDirectory(serverDir) @@ -157,16 +166,19 @@ func GetApp(apps AppFiles, name AppName) (App, error) { if !exists { return App{}, fmt.Errorf("cannot find app with name '%s'", name) } + app, err := readAppEnvFile(appFile, name) if err != nil { return App{}, err } + return app, nil } // GetApps returns a slice of Apps with their env files read from a given slice of AppFiles func GetApps(appFiles AppFiles) ([]App, error) { var apps []App + for name := range appFiles { app, err := GetApp(appFiles, name) if err != nil { @@ -174,14 +186,17 @@ func GetApps(appFiles AppFiles) ([]App, error) { } apps = append(apps, app) } + return apps, nil } +// GetAppNames retrieves a list of app names. func GetAppNames() ([]string, error) { appFiles, err := LoadAppFiles("") if err != nil { return []string{}, err } + apps, err := GetApps(appFiles) if err != nil { return []string{}, err @@ -213,6 +228,8 @@ func CopyAppEnvSample(appType, appName, server string) error { return err } + logrus.Debugf("copied '%s' to '%s'", envSamplePath, appEnvPath) + return nil } @@ -240,6 +257,8 @@ func GetAppStatuses(appFiles AppFiles) (map[string]string, error) { } } + logrus.Debugf("retrieved app statuses: '%s'", statuses) + return statuses, nil } @@ -261,6 +280,9 @@ func GetAppComposeFiles(recipe string, appEnv AppEnv) ([]string, error) { path := fmt.Sprintf("%s/%s/%s", APPS_DIR, recipe, file) composeFiles = append(composeFiles, path) } + + logrus.Debugf("retrieved '%s' configs for '%s'", strings.Join(composeFiles, ", "), recipe) + return composeFiles, nil } @@ -272,5 +294,8 @@ func GetAppComposeConfig(recipe string, opts stack.Deploy, appEnv AppEnv) (*comp if err != nil { return &composetypes.Config{}, err } + + logrus.Debugf("retrieved '%s' for '%s'", compose, recipe) + return compose, nil } diff --git a/pkg/config/env.go b/pkg/config/env.go index 4e400929..8550c769 100644 --- a/pkg/config/env.go +++ b/pkg/config/env.go @@ -20,8 +20,10 @@ var APPS_JSON = path.Join(ABRA_DIR, "apps.json") var APPS_DIR = path.Join(ABRA_DIR, "apps") var REPOS_BASE_URL = "https://git.coopcloud.tech/coop-cloud" +// GetServers retrieves all servers. func (a AppFiles) GetServers() []string { var unique []string + servers := make(map[string]struct{}) for _, appFile := range a { if _, ok := servers[appFile.Server]; !ok { @@ -29,33 +31,48 @@ func (a AppFiles) GetServers() []string { unique = append(unique, appFile.Server) } } + + logrus.Debugf("retrieved servers: '%s'", unique) + return unique } +// ReadEnv loads an app envivornment into a map. func ReadEnv(filePath string) (AppEnv, error) { var envFile AppEnv + envFile, err := godotenv.Read(filePath) if err != nil { return nil, err } + + logrus.Debugf("read '%s' from '%s'", envFile, filePath) + return envFile, nil } +// ReadServerNames retrieves all server names. func ReadServerNames() ([]string, error) { serverNames, err := getAllFoldersInDirectory(ABRA_SERVER_FOLDER) + if err != nil { return nil, err } + + logrus.Debugf("read '%s' from '%s'", strings.Join(serverNames, ","), ABRA_SERVER_FOLDER) + return serverNames, nil } // getAllFilesInDirectory returns filenames of all files in directory func getAllFilesInDirectory(directory string) ([]fs.FileInfo, error) { var realFiles []fs.FileInfo + files, err := ioutil.ReadDir(directory) if err != nil { return nil, err } + for _, file := range files { // Follow any symlinks filePath := path.Join(directory, file.Name()) @@ -71,14 +88,15 @@ func getAllFilesInDirectory(directory string) ([]fs.FileInfo, error) { realFiles = append(realFiles, file) } } - } + return realFiles, nil } // getAllFoldersInDirectory returns both folder and symlink paths func getAllFoldersInDirectory(directory string) ([]string, error) { var folders []string + files, err := ioutil.ReadDir(directory) if err != nil { return nil, err @@ -86,6 +104,7 @@ func getAllFoldersInDirectory(directory string) ([]string, error) { if len(files) == 0 { return nil, fmt.Errorf("directory is empty: '%s'", directory) } + for _, file := range files { // Check if file is directory or symlink if file.IsDir() || file.Mode()&fs.ModeSymlink != 0 { @@ -99,12 +118,14 @@ func getAllFoldersInDirectory(directory string) ([]string, error) { } } } + return folders, nil } // EnsureAbraDirExists checks for the abra config folder and throws error if not func EnsureAbraDirExists() error { if _, err := os.Stat(ABRA_DIR); os.IsNotExist(err) { + logrus.Debugf("'%s' does not exist, creating it", ABRA_DIR) if err := os.Mkdir(ABRA_DIR, 0777); err != nil { return err } @@ -112,6 +133,7 @@ func EnsureAbraDirExists() error { return nil } +// ReadAbraShEnvVars reads env vars from an abra.sh recipe file. func ReadAbraShEnvVars(abraSh string) (map[string]string, error) { envVars := make(map[string]string) @@ -137,5 +159,7 @@ func ReadAbraShEnvVars(abraSh string) (map[string]string, error) { } } + logrus.Debugf("read '%s' from '%s'", envVars, abraSh) + return envVars, nil } diff --git a/pkg/git/clone.go b/pkg/git/clone.go new file mode 100644 index 00000000..6e1121d2 --- /dev/null +++ b/pkg/git/clone.go @@ -0,0 +1,33 @@ +package git + +import ( + "os" + + "github.com/go-git/go-git/v5" + "github.com/go-git/go-git/v5/plumbing" + "github.com/sirupsen/logrus" +) + +// Clone runs a git clone which accounts for different default branches. +func Clone(dir, url string) error { + if _, err := os.Stat(dir); os.IsNotExist(err) { + logrus.Debugf("'%s' does not exist, attempting to git clone from '%s'", dir, url) + _, err := git.PlainClone(dir, false, &git.CloneOptions{URL: url, Tags: git.AllTags}) + if err != nil { + logrus.Debugf("cloning from default branch failed, attempting from main branch") + // try with main branch because Git is being a Git + _, err := git.PlainClone(dir, false, &git.CloneOptions{ + URL: url, + Tags: git.AllTags, + ReferenceName: plumbing.ReferenceName("refs/heads/main"), + }) + if err != nil { + return err + } + } + } + + logrus.Debugf("'%s' has been git cloned successfully", dir) + + return nil +} diff --git a/pkg/recipe/recipe.go b/pkg/recipe/recipe.go index 433b7158..8d3ae2bb 100644 --- a/pkg/recipe/recipe.go +++ b/pkg/recipe/recipe.go @@ -2,7 +2,6 @@ package recipe import ( "fmt" - "os" "path" "path/filepath" "strings" @@ -11,9 +10,11 @@ import ( loader "coopcloud.tech/abra/pkg/client/stack" "coopcloud.tech/abra/pkg/compose" "coopcloud.tech/abra/pkg/config" + gitPkg "coopcloud.tech/abra/pkg/git" composetypes "github.com/docker/cli/cli/compose/types" "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/plumbing" + "github.com/sirupsen/logrus" ) // Recipe represents a recipe. @@ -65,23 +66,10 @@ func Get(recipeName string) (Recipe, error) { // EnsureExists checks whether a recipe has been cloned locally or not. func EnsureExists(recipe string) error { recipeDir := path.Join(config.ABRA_DIR, "apps", strings.ToLower(recipe)) - - if _, err := os.Stat(recipeDir); os.IsNotExist(err) { - url := fmt.Sprintf("%s/%s.git", config.REPOS_BASE_URL, recipe) - _, err := git.PlainClone(recipeDir, false, &git.CloneOptions{URL: url, Tags: git.AllTags}) - if err != nil { - // try with main branch because Git is being a Git - _, err := git.PlainClone(recipeDir, false, &git.CloneOptions{ - URL: url, - Tags: git.AllTags, - ReferenceName: plumbing.ReferenceName("refs/heads/main"), - }) - if err != nil { - return err - } - } + url := fmt.Sprintf("%s/%s.git", config.REPOS_BASE_URL, recipe) + if err := gitPkg.Clone(recipeDir, url); err != nil { + return err } - return nil } @@ -99,6 +87,8 @@ func EnsureVersion(recipeName, version string) error { return nil } + logrus.Debugf("read '%s' as tags for recipe '%s'", tags, recipeName) + var tagRef plumbing.ReferenceName if err := tags.ForEach(func(ref *plumbing.Reference) (err error) { if ref.Name().Short() == version { @@ -123,5 +113,7 @@ func EnsureVersion(recipeName, version string) error { return err } + logrus.Debugf("successfully checked '%s' out to '%s' in '%s'", recipeName, tagRef, recipeDir) + return nil } diff --git a/pkg/secret/pass.go b/pkg/secret/pass.go index 3f058aa3..5025dde7 100644 --- a/pkg/secret/pass.go +++ b/pkg/secret/pass.go @@ -19,6 +19,8 @@ func PassInsertSecret(secretValue, secretName, appName, server string) error { secretValue, server, appName, secretName, ) + logrus.Debugf("attempting to run '%s'", cmd) + if err := exec.Command("bash", "-c", cmd).Run(); err != nil { return err } @@ -39,6 +41,8 @@ func PassRmSecret(secretName, appName, server string) error { server, appName, secretName, ) + logrus.Debugf("attempting to run '%s'", cmd) + if err := exec.Command("bash", "-c", cmd).Run(); err != nil { return err } diff --git a/pkg/secret/secret.go b/pkg/secret/secret.go index e8437c11..22c0e01b 100644 --- a/pkg/secret/secret.go +++ b/pkg/secret/secret.go @@ -12,6 +12,7 @@ import ( "coopcloud.tech/abra/pkg/client" "coopcloud.tech/abra/pkg/config" "github.com/schultz-is/passgen" + "github.com/sirupsen/logrus" ) // secretValue represents a parsed `SECRET_FOO=v1 # length=bar` env var config @@ -33,6 +34,8 @@ func GeneratePasswords(count, length uint) ([]string, error) { return nil, err } + logrus.Debugf("generated '%s'", strings.Join(passwords, ", ")) + return passwords, nil } @@ -50,17 +53,24 @@ func GeneratePassphrases(count uint) ([]string, error) { return nil, err } + logrus.Debugf("generated '%s'", strings.Join(passphrases, ", ")) + return passphrases, nil } +// ReadSecretEnvVars reads secret env vars from an app env var config. func ReadSecretEnvVars(appEnv config.AppEnv) map[string]string { secretEnvVars := make(map[string]string) + for envVar := range appEnv { regex := regexp.MustCompile(`^SECRET.*VERSION.*`) if string(regex.Find([]byte(envVar))) != "" { secretEnvVars[envVar] = appEnv[envVar] } } + + logrus.Debugf("read '%s' as secrets from '%s'", secretEnvVars, appEnv) + return secretEnvVars } @@ -68,7 +78,9 @@ func ReadSecretEnvVars(appEnv config.AppEnv) map[string]string { func ParseSecretEnvVarName(secretEnvVar string) string { withoutPrefix := strings.TrimPrefix(secretEnvVar, "SECRET_") withoutSuffix := strings.TrimSuffix(withoutPrefix, "_VERSION") - return strings.ToLower(withoutSuffix) + name := strings.ToLower(withoutSuffix) + logrus.Debugf("parsed '%s' as name from '%s'", name, secretEnvVar) + return name } // TODO: should probably go in the config/app package? @@ -76,7 +88,9 @@ func ParseGeneratedSecretName(secret string, appEnv config.App) string { name := fmt.Sprintf("%s_", appEnv.StackName()) withoutAppName := strings.TrimPrefix(secret, name) idx := strings.LastIndex(withoutAppName, "_") - return withoutAppName[:idx] + parsed := withoutAppName[:idx] + logrus.Debugf("parsed '%s' as name from '%s'", parsed, secret) + return parsed } // TODO: should probably go in the config/app package? @@ -85,19 +99,23 @@ func ParseSecretEnvVarValue(secret string) (secretValue, error) { if len(values) == 0 { return secretValue{}, fmt.Errorf("unable to parse '%s'", secret) } + if len(values) == 1 { return secretValue{Version: values[0], Length: 0}, nil - } else { - split := strings.Split(values[1], "=") - parsed := split[len(split)-1] - stripped := strings.ReplaceAll(parsed, " ", "") - length, err := strconv.Atoi(stripped) - if err != nil { - return secretValue{}, err - } - version := strings.ReplaceAll(values[0], " ", "") - return secretValue{Version: version, Length: length}, nil } + + split := strings.Split(values[1], "=") + parsed := split[len(split)-1] + stripped := strings.ReplaceAll(parsed, " ", "") + length, err := strconv.Atoi(stripped) + if err != nil { + return secretValue{}, err + } + version := strings.ReplaceAll(values[0], " ", "") + + logrus.Debugf("parsed version '%s' and length '%s' from '%s'", version, length, secret) + + return secretValue{Version: version, Length: length}, nil } // GenerateSecrets generates secrets locally and sends them to a remote server for storage. @@ -114,6 +132,7 @@ func GenerateSecrets(secretEnvVars map[string]string, appName, server string) (m return } secretRemoteName := fmt.Sprintf("%s_%s_%s", appName, secretName, secretValue.Version) + logrus.Debugf("attempting to generate and store '%s' on '%s'", secretRemoteName, server) if secretValue.Length > 0 { passwords, err := GeneratePasswords(1, uint(secretValue.Length)) if err != nil { @@ -147,5 +166,7 @@ func GenerateSecrets(secretEnvVars map[string]string, appName, server string) (m } } + logrus.Debugf("generated and stored '%s' on '%s'", secrets, server) + return secrets, nil }