From 906bb77d9d021abe93b6b4e10bb6c2b05f0ea88e Mon Sep 17 00:00:00 2001 From: Diptanu Choudhury Date: Wed, 30 Mar 2016 13:09:32 -0700 Subject: [PATCH] Added ability to create image from archive --- client/driver/docker.go | 180 ++++++++++++++++++++++------------------ 1 file changed, 100 insertions(+), 80 deletions(-) diff --git a/client/driver/docker.go b/client/driver/docker.go index 79b535805..7571455e2 100644 --- a/client/driver/docker.go +++ b/client/driver/docker.go @@ -51,6 +51,7 @@ type DockerDriverAuth struct { type DockerDriverConfig struct { ImageName string `mapstructure:"image"` // Container's Image Name + LoadImage string `mapstructure:"load"` // LoadImage is the path to the image archive Command string `mapstructure:"command"` // The Command/Entrypoint to run when the container starts up Args []string `mapstructure:"args"` // The arguments to the Command/Entrypoint IpcMode string `mapstructure:"ipc_mode"` // The IPC mode of the container - host and none @@ -419,6 +420,93 @@ func (d *DockerDriver) Periodic() (bool, time.Duration) { return true, 15 * time.Second } +func (d *DockerDriver) createImage(driverConfig *DockerDriverConfig, client *docker.Client, taskDir string) error { + image := driverConfig.ImageName + repo, tag := docker.ParseRepositoryTag(image) + if tag == "" { + tag = "latest" + } + + var dockerImage *docker.Image + var err error + // We're going to check whether the image is already downloaded. If the tag + // is "latest" we have to check for a new version every time so we don't + // bother to check and cache the id here. We'll download first, then cache. + if tag != "latest" { + dockerImage, err = client.InspectImage(image) + } + + // Download the image + if dockerImage == nil { + if driverConfig.LoadImage != "" { + return d.loadImage(driverConfig, client, taskDir) + } else if driverConfig.ImageName != "" { + return d.pullImage(driverConfig, client, repo, tag) + } else { + return fmt.Errorf("either image name or load image has to be specified") + } + } + return err +} + +func (d *DockerDriver) pullImage(driverConfig *DockerDriverConfig, client *docker.Client, repo string, tag string) error { + pullOptions := docker.PullImageOptions{ + Repository: repo, + Tag: tag, + } + + authOptions := docker.AuthConfiguration{} + if len(driverConfig.Auth) != 0 { + authOptions = docker.AuthConfiguration{ + Username: driverConfig.Auth[0].Username, + Password: driverConfig.Auth[0].Password, + Email: driverConfig.Auth[0].Email, + ServerAddress: driverConfig.Auth[0].ServerAddress, + } + } + + if authConfigFile := d.config.Read("docker.auth.config"); authConfigFile != "" { + if f, err := os.Open(authConfigFile); err == nil { + defer f.Close() + var authConfigurations *docker.AuthConfigurations + if authConfigurations, err = docker.NewAuthConfigurations(f); err != nil { + return fmt.Errorf("Failed to create docker auth object: %v", err) + } + + authConfigurationKey := "" + if driverConfig.SSL { + authConfigurationKey += "https://" + } + + authConfigurationKey += strings.Split(driverConfig.ImageName, "/")[0] + if authConfiguration, ok := authConfigurations.Configs[authConfigurationKey]; ok { + authOptions = authConfiguration + } + } else { + return fmt.Errorf("Failed to open auth config file: %v, error: %v", authConfigFile, err) + } + } + + err := client.PullImage(pullOptions, authOptions) + if err != nil { + d.logger.Printf("[ERR] driver.docker: failed pulling container %s:%s: %s", repo, tag, err) + return d.recoverablePullError(err, driverConfig.ImageName) + } + d.logger.Printf("[DEBUG] driver.docker: docker pull %s:%s succeeded", repo, tag) + return nil +} + +func (d *DockerDriver) loadImage(driverConfig *DockerDriverConfig, client *docker.Client, taskDir string) error { + archive := filepath.Join(taskDir, allocdir.TaskLocal, driverConfig.LoadImage) + d.logger.Printf("[DEBUG] driver.docker: loading image from: %v", archive) + f, err := os.Open(archive) + if err != nil { + return fmt.Errorf("unable to open image archive: %v", err) + } + defer f.Close() + return client.LoadImage(docker.LoadImageOptions{InputStream: f}) +} + func (d *DockerDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, error) { var driverConfig DockerDriverConfig if err := mapstructure.WeakDecode(task.Config, &driverConfig); err != nil { @@ -434,99 +522,31 @@ func (d *DockerDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle if err := driverConfig.Validate(); err != nil { return nil, err } - if task.Resources == nil { - return nil, fmt.Errorf("Resources are not specified") - } - if task.Resources.MemoryMB == 0 { - return nil, fmt.Errorf("Memory limit cannot be zero") - } - if task.Resources.CPU == 0 { - return nil, fmt.Errorf("CPU limit cannot be zero") - } cleanupContainer := d.config.ReadBoolDefault("docker.cleanup.container", true) cleanupImage := d.config.ReadBoolDefault("docker.cleanup.image", true) + taskDir, ok := ctx.AllocDir.TaskDirs[d.DriverContext.taskName] + if !ok { + return nil, fmt.Errorf("Could not find task directory for task: %v", d.DriverContext.taskName) + } + // Initialize docker API client client, err := d.dockerClient() if err != nil { return nil, fmt.Errorf("Failed to connect to docker daemon: %s", err) } - repo, tag := docker.ParseRepositoryTag(image) - // Make sure tag is always explicitly set. We'll default to "latest" if it - // isn't, which is the expected behavior. - if tag == "" { - tag = "latest" + if err := d.createImage(&driverConfig, client, taskDir); err != nil { + return nil, fmt.Errorf("failed to pull image: %v", err) } - var dockerImage *docker.Image - // We're going to check whether the image is already downloaded. If the tag - // is "latest" we have to check for a new version every time so we don't - // bother to check and cache the id here. We'll download first, then cache. - if tag != "latest" { - dockerImage, err = client.InspectImage(image) + // Now that we have the image we can get the image id + dockerImage, err := client.InspectImage(image) + if err != nil { + d.logger.Printf("[ERR] driver.docker: failed getting image id for %s: %s", image, err) + return nil, fmt.Errorf("Failed to determine image id for `%s`: %s", image, err) } - - // Download the image - if dockerImage == nil { - pullOptions := docker.PullImageOptions{ - Repository: repo, - Tag: tag, - } - - authOptions := docker.AuthConfiguration{} - if len(driverConfig.Auth) != 0 { - authOptions = docker.AuthConfiguration{ - Username: driverConfig.Auth[0].Username, - Password: driverConfig.Auth[0].Password, - Email: driverConfig.Auth[0].Email, - ServerAddress: driverConfig.Auth[0].ServerAddress, - } - } - - if authConfigFile := d.config.Read("docker.auth.config"); authConfigFile != "" { - if f, err := os.Open(authConfigFile); err == nil { - defer f.Close() - var authConfigurations *docker.AuthConfigurations - if authConfigurations, err = docker.NewAuthConfigurations(f); err != nil { - return nil, fmt.Errorf("Failed to create docker auth object: %v", err) - } - - authConfigurationKey := "" - if driverConfig.SSL { - authConfigurationKey += "https://" - } - - authConfigurationKey += strings.Split(driverConfig.ImageName, "/")[0] - if authConfiguration, ok := authConfigurations.Configs[authConfigurationKey]; ok { - authOptions = authConfiguration - } - } else { - return nil, fmt.Errorf("Failed to open auth config file: %v, error: %v", authConfigFile, err) - } - } - - err = client.PullImage(pullOptions, authOptions) - if err != nil { - d.logger.Printf("[ERR] driver.docker: failed pulling container %s:%s: %s", repo, tag, err) - return nil, d.recoverablePullError(err, image) - } - d.logger.Printf("[DEBUG] driver.docker: docker pull %s:%s succeeded", repo, tag) - - // Now that we have the image we can get the image id - dockerImage, err = client.InspectImage(image) - if err != nil { - d.logger.Printf("[ERR] driver.docker: failed getting image id for %s: %s", image, err) - return nil, fmt.Errorf("Failed to determine image id for `%s`: %s", image, err) - } - } - - taskDir, ok := ctx.AllocDir.TaskDirs[d.DriverContext.taskName] - if !ok { - return nil, fmt.Errorf("Could not find task directory for task: %v", d.DriverContext.taskName) - } - d.logger.Printf("[DEBUG] driver.docker: identified image %s as %s", image, dockerImage.ID) bin, err := discover.NomadExecutable()