File: step_create_template.go

package info (click to toggle)
packer 1.3.4%2Bdfsg-4
  • links: PTS, VCS
  • area: main
  • in suites: buster
  • size: 8,324 kB
  • sloc: python: 619; sh: 557; makefile: 111
file content (112 lines) | stat: -rw-r--r-- 3,314 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
package cloudstack

import (
	"context"
	"fmt"
	"time"

	"github.com/hashicorp/packer/helper/multistep"
	"github.com/hashicorp/packer/packer"
	"github.com/xanzy/go-cloudstack/cloudstack"
)

type stepCreateTemplate struct{}

func (s *stepCreateTemplate) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
	client := state.Get("client").(*cloudstack.CloudStackClient)
	config := state.Get("config").(*Config)
	ui := state.Get("ui").(packer.Ui)

	ui.Say(fmt.Sprintf("Creating template: %s", config.TemplateName))

	// Retrieve the instance ID from the previously saved state.
	instanceID, ok := state.Get("instance_id").(string)
	if !ok || instanceID == "" {
		err := fmt.Errorf("Could not retrieve instance_id from state!")
		state.Put("error", err)
		ui.Error(err.Error())
		return multistep.ActionHalt
	}

	// Create a new parameter struct.
	p := client.Template.NewCreateTemplateParams(
		config.TemplateDisplayText,
		config.TemplateName,
		config.TemplateOS,
	)

	// Configure the template according to the supplied config.
	p.SetIsfeatured(config.TemplateFeatured)
	p.SetIspublic(config.TemplatePublic)
	p.SetIsdynamicallyscalable(config.TemplateScalable)
	p.SetPasswordenabled(config.TemplatePasswordEnabled)
	p.SetRequireshvm(config.TemplateRequiresHVM)

	if config.Project != "" {
		p.SetProjectid(config.Project)
	}

	if config.TemplateTag != "" {
		p.SetTemplatetag(config.TemplateTag)
	}

	ui.Message("Retrieving the ROOT volume ID...")
	volumeID, err := getRootVolumeID(client, instanceID, config.Project)
	if err != nil {
		state.Put("error", err)
		ui.Error(err.Error())
		return multistep.ActionHalt
	}

	// Set the volume ID from which to create the template.
	p.SetVolumeid(volumeID)

	ui.Message("Creating the new template...")
	template, err := client.Template.CreateTemplate(p)
	if err != nil {
		err := fmt.Errorf("Error creating the new template %s: %s", config.TemplateName, err)
		state.Put("error", err)
		ui.Error(err.Error())
		return multistep.ActionHalt
	}

	// This is kind of nasty, but it appears to be needed to prevent corrupt templates.
	// When CloudStack says the template creation is done and you then delete the source
	// volume shortly after, it seems to corrupt the newly created template. Giving it an
	// additional 30 seconds to really finish up, seem to prevent that from happening.
	time.Sleep(30 * time.Second)

	ui.Message("Template has been created!")

	// Store the template.
	state.Put("template", template)

	return multistep.ActionContinue
}

// Cleanup any resources that may have been created during the Run phase.
func (s *stepCreateTemplate) Cleanup(state multistep.StateBag) {
	// Nothing to cleanup for this step.
}

func getRootVolumeID(client *cloudstack.CloudStackClient, instanceID, projectID string) (string, error) {
	// Retrieve the virtual machine object.
	p := client.Volume.NewListVolumesParams()

	// Set the type and virtual machine ID
	p.SetType("ROOT")
	p.SetVirtualmachineid(instanceID)
	if projectID != "" {
		p.SetProjectid(projectID)
	}

	volumes, err := client.Volume.ListVolumes(p)
	if err != nil {
		return "", fmt.Errorf("Failed to retrieve ROOT volume: %s", err)
	}
	if volumes.Count != 1 {
		return "", fmt.Errorf("Could not find ROOT disk of instance %s", instanceID)
	}

	return volumes.Volumes[0].Id, nil
}