Merge remote-tracking branch 'upstream/master' into puppet-server
This commit is contained in:
commit
ee50657097
15
CHANGELOG.md
15
CHANGELOG.md
@ -1,10 +1,25 @@
|
|||||||
## 0.5.2 (unreleased)
|
## 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:
|
BUG FIXES:
|
||||||
|
|
||||||
|
* core: Fix crash case if blank parameters are given to Packer. [GH-832]
|
||||||
* builders/docker: user variables work properly. [GH-777]
|
* builders/docker: user variables work properly. [GH-777]
|
||||||
* builder/virtualbox,vmware: iso\_checksum is not required if the
|
* builder/virtualbox,vmware: iso\_checksum is not required if the
|
||||||
checksum type is "none"
|
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)
|
## 0.5.1 (01/02/2014)
|
||||||
|
|
||||||
|
33
builder/docker/artifact_import.go
Normal file
33
builder/docker/artifact_import.go
Normal 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())
|
||||||
|
}
|
57
builder/docker/artifact_import_test.go
Normal file
57
builder/docker/artifact_import_test.go
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
@ -8,12 +8,21 @@ import (
|
|||||||
// Docker. The Driver interface also allows the steps to be tested since
|
// Docker. The Driver interface also allows the steps to be tested since
|
||||||
// a mock driver can be shimmed in.
|
// a mock driver can be shimmed in.
|
||||||
type Driver interface {
|
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 exports the container with the given ID to the given writer.
|
||||||
Export(id string, dst io.Writer) error
|
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 should pull down the given image.
|
||||||
Pull(image string) error
|
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,
|
// StartContainer starts a container and returns the ID for that container,
|
||||||
// along with a potential error.
|
// along with a potential error.
|
||||||
StartContainer(*ContainerConfig) (string, error)
|
StartContainer(*ContainerConfig) (string, error)
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
"github.com/mitchellh/packer/packer"
|
"github.com/mitchellh/packer/packer"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
@ -15,6 +16,25 @@ type DockerDriver struct {
|
|||||||
Tpl *packer.ConfigTemplate
|
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 {
|
func (d *DockerDriver) Export(id string, dst io.Writer) error {
|
||||||
var stderr bytes.Buffer
|
var stderr bytes.Buffer
|
||||||
cmd := exec.Command("docker", "export", id)
|
cmd := exec.Command("docker", "export", id)
|
||||||
@ -35,11 +55,49 @@ func (d *DockerDriver) Export(id string, dst io.Writer) error {
|
|||||||
return nil
|
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 {
|
func (d *DockerDriver) Pull(image string) error {
|
||||||
cmd := exec.Command("docker", "pull", image)
|
cmd := exec.Command("docker", "pull", image)
|
||||||
return runAndStream(cmd, d.Ui)
|
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) {
|
func (d *DockerDriver) StartContainer(config *ContainerConfig) (string, error) {
|
||||||
// Build up the template data
|
// Build up the template data
|
||||||
var tplData startContainerTemplate
|
var tplData startContainerTemplate
|
||||||
|
@ -6,6 +6,20 @@ import (
|
|||||||
|
|
||||||
// MockDriver is a driver implementation that can be used for tests.
|
// MockDriver is a driver implementation that can be used for tests.
|
||||||
type MockDriver struct {
|
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
|
ExportReader io.Reader
|
||||||
ExportError error
|
ExportError error
|
||||||
PullError error
|
PullError error
|
||||||
@ -25,6 +39,12 @@ type MockDriver struct {
|
|||||||
VerifyCalled bool
|
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 {
|
func (d *MockDriver) Export(id string, dst io.Writer) error {
|
||||||
d.ExportCalled = true
|
d.ExportCalled = true
|
||||||
d.ExportID = id
|
d.ExportID = id
|
||||||
@ -39,12 +59,25 @@ func (d *MockDriver) Export(id string, dst io.Writer) error {
|
|||||||
return d.ExportError
|
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 {
|
func (d *MockDriver) Pull(image string) error {
|
||||||
d.PullCalled = true
|
d.PullCalled = true
|
||||||
d.PullImage = image
|
d.PullImage = image
|
||||||
return d.PullError
|
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) {
|
func (d *MockDriver) StartContainer(config *ContainerConfig) (string, error) {
|
||||||
d.StartCalled = true
|
d.StartCalled = true
|
||||||
d.StartConfig = config
|
d.StartConfig = config
|
||||||
|
@ -92,6 +92,7 @@ func (s *stepTypeBootCommand) Run(state multistep.StateBag) multistep.StepAction
|
|||||||
func (*stepTypeBootCommand) Cleanup(multistep.StateBag) {}
|
func (*stepTypeBootCommand) Cleanup(multistep.StateBag) {}
|
||||||
|
|
||||||
func vncSendString(c *vnc.ClientConn, original string) {
|
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 := make(map[string]uint32)
|
||||||
special["<bs>"] = 0xFF08
|
special["<bs>"] = 0xFF08
|
||||||
special["<del>"] = 0xFFFF
|
special["<del>"] = 0xFFFF
|
||||||
@ -111,6 +112,16 @@ func vncSendString(c *vnc.ClientConn, original string) {
|
|||||||
special["<f12>"] = 0xFFC9
|
special["<f12>"] = 0xFFC9
|
||||||
special["<return>"] = 0xFF0D
|
special["<return>"] = 0xFF0D
|
||||||
special["<tab>"] = 0xFF09
|
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 := "~!@#$%^&*()_+{}|:\"<>?"
|
shiftedChars := "~!@#$%^&*()_+{}|:\"<>?"
|
||||||
|
|
||||||
|
@ -118,6 +118,16 @@ func scancodes(message string) []string {
|
|||||||
special["<f10>"] = []string{"44", "c4"}
|
special["<f10>"] = []string{"44", "c4"}
|
||||||
special["<return>"] = []string{"1c", "9c"}
|
special["<return>"] = []string{"1c", "9c"}
|
||||||
special["<tab>"] = []string{"0f", "8f"}
|
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 := "~!@#$%^&*()_+{}|:\"<>?"
|
shiftedChars := "~!@#$%^&*()_+{}|:\"<>?"
|
||||||
|
|
||||||
|
@ -116,6 +116,7 @@ func (s *stepTypeBootCommand) Run(state multistep.StateBag) multistep.StepAction
|
|||||||
func (*stepTypeBootCommand) Cleanup(multistep.StateBag) {}
|
func (*stepTypeBootCommand) Cleanup(multistep.StateBag) {}
|
||||||
|
|
||||||
func vncSendString(c *vnc.ClientConn, original string) {
|
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 := make(map[string]uint32)
|
||||||
special["<bs>"] = 0xFF08
|
special["<bs>"] = 0xFF08
|
||||||
special["<del>"] = 0xFFFF
|
special["<del>"] = 0xFFFF
|
||||||
@ -135,6 +136,16 @@ func vncSendString(c *vnc.ClientConn, original string) {
|
|||||||
special["<f12>"] = 0xFFC9
|
special["<f12>"] = 0xFFC9
|
||||||
special["<return>"] = 0xFF0D
|
special["<return>"] = 0xFF0D
|
||||||
special["<tab>"] = 0xFF09
|
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 := "~!@#$%^&*()_+{}|:\"<>?"
|
shiftedChars := "~!@#$%^&*()_+{}|:\"<>?"
|
||||||
|
|
||||||
|
@ -43,6 +43,10 @@ type DownloadConfig struct {
|
|||||||
// for the downloader will be used to verify with this checksum after
|
// for the downloader will be used to verify with this checksum after
|
||||||
// it is downloaded.
|
// it is downloaded.
|
||||||
Checksum []byte
|
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.
|
// A DownloadClient helps download, verify checksums, etc.
|
||||||
@ -73,8 +77,8 @@ func HashForType(t string) hash.Hash {
|
|||||||
func NewDownloadClient(c *DownloadConfig) *DownloadClient {
|
func NewDownloadClient(c *DownloadConfig) *DownloadClient {
|
||||||
if c.DownloaderMap == nil {
|
if c.DownloaderMap == nil {
|
||||||
c.DownloaderMap = map[string]Downloader{
|
c.DownloaderMap = map[string]Downloader{
|
||||||
"http": new(HTTPDownloader),
|
"http": &HTTPDownloader{userAgent: c.UserAgent},
|
||||||
"https": new(HTTPDownloader),
|
"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
|
// HTTPDownloader is an implementation of Downloader that downloads
|
||||||
// files over HTTP.
|
// files over HTTP.
|
||||||
type HTTPDownloader struct {
|
type HTTPDownloader struct {
|
||||||
progress uint
|
progress uint
|
||||||
total uint
|
total uint
|
||||||
|
userAgent string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*HTTPDownloader) Cancel() {
|
func (*HTTPDownloader) Cancel() {
|
||||||
@ -197,6 +202,10 @@ func (d *HTTPDownloader) Download(dst io.Writer, src *url.URL) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if d.userAgent != "" {
|
||||||
|
req.Header.Set("User-Agent", d.userAgent)
|
||||||
|
}
|
||||||
|
|
||||||
httpClient := &http.Client{
|
httpClient := &http.Client{
|
||||||
Transport: &http.Transport{
|
Transport: &http.Transport{
|
||||||
Proxy: http.ProxyFromEnvironment,
|
Proxy: http.ProxyFromEnvironment,
|
||||||
|
@ -4,6 +4,8 @@ import (
|
|||||||
"crypto/md5"
|
"crypto/md5"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"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) {
|
func TestHashForType(t *testing.T) {
|
||||||
if h := HashForType("md5"); h == nil {
|
if h := HashForType("md5"); h == nil {
|
||||||
t.Fatalf("md5 hash is nil")
|
t.Fatalf("md5 hash is nil")
|
||||||
|
@ -70,6 +70,7 @@ func (s *StepDownload) Run(state multistep.StateBag) multistep.StepAction {
|
|||||||
CopyFile: false,
|
CopyFile: false,
|
||||||
Hash: HashForType(s.ChecksumType),
|
Hash: HashForType(s.ChecksumType),
|
||||||
Checksum: checksum,
|
Checksum: checksum,
|
||||||
|
UserAgent: packer.VersionString(),
|
||||||
}
|
}
|
||||||
|
|
||||||
path, err, retry := s.download(config, state)
|
path, err, retry := s.download(config, state)
|
||||||
|
@ -42,7 +42,9 @@ const defaultConfig = `
|
|||||||
|
|
||||||
"post-processors": {
|
"post-processors": {
|
||||||
"vagrant": "packer-post-processor-vagrant",
|
"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": {
|
"provisioners": {
|
||||||
|
@ -221,7 +221,7 @@ func (e *coreEnvironment) Cli(args []string) (result int, err error) {
|
|||||||
|
|
||||||
// Trim up to the command name
|
// Trim up to the command name
|
||||||
for i, v := range args {
|
for i, v := range args {
|
||||||
if v[0] != '-' {
|
if len(v) > 0 && v[0] != '-' {
|
||||||
args = args[i:]
|
args = args[i:]
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -198,10 +198,17 @@ func TestEnvironment_Cli_CallsRun(t *testing.T) {
|
|||||||
func TestEnvironment_DefaultCli_Empty(t *testing.T) {
|
func TestEnvironment_DefaultCli_Empty(t *testing.T) {
|
||||||
defaultEnv := testEnvironment()
|
defaultEnv := testEnvironment()
|
||||||
|
|
||||||
|
// Test with no args
|
||||||
exitCode, _ := defaultEnv.Cli([]string{})
|
exitCode, _ := defaultEnv.Cli([]string{})
|
||||||
if exitCode != 1 {
|
if exitCode != 1 {
|
||||||
t.Fatalf("bad: %d", exitCode)
|
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) {
|
func TestEnvironment_DefaultCli_Help(t *testing.T) {
|
||||||
|
@ -31,6 +31,18 @@ func (versionCommand) Run(env Environment, args []string) int {
|
|||||||
env.Ui().Machine("version-prelease", VersionPrerelease)
|
env.Ui().Machine("version-prelease", VersionPrerelease)
|
||||||
env.Ui().Machine("version-commit", GitCommit)
|
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
|
var versionString bytes.Buffer
|
||||||
fmt.Fprintf(&versionString, "Packer v%s", Version)
|
fmt.Fprintf(&versionString, "Packer v%s", Version)
|
||||||
if VersionPrerelease != "" {
|
if VersionPrerelease != "" {
|
||||||
@ -41,10 +53,5 @@ func (versionCommand) Run(env Environment, args []string) int {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
env.Ui().Say(versionString.String())
|
return versionString.String()
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (versionCommand) Synopsis() string {
|
|
||||||
return "print Packer version"
|
|
||||||
}
|
}
|
||||||
|
15
plugin/post-processor-docker-import/main.go
Normal file
15
plugin/post-processor-docker-import/main.go
Normal 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()
|
||||||
|
}
|
1
plugin/post-processor-docker-import/main_test.go
Normal file
1
plugin/post-processor-docker-import/main_test.go
Normal file
@ -0,0 +1 @@
|
|||||||
|
package main
|
15
plugin/post-processor-docker-push/main.go
Normal file
15
plugin/post-processor-docker-push/main.go
Normal 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()
|
||||||
|
}
|
1
plugin/post-processor-docker-push/main_test.go
Normal file
1
plugin/post-processor-docker-push/main_test.go
Normal file
@ -0,0 +1 @@
|
|||||||
|
package main
|
98
post-processor/docker-import/post-processor.go
Normal file
98
post-processor/docker-import/post-processor.go
Normal 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
|
||||||
|
}
|
31
post-processor/docker-import/post-processor_test.go
Normal file
31
post-processor/docker-import/post-processor_test.go
Normal 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)
|
||||||
|
}
|
67
post-processor/docker-push/post-processor.go
Normal file
67
post-processor/docker-push/post-processor.go
Normal 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
|
||||||
|
}
|
31
post-processor/docker-push/post-processor_test.go
Normal file
31
post-processor/docker-push/post-processor_test.go
Normal 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)
|
||||||
|
}
|
@ -137,7 +137,7 @@ func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error {
|
|||||||
}
|
}
|
||||||
for _, src := range p.config.PlaybookPaths {
|
for _, src := range p.config.PlaybookPaths {
|
||||||
dst := filepath.Join(p.config.StagingDir, "playbooks", filepath.Base(src))
|
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)
|
return fmt.Errorf("Error uploading playbooks: %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ verify_go () {
|
|||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
local IFS=.
|
local IFS=.
|
||||||
local i ver1=($1) ver2=($2)
|
local i ver1="$1" ver2="$2"
|
||||||
|
|
||||||
for ((i=${#ver1[@]}; i<${#ver2[@]}; i++))
|
for ((i=${#ver1[@]}; i<${#ver2[@]}; i++))
|
||||||
do
|
do
|
||||||
@ -56,4 +56,10 @@ export XC_OS=$(go env GOOS)
|
|||||||
./scripts/compile.sh
|
./scripts/compile.sh
|
||||||
|
|
||||||
# Move all the compiled things to the PATH
|
# 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
|
||||||
|
@ -36,14 +36,14 @@ Required:
|
|||||||
Optional:
|
Optional:
|
||||||
|
|
||||||
* `image_id` (int) - The ID of the base image to use. This is the image that
|
* `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",
|
will be used to launch a new droplet and provision it. Defaults to "1505447",
|
||||||
which happens to be "Ubuntu 12.04 x64 Server."
|
which happens to be "Ubuntu 12.04.3 x64 Server."
|
||||||
|
|
||||||
* `region_id` (int) - The ID of the region to launch the droplet in. Consequently,
|
* `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
|
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.
|
which is the 512MB droplet.
|
||||||
|
|
||||||
* `private_networking` (bool) - Set to `true` to enable private networking
|
* `private_networking` (bool) - Set to `true` to enable private networking
|
||||||
|
@ -64,15 +64,35 @@ Optional:
|
|||||||
`["run", "-d", "-i", "-t", "-v", "{{.Volumes}}", "{{.Image}}", "/bin/bash"]`.
|
`["run", "-d", "-i", "-t", "-v", "{{.Volumes}}", "{{.Image}}", "/bin/bash"]`.
|
||||||
As you can see, you have a couple template variables to customize, as well.
|
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,
|
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
|
and push it to a container repository. Packer can do this for you automatically
|
||||||
docker image metadata, this process is manual. For example, the following will
|
with the [docker-import](/docs/post-processors/docker-import.html) and
|
||||||
import `mycontainer-123456789.tar` to the repository
|
[docker-push](/docs/post-processors/docker-push.html) post-processors.
|
||||||
`registry.mydomain.com/mycontainer`, tagged with `latest`:
|
|
||||||
|
|
||||||
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`
|
You can then add additional tags and push the image as usual with `docker tag`
|
||||||
and `docker push`, respectively.
|
and `docker push`, respectively.
|
||||||
@ -103,8 +123,3 @@ by Packer in the future:
|
|||||||
volumes, and other metadata. Packer builds a raw Docker container image
|
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
|
that has none of this metadata. You can pass in much of this metadata
|
||||||
at runtime with `docker run`.
|
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.
|
|
||||||
|
@ -28,16 +28,10 @@ Follow the steps below:
|
|||||||
2. Click on the project you want to use Packer with (or create one if you
|
2. Click on the project you want to use Packer with (or create one if you
|
||||||
don't have one yet).
|
don't have one yet).
|
||||||
3. Click "APIs & auth" in the left sidebar
|
3. Click "APIs & auth" in the left sidebar
|
||||||
4. Click "Registered apps" in the left sidebar
|
4. Click "Credentials" in the left sidebar
|
||||||
5. Click "Register App" and register a "Web Application". Choose any
|
5. Click "Create New Client ID" and choose "Service Account"
|
||||||
name you'd like.
|
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 app, click "Certificate" (below the OAuth 2.0 Client
|
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.
|
||||||
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_.
|
|
||||||
|
|
||||||
Finally, one last step, you'll have to convert the `p12` file you
|
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
|
got from Google into the PEM format. You can do this with OpenSSL, which
|
||||||
|
@ -8,7 +8,7 @@ Type: `qemu`
|
|||||||
|
|
||||||
The Qemu builder is able to create [KVM](http://www.linux-kvm.org)
|
The Qemu builder is able to create [KVM](http://www.linux-kvm.org)
|
||||||
and [Xen](http://www.xenproject.org) virtual machine images. Support
|
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
|
The builder builds a virtual machine by creating a new virtual machine
|
||||||
from scratch, booting it, installing an OS, rebooting the machine with the
|
from scratch, booting it, installing an OS, rebooting the machine with the
|
||||||
|
@ -5,7 +5,7 @@ page_title: "VirtualBox Builder (from an ISO)"
|
|||||||
|
|
||||||
# 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/)
|
The VirtualBox builder is able to create [VirtualBox](https://www.virtualbox.org/)
|
||||||
virtual machines and export them in the OVF format, starting from an
|
virtual machines and export them in the OVF format, starting from an
|
||||||
|
@ -46,11 +46,11 @@ type PostProcessor interface {
|
|||||||
|
|
||||||
The `Configure` method for each post-processor is called early in the
|
The `Configure` method for each post-processor is called early in the
|
||||||
build process to configure the post-processor. The configuration is passed
|
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
|
this configuration into an internal structure, validating it, and returning
|
||||||
any errors.
|
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
|
[mapstructure](https://github.com/mitchellh/mapstructure) library is
|
||||||
recommended. Mapstructure will take an `interface{}` and decode it into an
|
recommended. Mapstructure will take an `interface{}` and decode it into an
|
||||||
arbitrarily complex struct. If there are any errors, it generates very
|
arbitrarily complex struct. If there are any errors, it generates very
|
||||||
|
@ -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.
|
@ -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).
|
@ -63,7 +63,7 @@ briefly. Create a file `example.json` and fill it with the following contents:
|
|||||||
}
|
}
|
||||||
</pre>
|
</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
|
a [user variable](/docs/templates/user-variables.html), keeping your secret
|
||||||
keys out of the template. You can create security credentials
|
keys out of the template. You can create security credentials
|
||||||
on [this page](https://console.aws.amazon.com/iam/home?#security_credential).
|
on [this page](https://console.aws.amazon.com/iam/home?#security_credential).
|
||||||
|
@ -55,6 +55,8 @@
|
|||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
<li><h4>Post-Processors</h4></li>
|
<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/vagrant.html">Vagrant</a></li>
|
||||||
<li><a href="/docs/post-processors/vsphere.html">vSphere</a></li>
|
<li><a href="/docs/post-processors/vsphere.html">vSphere</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user