@@ -2,7 +2,6 @@ package controllers
22
33import (
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+ }
0 commit comments