From 3a0ef7b8b8f423ef12d9b58513f42aab08a45e45 Mon Sep 17 00:00:00 2001 From: Chris Lundquist Date: Mon, 30 May 2016 23:13:59 +0000 Subject: [PATCH] [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)