func containerSpecFromGRPC(c *swarmapi.ContainerSpec) types.ContainerSpec { containerSpec := types.ContainerSpec{ Image: c.Image, Labels: c.Labels, Command: c.Command, Args: c.Args, Hostname: c.Hostname, Env: c.Env, Dir: c.Dir, User: c.User, Groups: c.Groups, TTY: c.TTY, } // Mounts for _, m := range c.Mounts { mount := mounttypes.Mount{ Target: m.Target, Source: m.Source, Type: mounttypes.Type(strings.ToLower(swarmapi.Mount_MountType_name[int32(m.Type)])), ReadOnly: m.ReadOnly, } if m.BindOptions != nil { mount.BindOptions = &mounttypes.BindOptions{ Propagation: mounttypes.Propagation(strings.ToLower(swarmapi.Mount_BindOptions_MountPropagation_name[int32(m.BindOptions.Propagation)])), } } if m.VolumeOptions != nil { mount.VolumeOptions = &mounttypes.VolumeOptions{ NoCopy: m.VolumeOptions.NoCopy, Labels: m.VolumeOptions.Labels, } if m.VolumeOptions.DriverConfig != nil { mount.VolumeOptions.DriverConfig = &mounttypes.Driver{ Name: m.VolumeOptions.DriverConfig.Name, Options: m.VolumeOptions.DriverConfig.Options, } } } containerSpec.Mounts = append(containerSpec.Mounts, mount) } if c.StopGracePeriod != nil { grace, _ := ptypes.Duration(c.StopGracePeriod) containerSpec.StopGracePeriod = &grace } if c.Healthcheck != nil { containerSpec.Healthcheck = healthConfigFromGRPC(c.Healthcheck) } return containerSpec }
// ParseMountRaw parses a raw volume spec (e.g. `-v /foo:/bar:shared`) into a // structured spec. Once the raw spec is parsed it relies on `ParseMountSpec` to // validate the spec and create a MountPoint func ParseMountRaw(raw, volumeDriver string) (*MountPoint, error) { arr, err := splitRawSpec(convertSlash(raw)) if err != nil { return nil, err } var spec mounttypes.Mount var mode string switch len(arr) { case 1: // Just a destination path in the container spec.Target = arr[0] case 2: if ValidMountMode(arr[1]) { // Destination + Mode is not a valid volume - volumes // cannot include a mode. eg /foo:rw return nil, errInvalidSpec(raw) } // Host Source Path or Name + Destination spec.Source = arr[0] spec.Target = arr[1] case 3: // HostSourcePath+DestinationPath+Mode spec.Source = arr[0] spec.Target = arr[1] mode = arr[2] default: return nil, errInvalidSpec(raw) } if !ValidMountMode(mode) { return nil, errInvalidMode(mode) } if filepath.IsAbs(spec.Source) { spec.Type = mounttypes.TypeBind } else { spec.Type = mounttypes.TypeVolume } spec.ReadOnly = !ReadWrite(mode) // cannot assume that if a volume driver is passed in that we should set it if volumeDriver != "" && spec.Type == mounttypes.TypeVolume { spec.VolumeOptions = &mounttypes.VolumeOptions{ DriverConfig: &mounttypes.Driver{Name: volumeDriver}, } } if copyData, isSet := getCopyMode(mode); isSet { if spec.VolumeOptions == nil { spec.VolumeOptions = &mounttypes.VolumeOptions{} } spec.VolumeOptions.NoCopy = !copyData } if HasPropagation(mode) { spec.BindOptions = &mounttypes.BindOptions{ Propagation: GetPropagation(mode), } } mp, err := ParseMountSpec(spec, platformRawValidationOpts...) if mp != nil { mp.Mode = mode } if err != nil { err = fmt.Errorf("%v: %v", errInvalidSpec(raw), err) } return mp, err }
func convertMount(m api.Mount) enginemount.Mount { mount := enginemount.Mount{ Source: m.Source, Target: m.Target, ReadOnly: m.ReadOnly, } switch m.Type { case api.MountTypeBind: mount.Type = enginemount.TypeBind case api.MountTypeVolume: mount.Type = enginemount.TypeVolume case api.MountTypeTmpfs: mount.Type = enginemount.TypeTmpfs } if m.BindOptions != nil { mount.BindOptions = &enginemount.BindOptions{} switch m.BindOptions.Propagation { case api.MountPropagationRPrivate: mount.BindOptions.Propagation = enginemount.PropagationRPrivate case api.MountPropagationPrivate: mount.BindOptions.Propagation = enginemount.PropagationPrivate case api.MountPropagationRSlave: mount.BindOptions.Propagation = enginemount.PropagationRSlave case api.MountPropagationSlave: mount.BindOptions.Propagation = enginemount.PropagationSlave case api.MountPropagationRShared: mount.BindOptions.Propagation = enginemount.PropagationRShared case api.MountPropagationShared: mount.BindOptions.Propagation = enginemount.PropagationShared } } if m.VolumeOptions != nil { mount.VolumeOptions = &enginemount.VolumeOptions{ NoCopy: m.VolumeOptions.NoCopy, } if m.VolumeOptions.Labels != nil { mount.VolumeOptions.Labels = make(map[string]string, len(m.VolumeOptions.Labels)) for k, v := range m.VolumeOptions.Labels { mount.VolumeOptions.Labels[k] = v } } if m.VolumeOptions.DriverConfig != nil { mount.VolumeOptions.DriverConfig = &enginemount.Driver{ Name: m.VolumeOptions.DriverConfig.Name, } if m.VolumeOptions.DriverConfig.Options != nil { mount.VolumeOptions.DriverConfig.Options = make(map[string]string, len(m.VolumeOptions.DriverConfig.Options)) for k, v := range m.VolumeOptions.DriverConfig.Options { mount.VolumeOptions.DriverConfig.Options[k] = v } } } } if m.TmpfsOptions != nil { mount.TmpfsOptions = &enginemount.TmpfsOptions{ SizeBytes: m.TmpfsOptions.SizeBytes, Mode: m.TmpfsOptions.Mode, } } return mount }
// Set a new mount value func (m *MountOpt) Set(value string) error { csvReader := csv.NewReader(strings.NewReader(value)) fields, err := csvReader.Read() if err != nil { return err } mount := mounttypes.Mount{} volumeOptions := func() *mounttypes.VolumeOptions { if mount.VolumeOptions == nil { mount.VolumeOptions = &mounttypes.VolumeOptions{ Labels: make(map[string]string), } } if mount.VolumeOptions.DriverConfig == nil { mount.VolumeOptions.DriverConfig = &mounttypes.Driver{} } return mount.VolumeOptions } bindOptions := func() *mounttypes.BindOptions { if mount.BindOptions == nil { mount.BindOptions = new(mounttypes.BindOptions) } return mount.BindOptions } setValueOnMap := func(target map[string]string, value string) { parts := strings.SplitN(value, "=", 2) if len(parts) == 1 { target[value] = "" } else { target[parts[0]] = parts[1] } } mount.Type = mounttypes.TypeVolume // default to volume mounts // Set writable as the default for _, field := range fields { parts := strings.SplitN(field, "=", 2) key := strings.ToLower(parts[0]) if len(parts) == 1 { switch key { case "readonly", "ro": mount.ReadOnly = true continue case "volume-nocopy": volumeOptions().NoCopy = true continue } } if len(parts) != 2 { return fmt.Errorf("invalid field '%s' must be a key=value pair", field) } value := parts[1] switch key { case "type": mount.Type = mounttypes.Type(strings.ToLower(value)) case "source", "src": mount.Source = value case "target", "dst", "destination": mount.Target = value case "readonly", "ro": mount.ReadOnly, err = strconv.ParseBool(value) if err != nil { return fmt.Errorf("invalid value for %s: %s", key, value) } case "bind-propagation": bindOptions().Propagation = mounttypes.Propagation(strings.ToLower(value)) case "volume-nocopy": volumeOptions().NoCopy, err = strconv.ParseBool(value) if err != nil { return fmt.Errorf("invalid value for populate: %s", value) } case "volume-label": setValueOnMap(volumeOptions().Labels, value) case "volume-driver": volumeOptions().DriverConfig.Name = value case "volume-opt": if volumeOptions().DriverConfig.Options == nil { volumeOptions().DriverConfig.Options = make(map[string]string) } setValueOnMap(volumeOptions().DriverConfig.Options, value) default: return fmt.Errorf("unexpected key '%s' in '%s'", key, field) } } if mount.Type == "" { return fmt.Errorf("type is required") } if mount.Target == "" { return fmt.Errorf("target is required") } if mount.VolumeOptions != nil && mount.Source == "" { return fmt.Errorf("source is required when specifying volume-* options") } if mount.Type == mounttypes.TypeBind && mount.VolumeOptions != nil { return fmt.Errorf("cannot mix 'volume-*' options with mount type '%s'", mounttypes.TypeBind) } if mount.Type == mounttypes.TypeVolume && mount.BindOptions != nil { return fmt.Errorf("cannot mix 'bind-*' options with mount type '%s'", mounttypes.TypeVolume) } m.values = append(m.values, mount) return nil }