Skip to content

Commit 0a2765d

Browse files
author
Chanwit Kaewkasi
authored
Merge pull request #504 from weaveworks/add-output-typeinfo
enhance outputs with type information
2 parents b1a225e + 1583733 commit 0a2765d

12 files changed

+970
-590
lines changed

controllers/tc000061_vars_hcl_input_test.go

+15-9
Original file line numberDiff line numberDiff line change
@@ -137,28 +137,34 @@ func Test_000061_vars_hcl_input_test(t *testing.T) {
137137
return -1, err
138138
}
139139
return len(outputSecret.Data), nil
140-
}, timeout, interval).Should(Equal(4))
140+
}, timeout, interval).Should(Equal(7))
141141

142142
By("checking that the TF output secrets contains the correct output provisioned by the TF hello world")
143143
// Value is a JSON representation of TF's OutputMeta
144144
expectedOutputValue := map[string]interface{}{
145145
"Name": "tf-output-" + terraformName,
146146
"Namespace": "flux-system",
147147
"Values": map[string]string{
148-
"cluster_id": "eu-test-1:stg:winter-squirrel",
149-
"active": "true",
150-
"node_count": "10",
151-
"azs": `["eu-test-1a","eu-test-1b","eu-test-1c"]`,
148+
"cluster_id": "eu-test-1:stg:winter-squirrel",
149+
"active": "true",
150+
"active.type": `"bool"`,
151+
"node_count": "10",
152+
"node_count.type": `"number"`,
153+
"azs": "[\n \"eu-test-1a\",\n \"eu-test-1b\",\n \"eu-test-1c\"\n ]",
154+
"azs.type": "[\n \"list\",\n \"string\"\n ]",
152155
},
153156
"OwnerRef[0]": string(createdHelloWorldTF.UID),
154157
}
155158
g.Eventually(func() (map[string]interface{}, error) {
156159
err := k8sClient.Get(ctx, outputKey, &outputSecret)
157160
values := map[string]string{
158-
"cluster_id": string(outputSecret.Data["cluster_id"]),
159-
"active": string(outputSecret.Data["active"]),
160-
"node_count": string(outputSecret.Data["node_count"]),
161-
"azs": string(outputSecret.Data["azs"]),
161+
"cluster_id": string(outputSecret.Data["cluster_id"]),
162+
"active": string(outputSecret.Data["active"]),
163+
"active.type": string(outputSecret.Data["active.type"]),
164+
"node_count": string(outputSecret.Data["node_count"]),
165+
"node_count.type": string(outputSecret.Data["node_count.type"]),
166+
"azs": string(outputSecret.Data["azs"]),
167+
"azs.type": string(outputSecret.Data["azs.type"]),
162168
}
163169
return map[string]interface{}{
164170
"Name": outputSecret.Name,

controllers/tf_controller_outputs.go

+65-69
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package controllers
22

33
import (
44
"context"
5-
"encoding/json"
65
"fmt"
76
"sort"
87
"strings"
@@ -75,6 +74,9 @@ func (r *TerraformReconciler) processOutputs(ctx context.Context, runnerClient r
7574
log := ctrl.LoggerFrom(ctx)
7675
objectKey := types.NamespacedName{Namespace: terraform.Namespace, Name: terraform.Name}
7776

77+
// OutputMeta has
78+
// 1. type
79+
// 2. value
7880
outputs := map[string]tfexec.OutputMeta{}
7981
var err error
8082
terraform, err = r.obtainOutputs(ctx, terraform, tfInstance, runnerClient, revision, &outputs)
@@ -133,81 +135,37 @@ func (r *TerraformReconciler) writeOutput(ctx context.Context, terraform infrav1
133135
wots := terraform.Spec.WriteOutputsToSecret
134136
data := map[string][]byte{}
135137

136-
// if not specified .spec.writeOutputsToSecret.outputs,
137-
// then it means export all outputs
138+
var filteredOutputs map[string]tfexec.OutputMeta
138139
if len(wots.Outputs) == 0 {
139-
for output, v := range outputs {
140-
ct, err := ctyjson.UnmarshalType(v.Type)
141-
if err != nil {
142-
return terraform, err
143-
}
144-
// if it's a string, we can embed it directly into Secret's data
145-
switch ct {
146-
case cty.String:
147-
cv, err := ctyjson.Unmarshal(v.Value, ct)
148-
if err != nil {
149-
return terraform, err
150-
}
151-
data[output] = []byte(cv.AsString())
152-
// there's no need to unmarshal and convert to []byte
153-
// we'll just pass the []byte directly from OutputMeta Value
154-
case cty.Number, cty.Bool:
155-
data[output] = v.Value
156-
default:
157-
outputBytes, err := json.Marshal(v.Value)
158-
if err != nil {
159-
return terraform, err
160-
}
161-
data[output] = outputBytes
162-
}
163-
}
140+
filteredOutputs = outputs
164141
} else {
165-
// filter only defined output
166-
// output maybe contain mapping output:mapped_name
167-
for _, outputMapping := range wots.Outputs {
168-
parts := strings.SplitN(outputMapping, ":", 2)
169-
var output string
170-
var mappedTo string
171-
if len(parts) == 1 {
172-
output = parts[0]
173-
mappedTo = parts[0]
174-
// no mapping
175-
} else if len(parts) == 2 {
176-
output = parts[0]
177-
mappedTo = parts[1]
178-
} else {
179-
log.Error(fmt.Errorf("invalid mapping format"), outputMapping)
180-
continue
181-
}
142+
if result, err := filterOutputs(outputs, wots.Outputs); err != nil {
143+
return infrav1.TerraformNotReady(
144+
terraform,
145+
revision,
146+
infrav1.OutputsWritingFailedReason,
147+
err.Error(),
148+
), err
149+
} else {
150+
filteredOutputs = result
151+
}
152+
}
182153

183-
v, exist := outputs[output]
184-
if !exist {
185-
log.Error(fmt.Errorf("output not found"), output)
186-
continue
187-
}
154+
for outputOrAlias, outputMeta := range filteredOutputs {
155+
ct, err := ctyjson.UnmarshalType(outputMeta.Type)
156+
if err != nil {
157+
return terraform, err
158+
}
188159

189-
ct, err := ctyjson.UnmarshalType(v.Type)
160+
if ct == cty.String {
161+
cv, err := ctyjson.Unmarshal(outputMeta.Value, ct)
190162
if err != nil {
191163
return terraform, err
192164
}
193-
switch ct {
194-
case cty.String:
195-
cv, err := ctyjson.Unmarshal(v.Value, ct)
196-
if err != nil {
197-
return terraform, err
198-
}
199-
data[mappedTo] = []byte(cv.AsString())
200-
// there's no need to unmarshal and convert to []byte
201-
// we'll just pass the []byte directly from OutputMeta Value
202-
case cty.Number, cty.Bool:
203-
data[mappedTo] = v.Value
204-
default:
205-
outputBytes, err := json.Marshal(v.Value)
206-
if err != nil {
207-
return terraform, err
208-
}
209-
data[mappedTo] = outputBytes
210-
}
165+
data[outputOrAlias] = []byte(cv.AsString())
166+
} else {
167+
data[outputOrAlias] = outputMeta.Value
168+
data[outputOrAlias+".type"] = outputMeta.Type
211169
}
212170
}
213171

@@ -243,3 +201,41 @@ func (r *TerraformReconciler) writeOutput(ctx context.Context, terraform infrav1
243201

244202
return infrav1.TerraformOutputsWritten(terraform, revision, "Outputs written"), nil
245203
}
204+
205+
func filterOutputs(outputs map[string]tfexec.OutputMeta, outputsToWrite []string) (map[string]tfexec.OutputMeta, error) {
206+
if outputs == nil || outputsToWrite == nil {
207+
return nil, fmt.Errorf("input maps or outputsToWrite slice cannot be nil")
208+
}
209+
210+
filteredOutputs := make(map[string]tfexec.OutputMeta)
211+
for _, outputMapping := range outputsToWrite {
212+
if len(outputMapping) == 0 {
213+
return nil, fmt.Errorf("output mapping cannot be empty")
214+
}
215+
216+
// parse output mapping (output[:alias])
217+
parts := strings.SplitN(outputMapping, ":", 2)
218+
var (
219+
output string
220+
alias string
221+
)
222+
if len(parts) == 1 {
223+
output = parts[0]
224+
alias = parts[0]
225+
} else if len(parts) == 2 {
226+
output = parts[0]
227+
alias = parts[1]
228+
} else {
229+
return nil, fmt.Errorf("invalid output mapping format: %s", outputMapping)
230+
}
231+
232+
outputMeta, exist := outputs[output]
233+
if !exist {
234+
return nil, fmt.Errorf("output not found: %s", output)
235+
}
236+
237+
filteredOutputs[alias] = outputMeta
238+
}
239+
240+
return filteredOutputs, nil
241+
}

docs/References/terraform.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -1485,7 +1485,7 @@ int32
14851485
</td>
14861486
<td>
14871487
<em>(Optional)</em>
1488-
<p>Parallelism limits the number of concurrent operations of Terraform apply step.</p>
1488+
<p>Parallelism limits the number of concurrent operations of Terraform apply step. Zero (0) means using the default value.</p>
14891489
</td>
14901490
</tr>
14911491
<tr>
@@ -1969,7 +1969,7 @@ int32
19691969
</td>
19701970
<td>
19711971
<em>(Optional)</em>
1972-
<p>Parallelism limits the number of concurrent operations of Terraform apply step.</p>
1972+
<p>Parallelism limits the number of concurrent operations of Terraform apply step. Zero (0) means using the default value.</p>
19731973
</td>
19741974
</tr>
19751975
<tr>

go.mod

+6-1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ require (
2323
github.com/google/uuid v1.3.0
2424
github.com/hashicorp/go-cleanhttp v0.5.2
2525
github.com/hashicorp/go-retryablehttp v0.7.1
26+
github.com/hashicorp/hcl2 v0.0.0-20191002203319-fb75b3253c80
2627
github.com/hashicorp/terraform-exec v0.16.1
2728
github.com/hashicorp/terraform-json v0.13.0
2829
github.com/onsi/gomega v1.24.0
@@ -60,6 +61,9 @@ require (
6061
github.com/Microsoft/go-winio v0.5.2 // indirect
6162
github.com/PuerkitoBio/purell v1.1.1 // indirect
6263
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
64+
github.com/agext/levenshtein v1.2.1 // indirect
65+
github.com/apparentlymart/go-textseg v1.0.0 // indirect
66+
github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect
6367
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.4 // indirect
6468
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.12 // indirect
6569
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.18 // indirect
@@ -116,7 +120,7 @@ require (
116120
github.com/json-iterator/go v1.1.12 // indirect
117121
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect
118122
github.com/magiconair/properties v1.8.6 // indirect
119-
github.com/mailru/easyjson v0.7.6 // indirect
123+
github.com/mailru/easyjson v0.7.7 // indirect
120124
github.com/mattn/go-colorable v0.1.12 // indirect
121125
github.com/mattn/go-isatty v0.0.14 // indirect
122126
github.com/mattn/go-runewidth v0.0.13 // indirect
@@ -149,6 +153,7 @@ require (
149153
github.com/spf13/afero v1.8.2 // indirect
150154
github.com/spf13/cast v1.5.0 // indirect
151155
github.com/spf13/jwalterweatherman v1.1.0 // indirect
156+
github.com/stretchr/testify v1.8.1 // indirect
152157
github.com/subosito/gotenv v1.4.1 // indirect
153158
github.com/theckman/yacspin v0.13.12 // indirect
154159
github.com/xlab/treeprint v1.1.0 // indirect

0 commit comments

Comments
 (0)