Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
P
packer
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Kristopher Ruzic
packer
Commits
2bcd9a30
Commit
2bcd9a30
authored
Apr 29, 2014
by
Ross Smith II
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
builder/digitalOcean: use names/slugs as well as IDs for image/region/size
parent
0e4fa696
Changes
8
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
229 additions
and
53 deletions
+229
-53
builder/digitalocean/api.go
builder/digitalocean/api.go
+133
-10
builder/digitalocean/artifact.go
builder/digitalocean/artifact.go
+0
-3
builder/digitalocean/artifact_test.go
builder/digitalocean/artifact_test.go
+1
-1
builder/digitalocean/builder.go
builder/digitalocean/builder.go
+46
-13
builder/digitalocean/builder_test.go
builder/digitalocean/builder_test.go
+27
-18
builder/digitalocean/step_create_droplet.go
builder/digitalocean/step_create_droplet.go
+1
-1
builder/digitalocean/step_snapshot.go
builder/digitalocean/step_snapshot.go
+1
-1
website/source/docs/builders/digitalocean.html.markdown
website/source/docs/builders/digitalocean.html.markdown
+20
-6
No files found.
builder/digitalocean/api.go
View file @
2bcd9a30
...
...
@@ -13,6 +13,7 @@ import (
"log"
"net/http"
"net/url"
"strconv"
"strings"
"time"
)
...
...
@@ -22,6 +23,7 @@ const DIGITALOCEAN_API_URL = "https://api.digitalocean.com"
type
Image
struct
{
Id
uint
Name
string
Slug
string
Distribution
string
}
...
...
@@ -32,12 +34,23 @@ type ImagesResp struct {
type
Region
struct
{
Id
uint
Name
string
Slug
string
}
type
RegionsResp
struct
{
Regions
[]
Region
}
type
Size
struct
{
Id
uint
Name
string
Slug
string
}
type
SizesResp
struct
{
Sizes
[]
Size
}
type
DigitalOceanClient
struct
{
// The http client for communicating
client
*
http
.
Client
...
...
@@ -90,12 +103,28 @@ func (d DigitalOceanClient) DestroyKey(id uint) error {
}
// Creates a droplet and returns it's id
func
(
d
DigitalOceanClient
)
CreateDroplet
(
name
string
,
size
uint
,
image
uint
,
region
uint
,
keyId
uint
,
privateNetworking
bool
)
(
uint
,
error
)
{
func
(
d
DigitalOceanClient
)
CreateDroplet
(
name
string
,
size
string
,
image
string
,
region
string
,
keyId
uint
,
privateNetworking
bool
)
(
uint
,
error
)
{
params
:=
url
.
Values
{}
params
.
Set
(
"name"
,
name
)
params
.
Set
(
"size_id"
,
fmt
.
Sprintf
(
"%v"
,
size
))
params
.
Set
(
"image_id"
,
fmt
.
Sprintf
(
"%v"
,
image
))
params
.
Set
(
"region_id"
,
fmt
.
Sprintf
(
"%v"
,
region
))
found_size
,
err
:=
d
.
Size
(
size
)
if
err
!=
nil
{
return
0
,
fmt
.
Errorf
(
"Invalid size or lookup failure: '%s': %s"
,
size
,
err
)
}
found_image
,
err
:=
d
.
Image
(
image
)
if
err
!=
nil
{
return
0
,
fmt
.
Errorf
(
"Invalid image or lookup failure: '%s': %s"
,
image
,
err
)
}
found_region
,
err
:=
d
.
Region
(
region
)
if
err
!=
nil
{
return
0
,
fmt
.
Errorf
(
"Invalid region or lookup failure: '%s': %s"
,
region
,
err
)
}
params
.
Set
(
"size_slug"
,
found_size
.
Slug
)
params
.
Set
(
"image_slug"
,
found_image
.
Slug
)
params
.
Set
(
"region_slug"
,
found_region
.
Slug
)
params
.
Set
(
"ssh_key_ids"
,
fmt
.
Sprintf
(
"%v"
,
keyId
))
params
.
Set
(
"private_networking"
,
fmt
.
Sprintf
(
"%v"
,
privateNetworking
))
...
...
@@ -263,6 +292,38 @@ func NewRequest(d DigitalOceanClient, path string, params url.Values) (map[strin
return
nil
,
lastErr
}
func
(
d
DigitalOceanClient
)
Image
(
slug_or_name_or_id
string
)
(
Image
,
error
)
{
images
,
err
:=
d
.
Images
()
if
err
!=
nil
{
return
Image
{},
err
}
for
_
,
image
:=
range
images
{
if
strings
.
EqualFold
(
image
.
Slug
,
slug_or_name_or_id
)
{
return
image
,
nil
}
}
for
_
,
image
:=
range
images
{
if
strings
.
EqualFold
(
image
.
Name
,
slug_or_name_or_id
)
{
return
image
,
nil
}
}
for
_
,
image
:=
range
images
{
id
,
err
:=
strconv
.
Atoi
(
slug_or_name_or_id
)
if
err
==
nil
{
if
image
.
Id
==
uint
(
id
)
{
return
image
,
nil
}
}
}
err
=
errors
.
New
(
fmt
.
Sprintf
(
"Unknown image '%v'"
,
slug_or_name_or_id
))
return
Image
{},
err
}
// Returns all available regions.
func
(
d
DigitalOceanClient
)
Regions
()
([]
Region
,
error
)
{
resp
,
err
:=
NewRequest
(
d
,
"regions"
,
url
.
Values
{})
...
...
@@ -278,19 +339,81 @@ func (d DigitalOceanClient) Regions() ([]Region, error) {
return
result
.
Regions
,
nil
}
func
(
d
DigitalOceanClient
)
Region
Name
(
region_id
uint
)
(
string
,
error
)
{
func
(
d
DigitalOceanClient
)
Region
(
slug_or_name_or_id
string
)
(
Region
,
error
)
{
regions
,
err
:=
d
.
Regions
()
if
err
!=
nil
{
return
""
,
err
return
Region
{}
,
err
}
for
_
,
region
:=
range
regions
{
if
region
.
Id
==
region_id
{
return
region
.
Name
,
nil
if
strings
.
EqualFold
(
region
.
Slug
,
slug_or_name_or_id
)
{
return
region
,
nil
}
}
for
_
,
region
:=
range
regions
{
if
strings
.
EqualFold
(
region
.
Name
,
slug_or_name_or_id
)
{
return
region
,
nil
}
}
for
_
,
region
:=
range
regions
{
id
,
err
:=
strconv
.
Atoi
(
slug_or_name_or_id
)
if
err
==
nil
{
if
region
.
Id
==
uint
(
id
)
{
return
region
,
nil
}
}
}
err
=
errors
.
New
(
fmt
.
Sprintf
(
"Unknown region '%v'"
,
slug_or_name_or_id
))
return
Region
{},
err
}
// Returns all available sizes.
func
(
d
DigitalOceanClient
)
Sizes
()
([]
Size
,
error
)
{
resp
,
err
:=
NewRequest
(
d
,
"sizes"
,
url
.
Values
{})
if
err
!=
nil
{
return
nil
,
err
}
var
result
SizesResp
if
err
:=
mapstructure
.
Decode
(
resp
,
&
result
);
err
!=
nil
{
return
nil
,
err
}
return
result
.
Sizes
,
nil
}
func
(
d
DigitalOceanClient
)
Size
(
slug_or_name_or_id
string
)
(
Size
,
error
)
{
sizes
,
err
:=
d
.
Sizes
()
if
err
!=
nil
{
return
Size
{},
err
}
for
_
,
size
:=
range
sizes
{
if
strings
.
EqualFold
(
size
.
Slug
,
slug_or_name_or_id
)
{
return
size
,
nil
}
}
for
_
,
size
:=
range
sizes
{
if
strings
.
EqualFold
(
size
.
Name
,
slug_or_name_or_id
)
{
return
size
,
nil
}
}
for
_
,
size
:=
range
sizes
{
id
,
err
:=
strconv
.
Atoi
(
slug_or_name_or_id
)
if
err
==
nil
{
if
size
.
Id
==
uint
(
id
)
{
return
size
,
nil
}
}
}
err
=
errors
.
New
(
fmt
.
Sprintf
(
"Unknown
region id %v"
,
region
_id
))
err
=
errors
.
New
(
fmt
.
Sprintf
(
"Unknown
size '%v'"
,
slug_or_name_or
_id
))
return
""
,
err
return
Size
{}
,
err
}
builder/digitalocean/artifact.go
View file @
2bcd9a30
...
...
@@ -15,9 +15,6 @@ type Artifact struct {
// The name of the region
regionName
string
// The ID of the region
regionId
uint
// The client for making API calls
client
*
DigitalOceanClient
}
...
...
builder/digitalocean/artifact_test.go
View file @
2bcd9a30
...
...
@@ -14,7 +14,7 @@ func TestArtifact_Impl(t *testing.T) {
}
func
TestArtifactString
(
t
*
testing
.
T
)
{
a
:=
&
Artifact
{
"packer-foobar"
,
42
,
"San Francisco"
,
3
,
nil
}
a
:=
&
Artifact
{
"packer-foobar"
,
42
,
"San Francisco"
,
nil
}
expected
:=
"A snapshot was created: 'packer-foobar' in region 'San Francisco'"
if
a
.
String
()
!=
expected
{
...
...
builder/digitalocean/builder.go
View file @
2bcd9a30
...
...
@@ -15,6 +15,18 @@ import (
"time"
)
// see https://api.digitalocean.com/images/?client_id=[client_id]&api_key=[api_key]
// name="Ubuntu 12.04.4 x64", id=3101045,
const
DefaultImage
=
"ubuntu-12-04-x64"
// see https://api.digitalocean.com/regions/?client_id=[client_id]&api_key=[api_key]
// name="New York", id=1
const
DefaultRegion
=
"nyc1"
// see https://api.digitalocean.com/sizes/?client_id=[client_id]&api_key=[api_key]
// name="512MB", id=66 (the smallest droplet size)
const
DefaultSize
=
"512mb"
// The unique id for the builder
const
BuilderId
=
"pearkes.digitalocean"
...
...
@@ -30,6 +42,10 @@ type config struct {
SizeID
uint
`mapstructure:"size_id"`
ImageID
uint
`mapstructure:"image_id"`
Region
string
`mapstructure:"region"`
Size
string
`mapstructure:"size"`
Image
string
`mapstructure:"image"`
PrivateNetworking
bool
`mapstructure:"private_networking"`
SnapshotName
string
`mapstructure:"snapshot_name"`
DropletName
string
`mapstructure:"droplet_name"`
...
...
@@ -78,19 +94,28 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
b
.
config
.
ClientID
=
os
.
Getenv
(
"DIGITALOCEAN_CLIENT_ID"
)
}
if
b
.
config
.
RegionID
==
0
{
// Default to Region "New York"
b
.
config
.
RegionID
=
1
if
b
.
config
.
Region
==
""
{
if
b
.
config
.
RegionID
!=
0
{
b
.
config
.
Region
=
fmt
.
Sprintf
(
"%v"
,
b
.
config
.
RegionID
)
}
else
{
b
.
config
.
Region
=
DefaultRegion
}
}
if
b
.
config
.
SizeID
==
0
{
// Default to 512mb, the smallest droplet size
b
.
config
.
SizeID
=
66
if
b
.
config
.
Size
==
""
{
if
b
.
config
.
SizeID
!=
0
{
b
.
config
.
Size
=
fmt
.
Sprintf
(
"%v"
,
b
.
config
.
SizeID
)
}
else
{
b
.
config
.
Size
=
DefaultSize
}
}
if
b
.
config
.
ImageID
==
0
{
// Default to base image "Ubuntu 12.04.4 x64 (id: 3101045)"
b
.
config
.
ImageID
=
3101045
if
b
.
config
.
Image
==
""
{
if
b
.
config
.
ImageID
!=
0
{
b
.
config
.
Image
=
fmt
.
Sprintf
(
"%v"
,
b
.
config
.
ImageID
)
}
else
{
b
.
config
.
Image
=
DefaultImage
}
}
if
b
.
config
.
SnapshotName
==
""
{
...
...
@@ -226,9 +251,18 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
return
nil
,
nil
}
region_id
:=
state
.
Get
(
"region_id"
)
.
(
uint
)
sregion
:=
state
.
Get
(
"region"
)
var
region
string
if
sregion
!=
nil
{
region
=
sregion
.
(
string
)
}
else
{
region
=
fmt
.
Sprintf
(
"%v"
,
state
.
Get
(
"region_id"
)
.
(
uint
))
}
found_region
,
err
:=
client
.
Region
(
region
)
regionName
,
err
:=
client
.
RegionName
(
region_id
)
if
err
!=
nil
{
return
nil
,
err
}
...
...
@@ -236,8 +270,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
artifact
:=
&
Artifact
{
snapshotName
:
state
.
Get
(
"snapshot_name"
)
.
(
string
),
snapshotId
:
state
.
Get
(
"snapshot_image_id"
)
.
(
uint
),
regionId
:
region_id
,
regionName
:
regionName
,
regionName
:
found_region
.
Name
,
client
:
client
,
}
...
...
builder/digitalocean/builder_test.go
View file @
2bcd9a30
...
...
@@ -142,7 +142,7 @@ func TestBuilderPrepare_InvalidKey(t *testing.T) {
}
}
func
TestBuilderPrepare_Region
ID
(
t
*
testing
.
T
)
{
func
TestBuilderPrepare_Region
(
t
*
testing
.
T
)
{
var
b
Builder
config
:=
testConfig
()
...
...
@@ -155,12 +155,15 @@ func TestBuilderPrepare_RegionID(t *testing.T) {
t
.
Fatalf
(
"should not have error: %s"
,
err
)
}
if
b
.
config
.
Region
ID
!=
1
{
t
.
Errorf
(
"
invalid: %d"
,
b
.
config
.
RegionID
)
if
b
.
config
.
Region
!=
DefaultRegion
{
t
.
Errorf
(
"
found %s, expected %s"
,
b
.
config
.
Region
,
DefaultRegion
)
}
expected
:=
"sfo1"
// Test set
config
[
"region_id"
]
=
2
config
[
"region_id"
]
=
0
config
[
"region"
]
=
expected
b
=
Builder
{}
warnings
,
err
=
b
.
Prepare
(
config
)
if
len
(
warnings
)
>
0
{
...
...
@@ -170,12 +173,12 @@ func TestBuilderPrepare_RegionID(t *testing.T) {
t
.
Fatalf
(
"should not have error: %s"
,
err
)
}
if
b
.
config
.
Region
ID
!=
2
{
t
.
Errorf
(
"
invalid: %d"
,
b
.
config
.
RegionID
)
if
b
.
config
.
Region
!=
expected
{
t
.
Errorf
(
"
found %s, expected %s"
,
b
.
config
.
Region
,
expected
)
}
}
func
TestBuilderPrepare_Size
ID
(
t
*
testing
.
T
)
{
func
TestBuilderPrepare_Size
(
t
*
testing
.
T
)
{
var
b
Builder
config
:=
testConfig
()
...
...
@@ -188,12 +191,15 @@ func TestBuilderPrepare_SizeID(t *testing.T) {
t
.
Fatalf
(
"should not have error: %s"
,
err
)
}
if
b
.
config
.
Size
ID
!=
66
{
t
.
Errorf
(
"
invalid: %d"
,
b
.
config
.
SizeID
)
if
b
.
config
.
Size
!=
DefaultSize
{
t
.
Errorf
(
"
found %s, expected %s"
,
b
.
config
.
Size
,
DefaultSize
)
}
expected
:=
"1024mb"
// Test set
config
[
"size_id"
]
=
67
config
[
"size_id"
]
=
0
config
[
"size"
]
=
expected
b
=
Builder
{}
warnings
,
err
=
b
.
Prepare
(
config
)
if
len
(
warnings
)
>
0
{
...
...
@@ -203,12 +209,12 @@ func TestBuilderPrepare_SizeID(t *testing.T) {
t
.
Fatalf
(
"should not have error: %s"
,
err
)
}
if
b
.
config
.
Size
ID
!=
67
{
t
.
Errorf
(
"
invalid: %d"
,
b
.
config
.
SizeID
)
if
b
.
config
.
Size
!=
expected
{
t
.
Errorf
(
"
found %s, expected %s"
,
b
.
config
.
Size
,
expected
)
}
}
func
TestBuilderPrepare_Image
ID
(
t
*
testing
.
T
)
{
func
TestBuilderPrepare_Image
(
t
*
testing
.
T
)
{
var
b
Builder
config
:=
testConfig
()
...
...
@@ -221,12 +227,15 @@ func TestBuilderPrepare_ImageID(t *testing.T) {
t
.
Fatalf
(
"should not have error: %s"
,
err
)
}
if
b
.
config
.
SizeID
!=
66
{
t
.
Errorf
(
"
invalid: %d"
,
b
.
config
.
SizeID
)
if
b
.
config
.
Image
!=
DefaultImage
{
t
.
Errorf
(
"
found %s, expected %s"
,
b
.
config
.
Image
,
DefaultImage
)
}
expected
:=
"ubuntu-14-04-x64"
// Test set
config
[
"size_id"
]
=
2
config
[
"image_id"
]
=
0
config
[
"image"
]
=
expected
b
=
Builder
{}
warnings
,
err
=
b
.
Prepare
(
config
)
if
len
(
warnings
)
>
0
{
...
...
@@ -236,8 +245,8 @@ func TestBuilderPrepare_ImageID(t *testing.T) {
t
.
Fatalf
(
"should not have error: %s"
,
err
)
}
if
b
.
config
.
SizeID
!=
2
{
t
.
Errorf
(
"
invalid: %d"
,
b
.
config
.
SizeID
)
if
b
.
config
.
Image
!=
expected
{
t
.
Errorf
(
"
found %s, expected %s"
,
b
.
config
.
Image
,
expected
)
}
}
...
...
builder/digitalocean/step_create_droplet.go
View file @
2bcd9a30
...
...
@@ -19,7 +19,7 @@ func (s *stepCreateDroplet) Run(state multistep.StateBag) multistep.StepAction {
ui
.
Say
(
"Creating droplet..."
)
// Create the droplet based on configuration
dropletId
,
err
:=
client
.
CreateDroplet
(
c
.
DropletName
,
c
.
Size
ID
,
c
.
ImageID
,
c
.
RegionID
,
sshKeyId
,
c
.
PrivateNetworking
)
dropletId
,
err
:=
client
.
CreateDroplet
(
c
.
DropletName
,
c
.
Size
,
c
.
Image
,
c
.
Region
,
sshKeyId
,
c
.
PrivateNetworking
)
if
err
!=
nil
{
err
:=
fmt
.
Errorf
(
"Error creating droplet: %s"
,
err
)
...
...
builder/digitalocean/step_snapshot.go
View file @
2bcd9a30
...
...
@@ -62,7 +62,7 @@ func (s *stepSnapshot) Run(state multistep.StateBag) multistep.StepAction {
state
.
Put
(
"snapshot_image_id"
,
imageId
)
state
.
Put
(
"snapshot_name"
,
c
.
SnapshotName
)
state
.
Put
(
"region
_id"
,
c
.
RegionID
)
state
.
Put
(
"region
"
,
c
.
Region
)
return
multistep
.
ActionContinue
}
...
...
website/source/docs/builders/digitalocean.html.markdown
View file @
2bcd9a30
...
...
@@ -35,16 +35,30 @@ Required:
Optional:
*
`image`
(string) - The name (or slug) of the base image to use. This is the
image that will be used to launch a new droplet and provision it. This
defaults to 'ubuntu-12-04-x64' which is the slug for "Ubuntu 12.04.4 x64".
See https://developers.digitalocean.com/images/ for the accepted image names/slugs.
*
`image_id`
(int) - The ID of the base image to use. This is the image that
will be used to launch a new droplet and provision it. Defaults to "3101045",
which happens to be "Ubuntu 12.04.4 x64".
will be used to launch a new droplet and provision it.
This setting is deprecated. Use
`image`
instead.
*
`region`
(string) - The name (or slug) of the region to launch the droplet in.
Consequently, this is the region where the snapshot will be available.
This defaults to "nyc1", which the slug for "New York 1".
See https://developers.digitalocean.com/regions/ for the accepted region names/slugs.
*
`region_id`
(int) - The ID of the region to launch the droplet in. Consequently,
this is the region where the snapshot will be available. This defaults to
"1", which is "New York 1".
this is the region where the snapshot will be available.
This setting is deprecated. Use
`region`
instead.
*
`size`
(string) - The name (or slug) of the droplet size to use.
This defaults to "512mb", which is the slug for "512MB".
See https://developers.digitalocean.com/sizes/ for the accepted size names/slugs.
*
`size_id`
(int) - The ID of the droplet size to use.
This defaults to "66",
which is the 512MB droplet
.
*
`size_id`
(int) - The ID of the droplet size to use.
This setting is deprecated. Use
`size`
instead
.
*
`private_networking`
(bool) - Set to
`true`
to enable private networking
for the droplet being created. This defaults to
`false`
, or not enabled.
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment