update_test.go 13 KB

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