service.go 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739
  1. package controlapi
  2. import (
  3. "errors"
  4. "path/filepath"
  5. "reflect"
  6. "strconv"
  7. "strings"
  8. "time"
  9. "github.com/docker/distribution/reference"
  10. "github.com/docker/swarmkit/api"
  11. "github.com/docker/swarmkit/api/naming"
  12. "github.com/docker/swarmkit/identity"
  13. "github.com/docker/swarmkit/manager/allocator"
  14. "github.com/docker/swarmkit/manager/constraint"
  15. "github.com/docker/swarmkit/manager/state/store"
  16. "github.com/docker/swarmkit/protobuf/ptypes"
  17. "github.com/docker/swarmkit/template"
  18. gogotypes "github.com/gogo/protobuf/types"
  19. "golang.org/x/net/context"
  20. "google.golang.org/grpc"
  21. "google.golang.org/grpc/codes"
  22. )
  23. var (
  24. errNetworkUpdateNotSupported = errors.New("networks must be migrated to TaskSpec before being changed")
  25. errRenameNotSupported = errors.New("renaming services is not supported")
  26. errModeChangeNotAllowed = errors.New("service mode change is not allowed")
  27. )
  28. func validateResources(r *api.Resources) error {
  29. if r == nil {
  30. return nil
  31. }
  32. if r.NanoCPUs != 0 && r.NanoCPUs < 1e6 {
  33. return grpc.Errorf(codes.InvalidArgument, "invalid cpu value %g: Must be at least %g", float64(r.NanoCPUs)/1e9, 1e6/1e9)
  34. }
  35. if r.MemoryBytes != 0 && r.MemoryBytes < 4*1024*1024 {
  36. return grpc.Errorf(codes.InvalidArgument, "invalid memory value %d: Must be at least 4MiB", r.MemoryBytes)
  37. }
  38. return nil
  39. }
  40. func validateResourceRequirements(r *api.ResourceRequirements) error {
  41. if r == nil {
  42. return nil
  43. }
  44. if err := validateResources(r.Limits); err != nil {
  45. return err
  46. }
  47. if err := validateResources(r.Reservations); err != nil {
  48. return err
  49. }
  50. return nil
  51. }
  52. func validateRestartPolicy(rp *api.RestartPolicy) error {
  53. if rp == nil {
  54. return nil
  55. }
  56. if rp.Delay != nil {
  57. delay, err := gogotypes.DurationFromProto(rp.Delay)
  58. if err != nil {
  59. return err
  60. }
  61. if delay < 0 {
  62. return grpc.Errorf(codes.InvalidArgument, "TaskSpec: restart-delay cannot be negative")
  63. }
  64. }
  65. if rp.Window != nil {
  66. win, err := gogotypes.DurationFromProto(rp.Window)
  67. if err != nil {
  68. return err
  69. }
  70. if win < 0 {
  71. return grpc.Errorf(codes.InvalidArgument, "TaskSpec: restart-window cannot be negative")
  72. }
  73. }
  74. return nil
  75. }
  76. func validatePlacement(placement *api.Placement) error {
  77. if placement == nil {
  78. return nil
  79. }
  80. _, err := constraint.Parse(placement.Constraints)
  81. return err
  82. }
  83. func validateUpdate(uc *api.UpdateConfig) error {
  84. if uc == nil {
  85. return nil
  86. }
  87. if uc.Delay < 0 {
  88. return grpc.Errorf(codes.InvalidArgument, "TaskSpec: update-delay cannot be negative")
  89. }
  90. if uc.Monitor != nil {
  91. monitor, err := gogotypes.DurationFromProto(uc.Monitor)
  92. if err != nil {
  93. return err
  94. }
  95. if monitor < 0 {
  96. return grpc.Errorf(codes.InvalidArgument, "TaskSpec: update-monitor cannot be negative")
  97. }
  98. }
  99. if uc.MaxFailureRatio < 0 || uc.MaxFailureRatio > 1 {
  100. return grpc.Errorf(codes.InvalidArgument, "TaskSpec: update-maxfailureratio cannot be less than 0 or bigger than 1")
  101. }
  102. return nil
  103. }
  104. func validateContainerSpec(taskSpec api.TaskSpec) error {
  105. // Building a empty/dummy Task to validate the templating and
  106. // the resulting container spec as well. This is a *best effort*
  107. // validation.
  108. container, err := template.ExpandContainerSpec(&api.Task{
  109. Spec: taskSpec,
  110. ServiceID: "serviceid",
  111. Slot: 1,
  112. NodeID: "nodeid",
  113. Networks: []*api.NetworkAttachment{},
  114. Annotations: api.Annotations{
  115. Name: "taskname",
  116. },
  117. ServiceAnnotations: api.Annotations{
  118. Name: "servicename",
  119. },
  120. Endpoint: &api.Endpoint{},
  121. LogDriver: taskSpec.LogDriver,
  122. })
  123. if err != nil {
  124. return grpc.Errorf(codes.InvalidArgument, err.Error())
  125. }
  126. if container.Image == "" {
  127. return grpc.Errorf(codes.InvalidArgument, "ContainerSpec: image reference must be provided")
  128. }
  129. if _, err := reference.ParseNormalizedNamed(container.Image); err != nil {
  130. return grpc.Errorf(codes.InvalidArgument, "ContainerSpec: %q is not a valid repository/tag", container.Image)
  131. }
  132. mountMap := make(map[string]bool)
  133. for _, mount := range container.Mounts {
  134. if _, exists := mountMap[mount.Target]; exists {
  135. return grpc.Errorf(codes.InvalidArgument, "ContainerSpec: duplicate mount point: %s", mount.Target)
  136. }
  137. mountMap[mount.Target] = true
  138. }
  139. return nil
  140. }
  141. func validateGenericRuntimeSpec(taskSpec api.TaskSpec) error {
  142. generic := taskSpec.GetGeneric()
  143. if len(generic.Kind) < 3 {
  144. return grpc.Errorf(codes.InvalidArgument, "Generic runtime: Invalid name %q", generic.Kind)
  145. }
  146. reservedNames := []string{"container", "attachment"}
  147. for _, n := range reservedNames {
  148. if strings.ToLower(generic.Kind) == n {
  149. return grpc.Errorf(codes.InvalidArgument, "Generic runtime: %q is a reserved name", generic.Kind)
  150. }
  151. }
  152. payload := generic.Payload
  153. if payload == nil {
  154. return grpc.Errorf(codes.InvalidArgument, "Generic runtime is missing payload")
  155. }
  156. if payload.TypeUrl == "" {
  157. return grpc.Errorf(codes.InvalidArgument, "Generic runtime is missing payload type")
  158. }
  159. if len(payload.Value) == 0 {
  160. return grpc.Errorf(codes.InvalidArgument, "Generic runtime has an empty payload")
  161. }
  162. return nil
  163. }
  164. func validateTaskSpec(taskSpec api.TaskSpec) error {
  165. if err := validateResourceRequirements(taskSpec.Resources); err != nil {
  166. return err
  167. }
  168. if err := validateRestartPolicy(taskSpec.Restart); err != nil {
  169. return err
  170. }
  171. if err := validatePlacement(taskSpec.Placement); err != nil {
  172. return err
  173. }
  174. // Check to see if the Secret Reference portion of the spec is valid
  175. if err := validateSecretRefsSpec(taskSpec); err != nil {
  176. return err
  177. }
  178. if taskSpec.GetRuntime() == nil {
  179. return grpc.Errorf(codes.InvalidArgument, "TaskSpec: missing runtime")
  180. }
  181. switch taskSpec.GetRuntime().(type) {
  182. case *api.TaskSpec_Container:
  183. if err := validateContainerSpec(taskSpec); err != nil {
  184. return err
  185. }
  186. case *api.TaskSpec_Generic:
  187. if err := validateGenericRuntimeSpec(taskSpec); err != nil {
  188. return err
  189. }
  190. default:
  191. return grpc.Errorf(codes.Unimplemented, "RuntimeSpec: unimplemented runtime in service spec")
  192. }
  193. return nil
  194. }
  195. func validateEndpointSpec(epSpec *api.EndpointSpec) error {
  196. // Endpoint spec is optional
  197. if epSpec == nil {
  198. return nil
  199. }
  200. type portSpec struct {
  201. publishedPort uint32
  202. protocol api.PortConfig_Protocol
  203. }
  204. portSet := make(map[portSpec]struct{})
  205. for _, port := range epSpec.Ports {
  206. // Publish mode = "ingress" represents Routing-Mesh and current implementation
  207. // of routing-mesh relies on IPVS based load-balancing with input=published-port.
  208. // But Endpoint-Spec mode of DNSRR relies on multiple A records and cannot be used
  209. // with routing-mesh (PublishMode="ingress") which cannot rely on DNSRR.
  210. // But PublishMode="host" doesn't provide Routing-Mesh and the DNSRR is applicable
  211. // for the backend network and hence we accept that configuration.
  212. if epSpec.Mode == api.ResolutionModeDNSRoundRobin && port.PublishMode == api.PublishModeIngress {
  213. return grpc.Errorf(codes.InvalidArgument, "EndpointSpec: port published with ingress mode can't be used with dnsrr mode")
  214. }
  215. // If published port is not specified, it does not conflict
  216. // with any others.
  217. if port.PublishedPort == 0 {
  218. continue
  219. }
  220. portSpec := portSpec{publishedPort: port.PublishedPort, protocol: port.Protocol}
  221. if _, ok := portSet[portSpec]; ok {
  222. return grpc.Errorf(codes.InvalidArgument, "EndpointSpec: duplicate published ports provided")
  223. }
  224. portSet[portSpec] = struct{}{}
  225. }
  226. return nil
  227. }
  228. // validateSecretRefsSpec finds if the secrets passed in spec are valid and have no
  229. // conflicting targets.
  230. func validateSecretRefsSpec(spec api.TaskSpec) error {
  231. container := spec.GetContainer()
  232. if container == nil {
  233. return nil
  234. }
  235. // Keep a map to track all the targets that will be exposed
  236. // The string returned is only used for logging. It could as well be struct{}{}
  237. existingTargets := make(map[string]string)
  238. for _, secretRef := range container.Secrets {
  239. // SecretID and SecretName are mandatory, we have invalid references without them
  240. if secretRef.SecretID == "" || secretRef.SecretName == "" {
  241. return grpc.Errorf(codes.InvalidArgument, "malformed secret reference")
  242. }
  243. // Every secret reference requires a Target
  244. if secretRef.GetTarget() == nil {
  245. return grpc.Errorf(codes.InvalidArgument, "malformed secret reference, no target provided")
  246. }
  247. // If this is a file target, we will ensure filename uniqueness
  248. if secretRef.GetFile() != nil {
  249. fileName := secretRef.GetFile().Name
  250. // Validate the file name
  251. if fileName == "" || fileName != filepath.Base(filepath.Clean(fileName)) {
  252. return grpc.Errorf(codes.InvalidArgument, "malformed file secret reference, invalid target file name provided")
  253. }
  254. // If this target is already in use, we have conflicting targets
  255. if prevSecretName, ok := existingTargets[fileName]; ok {
  256. return grpc.Errorf(codes.InvalidArgument, "secret references '%s' and '%s' have a conflicting target: '%s'", prevSecretName, secretRef.SecretName, fileName)
  257. }
  258. existingTargets[fileName] = secretRef.SecretName
  259. }
  260. }
  261. return nil
  262. }
  263. func (s *Server) validateNetworks(networks []*api.NetworkAttachmentConfig) error {
  264. for _, na := range networks {
  265. var network *api.Network
  266. s.store.View(func(tx store.ReadTx) {
  267. network = store.GetNetwork(tx, na.Target)
  268. })
  269. if network == nil {
  270. continue
  271. }
  272. if network.Spec.Internal {
  273. return grpc.Errorf(codes.InvalidArgument,
  274. "Service cannot be explicitly attached to %q network which is a swarm internal network",
  275. network.Spec.Annotations.Name)
  276. }
  277. }
  278. return nil
  279. }
  280. func validateMode(s *api.ServiceSpec) error {
  281. m := s.GetMode()
  282. switch m.(type) {
  283. case *api.ServiceSpec_Replicated:
  284. if int64(m.(*api.ServiceSpec_Replicated).Replicated.Replicas) < 0 {
  285. return grpc.Errorf(codes.InvalidArgument, "Number of replicas must be non-negative")
  286. }
  287. case *api.ServiceSpec_Global:
  288. default:
  289. return grpc.Errorf(codes.InvalidArgument, "Unrecognized service mode")
  290. }
  291. return nil
  292. }
  293. func validateServiceSpec(spec *api.ServiceSpec) error {
  294. if spec == nil {
  295. return grpc.Errorf(codes.InvalidArgument, errInvalidArgument.Error())
  296. }
  297. if err := validateAnnotations(spec.Annotations); err != nil {
  298. return err
  299. }
  300. if err := validateTaskSpec(spec.Task); err != nil {
  301. return err
  302. }
  303. if err := validateUpdate(spec.Update); err != nil {
  304. return err
  305. }
  306. if err := validateEndpointSpec(spec.Endpoint); err != nil {
  307. return err
  308. }
  309. if err := validateMode(spec); err != nil {
  310. return err
  311. }
  312. return nil
  313. }
  314. // checkPortConflicts does a best effort to find if the passed in spec has port
  315. // conflicts with existing services.
  316. // `serviceID string` is the service ID of the spec in service update. If
  317. // `serviceID` is not "", then conflicts check will be skipped against this
  318. // service (the service being updated).
  319. func (s *Server) checkPortConflicts(spec *api.ServiceSpec, serviceID string) error {
  320. if spec.Endpoint == nil {
  321. return nil
  322. }
  323. pcToString := func(pc *api.PortConfig) string {
  324. port := strconv.FormatUint(uint64(pc.PublishedPort), 10)
  325. return port + "/" + pc.Protocol.String()
  326. }
  327. reqPorts := make(map[string]bool)
  328. for _, pc := range spec.Endpoint.Ports {
  329. if pc.PublishedPort > 0 {
  330. reqPorts[pcToString(pc)] = true
  331. }
  332. }
  333. if len(reqPorts) == 0 {
  334. return nil
  335. }
  336. var (
  337. services []*api.Service
  338. err error
  339. )
  340. s.store.View(func(tx store.ReadTx) {
  341. services, err = store.FindServices(tx, store.All)
  342. })
  343. if err != nil {
  344. return err
  345. }
  346. for _, service := range services {
  347. // If service ID is the same (and not "") then this is an update
  348. if serviceID != "" && serviceID == service.ID {
  349. continue
  350. }
  351. if service.Spec.Endpoint != nil {
  352. for _, pc := range service.Spec.Endpoint.Ports {
  353. if reqPorts[pcToString(pc)] {
  354. return grpc.Errorf(codes.InvalidArgument, "port '%d' is already in use by service '%s' (%s)", pc.PublishedPort, service.Spec.Annotations.Name, service.ID)
  355. }
  356. }
  357. }
  358. if service.Endpoint != nil {
  359. for _, pc := range service.Endpoint.Ports {
  360. if reqPorts[pcToString(pc)] {
  361. return grpc.Errorf(codes.InvalidArgument, "port '%d' is already in use by service '%s' (%s)", pc.PublishedPort, service.Spec.Annotations.Name, service.ID)
  362. }
  363. }
  364. }
  365. }
  366. return nil
  367. }
  368. // checkSecretExistence finds if the secret exists
  369. func (s *Server) checkSecretExistence(tx store.Tx, spec *api.ServiceSpec) error {
  370. container := spec.Task.GetContainer()
  371. if container == nil {
  372. return nil
  373. }
  374. var failedSecrets []string
  375. for _, secretRef := range container.Secrets {
  376. secret := store.GetSecret(tx, secretRef.SecretID)
  377. // Check to see if the secret exists and secretRef.SecretName matches the actual secretName
  378. if secret == nil || secret.Spec.Annotations.Name != secretRef.SecretName {
  379. failedSecrets = append(failedSecrets, secretRef.SecretName)
  380. }
  381. }
  382. if len(failedSecrets) > 0 {
  383. secretStr := "secrets"
  384. if len(failedSecrets) == 1 {
  385. secretStr = "secret"
  386. }
  387. return grpc.Errorf(codes.InvalidArgument, "%s not found: %v", secretStr, strings.Join(failedSecrets, ", "))
  388. }
  389. return nil
  390. }
  391. // CreateService creates and returns a Service based on the provided ServiceSpec.
  392. // - Returns `InvalidArgument` if the ServiceSpec is malformed.
  393. // - Returns `Unimplemented` if the ServiceSpec references unimplemented features.
  394. // - Returns `AlreadyExists` if the ServiceID conflicts.
  395. // - Returns an error if the creation fails.
  396. func (s *Server) CreateService(ctx context.Context, request *api.CreateServiceRequest) (*api.CreateServiceResponse, error) {
  397. if err := validateServiceSpec(request.Spec); err != nil {
  398. return nil, err
  399. }
  400. if err := s.validateNetworks(request.Spec.Networks); err != nil {
  401. return nil, err
  402. }
  403. if err := s.checkPortConflicts(request.Spec, ""); err != nil {
  404. return nil, err
  405. }
  406. // TODO(aluzzardi): Consider using `Name` as a primary key to handle
  407. // duplicate creations. See #65
  408. service := &api.Service{
  409. ID: identity.NewID(),
  410. Spec: *request.Spec,
  411. SpecVersion: &api.Version{},
  412. }
  413. if allocator.IsIngressNetworkNeeded(service) {
  414. if _, err := allocator.GetIngressNetwork(s.store); err == allocator.ErrNoIngress {
  415. return nil, grpc.Errorf(codes.FailedPrecondition, "service needs ingress network, but no ingress network is present")
  416. }
  417. }
  418. err := s.store.Update(func(tx store.Tx) error {
  419. // Check to see if all the secrets being added exist as objects
  420. // in our datastore
  421. err := s.checkSecretExistence(tx, request.Spec)
  422. if err != nil {
  423. return err
  424. }
  425. return store.CreateService(tx, service)
  426. })
  427. if err != nil {
  428. return nil, err
  429. }
  430. return &api.CreateServiceResponse{
  431. Service: service,
  432. }, nil
  433. }
  434. // GetService returns a Service given a ServiceID.
  435. // - Returns `InvalidArgument` if ServiceID is not provided.
  436. // - Returns `NotFound` if the Service is not found.
  437. func (s *Server) GetService(ctx context.Context, request *api.GetServiceRequest) (*api.GetServiceResponse, error) {
  438. if request.ServiceID == "" {
  439. return nil, grpc.Errorf(codes.InvalidArgument, errInvalidArgument.Error())
  440. }
  441. var service *api.Service
  442. s.store.View(func(tx store.ReadTx) {
  443. service = store.GetService(tx, request.ServiceID)
  444. })
  445. if service == nil {
  446. return nil, grpc.Errorf(codes.NotFound, "service %s not found", request.ServiceID)
  447. }
  448. return &api.GetServiceResponse{
  449. Service: service,
  450. }, nil
  451. }
  452. // UpdateService updates a Service referenced by ServiceID with the given ServiceSpec.
  453. // - Returns `NotFound` if the Service is not found.
  454. // - Returns `InvalidArgument` if the ServiceSpec is malformed.
  455. // - Returns `Unimplemented` if the ServiceSpec references unimplemented features.
  456. // - Returns an error if the update fails.
  457. func (s *Server) UpdateService(ctx context.Context, request *api.UpdateServiceRequest) (*api.UpdateServiceResponse, error) {
  458. if request.ServiceID == "" || request.ServiceVersion == nil {
  459. return nil, grpc.Errorf(codes.InvalidArgument, errInvalidArgument.Error())
  460. }
  461. if err := validateServiceSpec(request.Spec); err != nil {
  462. return nil, err
  463. }
  464. var service *api.Service
  465. s.store.View(func(tx store.ReadTx) {
  466. service = store.GetService(tx, request.ServiceID)
  467. })
  468. if service == nil {
  469. return nil, grpc.Errorf(codes.NotFound, "service %s not found", request.ServiceID)
  470. }
  471. if request.Spec.Endpoint != nil && !reflect.DeepEqual(request.Spec.Endpoint, service.Spec.Endpoint) {
  472. if err := s.checkPortConflicts(request.Spec, request.ServiceID); err != nil {
  473. return nil, err
  474. }
  475. }
  476. err := s.store.Update(func(tx store.Tx) error {
  477. service = store.GetService(tx, request.ServiceID)
  478. if service == nil {
  479. return grpc.Errorf(codes.NotFound, "service %s not found", request.ServiceID)
  480. }
  481. // It's not okay to update Service.Spec.Networks on its own.
  482. // However, if Service.Spec.Task.Networks is also being
  483. // updated, that's okay (for example when migrating from the
  484. // deprecated Spec.Networks field to Spec.Task.Networks).
  485. if (len(request.Spec.Networks) != 0 || len(service.Spec.Networks) != 0) &&
  486. !reflect.DeepEqual(request.Spec.Networks, service.Spec.Networks) &&
  487. reflect.DeepEqual(request.Spec.Task.Networks, service.Spec.Task.Networks) {
  488. return grpc.Errorf(codes.Unimplemented, errNetworkUpdateNotSupported.Error())
  489. }
  490. // Check to see if all the secrets being added exist as objects
  491. // in our datastore
  492. err := s.checkSecretExistence(tx, request.Spec)
  493. if err != nil {
  494. return err
  495. }
  496. // orchestrator is designed to be stateless, so it should not deal
  497. // with service mode change (comparing current config with previous config).
  498. // proper way to change service mode is to delete and re-add.
  499. if reflect.TypeOf(service.Spec.Mode) != reflect.TypeOf(request.Spec.Mode) {
  500. return grpc.Errorf(codes.Unimplemented, errModeChangeNotAllowed.Error())
  501. }
  502. if service.Spec.Annotations.Name != request.Spec.Annotations.Name {
  503. return grpc.Errorf(codes.Unimplemented, errRenameNotSupported.Error())
  504. }
  505. service.Meta.Version = *request.ServiceVersion
  506. if request.Rollback == api.UpdateServiceRequest_PREVIOUS {
  507. if service.PreviousSpec == nil {
  508. return grpc.Errorf(codes.FailedPrecondition, "service %s does not have a previous spec", request.ServiceID)
  509. }
  510. curSpec := service.Spec.Copy()
  511. curSpecVersion := service.SpecVersion
  512. service.Spec = *service.PreviousSpec.Copy()
  513. service.SpecVersion = service.PreviousSpecVersion.Copy()
  514. service.PreviousSpec = curSpec
  515. service.PreviousSpecVersion = curSpecVersion
  516. service.UpdateStatus = &api.UpdateStatus{
  517. State: api.UpdateStatus_ROLLBACK_STARTED,
  518. Message: "manually requested rollback",
  519. StartedAt: ptypes.MustTimestampProto(time.Now()),
  520. }
  521. } else {
  522. service.PreviousSpec = service.Spec.Copy()
  523. service.PreviousSpecVersion = service.SpecVersion
  524. service.Spec = *request.Spec.Copy()
  525. // Set spec version. Note that this will not match the
  526. // service's Meta.Version after the store update. The
  527. // versionsfor the spec and the service itself are not
  528. // meant to be directly comparable.
  529. service.SpecVersion = service.Meta.Version.Copy()
  530. // Reset update status
  531. service.UpdateStatus = nil
  532. }
  533. if allocator.IsIngressNetworkNeeded(service) {
  534. if _, err := allocator.GetIngressNetwork(s.store); err == allocator.ErrNoIngress {
  535. return grpc.Errorf(codes.FailedPrecondition, "service needs ingress network, but no ingress network is present")
  536. }
  537. }
  538. return store.UpdateService(tx, service)
  539. })
  540. if err != nil {
  541. return nil, err
  542. }
  543. return &api.UpdateServiceResponse{
  544. Service: service,
  545. }, nil
  546. }
  547. // RemoveService removes a Service referenced by ServiceID.
  548. // - Returns `InvalidArgument` if ServiceID is not provided.
  549. // - Returns `NotFound` if the Service is not found.
  550. // - Returns an error if the deletion fails.
  551. func (s *Server) RemoveService(ctx context.Context, request *api.RemoveServiceRequest) (*api.RemoveServiceResponse, error) {
  552. if request.ServiceID == "" {
  553. return nil, grpc.Errorf(codes.InvalidArgument, errInvalidArgument.Error())
  554. }
  555. err := s.store.Update(func(tx store.Tx) error {
  556. return store.DeleteService(tx, request.ServiceID)
  557. })
  558. if err != nil {
  559. if err == store.ErrNotExist {
  560. return nil, grpc.Errorf(codes.NotFound, "service %s not found", request.ServiceID)
  561. }
  562. return nil, err
  563. }
  564. return &api.RemoveServiceResponse{}, nil
  565. }
  566. func filterServices(candidates []*api.Service, filters ...func(*api.Service) bool) []*api.Service {
  567. result := []*api.Service{}
  568. for _, c := range candidates {
  569. match := true
  570. for _, f := range filters {
  571. if !f(c) {
  572. match = false
  573. break
  574. }
  575. }
  576. if match {
  577. result = append(result, c)
  578. }
  579. }
  580. return result
  581. }
  582. // ListServices returns a list of all services.
  583. func (s *Server) ListServices(ctx context.Context, request *api.ListServicesRequest) (*api.ListServicesResponse, error) {
  584. var (
  585. services []*api.Service
  586. err error
  587. )
  588. s.store.View(func(tx store.ReadTx) {
  589. switch {
  590. case request.Filters != nil && len(request.Filters.Names) > 0:
  591. services, err = store.FindServices(tx, buildFilters(store.ByName, request.Filters.Names))
  592. case request.Filters != nil && len(request.Filters.NamePrefixes) > 0:
  593. services, err = store.FindServices(tx, buildFilters(store.ByNamePrefix, request.Filters.NamePrefixes))
  594. case request.Filters != nil && len(request.Filters.IDPrefixes) > 0:
  595. services, err = store.FindServices(tx, buildFilters(store.ByIDPrefix, request.Filters.IDPrefixes))
  596. case request.Filters != nil && len(request.Filters.Runtimes) > 0:
  597. services, err = store.FindServices(tx, buildFilters(store.ByRuntime, request.Filters.Runtimes))
  598. default:
  599. services, err = store.FindServices(tx, store.All)
  600. }
  601. })
  602. if err != nil {
  603. return nil, err
  604. }
  605. if request.Filters != nil {
  606. services = filterServices(services,
  607. func(e *api.Service) bool {
  608. return filterContains(e.Spec.Annotations.Name, request.Filters.Names)
  609. },
  610. func(e *api.Service) bool {
  611. return filterContainsPrefix(e.Spec.Annotations.Name, request.Filters.NamePrefixes)
  612. },
  613. func(e *api.Service) bool {
  614. return filterContainsPrefix(e.ID, request.Filters.IDPrefixes)
  615. },
  616. func(e *api.Service) bool {
  617. return filterMatchLabels(e.Spec.Annotations.Labels, request.Filters.Labels)
  618. },
  619. func(e *api.Service) bool {
  620. if len(request.Filters.Runtimes) == 0 {
  621. return true
  622. }
  623. r, err := naming.Runtime(e.Spec.Task)
  624. if err != nil {
  625. return false
  626. }
  627. return filterContains(r, request.Filters.Runtimes)
  628. },
  629. )
  630. }
  631. return &api.ListServicesResponse{
  632. Services: services,
  633. }, nil
  634. }