in internal/tfengine/tfengine.go [157:251]
func dumpTemplate(conf *Config, pwd, cacheDir, outputPath string, ti *templateInfo) error {
outputPath = filepath.Join(outputPath, ti.OutputPath)
data, err := template.CopyData(conf.Data)
if err != nil {
return err
}
// Make the schema available.
trimDescriptions(conf.Schema)
data["__schema__"] = conf.Schema
flattenedData, err := template.FlattenData(data, ti.Flatten)
if err != nil {
return err
}
// Merge flattened data into template data so that it gets checked by the schema check later.
if err := template.MergeData(ti.Data, flattenedData); err != nil {
return err
}
if err := template.MergeData(data, ti.Data); err != nil {
return err
}
// Pass through keys that should be validated against the schema.
// Don't do error checking if keys are missing, instead let schemas
// in the child templates do the validation.
for _, k := range ti.Passthrough {
if v, ok := conf.Data[k]; ok {
ti.Data[k] = v
}
}
switch {
case ti.RecipePath != "":
rp, err := fileutil.Fetch(ti.RecipePath, pwd, cacheDir)
if err != nil {
return err
}
rc, err := loadConfig(rp, data)
if err != nil {
return fmt.Errorf("load recipe %q: %v", rp, err)
}
compat, err := version.IsCompatible(rc.Version)
if err != nil {
return err
}
if !compat {
return fmt.Errorf("binary version %v incompatible with template version constraint %v in %v", cmd.Version, rc.Version, rp)
}
// Validate the schema, if present.
if len(rc.Schema) > 0 {
// Only check against unmerged template data so we can disallow additional properties in the schema.
if err := jsonschema.ValidateMap(rc.Schema, ti.Data); err != nil {
return fmt.Errorf("recipe %q: %v", rp, err)
}
}
// Each recipe could have a top-level data block. Keep it and merge, instead of overrwriting.
if err := template.MergeData(rc.Data, data); err != nil {
return err
}
if err := dump(rc, filepath.Dir(rp), cacheDir, outputPath, nil); err != nil {
return fmt.Errorf("recipe %q: %v", rp, err)
}
case ti.ComponentPath != "":
// Fetch the component, which could be remote.
cp, err := fileutil.Fetch(ti.ComponentPath, pwd, cacheDir)
if err != nil {
return err
}
write := template.WriteDir
// If the component path is a single file,
// treat the output_path as a file name and only write that out.
// Otherwise, treat as directory path
info, err := os.Stat(cp)
if err != nil {
return fmt.Errorf("stat %q: %v", cp, err)
}
if info.Mode().IsRegular() {
write = template.WriteFile
}
if err := write(cp, outputPath, data); err != nil {
return fmt.Errorf("component %q: %v", cp, err)
}
}
return nil
}