From 76e3d57caee0891006b752d749c58cc884a28ebf Mon Sep 17 00:00:00 2001 From: sylviamoss Date: Fri, 12 Feb 2021 15:18:53 +0100 Subject: [PATCH 1/3] add with-annotation flag to hcl2_upgrade --- command/cli.go | 4 +- command/hcl2_upgrade.go | 788 ++++++++++++------ command/hcl2_upgrade_test.go | 17 +- .../hcl2_upgrade/complete/expected.pkr.hcl | 10 + .../expected.pkr.hcl | 2 + .../without-annotations/expected.pkr.hcl | 229 +++++ .../without-annotations/input.json | 235 ++++++ 7 files changed, 1002 insertions(+), 283 deletions(-) create mode 100644 command/test-fixtures/hcl2_upgrade/without-annotations/expected.pkr.hcl create mode 100644 command/test-fixtures/hcl2_upgrade/without-annotations/input.json diff --git a/command/cli.go b/command/cli.go index 9f53cd7bf..10f7242a4 100644 --- a/command/cli.go +++ b/command/cli.go @@ -145,6 +145,7 @@ type InspectArgs struct { func (va *HCL2UpgradeArgs) AddFlagSets(flags *flag.FlagSet) { flags.StringVar(&va.OutputFile, "output-file", "", "File where to put the hcl2 generated config. Defaults to JSON_TEMPLATE.pkr.hcl") + flags.BoolVar(&va.WithAnnotations, "with-annotations", false, "Add to the generated file helper annotations about the generated HCL2 blocks.") va.MetaArgs.AddFlagSets(flags) } @@ -152,7 +153,8 @@ func (va *HCL2UpgradeArgs) AddFlagSets(flags *flag.FlagSet) { // HCL2UpgradeArgs represents a parsed cli line for a `packer hcl2_upgrade` type HCL2UpgradeArgs struct { MetaArgs - OutputFile string + OutputFile string + WithAnnotations bool } func (va *FormatArgs) AddFlagSets(flags *flag.FlagSet) { diff --git a/command/hcl2_upgrade.go b/command/hcl2_upgrade.go index 222d82053..9cb0e0c00 100644 --- a/command/hcl2_upgrade.go +++ b/command/hcl2_upgrade.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "fmt" + "github.com/hashicorp/packer/packer" "io" "os" "path/filepath" @@ -94,7 +95,6 @@ const ( # a build block invokes sources and runs provisioning steps on them. The # documentation for build blocks can be found here: # https://www.packer.io/docs/templates/hcl_templates/blocks/build -build { ` amazonAmiDataHeader = ` @@ -120,6 +120,11 @@ var ( timestamp = false ) +type BlockParser interface { + Parse(*template.Template) error + Write(*bytes.Buffer) +} + func (c *HCL2UpgradeCommand) RunContext(_ context.Context, cla *HCL2UpgradeArgs) int { var output io.Writer if err := os.MkdirAll(filepath.Dir(cla.OutputFile), 0); err != nil { @@ -134,9 +139,11 @@ func (c *HCL2UpgradeCommand) RunContext(_ context.Context, cla *HCL2UpgradeArgs) return 1 } - if _, err := output.Write([]byte(hcl2UpgradeFileHeader)); err != nil { - c.Ui.Error(fmt.Sprintf("Failed to write to file: %v", err)) - return 1 + if cla.WithAnnotations { + if _, err := output.Write([]byte(hcl2UpgradeFileHeader)); err != nil { + c.Ui.Error(fmt.Sprintf("Failed to write to file: %v", err)) + return 1 + } } hdl, ret := c.GetConfigFromJSON(&cla.MetaArgs) @@ -150,67 +157,31 @@ func (c *HCL2UpgradeCommand) RunContext(_ context.Context, cla *HCL2UpgradeArgs) } tpl := core.Template - // OutPut Locals and Local blocks - localsContent := hclwrite.NewEmptyFile() - localsBody := localsContent.Body() - localsBody.AppendNewline() - localBody := localsBody.AppendNewBlock("locals", nil).Body() + // Parse blocks - localsOut := []byte{} - - // Output variables section - variablesOut := []byte{} - variables := []*template.Variable{} - { - // sort variables to avoid map's randomness - for _, variable := range tpl.Variables { - variables = append(variables, variable) - } - sort.Slice(variables, func(i, j int) bool { - return variables[i].Key < variables[j].Key - }) + packerBlock := &PackerParser{ + withAnnotations: cla.WithAnnotations, + } + if err := packerBlock.Parse(tpl); err != nil { + c.Ui.Error(err.Error()) + return 1 } - 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)) - } - sensitive := false - if isSensitiveVariable(variable.Key, tpl.SensitiveVariables) { - sensitive = true - variableBody.SetAttributeValue("sensitive", cty.BoolVal(true)) - } - isLocal, out := variableTransposeTemplatingCalls(variablesContent.Bytes()) - 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)) - localsOut = append(localsOut, transposeTemplatingCalls(localContent.Bytes())...) - localsVariableMap[variable.Key] = "local" - continue - } - localBody.SetAttributeValue(variable.Key, hcl2shim.HCL2ValueFromConfigValue(variable.Default)) - localsVariableMap[variable.Key] = "locals" - hasLocals = true - continue - } - variablesOut = append(variablesOut, out...) + variables := &VariableParser{ + withAnnotations: cla.WithAnnotations, + } + if err := variables.Parse(tpl); err != nil { + c.Ui.Error(err.Error()) + return 1 } - if hasLocals { - localsOut = append(localsOut, transposeTemplatingCalls(localsContent.Bytes())...) + locals := &LocalsParser{ + LocalsOut: variables.localsOut, + withAnnotations: cla.WithAnnotations, + } + if err := locals.Parse(tpl); err != nil { + c.Ui.Error(err.Error()) + return 1 } builders := []*template.Builder{} @@ -221,9 +192,12 @@ func (c *HCL2UpgradeCommand) RunContext(_ context.Context, cla *HCL2UpgradeArgs) } } - // Output amazon-ami data source section - amazonAmiOut, err := c.writeAmazonAmiDatasource(builders) - if err != nil { + amazonAmiDatasource := &AmazonAmiDatasourceParser{ + builders: builders, + withAnnotations: cla.WithAnnotations, + } + if err := amazonAmiDatasource.Parse(tpl); err != nil { + c.Ui.Error(err.Error()) return 1 } @@ -231,238 +205,58 @@ func (c *HCL2UpgradeCommand) RunContext(_ context.Context, cla *HCL2UpgradeArgs) return builders[i].Type+builders[i].Name < builders[j].Type+builders[j].Name }) - // Output sources section - sourcesOut := []byte{} - for i, builderCfg := range builders { - sourcesContent := hclwrite.NewEmptyFile() - body := sourcesContent.Body() - - body.AppendNewline() - if !c.Meta.CoreConfig.Components.PluginConfig.Builders.Has(builderCfg.Type) { - c.Ui.Error(fmt.Sprintf("unknown builder type: %q\n", builderCfg.Type)) - return 1 - } - if builderCfg.Name == "" || builderCfg.Name == builderCfg.Type { - builderCfg.Name = fmt.Sprintf("autogenerated_%d", i+1) - } - builderCfg.Name = strings.ReplaceAll(strings.TrimSpace(builderCfg.Name), " ", "_") - - sourceBody := body.AppendNewBlock("source", []string{builderCfg.Type, builderCfg.Name}).Body() - - jsonBodyToHCL2Body(sourceBody, builderCfg.Config) - - sourcesOut = append(sourcesOut, transposeTemplatingCalls(sourcesContent.Bytes())...) + sources := &SourceParser{ + Builders: builders, + BuilderPlugins: c.Meta.CoreConfig.Components.PluginConfig.Builders, + withAnnotations: cla.WithAnnotations, + } + if err := sources.Parse(tpl); err != nil { + c.Ui.Error(err.Error()) + return 1 } - // Output build section - buildContent := hclwrite.NewEmptyFile() - buildBody := buildContent.Body() - if tpl.Description != "" { - buildBody.SetAttributeValue("description", cty.StringVal(tpl.Description)) - buildBody.AppendNewline() + build := &BuildParser{ + Builders: builders, + withAnnotations: cla.WithAnnotations, + } + if err := build.Parse(tpl); err != nil { + c.Ui.Error(err.Error()) + return 1 } - sourceNames := []string{} - for _, builder := range builders { - sourceNames = append(sourceNames, fmt.Sprintf("source.%s.%s", builder.Type, builder.Name)) + amazonSecretsDatasource := &AmazonSecretsDatasourceParser{ + withAnnotations: cla.WithAnnotations, } - buildBody.SetAttributeValue("sources", hcl2shim.HCL2ValueFromConfigValue(sourceNames)) - buildBody.AppendNewline() - buildOut := buildContent.Bytes() - - // Output provisioners section - provisionersOut := []byte{} - for _, provisioner := range tpl.Provisioners { - buildBody.AppendNewline() - contentBytes := c.writeProvisioner("provisioner", provisioner) - provisionersOut = append(provisionersOut, transposeTemplatingCalls(contentBytes)...) - } - - if tpl.CleanupProvisioner != nil { - buildBody.AppendNewline() - contentBytes := c.writeProvisioner("error-cleanup-provisioner", tpl.CleanupProvisioner) - provisionersOut = append(provisionersOut, transposeTemplatingCalls(contentBytes)...) - } - - // Output post-processors section - postProcessorsOut := []byte{} - for _, pps := range tpl.PostProcessors { - postProcessorContent := hclwrite.NewEmptyFile() - body := postProcessorContent.Body() - - switch len(pps) { - case 0: - continue - case 1: - default: - body = body.AppendNewBlock("post-processors", nil).Body() - } - for _, pp := range pps { - ppBody := body.AppendNewBlock("post-processor", []string{pp.Type}).Body() - if pp.KeepInputArtifact != nil { - ppBody.SetAttributeValue("keep_input_artifact", cty.BoolVal(*pp.KeepInputArtifact)) - } - cfg := pp.Config - if len(pp.Except) > 0 { - cfg["except"] = pp.Except - } - if len(pp.Only) > 0 { - cfg["only"] = pp.Only - } - if pp.Name != "" && pp.Name != pp.Type { - cfg["name"] = pp.Name - } - jsonBodyToHCL2Body(ppBody, cfg) - } - - postProcessorsOut = append(postProcessorsOut, transposeTemplatingCalls(postProcessorContent.Bytes())...) - } - - // Output amazon-secretsmanager data source section - keys := make([]string, 0, len(amazonSecretsManagerMap)) - for k := range amazonSecretsManagerMap { - keys = append(keys, k) - } - sort.Strings(keys) - - amazonSecretsDataOut := []byte{} - for _, dataSourceName := range keys { - datasourceContent := hclwrite.NewEmptyFile() - body := datasourceContent.Body() - body.AppendNewline() - datasourceBody := body.AppendNewBlock("data", []string{"amazon-secretsmanager", dataSourceName}).Body() - jsonBodyToHCL2Body(datasourceBody, amazonSecretsManagerMap[dataSourceName]) - amazonSecretsDataOut = append(amazonSecretsDataOut, datasourceContent.Bytes()...) + if err := amazonSecretsDatasource.Parse(tpl); err != nil { + c.Ui.Error(err.Error()) + return 1 } // Write file out := &bytes.Buffer{} - // Packer section - if tpl.MinVersion != "" { - out.Write([]byte(packerBlockHeader)) - fileContent := hclwrite.NewEmptyFile() - body := fileContent.Body() - packerBody := body.AppendNewBlock("packer", nil).Body() - packerBody.SetAttributeValue("required_version", cty.StringVal(fmt.Sprintf(">= %s", tpl.MinVersion))) - out.Write(fileContent.Bytes()) + blocks := map[int]BlockParser{ + 1: packerBlock, + 2: variables, + 3: amazonSecretsDatasource, + 4: amazonAmiDatasource, + 5: locals, + 6: sources, + 7: build, + } + for i := 1; i <= len(blocks); i++ { + blocks[i].Write(out) } - if len(variablesOut) > 0 { - out.Write([]byte(inputVarHeader)) - out.Write(variablesOut) + if _, err := output.Write(hclwrite.Format(out.Bytes())); err != nil { + c.Ui.Error(fmt.Sprintf("Failed to write to file: %v", err)) + return 1 } - if len(amazonSecretsManagerMap) > 0 { - out.Write([]byte(amazonSecretsManagerDataHeader)) - out.Write(amazonSecretsDataOut) - } - - if len(amazonAmiOut) > 0 { - out.Write([]byte(amazonAmiDataHeader)) - out.Write(amazonAmiOut) - } - - if timestamp { - _, _ = out.Write([]byte("\n")) - fmt.Fprintln(out, `# "timestamp" template function replacement`) - fmt.Fprintln(out, `locals { timestamp = regex_replace(timestamp(), "[- TZ:]", "") }`) - } - - if len(localsOut) > 0 { - out.Write([]byte(localsVarHeader)) - out.Write(localsOut) - } - - out.Write([]byte(sourcesHeader)) - out.Write(sourcesOut) - - out.Write([]byte(buildHeader)) - out.Write(buildOut) - out.Write(provisionersOut) - out.Write(postProcessorsOut) - - _, _ = out.Write([]byte("}\n")) - - _, _ = output.Write(hclwrite.Format(out.Bytes())) - c.Ui.Say(fmt.Sprintf("Successfully created %s ", cla.OutputFile)) - return 0 } -func (c *HCL2UpgradeCommand) writeProvisioner(typeName string, provisioner *template.Provisioner) []byte { - provisionerContent := hclwrite.NewEmptyFile() - body := provisionerContent.Body() - block := body.AppendNewBlock(typeName, []string{provisioner.Type}) - cfg := provisioner.Config - if len(provisioner.Except) > 0 { - cfg["except"] = provisioner.Except - } - if len(provisioner.Only) > 0 { - cfg["only"] = provisioner.Only - } - if provisioner.MaxRetries != "" { - cfg["max_retries"] = provisioner.MaxRetries - } - if provisioner.Timeout > 0 { - cfg["timeout"] = provisioner.Timeout.String() - } - jsonBodyToHCL2Body(block.Body(), cfg) - return provisionerContent.Bytes() -} - -func (c *HCL2UpgradeCommand) writeAmazonAmiDatasource(builders []*template.Builder) ([]byte, error) { - amazonAmiOut := []byte{} - amazonAmiFilters := []map[string]interface{}{} - i := 1 - for _, builder := range builders { - if strings.HasPrefix(builder.Type, "amazon-") { - if sourceAmiFilter, ok := builder.Config["source_ami_filter"]; ok { - sourceAmiFilterCfg := map[string]interface{}{} - if err := mapstructure.Decode(sourceAmiFilter, &sourceAmiFilterCfg); err != nil { - c.Ui.Error(fmt.Sprintf("Failed to write amazon-ami data source: %v", err)) - return nil, err - } - - duplicate := false - dataSourceName := fmt.Sprintf("autogenerated_%d", i) - for j, filter := range amazonAmiFilters { - if reflect.DeepEqual(filter, sourceAmiFilter) { - duplicate = true - dataSourceName = fmt.Sprintf("autogenerated_%d", j+1) - continue - } - } - - // This is a hack... - // Use templating so that it could be correctly transformed later into a data resource - sourceAmiDataRef := fmt.Sprintf("{{ data `amazon-ami.%s.id` }}", dataSourceName) - - if duplicate { - delete(builder.Config, "source_ami_filter") - builder.Config["source_ami"] = sourceAmiDataRef - continue - } - - amazonAmiFilters = append(amazonAmiFilters, sourceAmiFilterCfg) - delete(builder.Config, "source_ami_filter") - builder.Config["source_ami"] = sourceAmiDataRef - i++ - - datasourceContent := hclwrite.NewEmptyFile() - body := datasourceContent.Body() - body.AppendNewline() - sourceBody := body.AppendNewBlock("data", []string{"amazon-ami", dataSourceName}).Body() - jsonBodyToHCL2Body(sourceBody, sourceAmiFilterCfg) - amazonAmiOut = append(amazonAmiOut, transposeTemplatingCalls(datasourceContent.Bytes())...) - } - } - } - - return amazonAmiOut, nil -} - type UnhandleableArgumentError struct { Call string Correspondance string @@ -789,3 +583,443 @@ func (*HCL2UpgradeCommand) AutocompleteArgs() complete.Predictor { func (*HCL2UpgradeCommand) AutocompleteFlags() complete.Flags { return complete.Flags{} } + +// Specific blocks parser + +type PackerParser struct { + out []byte + withAnnotations bool +} + +func (p *PackerParser) Parse(tpl *template.Template) error { + if tpl.MinVersion != "" { + fileContent := hclwrite.NewEmptyFile() + body := fileContent.Body() + packerBody := body.AppendNewBlock("packer", nil).Body() + packerBody.SetAttributeValue("required_version", cty.StringVal(fmt.Sprintf(">= %s", tpl.MinVersion))) + p.out = fileContent.Bytes() + } + return nil +} + +func (p *PackerParser) Write(out *bytes.Buffer) { + if len(p.out) > 0 { + if p.withAnnotations { + out.Write([]byte(packerBlockHeader)) + } + out.Write(p.out) + } +} + +type VariableParser struct { + variablesOut []byte + localsOut []byte + withAnnotations bool +} + +func (p *VariableParser) Parse(tpl *template.Template) error { + // OutPut Locals and Local blocks + localsContent := hclwrite.NewEmptyFile() + localsBody := localsContent.Body() + localsBody.AppendNewline() + localBody := localsBody.AppendNewBlock("locals", nil).Body() + + if len(p.variablesOut) == 0 { + p.variablesOut = []byte{} + } + if len(p.localsOut) == 0 { + p.localsOut = []byte{} + } + + variables := []*template.Variable{} + { + // sort variables to avoid map's randomness + for _, variable := range tpl.Variables { + variables = append(variables, variable) + } + sort.Slice(variables, func(i, j int) bool { + return variables[i].Key < variables[j].Key + }) + } + + 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)) + } + sensitive := false + if isSensitiveVariable(variable.Key, tpl.SensitiveVariables) { + sensitive = true + variableBody.SetAttributeValue("sensitive", cty.BoolVal(true)) + } + isLocal, out := variableTransposeTemplatingCalls(variablesContent.Bytes()) + 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 + } + localBody.SetAttributeValue(variable.Key, hcl2shim.HCL2ValueFromConfigValue(variable.Default)) + localsVariableMap[variable.Key] = "locals" + hasLocals = true + continue + } + p.variablesOut = append(p.variablesOut, out...) + } + + if hasLocals { + p.localsOut = append(p.localsOut, transposeTemplatingCalls(localsContent.Bytes())...) + } + return nil +} + +func (p *VariableParser) Write(out *bytes.Buffer) { + if len(p.variablesOut) > 0 { + if p.withAnnotations { + out.Write([]byte(inputVarHeader)) + } + out.Write(p.variablesOut) + } +} + +type LocalsParser struct { + LocalsOut []byte + withAnnotations bool +} + +func (p *LocalsParser) Parse(tpl *template.Template) error { + // Locals where parsed with Variables + return nil +} + +func (p *LocalsParser) Write(out *bytes.Buffer) { + if timestamp { + _, _ = out.Write([]byte("\n")) + if p.withAnnotations { + fmt.Fprintln(out, `# "timestamp" template function replacement`) + } + fmt.Fprintln(out, `locals { timestamp = regex_replace(timestamp(), "[- TZ:]", "") }`) + } + if len(p.LocalsOut) > 0 { + if p.withAnnotations { + out.Write([]byte(localsVarHeader)) + } + out.Write(p.LocalsOut) + } +} + +type AmazonSecretsDatasourceParser struct { + out []byte + withAnnotations bool +} + +func (p *AmazonSecretsDatasourceParser) Parse(_ *template.Template) error { + if p.out == nil { + p.out = []byte{} + } + + keys := make([]string, 0, len(amazonSecretsManagerMap)) + for k := range amazonSecretsManagerMap { + keys = append(keys, k) + } + sort.Strings(keys) + + for _, dataSourceName := range keys { + datasourceContent := hclwrite.NewEmptyFile() + body := datasourceContent.Body() + body.AppendNewline() + datasourceBody := body.AppendNewBlock("data", []string{"amazon-secretsmanager", dataSourceName}).Body() + jsonBodyToHCL2Body(datasourceBody, amazonSecretsManagerMap[dataSourceName]) + p.out = append(p.out, datasourceContent.Bytes()...) + } + + return nil +} + +func (p *AmazonSecretsDatasourceParser) Write(out *bytes.Buffer) { + if len(p.out) > 0 { + if p.withAnnotations { + out.Write([]byte(amazonSecretsManagerDataHeader)) + } + out.Write(p.out) + } +} + +type AmazonAmiDatasourceParser struct { + out []byte + builders []*template.Builder + withAnnotations bool +} + +func (p *AmazonAmiDatasourceParser) Parse(_ *template.Template) error { + if p.out == nil { + p.out = []byte{} + } + + amazonAmiFilters := []map[string]interface{}{} + i := 1 + for _, builder := range p.builders { + if strings.HasPrefix(builder.Type, "amazon-") { + if sourceAmiFilter, ok := builder.Config["source_ami_filter"]; ok { + sourceAmiFilterCfg := map[string]interface{}{} + if err := mapstructure.Decode(sourceAmiFilter, &sourceAmiFilterCfg); err != nil { + return fmt.Errorf("Failed to write amazon-ami data source: %v", err) + } + + duplicate := false + dataSourceName := fmt.Sprintf("autogenerated_%d", i) + for j, filter := range amazonAmiFilters { + if reflect.DeepEqual(filter, sourceAmiFilter) { + duplicate = true + dataSourceName = fmt.Sprintf("autogenerated_%d", j+1) + continue + } + } + + // This is a hack... + // Use templating so that it could be correctly transformed later into a data resource + sourceAmiDataRef := fmt.Sprintf("{{ data `amazon-ami.%s.id` }}", dataSourceName) + + if duplicate { + delete(builder.Config, "source_ami_filter") + builder.Config["source_ami"] = sourceAmiDataRef + continue + } + + amazonAmiFilters = append(amazonAmiFilters, sourceAmiFilterCfg) + delete(builder.Config, "source_ami_filter") + builder.Config["source_ami"] = sourceAmiDataRef + i++ + + datasourceContent := hclwrite.NewEmptyFile() + body := datasourceContent.Body() + body.AppendNewline() + sourceBody := body.AppendNewBlock("data", []string{"amazon-ami", dataSourceName}).Body() + jsonBodyToHCL2Body(sourceBody, sourceAmiFilterCfg) + p.out = append(p.out, transposeTemplatingCalls(datasourceContent.Bytes())...) + } + } + } + return nil +} + +func (p *AmazonAmiDatasourceParser) Write(out *bytes.Buffer) { + if len(p.out) > 0 { + if p.withAnnotations { + out.Write([]byte(amazonAmiDataHeader)) + } + out.Write(p.out) + } +} + +type SourceParser struct { + Builders []*template.Builder + BuilderPlugins packer.BuilderSet + + out []byte + withAnnotations bool +} + +func (p *SourceParser) Parse(tpl *template.Template) error { + if p.out == nil { + p.out = []byte{} + } + for i, builderCfg := range p.Builders { + sourcesContent := hclwrite.NewEmptyFile() + body := sourcesContent.Body() + + body.AppendNewline() + if !p.BuilderPlugins.Has(builderCfg.Type) { + return fmt.Errorf("unknown builder type: %q\n", builderCfg.Type) + } + if builderCfg.Name == "" || builderCfg.Name == builderCfg.Type { + builderCfg.Name = fmt.Sprintf("autogenerated_%d", i+1) + } + builderCfg.Name = strings.ReplaceAll(strings.TrimSpace(builderCfg.Name), " ", "_") + + sourceBody := body.AppendNewBlock("source", []string{builderCfg.Type, builderCfg.Name}).Body() + + jsonBodyToHCL2Body(sourceBody, builderCfg.Config) + + p.out = append(p.out, transposeTemplatingCalls(sourcesContent.Bytes())...) + } + return nil +} + +func (p *SourceParser) Write(out *bytes.Buffer) { + if len(p.out) > 0 { + if p.withAnnotations { + out.Write([]byte(sourcesHeader)) + } + out.Write(p.out) + } +} + +type BuildParser struct { + Builders []*template.Builder + + provisioners BlockParser + postProcessors BlockParser + + out []byte + withAnnotations bool +} + +func (p *BuildParser) Parse(tpl *template.Template) error { + buildContent := hclwrite.NewEmptyFile() + buildBody := buildContent.Body() + if tpl.Description != "" { + buildBody.SetAttributeValue("description", cty.StringVal(tpl.Description)) + buildBody.AppendNewline() + } + + sourceNames := []string{} + for _, builder := range p.Builders { + sourceNames = append(sourceNames, fmt.Sprintf("source.%s.%s", builder.Type, builder.Name)) + } + buildBody.SetAttributeValue("sources", hcl2shim.HCL2ValueFromConfigValue(sourceNames)) + buildBody.AppendNewline() + p.out = buildContent.Bytes() + + p.provisioners = &ProvisionerParser{ + withAnnotations: p.withAnnotations, + } + if err := p.provisioners.Parse(tpl); err != nil { + return err + } + + p.postProcessors = &PostProcessorParser{ + withAnnotations: p.withAnnotations, + } + if err := p.postProcessors.Parse(tpl); err != nil { + return err + } + + return nil +} + +func (p *BuildParser) Write(out *bytes.Buffer) { + if len(p.out) > 0 { + if p.withAnnotations { + out.Write([]byte(buildHeader)) + } else { + _, _ = out.Write([]byte("\n")) + } + _, _ = out.Write([]byte("build {\n")) + out.Write(p.out) + p.provisioners.Write(out) + p.postProcessors.Write(out) + _, _ = out.Write([]byte("}\n")) + } +} + +type ProvisionerParser struct { + out []byte + withAnnotations bool +} + +func (p *ProvisionerParser) Parse(tpl *template.Template) error { + if p.out == nil { + p.out = []byte{} + } + for _, provisioner := range tpl.Provisioners { + contentBytes := writeProvisioner("provisioner", provisioner) + p.out = append(p.out, transposeTemplatingCalls(contentBytes)...) + } + + if tpl.CleanupProvisioner != nil { + contentBytes := writeProvisioner("error-cleanup-provisioner", tpl.CleanupProvisioner) + p.out = append(p.out, transposeTemplatingCalls(contentBytes)...) + } + return nil +} + +func writeProvisioner(typeName string, provisioner *template.Provisioner) []byte { + provisionerContent := hclwrite.NewEmptyFile() + body := provisionerContent.Body() + block := body.AppendNewBlock(typeName, []string{provisioner.Type}) + cfg := provisioner.Config + if len(provisioner.Except) > 0 { + cfg["except"] = provisioner.Except + } + if len(provisioner.Only) > 0 { + cfg["only"] = provisioner.Only + } + if provisioner.MaxRetries != "" { + cfg["max_retries"] = provisioner.MaxRetries + } + if provisioner.Timeout > 0 { + cfg["timeout"] = provisioner.Timeout.String() + } + body.AppendNewline() + jsonBodyToHCL2Body(block.Body(), cfg) + return provisionerContent.Bytes() +} + +func (p *ProvisionerParser) Write(out *bytes.Buffer) { + if len(p.out) > 0 { + out.Write(p.out) + } +} + +type PostProcessorParser struct { + out []byte + withAnnotations bool +} + +func (p *PostProcessorParser) Parse(tpl *template.Template) error { + if p.out == nil { + p.out = []byte{} + } + for _, pps := range tpl.PostProcessors { + postProcessorContent := hclwrite.NewEmptyFile() + body := postProcessorContent.Body() + + switch len(pps) { + case 0: + continue + case 1: + default: + body = body.AppendNewBlock("post-processors", nil).Body() + } + for _, pp := range pps { + ppBody := body.AppendNewBlock("post-processor", []string{pp.Type}).Body() + if pp.KeepInputArtifact != nil { + ppBody.SetAttributeValue("keep_input_artifact", cty.BoolVal(*pp.KeepInputArtifact)) + } + cfg := pp.Config + if len(pp.Except) > 0 { + cfg["except"] = pp.Except + } + if len(pp.Only) > 0 { + cfg["only"] = pp.Only + } + if pp.Name != "" && pp.Name != pp.Type { + cfg["name"] = pp.Name + } + jsonBodyToHCL2Body(ppBody, cfg) + } + + p.out = append(p.out, transposeTemplatingCalls(postProcessorContent.Bytes())...) + } + return nil +} + +func (p *PostProcessorParser) Write(out *bytes.Buffer) { + if len(p.out) > 0 { + out.Write(p.out) + } +} diff --git a/command/hcl2_upgrade_test.go b/command/hcl2_upgrade_test.go index bd8c3148e..2aa166167 100644 --- a/command/hcl2_upgrade_test.go +++ b/command/hcl2_upgrade_test.go @@ -18,11 +18,13 @@ func Test_hcl2_upgrade(t *testing.T) { tc := []struct { folder string + flags []string }{ - {"complete"}, - {"minimal"}, - {"source-name"}, - {"error-cleanup-provisioner"}, + {folder: "complete", flags: []string{"-with-annotations"}}, + {folder: "without-annotations", flags: []string{}}, + {folder: "minimal", flags: []string{"-with-annotations"}}, + {folder: "source-name", flags: []string{"-with-annotations"}}, + {folder: "error-cleanup-provisioner", flags: []string{"-with-annotations"}}, } for _, tc := range tc { @@ -30,7 +32,12 @@ func Test_hcl2_upgrade(t *testing.T) { inputPath := filepath.Join(testFixture("hcl2_upgrade", tc.folder, "input.json")) outputPath := inputPath + ".pkr.hcl" expectedPath := filepath.Join(testFixture("hcl2_upgrade", tc.folder, "expected.pkr.hcl")) - p := helperCommand(t, "hcl2_upgrade", inputPath) + args := []string{"hcl2_upgrade"} + if len(tc.flags) > 0 { + args = append(args, tc.flags...) + } + args = append(args, inputPath) + p := helperCommand(t, args...) bs, err := p.CombinedOutput() if err != nil { t.Fatalf("%v %s", err, bs) diff --git a/command/test-fixtures/hcl2_upgrade/complete/expected.pkr.hcl b/command/test-fixtures/hcl2_upgrade/complete/expected.pkr.hcl index aaa0bdd7b..1cee1af89 100644 --- a/command/test-fixtures/hcl2_upgrade/complete/expected.pkr.hcl +++ b/command/test-fixtures/hcl2_upgrade/complete/expected.pkr.hcl @@ -174,13 +174,16 @@ build { inline = ["echo ${var.secret_account}", "echo ${build.ID}", "echo ${build.SSHPublicKey} | head -c 14", "echo ${path.root} is not ${path.cwd}", "echo ${packer.version}", "echo ${uuidv4()}"] max_retries = "5" } + provisioner "shell" { inline = ["echo ${local.password}", "echo ${data.amazon-secretsmanager.autogenerated_1.value}", "echo ${local.password_key}", "echo ${data.amazon-secretsmanager.autogenerated_2.value}"] } + provisioner "shell" { inline = ["echo ${data.amazon-secretsmanager.autogenerated_3.value}", "echo ${data.amazon-secretsmanager.autogenerated_4.value}"] } + # template: hcl2_upgrade:2:38: executing "hcl2_upgrade" at : error calling clean_resource_name: unhandled "clean_resource_name" call: # there is no way to automatically upgrade the "clean_resource_name" call. # Please manually upgrade to use custom validation rules, `replace(string, substring, replacement)` or `regex_replace(string, substring, replacement)` @@ -189,6 +192,7 @@ build { inline = ["echo mybuild-{{isotime | clean_resource_name}}"] } + # template: hcl2_upgrade:2:35: executing "hcl2_upgrade" at : error calling lower: unhandled "lower" call: # there is no way to automatically upgrade the "lower" call. # Please manually upgrade to `lower(var.example)` @@ -197,6 +201,7 @@ build { inline = ["echo {{ `SOMETHING` | lower }}"] } + # template: hcl2_upgrade:2:35: executing "hcl2_upgrade" at : error calling upper: unhandled "upper" call: # there is no way to automatically upgrade the "upper" call. # Please manually upgrade to `upper(var.example)` @@ -205,6 +210,7 @@ build { inline = ["echo {{ `something` | upper }}"] } + # template: hcl2_upgrade:2:21: executing "hcl2_upgrade" at : error calling split: unhandled "split" call: # there is no way to automatically upgrade the "split" call. # Please manually upgrade to `split(separator, string)` @@ -213,6 +219,7 @@ build { inline = ["echo {{ split `some-string` `-` 0 }}"] } + # template: hcl2_upgrade:2:21: executing "hcl2_upgrade" at : error calling replace_all: unhandled "replace_all" call: # there is no way to automatically upgrade the "replace_all" call. # Please manually upgrade to `replace(string, substring, replacement)` or `regex_replace(string, substring, replacement)` @@ -221,6 +228,7 @@ build { inline = ["echo {{ replace_all `-` `/` build_name }}"] } + # template: hcl2_upgrade:2:21: executing "hcl2_upgrade" at : error calling replace: unhandled "replace" call: # there is no way to automatically upgrade the "replace" call. # Please manually upgrade to `replace(string, substring, replacement)` or `regex_replace(string, substring, replacement)` @@ -228,11 +236,13 @@ build { provisioner "shell" { inline = ["echo {{ replace `some-string` `-` `/` 1 }}"] } + provisioner "shell-local" { inline = ["sleep 100000"] only = ["amazon-ebs"] timeout = "5s" } + post-processor "amazon-import" { format = "vmdk" license_type = "BYOL" diff --git a/command/test-fixtures/hcl2_upgrade/error-cleanup-provisioner/expected.pkr.hcl b/command/test-fixtures/hcl2_upgrade/error-cleanup-provisioner/expected.pkr.hcl index 99fa3d653..200fcc915 100644 --- a/command/test-fixtures/hcl2_upgrade/error-cleanup-provisioner/expected.pkr.hcl +++ b/command/test-fixtures/hcl2_upgrade/error-cleanup-provisioner/expected.pkr.hcl @@ -28,7 +28,9 @@ build { provisioner "shell-local" { inline = ["exit 2"] } + error-cleanup-provisioner "shell-local" { inline = ["echo 'rubber ducky'> ducky.txt"] } + } diff --git a/command/test-fixtures/hcl2_upgrade/without-annotations/expected.pkr.hcl b/command/test-fixtures/hcl2_upgrade/without-annotations/expected.pkr.hcl new file mode 100644 index 000000000..ba7fa0e54 --- /dev/null +++ b/command/test-fixtures/hcl2_upgrade/without-annotations/expected.pkr.hcl @@ -0,0 +1,229 @@ +packer { + required_version = ">= 1.6.0" +} + +variable "aws_access_key" { + type = string + default = "" + sensitive = true +} + +variable "aws_region" { + type = string +} + +variable "aws_secondary_region" { + type = string + default = "${env("AWS_DEFAULT_REGION")}" +} + +variable "aws_secret_key" { + type = string + default = "" + sensitive = true +} + +variable "secret_account" { + type = string + default = "🤷" + sensitive = true +} + +data "amazon-secretsmanager" "autogenerated_1" { + name = "sample/app/password" +} + +data "amazon-secretsmanager" "autogenerated_2" { + key = "api_key" + name = "sample/app/passwords" +} + +data "amazon-secretsmanager" "autogenerated_3" { + name = "some_secret" +} + +data "amazon-secretsmanager" "autogenerated_4" { + key = "with_key" + name = "some_secret" +} + +data "amazon-ami" "autogenerated_1" { + filters = { + name = "ubuntu/images/*/ubuntu-xenial-16.04-amd64-server-*" + root-device-type = "ebs" + virtualization-type = "hvm" + } + most_recent = true + owners = ["099720109477"] +} + +locals { timestamp = regex_replace(timestamp(), "[- TZ:]", "") } + +local "password" { + sensitive = true + expression = "${data.amazon-secretsmanager.autogenerated_1.value}" +} + +locals { + password_key = "MY_KEY_${data.amazon-secretsmanager.autogenerated_2.value}" +} + +source "amazon-ebs" "autogenerated_1" { + access_key = "${var.aws_access_key}" + ami_description = "Ubuntu 16.04 LTS - expand root partition" + ami_name = "ubuntu-16-04-test-${local.timestamp}" + encrypt_boot = true + launch_block_device_mappings { + delete_on_termination = true + device_name = "/dev/sda1" + volume_size = 48 + volume_type = "gp2" + } + region = "${var.aws_region}" + secret_key = "${var.aws_secret_key}" + source_ami = "${data.amazon-ami.autogenerated_1.id}" + spot_instance_types = ["t2.small", "t2.medium", "t2.large"] + spot_price = "0.0075" + ssh_interface = "session_manager" + ssh_username = "ubuntu" + temporary_iam_instance_profile_policy_document { + Statement { + Action = ["*"] + Effect = "Allow" + Resource = ["*"] + } + Version = "2012-10-17" + } +} + +source "amazon-ebs" "named_builder" { + access_key = "${var.aws_access_key}" + ami_description = "Ubuntu 16.04 LTS - expand root partition" + ami_name = "ubuntu-16-04-test-${local.timestamp}" + encrypt_boot = true + launch_block_device_mappings { + delete_on_termination = true + device_name = "/dev/sda1" + volume_size = 48 + volume_type = "gp2" + } + region = "${var.aws_region}" + secret_key = "${var.aws_secret_key}" + source_ami = "${data.amazon-ami.autogenerated_1.id}" + spot_instance_types = ["t2.small", "t2.medium", "t2.large"] + spot_price = "0.0075" + ssh_interface = "session_manager" + ssh_username = "ubuntu" + temporary_iam_instance_profile_policy_document { + Statement { + Action = ["*"] + Effect = "Allow" + Resource = ["*"] + } + Version = "2012-10-17" + } +} + +build { + sources = ["source.amazon-ebs.autogenerated_1", "source.amazon-ebs.named_builder"] + + provisioner "shell" { + except = ["amazon-ebs"] + inline = ["echo ${var.secret_account}", "echo ${build.ID}", "echo ${build.SSHPublicKey} | head -c 14", "echo ${path.root} is not ${path.cwd}", "echo ${packer.version}", "echo ${uuidv4()}"] + max_retries = "5" + } + + provisioner "shell" { + inline = ["echo ${local.password}", "echo ${data.amazon-secretsmanager.autogenerated_1.value}", "echo ${local.password_key}", "echo ${data.amazon-secretsmanager.autogenerated_2.value}"] + } + + provisioner "shell" { + inline = ["echo ${data.amazon-secretsmanager.autogenerated_3.value}", "echo ${data.amazon-secretsmanager.autogenerated_4.value}"] + } + + + # template: hcl2_upgrade:2:38: executing "hcl2_upgrade" at : error calling clean_resource_name: unhandled "clean_resource_name" call: + # there is no way to automatically upgrade the "clean_resource_name" call. + # Please manually upgrade to use custom validation rules, `replace(string, substring, replacement)` or `regex_replace(string, substring, replacement)` + # Visit https://packer.io/docs/templates/hcl_templates/variables#custom-validation-rules , https://www.packer.io/docs/templates/hcl_templates/functions/string/replace or https://www.packer.io/docs/templates/hcl_templates/functions/string/regex_replace for more infos. + provisioner "shell" { + inline = ["echo mybuild-{{isotime | clean_resource_name}}"] + } + + + # template: hcl2_upgrade:2:35: executing "hcl2_upgrade" at : error calling lower: unhandled "lower" call: + # there is no way to automatically upgrade the "lower" call. + # Please manually upgrade to `lower(var.example)` + # Visit https://www.packer.io/docs/templates/hcl_templates/functions/string/lower for more infos. + provisioner "shell" { + inline = ["echo {{ `SOMETHING` | lower }}"] + } + + + # template: hcl2_upgrade:2:35: executing "hcl2_upgrade" at : error calling upper: unhandled "upper" call: + # there is no way to automatically upgrade the "upper" call. + # Please manually upgrade to `upper(var.example)` + # Visit https://www.packer.io/docs/templates/hcl_templates/functions/string/upper for more infos. + provisioner "shell" { + inline = ["echo {{ `something` | upper }}"] + } + + + # template: hcl2_upgrade:2:21: executing "hcl2_upgrade" at : error calling split: unhandled "split" call: + # there is no way to automatically upgrade the "split" call. + # Please manually upgrade to `split(separator, string)` + # Visit https://www.packer.io/docs/templates/hcl_templates/functions/string/split for more infos. + provisioner "shell" { + inline = ["echo {{ split `some-string` `-` 0 }}"] + } + + + # template: hcl2_upgrade:2:21: executing "hcl2_upgrade" at : error calling replace_all: unhandled "replace_all" call: + # there is no way to automatically upgrade the "replace_all" call. + # Please manually upgrade to `replace(string, substring, replacement)` or `regex_replace(string, substring, replacement)` + # Visit https://www.packer.io/docs/templates/hcl_templates/functions/string/replace or https://www.packer.io/docs/templates/hcl_templates/functions/string/regex_replace for more infos. + provisioner "shell" { + inline = ["echo {{ replace_all `-` `/` build_name }}"] + } + + + # template: hcl2_upgrade:2:21: executing "hcl2_upgrade" at : error calling replace: unhandled "replace" call: + # there is no way to automatically upgrade the "replace" call. + # Please manually upgrade to `replace(string, substring, replacement)` or `regex_replace(string, substring, replacement)` + # Visit https://www.packer.io/docs/templates/hcl_templates/functions/string/replace or https://www.packer.io/docs/templates/hcl_templates/functions/string/regex_replace for more infos. + provisioner "shell" { + inline = ["echo {{ replace `some-string` `-` `/` 1 }}"] + } + + provisioner "shell-local" { + inline = ["sleep 100000"] + only = ["amazon-ebs"] + timeout = "5s" + } + + post-processor "amazon-import" { + format = "vmdk" + license_type = "BYOL" + region = "eu-west-3" + s3_bucket_name = "hashicorp.adrien" + tags = { + Description = "packer amazon-import ${local.timestamp}" + } + } + post-processors { + post-processor "artifice" { + keep_input_artifact = true + files = ["path/something.ova"] + name = "very_special_artifice_post-processor" + only = ["amazon-ebs"] + } + post-processor "amazon-import" { + except = ["amazon-ebs"] + license_type = "BYOL" + s3_bucket_name = "hashicorp.adrien" + tags = { + Description = "packer amazon-import ${local.timestamp}" + } + } + } +} diff --git a/command/test-fixtures/hcl2_upgrade/without-annotations/input.json b/command/test-fixtures/hcl2_upgrade/without-annotations/input.json new file mode 100644 index 000000000..1b4eb5dec --- /dev/null +++ b/command/test-fixtures/hcl2_upgrade/without-annotations/input.json @@ -0,0 +1,235 @@ +{ + "min_packer_version": "1.6.0", + "variables": { + "secret_account": "🤷", + "aws_region": null, + "aws_secondary_region": "{{ env `AWS_DEFAULT_REGION` }}", + "aws_secret_key": "", + "aws_access_key": "", + "password": "{{ aws_secretsmanager `sample/app/password` }}", + "password_key": "MY_KEY_{{ aws_secretsmanager `sample/app/passwords` `api_key` }}" + }, + "sensitive-variables": [ + "aws_secret_key", + "aws_access_key", + "secret_account", + "potato", + "password" + ], + "builders": [ + { + "type": "amazon-ebs", + "region": "{{ user `aws_region` }}", + "secret_key": "{{ user `aws_secret_key` }}", + "access_key": "{{ user `aws_access_key` }}", + "ami_name": "ubuntu-16-04-test-{{ timestamp }}", + "ami_description": "Ubuntu 16.04 LTS - expand root partition", + "source_ami_filter": { + "filters": { + "virtualization-type": "hvm", + "name": "ubuntu/images/*/ubuntu-xenial-16.04-amd64-server-*", + "root-device-type": "ebs" + }, + "owners": [ + "099720109477" + ], + "most_recent": true + }, + "launch_block_device_mappings": [ + { + "delete_on_termination": true, + "device_name": "/dev/sda1", + "volume_type": "gp2", + "volume_size": 48 + } + ], + "spot_price": "0.0075", + "spot_instance_types": [ + "t2.small", + "t2.medium", + "t2.large" + ], + "encrypt_boot": true, + "ssh_username": "ubuntu", + "temporary_iam_instance_profile_policy_document": { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": [ + "*" + ], + "Resource": ["*"] + } + ] + }, + "ssh_interface": "session_manager" + }, + { + "type": "amazon-ebs", + "name": "named_builder", + "region": "{{ user `aws_region` }}", + "secret_key": "{{ user `aws_secret_key` }}", + "access_key": "{{ user `aws_access_key` }}", + "ami_name": "ubuntu-16-04-test-{{ timestamp }}", + "ami_description": "Ubuntu 16.04 LTS - expand root partition", + "source_ami_filter": { + "filters": { + "virtualization-type": "hvm", + "name": "ubuntu/images/*/ubuntu-xenial-16.04-amd64-server-*", + "root-device-type": "ebs" + }, + "owners": [ + "099720109477" + ], + "most_recent": true + }, + "launch_block_device_mappings": [ + { + "delete_on_termination": true, + "device_name": "/dev/sda1", + "volume_type": "gp2", + "volume_size": 48 + } + ], + "spot_price": "0.0075", + "spot_instance_types": [ + "t2.small", + "t2.medium", + "t2.large" + ], + "encrypt_boot": true, + "ssh_username": "ubuntu", + "temporary_iam_instance_profile_policy_document": { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": [ + "*" + ], + "Resource": ["*"] + } + ] + }, + "ssh_interface": "session_manager" + } + ], + "provisioners": [ + { + "type": "shell", + "except": [ + "amazon-ebs" + ], + "max_retries": 5, + "inline": [ + "echo {{ user `secret_account` }}", + "echo {{ build `ID` }}", + "echo {{ build `SSHPublicKey` }} | head -c 14", + "echo {{ template_dir }} is not {{ pwd }}", + "echo {{ packer_version }}", + "echo {{ uuid }}" + ] + }, + { + "type": "shell", + "inline": [ + "echo {{ user `password` }}", + "echo {{ aws_secretsmanager `sample/app/password` }}", + "echo {{ user `password_key` }}", + "echo {{ aws_secretsmanager `sample/app/passwords` `api_key` }}" + ] + }, + { + "type": "shell", + "inline": [ + "echo {{ aws_secretsmanager `some_secret` }}", + "echo {{ aws_secretsmanager `some_secret` `with_key` }}" + ] + }, + { + "type": "shell", + "inline": [ + "echo mybuild-{{isotime | clean_resource_name}}" + ] + }, + { + "type": "shell", + "inline": [ + "echo {{ `SOMETHING` | lower }}" + ] + }, + { + "type": "shell", + "inline": [ + "echo {{ `something` | upper }}" + ] + }, + { + "type": "shell", + "inline": [ + "echo {{ split `some-string` `-` 0 }}" + ] + }, + { + "type": "shell", + "inline": [ + "echo {{ replace_all `-` `/` build_name }}" + ] + }, + { + "type": "shell", + "inline": [ + "echo {{ replace `some-string` `-` `/` 1 }}" + ] + }, + { + "type": "shell-local", + "only": [ + "amazon-ebs" + ], + "timeout": "5s", + "inline": [ + "sleep 100000" + ] + } + ], + "post-processors": [ + [ + { + "type": "amazon-import", + "region": "eu-west-3", + "s3_bucket_name": "hashicorp.adrien", + "license_type": "BYOL", + "format": "vmdk", + "tags": { + "Description": "packer amazon-import {{timestamp}}" + } + } + ], + [ + { + "only": [ + "amazon-ebs" + ], + "files": [ + "path/something.ova" + ], + "keep_input_artifact": true, + "name": "very_special_artifice_post-processor", + "type": "artifice" + }, + { + "except": [ + "amazon-ebs" + ], + "type": "amazon-import", + "s3_bucket_name": "hashicorp.adrien", + "license_type": "BYOL", + "tags": { + "Description": "packer amazon-import {{timestamp}}" + } + } + ] + ] +} \ No newline at end of file From 855ba9775a75cb9b61796c00d7ec8013130e645f Mon Sep 17 00:00:00 2001 From: sylviamoss Date: Fri, 12 Feb 2021 15:31:13 +0100 Subject: [PATCH 2/3] add docs --- command/cli.go | 2 +- website/content/docs/commands/hcl2_upgrade.mdx | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/command/cli.go b/command/cli.go index 10f7242a4..e7ea6cbf4 100644 --- a/command/cli.go +++ b/command/cli.go @@ -145,7 +145,7 @@ type InspectArgs struct { func (va *HCL2UpgradeArgs) AddFlagSets(flags *flag.FlagSet) { flags.StringVar(&va.OutputFile, "output-file", "", "File where to put the hcl2 generated config. Defaults to JSON_TEMPLATE.pkr.hcl") - flags.BoolVar(&va.WithAnnotations, "with-annotations", false, "Add to the generated file helper annotations about the generated HCL2 blocks.") + flags.BoolVar(&va.WithAnnotations, "with-annotations", false, "Adds helper annotations with information about the generated HCL2 blocks.") va.MetaArgs.AddFlagSets(flags) } diff --git a/website/content/docs/commands/hcl2_upgrade.mdx b/website/content/docs/commands/hcl2_upgrade.mdx index 3a974d7f5..d6a5b44e6 100644 --- a/website/content/docs/commands/hcl2_upgrade.mdx +++ b/website/content/docs/commands/hcl2_upgrade.mdx @@ -78,3 +78,4 @@ working on improving this part of the transformer. - `-output-file` - File where to put the hcl2 generated config. Defaults to JSON_TEMPLATE.pkr.hcl +- `-with-annotations` - Adds helper annotations with information about the generated HCL2 blocks. From e69410b63343bb937cc921c7d96f9d66204ad078 Mon Sep 17 00:00:00 2001 From: sylviamoss Date: Fri, 12 Feb 2021 15:37:41 +0100 Subject: [PATCH 3/3] fix lint --- command/hcl2_upgrade.go | 72 ++++++++++++++++++++--------------------- 1 file changed, 35 insertions(+), 37 deletions(-) diff --git a/command/hcl2_upgrade.go b/command/hcl2_upgrade.go index 9cb0e0c00..4da7bea86 100644 --- a/command/hcl2_upgrade.go +++ b/command/hcl2_upgrade.go @@ -4,7 +4,6 @@ import ( "bytes" "context" "fmt" - "github.com/hashicorp/packer/packer" "io" "os" "path/filepath" @@ -16,6 +15,7 @@ import ( "github.com/hashicorp/hcl/v2/hclwrite" hcl2shim "github.com/hashicorp/packer-plugin-sdk/hcl2helper" "github.com/hashicorp/packer-plugin-sdk/template" + "github.com/hashicorp/packer/packer" "github.com/mitchellh/mapstructure" "github.com/posener/complete" "github.com/zclconf/go-cty/cty" @@ -160,7 +160,7 @@ func (c *HCL2UpgradeCommand) RunContext(_ context.Context, cla *HCL2UpgradeArgs) // Parse blocks packerBlock := &PackerParser{ - withAnnotations: cla.WithAnnotations, + WithAnnotations: cla.WithAnnotations, } if err := packerBlock.Parse(tpl); err != nil { c.Ui.Error(err.Error()) @@ -168,7 +168,7 @@ func (c *HCL2UpgradeCommand) RunContext(_ context.Context, cla *HCL2UpgradeArgs) } variables := &VariableParser{ - withAnnotations: cla.WithAnnotations, + WithAnnotations: cla.WithAnnotations, } if err := variables.Parse(tpl); err != nil { c.Ui.Error(err.Error()) @@ -177,7 +177,7 @@ func (c *HCL2UpgradeCommand) RunContext(_ context.Context, cla *HCL2UpgradeArgs) locals := &LocalsParser{ LocalsOut: variables.localsOut, - withAnnotations: cla.WithAnnotations, + WithAnnotations: cla.WithAnnotations, } if err := locals.Parse(tpl); err != nil { c.Ui.Error(err.Error()) @@ -193,8 +193,8 @@ func (c *HCL2UpgradeCommand) RunContext(_ context.Context, cla *HCL2UpgradeArgs) } amazonAmiDatasource := &AmazonAmiDatasourceParser{ - builders: builders, - withAnnotations: cla.WithAnnotations, + Builders: builders, + WithAnnotations: cla.WithAnnotations, } if err := amazonAmiDatasource.Parse(tpl); err != nil { c.Ui.Error(err.Error()) @@ -208,7 +208,7 @@ func (c *HCL2UpgradeCommand) RunContext(_ context.Context, cla *HCL2UpgradeArgs) sources := &SourceParser{ Builders: builders, BuilderPlugins: c.Meta.CoreConfig.Components.PluginConfig.Builders, - withAnnotations: cla.WithAnnotations, + WithAnnotations: cla.WithAnnotations, } if err := sources.Parse(tpl); err != nil { c.Ui.Error(err.Error()) @@ -217,7 +217,7 @@ func (c *HCL2UpgradeCommand) RunContext(_ context.Context, cla *HCL2UpgradeArgs) build := &BuildParser{ Builders: builders, - withAnnotations: cla.WithAnnotations, + WithAnnotations: cla.WithAnnotations, } if err := build.Parse(tpl); err != nil { c.Ui.Error(err.Error()) @@ -225,7 +225,7 @@ func (c *HCL2UpgradeCommand) RunContext(_ context.Context, cla *HCL2UpgradeArgs) } amazonSecretsDatasource := &AmazonSecretsDatasourceParser{ - withAnnotations: cla.WithAnnotations, + WithAnnotations: cla.WithAnnotations, } if err := amazonSecretsDatasource.Parse(tpl); err != nil { c.Ui.Error(err.Error()) @@ -584,11 +584,11 @@ func (*HCL2UpgradeCommand) AutocompleteFlags() complete.Flags { return complete.Flags{} } -// Specific blocks parser +// Specific blocks parser responsible to parse and write the block type PackerParser struct { + WithAnnotations bool out []byte - withAnnotations bool } func (p *PackerParser) Parse(tpl *template.Template) error { @@ -604,7 +604,7 @@ func (p *PackerParser) Parse(tpl *template.Template) error { func (p *PackerParser) Write(out *bytes.Buffer) { if len(p.out) > 0 { - if p.withAnnotations { + if p.WithAnnotations { out.Write([]byte(packerBlockHeader)) } out.Write(p.out) @@ -612,9 +612,9 @@ func (p *PackerParser) Write(out *bytes.Buffer) { } type VariableParser struct { + WithAnnotations bool variablesOut []byte localsOut []byte - withAnnotations bool } func (p *VariableParser) Parse(tpl *template.Template) error { @@ -688,7 +688,7 @@ func (p *VariableParser) Parse(tpl *template.Template) error { func (p *VariableParser) Write(out *bytes.Buffer) { if len(p.variablesOut) > 0 { - if p.withAnnotations { + if p.WithAnnotations { out.Write([]byte(inputVarHeader)) } out.Write(p.variablesOut) @@ -696,8 +696,8 @@ func (p *VariableParser) Write(out *bytes.Buffer) { } type LocalsParser struct { + WithAnnotations bool LocalsOut []byte - withAnnotations bool } func (p *LocalsParser) Parse(tpl *template.Template) error { @@ -708,13 +708,13 @@ func (p *LocalsParser) Parse(tpl *template.Template) error { func (p *LocalsParser) Write(out *bytes.Buffer) { if timestamp { _, _ = out.Write([]byte("\n")) - if p.withAnnotations { + if p.WithAnnotations { fmt.Fprintln(out, `# "timestamp" template function replacement`) } fmt.Fprintln(out, `locals { timestamp = regex_replace(timestamp(), "[- TZ:]", "") }`) } if len(p.LocalsOut) > 0 { - if p.withAnnotations { + if p.WithAnnotations { out.Write([]byte(localsVarHeader)) } out.Write(p.LocalsOut) @@ -722,8 +722,8 @@ func (p *LocalsParser) Write(out *bytes.Buffer) { } type AmazonSecretsDatasourceParser struct { + WithAnnotations bool out []byte - withAnnotations bool } func (p *AmazonSecretsDatasourceParser) Parse(_ *template.Template) error { @@ -751,7 +751,7 @@ func (p *AmazonSecretsDatasourceParser) Parse(_ *template.Template) error { func (p *AmazonSecretsDatasourceParser) Write(out *bytes.Buffer) { if len(p.out) > 0 { - if p.withAnnotations { + if p.WithAnnotations { out.Write([]byte(amazonSecretsManagerDataHeader)) } out.Write(p.out) @@ -759,9 +759,9 @@ func (p *AmazonSecretsDatasourceParser) Write(out *bytes.Buffer) { } type AmazonAmiDatasourceParser struct { + Builders []*template.Builder + WithAnnotations bool out []byte - builders []*template.Builder - withAnnotations bool } func (p *AmazonAmiDatasourceParser) Parse(_ *template.Template) error { @@ -771,7 +771,7 @@ func (p *AmazonAmiDatasourceParser) Parse(_ *template.Template) error { amazonAmiFilters := []map[string]interface{}{} i := 1 - for _, builder := range p.builders { + for _, builder := range p.Builders { if strings.HasPrefix(builder.Type, "amazon-") { if sourceAmiFilter, ok := builder.Config["source_ami_filter"]; ok { sourceAmiFilterCfg := map[string]interface{}{} @@ -818,7 +818,7 @@ func (p *AmazonAmiDatasourceParser) Parse(_ *template.Template) error { func (p *AmazonAmiDatasourceParser) Write(out *bytes.Buffer) { if len(p.out) > 0 { - if p.withAnnotations { + if p.WithAnnotations { out.Write([]byte(amazonAmiDataHeader)) } out.Write(p.out) @@ -826,11 +826,10 @@ func (p *AmazonAmiDatasourceParser) Write(out *bytes.Buffer) { } type SourceParser struct { - Builders []*template.Builder - BuilderPlugins packer.BuilderSet - + Builders []*template.Builder + BuilderPlugins packer.BuilderSet + WithAnnotations bool out []byte - withAnnotations bool } func (p *SourceParser) Parse(tpl *template.Template) error { @@ -861,7 +860,7 @@ func (p *SourceParser) Parse(tpl *template.Template) error { func (p *SourceParser) Write(out *bytes.Buffer) { if len(p.out) > 0 { - if p.withAnnotations { + if p.WithAnnotations { out.Write([]byte(sourcesHeader)) } out.Write(p.out) @@ -869,13 +868,12 @@ func (p *SourceParser) Write(out *bytes.Buffer) { } type BuildParser struct { - Builders []*template.Builder + Builders []*template.Builder + WithAnnotations bool provisioners BlockParser postProcessors BlockParser - - out []byte - withAnnotations bool + out []byte } func (p *BuildParser) Parse(tpl *template.Template) error { @@ -895,14 +893,14 @@ func (p *BuildParser) Parse(tpl *template.Template) error { p.out = buildContent.Bytes() p.provisioners = &ProvisionerParser{ - withAnnotations: p.withAnnotations, + WithAnnotations: p.WithAnnotations, } if err := p.provisioners.Parse(tpl); err != nil { return err } p.postProcessors = &PostProcessorParser{ - withAnnotations: p.withAnnotations, + WithAnnotations: p.WithAnnotations, } if err := p.postProcessors.Parse(tpl); err != nil { return err @@ -913,7 +911,7 @@ func (p *BuildParser) Parse(tpl *template.Template) error { func (p *BuildParser) Write(out *bytes.Buffer) { if len(p.out) > 0 { - if p.withAnnotations { + if p.WithAnnotations { out.Write([]byte(buildHeader)) } else { _, _ = out.Write([]byte("\n")) @@ -927,8 +925,8 @@ func (p *BuildParser) Write(out *bytes.Buffer) { } type ProvisionerParser struct { + WithAnnotations bool out []byte - withAnnotations bool } func (p *ProvisionerParser) Parse(tpl *template.Template) error { @@ -976,8 +974,8 @@ func (p *ProvisionerParser) Write(out *bytes.Buffer) { } type PostProcessorParser struct { + WithAnnotations bool out []byte - withAnnotations bool } func (p *PostProcessorParser) Parse(tpl *template.Template) error {