Skip to content

Commit 843163f

Browse files
Feat: Support encrypted variables in project and pipeline (including triggers) (#142)
## What Support for encrypted variables - closes #97 ## Why ## Notes <!-- Add any notes here --> ## Checklist * [x] _I have read [CONTRIBUTING.md](https://github.com/codefresh-io/terraform-provider-codefresh/blob/master/CONTRIBUTING.md)._ * [x] _I have [allowed changes to my fork to be made](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/allowing-changes-to-a-pull-request-branch-created-from-a-fork)._ * [x] _I have added tests, assuming new tests are warranted_. * [x] _I understand that the `/test` comment will be ignored by the CI trigger [unless it is made by a repo admin or collaborator](https://codefresh.io/docs/docs/pipelines/triggers/git-triggers/#support-for-building-pull-requests-from-forks)._
1 parent 92a08ac commit 843163f

File tree

10 files changed

+218
-42
lines changed

10 files changed

+218
-42
lines changed

codefresh/cfclient/pipeline.go

+6-6
Original file line numberDiff line numberDiff line change
@@ -90,15 +90,15 @@ type RuntimeEnvironment struct {
9090
RequiredAvailableStorage string `json:"requiredAvailableStorage,omitempty"`
9191
}
9292

93-
func (t *Trigger) SetVariables(variables map[string]interface{}) {
93+
func (t *Trigger) SetVariables(variables map[string]interface{}, encrypted bool) {
9494
for key, value := range variables {
95-
t.Variables = append(t.Variables, Variable{Key: key, Value: value.(string)})
95+
t.Variables = append(t.Variables, Variable{Key: key, Value: value.(string), Encrypted: encrypted})
9696
}
9797
}
9898

99-
func (t *CronTrigger) SetVariables(variables map[string]interface{}) {
99+
func (t *CronTrigger) SetVariables(variables map[string]interface{}, encrypted bool) {
100100
for key, value := range variables {
101-
t.Variables = append(t.Variables, Variable{Key: key, Value: value.(string)})
101+
t.Variables = append(t.Variables, Variable{Key: key, Value: value.(string), Encrypted: encrypted})
102102
}
103103
}
104104

@@ -169,9 +169,9 @@ type Pipeline struct {
169169
Version string `json:"version,omitempty"`
170170
}
171171

172-
func (p *Pipeline) SetVariables(variables map[string]interface{}) {
172+
func (p *Pipeline) SetVariables(variables map[string]interface{}, encrypted bool) {
173173
for key, value := range variables {
174-
p.Spec.Variables = append(p.Spec.Variables, Variable{Key: key, Value: value.(string)})
174+
p.Spec.Variables = append(p.Spec.Variables, Variable{Key: key, Value: value.(string), Encrypted: encrypted})
175175
}
176176
}
177177

codefresh/cfclient/project.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@ func (project *Project) GetID() string {
1919
}
2020

2121
// SetVariables project variables
22-
func (project *Project) SetVariables(variables map[string]interface{}) {
22+
func (project *Project) SetVariables(variables map[string]interface{}, encrypted bool) {
2323
for key, value := range variables {
24-
project.Variables = append(project.Variables, Variable{Key: key, Value: value.(string)})
24+
project.Variables = append(project.Variables, Variable{Key: key, Value: value.(string), Encrypted: encrypted})
2525
}
2626
}
2727

codefresh/cfclient/utils.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@ import (
77

88
// Variable spec
99
type Variable struct {
10-
Key string `json:"key"`
11-
Value string `json:"value"`
10+
Key string `json:"key"`
11+
Value string `json:"value"`
12+
Encrypted bool `json:"encrypted",omitempty`
1213
}
1314

1415
// CodefreshObject codefresh interface

codefresh/internal/datautil/strings.go

+21-5
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,29 @@ func ConvertAndMapStringArr(ifaceArr []interface{}, f func(string) string) []str
2222
return arr
2323
}
2424

25-
// ConvertVariables converts an array of cfclient.Variables to a map of key/value pairs.
26-
func ConvertVariables(vars []cfclient.Variable) map[string]string {
27-
res := make(map[string]string, len(vars))
25+
// ConvertVariables converts an array of cfclient. Variables to 2 maps of key/value pairs - first one for un-encrypted variables second one for encrypted variables.
26+
func ConvertVariables(vars []cfclient.Variable) (map[string]string, map[string]string) {
27+
28+
numberOfEncryptedVars := 0
29+
2830
for _, v := range vars {
29-
res[v.Key] = v.Value
31+
if v.Encrypted {
32+
numberOfEncryptedVars++
33+
}
3034
}
31-
return res
35+
36+
resUnencrptedVars := make(map[string]string, len(vars)-numberOfEncryptedVars)
37+
resEncryptedVars := make(map[string]string, numberOfEncryptedVars)
38+
39+
for _, v := range vars {
40+
if v.Encrypted {
41+
resEncryptedVars[v.Key] = v.Value
42+
} else {
43+
resUnencrptedVars[v.Key] = v.Value
44+
}
45+
}
46+
47+
return resUnencrptedVars, resEncryptedVars
3248
}
3349

3450
// FlattenStringArr flattens an array of strings.

codefresh/resource_pipeline.go

+106-11
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,15 @@ Or: <code>original_yaml_string = file("/path/to/my/codefresh.yml")</code>
151151
Type: schema.TypeString,
152152
},
153153
},
154+
"encrypted_variables": {
155+
Description: "Pipeline level encrypted variables. Please note that drift will not be detected for encrypted variables",
156+
Type: schema.TypeMap,
157+
Optional: true,
158+
Elem: &schema.Schema{
159+
Type: schema.TypeString,
160+
Sensitive: true,
161+
},
162+
},
154163
"trigger": {
155164
Description: "The pipeline's triggers (currently the only nested trigger supported is git; for other trigger types, use the `codefresh_pipeline_*_trigger` resources).",
156165
Type: schema.TypeList,
@@ -336,6 +345,15 @@ Or: <code>original_yaml_string = file("/path/to/my/codefresh.yml")</code>
336345
Type: schema.TypeString,
337346
},
338347
},
348+
"encrypted_variables": {
349+
Description: "Trigger level encrypted variables. Please note that drift will not be detected for encrypted variables",
350+
Type: schema.TypeMap,
351+
Optional: true,
352+
Elem: &schema.Schema{
353+
Type: schema.TypeString,
354+
Sensitive: true,
355+
},
356+
},
339357
},
340358
},
341359
},
@@ -467,6 +485,15 @@ Or: <code>original_yaml_string = file("/path/to/my/codefresh.yml")</code>
467485
Type: schema.TypeString,
468486
},
469487
},
488+
"encrypted_variables": {
489+
Description: "Trigger level encrypted variables. Please note that drift will not be detected for encrypted variables",
490+
Type: schema.TypeMap,
491+
Optional: true,
492+
Elem: &schema.Schema{
493+
Type: schema.TypeString,
494+
Sensitive: true,
495+
},
496+
},
470497
},
471498
},
472499
},
@@ -608,8 +635,8 @@ Pipeline concurrency policy: Builds on 'Pending Approval' state should be:
608635
},
609636
"enable_notifications": {
610637
Type: schema.TypeBool,
611-
Optional: true,
612-
Default: false,
638+
Optional: true,
639+
Default: false,
613640
},
614641
},
615642
},
@@ -717,7 +744,51 @@ func mapPipelineToResource(pipeline cfclient.Pipeline, d *schema.ResourceData) e
717744
return err
718745
}
719746

