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,7 +14,7 @@ type rawTemplate struct {
|
|||
Builders []map[string]interface{}
|
||||
Hooks map[string][]string
|
||||
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
|
||||
|
@ -22,6 +22,7 @@ type rawTemplate struct {
|
|||
type Template struct {
|
||||
Builders map[string]rawBuilderConfig
|
||||
Hooks map[string][]string
|
||||
PostProcessors [][]rawPostProcessorConfig
|
||||
Provisioners []rawProvisionerConfig
|
||||
}
|
||||
|
||||
|
@ -36,6 +37,14 @@ type rawBuilderConfig struct {
|
|||
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.
|
||||
// It contains the type of the provisioner as well as the raw configuration
|
||||
// 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.Builders = make(map[string]rawBuilderConfig)
|
||||
t.Hooks = rawTpl.Hooks
|
||||
t.PostProcessors = make([][]rawPostProcessorConfig, len(rawTpl.PostProcessors))
|
||||
t.Provisioners = make([]rawProvisionerConfig, len(rawTpl.Provisioners))
|
||||
|
||||
errors := make([]error, 0)
|
||||
|
@ -103,6 +113,43 @@ func ParseTemplate(data []byte) (t *Template, err error) {
|
|||
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
|
||||
for i, v := range rawTpl.Provisioners {
|
||||
raw := &t.Provisioners[i]
|
||||
|
@ -135,6 +182,43 @@ func ParseTemplate(data []byte) (t *Template, err error) {
|
|||
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
|
||||
// this template represents.
|
||||
func (t *Template) BuildNames() []string {
|
||||
|
|
|
@ -28,8 +28,7 @@ func TestParseTemplate_Invalid(t *testing.T) {
|
|||
// syntax error in the JSON.
|
||||
data := `
|
||||
{
|
||||
"name": "my-image",,
|
||||
"builders": []
|
||||
"builders": [],
|
||||
}
|
||||
`
|
||||
|
||||
|
@ -43,7 +42,6 @@ func TestParseTemplate_BuilderWithoutType(t *testing.T) {
|
|||
|
||||
data := `
|
||||
{
|
||||
"name": "my-image",
|
||||
"builders": [{}]
|
||||
}
|
||||
`
|
||||
|
@ -57,7 +55,6 @@ func TestParseTemplate_BuilderWithNonStringType(t *testing.T) {
|
|||
|
||||
data := `
|
||||
{
|
||||
"name": "my-image",
|
||||
"builders": [{
|
||||
"type": 42
|
||||
}]
|
||||
|
@ -73,7 +70,6 @@ func TestParseTemplate_BuilderWithoutName(t *testing.T) {
|
|||
|
||||
data := `
|
||||
{
|
||||
"name": "my-image",
|
||||
"builders": [
|
||||
{
|
||||
"type": "amazon-ebs"
|
||||
|
@ -97,7 +93,6 @@ func TestParseTemplate_BuilderWithName(t *testing.T) {
|
|||
|
||||
data := `
|
||||
{
|
||||
"name": "my-image",
|
||||
"builders": [
|
||||
{
|
||||
"name": "bob",
|
||||
|
@ -122,7 +117,6 @@ func TestParseTemplate_BuilderWithConflictingName(t *testing.T) {
|
|||
|
||||
data := `
|
||||
{
|
||||
"name": "my-image",
|
||||
"builders": [
|
||||
{
|
||||
"name": "bob",
|
||||
|
@ -145,7 +139,6 @@ func TestParseTemplate_Hooks(t *testing.T) {
|
|||
|
||||
data := `
|
||||
{
|
||||
"name": "my-image",
|
||||
|
||||
"hooks": {
|
||||
"event": ["foo", "bar"]
|
||||
|
@ -163,12 +156,65 @@ func TestParseTemplate_Hooks(t *testing.T) {
|
|||
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) {
|
||||
assert := asserts.NewTestingAsserts(t, true)
|
||||
|
||||
data := `
|
||||
{
|
||||
"name": "my-image",
|
||||
"provisioners": [{}]
|
||||
}
|
||||
`
|
||||
|
@ -182,7 +228,6 @@ func TestParseTemplate_ProvisionerWithNonStringType(t *testing.T) {
|
|||
|
||||
data := `
|
||||
{
|
||||
"name": "my-image",
|
||||
"provisioners": [{
|
||||
"type": 42
|
||||
}]
|
||||
|
@ -198,7 +243,6 @@ func TestParseTemplate_Provisioners(t *testing.T) {
|
|||
|
||||
data := `
|
||||
{
|
||||
"name": "my-image",
|
||||
"provisioners": [
|
||||
{
|
||||
"type": "shell"
|
||||
|
@ -220,7 +264,6 @@ func TestTemplate_BuildNames(t *testing.T) {
|
|||
|
||||
data := `
|
||||
{
|
||||
"name": "my-image",
|
||||
"builders": [
|
||||
{
|
||||
"name": "bob",
|
||||
|
@ -247,7 +290,6 @@ func TestTemplate_BuildUnknown(t *testing.T) {
|
|||
|
||||
data := `
|
||||
{
|
||||
"name": "my-image",
|
||||
"builders": [
|
||||
{
|
||||
"name": "test1",
|
||||
|
@ -270,7 +312,6 @@ func TestTemplate_BuildUnknownBuilder(t *testing.T) {
|
|||
|
||||
data := `
|
||||
{
|
||||
"name": "my-image",
|
||||
"builders": [
|
||||
{
|
||||
"name": "test1",
|
||||
|
@ -295,7 +336,6 @@ func TestTemplate_Build_NilBuilderFunc(t *testing.T) {
|
|||
|
||||
data := `
|
||||
{
|
||||
"name": "my-image",
|
||||
"builders": [
|
||||
{
|
||||
"name": "test1",
|
||||
|
@ -331,7 +371,6 @@ func TestTemplate_Build_NilProvisionerFunc(t *testing.T) {
|
|||
|
||||
data := `
|
||||
{
|
||||
"name": "my-image",
|
||||
"builders": [
|
||||
{
|
||||
"name": "test1",
|
||||
|
@ -369,7 +408,6 @@ func TestTemplate_Build_NilProvisionerFunc_WithNoProvisioners(t *testing.T) {
|
|||
|
||||
data := `
|
||||
{
|
||||
"name": "my-image",
|
||||
"builders": [
|
||||
{
|
||||
"name": "test1",
|
||||
|
@ -394,7 +432,6 @@ func TestTemplate_Build(t *testing.T) {
|
|||
|
||||
data := `
|
||||
{
|
||||
"name": "my-image",
|
||||
"builders": [
|
||||
{
|
||||
"name": "test1",
|
||||
|
@ -452,7 +489,6 @@ func TestTemplate_Build_ProvisionerOverride(t *testing.T) {
|
|||
|
||||
data := `
|
||||
{
|
||||
"name": "my-image",
|
||||
"builders": [
|
||||
{
|
||||
"name": "test1",
|
||||
|
|
Loading…
Reference in New Issue