builder/docker: support custom communicators
This commit is contained in:
parent
7fc69828c5
commit
cab2665119
@ -5,6 +5,7 @@ import (
|
|||||||
|
|
||||||
"github.com/mitchellh/multistep"
|
"github.com/mitchellh/multistep"
|
||||||
"github.com/mitchellh/packer/common"
|
"github.com/mitchellh/packer/common"
|
||||||
|
"github.com/mitchellh/packer/helper/communicator"
|
||||||
"github.com/mitchellh/packer/packer"
|
"github.com/mitchellh/packer/packer"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -42,7 +43,15 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
|||||||
&StepTempDir{},
|
&StepTempDir{},
|
||||||
&StepPull{},
|
&StepPull{},
|
||||||
&StepRun{},
|
&StepRun{},
|
||||||
&StepProvision{},
|
&communicator.StepConnect{
|
||||||
|
Config: &b.config.Comm,
|
||||||
|
Host: commHost,
|
||||||
|
SSHConfig: sshConfig(&b.config.Comm),
|
||||||
|
CustomConnect: map[string]multistep.Step{
|
||||||
|
"docker": &StepConnectDocker{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
&common.StepProvision{},
|
||||||
}
|
}
|
||||||
|
|
||||||
if b.config.Commit {
|
if b.config.Commit {
|
||||||
|
52
builder/docker/comm.go
Normal file
52
builder/docker/comm.go
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
package docker
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
|
||||||
|
"github.com/mitchellh/multistep"
|
||||||
|
"github.com/mitchellh/packer/communicator/ssh"
|
||||||
|
"github.com/mitchellh/packer/helper/communicator"
|
||||||
|
gossh "golang.org/x/crypto/ssh"
|
||||||
|
)
|
||||||
|
|
||||||
|
func commHost(state multistep.StateBag) (string, error) {
|
||||||
|
containerId := state.Get("container_id").(string)
|
||||||
|
driver := state.Get("driver").(Driver)
|
||||||
|
return driver.IPAddress(containerId)
|
||||||
|
}
|
||||||
|
|
||||||
|
func sshConfig(comm *communicator.Config) func(state multistep.StateBag) (*gossh.ClientConfig, error) {
|
||||||
|
return func(state multistep.StateBag) (*gossh.ClientConfig, error) {
|
||||||
|
if comm.SSHPrivateKey != "" {
|
||||||
|
// key based auth
|
||||||
|
bytes, err := ioutil.ReadFile(comm.SSHPrivateKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Error setting up SSH config: %s", err)
|
||||||
|
}
|
||||||
|
privateKey := string(bytes)
|
||||||
|
|
||||||
|
signer, err := gossh.ParsePrivateKey([]byte(privateKey))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Error setting up SSH config: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &gossh.ClientConfig{
|
||||||
|
User: comm.SSHUsername,
|
||||||
|
Auth: []gossh.AuthMethod{
|
||||||
|
gossh.PublicKeys(signer),
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
} else {
|
||||||
|
// password based auth
|
||||||
|
return &gossh.ClientConfig{
|
||||||
|
User: comm.SSHUsername,
|
||||||
|
Auth: []gossh.AuthMethod{
|
||||||
|
gossh.Password(comm.SSHPassword),
|
||||||
|
gossh.KeyboardInteractive(
|
||||||
|
ssh.PasswordKeyboardInteractive(comm.SSHPassword)),
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -6,6 +6,7 @@ import (
|
|||||||
|
|
||||||
"github.com/mitchellh/mapstructure"
|
"github.com/mitchellh/mapstructure"
|
||||||
"github.com/mitchellh/packer/common"
|
"github.com/mitchellh/packer/common"
|
||||||
|
"github.com/mitchellh/packer/helper/communicator"
|
||||||
"github.com/mitchellh/packer/helper/config"
|
"github.com/mitchellh/packer/helper/config"
|
||||||
"github.com/mitchellh/packer/packer"
|
"github.com/mitchellh/packer/packer"
|
||||||
"github.com/mitchellh/packer/template/interpolate"
|
"github.com/mitchellh/packer/template/interpolate"
|
||||||
@ -13,6 +14,7 @@ import (
|
|||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
common.PackerConfig `mapstructure:",squash"`
|
common.PackerConfig `mapstructure:",squash"`
|
||||||
|
Comm communicator.Config `mapstructure:",squash"`
|
||||||
|
|
||||||
Commit bool
|
Commit bool
|
||||||
ExportPath string `mapstructure:"export_path"`
|
ExportPath string `mapstructure:"export_path"`
|
||||||
@ -69,7 +71,15 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) {
|
|||||||
c.Pull = true
|
c.Pull = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Default to the normal Docker type
|
||||||
|
if c.Comm.Type == "" {
|
||||||
|
c.Comm.Type = "docker"
|
||||||
|
}
|
||||||
|
|
||||||
var errs *packer.MultiError
|
var errs *packer.MultiError
|
||||||
|
if es := c.Comm.Prepare(&c.ctx); len(es) > 0 {
|
||||||
|
errs = packer.MultiErrorAppend(errs, es...)
|
||||||
|
}
|
||||||
if c.Image == "" {
|
if c.Image == "" {
|
||||||
errs = packer.MultiErrorAppend(errs,
|
errs = packer.MultiErrorAppend(errs,
|
||||||
fmt.Errorf("image must be specified"))
|
fmt.Errorf("image must be specified"))
|
||||||
|
@ -22,6 +22,10 @@ type Driver interface {
|
|||||||
// Import imports a container from a tar file
|
// Import imports a container from a tar file
|
||||||
Import(path, repo string) (string, error)
|
Import(path, repo string) (string, error)
|
||||||
|
|
||||||
|
// IPAddress returns the address of the container that can be used
|
||||||
|
// for external access.
|
||||||
|
IPAddress(id string) (string, error)
|
||||||
|
|
||||||
// Login. This will lock the driver from performing another Login
|
// Login. This will lock the driver from performing another Login
|
||||||
// until Logout is called. Therefore, any users MUST call Logout.
|
// until Logout is called. Therefore, any users MUST call Logout.
|
||||||
Login(repo, email, username, password string) error
|
Login(repo, email, username, password string) error
|
||||||
|
@ -116,6 +116,23 @@ func (d *DockerDriver) Import(path string, repo string) (string, error) {
|
|||||||
return strings.TrimSpace(stdout.String()), nil
|
return strings.TrimSpace(stdout.String()), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *DockerDriver) IPAddress(id string) (string, error) {
|
||||||
|
var stderr, stdout bytes.Buffer
|
||||||
|
cmd := exec.Command(
|
||||||
|
"docker",
|
||||||
|
"inspect",
|
||||||
|
"--format",
|
||||||
|
"{{ .NetworkSettings.IPAddress }}",
|
||||||
|
id)
|
||||||
|
cmd.Stdout = &stdout
|
||||||
|
cmd.Stderr = &stderr
|
||||||
|
if err := cmd.Run(); err != nil {
|
||||||
|
return "", fmt.Errorf("Error: %s\n\nStderr: %s", err, stderr.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.TrimSpace(stdout.String()), nil
|
||||||
|
}
|
||||||
|
|
||||||
func (d *DockerDriver) Login(repo, email, user, pass string) error {
|
func (d *DockerDriver) Login(repo, email, user, pass string) error {
|
||||||
d.l.Lock()
|
d.l.Lock()
|
||||||
|
|
||||||
|
@ -23,6 +23,11 @@ type MockDriver struct {
|
|||||||
ImportId string
|
ImportId string
|
||||||
ImportErr error
|
ImportErr error
|
||||||
|
|
||||||
|
IPAddressCalled bool
|
||||||
|
IPAddressID string
|
||||||
|
IPAddressResult string
|
||||||
|
IPAddressErr error
|
||||||
|
|
||||||
LoginCalled bool
|
LoginCalled bool
|
||||||
LoginEmail string
|
LoginEmail string
|
||||||
LoginUsername string
|
LoginUsername string
|
||||||
@ -104,6 +109,12 @@ func (d *MockDriver) Import(path, repo string) (string, error) {
|
|||||||
return d.ImportId, d.ImportErr
|
return d.ImportId, d.ImportErr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *MockDriver) IPAddress(id string) (string, error) {
|
||||||
|
d.IPAddressCalled = true
|
||||||
|
d.IPAddressID = id
|
||||||
|
return d.IPAddressResult, d.IPAddressErr
|
||||||
|
}
|
||||||
|
|
||||||
func (d *MockDriver) Login(r, e, u, p string) error {
|
func (d *MockDriver) Login(r, e, u, p string) error {
|
||||||
d.LoginCalled = true
|
d.LoginCalled = true
|
||||||
d.LoginRepo = r
|
d.LoginRepo = r
|
||||||
|
@ -2,12 +2,11 @@ package docker
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/mitchellh/multistep"
|
"github.com/mitchellh/multistep"
|
||||||
"github.com/mitchellh/packer/common"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type StepProvision struct{}
|
type StepConnectDocker struct{}
|
||||||
|
|
||||||
func (s *StepProvision) Run(state multistep.StateBag) multistep.StepAction {
|
func (s *StepConnectDocker) Run(state multistep.StateBag) multistep.StepAction {
|
||||||
containerId := state.Get("container_id").(string)
|
containerId := state.Get("container_id").(string)
|
||||||
driver := state.Get("driver").(Driver)
|
driver := state.Get("driver").(Driver)
|
||||||
tempDir := state.Get("temp_dir").(string)
|
tempDir := state.Get("temp_dir").(string)
|
||||||
@ -28,8 +27,8 @@ func (s *StepProvision) Run(state multistep.StateBag) multistep.StepAction {
|
|||||||
Version: version,
|
Version: version,
|
||||||
}
|
}
|
||||||
|
|
||||||
prov := common.StepProvision{Comm: comm}
|
state.Put("communicator", comm)
|
||||||
return prov.Run(state)
|
return multistep.ActionContinue
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StepProvision) Cleanup(state multistep.StateBag) {}
|
func (s *StepConnectDocker) Cleanup(state multistep.StateBag) {}
|
@ -32,6 +32,11 @@ type StepConnect struct {
|
|||||||
// connecting via WinRM.
|
// connecting via WinRM.
|
||||||
WinRMConfig func(multistep.StateBag) (*WinRMConfig, error)
|
WinRMConfig func(multistep.StateBag) (*WinRMConfig, error)
|
||||||
|
|
||||||
|
// CustomConnect can be set to have custom connectors for specific
|
||||||
|
// types. These take highest precedence so you can also override
|
||||||
|
// existing types.
|
||||||
|
CustomConnect map[string]multistep.Step
|
||||||
|
|
||||||
substep multistep.Step
|
substep multistep.Step
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,6 +55,9 @@ func (s *StepConnect) Run(state multistep.StateBag) multistep.StepAction {
|
|||||||
WinRMConfig: s.WinRMConfig,
|
WinRMConfig: s.WinRMConfig,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
for k, v := range s.CustomConnect {
|
||||||
|
typeMap[k] = v
|
||||||
|
}
|
||||||
|
|
||||||
step, ok := typeMap[s.Config.Type]
|
step, ok := typeMap[s.Config.Type]
|
||||||
if !ok {
|
if !ok {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user