// ParseLogDriverFlags parses a silly string format for log driver and options. // Fully baked log driver config should be returned. // // If no log driver is available, nil, nil will be returned. func ParseLogDriverFlags(flags *pflag.FlagSet) (*api.Driver, error) { if !flags.Changed("log-driver") { return nil, nil } name, err := flags.GetString("log-driver") if err != nil { return nil, err } var opts map[string]string if flags.Changed("log-opt") { rawOpts, err := flags.GetStringSlice("log-opt") if err != nil { return nil, err } opts = make(map[string]string, len(rawOpts)) for _, rawOpt := range rawOpts { parts := strings.SplitN(rawOpt, "=", 2) if len(parts) == 1 { opts[parts[0]] = "" continue } opts[parts[0]] = parts[1] } } return &api.Driver{ Name: name, Options: opts, }, nil }
func updatePlacement(flags *pflag.FlagSet, placement *swarm.Placement) { field, _ := flags.GetStringSlice(flagConstraintAdd) placement.Constraints = append(placement.Constraints, field...) toRemove := buildToRemoveSet(flags, flagConstraintRemove) placement.Constraints = removeItems(placement.Constraints, toRemove, itemKey) }
// parseBind only supports a very simple version of bind for testing the most // basic of data flows. Replace with a --mount flag, similar to what we have in // docker service. func parseBind(flags *pflag.FlagSet, spec *api.ServiceSpec) error { if flags.Changed("bind") { binds, err := flags.GetStringSlice("bind") if err != nil { return err } container := spec.Task.GetContainer() for _, bind := range binds { parts := strings.SplitN(bind, ":", 2) if len(parts) != 2 { return fmt.Errorf("bind format %q not supported", bind) } container.Mounts = append(container.Mounts, api.Mount{ Type: api.MountTypeBind, Source: parts[0], Target: parts[1], Writable: true, }) } } return nil }
func parseContainer(flags *pflag.FlagSet, spec *api.ServiceSpec) error { if flags.Changed("image") { image, err := flags.GetString("image") if err != nil { return err } spec.Task.GetContainer().Image = image } if flags.Changed("args") { args, err := flags.GetStringSlice("args") if err != nil { return err } spec.Task.GetContainer().Args = args } if flags.Changed("env") { env, err := flags.GetStringSlice("env") if err != nil { return err } spec.Task.GetContainer().Env = env } return nil }
func parsePorts(flags *pflag.FlagSet, spec *api.ServiceSpec) error { if !flags.Changed("ports") { return nil } portConfigs, err := flags.GetStringSlice("ports") if err != nil { return err } ports := []*api.PortConfig{} for _, portConfig := range portConfigs { name, protocol, port, swarmPort, err := parsePortConfig(portConfig) if err != nil { return err } ports = append(ports, &api.PortConfig{ Name: name, Protocol: protocol, TargetPort: port, PublishedPort: swarmPort, // In swarmctl all ports are by default // PublishModeHost PublishMode: api.PublishModeHost, }) } spec.Endpoint = &api.EndpointSpec{ Ports: ports, } return nil }
func mergeNetworks(flags *pflag.FlagSet, attachments *[]swarm.NetworkAttachmentConfig) { if !flags.Changed("network") { return } networks, _ := flags.GetStringSlice("network") for _, network := range networks { *attachments = append(*attachments, swarm.NetworkAttachmentConfig{Target: network}) } }
func updateNetworks(flags *pflag.FlagSet, attachments *[]swarm.NetworkAttachmentConfig) { if !flags.Changed(flagNetwork) { return } networks, _ := flags.GetStringSlice(flagNetwork) var localAttachments []swarm.NetworkAttachmentConfig for _, network := range networks { localAttachments = append(localAttachments, swarm.NetworkAttachmentConfig{Target: network}) } *attachments = localAttachments }
func parsePlacement(flags *pflag.FlagSet, spec *api.ServiceSpec) error { if flags.Changed("constraint") { constraints, err := flags.GetStringSlice("constraint") if err != nil { return err } if spec.Task.Placement == nil { spec.Task.Placement = &api.Placement{} } spec.Task.Placement.Constraints = constraints } return nil }
func updateNetworks(flags *pflag.FlagSet, attachments *[]swarm.NetworkAttachmentConfig) { if flags.Changed(flagNetworkAdd) { networks, _ := flags.GetStringSlice(flagNetworkAdd) for _, network := range networks { *attachments = append(*attachments, swarm.NetworkAttachmentConfig{Target: network}) } } toRemove := buildToRemoveSet(flags, flagNetworkRemove) newNetworks := []swarm.NetworkAttachmentConfig{} for _, network := range *attachments { if _, exists := toRemove[network.Target]; !exists { newNetworks = append(newNetworks, network) } } *attachments = newNetworks }
func mergeNodeUpdate(flags *pflag.FlagSet) func(*swarm.Node) { return func(node *swarm.Node) { mergeString := func(flag string, field *string) { if flags.Changed(flag) { *field, _ = flags.GetString(flag) } } mergeRole := func(flag string, field *swarm.NodeRole) { if flags.Changed(flag) { str, _ := flags.GetString(flag) *field = swarm.NodeRole(str) } } mergeMembership := func(flag string, field *swarm.NodeMembership) { if flags.Changed(flag) { str, _ := flags.GetString(flag) *field = swarm.NodeMembership(str) } } mergeAvailability := func(flag string, field *swarm.NodeAvailability) { if flags.Changed(flag) { str, _ := flags.GetString(flag) *field = swarm.NodeAvailability(str) } } mergeLabels := func(flag string, field *map[string]string) { if flags.Changed(flag) { values, _ := flags.GetStringSlice(flag) for key, value := range runconfigopts.ConvertKVStringsToMap(values) { (*field)[key] = value } } } spec := &node.Spec mergeString("name", &spec.Name) // TODO: setting labels is not working mergeLabels("label", &spec.Labels) mergeRole("role", &spec.Role) mergeMembership("membership", &spec.Membership) mergeAvailability("availability", &spec.Availability) } }
func updateGroups(flags *pflag.FlagSet, groups *[]string) error { if flags.Changed(flagGroupAdd) { values, err := flags.GetStringSlice(flagGroupAdd) if err != nil { return err } *groups = append(*groups, values...) } toRemove := buildToRemoveSet(flags, flagGroupRemove) newGroups := []string{} for _, group := range *groups { if _, exists := toRemove[group]; !exists { newGroups = append(newGroups, group) } } // Sort so that result is predictable. sort.Strings(newGroups) *groups = newGroups return nil }
// parseVolume only supports a very simple version of annonymous volumes for // testing the most basic of data flows. Replace with a --mount flag, similar // to what we have in docker service. func parseVolume(flags *pflag.FlagSet, spec *api.ServiceSpec) error { if flags.Changed("volume") { volumes, err := flags.GetStringSlice("volume") if err != nil { return err } container := spec.Task.GetContainer() for _, volume := range volumes { if strings.Contains(volume, ":") { return fmt.Errorf("volume format %q not supported", volume) } container.Mounts = append(container.Mounts, api.Mount{ Type: api.MountTypeVolume, Target: volume, }) } } return nil }
func mergeService(spec *swarm.ServiceSpec, flags *pflag.FlagSet) error { mergeString := func(flag string, field *string) { if flags.Changed(flag) { *field, _ = flags.GetString(flag) } } mergeListOpts := func(flag string, field *[]string) { if flags.Changed(flag) { value := flags.Lookup(flag).Value.(*opts.ListOpts) *field = value.GetAll() } } mergeSlice := func(flag string, field *[]string) { if flags.Changed(flag) { *field, _ = flags.GetStringSlice(flag) } } mergeInt64Value := func(flag string, field *int64) { if flags.Changed(flag) { *field = flags.Lookup(flag).Value.(int64Value).Value() } } mergeDuration := func(flag string, field *time.Duration) { if flags.Changed(flag) { *field, _ = flags.GetDuration(flag) } } mergeDurationOpt := func(flag string, field *time.Duration) { if flags.Changed(flag) { *field = *flags.Lookup(flag).Value.(*DurationOpt).Value() } } mergeUint64 := func(flag string, field *uint64) { if flags.Changed(flag) { *field, _ = flags.GetUint64(flag) } } mergeUint64Opt := func(flag string, field *uint64) { if flags.Changed(flag) { *field = *flags.Lookup(flag).Value.(*Uint64Opt).Value() } } cspec := &spec.TaskTemplate.ContainerSpec task := &spec.TaskTemplate mergeString("name", &spec.Name) mergeLabels(flags, &spec.Labels) mergeString("image", &cspec.Image) mergeSlice("command", &cspec.Command) mergeSlice("arg", &cspec.Command) mergeListOpts("env", &cspec.Env) mergeString("workdir", &cspec.Dir) mergeString("user", &cspec.User) mergeMounts(flags, &cspec.Mounts) mergeInt64Value("limit-cpu", &task.Resources.Limits.NanoCPUs) mergeInt64Value("limit-memory", &task.Resources.Limits.MemoryBytes) mergeInt64Value("reserve-cpu", &task.Resources.Reservations.NanoCPUs) mergeInt64Value("reserve-memory", &task.Resources.Reservations.MemoryBytes) mergeDurationOpt("stop-grace-period", cspec.StopGracePeriod) if flags.Changed("restart-policy-condition") { value, _ := flags.GetString("restart-policy-condition") task.RestartPolicy.Condition = swarm.RestartPolicyCondition(value) } mergeDurationOpt("restart-policy-delay", task.RestartPolicy.Delay) mergeUint64Opt("restart-policy-max-attempts", task.RestartPolicy.MaxAttempts) mergeDurationOpt("restart-policy-window", task.RestartPolicy.Window) mergeSlice("constraint", &task.Placement.Constraints) if err := mergeMode(flags, &spec.Mode); err != nil { return err } mergeUint64("updateconfig-parallelism", &spec.UpdateConfig.Parallelism) mergeDuration("updateconfig-delay", &spec.UpdateConfig.Delay) mergeNetworks(flags, &spec.Networks) if flags.Changed("endpoint-mode") { value, _ := flags.GetString("endpoint-mode") spec.EndpointSpec.Mode = swarm.ResolutionMode(value) } mergePorts(flags, &spec.EndpointSpec.Ports) return nil }
func mergeService(spec *swarm.ServiceSpec, flags *pflag.FlagSet) error { mergeString := func(flag string, field *string) { if flags.Changed(flag) { *field, _ = flags.GetString(flag) } } mergeListOpts := func(flag string, field *[]string) { if flags.Changed(flag) { value := flags.Lookup(flag).Value.(*opts.ListOpts) *field = value.GetAll() } } mergeSlice := func(flag string, field *[]string) { if flags.Changed(flag) { *field, _ = flags.GetStringSlice(flag) } } mergeInt64Value := func(flag string, field *int64) { if flags.Changed(flag) { *field = flags.Lookup(flag).Value.(int64Value).Value() } } mergeDuration := func(flag string, field *time.Duration) { if flags.Changed(flag) { *field, _ = flags.GetDuration(flag) } } mergeDurationOpt := func(flag string, field *time.Duration) { if flags.Changed(flag) { *field = *flags.Lookup(flag).Value.(*DurationOpt).Value() } } mergeUint64 := func(flag string, field *uint64) { if flags.Changed(flag) { *field, _ = flags.GetUint64(flag) } } mergeUint64Opt := func(flag string, field *uint64) { if flags.Changed(flag) { *field = *flags.Lookup(flag).Value.(*Uint64Opt).Value() } } cspec := &spec.TaskTemplate.ContainerSpec task := &spec.TaskTemplate mergeString(flagName, &spec.Name) mergeLabels(flags, &spec.Labels) mergeString("image", &cspec.Image) mergeSlice("command", &cspec.Command) mergeSlice("arg", &cspec.Command) mergeListOpts("env", &cspec.Env) mergeString("workdir", &cspec.Dir) mergeString("user", &cspec.User) mergeMounts(flags, &cspec.Mounts) if flags.Changed(flagLimitCPU) || flags.Changed(flagLimitMemory) { if task.Resources == nil { task.Resources = &swarm.ResourceRequirements{} } task.Resources.Limits = &swarm.Resources{} mergeInt64Value(flagLimitCPU, &task.Resources.Limits.NanoCPUs) mergeInt64Value(flagLimitMemory, &task.Resources.Limits.MemoryBytes) } if flags.Changed(flagReserveCPU) || flags.Changed(flagReserveMemory) { if task.Resources == nil { task.Resources = &swarm.ResourceRequirements{} } task.Resources.Reservations = &swarm.Resources{} mergeInt64Value(flagReserveCPU, &task.Resources.Reservations.NanoCPUs) mergeInt64Value(flagReserveMemory, &task.Resources.Reservations.MemoryBytes) } mergeDurationOpt("stop-grace-period", cspec.StopGracePeriod) if flags.Changed(flagRestartCondition) || flags.Changed(flagRestartDelay) || flags.Changed(flagRestartMaxAttempts) || flags.Changed(flagRestartWindow) { if task.RestartPolicy == nil { task.RestartPolicy = &swarm.RestartPolicy{} } if flags.Changed(flagRestartCondition) { value, _ := flags.GetString(flagRestartCondition) task.RestartPolicy.Condition = swarm.RestartPolicyCondition(value) } mergeDurationOpt(flagRestartDelay, task.RestartPolicy.Delay) mergeUint64Opt(flagRestartMaxAttempts, task.RestartPolicy.MaxAttempts) mergeDurationOpt((flagRestartWindow), task.RestartPolicy.Window) } if flags.Changed(flagConstraint) { task.Placement = &swarm.Placement{} mergeSlice(flagConstraint, &task.Placement.Constraints) } if err := mergeMode(flags, &spec.Mode); err != nil { return err } if flags.Changed(flagUpdateParallelism) || flags.Changed(flagUpdateDelay) { if spec.UpdateConfig == nil { spec.UpdateConfig = &swarm.UpdateConfig{} } mergeUint64(flagUpdateParallelism, &spec.UpdateConfig.Parallelism) mergeDuration(flagUpdateDelay, &spec.UpdateConfig.Delay) } mergeNetworks(flags, &spec.Networks) if flags.Changed(flagEndpointMode) { value, _ := flags.GetString(flagEndpointMode) spec.EndpointSpec.Mode = swarm.ResolutionMode(value) } if flags.Changed(flagPublish) { if spec.EndpointSpec == nil { spec.EndpointSpec = &swarm.EndpointSpec{} } mergePorts(flags, &spec.EndpointSpec.Ports) } return nil }
// parseTmpfs supports a simple tmpfs decl, similar to docker run. // // This should go away. func parseTmpfs(flags *pflag.FlagSet, spec *api.ServiceSpec) error { if flags.Changed("tmpfs") { tmpfss, err := flags.GetStringSlice("tmpfs") if err != nil { return err } container := spec.Task.GetContainer() // TODO(stevvooe): Nasty inline parsing code, replace with mount syntax. for _, tmpfs := range tmpfss { parts := strings.SplitN(tmpfs, ":", 2) if len(parts) < 1 { return errors.Errorf("invalid mount spec: %v", tmpfs) } if len(parts[0]) == 0 || !path.IsAbs(parts[0]) { return errors.Errorf("invalid mount spec: %v", tmpfs) } m := api.Mount{ Type: api.MountTypeTmpfs, Target: parts[0], } if len(parts) == 2 { if strings.Contains(parts[1], ":") { // repeated colon is illegal return errors.Errorf("invalid mount spec: %v", tmpfs) } // BUG(stevvooe): Cobra stringslice actually doesn't correctly // handle comma separated values, so multiple flags aren't // really supported. We'll have to replace StringSlice with a // type that doesn't use the csv parser. This is good enough // for now. flags := strings.Split(parts[1], ",") var opts api.Mount_TmpfsOptions for _, flag := range flags { switch { case strings.HasPrefix(flag, "size="): meat := strings.TrimPrefix(flag, "size=") // try to parse this into bytes i, err := strconv.ParseInt(meat, 10, 64) if err != nil { // remove suffux and try again suffix := meat[len(meat)-1] meat = meat[:len(meat)-1] var multiplier int64 = 1 switch suffix { case 'g': multiplier = 1 << 30 case 'm': multiplier = 1 << 20 case 'k': multiplier = 1 << 10 default: return errors.Errorf("invalid size format: %v", flag) } // reparse the meat var err error i, err = strconv.ParseInt(meat, 10, 64) if err != nil { return err } i *= multiplier } opts.SizeBytes = i case strings.HasPrefix(flag, "mode="): meat := strings.TrimPrefix(flag, "mode=") i, err := strconv.ParseInt(meat, 8, 32) if err != nil { return err } opts.Mode = os.FileMode(i) case flag == "ro": m.ReadOnly = true case flag == "rw": m.ReadOnly = false default: return errors.New("unsupported flag") } } m.TmpfsOptions = &opts } fmt.Println("mount", m) container.Mounts = append(container.Mounts, m) } } return nil }
func parseContainer(flags *pflag.FlagSet, spec *api.ServiceSpec) error { if flags.Changed("image") { image, err := flags.GetString("image") if err != nil { return err } spec.Task.GetContainer().Image = image } if flags.Changed("hostname") { hostname, err := flags.GetString("hostname") if err != nil { return err } spec.Task.GetContainer().Hostname = hostname } if flags.Changed("command") { command, err := flags.GetStringSlice("command") if err != nil { return err } spec.Task.GetContainer().Command = command } if flags.Changed("args") { args, err := flags.GetStringSlice("args") if err != nil { return err } spec.Task.GetContainer().Args = args } if flags.Changed("env") { env, err := flags.GetStringSlice("env") if err != nil { return err } spec.Task.GetContainer().Env = env } if flags.Changed("tty") { tty, err := flags.GetBool("tty") if err != nil { return err } spec.Task.GetContainer().TTY = tty } if flags.Changed("open-stdin") { openStdin, err := flags.GetBool("open-stdin") if err != nil { return err } spec.Task.GetContainer().OpenStdin = openStdin } return nil }