Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 15 additions & 15 deletions cmd/gitops/kubernetes.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,15 @@ func applyKubernetes(c *cli.Context) error {
println(color.InGreen("No changes to apply."))
exitApplication(c, true)
return nil
}
}

prettyPrintPlan(p, c.Bool("show-unchanged"))

prettyPrintPlan(p)

if !c.Bool("auto-approve") {
println("GitOps CLI will apply these changes to your Kubernetes cluster.")
println("Only 'yes' will be accepted to approve.")
promtAnswer := util.StringPrompt("Apply changes above: ")

if promtAnswer != "yes" {
println("Aborting")
return nil
Expand Down Expand Up @@ -70,7 +70,7 @@ func planKubernetes(c *cli.Context) error {
if p.NothingToDo() {
println(color.InGreen("No changes to apply."))
} else {
prettyPrintPlan(p)
prettyPrintPlan(p, c.Bool("show-unchanged"))
dirLimitString := ""
if c.String("dir") != "" {
dirLimitString = " --dir " + c.String("dir")
Expand All @@ -92,7 +92,7 @@ func createKubernetesPlan(c *cli.Context) (*plan.Plan, error) {
println("Limiting to cluster " + color.InBlue(clusterLimit))
limitPrintln = true
}

dirLimit := getDirLimit(c)
if dirLimit != "" {
println("Limiting to directory " + color.InPurple(dirLimit))
Expand All @@ -115,10 +115,10 @@ func createKubernetesPlan(c *cli.Context) (*plan.Plan, error) {
return nil, err
}
log.Trace("Loaded ", len(localSecrets), " local secrets with target ", secret.SecretTargetTypeKubernetes)

p := &plan.Plan{
TargetType: secret.SecretTargetTypeKubernetes,
Items: []plan.PlanItem{},
Items: []plan.PlanItem{},
}

bar := progressbar.NewOptions(len(localSecrets),
Expand All @@ -130,7 +130,7 @@ func createKubernetesPlan(c *cli.Context) (*plan.Plan, error) {
progressbar.OptionSetPredictTime(false),
progressbar.OptionSetDescription("[green][Syncing local state with cluster][reset]"),
)

for _, localSecret := range localSecrets {
bar.Add(1)
// check for local secret in state
Expand Down Expand Up @@ -173,7 +173,7 @@ func createKubernetesPlan(c *cli.Context) (*plan.Plan, error) {
stateSecretFound := false
for _, localSecret := range localSecrets {
if stateSecret.Path == localSecret.Path {
stateSecretFound = true
stateSecretFound = true
break
}
}
Expand All @@ -188,9 +188,9 @@ func createKubernetesPlan(c *cli.Context) (*plan.Plan, error) {
// therefore we are checking if the cluster secret actually exists
log.Trace("Secret ", stateSecret.CombinedName(), " does not exist locally")
remoteSecret, err := k8s.GetSecret(&secret.Secret{
Name: stateSecret.Name,
Name: stateSecret.Name,
Namespace: stateSecret.Namespace,
Type: stateSecret.Type,
Type: stateSecret.Type,
}, stateSecret.Target)
if err != nil {
// only throw error if err is not "not found"
Expand All @@ -211,7 +211,7 @@ func createKubernetesPlan(c *cli.Context) (*plan.Plan, error) {
// at this state, the local secret does not exist anymore, but the secret is still in the state
// also, the cluster still holds the secret which needs to be deleted
planItem := plan.PlanItem{
LocalSecret: nil,
LocalSecret: nil,
RemoteSecret: remoteSecret,
}
planItem.ComputeDiff()
Expand Down Expand Up @@ -243,12 +243,12 @@ func getDirLimit(c *cli.Context) string {
return dirLimit
}

func prettyPrintPlan(p *plan.Plan) {
func prettyPrintPlan(p *plan.Plan, showUnchanged bool) {
println("")
println("GitOps CLI computed the following changes for your cluster:")
println("-------------------------------------------------------")
println("")
p.Print()
p.Print(showUnchanged)
println("")
println("-------------------------------------------------------")
println("")
Expand Down
77 changes: 41 additions & 36 deletions cmd/gitops/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,68 +17,73 @@ func main() {
Usage: "GitOps CLI",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "root-dir",
Value: "",
Usage: "root directory of the git repository",
Name: "root-dir",
Value: "",
Usage: "root directory of the git repository",
EnvVars: []string{"GITOPS_ROOT_DIR"},
},
&cli.StringFlag{
Name: "kubeconfig",
Name: "kubeconfig",
Aliases: []string{"k"},
Value: "",
Usage: "kubeconfig file to use for connecting to the Kubernetes cluster",
Value: "",
Usage: "kubeconfig file to use for connecting to the Kubernetes cluster",
EnvVars: []string{"KUBECONFIG", "GITOPS_KUBECONFIG"},
},
&cli.BoolFlag{
Name: "verbose",
Name: "verbose",
Aliases: []string{"v"},
Usage: "debug output",
Usage: "debug output",
EnvVars: []string{"GITOPS_VERBOSE"},
},
&cli.BoolFlag{
Name: "very-verbose",
Name: "very-verbose",
Aliases: []string{"vv"},
Usage: "trace output",
Usage: "trace output",
EnvVars: []string{"GITOPS_VERY_VERBOSE"},
},
&cli.BoolFlag{
Name: "cleartext",
Usage: "print secrets in cleartext to the console",
Name: "cleartext",
Usage: "print secrets in cleartext to the console",
EnvVars: []string{"GITOPS_CLEARTEXT"},
},
&cli.BoolFlag{
Name: "print",
Usage: "print secrets to the console",
Name: "print",
Usage: "print secrets to the console",
EnvVars: []string{"GITOPS_PRINT"},
},
&cli.BoolFlag{
Name: "show-unchanged",
Usage: "display unchanged secrets in the plan overview",
EnvVars: []string{"GITOPS_SHOW_UNCHANGED"},
},
},
Commands: []*cli.Command{
{
Name: "secrets",
Name: "secrets",
Aliases: []string{"s"},
Usage: "GitOps managed secrets",
Usage: "GitOps managed secrets",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "dir",
Name: "dir",
Aliases: []string{"d"},
Value: "",
Usage: "directory to limit secret discovery to",
Value: "",
Usage: "directory to limit secret discovery to",
EnvVars: []string{"GITOPS_SECRETS_DIR"},
},
},
Subcommands: []*cli.Command{
{
Name: "apply",
Name: "apply",
Aliases: []string{"a"},
Usage: "Push secrets into your infrastructure",
Usage: "Push secrets into your infrastructure",
Subcommands: []*cli.Command{
{
Name: "kubernetes",
Name: "kubernetes",
Aliases: []string{"k8s"},
Usage: "Push secrets into a Kubernetes cluster",
Usage: "Push secrets into a Kubernetes cluster",
Flags: []cli.Flag{
&cli.BoolFlag{
Name: "auto-approve",
Name: "auto-approve",
Usage: "apply the changes without prompting for approval",
},
},
Expand All @@ -88,7 +93,7 @@ func main() {
},
},
{
Name: "vault",
Name: "vault",
Usage: "Push secrets into vault",
Action: func(c *cli.Context) error {
log.Fatal("Not implemented yet")
Expand All @@ -98,14 +103,14 @@ func main() {
},
},
{
Name: "plan",
Name: "plan",
Aliases: []string{"p"},
Usage: "Plan the application of secrets into your infrastructure",
Usage: "Plan the application of secrets into your infrastructure",
Subcommands: []*cli.Command{
{
Name: "kubernetes",
Name: "kubernetes",
Aliases: []string{"k8s"},
Usage: "Plan the application of secrets into a Kubernetes cluster",
Usage: "Plan the application of secrets into a Kubernetes cluster",

Action: func(c *cli.Context) error {
initApplication(c)
Expand All @@ -115,7 +120,7 @@ func main() {
},
},
{
Name: "template",
Name: "template",
Usage: "Test the templating of secrets",
Action: func(c *cli.Context) error {
initApplication(c)
Expand All @@ -125,11 +130,11 @@ func main() {
},
},
{
Name: "clusters",
Name: "clusters",
Usage: "Managing target clusters",
Subcommands: []*cli.Command{
{
Name: "list",
Name: "list",
Usage: "List all target clusters",
Action: func(c *cli.Context) error {
initApplication(c)
Expand All @@ -145,7 +150,7 @@ func main() {
},
},
{
Name: "add",
Name: "add",
Usage: "Add a target cluster. <name> <configFile>",
Action: func(c *cli.Context) error {
initApplication(c)
Expand All @@ -159,7 +164,7 @@ func main() {
log.Fatal("Usage: gitops clusters add <name> <configFile>")
}
err := state.GetState().AddCluster(&state.ClusterState{
Name: c.Args().Get(0),
Name: c.Args().Get(0),
ConfigFile: kubeconfig,
})
if err != nil {
Expand All @@ -169,7 +174,7 @@ func main() {
},
},
{
Name: "remove",
Name: "remove",
Usage: "Remove a target cluster",
Action: func(c *cli.Context) error {
initApplication(c)
Expand All @@ -184,7 +189,7 @@ func main() {
},
},
{
Name: "test",
Name: "test",
Usage: "Test a target cluster connection",
Action: func(c *cli.Context) error {
initApplication(c)
Expand Down
7 changes: 5 additions & 2 deletions internal/plan/plan.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,11 @@ func (i *PlanItem) ComputeDiff() {
i.Diff = secret.CompareSecrets(i.RemoteSecret, i.LocalSecret)
}

func (p *Plan) Print() {
func (p *Plan) Print(showUnchanged bool) {
for i, item := range p.Items {
if !showUnchanged && item.Diff.Equal {
continue
}
item.Diff.Print()
if i < len(p.Items)-1 {
println("---")
Expand Down Expand Up @@ -90,4 +93,4 @@ func executeKubernetesPlan(p *Plan) error {
}
}
return nil
}
}
23 changes: 16 additions & 7 deletions internal/secret/loader.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package secret

import (
"errors"
"strings"

"github.com/mxcd/gitops-cli/internal/util"
Expand All @@ -9,9 +10,9 @@ import (
)

/*
Loads all the secrets from the local file system
Applies the specified target filter
Use SecretTargetAll to load all secrets
Loads all the secrets from the local file system
Applies the specified target filter
Use SecretTargetAll to load all secrets
*/
func LoadLocalSecrets(targetTypeFilter SecretTargetType) ([]*Secret, error) {
return LoadLocalSecretsLimited(targetTypeFilter, "", "")
Expand All @@ -23,22 +24,21 @@ func LoadLocalSecretsLimited(targetTypeFilter SecretTargetType, directoryLimit s
if err != nil {
return nil, err
}

// Filter by directory limit
filteredFileNames := []string{}
for _, secretFileName := range secretFileNames {
if !strings.HasPrefix(secretFileName, directoryLimit) {
log.Trace("Skipping file due to directory filter: ", secretFileName)
continue
}
if strings.HasSuffix(secretFileName, "values.gitops.secret.enc.yml") || strings.HasSuffix(secretFileName, "values.gitops.secret.enc.yaml") {
if strings.HasSuffix(secretFileName, "values.gitops.secret.enc.yml") || strings.HasSuffix(secretFileName, "values.gitops.secret.enc.yaml") {
log.Trace("Skipping values file: ", secretFileName)
continue
}
filteredFileNames = append(filteredFileNames, secretFileName)
}
secretFileNames = filteredFileNames


secrets := []*Secret{}
bar := progressbar.NewOptions(len(secretFileNames),
Expand All @@ -54,6 +54,7 @@ func LoadLocalSecretsLimited(targetTypeFilter SecretTargetType, directoryLimit s
bar.Add(1)
secret, err := FromPath(secretFileName)
if err != nil {
bar.Finish()
return nil, err
}
if secret.TargetType != targetTypeFilter && targetTypeFilter != SecretTargetTypeAll {
Expand All @@ -64,10 +65,18 @@ func LoadLocalSecretsLimited(targetTypeFilter SecretTargetType, directoryLimit s
log.Trace("Skipping file due to target filter: ", secretFileName)
continue
}
for _, s := range secrets {
if s.Name == secret.Name && s.Target == secret.Target {
bar.Finish()
println("")
log.Error("Unable to load secret '", secret.Name, "' from '", secret.Path, "' because a secret with the same name and target already exists: '", s.Path, "'")
return nil, errors.New("error loading secrets: duplicate secret name and target")
}
}
secrets = append(secrets, secret)
}
bar.Finish()
println("")
println("")
return secrets, nil
}
}