github.com/kaisenlinux/docker.io@v0.0.0-20230510090727-ea55db55fac7/engine/integration/service/update_test.go (about) 1 package service // import "github.com/docker/docker/integration/service" 2 3 import ( 4 "context" 5 "testing" 6 7 "github.com/docker/docker/api/types" 8 "github.com/docker/docker/api/types/filters" 9 swarmtypes "github.com/docker/docker/api/types/swarm" 10 "github.com/docker/docker/api/types/versions" 11 "github.com/docker/docker/client" 12 "github.com/docker/docker/integration/internal/network" 13 "github.com/docker/docker/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 if service.Spec.TaskTemplate.Resources == nil { 299 service.Spec.TaskTemplate.Resources = &swarmtypes.ResourceRequirements{} 300 } 301 if service.Spec.TaskTemplate.Resources.Limits == nil { 302 service.Spec.TaskTemplate.Resources.Limits = &swarmtypes.Limit{} 303 } 304 service.Spec.TaskTemplate.Resources.Limits.Pids = tc.pidsLimit 305 _, err := cli.ServiceUpdate(ctx, serviceID, service.Version, service.Spec, types.ServiceUpdateOptions{}) 306 assert.NilError(t, err) 307 poll.WaitOn(t, serviceIsUpdated(cli, serviceID), swarm.ServicePoll) 308 } 309 310 poll.WaitOn(t, swarm.RunningTasksCount(cli, serviceID, 1), swarm.ServicePoll) 311 service = getService(t, cli, serviceID) 312 container := getServiceTaskContainer(ctx, t, cli, serviceID) 313 assert.Equal(t, service.Spec.TaskTemplate.Resources.Limits.Pids, tc.expected) 314 if tc.expected == 0 { 315 if container.HostConfig.Resources.PidsLimit != nil { 316 t.Fatalf("Expected container.HostConfig.Resources.PidsLimit to be nil") 317 } 318 } else { 319 assert.Assert(t, container.HostConfig.Resources.PidsLimit != nil) 320 assert.Equal(t, *container.HostConfig.Resources.PidsLimit, tc.expected) 321 } 322 }) 323 } 324 325 err := cli.ServiceRemove(ctx, serviceID) 326 assert.NilError(t, err) 327 } 328 329 func getServiceTaskContainer(ctx context.Context, t *testing.T, cli client.APIClient, serviceID string) types.ContainerJSON { 330 t.Helper() 331 filter := filters.NewArgs() 332 filter.Add("service", serviceID) 333 filter.Add("desired-state", "running") 334 tasks, err := cli.TaskList(ctx, types.TaskListOptions{Filters: filter}) 335 assert.NilError(t, err) 336 assert.Assert(t, len(tasks) > 0) 337 338 ctr, err := cli.ContainerInspect(ctx, tasks[0].Status.ContainerStatus.ContainerID) 339 assert.NilError(t, err) 340 assert.Equal(t, ctr.State.Running, true) 341 return ctr 342 } 343 344 func getService(t *testing.T, cli client.ServiceAPIClient, serviceID string) swarmtypes.Service { 345 t.Helper() 346 service, _, err := cli.ServiceInspectWithRaw(context.Background(), serviceID, types.ServiceInspectOptions{}) 347 assert.NilError(t, err) 348 return service 349 } 350 351 func serviceIsUpdated(client client.ServiceAPIClient, serviceID string) func(log poll.LogT) poll.Result { 352 return func(log poll.LogT) poll.Result { 353 service, _, err := client.ServiceInspectWithRaw(context.Background(), serviceID, types.ServiceInspectOptions{}) 354 switch { 355 case err != nil: 356 return poll.Error(err) 357 case service.UpdateStatus != nil && service.UpdateStatus.State == swarmtypes.UpdateStateCompleted: 358 return poll.Success() 359 default: 360 if service.UpdateStatus != nil { 361 return poll.Continue("waiting for service %s to be updated, state: %s, message: %s", serviceID, service.UpdateStatus.State, service.UpdateStatus.Message) 362 } 363 return poll.Continue("waiting for service %s to be updated", serviceID) 364 } 365 } 366 } 367 368 func serviceSpecIsUpdated(client client.ServiceAPIClient, serviceID string, serviceOldVersion uint64) func(log poll.LogT) poll.Result { 369 return func(log poll.LogT) poll.Result { 370 service, _, err := client.ServiceInspectWithRaw(context.Background(), serviceID, types.ServiceInspectOptions{}) 371 switch { 372 case err != nil: 373 return poll.Error(err) 374 case service.Version.Index > serviceOldVersion: 375 return poll.Success() 376 default: 377 return poll.Continue("waiting for service %s to be updated", serviceID) 378 } 379 } 380 }