func removeVolume(ctx *cobra.Command, volume *Volume) error { var ( config api.Config hostConfig api.HostConfig ) config.Cmd = []string{"/bin/sh", "-c", "rm -rf /.docker_volume_config/" + volume.ID} config.Image = "busybox:latest" hostConfig.Binds = []string{filepath.Dir(volume.configPath) + ":/.docker_volume_config"} if !volume.IsBindMount { config.Cmd[2] = config.Cmd[2] + (" && rm -rf /.docker_volume/" + volume.ID) hostConfig.Binds = append(hostConfig.Binds, filepath.Dir(volume.Path)+":/.docker_volume") } docker, err := client.NewDockerClient(configPath, hostName, ctx.Out()) if err != nil { return err } var cid string cid, err = docker.CreateContainer("", config, hostConfig) if err != nil { if apiErr, ok := err.(api.Error); ok && (apiErr.StatusCode == 404) { if err := pullImageInSilence(ctx, config.Image); err != nil { return err } cid, err = docker.CreateContainer("", config, hostConfig) if err != nil { return err } } else { return err } } defer docker.RemoveContainer(cid, true) if err := docker.StartContainer(cid); err != nil { return err } if _, err := docker.WaitContainer(cid); err != nil { return err } return nil }
func composeContainer(ctx *cobra.Command, root string, composer Composer) (string, error) { var ( config api.Config hostConfig api.HostConfig localVolumes = make(map[string]struct{}) bindVolumes []string exposedPorts = make(map[string]struct{}) portBindings = make(map[string][]api.PortBinding) links []string deviceMappings []api.DeviceMapping ) if composer.Image != "" { r, n, t, err := client.ParseRepositoryName(composer.Image) if err != nil { return "", err } composer.Image = n + ":" + t if r != "" { composer.Image = r + "/" + composer.Image } } if (composer.WorkingDir != "") && !filepath.IsAbs(composer.WorkingDir) { return "", fmt.Errorf("Invalid working directory: it must be absolute.") } docker, err := client.NewDockerClient(configPath, hostName, ctx.Out()) if err != nil { return "", err } if composer.Build != "" { if !filepath.IsAbs(composer.Build) { composer.Build = filepath.Join(root, composer.Build) } message, err := docker.BuildImage(composer.Build, composer.Image, false) if err != nil { return "", err } if composer.Image == "" { if _, err := fmt.Sscanf(message, "Successfully built %s", &composer.Image); err != nil { return "", err } } } for _, port := range composer.Ports { var ( rawPort = port hostIp = "" hostPort = "" containerPort = "" proto = "tcp" ) if i := strings.LastIndex(port, "/"); i != -1 { proto = strings.ToLower(port[i+1:]) port = port[:i] } parts := strings.Split(port, ":") switch len(parts) { case 1: containerPort = parts[0] case 2: hostPort = parts[0] containerPort = parts[1] case 3: hostIp = parts[0] hostPort = parts[1] containerPort = parts[2] default: return "", fmt.Errorf("Invalid port specification: %s", rawPort) } port := fmt.Sprintf("%s/%s", containerPort, proto) if _, exists := exposedPorts[port]; !exists { exposedPorts[port] = struct{}{} } portBinding := api.PortBinding{ HostIp: hostIp, HostPort: hostPort, } bslice, exists := portBindings[port] if !exists { bslice = []api.PortBinding{} } portBindings[port] = append(bslice, portBinding) } for _, port := range composer.ExposedPorts { var ( rawPort = port containerPort = "" proto = "tcp" ) parts := strings.Split(containerPort, "/") switch len(parts) { case 1: containerPort = parts[0] case 2: containerPort = parts[0] proto = strings.ToLower(parts[1]) default: return "", fmt.Errorf("Invalid port specification: %s", rawPort) } port := fmt.Sprintf("%s/%s", containerPort, proto) if _, exists := exposedPorts[port]; !exists { exposedPorts[port] = struct{}{} } } for _, volume := range composer.Volumes { if arr := strings.Split(volume, ":"); len(arr) > 1 { if arr[1] == "/" { return "", fmt.Errorf("Invalid bind mount: destination can't be '/'") } if !filepath.IsAbs(arr[0]) { return "", fmt.Errorf("Invalid bind mount: the host path must be absolute.") } bindVolumes = append(bindVolumes, volume) } else if volume == "/" { return "", fmt.Errorf("Invalid volume: path can't be '/'") } else { localVolumes[volume] = struct{}{} } } for _, link := range append(composer.Links, composer.ExternalLinks...) { arr := strings.Split(link, ":") if len(arr) < 2 { links = append(links, arr[0]+":"+arr[0]) } else { links = append(links, link) } } for _, device := range composer.Devices { src := "" dst := "" permissions := "rwm" arr := strings.Split(device, ":") switch len(arr) { case 3: permissions = arr[2] fallthrough case 2: dst = arr[1] fallthrough case 1: src = arr[0] default: return "", fmt.Errorf("Invalid device specification: %s", device) } if dst == "" { dst = src } deviceMapping := api.DeviceMapping{ PathOnHost: src, PathInContainer: dst, CgroupPermissions: permissions, } deviceMappings = append(deviceMappings, deviceMapping) } parts := strings.Split(composer.RestartPolicy, ":") restartPolicy := api.RestartPolicy{} restartPolicy.Name = parts[0] if (restartPolicy.Name == "on-failure") && (len(parts) == 2) { count, err := strconv.Atoi(parts[1]) if err != nil { return "", err } restartPolicy.MaximumRetryCount = count } config.Hostname = composer.Hostname config.Domainname = composer.Domainname config.User = composer.User config.Memory = composer.Memory config.MemorySwap = composer.MemorySwap config.CpuShares = composer.CpuShares config.Cpuset = composer.Cpuset config.ExposedPorts = exposedPorts config.Tty = composer.Tty config.OpenStdin = composer.OpenStdin config.Env = composer.Env config.Cmd = composer.Cmd config.Image = composer.Image config.Volumes = localVolumes config.WorkingDir = composer.WorkingDir if composer.Entrypoint != "" { config.Entrypoint = []string{composer.Entrypoint} } config.MacAddress = composer.MacAddress hostConfig.Binds = bindVolumes hostConfig.Privileged = composer.Privileged hostConfig.PortBindings = portBindings hostConfig.Links = links hostConfig.PublishAllPorts = composer.PublishAllPorts hostConfig.Dns = composer.Dns hostConfig.DnsSearch = composer.DnsSearch hostConfig.ExtraHosts = composer.ExtraHosts hostConfig.VolumesFrom = composer.VolumesFrom hostConfig.Devices = deviceMappings hostConfig.NetworkMode = composer.NetworkMode hostConfig.IpcMode = composer.IpcMode hostConfig.PidMode = composer.PidMode hostConfig.CapAdd = composer.CapAdd hostConfig.CapDrop = composer.CapDrop hostConfig.RestartPolicy = restartPolicy hostConfig.SecurityOpt = composer.SecurityOpt hostConfig.ReadonlyRootfs = composer.ReadonlyRootfs var cid string cid, err = docker.CreateContainer(composer.Name, config, hostConfig) if err != nil { if apiErr, ok := err.(api.Error); ok && (apiErr.StatusCode == 404) { if _, err := docker.PullImage(config.Image); err != nil { return "", err } cid, err = docker.CreateContainer(composer.Name, config, hostConfig) if err != nil { return "", err } } else { return "", err } } return cid, nil }
func getVolumes(ctx *cobra.Command) (Volumes, error) { docker, err := client.NewDockerClient(configPath, hostName, ctx.Out()) if err != nil { return nil, err } info, err := docker.Info() if err != nil { return nil, err } rootDir := "/var/lib/docker" if info.DockerRootDir != "" { rootDir = info.DockerRootDir } else { for _, pair := range info.DriverStatus { if pair[0] == "Root Dir" { rootDir = filepath.Dir(pair[1]) } } } path := filepath.Join(rootDir, "/volumes") var ( config api.Config hostConfig api.HostConfig ) config.Cmd = []string{"/bin/sh", "-c", "awk '{cmd=\"ls -e \" FILENAME; cmd | getline line; close(cmd); split(line,a,\" \"); print $0,a[6],a[7],a[8],a[9],a[10]}' /.docker_volumes/*/config.json"} config.Image = "busybox:latest" hostConfig.Binds = []string{path + ":/.docker_volumes:ro"} var cid string cid, err = docker.CreateContainer("", config, hostConfig) if err != nil { if apiErr, ok := err.(api.Error); ok && (apiErr.StatusCode == 404) { if err := pullImageInSilence(ctx, config.Image); err != nil { return nil, err } cid, err = docker.CreateContainer("", config, hostConfig) if err != nil { return nil, err } } else { return nil, err } } defer docker.RemoveContainer(cid, true) if err := docker.StartContainer(cid); err != nil { return nil, err } if _, err := docker.WaitContainer(cid); err != nil { return nil, err } logs, err := docker.GetContainerLogs(cid, false, true, true, false, 0) if err != nil { return nil, err } if logs[0] == "" { return nil, nil } vols := strings.Split(strings.TrimSpace(logs[0]), "\n") var volumes Volumes for _, vol := range vols { arr := strings.SplitN(vol, "} ", 2) volume := &Volume{} if err := json.Unmarshal([]byte(arr[0]+"}"), volume); err != nil { log.Debugf("%s: '%s'", err, arr[0]) return nil, err } volume.Created, _ = time.Parse(time.ANSIC, arr[1]) volume.configPath = filepath.Join(path, "/"+volume.ID) volumes = append(volumes, volume) } if err := docker.RemoveContainer(cid, true); err != nil { return nil, err } mounts, err := getMounts(ctx) if err != nil { return nil, err } for _, volume := range volumes { for _, mount := range mounts { if mount.hostPath == volume.Path { volume.MountedOn = append(volume.MountedOn, mount) } } } return volumes, nil }