func TestUpdateMounts(t *testing.T) { flags := newUpdateCommand(nil).Flags() flags.Set("mount-add", "type=volume,target=/toadd") flags.Set("mount-rm", "/toremove") mounts := []swarm.Mount{ {Target: "/toremove", Type: swarm.MountType("BIND")}, {Target: "/tokeep", Type: swarm.MountType("BIND")}, } updateMounts(flags, &mounts) assert.Equal(t, len(mounts), 2) assert.Equal(t, mounts[0].Target, "/tokeep") assert.Equal(t, mounts[1].Target, "/toadd") }
func TestMountOptString(t *testing.T) { mount := MountOpt{ values: []swarm.Mount{ { Type: swarm.MountType("BIND"), Source: "/home/path", Target: "/target", }, { Type: swarm.MountType("VOLUME"), Source: "foo", Target: "/target/foo", }, }, } expected := "BIND /home/path /target, VOLUME foo /target/foo" assertEqual(t, mount.String(), expected) }
func TestMountOptSetNoError(t *testing.T) { var mount MountOpt assertNilError(t, mount.Set("type=bind,target=/target,source=/foo")) mounts := mount.Value() assertEqual(t, len(mounts), 1) assertEqual(t, mounts[0], swarm.Mount{ Type: swarm.MountType("BIND"), Source: "/foo", Target: "/target", }) }
func containerSpecFromGRPC(c *swarmapi.ContainerSpec) types.ContainerSpec { containerSpec := types.ContainerSpec{ Image: c.Image, Labels: c.Labels, Command: c.Command, Args: c.Args, Env: c.Env, Dir: c.Dir, User: c.User, } // Mounts for _, m := range c.Mounts { mount := types.Mount{ Target: m.Target, Source: m.Source, Type: types.MountType(strings.ToLower(swarmapi.Mount_MountType_name[int32(m.Type)])), Writable: m.Writable, } if m.BindOptions != nil { mount.BindOptions = &types.BindOptions{ Propagation: types.MountPropagation(strings.ToLower(swarmapi.Mount_BindOptions_MountPropagation_name[int32(m.BindOptions.Propagation)])), } } if m.VolumeOptions != nil { mount.VolumeOptions = &types.VolumeOptions{ Populate: m.VolumeOptions.Populate, Labels: m.VolumeOptions.Labels, } if m.VolumeOptions.DriverConfig != nil { mount.VolumeOptions.DriverConfig = &types.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 } return containerSpec }
// 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 := swarm.Mount{} volumeOptions := func() *swarm.VolumeOptions { if mount.VolumeOptions == nil { mount.VolumeOptions = &swarm.VolumeOptions{ Labels: make(map[string]string), } } if mount.VolumeOptions.DriverConfig == nil { mount.VolumeOptions.DriverConfig = &swarm.Driver{} } return mount.VolumeOptions } bindOptions := func() *swarm.BindOptions { if mount.BindOptions == nil { mount.BindOptions = new(swarm.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] } } // Set writable as the default for _, field := range fields { parts := strings.SplitN(field, "=", 2) if len(parts) == 1 && strings.ToLower(parts[0]) == "readonly" { mount.ReadOnly = true continue } if len(parts) == 1 && strings.ToLower(parts[0]) == "volume-nocopy" { volumeOptions().NoCopy = true continue } if len(parts) != 2 { return fmt.Errorf("invalid field '%s' must be a key=value pair", field) } key, value := parts[0], parts[1] switch strings.ToLower(key) { case "type": mount.Type = swarm.MountType(strings.ToUpper(value)) case "source": mount.Source = value case "target": mount.Target = value case "readonly": ro, err := strconv.ParseBool(value) if err != nil { return fmt.Errorf("invalid value for readonly: %s", value) } mount.ReadOnly = ro case "bind-propagation": bindOptions().Propagation = swarm.MountPropagation(strings.ToUpper(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-driver-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 == swarm.MountType("BIND") && mount.VolumeOptions != nil { return fmt.Errorf("cannot mix 'volume-*' options with mount type '%s'", swarm.MountTypeBind) } if mount.Type == swarm.MountType("VOLUME") && mount.BindOptions != nil { return fmt.Errorf("cannot mix 'bind-*' options with mount type '%s'", swarm.MountTypeVolume) } m.values = append(m.values, mount) return nil }
// 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 := swarm.Mount{} volumeOptions := func() *swarm.VolumeOptions { if mount.VolumeOptions == nil { mount.VolumeOptions = &swarm.VolumeOptions{ Labels: make(map[string]string), } } if mount.VolumeOptions.DriverConfig == nil { mount.VolumeOptions.DriverConfig = &swarm.Driver{} } return mount.VolumeOptions } 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] } } for _, field := range fields { parts := strings.SplitN(field, "=", 2) if len(parts) == 1 && strings.ToLower(parts[0]) == "writable" { mount.Writable = true continue } if len(parts) != 2 { return fmt.Errorf("invalid field '%s' must be a key=value pair", field) } key, value := parts[0], parts[1] switch strings.ToLower(key) { case "type": mount.Type = swarm.MountType(strings.ToUpper(value)) case "source": mount.Source = value case "target": mount.Target = value case "writable": mount.Writable, err = strconv.ParseBool(value) if err != nil { return fmt.Errorf("invalid value for writable: %s", value) } case "bind-propagation": mount.BindOptions.Propagation = swarm.MountPropagation(strings.ToUpper(value)) case "volume-populate": volumeOptions().Populate, 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-driver-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, value) } } if mount.Type == "" { return fmt.Errorf("type is required") } if mount.Target == "" { return fmt.Errorf("target is required") } m.values = append(m.values, mount) return nil }