720-
err = d.Set("spec", flattenSpec(pipeline.Spec))
747+
flattenedSpec := flattenSpec(pipeline.Spec)
748+
749+
// Set encrypted variables from resource data, as otherwise they cause constant diff as the value is always returned as *****
750+
encryptedVariables, ok := flattenedSpec[0]["encrypted_variables"].(map[string]string)
751+
752+
if ok {
753+
if len(encryptedVariables) > 0 {
754+
setEncryptedVariablesValuesFromResource(d, encryptedVariables, "spec.0.encrypted_variables")
755+
}
756+
}
757+
758+
// Set trigger encrypted variables from resource data
759+
triggers, getTriggersOK := flattenedSpec[0]["trigger"]
760+
761+
if getTriggersOK {
762+
for triggerIndex, triggerSpec := range triggers.([]map[string]interface{}) {
763+
764+
triggerEncryptedVariables, ok := triggerSpec["encrypted_variables"].(map[string]string)
765+
766+
if ok {
767+
if len(triggerEncryptedVariables) > 0 {
768+
setEncryptedVariablesValuesFromResource(d, triggerEncryptedVariables, fmt.Sprintf("spec.0.trigger.%d.encrypted_variables", triggerIndex))
769+
}
770+
}
771+
}
772+
}
773+
774+
// Set cron trigger encrypted variables from resource data
775+
cronTriggers, getCronTriggersOK := flattenedSpec[0]["cron_trigger"]
776+
777+
if getCronTriggersOK {
778+
for triggerIndex, triggerSpec := range cronTriggers.([]map[string]interface{}) {
779+
780+
triggerEncryptedVariables, ok := triggerSpec["encrypted_variables"].(map[string]string)
781+
782+
if ok {
783+
if len(triggerEncryptedVariables) > 0 {
784+
setEncryptedVariablesValuesFromResource(d, triggerEncryptedVariables, fmt.Sprintf("spec.0.cron_trigger.%d.encrypted_variables", triggerIndex))
785+
}
786+
}
787+
}
788+
}
789+
790+
err = d.Set("spec", flattenedSpec)
791+
721792
if err != nil {
722793
return err
723794
}
@@ -735,9 +806,9 @@ func mapPipelineToResource(pipeline cfclient.Pipeline, d *schema.ResourceData) e
735806
return nil
736807
}
737808

