Merge remote-tracking branch 'upstream/master' into puppet-server

This commit is contained in:
Jan Brauer 2014-02-18 09:41:10 +01:00
commit ee50657097
36 changed files with 743 additions and 45 deletions

View File

@ -1,10 +1,25 @@
## 0.5.2 (unreleased)
FEATURES:
* **New post-processor:** `docker-import` - Import a Docker image
and give it a specific repository/tag.
* **New post-processor:** `docker-push` - Push an imported image to
a registry.
IMPROVEMENTS:
* core: Most downloads made by Packer now use a custom user agent. [GH-803]
BUG FIXES:
* core: Fix crash case if blank parameters are given to Packer. [GH-832]
* builders/docker: user variables work properly. [GH-777]
* builder/virtualbox,vmware: iso\_checksum is not required if the
checksum type is "none"
* builder/virtualbox,vmware/qemu: Support for additional scancodes for
`boot_command` such as `<up>`, `<left>`, `<insert>`, etc. [GH-808]
* provisioners/ansible-local: Properly upload custom playbooks. [GH-829]
## 0.5.1 (01/02/2014)

View File

@ -0,0 +1,33 @@
package docker
import (
"fmt"
)
// ImportArtifact is an Artifact implementation for when a container is
// exported from docker into a single flat file.
type ImportArtifact struct {
BuilderIdValue string
Driver Driver
IdValue string
}
func (a *ImportArtifact) BuilderId() string {
return a.BuilderIdValue
}
func (*ImportArtifact) Files() []string {
return nil
}
func (a *ImportArtifact) Id() string {
return a.IdValue
}
func (a *ImportArtifact) String() string {
return fmt.Sprintf("Imported Docker image: %s", a.Id())
}
func (a *ImportArtifact) Destroy() error {
return a.Driver.DeleteImage(a.Id())
}

View File

@ -0,0 +1,57 @@
package docker
import (
"errors"
"github.com/mitchellh/packer/packer"
"testing"
)
func TestImportArtifact_impl(t *testing.T) {
var _ packer.Artifact = new(ImportArtifact)
}
func TestImportArtifactBuilderId(t *testing.T) {
a := &ImportArtifact{BuilderIdValue: "foo"}
if a.BuilderId() != "foo" {
t.Fatalf("bad: %#v", a.BuilderId())
}
}
func TestImportArtifactFiles(t *testing.T) {
a := &ImportArtifact{}
if a.Files() != nil {
t.Fatalf("bad: %#v", a.Files())
}
}
func TestImportArtifactId(t *testing.T) {
a := &ImportArtifact{IdValue: "foo"}
if a.Id() != "foo" {
t.Fatalf("bad: %#v", a.Id())
}
}
func TestImportArtifactDestroy(t *testing.T) {
d := new(MockDriver)
a := &ImportArtifact{
Driver: d,
IdValue: "foo",
}
// No error
if err := a.Destroy(); err != nil {
t.Fatalf("err: %s", err)
}
if !d.DeleteImageCalled {
t.Fatal("delete image should be called")
}
if d.DeleteImageId != "foo" {
t.Fatalf("bad: %#v", d.DeleteImageId)
}
// With an error
d.DeleteImageErr = errors.New("foo")
if err := a.Destroy(); err != d.DeleteImageErr {
t.Fatalf("err: %#v", err)
}
}

View File

