packer: Parse post-processors in templates
This includes parsing for the simple, detailed, and sequential processors.
This commit is contained in:
parent
886ac53665
commit
7a07802eb4
|
@ -14,15 +14,16 @@ type rawTemplate struct {
|
||||||
Builders []map[string]interface{}
|
Builders []map[string]interface{}
|
||||||
Hooks map[string][]string
|
Hooks map[string][]string
|
||||||
Provisioners []map[string]interface{}
|
Provisioners []map[string]interface{}
|
||||||
PostProcessors []map[string]interface{} `json:"post-processors"`
|
PostProcessors []interface{} `json:"post-processors"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// The Template struct represents a parsed template, parsed into the most
|
// The Template struct represents a parsed template, parsed into the most
|
||||||
// completed form it can be without additional processing by the caller.
|
// completed form it can be without additional processing by the caller.
|
||||||
type Template struct {
|
type Template struct {
|
||||||
Builders map[string]rawBuilderConfig
|
Builders map[string]rawBuilderConfig
|
||||||
Hooks map[string][]string
|
Hooks map[string][]string
|
||||||
Provisioners []rawProvisionerConfig
|
PostProcessors [][]rawPostProcessorConfig
|
||||||
|
Provisioners []rawProvisionerConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
// The rawBuilderConfig struct represents a raw, unprocessed builder
|
// The rawBuilderConfig struct represents a raw, unprocessed builder
|
||||||
|
@ -36,6 +37,14 @@ type rawBuilderConfig struct {
|
||||||
rawConfig interface{}
|
rawConfig interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// rawPostProcessorConfig represents a raw, unprocessed post-processor
|
||||||
|
// 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.
|
||||||
|
type rawPostProcessorConfig struct {
|
||||||
|
Type string
|
||||||
|
rawConfig interface{}
|
||||||
|
}
|
||||||
|
|
||||||
// rawProvisionerConfig represents a raw, unprocessed provisioner configuration.
|
// rawProvisionerConfig represents a raw, unprocessed provisioner configuration.
|
||||||
// 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.
|
||||||
|
@ -61,6 +70,7 @@ func ParseTemplate(data []byte) (t *Template, err error) {
|
||||||
t = &Template{}
|
t = &Template{}
|
||||||
t.Builders = make(map[string]rawBuilderConfig)
|
t.Builders = make(map[string]rawBuilderConfig)
|
||||||
t.Hooks = rawTpl.Hooks
|
t.Hooks = rawTpl.Hooks
|
||||||
|
t.PostProcessors = make([][]rawPostProcessorConfig, len(rawTpl.PostProcessors))
|
||||||
t.Provisioners = make([]rawProvisionerConfig, len(rawTpl.Provisioners))
|
t.Provisioners = make([]rawProvisionerConfig, len(rawTpl.Provisioners))
|
||||||
|
|
||||||
errors := make([]error, 0)
|
errors := make([]error, 0)
|
||||||
|
@ -103,6 +113,43 @@ func ParseTemplate(data []byte) (t *Template, err error) {
|
||||||
t.Builders[raw.Name] = raw
|
t.Builders[raw.Name] = raw
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Gather all the post-processors. This is a complicated process since there
|
||||||
|
// are actually three different formats that the user can use to define
|
||||||
|
// a post-processor.
|
||||||
|
for i, rawV := range rawTpl.PostProcessors {
|
||||||
|
rawPP, err := parsePostProvisioner(i, rawV)
|
||||||
|
if err != nil {
|
||||||
|
errors = append(errors, err...)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
t.PostProcessors[i] = make([]rawPostProcessorConfig, len(rawPP))
|
||||||
|
configs := t.PostProcessors[i]
|
||||||
|
for j, pp := range rawPP {
|
||||||
|
var config rawPostProcessorConfig
|
||||||
|
if err := mapstructure.Decode(pp, &config); err != nil {
|
||||||
|
if merr, ok := err.(*mapstructure.Error); ok {
|
||||||
|
for _, err := range merr.Errors {
|
||||||
|
errors = append(errors, fmt.Errorf("Post-processor #%d.%d: %s", i+1, j+1, err))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
errors = append(errors, fmt.Errorf("Post-processor %d.%d: %s", i+1, j+1, err))
|
||||||
|
}
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.Type == "" {
|
||||||
|
errors = append(errors, fmt.Errorf("Post-processor %d.%d: missing 'type'", i+1, j+1))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
config.rawConfig = pp
|
||||||
|
|
||||||
|
configs[j] = config
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Gather all the provisioners
|
// Gather all the provisioners
|
||||||
for i, v := range rawTpl.Provisioners {
|
for i, v := range rawTpl.Provisioners {
|
||||||
raw := &t.Provisioners[i]
|
raw := &t.Provisioners[i]
|
||||||
|
@ -135,6 +182,43 @@ func ParseTemplate(data []byte) (t *Template, err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func parsePostProvisioner(i int, rawV interface{}) (result []map[string]interface{}, errors []error) {
|
||||||
|
switch v := rawV.(type) {
|
||||||
|
case string:
|
||||||
|
result = []map[string]interface{}{
|
||||||
|
{"type": v},
|
||||||
|
}
|
||||||
|
case map[string]interface{}:
|
||||||
|
result = []map[string]interface{}{v}
|
||||||
|
case []interface{}:
|
||||||
|
result = make([]map[string]interface{}, len(v))
|
||||||
|
errors = make([]error, 0)
|
||||||
|
for j, innerRawV := range v {
|
||||||
|
switch innerV := innerRawV.(type) {
|
||||||
|
case string:
|
||||||
|
result[j] = map[string]interface{}{"type": innerV}
|
||||||
|
case map[string]interface{}:
|
||||||
|
result[j] = innerV
|
||||||
|
case []interface{}:
|
||||||
|
errors = append(
|
||||||
|
errors,
|
||||||
|
fmt.Errorf("Post-processor %d.%d: sequences not allowed to be nested in sequences", i+1, j+1))
|
||||||
|
default:
|
||||||
|
errors = append(errors, fmt.Errorf("Post-processor %d.%d is in a bad format.", i+1, j+1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(errors) == 0 {
|
||||||
|
errors = nil
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
result = nil
|
||||||
|
errors = []error{fmt.Errorf("Post-processor %d is in a bad format.", i+1)}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// BuildNames returns a slice of the available names of builds that
|
// BuildNames returns a slice of the available names of builds that
|
||||||
// this template represents.
|
// this template represents.
|
||||||
func (t *Template) BuildNames() []string {
|
func (t *Template) BuildNames() []string {
|
||||||
|
|
|
@ -28,8 +28,7 @@ func TestParseTemplate_Invalid(t *testing.T) {
|
||||||
// syntax error in the JSON.
|
// syntax error in the JSON.
|
||||||
data := `
|
data := `
|
||||||
{
|
{
|
||||||
"name": "my-image",,
|
"builders": [],
|
||||||
"builders": []
|
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
|
@ -43,7 +42,6 @@ func TestParseTemplate_BuilderWithoutType(t *testing.T) {
|
||||||
|
|
||||||
data := `
|
data := `
|
||||||
{
|
{
|
||||||
"name": "my-image",
|
|
||||||
"builders": [{}]
|
"builders": [{}]
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
@ -57,7 +55,6 @@ func TestParseTemplate_BuilderWithNonStringType(t *testing.T) {
|
||||||
|
|
||||||
data := `
|
data := `
|
||||||
{
|
{
|
||||||
"name": "my-image",
|
|
||||||
"builders": [{
|
"builders": [{
|
||||||
"type": 42
|
"type": 42
|
||||||
}]
|
}]
|
||||||
|
@ -73,7 +70,6 @@ func TestParseTemplate_BuilderWithoutName(t *testing.T) {
|
||||||
|
|
||||||
data := `
|
data := `
|
||||||
{
|
{
|
||||||
"name": "my-image",
|
|
||||||
"builders": [
|
"builders": [
|
||||||
{
|
{
|
||||||
"type": "amazon-ebs"
|
"type": "amazon-ebs"
|
||||||
|
@ -97,7 +93,6 @@ func TestParseTemplate_BuilderWithName(t *testing.T) {
|
||||||
|
|
||||||
data := `
|
data := `
|
||||||
{
|
{
|
||||||
"name": "my-image",
|
|
||||||
"builders": [
|
"builders": [
|
||||||
{
|
{
|
||||||
"name": "bob",
|
"name": "bob",
|
||||||
|
@ -122,7 +117,6 @@ func TestParseTemplate_BuilderWithConflictingName(t *testing.T) {
|
||||||
|
|
||||||
data := `
|
data := `
|
||||||
{
|
{
|
||||||
"name": "my-image",
|
|
||||||
"builders": [
|
"builders": [
|
||||||
{
|
{
|
||||||
"name": "bob",
|
"name": "bob",
|
||||||
|
@ -145,7 +139,6 @@ func TestParseTemplate_Hooks(t *testing.T) {
|
||||||
|
|
||||||
data := `
|
data := `
|
||||||
{
|
{
|
||||||
"name": "my-image",
|
|
||||||
|
|
||||||
"hooks": {
|
"hooks": {
|
||||||
"event": ["foo", "bar"]
|
"event": ["foo", "bar"]
|
||||||
|
@ -163,12 +156,65 @@ func TestParseTemplate_Hooks(t *testing.T) {
|
||||||
assert.Equal(hooks, []string{"foo", "bar"}, "hooks should be correct")
|
assert.Equal(hooks, []string{"foo", "bar"}, "hooks should be correct")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestParseTemplate_PostProcessors(t *testing.T) {
|
||||||
|
data := `
|
||||||
|
{
|
||||||
|
"post-processors": [
|
||||||
|
"simple",
|
||||||
|
|
||||||
|
{ "type": "detailed" },
|
||||||
|
|
||||||
|
[ "foo", { "type": "bar" } ]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
tpl, err := ParseTemplate([]byte(data))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error parsing: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(tpl.PostProcessors) != 3 {
|
||||||
|
t.Fatalf("bad number of post-processors: %d", len(tpl.PostProcessors))
|
||||||
|
}
|
||||||
|
|
||||||
|
pp := tpl.PostProcessors[0]
|
||||||
|
if len(pp) != 1 {
|
||||||
|
t.Fatalf("wrong number of configs in simple: %d", len(pp))
|
||||||
|
}
|
||||||
|
|
||||||
|
if pp[0].Type != "simple" {
|
||||||
|
t.Fatalf("wrong type for simple: %s", pp[0].Type)
|
||||||
|
}
|
||||||
|
|
||||||
|
pp = tpl.PostProcessors[1]
|
||||||
|
if len(pp) != 1 {
|
||||||
|
t.Fatalf("wrong number of configs in detailed: %d", len(pp))
|
||||||
|
}
|
||||||
|
|
||||||
|
if pp[0].Type != "detailed" {
|
||||||
|
t.Fatalf("wrong type for detailed: %s", pp[0].Type)
|
||||||
|
}
|
||||||
|
|
||||||
|
pp = tpl.PostProcessors[2]
|
||||||
|
if len(pp) != 2 {
|
||||||
|
t.Fatalf("wrong number of configs for sequence: %d", len(pp))
|
||||||
|
}
|
||||||
|
|
||||||
|
if pp[0].Type != "foo" {
|
||||||
|
t.Fatalf("wrong type for sequence 0: %s", pp[0].Type)
|
||||||
|
}
|
||||||
|
|
||||||
|
if pp[1].Type != "bar" {
|
||||||
|
t.Fatalf("wrong type for sequence 1: %s", pp[1].Type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestParseTemplate_ProvisionerWithoutType(t *testing.T) {
|
func TestParseTemplate_ProvisionerWithoutType(t *testing.T) {
|
||||||
assert := asserts.NewTestingAsserts(t, true)
|
assert := asserts.NewTestingAsserts(t, true)
|
||||||
|
|
||||||
data := `
|
data := `
|
||||||
{
|
{
|
||||||
"name": "my-image",
|
|
||||||
"provisioners": [{}]
|
"provisioners": [{}]
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
@ -182,7 +228,6 @@ func TestParseTemplate_ProvisionerWithNonStringType(t *testing.T) {
|
||||||
|
|
||||||
data := `
|
data := `
|
||||||
{
|
{
|
||||||
"name": "my-image",
|
|
||||||
"provisioners": [{
|
"provisioners": [{
|
||||||
"type": 42
|
"type": 42
|
||||||
}]
|
}]
|
||||||
|
@ -198,7 +243,6 @@ func TestParseTemplate_Provisioners(t *testing.T) {
|
||||||
|
|
||||||
data := `
|
data := `
|
||||||
{
|
{
|
||||||
"name": "my-image",
|
|
||||||
"provisioners": [
|
"provisioners": [
|
||||||
{
|
{
|
||||||
"type": "shell"
|
"type": "shell"
|
||||||
|
@ -220,7 +264,6 @@ func TestTemplate_BuildNames(t *testing.T) {
|
||||||
|
|
||||||
data := `
|
data := `
|
||||||
{
|
{
|
||||||
"name": "my-image",
|
|
||||||
"builders": [
|
"builders": [
|
||||||
{
|
{
|
||||||
"name": "bob",
|
"name": "bob",
|
||||||
|
@ -247,7 +290,6 @@ func TestTemplate_BuildUnknown(t *testing.T) {
|
||||||
|
|
||||||
data := `
|
data := `
|
||||||
{
|
{
|
||||||
"name": "my-image",
|
|
||||||
"builders": [
|
"builders": [
|
||||||
{
|
{
|
||||||
"name": "test1",
|
"name": "test1",
|
||||||
|
@ -270,7 +312,6 @@ func TestTemplate_BuildUnknownBuilder(t *testing.T) {
|
||||||
|
|
||||||
data := `
|
data := `
|
||||||
{
|
{
|
||||||
"name": "my-image",
|
|
||||||
"builders": [
|
"builders": [
|
||||||
{
|
{
|
||||||
"name": "test1",
|
"name": "test1",
|
||||||
|
@ -295,7 +336,6 @@ func TestTemplate_Build_NilBuilderFunc(t *testing.T) {
|
||||||
|
|
||||||
data := `
|
data := `
|
||||||
{
|
{
|
||||||
"name": "my-image",
|
|
||||||
"builders": [
|
"builders": [
|
||||||
{
|
{
|
||||||
"name": "test1",
|
"name": "test1",
|
||||||
|
@ -331,7 +371,6 @@ func TestTemplate_Build_NilProvisionerFunc(t *testing.T) {
|
||||||
|
|
||||||
data := `
|
data := `
|
||||||
{
|
{
|
||||||
"name": "my-image",
|
|
||||||
"builders": [
|
"builders": [
|
||||||
{
|
{
|
||||||
"name": "test1",
|
"name": "test1",
|
||||||
|
@ -369,7 +408,6 @@ func TestTemplate_Build_NilProvisionerFunc_WithNoProvisioners(t *testing.T) {
|
||||||
|
|
||||||
data := `
|
data := `
|
||||||
{
|
{
|
||||||
"name": "my-image",
|
|
||||||
"builders": [
|
"builders": [
|
||||||
{
|
{
|
||||||
"name": "test1",
|
"name": "test1",
|
||||||
|
@ -394,7 +432,6 @@ func TestTemplate_Build(t *testing.T) {
|
||||||
|
|
||||||
data := `
|
data := `
|
||||||
{
|
{
|
||||||
"name": "my-image",
|
|
||||||
"builders": [
|
"builders": [
|
||||||
{
|
{
|
||||||
"name": "test1",
|
"name": "test1",
|
||||||
|
@ -452,7 +489,6 @@ func TestTemplate_Build_ProvisionerOverride(t *testing.T) {
|
||||||
|
|
||||||
data := `
|
data := `
|
||||||
{
|
{
|
||||||
"name": "my-image",
|
|
||||||
"builders": [
|
"builders": [
|
||||||
{
|
{
|
||||||
"name": "test1",
|
"name": "test1",
|
||||||
|
|
Loading…
Reference in New Issue