Skip to content

Commit 85691c9

Browse files
authored
feat: checking for secret name duplication; hiding unchanged secrets in plan (#3)
1 parent f9c4c14 commit 85691c9

4 files changed

Lines changed: 77 additions & 60 deletions

File tree

cmd/gitops/kubernetes.go

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,15 @@ func applyKubernetes(c *cli.Context) error {
2828
println(color.InGreen("No changes to apply."))
2929
exitApplication(c, true)
3030
return nil
31-
}
31+
}
32+
33+
prettyPrintPlan(p, c.Bool("show-unchanged"))
3234

33-
prettyPrintPlan(p)
34-
3535
if !c.Bool("auto-approve") {
3636
println("GitOps CLI will apply these changes to your Kubernetes cluster.")
3737
println("Only 'yes' will be accepted to approve.")
3838
promtAnswer := util.StringPrompt("Apply changes above: ")
39-
39+
4040
if promtAnswer != "yes" {
4141
println("Aborting")
4242
return nil
@@ -70,7 +70,7 @@ func planKubernetes(c *cli.Context) error {
7070
if p.NothingToDo() {
7171
println(color.InGreen("No changes to apply."))
7272
} else {
73-
prettyPrintPlan(p)
73+
prettyPrintPlan(p, c.Bool("show-unchanged"))
7474
dirLimitString := ""
7575
if c.String("dir") != "" {
7676
dirLimitString = " --dir " + c.String("dir")
@@ -92,7 +92,7 @@ func createKubernetesPlan(c *cli.Context) (*plan.Plan, error) {
9292
println("Limiting to cluster " + color.InBlue(clusterLimit))
9393
limitPrintln = true
9494
}
95-
95+
9696
dirLimit := getDirLimit(c)
9797
if dirLimit != "" {
9898
println("Limiting to directory " + color.InPurple(dirLimit))
@@ -115,10 +115,10 @@ func createKubernetesPlan(c *cli.Context) (*plan.Plan, error) {
115115
return nil, err
116116
}
117117
log.Trace("Loaded ", len(localSecrets), " local secrets with target ", secret.SecretTargetTypeKubernetes)
118-
118+
119119
p := &plan.Plan{
120120
TargetType: secret.SecretTargetTypeKubernetes,
121-
Items: []plan.PlanItem{},
121+
Items: []plan.PlanItem{},
122122
}
123123

124124
bar := progressbar.NewOptions(len(localSecrets),
@@ -130,7 +130,7 @@ func createKubernetesPlan(c *cli.Context) (*plan.Plan, error) {
130130
progressbar.OptionSetPredictTime(false),
131131
progressbar.OptionSetDescription("[green][Syncing local state with cluster][reset]"),
132132
)
133-
133+
134134
for _, localSecret := range localSecrets {
135135
bar.Add(1)
136136
// check for local secret in state
@@ -173,7 +173,7 @@ func createKubernetesPlan(c *cli.Context) (*plan.Plan, error) {
173173
stateSecretFound := false
174174
for _, localSecret := range localSecrets {
175175
if stateSecret.Path == localSecret.Path {
176-
stateSecretFound = true
176+
stateSecretFound = true
177177
break
178178
}
179179
}
@@ -188,9 +188,9 @@ func createKubernetesPlan(c *cli.Context) (*plan.Plan, error) {
188188
// therefore we are checking if the cluster secret actually exists
189189
log.Trace("Secret ", stateSecret.CombinedName(), " does not exist locally")
190190
remoteSecret, err := k8s.GetSecret(&secret.Secret{
191-
Name: stateSecret.Name,
191+
Name: stateSecret.Name,
192192
Namespace: stateSecret.Namespace,
193-
Type: stateSecret.Type,
193+
Type: stateSecret.Type,
194194
}, stateSecret.Target)
195195
if err != nil {
196196
// only throw error if err is not "not found"
@@ -211,7 +211,7 @@ func createKubernetesPlan(c *cli.Context) (*plan.Plan, error) {
211211
// at this state, the local secret does not exist anymore, but the secret is still in the state
212212
// also, the cluster still holds the secret which needs to be deleted
213213
planItem := plan.PlanItem{
214-
LocalSecret: nil,
214+
LocalSecret: nil,
215215
RemoteSecret: remoteSecret,
216216
}
217217
planItem.ComputeDiff()
@@ -243,12 +243,12 @@ func getDirLimit(c *cli.Context) string {
243243
return dirLimit
244244
}
245245

246-
func prettyPrintPlan(p *plan.Plan) {
246+
func prettyPrintPlan(p *plan.Plan, showUnchanged bool) {
247247
println("")
248248
println("GitOps CLI computed the following changes for your cluster:")
249249
println("-------------------------------------------------------")
250250
println("")
251-
p.Print()
251+
p.Print(showUnchanged)
252252
println("")
253253
println("-------------------------------------------------------")
254254
println("")

cmd/gitops/main.go

Lines changed: 41 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -17,68 +17,73 @@ func main() {
1717
Usage: "GitOps CLI",
1818
Flags: []cli.Flag{
1919
&cli.StringFlag{
20-
Name: "root-dir",
21-
Value: "",
22-
Usage: "root directory of the git repository",
20+
Name: "root-dir",
21+
Value: "",
22+
Usage: "root directory of the git repository",
2323
EnvVars: []string{"GITOPS_ROOT_DIR"},
2424
},
2525
&cli.StringFlag{
26-
Name: "kubeconfig",
26+
Name: "kubeconfig",
2727
Aliases: []string{"k"},
28-
Value: "",
29-
Usage: "kubeconfig file to use for connecting to the Kubernetes cluster",
28+
Value: "",
29+
Usage: "kubeconfig file to use for connecting to the Kubernetes cluster",
3030
EnvVars: []string{"KUBECONFIG", "GITOPS_KUBECONFIG"},
3131
},
3232
&cli.BoolFlag{
33-
Name: "verbose",
33+
Name: "verbose",
3434
Aliases: []string{"v"},
35-
Usage: "debug output",
35+
Usage: "debug output",
3636
EnvVars: []string{"GITOPS_VERBOSE"},
3737
},
3838
&cli.BoolFlag{
39-
Name: "very-verbose",
39+
Name: "very-verbose",
4040
Aliases: []string{"vv"},
41-
Usage: "trace output",
41+
Usage: "trace output",
4242
EnvVars: []string{"GITOPS_VERY_VERBOSE"},
4343
},
4444
&cli.BoolFlag{
45-
Name: "cleartext",
46-
Usage: "print secrets in cleartext to the console",
45+
Name: "cleartext",
46+
Usage: "print secrets in cleartext to the console",
4747
EnvVars: []string{"GITOPS_CLEARTEXT"},
4848
},
4949
&cli.BoolFlag{
50-
Name: "print",
51-
Usage: "print secrets to the console",
50+
Name: "print",
51+
Usage: "print secrets to the console",
5252
EnvVars: []string{"GITOPS_PRINT"},
5353
},
54+
&cli.BoolFlag{
55+
Name: "show-unchanged",
56+
Usage: "display unchanged secrets in the plan overview",
57+
EnvVars: []string{"GITOPS_SHOW_UNCHANGED"},
58+
},
5459
},
5560
Commands: []*cli.Command{
5661
{
57-
Name: "secrets",
62+
Name: "secrets",
5863
Aliases: []string{"s"},
59-
Usage: "GitOps managed secrets",
64+
Usage: "GitOps managed secrets",
6065
Flags: []cli.Flag{
6166
&cli.StringFlag{
62-
Name: "dir",
67+
Name: "dir",
6368
Aliases: []string{"d"},
64-
Value: "",
65-
Usage: "directory to limit secret discovery to",
69+
Value: "",
70+
Usage: "directory to limit secret discovery to",
6671
EnvVars: []string{"GITOPS_SECRETS_DIR"},
6772
},
6873
},
6974
Subcommands: []*cli.Command{
7075
{
71-
Name: "apply",
76+
Name: "apply",
7277
Aliases: []string{"a"},
73-
Usage: "Push secrets into your infrastructure",
78+
Usage: "Push secrets into your infrastructure",
7479
Subcommands: []*cli.Command{
7580
{
76-
Name: "kubernetes",
81+
Name: "kubernetes",
7782
Aliases: []string{"k8s"},
78-
Usage: "Push secrets into a Kubernetes cluster",
83+
Usage: "Push secrets into a Kubernetes cluster",
7984
Flags: []cli.Flag{
8085
&cli.BoolFlag{
81-
Name: "auto-approve",
86+
Name: "auto-approve",
8287
Usage: "apply the changes without prompting for approval",
8388
},
8489
},
@@ -88,7 +93,7 @@ func main() {
8893
},
8994
},
9095
{
91-
Name: "vault",
96+
Name: "vault",
9297
Usage: "Push secrets into vault",
9398
Action: func(c *cli.Context) error {
9499
log.Fatal("Not implemented yet")
@@ -98,14 +103,14 @@ func main() {
98103
},
99104
},
100105
{
101-
Name: "plan",
106+
Name: "plan",
102107
Aliases: []string{"p"},
103-
Usage: "Plan the application of secrets into your infrastructure",
108+
Usage: "Plan the application of secrets into your infrastructure",
104109
Subcommands: []*cli.Command{
105110
{
106-
Name: "kubernetes",
111+
Name: "kubernetes",
107112
Aliases: []string{"k8s"},
108-
Usage: "Plan the application of secrets into a Kubernetes cluster",
113+
Usage: "Plan the application of secrets into a Kubernetes cluster",
109114

110115
Action: func(c *cli.Context) error {
111116
initApplication(c)
@@ -115,7 +120,7 @@ func main() {
115120
},
116121
},
117122
{
118-
Name: "template",
123+
Name: "template",
119124
Usage: "Test the templating of secrets",
120125
Action: func(c *cli.Context) error {
121126
initApplication(c)
@@ -125,11 +130,11 @@ func main() {
125130
},
126131
},
127132
{
128-
Name: "clusters",
133+
Name: "clusters",
129134
Usage: "Managing target clusters",
130135
Subcommands: []*cli.Command{
131136
{
132-
Name: "list",
137+
Name: "list",
133138
Usage: "List all target clusters",
134139
Action: func(c *cli.Context) error {
135140
initApplication(c)
@@ -145,7 +150,7 @@ func main() {
145150
},
146151
},
147152
{
148-
Name: "add",
153+
Name: "add",
149154
Usage: "Add a target cluster. <name> <configFile>",
150155
Action: func(c *cli.Context) error {
151156
initApplication(c)
@@ -159,7 +164,7 @@ func main() {
159164
log.Fatal("Usage: gitops clusters add <name> <configFile>")
160165
}
161166
err := state.GetState().AddCluster(&state.ClusterState{
162-
Name: c.Args().Get(0),
167+
Name: c.Args().Get(0),
163168
ConfigFile: kubeconfig,
164169
})
165170
if err != nil {
@@ -169,7 +174,7 @@ func main() {
169174
},
170175
},
171176
{
172-
Name: "remove",
177+
Name: "remove",
173178
Usage: "Remove a target cluster",
174179
Action: func(c *cli.Context) error {
175180
initApplication(c)
@@ -184,7 +189,7 @@ func main() {
184189
},
185190
},
186191
{
187-
Name: "test",
192+
Name: "test",
188193
Usage: "Test a target cluster connection",
189194
Action: func(c *cli.Context) error {
190195
initApplication(c)

internal/plan/plan.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,11 @@ func (i *PlanItem) ComputeDiff() {
4040
i.Diff = secret.CompareSecrets(i.RemoteSecret, i.LocalSecret)
4141
}
4242

43-
func (p *Plan) Print() {
43+
func (p *Plan) Print(showUnchanged bool) {
4444
for i, item := range p.Items {
45+
if !showUnchanged && item.Diff.Equal {
46+
continue
47+
}
4548
item.Diff.Print()
4649
if i < len(p.Items)-1 {
4750
println("---")
@@ -90,4 +93,4 @@ func executeKubernetesPlan(p *Plan) error {
9093
}
9194
}
9295
return nil
93-
}
96+
}

internal/secret/loader.go

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package secret
22

33
import (
4+
"errors"
45
"strings"
56

67
"github.com/mxcd/gitops-cli/internal/util"
@@ -9,9 +10,9 @@ import (
910
)
1011

1112
/*
12-
Loads all the secrets from the local file system
13-
Applies the specified target filter
14-
Use SecretTargetAll to load all secrets
13+
Loads all the secrets from the local file system
14+
Applies the specified target filter
15+
Use SecretTargetAll to load all secrets
1516
*/
1617
func LoadLocalSecrets(targetTypeFilter SecretTargetType) ([]*Secret, error) {
1718
return LoadLocalSecretsLimited(targetTypeFilter, "", "")
@@ -23,22 +24,21 @@ func LoadLocalSecretsLimited(targetTypeFilter SecretTargetType, directoryLimit s
2324
if err != nil {
2425
return nil, err
2526
}
26-
27+
2728
// Filter by directory limit
2829
filteredFileNames := []string{}
2930
for _, secretFileName := range secretFileNames {
3031
if !strings.HasPrefix(secretFileName, directoryLimit) {
3132
log.Trace("Skipping file due to directory filter: ", secretFileName)
3233
continue
3334
}
34-
if strings.HasSuffix(secretFileName, "values.gitops.secret.enc.yml") || strings.HasSuffix(secretFileName, "values.gitops.secret.enc.yaml") {
35+
if strings.HasSuffix(secretFileName, "values.gitops.secret.enc.yml") || strings.HasSuffix(secretFileName, "values.gitops.secret.enc.yaml") {
3536
log.Trace("Skipping values file: ", secretFileName)
3637
continue
3738
}
3839
filteredFileNames = append(filteredFileNames, secretFileName)
3940
}
4041
secretFileNames = filteredFileNames
41-
4242

4343
secrets := []*Secret{}
4444
bar := progressbar.NewOptions(len(secretFileNames),
@@ -54,6 +54,7 @@ func LoadLocalSecretsLimited(targetTypeFilter SecretTargetType, directoryLimit s
5454
bar.Add(1)
5555
secret, err := FromPath(secretFileName)
5656
if err != nil {
57+
bar.Finish()
5758
return nil, err
5859
}
5960
if secret.TargetType != targetTypeFilter && targetTypeFilter != SecretTargetTypeAll {
@@ -64,10 +65,18 @@ func LoadLocalSecretsLimited(targetTypeFilter SecretTargetType, directoryLimit s
6465
log.Trace("Skipping file due to target filter: ", secretFileName)
6566
continue
6667
}
68+
for _, s := range secrets {
69+
if s.Name == secret.Name && s.Target == secret.Target {
70+
bar.Finish()
71+
println("")
72+
log.Error("Unable to load secret '", secret.Name, "' from '", secret.Path, "' because a secret with the same name and target already exists: '", s.Path, "'")
73+
return nil, errors.New("error loading secrets: duplicate secret name and target")
74+
}
75+
}
6776
secrets = append(secrets, secret)
6877
}
6978
bar.Finish()
7079
println("")
7180
println("")
7281
return secrets, nil
73-
}
82+
}

0 commit comments

Comments
 (0)