update_test.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382
  1. package service // import "github.com/docker/docker/integration/service"
  2. import (
  3. "context"
  4. "testing"
  5. "github.com/docker/docker/api/types"
  6. "github.com/docker/docker/api/types/filters"
  7. swarmtypes "github.com/docker/docker/api/types/swarm"
  8. "github.com/docker/docker/api/types/versions"
  9. "github.com/docker/docker/client"
  10. "github.com/docker/docker/integration/internal/network"
  11. "github.com/docker/docker/integration/internal/swarm"
  12. "gotest.tools/v3/assert"
  13. is "gotest.tools/v3/assert/cmp"
  14. "gotest.tools/v3/poll"
  15. "gotest.tools/v3/skip"
  16. )
  17. func TestServiceUpdateLabel(t *testing.T) {
  18. skip.If(t, testEnv.DaemonInfo.OSType != "linux")
  19. defer setupTest(t)()
  20. d := swarm.NewSwarm(t, testEnv)
  21. defer d.Stop(t)
  22. cli := d.NewClientT(t)
  23. defer cli.Close()
  24. ctx := context.Background()
  25. serviceName := "TestService_" + t.Name()
  26. serviceID := swarm.CreateService(t, d, swarm.ServiceWithName(serviceName))
  27. service := getService(t, cli, serviceID)
  28. assert.Check(t, is.DeepEqual(service.Spec.Labels, map[string]string{}))
  29. // add label to empty set
  30. service.Spec.Labels["foo"] = "bar"
  31. _, err := cli.ServiceUpdate(ctx, serviceID, service.Version, service.Spec, types.ServiceUpdateOptions{})
  32. assert.NilError(t, err)
  33. poll.WaitOn(t, serviceSpecIsUpdated(cli, serviceID, service.Version.Index), swarm.ServicePoll)
  34. service = getService(t, cli, serviceID)
  35. assert.Check(t, is.DeepEqual(service.Spec.Labels, map[string]string{"foo": "bar"}))
  36. // add label to non-empty set
  37. service.Spec.Labels["foo2"] = "bar"
  38. _, err = cli.ServiceUpdate(ctx, serviceID, service.Version, service.Spec, types.ServiceUpdateOptions{})
  39. assert.NilError(t, err)
  40. poll.WaitOn(t, serviceSpecIsUpdated(cli, serviceID, service.Version.Index), swarm.ServicePoll)
  41. service = getService(t, cli, serviceID)
  42. assert.Check(t, is.DeepEqual(service.Spec.Labels, map[string]string{"foo": "bar", "foo2": "bar"}))
  43. delete(service.Spec.Labels, "foo2")
  44. _, err = cli.ServiceUpdate(ctx, serviceID, service.Version, service.Spec, types.ServiceUpdateOptions{})
  45. assert.NilError(t, err)
  46. poll.WaitOn(t, serviceSpecIsUpdated(cli, serviceID, service.Version.Index), swarm.ServicePoll)
  47. service = getService(t, cli, serviceID)
  48. assert.Check(t, is.DeepEqual(service.Spec.Labels, map[string]string{"foo": "bar"}))
  49. delete(service.Spec.Labels, "foo")
  50. _, err = cli.ServiceUpdate(ctx, serviceID, service.Version, service.Spec, types.ServiceUpdateOptions{})
  51. assert.NilError(t, err)
  52. poll.WaitOn(t, serviceSpecIsUpdated(cli, serviceID, service.Version.Index), swarm.ServicePoll)
  53. service = getService(t, cli, serviceID)
  54. assert.Check(t, is.DeepEqual(service.Spec.Labels, map[string]string{}))
  55. // now make sure we can add again
  56. service.Spec.Labels["foo"] = "bar"
  57. _, err = cli.ServiceUpdate(ctx, serviceID, service.Version, service.Spec, types.ServiceUpdateOptions{})
  58. assert.NilError(t, err)
  59. poll.WaitOn(t, serviceSpecIsUpdated(cli, serviceID, service.Version.Index), swarm.ServicePoll)
  60. service = getService(t, cli, serviceID)
  61. assert.Check(t, is.DeepEqual(service.Spec.Labels, map[string]string{"foo": "bar"}))
  62. err = cli.ServiceRemove(context.Background(), serviceID)
  63. assert.NilError(t, err)
  64. }
  65. func TestServiceUpdateSecrets(t *testing.T) {
  66. skip.If(t, testEnv.DaemonInfo.OSType != "linux")
  67. defer setupTest(t)()
  68. d := swarm.NewSwarm(t, testEnv)
  69. defer d.Stop(t)
  70. cli := d.NewClientT(t)
  71. defer cli.Close()
  72. ctx := context.Background()
  73. secretName := "TestSecret_" + t.Name()
  74. secretTarget := "targetName"
  75. resp, err := cli.SecretCreate(ctx, swarmtypes.SecretSpec{
  76. Annotations: swarmtypes.Annotations{
  77. Name: secretName,
  78. },
  79. Data: []byte("TESTINGDATA"),
  80. })
  81. assert.NilError(t, err)
  82. assert.Check(t, resp.ID != "")
  83. serviceName := "TestService_" + t.Name()
  84. serviceID := swarm.CreateService(t, d, swarm.ServiceWithName(serviceName))
  85. service := getService(t, cli, serviceID)
  86. // add secret
  87. service.Spec.TaskTemplate.ContainerSpec.Secrets = append(service.Spec.TaskTemplate.ContainerSpec.Secrets,
  88. &swarmtypes.SecretReference{
  89. File: &swarmtypes.SecretReferenceFileTarget{
  90. Name: secretTarget,
  91. UID: "0",
  92. GID: "0",
  93. Mode: 0o600,
  94. },
  95. SecretID: resp.ID,
  96. SecretName: secretName,
  97. },
  98. )
  99. _, err = cli.ServiceUpdate(ctx, serviceID, service.Version, service.Spec, types.ServiceUpdateOptions{})
  100. assert.NilError(t, err)
  101. poll.WaitOn(t, serviceIsUpdated(cli, serviceID), swarm.ServicePoll)
  102. service = getService(t, cli, serviceID)
  103. secrets := service.Spec.TaskTemplate.ContainerSpec.Secrets
  104. assert.Assert(t, is.Equal(1, len(secrets)))
  105. secret := *secrets[0]
  106. assert.Check(t, is.Equal(secretName, secret.SecretName))
  107. assert.Check(t, nil != secret.File)
  108. assert.Check(t, is.Equal(secretTarget, secret.File.Name))
  109. // remove
  110. service.Spec.TaskTemplate.ContainerSpec.Secrets = []*swarmtypes.SecretReference{}
  111. _, err = cli.ServiceUpdate(ctx, serviceID, service.Version, service.Spec, types.ServiceUpdateOptions{})
  112. assert.NilError(t, err)
  113. poll.WaitOn(t, serviceIsUpdated(cli, serviceID), swarm.ServicePoll)
  114. service = getService(t, cli, serviceID)
  115. assert.Check(t, is.Equal(0, len(service.Spec.TaskTemplate.ContainerSpec.Secrets)))
  116. err = cli.ServiceRemove(context.Background(), serviceID)
  117. assert.NilError(t, err)
  118. }
  119. func TestServiceUpdateConfigs(t *testing.T) {
  120. skip.If(t, testEnv.DaemonInfo.OSType != "linux")
  121. defer setupTest(t)()
  122. d := swarm.NewSwarm(t, testEnv)
  123. defer d.Stop(t)
  124. cli := d.NewClientT(t)
  125. defer cli.Close()
  126. ctx := context.Background()
  127. configName := "TestConfig_" + t.Name()
  128. configTarget := "targetName"
  129. resp, err := cli.ConfigCreate(ctx, swarmtypes.ConfigSpec{
  130. Annotations: swarmtypes.Annotations{
  131. Name: configName,
  132. },
  133. Data: []byte("TESTINGDATA"),
  134. })
  135. assert.NilError(t, err)
  136. assert.Check(t, resp.ID != "")
  137. serviceName := "TestService_" + t.Name()
  138. serviceID := swarm.CreateService(t, d, swarm.ServiceWithName(serviceName))
  139. service := getService(t, cli, serviceID)
  140. // add config
  141. service.Spec.TaskTemplate.ContainerSpec.Configs = append(service.Spec.TaskTemplate.ContainerSpec.Configs,
  142. &swarmtypes.ConfigReference{
  143. File: &swarmtypes.ConfigReferenceFileTarget{
  144. Name: configTarget,
  145. UID: "0",
  146. GID: "0",
  147. Mode: 0o600,
  148. },
  149. ConfigID: resp.ID,
  150. ConfigName: configName,
  151. },
  152. )
  153. _, err = cli.ServiceUpdate(ctx, serviceID, service.Version, service.Spec, types.ServiceUpdateOptions{})
  154. assert.NilError(t, err)
  155. poll.WaitOn(t, serviceIsUpdated(cli, serviceID), swarm.ServicePoll)
  156. service = getService(t, cli, serviceID)
  157. configs := service.Spec.TaskTemplate.ContainerSpec.Configs
  158. assert.Assert(t, is.Equal(1, len(configs)))
  159. config := *configs[0]
  160. assert.Check(t, is.Equal(configName, config.ConfigName))
  161. assert.Check(t, nil != config.File)
  162. assert.Check(t, is.Equal(configTarget, config.File.Name))
  163. // remove
  164. service.Spec.TaskTemplate.ContainerSpec.Configs = []*swarmtypes.ConfigReference{}
  165. _, err = cli.ServiceUpdate(ctx, serviceID, service.Version, service.Spec, types.ServiceUpdateOptions{})
  166. assert.NilError(t, err)
  167. poll.WaitOn(t, serviceIsUpdated(cli, serviceID), swarm.ServicePoll)
  168. service = getService(t, cli, serviceID)
  169. assert.Check(t, is.Equal(0, len(service.Spec.TaskTemplate.ContainerSpec.Configs)))
  170. err = cli.ServiceRemove(context.Background(), serviceID)
  171. assert.NilError(t, err)
  172. }
  173. func TestServiceUpdateNetwork(t *testing.T) {
  174. skip.If(t, testEnv.DaemonInfo.OSType != "linux")
  175. defer setupTest(t)()
  176. d := swarm.NewSwarm(t, testEnv)
  177. defer d.Stop(t)
  178. cli := d.NewClientT(t)
  179. defer cli.Close()
  180. ctx := context.Background()
  181. // Create a overlay network
  182. testNet := "testNet" + t.Name()
  183. overlayID := network.CreateNoError(ctx, t, cli, testNet,
  184. network.WithDriver("overlay"))
  185. var instances uint64 = 1
  186. // Create service with the overlay network
  187. serviceName := "TestServiceUpdateNetworkRM_" + t.Name()
  188. serviceID := swarm.CreateService(t, d,
  189. swarm.ServiceWithReplicas(instances),
  190. swarm.ServiceWithName(serviceName),
  191. swarm.ServiceWithNetwork(testNet))
  192. poll.WaitOn(t, swarm.RunningTasksCount(cli, serviceID, instances), swarm.ServicePoll)
  193. service := getService(t, cli, serviceID)
  194. netInfo, err := cli.NetworkInspect(ctx, testNet, types.NetworkInspectOptions{
  195. Verbose: true,
  196. Scope: "swarm",
  197. })
  198. assert.NilError(t, err)
  199. assert.Assert(t, len(netInfo.Containers) == 2, "Expected 2 endpoints, one for container and one for LB Sandbox")
  200. // Remove network from service
  201. service.Spec.TaskTemplate.Networks = []swarmtypes.NetworkAttachmentConfig{}
  202. _, err = cli.ServiceUpdate(ctx, serviceID, service.Version, service.Spec, types.ServiceUpdateOptions{})
  203. assert.NilError(t, err)
  204. poll.WaitOn(t, serviceIsUpdated(cli, serviceID), swarm.ServicePoll)
  205. netInfo, err = cli.NetworkInspect(ctx, testNet, types.NetworkInspectOptions{
  206. Verbose: true,
  207. Scope: "swarm",
  208. })
  209. assert.NilError(t, err)
  210. assert.Assert(t, len(netInfo.Containers) == 0, "Load balancing endpoint still exists in network")
  211. err = cli.NetworkRemove(ctx, overlayID)
  212. assert.NilError(t, err)
  213. err = cli.ServiceRemove(ctx, serviceID)
  214. assert.NilError(t, err)
  215. }
  216. // TestServiceUpdatePidsLimit tests creating and updating a service with PidsLimit
  217. func TestServiceUpdatePidsLimit(t *testing.T) {
  218. skip.If(
  219. t, versions.LessThan(testEnv.DaemonAPIVersion(), "1.41"),
  220. "setting pidslimit for services is not supported before api v1.41",
  221. )
  222. skip.If(t, testEnv.DaemonInfo.OSType != "linux")
  223. tests := []struct {
  224. name string
  225. pidsLimit int64
  226. expected int64
  227. }{
  228. {
  229. name: "create service with PidsLimit 300",
  230. pidsLimit: 300,
  231. expected: 300,
  232. },
  233. {
  234. name: "unset PidsLimit to 0",
  235. pidsLimit: 0,
  236. expected: 0,
  237. },
  238. {
  239. name: "update PidsLimit to 100",
  240. pidsLimit: 100,
  241. expected: 100,
  242. },
  243. }
  244. defer setupTest(t)()
  245. d := swarm.NewSwarm(t, testEnv)
  246. defer d.Stop(t)
  247. cli := d.NewClientT(t)
  248. defer func() { _ = cli.Close() }()
  249. ctx := context.Background()
  250. var (
  251. serviceID string
  252. service swarmtypes.Service
  253. )
  254. for i, tc := range tests {
  255. t.Run(tc.name, func(t *testing.T) {
  256. if i == 0 {
  257. serviceID = swarm.CreateService(t, d, swarm.ServiceWithPidsLimit(tc.pidsLimit))
  258. } else {
  259. service = getService(t, cli, serviceID)
  260. if service.Spec.TaskTemplate.Resources == nil {
  261. service.Spec.TaskTemplate.Resources = &swarmtypes.ResourceRequirements{}
  262. }
  263. if service.Spec.TaskTemplate.Resources.Limits == nil {
  264. service.Spec.TaskTemplate.Resources.Limits = &swarmtypes.Limit{}
  265. }
  266. service.Spec.TaskTemplate.Resources.Limits.Pids = tc.pidsLimit
  267. _, err := cli.ServiceUpdate(ctx, serviceID, service.Version, service.Spec, types.ServiceUpdateOptions{})
  268. assert.NilError(t, err)
  269. poll.WaitOn(t, serviceIsUpdated(cli, serviceID), swarm.ServicePoll)
  270. }
  271. poll.WaitOn(t, swarm.RunningTasksCount(cli, serviceID, 1), swarm.ServicePoll)
  272. service = getService(t, cli, serviceID)
  273. container := getServiceTaskContainer(ctx, t, cli, serviceID)
  274. assert.Equal(t, service.Spec.TaskTemplate.Resources.Limits.Pids, tc.expected)
  275. if tc.expected == 0 {
  276. if container.HostConfig.Resources.PidsLimit != nil {
  277. t.Fatalf("Expected container.HostConfig.Resources.PidsLimit to be nil")
  278. }
  279. } else {
  280. assert.Assert(t, container.HostConfig.Resources.PidsLimit != nil)
  281. assert.Equal(t, *container.HostConfig.Resources.PidsLimit, tc.expected)
  282. }
  283. })
  284. }
  285. err := cli.ServiceRemove(ctx, serviceID)
  286. assert.NilError(t, err)
  287. }
  288. func getServiceTaskContainer(ctx context.Context, t *testing.T, cli client.APIClient, serviceID string) types.ContainerJSON {
  289. t.Helper()
  290. tasks, err := cli.TaskList(ctx, types.TaskListOptions{
  291. Filters: filters.NewArgs(
  292. filters.Arg("service", serviceID),
  293. filters.Arg("desired-state", "running"),
  294. ),
  295. })
  296. assert.NilError(t, err)
  297. assert.Assert(t, len(tasks) > 0)
  298. ctr, err := cli.ContainerInspect(ctx, tasks[0].Status.ContainerStatus.ContainerID)
  299. assert.NilError(t, err)
  300. assert.Equal(t, ctr.State.Running, true)
  301. return ctr
  302. }
  303. func getService(t *testing.T, cli client.ServiceAPIClient, serviceID string) swarmtypes.Service {
  304. t.Helper()
  305. service, _, err := cli.ServiceInspectWithRaw(context.Background(), serviceID, types.ServiceInspectOptions{})
  306. assert.NilError(t, err)
  307. return service
  308. }
  309. func serviceIsUpdated(client client.ServiceAPIClient, serviceID string) func(log poll.LogT) poll.Result {
  310. return func(log poll.LogT) poll.Result {
  311. service, _, err := client.ServiceInspectWithRaw(context.Background(), serviceID, types.ServiceInspectOptions{})
  312. switch {
  313. case err != nil:
  314. return poll.Error(err)
  315. case service.UpdateStatus != nil && service.UpdateStatus.State == swarmtypes.UpdateStateCompleted:
  316. return poll.Success()
  317. default:
  318. if service.UpdateStatus != nil {
  319. return poll.Continue("waiting for service %s to be updated, state: %s, message: %s", serviceID, service.UpdateStatus.State, service.UpdateStatus.Message)
  320. }
  321. return poll.Continue("waiting for service %s to be updated", serviceID)
  322. }
  323. }
  324. }
  325. func serviceSpecIsUpdated(client client.ServiceAPIClient, serviceID string, serviceOldVersion uint64) func(log poll.LogT) poll.Result {
  326. return func(log poll.LogT) poll.Result {
  327. service, _, err := client.ServiceInspectWithRaw(context.Background(), serviceID, types.ServiceInspectOptions{})
  328. switch {
  329. case err != nil:
  330. return poll.Error(err)
  331. case service.Version.Index > serviceOldVersion:
  332. return poll.Success()
  333. default:
  334. return poll.Continue("waiting for service %s to be updated", serviceID)
  335. }
  336. }
  337. }