// To avoid complexity of having a sort-of public host, and to ensure we // can just instead easily store images on S3 (or similar) we attempt to // sync images in a similar fashion to the LXC image downloader. This means // that when we attempt to run the image, the download will look for our // existing cache (that we've correctly populated) and just reference the // image from there. func (c *Container) ensureImageCached(snapshot string, clientLog *client.Log) error { var err error relPath := c.getImagePath(snapshot) localPath := filepath.Join("/var/cache/lxc/download", relPath) // list of files required to avoid network hit fileList := []string{fmt.Sprintf("rootfs.tar.%s", c.Compression), "config", "snapshot_id"} var missingFiles bool = false for n := range fileList { if _, err = os.Stat(filepath.Join(localPath, fileList[n])); os.IsNotExist(err) { missingFiles = true break } } if !missingFiles { return nil } if c.S3Bucket == "" { return errors.New("Unable to find cached image, and no S3 bucket defined.") } err = os.MkdirAll(localPath, 0755) if err != nil { return err } remotePath := fmt.Sprintf("s3://%s/%s", c.S3Bucket, relPath) clientLog.Writeln(fmt.Sprintf("==> Downloading image %s", snapshot)) // TODO(dcramer): verify env is passed correctly here cw := client.NewCmdWrapper([]string{"aws", "s3", "sync", "--quiet", remotePath, localPath}, "", []string{ "HOME=/root", }) start := time.Now() result, err := cw.Run(false, clientLog) dur := time.Since(start) if err != nil { return err } if !result.Success { return errors.New("Failed downloading image") } clientLog.Writeln(fmt.Sprintf("==> Image downloaded in %s", dur)) return nil }
// Compresses the root of the filesystem into the desired compressed tarball. // The compression here can vary based on flags. func (c *Container) createImageRootFs(snapshotPath string, clientLog *client.Log) error { rootFsTxz := filepath.Join(snapshotPath, fmt.Sprintf("rootfs.tar.%s", c.Compression)) clientLog.Writeln(fmt.Sprintf("==> Creating rootfs.tar.%s", c.Compression)) var cw *client.CmdWrapper if c.Compression == "xz" { cw = client.NewCmdWrapper([]string{"tar", "-Jcf", rootFsTxz, "-C", c.RootFs(), "."}, "", []string{}) } else { cw = client.NewCmdWrapper([]string{"tar", "-cf", rootFsTxz, "-I", "lz4", "-C", c.RootFs(), "."}, "", []string{}) } result, err := cw.Run(false, clientLog) if err != nil { return err } if !result.Success { return errors.New(fmt.Sprintf("Failed creating rootfs.tar.%s", c.Compression)) } return nil }
// Runs the prelaunch script which is essentially responsible for setting up // the environment for the post-launch script. It runs within the host // environment with the container mounted at LXC_ROOTFS. Runs as the user // that changes-client runs as (usually root). func (c *Container) runPreLaunch(clientLog *client.Log) error { preEnv := []string{fmt.Sprintf("LXC_ROOTFS=%s", c.RootFs()), fmt.Sprintf("LXC_NAME=%s", c.Name)} cw := client.NewCmdWrapper([]string{c.PreLaunch}, "", preEnv) result, err := cw.Run(false, clientLog) if err != nil { return err } if !result.Success { return errors.New("Post-launch script failed") } return nil }
// Uploads a snapshot outcome to an s3 bucket, at the same path that // changes-client will expect to download it from. The snapshot itself // is just a tarball of the rootfs of the container - compressed with // either xz for high compression or lz4 for raw speed. func (c *Container) UploadImage(snapshot string, clientLog *client.Log) error { relPath := c.getImagePath(snapshot) localPath := filepath.Join("/var/cache/lxc/download", relPath) remotePath := fmt.Sprintf("s3://%s/%s", c.S3Bucket, relPath) clientLog.Writeln(fmt.Sprintf("==> Uploading image %s", snapshot)) // TODO(dcramer): verify env is passed correctly here cw := client.NewCmdWrapper([]string{"aws", "s3", "sync", "--quiet", localPath, remotePath}, "", []string{}) start := time.Now() result, err := cw.Run(false, clientLog) dur := time.Since(start) if err != nil { return err } if !result.Success { return errors.New("Failed uploading image") } clientLog.Writeln(fmt.Sprintf("==> Image uploaded in %s", dur)) return nil }
// Runs a given command. This may be called multiple times depending func (a *Adapter) Run(cmd *client.Command, clientLog *client.Log) (*client.CommandResult, error) { cw := client.NewCmdWrapper([]string{cmd.Path}, cmd.Cwd, cmd.Env) return cw.Run(cmd.CaptureOutput, clientLog) }