github.com/chenbh/concourse/v6@v6.4.2/fly/commands/internal/templatehelpers/yaml_template.go (about)

     1  package templatehelpers
     2  
     3  import (
     4  	"fmt"
     5  	"io/ioutil"
     6  
     7  	"github.com/chenbh/concourse/v6/atc"
     8  	"github.com/chenbh/concourse/v6/fly/commands/internal/flaghelpers"
     9  	"github.com/chenbh/concourse/v6/vars"
    10  	"sigs.k8s.io/yaml"
    11  )
    12  
    13  type YamlTemplateWithParams struct {
    14  	filePath               atc.PathFlag
    15  	templateVariablesFiles []atc.PathFlag
    16  	templateVariables      []flaghelpers.VariablePairFlag
    17  	yamlTemplateVariables  []flaghelpers.YAMLVariablePairFlag
    18  }
    19  
    20  func NewYamlTemplateWithParams(filePath atc.PathFlag, templateVariablesFiles []atc.PathFlag, templateVariables []flaghelpers.VariablePairFlag, yamlTemplateVariables []flaghelpers.YAMLVariablePairFlag) YamlTemplateWithParams {
    21  	return YamlTemplateWithParams{
    22  		filePath:               filePath,
    23  		templateVariablesFiles: templateVariablesFiles,
    24  		templateVariables:      templateVariables,
    25  		yamlTemplateVariables:  yamlTemplateVariables,
    26  	}
    27  }
    28  
    29  func (yamlTemplate YamlTemplateWithParams) Evaluate(
    30  	allowEmpty bool,
    31  	strict bool,
    32  ) ([]byte, error) {
    33  	config, err := ioutil.ReadFile(string(yamlTemplate.filePath))
    34  	if err != nil {
    35  		return nil, fmt.Errorf("could not read file: %s", err.Error())
    36  	}
    37  
    38  	if strict {
    39  		// We use a generic map here, since templates are not evaluated yet.
    40  		// (else a template string may cause an error when a struct is expected)
    41  		// If we don't check Strict now, then the subsequent steps will mask any
    42  		// duplicate key errors.
    43  		// We should consider being strict throughout the entire stack by default.
    44  		err = yaml.UnmarshalStrict(config, make(map[string]interface{}))
    45  		if err != nil {
    46  			return nil, fmt.Errorf("error parsing yaml before applying templates: %s", err.Error())
    47  		}
    48  	}
    49  
    50  	var params []vars.Variables
    51  
    52  	// first, we take explicitly specified variables on the command line
    53  	flagVars := vars.StaticVariables{}
    54  	for _, f := range yamlTemplate.templateVariables {
    55  		flagVars[f.Name] = f.Value
    56  	}
    57  	for _, f := range yamlTemplate.yamlTemplateVariables {
    58  		flagVars[f.Name] = f.Value
    59  	}
    60  	params = append(params, flagVars)
    61  
    62  	// second, we take all files. with values in the files specified later on command line taking precedence over the
    63  	// same values in the files specified earlier on command line
    64  	for i := len(yamlTemplate.templateVariablesFiles) - 1; i >= 0; i-- {
    65  		path := yamlTemplate.templateVariablesFiles[i]
    66  		templateVars, err := ioutil.ReadFile(string(path))
    67  		if err != nil {
    68  			return nil, fmt.Errorf("could not read template variables file (%s): %s", string(path), err.Error())
    69  		}
    70  
    71  		var staticVars vars.StaticVariables
    72  		err = yaml.Unmarshal(templateVars, &staticVars)
    73  		if err != nil {
    74  			return nil, fmt.Errorf("could not unmarshal template variables (%s): %s", string(path), err.Error())
    75  		}
    76  
    77  		params = append(params, staticVars)
    78  	}
    79  
    80  	evaluatedConfig, err := vars.NewTemplateResolver(config, params).Resolve(false, allowEmpty)
    81  	if err != nil {
    82  		return nil, err
    83  	}
    84  
    85  	return evaluatedConfig, nil
    86  }