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
36326ee8
Commit
36326ee8
authored
Sep 26, 2013
by
Matthew Hooker
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'master' into 430
parents
7fa23850
6face65e
Changes
43
Hide whitespace changes
Inline
Side-by-side
Showing
43 changed files
with
1286 additions
and
201 deletions
+1286
-201
CHANGELOG.md
CHANGELOG.md
+31
-4
Makefile
Makefile
+9
-3
builder/amazon/common/access_config.go
builder/amazon/common/access_config.go
+8
-1
builder/amazon/instance/builder.go
builder/amazon/instance/builder.go
+1
-1
builder/digitalocean/step_create_ssh_key.go
builder/digitalocean/step_create_ssh_key.go
+2
-1
builder/virtualbox/builder.go
builder/virtualbox/builder.go
+2
-0
builder/virtualbox/step_attach_guest_additions.go
builder/virtualbox/step_attach_guest_additions.go
+81
-0
builder/virtualbox/step_upload_guest_additions.go
builder/virtualbox/step_upload_guest_additions.go
+7
-0
common/config.go
common/config.go
+3
-2
communicator/ssh/keychain.go
communicator/ssh/keychain.go
+2
-2
packer/plugin/client.go
packer/plugin/client.go
+18
-4
packer/plugin/client_test.go
packer/plugin/client_test.go
+15
-0
packer/plugin/plugin.go
packer/plugin/plugin.go
+8
-1
packer/plugin/plugin_test.go
packer/plugin/plugin_test.go
+6
-3
packer/rpc/build.go
packer/rpc/build.go
+2
-2
packer/rpc/builder.go
packer/rpc/builder.go
+3
-4
packer/rpc/command.go
packer/rpc/command.go
+1
-1
packer/rpc/communicator.go
packer/rpc/communicator.go
+6
-6
packer/rpc/dial.go
packer/rpc/dial.go
+33
-0
packer/rpc/environment.go
packer/rpc/environment.go
+6
-6
packer/rpc/hook.go
packer/rpc/hook.go
+1
-1
packer/rpc/post_processor.go
packer/rpc/post_processor.go
+2
-2
packer/rpc/provisioner.go
packer/rpc/provisioner.go
+1
-1
packer/template.go
packer/template.go
+139
-23
packer/template_test.go
packer/template_test.go
+418
-2
packer/version.go
packer/version.go
+1
-1
post-processor/vagrant/post-processor.go
post-processor/vagrant/post-processor.go
+42
-26
provisioner/chef-solo/provisioner.go
provisioner/chef-solo/provisioner.go
+60
-4
provisioner/chef-solo/provisioner_test.go
provisioner/chef-solo/provisioner_test.go
+66
-0
provisioner/puppet-masterless/provisioner.go
provisioner/puppet-masterless/provisioner.go
+3
-1
provisioner/shell/provisioner.go
provisioner/shell/provisioner.go
+10
-0
provisioner/shell/unix_reader.go
provisioner/shell/unix_reader.go
+88
-0
provisioner/shell/unix_reader_test.go
provisioner/shell/unix_reader_test.go
+33
-0
scripts/build.sh
scripts/build.sh
+39
-0
website/Gemfile
website/Gemfile
+4
-4
website/Gemfile.lock
website/Gemfile.lock
+67
-92
website/source/docs/builders/virtualbox.html.markdown
website/source/docs/builders/virtualbox.html.markdown
+4
-0
website/source/docs/provisioners/chef-solo.html.markdown
website/source/docs/provisioners/chef-solo.html.markdown
+8
-0
website/source/docs/provisioners/puppet-masterless.html.markdown
.../source/docs/provisioners/puppet-masterless.html.markdown
+1
-1
website/source/docs/provisioners/shell.html.markdown
website/source/docs/provisioners/shell.html.markdown
+5
-1
website/source/docs/templates/post-processors.html.markdown
website/source/docs/templates/post-processors.html.markdown
+25
-0
website/source/docs/templates/provisioners.html.markdown
website/source/docs/templates/provisioners.html.markdown
+24
-0
website/source/docs/templates/user-variables.html.markdown
website/source/docs/templates/user-variables.html.markdown
+1
-1
No files found.
CHANGELOG.md
View file @
36326ee8
## 0.3.8 (unreleased)
## 0.3.9 (unreleased)
BUG FIXES:
*
core: default user variable values don't need to be strings. [GH-456]
## 0.3.8 (September 22, 2013)
FEATURES:
*
core: You can now specify
`only`
and
`except`
configurations on any
provisioner or post-processor to specify a list of builds that they
are valid for. [GH-438]
*
builders/virtualbox: Guest additions can be attached rather than uploaded,
easier to handle for Windows guests. [GH-405]
*
provisioner/chef-solo: Ability to specify a custom Chef configuration
template.
*
provisioner/chef-solo: Roles and data bags support. [GH-348]
IMPROVEMENTS:
*
builder/amazon/
*
: Interrupts work while waiting for AMI to be ready.
*
core: User variables can now be used for integer, boolean, etc.
values. [GH-418]
*
core: Plugins made with incompatible versions will no longer load.
*
builder/amazon/all: Interrupts work while waiting for AMI to be ready.
*
provisioner/shell: Script line-endings are automatically converted to
Unix-style line-endings. Can be disabled by setting "binary" to "true".
[GH-277]
BUG FIXES:
*
builder/amazon/
*
: While waiting for AMI, will detect "failed" state.
*
builder/amazon/
*
: Waiting for state will detect if the resource (AMI,
*
core: Set TCP KeepAlives on internally created RPC connections so that
they don't die. [GH-416]
*
builder/amazon/all: While waiting for AMI, will detect "failed" state.
*
builder/amazon/all: Waiting for state will detect if the resource (AMI,
instance, etc.) disappears from under it.
*
builder/amazon/instance: Exclude only contents of /tmp, not /tmp
itself. [GH-437]
*
builder/amazon/instance: Make AccessKey/SecretKey available to bundle
command even when they come from the environment. [GH-434]
*
builder/virtualbox: F1-F12 and delete scancodes now work. [GH-425]
*
post-processor/vagrant: Override configurations properly work. [GH-426]
*
provisioner/puppet-masterless: Fix failure case when both facter vars
are used and prevent_sudo. [GH-415]
*
provisioner/puppet-masterless: User variables now work properly in
manifest file and hiera path. [GH-448]
## 0.3.7 (September 9, 2013)
...
...
Makefile
View file @
36326ee8
...
...
@@ -2,16 +2,22 @@ NO_COLOR=\033[0m
OK_COLOR
=
\0
33[32
;
01m
ERROR_COLOR
=
\0
33[31
;
01m
WARN_COLOR
=
\0
33[33
;
01m
DEPS
=
$
(
go list
-f
'{{range .TestImports}}{{.}} {{end}}'
./...
)
all
:
deps
@
mkdir
-p
bin/
@
echo
"
$(OK_COLOR)
==> Building
$(NO_COLOR)
"
@
./scripts/build.sh
@
bash
--norc
-i
./scripts/build.sh
deps
:
@
echo
"
$(OK_COLOR)
==> Installing dependencies
$(NO_COLOR)
"
@
go get
-d
-v
./...
@
go list
-f
'{{range .TestImports}}{{.}} {{end}}'
./... | xargs
-n1
go get
-d
@
echo
$(DEPS)
| xargs
-n1
go get
-d
updatedeps
:
@
echo
"
$(OK_COLOR)
==> Updating all dependencies
$(NO_COLOR)
"
@
go get
-d
-v
-u
./...
@
echo
$(DEPS)
| xargs
-n1
go get
-d
-u
clean
:
@
rm
-rf
bin/
local
/ pkg/ src/ website/.sass-cache website/build
...
...
@@ -23,4 +29,4 @@ test: deps
@
echo
"
$(OK_COLOR)
==> Testing Packer...
$(NO_COLOR)
"
go
test
./...
.PHONY
:
all
deps format test
.PHONY
:
all
clean deps format test updatedeps
builder/amazon/common/access_config.go
View file @
36326ee8
...
...
@@ -18,7 +18,14 @@ type AccessConfig struct {
// Auth returns a valid aws.Auth object for access to AWS services, or
// an error if the authentication couldn't be resolved.
func
(
c
*
AccessConfig
)
Auth
()
(
aws
.
Auth
,
error
)
{
return
aws
.
GetAuth
(
c
.
AccessKey
,
c
.
SecretKey
)
auth
,
err
:=
aws
.
GetAuth
(
c
.
AccessKey
,
c
.
SecretKey
)
if
err
==
nil
{
// Store the accesskey and secret that we got...
c
.
AccessKey
=
auth
.
AccessKey
c
.
SecretKey
=
auth
.
SecretKey
}
return
auth
,
err
}
// Region returns the aws.Region object for access to AWS services, requesting
...
...
builder/amazon/instance/builder.go
View file @
36326ee8
...
...
@@ -83,7 +83,7 @@ func (b *Builder) Prepare(raws ...interface{}) error {
"-u {{.AccountId}} "
+
"-c {{.CertPath}} "
+
"-r {{.Architecture}} "
+
"-e {{.PrivatePath}} "
+
"-e {{.PrivatePath}}
/*
"
+
"-d {{.Destination}} "
+
"-p {{.Prefix}} "
+
"--batch"
...
...
builder/digitalocean/step_create_ssh_key.go
View file @
36326ee8
...
...
@@ -38,7 +38,8 @@ func (s *stepCreateSSHKey) Run(state multistep.StateBag) multistep.StepAction {
state
.
Put
(
"privateKey"
,
string
(
pem
.
EncodeToMemory
(
&
priv_blk
)))
// Marshal the public key into SSH compatible format
pub
:=
ssh
.
NewRSAPublicKey
(
&
priv
.
PublicKey
)
// TODO properly handle the public key error
pub
,
_
:=
ssh
.
NewPublicKey
(
&
priv
.
PublicKey
)
pub_sshformat
:=
string
(
ssh
.
MarshalAuthorizedKey
(
pub
))
// The name of the public key on DO
...
...
builder/virtualbox/builder.go
View file @
36326ee8
...
...
@@ -28,6 +28,7 @@ type config struct {
DiskSize
uint
`mapstructure:"disk_size"`
FloppyFiles
[]
string
`mapstructure:"floppy_files"`
Format
string
`mapstructure:"format"`
GuestAdditionsAttach
bool
`mapstructure:"guest_additions_attach"`
GuestAdditionsPath
string
`mapstructure:"guest_additions_path"`
GuestAdditionsURL
string
`mapstructure:"guest_additions_url"`
GuestAdditionsSHA256
string
`mapstructure:"guest_additions_sha256"`
...
...
@@ -361,6 +362,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
new
(
stepCreateVM
),
new
(
stepCreateDisk
),
new
(
stepAttachISO
),
new
(
stepAttachGuestAdditions
),
new
(
stepAttachFloppy
),
new
(
stepForwardSSH
),
new
(
stepVBoxManage
),
...
...
builder/virtualbox/step_attach_guest_additions.go
0 → 100644
View file @
36326ee8
package
virtualbox
import
(
"fmt"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
"log"
)
// This step attaches the VirtualBox guest additions as a inserted CD onto
// the virtual machine.
//
// Uses:
// config *config
// driver Driver
// guest_additions_path string
// ui packer.Ui
// vmName string
//
// Produces:
type
stepAttachGuestAdditions
struct
{
attachedPath
string
}
func
(
s
*
stepAttachGuestAdditions
)
Run
(
state
multistep
.
StateBag
)
multistep
.
StepAction
{
config
:=
state
.
Get
(
"config"
)
.
(
*
config
)
driver
:=
state
.
Get
(
"driver"
)
.
(
Driver
)
guestAdditionsPath
:=
state
.
Get
(
"guest_additions_path"
)
.
(
string
)
ui
:=
state
.
Get
(
"ui"
)
.
(
packer
.
Ui
)
vmName
:=
state
.
Get
(
"vmName"
)
.
(
string
)
// If we're not attaching the guest additions then just return
if
!
config
.
GuestAdditionsAttach
{
log
.
Println
(
"Not attaching guest additions since we're uploading."
)
return
multistep
.
ActionContinue
}
// Attach the guest additions to the computer
log
.
Println
(
"Attaching guest additions ISO onto IDE controller..."
)
command
:=
[]
string
{
"storageattach"
,
vmName
,
"--storagectl"
,
"IDE Controller"
,
"--port"
,
"1"
,
"--device"
,
"0"
,
"--type"
,
"dvddrive"
,
"--medium"
,
guestAdditionsPath
,
}
if
err
:=
driver
.
VBoxManage
(
command
...
);
err
!=
nil
{
err
:=
fmt
.
Errorf
(
"Error attaching guest additions: %s"
,
err
)
state
.
Put
(
"error"
,
err
)
ui
.
Error
(
err
.
Error
())
return
multistep
.
ActionHalt
}
// Track the path so that we can unregister it from VirtualBox later
s
.
attachedPath
=
guestAdditionsPath
return
multistep
.
ActionContinue
}
func
(
s
*
stepAttachGuestAdditions
)
Cleanup
(
state
multistep
.
StateBag
)
{
if
s
.
attachedPath
==
""
{
return
}
driver
:=
state
.
Get
(
"driver"
)
.
(
Driver
)
ui
:=
state
.
Get
(
"ui"
)
.
(
packer
.
Ui
)
vmName
:=
state
.
Get
(
"vmName"
)
.
(
string
)
command
:=
[]
string
{
"storageattach"
,
vmName
,
"--storagectl"
,
"IDE Controller"
,
"--port"
,
"1"
,
"--device"
,
"0"
,
"--medium"
,
"none"
,
}
if
err
:=
driver
.
VBoxManage
(
command
...
);
err
!=
nil
{
ui
.
Error
(
fmt
.
Sprintf
(
"Error unregistering guest additions: %s"
,
err
))
}
}
builder/virtualbox/step_upload_guest_additions.go
View file @
36326ee8
...
...
@@ -4,6 +4,7 @@ import (
"fmt"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
"log"
"os"
)
...
...
@@ -21,6 +22,12 @@ func (s *stepUploadGuestAdditions) Run(state multistep.StateBag) multistep.StepA
guestAdditionsPath
:=
state
.
Get
(
"guest_additions_path"
)
.
(
string
)
ui
:=
state
.
Get
(
"ui"
)
.
(
packer
.
Ui
)
// If we're attaching then don't do this, since we attached.
if
config
.
GuestAdditionsAttach
{
log
.
Println
(
"Not uploading guest additions since we're attaching."
)
return
multistep
.
ActionContinue
}
version
,
err
:=
driver
.
Version
()
if
err
!=
nil
{
state
.
Put
(
"error"
,
fmt
.
Errorf
(
"Error reading version for guest additions upload: %s"
,
err
))
...
...
common/config.go
View file @
36326ee8
...
...
@@ -41,8 +41,9 @@ func CheckUnusedConfig(md *mapstructure.Metadata) *packer.MultiError {
func
DecodeConfig
(
target
interface
{},
raws
...
interface
{})
(
*
mapstructure
.
Metadata
,
error
)
{
var
md
mapstructure
.
Metadata
decoderConfig
:=
&
mapstructure
.
DecoderConfig
{
Metadata
:
&
md
,
Result
:
target
,
Metadata
:
&
md
,
Result
:
target
,
WeaklyTypedInput
:
true
,
}
decoder
,
err
:=
mapstructure
.
NewDecoder
(
decoderConfig
)
...
...
communicator/ssh/keychain.go
View file @
36326ee8
...
...
@@ -60,9 +60,9 @@ func (k *SimpleKeychain) Key(i int) (ssh.PublicKey, error) {
}
switch
key
:=
k
.
keys
[
i
]
.
(
type
)
{
case
*
rsa
.
PrivateKey
:
return
ssh
.
New
RSAPublicKey
(
&
key
.
PublicKey
),
nil
return
ssh
.
New
PublicKey
(
&
key
.
PublicKey
)
case
*
dsa
.
PrivateKey
:
return
ssh
.
New
DSAPublicKey
(
&
key
.
PublicKey
),
nil
return
ssh
.
New
PublicKey
(
&
key
.
PublicKey
)
}
panic
(
"unknown key type"
)
}
...
...
packer/plugin/client.go
View file @
36326ee8
...
...
@@ -317,10 +317,24 @@ func (c *Client) Start() (address string, err error) {
err
=
errors
.
New
(
"timeout while waiting for plugin to start"
)
case
<-
exitCh
:
err
=
errors
.
New
(
"plugin exited before we could connect"
)
case
line
:=
<-
linesCh
:
// Trim the address and reset the err since we were able
// to read some sort of address.
c
.
address
=
strings
.
TrimSpace
(
string
(
line
))
case
lineBytes
:=
<-
linesCh
:
// Trim the line and split by "|" in order to get the parts of
// the output.
line
:=
strings
.
TrimSpace
(
string
(
lineBytes
))
parts
:=
strings
.
SplitN
(
line
,
"|"
,
2
)
if
len
(
parts
)
<
2
{
err
=
fmt
.
Errorf
(
"Unrecognized remote plugin message: %s"
,
line
)
return
}
// Test the API version
if
parts
[
0
]
!=
APIVersion
{
err
=
fmt
.
Errorf
(
"Incompatible API version with plugin. "
+
"Plugin version: %s, Ours: %s"
,
parts
[
0
],
APIVersion
)
return
}
c
.
address
=
parts
[
1
]
address
=
c
.
address
}
...
...
packer/plugin/client_test.go
View file @
36326ee8
...
...
@@ -37,6 +37,21 @@ func TestClient(t *testing.T) {
}
}
func
TestClientStart_badVersion
(
t
*
testing
.
T
)
{
config
:=
&
ClientConfig
{
Cmd
:
helperProcess
(
"bad-version"
),
StartTimeout
:
50
*
time
.
Millisecond
,
}
c
:=
NewClient
(
config
)
defer
c
.
Kill
()
_
,
err
:=
c
.
Start
()
if
err
==
nil
{
t
.
Fatal
(
"err should not be nil"
)
}
}
func
TestClient_Start_Timeout
(
t
*
testing
.
T
)
{
config
:=
&
ClientConfig
{
Cmd
:
helperProcess
(
"start-timeout"
),
...
...
packer/plugin/plugin.go
View file @
36326ee8
...
...
@@ -30,9 +30,16 @@ var Interrupts int32 = 0
const
MagicCookieKey
=
"PACKER_PLUGIN_MAGIC_COOKIE"
const
MagicCookieValue
=
"d602bf8f470bc67ca7faa0386276bbdd4330efaf76d1a219cb4d6991ca9872b2"
// The APIVersion is outputted along with the RPC address. The plugin
// client validates this API version and will show an error if it doesn't
// know how to speak it.
const
APIVersion
=
"1"
// This serves a single RPC connection on the given RPC server on
// a random port.
func
serve
(
server
*
rpc
.
Server
)
(
err
error
)
{
log
.
Printf
(
"Plugin build against Packer '%s'"
,
packer
.
GitCommit
)
if
os
.
Getenv
(
MagicCookieKey
)
!=
MagicCookieValue
{
return
errors
.
New
(
"Please do not execute plugins directly. Packer will execute these for you."
)
}
...
...
@@ -75,7 +82,7 @@ func serve(server *rpc.Server) (err error) {
// Output the address to stdout
log
.
Printf
(
"Plugin address: %s
\n
"
,
address
)
fmt
.
Print
ln
(
address
)
fmt
.
Print
f
(
"%s|%s
\n
"
,
APIVersion
,
address
)
os
.
Stdout
.
Sync
()
// Accept a connection
...
...
packer/plugin/plugin_test.go
View file @
36326ee8
...
...
@@ -50,6 +50,9 @@ func TestHelperProcess(*testing.T) {
cmd
,
args
:=
args
[
0
],
args
[
1
:
]
switch
cmd
{
case
"bad-version"
:
fmt
.
Printf
(
"%s1|:1234
\n
"
,
APIVersion
)
<-
make
(
chan
int
)
case
"builder"
:
ServeBuilder
(
new
(
helperBuilder
))
case
"command"
:
...
...
@@ -59,7 +62,7 @@ func TestHelperProcess(*testing.T) {
case
"invalid-rpc-address"
:
fmt
.
Println
(
"lolinvalid"
)
case
"mock"
:
fmt
.
Print
ln
(
":1234"
)
fmt
.
Print
f
(
"%s|:1234
\n
"
,
APIVersion
)
<-
make
(
chan
int
)
case
"post-processor"
:
ServePostProcessor
(
new
(
helperPostProcessor
))
...
...
@@ -69,11 +72,11 @@ func TestHelperProcess(*testing.T) {
time
.
Sleep
(
1
*
time
.
Minute
)
os
.
Exit
(
1
)
case
"stderr"
:
fmt
.
Print
ln
(
":1234"
)
fmt
.
Print
f
(
"%s|:1234
\n
"
,
APIVersion
)
log
.
Println
(
"HELLO"
)
log
.
Println
(
"WORLD"
)
case
"stdin"
:
fmt
.
Print
ln
(
":1234"
)
fmt
.
Print
f
(
"%s|:1234
\n
"
,
APIVersion
)
data
:=
make
([]
byte
,
5
)
if
_
,
err
:=
os
.
Stdin
.
Read
(
data
);
err
!=
nil
{
log
.
Printf
(
"stdin read error: %s"
,
err
)
...
...
packer/rpc/build.go
View file @
36326ee8
...
...
@@ -52,7 +52,7 @@ func (b *build) Run(ui packer.Ui, cache packer.Cache) ([]packer.Artifact, error)
artifacts
:=
make
([]
packer
.
Artifact
,
len
(
result
))
for
i
,
addr
:=
range
result
{
client
,
err
:=
rpc
.
Dial
(
"tcp"
,
addr
)
client
,
err
:=
rpc
Dial
(
addr
)
if
err
!=
nil
{
return
nil
,
err
}
...
...
@@ -92,7 +92,7 @@ func (b *BuildServer) Prepare(v map[string]string, reply *error) error {
}
func
(
b
*
BuildServer
)
Run
(
args
*
BuildRunArgs
,
reply
*
[]
string
)
error
{
client
,
err
:=
rpc
.
Dial
(
"tcp"
,
args
.
UiRPCAddress
)
client
,
err
:=
rpc
Dial
(
args
.
UiRPCAddress
)
if
err
!=
nil
{
return
err
}
...
...
packer/rpc/builder.go
View file @
36326ee8
...
...
@@ -5,7 +5,6 @@ import (
"fmt"
"github.com/mitchellh/packer/packer"
"log"
"net"
"net/rpc"
)
...
...
@@ -95,7 +94,7 @@ func (b *builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
return
nil
,
nil
}
client
,
err
:=
rpc
.
Dial
(
"tcp"
,
response
.
RPCAddress
)
client
,
err
:=
rpc
Dial
(
response
.
RPCAddress
)
if
err
!=
nil
{
return
nil
,
err
}
...
...
@@ -119,12 +118,12 @@ func (b *BuilderServer) Prepare(args *BuilderPrepareArgs, reply *error) error {
}
func
(
b
*
BuilderServer
)
Run
(
args
*
BuilderRunArgs
,
reply
*
interface
{})
error
{
client
,
err
:=
rpc
.
Dial
(
"tcp"
,
args
.
RPCAddress
)
client
,
err
:=
rpc
Dial
(
args
.
RPCAddress
)
if
err
!=
nil
{
return
err
}
responseC
,
err
:=
net
.
Dial
(
"tcp"
,
args
.
ResponseAddress
)
responseC
,
err
:=
tcpDial
(
args
.
ResponseAddress
)
if
err
!=
nil
{
return
err
}
...
...
packer/rpc/command.go
View file @
36326ee8
...
...
@@ -66,7 +66,7 @@ func (c *CommandServer) Help(args *interface{}, reply *string) error {
}
func
(
c
*
CommandServer
)
Run
(
args
*
CommandRunArgs
,
reply
*
int
)
error
{
client
,
err
:=
rpc
.
Dial
(
"tcp"
,
args
.
RPCAddress
)
client
,
err
:=
rpc
Dial
(
args
.
RPCAddress
)
if
err
!=
nil
{
return
err
}
...
...
packer/rpc/communicator.go
View file @
36326ee8
...
...
@@ -177,7 +177,7 @@ func (c *CommunicatorServer) Start(args *CommunicatorStartArgs, reply *interface
toClose
:=
make
([]
net
.
Conn
,
0
)
if
args
.
StdinAddress
!=
""
{
stdinC
,
err
:=
net
.
Dial
(
"tcp"
,
args
.
StdinAddress
)
stdinC
,
err
:=
tcpDial
(
args
.
StdinAddress
)
if
err
!=
nil
{
return
err
}
...
...
@@ -187,7 +187,7 @@ func (c *CommunicatorServer) Start(args *CommunicatorStartArgs, reply *interface
}
if
args
.
StdoutAddress
!=
""
{
stdoutC
,
err
:=
net
.
Dial
(
"tcp"
,
args
.
StdoutAddress
)
stdoutC
,
err
:=
tcpDial
(
args
.
StdoutAddress
)
if
err
!=
nil
{
return
err
}
...
...
@@ -197,7 +197,7 @@ func (c *CommunicatorServer) Start(args *CommunicatorStartArgs, reply *interface
}
if
args
.
StderrAddress
!=
""
{
stderrC
,
err
:=
net
.
Dial
(
"tcp"
,
args
.
StderrAddress
)
stderrC
,
err
:=
tcpDial
(
args
.
StderrAddress
)
if
err
!=
nil
{
return
err
}
...
...
@@ -208,7 +208,7 @@ func (c *CommunicatorServer) Start(args *CommunicatorStartArgs, reply *interface
// Connect to the response address so we can write our result to it
// when ready.
responseC
,
err
:=
net
.
Dial
(
"tcp"
,
args
.
ResponseAddress
)
responseC
,
err
:=
tcpDial
(
args
.
ResponseAddress
)
if
err
!=
nil
{
return
err
}
...
...
@@ -234,7 +234,7 @@ func (c *CommunicatorServer) Start(args *CommunicatorStartArgs, reply *interface
}
func
(
c
*
CommunicatorServer
)
Upload
(
args
*
CommunicatorUploadArgs
,
reply
*
interface
{})
(
err
error
)
{
readerC
,
err
:=
net
.
Dial
(
"tcp"
,
args
.
ReaderAddress
)
readerC
,
err
:=
tcpDial
(
args
.
ReaderAddress
)
if
err
!=
nil
{
return
}
...
...
@@ -250,7 +250,7 @@ func (c *CommunicatorServer) UploadDir(args *CommunicatorUploadDirArgs, reply *e
}
func
(
c
*
CommunicatorServer
)
Download
(
args
*
CommunicatorDownloadArgs
,
reply
*
interface
{})
(
err
error
)
{
writerC
,
err
:=
net
.
Dial
(
"tcp"
,
args
.
WriterAddress
)
writerC
,
err
:=
tcpDial
(
args
.
WriterAddress
)
if
err
!=
nil
{
return
}
...
...
packer/rpc/dial.go
0 → 100644
View file @
36326ee8
package
rpc
import
(
"net"
"net/rpc"
)
// rpcDial makes a TCP connection to a remote RPC server and returns
// the client. This will set the connection up properly so that keep-alives
// are set and so on and should be used to make all RPC connections within
// this package.
func
rpcDial
(
address
string
)
(
*
rpc
.
Client
,
error
)
{
tcpConn
,
err
:=
tcpDial
(
address
)
if
err
!=
nil
{
return
nil
,
err
}
// Create an RPC client around our connection
return
rpc
.
NewClient
(
tcpConn
),
nil
}
// tcpDial connects via TCP to the designated address.
func
tcpDial
(
address
string
)
(
*
net
.
TCPConn
,
error
)
{
conn
,
err
:=
net
.
Dial
(
"tcp"
,
address
)
if
err
!=
nil
{
return
nil
,
err
}
// Set a keep-alive so that the connection stays alive even when idle
tcpConn
:=
conn
.
(
*
net
.
TCPConn
)
tcpConn
.
SetKeepAlive
(
true
)
return
tcpConn
,
nil
}
packer/rpc/environment.go
View file @
36326ee8
...
...
@@ -28,7 +28,7 @@ func (e *Environment) Builder(name string) (b packer.Builder, err error) {
return
}
client
,
err
:=
rpc
.
Dial
(
"tcp"
,
reply
)
client
,
err
:=
rpc
Dial
(
reply
)
if
err
!=
nil
{
return
}
...
...
@@ -43,7 +43,7 @@ func (e *Environment) Cache() packer.Cache {
panic
(
err
)
}
client
,
err
:=
rpc
.
Dial
(
"tcp"
,
reply
)
client
,
err
:=
rpc
Dial
(
reply
)
if
err
!=
nil
{
panic
(
err
)
}
...
...
@@ -64,7 +64,7 @@ func (e *Environment) Hook(name string) (h packer.Hook, err error) {
return
}
client
,
err
:=
rpc
.
Dial
(
"tcp"
,
reply
)
client
,
err
:=
rpc
Dial
(
reply
)
if
err
!=
nil
{
return
}
...
...
@@ -80,7 +80,7 @@ func (e *Environment) PostProcessor(name string) (p packer.PostProcessor, err er
return
}
client
,
err
:=
rpc
.
Dial
(
"tcp"
,
reply
)
client
,
err
:=
rpc
Dial
(
reply
)
if
err
!=
nil
{
return
}
...
...
@@ -96,7 +96,7 @@ func (e *Environment) Provisioner(name string) (p packer.Provisioner, err error)
return
}
client
,
err
:=
rpc
.
Dial
(
"tcp"
,
reply
)
client
,
err
:=
rpc
Dial
(
reply
)
if
err
!=
nil
{
return
}
...
...
@@ -109,7 +109,7 @@ func (e *Environment) Ui() packer.Ui {
var
reply
string
e
.
client
.
Call
(
"Environment.Ui"
,
new
(
interface
{}),
&
reply
)
client
,
err
:=
rpc
.
Dial
(
"tcp"
,
reply
)
client
,
err
:=
rpc
Dial
(
reply
)
if
err
!=
nil
{
panic
(
err
)
}
...
...
packer/rpc/hook.go
View file @
36326ee8
...
...
@@ -46,7 +46,7 @@ func (h *hook) Cancel() {
}
func
(
h
*
HookServer
)
Run
(
args
*
HookRunArgs
,
reply
*
interface
{})
error
{
client
,
err
:=
rpc
.
Dial
(
"tcp"
,
args
.
RPCAddress
)
client
,
err
:=
rpc
Dial
(
args
.
RPCAddress
)
if
err
!=
nil
{
return
err
}
...
...
packer/rpc/post_processor.go
View file @
36326ee8
...
...
@@ -57,7 +57,7 @@ func (p *postProcessor) PostProcess(ui packer.Ui, a packer.Artifact) (packer.Art
return
nil
,
false
,
nil
}
client
,
err
:=
rpc
.
Dial
(
"tcp"
,
response
.
RPCAddress
)
client
,
err
:=
rpc
Dial
(
response
.
RPCAddress
)
if
err
!=
nil
{
return
nil
,
false
,
err
}
...
...
@@ -75,7 +75,7 @@ func (p *PostProcessorServer) Configure(args *PostProcessorConfigureArgs, reply
}
func
(
p
*
PostProcessorServer
)
PostProcess
(
address
string
,
reply
*
PostProcessorProcessResponse
)
error
{
client
,
err
:=
rpc
.
Dial
(
"tcp"
,
address
)
client
,
err
:=
rpc
Dial
(
address
)
if
err
!=
nil
{
return
err
}
...
...
packer/rpc/provisioner.go
View file @
36326ee8
...
...
@@ -65,7 +65,7 @@ func (p *ProvisionerServer) Prepare(args *ProvisionerPrepareArgs, reply *error)
}
func
(
p
*
ProvisionerServer
)
Provision
(
args
*
ProvisionerProvisionArgs
,
reply
*
interface
{})
error
{
client
,
err
:=
rpc
.
Dial
(
"tcp"
,
args
.
RPCAddress
)
client
,
err
:=
rpc
Dial
(
args
.
RPCAddress
)
if
err
!=
nil
{
return
err
}
...
...
packer/template.go
View file @
36326ee8
...
...
@@ -48,6 +48,8 @@ type RawBuilderConfig struct {
// configuration. It contains the type of the post processor as well as the
// raw configuration that is handed to the post-processor for it to process.
type
RawPostProcessorConfig
struct
{
TemplateOnlyExcept
`mapstructure:",squash"`
Type
string
KeepInputArtifact
bool
`mapstructure:"keep_input_artifact"`
RawConfig
map
[
string
]
interface
{}
...
...
@@ -57,6 +59,8 @@ type RawPostProcessorConfig struct {
// It contains the type of the provisioner as well as the raw configuration
// that is handed to the provisioner for it to process.
type
RawProvisionerConfig
struct
{
TemplateOnlyExcept
`mapstructure:",squash"`
Type
string
Override
map
[
string
]
interface
{}
...
...
@@ -120,18 +124,25 @@ func ParseTemplate(data []byte) (t *Template, err error) {
// Gather all the variables
for
k
,
v
:=
range
rawTpl
.
Variables
{
var
variable
RawVariable
variable
.
Default
=
""
variable
.
Required
=
v
==
nil
if
v
!=
nil
{
def
,
ok
:=
v
.
(
string
)
if
!
ok
{
errors
=
append
(
errors
,
fmt
.
Errorf
(
"variable '%s': default value must be string or null"
,
k
))
continue
}
// Create a new mapstructure decoder in order to decode the default
// value since this is the only value in the regular template that
// can be weakly typed.
decoder
,
err
:=
mapstructure
.
NewDecoder
(
&
mapstructure
.
DecoderConfig
{
Result
:
&
variable
.
Default
,
WeaklyTypedInput
:
true
,
})
if
err
!=
nil
{
// This should never happen.
panic
(
err
)
}
variable
.
Default
=
def
err
=
decoder
.
Decode
(
v
)
if
err
!=
nil
{
errors
=
append
(
errors
,
fmt
.
Errorf
(
"Error decoding default value for user var '%s': %s"
,
k
,
err
))
continue
}
t
.
Variables
[
k
]
=
variable
...
...
@@ -189,32 +200,50 @@ func ParseTemplate(data []byte) (t *Template, err error) {
continue
}
t
.
PostProcessors
[
i
]
=
make
([]
RawPostProcessorConfig
,
len
(
rawPP
))
configs
:=
t
.
PostProcessors
[
i
]
configs
:=
make
([]
RawPostProcessorConfig
,
0
,
len
(
rawPP
))
for
j
,
pp
:=
range
rawPP
{
config
:=
&
configs
[
j
]
if
err
:=
mapstructure
.
Decode
(
pp
,
config
);
err
!=
nil
{
var
config
RawPostProcessorConfig
if
err
:=
mapstructure
.
Decode
(
pp
,
&
config
);
err
!=
nil
{
if
merr
,
ok
:=
err
.
(
*
mapstructure
.
Error
);
ok
{
for
_
,
err
:=
range
merr
.
Errors
{
errors
=
append
(
errors
,
fmt
.
Errorf
(
"Post-processor #%d.%d: %s"
,
i
+
1
,
j
+
1
,
err
))
errors
=
append
(
errors
,
fmt
.
Errorf
(
"Post-processor #%d.%d: %s"
,
i
+
1
,
j
+
1
,
err
))
}
}
else
{
errors
=
append
(
errors
,
fmt
.
Errorf
(
"Post-processor %d.%d: %s"
,
i
+
1
,
j
+
1
,
err
))
errors
=
append
(
errors
,
fmt
.
Errorf
(
"Post-processor %d.%d: %s"
,
i
+
1
,
j
+
1
,
err
))
}
continue
}
if
config
.
Type
==
""
{
errors
=
append
(
errors
,
fmt
.
Errorf
(
"Post-processor %d.%d: missing 'type'"
,
i
+
1
,
j
+
1
))
errors
=
append
(
errors
,
fmt
.
Errorf
(
"Post-processor %d.%d: missing 'type'"
,
i
+
1
,
j
+
1
))
continue
}
// Remove the input keep_input_artifact option
config
.
TemplateOnlyExcept
.
Prune
(
pp
)
delete
(
pp
,
"keep_input_artifact"
)
// Verify that the only settings are good
if
errs
:=
config
.
TemplateOnlyExcept
.
Validate
(
t
.
Builders
);
len
(
errs
)
>
0
{
for
_
,
err
:=
range
errs
{
errors
=
append
(
errors
,
fmt
.
Errorf
(
"Post-processor %d.%d: %s"
,
i
+
1
,
j
+
1
,
err
))
}
continue
}
config
.
RawConfig
=
pp
// Add it to the list of configs
configs
=
append
(
configs
,
config
)
}
t
.
PostProcessors
[
i
]
=
configs
}
// Gather all the provisioners
...
...
@@ -237,9 +266,8 @@ func ParseTemplate(data []byte) (t *Template, err error) {
continue
}
// The provisioners not only don't need or want the override settings
// (as they are processed as part of the preparation below), but will
// actively reject them as invalid configuration.
// Delete the keys that we used
raw
.
TemplateOnlyExcept
.
Prune
(
v
)
delete
(
v
,
"override"
)
// Verify that the override keys exist...
...
...
@@ -250,6 +278,14 @@ func ParseTemplate(data []byte) (t *Template, err error) {
}
}
// Verify that the only settings are good
if
errs
:=
raw
.
TemplateOnlyExcept
.
Validate
(
t
.
Builders
);
len
(
errs
)
>
0
{
for
_
,
err
:=
range
errs
{
errors
=
append
(
errors
,
fmt
.
Errorf
(
"provisioner %d: %s"
,
i
+
1
,
err
))
}
}
raw
.
RawConfig
=
v
}
...
...
@@ -400,8 +436,12 @@ func (t *Template) Build(name string, components *ComponentFinder) (b Build, err
// Prepare the post-processors
postProcessors
:=
make
([][]
coreBuildPostProcessor
,
0
,
len
(
t
.
PostProcessors
))
for
_
,
rawPPs
:=
range
t
.
PostProcessors
{
current
:=
make
([]
coreBuildPostProcessor
,
len
(
rawPPs
))
for
i
,
rawPP
:=
range
rawPPs
{
current
:=
make
([]
coreBuildPostProcessor
,
0
,
len
(
rawPPs
))
for
_
,
rawPP
:=
range
rawPPs
{
if
rawPP
.
TemplateOnlyExcept
.
Skip
(
name
)
{
continue
}
pp
,
err
:=
components
.
PostProcessor
(
rawPP
.
Type
)
if
err
!=
nil
{
return
nil
,
err
...
...
@@ -411,12 +451,18 @@ func (t *Template) Build(name string, components *ComponentFinder) (b Build, err
return
nil
,
fmt
.
Errorf
(
"PostProcessor type not found: %s"
,
rawPP
.
Type
)
}
current
[
i
]
=
coreBuildPostProcessor
{
current
=
append
(
current
,
coreBuildPostProcessor
{
processor
:
pp
,
processorType
:
rawPP
.
Type
,
config
:
rawPP
.
RawConfig
,
keepInputArtifact
:
rawPP
.
KeepInputArtifact
,
}
})
}
// If we have no post-processors in this chain, just continue.
// This can happen if the post-processors skip certain builds.
if
len
(
current
)
==
0
{
continue
}
postProcessors
=
append
(
postProcessors
,
current
)
...
...
@@ -425,6 +471,10 @@ func (t *Template) Build(name string, components *ComponentFinder) (b Build, err
// Prepare the provisioners
provisioners
:=
make
([]
coreBuildProvisioner
,
0
,
len
(
t
.
Provisioners
))
for
_
,
rawProvisioner
:=
range
t
.
Provisioners
{
if
rawProvisioner
.
TemplateOnlyExcept
.
Skip
(
name
)
{
continue
}
var
provisioner
Provisioner
provisioner
,
err
=
components
.
Provisioner
(
rawProvisioner
.
Type
)
if
err
!=
nil
{
...
...
@@ -471,3 +521,69 @@ func (t *Template) Build(name string, components *ComponentFinder) (b Build, err
return
}
// TemplateOnlyExcept contains the logic required for "only" and "except"
// meta-parameters.
type
TemplateOnlyExcept
struct
{
Only
[]
string
Except
[]
string
}
// Prune will prune out the used values from the raw map.
func
(
t
*
TemplateOnlyExcept
)
Prune
(
raw
map
[
string
]
interface
{})
{
delete
(
raw
,
"except"
)
delete
(
raw
,
"only"
)
}
// Skip tests if we should skip putting this item onto a build.
func
(
t
*
TemplateOnlyExcept
)
Skip
(
name
string
)
bool
{
if
len
(
t
.
Only
)
>
0
{
onlyFound
:=
false
for
_
,
n
:=
range
t
.
Only
{
if
n
==
name
{
onlyFound
=
true
break
}
}
if
!
onlyFound
{
// Skip this provisioner
return
true
}
}
// If the name is in the except list, then skip that
for
_
,
n
:=
range
t
.
Except
{
if
n
==
name
{
return
true
}
}
return
false
}
// Validates the only/except parameters.
func
(
t
*
TemplateOnlyExcept
)
Validate
(
b
map
[
string
]
RawBuilderConfig
)
(
e
[]
error
)
{
if
len
(
t
.
Only
)
>
0
&&
len
(
t
.
Except
)
>
0
{
e
=
append
(
e
,
fmt
.
Errorf
(
"Only one of 'only' or 'except' may be specified."
))
}
if
len
(
t
.
Only
)
>
0
{
for
_
,
n
:=
range
t
.
Only
{
if
_
,
ok
:=
b
[
n
];
!
ok
{
e
=
append
(
e
,
fmt
.
Errorf
(
"'only' specified builder '%s' not found"
,
n
))
}
}
}
for
_
,
n
:=
range
t
.
Except
{
if
_
,
ok
:=
b
[
n
];
!
ok
{
e
=
append
(
e
,
fmt
.
Errorf
(
"'except' specified builder '%s' not found"
,
n
))
}
}
return
}
packer/template_test.go
View file @
36326ee8
...
...
@@ -9,6 +9,33 @@ import (
"testing"
)
func
testTemplateComponentFinder
()
*
ComponentFinder
{
builder
:=
testBuilder
()
pp
:=
new
(
TestPostProcessor
)
provisioner
:=
&
MockProvisioner
{}
builderMap
:=
map
[
string
]
Builder
{
"test-builder"
:
builder
,
}
ppMap
:=
map
[
string
]
PostProcessor
{
"test-pp"
:
pp
,
}
provisionerMap
:=
map
[
string
]
Provisioner
{
"test-prov"
:
provisioner
,
}
builderFactory
:=
func
(
n
string
)
(
Builder
,
error
)
{
return
builderMap
[
n
],
nil
}
ppFactory
:=
func
(
n
string
)
(
PostProcessor
,
error
)
{
return
ppMap
[
n
],
nil
}
provFactory
:=
func
(
n
string
)
(
Provisioner
,
error
)
{
return
provisionerMap
[
n
],
nil
}
return
&
ComponentFinder
{
Builder
:
builderFactory
,
PostProcessor
:
ppFactory
,
Provisioner
:
provFactory
,
}
}
func
TestParseTemplateFile_basic
(
t
*
testing
.
T
)
{
data
:=
`
{
...
...
@@ -364,7 +391,8 @@ func TestParseTemplate_Variables(t *testing.T) {
{
"variables": {
"foo": "bar",
"bar": null
"bar": null,
"baz": 27
},
"builders": [{"type": "something"}]
...
...
@@ -376,7 +404,7 @@ func TestParseTemplate_Variables(t *testing.T) {
t
.
Fatalf
(
"err: %s"
,
err
)
}
if
result
.
Variables
==
nil
||
len
(
result
.
Variables
)
!=
2
{
if
result
.
Variables
==
nil
||
len
(
result
.
Variables
)
!=
3
{
t
.
Fatalf
(
"bad vars: %#v"
,
result
.
Variables
)
}
...
...
@@ -395,6 +423,14 @@ func TestParseTemplate_Variables(t *testing.T) {
if
!
result
.
Variables
[
"bar"
]
.
Required
{
t
.
Fatal
(
"bar should be required"
)
}
if
result
.
Variables
[
"baz"
]
.
Default
!=
"27"
{
t
.
Fatal
(
"default should be empty"
)
}
if
result
.
Variables
[
"baz"
]
.
Required
{
t
.
Fatal
(
"baz should not be required"
)
}
}
func
TestParseTemplate_variablesBadDefault
(
t
*
testing
.
T
)
{
...
...
@@ -663,6 +699,386 @@ func TestTemplate_Build(t *testing.T) {
}
}
func
TestTemplateBuild_exceptOnlyPP
(
t
*
testing
.
T
)
{
data
:=
`
{
"builders": [
{
"name": "test1",
"type": "test-builder"
},
{
"name": "test2",
"type": "test-builder"
}
],
"post-processors": [
{
"type": "test-pp",
"except": ["test1"],
"only": ["test1"]
}
]
}
`
_
,
err
:=
ParseTemplate
([]
byte
(
data
))
if
err
==
nil
{
t
.
Fatal
(
"should have error"
)
}
}
func
TestTemplateBuild_exceptOnlyProv
(
t
*
testing
.
T
)
{
data
:=
`
{
"builders": [
{
"name": "test1",
"type": "test-builder"
},
{
"name": "test2",
"type": "test-builder"
}
],
"provisioners": [
{
"type": "test-prov",
"except": ["test1"],
"only": ["test1"]
}
]
}
`
_
,
err
:=
ParseTemplate
([]
byte
(
data
))
if
err
==
nil
{
t
.
Fatal
(
"should have error"
)
}
}
func
TestTemplateBuild_exceptPPInvalid
(
t
*
testing
.
T
)
{
data
:=
`
{
"builders": [
{
"name": "test1",
"type": "test-builder"
},
{
"name": "test2",
"type": "test-builder"
}
],
"post-processors": [
{
"type": "test-pp",
"except": ["test5"]
}
]
}
`
_
,
err
:=
ParseTemplate
([]
byte
(
data
))
if
err
==
nil
{
t
.
Fatal
(
"should have error"
)
}
}
func
TestTemplateBuild_exceptPP
(
t
*
testing
.
T
)
{
data
:=
`
{
"builders": [
{
"name": "test1",
"type": "test-builder"
},
{
"name": "test2",
"type": "test-builder"
}
],
"post-processors": [
{
"type": "test-pp",
"except": ["test1"]
}
]
}
`
template
,
err
:=
ParseTemplate
([]
byte
(
data
))
if
err
!=
nil
{
t
.
Fatalf
(
"err: %s"
,
err
)
}
// Verify test1 has no post-processors
build
,
err
:=
template
.
Build
(
"test1"
,
testTemplateComponentFinder
())
if
err
!=
nil
{
t
.
Fatalf
(
"err: %s"
,
err
)
}
cbuild
:=
build
.
(
*
coreBuild
)
if
len
(
cbuild
.
postProcessors
)
>
0
{
t
.
Fatal
(
"should have no postProcessors"
)
}
// Verify test2 has no post-processors
build
,
err
=
template
.
Build
(
"test2"
,
testTemplateComponentFinder
())
if
err
!=
nil
{
t
.
Fatalf
(
"err: %s"
,
err
)
}
cbuild
=
build
.
(
*
coreBuild
)
if
len
(
cbuild
.
postProcessors
)
!=
1
{
t
.
Fatalf
(
"invalid: %d"
,
len
(
cbuild
.
postProcessors
))
}
}
func
TestTemplateBuild_exceptProvInvalid
(
t
*
testing
.
T
)
{
data
:=
`
{
"builders": [
{
"name": "test1",
"type": "test-builder"
},
{
"name": "test2",
"type": "test-builder"
}
],
"provisioners": [
{
"type": "test-prov",
"except": ["test5"]
}
]
}
`
_
,
err
:=
ParseTemplate
([]
byte
(
data
))
if
err
==
nil
{
t
.
Fatal
(
"should have error"
)
}
}
func
TestTemplateBuild_exceptProv
(
t
*
testing
.
T
)
{
data
:=
`
{
"builders": [
{
"name": "test1",
"type": "test-builder"
},
{
"name": "test2",
"type": "test-builder"
}
],
"provisioners": [
{
"type": "test-prov",
"except": ["test1"]
}
]
}
`
template
,
err
:=
ParseTemplate
([]
byte
(
data
))
if
err
!=
nil
{
t
.
Fatalf
(
"err: %s"
,
err
)
}
// Verify test1 has no provisioners
build
,
err
:=
template
.
Build
(
"test1"
,
testTemplateComponentFinder
())
if
err
!=
nil
{
t
.
Fatalf
(
"err: %s"
,
err
)
}
cbuild
:=
build
.
(
*
coreBuild
)
if
len
(
cbuild
.
provisioners
)
>
0
{
t
.
Fatal
(
"should have no provisioners"
)
}
// Verify test2 has no provisioners
build
,
err
=
template
.
Build
(
"test2"
,
testTemplateComponentFinder
())
if
err
!=
nil
{
t
.
Fatalf
(
"err: %s"
,
err
)
}
cbuild
=
build
.
(
*
coreBuild
)
if
len
(
cbuild
.
provisioners
)
!=
1
{
t
.
Fatalf
(
"invalid: %d"
,
len
(
cbuild
.
provisioners
))
}
}
func
TestTemplateBuild_onlyPPInvalid
(
t
*
testing
.
T
)
{
data
:=
`
{
"builders": [
{
"name": "test1",
"type": "test-builder"
},
{
"name": "test2",
"type": "test-builder"
}
],
"post-processors": [
{
"type": "test-pp",
"only": ["test5"]
}
]
}
`
_
,
err
:=
ParseTemplate
([]
byte
(
data
))
if
err
==
nil
{
t
.
Fatal
(
"should have error"
)
}
}
func
TestTemplateBuild_onlyPP
(
t
*
testing
.
T
)
{
data
:=
`
{
"builders": [
{
"name": "test1",
"type": "test-builder"
},
{
"name": "test2",
"type": "test-builder"
}
],
"post-processors": [
{
"type": "test-pp",
"only": ["test2"]
}
]
}
`
template
,
err
:=
ParseTemplate
([]
byte
(
data
))
if
err
!=
nil
{
t
.
Fatalf
(
"err: %s"
,
err
)
}
// Verify test1 has no post-processors
build
,
err
:=
template
.
Build
(
"test1"
,
testTemplateComponentFinder
())
if
err
!=
nil
{
t
.
Fatalf
(
"err: %s"
,
err
)
}
cbuild
:=
build
.
(
*
coreBuild
)
if
len
(
cbuild
.
postProcessors
)
>
0
{
t
.
Fatal
(
"should have no postProcessors"
)
}
// Verify test2 has no post-processors
build
,
err
=
template
.
Build
(
"test2"
,
testTemplateComponentFinder
())
if
err
!=
nil
{
t
.
Fatalf
(
"err: %s"
,
err
)
}
cbuild
=
build
.
(
*
coreBuild
)
if
len
(
cbuild
.
postProcessors
)
!=
1
{
t
.
Fatalf
(
"invalid: %d"
,
len
(
cbuild
.
postProcessors
))
}
}
func
TestTemplateBuild_onlyProvInvalid
(
t
*
testing
.
T
)
{
data
:=
`
{
"builders": [
{
"name": "test1",
"type": "test-builder"
},
{
"name": "test2",
"type": "test-builder"
}
],
"provisioners": [
{
"type": "test-prov",
"only": ["test5"]
}
]
}
`
_
,
err
:=
ParseTemplate
([]
byte
(
data
))
if
err
==
nil
{
t
.
Fatal
(
"should have error"
)
}
}
func
TestTemplateBuild_onlyProv
(
t
*
testing
.
T
)
{
data
:=
`
{
"builders": [
{
"name": "test1",
"type": "test-builder"
},
{
"name": "test2",
"type": "test-builder"
}
],
"provisioners": [
{
"type": "test-prov",
"only": ["test2"]
}
]
}
`
template
,
err
:=
ParseTemplate
([]
byte
(
data
))
if
err
!=
nil
{
t
.
Fatalf
(
"err: %s"
,
err
)
}
// Verify test1 has no provisioners
build
,
err
:=
template
.
Build
(
"test1"
,
testTemplateComponentFinder
())
if
err
!=
nil
{
t
.
Fatalf
(
"err: %s"
,
err
)
}
cbuild
:=
build
.
(
*
coreBuild
)
if
len
(
cbuild
.
provisioners
)
>
0
{
t
.
Fatal
(
"should have no provisioners"
)
}
// Verify test2 has no provisioners
build
,
err
=
template
.
Build
(
"test2"
,
testTemplateComponentFinder
())
if
err
!=
nil
{
t
.
Fatalf
(
"err: %s"
,
err
)
}
cbuild
=
build
.
(
*
coreBuild
)
if
len
(
cbuild
.
provisioners
)
!=
1
{
t
.
Fatalf
(
"invalid: %d"
,
len
(
cbuild
.
provisioners
))
}
}
func
TestTemplate_Build_ProvisionerOverride
(
t
*
testing
.
T
)
{
assert
:=
asserts
.
NewTestingAsserts
(
t
,
true
)
...
...
packer/version.go
View file @
36326ee8
...
...
@@ -10,7 +10,7 @@ import (
var
GitCommit
string
// The version of packer.
const
Version
=
"0.3.
8
"
const
Version
=
"0.3.
9
"
// Any pre-release marker for the version. If this is "" (empty string),
// then it means that it is a final release. Otherwise, this is the
...
...
post-processor/vagrant/post-processor.go
View file @
36326ee8
...
...
@@ -24,15 +24,12 @@ type Config struct {
}
type
PostProcessor
struct
{
config
Config
premade
map
[
string
]
packer
.
PostProcessor
rawConfigs
[
]
interface
{}
config
Config
premade
map
[
string
]
packer
.
PostProcessor
extraConfig
map
[
string
]
interface
{}
}
func
(
p
*
PostProcessor
)
Configure
(
raws
...
interface
{})
error
{
// Store the raw configs for usage later
p
.
rawConfigs
=
raws
_
,
err
:=
common
.
DecodeConfig
(
&
p
.
config
,
raws
...
)
if
err
!=
nil
{
return
err
...
...
@@ -45,10 +42,8 @@ func (p *PostProcessor) Configure(raws ...interface{}) error {
tpl
.
UserVars
=
p
.
config
.
PackerUserVars
// Defaults
ppExtraConfig
:=
make
(
map
[
string
]
interface
{})
if
p
.
config
.
OutputPath
==
""
{
p
.
config
.
OutputPath
=
"packer_{{ .BuildName }}_{{.Provider}}.box"
ppExtraConfig
[
"output"
]
=
p
.
config
.
OutputPath
}
// Accumulate any errors
...
...
@@ -58,10 +53,18 @@ func (p *PostProcessor) Configure(raws ...interface{}) error {
errs
,
fmt
.
Errorf
(
"Error parsing output template: %s"
,
err
))
}
// Store the extra configuration for post-processors
p
.
rawConfigs
=
append
(
p
.
rawConfigs
,
ppExtraConfig
)
// TODO(mitchellh): Properly handle multiple raw configs
// Store extra configuration we'll send to each post-processor type
p
.
extraConfig
=
make
(
map
[
string
]
interface
{})
p
.
extraConfig
[
"output"
]
=
p
.
config
.
OutputPath
p
.
extraConfig
[
"packer_build_name"
]
=
p
.
config
.
PackerBuildName
p
.
extraConfig
[
"packer_builder_type"
]
=
p
.
config
.
PackerBuilderType
p
.
extraConfig
[
"packer_debug"
]
=
p
.
config
.
PackerDebug
p
.
extraConfig
[
"packer_force"
]
=
p
.
config
.
PackerForce
p
.
extraConfig
[
"packer_user_variables"
]
=
p
.
config
.
PackerUserVars
// TODO(mitchellh): Properly handle multiple raw configs. This isn't
// very pressing at the moment because at the time of this comment
// only the first member of raws can contain the actual type-overrides.
var
mapConfig
map
[
string
]
interface
{}
if
err
:=
mapstructure
.
Decode
(
raws
[
0
],
&
mapConfig
);
err
!=
nil
{
errs
=
packer
.
MultiErrorAppend
(
errs
,
...
...
@@ -71,18 +74,14 @@ func (p *PostProcessor) Configure(raws ...interface{}) error {
p
.
premade
=
make
(
map
[
string
]
packer
.
PostProcessor
)
for
k
,
raw
:=
range
mapConfig
{
pp
:=
keyToPostProcessor
(
k
)
if
pp
==
nil
{
pp
,
err
:=
p
.
subPostProcessor
(
k
,
raw
,
p
.
extraConfig
)
if
err
!=
nil
{
errs
=
packer
.
MultiErrorAppend
(
errs
,
err
)
continue
}
// Create the proper list of configurations
ppConfigs
:=
make
([]
interface
{},
0
,
len
(
p
.
rawConfigs
)
+
1
)
copy
(
ppConfigs
,
p
.
rawConfigs
)
ppConfigs
=
append
(
ppConfigs
,
raw
)
if
err
:=
pp
.
Configure
(
ppConfigs
...
);
err
!=
nil
{
errs
=
packer
.
MultiErrorAppend
(
errs
,
err
)
if
pp
==
nil
{
continue
}
p
.
premade
[
k
]
=
pp
...
...
@@ -106,20 +105,37 @@ func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (pac
pp
,
ok
:=
p
.
premade
[
ppName
]
if
!
ok
{
log
.
Printf
(
"Premade post-processor for '%s' not found. Creating."
,
ppName
)
pp
=
keyToPostProcessor
(
ppName
)
if
pp
==
nil
{
return
nil
,
false
,
fmt
.
Errorf
(
"Vagrant box post-processor not found: %s"
,
ppName
)
}
if
err
:=
pp
.
Configure
(
p
.
rawConfigs
...
);
err
!=
nil
{
var
err
error
pp
,
err
=
p
.
subPostProcessor
(
ppName
,
nil
,
p
.
extraConfig
)
if
err
!=
nil
{
return
nil
,
false
,
err
}
if
pp
==
nil
{
return
nil
,
false
,
fmt
.
Errorf
(
"Vagrant box post-processor not found: %s"
,
ppName
)
}
}
ui
.
Say
(
fmt
.
Sprintf
(
"Creating Vagrant box for '%s' provider"
,
ppName
))
return
pp
.
PostProcess
(
ui
,
artifact
)
}
func
(
p
*
PostProcessor
)
subPostProcessor
(
key
string
,
specific
interface
{},
extra
map
[
string
]
interface
{})
(
packer
.
PostProcessor
,
error
)
{
pp
:=
keyToPostProcessor
(
key
)
if
pp
==
nil
{
return
nil
,
nil
}
if
err
:=
pp
.
Configure
(
extra
,
specific
);
err
!=
nil
{
return
nil
,
err
}
return
pp
,
nil
}
// keyToPostProcessor maps a configuration key to the actual post-processor
// it will be configuring. This returns a new instance of that post-processor.
func
keyToPostProcessor
(
key
string
)
packer
.
PostProcessor
{
switch
key
{
case
"aws"
:
...
...
provisioner/chef-solo/provisioner.go
View file @
36326ee8
...
...
@@ -20,6 +20,8 @@ type Config struct {
ConfigTemplate
string
`mapstructure:"config_template"`
CookbookPaths
[]
string
`mapstructure:"cookbook_paths"`
RolesPath
string
`mapstructure:"roles_path"`
DataBagsPath
string
`mapstructure:"data_bags_path"`
ExecuteCommand
string
`mapstructure:"execute_command"`
InstallCommand
string
`mapstructure:"install_command"`
RemoteCookbookPaths
[]
string
`mapstructure:"remote_cookbook_paths"`
...
...
@@ -38,6 +40,14 @@ type Provisioner struct {
type
ConfigTemplate
struct
{
CookbookPaths
string
DataBagsPath
string
RolesPath
string
// Templates don't support boolean statements until Go 1.2. In the
// mean time, we do this.
// TODO(mitchellh): Remove when Go 1.2 is released
HasDataBagsPath
bool
HasRolesPath
bool
}
type
ExecuteTemplate
struct
{
...
...
@@ -83,6 +93,8 @@ func (p *Provisioner) Prepare(raws ...interface{}) error {
templates
:=
map
[
string
]
*
string
{
"config_template"
:
&
p
.
config
.
ConfigTemplate
,
"data_bags_path"
:
&
p
.
config
.
DataBagsPath
,
"roles_path"
:
&
p
.
config
.
RolesPath
,
"staging_dir"
:
&
p
.
config
.
StagingDir
,
}
...
...
@@ -144,6 +156,24 @@ func (p *Provisioner) Prepare(raws ...interface{}) error {
}
}
if
p
.
config
.
RolesPath
!=
""
{
pFileInfo
,
err
:=
os
.
Stat
(
p
.
config
.
RolesPath
)
if
err
!=
nil
||
!
pFileInfo
.
IsDir
()
{
errs
=
packer
.
MultiErrorAppend
(
errs
,
fmt
.
Errorf
(
"Bad roles path '%s': %s"
,
p
.
config
.
RolesPath
,
err
))
}
}
if
p
.
config
.
DataBagsPath
!=
""
{
pFileInfo
,
err
:=
os
.
Stat
(
p
.
config
.
DataBagsPath
)
if
err
!=
nil
||
!
pFileInfo
.
IsDir
()
{
errs
=
packer
.
MultiErrorAppend
(
errs
,
fmt
.
Errorf
(
"Bad data bags path '%s': %s"
,
p
.
config
.
DataBagsPath
,
err
))
}
}
// Process the user variables within the JSON and set the JSON.
// Do this early so that we can validate and show errors.
p
.
config
.
Json
,
err
=
p
.
processJsonUserVars
()
...
...
@@ -180,7 +210,23 @@ func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error {
cookbookPaths
=
append
(
cookbookPaths
,
targetPath
)
}
configPath
,
err
:=
p
.
createConfig
(
ui
,
comm
,
cookbookPaths
)
rolesPath
:=
""
if
p
.
config
.
RolesPath
!=
""
{
rolesPath
:=
fmt
.
Sprintf
(
"%s/roles"
,
p
.
config
.
StagingDir
)
if
err
:=
p
.
uploadDirectory
(
ui
,
comm
,
rolesPath
,
p
.
config
.
RolesPath
);
err
!=
nil
{
return
fmt
.
Errorf
(
"Error uploading roles: %s"
,
err
)
}
}
dataBagsPath
:=
""
if
p
.
config
.
DataBagsPath
!=
""
{
dataBagsPath
:=
fmt
.
Sprintf
(
"%s/data_bags"
,
p
.
config
.
StagingDir
)
if
err
:=
p
.
uploadDirectory
(
ui
,
comm
,
dataBagsPath
,
p
.
config
.
DataBagsPath
);
err
!=
nil
{
return
fmt
.
Errorf
(
"Error uploading data bags: %s"
,
err
)
}
}
configPath
,
err
:=
p
.
createConfig
(
ui
,
comm
,
cookbookPaths
,
rolesPath
,
dataBagsPath
)
if
err
!=
nil
{
return
fmt
.
Errorf
(
"Error creating Chef config file: %s"
,
err
)
}
...
...
@@ -217,7 +263,7 @@ func (p *Provisioner) uploadDirectory(ui packer.Ui, comm packer.Communicator, ds
return
comm
.
UploadDir
(
dst
,
src
,
nil
)
}
func
(
p
*
Provisioner
)
createConfig
(
ui
packer
.
Ui
,
comm
packer
.
Communicator
,
localCookbooks
[]
string
)
(
string
,
error
)
{
func
(
p
*
Provisioner
)
createConfig
(
ui
packer
.
Ui
,
comm
packer
.
Communicator
,
localCookbooks
[]
string
,
rolesPath
string
,
dataBagsPath
string
)
(
string
,
error
)
{
ui
.
Message
(
"Creating configuration file 'solo.rb'"
)
cookbook_paths
:=
make
([]
string
,
len
(
p
.
config
.
RemoteCookbookPaths
)
+
len
(
localCookbooks
))
...
...
@@ -248,7 +294,11 @@ func (p *Provisioner) createConfig(ui packer.Ui, comm packer.Communicator, local
}
configString
,
err
:=
p
.
config
.
tpl
.
Process
(
tpl
,
&
ConfigTemplate
{
CookbookPaths
:
strings
.
Join
(
cookbook_paths
,
","
),
CookbookPaths
:
strings
.
Join
(
cookbook_paths
,
","
),
RolesPath
:
rolesPath
,
DataBagsPath
:
dataBagsPath
,
HasRolesPath
:
rolesPath
!=
""
,
HasDataBagsPath
:
dataBagsPath
!=
""
,
})
if
err
!=
nil
{
return
""
,
err
...
...
@@ -399,5 +449,11 @@ func (p *Provisioner) processJsonUserVars() (map[string]interface{}, error) {
}
var
DefaultConfigTemplate
=
`
cookbook_path [{{.CookbookPaths}}]
cookbook_path [{{.CookbookPaths}}]
{{if .HasRolesPath}}
role_path "{{.RolesPath}}"
{{end}}
{{if .HasDataBagsPath}}
data_bag_path "{{.DataBagsPath}}"
{{end}}
`
provisioner/chef-solo/provisioner_test.go
View file @
36326ee8
...
...
@@ -75,11 +75,25 @@ func TestProvisionerPrepare_cookbookPaths(t *testing.T) {
t
.
Fatalf
(
"err: %s"
,
err
)
}
rolesPath
,
err
:=
ioutil
.
TempDir
(
""
,
"roles"
)
if
err
!=
nil
{
t
.
Fatalf
(
"err: %s"
,
err
)
}
dataBagsPath
,
err
:=
ioutil
.
TempDir
(
""
,
"data_bags"
)
if
err
!=
nil
{
t
.
Fatalf
(
"err: %s"
,
err
)
}
defer
os
.
Remove
(
path1
)
defer
os
.
Remove
(
path2
)
defer
os
.
Remove
(
rolesPath
)
defer
os
.
Remove
(
dataBagsPath
)
config
:=
testConfig
()
config
[
"cookbook_paths"
]
=
[]
string
{
path1
,
path2
}
config
[
"roles_path"
]
=
rolesPath
config
[
"data_bags_path"
]
=
dataBagsPath
err
=
p
.
Prepare
(
config
)
if
err
!=
nil
{
...
...
@@ -93,6 +107,58 @@ func TestProvisionerPrepare_cookbookPaths(t *testing.T) {
if
p
.
config
.
CookbookPaths
[
0
]
!=
path1
||
p
.
config
.
CookbookPaths
[
1
]
!=
path2
{
t
.
Fatalf
(
"unexpected: %#v"
,
p
.
config
.
CookbookPaths
)
}
if
p
.
config
.
RolesPath
!=
rolesPath
{
t
.
Fatalf
(
"unexpected: %#v"
,
p
.
config
.
RolesPath
)
}
if
p
.
config
.
DataBagsPath
!=
dataBagsPath
{
t
.
Fatalf
(
"unexpected: %#v"
,
p
.
config
.
DataBagsPath
)
}
}
func
TestProvisionerPrepare_dataBagsPath
(
t
*
testing
.
T
)
{
var
p
Provisioner
dataBagsPath
,
err
:=
ioutil
.
TempDir
(
""
,
"data_bags"
)
if
err
!=
nil
{
t
.
Fatalf
(
"err: %s"
,
err
)
}
defer
os
.
Remove
(
dataBagsPath
)
config
:=
testConfig
()
config
[
"data_bags_path"
]
=
dataBagsPath
err
=
p
.
Prepare
(
config
)
if
err
!=
nil
{
t
.
Fatalf
(
"err: %s"
,
err
)
}
if
p
.
config
.
DataBagsPath
!=
dataBagsPath
{
t
.
Fatalf
(
"unexpected: %#v"
,
p
.
config
.
DataBagsPath
)
}
}
func
TestProvisionerPrepare_rolesPath
(
t
*
testing
.
T
)
{
var
p
Provisioner
rolesPath
,
err
:=
ioutil
.
TempDir
(
""
,
"roles"
)
if
err
!=
nil
{
t
.
Fatalf
(
"err: %s"
,
err
)
}
defer
os
.
Remove
(
rolesPath
)
config
:=
testConfig
()
config
[
"roles_path"
]
=
rolesPath
err
=
p
.
Prepare
(
config
)
if
err
!=
nil
{
t
.
Fatalf
(
"err: %s"
,
err
)
}
if
p
.
config
.
RolesPath
!=
rolesPath
{
t
.
Fatalf
(
"unexpected: %#v"
,
p
.
config
.
RolesPath
)
}
}
func
TestProvisionerPrepare_json
(
t
*
testing
.
T
)
{
...
...
provisioner/puppet-masterless/provisioner.go
View file @
36326ee8
...
...
@@ -82,7 +82,9 @@ func (p *Provisioner) Prepare(raws ...interface{}) error {
// Templates
templates
:=
map
[
string
]
*
string
{
"staging_dir"
:
&
p
.
config
.
StagingDir
,
"hiera_config_path"
:
&
p
.
config
.
HieraConfigPath
,
"manifest_file"
:
&
p
.
config
.
ManifestFile
,
"staging_dir"
:
&
p
.
config
.
StagingDir
,
}
for
n
,
ptr
:=
range
templates
{
...
...
provisioner/shell/provisioner.go
View file @
36326ee8
...
...
@@ -8,6 +8,7 @@ import (
"fmt"
"github.com/mitchellh/packer/common"
"github.com/mitchellh/packer/packer"
"io"
"io/ioutil"
"log"
"os"
...
...
@@ -20,6 +21,10 @@ const DefaultRemotePath = "/tmp/script.sh"
type
config
struct
{
common
.
PackerConfig
`mapstructure:",squash"`
// If true, the script contains binary and line endings will not be
// converted from Windows to Unix-style.
Binary
bool
// An inline script to execute. Multiple strings are all executed
// in the context of a single shell.
Inline
[]
string
...
...
@@ -259,6 +264,11 @@ func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error {
return
err
}
var
r
io
.
Reader
=
f
if
!
p
.
config
.
Binary
{
r
=
&
UnixReader
{
Reader
:
r
}
}
if
err
:=
comm
.
Upload
(
p
.
config
.
RemotePath
,
f
);
err
!=
nil
{
return
fmt
.
Errorf
(
"Error uploading script: %s"
,
err
)
}
...
...
provisioner/shell/unix_reader.go
0 → 100644
View file @
36326ee8
package
shell
import
(
"bufio"
"bytes"
"io"
"sync"
)
// UnixReader is a Reader implementation that automatically converts
// Windows line endings to Unix line endings.
type
UnixReader
struct
{
Reader
io
.
Reader
buf
[]
byte
once
sync
.
Once
scanner
*
bufio
.
Scanner
}
func
(
r
*
UnixReader
)
Read
(
p
[]
byte
)
(
n
int
,
err
error
)
{
// Create the buffered reader once
r
.
once
.
Do
(
func
()
{
r
.
scanner
=
bufio
.
NewScanner
(
r
.
Reader
)
r
.
scanner
.
Split
(
scanUnixLine
)
})
// If we have no data in our buffer, scan to the next token
if
len
(
r
.
buf
)
==
0
{
if
!
r
.
scanner
.
Scan
()
{
err
=
r
.
scanner
.
Err
()
if
err
==
nil
{
err
=
io
.
EOF
}
return
0
,
err
}
r
.
buf
=
r
.
scanner
.
Bytes
()
}
// Write out as much data as we can to the buffer, storing the rest
// for the next read.
n
=
len
(
p
)
if
n
>
len
(
r
.
buf
)
{
n
=
len
(
r
.
buf
)
}
copy
(
p
,
r
.
buf
)
r
.
buf
=
r
.
buf
[
n
:
]
return
}
// scanUnixLine is a bufio.Scanner SplitFunc. It tokenizes on lines, but
// only returns unix-style lines. So even if the line is "one\r\n", the
// token returned will be "one\n".
func
scanUnixLine
(
data
[]
byte
,
atEOF
bool
)
(
advance
int
,
token
[]
byte
,
err
error
)
{
if
atEOF
&&
len
(
data
)
==
0
{
return
0
,
nil
,
nil
}
if
i
:=
bytes
.
IndexByte
(
data
,
'\n'
);
i
>=
0
{
// We have a new-line terminated line. Return the line with the newline
return
i
+
1
,
dropCR
(
data
[
0
:
i
+
1
]),
nil
}
if
atEOF
{
// We have a final, non-terminated line
return
len
(
data
),
dropCR
(
data
),
nil
}
if
data
[
len
(
data
)
-
1
]
!=
'\r'
{
// We have a normal line, just let it tokenize
return
len
(
data
),
data
,
nil
}
// We need more data
return
0
,
nil
,
nil
}
func
dropCR
(
data
[]
byte
)
[]
byte
{
if
len
(
data
)
>
0
&&
data
[
len
(
data
)
-
2
]
==
'\r'
{
// Trim off the last byte and replace it with a '\n'
data
=
data
[
0
:
len
(
data
)
-
1
]
data
[
len
(
data
)
-
1
]
=
'\n'
}
return
data
}
provisioner/shell/unix_reader_test.go
0 → 100644
View file @
36326ee8
package
shell
import
(
"bytes"
"io"
"testing"
)
func
TestUnixReader_impl
(
t
*
testing
.
T
)
{
var
raw
interface
{}
raw
=
new
(
UnixReader
)
if
_
,
ok
:=
raw
.
(
io
.
Reader
);
!
ok
{
t
.
Fatal
(
"should be reader"
)
}
}
func
TestUnixReader
(
t
*
testing
.
T
)
{
input
:=
"one
\r\n
two
\n
three
\r\n
"
expected
:=
"one
\n
two
\n
three
\n
"
r
:=
&
UnixReader
{
Reader
:
bytes
.
NewReader
([]
byte
(
input
)),
}
result
:=
new
(
bytes
.
Buffer
)
if
_
,
err
:=
io
.
Copy
(
result
,
r
);
err
!=
nil
{
t
.
Fatalf
(
"err: %s"
,
err
)
}
if
result
.
String
()
!=
expected
{
t
.
Fatalf
(
"bad: %#v"
,
result
.
String
())
}
}
scripts/build.sh
View file @
36326ee8
...
...
@@ -26,6 +26,9 @@ if [ "$(go env GOOS)" = "windows" ]; then
EXTENSION
=
".exe"
fi
# Make sure that if we're killed, we kill all our subprocseses
trap
"kill 0"
SIGINT SIGTERM EXIT
# If we're building a race-enabled build, then set that up.
if
[
!
-z
$PACKER_RACE
]
;
then
echo
-e
"
${
OK_COLOR
}
--> Building with race detection enabled
${
NO_COLOR
}
"
...
...
@@ -35,21 +38,57 @@ fi
echo
-e
"
${
OK_COLOR
}
--> Installing dependencies to speed up builds...
${
NO_COLOR
}
"
go get ./...
# This function waits for all background tasks to complete
waitAll
()
{
RESULT
=
0
for
job
in
`
jobs
-p
`
;
do
wait
$job
if
[
$?
-ne
0
]
;
then
RESULT
=
1
fi
done
if
[
$RESULT
-ne
0
]
;
then
exit
$RESULT
fi
}
waitSingle
()
{
if
[
!
-z
$PACKER_NO_BUILD_PARALLEL
]
;
then
waitAll
fi
}
if
[
-z
$PACKER_NO_BUILD_PARALLEL
]
;
then
echo
-e
"
${
OK_COLOR
}
--> NOTE: Compilation of components "
\
"will be done in parallel.
${
NO_COLOR
}
"
fi
# Compile the main Packer app
echo
-e
"
${
OK_COLOR
}
--> Compiling Packer
${
NO_COLOR
}
"
(
go build
\
${
PACKER_RACE
}
\
-ldflags
"-X github.com/mitchellh/packer/packer.GitCommit
${
GIT_COMMIT
}${
GIT_DIRTY
}
"
\
-v
\
-o
bin/packer
${
EXTENSION
}
.
)
&
waitSingle
# Go over each plugin and build it
for
PLUGIN
in
$(
find ./plugin
-mindepth
1
-maxdepth
1
-type
d
)
;
do
PLUGIN_NAME
=
$(
basename
${
PLUGIN
}
)
echo
-e
"
${
OK_COLOR
}
--> Compiling Plugin:
${
PLUGIN_NAME
}${
NO_COLOR
}
"
(
go build
\
${
PACKER_RACE
}
\
-ldflags
"-X github.com/mitchellh/packer/packer.GitCommit
${
GIT_COMMIT
}${
GIT_DIRTY
}
"
\
-v
\
-o
bin/packer-
${
PLUGIN_NAME
}${
EXTENSION
}
${
PLUGIN
}
)
&
waitSingle
done
waitAll
website/Gemfile
View file @
36326ee8
...
...
@@ -2,11 +2,11 @@ source 'https://rubygems.org'
ruby
'1.9.3'
gem
"middleman"
,
"~> 3.
0.6
"
gem
"middleman-minify-html"
,
"~> 3.
0.0
"
gem
"middleman"
,
"~> 3.
1.5
"
gem
"middleman-minify-html"
,
"~> 3.
1.1
"
gem
"rack-contrib"
,
"~> 1.1.0"
gem
"redcarpet"
,
"~>
2.2.2
"
gem
"therubyracer"
,
"~> 0.1
0.2
"
gem
"redcarpet"
,
"~>
3.0.0
"
gem
"therubyracer"
,
"~> 0.1
2.0
"
gem
"thin"
,
"~> 1.5.0"
group
:development
do
...
...
website/Gemfile.lock
View file @
36326ee8
GEM
remote: https://rubygems.org/
specs:
POpen4 (0.1.4)
Platform (>= 0.4.0)
open4
Platform (0.4.0)
activesupport (3.2.9)
i18n (~> 0.6)
activesupport (3.2.14)
i18n (~> 0.6, >= 0.6.4)
multi_json (~> 1.0)
chunky_png (1.2.
6
)
chunky_png (1.2.
8
)
coffee-script (2.2.0)
coffee-script-source
execjs
coffee-script-source (1.
3
.3)
coffee-script-source (1.
6
.3)
compass (0.12.2)
chunky_png (~> 1.2)
fssm (>= 0.2.7)
sass (~> 3.1)
daemons (1.1.9)
eventmachine (1.0.
0
)
eventmachine (1.0.
3
)
execjs (1.4.0)
multi_json (~> 1.0)
ffi (1.2.0)
fssm (0.2.9)
haml (3.1.7)
highline (1.6.15)
hike (1.2.1)
htmlcompressor (0.0.3)
yui-compressor (~> 0.9.6)
http_router (0.10.2)
rack (>= 1.0.0)
url_mount (~> 0.2.1)
i18n (0.6.1)
libv8 (3.3.10.4)
listen (0.5.3)
maruku (0.6.1)
syntax (>= 1.0.0)
middleman (3.0.6)
middleman-core (= 3.0.6)
middleman-more (= 3.0.6)
middleman-sprockets (~> 3.0.2)
middleman-core (3.0.6)
activesupport (~> 3.2.6)
bundler (~> 1.1)
listen (~> 0.5.2)
rack (~> 1.4.1)
rack-test (~> 0.6.1)
rb-fsevent (~> 0.9.1)
rb-inotify (~> 0.8.8)
thor (~> 0.15.4)
tilt (~> 1.3.1)
middleman-minify-html (3.0.0)
htmlcompressor
middleman-core (~> 3.0.0)
middleman-more (3.0.6)
ffi (1.9.0)
fssm (0.2.10)
haml (4.0.3)
tilt
highline (1.6.19)
hike (1.2.3)
i18n (0.6.5)
kramdown (1.1.0)
libv8 (3.16.14.3)
listen (1.2.3)
rb-fsevent (>= 0.9.3)
rb-inotify (>= 0.9)
rb-kqueue (>= 0.2)
middleman (3.1.5)
coffee-script (~> 2.2.0)
coffee-script-source (~> 1.3.3)
compass (>= 0.12.2)
execjs (~> 1.4.0)
haml (>= 3.1.6)
i18n (~> 0.6
.0)
m
aruku (~> 0.6.0
)
middleman-
core (= 3.0.6
)
padrino-helpers (= 0.10.7
)
kramdown (~> 1.1
.0)
m
iddleman-core (= 3.1.5
)
middleman-
more (= 3.1.5
)
middleman-sprockets (>= 3.1.2
)
sass (>= 3.1.20)
uglifier (~> 1.2.6)
middleman-sprockets (3.0.4)
middleman-more (~> 3.0.1)
sprockets (~> 2.1, < 2.5)
sprockets-sass (~> 0.8.0)
multi_json (1.4.0)
open4 (1.3.0)
padrino-core (0.10.7)
activesupport (~> 3.2.0)
http_router (~> 0.10.2)
sinatra (~> 1.3.1)
thor (~> 0.15.2)
tilt (~> 1.3.0)
padrino-helpers (0.10.7)
i18n (~> 0.6)
padrino-core (= 0.10.7)
rack (1.4.1)
uglifier (~> 2.1.0)
middleman-core (3.1.5)
activesupport (~> 3.2.6)
bundler (~> 1.1)
i18n (~> 0.6.1)
listen (~> 1.2.2)
rack (>= 1.4.5)
rack-test (~> 0.6.1)
thor (>= 0.15.2, < 2.0)
tilt (~> 1.3.6)
middleman-minify-html (3.1.1)
middleman-core (~> 3.0)
middleman-more (3.1.5)
middleman-sprockets (3.1.4)
middleman-core (>= 3.0.14)
middleman-more (>= 3.0.14)
sprockets (~> 2.1)
sprockets-helpers (~> 1.0.0)
sprockets-sass (~> 1.0.0)
multi_json (1.8.0)
rack (1.5.2)
rack-contrib (1.1.0)
rack (>= 0.9.1)
rack-protection (1.2.0)
rack
rack-test (0.6.2)
rack (>= 1.0)
rb-fsevent (0.9.
2
)
rb-inotify (0.
8.8
)
rb-fsevent (0.9.
3
)
rb-inotify (0.
9.2
)
ffi (>= 0.5.0)
redcarpet (2.2.2)
sass (3.2.3)
sinatra (1.3.3)
rack (~> 1.3, >= 1.3.6)
rack-protection (~> 1.2)
tilt (~> 1.3, >= 1.3.3)
sprockets (2.4.5)
rb-kqueue (0.2.0)
ffi (>= 0.5.0)
redcarpet (3.0.0)
ref (1.0.5)
sass (3.2.10)
sprockets (2.10.0)
hike (~> 1.2)
multi_json (~> 1.0)
rack (~> 1.0)
tilt (~> 1.1, != 1.3.0)
sprockets-sass (0.8.0)
sprockets-helpers (1.0.1)
sprockets (~> 2.0)
sprockets-sass (1.0.1)
sprockets (~> 2.0)
tilt (~> 1.1)
syntax (1.0
.0)
therubyracer (0.10.2
)
libv8 (~> 3.3.10)
thin (1.5.
0
)
therubyracer (0.12
.0)
libv8 (~> 3.16.14.0
)
ref
thin (1.5.
1
)
daemons (>= 1.0.9)
eventmachine (>= 0.12.6)
rack (>= 1.0.0)
thor (0.1
5.4
)
tilt (1.3.
3
)
uglifier (
1.2.7
)
thor (0.1
8.1
)
tilt (1.3.
7
)
uglifier (
2.1.2
)
execjs (>= 0.3.0)
multi_json (~> 1.3)
url_mount (0.2.1)
rack
yui-compressor (0.9.6)
POpen4 (>= 0.1.4)
multi_json (~> 1.0, >= 1.0.2)
PLATFORMS
ruby
DEPENDENCIES
highline (~> 1.6.15)
middleman (~> 3.
0.6
)
middleman-minify-html (~> 3.
0.0
)
middleman (~> 3.
1.5
)
middleman-minify-html (~> 3.
1.1
)
rack-contrib (~> 1.1.0)
redcarpet (~>
2.2.2
)
therubyracer (~> 0.1
0.2
)
redcarpet (~>
3.0.0
)
therubyracer (~> 0.1
2.0
)
thin (~> 1.5.0)
website/source/docs/builders/virtualbox.html.markdown
View file @
36326ee8
...
...
@@ -85,6 +85,10 @@ Optional:
*
`format`
(string) - Either "ovf" or "ova", this specifies the output
format of the exported virtual machine. This defaults to "ovf".
*
`guest_additions_attach`
(bool) - If this is true (defaults to "false"),
the guest additions ISO will be attached to the virtual machine as a CD
rather than uploaded as a raw ISO.
*
`guest_additions_path`
(string) - The path on the guest virtual machine
where the VirtualBox guest additions ISO will be uploaded. By default this
is "VBoxGuestAdditions.iso" which should upload into the login directory
...
...
website/source/docs/provisioners/chef-solo.html.markdown
View file @
36326ee8
...
...
@@ -44,6 +44,14 @@ configuration is actually required, but at least `run_list` is recommended.
to the remote machine in the directory specified by the
`staging_directory`
.
By default, this is empty.
*
`roles_path`
(string) - The path to the "roles" directory on your local filesystem.
These will be uploaded to the remote machine in the directory specified by the
`staging_directory`
. By default, this is empty.
*
`data_bags_path`
(string) - The path to the "data_bags" directory on your local filesystem.
These will be uploaded to the remote machine in the directory specified by the
`staging_directory`
. By default, this is empty.
*
`execute_command`
(string) - The command used to execute Chef. This has
various
[
configuration template variables
](
/docs/templates/configuration-templates.html
)
available. See below for more information.
...
...
website/source/docs/provisioners/puppet-masterless.html.markdown
View file @
36326ee8
...
...
@@ -79,7 +79,7 @@ By default, Packer uses the following command (broken across multiple lines
for readability) to execute Puppet:
```
{{.FacterVars}}{{if .Sudo} sudo -E {{end}}puppet apply \
{{.FacterVars}}{{if .Sudo}
}
sudo -E {{end}}puppet apply \
--verbose \
--modulepath='{{.ModulePath}}' \
{{if .HasHieraConfigPath}}--hiera_config='{{.HieraConfigPath}}' {{end}} \
...
...
website/source/docs/provisioners/shell.html.markdown
View file @
36326ee8
...
...
@@ -47,6 +47,10 @@ Exactly _one_ of the following is required:
Optional parameters:
*
`binary`
(boolean) - If true, specifies that the script(s) are binary
files, and Packer should therefore not convert Windows line endings to
Unix line endings (if there are any). By default this is false.
*
`environment_vars`
(array of strings) - An array of key/value pairs
to inject prior to the execute_command. The format should be
`key=value`
. Packer injects some environmental variables by default
...
...
@@ -98,7 +102,7 @@ root privileges without worrying about password prompts.
## Default Environmental Variables
In addition to being able to specify custom environmental variables using
the
`environment
al
_vars`
configuration, the provisioner automatically
the
`environment_vars`
configuration, the provisioner automatically
defines certain commonly useful environmental variables:
*
`PACKER_BUILD_NAME`
is set to the name of the build that Packer is running.
...
...
website/source/docs/templates/post-processors.html.markdown
View file @
36326ee8
...
...
@@ -125,3 +125,28 @@ The answer is no, of course not. Packer is smart enough to figure out
that at least one post-processor requested that the input be kept, so it will keep
it around.
</div>
## Run on Specific Builds
You can use the
`only`
or
`except`
configurations to run a post-processor
only with specific builds. These two configurations do what you expect:
`only`
will only run the post-processor on the specified builds and
`except`
will run the post-processor on anything other than the specified
builds.
An example of
`only`
being used is shown below, but the usage of
`except`
is effectively the same.
`only`
and
`except`
can only be specified on "detailed"
configurations. If you have a sequence of post-processors to run,
`only`
and
`except`
will only affect that single post-processor in the sequence.
<pre
class=
"prettyprint"
>
{
"type": "vagrant",
"only": ["virtualbox"]
}
</pre>
The values within
`only`
or
`except`
are _build names_, not builder
types. If you recall, build names by default are just their builder type,
but if you specify a custom
`name`
parameter, then you should use that
as the value instead of the type.
website/source/docs/templates/provisioners.html.markdown
View file @
36326ee8
...
...
@@ -53,6 +53,30 @@ provisioner to run a local script within the machines:
}
</pre>
## Run on Specific Builds
You can use the
`only`
or
`except`
configurations to run a provisioner
only with specific builds. These two configurations do what you expect:
`only`
will only run the provisioner on the specified builds and
`except`
will run the provisioner on anything other than the specified
builds.
An example of
`only`
being used is shown below, but the usage of
`except`
is effectively the same:
<pre
class=
"prettyprint"
>
{
"type": "shell",
"script": "script.sh",
"only": ["virtualbox"]
}
</pre>
The values within
`only`
or
`except`
are _build names_, not builder
types. If you recall, build names by default are just their builder type,
but if you specify a custom
`name`
parameter, then you should use that
as the value instead of the type.
## Build-Specific Overrides
While the goal of Packer is to produce identical machine images, it
...
...
website/source/docs/templates/user-variables.html.markdown
View file @
36326ee8
...
...
@@ -54,7 +54,7 @@ validation will fail.
Using the variables is extremely easy. Variables are used by calling
the user function in the form of
<code>
{{user
`
variable
`
}}
</code>
.
This function can be used in _any
string
_ within the template, in
This function can be used in _any
value
_ within the template, in
builders, provisioners, _anything_. The user variable is available globally
within the template.
...
...
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