From 0d7c5dc67047ad26e1b5f45772cdccf4b43d10da Mon Sep 17 00:00:00 2001 From: Jeremiah Snapp Date: Fri, 5 Jun 2020 05:51:23 -0400 Subject: [PATCH] Enable only/except for provisioners and post-processors (#9357) --- hcl2template/common_test.go | 1 + .../build/post-processor_onlyexcept.pkr.hcl | 21 +++ .../build/provisioner_onlyexcept.pkr.hcl | 21 +++ hcl2template/types.build.post-processor.go | 23 ++- hcl2template/types.build.provisioners.go | 55 +++++++ hcl2template/types.build_test.go | 134 ++++++++++++++++++ hcl2template/types.packer_config.go | 6 + hcl2template/types.packer_config_test.go | 6 +- 8 files changed, 257 insertions(+), 10 deletions(-) create mode 100644 hcl2template/testdata/build/post-processor_onlyexcept.pkr.hcl create mode 100644 hcl2template/testdata/build/provisioner_onlyexcept.pkr.hcl diff --git a/hcl2template/common_test.go b/hcl2template/common_test.go index 61b41a186..5db6dc6c2 100644 --- a/hcl2template/common_test.go +++ b/hcl2template/common_test.go @@ -30,6 +30,7 @@ func getBasicParser() *Parser { }, PostProcessorsSchemas: packer.MapOfPostProcessor{ "amazon-import": func() (packer.PostProcessor, error) { return &MockPostProcessor{}, nil }, + "manifest": func() (packer.PostProcessor, error) { return &MockPostProcessor{}, nil }, }, } } diff --git a/hcl2template/testdata/build/post-processor_onlyexcept.pkr.hcl b/hcl2template/testdata/build/post-processor_onlyexcept.pkr.hcl new file mode 100644 index 000000000..bb02c563c --- /dev/null +++ b/hcl2template/testdata/build/post-processor_onlyexcept.pkr.hcl @@ -0,0 +1,21 @@ + +// starts resources to provision them. +build { + sources = [ + "source.virtualbox-iso.ubuntu-1204", + "source.amazon-ebs.ubuntu-1604", + ] + + post-processor "amazon-import" { + only = ["virtualbox-iso.ubuntu-1204"] + } + post-processor "manifest" { + except = ["virtualbox-iso.ubuntu-1204"] + } +} + +source "virtualbox-iso" "ubuntu-1204" { +} + +source "amazon-ebs" "ubuntu-1604" { +} diff --git a/hcl2template/testdata/build/provisioner_onlyexcept.pkr.hcl b/hcl2template/testdata/build/provisioner_onlyexcept.pkr.hcl new file mode 100644 index 000000000..5befb1a26 --- /dev/null +++ b/hcl2template/testdata/build/provisioner_onlyexcept.pkr.hcl @@ -0,0 +1,21 @@ + +// starts resources to provision them. +build { + sources = [ + "source.virtualbox-iso.ubuntu-1204", + "source.amazon-ebs.ubuntu-1604", + ] + + provisioner "shell" { + only = ["virtualbox-iso.ubuntu-1204"] + } + provisioner "file" { + except = ["virtualbox-iso.ubuntu-1204"] + } +} + +source "virtualbox-iso" "ubuntu-1204" { +} + +source "amazon-ebs" "ubuntu-1604" { +} diff --git a/hcl2template/types.build.post-processor.go b/hcl2template/types.build.post-processor.go index bedbb1ad0..721a4def9 100644 --- a/hcl2template/types.build.post-processor.go +++ b/hcl2template/types.build.post-processor.go @@ -10,8 +10,9 @@ import ( // ProvisionerBlock references a detected but unparsed post processor type PostProcessorBlock struct { - PType string - PName string + PType string + PName string + OnlyExcept OnlyExcept HCL2Ref } @@ -22,8 +23,10 @@ func (p *PostProcessorBlock) String() string { func (p *Parser) decodePostProcessor(block *hcl.Block) (*PostProcessorBlock, hcl.Diagnostics) { var b struct { - Name string `hcl:"name,optional"` - Rest hcl.Body `hcl:",remain"` + Name string `hcl:"name,optional"` + Only []string `hcl:"only,optional"` + Except []string `hcl:"except,optional"` + Rest hcl.Body `hcl:",remain"` } diags := gohcl.DecodeBody(block.Body, nil, &b) if diags.HasErrors() { @@ -31,9 +34,15 @@ func (p *Parser) decodePostProcessor(block *hcl.Block) (*PostProcessorBlock, hcl } postProcessor := &PostProcessorBlock{ - PType: block.Labels[0], - PName: b.Name, - HCL2Ref: newHCL2Ref(block, b.Rest), + PType: block.Labels[0], + PName: b.Name, + OnlyExcept: OnlyExcept{Only: b.Only, Except: b.Except}, + HCL2Ref: newHCL2Ref(block, b.Rest), + } + + diags = diags.Extend(postProcessor.OnlyExcept.Validate()) + if diags.HasErrors() { + return nil, diags } if !p.PostProcessorsSchemas.Has(postProcessor.PType) { diff --git a/hcl2template/types.build.provisioners.go b/hcl2template/types.build.provisioners.go index c41ba2eb4..70756f39e 100644 --- a/hcl2template/types.build.provisioners.go +++ b/hcl2template/types.build.provisioners.go @@ -9,6 +9,52 @@ import ( "github.com/hashicorp/packer/packer" ) +// OnlyExcept is a struct that is meant to be embedded that contains the +// logic required for "only" and "except" meta-parameters. +type OnlyExcept struct { + Only []string `json:"only,omitempty"` + Except []string `json:"except,omitempty"` +} + +// Skip says whether or not to skip the build with the given name. +func (o *OnlyExcept) Skip(n string) bool { + if len(o.Only) > 0 { + for _, v := range o.Only { + if v == n { + return false + } + } + + return true + } + + if len(o.Except) > 0 { + for _, v := range o.Except { + if v == n { + return true + } + } + + return false + } + + return false +} + +// Validate validates that the OnlyExcept settings are correct for a thing. +func (o *OnlyExcept) Validate() hcl.Diagnostics { + var diags hcl.Diagnostics + + if len(o.Only) > 0 && len(o.Except) > 0 { + diags = diags.Append(&hcl.Diagnostic{ + Summary: "only one of 'only' or 'except' may be specified", + Severity: hcl.DiagError, + }) + } + + return diags +} + // ProvisionerBlock references a detected but unparsed provisioner type ProvisionerBlock struct { PType string @@ -16,6 +62,7 @@ type ProvisionerBlock struct { PauseBefore time.Duration MaxRetries int Timeout time.Duration + OnlyExcept OnlyExcept HCL2Ref } @@ -29,6 +76,8 @@ func (p *Parser) decodeProvisioner(block *hcl.Block) (*ProvisionerBlock, hcl.Dia PauseBefore string `hcl:"pause_before,optional"` MaxRetries int `hcl:"max_retries,optional"` Timeout string `hcl:"timeout,optional"` + Only []string `hcl:"only,optional"` + Except []string `hcl:"except,optional"` Rest hcl.Body `hcl:",remain"` } diags := gohcl.DecodeBody(block.Body, nil, &b) @@ -40,9 +89,15 @@ func (p *Parser) decodeProvisioner(block *hcl.Block) (*ProvisionerBlock, hcl.Dia PType: block.Labels[0], PName: b.Name, MaxRetries: b.MaxRetries, + OnlyExcept: OnlyExcept{Only: b.Only, Except: b.Except}, HCL2Ref: newHCL2Ref(block, b.Rest), } + diags = diags.Extend(provisioner.OnlyExcept.Validate()) + if diags.HasErrors() { + return nil, diags + } + if b.PauseBefore != "" { pauseBefore, err := time.ParseDuration(b.PauseBefore) if err != nil { diff --git a/hcl2template/types.build_test.go b/hcl2template/types.build_test.go index 9b3c71161..38d0c4806 100644 --- a/hcl2template/types.build_test.go +++ b/hcl2template/types.build_test.go @@ -4,6 +4,7 @@ import ( "path/filepath" "testing" + . "github.com/hashicorp/packer/hcl2template/internal" "github.com/hashicorp/packer/packer" ) @@ -122,6 +123,139 @@ func TestParse_build(t *testing.T) { []packer.Build{}, true, }, + {"post-processor with only and except", + defaultParser, + parseTestArgs{"testdata/build/post-processor_onlyexcept.pkr.hcl", nil, nil}, + &PackerConfig{ + Basedir: filepath.Join("testdata", "build"), + Sources: map[SourceRef]SourceBlock{ + refVBIsoUbuntu1204: {Type: "virtualbox-iso", Name: "ubuntu-1204"}, + refAWSEBSUbuntu1604: {Type: "amazon-ebs", Name: "ubuntu-1604"}, + }, + Builds: Builds{ + &BuildBlock{ + Sources: []SourceRef{refVBIsoUbuntu1204, refAWSEBSUbuntu1604}, + ProvisionerBlocks: nil, + PostProcessors: []*PostProcessorBlock{ + { + PType: "amazon-import", + OnlyExcept: OnlyExcept{Only: []string{"virtualbox-iso.ubuntu-1204"}, Except: nil}, + }, + { + PType: "manifest", + OnlyExcept: OnlyExcept{Only: nil, Except: []string{"virtualbox-iso.ubuntu-1204"}}, + }, + }, + }, + }, + }, + false, false, + []packer.Build{ + &packer.CoreBuild{ + Type: "virtualbox-iso.ubuntu-1204", + Prepared: true, + Builder: emptyMockBuilder, + Provisioners: []packer.CoreBuildProvisioner{}, + PostProcessors: [][]packer.CoreBuildPostProcessor{ + { + { + PType: "amazon-import", + PostProcessor: &MockPostProcessor{ + Config: MockConfig{ + NestedMockConfig: NestedMockConfig{Tags: []MockTag{}}, + NestedSlice: []NestedMockConfig{}, + }, + }, + }, + }, + }, + }, + &packer.CoreBuild{ + Type: "amazon-ebs.ubuntu-1604", + Prepared: true, + Builder: emptyMockBuilder, + Provisioners: []packer.CoreBuildProvisioner{}, + PostProcessors: [][]packer.CoreBuildPostProcessor{ + { + { + PType: "manifest", + PostProcessor: &MockPostProcessor{ + Config: MockConfig{ + NestedMockConfig: NestedMockConfig{Tags: []MockTag{}}, + NestedSlice: []NestedMockConfig{}, + }, + }, + }, + }, + }, + }, + }, + false, + }, + {"provisioner with only and except", + defaultParser, + parseTestArgs{"testdata/build/provisioner_onlyexcept.pkr.hcl", nil, nil}, + &PackerConfig{ + Basedir: filepath.Join("testdata", "build"), + Sources: map[SourceRef]SourceBlock{ + refVBIsoUbuntu1204: {Type: "virtualbox-iso", Name: "ubuntu-1204"}, + refAWSEBSUbuntu1604: {Type: "amazon-ebs", Name: "ubuntu-1604"}, + }, + Builds: Builds{ + &BuildBlock{ + Sources: []SourceRef{refVBIsoUbuntu1204, refAWSEBSUbuntu1604}, + ProvisionerBlocks: []*ProvisionerBlock{ + { + PType: "shell", + OnlyExcept: OnlyExcept{Only: []string{"virtualbox-iso.ubuntu-1204"}, Except: nil}, + }, + { + PType: "file", + OnlyExcept: OnlyExcept{Only: nil, Except: []string{"virtualbox-iso.ubuntu-1204"}}, + }, + }, + }, + }, + }, + false, false, + []packer.Build{ + &packer.CoreBuild{ + Type: "virtualbox-iso.ubuntu-1204", + Prepared: true, + Builder: emptyMockBuilder, + Provisioners: []packer.CoreBuildProvisioner{ + { + PType: "shell", + Provisioner: &MockProvisioner{ + Config: MockConfig{ + NestedMockConfig: NestedMockConfig{Tags: []MockTag{}}, + NestedSlice: []NestedMockConfig{}, + }, + }, + }, + }, + PostProcessors: [][]packer.CoreBuildPostProcessor{}, + }, + &packer.CoreBuild{ + Type: "amazon-ebs.ubuntu-1604", + Prepared: true, + Builder: emptyMockBuilder, + Provisioners: []packer.CoreBuildProvisioner{ + { + PType: "file", + Provisioner: &MockProvisioner{ + Config: MockConfig{ + NestedMockConfig: NestedMockConfig{Tags: []MockTag{}}, + NestedSlice: []NestedMockConfig{}, + }, + }, + }, + }, + PostProcessors: [][]packer.CoreBuildPostProcessor{}, + }, + }, + false, + }, } testParse(t, tests) } diff --git a/hcl2template/types.packer_config.go b/hcl2template/types.packer_config.go index 81eb4bad8..55954401f 100644 --- a/hcl2template/types.packer_config.go +++ b/hcl2template/types.packer_config.go @@ -198,6 +198,9 @@ func (cfg *PackerConfig) getCoreBuildProvisioners(source SourceBlock, blocks []* var diags hcl.Diagnostics res := []packer.CoreBuildProvisioner{} for _, pb := range blocks { + if pb.OnlyExcept.Skip(source.Type + "." + source.Name) { + continue + } provisioner, moreDiags := cfg.startProvisioner(source, pb, ectx, generatedVars) diags = append(diags, moreDiags...) if moreDiags.HasErrors() { @@ -238,6 +241,9 @@ func (cfg *PackerConfig) getCoreBuildPostProcessors(source SourceBlock, blocks [ var diags hcl.Diagnostics res := []packer.CoreBuildPostProcessor{} for _, ppb := range blocks { + if ppb.OnlyExcept.Skip(source.Type + "." + source.Name) { + continue + } postProcessor, moreDiags := cfg.startPostProcessor(source, ppb, ectx, generatedVars) diags = append(diags, moreDiags...) if moreDiags.HasErrors() { diff --git a/hcl2template/types.packer_config_test.go b/hcl2template/types.packer_config_test.go index e3675d478..74f688bb6 100644 --- a/hcl2template/types.packer_config_test.go +++ b/hcl2template/types.packer_config_test.go @@ -10,7 +10,7 @@ import ( var ( refVBIsoUbuntu1204 = SourceRef{Type: "virtualbox-iso", Name: "ubuntu-1204"} - refAWSEBSUbuntu1204 = SourceRef{Type: "amazon-ebs", Name: "ubuntu-1604"} + refAWSEBSUbuntu1604 = SourceRef{Type: "amazon-ebs", Name: "ubuntu-1604"} ) func TestParser_complete(t *testing.T) { @@ -73,13 +73,13 @@ func TestParser_complete(t *testing.T) { }, Sources: map[SourceRef]SourceBlock{ refVBIsoUbuntu1204: {Type: "virtualbox-iso", Name: "ubuntu-1204"}, - refAWSEBSUbuntu1204: {Type: "amazon-ebs", Name: "ubuntu-1604"}, + refAWSEBSUbuntu1604: {Type: "amazon-ebs", Name: "ubuntu-1604"}, }, Builds: Builds{ &BuildBlock{ Sources: []SourceRef{ refVBIsoUbuntu1204, - refAWSEBSUbuntu1204, + refAWSEBSUbuntu1604, }, ProvisionerBlocks: []*ProvisionerBlock{ {