parent
6fc4573a71
commit
b57edb440a
|
@ -1,13 +1,10 @@
|
||||||
package app
|
package app
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"coopcloud.tech/abra/cli/internal"
|
"coopcloud.tech/abra/cli/internal"
|
||||||
"coopcloud.tech/abra/pkg/autocomplete"
|
"coopcloud.tech/abra/pkg/autocomplete"
|
||||||
"coopcloud.tech/abra/pkg/config"
|
"coopcloud.tech/abra/pkg/config"
|
||||||
|
"coopcloud.tech/abra/pkg/formatter"
|
||||||
"coopcloud.tech/abra/pkg/recipe"
|
"coopcloud.tech/abra/pkg/recipe"
|
||||||
recipePkg "coopcloud.tech/abra/pkg/recipe"
|
recipePkg "coopcloud.tech/abra/pkg/recipe"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
@ -15,9 +12,21 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var appCheckCommand = cli.Command{
|
var appCheckCommand = cli.Command{
|
||||||
Name: "check",
|
Name: "check",
|
||||||
Aliases: []string{"chk"},
|
Aliases: []string{"chk"},
|
||||||
Usage: "Check if an app is configured correctly",
|
Usage: "Ensure an app is well configured",
|
||||||
|
Description: `
|
||||||
|
This command compares env vars in both the app ".env" and recipe ".env.sample"
|
||||||
|
file.
|
||||||
|
|
||||||
|
The goal is to ensure that recipe ".env.sample" env vars are defined in your
|
||||||
|
app ".env" file. Only env var definitions in the ".env.sample" which are
|
||||||
|
uncommented, e.g. "FOO=bar" are checked. If an app ".env" file does not include
|
||||||
|
these env vars, then "check" will complain.
|
||||||
|
|
||||||
|
Recipe maintainers may or may not provide defaults for env vars within their
|
||||||
|
recipes regardless of commenting or not (e.g. through the use of
|
||||||
|
${FOO:<default>} syntax). "check" does not confirm or deny this for you.`,
|
||||||
ArgsUsage: "<domain>",
|
ArgsUsage: "<domain>",
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
internal.DebugFlag,
|
internal.DebugFlag,
|
||||||
|
@ -49,32 +58,23 @@ var appCheckCommand = cli.Command{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
envSamplePath := path.Join(config.RECIPES_DIR, app.Recipe, ".env.sample")
|
tableCol := []string{"recipe env sample", "app env"}
|
||||||
if _, err := os.Stat(envSamplePath); err != nil {
|
table := formatter.CreateTable(tableCol)
|
||||||
if os.IsNotExist(err) {
|
|
||||||
logrus.Fatalf("%s does not exist?", envSamplePath)
|
|
||||||
}
|
|
||||||
logrus.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
envSample, err := config.ReadEnv(envSamplePath)
|
envVars, err := config.CheckEnv(app)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var missing []string
|
for _, envVar := range envVars {
|
||||||
for k := range envSample {
|
if envVar.Present {
|
||||||
if _, ok := app.Env[k]; !ok {
|
table.Append([]string{envVar.Name, "✅"})
|
||||||
missing = append(missing, k)
|
} else {
|
||||||
|
table.Append([]string{envVar.Name, "❌"})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(missing) > 0 {
|
table.Render()
|
||||||
missingEnvVars := strings.Join(missing, ", ")
|
|
||||||
logrus.Fatalf("%s is missing %s", app.Path, missingEnvVars)
|
|
||||||
}
|
|
||||||
|
|
||||||
logrus.Infof("all necessary environment variables defined for %s", app.Name)
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
|
|
|
@ -200,6 +200,17 @@ recipes.
|
||||||
config.SetChaosVersionLabel(compose, stackName, version)
|
config.SetChaosVersionLabel(compose, stackName, version)
|
||||||
config.SetUpdateLabel(compose, stackName, app.Env)
|
config.SetUpdateLabel(compose, stackName, app.Env)
|
||||||
|
|
||||||
|
envVars, err := config.CheckEnv(app)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, envVar := range envVars {
|
||||||
|
if !envVar.Present {
|
||||||
|
logrus.Warnf("env var %s missing from %s.env, present in recipe .env.sample", envVar.Name, app.Domain)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if err := internal.DeployOverview(app, version, "continue with deployment?"); err != nil {
|
if err := internal.DeployOverview(app, version, "continue with deployment?"); err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -254,6 +254,17 @@ recipes.
|
||||||
config.SetChaosVersionLabel(compose, stackName, chosenUpgrade)
|
config.SetChaosVersionLabel(compose, stackName, chosenUpgrade)
|
||||||
config.SetUpdateLabel(compose, stackName, app.Env)
|
config.SetUpdateLabel(compose, stackName, app.Env)
|
||||||
|
|
||||||
|
envVars, err := config.CheckEnv(app)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, envVar := range envVars {
|
||||||
|
if !envVar.Present {
|
||||||
|
logrus.Warnf("env var %s missing from %s.env, present in recipe .env.sample", envVar.Name, app.Domain)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if err := internal.NewVersionOverview(app, deployedVersion, chosenUpgrade, releaseNotes); err != nil {
|
if err := internal.NewVersionOverview(app, deployedVersion, chosenUpgrade, releaseNotes); err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/Autonomic-Cooperative/godotenv"
|
"github.com/Autonomic-Cooperative/godotenv"
|
||||||
|
@ -179,3 +180,42 @@ func ReadAbraShEnvVars(abraSh string) (map[string]string, error) {
|
||||||
|
|
||||||
return envVars, nil
|
return envVars, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type EnvVar struct {
|
||||||
|
Name string
|
||||||
|
Present bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func CheckEnv(app App) ([]EnvVar, error) {
|
||||||
|
var envVars []EnvVar
|
||||||
|
|
||||||
|
envSamplePath := path.Join(RECIPES_DIR, app.Recipe, ".env.sample")
|
||||||
|
if _, err := os.Stat(envSamplePath); err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return envVars, fmt.Errorf("%s does not exist?", envSamplePath)
|
||||||
|
}
|
||||||
|
return envVars, err
|
||||||
|
}
|
||||||
|
|
||||||
|
envSample, err := ReadEnv(envSamplePath)
|
||||||
|
if err != nil {
|
||||||
|
return envVars, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var keys []string
|
||||||
|
for key := range envSample {
|
||||||
|
keys = append(keys, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Strings(keys)
|
||||||
|
|
||||||
|
for _, key := range keys {
|
||||||
|
if _, ok := app.Env[key]; ok {
|
||||||
|
envVars = append(envVars, EnvVar{Name: key, Present: true})
|
||||||
|
} else {
|
||||||
|
envVars = append(envVars, EnvVar{Name: key, Present: false})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return envVars, nil
|
||||||
|
}
|
||||||
|
|
|
@ -114,3 +114,73 @@ func TestReadAbraShEnvVars(t *testing.T) {
|
||||||
t.Error("OUTER_FOO should be exported")
|
t.Error("OUTER_FOO should be exported")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCheckEnv(t *testing.T) {
|
||||||
|
offline := true
|
||||||
|
r, err := recipe.Get("abra-test-recipe", offline)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
envSamplePath := path.Join(config.RECIPES_DIR, r.Name, ".env.sample")
|
||||||
|
envSample, err := config.ReadEnv(envSamplePath)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
app := config.App{
|
||||||
|
Name: "test-app",
|
||||||
|
Recipe: r.Name,
|
||||||
|
Domain: "example.com",
|
||||||
|
Env: envSample,
|
||||||
|
Path: "example.com.env",
|
||||||
|
Server: "example.com",
|
||||||
|
}
|
||||||
|
|
||||||
|
envVars, err := config.CheckEnv(app)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, envVar := range envVars {
|
||||||
|
if !envVar.Present {
|
||||||
|
t.Fatalf("%s should be present", envVar.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCheckEnvError(t *testing.T) {
|
||||||
|
offline := true
|
||||||
|
r, err := recipe.Get("abra-test-recipe", offline)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
envSamplePath := path.Join(config.RECIPES_DIR, r.Name, ".env.sample")
|
||||||
|
envSample, err := config.ReadEnv(envSamplePath)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(envSample, "DOMAIN")
|
||||||
|
|
||||||
|
app := config.App{
|
||||||
|
Name: "test-app",
|
||||||
|
Recipe: r.Name,
|
||||||
|
Domain: "example.com",
|
||||||
|
Env: envSample,
|
||||||
|
Path: "example.com.env",
|
||||||
|
Server: "example.com",
|
||||||
|
}
|
||||||
|
|
||||||
|
envVars, err := config.CheckEnv(app)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, envVar := range envVars {
|
||||||
|
if envVar.Name == "DOMAIN" && envVar.Present {
|
||||||
|
t.Fatalf("%s should not be present", envVar.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue