forked from toolshed/abra
Compare commits
25 Commits
0.1.5-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
|
@ -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:"
|
||||||
|
@ -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,
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ package catalogue
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"path"
|
"path"
|
||||||
|
|
||||||
@ -45,11 +46,22 @@ var CatalogueSkipList = map[string]bool{
|
|||||||
}
|
}
|
||||||
|
|
||||||
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",
|
||||||
ArgsUsage: "[<recipe>]",
|
ArgsUsage: "[<recipe>]",
|
||||||
BashComplete: func(c *cli.Context) {},
|
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 {
|
||||||
recipeName := c.Args().First()
|
recipeName := c.Args().First()
|
||||||
|
|
||||||
@ -60,18 +72,18 @@ var catalogueGenerateCommand = &cli.Command{
|
|||||||
|
|
||||||
logrus.Debugf("ensuring '%v' recipe(s) are locally present and up-to-date", len(repos))
|
logrus.Debugf("ensuring '%v' recipe(s) are locally present and up-to-date", len(repos))
|
||||||
|
|
||||||
bar := formatter.CreateProgressbar(len(repos), "retrieving recipes...")
|
retrieveBar := formatter.CreateProgressbar(len(repos), "retrieving recipes...")
|
||||||
ch := make(chan string, len(repos))
|
ch := make(chan string, len(repos))
|
||||||
for _, repoMeta := range repos {
|
for _, repoMeta := range repos {
|
||||||
go func(rm catalogue.RepoMeta) {
|
go func(rm catalogue.RepoMeta) {
|
||||||
if recipeName != "" && recipeName != rm.Name {
|
if recipeName != "" && recipeName != rm.Name {
|
||||||
ch <- rm.Name
|
ch <- rm.Name
|
||||||
bar.Add(1)
|
retrieveBar.Add(1)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if _, exists := CatalogueSkipList[rm.Name]; exists {
|
if _, exists := CatalogueSkipList[rm.Name]; exists {
|
||||||
ch <- rm.Name
|
ch <- rm.Name
|
||||||
bar.Add(1)
|
retrieveBar.Add(1)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,7 +98,7 @@ var catalogueGenerateCommand = &cli.Command{
|
|||||||
}
|
}
|
||||||
|
|
||||||
ch <- rm.Name
|
ch <- rm.Name
|
||||||
bar.Add(1)
|
retrieveBar.Add(1)
|
||||||
}(repoMeta)
|
}(repoMeta)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,14 +107,23 @@ var catalogueGenerateCommand = &cli.Command{
|
|||||||
}
|
}
|
||||||
|
|
||||||
catl := make(catalogue.RecipeCatalogue)
|
catl := make(catalogue.RecipeCatalogue)
|
||||||
|
catlBar := formatter.CreateProgressbar(len(repos), "generating catalogue...")
|
||||||
for _, recipeMeta := range repos {
|
for _, recipeMeta := range repos {
|
||||||
if recipeName != "" && recipeName != recipeMeta.Name {
|
if recipeName != "" && recipeName != recipeMeta.Name {
|
||||||
|
catlBar.Add(1)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, exists := CatalogueSkipList[recipeMeta.Name]; exists {
|
if _, exists := CatalogueSkipList[recipeMeta.Name]; exists {
|
||||||
|
catlBar.Add(1)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
versions, err := catalogue.GetRecipeVersions(recipeMeta.Name)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
catl[recipeMeta.Name] = catalogue.RecipeMeta{
|
catl[recipeMeta.Name] = catalogue.RecipeMeta{
|
||||||
Name: recipeMeta.Name,
|
Name: recipeMeta.Name,
|
||||||
Repository: recipeMeta.CloneURL,
|
Repository: recipeMeta.CloneURL,
|
||||||
@ -110,10 +131,11 @@ var catalogueGenerateCommand = &cli.Command{
|
|||||||
DefaultBranch: recipeMeta.DefaultBranch,
|
DefaultBranch: recipeMeta.DefaultBranch,
|
||||||
Description: recipeMeta.Description,
|
Description: recipeMeta.Description,
|
||||||
Website: recipeMeta.Website,
|
Website: recipeMeta.Website,
|
||||||
// Versions: ..., // FIXME: once the new versions work goes down
|
Versions: versions,
|
||||||
// Category: ..., // FIXME: once we sort out the machine-readable catalogue interface
|
// Category: ..., // FIXME: once we sort out the machine-readable catalogue interface
|
||||||
// Features: ..., // FIXME: once we figure 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, "", " ")
|
recipesJSON, err := json.MarshalIndent(catl, "", " ")
|
||||||
@ -125,7 +147,7 @@ var catalogueGenerateCommand = &cli.Command{
|
|||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
logrus.Debugf("generated new recipe catalogue in '%s'", config.APPS_JSON)
|
logrus.Infof("generated new recipe catalogue in '%s'", config.APPS_JSON)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
|
25
cli/cli.go
25
cli/cli.go
@ -4,11 +4,13 @@ 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"
|
logrusStack "github.com/Gurpartap/logrus-stack"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
@ -81,11 +83,30 @@ func RunApp(version, commit string) {
|
|||||||
logrus.SetOutput(os.Stderr)
|
logrus.SetOutput(os.Stderr)
|
||||||
logrus.AddHook(logrusStack.StandardHook())
|
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)
|
||||||
}
|
}
|
||||||
|
@ -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",
|
||||||
|
@ -33,34 +33,10 @@ var DryFlag = &cli.BoolFlag{
|
|||||||
Destination: &Dry,
|
Destination: &Dry,
|
||||||
}
|
}
|
||||||
|
|
||||||
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,
|
|
||||||
}
|
|
||||||
|
|
||||||
var CommitMessage string
|
var CommitMessage string
|
||||||
var CommitMessageFlag = &cli.StringFlag{
|
var CommitMessageFlag = &cli.StringFlag{
|
||||||
Name: "commit-message",
|
Name: "commit-message",
|
||||||
Usage: "commit message",
|
Usage: "commit message. Implies --commit",
|
||||||
Aliases: []string{"cm"},
|
Aliases: []string{"cm"},
|
||||||
Destination: &CommitMessage,
|
Destination: &CommitMessage,
|
||||||
}
|
}
|
||||||
@ -68,6 +44,7 @@ var CommitMessageFlag = &cli.StringFlag{
|
|||||||
var Commit bool
|
var Commit bool
|
||||||
var CommitFlag = &cli.BoolFlag{
|
var CommitFlag = &cli.BoolFlag{
|
||||||
Name: "commit",
|
Name: "commit",
|
||||||
|
Usage: "add compose.yml to staging area and commit changes",
|
||||||
Value: false,
|
Value: false,
|
||||||
Aliases: []string{"c"},
|
Aliases: []string{"c"},
|
||||||
Destination: &Commit,
|
Destination: &Commit,
|
||||||
@ -142,6 +119,12 @@ or a rollback of an app.
|
|||||||
}
|
}
|
||||||
survey.AskOne(prompt, &CommitMessage)
|
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{})
|
_, err = commitWorktree.Commit(CommitMessage, &git.CommitOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
@ -238,12 +221,15 @@ or a rollback of an app.
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
|
newTag.Patch = "0"
|
||||||
newTag.Minor = strconv.Itoa(now + 1)
|
newTag.Minor = strconv.Itoa(now + 1)
|
||||||
} else if Major {
|
} else if Major {
|
||||||
now, err := strconv.Atoi(newTag.Major)
|
now, err := strconv.Atoi(newTag.Major)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
|
newTag.Patch = "0"
|
||||||
|
newTag.Minor = "0"
|
||||||
newTag.Major = strconv.Itoa(now + 1)
|
newTag.Major = strconv.Itoa(now + 1)
|
||||||
}
|
}
|
||||||
newTagString = newTag.String()
|
newTagString = newTag.String()
|
||||||
|
@ -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,14 +27,24 @@ 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 {
|
||||||
@ -107,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
|
||||||
|
@ -3,14 +3,12 @@ package server
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"os"
|
|
||||||
"os/user"
|
"os/user"
|
||||||
"path"
|
|
||||||
"strings"
|
"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/config"
|
"coopcloud.tech/abra/pkg/server"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
@ -30,10 +28,14 @@ var serverAddCommand = &cli.Command{
|
|||||||
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:
|
||||||
|
|
||||||
@ -44,9 +46,6 @@ 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.
|
||||||
|
|
||||||
NOTE: If you specify --local, none of the above applies 🙃
|
|
||||||
|
|
||||||
`,
|
`,
|
||||||
Aliases: []string{"a"},
|
Aliases: []string{"a"},
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
@ -67,7 +66,10 @@ NOTE: If you specify --local, none of the above applies 🙃
|
|||||||
domainName := "default"
|
domainName := "default"
|
||||||
|
|
||||||
if local {
|
if local {
|
||||||
os.Mkdir(path.Join(config.ABRA_DIR, "servers", domainName), 0755)
|
if err := server.CreateServerDir(domainName); err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
logrus.Info("local server has been added")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,9 +126,12 @@ NOTE: If you specify --local, none of the above applies 🙃
|
|||||||
}
|
}
|
||||||
|
|
||||||
logrus.Debugf("remote connection to '%s' is definitely up", domainName)
|
logrus.Debugf("remote connection to '%s' is definitely up", domainName)
|
||||||
logrus.Infof("server at '%s' has been added", domainName)
|
|
||||||
|
|
||||||
os.Mkdir(path.Join(config.ABRA_DIR, "servers", domainName), 0755)
|
if err := server.CreateServerDir(domainName); err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
}
|
}
|
||||||
|
2
go.mod
2
go.mod
@ -3,7 +3,7 @@ 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/Gurpartap/logrus-stack v0.0.0-20170710170904-89c00d8a28f4
|
||||||
|
4
go.sum
4
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=
|
||||||
|
@ -9,12 +9,17 @@ 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"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -56,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.
|
||||||
@ -365,3 +373,110 @@ func ReadReposMetadata() (RepoCatalogue, error) {
|
|||||||
|
|
||||||
return reposMeta, nil
|
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
|
||||||
}
|
}
|
||||||
|
@ -139,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)
|
||||||
|
@ -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
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
ABRA_VERSION="0.1.5-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/cruft.txt
Normal file
0
tests/resources/test_folder/cruft.txt
Normal file
Reference in New Issue
Block a user