@ -120,6 +120,7 @@ var (
amazonSecretsManagerMap = map [ string ] map [ string ] interface { } { }
localsVariableMap = map [ string ] string { }
timestamp = false
isotime = false
)
type BlockParser interface {
@ -269,18 +270,18 @@ func (uc UnhandleableArgumentError) Error() string {
# Visit % s for more infos . ` , uc . Call , uc . Correspondance , uc . Docs )
}
func fallbackReturn ( err error , s [ ] byte ) [ ] byte {
if strings . Contains ( err . Error ( ) , "unhandled" ) {
return append ( [ ] byte ( fmt . Sprintf ( "\n# %s\n" , err ) ) , s ... )
}
return append ( [ ] byte ( fmt . Sprintf ( "\n# could not parse template for following block: %q\n" , err ) ) , s ... )
}
// transposeTemplatingCalls executes parts of blocks as go template files and replaces
// their result with their hcl2 variant. If something goes wrong the template
// containing the go template string is returned.
func transposeTemplatingCalls ( s [ ] byte ) [ ] byte {
fallbackReturn := func ( err error ) [ ] byte {
if strings . Contains ( err . Error ( ) , "unhandled" ) {
return append ( [ ] byte ( fmt . Sprintf ( "\n# %s\n" , err ) ) , s ... )
}
return append ( [ ] byte ( fmt . Sprintf ( "\n# could not parse template for following block: %q\n" , err ) ) , s ... )
}
funcErrors := & multierror . Error {
ErrorFormat : func ( es [ ] error ) string {
if len ( es ) == 1 {
@ -336,8 +337,14 @@ func transposeTemplatingCalls(s []byte) []byte {
return "${local.timestamp}"
} ,
"isotime" : func ( a ... string ) string {
timestamp = true
return "${local.timestamp}"
if len ( a ) == 0 {
// returns rfc3339 formatted string.
return "${timestamp()}"
}
// otherwise a valid isotime func has one input.
isotime = true
return fmt . Sprintf ( "${legacy_isotime(\"%s\")}" , a [ 0 ] )
} ,
"user" : func ( in string ) string {
if _ , ok := localsVariableMap [ in ] ; ok {
@ -430,14 +437,14 @@ func transposeTemplatingCalls(s []byte) []byte {
Parse ( string ( s ) )
if err != nil {
return fallbackReturn ( err )
return fallbackReturn ( err , s )
}
str := & bytes . Buffer { }
// PASSTHROUGHS is a map of variable-specific golang text template fields
// that should remain in the text template format.
if err := tpl . Execute ( str , PASSTHROUGHS ) ; err != nil {
return fallbackReturn ( err )
return fallbackReturn ( err , s )
}
out := str . Bytes ( )
@ -454,14 +461,6 @@ func transposeTemplatingCalls(s []byte) []byte {
// In variableTransposeTemplatingCalls the definition of aws_secretsmanager function will create a data source
// with the same name as the variable.
func variableTransposeTemplatingCalls ( s [ ] byte ) ( isLocal bool , body [ ] byte ) {
fallbackReturn := func ( err error ) [ ] byte {
if strings . Contains ( err . Error ( ) , "unhandled" ) {
return append ( [ ] byte ( fmt . Sprintf ( "\n# %s\n" , err ) ) , s ... )
}
return append ( [ ] byte ( fmt . Sprintf ( "\n# could not parse template for following block: %q\n" , err ) ) , s ... )
}
setIsLocal := func ( a ... string ) string {
isLocal = true
return ""
@ -503,14 +502,14 @@ func variableTransposeTemplatingCalls(s []byte) (isLocal bool, body []byte) {
Parse ( string ( s ) )
if err != nil {
return isLocal , fallbackReturn ( err )
return isLocal , fallbackReturn ( err , s )
}
str := & bytes . Buffer { }
// PASSTHROUGHS is a map of variable-specific golang text template fields
// that should remain in the text template format.
if err := tpl . Execute ( str , PASSTHROUGHS ) ; err != nil {
return isLocal , fallbackReturn ( err )
return isLocal , fallbackReturn ( err , s )
}
return isLocal , str . Bytes ( )
@ -675,12 +674,49 @@ type VariableParser struct {
localsOut [ ] byte
}
func makeLocal ( variable * template . Variable , sensitive bool , localBody * hclwrite . Body , localsContent * hclwrite . File , hasLocals * bool ) [ ] byte {
if sensitive {
// Create Local block because this is sensitive
sensitiveLocalContent := hclwrite . NewEmptyFile ( )
body := sensitiveLocalContent . Body ( )
body . AppendNewline ( )
sensitiveLocalBody := body . AppendNewBlock ( "local" , [ ] string { variable . Key } ) . Body ( )
sensitiveLocalBody . SetAttributeValue ( "sensitive" , cty . BoolVal ( true ) )
sensitiveLocalBody . SetAttributeValue ( "expression" , hcl2shim . HCL2ValueFromConfigValue ( variable . Default ) )
localsVariableMap [ variable . Key ] = "local"
return sensitiveLocalContent . Bytes ( )
}
localBody . SetAttributeValue ( variable . Key , hcl2shim . HCL2ValueFromConfigValue ( variable . Default ) )
localsVariableMap [ variable . Key ] = "locals"
* hasLocals = true
return [ ] byte { }
}
func makeVariable ( variable * template . Variable , sensitive bool ) [ ] byte {
variablesContent := hclwrite . NewEmptyFile ( )
variablesBody := variablesContent . Body ( )
variablesBody . AppendNewline ( )
variableBody := variablesBody . AppendNewBlock ( "variable" , [ ] string { variable . Key } ) . Body ( )
variableBody . SetAttributeRaw ( "type" , hclwrite . Tokens { & hclwrite . Token { Bytes : [ ] byte ( "string" ) } } )
if variable . Default != "" || ! variable . Required {
shimmed := hcl2shim . HCL2ValueFromConfigValue ( variable . Default )
variableBody . SetAttributeValue ( "default" , shimmed )
}
if sensitive {
variableBody . SetAttributeValue ( "sensitive" , cty . BoolVal ( true ) )
}
return variablesContent . Bytes ( )
}
func ( p * VariableParser ) Parse ( tpl * template . Template ) error {
// OutPut Locals and Local blocks
// Out p ut Locals and Local blocks
localsContent := hclwrite . NewEmptyFile ( )
localsBody := localsContent . Body ( )
localsBody . AppendNewline ( )
localBody := localsBody . AppendNewBlock ( "locals" , nil ) . Body ( )
hasLocals := false
if len ( p . variablesOut ) == 0 {
p . variablesOut = [ ] byte { }
@ -700,47 +736,34 @@ func (p *VariableParser) Parse(tpl *template.Template) error {
} )
}
hasLocals := false
for _ , variable := range variables {
variablesContent := hclwrite . NewEmptyFile ( )
variablesBody := variablesContent . Body ( )
variablesBody . AppendNewline ( )
variableBody := variablesBody . AppendNewBlock ( "variable" , [ ] string { variable . Key } ) . Body ( )
variableBody . SetAttributeRaw ( "type" , hclwrite . Tokens { & hclwrite . Token { Bytes : [ ] byte ( "string" ) } } )
if variable . Default != "" || ! variable . Required {
variableBody . SetAttributeValue ( "default" , hcl2shim . HCL2ValueFromConfigValue ( variable . Default ) )
}
// Create new HCL2 "variables" block, and populate the "value"
// field with the "Default" value from the JSON variable.
// Interpolate Jsonval first as an hcl variable to determine if it is
// a local.
isLocal , _ := variableTransposeTemplatingCalls ( [ ] byte ( variable . Default ) )
sensitive := false
if isSensitiveVariable ( variable . Key , tpl . SensitiveVariables ) {
sensitive = true
variableBody . SetAttributeValue ( "sensitive" , cty . BoolVal ( true ) )
}
isLocal , out := variableTransposeTemplatingCalls ( variablesContent . Bytes ( ) )
// Create final HCL block and append.
if isLocal {
if sensitive {
// Create Local block because this is sensitive
localContent := hclwrite . NewEmptyFile ( )
body := localContent . Body ( )
body . AppendNewline ( )
localBody := body . AppendNewBlock ( "local" , [ ] string { variable . Key } ) . Body ( )
localBody . SetAttributeValue ( "sensitive" , cty . BoolVal ( true ) )
localBody . SetAttributeValue ( "expression" , hcl2shim . HCL2ValueFromConfigValue ( variable . Default ) )
p . localsOut = append ( p . localsOut , transposeTemplatingCalls ( localContent . Bytes ( ) ) ... )
localsVariableMap [ variable . Key ] = "local"
continue
sensitiveBlocks := makeLocal ( variable , sensitive , localBody , localsContent , & hasLocals )
if len ( sensitiveBlocks ) > 0 {
p . localsOut = append ( p . localsOut , transposeTemplatingCalls ( sensitiveBlocks ) ... )
}
localBody . SetAttributeValue ( variable . Key , hcl2shim . HCL2ValueFromConfigValue ( variable . Default ) )
localsVariableMap [ variable . Key ] = "locals"
hasLocals = true
continue
}
varbytes := makeVariable ( variable , sensitive )
_ , out := variableTransposeTemplatingCalls ( varbytes )
p . variablesOut = append ( p . variablesOut , out ... )
}
if hasLocals {
if hasLocals == true {
p . localsOut = append ( p . localsOut , transposeTemplatingCalls ( localsContent . Bytes ( ) ) ... )
}
return nil
}
@ -771,6 +794,9 @@ func (p *LocalsParser) Write(out *bytes.Buffer) {
}
fmt . Fprintln ( out , ` locals { timestamp = regex_replace(timestamp(), "[- TZ:]", "") } ` )
}
if isotime {
fmt . Fprintln ( out , ` # The "legacy_isotime" function has been provided for backwards compatability, but we recommend switching to the timestamp and formatdate functions. ` )
}
if len ( p . LocalsOut ) > 0 {
if p . WithAnnotations {
out . Write ( [ ] byte ( localsVarHeader ) )