func TestCreateService(t *testing.T) { ts := newTestServer(t) defer ts.Stop() _, err := ts.Client.CreateService(context.Background(), &api.CreateServiceRequest{}) assert.Error(t, err) assert.Equal(t, codes.InvalidArgument, grpc.Code(err)) spec := createSpec("name", "image", 1) r, err := ts.Client.CreateService(context.Background(), &api.CreateServiceRequest{Spec: spec}) assert.NoError(t, err) assert.NotEmpty(t, r.Service.ID) // test port conflicts spec = createSpec("name2", "image", 1) spec.Endpoint = &api.EndpointSpec{Ports: []*api.PortConfig{ {PublishedPort: uint32(9000), TargetPort: uint32(9000), Protocol: api.PortConfig_Protocol(api.ProtocolTCP)}, }} r, err = ts.Client.CreateService(context.Background(), &api.CreateServiceRequest{Spec: spec}) assert.NoError(t, err) assert.NotEmpty(t, r.Service.ID) spec2 := createSpec("name3", "image", 1) spec2.Endpoint = &api.EndpointSpec{Ports: []*api.PortConfig{ {PublishedPort: uint32(9000), TargetPort: uint32(9000), Protocol: api.PortConfig_Protocol(api.ProtocolTCP)}, }} _, err = ts.Client.CreateService(context.Background(), &api.CreateServiceRequest{Spec: spec2}) assert.Error(t, err) assert.Equal(t, codes.InvalidArgument, grpc.Code(err)) // test no port conflicts when no publish port is specified spec3 := createSpec("name4", "image", 1) spec3.Endpoint = &api.EndpointSpec{Ports: []*api.PortConfig{ {TargetPort: uint32(9000), Protocol: api.PortConfig_Protocol(api.ProtocolTCP)}, }} r, err = ts.Client.CreateService(context.Background(), &api.CreateServiceRequest{Spec: spec3}) assert.NoError(t, err) assert.NotEmpty(t, r.Service.ID) spec4 := createSpec("name5", "image", 1) spec4.Endpoint = &api.EndpointSpec{Ports: []*api.PortConfig{ {TargetPort: uint32(9001), Protocol: api.PortConfig_Protocol(api.ProtocolTCP)}, }} _, err = ts.Client.CreateService(context.Background(), &api.CreateServiceRequest{Spec: spec4}) assert.NoError(t, err) }
func parsePortSpec(portSpec string) (api.PortConfig_Protocol, uint32, error) { parts := strings.Split(portSpec, "/") p := parts[0] port, err := strconv.ParseUint(p, 10, 32) if err != nil { return 0, 0, err } if len(parts) > 1 { proto := parts[1] protocol, ok := api.PortConfig_Protocol_value[strings.ToUpper(proto)] if !ok { return 0, 0, errors.Errorf("invalid protocol string: %s", proto) } return api.PortConfig_Protocol(protocol), uint32(port), nil } return api.ProtocolTCP, uint32(port), nil }
// ServiceSpecToGRPC converts a ServiceSpec to a grpc ServiceSpec. func ServiceSpecToGRPC(s types.ServiceSpec) (swarmapi.ServiceSpec, error) { name := s.Name if name == "" { name = namesgenerator.GetRandomName(0) } serviceNetworks := make([]*swarmapi.NetworkAttachmentConfig, 0, len(s.Networks)) for _, n := range s.Networks { serviceNetworks = append(serviceNetworks, &swarmapi.NetworkAttachmentConfig{Target: n.Target, Aliases: n.Aliases}) } taskNetworks := make([]*swarmapi.NetworkAttachmentConfig, 0, len(s.TaskTemplate.Networks)) for _, n := range s.TaskTemplate.Networks { taskNetworks = append(taskNetworks, &swarmapi.NetworkAttachmentConfig{Target: n.Target, Aliases: n.Aliases}) } spec := swarmapi.ServiceSpec{ Annotations: swarmapi.Annotations{ Name: name, Labels: s.Labels, }, Task: swarmapi.TaskSpec{ Resources: resourcesToGRPC(s.TaskTemplate.Resources), LogDriver: driverToGRPC(s.TaskTemplate.LogDriver), Networks: taskNetworks, ForceUpdate: s.TaskTemplate.ForceUpdate, }, Networks: serviceNetworks, } containerSpec, err := containerToGRPC(s.TaskTemplate.ContainerSpec) if err != nil { return swarmapi.ServiceSpec{}, err } spec.Task.Runtime = &swarmapi.TaskSpec_Container{Container: containerSpec} restartPolicy, err := restartPolicyToGRPC(s.TaskTemplate.RestartPolicy) if err != nil { return swarmapi.ServiceSpec{}, err } spec.Task.Restart = restartPolicy if s.TaskTemplate.Placement != nil { spec.Task.Placement = &swarmapi.Placement{ Constraints: s.TaskTemplate.Placement.Constraints, } } if s.UpdateConfig != nil { var failureAction swarmapi.UpdateConfig_FailureAction switch s.UpdateConfig.FailureAction { case types.UpdateFailureActionPause, "": failureAction = swarmapi.UpdateConfig_PAUSE case types.UpdateFailureActionContinue: failureAction = swarmapi.UpdateConfig_CONTINUE default: return swarmapi.ServiceSpec{}, fmt.Errorf("unrecongized update failure action %s", s.UpdateConfig.FailureAction) } spec.Update = &swarmapi.UpdateConfig{ Parallelism: s.UpdateConfig.Parallelism, Delay: *ptypes.DurationProto(s.UpdateConfig.Delay), FailureAction: failureAction, MaxFailureRatio: s.UpdateConfig.MaxFailureRatio, } if s.UpdateConfig.Monitor != 0 { spec.Update.Monitor = ptypes.DurationProto(s.UpdateConfig.Monitor) } } if s.EndpointSpec != nil { if s.EndpointSpec.Mode != "" && s.EndpointSpec.Mode != types.ResolutionModeVIP && s.EndpointSpec.Mode != types.ResolutionModeDNSRR { return swarmapi.ServiceSpec{}, fmt.Errorf("invalid resolution mode: %q", s.EndpointSpec.Mode) } spec.Endpoint = &swarmapi.EndpointSpec{} spec.Endpoint.Mode = swarmapi.EndpointSpec_ResolutionMode(swarmapi.EndpointSpec_ResolutionMode_value[strings.ToUpper(string(s.EndpointSpec.Mode))]) for _, portConfig := range s.EndpointSpec.Ports { spec.Endpoint.Ports = append(spec.Endpoint.Ports, &swarmapi.PortConfig{ Name: portConfig.Name, Protocol: swarmapi.PortConfig_Protocol(swarmapi.PortConfig_Protocol_value[strings.ToUpper(string(portConfig.Protocol))]), PublishMode: swarmapi.PortConfig_PublishMode(swarmapi.PortConfig_PublishMode_value[strings.ToUpper(string(portConfig.PublishMode))]), TargetPort: portConfig.TargetPort, PublishedPort: portConfig.PublishedPort, }) } } // Mode if s.Mode.Global != nil && s.Mode.Replicated != nil { return swarmapi.ServiceSpec{}, fmt.Errorf("cannot specify both replicated mode and global mode") } if s.Mode.Global != nil { spec.Mode = &swarmapi.ServiceSpec_Global{ Global: &swarmapi.GlobalService{}, } } else if s.Mode.Replicated != nil && s.Mode.Replicated.Replicas != nil { spec.Mode = &swarmapi.ServiceSpec_Replicated{ Replicated: &swarmapi.ReplicatedService{Replicas: *s.Mode.Replicated.Replicas}, } } else { spec.Mode = &swarmapi.ServiceSpec_Replicated{ Replicated: &swarmapi.ReplicatedService{Replicas: 1}, } } return spec, nil }
func TestUpdateService(t *testing.T) { ts := newTestServer(t) service := createService(t, ts, "name", "image", 1) _, err := ts.Client.UpdateService(context.Background(), &api.UpdateServiceRequest{}) assert.Error(t, err) assert.Equal(t, codes.InvalidArgument, grpc.Code(err)) _, err = ts.Client.UpdateService(context.Background(), &api.UpdateServiceRequest{ServiceID: "invalid", Spec: &service.Spec, ServiceVersion: &api.Version{}}) assert.Error(t, err) assert.Equal(t, codes.NotFound, grpc.Code(err)) // No update options. _, err = ts.Client.UpdateService(context.Background(), &api.UpdateServiceRequest{ServiceID: service.ID, Spec: &service.Spec}) assert.Error(t, err) assert.Equal(t, codes.InvalidArgument, grpc.Code(err)) _, err = ts.Client.UpdateService(context.Background(), &api.UpdateServiceRequest{ServiceID: service.ID, Spec: &service.Spec, ServiceVersion: &service.Meta.Version}) assert.NoError(t, err) r, err := ts.Client.GetService(context.Background(), &api.GetServiceRequest{ServiceID: service.ID}) assert.NoError(t, err) assert.Equal(t, service.Spec.Annotations.Name, r.Service.Spec.Annotations.Name) mode, ok := r.Service.Spec.GetMode().(*api.ServiceSpec_Replicated) assert.Equal(t, ok, true) assert.True(t, mode.Replicated.Replicas == 1) mode.Replicated.Replicas = 42 _, err = ts.Client.UpdateService(context.Background(), &api.UpdateServiceRequest{ ServiceID: service.ID, Spec: &r.Service.Spec, ServiceVersion: &r.Service.Meta.Version, }) assert.NoError(t, err) r, err = ts.Client.GetService(context.Background(), &api.GetServiceRequest{ServiceID: service.ID}) assert.NoError(t, err) assert.Equal(t, service.Spec.Annotations.Name, r.Service.Spec.Annotations.Name) mode, ok = r.Service.Spec.GetMode().(*api.ServiceSpec_Replicated) assert.Equal(t, ok, true) assert.True(t, mode.Replicated.Replicas == 42) // mode change not allowed r, err = ts.Client.GetService(context.Background(), &api.GetServiceRequest{ServiceID: service.ID}) assert.NoError(t, err) r.Service.Spec.Mode = &api.ServiceSpec_Global{ Global: &api.GlobalService{}, } _, err = ts.Client.UpdateService(context.Background(), &api.UpdateServiceRequest{ ServiceID: service.ID, Spec: &r.Service.Spec, ServiceVersion: &r.Service.Meta.Version, }) assert.Error(t, err) assert.True(t, strings.Contains(err.Error(), errModeChangeNotAllowed.Error())) // Versioning. r, err = ts.Client.GetService(context.Background(), &api.GetServiceRequest{ServiceID: service.ID}) assert.NoError(t, err) version := &r.Service.Meta.Version _, err = ts.Client.UpdateService(context.Background(), &api.UpdateServiceRequest{ ServiceID: service.ID, Spec: &r.Service.Spec, ServiceVersion: version, }) assert.NoError(t, err) // Perform an update with the "old" version. _, err = ts.Client.UpdateService(context.Background(), &api.UpdateServiceRequest{ ServiceID: service.ID, Spec: &r.Service.Spec, ServiceVersion: version, }) assert.Error(t, err) // test port conflicts spec2 := createSpec("name2", "image", 1) spec2.Endpoint = &api.EndpointSpec{Ports: []*api.PortConfig{ {PublishedPort: uint32(9000), TargetPort: uint32(9000), Protocol: api.PortConfig_Protocol(api.ProtocolTCP)}, }} _, err = ts.Client.CreateService(context.Background(), &api.CreateServiceRequest{Spec: spec2}) assert.NoError(t, err) spec3 := createSpec("name3", "image", 1) rs, err := ts.Client.CreateService(context.Background(), &api.CreateServiceRequest{Spec: spec3}) assert.NoError(t, err) spec3.Endpoint = &api.EndpointSpec{Ports: []*api.PortConfig{ {PublishedPort: uint32(9000), TargetPort: uint32(9000), Protocol: api.PortConfig_Protocol(api.ProtocolTCP)}, }} _, err = ts.Client.UpdateService(context.Background(), &api.UpdateServiceRequest{ ServiceID: rs.Service.ID, Spec: spec3, ServiceVersion: &rs.Service.Meta.Version, }) assert.Error(t, err) assert.Equal(t, codes.InvalidArgument, grpc.Code(err)) spec3.Endpoint = &api.EndpointSpec{Ports: []*api.PortConfig{ {PublishedPort: uint32(9001), TargetPort: uint32(9000), Protocol: api.PortConfig_Protocol(api.ProtocolTCP)}, }} _, err = ts.Client.UpdateService(context.Background(), &api.UpdateServiceRequest{ ServiceID: rs.Service.ID, Spec: spec3, ServiceVersion: &rs.Service.Meta.Version, }) assert.NoError(t, err) }
// ServiceSpecToGRPC converts a ServiceSpec to a grpc ServiceSpec. func ServiceSpecToGRPC(s types.ServiceSpec) (swarmapi.ServiceSpec, error) { name := s.Name if name == "" { name = namesgenerator.GetRandomName(0) } networks := make([]*swarmapi.ServiceSpec_NetworkAttachmentConfig, 0, len(s.Networks)) for _, n := range s.Networks { networks = append(networks, &swarmapi.ServiceSpec_NetworkAttachmentConfig{Target: n.Target, Aliases: n.Aliases}) } spec := swarmapi.ServiceSpec{ Annotations: swarmapi.Annotations{ Name: name, Labels: s.Labels, }, Task: swarmapi.TaskSpec{ Resources: resourcesToGRPC(s.TaskTemplate.Resources), LogDriver: driverToGRPC(s.TaskTemplate.LogDriver), }, Networks: networks, } containerSpec, err := containerToGRPC(s.TaskTemplate.ContainerSpec) if err != nil { return swarmapi.ServiceSpec{}, err } spec.Task.Runtime = &swarmapi.TaskSpec_Container{Container: containerSpec} restartPolicy, err := restartPolicyToGRPC(s.TaskTemplate.RestartPolicy) if err != nil { return swarmapi.ServiceSpec{}, err } spec.Task.Restart = restartPolicy if s.TaskTemplate.Placement != nil { spec.Task.Placement = &swarmapi.Placement{ Constraints: s.TaskTemplate.Placement.Constraints, } } if s.UpdateConfig != nil { spec.Update = &swarmapi.UpdateConfig{ Parallelism: s.UpdateConfig.Parallelism, Delay: *ptypes.DurationProto(s.UpdateConfig.Delay), } } if s.EndpointSpec != nil { if s.EndpointSpec.Mode != "" && s.EndpointSpec.Mode != types.ResolutionModeVIP && s.EndpointSpec.Mode != types.ResolutionModeDNSRR { return swarmapi.ServiceSpec{}, fmt.Errorf("invalid resolution mode: %q", s.EndpointSpec.Mode) } spec.Endpoint = &swarmapi.EndpointSpec{} spec.Endpoint.Mode = swarmapi.EndpointSpec_ResolutionMode(swarmapi.EndpointSpec_ResolutionMode_value[strings.ToUpper(string(s.EndpointSpec.Mode))]) for _, portConfig := range s.EndpointSpec.Ports { spec.Endpoint.Ports = append(spec.Endpoint.Ports, &swarmapi.PortConfig{ Name: portConfig.Name, Protocol: swarmapi.PortConfig_Protocol(swarmapi.PortConfig_Protocol_value[strings.ToUpper(string(portConfig.Protocol))]), TargetPort: portConfig.TargetPort, PublishedPort: portConfig.PublishedPort, }) } } //Mode if s.Mode.Global != nil { spec.Mode = &swarmapi.ServiceSpec_Global{ Global: &swarmapi.GlobalService{}, } } else if s.Mode.Replicated != nil && s.Mode.Replicated.Replicas != nil { spec.Mode = &swarmapi.ServiceSpec_Replicated{ Replicated: &swarmapi.ReplicatedService{Replicas: *s.Mode.Replicated.Replicas}, } } else { spec.Mode = &swarmapi.ServiceSpec_Replicated{ Replicated: &swarmapi.ReplicatedService{Replicas: 1}, } } return spec, nil }