-
Notifications
You must be signed in to change notification settings - Fork 3.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Sander van Harmelen
committed
Oct 16, 2016
1 parent
ba00afd
commit dbf3bf5
Showing
17 changed files
with
1,539 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
package cloudstack | ||
|
||
import ( | ||
"fmt" | ||
"log" | ||
"strings" | ||
|
||
"github.com/xanzy/go-cloudstack/cloudstack" | ||
) | ||
|
||
// Artifact represents a CloudStack template as the result of a Packer build. | ||
type Artifact struct { | ||
client *cloudstack.CloudStackClient | ||
config *Config | ||
template *cloudstack.CreateTemplateResponse | ||
} | ||
|
||
// BuilderId returns the builder ID. | ||
func (a *Artifact) BuilderId() string { | ||
return BuilderId | ||
} | ||
|
||
// Destroy the CloudStack template represented by the artifact. | ||
func (a *Artifact) Destroy() error { | ||
// Create a new parameter struct. | ||
p := a.client.Template.NewDeleteTemplateParams(a.template.Id) | ||
|
||
// Destroy the template. | ||
log.Printf("Destroying template: %s", a.template.Name) | ||
_, err := a.client.Template.DeleteTemplate(p) | ||
if err != nil { | ||
// This is a very poor way to be told the ID does no longer exist :( | ||
if strings.Contains(err.Error(), fmt.Sprintf( | ||
"Invalid parameter id value=%s due to incorrect long value format, "+ | ||
"or entity does not exist", a.template.Id)) { | ||
return nil | ||
} | ||
|
||
return fmt.Errorf("Error destroying template %s: %s", a.template.Name, err) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// Files returns the files represented by the artifact. | ||
func (a *Artifact) Files() []string { | ||
// We have no files. | ||
return nil | ||
} | ||
|
||
// Id returns CloudStack template ID. | ||
func (a *Artifact) Id() string { | ||
return a.template.Id | ||
} | ||
|
||
// String returns the string representation of the artifact. | ||
func (a *Artifact) String() string { | ||
return fmt.Sprintf("A template was created: %s", a.template.Name) | ||
} | ||
|
||
// State returns specific details from the artifact. | ||
func (a *Artifact) State(name string) interface{} { | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
package cloudstack | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/mitchellh/packer/packer" | ||
"github.com/xanzy/go-cloudstack/cloudstack" | ||
) | ||
|
||
const templateID = "286dd44a-ec6b-4789-b192-804f08f04b4c" | ||
|
||
func TestArtifact_Impl(t *testing.T) { | ||
var raw interface{} = &Artifact{} | ||
|
||
if _, ok := raw.(packer.Artifact); !ok { | ||
t.Fatalf("Artifact does not implement packer.Artifact") | ||
} | ||
} | ||
|
||
func TestArtifactId(t *testing.T) { | ||
a := &Artifact{ | ||
client: nil, | ||
config: nil, | ||
template: &cloudstack.CreateTemplateResponse{ | ||
Id: "286dd44a-ec6b-4789-b192-804f08f04b4c", | ||
}, | ||
} | ||
|
||
if a.Id() != templateID { | ||
t.Fatalf("artifact ID should match: %s", templateID) | ||
} | ||
} | ||
|
||
func TestArtifactString(t *testing.T) { | ||
a := &Artifact{ | ||
client: nil, | ||
config: nil, | ||
template: &cloudstack.CreateTemplateResponse{ | ||
Name: "packer-foobar", | ||
}, | ||
} | ||
expected := "A template was created: packer-foobar" | ||
|
||
if a.String() != expected { | ||
t.Fatalf("artifact string should match: %s", expected) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
package cloudstack | ||
|
||
import ( | ||
"github.com/mitchellh/multistep" | ||
"github.com/mitchellh/packer/common" | ||
"github.com/mitchellh/packer/helper/communicator" | ||
"github.com/mitchellh/packer/packer" | ||
"github.com/xanzy/go-cloudstack/cloudstack" | ||
) | ||
|
||
const BuilderId = "packer.cloudstack" | ||
|
||
// Builder represents the CloudStack builder. | ||
type Builder struct { | ||
config *Config | ||
runner multistep.Runner | ||
ui packer.Ui | ||
} | ||
|
||
// Prepare implements the packer.Builder interface. | ||
func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { | ||
config, errs := NewConfig(raws...) | ||
if errs != nil { | ||
return nil, errs | ||
} | ||
b.config = config | ||
|
||
return nil, nil | ||
} | ||
|
||
// Run implements the packer.Builder interface. | ||
func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) { | ||
b.ui = ui | ||
|
||
// Create a CloudStack API client. | ||
client := cloudstack.NewAsyncClient( | ||
b.config.APIURL, | ||
b.config.APIKey, | ||
b.config.SecretKey, | ||
!b.config.SSLNoVerify, | ||
) | ||
|
||
// Set the time to wait before timing out | ||
client.AsyncTimeout(int64(b.config.AsyncTimeout.Seconds())) | ||
|
||
// Some CloudStack service providers only allow HTTP GET calls. | ||
client.HTTPGETOnly = b.config.HTTPGetOnly | ||
|
||
// Set up the state. | ||
state := new(multistep.BasicStateBag) | ||
state.Put("client", client) | ||
state.Put("config", b.config) | ||
state.Put("hook", hook) | ||
state.Put("ui", ui) | ||
|
||
// Build the steps. | ||
steps := []multistep.Step{ | ||
&stepPrepareConfig{}, | ||
&stepCreateInstance{}, | ||
&stepSetupNetworking{}, | ||
&communicator.StepConnect{ | ||
Config: &b.config.Comm, | ||
Host: commHost, | ||
SSHConfig: sshConfig, | ||
}, | ||
&common.StepProvision{}, | ||
&stepShutdownInstance{}, | ||
&stepCreateTemplate{}, | ||
} | ||
|
||
// Configure the runner. | ||
if b.config.PackerDebug { | ||
b.runner = &multistep.DebugRunner{ | ||
Steps: steps, | ||
PauseFn: common.MultistepDebugFn(ui), | ||
} | ||
} else { | ||
b.runner = &multistep.BasicRunner{Steps: steps} | ||
} | ||
|
||
// Run the steps. | ||
b.runner.Run(state) | ||
|
||
// If there are no templates, then just return | ||
template, ok := state.Get("template").(*cloudstack.CreateTemplateResponse) | ||
if !ok || template == nil { | ||
return nil, nil | ||
} | ||
|
||
// Build the artifact and return it | ||
artifact := &Artifact{ | ||
client: client, | ||
config: b.config, | ||
template: template, | ||
} | ||
|
||
return artifact, nil | ||
} | ||
|
||
// Cancel the step runner. | ||
func (b *Builder) Cancel() { | ||
if b.runner != nil { | ||
b.ui.Say("Cancelling the step runner...") | ||
b.runner.Cancel() | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
package cloudstack | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/mitchellh/packer/packer" | ||
) | ||
|
||
func TestBuilder_Impl(t *testing.T) { | ||
var raw interface{} = &Builder{} | ||
|
||
if _, ok := raw.(packer.Builder); !ok { | ||
t.Fatalf("Builder does not implement packer.Builder") | ||
} | ||
} | ||
|
||
func TestBuilder_Prepare(t *testing.T) { | ||
cases := map[string]struct { | ||
Config map[string]interface{} | ||
Err bool | ||
}{ | ||
"good": { | ||
Config: map[string]interface{}{ | ||
"api_url": "https://cloudstack.com/client/api", | ||
"api_key": "some-api-key", | ||
"secret_key": "some-secret-key", | ||
"cidr_list": []interface{}{"0.0.0.0/0"}, | ||
"disk_size": "20", | ||
"network": "c5ed8a14-3f21-4fa9-bd74-bb887fc0ed0d", | ||
"service_offering": "a29c52b1-a83d-4123-a57d-4548befa47a0", | ||
"source_template": "d31e6af5-94a8-4756-abf3-6493c38db7e5", | ||
"ssh_username": "ubuntu", | ||
"template_os": "52d54d24-cef1-480b-b963-527703aa4ff9", | ||
"zone": "a3b594d9-25e9-47c1-9c03-7a5fc61e3f43", | ||
}, | ||
Err: false, | ||
}, | ||
"bad": { | ||
Err: true, | ||
}, | ||
} | ||
|
||
b := &Builder{} | ||
|
||
for desc, tc := range cases { | ||
_, errs := b.Prepare(tc.Config) | ||
|
||
if tc.Err { | ||
if errs == nil { | ||
t.Fatalf("%s prepare should err", desc) | ||
} | ||
} else { | ||
if errs != nil { | ||
t.Fatalf("%s prepare should not fail: %s", desc, errs) | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.