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
58c73727
Commit
58c73727
authored
Dec 08, 2013
by
Kelsey Hightower
Committed by
Mitchell Hashimoto
Dec 12, 2013
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
builder/googlecompute: Add new googlecompute builder
parent
9307d8a8
Changes
17
Hide whitespace changes
Inline
Side-by-side
Showing
17 changed files
with
1231 additions
and
0 deletions
+1231
-0
builder/googlecompute/api.go
builder/googlecompute/api.go
+318
-0
builder/googlecompute/artifact.go
builder/googlecompute/artifact.go
+47
-0
builder/googlecompute/builder.go
builder/googlecompute/builder.go
+241
-0
builder/googlecompute/builder_test.go
builder/googlecompute/builder_test.go
+5
-0
builder/googlecompute/client_secrets.go
builder/googlecompute/client_secrets.go
+34
-0
builder/googlecompute/private_key.go
builder/googlecompute/private_key.go
+40
-0
builder/googlecompute/ssh.go
builder/googlecompute/ssh.go
+32
-0
builder/googlecompute/step_create_image.go
builder/googlecompute/step_create_image.go
+50
-0
builder/googlecompute/step_create_instance.go
builder/googlecompute/step_create_instance.go
+135
-0
builder/googlecompute/step_create_ssh_key.go
builder/googlecompute/step_create_ssh_key.go
+55
-0
builder/googlecompute/step_instance_info.go
builder/googlecompute/step_instance_info.go
+44
-0
builder/googlecompute/step_register_image.go
builder/googlecompute/step_register_image.go
+46
-0
builder/googlecompute/step_update_gsutil.go
builder/googlecompute/step_update_gsutil.go
+49
-0
builder/googlecompute/step_upload_image.go
builder/googlecompute/step_upload_image.go
+44
-0
builder/googlecompute/wait.go
builder/googlecompute/wait.go
+76
-0
plugin/builder-googlecompute/main.go
plugin/builder-googlecompute/main.go
+14
-0
plugin/builder-googlecompute/main_test.go
plugin/builder-googlecompute/main_test.go
+1
-0
No files found.
builder/googlecompute/api.go
0 → 100644
View file @
58c73727
// Copyright (c) 2013 Kelsey Hightower. All rights reserved.
// Use of this source code is governed by the Apache License, Version 2.0
// that can be found in the LICENSE file.
package
googlecompute
import
(
"errors"
"net/http"
"strings"
"code.google.com/p/goauth2/oauth"
"code.google.com/p/goauth2/oauth/jwt"
"code.google.com/p/google-api-go-client/compute/v1beta16"
)
// GoogleComputeClient represents a GCE client.
type
GoogleComputeClient
struct
{
ProjectId
string
Service
*
compute
.
Service
Zone
string
clientSecrets
*
clientSecrets
}
// InstanceConfig represents a GCE instance configuration.
// Used for creating machine instances.
type
InstanceConfig
struct
{
Description
string
Image
string
MachineType
string
Metadata
*
compute
.
Metadata
Name
string
NetworkInterfaces
[]
*
compute
.
NetworkInterface
ServiceAccounts
[]
*
compute
.
ServiceAccount
Tags
*
compute
.
Tags
}
// New initializes and returns a *GoogleComputeClient.
//
// The projectId must be the project name, i.e. myproject, not the project
// number.
func
New
(
projectId
string
,
zone
string
,
c
*
clientSecrets
,
pemKey
[]
byte
)
(
*
GoogleComputeClient
,
error
)
{
googleComputeClient
:=
&
GoogleComputeClient
{
ProjectId
:
projectId
,
Zone
:
zone
,
}
// Get the access token.
t
:=
jwt
.
NewToken
(
c
.
Web
.
ClientEmail
,
scopes
(),
pemKey
)
t
.
ClaimSet
.
Aud
=
c
.
Web
.
TokenURI
httpClient
:=
&
http
.
Client
{}
token
,
err
:=
t
.
Assert
(
httpClient
)
if
err
!=
nil
{
return
nil
,
err
}
config
:=
&
oauth
.
Config
{
ClientId
:
c
.
Web
.
ClientId
,
Scope
:
scopes
(),
TokenURL
:
c
.
Web
.
TokenURI
,
AuthURL
:
c
.
Web
.
AuthURI
,
}
transport
:=
&
oauth
.
Transport
{
Config
:
config
}
transport
.
Token
=
token
s
,
err
:=
compute
.
New
(
transport
.
Client
())
if
err
!=
nil
{
return
nil
,
err
}
googleComputeClient
.
Service
=
s
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".
func
(
g
*
GoogleComputeClient
)
InstanceStatus
(
zone
,
name
string
)
(
string
,
error
)
{
instanceGetCall
:=
g
.
Service
.
Instances
.
Get
(
g
.
ProjectId
,
zone
,
name
)
instance
,
err
:=
instanceGetCall
.
Do
()
if
err
!=
nil
{
return
""
,
err
}
return
instance
.
Status
,
nil
}
// CreateImage registers a GCE Image with a project.
func
(
g
*
GoogleComputeClient
)
CreateImage
(
name
,
description
,
sourceURL
string
)
(
*
compute
.
Operation
,
error
)
{
imageRawDisk
:=
&
compute
.
ImageRawDisk
{
ContainerType
:
"TAR"
,
Source
:
sourceURL
,
}
image
:=
&
compute
.
Image
{
Description
:
description
,
Name
:
name
,
RawDisk
:
imageRawDisk
,
SourceType
:
"RAW"
,
}
imageInsertCall
:=
g
.
Service
.
Images
.
Insert
(
g
.
ProjectId
,
image
)
operation
,
err
:=
imageInsertCall
.
Do
()
if
err
!=
nil
{
return
nil
,
err
}
return
operation
,
nil
}
// GetNatIp returns the public IPv4 address for named GCE instance.
func
(
g
*
GoogleComputeClient
)
GetNatIP
(
zone
,
name
string
)
(
string
,
error
)
{
instanceGetCall
:=
g
.
Service
.
Instances
.
Get
(
g
.
ProjectId
,
zone
,
name
)
instance
,
err
:=
instanceGetCall
.
Do
()
if
err
!=
nil
{
return
""
,
err
}
for
_
,
ni
:=
range
instance
.
NetworkInterfaces
{
if
ni
.
AccessConfigs
==
nil
{
continue
}
for
_
,
ac
:=
range
ni
.
AccessConfigs
{
if
ac
.
NatIP
!=
""
{
return
ac
.
NatIP
,
nil
}
}
}
return
""
,
nil
}
// ZoneOperationStatus returns the status for the named zone operation.
func
(
g
*
GoogleComputeClient
)
ZoneOperationStatus
(
zone
,
name
string
)
(
string
,
error
)
{
zoneOperationsGetCall
:=
g
.
Service
.
ZoneOperations
.
Get
(
g
.
ProjectId
,
zone
,
name
)
operation
,
err
:=
zoneOperationsGetCall
.
Do
()
if
err
!=
nil
{
return
""
,
err
}
if
operation
.
Status
==
"DONE"
{
err
=
processOperationStatus
(
operation
)
if
err
!=
nil
{
return
operation
.
Status
,
err
}
}
return
operation
.
Status
,
nil
}
// GlobalOperationStatus returns the status for the named global operation.
func
(
g
*
GoogleComputeClient
)
GlobalOperationStatus
(
name
string
)
(
string
,
error
)
{
globalOperationsGetCall
:=
g
.
Service
.
GlobalOperations
.
Get
(
g
.
ProjectId
,
name
)
operation
,
err
:=
globalOperationsGetCall
.
Do
()
if
err
!=
nil
{
return
""
,
err
}
if
operation
.
Status
==
"DONE"
{
err
=
processOperationStatus
(
operation
)
if
err
!=
nil
{
return
operation
.
Status
,
err
}
}
return
operation
.
Status
,
nil
}
// processOperationStatus extracts errors from the specified operation.
func
processOperationStatus
(
o
*
compute
.
Operation
)
error
{
if
o
.
Error
!=
nil
{
messages
:=
make
([]
string
,
len
(
o
.
Error
.
Errors
))
for
_
,
e
:=
range
o
.
Error
.
Errors
{
messages
=
append
(
messages
,
e
.
Message
)
}
return
errors
.
New
(
strings
.
Join
(
messages
,
"
\n
"
))
}
return
nil
}
// DeleteImage deletes the named image. Returns a Global Operation.
func
(
g
*
GoogleComputeClient
)
DeleteImage
(
name
string
)
(
*
compute
.
Operation
,
error
)
{
imagesDeleteCall
:=
g
.
Service
.
Images
.
Delete
(
g
.
ProjectId
,
name
)
operation
,
err
:=
imagesDeleteCall
.
Do
()
if
err
!=
nil
{
return
nil
,
err
}
return
operation
,
nil
}
// DeleteInstance deletes the named instance. Returns a Zone Operation.
func
(
g
*
GoogleComputeClient
)
DeleteInstance
(
zone
,
name
string
)
(
*
compute
.
Operation
,
error
)
{
instanceDeleteCall
:=
g
.
Service
.
Instances
.
Delete
(
g
.
ProjectId
,
zone
,
name
)
operation
,
err
:=
instanceDeleteCall
.
Do
()
if
err
!=
nil
{
return
nil
,
err
}
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/artifact.go
0 → 100644
View file @
58c73727
// Copyright (c) 2013 Kelsey Hightower. All rights reserved.
// Use of this source code is governed by the Apache License, Version 2.0
// that can be found in the LICENSE file.
package
googlecompute
import
(
"fmt"
"log"
)
// Artifact represents a GCE image as the result of a Packer build.
type
Artifact
struct
{
imageName
string
client
*
GoogleComputeClient
}
// BuilderId returns the builder Id.
func
(
*
Artifact
)
BuilderId
()
string
{
return
BuilderId
}
// Destroy destroys the GCE image represented by the artifact.
func
(
a
*
Artifact
)
Destroy
()
error
{
log
.
Printf
(
"Destroying image: %s"
,
a
.
imageName
)
// Ignore the operation result as we are not waiting until it completes.
_
,
err
:=
a
.
client
.
DeleteImage
(
a
.
imageName
)
if
err
!=
nil
{
return
err
}
return
nil
}
// Files returns the files represented by the artifact.
func
(
*
Artifact
)
Files
()
[]
string
{
return
nil
}
// Id returns the GCE image name.
func
(
a
*
Artifact
)
Id
()
string
{
return
a
.
imageName
}
// String returns the string representation of the artifact.
func
(
a
*
Artifact
)
String
()
string
{
return
fmt
.
Sprintf
(
"A disk image was created: %v"
,
a
.
imageName
)
}
builder/googlecompute/builder.go
0 → 100644
View file @
58c73727
// Copyright (c) 2013 Kelsey Hightower. All rights reserved.
// Use of this source code is governed by the Apache License, Version 2.0
// that can be found in the LICENSE file.
// The googlecompute package contains a packer.Builder implementation that
// builds images for Google Compute Engine.
package
googlecompute
import
(
"errors"
"fmt"
"log"
"time"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/common"
"github.com/mitchellh/packer/packer"
)
// The unique ID for this builder.
const
BuilderId
=
"kelseyhightower.googlecompute"
// Builder represents a Packer Builder.
type
Builder
struct
{
config
config
runner
multistep
.
Runner
}
// config holds the googlecompute builder configuration settings.
type
config
struct
{
BucketName
string
`mapstructure:"bucket_name"`
ClientSecretsFile
string
`mapstructure:"client_secrets_file"`
ImageName
string
`mapstructure:"image_name"`
ImageDescription
string
`mapstructure:"image_description"`
MachineType
string
`mapstructure:"machine_type"`
Metadata
map
[
string
]
string
`mapstructure:"metadata"`
Network
string
`mapstructure:"network"`
Passphrase
string
`mapstructure:"passphrase"`
PrivateKeyFile
string
`mapstructure:"private_key_file"`
ProjectId
string
`mapstructure:"project_id"`
SourceImage
string
`mapstructure:"source_image"`
SSHUsername
string
`mapstructure:"ssh_username"`
SSHPort
uint
`mapstructure:"ssh_port"`
RawSSHTimeout
string
`mapstructure:"ssh_timeout"`
RawStateTimeout
string
`mapstructure:"state_timeout"`
Tags
[]
string
`mapstructure:"tags"`
Zone
string
`mapstructure:"zone"`
clientSecrets
*
clientSecrets
common
.
PackerConfig
`mapstructure:",squash"`
instanceName
string
privateKeyBytes
[]
byte
sshTimeout
time
.
Duration
stateTimeout
time
.
Duration
tpl
*
packer
.
ConfigTemplate
}
// Prepare processes the build configuration parameters.
func
(
b
*
Builder
)
Prepare
(
raws
...
interface
{})
([]
string
,
error
)
{
// Load the packer config.
md
,
err
:=
common
.
DecodeConfig
(
&
b
.
config
,
raws
...
)
if
err
!=
nil
{
return
nil
,
err
}
b
.
config
.
tpl
,
err
=
packer
.
NewConfigTemplate
()
if
err
!=
nil
{
return
nil
,
err
}
b
.
config
.
tpl
.
UserVars
=
b
.
config
.
PackerUserVars
errs
:=
common
.
CheckUnusedConfig
(
md
)
// Collect errors if any.
if
err
:=
common
.
CheckUnusedConfig
(
md
);
err
!=
nil
{
return
nil
,
err
}
// Set defaults.
if
b
.
config
.
Network
==
""
{
b
.
config
.
Network
=
"default"
}
if
b
.
config
.
ImageDescription
==
""
{
b
.
config
.
ImageDescription
=
"Created by Packer"
}
if
b
.
config
.
ImageName
==
""
{
// Default to packer-{{ unix timestamp (utc) }}
b
.
config
.
ImageName
=
"packer-{{timestamp}}"
}
if
b
.
config
.
MachineType
==
""
{
b
.
config
.
MachineType
=
"n1-standard-1"
}
if
b
.
config
.
RawSSHTimeout
==
""
{
b
.
config
.
RawSSHTimeout
=
"5m"
}
if
b
.
config
.
RawStateTimeout
==
""
{
b
.
config
.
RawStateTimeout
=
"5m"
}
if
b
.
config
.
SSHUsername
==
""
{
b
.
config
.
SSHUsername
=
"root"
}
if
b
.
config
.
SSHPort
==
0
{
b
.
config
.
SSHPort
=
22
}
// Process Templates
templates
:=
map
[
string
]
*
string
{
"bucket_name"
:
&
b
.
config
.
BucketName
,
"client_secrets_file"
:
&
b
.
config
.
ClientSecretsFile
,
"image_name"
:
&
b
.
config
.
ImageName
,
"image_description"
:
&
b
.
config
.
ImageDescription
,
"machine_type"
:
&
b
.
config
.
MachineType
,
"network"
:
&
b
.
config
.
Network
,
"passphrase"
:
&
b
.
config
.
Passphrase
,
"private_key_file"
:
&
b
.
config
.
PrivateKeyFile
,
"project_id"
:
&
b
.
config
.
ProjectId
,
"source_image"
:
&
b
.
config
.
SourceImage
,
"ssh_username"
:
&
b
.
config
.
SSHUsername
,
"ssh_timeout"
:
&
b
.
config
.
RawSSHTimeout
,
"state_timeout"
:
&
b
.
config
.
RawStateTimeout
,
"zone"
:
&
b
.
config
.
Zone
,
}
for
n
,
ptr
:=
range
templates
{
var
err
error
*
ptr
,
err
=
b
.
config
.
tpl
.
Process
(
*
ptr
,
nil
)
if
err
!=
nil
{
errs
=
packer
.
MultiErrorAppend
(
errs
,
fmt
.
Errorf
(
"Error processing %s: %s"
,
n
,
err
))
}
}
// Process required parameters.
if
b
.
config
.
BucketName
==
""
{
errs
=
packer
.
MultiErrorAppend
(
errs
,
errors
.
New
(
"a bucket_name must be specified"
))
}
if
b
.
config
.
ClientSecretsFile
==
""
{
errs
=
packer
.
MultiErrorAppend
(
errs
,
errors
.
New
(
"a client_secrets_file must be specified"
))
}
if
b
.
config
.
PrivateKeyFile
==
""
{
errs
=
packer
.
MultiErrorAppend
(
errs
,
errors
.
New
(
"a private_key_file must be specified"
))
}
if
b
.
config
.
ProjectId
==
""
{
errs
=
packer
.
MultiErrorAppend
(
errs
,
errors
.
New
(
"a project_id must be specified"
))
}
if
b
.
config
.
SourceImage
==
""
{
errs
=
packer
.
MultiErrorAppend
(
errs
,
errors
.
New
(
"a source_image must be specified"
))
}
if
b
.
config
.
Zone
==
""
{
errs
=
packer
.
MultiErrorAppend
(
errs
,
errors
.
New
(
"a zone must be specified"
))
}
// Process timeout settings.
sshTimeout
,
err
:=
time
.
ParseDuration
(
b
.
config
.
RawSSHTimeout
)
if
err
!=
nil
{
errs
=
packer
.
MultiErrorAppend
(
errs
,
fmt
.
Errorf
(
"Failed parsing ssh_timeout: %s"
,
err
))
}
b
.
config
.
sshTimeout
=
sshTimeout
stateTimeout
,
err
:=
time
.
ParseDuration
(
b
.
config
.
RawStateTimeout
)
if
err
!=
nil
{
errs
=
packer
.
MultiErrorAppend
(
errs
,
fmt
.
Errorf
(
"Failed parsing state_timeout: %s"
,
err
))
}
b
.
config
.
stateTimeout
=
stateTimeout
// Load the client secrets file.
cs
,
err
:=
loadClientSecrets
(
b
.
config
.
ClientSecretsFile
)
if
err
!=
nil
{
errs
=
packer
.
MultiErrorAppend
(
errs
,
fmt
.
Errorf
(
"Failed parsing client secrets file: %s"
,
err
))
}
b
.
config
.
clientSecrets
=
cs
// Load the private key.
b
.
config
.
privateKeyBytes
,
err
=
processPrivateKeyFile
(
b
.
config
.
PrivateKeyFile
,
b
.
config
.
Passphrase
)
if
err
!=
nil
{
errs
=
packer
.
MultiErrorAppend
(
errs
,
fmt
.
Errorf
(
"Failed loading private key file: %s"
,
err
))
}
// Check for any errors.
if
errs
!=
nil
&&
len
(
errs
.
Errors
)
>
0
{
return
nil
,
errs
}
return
nil
,
nil
}
// Run executes a googlecompute Packer build and returns a packer.Artifact
// representing a GCE machine image.
func
(
b
*
Builder
)
Run
(
ui
packer
.
Ui
,
hook
packer
.
Hook
,
cache
packer
.
Cache
)
(
packer
.
Artifact
,
error
)
{
// Initialize the Google Compute Engine API.
client
,
err
:=
New
(
b
.
config
.
ProjectId
,
b
.
config
.
Zone
,
b
.
config
.
clientSecrets
,
b
.
config
.
privateKeyBytes
)
if
err
!=
nil
{
log
.
Println
(
"Failed to create the Google Compute Engine client."
)
return
nil
,
err
}
// Set up the state.
state
:=
new
(
multistep
.
BasicStateBag
)
state
.
Put
(
"config"
,
b
.
config
)
state
.
Put
(
"client"
,
client
)
state
.
Put
(
"hook"
,
hook
)
state
.
Put
(
"ui"
,
ui
)
// Build the steps.
steps
:=
[]
multistep
.
Step
{
new
(
stepCreateSSHKey
),
new
(
stepCreateInstance
),
new
(
stepInstanceInfo
),
&
common
.
StepConnectSSH
{
SSHAddress
:
sshAddress
,
SSHConfig
:
sshConfig
,
SSHWaitTimeout
:
5
*
time
.
Minute
,
},
new
(
common
.
StepProvision
),
new
(
stepUpdateGsutil
),
new
(
stepCreateImage
),
new
(
stepUploadImage
),
new
(
stepRegisterImage
),
}
// Run the steps.
if
b
.
config
.
PackerDebug
{
b
.
runner
=
&
multistep
.
DebugRunner
{
Steps
:
steps
,
PauseFn
:
common
.
MultistepDebugFn
(
ui
),
}
}
else
{
b
.
runner
=
&
multistep
.
BasicRunner
{
Steps
:
steps
}
}
b
.
runner
.
Run
(
state
)
// Report any errors.
if
rawErr
,
ok
:=
state
.
GetOk
(
"error"
);
ok
{
return
nil
,
rawErr
.
(
error
)
}
if
_
,
ok
:=
state
.
GetOk
(
"image_name"
);
!
ok
{
log
.
Println
(
"Failed to find image_name in state. Bug?"
)
return
nil
,
nil
}
artifact
:=
&
Artifact
{
imageName
:
state
.
Get
(
"image_name"
)
.
(
string
),
client
:
client
,
}
return
artifact
,
nil
}
// Cancel.
func
(
b
*
Builder
)
Cancel
()
{}
builder/googlecompute/builder_test.go
0 → 100644
View file @
58c73727
// Copyright (c) 2013 Kelsey Hightower. All rights reserved.
// Use of this source code is governed by the Apache License, Version 2.0
// that can be found in the LICENSE file.
package
googlecompute
builder/googlecompute/client_secrets.go
0 → 100644
View file @
58c73727
// Copyright (c) 2013 Kelsey Hightower. All rights reserved.
// Use of this source code is governed by the Apache License, Version 2.0
// that can be found in the LICENSE file.
package
googlecompute
import
(
"encoding/json"
"io/ioutil"
)
// clientSecrets represents the client secrets of a GCE service account.
type
clientSecrets
struct
{
Web
struct
{
AuthURI
string
`json:"auth_uri"`
ClientEmail
string
`json:"client_email"`
ClientId
string
`json:"client_id"`
TokenURI
string
`json:"token_uri"`
}
}
// loadClientSecrets loads the GCE client secrets file identified by path.
func
loadClientSecrets
(
path
string
)
(
*
clientSecrets
,
error
)
{
var
cs
*
clientSecrets
secretBytes
,
err
:=
ioutil
.
ReadFile
(
path
)
if
err
!=
nil
{
return
nil
,
err
}
err
=
json
.
Unmarshal
(
secretBytes
,
&
cs
)
if
err
!=
nil
{
return
nil
,
err
}
return
cs
,
nil
}
builder/googlecompute/private_key.go
0 → 100644
View file @
58c73727
// Copyright (c) 2013 Kelsey Hightower. All rights reserved.
// Use of this source code is governed by the Apache License, Version 2.0
// that can be found in the LICENSE file.
package
googlecompute
import
(
"crypto/x509"
"encoding/pem"
"errors"
"fmt"
"io/ioutil"
)
// processPrivateKeyFile.
func
processPrivateKeyFile
(
privateKeyFile
,
passphrase
string
)
([]
byte
,
error
)
{
rawPrivateKeyBytes
,
err
:=
ioutil
.
ReadFile
(
privateKeyFile
)
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"Failed loading private key file: %s"
,
err
)
}
PEMBlock
,
_
:=
pem
.
Decode
(
rawPrivateKeyBytes
)
if
PEMBlock
==
nil
{
return
nil
,
fmt
.
Errorf
(
"%s does not contain a vaild private key"
,
privateKeyFile
)
}
if
x509
.
IsEncryptedPEMBlock
(
PEMBlock
)
{
if
passphrase
==
""
{
return
nil
,
errors
.
New
(
"a passphrase must be specified when using an encrypted private key"
)
}
decryptedPrivateKeyBytes
,
err
:=
x509
.
DecryptPEMBlock
(
PEMBlock
,
[]
byte
(
passphrase
))
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"Failed decrypting private key: %s"
,
err
)
}
b
:=
&
pem
.
Block
{
Type
:
"RSA PRIVATE KEY"
,
Bytes
:
decryptedPrivateKeyBytes
,
}
return
pem
.
EncodeToMemory
(
b
),
nil
}
return
rawPrivateKeyBytes
,
nil
}
builder/googlecompute/ssh.go
0 → 100644
View file @
58c73727
package
googlecompute
import
(
"fmt"
gossh
"code.google.com/p/go.crypto/ssh"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/communicator/ssh"
)
// sshAddress returns the ssh address.
func
sshAddress
(
state
multistep
.
StateBag
)
(
string
,
error
)
{
config
:=
state
.
Get
(
"config"
)
.
(
config
)
ipAddress
:=
state
.
Get
(
"instance_ip"
)
.
(
string
)
return
fmt
.
Sprintf
(
"%s:%d"
,
ipAddress
,
config
.
SSHPort
),
nil
}
// sshConfig returns the ssh configuration.
func
sshConfig
(
state
multistep
.
StateBag
)
(
*
gossh
.
ClientConfig
,
error
)
{
config
:=
state
.
Get
(
"config"
)
.
(
config
)
privateKey
:=
state
.
Get
(
"ssh_private_key"
)
.
(
string
)
keyring
:=
new
(
ssh
.
SimpleKeychain
)
if
err
:=
keyring
.
AddPEMKey
(
privateKey
);
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"Error setting up SSH config: %s"
,
err
)
}
sshConfig
:=
&
gossh
.
ClientConfig
{
User
:
config
.
SSHUsername
,
Auth
:
[]
gossh
.
ClientAuth
{
gossh
.
ClientAuthKeyring
(
keyring
)},
}
return
sshConfig
,
nil
}
builder/googlecompute/step_create_image.go
0 → 100644
View file @
58c73727
// Copyright (c) 2013 Kelsey Hightower. All rights reserved.
// Use of this source code is governed by the Apache License, Version 2.0
// that can be found in the LICENSE file.
package
googlecompute
import
(
"fmt"
"path/filepath"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
)
// stepCreateImage represents a Packer build step that creates GCE machine
// images.
type
stepCreateImage
int
// Run executes the Packer build step that creates a GCE machine image.
//
// Currently the only way to create a GCE image is to run the gcimagebundle
// command on the running GCE instance.
func
(
s
*
stepCreateImage
)
Run
(
state
multistep
.
StateBag
)
multistep
.
StepAction
{
var
(
config
=
state
.
Get
(
"config"
)
.
(
config
)
comm
=
state
.
Get
(
"communicator"
)
.
(
packer
.
Communicator
)
sudoPrefix
=
""
ui
=
state
.
Get
(
"ui"
)
.
(
packer
.
Ui
)
)
ui
.
Say
(
"Creating image..."
)
if
config
.
SSHUsername
!=
"root"
{
sudoPrefix
=
"sudo "
}
imageFilename
:=
fmt
.
Sprintf
(
"%s.tar.gz"
,
config
.
ImageName
)
imageBundleCmd
:=
"/usr/bin/gcimagebundle -d /dev/sda -o /tmp/"
cmd
:=
new
(
packer
.
RemoteCmd
)
cmd
.
Command
=
fmt
.
Sprintf
(
"%s%s --output_file_name %s"
,
sudoPrefix
,
imageBundleCmd
,
imageFilename
)
err
:=
cmd
.
StartWithUi
(
comm
,
ui
)
if
err
!=
nil
{
err
:=
fmt
.
Errorf
(
"Error creating image: %s"
,
err
)
state
.
Put
(
"error"
,
err
)
ui
.
Error
(
err
.
Error
())
return
multistep
.
ActionHalt
}
state
.
Put
(
"image_file_name"
,
filepath
.
Join
(
"/tmp"
,
imageFilename
))
return
multistep
.
ActionContinue
}
func
(
s
*
stepCreateImage
)
Cleanup
(
state
multistep
.
StateBag
)
{}
builder/googlecompute/step_create_instance.go
0 → 100644
View file @
58c73727
// Copyright (c) 2013 Kelsey Hightower. All rights reserved.
// Use of this source code is governed by the Apache License, Version 2.0
// that can be found in the LICENSE file.
package
googlecompute
import
(
"fmt"
"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"
)
// stepCreateInstance represents a Packer build step that creates GCE instances.
type
stepCreateInstance
struct
{
instanceName
string
}
// Run executes the Packer build step that creates a GCE instance.
func
(
s
*
stepCreateInstance
)
Run
(
state
multistep
.
StateBag
)
multistep
.
StepAction
{
var
(
client
=
state
.
Get
(
"client"
)
.
(
*
GoogleComputeClient
)
config
=
state
.
Get
(
"config"
)
.
(
config
)
ui
=
state
.
Get
(
"ui"
)
.
(
packer
.
Ui
)
)
ui
.
Say
(
"Creating instance..."
)
name
:=
fmt
.
Sprintf
(
"packer-%s"
,
uuid
.
TimeOrderedUUID
())
// Build up the instance config.
instanceConfig
:=
&
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
}
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.
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
)
)
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
)
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_ssh_key.go
0 → 100644
View file @
58c73727
// Copyright (c) 2013 Kelsey Hightower. All rights reserved.
// Use of this source code is governed by the Apache License, Version 2.0
// that can be found in the LICENSE file.
package
googlecompute
import
(
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"fmt"
"code.google.com/p/go.crypto/ssh"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
)
// stepCreateSSHKey represents a Packer build step that generates SSH key pairs.
type
stepCreateSSHKey
int
// Run executes the Packer build step that generates SSH key pairs.
func
(
s
*
stepCreateSSHKey
)
Run
(
state
multistep
.
StateBag
)
multistep
.
StepAction
{
var
(
ui
=
state
.
Get
(
"ui"
)
.
(
packer
.
Ui
)
)
ui
.
Say
(
"Creating temporary ssh key for instance..."
)
priv
,
err
:=
rsa
.
GenerateKey
(
rand
.
Reader
,
2014
)
if
err
!=
nil
{
err
:=
fmt
.
Errorf
(
"Error creating temporary ssh key: %s"
,
err
)
state
.
Put
(
"error"
,
err
)
ui
.
Error
(
err
.
Error
())
return
multistep
.
ActionHalt
}
priv_der
:=
x509
.
MarshalPKCS1PrivateKey
(
priv
)
priv_blk
:=
pem
.
Block
{
Type
:
"RSA PRIVATE KEY"
,
Headers
:
nil
,
Bytes
:
priv_der
,
}
pub
,
err
:=
ssh
.
NewPublicKey
(
&
priv
.
PublicKey
)
if
err
!=
nil
{
err
:=
fmt
.
Errorf
(
"Error creating temporary ssh key: %s"
,
err
)
state
.
Put
(
"error"
,
err
)
ui
.
Error
(
err
.
Error
())
return
multistep
.
ActionHalt
}
state
.
Put
(
"ssh_private_key"
,
string
(
pem
.
EncodeToMemory
(
&
priv_blk
)))
state
.
Put
(
"ssh_public_key"
,
string
(
ssh
.
MarshalAuthorizedKey
(
pub
)))
return
multistep
.
ActionContinue
}
// Cleanup.
// Nothing to clean up. SSH keys are associated with a single GCE instance.
func
(
s
*
stepCreateSSHKey
)
Cleanup
(
state
multistep
.
StateBag
)
{}
builder/googlecompute/step_instance_info.go
0 → 100644
View file @
58c73727
// Copyright (c) 2013 Kelsey Hightower. All rights reserved.
// Use of this source code is governed by the Apache License, Version 2.0
// that can be found in the LICENSE file.
package
googlecompute
import
(
"fmt"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
)
// stepInstanceInfo represents a Packer build step that gathers GCE instance info.
type
stepInstanceInfo
int
// Run executes the Packer build step that gathers GCE instance info.
func
(
s
*
stepInstanceInfo
)
Run
(
state
multistep
.
StateBag
)
multistep
.
StepAction
{
var
(
client
=
state
.
Get
(
"client"
)
.
(
*
GoogleComputeClient
)
config
=
state
.
Get
(
"config"
)
.
(
config
)
ui
=
state
.
Get
(
"ui"
)
.
(
packer
.
Ui
)
)
instanceName
:=
state
.
Get
(
"instance_name"
)
.
(
string
)
err
:=
waitForInstanceState
(
"RUNNING"
,
config
.
Zone
,
instanceName
,
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
}
ip
,
err
:=
client
.
GetNatIP
(
config
.
Zone
,
instanceName
)
if
err
!=
nil
{
err
:=
fmt
.
Errorf
(
"Error retrieving instance nat ip address: %s"
,
err
)
state
.
Put
(
"error"
,
err
)
ui
.
Error
(
err
.
Error
())
return
multistep
.
ActionHalt
}
state
.
Put
(
"instance_ip"
,
ip
)
return
multistep
.
ActionContinue
}
// Cleanup.
func
(
s
*
stepInstanceInfo
)
Cleanup
(
state
multistep
.
StateBag
)
{}
builder/googlecompute/step_register_image.go
0 → 100644
View file @
58c73727
// Copyright (c) 2013 Kelsey Hightower. All rights reserved.
// Use of this source code is governed by the Apache License, Version 2.0
// that can be found in the LICENSE file.
package
googlecompute
import
(
"fmt"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
)
// stepRegisterImage represents a Packer build step that registers GCE machine images.
type
stepRegisterImage
int
// Run executes the Packer build step that registers a GCE machine image.
func
(
s
*
stepRegisterImage
)
Run
(
state
multistep
.
StateBag
)
multistep
.
StepAction
{
var
(
client
=
state
.
Get
(
"client"
)
.
(
*
GoogleComputeClient
)
config
=
state
.
Get
(
"config"
)
.
(
config
)
ui
=
state
.
Get
(
"ui"
)
.
(
packer
.
Ui
)
)
ui
.
Say
(
"Adding image to the project..."
)
imageURL
:=
fmt
.
Sprintf
(
"https://storage.cloud.google.com/%s/%s.tar.gz"
,
config
.
BucketName
,
config
.
ImageName
)
operation
,
err
:=
client
.
CreateImage
(
config
.
ImageName
,
config
.
ImageDescription
,
imageURL
)
if
err
!=
nil
{
err
:=
fmt
.
Errorf
(
"Error creating image: %s"
,
err
)
state
.
Put
(
"error"
,
err
)
ui
.
Error
(
err
.
Error
())
return
multistep
.
ActionHalt
}
ui
.
Say
(
"Waiting for image to become available..."
)
err
=
waitForGlobalOperationState
(
"DONE"
,
operation
.
Name
,
client
,
config
.
stateTimeout
)
if
err
!=
nil
{
err
:=
fmt
.
Errorf
(
"Error creating image: %s"
,
err
)
state
.
Put
(
"error"
,
err
)
ui
.
Error
(
err
.
Error
())
return
multistep
.
ActionHalt
}
state
.
Put
(
"image_name"
,
config
.
ImageName
)
return
multistep
.
ActionContinue
}
// Cleanup.
func
(
s
*
stepRegisterImage
)
Cleanup
(
state
multistep
.
StateBag
)
{}
builder/googlecompute/step_update_gsutil.go
0 → 100644
View file @
58c73727
// Copyright (c) 2013 Kelsey Hightower. All rights reserved.
// Use of this source code is governed by the Apache License, Version 2.0
// that can be found in the LICENSE file.
package
googlecompute
import
(
"fmt"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
)
// stepUpdateGsutil represents a Packer build step that updates the gsutil
// utility to the latest version available.
type
stepUpdateGsutil
int
// Run executes the Packer build step that updates the gsutil utility to the
// latest version available.
//
// This step is required to prevent the image creation process from hanging;
// the image creation process utilizes the gcimagebundle cli tool which will
// prompt to update gsutil if a newer version is available.
func
(
s
*
stepUpdateGsutil
)
Run
(
state
multistep
.
StateBag
)
multistep
.
StepAction
{
var
(
config
=
state
.
Get
(
"config"
)
.
(
config
)
comm
=
state
.
Get
(
"communicator"
)
.
(
packer
.
Communicator
)
sudoPrefix
=
""
ui
=
state
.
Get
(
"ui"
)
.
(
packer
.
Ui
)
)
ui
.
Say
(
"Updating gsutil..."
)
if
config
.
SSHUsername
!=
"root"
{
sudoPrefix
=
"sudo "
}
gsutilUpdateCmd
:=
"/usr/local/bin/gsutil update -n -f"
cmd
:=
new
(
packer
.
RemoteCmd
)
cmd
.
Command
=
fmt
.
Sprintf
(
"%s%s"
,
sudoPrefix
,
gsutilUpdateCmd
)
err
:=
cmd
.
StartWithUi
(
comm
,
ui
)
if
err
!=
nil
{
err
:=
fmt
.
Errorf
(
"Error updating gsutil: %s"
,
err
)
state
.
Put
(
"error"
,
err
)
ui
.
Error
(
err
.
Error
())
return
multistep
.
ActionHalt
}
return
multistep
.
ActionContinue
}
// Cleanup.
func
(
s
*
stepUpdateGsutil
)
Cleanup
(
state
multistep
.
StateBag
)
{}
builder/googlecompute/step_upload_image.go
0 → 100644
View file @
58c73727
// Copyright (c) 2013 Kelsey Hightower. All rights reserved.
// Use of this source code is governed by the Apache License, Version 2.0
// that can be found in the LICENSE file.
package
googlecompute
import
(
"fmt"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
)
// stepUploadImage represents a Packer build step that uploads GCE machine images.
type
stepUploadImage
int
// Run executes the Packer build step that uploads a GCE machine image.
func
(
s
*
stepUploadImage
)
Run
(
state
multistep
.
StateBag
)
multistep
.
StepAction
{
var
(
config
=
state
.
Get
(
"config"
)
.
(
config
)
comm
=
state
.
Get
(
"communicator"
)
.
(
packer
.
Communicator
)
sudoPrefix
=
""
ui
=
state
.
Get
(
"ui"
)
.
(
packer
.
Ui
)
imageFilename
=
state
.
Get
(
"image_file_name"
)
.
(
string
)
)
ui
.
Say
(
"Uploading image..."
)
if
config
.
SSHUsername
!=
"root"
{
sudoPrefix
=
"sudo "
}
cmd
:=
new
(
packer
.
RemoteCmd
)
cmd
.
Command
=
fmt
.
Sprintf
(
"%s/usr/local/bin/gsutil cp %s gs://%s"
,
sudoPrefix
,
imageFilename
,
config
.
BucketName
)
err
:=
cmd
.
StartWithUi
(
comm
,
ui
)
if
err
!=
nil
{
err
:=
fmt
.
Errorf
(
"Error uploading image: %s"
,
err
)
state
.
Put
(
"error"
,
err
)
ui
.
Error
(
err
.
Error
())
return
multistep
.
ActionHalt
}
return
multistep
.
ActionContinue
}
// Cleanup.
func
(
s
*
stepUploadImage
)
Cleanup
(
state
multistep
.
StateBag
)
{}
builder/googlecompute/wait.go
0 → 100644
View file @
58c73727
// Copyright (c) 2013 Kelsey Hightower. All rights reserved.
// Use of this source code is governed by the Apache License, Version 2.0
// that can be found in the LICENSE file.
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
)
}
// 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
)
}
// 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
}
}
}()
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
}
}
plugin/builder-googlecompute/main.go
0 → 100644
View file @
58c73727
// Copyright (c) 2013 Kelsey Hightower. All rights reserved.
// Use of this source code is governed by the Apache License, Version 2.0
// that can be found in the LICENSE file.
package
main
import
(
"github.com/kelseyhightower/packer-builder-googlecompute/builder/googlecompute"
"github.com/mitchellh/packer/packer/plugin"
)
func
main
()
{
plugin
.
ServeBuilder
(
new
(
googlecompute
.
Builder
))
}
plugin/builder-googlecompute/main_test.go
0 → 100644
View file @
58c73727
package
main
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