Compare commits
68 Commits
0.1.3-alph
...
0.1.7-alph
Author | SHA1 | Date | |
---|---|---|---|
7c0d883135 | |||
e78ced41fb | |||
e9113500d8 | |||
7368cabc49 | |||
f75e264811 | |||
8bfd76fd04 | |||
1cb5e3509d
|
|||
3cd2399cca | |||
11c4651a3b | |||
49f90674f2 | |||
74a70edb03 | |||
6fc5c31347 | |||
c616907b71 | |||
a58cea3e0a | |||
700f89425a | |||
8cc0a350e6 | |||
46e67fa420 | |||
cacbb5a0f1 | |||
e7046a15aa | |||
c1fd97c427 | |||
2f218bd99f | |||
48290aa316 | |||
db5cbfa992 | |||
4c11e813e8 | |||
6ae75e013a
|
|||
09f49cdc76 | |||
22118b88e4 | |||
e6db064149 | |||
3688ea9d69 | |||
7c4cdc530c | |||
49781c7e3f | |||
10b15d65b4 | |||
1c5d6d6357 | |||
75bdd59585 | |||
96bb145981
|
|||
c4c76f4848
|
|||
2076c566bb | |||
62f6327b66 | |||
6f9120b59c | |||
8c617a9f12 | |||
857d12d23c
|
|||
22c4d0d864
|
|||
e700e44363
|
|||
9faefd2592
|
|||
cd179175f5
|
|||
c0f92ca13d
|
|||
48d28c8dd1
|
|||
e840328e44 | |||
6f43778691 | |||
9783563fa6 | |||
1392afc015 | |||
886009975d | |||
b1147cd136 | |||
95a9013658 | |||
bd1bf3b0d6 | |||
7b349732ac | |||
a8ce64a9db | |||
96aa74a977 | |||
700f022790 | |||
d188327b17 | |||
fdd46a4d98 | |||
e00920643e | |||
754fe81e01 | |||
bece2e8351 | |||
e47d7029d7
|
|||
31edbbd32e
|
|||
0a1c73bf00
|
|||
a74a8bc21b |
@ -14,12 +14,12 @@ builds:
|
|||||||
dir: cmd/abra
|
dir: cmd/abra
|
||||||
goos:
|
goos:
|
||||||
- linux
|
- linux
|
||||||
|
- darwin
|
||||||
ldflags:
|
ldflags:
|
||||||
- "-X 'main.Commit={{ .Commit }}'"
|
- "-X 'main.Commit={{ .Commit }}'"
|
||||||
- "-X 'main.Version={{ .Version }}'"
|
- "-X 'main.Version={{ .Version }}'"
|
||||||
archives:
|
archives:
|
||||||
- replacements:
|
- replacements:
|
||||||
linux: Linux
|
|
||||||
386: i386
|
386: i386
|
||||||
amd64: x86_64
|
amd64: x86_64
|
||||||
format: binary
|
format: binary
|
||||||
@ -31,5 +31,10 @@ changelog:
|
|||||||
sort: asc
|
sort: asc
|
||||||
filters:
|
filters:
|
||||||
exclude:
|
exclude:
|
||||||
|
- "^WIP:"
|
||||||
|
- "^chore:"
|
||||||
- "^docs:"
|
- "^docs:"
|
||||||
|
- "^refactor:"
|
||||||
|
- "^style:"
|
||||||
- "^test:"
|
- "^test:"
|
||||||
|
- "^tests:"
|
||||||
|
@ -89,6 +89,7 @@ For developers, while using this `-alpha` format, the `y` part is the "major" ve
|
|||||||
- Make a new tag (e.g. `git tag 0.y.z-alpha`)
|
- Make a new tag (e.g. `git tag 0.y.z-alpha`)
|
||||||
- Push the new tag (e.g. `git push && git push --tags`)
|
- Push the new tag (e.g. `git push && git push --tags`)
|
||||||
- Wait until the build finishes on [build.coopcloud.tech](https://build.coopcloud.tech/coop-cloud/abra)
|
- Wait until the build finishes on [build.coopcloud.tech](https://build.coopcloud.tech/coop-cloud/abra)
|
||||||
|
- Deploy the new installer script (e.g. `cd ./scripts/installer && make`)
|
||||||
- Check the release worked, (e.g. `abra upgrade; abra version`)
|
- Check the release worked, (e.g. `abra upgrade; abra version`)
|
||||||
|
|
||||||
## Fork maintenance
|
## Fork maintenance
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package app
|
package app
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
@ -75,7 +74,6 @@ var appCpCommand = &cli.Command{
|
|||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx := context.Background()
|
|
||||||
cl, err := client.New(app.Server)
|
cl, err := client.New(app.Server)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
@ -83,7 +81,7 @@ var appCpCommand = &cli.Command{
|
|||||||
|
|
||||||
filters := filters.NewArgs()
|
filters := filters.NewArgs()
|
||||||
filters.Add("name", fmt.Sprintf("%s_%s", appEnv.StackName(), service))
|
filters.Add("name", fmt.Sprintf("%s_%s", appEnv.StackName(), service))
|
||||||
containers, err := cl.ContainerList(ctx, types.ContainerListOptions{Filters: filters})
|
containers, err := cl.ContainerList(c.Context, types.ContainerListOptions{Filters: filters})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -107,11 +105,11 @@ var appCpCommand = &cli.Command{
|
|||||||
}
|
}
|
||||||
|
|
||||||
copyOpts := types.CopyToContainerOptions{AllowOverwriteDirWithFile: false, CopyUIDGID: false}
|
copyOpts := types.CopyToContainerOptions{AllowOverwriteDirWithFile: false, CopyUIDGID: false}
|
||||||
if err := cl.CopyToContainer(ctx, container.ID, dstPath, content, copyOpts); err != nil {
|
if err := cl.CopyToContainer(c.Context, container.ID, dstPath, content, copyOpts); err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
content, _, err := cl.CopyFromContainer(ctx, container.ID, srcPath)
|
content, _, err := cl.CopyFromContainer(c.Context, container.ID, srcPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,10 @@ var appDeployCommand = &cli.Command{
|
|||||||
for k, v := range abraShEnv {
|
for k, v := range abraShEnv {
|
||||||
app.Env[k] = v
|
app.Env[k] = v
|
||||||
}
|
}
|
||||||
app.Env["STACK_NAME"] = app.StackName()
|
|
||||||
|
if _, exists := app.Env["STACK_NAME"]; !exists {
|
||||||
|
app.Env["STACK_NAME"] = app.StackName()
|
||||||
|
}
|
||||||
|
|
||||||
composeFiles, err := config.GetAppComposeFiles(app.Type, app.Env)
|
composeFiles, err := config.GetAppComposeFiles(app.Type, app.Env)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -39,7 +42,7 @@ var appDeployCommand = &cli.Command{
|
|||||||
}
|
}
|
||||||
deployOpts := stack.Deploy{
|
deployOpts := stack.Deploy{
|
||||||
Composefiles: composeFiles,
|
Composefiles: composeFiles,
|
||||||
Namespace: app.StackName(),
|
Namespace: app.Env["STACK_NAME"],
|
||||||
Prune: false,
|
Prune: false,
|
||||||
ResolveImage: stack.ResolveImageAlways,
|
ResolveImage: stack.ResolveImageAlways,
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package app
|
package app
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
@ -18,12 +17,11 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// stackLogs lists logs for all stack services
|
// stackLogs lists logs for all stack services
|
||||||
func stackLogs(stackName string, client *dockerClient.Client) {
|
func stackLogs(c *cli.Context, stackName string, client *dockerClient.Client) {
|
||||||
ctx := context.Background()
|
|
||||||
filters := filters.NewArgs()
|
filters := filters.NewArgs()
|
||||||
filters.Add("name", stackName)
|
filters.Add("name", stackName)
|
||||||
serviceOpts := types.ServiceListOptions{Filters: filters}
|
serviceOpts := types.ServiceListOptions{Filters: filters}
|
||||||
services, err := client.ServiceList(ctx, serviceOpts)
|
services, err := client.ServiceList(c.Context, serviceOpts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -40,7 +38,7 @@ func stackLogs(stackName string, client *dockerClient.Client) {
|
|||||||
Tail: "20",
|
Tail: "20",
|
||||||
Timestamps: true,
|
Timestamps: true,
|
||||||
}
|
}
|
||||||
logs, err := client.ServiceLogs(ctx, s, logOpts)
|
logs, err := client.ServiceLogs(c.Context, s, logOpts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -65,7 +63,6 @@ var appLogsCommand = &cli.Command{
|
|||||||
Action: func(c *cli.Context) error {
|
Action: func(c *cli.Context) error {
|
||||||
app := internal.ValidateApp(c)
|
app := internal.ValidateApp(c)
|
||||||
|
|
||||||
ctx := context.Background()
|
|
||||||
cl, err := client.New(app.Server)
|
cl, err := client.New(app.Server)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
@ -74,7 +71,7 @@ var appLogsCommand = &cli.Command{
|
|||||||
serviceName := c.Args().Get(1)
|
serviceName := c.Args().Get(1)
|
||||||
if serviceName == "" {
|
if serviceName == "" {
|
||||||
logrus.Debug("tailing logs for all app services")
|
logrus.Debug("tailing logs for all app services")
|
||||||
stackLogs(app.StackName(), cl)
|
stackLogs(c, app.StackName(), cl)
|
||||||
}
|
}
|
||||||
logrus.Debugf("tailing logs for '%s'", serviceName)
|
logrus.Debugf("tailing logs for '%s'", serviceName)
|
||||||
|
|
||||||
@ -82,7 +79,7 @@ var appLogsCommand = &cli.Command{
|
|||||||
filters := filters.NewArgs()
|
filters := filters.NewArgs()
|
||||||
filters.Add("name", service)
|
filters.Add("name", service)
|
||||||
serviceOpts := types.ServiceListOptions{Filters: filters}
|
serviceOpts := types.ServiceListOptions{Filters: filters}
|
||||||
services, err := cl.ServiceList(ctx, serviceOpts)
|
services, err := cl.ServiceList(c.Context, serviceOpts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -98,7 +95,7 @@ var appLogsCommand = &cli.Command{
|
|||||||
Tail: "20",
|
Tail: "20",
|
||||||
Timestamps: true,
|
Timestamps: true,
|
||||||
}
|
}
|
||||||
logs, err := cl.ServiceLogs(ctx, services[0].ID, logOpts)
|
logs, err := cl.ServiceLogs(c.Context, services[0].ID, logOpts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,6 @@ import (
|
|||||||
"coopcloud.tech/abra/cli/internal"
|
"coopcloud.tech/abra/cli/internal"
|
||||||
"coopcloud.tech/abra/pkg/catalogue"
|
"coopcloud.tech/abra/pkg/catalogue"
|
||||||
"coopcloud.tech/abra/pkg/config"
|
"coopcloud.tech/abra/pkg/config"
|
||||||
recipePkg "coopcloud.tech/abra/pkg/recipe"
|
|
||||||
"coopcloud.tech/abra/pkg/secret"
|
"coopcloud.tech/abra/pkg/secret"
|
||||||
"github.com/AlecAivazis/survey/v2"
|
"github.com/AlecAivazis/survey/v2"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
@ -86,7 +85,7 @@ var appNewCommand = &cli.Command{
|
|||||||
if c.NArg() > 0 {
|
if c.NArg() > 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for name, _ := range catl {
|
for name := range catl {
|
||||||
fmt.Println(name)
|
fmt.Println(name)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -172,16 +171,6 @@ func action(c *cli.Context) error {
|
|||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
recipeMeta, err := catalogue.GetRecipeMeta(recipe.Name)
|
|
||||||
if err != nil {
|
|
||||||
logrus.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
latestVersion := recipeMeta.LatestVersion()
|
|
||||||
if err := recipePkg.EnsureVersion(recipe.Name, latestVersion); err != nil {
|
|
||||||
logrus.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := ensureServerFlag(); err != nil {
|
if err := ensureServerFlag(); err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -200,7 +189,7 @@ func action(c *cli.Context) error {
|
|||||||
}
|
}
|
||||||
logrus.Debugf("'%s' sanitised as '%s' for new app", newAppName, sanitisedAppName)
|
logrus.Debugf("'%s' sanitised as '%s' for new app", newAppName, sanitisedAppName)
|
||||||
|
|
||||||
if err := config.CopyAppEnvSample(recipe.Name, newAppName, newAppServer); err != nil {
|
if err := config.TemplateAppEnvSample(recipe.Name, newAppName, newAppServer, domain, recipe.Name); err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -224,7 +213,18 @@ func action(c *cli.Context) error {
|
|||||||
tableCol := []string{"Name", "Domain", "Type", "Server"}
|
tableCol := []string{"Name", "Domain", "Type", "Server"}
|
||||||
table := abraFormatter.CreateTable(tableCol)
|
table := abraFormatter.CreateTable(tableCol)
|
||||||
table.Append([]string{sanitisedAppName, domain, recipe.Name, newAppServer})
|
table.Append([]string{sanitisedAppName, domain, recipe.Name, newAppServer})
|
||||||
defer table.Render()
|
|
||||||
|
fmt.Println("")
|
||||||
|
fmt.Println(fmt.Sprintf("New '%s' created! Here is your new app overview:", recipe.Name))
|
||||||
|
fmt.Println("")
|
||||||
|
table.Render()
|
||||||
|
fmt.Println("")
|
||||||
|
fmt.Println("You can configure this app by running the following:")
|
||||||
|
fmt.Println(fmt.Sprintf("\n abra app config %s", sanitisedAppName))
|
||||||
|
fmt.Println("")
|
||||||
|
fmt.Println("You can deploy this app by running the following:")
|
||||||
|
fmt.Println(fmt.Sprintf("\n abra app deploy %s", sanitisedAppName))
|
||||||
|
fmt.Println("")
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package app
|
package app
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@ -23,7 +22,6 @@ var appPsCommand = &cli.Command{
|
|||||||
Action: func(c *cli.Context) error {
|
Action: func(c *cli.Context) error {
|
||||||
app := internal.ValidateApp(c)
|
app := internal.ValidateApp(c)
|
||||||
|
|
||||||
ctx := context.Background()
|
|
||||||
cl, err := client.New(app.Server)
|
cl, err := client.New(app.Server)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
@ -32,7 +30,7 @@ var appPsCommand = &cli.Command{
|
|||||||
filters := filters.NewArgs()
|
filters := filters.NewArgs()
|
||||||
filters.Add("name", app.StackName())
|
filters.Add("name", app.StackName())
|
||||||
|
|
||||||
containers, err := cl.ContainerList(ctx, types.ContainerListOptions{Filters: filters})
|
containers, err := cl.ContainerList(c.Context, types.ContainerListOptions{Filters: filters})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package app
|
package app
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
@ -54,7 +53,6 @@ var appRemoveCommand = &cli.Command{
|
|||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx := context.Background()
|
|
||||||
cl, err := client.New(app.Server)
|
cl, err := client.New(app.Server)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
@ -72,7 +70,7 @@ var appRemoveCommand = &cli.Command{
|
|||||||
|
|
||||||
fs := filters.NewArgs()
|
fs := filters.NewArgs()
|
||||||
fs.Add("name", app.Name)
|
fs.Add("name", app.Name)
|
||||||
secretList, err := cl.SecretList(ctx, types.SecretListOptions{Filters: fs})
|
secretList, err := cl.SecretList(c.Context, types.SecretListOptions{Filters: fs})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -99,7 +97,7 @@ var appRemoveCommand = &cli.Command{
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, name := range secretNamesToRemove {
|
for _, name := range secretNamesToRemove {
|
||||||
err := cl.SecretRemove(ctx, secrets[name])
|
err := cl.SecretRemove(c.Context, secrets[name])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -109,7 +107,7 @@ var appRemoveCommand = &cli.Command{
|
|||||||
logrus.Info("no secrets to remove")
|
logrus.Info("no secrets to remove")
|
||||||
}
|
}
|
||||||
|
|
||||||
volumeListOKBody, err := cl.VolumeList(ctx, fs)
|
volumeListOKBody, err := cl.VolumeList(c.Context, fs)
|
||||||
volumeList := volumeListOKBody.Volumes
|
volumeList := volumeListOKBody.Volumes
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
@ -134,7 +132,7 @@ var appRemoveCommand = &cli.Command{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, vol := range removeVols {
|
for _, vol := range removeVols {
|
||||||
err := cl.VolumeRemove(ctx, vol, internal.Force) // last argument is for force removing
|
err := cl.VolumeRemove(c.Context, vol, internal.Force) // last argument is for force removing
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package app
|
package app
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
@ -50,7 +49,6 @@ var appRunCommand = &cli.Command{
|
|||||||
internal.ShowSubcommandHelpAndError(c, errors.New("no <args> provided?"))
|
internal.ShowSubcommandHelpAndError(c, errors.New("no <args> provided?"))
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx := context.Background()
|
|
||||||
cl, err := client.New(app.Server)
|
cl, err := client.New(app.Server)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
@ -60,7 +58,7 @@ var appRunCommand = &cli.Command{
|
|||||||
filters := filters.NewArgs()
|
filters := filters.NewArgs()
|
||||||
filters.Add("name", fmt.Sprintf("%s_%s", app.StackName(), serviceName))
|
filters.Add("name", fmt.Sprintf("%s_%s", app.StackName(), serviceName))
|
||||||
|
|
||||||
containers, err := cl.ContainerList(ctx, types.ContainerListOptions{Filters: filters})
|
containers, err := cl.ContainerList(c.Context, types.ContainerListOptions{Filters: filters})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package app
|
package app
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
@ -144,7 +143,6 @@ var appSecretRmCommand = &cli.Command{
|
|||||||
internal.ShowSubcommandHelpAndError(c, errors.New("no secret(s) specified?"))
|
internal.ShowSubcommandHelpAndError(c, errors.New("no secret(s) specified?"))
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx := context.Background()
|
|
||||||
cl, err := client.New(app.Server)
|
cl, err := client.New(app.Server)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
@ -152,7 +150,7 @@ var appSecretRmCommand = &cli.Command{
|
|||||||
|
|
||||||
filters := filters.NewArgs()
|
filters := filters.NewArgs()
|
||||||
filters.Add("name", app.StackName())
|
filters.Add("name", app.StackName())
|
||||||
secretList, err := cl.SecretList(ctx, types.SecretListOptions{Filters: filters})
|
secretList, err := cl.SecretList(c.Context, types.SecretListOptions{Filters: filters})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -162,7 +160,7 @@ var appSecretRmCommand = &cli.Command{
|
|||||||
secretName := cont.Spec.Annotations.Name
|
secretName := cont.Spec.Annotations.Name
|
||||||
parsed := secret.ParseGeneratedSecretName(secretName, app)
|
parsed := secret.ParseGeneratedSecretName(secretName, app)
|
||||||
if allSecrets {
|
if allSecrets {
|
||||||
if err := cl.SecretRemove(ctx, secretName); err != nil {
|
if err := cl.SecretRemove(c.Context, secretName); err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
if internal.Pass {
|
if internal.Pass {
|
||||||
@ -172,7 +170,7 @@ var appSecretRmCommand = &cli.Command{
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if parsed == secretToRm {
|
if parsed == secretToRm {
|
||||||
if err := cl.SecretRemove(ctx, secretName); err != nil {
|
if err := cl.SecretRemove(c.Context, secretName); err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
if internal.Pass {
|
if internal.Pass {
|
||||||
@ -199,7 +197,6 @@ var appSecretLsCommand = &cli.Command{
|
|||||||
tableCol := []string{"Name", "Version", "Generated Name", "Created On Server"}
|
tableCol := []string{"Name", "Version", "Generated Name", "Created On Server"}
|
||||||
table := abraFormatter.CreateTable(tableCol)
|
table := abraFormatter.CreateTable(tableCol)
|
||||||
|
|
||||||
ctx := context.Background()
|
|
||||||
cl, err := client.New(app.Server)
|
cl, err := client.New(app.Server)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
@ -207,7 +204,7 @@ var appSecretLsCommand = &cli.Command{
|
|||||||
|
|
||||||
filters := filters.NewArgs()
|
filters := filters.NewArgs()
|
||||||
filters.Add("name", app.StackName())
|
filters.Add("name", app.StackName())
|
||||||
secretList, err := cl.SecretList(ctx, types.SecretListOptions{Filters: filters})
|
secretList, err := cl.SecretList(c.Context, types.SecretListOptions{Filters: filters})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package app
|
package app
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"coopcloud.tech/abra/cli/internal"
|
"coopcloud.tech/abra/cli/internal"
|
||||||
@ -24,14 +23,13 @@ volumes as eligiblef or pruning once undeployed.
|
|||||||
Action: func(c *cli.Context) error {
|
Action: func(c *cli.Context) error {
|
||||||
app := internal.ValidateApp(c)
|
app := internal.ValidateApp(c)
|
||||||
|
|
||||||
ctx := context.Background()
|
|
||||||
cl, err := client.New(app.Server)
|
cl, err := client.New(app.Server)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
rmOpts := stack.Remove{Namespaces: []string{app.StackName()}}
|
rmOpts := stack.Remove{Namespaces: []string{app.StackName()}}
|
||||||
if err := stack.RunRemove(ctx, cl, rmOpts); err != nil {
|
if err := stack.RunRemove(c.Context, cl, rmOpts); err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package app
|
package app
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
abraFormatter "coopcloud.tech/abra/cli/formatter"
|
abraFormatter "coopcloud.tech/abra/cli/formatter"
|
||||||
@ -20,8 +19,7 @@ var appVolumeListCommand = &cli.Command{
|
|||||||
Action: func(c *cli.Context) error {
|
Action: func(c *cli.Context) error {
|
||||||
app := internal.ValidateApp(c)
|
app := internal.ValidateApp(c)
|
||||||
|
|
||||||
ctx := context.Background()
|
volumeList, err := client.GetVolumes(c.Context, app.Server, app.Name)
|
||||||
volumeList, err := client.GetVolumes(ctx, app.Server, app.Name)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -53,8 +51,7 @@ var appVolumeRemoveCommand = &cli.Command{
|
|||||||
Action: func(c *cli.Context) error {
|
Action: func(c *cli.Context) error {
|
||||||
app := internal.ValidateApp(c)
|
app := internal.ValidateApp(c)
|
||||||
|
|
||||||
ctx := context.Background()
|
volumeList, err := client.GetVolumes(c.Context, app.Server, app.Name)
|
||||||
volumeList, err := client.GetVolumes(ctx, app.Server, app.Name)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -74,7 +71,7 @@ var appVolumeRemoveCommand = &cli.Command{
|
|||||||
volumesToRemove = volumeNames
|
volumesToRemove = volumeNames
|
||||||
}
|
}
|
||||||
|
|
||||||
err = client.RemoveVolumes(ctx, app.Server, volumesToRemove, internal.Force)
|
err = client.RemoveVolumes(c.Context, app.Server, volumesToRemove, internal.Force)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
package catalogue
|
package catalogue
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
"path"
|
"path"
|
||||||
"strings"
|
|
||||||
|
|
||||||
|
"coopcloud.tech/abra/cli/formatter"
|
||||||
"coopcloud.tech/abra/pkg/catalogue"
|
"coopcloud.tech/abra/pkg/catalogue"
|
||||||
"coopcloud.tech/abra/pkg/config"
|
"coopcloud.tech/abra/pkg/config"
|
||||||
"coopcloud.tech/abra/pkg/git"
|
"coopcloud.tech/abra/pkg/git"
|
||||||
@ -11,30 +14,140 @@ import (
|
|||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// CatalogueSkipList is all the repos that are not recipes.
|
||||||
|
var CatalogueSkipList = map[string]bool{
|
||||||
|
"abra": true,
|
||||||
|
"abra-bash": true,
|
||||||
|
"abra-apps": true,
|
||||||
|
"abra-aur": true,
|
||||||
|
"abra-capsul": true,
|
||||||
|
"abra-gandi": true,
|
||||||
|
"abra-hetzner": true,
|
||||||
|
"apps": true,
|
||||||
|
"aur-abra-git": true,
|
||||||
|
"auto-apps-json": true,
|
||||||
|
"auto-mirror": true,
|
||||||
|
"backup-bot": true,
|
||||||
|
"coopcloud.tech": true,
|
||||||
|
"coturn": true,
|
||||||
|
"docker-cp-deploy": true,
|
||||||
|
"docker-dind-bats-kcov": true,
|
||||||
|
"docs.coopcloud.tech": true,
|
||||||
|
"example": true,
|
||||||
|
"gardening": true,
|
||||||
|
"go-abra": true,
|
||||||
|
"organising": true,
|
||||||
|
"pyabra": true,
|
||||||
|
"radicle-seed-node": true,
|
||||||
|
"stack-ssh-deploy": true,
|
||||||
|
"swarm-cronjob": true,
|
||||||
|
"tagcmp": true,
|
||||||
|
"tyop": true,
|
||||||
|
}
|
||||||
|
|
||||||
var catalogueGenerateCommand = &cli.Command{
|
var catalogueGenerateCommand = &cli.Command{
|
||||||
Name: "generate",
|
Name: "generate",
|
||||||
Aliases: []string{"g"},
|
Aliases: []string{"g"},
|
||||||
Usage: "Generate a new copy of the catalogue",
|
Usage: "Generate a new copy of the catalogue",
|
||||||
BashComplete: func(c *cli.Context) {},
|
ArgsUsage: "[<recipe>]",
|
||||||
Action: func(c *cli.Context) error {
|
BashComplete: func(c *cli.Context) {
|
||||||
catl, err := catalogue.ReadRecipeCatalogue()
|
catl, err := catalogue.ReadRecipeCatalogue()
|
||||||
|
if err != nil {
|
||||||
|
logrus.Warn(err)
|
||||||
|
}
|
||||||
|
if c.NArg() > 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for name := range catl {
|
||||||
|
fmt.Println(name)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Action: func(c *cli.Context) error {
|
||||||
|
recipeName := c.Args().First()
|
||||||
|
|
||||||
|
repos, err := catalogue.ReadReposMetadata()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for recipeName, recipeMeta := range catl {
|
logrus.Debugf("ensuring '%v' recipe(s) are locally present and up-to-date", len(repos))
|
||||||
recipeDir := path.Join(config.ABRA_DIR, "apps", strings.ToLower(recipeName))
|
|
||||||
if err := git.Clone(recipeDir, recipeMeta.Repository); err != nil {
|
|
||||||
logrus.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := git.EnsureUpToDate(recipeDir); err != nil {
|
retrieveBar := formatter.CreateProgressbar(len(repos), "retrieving recipes...")
|
||||||
logrus.Fatal(err)
|
ch := make(chan string, len(repos))
|
||||||
}
|
for _, repoMeta := range repos {
|
||||||
|
go func(rm catalogue.RepoMeta) {
|
||||||
|
if recipeName != "" && recipeName != rm.Name {
|
||||||
|
ch <- rm.Name
|
||||||
|
retrieveBar.Add(1)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if _, exists := CatalogueSkipList[rm.Name]; exists {
|
||||||
|
ch <- rm.Name
|
||||||
|
retrieveBar.Add(1)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
recipeDir := path.Join(config.ABRA_DIR, "apps", rm.Name)
|
||||||
|
|
||||||
|
if err := git.Clone(recipeDir, rm.SSHURL); err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := git.EnsureUpToDate(recipeDir); err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ch <- rm.Name
|
||||||
|
retrieveBar.Add(1)
|
||||||
|
}(repoMeta)
|
||||||
}
|
}
|
||||||
|
|
||||||
// for reach app, build the recipemeta from parsing
|
for range repos {
|
||||||
// spit out a JSON file
|
<-ch // wait for everything
|
||||||
|
}
|
||||||
|
|
||||||
|
catl := make(catalogue.RecipeCatalogue)
|
||||||
|
catlBar := formatter.CreateProgressbar(len(repos), "generating catalogue...")
|
||||||
|
for _, recipeMeta := range repos {
|
||||||
|
if recipeName != "" && recipeName != recipeMeta.Name {
|
||||||
|
catlBar.Add(1)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, exists := CatalogueSkipList[recipeMeta.Name]; exists {
|
||||||
|
catlBar.Add(1)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
versions, err := catalogue.GetRecipeVersions(recipeMeta.Name)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
catl[recipeMeta.Name] = catalogue.RecipeMeta{
|
||||||
|
Name: recipeMeta.Name,
|
||||||
|
Repository: recipeMeta.CloneURL,
|
||||||
|
Icon: recipeMeta.AvatarURL,
|
||||||
|
DefaultBranch: recipeMeta.DefaultBranch,
|
||||||
|
Description: recipeMeta.Description,
|
||||||
|
Website: recipeMeta.Website,
|
||||||
|
Versions: versions,
|
||||||
|
// Category: ..., // FIXME: once we sort out the machine-readable catalogue interface
|
||||||
|
// Features: ..., // FIXME: once we figure out the machine-readable catalogue interface
|
||||||
|
}
|
||||||
|
catlBar.Add(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
recipesJSON, err := json.MarshalIndent(catl, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ioutil.WriteFile(config.APPS_JSON, recipesJSON, 0644); err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Infof("generated new recipe catalogue in '%s'", config.APPS_JSON)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
|
31
cli/cli.go
31
cli/cli.go
@ -4,11 +4,14 @@ package cli
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"path"
|
||||||
|
|
||||||
"coopcloud.tech/abra/cli/app"
|
"coopcloud.tech/abra/cli/app"
|
||||||
"coopcloud.tech/abra/cli/catalogue"
|
"coopcloud.tech/abra/cli/catalogue"
|
||||||
"coopcloud.tech/abra/cli/recipe"
|
"coopcloud.tech/abra/cli/recipe"
|
||||||
"coopcloud.tech/abra/cli/server"
|
"coopcloud.tech/abra/cli/server"
|
||||||
|
"coopcloud.tech/abra/pkg/config"
|
||||||
|
logrusStack "github.com/Gurpartap/logrus-stack"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
@ -64,7 +67,7 @@ func RunApp(version, commit string) {
|
|||||||
DebugFlag,
|
DebugFlag,
|
||||||
},
|
},
|
||||||
Authors: []*cli.Author{
|
Authors: []*cli.Author{
|
||||||
&cli.Author{
|
{
|
||||||
Name: "Autonomic Co-op",
|
Name: "Autonomic Co-op",
|
||||||
Email: "helo@autonomic.zone",
|
Email: "helo@autonomic.zone",
|
||||||
},
|
},
|
||||||
@ -76,12 +79,34 @@ func RunApp(version, commit string) {
|
|||||||
app.Before = func(c *cli.Context) error {
|
app.Before = func(c *cli.Context) error {
|
||||||
if Debug {
|
if Debug {
|
||||||
logrus.SetLevel(logrus.DebugLevel)
|
logrus.SetLevel(logrus.DebugLevel)
|
||||||
|
logrus.SetFormatter(&logrus.TextFormatter{})
|
||||||
|
logrus.SetOutput(os.Stderr)
|
||||||
|
logrus.AddHook(logrusStack.StandardHook())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
paths := []string{
|
||||||
|
config.ABRA_DIR,
|
||||||
|
path.Join(config.ABRA_DIR, "servers"),
|
||||||
|
path.Join(config.ABRA_DIR, "apps"),
|
||||||
|
path.Join(config.ABRA_DIR, "vendor"),
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, path := range paths {
|
||||||
|
if err := os.Mkdir(path, 0755); err != nil {
|
||||||
|
if !os.IsExist(err) {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
logrus.Debugf("'%s' already created, moving on...", path)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
logrus.Debugf("'%s' is missing, creating...", path)
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debugf("abra version '%s', commit '%s'", version, commit)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
logrus.Debugf("Flying abra version '%s', commit '%s', enjoy the ride", version, commit)
|
|
||||||
|
|
||||||
if err := app.Run(os.Args); err != nil {
|
if err := app.Run(os.Args); err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ import (
|
|||||||
"github.com/docker/cli/cli/command/formatter"
|
"github.com/docker/cli/cli/command/formatter"
|
||||||
"github.com/docker/go-units"
|
"github.com/docker/go-units"
|
||||||
"github.com/olekukonko/tablewriter"
|
"github.com/olekukonko/tablewriter"
|
||||||
|
"github.com/schollz/progressbar/v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
func ShortenID(str string) string {
|
func ShortenID(str string) string {
|
||||||
@ -37,3 +38,15 @@ func CreateTable(columns []string) *tablewriter.Table {
|
|||||||
table.SetHeader(columns)
|
table.SetHeader(columns)
|
||||||
return table
|
return table
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreateProgressbar generates a progress bar
|
||||||
|
func CreateProgressbar(length int, title string) *progressbar.ProgressBar {
|
||||||
|
return progressbar.NewOptions(
|
||||||
|
length,
|
||||||
|
progressbar.OptionClearOnFinish(),
|
||||||
|
progressbar.OptionSetPredictTime(false),
|
||||||
|
progressbar.OptionShowCount(),
|
||||||
|
progressbar.OptionFullWidth(),
|
||||||
|
progressbar.OptionSetDescription(title),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
@ -99,7 +99,7 @@ var recipeLintCommand = &cli.Command{
|
|||||||
if c.NArg() > 0 {
|
if c.NArg() > 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for name, _ := range catl {
|
for name := range catl {
|
||||||
fmt.Println(name)
|
fmt.Println(name)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -4,6 +4,30 @@ import (
|
|||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var Major bool
|
||||||
|
var MajorFlag = &cli.BoolFlag{
|
||||||
|
Name: "major",
|
||||||
|
Value: false,
|
||||||
|
Aliases: []string{"ma", "x"},
|
||||||
|
Destination: &Major,
|
||||||
|
}
|
||||||
|
|
||||||
|
var Minor bool
|
||||||
|
var MinorFlag = &cli.BoolFlag{
|
||||||
|
Name: "minor",
|
||||||
|
Value: false,
|
||||||
|
Aliases: []string{"mi", "y"},
|
||||||
|
Destination: &Minor,
|
||||||
|
}
|
||||||
|
|
||||||
|
var Patch bool
|
||||||
|
var PatchFlag = &cli.BoolFlag{
|
||||||
|
Name: "patch",
|
||||||
|
Value: false,
|
||||||
|
Aliases: []string{"p", "z"},
|
||||||
|
Destination: &Patch,
|
||||||
|
}
|
||||||
|
|
||||||
// RecipeCommand defines all recipe related sub-commands.
|
// RecipeCommand defines all recipe related sub-commands.
|
||||||
var RecipeCommand = &cli.Command{
|
var RecipeCommand = &cli.Command{
|
||||||
Name: "recipe",
|
Name: "recipe",
|
||||||
@ -18,6 +42,7 @@ Cloud community and you can use Abra to read them and create apps for you.
|
|||||||
Subcommands: []*cli.Command{
|
Subcommands: []*cli.Command{
|
||||||
recipeListCommand,
|
recipeListCommand,
|
||||||
recipeVersionCommand,
|
recipeVersionCommand,
|
||||||
|
recipeReleaseCommand,
|
||||||
recipeNewCommand,
|
recipeNewCommand,
|
||||||
recipeUpgradeCommand,
|
recipeUpgradeCommand,
|
||||||
recipeSyncCommand,
|
recipeSyncCommand,
|
||||||
|
288
cli/recipe/release.go
Normal file
288
cli/recipe/release.go
Normal file
@ -0,0 +1,288 @@
|
|||||||
|
package recipe
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"path"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"coopcloud.tech/abra/cli/internal"
|
||||||
|
"coopcloud.tech/abra/pkg/config"
|
||||||
|
"coopcloud.tech/abra/pkg/recipe"
|
||||||
|
"coopcloud.tech/tagcmp"
|
||||||
|
"github.com/AlecAivazis/survey/v2"
|
||||||
|
"github.com/go-git/go-git/v5"
|
||||||
|
"github.com/go-git/go-git/v5/plumbing"
|
||||||
|
"github.com/go-git/go-git/v5/plumbing/object"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
var Push bool
|
||||||
|
var PushFlag = &cli.BoolFlag{
|
||||||
|
Name: "push",
|
||||||
|
Value: false,
|
||||||
|
Destination: &Push,
|
||||||
|
}
|
||||||
|
|
||||||
|
var Dry bool
|
||||||
|
var DryFlag = &cli.BoolFlag{
|
||||||
|
Name: "dry-run",
|
||||||
|
Value: false,
|
||||||
|
Aliases: []string{"d"},
|
||||||
|
Destination: &Dry,
|
||||||
|
}
|
||||||
|
|
||||||
|
var CommitMessage string
|
||||||
|
var CommitMessageFlag = &cli.StringFlag{
|
||||||
|
Name: "commit-message",
|
||||||
|
Usage: "commit message. Implies --commit",
|
||||||
|
Aliases: []string{"cm"},
|
||||||
|
Destination: &CommitMessage,
|
||||||
|
}
|
||||||
|
|
||||||
|
var Commit bool
|
||||||
|
var CommitFlag = &cli.BoolFlag{
|
||||||
|
Name: "commit",
|
||||||
|
Usage: "add compose.yml to staging area and commit changes",
|
||||||
|
Value: false,
|
||||||
|
Aliases: []string{"c"},
|
||||||
|
Destination: &Commit,
|
||||||
|
}
|
||||||
|
|
||||||
|
var recipeReleaseCommand = &cli.Command{
|
||||||
|
Name: "release",
|
||||||
|
Usage: "tag a recipe",
|
||||||
|
Aliases: []string{"rl"},
|
||||||
|
ArgsUsage: "<recipe> [<tag>]",
|
||||||
|
Description: `
|
||||||
|
This command is used to specify a new tag for a recipe. These tags are used to
|
||||||
|
identify different versions of the recipe and are published on the Co-op Cloud
|
||||||
|
recipe catalogue.
|
||||||
|
|
||||||
|
These tags take the following form:
|
||||||
|
|
||||||
|
a.b.c+x.y.z
|
||||||
|
|
||||||
|
Where the "a.b.c" part is maintained as a semantic version of the recipe by the
|
||||||
|
recipe maintainer. And the "x.y.z" part is the image tag of the recipe "app"
|
||||||
|
service (the main container which contains the software to be used).
|
||||||
|
|
||||||
|
We maintain a semantic versioning scheme ("a.b.c") alongside the libre app
|
||||||
|
versioning scheme in order to maximise the chances that the nature of recipe
|
||||||
|
updates are properly communicated.
|
||||||
|
|
||||||
|
Abra does its best to read the "a.b.c" version scheme and communicate what
|
||||||
|
action needs to be taken when performing different operations such as an update
|
||||||
|
or a rollback of an app.
|
||||||
|
`,
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
DryFlag,
|
||||||
|
PatchFlag,
|
||||||
|
MinorFlag,
|
||||||
|
MajorFlag,
|
||||||
|
PushFlag,
|
||||||
|
CommitFlag,
|
||||||
|
CommitMessageFlag,
|
||||||
|
},
|
||||||
|
Action: func(c *cli.Context) error {
|
||||||
|
recipe := internal.ValidateRecipe(c)
|
||||||
|
directory := path.Join(config.APPS_DIR, recipe.Name)
|
||||||
|
tagstring := c.Args().Get(1)
|
||||||
|
imagesTmp := getImageVersions(recipe)
|
||||||
|
mainApp := getMainApp(recipe)
|
||||||
|
mainAppVersion := imagesTmp[mainApp]
|
||||||
|
if mainAppVersion == "" {
|
||||||
|
logrus.Fatal("main app version is empty?")
|
||||||
|
}
|
||||||
|
|
||||||
|
if tagstring != "" {
|
||||||
|
_, err := tagcmp.Parse(tagstring)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Fatal("invalid tag specified")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if Commit || (CommitMessage != "") {
|
||||||
|
commitRepo, err := git.PlainOpen(directory)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
commitWorktree, err := commitRepo.Worktree()
|
||||||
|
if err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if CommitMessage == "" {
|
||||||
|
prompt := &survey.Input{
|
||||||
|
Message: "commit message",
|
||||||
|
}
|
||||||
|
survey.AskOne(prompt, &CommitMessage)
|
||||||
|
}
|
||||||
|
err = commitWorktree.AddGlob("compose.**yml")
|
||||||
|
if err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
logrus.Debug("staged compose.**yml for commit")
|
||||||
|
|
||||||
|
_, err = commitWorktree.Commit(CommitMessage, &git.CommitOptions{})
|
||||||
|
if err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
logrus.Info("changes commited")
|
||||||
|
}
|
||||||
|
|
||||||
|
repo, err := git.PlainOpen(directory)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
head, err := repo.Head()
|
||||||
|
if err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// bumpType is used to decide what part of the tag should be incremented
|
||||||
|
bumpType := btoi(Major)*4 + btoi(Minor)*2 + btoi(Patch)
|
||||||
|
if bumpType != 0 {
|
||||||
|
// a bitwise check if the number is a power of 2
|
||||||
|
if (bumpType & (bumpType - 1)) != 0 {
|
||||||
|
logrus.Fatal("you can only use one of: --major, --minor, --patch.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if tagstring != "" {
|
||||||
|
if bumpType > 0 {
|
||||||
|
logrus.Warn("user specified a version number and --major/--minor/--patch at the same time! using version number...")
|
||||||
|
}
|
||||||
|
tag, err := tagcmp.Parse(tagstring)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
if tag.MissingMinor {
|
||||||
|
tag.Minor = "0"
|
||||||
|
tag.MissingMinor = false
|
||||||
|
}
|
||||||
|
if tag.MissingPatch {
|
||||||
|
tag.Patch = "0"
|
||||||
|
tag.MissingPatch = false
|
||||||
|
}
|
||||||
|
tagstring = fmt.Sprintf("%s+%s", tag.String(), mainAppVersion)
|
||||||
|
if Dry {
|
||||||
|
logrus.Info(fmt.Sprintf("dry run only: NOT creating tag %s at %s", tagstring, head.Hash()))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
repo.CreateTag(tagstring, head.Hash(), nil) /* &git.CreateTagOptions{
|
||||||
|
Message: tag,
|
||||||
|
})*/
|
||||||
|
logrus.Info(fmt.Sprintf("created tag %s at %s", tagstring, head.Hash()))
|
||||||
|
if Push {
|
||||||
|
if err := repo.Push(&git.PushOptions{}); err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
logrus.Info(fmt.Sprintf("pushed tag %s to remote", tagstring))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the latest tag with its hash, name etc
|
||||||
|
var lastGitTag *object.Tag
|
||||||
|
iter, err := repo.Tags()
|
||||||
|
if err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := iter.ForEach(func(ref *plumbing.Reference) error {
|
||||||
|
obj, err := repo.TagObject(ref.Hash())
|
||||||
|
if err == nil {
|
||||||
|
lastGitTag = obj
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
|
||||||
|
}); err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
newTag, err := tagcmp.Parse(lastGitTag.Name)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var newTagString string
|
||||||
|
if bumpType > 0 {
|
||||||
|
if Patch {
|
||||||
|
now, err := strconv.Atoi(newTag.Patch)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
newTag.Patch = strconv.Itoa(now + 1)
|
||||||
|
} else if Minor {
|
||||||
|
now, err := strconv.Atoi(newTag.Minor)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
newTag.Patch = "0"
|
||||||
|
newTag.Minor = strconv.Itoa(now + 1)
|
||||||
|
} else if Major {
|
||||||
|
now, err := strconv.Atoi(newTag.Major)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
newTag.Patch = "0"
|
||||||
|
newTag.Minor = "0"
|
||||||
|
newTag.Major = strconv.Itoa(now + 1)
|
||||||
|
}
|
||||||
|
newTagString = newTag.String()
|
||||||
|
} else {
|
||||||
|
logrus.Fatal("we don't support automatic tag generation yet - specify a version or use one of: --major --minor --patch")
|
||||||
|
}
|
||||||
|
|
||||||
|
newTagString = fmt.Sprintf("%s+%s", newTagString, mainAppVersion)
|
||||||
|
if Dry {
|
||||||
|
logrus.Info(fmt.Sprintf("dry run only: NOT creating tag %s at %s", newTagString, head.Hash()))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
repo.CreateTag(newTagString, head.Hash(), nil) /* &git.CreateTagOptions{
|
||||||
|
Message: tag,
|
||||||
|
})*/
|
||||||
|
logrus.Info(fmt.Sprintf("created tag %s at %s", newTagString, head.Hash()))
|
||||||
|
if Push {
|
||||||
|
if err := repo.Push(&git.PushOptions{}); err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
logrus.Info(fmt.Sprintf("pushed tag %s to remote", newTagString))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func getImageVersions(recipe recipe.Recipe) map[string]string {
|
||||||
|
|
||||||
|
var services = make(map[string]string)
|
||||||
|
for _, service := range recipe.Config.Services {
|
||||||
|
srv := strings.Split(service.Image, ":")
|
||||||
|
services[srv[0]] = srv[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
return services
|
||||||
|
}
|
||||||
|
|
||||||
|
func getMainApp(recipe recipe.Recipe) string {
|
||||||
|
for _, service := range recipe.Config.Services {
|
||||||
|
name := service.Name
|
||||||
|
if name == "app" {
|
||||||
|
return strings.Split(service.Image, ":")[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func btoi(b bool) int {
|
||||||
|
if b {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
@ -4,62 +4,88 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"coopcloud.tech/abra/cli/internal"
|
"coopcloud.tech/abra/cli/internal"
|
||||||
"coopcloud.tech/abra/pkg/client"
|
"coopcloud.tech/abra/pkg/catalogue"
|
||||||
"github.com/docker/distribution/reference"
|
"github.com/AlecAivazis/survey/v2"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
var recipeSyncCommand = &cli.Command{
|
var recipeSyncCommand = &cli.Command{
|
||||||
Name: "sync",
|
Name: "sync",
|
||||||
Usage: "Generate new recipe labels",
|
Usage: "Ensure recipe version labels are up-to-date",
|
||||||
Aliases: []string{"s"},
|
Aliases: []string{"s"},
|
||||||
Description: `
|
Description: `
|
||||||
This command will generate labels for each service which correspond to the
|
This command will generate labels for the main recipe service (i.e. the service
|
||||||
following format:
|
named "app", by convention) which corresponds to the following format:
|
||||||
|
|
||||||
coop-cloud.${STACK_NAME}.${SERVICE_NAME}.version=${IMAGE_TAG}-${IMAGE_DIGEST}
|
coop-cloud.${STACK_NAME}.version=${RECIPE_TAG}
|
||||||
|
|
||||||
The <recipe> configuration will be updated on the local file system. These
|
The ${RECIPE_TAG} is determined by the recipe maintainer and is retrieved by
|
||||||
labels are consumed by abra in other command invocations and used to determine
|
this command by asking for the list of git tags on the local git repository.
|
||||||
the versioning metadata of up-and-running containers are.
|
The <recipe> configuration will be updated on the local file system.
|
||||||
`,
|
`,
|
||||||
ArgsUsage: "<recipe>",
|
ArgsUsage: "<recipe>",
|
||||||
|
BashComplete: func(c *cli.Context) {
|
||||||
|
catl, err := catalogue.ReadRecipeCatalogue()
|
||||||
|
if err != nil {
|
||||||
|
logrus.Warn(err)
|
||||||
|
}
|
||||||
|
if c.NArg() > 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for name := range catl {
|
||||||
|
fmt.Println(name)
|
||||||
|
}
|
||||||
|
},
|
||||||
Action: func(c *cli.Context) error {
|
Action: func(c *cli.Context) error {
|
||||||
recipe := internal.ValidateRecipe(c)
|
recipe := internal.ValidateRecipe(c)
|
||||||
|
|
||||||
|
mainService := "app"
|
||||||
|
|
||||||
|
var services []string
|
||||||
hasAppService := false
|
hasAppService := false
|
||||||
for _, service := range recipe.Config.Services {
|
for _, service := range recipe.Config.Services {
|
||||||
|
services = append(services, service.Name)
|
||||||
if service.Name == "app" {
|
if service.Name == "app" {
|
||||||
hasAppService = true
|
hasAppService = true
|
||||||
|
logrus.Debugf("detected app service in '%s'", recipe.Name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !hasAppService {
|
if !hasAppService {
|
||||||
logrus.Fatal(fmt.Sprintf("no 'app' service defined in '%s'", recipe.Name))
|
logrus.Warnf("no 'app' service defined in '%s'", recipe.Name)
|
||||||
|
var chosenService string
|
||||||
|
prompt := &survey.Select{
|
||||||
|
Message: fmt.Sprintf("what is the main service name for '%s'?", recipe.Name),
|
||||||
|
Options: services,
|
||||||
|
}
|
||||||
|
if err := survey.AskOne(prompt, &chosenService); err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
mainService = chosenService
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, service := range recipe.Config.Services {
|
logrus.Debugf("selecting '%s' as the service to sync version labels", mainService)
|
||||||
img, err := reference.ParseNormalizedNamed(service.Image)
|
|
||||||
if err != nil {
|
|
||||||
logrus.Fatal(err)
|
|
||||||
}
|
|
||||||
logrus.Debugf("detected image '%s' for service '%s'", img, service.Name)
|
|
||||||
|
|
||||||
digest, err := client.GetTagDigest(img)
|
tags, err := recipe.Tags()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(tags) == 0 {
|
||||||
|
logrus.Fatalf("no tags detected for '%s'", recipe.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
latestTag := tags[len(tags)-1]
|
||||||
|
logrus.Infof("choosing '%s' as latest tag for recipe '%s'", latestTag, recipe.Name)
|
||||||
|
|
||||||
|
label := fmt.Sprintf("coop-cloud.${STACK_NAME}.version=%s", latestTag)
|
||||||
|
if err := recipe.UpdateLabel(mainService, label); err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Infof("added label '%s' to service '%s'", label, mainService)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -27,20 +27,29 @@ update the relevant compose file tags on the local file system.
|
|||||||
Some image tags cannot be parsed because they do not follow some sort of
|
Some image tags cannot be parsed because they do not follow some sort of
|
||||||
semver-like convention. In this case, all possible tags will be listed and it
|
semver-like convention. In this case, all possible tags will be listed and it
|
||||||
is up to the end-user to decide.
|
is up to the end-user to decide.
|
||||||
|
|
||||||
This is step 1 of upgrading a recipe. Step 2 is running "abra recipe sync
|
|
||||||
<recipe>".
|
|
||||||
`,
|
`,
|
||||||
ArgsUsage: "<recipe>",
|
ArgsUsage: "<recipe>",
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
PatchFlag,
|
||||||
|
MinorFlag,
|
||||||
|
MajorFlag,
|
||||||
|
},
|
||||||
Action: func(c *cli.Context) error {
|
Action: func(c *cli.Context) error {
|
||||||
recipe := internal.ValidateRecipe(c)
|
recipe := internal.ValidateRecipe(c)
|
||||||
|
|
||||||
|
bumpType := btoi(Major)*4 + btoi(Minor)*2 + btoi(Patch)
|
||||||
|
if bumpType != 0 {
|
||||||
|
// a bitwise check if the number is a power of 2
|
||||||
|
if (bumpType & (bumpType - 1)) != 0 {
|
||||||
|
logrus.Fatal("you can only use one of: --major, --minor, --patch.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for _, service := range recipe.Config.Services {
|
for _, service := range recipe.Config.Services {
|
||||||
catlVersions, err := catalogue.VersionsOfService(recipe.Name, service.Name)
|
catlVersions, err := catalogue.VersionsOfService(recipe.Name, service.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
logrus.Debugf("read '%s' from the recipe catalogue for '%s'", catlVersions, service.Name)
|
|
||||||
|
|
||||||
img, err := reference.ParseNormalizedNamed(service.Image)
|
img, err := reference.ParseNormalizedNamed(service.Image)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -108,31 +117,48 @@ This is step 1 of upgrading a recipe. Step 2 is running "abra recipe sync
|
|||||||
}
|
}
|
||||||
|
|
||||||
logrus.Debugf("detected compatible upgradable tags '%s' for '%s'", compatibleStrings, service.Name)
|
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("upgrade to which tag? (service: %s, tag: %s)", service.Name, tag)
|
|
||||||
compatibleStrings = []string{}
|
|
||||||
for _, regVersion := range regVersions {
|
|
||||||
compatibleStrings = append(compatibleStrings, regVersion.Name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var upgradeTag string
|
var upgradeTag string
|
||||||
prompt := &survey.Select{
|
if bumpType != 0 {
|
||||||
Message: msg,
|
for _, upTag := range compatible {
|
||||||
Options: compatibleStrings,
|
upElement, err := tag.UpgradeElement(upTag)
|
||||||
}
|
if err != nil {
|
||||||
if err := survey.AskOne(prompt, &upgradeTag); err != nil {
|
return err
|
||||||
logrus.Fatal(err)
|
}
|
||||||
|
delta := tagcmp.UpgradeType(upElement)
|
||||||
|
if delta <= bumpType {
|
||||||
|
upgradeTag = upTag.String()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if upgradeTag == "" {
|
||||||
|
logrus.Warnf("not upgrading from '%s' to '%s' for '%s', because the upgrade type is more serious than what user wants.", tag.String(), compatible[0].String(), image)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
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("upgrade to which tag? (service: %s, tag: %s)", service.Name, tag)
|
||||||
|
compatibleStrings = []string{}
|
||||||
|
for _, regVersion := range regVersions {
|
||||||
|
compatibleStrings = append(compatibleStrings, regVersion.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
prompt := &survey.Select{
|
||||||
|
Message: msg,
|
||||||
|
Options: compatibleStrings,
|
||||||
|
}
|
||||||
|
if err := survey.AskOne(prompt, &upgradeTag); err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := recipe.UpdateTag(image, upgradeTag); err != nil {
|
if err := recipe.UpdateTag(image, upgradeTag); err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
logrus.Debugf("tag updated from '%s' to '%s' for '%s'", image, upgradeTag, recipe.Name)
|
logrus.Infof("tag upgraded from '%s' to '%s' for '%s'", tag.String(), upgradeTag, image)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -2,40 +2,78 @@ package server
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"os/user"
|
"os/user"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"coopcloud.tech/abra/cli/internal"
|
"coopcloud.tech/abra/cli/internal"
|
||||||
"coopcloud.tech/abra/pkg/client"
|
"coopcloud.tech/abra/pkg/client"
|
||||||
|
"coopcloud.tech/abra/pkg/server"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var local bool
|
||||||
|
var localFlag = &cli.BoolFlag{
|
||||||
|
Name: "local",
|
||||||
|
Aliases: []string{"L"},
|
||||||
|
Value: false,
|
||||||
|
Usage: "Set up the local server",
|
||||||
|
Destination: &local,
|
||||||
|
}
|
||||||
|
|
||||||
var serverAddCommand = &cli.Command{
|
var serverAddCommand = &cli.Command{
|
||||||
Name: "add",
|
Name: "add",
|
||||||
Usage: "Add a new server",
|
Usage: "Add a new server",
|
||||||
Description: `
|
Description: `
|
||||||
This command adds a new server that abra will communicate with, to deploy apps.
|
This command adds a new server that abra will communicate with, to deploy apps.
|
||||||
|
|
||||||
The <domain> argument must be a publicy accessible domain name which points to
|
If "--local" is passed, then Abra assumes that the current local server is
|
||||||
your server. You should have SSH access to this server, Abra will assume port
|
intended as the target server.
|
||||||
22 and will use your current system username to make an initial connection. You
|
|
||||||
can use the <user> and <port> arguments to adjust this.
|
Otherwise, you may specify a remote server. The <domain> argument must be a
|
||||||
|
publicy accessible domain name which points to your server. You should have SSH
|
||||||
|
access to this server, Abra will assume port 22 and will use your current
|
||||||
|
system username to make an initial connection. You can use the <user> and
|
||||||
|
<port> arguments to adjust this.
|
||||||
|
|
||||||
For example:
|
For example:
|
||||||
|
|
||||||
abra server add varia.zone 12345 glodemodem
|
abra server add varia.zone glodemodem 12345
|
||||||
|
|
||||||
Abra will construct the following SSH connection string then:
|
Abra will construct the following SSH connection string then:
|
||||||
|
|
||||||
ssh://globemodem@varia.zone:12345
|
ssh://globemodem@varia.zone:12345
|
||||||
|
|
||||||
All communication between Abra and the server will use this SSH connection.
|
All communication between Abra and the server will use this SSH connection.
|
||||||
|
|
||||||
`,
|
`,
|
||||||
Aliases: []string{"a"},
|
Aliases: []string{"a"},
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
localFlag,
|
||||||
|
},
|
||||||
ArgsUsage: "<domain> [<user>] [<port>]",
|
ArgsUsage: "<domain> [<user>] [<port>]",
|
||||||
Action: func(c *cli.Context) error {
|
Action: func(c *cli.Context) error {
|
||||||
domainName := internal.ValidateDomain(c)
|
if c.Args().Len() == 1 && !local {
|
||||||
|
err := errors.New("missing arguments <domain> or '--local'")
|
||||||
|
internal.ShowSubcommandHelpAndError(c, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.Args().Get(1) != "" && local {
|
||||||
|
err := errors.New("cannot use '<domain>' and '--local' together")
|
||||||
|
internal.ShowSubcommandHelpAndError(c, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
domainName := "default"
|
||||||
|
|
||||||
|
if local {
|
||||||
|
if err := server.CreateServerDir(domainName); err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
logrus.Info("local server has been added")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
domainName = internal.ValidateDomain(c)
|
||||||
|
|
||||||
var username string
|
var username string
|
||||||
var port string
|
var port string
|
||||||
@ -79,11 +117,20 @@ All communication between Abra and the server will use this SSH connection.
|
|||||||
}
|
}
|
||||||
|
|
||||||
if _, err := cl.Info(ctx); err != nil {
|
if _, err := cl.Info(ctx); err != nil {
|
||||||
logrus.Fatalf("unable to make a connection to '%s'?", domainName)
|
if strings.Contains(err.Error(), "command not found") {
|
||||||
|
logrus.Fatalf("docker is not installed on '%s'?", domainName)
|
||||||
|
} else {
|
||||||
|
logrus.Fatalf("unable to make a connection to '%s'?", domainName)
|
||||||
|
}
|
||||||
logrus.Debug(err)
|
logrus.Debug(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
logrus.Debugf("remote connection to '%s' is definitely up", domainName)
|
logrus.Debugf("remote connection to '%s' is definitely up", domainName)
|
||||||
|
|
||||||
|
if err := server.CreateServerDir(domainName); err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
logrus.Infof("server at '%s' has been added", domainName)
|
logrus.Infof("server at '%s' has been added", domainName)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -35,6 +35,9 @@ later for more advanced use cases.
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://www.privacy-handbuch.de/handbuch_93d.htm
|
||||||
|
freifunkDNS := "5.1.66.255:53"
|
||||||
|
|
||||||
resolver := &net.Resolver{
|
resolver := &net.Resolver{
|
||||||
PreferGo: false,
|
PreferGo: false,
|
||||||
Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
|
Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
|
||||||
@ -42,10 +45,10 @@ later for more advanced use cases.
|
|||||||
Timeout: time.Millisecond * time.Duration(10000),
|
Timeout: time.Millisecond * time.Duration(10000),
|
||||||
}
|
}
|
||||||
// comrade librehosters DNS resolver https://snopyta.org/service/dns/
|
// comrade librehosters DNS resolver https://snopyta.org/service/dns/
|
||||||
return d.DialContext(ctx, "udp", "95.216.24.230:53")
|
return d.DialContext(ctx, "udp", freifunkDNS)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
logrus.Debugf("created DNS resolver via 95.216.24.230")
|
logrus.Debugf("created DNS resolver via '%s'", freifunkDNS)
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
ips, err := resolver.LookupIPAddr(ctx, domainName)
|
ips, err := resolver.LookupIPAddr(ctx, domainName)
|
||||||
|
@ -45,7 +45,11 @@ var serverListCommand = &cli.Command{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(row) == 0 {
|
if len(row) == 0 {
|
||||||
row = []string{serverName, "UNKNOWN"}
|
if serverName == "default" {
|
||||||
|
row = []string{serverName, "local"}
|
||||||
|
} else {
|
||||||
|
row = []string{serverName, "unknown"}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
table.Append(row)
|
table.Append(row)
|
||||||
}
|
}
|
||||||
|
17
go.mod
17
go.mod
@ -3,9 +3,10 @@ module coopcloud.tech/abra
|
|||||||
go 1.17
|
go 1.17
|
||||||
|
|
||||||
require (
|
require (
|
||||||
coopcloud.tech/tagcmp v0.0.0-20210906102006-2a8edd82d75d
|
coopcloud.tech/tagcmp v0.0.0-20211003080922-7b06d1c16182
|
||||||
github.com/AlecAivazis/survey/v2 v2.3.1
|
github.com/AlecAivazis/survey/v2 v2.3.1
|
||||||
github.com/Autonomic-Cooperative/godotenv v1.3.1-0.20210731170023-c37c0920d1a4
|
github.com/Autonomic-Cooperative/godotenv v1.3.1-0.20210731170023-c37c0920d1a4
|
||||||
|
github.com/Gurpartap/logrus-stack v0.0.0-20170710170904-89c00d8a28f4
|
||||||
github.com/docker/cli v20.10.8+incompatible
|
github.com/docker/cli v20.10.8+incompatible
|
||||||
github.com/docker/distribution v2.7.1+incompatible
|
github.com/docker/distribution v2.7.1+incompatible
|
||||||
github.com/docker/docker v20.10.8+incompatible
|
github.com/docker/docker v20.10.8+incompatible
|
||||||
@ -16,6 +17,7 @@ require (
|
|||||||
github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6
|
github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6
|
||||||
github.com/olekukonko/tablewriter v0.0.5
|
github.com/olekukonko/tablewriter v0.0.5
|
||||||
github.com/pkg/errors v0.9.1
|
github.com/pkg/errors v0.9.1
|
||||||
|
github.com/schollz/progressbar/v3 v3.8.3
|
||||||
github.com/schultz-is/passgen v1.0.1
|
github.com/schultz-is/passgen v1.0.1
|
||||||
github.com/sirupsen/logrus v1.8.1
|
github.com/sirupsen/logrus v1.8.1
|
||||||
github.com/urfave/cli/v2 v2.3.0
|
github.com/urfave/cli/v2 v2.3.0
|
||||||
@ -38,6 +40,7 @@ require (
|
|||||||
github.com/docker/go-connections v0.4.0 // indirect
|
github.com/docker/go-connections v0.4.0 // indirect
|
||||||
github.com/docker/go-metrics v0.0.1 // indirect
|
github.com/docker/go-metrics v0.0.1 // indirect
|
||||||
github.com/emirpasic/gods v1.12.0 // indirect
|
github.com/emirpasic/gods v1.12.0 // indirect
|
||||||
|
github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 // indirect
|
||||||
github.com/fvbommel/sortorder v1.0.2 // indirect
|
github.com/fvbommel/sortorder v1.0.2 // indirect
|
||||||
github.com/go-git/gcfg v1.5.0 // indirect
|
github.com/go-git/gcfg v1.5.0 // indirect
|
||||||
github.com/go-git/go-billy/v5 v5.3.1 // indirect
|
github.com/go-git/go-billy/v5 v5.3.1 // indirect
|
||||||
@ -53,11 +56,12 @@ require (
|
|||||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
||||||
github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 // indirect
|
github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.2 // indirect
|
github.com/mattn/go-colorable v0.1.2 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.8 // indirect
|
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||||
github.com/mattn/go-runewidth v0.0.9 // indirect
|
github.com/mattn/go-runewidth v0.0.13 // indirect
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
|
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
|
||||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
|
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
|
||||||
github.com/miekg/pkcs11 v1.0.3 // indirect
|
github.com/miekg/pkcs11 v1.0.3 // indirect
|
||||||
|
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
|
||||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||||
github.com/mitchellh/mapstructure v1.1.2 // indirect
|
github.com/mitchellh/mapstructure v1.1.2 // indirect
|
||||||
github.com/moby/sys/mount v0.2.0 // indirect
|
github.com/moby/sys/mount v0.2.0 // indirect
|
||||||
@ -70,6 +74,7 @@ require (
|
|||||||
github.com/prometheus/client_model v0.2.0 // indirect
|
github.com/prometheus/client_model v0.2.0 // indirect
|
||||||
github.com/prometheus/common v0.26.0 // indirect
|
github.com/prometheus/common v0.26.0 // indirect
|
||||||
github.com/prometheus/procfs v0.6.0 // indirect
|
github.com/prometheus/procfs v0.6.0 // indirect
|
||||||
|
github.com/rivo/uniseg v0.2.0 // indirect
|
||||||
github.com/russross/blackfriday/v2 v2.0.1 // indirect
|
github.com/russross/blackfriday/v2 v2.0.1 // indirect
|
||||||
github.com/sergi/go-diff v1.1.0 // indirect
|
github.com/sergi/go-diff v1.1.0 // indirect
|
||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
|
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
|
||||||
@ -81,10 +86,10 @@ require (
|
|||||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
|
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
|
||||||
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
|
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
|
||||||
go.opencensus.io v0.22.3 // indirect
|
go.opencensus.io v0.22.3 // indirect
|
||||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b // indirect
|
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 // indirect
|
||||||
golang.org/x/net v0.0.0-20210326060303-6b1517762897 // indirect
|
golang.org/x/net v0.0.0-20210326060303-6b1517762897 // indirect
|
||||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect
|
golang.org/x/sys v0.0.0-20210910150752-751e447fb3d0 // indirect
|
||||||
golang.org/x/term v0.0.0-20210503060354-a79de5458b56 // indirect
|
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b // indirect
|
||||||
golang.org/x/text v0.3.4 // indirect
|
golang.org/x/text v0.3.4 // indirect
|
||||||
google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a // indirect
|
google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a // indirect
|
||||||
google.golang.org/grpc v1.33.2 // indirect
|
google.golang.org/grpc v1.33.2 // indirect
|
||||||
|
31
go.sum
31
go.sum
@ -23,6 +23,10 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo
|
|||||||
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
|
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
|
||||||
coopcloud.tech/tagcmp v0.0.0-20210906102006-2a8edd82d75d h1:5jeUIiToqQ7vTlLeycdGp4Ezurd6/RTNl5K38usHtoo=
|
coopcloud.tech/tagcmp v0.0.0-20210906102006-2a8edd82d75d h1:5jeUIiToqQ7vTlLeycdGp4Ezurd6/RTNl5K38usHtoo=
|
||||||
coopcloud.tech/tagcmp v0.0.0-20210906102006-2a8edd82d75d/go.mod h1:ESVm0wQKcbcFi06jItF3rI7enf4Jt2PvbkWpDDHk1DQ=
|
coopcloud.tech/tagcmp v0.0.0-20210906102006-2a8edd82d75d/go.mod h1:ESVm0wQKcbcFi06jItF3rI7enf4Jt2PvbkWpDDHk1DQ=
|
||||||
|
coopcloud.tech/tagcmp v0.0.0-20211003074705-03d2daced95c h1:7kCjnhjrOevcJeA/koCyyv20E6AglvqC7biGbLzyrbU=
|
||||||
|
coopcloud.tech/tagcmp v0.0.0-20211003074705-03d2daced95c/go.mod h1:ESVm0wQKcbcFi06jItF3rI7enf4Jt2PvbkWpDDHk1DQ=
|
||||||
|
coopcloud.tech/tagcmp v0.0.0-20211003080922-7b06d1c16182 h1:VGFzudsoGXGRaw5eJE3rErHHUDsmuIJpQkdfKJgrNs4=
|
||||||
|
coopcloud.tech/tagcmp v0.0.0-20211003080922-7b06d1c16182/go.mod h1:ESVm0wQKcbcFi06jItF3rI7enf4Jt2PvbkWpDDHk1DQ=
|
||||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||||
github.com/AlecAivazis/survey/v2 v2.3.1 h1:lzkuHA60pER7L4eYL8qQJor4bUWlJe4V0gqAT19tdOA=
|
github.com/AlecAivazis/survey/v2 v2.3.1 h1:lzkuHA60pER7L4eYL8qQJor4bUWlJe4V0gqAT19tdOA=
|
||||||
github.com/AlecAivazis/survey/v2 v2.3.1/go.mod h1:TH2kPCDU3Kqq7pLbnCWwZXDBjnhZtmsCle5EiYDJ2fg=
|
github.com/AlecAivazis/survey/v2 v2.3.1/go.mod h1:TH2kPCDU3Kqq7pLbnCWwZXDBjnhZtmsCle5EiYDJ2fg=
|
||||||
@ -44,6 +48,8 @@ github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZ
|
|||||||
github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
|
github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||||
|
github.com/Gurpartap/logrus-stack v0.0.0-20170710170904-89c00d8a28f4 h1:vdT7QwBhJJEVNFMBNhRSFDRCB6O16T28VhvqRgqFyn8=
|
||||||
|
github.com/Gurpartap/logrus-stack v0.0.0-20170710170904-89c00d8a28f4/go.mod h1:SvXOG8ElV28oAiG9zv91SDe5+9PfIr7PPccpr8YyXNs=
|
||||||
github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
|
github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
|
||||||
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
|
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
|
||||||
github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw=
|
github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw=
|
||||||
@ -295,6 +301,8 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m
|
|||||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||||
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0=
|
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0=
|
||||||
github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||||
|
github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 h1:JWuenKqqX8nojtoVVWjGfOF9635RETekkoH6Cc9SX0A=
|
||||||
|
github.com/facebookgo/stack v0.0.0-20160209184415-751773369052/go.mod h1:UbMTZqLaRiH3MsBH8va0n7s1pQYcu3uTb8G4tygF4Zg=
|
||||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
||||||
github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
|
github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
|
||||||
@ -479,6 +487,7 @@ github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfV
|
|||||||
github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U=
|
github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U=
|
||||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
||||||
|
github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw=
|
||||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
|
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
|
||||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
||||||
github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 h1:DowS9hvgyYSX4TO5NpyC606/Z4SxnNYbT+WX27or6Ck=
|
github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 h1:DowS9hvgyYSX4TO5NpyC606/Z4SxnNYbT+WX27or6Ck=
|
||||||
@ -518,11 +527,13 @@ github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaO
|
|||||||
github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU=
|
github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU=
|
||||||
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||||
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||||
github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE=
|
|
||||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||||
|
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
||||||
|
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||||
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||||
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
|
|
||||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||||
|
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
|
||||||
|
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||||
github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
|
github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
|
||||||
github.com/mattn/go-sqlite3 v1.6.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
github.com/mattn/go-sqlite3 v1.6.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||||
@ -534,6 +545,8 @@ github.com/miekg/pkcs11 v1.0.2/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WT
|
|||||||
github.com/miekg/pkcs11 v1.0.3 h1:iMwmD7I5225wv84WxIG/bmxz9AXjWvTWIbM/TYHvWtw=
|
github.com/miekg/pkcs11 v1.0.3 h1:iMwmD7I5225wv84WxIG/bmxz9AXjWvTWIbM/TYHvWtw=
|
||||||
github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
|
github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
|
||||||
github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4=
|
github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4=
|
||||||
|
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ=
|
||||||
|
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw=
|
||||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||||
github.com/mitchellh/mapstructure v0.0.0-20150613213606-2caf8efc9366/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
github.com/mitchellh/mapstructure v0.0.0-20150613213606-2caf8efc9366/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
@ -662,12 +675,16 @@ github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O
|
|||||||
github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4=
|
github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4=
|
||||||
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||||
|
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||||
|
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||||
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
|
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
|
||||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4=
|
github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4=
|
||||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||||
|
github.com/schollz/progressbar/v3 v3.8.3 h1:FnLGl3ewlDUP+YdSwveXBaXs053Mem/du+wr7XSYKl8=
|
||||||
|
github.com/schollz/progressbar/v3 v3.8.3/go.mod h1:pWnVCjSBZsT2X3nx9HfRdnCDrpbevliMeoEVhStwHko=
|
||||||
github.com/schultz-is/passgen v1.0.1 h1:wUINzqW1Xmmy3yREHR6YTj+83VlFYjj2DIDMHzIi5TQ=
|
github.com/schultz-is/passgen v1.0.1 h1:wUINzqW1Xmmy3yREHR6YTj+83VlFYjj2DIDMHzIi5TQ=
|
||||||
github.com/schultz-is/passgen v1.0.1/go.mod h1:NnqzT2aSfvyheNQvBtlLUa0YlPFLDj60Jw2DZVwqiJk=
|
github.com/schultz-is/passgen v1.0.1/go.mod h1:NnqzT2aSfvyheNQvBtlLUa0YlPFLDj60Jw2DZVwqiJk=
|
||||||
github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo=
|
github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo=
|
||||||
@ -798,8 +815,9 @@ golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPh
|
|||||||
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||||
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b h1:7mWr3k41Qtv8XlltBkDkl8LoP3mpSgBW8BUoxtEdbXg=
|
|
||||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||||
|
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ=
|
||||||
|
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||||
@ -947,13 +965,16 @@ golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210502180810-71e4cd670f79/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210502180810-71e4cd670f79/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I=
|
|
||||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20210910150752-751e447fb3d0 h1:xrCZDmdtoloIiooiA9q0OQb9r8HejIHYoHGhGCe1pGg=
|
||||||
|
golang.org/x/sys v0.0.0-20210910150752-751e447fb3d0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210503060354-a79de5458b56 h1:b8jxX3zqjpqb2LklXPzKSGJhzyxCOZSz8ncv8Nv+y7w=
|
|
||||||
golang.org/x/term v0.0.0-20210503060354-a79de5458b56/go.mod h1:tfny5GFUkzUvx4ps4ajbZsCe5lw1metzhBm9T3x7oIY=
|
golang.org/x/term v0.0.0-20210503060354-a79de5458b56/go.mod h1:tfny5GFUkzUvx4ps4ajbZsCe5lw1metzhBm9T3x7oIY=
|
||||||
|
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b h1:9zKuko04nR4gjZ4+DNjHqRlAJqbJETHwiNKDqTfOjfE=
|
||||||
|
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
@ -9,18 +9,26 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"coopcloud.tech/abra/pkg/client"
|
||||||
"coopcloud.tech/abra/pkg/config"
|
"coopcloud.tech/abra/pkg/config"
|
||||||
"coopcloud.tech/abra/pkg/recipe"
|
"coopcloud.tech/abra/pkg/recipe"
|
||||||
"coopcloud.tech/abra/pkg/web"
|
"coopcloud.tech/abra/pkg/web"
|
||||||
|
"github.com/docker/distribution/reference"
|
||||||
|
"github.com/go-git/go-git/v5"
|
||||||
|
"github.com/go-git/go-git/v5/plumbing"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
// RecipeCatalogueURL is the only current recipe catalogue available.
|
// RecipeCatalogueURL is the only current recipe catalogue available.
|
||||||
const RecipeCatalogueURL = "https://apps.coopcloud.tech"
|
const RecipeCatalogueURL = "https://apps.coopcloud.tech"
|
||||||
|
|
||||||
|
// ReposMetadataURL is the recipe repository metadata
|
||||||
|
const ReposMetadataURL = "https://git.coopcloud.tech/api/v1/orgs/coop-cloud/repos"
|
||||||
|
|
||||||
// image represents a recipe container image.
|
// image represents a recipe container image.
|
||||||
type image struct {
|
type image struct {
|
||||||
Image string `json:"image"`
|
Image string `json:"image"`
|
||||||
@ -53,17 +61,20 @@ type serviceMeta struct {
|
|||||||
Tag string `json:"tag"`
|
Tag string `json:"tag"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RecipeVersions are the versions associated with a recipe.
|
||||||
|
type RecipeVersions []map[tag]map[service]serviceMeta
|
||||||
|
|
||||||
// RecipeMeta represents metadata for a recipe in the abra catalogue.
|
// RecipeMeta represents metadata for a recipe in the abra catalogue.
|
||||||
type RecipeMeta struct {
|
type RecipeMeta struct {
|
||||||
Category string `json:"category"`
|
Category string `json:"category"`
|
||||||
DefaultBranch string `json:"default_branch"`
|
DefaultBranch string `json:"default_branch"`
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
Features features `json:"features"`
|
Features features `json:"features"`
|
||||||
Icon string `json:"icon"`
|
Icon string `json:"icon"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Repository string `json:"repository"`
|
Repository string `json:"repository"`
|
||||||
Versions []map[tag]map[service]serviceMeta `json:"versions"`
|
Versions RecipeVersions `json:"versions"`
|
||||||
Website string `json:"website"`
|
Website string `json:"website"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// LatestVersion returns the latest version of a recipe.
|
// LatestVersion returns the latest version of a recipe.
|
||||||
@ -255,3 +266,217 @@ func GetRecipeMeta(recipeName string) (RecipeMeta, error) {
|
|||||||
|
|
||||||
return recipeMeta, nil
|
return recipeMeta, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RepoMeta is a single recipe repo metadata.
|
||||||
|
type RepoMeta struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
Owner Owner
|
||||||
|
Name string `json:"name"`
|
||||||
|
FullName string `json:"full_name"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
Empty bool `json:"empty"`
|
||||||
|
Private bool `json:"private"`
|
||||||
|
Fork bool `json:"fork"`
|
||||||
|
Template bool `json:"template"`
|
||||||
|
Parent interface{} `json:"parent"`
|
||||||
|
Mirror bool `json:"mirror"`
|
||||||
|
Size int `json:"size"`
|
||||||
|
HTMLURL string `json:"html_url"`
|
||||||
|
SSHURL string `json:"ssh_url"`
|
||||||
|
CloneURL string `json:"clone_url"`
|
||||||
|
OriginalURL string `json:"original_url"`
|
||||||
|
Website string `json:"website"`
|
||||||
|
StarsCount int `json:"stars_count"`
|
||||||
|
ForksCount int `json:"forks_count"`
|
||||||
|
WatchersCount int `json:"watchers_count"`
|
||||||
|
OpenIssuesCount int `json:"open_issues_count"`
|
||||||
|
OpenPRCount int `json:"open_pr_counter"`
|
||||||
|
ReleaseCounter int `json:"release_counter"`
|
||||||
|
DefaultBranch string `json:"default_branch"`
|
||||||
|
Archived bool `json:"archived"`
|
||||||
|
CreatedAt string `json:"created_at"`
|
||||||
|
UpdatedAt string `json:"updated_at"`
|
||||||
|
Permissions Permissions
|
||||||
|
HasIssues bool `json:"has_issues"`
|
||||||
|
InternalTracker InternalTracker
|
||||||
|
HasWiki bool `json:"has_wiki"`
|
||||||
|
HasPullRequests bool `json:"has_pull_requests"`
|
||||||
|
HasProjects bool `json:"has_projects"`
|
||||||
|
IgnoreWhitespaceConflicts bool `json:"ignore_whitespace_conflicts"`
|
||||||
|
AllowMergeCommits bool `json:"allow_merge_commits"`
|
||||||
|
AllowRebase bool `json:"allow_rebase"`
|
||||||
|
AllowRebaseExplicit bool `json:"allow_rebase_explicit"`
|
||||||
|
AllowSquashMerge bool `json:"allow_squash_merge"`
|
||||||
|
AvatarURL string `json:"avatar_url"`
|
||||||
|
Internal bool `json:"internal"`
|
||||||
|
MirrorInterval string `json:"mirror_interval"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Owner is the repo organisation owner metadata.
|
||||||
|
type Owner struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
Login string `json:"login"`
|
||||||
|
FullName string `json:"full_name"`
|
||||||
|
Email string `json:"email"`
|
||||||
|
AvatarURL string `json:"avatar_url"`
|
||||||
|
Language string `json:"language"`
|
||||||
|
IsAdmin bool `json:"is_admin"`
|
||||||
|
LastLogin string `json:"last_login"`
|
||||||
|
Created string `json:"created"`
|
||||||
|
Restricted bool `json:"restricted"`
|
||||||
|
Username string `json:"username"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Permissions is perms metadata for a repo.
|
||||||
|
type Permissions struct {
|
||||||
|
Admin bool `json:"admin"`
|
||||||
|
Push bool `json:"push"`
|
||||||
|
Pull bool `json:"pull"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// InternalTracker is issue tracker metadata for a repo.
|
||||||
|
type InternalTracker struct {
|
||||||
|
EnableTimeTracker bool `json:"enable_time_tracker"`
|
||||||
|
AllowOnlyContributorsToTrackTime bool `json:"allow_only_contributors_to_track_time"`
|
||||||
|
EnableIssuesDependencies bool `json:"enable_issue_dependencies"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// RepoCatalogue represents all the recipe repo metadata.
|
||||||
|
type RepoCatalogue map[string]RepoMeta
|
||||||
|
|
||||||
|
// ReadReposMetadata retrieves coop-cloud/... repo metadata from Gitea.
|
||||||
|
func ReadReposMetadata() (RepoCatalogue, error) {
|
||||||
|
reposMeta := make(RepoCatalogue)
|
||||||
|
|
||||||
|
pageIdx := 1
|
||||||
|
for {
|
||||||
|
var reposList []RepoMeta
|
||||||
|
|
||||||
|
pagedURL := fmt.Sprintf("%s?page=%v", ReposMetadataURL, pageIdx)
|
||||||
|
|
||||||
|
logrus.Debugf("fetching repo metadata from '%s'", pagedURL)
|
||||||
|
|
||||||
|
if err := web.ReadJSON(pagedURL, &reposList); err != nil {
|
||||||
|
return reposMeta, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(reposList) == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
for idx, repo := range reposList {
|
||||||
|
reposMeta[repo.Name] = reposList[idx]
|
||||||
|
}
|
||||||
|
|
||||||
|
pageIdx++
|
||||||
|
}
|
||||||
|
|
||||||
|
return reposMeta, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRecipeVersions retrieves all recipe versions.
|
||||||
|
func GetRecipeVersions(recipeName string) (RecipeVersions, error) {
|
||||||
|
versions := RecipeVersions{}
|
||||||
|
|
||||||
|
recipeDir := path.Join(config.ABRA_DIR, "apps", recipeName)
|
||||||
|
|
||||||
|
logrus.Debugf("attempting to open git repository in '%s'", recipeDir)
|
||||||
|
|
||||||
|
repo, err := git.PlainOpen(recipeDir)
|
||||||
|
if err != nil {
|
||||||
|
return versions, err
|
||||||
|
}
|
||||||
|
|
||||||
|
worktree, err := repo.Worktree()
|
||||||
|
if err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
gitTags, err := repo.Tags()
|
||||||
|
if err != nil {
|
||||||
|
return versions, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := gitTags.ForEach(func(ref *plumbing.Reference) (err error) {
|
||||||
|
tag := strings.TrimPrefix(string(ref.Name()), "refs/tags/")
|
||||||
|
|
||||||
|
logrus.Debugf("processing '%s' for '%s'", tag, recipeName)
|
||||||
|
|
||||||
|
checkOutOpts := &git.CheckoutOptions{
|
||||||
|
Create: false,
|
||||||
|
Force: true,
|
||||||
|
Keep: false,
|
||||||
|
Branch: plumbing.ReferenceName(ref.Name()),
|
||||||
|
}
|
||||||
|
if err := worktree.Checkout(checkOutOpts); err != nil {
|
||||||
|
logrus.Debugf("failed to check out '%s' in '%s'", tag, recipeDir)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debugf("successfully checked out '%s' in '%s'", ref.Name(), recipeDir)
|
||||||
|
|
||||||
|
recipe, err := recipe.Get(recipeName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
versionMeta := make(map[string]serviceMeta)
|
||||||
|
for _, service := range recipe.Config.Services {
|
||||||
|
|
||||||
|
img, err := reference.ParseNormalizedNamed(service.Image)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
path := reference.Path(img)
|
||||||
|
if strings.Contains(path, "library") {
|
||||||
|
path = strings.Split(path, "/")[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
digest, err := client.GetTagDigest(img)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
versionMeta[service.Name] = serviceMeta{
|
||||||
|
Digest: digest,
|
||||||
|
Image: path,
|
||||||
|
Tag: img.(reference.NamedTagged).Tag(),
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debugf("collecting digest: '%s', image: '%s', tag: '%s'", digest, path, tag)
|
||||||
|
}
|
||||||
|
|
||||||
|
versions = append(versions, map[string]map[string]serviceMeta{tag: versionMeta})
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}); err != nil {
|
||||||
|
return versions, err
|
||||||
|
}
|
||||||
|
|
||||||
|
branch := "master"
|
||||||
|
if _, err := repo.Branch("master"); err != nil {
|
||||||
|
if _, err := repo.Branch("main"); err != nil {
|
||||||
|
logrus.Debugf("failed to select branch in '%s'", recipeDir)
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
branch = "main"
|
||||||
|
}
|
||||||
|
|
||||||
|
refName := fmt.Sprintf("refs/heads/%s", branch)
|
||||||
|
checkOutOpts := &git.CheckoutOptions{
|
||||||
|
Create: false,
|
||||||
|
Force: true,
|
||||||
|
Keep: false,
|
||||||
|
Branch: plumbing.ReferenceName(refName),
|
||||||
|
}
|
||||||
|
if err := worktree.Checkout(checkOutOpts); err != nil {
|
||||||
|
logrus.Debugf("failed to check out '%s' in '%s'", branch, recipeDir)
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debugf("switched back to '%s' in '%s'", branch, recipeDir)
|
||||||
|
logrus.Debugf("collected '%s' for '%s'", versions, recipeName)
|
||||||
|
|
||||||
|
return versions, nil
|
||||||
|
}
|
||||||
|
@ -3,18 +3,20 @@ package compose
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"coopcloud.tech/abra/pkg/client/stack"
|
"coopcloud.tech/abra/pkg/client/stack"
|
||||||
loader "coopcloud.tech/abra/pkg/client/stack"
|
loader "coopcloud.tech/abra/pkg/client/stack"
|
||||||
|
"coopcloud.tech/abra/pkg/config"
|
||||||
composetypes "github.com/docker/cli/cli/compose/types"
|
composetypes "github.com/docker/cli/cli/compose/types"
|
||||||
"github.com/docker/distribution/reference"
|
"github.com/docker/distribution/reference"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
// UpdateTag updates an image tag in-place on file system local compose files.
|
// UpdateTag updates an image tag in-place on file system local compose files.
|
||||||
func UpdateTag(pattern, image, tag string) error {
|
func UpdateTag(pattern, image, tag, recipeName string) error {
|
||||||
composeFiles, err := filepath.Glob(pattern)
|
composeFiles, err := filepath.Glob(pattern)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -24,8 +26,14 @@ func UpdateTag(pattern, image, tag string) error {
|
|||||||
|
|
||||||
for _, composeFile := range composeFiles {
|
for _, composeFile := range composeFiles {
|
||||||
opts := stack.Deploy{Composefiles: []string{composeFile}}
|
opts := stack.Deploy{Composefiles: []string{composeFile}}
|
||||||
emptyEnv := make(map[string]string)
|
|
||||||
compose, err := loader.LoadComposefile(opts, emptyEnv)
|
envSamplePath := path.Join(config.ABRA_DIR, "apps", recipeName, ".env.sample")
|
||||||
|
sampleEnv, err := config.ReadEnv(envSamplePath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
compose, err := loader.LoadComposefile(opts, sampleEnv)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -74,7 +82,7 @@ func UpdateTag(pattern, image, tag string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// UpdateLabel updates a label in-place on file system local compose files.
|
// UpdateLabel updates a label in-place on file system local compose files.
|
||||||
func UpdateLabel(pattern, serviceName, label string) error {
|
func UpdateLabel(pattern, serviceName, label, recipeName string) error {
|
||||||
composeFiles, err := filepath.Glob(pattern)
|
composeFiles, err := filepath.Glob(pattern)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -84,8 +92,14 @@ func UpdateLabel(pattern, serviceName, label string) error {
|
|||||||
|
|
||||||
for _, composeFile := range composeFiles {
|
for _, composeFile := range composeFiles {
|
||||||
opts := stack.Deploy{Composefiles: []string{composeFile}}
|
opts := stack.Deploy{Composefiles: []string{composeFile}}
|
||||||
emptyEnv := make(map[string]string)
|
|
||||||
compose, err := loader.LoadComposefile(opts, emptyEnv)
|
envSamplePath := path.Join(config.ABRA_DIR, "apps", recipeName, ".env.sample")
|
||||||
|
sampleEnv, err := config.ReadEnv(envSamplePath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
compose, err := loader.LoadComposefile(opts, sampleEnv)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"coopcloud.tech/abra/cli/formatter"
|
||||||
"coopcloud.tech/abra/pkg/client/convert"
|
"coopcloud.tech/abra/pkg/client/convert"
|
||||||
loader "coopcloud.tech/abra/pkg/client/stack"
|
loader "coopcloud.tech/abra/pkg/client/stack"
|
||||||
stack "coopcloud.tech/abra/pkg/client/stack"
|
stack "coopcloud.tech/abra/pkg/client/stack"
|
||||||
@ -138,7 +139,7 @@ func LoadAppFiles(servers ...string) (AppFiles, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
logrus.Debugf("collecting metadata from '%v' servers: '%s'", len(servers), servers)
|
logrus.Debugf("collecting metadata from '%v' servers: '%s'", len(servers), strings.Join(servers, ", "))
|
||||||
|
|
||||||
for _, server := range servers {
|
for _, server := range servers {
|
||||||
serverDir := path.Join(ABRA_SERVER_FOLDER, server)
|
serverDir := path.Join(ABRA_SERVER_FOLDER, server)
|
||||||
@ -243,8 +244,8 @@ func GetAppNames() ([]string, error) {
|
|||||||
return appNames, nil
|
return appNames, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CopyAppEnvSample copies the example env file for the app into the users env files
|
// TemplateAppEnvSample copies the example env file for the app into the users env files
|
||||||
func CopyAppEnvSample(appType, appName, server string) error {
|
func TemplateAppEnvSample(appType, appName, server, domain, recipe string) error {
|
||||||
envSamplePath := path.Join(ABRA_DIR, "apps", appType, ".env.sample")
|
envSamplePath := path.Join(ABRA_DIR, "apps", appType, ".env.sample")
|
||||||
envSample, err := ioutil.ReadFile(envSamplePath)
|
envSample, err := ioutil.ReadFile(envSamplePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -256,6 +257,9 @@ func CopyAppEnvSample(appType, appName, server string) error {
|
|||||||
return fmt.Errorf("%s already exists?", appEnvPath)
|
return fmt.Errorf("%s already exists?", appEnvPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
envSample = []byte(strings.Replace(string(envSample), fmt.Sprintf("%s.example.com", recipe), domain, -1))
|
||||||
|
envSample = []byte(strings.Replace(string(envSample), "example.com", domain, -1))
|
||||||
|
|
||||||
err = ioutil.WriteFile(appEnvPath, envSample, 0755)
|
err = ioutil.WriteFile(appEnvPath, envSample, 0755)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -275,14 +279,22 @@ func SanitiseAppName(name string) string {
|
|||||||
func GetAppStatuses(appFiles AppFiles) (map[string]string, error) {
|
func GetAppStatuses(appFiles AppFiles) (map[string]string, error) {
|
||||||
statuses := map[string]string{}
|
statuses := map[string]string{}
|
||||||
|
|
||||||
servers, err := GetServers()
|
var unique []string
|
||||||
if err != nil {
|
servers := make(map[string]struct{})
|
||||||
return statuses, err
|
for _, appFile := range appFiles {
|
||||||
|
if _, ok := servers[appFile.Server]; !ok {
|
||||||
|
servers[appFile.Server] = struct{}{}
|
||||||
|
unique = append(unique, appFile.Server)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bar := formatter.CreateProgressbar(len(servers), "querying remote servers...")
|
||||||
ch := make(chan stack.StackStatus, len(servers))
|
ch := make(chan stack.StackStatus, len(servers))
|
||||||
for _, server := range servers {
|
for server := range servers {
|
||||||
go func(s string) { ch <- stack.GetAllDeployedServices(s) }(server)
|
go func(s string) {
|
||||||
|
ch <- stack.GetAllDeployedServices(s)
|
||||||
|
bar.Add(1)
|
||||||
|
}(server)
|
||||||
}
|
}
|
||||||
|
|
||||||
for range servers {
|
for range servers {
|
||||||
|
@ -73,6 +73,11 @@ func getAllFilesInDirectory(directory string) ([]fs.FileInfo, error) {
|
|||||||
for _, file := range files {
|
for _, file := range files {
|
||||||
// Follow any symlinks
|
// Follow any symlinks
|
||||||
filePath := path.Join(directory, file.Name())
|
filePath := path.Join(directory, file.Name())
|
||||||
|
|
||||||
|
if filepath.Ext(strings.TrimSpace(filePath)) != ".env" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
realPath, err := filepath.EvalSymlinks(filePath)
|
realPath, err := filepath.EvalSymlinks(filePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Warningf("broken symlink in your abra config folders: '%s'", filePath)
|
logrus.Warningf("broken symlink in your abra config folders: '%s'", filePath)
|
||||||
@ -137,7 +142,7 @@ func ReadAbraShEnvVars(abraSh string) (map[string]string, error) {
|
|||||||
file, err := os.Open(abraSh)
|
file, err := os.Open(abraSh)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
return envVars, fmt.Errorf("'%s' does not exist?", abraSh)
|
return envVars, nil
|
||||||
}
|
}
|
||||||
return envVars, err
|
return envVars, err
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ var validAbraConf = os.ExpandEnv("$PWD/../../tests/resources/valid_abra_config")
|
|||||||
|
|
||||||
// make sure these are in alphabetical order
|
// make sure these are in alphabetical order
|
||||||
var tFolders = []string{"folder1", "folder2"}
|
var tFolders = []string{"folder1", "folder2"}
|
||||||
var tFiles = []string{"bar", "foo"}
|
var tFiles = []string{"bar.env", "foo.env"}
|
||||||
|
|
||||||
var appName = "ecloud"
|
var appName = "ecloud"
|
||||||
var serverName = "evil.corp"
|
var serverName = "evil.corp"
|
||||||
|
@ -17,7 +17,7 @@ func Clone(dir, url string) error {
|
|||||||
logrus.Debugf("'%s' does not exist, attempting to git clone from '%s'", dir, url)
|
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})
|
_, err := git.PlainClone(dir, false, &git.CloneOptions{URL: url, Tags: git.AllTags})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Debugf("cloning from default branch failed, attempting from main branch")
|
logrus.Debugf("cloning '%s' default branch failed, attempting from main branch", url)
|
||||||
_, err := git.PlainClone(dir, false, &git.CloneOptions{
|
_, err := git.PlainClone(dir, false, &git.CloneOptions{
|
||||||
URL: url,
|
URL: url,
|
||||||
Tags: git.AllTags,
|
Tags: git.AllTags,
|
||||||
@ -45,12 +45,13 @@ func EnsureUpToDate(dir string) error {
|
|||||||
branch := "master"
|
branch := "master"
|
||||||
if _, err := repo.Branch("master"); err != nil {
|
if _, err := repo.Branch("master"); err != nil {
|
||||||
if _, err := repo.Branch("main"); err != nil {
|
if _, err := repo.Branch("main"); err != nil {
|
||||||
|
logrus.Debugf("failed to select branch in '%s'", dir)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
branch = "main"
|
branch = "main"
|
||||||
}
|
}
|
||||||
|
|
||||||
logrus.Debugf("choosing '%s' as main git branch for in '%s'", branch, dir)
|
logrus.Debugf("choosing '%s' as main git branch in '%s'", branch, dir)
|
||||||
|
|
||||||
worktree, err := repo.Worktree()
|
worktree, err := repo.Worktree()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -65,10 +66,11 @@ func EnsureUpToDate(dir string) error {
|
|||||||
Branch: plumbing.ReferenceName(refName),
|
Branch: plumbing.ReferenceName(refName),
|
||||||
}
|
}
|
||||||
if err := worktree.Checkout(checkOutOpts); err != nil {
|
if err := worktree.Checkout(checkOutOpts); err != nil {
|
||||||
|
logrus.Debugf("failed to check out '%s' in '%s'", refName, dir)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
logrus.Debugf("successfully checked out '%s'", branch)
|
logrus.Debugf("successfully checked out '%s' in '%s'", branch, dir)
|
||||||
|
|
||||||
remote, err := repo.Remote("origin")
|
remote, err := repo.Remote("origin")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -26,7 +26,7 @@ type Recipe struct {
|
|||||||
// UpdateLabel updates a recipe label
|
// UpdateLabel updates a recipe label
|
||||||
func (r Recipe) UpdateLabel(serviceName, label string) error {
|
func (r Recipe) UpdateLabel(serviceName, label string) error {
|
||||||
pattern := fmt.Sprintf("%s/%s/compose**yml", config.APPS_DIR, r.Name)
|
pattern := fmt.Sprintf("%s/%s/compose**yml", config.APPS_DIR, r.Name)
|
||||||
if err := compose.UpdateLabel(pattern, serviceName, label); err != nil {
|
if err := compose.UpdateLabel(pattern, serviceName, label, r.Name); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@ -35,12 +35,39 @@ func (r Recipe) UpdateLabel(serviceName, label string) error {
|
|||||||
// UpdateTag updates a recipe tag
|
// UpdateTag updates a recipe tag
|
||||||
func (r Recipe) UpdateTag(image, tag string) error {
|
func (r Recipe) UpdateTag(image, tag string) error {
|
||||||
pattern := fmt.Sprintf("%s/%s/compose**yml", config.APPS_DIR, r.Name)
|
pattern := fmt.Sprintf("%s/%s/compose**yml", config.APPS_DIR, r.Name)
|
||||||
if err := compose.UpdateTag(pattern, image, tag); err != nil {
|
if err := compose.UpdateTag(pattern, image, tag, r.Name); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tags list the recipe tags
|
||||||
|
func (r Recipe) Tags() ([]string, error) {
|
||||||
|
var tags []string
|
||||||
|
|
||||||
|
recipeDir := path.Join(config.ABRA_DIR, "apps", r.Name)
|
||||||
|
repo, err := git.PlainOpen(recipeDir)
|
||||||
|
if err != nil {
|
||||||
|
return tags, err
|
||||||
|
}
|
||||||
|
|
||||||
|
gitTags, err := repo.Tags()
|
||||||
|
if err != nil {
|
||||||
|
return tags, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := gitTags.ForEach(func(ref *plumbing.Reference) (err error) {
|
||||||
|
tags = append(tags, strings.TrimPrefix(string(ref.Name()), "refs/tags/"))
|
||||||
|
return nil
|
||||||
|
}); err != nil {
|
||||||
|
return tags, err
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debugf("detected '%s' as tags for recipe '%s'", strings.Join(tags, ", "), r.Name)
|
||||||
|
|
||||||
|
return tags, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Get retrieves a recipe.
|
// Get retrieves a recipe.
|
||||||
func Get(recipeName string) (Recipe, error) {
|
func Get(recipeName string) (Recipe, error) {
|
||||||
if err := EnsureExists(recipeName); err != nil {
|
if err := EnsureExists(recipeName); err != nil {
|
||||||
|
24
pkg/server/server.go
Normal file
24
pkg/server/server.go
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
|
||||||
|
"coopcloud.tech/abra/pkg/config"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CreateServerDir creates a server directory under ~/.abra.
|
||||||
|
func CreateServerDir(serverName string) error {
|
||||||
|
serverPath := path.Join(config.ABRA_DIR, "servers", serverName)
|
||||||
|
|
||||||
|
if err := os.Mkdir(serverPath, 0755); err != nil {
|
||||||
|
if !os.IsExist(err) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Infof("'%s' already exists, moving on...", serverPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
@ -6,4 +6,4 @@ Register-ArgumentCompleter -Native -CommandName $name -ScriptBlock {
|
|||||||
Invoke-Expression $other | ForEach-Object {
|
Invoke-Expression $other | ForEach-Object {
|
||||||
[System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
|
[System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
ABRA_VERSION="0.1.3-alpha"
|
ABRA_VERSION="0.1.7-alpha"
|
||||||
ABRA_RELEASE_URL="https://git.coopcloud.tech/api/v1/repos/coop-cloud/abra/releases/tags/$ABRA_VERSION"
|
ABRA_RELEASE_URL="https://git.coopcloud.tech/api/v1/repos/coop-cloud/abra/releases/tags/$ABRA_VERSION"
|
||||||
|
|
||||||
function show_banner {
|
function show_banner {
|
||||||
|
0
tests/resources/test_folder/foo.env
Normal file
0
tests/resources/test_folder/foo.env
Normal file
Reference in New Issue
Block a user