packer: `only` metaparameter for post-processors [GH-438]
This commit is contained in:
parent
28d25c718c
commit
d5555d45cc
|
@ -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
|
||||||
|
}
|
||||||
|
|
|
@ -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 := `
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue