Merge pull request #2244 from mitchellh/f-docker-ssh
builder/docker: support custom communicators
This commit is contained in:
commit
dc067b3f10
|
@ -5,6 +5,7 @@ import (
|
|||
|
||||
"github.com/mitchellh/multistep"
|
||||
"github.com/mitchellh/packer/common"
|
||||
"github.com/mitchellh/packer/helper/communicator"
|
||||
"github.com/mitchellh/packer/packer"
|
||||
)
|
||||
|
||||
|
@ -42,7 +43,15 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
|||
&StepTempDir{},
|
||||
&StepPull{},
|
||||
&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 {
|
||||
|
|
|
@ -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/packer/common"
|
||||
"github.com/mitchellh/packer/helper/communicator"
|
||||
"github.com/mitchellh/packer/helper/config"
|
||||
"github.com/mitchellh/packer/packer"
|
||||
"github.com/mitchellh/packer/template/interpolate"
|
||||
|
@ -13,6 +14,7 @@ import (
|
|||
|
||||
type Config struct {
|
||||
common.PackerConfig `mapstructure:",squash"`
|
||||
Comm communicator.Config `mapstructure:",squash"`
|
||||
|
||||
Commit bool
|
||||
ExportPath string `mapstructure:"export_path"`
|
||||
|
@ -69,7 +71,15 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) {
|
|||
c.Pull = true
|
||||
}
|
||||
|
||||
// Default to the normal Docker type
|
||||
if c.Comm.Type == "" {
|
||||
c.Comm.Type = "docker"
|
||||
}
|
||||
|
||||
var errs *packer.MultiError
|
||||
if es := c.Comm.Prepare(&c.ctx); len(es) > 0 {
|
||||
errs = packer.MultiErrorAppend(errs, es...)
|
||||
}
|
||||
if c.Image == "" {
|
||||
errs = packer.MultiErrorAppend(errs,
|
||||
fmt.Errorf("image must be specified"))
|
||||
|
|
|
@ -22,6 +22,10 @@ type Driver interface {
|
|||
// Import imports a container from a tar file
|
||||
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
|
||||
// until Logout is called. Therefore, any users MUST call Logout.
|
||||
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
|
||||
}
|
||||
|
||||
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 {
|
||||
d.l.Lock()
|
||||
|
||||
|
|
|
@ -23,6 +23,11 @@ type MockDriver struct {
|
|||
ImportId string
|
||||
ImportErr error
|
||||
|
||||
IPAddressCalled bool
|
||||
IPAddressID string
|
||||
IPAddressResult string
|
||||
IPAddressErr error
|
||||
|
||||
LoginCalled bool
|
||||
LoginEmail string
|
||||
LoginUsername string
|
||||
|
@ -104,6 +109,12 @@ func (d *MockDriver) Import(path, repo string) (string, error) {
|
|||
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 {
|
||||
d.LoginCalled = true
|
||||
d.LoginRepo = r
|
||||
|
|
|
@ -2,12 +2,11 @@ package docker
|
|||
|
||||
import (
|
||||
"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)
|
||||
driver := state.Get("driver").(Driver)
|
||||
tempDir := state.Get("temp_dir").(string)
|
||||
|
@ -28,8 +27,8 @@ func (s *StepProvision) Run(state multistep.StateBag) multistep.StepAction {
|
|||
Version: version,
|
||||
}
|
||||
|
||||
prov := common.StepProvision{Comm: comm}
|
||||
return prov.Run(state)
|
||||
state.Put("communicator", comm)
|
||||
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.
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -50,6 +55,9 @@ func (s *StepConnect) Run(state multistep.StateBag) multistep.StepAction {
|
|||
WinRMConfig: s.WinRMConfig,
|
||||
},
|
||||
}
|
||||
for k, v := range s.CustomConnect {
|
||||
typeMap[k] = v
|
||||
}
|
||||
|
||||
step, ok := typeMap[s.Config.Type]
|
||||
if !ok {
|
||||
|
|
Loading…
Reference in New Issue