738-
func flattenSpec(spec cfclient.Spec) []interface{} {
809+
func flattenSpec(spec cfclient.Spec) []map[string]interface{} {
739810

740-
var res = make([]interface{}, 0)
811+
var res = make([]map[string]interface{}, 0)
741812
m := make(map[string]interface{})
742813

743814
if len(spec.Triggers) > 0 {
@@ -753,7 +824,8 @@ func flattenSpec(spec cfclient.Spec) []interface{} {
753824
}
754825

755826
if len(spec.Variables) != 0 {
756-
m["variables"] = datautil.ConvertVariables(spec.Variables)
827+
// Do not set encrypted variables because they cause constant diff
828+
m["variables"], m["encrypted_variables"] = datautil.ConvertVariables(spec.Variables)
757829
}
758830

759831
if spec.RuntimeEnvironment != (cfclient.RuntimeEnvironment{}) {
@@ -884,7 +956,7 @@ func flattenTriggers(triggers []cfclient.Trigger) []map[string]interface{} {
884956
m["provider"] = trigger.Provider
885957
m["type"] = trigger.Type
886958
m["events"] = trigger.Events
887-
m["variables"] = datautil.ConvertVariables(trigger.Variables)
959+
m["variables"], m["encrypted_variables"] = datautil.ConvertVariables(trigger.Variables)
888960
if trigger.RuntimeEnvironment != nil {
889961
m["runtime_environment"] = flattenSpecRuntimeEnvironment(*trigger.RuntimeEnvironment)
890962
}
@@ -904,7 +976,7 @@ func flattenCronTriggers(cronTriggers []cfclient.CronTrigger) []map[string]inter
904976
m["disabled"] = trigger.Disabled
905977
m["git_trigger_id"] = trigger.GitTriggerId
906978
m["branch"] = trigger.Branch
907-
m["variables"] = datautil.ConvertVariables(trigger.Variables)
979+
m["variables"], m["encrypted_variables"] = datautil.ConvertVariables(trigger.Variables)
908980
if trigger.Options != nil {
909981
m["options"] = flattenTriggerOptions(*trigger.Options)
910982
}
@@ -977,7 +1049,11 @@ func mapResourceToPipeline(d *schema.ResourceData) (*cfclient.Pipeline, error) {
9771049
}
9781050

9791051
if variables, ok := d.GetOk("spec.0.variables"); ok {
980-
pipeline.SetVariables(variables.(map[string]interface{}))
1052+
pipeline.SetVariables(variables.(map[string]interface{}), false)
1053+
}
1054+
1055+
if encryptedVariables, ok := d.GetOk("spec.0.encrypted_variables"); ok {
1056+
pipeline.SetVariables(encryptedVariables.(map[string]interface{}), true)
9811057
}
9821058

9831059
if triggers, ok := d.GetOk("spec.0.trigger"); ok {
@@ -1003,7 +1079,11 @@ func mapResourceToPipeline(d *schema.ResourceData) (*cfclient.Pipeline, error) {
10031079
Events: datautil.ConvertStringArr(events),
10041080
}
10051081
variables := d.Get(fmt.Sprintf("spec.0.trigger.%v.variables", idx)).(map[string]interface{})
1006-
codefreshTrigger.SetVariables(variables)
1082+
codefreshTrigger.SetVariables(variables, false)
1083+
1084+
encryptedVariables := d.Get(fmt.Sprintf("spec.0.trigger.%v.encrypted_variables", idx)).(map[string]interface{})
1085+
codefreshTrigger.SetVariables(encryptedVariables, true)
1086+
10071087
if _, ok := d.GetOk(fmt.Sprintf("spec.0.trigger.%v.options", idx)); ok {
10081088
options := cfclient.TriggerOptions{
10091089
NoCache: d.Get(fmt.Sprintf("spec.0.trigger.%v.options.0.no_cache", idx)).(bool),
@@ -1039,7 +1119,10 @@ func mapResourceToPipeline(d *schema.ResourceData) (*cfclient.Pipeline, error) {
10391119
Branch: d.Get(fmt.Sprintf("spec.0.cron_trigger.%v.branch", idx)).(string),
10401120
}
10411121
variables := d.Get(fmt.Sprintf("spec.0.cron_trigger.%v.variables", idx)).(map[string]interface{})
1042-
codefreshCronTrigger.SetVariables(variables)
1122+
codefreshCronTrigger.SetVariables(variables, false)
1123+
encryptedVariables := d.Get(fmt.Sprintf("spec.0.cron_trigger.%v.encrypted_variables", idx)).(map[string]interface{})
1124+
codefreshCronTrigger.SetVariables(encryptedVariables, true)
1125+
10431126
if _, ok := d.GetOk(fmt.Sprintf("spec.0.cron_trigger.%v.options", idx)); ok {
10441127
options := cfclient.TriggerOptions{
10451128
NoCache: d.Get(fmt.Sprintf("spec.0.cron_trigger.%v.options.0.no_cache", idx)).(bool),
@@ -1181,3 +1264,15 @@ func convertOnCreateBranchAttributeToPipelineFormat(src string) string {
11811264
return "_" + strings.ToLower(w)
11821265
})
11831266
}
1267+
1268+
func setEncryptedVariablesValuesFromResource(d *schema.ResourceData, flattenedVariables map[string]string, schemaPath string) error {
1269+
1270+
if len(flattenedVariables) > 0 {
1271+
// Iterate over variables and set the value from resource data
1272+
for k := range flattenedVariables {
1273+
flattenedVariables[k] = d.Get(fmt.Sprintf("%s.%s", schemaPath, k)).(string)
1274+
}
1275+
}
1276+
1277+
return nil
1278+
}

0 commit comments

Comments
 (0)