Enable only/except for provisioners and post-processors (#9357)

This commit is contained in:
Jeremiah Snapp 2020-06-05 05:51:23 -04:00 committed by GitHub
parent fd0764d668
commit 0d7c5dc670
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 257 additions and 10 deletions

View File

@ -30,6 +30,7 @@ func getBasicParser() *Parser {
}, },
PostProcessorsSchemas: packer.MapOfPostProcessor{ PostProcessorsSchemas: packer.MapOfPostProcessor{
"amazon-import": func() (packer.PostProcessor, error) { return &MockPostProcessor{}, nil }, "amazon-import": func() (packer.PostProcessor, error) { return &MockPostProcessor{}, nil },
"manifest": func() (packer.PostProcessor, error) { return &MockPostProcessor{}, nil },
}, },
} }
} }

View File

@ -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" {
}

View File

@ -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" {
}

View File

@ -12,6 +12,7 @@ import (
type PostProcessorBlock struct { type PostProcessorBlock struct {
PType string PType string
PName string PName string
OnlyExcept OnlyExcept
HCL2Ref HCL2Ref
} }
@ -23,6 +24,8 @@ func (p *PostProcessorBlock) String() string {
func (p *Parser) decodePostProcessor(block *hcl.Block) (*PostProcessorBlock, hcl.Diagnostics) { func (p *Parser) decodePostProcessor(block *hcl.Block) (*PostProcessorBlock, hcl.Diagnostics) {
var b struct { var b struct {
Name string `hcl:"name,optional"` Name string `hcl:"name,optional"`
Only []string `hcl:"only,optional"`
Except []string `hcl:"except,optional"`
Rest hcl.Body `hcl:",remain"` Rest hcl.Body `hcl:",remain"`
} }
diags := gohcl.DecodeBody(block.Body, nil, &b) diags := gohcl.DecodeBody(block.Body, nil, &b)
@ -33,9 +36,15 @@ func (p *Parser) decodePostProcessor(block *hcl.Block) (*PostProcessorBlock, hcl
postProcessor := &PostProcessorBlock{ postProcessor := &PostProcessorBlock{
PType: block.Labels[0], PType: block.Labels[0],
PName: b.Name, PName: b.Name,
OnlyExcept: OnlyExcept{Only: b.Only, Except: b.Except},
HCL2Ref: newHCL2Ref(block, b.Rest), HCL2Ref: newHCL2Ref(block, b.Rest),
} }
diags = diags.Extend(postProcessor.OnlyExcept.Validate())
if diags.HasErrors() {
return nil, diags
}
if !p.PostProcessorsSchemas.Has(postProcessor.PType) { if !p.PostProcessorsSchemas.Has(postProcessor.PType) {
diags = append(diags, &hcl.Diagnostic{ diags = append(diags, &hcl.Diagnostic{
Summary: fmt.Sprintf("Unknown "+buildPostProcessorLabel+" type %q", postProcessor.PType), Summary: fmt.Sprintf("Unknown "+buildPostProcessorLabel+" type %q", postProcessor.PType),

View File

@ -9,6 +9,52 @@ import (
"github.com/hashicorp/packer/packer" "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 // ProvisionerBlock references a detected but unparsed provisioner
type ProvisionerBlock struct { type ProvisionerBlock struct {
PType string PType string
@ -16,6 +62,7 @@ type ProvisionerBlock struct {
PauseBefore time.Duration PauseBefore time.Duration
MaxRetries int MaxRetries int
Timeout time.Duration Timeout time.Duration
OnlyExcept OnlyExcept
HCL2Ref HCL2Ref
} }
@ -29,6 +76,8 @@ func (p *Parser) decodeProvisioner(block *hcl.Block) (*ProvisionerBlock, hcl.Dia
PauseBefore string `hcl:"pause_before,optional"` PauseBefore string `hcl:"pause_before,optional"`
MaxRetries int `hcl:"max_retries,optional"` MaxRetries int `hcl:"max_retries,optional"`
Timeout string `hcl:"timeout,optional"` Timeout string `hcl:"timeout,optional"`
Only []string `hcl:"only,optional"`
Except []string `hcl:"except,optional"`
Rest hcl.Body `hcl:",remain"` Rest hcl.Body `hcl:",remain"`
} }
diags := gohcl.DecodeBody(block.Body, nil, &b) 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], PType: block.Labels[0],
PName: b.Name, PName: b.Name,
MaxRetries: b.MaxRetries, MaxRetries: b.MaxRetries,
OnlyExcept: OnlyExcept{Only: b.Only, Except: b.Except},
HCL2Ref: newHCL2Ref(block, b.Rest), HCL2Ref: newHCL2Ref(block, b.Rest),
} }
diags = diags.Extend(provisioner.OnlyExcept.Validate())
if diags.HasErrors() {
return nil, diags
}
if b.PauseBefore != "" { if b.PauseBefore != "" {
pauseBefore, err := time.ParseDuration(b.PauseBefore) pauseBefore, err := time.ParseDuration(b.PauseBefore)
if err != nil { if err != nil {

View File

@ -4,6 +4,7 @@ import (
"path/filepath" "path/filepath"
"testing" "testing"
. "github.com/hashicorp/packer/hcl2template/internal"
"github.com/hashicorp/packer/packer" "github.com/hashicorp/packer/packer"
) )
@ -122,6 +123,139 @@ func TestParse_build(t *testing.T) {
[]packer.Build{}, []packer.Build{},
true, 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) testParse(t, tests)
} }

View File

@ -198,6 +198,9 @@ func (cfg *PackerConfig) getCoreBuildProvisioners(source SourceBlock, blocks []*
var diags hcl.Diagnostics var diags hcl.Diagnostics
res := []packer.CoreBuildProvisioner{} res := []packer.CoreBuildProvisioner{}
for _, pb := range blocks { for _, pb := range blocks {
if pb.OnlyExcept.Skip(source.Type + "." + source.Name) {
continue
}
provisioner, moreDiags := cfg.startProvisioner(source, pb, ectx, generatedVars) provisioner, moreDiags := cfg.startProvisioner(source, pb, ectx, generatedVars)
diags = append(diags, moreDiags...) diags = append(diags, moreDiags...)
if moreDiags.HasErrors() { if moreDiags.HasErrors() {
@ -238,6 +241,9 @@ func (cfg *PackerConfig) getCoreBuildPostProcessors(source SourceBlock, blocks [
var diags hcl.Diagnostics var diags hcl.Diagnostics
res := []packer.CoreBuildPostProcessor{} res := []packer.CoreBuildPostProcessor{}
for _, ppb := range blocks { for _, ppb := range blocks {
if ppb.OnlyExcept.Skip(source.Type + "." + source.Name) {
continue
}
postProcessor, moreDiags := cfg.startPostProcessor(source, ppb, ectx, generatedVars) postProcessor, moreDiags := cfg.startPostProcessor(source, ppb, ectx, generatedVars)
diags = append(diags, moreDiags...) diags = append(diags, moreDiags...)
if moreDiags.HasErrors() { if moreDiags.HasErrors() {

View File

@ -10,7 +10,7 @@ import (
var ( var (
refVBIsoUbuntu1204 = SourceRef{Type: "virtualbox-iso", Name: "ubuntu-1204"} 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) { func TestParser_complete(t *testing.T) {
@ -73,13 +73,13 @@ func TestParser_complete(t *testing.T) {
}, },
Sources: map[SourceRef]SourceBlock{ Sources: map[SourceRef]SourceBlock{
refVBIsoUbuntu1204: {Type: "virtualbox-iso", Name: "ubuntu-1204"}, refVBIsoUbuntu1204: {Type: "virtualbox-iso", Name: "ubuntu-1204"},
refAWSEBSUbuntu1204: {Type: "amazon-ebs", Name: "ubuntu-1604"}, refAWSEBSUbuntu1604: {Type: "amazon-ebs", Name: "ubuntu-1604"},
}, },
Builds: Builds{ Builds: Builds{
&BuildBlock{ &BuildBlock{
Sources: []SourceRef{ Sources: []SourceRef{
refVBIsoUbuntu1204, refVBIsoUbuntu1204,
refAWSEBSUbuntu1204, refAWSEBSUbuntu1604,
}, },
ProvisionerBlocks: []*ProvisionerBlock{ ProvisionerBlocks: []*ProvisionerBlock{
{ {