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