From 7366b6c78e3dbb0089b4203f0646b5adf8ec1584 Mon Sep 17 00:00:00 2001 From: Chris Lundquist Date: Fri, 27 May 2016 03:21:55 +0000 Subject: [PATCH 01/15] [lxd] first pass --- builder/lxd/artifact.go | 35 ++++++ builder/lxd/builder.go | 115 +++++++++++++++++++ builder/lxd/builder_test.go | 54 +++++++++ builder/lxd/command.go | 15 +++ builder/lxd/communicator.go | 151 +++++++++++++++++++++++++ builder/lxd/communicator_test.go | 14 +++ builder/lxd/config.go | 74 ++++++++++++ builder/lxd/step_export.go | 74 ++++++++++++ builder/lxd/step_lxd_launch.go | 84 ++++++++++++++ builder/lxd/step_prepare_output_dir.go | 49 ++++++++ builder/lxd/step_provision.go | 35 ++++++ command/plugin.go | 3 + 12 files changed, 703 insertions(+) create mode 100644 builder/lxd/artifact.go create mode 100644 builder/lxd/builder.go create mode 100644 builder/lxd/builder_test.go create mode 100644 builder/lxd/command.go create mode 100644 builder/lxd/communicator.go create mode 100644 builder/lxd/communicator_test.go create mode 100644 builder/lxd/config.go create mode 100644 builder/lxd/step_export.go create mode 100644 builder/lxd/step_lxd_launch.go create mode 100644 builder/lxd/step_prepare_output_dir.go create mode 100644 builder/lxd/step_provision.go diff --git a/builder/lxd/artifact.go b/builder/lxd/artifact.go new file mode 100644 index 000000000..65acbba91 --- /dev/null +++ b/builder/lxd/artifact.go @@ -0,0 +1,35 @@ +package lxd + +import ( + "fmt" + "os" +) + +type Artifact struct { + dir string + f []string +} + +func (*Artifact) BuilderId() string { + return BuilderId +} + +func (a *Artifact) Files() []string { + return a.f +} + +func (*Artifact) Id() string { + return "Container" +} + +func (a *Artifact) String() string { + return fmt.Sprintf("Container files in directory: %s", a.dir) +} + +func (a *Artifact) State(name string) interface{} { + return nil +} + +func (a *Artifact) Destroy() error { + return os.RemoveAll(a.dir) +} diff --git a/builder/lxd/builder.go b/builder/lxd/builder.go new file mode 100644 index 000000000..1b28a9563 --- /dev/null +++ b/builder/lxd/builder.go @@ -0,0 +1,115 @@ +package lxd + +import ( + "errors" + "github.com/mitchellh/multistep" + "github.com/mitchellh/packer/common" + "github.com/mitchellh/packer/packer" + "github.com/mitchellh/packer/template/interpolate" + "log" + "os" + "path/filepath" + "runtime" +) + +// The unique ID for this builder +const BuilderId = "lxd" + +type wrappedCommandTemplate struct { + Command string +} + +type Builder struct { + config *Config + runner multistep.Runner +} + +func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { + c, errs := NewConfig(raws...) + if errs != nil { + return nil, errs + } + b.config = c + + return nil, nil +} + +func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) { + if runtime.GOOS != "linux" { + return nil, errors.New("The lxc builder only works on linux environments.") + } + + wrappedCommand := func(command string) (string, error) { + b.config.ctx.Data = &wrappedCommandTemplate{Command: command} + return interpolate.Render(b.config.CommandWrapper, &b.config.ctx) + } + + steps := []multistep.Step{ + new(stepPrepareOutputDir), + new(stepLxdLaunch), + new(StepProvision), + new(stepExport), + } + + // Setup the state bag + state := new(multistep.BasicStateBag) + state.Put("config", b.config) + state.Put("cache", cache) + state.Put("hook", hook) + state.Put("ui", ui) + state.Put("wrappedCommand", CommandWrapper(wrappedCommand)) + + // Run + if b.config.PackerDebug { + b.runner = &multistep.DebugRunner{ + Steps: steps, + PauseFn: common.MultistepDebugFn(ui), + } + } else { + b.runner = &multistep.BasicRunner{Steps: steps} + } + + b.runner.Run(state) + + // If there was an error, return that + if rawErr, ok := state.GetOk("error"); ok { + return nil, rawErr.(error) + } + + // If we were interrupted or cancelled, then just exit. + if _, ok := state.GetOk(multistep.StateCancelled); ok { + return nil, errors.New("Build was cancelled.") + } + + if _, ok := state.GetOk(multistep.StateHalted); ok { + return nil, errors.New("Build was halted.") + } + + // Compile the artifact list + files := make([]string, 0, 5) + visit := func(path string, info os.FileInfo, err error) error { + if !info.IsDir() { + files = append(files, path) + } + + return err + } + + if err := filepath.Walk(b.config.OutputDir, visit); err != nil { + return nil, err + } + + artifact := &Artifact{ + dir: b.config.OutputDir, + f: files, + } + + return artifact, nil +} + +func (b *Builder) Cancel() { + if b.runner != nil { + log.Println("Cancelling the step runner...") + b.runner.Cancel() + } +} diff --git a/builder/lxd/builder_test.go b/builder/lxd/builder_test.go new file mode 100644 index 000000000..ac6c262ac --- /dev/null +++ b/builder/lxd/builder_test.go @@ -0,0 +1,54 @@ +package lxd + +import ( + "os" + "testing" + + "github.com/mitchellh/packer/packer" +) +func testConfig() map[string]interface{} { + return map[string]interface{}{ + "output_dir": "foo", + "image": "bar", + } +} + +func TestBuilder_Foo(t *testing.T) { + if os.Getenv("PACKER_ACC") == "" { + t.Skip("This test is only run with PACKER_ACC=1") + } +} + +func TestBuilderPrepare_ConfigFile(t *testing.T) { + var b Builder + // Good + config := testConfig() + warnings, err := b.Prepare(config) + if len(warnings) > 0 { + t.Fatalf("bad: %#v", warnings) + } + if err != nil { + t.Fatalf("should not have error: %s", err) + } + + // Bad, missing image name + config = testConfig() + delete(config, "image") + b = Builder{} + warnings, err = b.Prepare(config) + if len(warnings) > 0 { + t.Fatalf("bad: %#v", warnings) + } + if err == nil { + t.Fatalf("should have error") + } + +} + +func TestBuilder_ImplementsBuilder(t *testing.T) { + var raw interface{} + raw = &Builder{} + if _, ok := raw.(packer.Builder); !ok { + t.Fatalf("Builder should be a builder") + } +} diff --git a/builder/lxd/command.go b/builder/lxd/command.go new file mode 100644 index 000000000..a432e0bba --- /dev/null +++ b/builder/lxd/command.go @@ -0,0 +1,15 @@ +package lxd + +import ( + "os/exec" +) + +// CommandWrapper is a type that given a command, will possibly modify that +// command in-flight. This might return an error. +type CommandWrapper func(string) (string, error) + +// ShellCommand takes a command string and returns an *exec.Cmd to execute +// it within the context of a shell (/bin/sh). +func ShellCommand(command string) *exec.Cmd { + return exec.Command("/bin/sh", "-c", command) +} diff --git a/builder/lxd/communicator.go b/builder/lxd/communicator.go new file mode 100644 index 000000000..a69c4246c --- /dev/null +++ b/builder/lxd/communicator.go @@ -0,0 +1,151 @@ +package lxd + +import ( + "fmt" + "github.com/mitchellh/packer/packer" + "io" + "io/ioutil" + "log" + "os" + "os/exec" + "path/filepath" + "strings" + "syscall" +) + +type Communicator struct { + RootFs string + ContainerName string + CmdWrapper CommandWrapper +} + +func (c *Communicator) Start(cmd *packer.RemoteCmd) error { + localCmd, err := c.Execute(cmd.Command) + + if err != nil { + return err + } + + localCmd.Stdin = cmd.Stdin + localCmd.Stdout = cmd.Stdout + localCmd.Stderr = cmd.Stderr + if err := localCmd.Start(); err != nil { + return err + } + + go func() { + exitStatus := 0 + if err := localCmd.Wait(); err != nil { + if exitErr, ok := err.(*exec.ExitError); ok { + exitStatus = 1 + + // There is no process-independent way to get the REAL + // exit status so we just try to go deeper. + if status, ok := exitErr.Sys().(syscall.WaitStatus); ok { + exitStatus = status.ExitStatus() + } + } + } + + log.Printf( + "lxc-attach execution exited with '%d': '%s'", + exitStatus, cmd.Command) + cmd.SetExited(exitStatus) + }() + + return nil +} + +func (c *Communicator) Upload(dst string, r io.Reader, fi *os.FileInfo) error { + dst = filepath.Join(c.RootFs, dst) + log.Printf("Uploading to rootfs: %s", dst) + tf, err := ioutil.TempFile("", "packer-lxc-attach") + if err != nil { + return fmt.Errorf("Error uploading file to rootfs: %s", err) + } + defer os.Remove(tf.Name()) + io.Copy(tf, r) + + cpCmd, err := c.CmdWrapper(fmt.Sprintf("sudo cp %s %s", tf.Name(), dst)) + if err != nil { + return err + } + + log.Printf("Running copy command: %s", dst) + + return ShellCommand(cpCmd).Run() +} + +func (c *Communicator) UploadDir(dst string, src string, exclude []string) error { + // TODO: remove any file copied if it appears in `exclude` + dest := filepath.Join(c.RootFs, dst) + log.Printf("Uploading directory '%s' to rootfs '%s'", src, dest) + cpCmd, err := c.CmdWrapper(fmt.Sprintf("sudo cp -R %s/. %s", src, dest)) + if err != nil { + return err + } + + return ShellCommand(cpCmd).Run() +} + +func (c *Communicator) Download(src string, w io.Writer) error { + src = filepath.Join(c.RootFs, src) + log.Printf("Downloading from rootfs dir: %s", src) + f, err := os.Open(src) + if err != nil { + return err + } + defer f.Close() + + if _, err := io.Copy(w, f); err != nil { + return err + } + + return nil +} + +func (c *Communicator) DownloadDir(src string, dst string, exclude []string) error { + return fmt.Errorf("DownloadDir is not implemented for lxc") +} + +func (c *Communicator) Execute(commandString string) (*exec.Cmd, error) { + log.Printf("Executing with lxc-attach in container: %s %s %s", c.ContainerName, c.RootFs, commandString) + command, err := c.CmdWrapper( + fmt.Sprintf("sudo lxc-attach --name %s -- /bin/sh -c \"%s\"", c.ContainerName, commandString)) + if err != nil { + return nil, err + } + + localCmd := ShellCommand(command) + log.Printf("Executing lxc-attach: %s %#v", localCmd.Path, localCmd.Args) + + return localCmd, nil +} + +func (c *Communicator) CheckInit() (string, error) { + log.Printf("Debug runlevel exec") + localCmd, err := c.Execute("/sbin/runlevel") + + if err != nil { + return "", err + } + + pr, _ := localCmd.StdoutPipe() + if err = localCmd.Start(); err != nil { + return "", err + } + + output, err := ioutil.ReadAll(pr) + + if err != nil { + return "", err + } + + err = localCmd.Wait() + + if err != nil { + return "", err + } + + return strings.TrimSpace(string(output)), nil +} diff --git a/builder/lxd/communicator_test.go b/builder/lxd/communicator_test.go new file mode 100644 index 000000000..9d6a26dc1 --- /dev/null +++ b/builder/lxd/communicator_test.go @@ -0,0 +1,14 @@ +package lxd + +import ( + "github.com/mitchellh/packer/packer" + "testing" +) + +func TestCommunicator_ImplementsCommunicator(t *testing.T) { + var raw interface{} + raw = &Communicator{} + if _, ok := raw.(packer.Communicator); !ok { + t.Fatalf("Communicator should be a communicator") + } +} diff --git a/builder/lxd/config.go b/builder/lxd/config.go new file mode 100644 index 000000000..53c7deebe --- /dev/null +++ b/builder/lxd/config.go @@ -0,0 +1,74 @@ +package lxd + +import ( + "fmt" + "github.com/mitchellh/mapstructure" + "github.com/mitchellh/packer/common" + "github.com/mitchellh/packer/helper/config" + "github.com/mitchellh/packer/packer" + "github.com/mitchellh/packer/template/interpolate" + "time" +) + +type Config struct { + common.PackerConfig `mapstructure:",squash"` + ///ConfigFile string `mapstructure:"config_file"` + OutputDir string `mapstructure:"output_dir"` + ContainerName string `mapstructure:"container_name"` + CommandWrapper string `mapstructure:"command_wrapper"` + RawInitTimeout string `mapstructure:"init_timeout"` + Image string `mapstructure:"image"` + Remote string `mapstructure:"remote"` + //EnvVars []string `mapstructure:"template_environment_vars"` + //TargetRunlevel int `mapstructure:"target_runlevel"` + InitTimeout time.Duration + + ctx interpolate.Context +} + +func NewConfig(raws ...interface{}) (*Config, error) { + var c Config + + var md mapstructure.Metadata + err := config.Decode(&c, &config.DecodeOpts{ + Metadata: &md, + Interpolate: true, + }, raws...) + if err != nil { + return nil, err + } + + // Accumulate any errors + var errs *packer.MultiError + + if c.OutputDir == "" { + c.OutputDir = fmt.Sprintf("output-%s", c.PackerBuildName) + } + + if c.ContainerName == "" { + c.ContainerName = fmt.Sprintf("packer-%s", c.PackerBuildName) + } + + if c.CommandWrapper == "" { + c.CommandWrapper = "{{.Command}}" + } + + if c.RawInitTimeout == "" { + c.RawInitTimeout = "20s" + } + + if c.Image == "" { + errs = packer.MultiErrorAppend(errs, fmt.Errorf("`image` is a required parameter for LXD.")) + } + + c.InitTimeout, err = time.ParseDuration(c.RawInitTimeout) + if err != nil { + errs = packer.MultiErrorAppend(errs, fmt.Errorf("Failed parsing init_timeout: %s", err)) + } + + if errs != nil && len(errs.Errors) > 0 { + return nil, errs + } + + return &c, nil +} diff --git a/builder/lxd/step_export.go b/builder/lxd/step_export.go new file mode 100644 index 000000000..bf7e77e84 --- /dev/null +++ b/builder/lxd/step_export.go @@ -0,0 +1,74 @@ +package lxd + +import ( + "bytes" + "fmt" + "github.com/mitchellh/multistep" + "github.com/mitchellh/packer/packer" + "log" + "os/exec" + "path/filepath" + "strings" +) + +type stepExport struct{} + +func (s *stepExport) Run(state multistep.StateBag) multistep.StepAction { + config := state.Get("config").(*Config) + ui := state.Get("ui").(packer.Ui) + + name := config.ContainerName + + //outputPath := filepath.Join(config.OutputDir, "rootfs.tar.gz") + + commands := make([][]string, 3) + commands[0] = []string{ + "lxc", "stop", name, + } + commands[1] = []string{ + "lxc", "export", + } + //commands[1] = []string{ + // "tar", "-C", containerDir, "--numeric-owner", "--anchored", "--exclude=./rootfs/dev/log", "-czf", outputPath, "./rootfs", + //} + commands[2] = []string{ + "sh", "-c", "chown $USER:`id -gn` " + filepath.Join(config.OutputDir, "*"), + } + + ui.Say("Exporting container...") + for _, command := range commands { + err := s.SudoCommand(command...) + if err != nil { + err := fmt.Errorf("Error exporting container: %s", err) + state.Put("error", err) + ui.Error(err.Error()) + return multistep.ActionHalt + } + } + + return multistep.ActionContinue +} + +func (s *stepExport) Cleanup(state multistep.StateBag) {} + +func (s *stepExport) SudoCommand(args ...string) error { + var stdout, stderr bytes.Buffer + + log.Printf("Executing sudo command: %#v", args) + cmd := exec.Command("sudo", args...) + cmd.Stdout = &stdout + cmd.Stderr = &stderr + err := cmd.Run() + + stdoutString := strings.TrimSpace(stdout.String()) + stderrString := strings.TrimSpace(stderr.String()) + + if _, ok := err.(*exec.ExitError); ok { + err = fmt.Errorf("Sudo command error: %s", stderrString) + } + + log.Printf("stdout: %s", stdoutString) + log.Printf("stderr: %s", stderrString) + + return err +} diff --git a/builder/lxd/step_lxd_launch.go b/builder/lxd/step_lxd_launch.go new file mode 100644 index 000000000..52bcc9d50 --- /dev/null +++ b/builder/lxd/step_lxd_launch.go @@ -0,0 +1,84 @@ +package lxd + +import ( + "bytes" + "fmt" + "github.com/mitchellh/multistep" + "github.com/mitchellh/packer/packer" + "log" + "os/exec" + "strings" +) + +type stepLxdLaunch struct{} + +func (s *stepLxdLaunch) Run(state multistep.StateBag) multistep.StepAction { + config := state.Get("config").(*Config) + ui := state.Get("ui").(packer.Ui) + + name := config.ContainerName + image := config.Image + remote := config.Remote + + commands := make([][]string, 1) + if remote == "" { + commands[0] = []string{"lxc", "launch", image, name} + } else { + + commands[0] = []string{"lxc", "launch", fmt.Sprintf("%s:%s", remote, image), name} + } + //commands[0] = append(commands[0], config.Parameters...) + // todo: wait for init to finish before moving on to provisioning instead of this + + ui.Say("Creating container...") + for _, command := range commands { + log.Printf("Executing sudo command: %#v", command) + err := s.SudoCommand(command...) + if err != nil { + err := fmt.Errorf("Error creating container: %s", err) + state.Put("error", err) + ui.Error(err.Error()) + return multistep.ActionHalt + } + } + + //state.Put("mount_path", rootfs) + + return multistep.ActionContinue +} + +func (s *stepLxdLaunch) Cleanup(state multistep.StateBag) { + config := state.Get("config").(*Config) + ui := state.Get("ui").(packer.Ui) + + command := []string{ + "lxc", "delete", "--force", config.ContainerName, + } + + ui.Say("Unregistering and deleting deleting container...") + if err := s.SudoCommand(command...); err != nil { + ui.Error(fmt.Sprintf("Error deleting container: %s", err)) + } +} + +func (s *stepLxdLaunch) SudoCommand(args ...string) error { + var stdout, stderr bytes.Buffer + + log.Printf("Executing sudo command: %#v", args) + cmd := exec.Command("sudo", args...) + cmd.Stdout = &stdout + cmd.Stderr = &stderr + err := cmd.Run() + + stdoutString := strings.TrimSpace(stdout.String()) + stderrString := strings.TrimSpace(stderr.String()) + + if _, ok := err.(*exec.ExitError); ok { + err = fmt.Errorf("Sudo command error: %s", stderrString) + } + + log.Printf("stdout: %s", stdoutString) + log.Printf("stderr: %s", stderrString) + + return err +} diff --git a/builder/lxd/step_prepare_output_dir.go b/builder/lxd/step_prepare_output_dir.go new file mode 100644 index 000000000..aa0565b4c --- /dev/null +++ b/builder/lxd/step_prepare_output_dir.go @@ -0,0 +1,49 @@ +package lxd + +import ( + "github.com/mitchellh/multistep" + "github.com/mitchellh/packer/packer" + "log" + "os" + "time" +) + +type stepPrepareOutputDir struct{} + +func (stepPrepareOutputDir) Run(state multistep.StateBag) multistep.StepAction { + config := state.Get("config").(*Config) + ui := state.Get("ui").(packer.Ui) + + if _, err := os.Stat(config.OutputDir); err == nil && config.PackerForce { + ui.Say("Deleting previous output directory...") + os.RemoveAll(config.OutputDir) + } + + if err := os.MkdirAll(config.OutputDir, 0755); err != nil { + state.Put("error", err) + return multistep.ActionHalt + } + + return multistep.ActionContinue +} + +func (stepPrepareOutputDir) Cleanup(state multistep.StateBag) { + _, cancelled := state.GetOk(multistep.StateCancelled) + _, halted := state.GetOk(multistep.StateHalted) + + if cancelled || halted { + config := state.Get("config").(*Config) + ui := state.Get("ui").(packer.Ui) + + ui.Say("Deleting output directory...") + for i := 0; i < 5; i++ { + err := os.RemoveAll(config.OutputDir) + if err == nil { + break + } + + log.Printf("Error removing output dir: %s", err) + time.Sleep(2 * time.Second) + } + } +} diff --git a/builder/lxd/step_provision.go b/builder/lxd/step_provision.go new file mode 100644 index 000000000..b6fd65c71 --- /dev/null +++ b/builder/lxd/step_provision.go @@ -0,0 +1,35 @@ +package lxd + +import ( + "github.com/mitchellh/multistep" + "github.com/mitchellh/packer/packer" + "log" +) + +// StepProvision provisions the instance within a chroot. +type StepProvision struct{} + +func (s *StepProvision) Run(state multistep.StateBag) multistep.StepAction { + hook := state.Get("hook").(packer.Hook) + config := state.Get("config").(*Config) + ui := state.Get("ui").(packer.Ui) + wrappedCommand := state.Get("wrappedCommand").(CommandWrapper) + + // Create our communicator + comm := &Communicator{ + ContainerName: config.ContainerName, + //RootFs: mountPath, + CmdWrapper: wrappedCommand, + } + + // Provision + log.Println("Running the provision hook") + if err := hook.Run(packer.HookProvision, ui, comm, nil); err != nil { + state.Put("error", err) + return multistep.ActionHalt + } + + return multistep.ActionContinue +} + +func (s *StepProvision) Cleanup(state multistep.StateBag) {} diff --git a/command/plugin.go b/command/plugin.go index e915ab521..af2fa2985 100644 --- a/command/plugin.go +++ b/command/plugin.go @@ -69,6 +69,8 @@ import ( shelllocalprovisioner "github.com/hashicorp/packer/provisioner/shell-local" windowsrestartprovisioner "github.com/hashicorp/packer/provisioner/windows-restart" windowsshellprovisioner "github.com/hashicorp/packer/provisioner/windows-shell" + + lxdbuilder "github.com/hashicorp/packer/builder/lxd" ) type PluginCommand struct { @@ -89,6 +91,7 @@ var Builders = map[string]packer.Builder{ "file": new(filebuilder.Builder), "googlecompute": new(googlecomputebuilder.Builder), "hyperv-iso": new(hypervisobuilder.Builder), + "lxd": new(lxdbuilder.Builder), "null": new(nullbuilder.Builder), "oneandone": new(oneandonebuilder.Builder), "openstack": new(openstackbuilder.Builder), From 821cca56a818a90be0aca91c291950a718a34d15 Mon Sep 17 00:00:00 2001 From: Chris Lundquist Date: Sun, 29 May 2016 01:24:46 +0000 Subject: [PATCH 02/15] [lxd] hack for UploadDir, improve Upload/Download --- builder/lxd/communicator.go | 48 ++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 27 deletions(-) diff --git a/builder/lxd/communicator.go b/builder/lxd/communicator.go index a69c4246c..fb8836c74 100644 --- a/builder/lxd/communicator.go +++ b/builder/lxd/communicator.go @@ -14,7 +14,6 @@ import ( ) type Communicator struct { - RootFs string ContainerName string CmdWrapper CommandWrapper } @@ -48,7 +47,7 @@ func (c *Communicator) Start(cmd *packer.RemoteCmd) error { } log.Printf( - "lxc-attach execution exited with '%d': '%s'", + "lxc exec execution exited with '%d': '%s'", exitStatus, cmd.Command) cmd.SetExited(exitStatus) }() @@ -57,51 +56,46 @@ func (c *Communicator) Start(cmd *packer.RemoteCmd) error { } func (c *Communicator) Upload(dst string, r io.Reader, fi *os.FileInfo) error { - dst = filepath.Join(c.RootFs, dst) - log.Printf("Uploading to rootfs: %s", dst) - tf, err := ioutil.TempFile("", "packer-lxc-attach") - if err != nil { - return fmt.Errorf("Error uploading file to rootfs: %s", err) - } - defer os.Remove(tf.Name()) - io.Copy(tf, r) - - cpCmd, err := c.CmdWrapper(fmt.Sprintf("sudo cp %s %s", tf.Name(), dst)) + cpCmd, err := c.CmdWrapper(fmt.Sprintf("lxc file push - %s", filepath.Join(c.ContainerName, dst))) if err != nil { return err } - log.Printf("Running copy command: %s", dst) + log.Printf("Running copy command: %s", cpCmd) + command := ShellCommand(cpCmd) + command.Stdin = r - return ShellCommand(cpCmd).Run() + return command.Run() } func (c *Communicator) UploadDir(dst string, src string, exclude []string) error { - // TODO: remove any file copied if it appears in `exclude` - dest := filepath.Join(c.RootFs, dst) + // XXX FIXME. lxc file push doesn't yet support directory uploads + + // TODO: make a tar tmpfile of the source, upload it to the dest, then untar it on the remote. + // This approach would work when the LXD host is not the localhost. + dest := filepath.Join("/var/lib/lxd/containers/", c.ContainerName, "rootfs", dst) log.Printf("Uploading directory '%s' to rootfs '%s'", src, dest) cpCmd, err := c.CmdWrapper(fmt.Sprintf("sudo cp -R %s/. %s", src, dest)) if err != nil { + log.Printf("Error when uploading directory '%s' to rootfs '%s': %s", src, dest, err) return err } + log.Printf("Running copy command: %s", cpCmd) return ShellCommand(cpCmd).Run() } func (c *Communicator) Download(src string, w io.Writer) error { - src = filepath.Join(c.RootFs, src) - log.Printf("Downloading from rootfs dir: %s", src) - f, err := os.Open(src) + cpCmd, err := c.CmdWrapper(fmt.Sprintf("lxc file pull %s -", filepath.Join(c.ContainerName, src))) if err != nil { return err } - defer f.Close() - if _, err := io.Copy(w, f); err != nil { - return err - } + log.Printf("Running copy command: %s", cpCmd) + command := ShellCommand(cpCmd) + command.Stdout = w - return nil + return command.Run() } func (c *Communicator) DownloadDir(src string, dst string, exclude []string) error { @@ -109,15 +103,15 @@ func (c *Communicator) DownloadDir(src string, dst string, exclude []string) err } func (c *Communicator) Execute(commandString string) (*exec.Cmd, error) { - log.Printf("Executing with lxc-attach in container: %s %s %s", c.ContainerName, c.RootFs, commandString) + log.Printf("Executing with lxc exec in container: %s %s", c.ContainerName, commandString) command, err := c.CmdWrapper( - fmt.Sprintf("sudo lxc-attach --name %s -- /bin/sh -c \"%s\"", c.ContainerName, commandString)) + fmt.Sprintf("sudo lxc exec %s -- /bin/sh -c \"%s\"", c.ContainerName, commandString)) if err != nil { return nil, err } localCmd := ShellCommand(command) - log.Printf("Executing lxc-attach: %s %#v", localCmd.Path, localCmd.Args) + log.Printf("Executing lxc exec: %s %#v", localCmd.Path, localCmd.Args) return localCmd, nil } From 3a0ef7b8b8f423ef12d9b58513f42aab08a45e45 Mon Sep 17 00:00:00 2001 From: Chris Lundquist Date: Mon, 30 May 2016 23:13:59 +0000 Subject: [PATCH 03/15] [lxd] it 'works' --- builder/lxd/artifact.go | 6 +-- builder/lxd/builder.go | 23 ++------- builder/lxd/builder_test.go | 3 +- builder/lxd/communicator.go | 43 ++++++++++++---- builder/lxd/communicator_test.go | 6 +++ builder/lxd/config.go | 22 ++++----- builder/lxd/step_lxd_launch.go | 4 +- builder/lxd/step_prepare_output_dir.go | 49 ------------------- builder/lxd/step_provision.go | 1 - .../lxd/{step_export.go => step_publish.go} | 23 +++------ 10 files changed, 69 insertions(+), 111 deletions(-) delete mode 100644 builder/lxd/step_prepare_output_dir.go rename builder/lxd/{step_export.go => step_publish.go} (65%) diff --git a/builder/lxd/artifact.go b/builder/lxd/artifact.go index 65acbba91..fe4742181 100644 --- a/builder/lxd/artifact.go +++ b/builder/lxd/artifact.go @@ -2,7 +2,6 @@ package lxd import ( "fmt" - "os" ) type Artifact struct { @@ -23,7 +22,7 @@ func (*Artifact) Id() string { } func (a *Artifact) String() string { - return fmt.Sprintf("Container files in directory: %s", a.dir) + return fmt.Sprintf("Container: %s", a.dir) } func (a *Artifact) State(name string) interface{} { @@ -31,5 +30,6 @@ func (a *Artifact) State(name string) interface{} { } func (a *Artifact) Destroy() error { - return os.RemoveAll(a.dir) + //return os.RemoveAll(a.dir) + return nil } diff --git a/builder/lxd/builder.go b/builder/lxd/builder.go index 1b28a9563..735ca48e8 100644 --- a/builder/lxd/builder.go +++ b/builder/lxd/builder.go @@ -7,8 +7,6 @@ import ( "github.com/mitchellh/packer/packer" "github.com/mitchellh/packer/template/interpolate" "log" - "os" - "path/filepath" "runtime" ) @@ -45,10 +43,9 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe } steps := []multistep.Step{ - new(stepPrepareOutputDir), new(stepLxdLaunch), new(StepProvision), - new(stepExport), + new(stepPublish), } // Setup the state bag @@ -85,23 +82,9 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe return nil, errors.New("Build was halted.") } - // Compile the artifact list - files := make([]string, 0, 5) - visit := func(path string, info os.FileInfo, err error) error { - if !info.IsDir() { - files = append(files, path) - } - - return err - } - - if err := filepath.Walk(b.config.OutputDir, visit); err != nil { - return nil, err - } - artifact := &Artifact{ - dir: b.config.OutputDir, - f: files, + // dir: b.config.OutputDir, + // f: files, } return artifact, nil diff --git a/builder/lxd/builder_test.go b/builder/lxd/builder_test.go index ac6c262ac..f08c18449 100644 --- a/builder/lxd/builder_test.go +++ b/builder/lxd/builder_test.go @@ -6,10 +6,11 @@ import ( "github.com/mitchellh/packer/packer" ) + func testConfig() map[string]interface{} { return map[string]interface{}{ "output_dir": "foo", - "image": "bar", + "image": "bar", } } diff --git a/builder/lxd/communicator.go b/builder/lxd/communicator.go index fb8836c74..a19f9d33a 100644 --- a/builder/lxd/communicator.go +++ b/builder/lxd/communicator.go @@ -69,20 +69,44 @@ func (c *Communicator) Upload(dst string, r io.Reader, fi *os.FileInfo) error { } func (c *Communicator) UploadDir(dst string, src string, exclude []string) error { - // XXX FIXME. lxc file push doesn't yet support directory uploads + // NOTE:lxc file push doesn't yet support directory uploads. + // As a work around, we tar up the folder, upload it as a file, then extract it - // TODO: make a tar tmpfile of the source, upload it to the dest, then untar it on the remote. - // This approach would work when the LXD host is not the localhost. - dest := filepath.Join("/var/lib/lxd/containers/", c.ContainerName, "rootfs", dst) - log.Printf("Uploading directory '%s' to rootfs '%s'", src, dest) - cpCmd, err := c.CmdWrapper(fmt.Sprintf("sudo cp -R %s/. %s", src, dest)) + os.Chdir(src) + tar, err := c.CmdWrapper("tar -czf - .") if err != nil { - log.Printf("Error when uploading directory '%s' to rootfs '%s': %s", src, dest, err) return err } - log.Printf("Running copy command: %s", cpCmd) - return ShellCommand(cpCmd).Run() + cp, err := c.CmdWrapper(fmt.Sprintf("lxc exec %s -- tar -xzf - -C %s ", c.ContainerName, dst)) + if err != nil { + return err + } + + tarCmd := ShellCommand(tar) + cpCmd := ShellCommand(cp) + + cpCmd.Stdin, _ = tarCmd.StdoutPipe() + log.Printf("Starting tar command: %s", tar) + err = tarCmd.Start() + if err != nil { + return err + } + + log.Printf("Running cp command: %s", cp) + err = cpCmd.Run() + if err != nil { + log.Printf("Error running cp command: %s", err) + return err + } + + err = tarCmd.Wait() + if err != nil { + log.Printf("Error running tar command: %s", err) + return err + } + + return nil } func (c *Communicator) Download(src string, w io.Writer) error { @@ -99,6 +123,7 @@ func (c *Communicator) Download(src string, w io.Writer) error { } func (c *Communicator) DownloadDir(src string, dst string, exclude []string) error { + // TODO This could probably be "lxc exec -- cd && tar -czf - | tar -xzf - -C " return fmt.Errorf("DownloadDir is not implemented for lxc") } diff --git a/builder/lxd/communicator_test.go b/builder/lxd/communicator_test.go index 9d6a26dc1..a4a122185 100644 --- a/builder/lxd/communicator_test.go +++ b/builder/lxd/communicator_test.go @@ -12,3 +12,9 @@ func TestCommunicator_ImplementsCommunicator(t *testing.T) { t.Fatalf("Communicator should be a communicator") } } + +// Acceptance tests +// TODO Execute a command +// TODO Upload a file +// TODO Download a file +// TODO Upload a Directory diff --git a/builder/lxd/config.go b/builder/lxd/config.go index 53c7deebe..bcd8dfe83 100644 --- a/builder/lxd/config.go +++ b/builder/lxd/config.go @@ -13,15 +13,15 @@ import ( type Config struct { common.PackerConfig `mapstructure:",squash"` ///ConfigFile string `mapstructure:"config_file"` - OutputDir string `mapstructure:"output_dir"` - ContainerName string `mapstructure:"container_name"` - CommandWrapper string `mapstructure:"command_wrapper"` - RawInitTimeout string `mapstructure:"init_timeout"` - Image string `mapstructure:"image"` - Remote string `mapstructure:"remote"` + OutputImage string `mapstructure:"output_image"` + ContainerName string `mapstructure:"container_name"` + CommandWrapper string `mapstructure:"command_wrapper"` + RawInitTimeout string `mapstructure:"init_timeout"` + Image string `mapstructure:"image"` + Remote string `mapstructure:"remote"` //EnvVars []string `mapstructure:"template_environment_vars"` //TargetRunlevel int `mapstructure:"target_runlevel"` - InitTimeout time.Duration + InitTimeout time.Duration ctx interpolate.Context } @@ -41,14 +41,14 @@ func NewConfig(raws ...interface{}) (*Config, error) { // Accumulate any errors var errs *packer.MultiError - if c.OutputDir == "" { - c.OutputDir = fmt.Sprintf("output-%s", c.PackerBuildName) - } - if c.ContainerName == "" { c.ContainerName = fmt.Sprintf("packer-%s", c.PackerBuildName) } + if c.OutputImage == "" { + c.OutputImage = c.ContainerName + } + if c.CommandWrapper == "" { c.CommandWrapper = "{{.Command}}" } diff --git a/builder/lxd/step_lxd_launch.go b/builder/lxd/step_lxd_launch.go index 52bcc9d50..c42218170 100644 --- a/builder/lxd/step_lxd_launch.go +++ b/builder/lxd/step_lxd_launch.go @@ -8,6 +8,7 @@ import ( "log" "os/exec" "strings" + "time" ) type stepLxdLaunch struct{} @@ -41,8 +42,7 @@ func (s *stepLxdLaunch) Run(state multistep.StateBag) multistep.StepAction { return multistep.ActionHalt } } - - //state.Put("mount_path", rootfs) + time.Sleep(2 * time.Second) return multistep.ActionContinue } diff --git a/builder/lxd/step_prepare_output_dir.go b/builder/lxd/step_prepare_output_dir.go deleted file mode 100644 index aa0565b4c..000000000 --- a/builder/lxd/step_prepare_output_dir.go +++ /dev/null @@ -1,49 +0,0 @@ -package lxd - -import ( - "github.com/mitchellh/multistep" - "github.com/mitchellh/packer/packer" - "log" - "os" - "time" -) - -type stepPrepareOutputDir struct{} - -func (stepPrepareOutputDir) Run(state multistep.StateBag) multistep.StepAction { - config := state.Get("config").(*Config) - ui := state.Get("ui").(packer.Ui) - - if _, err := os.Stat(config.OutputDir); err == nil && config.PackerForce { - ui.Say("Deleting previous output directory...") - os.RemoveAll(config.OutputDir) - } - - if err := os.MkdirAll(config.OutputDir, 0755); err != nil { - state.Put("error", err) - return multistep.ActionHalt - } - - return multistep.ActionContinue -} - -func (stepPrepareOutputDir) Cleanup(state multistep.StateBag) { - _, cancelled := state.GetOk(multistep.StateCancelled) - _, halted := state.GetOk(multistep.StateHalted) - - if cancelled || halted { - config := state.Get("config").(*Config) - ui := state.Get("ui").(packer.Ui) - - ui.Say("Deleting output directory...") - for i := 0; i < 5; i++ { - err := os.RemoveAll(config.OutputDir) - if err == nil { - break - } - - log.Printf("Error removing output dir: %s", err) - time.Sleep(2 * time.Second) - } - } -} diff --git a/builder/lxd/step_provision.go b/builder/lxd/step_provision.go index b6fd65c71..21356fb59 100644 --- a/builder/lxd/step_provision.go +++ b/builder/lxd/step_provision.go @@ -18,7 +18,6 @@ func (s *StepProvision) Run(state multistep.StateBag) multistep.StepAction { // Create our communicator comm := &Communicator{ ContainerName: config.ContainerName, - //RootFs: mountPath, CmdWrapper: wrappedCommand, } diff --git a/builder/lxd/step_export.go b/builder/lxd/step_publish.go similarity index 65% rename from builder/lxd/step_export.go rename to builder/lxd/step_publish.go index bf7e77e84..16224d3df 100644 --- a/builder/lxd/step_export.go +++ b/builder/lxd/step_publish.go @@ -7,13 +7,12 @@ import ( "github.com/mitchellh/packer/packer" "log" "os/exec" - "path/filepath" "strings" ) -type stepExport struct{} +type stepPublish struct{} -func (s *stepExport) Run(state multistep.StateBag) multistep.StepAction { +func (s *stepPublish) Run(state multistep.StateBag) multistep.StepAction { config := state.Get("config").(*Config) ui := state.Get("ui").(packer.Ui) @@ -21,21 +20,15 @@ func (s *stepExport) Run(state multistep.StateBag) multistep.StepAction { //outputPath := filepath.Join(config.OutputDir, "rootfs.tar.gz") - commands := make([][]string, 3) + commands := make([][]string, 2) commands[0] = []string{ - "lxc", "stop", name, + "lxc", "stop", "--force", name, } commands[1] = []string{ - "lxc", "export", - } - //commands[1] = []string{ - // "tar", "-C", containerDir, "--numeric-owner", "--anchored", "--exclude=./rootfs/dev/log", "-czf", outputPath, "./rootfs", - //} - commands[2] = []string{ - "sh", "-c", "chown $USER:`id -gn` " + filepath.Join(config.OutputDir, "*"), + "lxc", "publish", name, } - ui.Say("Exporting container...") + ui.Say("Publishing container...") for _, command := range commands { err := s.SudoCommand(command...) if err != nil { @@ -49,9 +42,9 @@ func (s *stepExport) Run(state multistep.StateBag) multistep.StepAction { return multistep.ActionContinue } -func (s *stepExport) Cleanup(state multistep.StateBag) {} +func (s *stepPublish) Cleanup(state multistep.StateBag) {} -func (s *stepExport) SudoCommand(args ...string) error { +func (s *stepPublish) SudoCommand(args ...string) error { var stdout, stderr bytes.Buffer log.Printf("Executing sudo command: %#v", args) From c62f9a0301fc32c046f21f5ffb67d02780db846f Mon Sep 17 00:00:00 2001 From: Chris Lundquist Date: Mon, 30 May 2016 23:57:26 +0000 Subject: [PATCH 04/15] [lxd] cleanup and tweaks --- builder/lxd/builder.go | 6 +++--- builder/lxd/builder_test.go | 4 ++-- builder/lxd/config.go | 2 -- builder/lxd/step_lxd_launch.go | 15 +++++---------- builder/lxd/step_publish.go | 13 ++++--------- 5 files changed, 14 insertions(+), 26 deletions(-) diff --git a/builder/lxd/builder.go b/builder/lxd/builder.go index 735ca48e8..d43f6d1e0 100644 --- a/builder/lxd/builder.go +++ b/builder/lxd/builder.go @@ -43,9 +43,9 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe } steps := []multistep.Step{ - new(stepLxdLaunch), - new(StepProvision), - new(stepPublish), + &stepLxdLaunch{}, + &StepProvision{}, + &stepPublish{}, } // Setup the state bag diff --git a/builder/lxd/builder_test.go b/builder/lxd/builder_test.go index f08c18449..b84dd70d6 100644 --- a/builder/lxd/builder_test.go +++ b/builder/lxd/builder_test.go @@ -9,8 +9,8 @@ import ( func testConfig() map[string]interface{} { return map[string]interface{}{ - "output_dir": "foo", - "image": "bar", + "output_image": "foo", + "image": "bar", } } diff --git a/builder/lxd/config.go b/builder/lxd/config.go index bcd8dfe83..c2201943e 100644 --- a/builder/lxd/config.go +++ b/builder/lxd/config.go @@ -18,9 +18,7 @@ type Config struct { CommandWrapper string `mapstructure:"command_wrapper"` RawInitTimeout string `mapstructure:"init_timeout"` Image string `mapstructure:"image"` - Remote string `mapstructure:"remote"` //EnvVars []string `mapstructure:"template_environment_vars"` - //TargetRunlevel int `mapstructure:"target_runlevel"` InitTimeout time.Duration ctx interpolate.Context diff --git a/builder/lxd/step_lxd_launch.go b/builder/lxd/step_lxd_launch.go index c42218170..44a52b0b9 100644 --- a/builder/lxd/step_lxd_launch.go +++ b/builder/lxd/step_lxd_launch.go @@ -19,17 +19,10 @@ func (s *stepLxdLaunch) Run(state multistep.StateBag) multistep.StepAction { name := config.ContainerName image := config.Image - remote := config.Remote - commands := make([][]string, 1) - if remote == "" { - commands[0] = []string{"lxc", "launch", image, name} - } else { - - commands[0] = []string{"lxc", "launch", fmt.Sprintf("%s:%s", remote, image), name} + commands := [][]string{ + {"lxc", "launch", image, name}, } - //commands[0] = append(commands[0], config.Parameters...) - // todo: wait for init to finish before moving on to provisioning instead of this ui.Say("Creating container...") for _, command := range commands { @@ -42,7 +35,9 @@ func (s *stepLxdLaunch) Run(state multistep.StateBag) multistep.StepAction { return multistep.ActionHalt } } - time.Sleep(2 * time.Second) + // TODO: Should we check `lxc info ` for "Running"? + // We have to do this so /tmp doens't get cleared and lose our provisioner scripts. + time.Sleep(1 * time.Second) return multistep.ActionContinue } diff --git a/builder/lxd/step_publish.go b/builder/lxd/step_publish.go index 16224d3df..ccebcecdb 100644 --- a/builder/lxd/step_publish.go +++ b/builder/lxd/step_publish.go @@ -18,21 +18,16 @@ func (s *stepPublish) Run(state multistep.StateBag) multistep.StepAction { name := config.ContainerName - //outputPath := filepath.Join(config.OutputDir, "rootfs.tar.gz") - - commands := make([][]string, 2) - commands[0] = []string{ - "lxc", "stop", "--force", name, - } - commands[1] = []string{ - "lxc", "publish", name, + commands := [][]string{ + {"lxc", "stop", "--force", name}, + {"lxc", "publish", name, "--alias", config.OutputImage}, } ui.Say("Publishing container...") for _, command := range commands { err := s.SudoCommand(command...) if err != nil { - err := fmt.Errorf("Error exporting container: %s", err) + err := fmt.Errorf("Error publishing container: %s", err) state.Put("error", err) ui.Error(err.Error()) return multistep.ActionHalt From 8326d7b6ac6577350efa1544da96840830a1a700 Mon Sep 17 00:00:00 2001 From: Chris Lundquist Date: Tue, 31 May 2016 01:13:09 +0000 Subject: [PATCH 05/15] [lxd] fixup some publish stuff --- builder/lxd/artifact.go | 12 +++++----- builder/lxd/builder.go | 3 +-- builder/lxd/step_publish.go | 44 +++++++++++++++++-------------------- 3 files changed, 27 insertions(+), 32 deletions(-) diff --git a/builder/lxd/artifact.go b/builder/lxd/artifact.go index fe4742181..e91fa4070 100644 --- a/builder/lxd/artifact.go +++ b/builder/lxd/artifact.go @@ -5,8 +5,7 @@ import ( ) type Artifact struct { - dir string - f []string + id string } func (*Artifact) BuilderId() string { @@ -14,15 +13,15 @@ func (*Artifact) BuilderId() string { } func (a *Artifact) Files() []string { - return a.f + return nil } -func (*Artifact) Id() string { - return "Container" +func (a *Artifact) Id() string { + return a.id } func (a *Artifact) String() string { - return fmt.Sprintf("Container: %s", a.dir) + return fmt.Sprintf("image: %s", a.id) } func (a *Artifact) State(name string) interface{} { @@ -31,5 +30,6 @@ func (a *Artifact) State(name string) interface{} { func (a *Artifact) Destroy() error { //return os.RemoveAll(a.dir) + //TODO return nil } diff --git a/builder/lxd/builder.go b/builder/lxd/builder.go index d43f6d1e0..9447f777f 100644 --- a/builder/lxd/builder.go +++ b/builder/lxd/builder.go @@ -83,8 +83,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe } artifact := &Artifact{ - // dir: b.config.OutputDir, - // f: files, + id: state.Get("imageFingerprint").(string), } return artifact, nil diff --git a/builder/lxd/step_publish.go b/builder/lxd/step_publish.go index ccebcecdb..fa2d19914 100644 --- a/builder/lxd/step_publish.go +++ b/builder/lxd/step_publish.go @@ -7,47 +7,28 @@ import ( "github.com/mitchellh/packer/packer" "log" "os/exec" + "regexp" "strings" ) type stepPublish struct{} func (s *stepPublish) Run(state multistep.StateBag) multistep.StepAction { + var stdout, stderr bytes.Buffer config := state.Get("config").(*Config) ui := state.Get("ui").(packer.Ui) name := config.ContainerName - commands := [][]string{ - {"lxc", "stop", "--force", name}, - {"lxc", "publish", name, "--alias", config.OutputImage}, + args := []string{ + "lxc", "publish", "--force", name, "--alias", config.OutputImage, } ui.Say("Publishing container...") - for _, command := range commands { - err := s.SudoCommand(command...) - if err != nil { - err := fmt.Errorf("Error publishing container: %s", err) - state.Put("error", err) - ui.Error(err.Error()) - return multistep.ActionHalt - } - } - - return multistep.ActionContinue -} - -func (s *stepPublish) Cleanup(state multistep.StateBag) {} - -func (s *stepPublish) SudoCommand(args ...string) error { - var stdout, stderr bytes.Buffer - - log.Printf("Executing sudo command: %#v", args) cmd := exec.Command("sudo", args...) cmd.Stdout = &stdout cmd.Stderr = &stderr err := cmd.Run() - stdoutString := strings.TrimSpace(stdout.String()) stderrString := strings.TrimSpace(stderr.String()) @@ -58,5 +39,20 @@ func (s *stepPublish) SudoCommand(args ...string) error { log.Printf("stdout: %s", stdoutString) log.Printf("stderr: %s", stderrString) - return err + if err != nil { + state.Put("error", err) + ui.Error(err.Error()) + return multistep.ActionHalt + } + + r := regexp.MustCompile("([0-9a-fA-F]+)$") + fingerprint := r.FindAllStringSubmatch(stdoutString, -1)[0][0] + + ui.Say(fmt.Sprintf("Created image: %s", fingerprint)) + + state.Put("imageFingerprint", fingerprint) + + return multistep.ActionContinue } + +func (s *stepPublish) Cleanup(state multistep.StateBag) {} From 607da30547afe3f009a0ae7480c133c4a1eea76c Mon Sep 17 00:00:00 2001 From: Chris Lundquist Date: Tue, 31 May 2016 01:43:40 +0000 Subject: [PATCH 06/15] [lxd] remove CheckInit --- builder/lxd/communicator.go | 30 ------------------------------ 1 file changed, 30 deletions(-) diff --git a/builder/lxd/communicator.go b/builder/lxd/communicator.go index a19f9d33a..16f4d35eb 100644 --- a/builder/lxd/communicator.go +++ b/builder/lxd/communicator.go @@ -4,12 +4,10 @@ import ( "fmt" "github.com/mitchellh/packer/packer" "io" - "io/ioutil" "log" "os" "os/exec" "path/filepath" - "strings" "syscall" ) @@ -140,31 +138,3 @@ func (c *Communicator) Execute(commandString string) (*exec.Cmd, error) { return localCmd, nil } - -func (c *Communicator) CheckInit() (string, error) { - log.Printf("Debug runlevel exec") - localCmd, err := c.Execute("/sbin/runlevel") - - if err != nil { - return "", err - } - - pr, _ := localCmd.StdoutPipe() - if err = localCmd.Start(); err != nil { - return "", err - } - - output, err := ioutil.ReadAll(pr) - - if err != nil { - return "", err - } - - err = localCmd.Wait() - - if err != nil { - return "", err - } - - return strings.TrimSpace(string(output)), nil -} From c79e8ddc8f51362d375afc995fa566bbf5723c52 Mon Sep 17 00:00:00 2001 From: Chris Lundquist Date: Tue, 31 May 2016 23:53:50 +0000 Subject: [PATCH 07/15] [lxd] more cleanup --- builder/lxd/builder_test.go | 22 ++++++++++++++++++++++ builder/lxd/config.go | 24 ++++++------------------ builder/lxd/step_publish.go | 2 ++ 3 files changed, 30 insertions(+), 18 deletions(-) diff --git a/builder/lxd/builder_test.go b/builder/lxd/builder_test.go index b84dd70d6..ad5a6c95a 100644 --- a/builder/lxd/builder_test.go +++ b/builder/lxd/builder_test.go @@ -32,6 +32,28 @@ func TestBuilderPrepare_ConfigFile(t *testing.T) { t.Fatalf("should not have error: %s", err) } + // Good, remote image + config = testConfig() + config["image"] = "remote:bar" + warnings, err = b.Prepare(config) + if len(warnings) > 0 { + t.Fatalf("bad: %#v", warnings) + } + if err != nil { + t.Fatalf("should not have error: %s", err) + } + + // Good, remote output image + config = testConfig() + config["output_image"] = "remote:foo" + warnings, err = b.Prepare(config) + if len(warnings) > 0 { + t.Fatalf("bad: %#v", warnings) + } + if err != nil { + t.Fatalf("should not have error: %s", err) + } + // Bad, missing image name config = testConfig() delete(config, "image") diff --git a/builder/lxd/config.go b/builder/lxd/config.go index c2201943e..ff75deed2 100644 --- a/builder/lxd/config.go +++ b/builder/lxd/config.go @@ -12,14 +12,11 @@ import ( type Config struct { common.PackerConfig `mapstructure:",squash"` - ///ConfigFile string `mapstructure:"config_file"` - OutputImage string `mapstructure:"output_image"` - ContainerName string `mapstructure:"container_name"` - CommandWrapper string `mapstructure:"command_wrapper"` - RawInitTimeout string `mapstructure:"init_timeout"` - Image string `mapstructure:"image"` - //EnvVars []string `mapstructure:"template_environment_vars"` - InitTimeout time.Duration + OutputImage string `mapstructure:"output_image"` + ContainerName string `mapstructure:"container_name"` + CommandWrapper string `mapstructure:"command_wrapper"` + Image string `mapstructure:"image"` + InitTimeout time.Duration ctx interpolate.Context } @@ -51,17 +48,8 @@ func NewConfig(raws ...interface{}) (*Config, error) { c.CommandWrapper = "{{.Command}}" } - if c.RawInitTimeout == "" { - c.RawInitTimeout = "20s" - } - if c.Image == "" { - errs = packer.MultiErrorAppend(errs, fmt.Errorf("`image` is a required parameter for LXD.")) - } - - c.InitTimeout, err = time.ParseDuration(c.RawInitTimeout) - if err != nil { - errs = packer.MultiErrorAppend(errs, fmt.Errorf("Failed parsing init_timeout: %s", err)) + errs = packer.MultiErrorAppend(errs, fmt.Errorf("`image` is a required parameter for LXD. Please specify an image by alias or fingerprint. e.g. `ubuntu-daily:x`")) } if errs != nil && len(errs.Errors) > 0 { diff --git a/builder/lxd/step_publish.go b/builder/lxd/step_publish.go index fa2d19914..e519a7252 100644 --- a/builder/lxd/step_publish.go +++ b/builder/lxd/step_publish.go @@ -21,6 +21,8 @@ func (s *stepPublish) Run(state multistep.StateBag) multistep.StepAction { name := config.ContainerName args := []string{ + // If we use `lxc stop `, an ephemeral container would die forever. + // `lxc publish` has special logic to handle this case. "lxc", "publish", "--force", name, "--alias", config.OutputImage, } From 05f072929f37ff63b947d3603113d61480ca39ed Mon Sep 17 00:00:00 2001 From: Chris Lundquist Date: Fri, 1 Jul 2016 06:45:48 +0000 Subject: [PATCH 08/15] [lxd] first pass at docs --- website/source/docs/builders/lxd.html.md | 50 ++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 website/source/docs/builders/lxd.html.md diff --git a/website/source/docs/builders/lxd.html.md b/website/source/docs/builders/lxd.html.md new file mode 100644 index 000000000..716c8bf20 --- /dev/null +++ b/website/source/docs/builders/lxd.html.md @@ -0,0 +1,50 @@ +--- +description: | + The `lxd` Packer builder builds containers for LXD. The builder starts an LXD + container, runs provisioners within this container, then saves the container + as an LXD image. +layout: docs +page_title: LXD Builder +... + +# LXD Builder + +Type: `lxd` + +The `lxd` Packer builder builds containers for LXD. The builder starts an LXD +container, runs provisioners within this container, then saves the container +as an LXD image. + +The LXD builder requires a modern linux kernel and the `lxd` package. +This builder does not work with LXC. + +## Basic Example + +Below is a fully functioning example. + +``` {.javascript} +{ + "builders": [ + { + "type": "lxd", + "name": "lxd-xenial", + "image": "ubuntu-daily:xenial", + "output_image": "ubuntu-xenial" + } + ] +} +``` + +## Configuration Reference + +### Required: + +- `image` (string) - The source image to use when creating the build container. This can be a (local or remote) image (name or fingerprint). E.G. my-base-image, ubuntu-daily:x, 08fababf6f27... + Note: The builder may appear to pause if required to download a remote image, as they are usually 100-200MB. `/var/log/lxd/lxd.log` will mention starting such downloads. + +### Optional: + +- `name` (string) - The name of the started container. Defaults to `packer-$PACKER_BUILD_NAME`. + +- `output_image` (string) - The name of the output artifact. Defaults to `name` + From e29f06fe1c3639254cd55f49c072fe8d8cb3d372 Mon Sep 17 00:00:00 2001 From: Chris Lundquist Date: Sun, 7 Aug 2016 03:25:57 +0000 Subject: [PATCH 09/15] [lxd] refactor commands to dry things up --- builder/lxd/command.go | 28 +++++++++++++++++++ builder/lxd/communicator.go | 2 +- builder/lxd/step_lxd_launch.go | 51 ++++++++-------------------------- builder/lxd/step_provision.go | 2 +- builder/lxd/step_publish.go | 23 ++------------- 5 files changed, 44 insertions(+), 62 deletions(-) diff --git a/builder/lxd/command.go b/builder/lxd/command.go index a432e0bba..c9a39279d 100644 --- a/builder/lxd/command.go +++ b/builder/lxd/command.go @@ -1,7 +1,11 @@ package lxd import ( + "bytes" + "fmt" + "log" "os/exec" + "strings" ) // CommandWrapper is a type that given a command, will possibly modify that @@ -13,3 +17,27 @@ type CommandWrapper func(string) (string, error) func ShellCommand(command string) *exec.Cmd { return exec.Command("/bin/sh", "-c", command) } + +// Yeah...LXD calls `lxc` because the command line is different between the +// packages. This should also avoid a naming collision between the LXC builder. +func LXDCommand(args ...string) (string, error) { + var stdout, stderr bytes.Buffer + + log.Printf("Executing lxc command: %#v", args) + cmd := exec.Command("lxc", args...) + cmd.Stdout = &stdout + cmd.Stderr = &stderr + err := cmd.Run() + + stdoutString := strings.TrimSpace(stdout.String()) + stderrString := strings.TrimSpace(stderr.String()) + + if _, ok := err.(*exec.ExitError); ok { + err = fmt.Errorf("LXD command error: %s", stderrString) + } + + log.Printf("stdout: %s", stdoutString) + log.Printf("stderr: %s", stderrString) + + return stdoutString, err +} diff --git a/builder/lxd/communicator.go b/builder/lxd/communicator.go index 16f4d35eb..3df1cf8bc 100644 --- a/builder/lxd/communicator.go +++ b/builder/lxd/communicator.go @@ -128,7 +128,7 @@ func (c *Communicator) DownloadDir(src string, dst string, exclude []string) err func (c *Communicator) Execute(commandString string) (*exec.Cmd, error) { log.Printf("Executing with lxc exec in container: %s %s", c.ContainerName, commandString) command, err := c.CmdWrapper( - fmt.Sprintf("sudo lxc exec %s -- /bin/sh -c \"%s\"", c.ContainerName, commandString)) + fmt.Sprintf("lxc exec %s -- /bin/sh -c \"%s\"", c.ContainerName, commandString)) if err != nil { return nil, err } diff --git a/builder/lxd/step_lxd_launch.go b/builder/lxd/step_lxd_launch.go index 44a52b0b9..0cc0b80ac 100644 --- a/builder/lxd/step_lxd_launch.go +++ b/builder/lxd/step_lxd_launch.go @@ -1,13 +1,9 @@ package lxd import ( - "bytes" "fmt" "github.com/mitchellh/multistep" "github.com/mitchellh/packer/packer" - "log" - "os/exec" - "strings" "time" ) @@ -20,20 +16,17 @@ func (s *stepLxdLaunch) Run(state multistep.StateBag) multistep.StepAction { name := config.ContainerName image := config.Image - commands := [][]string{ - {"lxc", "launch", image, name}, + args := []string{ + "launch", image, name, } ui.Say("Creating container...") - for _, command := range commands { - log.Printf("Executing sudo command: %#v", command) - err := s.SudoCommand(command...) - if err != nil { - err := fmt.Errorf("Error creating container: %s", err) - state.Put("error", err) - ui.Error(err.Error()) - return multistep.ActionHalt - } + _, err := LXDCommand(args...) + if err != nil { + err := fmt.Errorf("Error creating container: %s", err) + state.Put("error", err) + ui.Error(err.Error()) + return multistep.ActionHalt } // TODO: Should we check `lxc info ` for "Running"? // We have to do this so /tmp doens't get cleared and lose our provisioner scripts. @@ -46,34 +39,12 @@ func (s *stepLxdLaunch) Cleanup(state multistep.StateBag) { config := state.Get("config").(*Config) ui := state.Get("ui").(packer.Ui) - command := []string{ - "lxc", "delete", "--force", config.ContainerName, + args := []string{ + "delete", "--force", config.ContainerName, } ui.Say("Unregistering and deleting deleting container...") - if err := s.SudoCommand(command...); err != nil { + if _, err := LXDCommand(args...); err != nil { ui.Error(fmt.Sprintf("Error deleting container: %s", err)) } } - -func (s *stepLxdLaunch) SudoCommand(args ...string) error { - var stdout, stderr bytes.Buffer - - log.Printf("Executing sudo command: %#v", args) - cmd := exec.Command("sudo", args...) - cmd.Stdout = &stdout - cmd.Stderr = &stderr - err := cmd.Run() - - stdoutString := strings.TrimSpace(stdout.String()) - stderrString := strings.TrimSpace(stderr.String()) - - if _, ok := err.(*exec.ExitError); ok { - err = fmt.Errorf("Sudo command error: %s", stderrString) - } - - log.Printf("stdout: %s", stdoutString) - log.Printf("stderr: %s", stderrString) - - return err -} diff --git a/builder/lxd/step_provision.go b/builder/lxd/step_provision.go index 21356fb59..584d61659 100644 --- a/builder/lxd/step_provision.go +++ b/builder/lxd/step_provision.go @@ -6,7 +6,7 @@ import ( "log" ) -// StepProvision provisions the instance within a chroot. +// StepProvision provisions the container type StepProvision struct{} func (s *StepProvision) Run(state multistep.StateBag) multistep.StepAction { diff --git a/builder/lxd/step_publish.go b/builder/lxd/step_publish.go index e519a7252..cf3c98bb3 100644 --- a/builder/lxd/step_publish.go +++ b/builder/lxd/step_publish.go @@ -1,20 +1,15 @@ package lxd import ( - "bytes" "fmt" "github.com/mitchellh/multistep" "github.com/mitchellh/packer/packer" - "log" - "os/exec" "regexp" - "strings" ) type stepPublish struct{} func (s *stepPublish) Run(state multistep.StateBag) multistep.StepAction { - var stdout, stderr bytes.Buffer config := state.Get("config").(*Config) ui := state.Get("ui").(packer.Ui) @@ -23,25 +18,13 @@ func (s *stepPublish) Run(state multistep.StateBag) multistep.StepAction { args := []string{ // If we use `lxc stop `, an ephemeral container would die forever. // `lxc publish` has special logic to handle this case. - "lxc", "publish", "--force", name, "--alias", config.OutputImage, + "publish", "--force", name, "--alias", config.OutputImage, } ui.Say("Publishing container...") - cmd := exec.Command("sudo", args...) - cmd.Stdout = &stdout - cmd.Stderr = &stderr - err := cmd.Run() - stdoutString := strings.TrimSpace(stdout.String()) - stderrString := strings.TrimSpace(stderr.String()) - - if _, ok := err.(*exec.ExitError); ok { - err = fmt.Errorf("Sudo command error: %s", stderrString) - } - - log.Printf("stdout: %s", stdoutString) - log.Printf("stderr: %s", stderrString) - + stdoutString, err := LXDCommand(args...) if err != nil { + err := fmt.Errorf("Error publishing container: %s", err) state.Put("error", err) ui.Error(err.Error()) return multistep.ActionHalt From 68bb72380aae3a72afcbc2900f4fd177ee56e59e Mon Sep 17 00:00:00 2001 From: Chris Lundquist Date: Sun, 7 Aug 2016 03:38:37 +0000 Subject: [PATCH 10/15] [lxd] avoid extra container start/stop and race Before we couldn't be sure if we were a permanent container or not. Now we explicitly pass this on the command line so we don't depend on the extra logic in `lxc publish --force` for ephemeral handling. This means we avoid restarting the container after we publish since we tear it down right away anyhow. Likewise, there was sometimes a race which prevented the deletion while the container was in a boot stage. --- builder/lxd/step_lxd_launch.go | 2 +- builder/lxd/step_publish.go | 21 ++++++++++++++++----- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/builder/lxd/step_lxd_launch.go b/builder/lxd/step_lxd_launch.go index 0cc0b80ac..cd51c34f2 100644 --- a/builder/lxd/step_lxd_launch.go +++ b/builder/lxd/step_lxd_launch.go @@ -17,7 +17,7 @@ func (s *stepLxdLaunch) Run(state multistep.StateBag) multistep.StepAction { image := config.Image args := []string{ - "launch", image, name, + "launch", "--ephemeral=false", image, name, } ui.Say("Creating container...") diff --git a/builder/lxd/step_publish.go b/builder/lxd/step_publish.go index cf3c98bb3..ecc547d92 100644 --- a/builder/lxd/step_publish.go +++ b/builder/lxd/step_publish.go @@ -14,15 +14,26 @@ func (s *stepPublish) Run(state multistep.StateBag) multistep.StepAction { ui := state.Get("ui").(packer.Ui) name := config.ContainerName + stop_args := []string{ + // We created the container with "--ephemeral=false" so we know it is safe to stop. + "stop", name, + } - args := []string{ - // If we use `lxc stop `, an ephemeral container would die forever. - // `lxc publish` has special logic to handle this case. - "publish", "--force", name, "--alias", config.OutputImage, + ui.Say("Stopping container...") + _, err := LXDCommand(stop_args...) + if err != nil { + err := fmt.Errorf("Error stopping container: %s", err) + state.Put("error", err) + ui.Error(err.Error()) + return multistep.ActionHalt + } + + publish_args := []string{ + "publish", name, "--alias", config.OutputImage, } ui.Say("Publishing container...") - stdoutString, err := LXDCommand(args...) + stdoutString, err := LXDCommand(publish_args...) if err != nil { err := fmt.Errorf("Error publishing container: %s", err) state.Put("error", err) From acae8fc2f9740f2dc100195f8b5f3d8d859e09cb Mon Sep 17 00:00:00 2001 From: Chris Lundquist Date: Sun, 7 Aug 2016 03:47:52 +0000 Subject: [PATCH 11/15] [lxd] implement artifact detroy --- builder/lxd/artifact.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/builder/lxd/artifact.go b/builder/lxd/artifact.go index e91fa4070..481f0758e 100644 --- a/builder/lxd/artifact.go +++ b/builder/lxd/artifact.go @@ -29,7 +29,6 @@ func (a *Artifact) State(name string) interface{} { } func (a *Artifact) Destroy() error { - //return os.RemoveAll(a.dir) - //TODO - return nil + _, err := LXDCommand("image", "delete", a.id) + return err } From 29bbe10c1ce2ec6e92b703634c020965b9e9d888 Mon Sep 17 00:00:00 2001 From: Chris Lundquist Date: Fri, 27 Jan 2017 21:06:18 +0000 Subject: [PATCH 12/15] [lxd] rework local tar command to avoid chdir --- builder/lxd/communicator.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/builder/lxd/communicator.go b/builder/lxd/communicator.go index 3df1cf8bc..abb1f16a1 100644 --- a/builder/lxd/communicator.go +++ b/builder/lxd/communicator.go @@ -70,13 +70,15 @@ func (c *Communicator) UploadDir(dst string, src string, exclude []string) error // NOTE:lxc file push doesn't yet support directory uploads. // As a work around, we tar up the folder, upload it as a file, then extract it - os.Chdir(src) - tar, err := c.CmdWrapper("tar -czf - .") + // Don't use 'z' flag as compressing may take longer and the transfer is likely local. + // If this isn't the case, it is possible for the user to crompress in another step then transfer. + // It wouldn't be possibe to disable compression, without exposing this option. + tar, err := c.CmdWrapper(fmt.Sprintf("tar -cf - -C %s .", src)) if err != nil { return err } - cp, err := c.CmdWrapper(fmt.Sprintf("lxc exec %s -- tar -xzf - -C %s ", c.ContainerName, dst)) + cp, err := c.CmdWrapper(fmt.Sprintf("lxc exec %s -- tar -xf - -C %s", c.ContainerName, dst)) if err != nil { return err } From 3b09a324c472c8c48baa04146e7a210ac10f118b Mon Sep 17 00:00:00 2001 From: Chris Lundquist Date: Tue, 7 Mar 2017 20:06:01 +0000 Subject: [PATCH 13/15] [lxd] add link to lxd docs from layouts --- website/source/layouts/docs.erb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/website/source/layouts/docs.erb b/website/source/layouts/docs.erb index 8cd3159f8..880f1ede9 100644 --- a/website/source/layouts/docs.erb +++ b/website/source/layouts/docs.erb @@ -190,8 +190,11 @@ > File + > + LXD + > - PowerShell + webPowerShell > Puppet Masterless From f6bc158a8038cb966e57abff8b47608b52868c31 Mon Sep 17 00:00:00 2001 From: Chris Lundquist Date: Mon, 4 Sep 2017 17:57:55 +0000 Subject: [PATCH 14/15] [lxd] minor fixups for new styles and docs --- builder/lxd/builder.go | 30 ++++-------------------- builder/lxd/communicator.go | 2 +- website/source/docs/builders/lxd.html.md | 4 +++- 3 files changed, 8 insertions(+), 28 deletions(-) diff --git a/builder/lxd/builder.go b/builder/lxd/builder.go index 9447f777f..bd3877c15 100644 --- a/builder/lxd/builder.go +++ b/builder/lxd/builder.go @@ -2,12 +2,11 @@ package lxd import ( "errors" + "github.com/hashicorp/packer/common" + "github.com/hashicorp/packer/packer" + "github.com/hashicorp/packer/template/interpolate" "github.com/mitchellh/multistep" - "github.com/mitchellh/packer/common" - "github.com/mitchellh/packer/packer" - "github.com/mitchellh/packer/template/interpolate" "log" - "runtime" ) // The unique ID for this builder @@ -33,10 +32,6 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { } func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) { - if runtime.GOOS != "linux" { - return nil, errors.New("The lxc builder only works on linux environments.") - } - wrappedCommand := func(command string) (string, error) { b.config.ctx.Data = &wrappedCommandTemplate{Command: command} return interpolate.Render(b.config.CommandWrapper, &b.config.ctx) @@ -57,15 +52,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe state.Put("wrappedCommand", CommandWrapper(wrappedCommand)) // Run - if b.config.PackerDebug { - b.runner = &multistep.DebugRunner{ - Steps: steps, - PauseFn: common.MultistepDebugFn(ui), - } - } else { - b.runner = &multistep.BasicRunner{Steps: steps} - } - + b.runner = common.NewRunnerWithPauseFn(steps, b.config.PackerConfig, ui, state) b.runner.Run(state) // If there was an error, return that @@ -73,15 +60,6 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe return nil, rawErr.(error) } - // If we were interrupted or cancelled, then just exit. - if _, ok := state.GetOk(multistep.StateCancelled); ok { - return nil, errors.New("Build was cancelled.") - } - - if _, ok := state.GetOk(multistep.StateHalted); ok { - return nil, errors.New("Build was halted.") - } - artifact := &Artifact{ id: state.Get("imageFingerprint").(string), } diff --git a/builder/lxd/communicator.go b/builder/lxd/communicator.go index abb1f16a1..4a8970668 100644 --- a/builder/lxd/communicator.go +++ b/builder/lxd/communicator.go @@ -71,7 +71,7 @@ func (c *Communicator) UploadDir(dst string, src string, exclude []string) error // As a work around, we tar up the folder, upload it as a file, then extract it // Don't use 'z' flag as compressing may take longer and the transfer is likely local. - // If this isn't the case, it is possible for the user to crompress in another step then transfer. + // If this isn't the case, it is possible for the user to compress in another step then transfer. // It wouldn't be possibe to disable compression, without exposing this option. tar, err := c.CmdWrapper(fmt.Sprintf("tar -cf - -C %s .", src)) if err != nil { diff --git a/website/source/docs/builders/lxd.html.md b/website/source/docs/builders/lxd.html.md index 716c8bf20..dd386b1b0 100644 --- a/website/source/docs/builders/lxd.html.md +++ b/website/source/docs/builders/lxd.html.md @@ -46,5 +46,7 @@ Below is a fully functioning example. - `name` (string) - The name of the started container. Defaults to `packer-$PACKER_BUILD_NAME`. -- `output_image` (string) - The name of the output artifact. Defaults to `name` +- `output_image` (string) - The name of the output artifact. Defaults to `name`. + +- `command_wrapper` (string) - lets you prefix all builder commands, such as with `ssh` for a remote build host. Defaults to `""`. From 7f0d253dbffddd712205e615f21080b577e9723e Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Tue, 5 Sep 2017 14:09:15 -0700 Subject: [PATCH 15/15] some last minute cleanup --- builder/lxd/builder.go | 1 - builder/lxd/builder_test.go | 2 +- builder/lxd/communicator.go | 2 +- builder/lxd/communicator_test.go | 2 +- builder/lxd/config.go | 8 ++++---- builder/lxd/step_lxd_launch.go | 2 +- builder/lxd/step_provision.go | 2 +- builder/lxd/step_publish.go | 2 +- 8 files changed, 10 insertions(+), 11 deletions(-) diff --git a/builder/lxd/builder.go b/builder/lxd/builder.go index bd3877c15..d59cf5bcf 100644 --- a/builder/lxd/builder.go +++ b/builder/lxd/builder.go @@ -1,7 +1,6 @@ package lxd import ( - "errors" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/packer" "github.com/hashicorp/packer/template/interpolate" diff --git a/builder/lxd/builder_test.go b/builder/lxd/builder_test.go index ad5a6c95a..b2ea70e55 100644 --- a/builder/lxd/builder_test.go +++ b/builder/lxd/builder_test.go @@ -4,7 +4,7 @@ import ( "os" "testing" - "github.com/mitchellh/packer/packer" + "github.com/hashicorp/packer/packer" ) func testConfig() map[string]interface{} { diff --git a/builder/lxd/communicator.go b/builder/lxd/communicator.go index 4a8970668..8eaa47a5f 100644 --- a/builder/lxd/communicator.go +++ b/builder/lxd/communicator.go @@ -2,7 +2,7 @@ package lxd import ( "fmt" - "github.com/mitchellh/packer/packer" + "github.com/hashicorp/packer/packer" "io" "log" "os" diff --git a/builder/lxd/communicator_test.go b/builder/lxd/communicator_test.go index a4a122185..4a70160b7 100644 --- a/builder/lxd/communicator_test.go +++ b/builder/lxd/communicator_test.go @@ -1,7 +1,7 @@ package lxd import ( - "github.com/mitchellh/packer/packer" + "github.com/hashicorp/packer/packer" "testing" ) diff --git a/builder/lxd/config.go b/builder/lxd/config.go index ff75deed2..73de7212e 100644 --- a/builder/lxd/config.go +++ b/builder/lxd/config.go @@ -2,11 +2,11 @@ package lxd import ( "fmt" + "github.com/hashicorp/packer/common" + "github.com/hashicorp/packer/helper/config" + "github.com/hashicorp/packer/packer" + "github.com/hashicorp/packer/template/interpolate" "github.com/mitchellh/mapstructure" - "github.com/mitchellh/packer/common" - "github.com/mitchellh/packer/helper/config" - "github.com/mitchellh/packer/packer" - "github.com/mitchellh/packer/template/interpolate" "time" ) diff --git a/builder/lxd/step_lxd_launch.go b/builder/lxd/step_lxd_launch.go index cd51c34f2..1ec573b18 100644 --- a/builder/lxd/step_lxd_launch.go +++ b/builder/lxd/step_lxd_launch.go @@ -2,8 +2,8 @@ package lxd import ( "fmt" + "github.com/hashicorp/packer/packer" "github.com/mitchellh/multistep" - "github.com/mitchellh/packer/packer" "time" ) diff --git a/builder/lxd/step_provision.go b/builder/lxd/step_provision.go index 584d61659..c46b900e7 100644 --- a/builder/lxd/step_provision.go +++ b/builder/lxd/step_provision.go @@ -1,8 +1,8 @@ package lxd import ( + "github.com/hashicorp/packer/packer" "github.com/mitchellh/multistep" - "github.com/mitchellh/packer/packer" "log" ) diff --git a/builder/lxd/step_publish.go b/builder/lxd/step_publish.go index ecc547d92..0c0aabd3f 100644 --- a/builder/lxd/step_publish.go +++ b/builder/lxd/step_publish.go @@ -2,8 +2,8 @@ package lxd import ( "fmt" + "github.com/hashicorp/packer/packer" "github.com/mitchellh/multistep" - "github.com/mitchellh/packer/packer" "regexp" )