@ -8,12 +8,21 @@ import (
// Docker. The Driver interface also allows the steps to be tested since
// a mock driver can be shimmed in.
type Driver interface {
// Delete an image that is imported into Docker
DeleteImage(id string) error
// Export exports the container with the given ID to the given writer.
Export(id string, dst io.Writer) error
// Import imports a container from a tar file
Import(path, repo string) (string, error)
// Pull should pull down the given image.
Pull(image string) error
// Push pushes an image to a Docker index/registry.
Push(name string) error
// StartContainer starts a container and returns the ID for that container,
// along with a potential error.
StartContainer(*ContainerConfig) (string, error)

View File

@ -6,6 +6,7 @@ import (
"github.com/mitchellh/packer/packer"
"io"
"log"
"os"
"os/exec"
"strings"
)
@ -15,6 +16,25 @@ type DockerDriver struct {
Tpl *packer.ConfigTemplate
}
func (d *DockerDriver) DeleteImage(id string) error {
var stderr bytes.Buffer
cmd := exec.Command("docker", "rmi", id)
cmd.Stderr = &stderr
log.Printf("Deleting image: %s", id)
if err := cmd.Start(); err != nil {
return err
}
if err := cmd.Wait(); err != nil {
err = fmt.Errorf("Error deleting image: %s\nStderr: %s",
err, stderr.String())
return err
}
return nil
}
func (d *DockerDriver) Export(id string, dst io.Writer) error {
var stderr bytes.Buffer
cmd := exec.Command("docker", "export", id)
@ -35,11 +55,49 @@ func (d *DockerDriver) Export(id string, dst io.Writer) error {
return nil
}
func (d *DockerDriver) Import(path string, repo string) (string, error) {
var stdout bytes.Buffer
cmd := exec.Command("docker", "import", "-", repo)
cmd.Stdout = &stdout
stdin, err := cmd.StdinPipe()
if err != nil {
return "", err
}
// There should be only one artifact of the Docker builder
file, err := os.Open(path)
if err != nil {
return "", err
}
defer file.Close()
if err := cmd.Start(); err != nil {
return "", err
}
go func() {
defer stdin.Close()
io.Copy(stdin, file)
}()
if err := cmd.Wait(); err != nil {
err = fmt.Errorf("Error importing container: %s", err)
return "", err
}
return strings.TrimSpace(stdout.String()), nil
}
func (d *DockerDriver) Pull(image string) error {
cmd := exec.Command("docker", "pull", image)
return runAndStream(cmd, d.Ui)
}
func (d *DockerDriver) Push(name string) error {
cmd := exec.Command("docker", "push", name)
return runAndStream(cmd, d.Ui)
}
func (d *DockerDriver) StartContainer(config *ContainerConfig) (string, error) {
// Build up the template data
var tplData startContainerTemplate

View File

@ -6,6 +6,20 @@ import (
// MockDriver is a driver implementation that can be used for tests.
type MockDriver struct {
DeleteImageCalled bool
DeleteImageId string
DeleteImageErr error
ImportCalled bool
ImportPath string
ImportRepo string
ImportId string
ImportErr error
PushCalled bool
PushName string
PushErr error
ExportReader io.Reader
ExportError error
PullError error
@ -25,6 +39,12 @@ type MockDriver struct {
VerifyCalled bool
}
func (d *MockDriver) DeleteImage(id string) error {
d.DeleteImageCalled = true
d.DeleteImageId = id
return d.DeleteImageErr
}
func (d *MockDriver) Export(id string, dst io.Writer) error {
d.ExportCalled = true
d.ExportID = id
@ -39,12 +59,25 @@ func (d *MockDriver) Export(id string, dst io.Writer) error {
return d.ExportError
}
func (d *MockDriver) Import(path, repo string) (string, error) {
d.ImportCalled = true
d.ImportPath = path
d.ImportRepo = repo
return d.ImportId, d.ImportErr
}
func (d *MockDriver) Pull(image string) error {
d.PullCalled = true
d.PullImage = image
return d.PullError
}
func (d *MockDriver) Push(name string) error {
d.PushCalled = true
d.PushName = name
return d.PushErr
}
func (d *MockDriver) StartContainer(config *ContainerConfig) (string, error) {
d.StartCalled = true
d.StartConfig = config

View File

@ -92,6 +92,7 @@ func (s *stepTypeBootCommand) Run(state multistep.StateBag) multistep.StepAction
func (*stepTypeBootCommand) Cleanup(multistep.StateBag) {}
func vncSendString(c *vnc.ClientConn, original string) {
// Scancodes reference: https://github.com/qemu/qemu/blob/master/ui/vnc_keysym.h
special := make(map[string]uint32)
special["<bs>"] = 0xFF08
special["<del>"] = 0xFFFF
@ -111,6 +112,16 @@ func vncSendString(c *vnc.ClientConn, original string) {
special["<f12>"] = 0xFFC9
special["<return>"] = 0xFF0D
special["<tab>"] = 0xFF09
special["<up>"] = 0xFF52
special["<down>"] = 0xFF54
special["<left>"] = 0xFF51
special["<right>"] = 0xFF53
special["<spacebar>"] = 0x020
special["<insert>"] = 0xFF63
special["<home>"] = 0xFF50
special["<end>"] = 0xFF57
special["<pageUp>"] = 0xFF55
special["<pageDown>"] = 0xFF56
shiftedChars := "~!@#$%^&*()_+{}|:\"<>?"

View File

@ -118,6 +118,16 @@ func scancodes(message string) []string {
special["<f10>"] = []string{"44", "c4"}
special["<return>"] = []string{"1c", "9c"}
special["<tab>"] = []string{"0f", "8f"}
special["<up>"] = []string{"48", "c8"}
special["<down>"] = []string{"50", "d0"}
special["<left>"] = []string{"4b", "cb"}
special["<right>"] = []string{"4d", "cd"}
special["<spacebar>"] = []string{"39", "b9"}
special["<insert>"] = []string{"52", "d2"}
special["<home>"] = []string{"47", "c7"}
special["<end>"] = []string{"4f", "cf"}
special["<pageUp>"] = []string{"49", "c9"}
special["<pageDown>"] = []string{"51", "d1"}
shiftedChars := "~!@#$%^&*()_+{}|:\"<>?"

View File

@ -116,6 +116,7 @@ func (s *stepTypeBootCommand) Run(state multistep.StateBag) multistep.StepAction
func (*stepTypeBootCommand) Cleanup(multistep.StateBag) {}
func vncSendString(c *vnc.ClientConn, original string) {
// Scancodes reference: https://github.com/qemu/qemu/blob/master/ui/vnc_keysym.h
special := make(map[string]uint32)
special["<bs>"] = 0xFF08
special["<del>"] = 0xFFFF
@ -135,6 +136,16 @@ func vncSendString(c *vnc.ClientConn, original string) {
special["<f12>"] = 0xFFC9
special["<return>"] = 0xFF0D
special["<tab>"] = 0xFF09
special["<up>"] = 0xFF52
special["<down>"] = 0xFF54
special["<left>"] = 0xFF51
special["<right>"] = 0xFF53
special["<spacebar>"] = 0x020
special["<insert>"] = 0xFF63
special["<home>"] = 0xFF50
special["<end>"] = 0xFF57
special["<pageUp>"] = 0xFF55
special["<pageDown>"] = 0xFF56
shiftedChars := "~!@#$%^&*()_+{}|:\"<>?"

View File

@ -43,6 +43,10 @@ type DownloadConfig struct {
// for the downloader will be used to verify with this checksum after
// it is downloaded.
Checksum []byte
// What to use for the user agent for HTTP requests. If set to "", use the
// default user agent provided by Go.
UserAgent string
}
// A DownloadClient helps download, verify checksums, etc.
@ -73,8 +77,8 @@ func HashForType(t string) hash.Hash {
func NewDownloadClient(c *DownloadConfig) *DownloadClient {
if c.DownloaderMap == nil {
c.DownloaderMap = map[string]Downloader{
"http": new(HTTPDownloader),
"https": new(HTTPDownloader),
"http": &HTTPDownloader{userAgent: c.UserAgent},
"https": &HTTPDownloader{userAgent: c.UserAgent},
}
}
@ -182,8 +186,9 @@ func (d *DownloadClient) VerifyChecksum(path string) (bool, error) {
// HTTPDownloader is an implementation of Downloader that downloads
// files over HTTP.
type HTTPDownloader struct {
progress uint
total uint
progress uint
total uint
userAgent string
}
func (*HTTPDownloader) Cancel() {
@ -197,6 +202,10 @@ func (d *HTTPDownloader) Download(dst io.Writer, src *url.URL) error {
return err
}
if d.userAgent != "" {
req.Header.Set("User-Agent", d.userAgent)
}
httpClient := &http.Client{
Transport: &http.Transport{
Proxy: http.ProxyFromEnvironment,

View File

@ -4,6 +4,8 @@ import (
"crypto/md5"
"encoding/hex"
"io/ioutil"
"net/http"
"net/http/httptest"
"os"
"testing"
)
@ -41,6 +43,91 @@ func TestDownloadClient_VerifyChecksum(t *testing.T) {
}
}
func TestDownloadClientUsesDefaultUserAgent(t *testing.T) {
tf, err := ioutil.TempFile("", "packer")
if err != nil {
t.Fatalf("tempfile error: %s", err)
}
defer os.Remove(tf.Name())
defaultUserAgent := ""
asserted := false
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if defaultUserAgent == "" {
defaultUserAgent = r.UserAgent()
} else {
incomingUserAgent := r.UserAgent()
if incomingUserAgent != defaultUserAgent {
t.Fatalf("Expected user agent %s, got: %s", defaultUserAgent, incomingUserAgent)
}
asserted = true
}
}))
req, err := http.NewRequest("GET", server.URL, nil)
if err != nil {
t.Fatal(err)
}
httpClient := &http.Client{
Transport: &http.Transport{
Proxy: http.ProxyFromEnvironment,
},
}
_, err = httpClient.Do(req)
if err != nil {
t.Fatal(err)
}
config := &DownloadConfig{
Url: server.URL,
TargetPath: tf.Name(),
}
client := NewDownloadClient(config)
_, err = client.Get()
if err != nil {
t.Fatal(err)
}
if !asserted {
t.Fatal("User-Agent never observed")
}
}
func TestDownloadClientSetsUserAgent(t *testing.T) {
tf, err := ioutil.TempFile("", "packer")
if err != nil {
t.Fatalf("tempfile error: %s", err)
}
defer os.Remove(tf.Name())
asserted := false
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
asserted = true
if r.UserAgent() != "fancy user agent" {
t.Fatalf("Expected useragent fancy user agent, got: %s", r.UserAgent())
}
}))
config := &DownloadConfig{
Url: server.URL,
TargetPath: tf.Name(),
UserAgent: "fancy user agent",
}
client := NewDownloadClient(config)
_, err = client.Get()
if err != nil {
t.Fatal(err)
}
if !asserted {
t.Fatal("HTTP request never made")
}
}
func TestHashForType(t *testing.T) {
if h := HashForType("md5"); h == nil {
t.Fatalf("md5 hash is nil")

View File

@ -70,6 +70,7 @@ func (s *StepDownload) Run(state multistep.StateBag) multistep.StepAction {
CopyFile: false,
Hash: HashForType(s.ChecksumType),
Checksum: checksum,
UserAgent: packer.VersionString(),
}
path, err, retry := s.download(config, state)

View File

@ -42,7 +42,9 @@ const defaultConfig = `
"post-processors": {
"vagrant": "packer-post-processor-vagrant",
"vsphere": "packer-post-processor-vsphere"
"vsphere": "packer-post-processor-vsphere",
"docker-push": "packer-post-processor-docker-push",
"docker-import": "packer-post-processor-docker-import"
},
"provisioners": {

View File

@ -221,7 +221,7 @@ func (e *coreEnvironment) Cli(args []string) (result int, err error) {
// Trim up to the command name
for i, v := range args {
if v[0] != '-' {
if len(v) > 0 && v[0] != '-' {
args = args[i:]
break
}

View File

@ -198,10 +198,17 @@ func TestEnvironment_Cli_CallsRun(t *testing.T) {
func TestEnvironment_DefaultCli_Empty(t *testing.T) {
defaultEnv := testEnvironment()
// Test with no args
exitCode, _ := defaultEnv.Cli([]string{})
if exitCode != 1 {
t.Fatalf("bad: %d", exitCode)
}
// Test with only blank args
exitCode, _ = defaultEnv.Cli([]string{""})
if exitCode != 1 {
t.Fatalf("bad: %d", exitCode)
}
}
func TestEnvironment_DefaultCli_Help(t *testing.T) {

View File

@ -31,6 +31,18 @@ func (versionCommand) Run(env Environment, args []string) int {
env.Ui().Machine("version-prelease", VersionPrerelease)
env.Ui().Machine("version-commit", GitCommit)
env.Ui().Say(VersionString())
return 0
}
func (versionCommand) Synopsis() string {
return "print Packer version"
}
// VersionString returns the Packer version in human-readable
// form complete with pre-release and git commit info if it is
// available.
func VersionString() string {
var versionString bytes.Buffer
fmt.Fprintf(&versionString, "Packer v%s", Version)
if VersionPrerelease != "" {
@ -41,10 +53,5 @@ func (versionCommand) Run(env Environment, args []string) int {
}
}
env.Ui().Say(versionString.String())
return 0
}
func (versionCommand) Synopsis() string {
return "print Packer version"
return versionString.String()
}

View File

@ -0,0 +1,15 @@
package main
import (
"github.com/mitchellh/packer/packer/plugin"
"github.com/mitchellh/packer/post-processor/docker-import"
)
func main() {
server, err := plugin.Server()
if err != nil {
panic(err)
}
server.RegisterPostProcessor(new(dockerimport.PostProcessor))
server.Serve()
}

View File

@ -0,0 +1 @@
package main

View File

@ -0,0 +1,15 @@
package main
import (
"github.com/mitchellh/packer/packer/plugin"
"github.com/mitchellh/packer/post-processor/docker-push"
)
func main() {
server, err := plugin.Server()
if err != nil {
panic(err)
}
server.RegisterPostProcessor(new(dockerpush.PostProcessor))
server.Serve()
}

View File

@ -0,0 +1 @@
package main

View File

@ -0,0 +1,98 @@
package dockerimport
import (
"fmt"
"github.com/mitchellh/packer/builder/docker"
"github.com/mitchellh/packer/common"
"github.com/mitchellh/packer/packer"
)
const BuilderId = "packer.post-processor.docker-import"
type Config struct {
common.PackerConfig `mapstructure:",squash"`
Repository string `mapstructure:"repository"`
Tag string `mapstructure:"tag"`
tpl *packer.ConfigTemplate
}
type PostProcessor struct {
config Config
}
func (p *PostProcessor) Configure(raws ...interface{}) error {
_, err := common.DecodeConfig(&p.config, raws...)
if err != nil {
return err
}
p.config.tpl, err = packer.NewConfigTemplate()
if err != nil {
return err
}
p.config.tpl.UserVars = p.config.PackerUserVars
// Accumulate any errors
errs := new(packer.MultiError)
templates := map[string]*string{
"repository": &p.config.Repository,
"tag": &p.config.Tag,
}
for key, ptr := range templates {
if *ptr == "" {
errs = packer.MultiErrorAppend(
errs, fmt.Errorf("%s must be set", key))
}
*ptr, err = p.config.tpl.Process(*ptr, nil)
if err != nil {
errs = packer.MultiErrorAppend(
errs, fmt.Errorf("Error processing %s: %s", key, err))
}
}
if len(errs.Errors) > 0 {
return errs
}
return nil
}
func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (packer.Artifact, bool, error) {
if artifact.BuilderId() != docker.BuilderId {
err := fmt.Errorf(
"Unknown artifact type: %s\nCan only import from Docker builder artifacts.",
artifact.BuilderId())
return nil, false, err
}
importRepo := p.config.Repository
if p.config.Tag != "" {
importRepo += ":" + p.config.Tag
}
driver := &docker.DockerDriver{Tpl: p.config.tpl, Ui: ui}
ui.Message("Importing image: " + artifact.Id())
ui.Message("Repository: " + importRepo)
id, err := driver.Import(artifact.Files()[0], importRepo)
if err != nil {
return nil, false, err
}
ui.Message("Imported ID: " + id)
// Build the artifact
artifact = &docker.ImportArtifact{
BuilderIdValue: BuilderId,
Driver: driver,
IdValue: importRepo,
}
return artifact, false, nil
}

View File

@ -0,0 +1,31 @@
package dockerimport
import (
"bytes"
"github.com/mitchellh/packer/packer"
"testing"
)
func testConfig() map[string]interface{} {
return map[string]interface{}{}
}
func testPP(t *testing.T) *PostProcessor {
var p PostProcessor
if err := p.Configure(testConfig()); err != nil {
t.Fatalf("err: %s", err)
}
return &p
}
func testUi() *packer.BasicUi {
return &packer.BasicUi{
Reader: new(bytes.Buffer),
Writer: new(bytes.Buffer),
}
}
func TestPostProcessor_ImplementsPostProcessor(t *testing.T) {
var _ packer.PostProcessor = new(PostProcessor)
}

View File

@ -0,0 +1,67 @@
package dockerpush
import (
"fmt"
"github.com/mitchellh/packer/builder/docker"
"github.com/mitchellh/packer/common"
"github.com/mitchellh/packer/packer"
"github.com/mitchellh/packer/post-processor/docker-import"
"strings"
)
type Config struct {
common.PackerConfig `mapstructure:",squash"`
tpl *packer.ConfigTemplate
}
type PostProcessor struct {
config Config
}
func (p *PostProcessor) Configure(raws ...interface{}) error {
_, err := common.DecodeConfig(&p.config, raws...)
if err != nil {
return err
}
p.config.tpl, err = packer.NewConfigTemplate()
if err != nil {
return err
}
p.config.tpl.UserVars = p.config.PackerUserVars
// Accumulate any errors
errs := new(packer.MultiError)
if len(errs.Errors) > 0 {
return errs
}
return nil
}
func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (packer.Artifact, bool, error) {
if artifact.BuilderId() != dockerimport.BuilderId {
err := fmt.Errorf(
"Unknown artifact type: %s\nCan only import from docker-import artifacts.",
artifact.BuilderId())
return nil, false, err
}
driver := &docker.DockerDriver{Tpl: p.config.tpl, Ui: ui}
// Get the name. We strip off any tags from the name because the
// push doesn't use those.
name := artifact.Id()
if i := strings.Index(name, ":"); i >= 0 {
name = name[:i]
}
ui.Message("Pushing: " + name)
if err := driver.Push(name); err != nil {
return nil, false, err
}
return nil, false, nil
}

View File

@ -0,0 +1,31 @@
package dockerpush
import (
"bytes"
"github.com/mitchellh/packer/packer"
"testing"
)
func testConfig() map[string]interface{} {
return map[string]interface{}{}
}
func testPP(t *testing.T) *PostProcessor {
var p PostProcessor
if err := p.Configure(testConfig()); err != nil {
t.Fatalf("err: %s", err)
}
return &p
}
func testUi() *packer.BasicUi {
return &packer.BasicUi{
Reader: new(bytes.Buffer),
Writer: new(bytes.Buffer),
}
}
func TestPostProcessor_ImplementsPostProcessor(t *testing.T) {
var _ packer.PostProcessor = new(PostProcessor)
}

View File

@ -137,7 +137,7 @@ func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error {
}
for _, src := range p.config.PlaybookPaths {
dst := filepath.Join(p.config.StagingDir, "playbooks", filepath.Base(src))
if err := p.uploadFile(ui, comm, dst, src); err != nil {
if err := p.uploadDir(ui, comm, dst, src); err != nil {
return fmt.Errorf("Error uploading playbooks: %s", err)
}
}

View File

@ -15,7 +15,7 @@ verify_go () {
return 0
fi
local IFS=.
local i ver1=($1) ver2=($2)
local i ver1="$1" ver2="$2"
for ((i=${#ver1[@]}; i<${#ver2[@]}; i++))
do
@ -56,4 +56,10 @@ export XC_OS=$(go env GOOS)
./scripts/compile.sh
# Move all the compiled things to the PATH
cp pkg/${XC_OS}_${XC_ARCH}/* ${GOPATH}/bin
case $(uname) in
CYGWIN*)
GOPATH="$(cygpath $GOPATH)"
;;
esac
IFS=: MAIN_GOPATH=( $GOPATH )
cp pkg/${XC_OS}_${XC_ARCH}/* ${MAIN_GOPATH}/bin

View File

@ -36,14 +36,14 @@ Required:
Optional:
* `image_id` (int) - The ID of the base image to use. This is the image that
will be used to launch a new droplet and provision it. Defaults to "284203",
which happens to be "Ubuntu 12.04 x64 Server."
will be used to launch a new droplet and provision it. Defaults to "1505447",
which happens to be "Ubuntu 12.04.3 x64 Server."
* `region_id` (int) - The ID of the region to launch the droplet in. Consequently,
this is the region where the snapshot will be available. This defaults to
"1", which is "New York."
"1", which is "New York 1."
* `size_id` (int) - The ID of the droplet size to use. This defaults to "66,"
* `size_id` (int) - The ID of the droplet size to use. This defaults to "66",
which is the 512MB droplet.
* `private_networking` (bool) - Set to `true` to enable private networking

View File

@ -64,15 +64,35 @@ Optional:
`["run", "-d", "-i", "-t", "-v", "{{.Volumes}}", "{{.Image}}", "/bin/bash"]`.
As you can see, you have a couple template variables to customize, as well.
## Using the generated artifact
## Using the Artifact
Once the tar artifact has been generated, you will likely want to import, tag,
and push it to a container repository. Until packer supports management of the
docker image metadata, this process is manual. For example, the following will
import `mycontainer-123456789.tar` to the repository
`registry.mydomain.com/mycontainer`, tagged with `latest`:
and push it to a container repository. Packer can do this for you automatically
with the [docker-import](/docs/post-processors/docker-import.html) and
[docker-push](/docs/post-processors/docker-push.html) post-processors.
sudo docker import - registry.mydomain.com/mycontainer:latest < mycontainer-123456789.tar
The example below shows a full configuration that would import and push
the created image:
<pre class="prettyprint">
{
"post-processors": [
[
{
"type": "docker-import",
"repository": "mitchellh/packer",
"tag": "0.7"
},
"docker-push"
]
]
}
</pre>
If you want to do this manually, however, perhaps from a script, you can
import the image using the process below:
docker import - registry.mydomain.com/mycontainer:latest < artifact.tar
You can then add additional tags and push the image as usual with `docker tag`
and `docker push`, respectively.
@ -103,8 +123,3 @@ by Packer in the future:
volumes, and other metadata. Packer builds a raw Docker container image
that has none of this metadata. You can pass in much of this metadata
at runtime with `docker run`.
* Images made without dockerfiles are missing critical metadata that
make them easily pushable to the Docker registry. You can work around
this by using a metadata-only Dockerfile with the exported image and
building that. A future Packer version will automatically do this for you.

View File

@ -28,16 +28,10 @@ Follow the steps below:
2. Click on the project you want to use Packer with (or create one if you
don't have one yet).
3. Click "APIs & auth" in the left sidebar
4. Click "Registered apps" in the left sidebar
5. Click "Register App" and register a "Web Application". Choose any
name you'd like.
7. After creating the app, click "Certificate" (below the OAuth 2.0 Client
ID section), and click "Download JSON". This is your _client secrets JSON_
file. Make sure you didn't download the JSON from the "OAuth 2.0" section!
This is a common mistake and will cause the builder to not work.
8. Next, click "Generate Certificate". You should be prompted to download
a private key. Note the password for the private key! This private key
is your _client private key_.
4. Click "Credentials" in the left sidebar
5. Click "Create New Client ID" and choose "Service Account"
6. A private key will be downloaded for you. Note the password for the private key! This private key is your _client private key_.
7. After creating the account, click "Download JSON". This is your _client secrets JSON_ file. Make sure you didn't download the JSON from the "OAuth 2.0" section! This is a common mistake and will cause the builder to not work.
Finally, one last step, you'll have to convert the `p12` file you
got from Google into the PEM format. You can do this with OpenSSL, which

View File

@ -8,7 +8,7 @@ Type: `qemu`
The Qemu builder is able to create [KVM](http://www.linux-kvm.org)
and [Xen](http://www.xenproject.org) virtual machine images. Support
for Xen is experimanetal at this time.
for Xen is experimental at this time.
The builder builds a virtual machine by creating a new virtual machine
from scratch, booting it, installing an OS, rebooting the machine with the

View File

@ -5,7 +5,7 @@ page_title: "VirtualBox Builder (from an ISO)"
# VirtualBox Builder (from an ISO)
Type: `virtualbox`
Type: `virtualbox-iso`
The VirtualBox builder is able to create [VirtualBox](https://www.virtualbox.org/)
virtual machines and export them in the OVF format, starting from an

View File

@ -46,11 +46,11 @@ type PostProcessor interface {
The `Configure` method for each post-processor is called early in the
build process to configure the post-processor. The configuration is passed
in as a raw `interface{]`. The configure method is responsible for translating
in as a raw `interface{}`. The configure method is responsible for translating
this configuration into an internal structure, validating it, and returning
any errors.
For decoding the `interface{]` into a meaningful structure, the
For decoding the `interface{}` into a meaningful structure, the
[mapstructure](https://github.com/mitchellh/mapstructure) library is
recommended. Mapstructure will take an `interface{}` and decode it into an
arbitrarily complex struct. If there are any errors, it generates very

View File

@ -0,0 +1,44 @@
---
layout: "docs"
page_title: "docker-import Post-Processor"
---
# Docker Import Post-Processor
Type: `docker-import`
The Docker import post-processor takes an artifact from the
[docker builder](/docs/builders/docker.html) and imports it with Docker
locally. This allows you to apply a repository and tag to the image
and lets you use the other Docker post-processors such as
[docker-push](/docs/post-processors/docker-push.html) to push the image
to a registry.
## Configuration
The configuration for this post-processor is extremely simple. At least
a repository is required. The tag is optional.
* `repository` (string) - The repository of the imported image.
* `tag` (string) - The tag for the imported image. By default this is not
set.
## Example
An example is shown below, showing only the post-processor configuration:
<pre class="prettyprint">
{
"type": "docker-import",
"repository": "mitchellh/packer",
"tag": "0.7"
}
</pre>
This example would take the image created by the Docker builder
and import it into the local Docker process with a name of `mitchellh/packer:0.7`.
Following this, you can use the
[docker-push](/docs/post-processors/docker-push.html)
post-processor to push it to a registry, if you want.

View File

@ -0,0 +1,28 @@
---
layout: "docs"
page_title: "Docker Push Post-Processor"
---
# Docker Push Post-Processor
Type: `docker-push`
The Docker push post-processor takes an artifact from the
[docker-import](/docs/post-processors/docker-import.html) post-processor
and pushes it to a Docker registry.
<div class="alert alert-info alert-block">
<strong>Before you use this,</strong> you must manually <code>docker login</code>
to the proper repository. A future version of Packer will automate this
for you, but for now you must manually do this.
</div>
## Configuration
This post-processor has no configuration! Simply add it to your chain
of post-processors and the image will be uploaded.
## Example
For an example of using docker-push, see the section on using
generated artifacts from the [docker builder](/docs/builders/docker.html).

View File

@ -63,7 +63,7 @@ briefly. Create a file `example.json` and fill it with the following contents:
}
</pre>
When building, you'll pass in the `aws_access_key` and `aws_access_key` as
When building, you'll pass in the `aws_access_key` and `aws_secret_key` as
a [user variable](/docs/templates/user-variables.html), keeping your secret
keys out of the template. You can create security credentials
on [this page](https://console.aws.amazon.com/iam/home?#security_credential).

View File

@ -55,6 +55,8 @@
<ul>
<li><h4>Post-Processors</h4></li>
<li><a href="/docs/post-processors/docker-import.html">docker-import</a></li>
<li><a href="/docs/post-processors/docker-push.html">docker-push</a></li>
<li><a href="/docs/post-processors/vagrant.html">Vagrant</a></li>
<li><a href="/docs/post-processors/vsphere.html">vSphere</a></li>
</ul>