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
2bd6f1e2
Commit
2bd6f1e2
authored
Dec 12, 2013
by
Mitchell Hashimoto
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
builder/googlecompute: driver and create instance tests
parent
3657f33a
Changes
9
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
434 additions
and
281 deletions
+434
-281
builder/googlecompute/api.go
builder/googlecompute/api.go
+4
-135
builder/googlecompute/builder.go
builder/googlecompute/builder.go
+1
-1
builder/googlecompute/driver.go
builder/googlecompute/driver.go
+20
-0
builder/googlecompute/driver_gce.go
builder/googlecompute/driver_gce.go
+199
-0
builder/googlecompute/driver_mock.go
builder/googlecompute/driver_mock.go
+22
-0
builder/googlecompute/step_create_instance.go
builder/googlecompute/step_create_instance.go
+53
-91
builder/googlecompute/step_create_instance_test.go
builder/googlecompute/step_create_instance_test.go
+114
-0
builder/googlecompute/step_test.go
builder/googlecompute/step_test.go
+1
-0
builder/googlecompute/wait.go
builder/googlecompute/wait.go
+20
-54
No files found.
builder/googlecompute/api.go
View file @
2bd6f1e2
...
...
@@ -20,6 +20,7 @@ type GoogleComputeClient struct {
// InstanceConfig represents a GCE instance configuration.
// Used for creating machine instances.
/*
type InstanceConfig struct {
Description string
Image string
...
...
@@ -30,6 +31,7 @@ type InstanceConfig struct {
ServiceAccounts []*compute.ServiceAccount
Tags *compute.Tags
}
*/
// New initializes and returns a *GoogleComputeClient.
//
...
...
@@ -41,7 +43,7 @@ func New(projectId string, zone string, c *clientSecrets, pemKey []byte) (*Googl
Zone
:
zone
,
}
// Get the access token.
t
:=
jwt
.
NewToken
(
c
.
Web
.
ClientEmail
,
scopes
()
,
pemKey
)
t
:=
jwt
.
NewToken
(
c
.
Web
.
ClientEmail
,
""
,
pemKey
)
t
.
ClaimSet
.
Aud
=
c
.
Web
.
TokenURI
httpClient
:=
&
http
.
Client
{}
token
,
err
:=
t
.
Assert
(
httpClient
)
...
...
@@ -50,7 +52,7 @@ func New(projectId string, zone string, c *clientSecrets, pemKey []byte) (*Googl
}
config
:=
&
oauth
.
Config
{
ClientId
:
c
.
Web
.
ClientId
,
Scope
:
scopes
()
,
Scope
:
""
,
TokenURL
:
c
.
Web
.
TokenURI
,
AuthURL
:
c
.
Web
.
AuthURI
,
}
...
...
@@ -64,83 +66,6 @@ func New(projectId string, zone string, c *clientSecrets, pemKey []byte) (*Googl
return
googleComputeClient
,
nil
}
// GetZone returns a *compute.Zone representing the named zone.
func
(
g
*
GoogleComputeClient
)
GetZone
(
name
string
)
(
*
compute
.
Zone
,
error
)
{
zoneGetCall
:=
g
.
Service
.
Zones
.
Get
(
g
.
ProjectId
,
name
)
zone
,
err
:=
zoneGetCall
.
Do
()
if
err
!=
nil
{
return
nil
,
err
}
return
zone
,
nil
}
// GetMachineType returns a *compute.MachineType representing the named machine type.
func
(
g
*
GoogleComputeClient
)
GetMachineType
(
name
,
zone
string
)
(
*
compute
.
MachineType
,
error
)
{
machineTypesGetCall
:=
g
.
Service
.
MachineTypes
.
Get
(
g
.
ProjectId
,
zone
,
name
)
machineType
,
err
:=
machineTypesGetCall
.
Do
()
if
err
!=
nil
{
return
nil
,
err
}
if
machineType
.
Deprecated
==
nil
{
return
machineType
,
nil
}
return
nil
,
errors
.
New
(
"Machine Type does not exist: "
+
name
)
}
// GetImage returns a *compute.Image representing the named image.
func
(
g
*
GoogleComputeClient
)
GetImage
(
name
string
)
(
*
compute
.
Image
,
error
)
{
var
err
error
var
image
*
compute
.
Image
projects
:=
[]
string
{
g
.
ProjectId
,
"debian-cloud"
,
"centos-cloud"
}
for
_
,
project
:=
range
projects
{
imagesGetCall
:=
g
.
Service
.
Images
.
Get
(
project
,
name
)
image
,
err
=
imagesGetCall
.
Do
()
if
image
!=
nil
{
break
}
}
if
err
!=
nil
{
return
nil
,
err
}
if
image
!=
nil
{
if
image
.
SelfLink
!=
""
{
return
image
,
nil
}
}
return
nil
,
errors
.
New
(
"Image does not exist: "
+
name
)
}
// GetNetwork returns a *compute.Network representing the named network.
func
(
g
*
GoogleComputeClient
)
GetNetwork
(
name
string
)
(
*
compute
.
Network
,
error
)
{
networkGetCall
:=
g
.
Service
.
Networks
.
Get
(
g
.
ProjectId
,
name
)
network
,
err
:=
networkGetCall
.
Do
()
if
err
!=
nil
{
return
nil
,
err
}
return
network
,
nil
}
// CreateInstance creates an instance in Google Compute Engine based on the
// supplied instanceConfig.
func
(
g
*
GoogleComputeClient
)
CreateInstance
(
zone
string
,
instanceConfig
*
InstanceConfig
)
(
*
compute
.
Operation
,
error
)
{
instance
:=
&
compute
.
Instance
{
Description
:
instanceConfig
.
Description
,
Image
:
instanceConfig
.
Image
,
MachineType
:
instanceConfig
.
MachineType
,
Metadata
:
instanceConfig
.
Metadata
,
Name
:
instanceConfig
.
Name
,
NetworkInterfaces
:
instanceConfig
.
NetworkInterfaces
,
ServiceAccounts
:
instanceConfig
.
ServiceAccounts
,
Tags
:
instanceConfig
.
Tags
,
}
instanceInsertCall
:=
g
.
Service
.
Instances
.
Insert
(
g
.
ProjectId
,
zone
,
instance
)
operation
,
err
:=
instanceInsertCall
.
Do
()
if
err
!=
nil
{
return
nil
,
err
}
return
operation
,
nil
}
// InstanceStatus returns a string representing the status of the named instance.
// Status will be one of: "PROVISIONING", "STAGING", "RUNNING", "STOPPING",
// "STOPPED", "TERMINATED".
...
...
@@ -256,59 +181,3 @@ func (g *GoogleComputeClient) DeleteInstance(zone, name string) (*compute.Operat
}
return
operation
,
nil
}
// NewNetworkInterface returns a *compute.NetworkInterface based on the data provided.
func
NewNetworkInterface
(
network
*
compute
.
Network
,
public
bool
)
*
compute
.
NetworkInterface
{
accessConfigs
:=
make
([]
*
compute
.
AccessConfig
,
0
)
if
public
{
c
:=
&
compute
.
AccessConfig
{
Name
:
"AccessConfig created by Packer"
,
Type
:
"ONE_TO_ONE_NAT"
,
}
accessConfigs
=
append
(
accessConfigs
,
c
)
}
return
&
compute
.
NetworkInterface
{
AccessConfigs
:
accessConfigs
,
Network
:
network
.
SelfLink
,
}
}
// NewServiceAccount returns a *compute.ServiceAccount with permissions required
// for creating GCE machine images.
func
NewServiceAccount
(
email
string
)
*
compute
.
ServiceAccount
{
return
&
compute
.
ServiceAccount
{
Email
:
email
,
Scopes
:
[]
string
{
"https://www.googleapis.com/auth/userinfo.email"
,
"https://www.googleapis.com/auth/compute"
,
"https://www.googleapis.com/auth/devstorage.full_control"
,
},
}
}
// MapToMetadata converts a map[string]string to a *compute.Metadata.
func
MapToMetadata
(
metadata
map
[
string
]
string
)
*
compute
.
Metadata
{
items
:=
make
([]
*
compute
.
MetadataItems
,
len
(
metadata
))
for
k
,
v
:=
range
metadata
{
items
=
append
(
items
,
&
compute
.
MetadataItems
{
k
,
v
})
}
return
&
compute
.
Metadata
{
Items
:
items
,
}
}
// SliceToTags converts a []string to a *compute.Tags.
func
SliceToTags
(
tags
[]
string
)
*
compute
.
Tags
{
return
&
compute
.
Tags
{
Items
:
tags
,
}
}
// scopes return a space separated list of scopes.
func
scopes
()
string
{
s
:=
[]
string
{
"https://www.googleapis.com/auth/compute"
,
"https://www.googleapis.com/auth/devstorage.full_control"
,
}
return
strings
.
Join
(
s
,
" "
)
}
builder/googlecompute/builder.go
View file @
2bd6f1e2
...
...
@@ -51,7 +51,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
// Build the steps.
steps
:=
[]
multistep
.
Step
{
new
(
StepCreateSSHKey
),
new
(
s
tepCreateInstance
),
new
(
S
tepCreateInstance
),
new
(
stepInstanceInfo
),
&
common
.
StepConnectSSH
{
SSHAddress
:
sshAddress
,
...
...
builder/googlecompute/driver.go
0 → 100644
View file @
2bd6f1e2
package
googlecompute
// Driver is the interface that has to be implemented to communicate
// with GCE. The Driver interface exists mostly to allow a mock implementation
// to be used to test the steps.
type
Driver
interface
{
// RunInstance takes the given config and launches an instance.
RunInstance
(
*
InstanceConfig
)
(
<-
chan
error
,
error
)
}
type
InstanceConfig
struct
{
Description
string
Image
string
MachineType
string
Metadata
map
[
string
]
string
Name
string
Network
string
Tags
[]
string
Zone
string
}
builder/googlecompute/driver_gce.go
0 → 100644
View file @
2bd6f1e2
package
googlecompute
import
(
"fmt"
"net/http"
"time"
"code.google.com/p/goauth2/oauth"
"code.google.com/p/goauth2/oauth/jwt"
"code.google.com/p/google-api-go-client/compute/v1beta16"
"github.com/mitchellh/packer/packer"
)
// driverGCE is a Driver implementation that actually talks to GCE.
// Create an instance using NewDriverGCE.
type
driverGCE
struct
{
projectId
string
service
*
compute
.
Service
ui
packer
.
Ui
}
const
DriverScopes
string
=
"https://www.googleapis.com/auth/compute "
+
"https://www.googleapis.com/auth/devstorage.full_control"
func
NewDriverGCE
(
ui
packer
.
Ui
,
projectId
string
,
c
*
clientSecrets
,
key
[]
byte
)
(
Driver
,
error
)
{
jwtTok
:=
jwt
.
NewToken
(
c
.
Web
.
ClientEmail
,
DriverScopes
,
key
)
jwtTok
.
ClaimSet
.
Aud
=
c
.
Web
.
TokenURI
token
,
err
:=
jwtTok
.
Assert
(
new
(
http
.
Client
))
if
err
!=
nil
{
return
nil
,
err
}
transport
:=
&
oauth
.
Transport
{
Config
:
&
oauth
.
Config
{
ClientId
:
c
.
Web
.
ClientId
,
Scope
:
DriverScopes
,
TokenURL
:
c
.
Web
.
TokenURI
,
AuthURL
:
c
.
Web
.
AuthURI
,
},
Token
:
token
,
}
service
,
err
:=
compute
.
New
(
transport
.
Client
())
if
err
!=
nil
{
return
nil
,
err
}
return
&
driverGCE
{
projectId
:
projectId
,
service
:
service
,
ui
:
ui
,
},
nil
}
func
(
d
*
driverGCE
)
RunInstance
(
c
*
InstanceConfig
)
(
<-
chan
error
,
error
)
{
// Get the zone
d
.
ui
.
Message
(
fmt
.
Sprintf
(
"Loading zone: %s"
,
c
.
Zone
))
zone
,
err
:=
d
.
service
.
Zones
.
Get
(
d
.
projectId
,
c
.
Zone
)
.
Do
()
if
err
!=
nil
{
return
nil
,
err
}
// Get the image
d
.
ui
.
Message
(
fmt
.
Sprintf
(
"Loading image: %s"
,
c
.
Image
))
image
,
err
:=
d
.
getImage
(
c
.
Image
)
if
err
!=
nil
{
return
nil
,
err
}
// Get the machine type
d
.
ui
.
Message
(
fmt
.
Sprintf
(
"Loading machine type: %s"
,
c
.
MachineType
))
machineType
,
err
:=
d
.
service
.
MachineTypes
.
Get
(
d
.
projectId
,
zone
.
Name
,
c
.
MachineType
)
.
Do
()
if
err
!=
nil
{
return
nil
,
err
}
// TODO(mitchellh): deprecation warnings
// Get the network
d
.
ui
.
Message
(
fmt
.
Sprintf
(
"Loading network: %s"
,
c
.
Network
))
network
,
err
:=
d
.
service
.
Networks
.
Get
(
d
.
projectId
,
c
.
Network
)
.
Do
()
if
err
!=
nil
{
return
nil
,
err
}
// Build up the metadata
metadata
:=
make
([]
*
compute
.
MetadataItems
,
len
(
c
.
Metadata
))
for
k
,
v
:=
range
c
.
Metadata
{
metadata
=
append
(
metadata
,
&
compute
.
MetadataItems
{
Key
:
k
,
Value
:
v
,
})
}
// Create the instance information
instance
:=
compute
.
Instance
{
Description
:
c
.
Description
,
Image
:
image
.
SelfLink
,
MachineType
:
machineType
.
SelfLink
,
Metadata
:
&
compute
.
Metadata
{
Items
:
metadata
,
},
Name
:
c
.
Name
,
NetworkInterfaces
:
[]
*
compute
.
NetworkInterface
{
&
compute
.
NetworkInterface
{
AccessConfigs
:
[]
*
compute
.
AccessConfig
{
&
compute
.
AccessConfig
{
Name
:
"AccessConfig created by Packer"
,
Type
:
"ONE_TO_ONE_NAT"
,
},
},
Network
:
network
.
SelfLink
,
},
},
ServiceAccounts
:
[]
*
compute
.
ServiceAccount
{
&
compute
.
ServiceAccount
{
Email
:
"default"
,
Scopes
:
[]
string
{
"https://www.googleapis.com/auth/userinfo.email"
,
"https://www.googleapis.com/auth/compute"
,
"https://www.googleapis.com/auth/devstorage.full_control"
,
},
},
},
Tags
:
&
compute
.
Tags
{
Items
:
c
.
Tags
,
},
}
d
.
ui
.
Message
(
"Requesting instance creation..."
)
op
,
err
:=
d
.
service
.
Instances
.
Insert
(
d
.
projectId
,
zone
.
Name
,
&
instance
)
.
Do
()
if
err
!=
nil
{
return
nil
,
err
}
errCh
:=
make
(
chan
error
,
1
)
go
waitForState
(
errCh
,
"DONE"
,
d
.
refreshZoneOp
(
op
))
return
errCh
,
nil
}
func
(
d
*
driverGCE
)
getImage
(
name
string
)
(
image
*
compute
.
Image
,
err
error
)
{
projects
:=
[]
string
{
d
.
projectId
,
"debian-cloud"
,
"centos-cloud"
}
for
_
,
project
:=
range
projects
{
image
,
err
=
d
.
service
.
Images
.
Get
(
project
,
name
)
.
Do
()
if
err
==
nil
&&
image
!=
nil
&&
image
.
SelfLink
!=
""
{
return
}
image
=
nil
}
if
err
==
nil
{
err
=
fmt
.
Errorf
(
"Image could not be found: %s"
,
name
)
}
return
}
func
(
d
*
driverGCE
)
refreshZoneOp
(
op
*
compute
.
Operation
)
stateRefreshFunc
{
return
func
()
(
string
,
error
)
{
newOp
,
err
:=
d
.
service
.
ZoneOperations
.
Get
(
d
.
projectId
,
op
.
Zone
,
op
.
Name
)
.
Do
()
if
err
!=
nil
{
return
""
,
err
}
// If the op is done, check for errors
err
=
nil
if
newOp
.
Status
==
"DONE"
{
if
newOp
.
Error
!=
nil
{
for
_
,
e
:=
range
newOp
.
Error
.
Errors
{
err
=
packer
.
MultiErrorAppend
(
err
,
fmt
.
Errorf
(
e
.
Message
))
}
}
}
return
newOp
.
Status
,
err
}
}
// stateRefreshFunc is used to refresh the state of a thing and is
// used in conjunction with waitForState.
type
stateRefreshFunc
func
()
(
string
,
error
)
// waitForState will spin in a loop forever waiting for state to
// reach a certain target.
func
waitForState
(
errCh
chan
<-
error
,
target
string
,
refresh
stateRefreshFunc
)
{
for
{
state
,
err
:=
refresh
()
if
err
!=
nil
{
errCh
<-
err
return
}
if
state
==
target
{
errCh
<-
nil
return
}
time
.
Sleep
(
2
*
time
.
Second
)
}
}
builder/googlecompute/driver_mock.go
0 → 100644
View file @
2bd6f1e2
package
googlecompute
// DriverMock is a Driver implementation that is a mocked out so that
// it can be used for tests.
type
DriverMock
struct
{
RunInstanceConfig
*
InstanceConfig
RunInstanceErrCh
<-
chan
error
RunInstanceErr
error
}
func
(
d
*
DriverMock
)
RunInstance
(
c
*
InstanceConfig
)
(
<-
chan
error
,
error
)
{
d
.
RunInstanceConfig
=
c
resultCh
:=
d
.
RunInstanceErrCh
if
resultCh
==
nil
{
ch
:=
make
(
chan
error
)
close
(
ch
)
resultCh
=
ch
}
return
resultCh
,
d
.
RunInstanceErr
}
builder/googlecompute/step_create_instance.go
View file @
2bd6f1e2
package
googlecompute
import
(
"errors"
"fmt"
"time"
"code.google.com/p/google-api-go-client/compute/v1beta16"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/common/uuid"
"github.com/mitchellh/packer/packer"
)
//
s
tepCreateInstance represents a Packer build step that creates GCE instances.
type
s
tepCreateInstance
struct
{
//
S
tepCreateInstance represents a Packer build step that creates GCE instances.
type
S
tepCreateInstance
struct
{
instanceName
string
}
// Run executes the Packer build step that creates a GCE instance.
func
(
s
*
s
tepCreateInstance
)
Run
(
state
multistep
.
StateBag
)
multistep
.
StepAction
{
var
(
client
=
state
.
Get
(
"client"
)
.
(
*
GoogleComputeClient
)
config
=
state
.
Get
(
"config"
)
.
(
*
Confi
g
)
ui
=
state
.
Get
(
"ui"
)
.
(
packer
.
Ui
)
)
func
(
s
*
S
tepCreateInstance
)
Run
(
state
multistep
.
StateBag
)
multistep
.
StepAction
{
config
:=
state
.
Get
(
"config"
)
.
(
*
Config
)
driver
:=
state
.
Get
(
"driver"
)
.
(
Driver
)
sshPublicKey
:=
state
.
Get
(
"ssh_public_key"
)
.
(
strin
g
)
ui
:
=
state
.
Get
(
"ui"
)
.
(
packer
.
Ui
)
ui
.
Say
(
"Creating instance..."
)
name
:=
fmt
.
Sprintf
(
"packer-%s"
,
uuid
.
TimeOrderedUUID
())
// Build up the instance config.
instanceConfig
:=
&
InstanceConfig
{
errCh
,
err
:=
driver
.
RunInstance
(
&
InstanceConfig
{
Description
:
"New instance created by Packer"
,
Name
:
name
,
}
// Validate the zone.
zone
,
err
:=
client
.
GetZone
(
config
.
Zone
)
if
err
!=
nil
{
err
:=
fmt
.
Errorf
(
"Error creating instance: %s"
,
err
)
state
.
Put
(
"error"
,
err
)
ui
.
Error
(
err
.
Error
())
return
multistep
.
ActionHalt
}
// Set the source image. Must be a fully-qualified URL.
image
,
err
:=
client
.
GetImage
(
config
.
SourceImage
)
if
err
!=
nil
{
err
:=
fmt
.
Errorf
(
"Error creating instance: %s"
,
err
)
state
.
Put
(
"error"
,
err
)
ui
.
Error
(
err
.
Error
())
return
multistep
.
ActionHalt
}
instanceConfig
.
Image
=
image
.
SelfLink
// Set the machineType. Must be a fully-qualified URL.
machineType
,
err
:=
client
.
GetMachineType
(
config
.
MachineType
,
zone
.
Name
)
if
err
!=
nil
{
err
:=
fmt
.
Errorf
(
"Error creating instance: %s"
,
err
)
state
.
Put
(
"error"
,
err
)
ui
.
Error
(
err
.
Error
())
return
multistep
.
ActionHalt
Image
:
config
.
SourceImage
,
MachineType
:
config
.
MachineType
,
Metadata
:
map
[
string
]
string
{
"sshKeys"
:
fmt
.
Sprintf
(
"%s:%s"
,
config
.
SSHUsername
,
sshPublicKey
),
},
Name
:
name
,
Network
:
config
.
Network
,
Tags
:
config
.
Tags
,
Zone
:
config
.
Zone
,
})
if
err
==
nil
{
select
{
case
err
=
<-
errCh
:
case
<-
time
.
After
(
config
.
stateTimeout
)
:
err
=
errors
.
New
(
"time out while waiting for instance to create"
)
}
}
instanceConfig
.
MachineType
=
machineType
.
SelfLink
// Set up the Network Interface.
network
,
err
:=
client
.
GetNetwork
(
config
.
Network
)
if
err
!=
nil
{
err
:=
fmt
.
Errorf
(
"Error creating instance: %s"
,
err
)
state
.
Put
(
"error"
,
err
)
ui
.
Error
(
err
.
Error
())
return
multistep
.
ActionHalt
}
networkInterface
:=
NewNetworkInterface
(
network
,
true
)
networkInterfaces
:=
[]
*
compute
.
NetworkInterface
{
networkInterface
,
}
instanceConfig
.
NetworkInterfaces
=
networkInterfaces
// Add the metadata, which also setups up the ssh key.
metadata
:=
make
(
map
[
string
]
string
)
sshPublicKey
:=
state
.
Get
(
"ssh_public_key"
)
.
(
string
)
metadata
[
"sshKeys"
]
=
fmt
.
Sprintf
(
"%s:%s"
,
config
.
SSHUsername
,
sshPublicKey
)
instanceConfig
.
Metadata
=
MapToMetadata
(
metadata
)
// Add the default service so we can create an image of the machine and
// upload it to cloud storage.
defaultServiceAccount
:=
NewServiceAccount
(
"default"
)
serviceAccounts
:=
[]
*
compute
.
ServiceAccount
{
defaultServiceAccount
,
}
instanceConfig
.
ServiceAccounts
=
serviceAccounts
// Create the instance based on configuration
operation
,
err
:=
client
.
CreateInstance
(
zone
.
Name
,
instanceConfig
)
if
err
!=
nil
{
err
:=
fmt
.
Errorf
(
"Error creating instance: %s"
,
err
)
state
.
Put
(
"error"
,
err
)
ui
.
Error
(
err
.
Error
())
return
multistep
.
ActionHalt
}
ui
.
Say
(
"Waiting for the instance to be created..."
)
err
=
waitForZoneOperationState
(
"DONE"
,
config
.
Zone
,
operation
.
Name
,
client
,
config
.
stateTimeout
)
if
err
!=
nil
{
err
:=
fmt
.
Errorf
(
"Error creating instance: %s"
,
err
)
state
.
Put
(
"error"
,
err
)
ui
.
Error
(
err
.
Error
())
return
multistep
.
ActionHalt
}
// Update the state.
ui
.
Message
(
"Instance has been created!"
)
// Things succeeded, store the name so we can remove it later
state
.
Put
(
"instance_name"
,
name
)
s
.
instanceName
=
name
return
multistep
.
ActionContinue
}
// Cleanup destroys the GCE instance created during the image creation process.
func
(
s
*
stepCreateInstance
)
Cleanup
(
state
multistep
.
StateBag
)
{
var
(
client
=
state
.
Get
(
"client"
)
.
(
*
GoogleComputeClient
)
config
=
state
.
Get
(
"config"
)
.
(
*
Config
)
ui
=
state
.
Get
(
"ui"
)
.
(
packer
.
Ui
)
)
func
(
s
*
StepCreateInstance
)
Cleanup
(
state
multistep
.
StateBag
)
{
if
s
.
instanceName
==
""
{
return
}
ui
.
Say
(
"Destroying instance..."
)
operation
,
err
:=
client
.
DeleteInstance
(
config
.
Zone
,
s
.
instanceName
)
if
err
!=
nil
{
ui
.
Error
(
fmt
.
Sprintf
(
"Error destroying instance. Please destroy it manually: %v"
,
s
.
instanceName
)
)
}
ui
.
Say
(
"Waiting for the instance to be deleted..."
)
for
{
status
,
err
:=
client
.
ZoneOperationStatus
(
config
.
Zone
,
operation
.
Name
)
/*
var (
client = state.Get("client").(*GoogleComputeClient)
config = state.Get("config").(*Config
)
ui = state.Get("ui").(packer.Ui)
)
ui.Say("Destroying instance...")
operation, err := client.DeleteInstance(config.Zone, s.instance
Name)
if err != nil {
ui.Error(fmt.Sprintf("Error destroying instance. Please destroy it manually: %v", s.instanceName))
}
if
status
==
"DONE"
{
break
ui.Say("Waiting for the instance to be deleted...")
for {
status, err := client.ZoneOperationStatus(config.Zone, operation.Name)
if err != nil {
ui.Error(fmt.Sprintf("Error destroying instance. Please destroy it manually: %v", s.instanceName))
}
if status == "DONE" {
break
}
}
}
*/
return
}
builder/googlecompute/step_create_instance_test.go
0 → 100644
View file @
2bd6f1e2
package
googlecompute
import
(
"errors"
"github.com/mitchellh/multistep"
"testing"
"time"
)
func
TestStepCreateInstance_impl
(
t
*
testing
.
T
)
{
var
_
multistep
.
Step
=
new
(
StepCreateInstance
)
}
func
TestStepCreateInstance
(
t
*
testing
.
T
)
{
state
:=
testState
(
t
)
step
:=
new
(
StepCreateInstance
)
defer
step
.
Cleanup
(
state
)
state
.
Put
(
"ssh_public_key"
,
"key"
)
// run the step
if
action
:=
step
.
Run
(
state
);
action
!=
multistep
.
ActionContinue
{
t
.
Fatalf
(
"bad action: %#v"
,
action
)
}
// Verify state
if
_
,
ok
:=
state
.
GetOk
(
"instance_name"
);
!
ok
{
t
.
Fatal
(
"should have instance name"
)
}
}
func
TestStepCreateInstance_error
(
t
*
testing
.
T
)
{
state
:=
testState
(
t
)
step
:=
new
(
StepCreateInstance
)
defer
step
.
Cleanup
(
state
)
state
.
Put
(
"ssh_public_key"
,
"key"
)
driver
:=
state
.
Get
(
"driver"
)
.
(
*
DriverMock
)
driver
.
RunInstanceErr
=
errors
.
New
(
"error"
)
// run the step
if
action
:=
step
.
Run
(
state
);
action
!=
multistep
.
ActionHalt
{
t
.
Fatalf
(
"bad action: %#v"
,
action
)
}
// Verify state
if
_
,
ok
:=
state
.
GetOk
(
"error"
);
!
ok
{
t
.
Fatal
(
"should have error"
)
}
if
_
,
ok
:=
state
.
GetOk
(
"instance_name"
);
ok
{
t
.
Fatal
(
"should NOT have instance name"
)
}
}
func
TestStepCreateInstance_errorOnChannel
(
t
*
testing
.
T
)
{
state
:=
testState
(
t
)
step
:=
new
(
StepCreateInstance
)
defer
step
.
Cleanup
(
state
)
errCh
:=
make
(
chan
error
,
1
)
errCh
<-
errors
.
New
(
"error"
)
state
.
Put
(
"ssh_public_key"
,
"key"
)
driver
:=
state
.
Get
(
"driver"
)
.
(
*
DriverMock
)
driver
.
RunInstanceErrCh
=
errCh
// run the step
if
action
:=
step
.
Run
(
state
);
action
!=
multistep
.
ActionHalt
{
t
.
Fatalf
(
"bad action: %#v"
,
action
)
}
// Verify state
if
_
,
ok
:=
state
.
GetOk
(
"error"
);
!
ok
{
t
.
Fatal
(
"should have error"
)
}
if
_
,
ok
:=
state
.
GetOk
(
"instance_name"
);
ok
{
t
.
Fatal
(
"should NOT have instance name"
)
}
}
func
TestStepCreateInstance_errorTimeout
(
t
*
testing
.
T
)
{
state
:=
testState
(
t
)
step
:=
new
(
StepCreateInstance
)
defer
step
.
Cleanup
(
state
)
errCh
:=
make
(
chan
error
,
1
)
go
func
()
{
<-
time
.
After
(
10
*
time
.
Millisecond
)
errCh
<-
nil
}()
state
.
Put
(
"ssh_public_key"
,
"key"
)
config
:=
state
.
Get
(
"config"
)
.
(
*
Config
)
config
.
stateTimeout
=
1
*
time
.
Microsecond
driver
:=
state
.
Get
(
"driver"
)
.
(
*
DriverMock
)
driver
.
RunInstanceErrCh
=
errCh
// run the step
if
action
:=
step
.
Run
(
state
);
action
!=
multistep
.
ActionHalt
{
t
.
Fatalf
(
"bad action: %#v"
,
action
)
}
// Verify state
if
_
,
ok
:=
state
.
GetOk
(
"error"
);
!
ok
{
t
.
Fatal
(
"should have error"
)
}
if
_
,
ok
:=
state
.
GetOk
(
"instance_name"
);
ok
{
t
.
Fatal
(
"should NOT have instance name"
)
}
}
builder/googlecompute/step_test.go
View file @
2bd6f1e2
...
...
@@ -10,6 +10,7 @@ import (
func
testState
(
t
*
testing
.
T
)
multistep
.
StateBag
{
state
:=
new
(
multistep
.
BasicStateBag
)
state
.
Put
(
"config"
,
testConfigStruct
(
t
))
state
.
Put
(
"driver"
,
&
DriverMock
{})
state
.
Put
(
"hook"
,
&
packer
.
MockHook
{})
state
.
Put
(
"ui"
,
&
packer
.
BasicUi
{
Reader
:
new
(
bytes
.
Buffer
),
...
...
builder/googlecompute/wait.go
View file @
2bd6f1e2
package
googlecompute
import
(
"fmt"
"log"
"time"
)
// statusFunc.
type
statusFunc
func
()
(
string
,
error
)
// waitForInstanceState.
func
waitForInstanceState
(
desiredState
string
,
zone
string
,
name
string
,
client
*
GoogleComputeClient
,
timeout
time
.
Duration
)
error
{
f
:=
func
()
(
string
,
error
)
{
return
client
.
InstanceStatus
(
zone
,
name
)
}
return
waitForState
(
"instance"
,
desiredState
,
f
,
timeout
)
return
nil
/*
f := func() (string, error) {
return client.InstanceStatus(zone, name)
}
return waitForState("instance", desiredState, f, timeout)
*/
}
// waitForZoneOperationState.
func
waitForZoneOperationState
(
desiredState
string
,
zone
string
,
name
string
,
client
*
GoogleComputeClient
,
timeout
time
.
Duration
)
error
{
f
:=
func
()
(
string
,
error
)
{
return
client
.
ZoneOperationStatus
(
zone
,
name
)
}
return
waitForState
(
"operation"
,
desiredState
,
f
,
timeout
)
return
nil
/*
f := func() (string, error) {
return client.ZoneOperationStatus(zone, name)
}
return waitForState("operation", desiredState, f, timeout)
*/
}
// waitForGlobalOperationState.
func
waitForGlobalOperationState
(
desiredState
string
,
name
string
,
client
*
GoogleComputeClient
,
timeout
time
.
Duration
)
error
{
f
:=
func
()
(
string
,
error
)
{
return
client
.
GlobalOperationStatus
(
name
)
}
return
waitForState
(
"operation"
,
desiredState
,
f
,
timeout
)
}
// waitForState.
func
waitForState
(
kind
string
,
desiredState
string
,
f
statusFunc
,
timeout
time
.
Duration
)
error
{
done
:=
make
(
chan
struct
{})
defer
close
(
done
)
result
:=
make
(
chan
error
,
1
)
go
func
()
{
attempts
:=
0
for
{
attempts
+=
1
log
.
Printf
(
"Checking %s state... (attempt: %d)"
,
kind
,
attempts
)
status
,
err
:=
f
()
if
err
!=
nil
{
result
<-
err
return
}
if
status
==
desiredState
{
result
<-
nil
return
}
time
.
Sleep
(
3
*
time
.
Second
)
select
{
case
<-
done
:
return
default
:
continue
}
/*
f := func() (string, error) {
return client.GlobalOperationStatus(name)
}
}()
log
.
Printf
(
"Waiting for up to %d seconds for %s to become %s"
,
timeout
,
kind
,
desiredState
)
select
{
case
err
:=
<-
result
:
return
err
case
<-
time
.
After
(
timeout
)
:
err
:=
fmt
.
Errorf
(
"Timeout while waiting to for the %s to become '%s'"
,
kind
,
desiredState
)
return
err
}
return waitForState("operation", desiredState, f, timeout)
*/
return
nil
}
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