packer: `only` metaparameter for post-processors [GH-438]

This commit is contained in:
Mitchell Hashimoto 2013-09-20 11:13:43 -07:00
parent 28d25c718c
commit d5555d45cc
2 changed files with 184 additions and 36 deletions

View File

@ -48,6 +48,8 @@ type RawBuilderConfig struct {
// configuration. It contains the type of the post processor as well as the // configuration. It contains the type of the post processor as well as the
// raw configuration that is handed to the post-processor for it to process. // raw configuration that is handed to the post-processor for it to process.
type RawPostProcessorConfig struct { type RawPostProcessorConfig struct {
TemplateOnlyExcept `mapstructure:",squash"`
Type string Type string
KeepInputArtifact bool `mapstructure:"keep_input_artifact"` KeepInputArtifact bool `mapstructure:"keep_input_artifact"`
RawConfig map[string]interface{} RawConfig map[string]interface{}
@ -57,9 +59,10 @@ type RawPostProcessorConfig struct {
// It contains the type of the provisioner as well as the raw configuration // It contains the type of the provisioner as well as the raw configuration
// that is handed to the provisioner for it to process. // that is handed to the provisioner for it to process.
type RawProvisionerConfig struct { type RawProvisionerConfig struct {
TemplateOnlyExcept `mapstructure:",squash"`
Type string Type string
Override map[string]interface{} Override map[string]interface{}
Only []string
RawConfig interface{} RawConfig interface{}
} }
@ -190,32 +193,50 @@ func ParseTemplate(data []byte) (t *Template, err error) {
continue continue
} }
t.PostProcessors[i] = make([]RawPostProcessorConfig, len(rawPP)) configs := make([]RawPostProcessorConfig, 0, len(rawPP))
configs := t.PostProcessors[i]
for j, pp := range rawPP { for j, pp := range rawPP {
config := &configs[j] var config RawPostProcessorConfig
if err := mapstructure.Decode(pp, config); err != nil { if err := mapstructure.Decode(pp, &config); err != nil {
if merr, ok := err.(*mapstructure.Error); ok { if merr, ok := err.(*mapstructure.Error); ok {
for _, err := range merr.Errors { for _, err := range merr.Errors {
errors = append(errors, fmt.Errorf("Post-processor #%d.%d: %s", i+1, j+1, err)) errors = append(errors,
fmt.Errorf("Post-processor #%d.%d: %s", i+1, j+1, err))
} }
} else { } else {
errors = append(errors, fmt.Errorf("Post-processor %d.%d: %s", i+1, j+1, err)) errors = append(errors,
fmt.Errorf("Post-processor %d.%d: %s", i+1, j+1, err))
} }
continue continue
} }
if config.Type == "" { if config.Type == "" {
errors = append(errors, fmt.Errorf("Post-processor %d.%d: missing 'type'", i+1, j+1)) errors = append(errors,
fmt.Errorf("Post-processor %d.%d: missing 'type'", i+1, j+1))
continue continue
} }
// Remove the input keep_input_artifact option // Remove the input keep_input_artifact option
config.TemplateOnlyExcept.Prune(pp)
delete(pp, "keep_input_artifact") delete(pp, "keep_input_artifact")
// Verify that the only settings are good
if errs := config.TemplateOnlyExcept.Validate(t.Builders); len(errs) > 0 {
for _, err := range errs {
errors = append(errors,
fmt.Errorf("Post-processor %d.%d: %s", i+1, j+1, err))
}
continue
}
config.RawConfig = pp config.RawConfig = pp
// Add it to the list of configs
configs = append(configs, config)
} }
t.PostProcessors[i] = configs
} }
// Gather all the provisioners // Gather all the provisioners
@ -239,7 +260,7 @@ func ParseTemplate(data []byte) (t *Template, err error) {
} }
// Delete the keys that we used // Delete the keys that we used
delete(v, "only") raw.TemplateOnlyExcept.Prune(v)
delete(v, "override") delete(v, "override")
// Verify that the override keys exist... // Verify that the override keys exist...
@ -251,14 +272,8 @@ func ParseTemplate(data []byte) (t *Template, err error) {
} }
// Verify that the only settings are good // Verify that the only settings are good
if len(raw.Only) > 0 { if errs := raw.TemplateOnlyExcept.Validate(t.Builders); len(errs) > 0 {
for _, n := range raw.Only { errors = append(errors, errs...)
if _, ok := t.Builders[n]; !ok {
errors = append(errors,
fmt.Errorf("provisioner %d: 'only' specified builder '%s' not found",
i+1, n))
}
}
} }
raw.RawConfig = v raw.RawConfig = v
@ -411,8 +426,12 @@ func (t *Template) Build(name string, components *ComponentFinder) (b Build, err
// Prepare the post-processors // Prepare the post-processors
postProcessors := make([][]coreBuildPostProcessor, 0, len(t.PostProcessors)) postProcessors := make([][]coreBuildPostProcessor, 0, len(t.PostProcessors))
for _, rawPPs := range t.PostProcessors { for _, rawPPs := range t.PostProcessors {
current := make([]coreBuildPostProcessor, len(rawPPs)) current := make([]coreBuildPostProcessor, 0, len(rawPPs))
for i, rawPP := range rawPPs { for _, rawPP := range rawPPs {
if rawPP.TemplateOnlyExcept.Skip(name) {
continue
}
pp, err := components.PostProcessor(rawPP.Type) pp, err := components.PostProcessor(rawPP.Type)
if err != nil { if err != nil {
return nil, err return nil, err
@ -422,12 +441,18 @@ func (t *Template) Build(name string, components *ComponentFinder) (b Build, err
return nil, fmt.Errorf("PostProcessor type not found: %s", rawPP.Type) return nil, fmt.Errorf("PostProcessor type not found: %s", rawPP.Type)
} }
current[i] = coreBuildPostProcessor{ current = append(current, coreBuildPostProcessor{
processor: pp, processor: pp,
processorType: rawPP.Type, processorType: rawPP.Type,
config: rawPP.RawConfig, config: rawPP.RawConfig,
keepInputArtifact: rawPP.KeepInputArtifact, keepInputArtifact: rawPP.KeepInputArtifact,
} })
}
// If we have no post-processors in this chain, just continue.
// This can happen if the post-processors skip certain builds.
if len(current) == 0 {
continue
} }
postProcessors = append(postProcessors, current) postProcessors = append(postProcessors, current)
@ -436,19 +461,8 @@ func (t *Template) Build(name string, components *ComponentFinder) (b Build, err
// Prepare the provisioners // Prepare the provisioners
provisioners := make([]coreBuildProvisioner, 0, len(t.Provisioners)) provisioners := make([]coreBuildProvisioner, 0, len(t.Provisioners))
for _, rawProvisioner := range t.Provisioners { for _, rawProvisioner := range t.Provisioners {
if len(rawProvisioner.Only) > 0 { if rawProvisioner.TemplateOnlyExcept.Skip(name) {
onlyFound := false continue
for _, n := range rawProvisioner.Only {
if n == name {
onlyFound = true
break
}
}
if !onlyFound {
// Skip this provisioner
continue
}
} }
var provisioner Provisioner var provisioner Provisioner
@ -497,3 +511,50 @@ func (t *Template) Build(name string, components *ComponentFinder) (b Build, err
return return
} }
// TemplateOnlyExcept contains the logic required for "only" and "except"
// meta-parameters.
type TemplateOnlyExcept struct {
Only []string
Except []string
}
// Prune will prune out the used values from the raw map.
func (t *TemplateOnlyExcept) Prune(raw map[string]interface{}) {
delete(raw, "except")
delete(raw, "only")
}
// Skip tests if we should skip putting this item onto a build.
func (t *TemplateOnlyExcept) Skip(name string) bool {
if len(t.Only) > 0 {
onlyFound := false
for _, n := range t.Only {
if n == name {
onlyFound = true
break
}
}
if !onlyFound {
// Skip this provisioner
return true
}
}
return false
}
// Validates the only/except parameters.
func (t *TemplateOnlyExcept) Validate(b map[string]RawBuilderConfig) (e []error) {
if len(t.Only) > 0 {
for _, n := range t.Only {
if _, ok := b[n]; !ok {
e = append(e,
fmt.Errorf("'only' specified builder '%s' not found", n))
}
}
}
return
}

View File

@ -11,21 +11,28 @@ import (
func testTemplateComponentFinder() *ComponentFinder { func testTemplateComponentFinder() *ComponentFinder {
builder := testBuilder() builder := testBuilder()
pp := new(TestPostProcessor)
provisioner := &MockProvisioner{} provisioner := &MockProvisioner{}
builderMap := map[string]Builder{ builderMap := map[string]Builder{
"test-builder": builder, "test-builder": builder,
} }
ppMap := map[string]PostProcessor{
"test-pp": pp,
}
provisionerMap := map[string]Provisioner{ provisionerMap := map[string]Provisioner{
"test-prov": provisioner, "test-prov": provisioner,
} }
builderFactory := func(n string) (Builder, error) { return builderMap[n], nil } builderFactory := func(n string) (Builder, error) { return builderMap[n], nil }
ppFactory := func(n string) (PostProcessor, error) { return ppMap[n], nil }
provFactory := func(n string) (Provisioner, error) { return provisionerMap[n], nil } provFactory := func(n string) (Provisioner, error) { return provisionerMap[n], nil }
return &ComponentFinder{ return &ComponentFinder{
Builder: builderFactory, Builder: builderFactory,
Provisioner: provFactory, PostProcessor: ppFactory,
Provisioner: provFactory,
} }
} }
@ -683,6 +690,86 @@ func TestTemplate_Build(t *testing.T) {
} }
} }
func TestTemplateBuild_onlyPPInvalid(t *testing.T) {
data := `
{
"builders": [
{
"name": "test1",
"type": "test-builder"
},
{
"name": "test2",
"type": "test-builder"
}
],
"post-processors": [
{
"type": "test-pp",
"only": "test5"
}
]
}
`
_, err := ParseTemplate([]byte(data))
if err == nil {
t.Fatal("should have error")
}
}
func TestTemplateBuild_onlyPP(t *testing.T) {
data := `
{
"builders": [
{
"name": "test1",
"type": "test-builder"
},
{
"name": "test2",
"type": "test-builder"
}
],
"post-processors": [
{
"type": "test-pp",
"only": ["test2"]
}
]
}
`
template, err := ParseTemplate([]byte(data))
if err != nil {
t.Fatalf("err: %s", err)
}
// Verify test1 has no post-processors
build, err := template.Build("test1", testTemplateComponentFinder())
if err != nil {
t.Fatalf("err: %s", err)
}
cbuild := build.(*coreBuild)
if len(cbuild.postProcessors) > 0 {
t.Fatal("should have no postProcessors")
}
// Verify test2 has no post-processors
build, err = template.Build("test2", testTemplateComponentFinder())
if err != nil {
t.Fatalf("err: %s", err)
}
cbuild = build.(*coreBuild)
if len(cbuild.postProcessors) != 1 {
t.Fatalf("invalid: %d", len(cbuild.postProcessors))
}
}
func TestTemplateBuild_onlyProvInvalid(t *testing.T) { func TestTemplateBuild_onlyProvInvalid(t *testing.T) {
data := ` data := `
{ {