github.com/Prakhar-Agarwal-byte/moby@v0.0.0-20231027092010-a14e3e8ab87e/integration/service/update_test.go (about) 1 package service // import "github.com/Prakhar-Agarwal-byte/moby/integration/service" 2 3 import ( 4 "context" 5 "testing" 6 7 "github.com/Prakhar-Agarwal-byte/moby/api/types" 8 "github.com/Prakhar-Agarwal-byte/moby/api/types/filters" 9 swarmtypes "github.com/Prakhar-Agarwal-byte/moby/api/types/swarm" 10 "github.com/Prakhar-Agarwal-byte/moby/api/types/versions" 11 "github.com/Prakhar-Agarwal-byte/moby/client" 12 "github.com/Prakhar-Agarwal-byte/moby/integration/internal/network" 13 "github.com/Prakhar-Agarwal-byte/moby/integration/internal/swarm" 14 "github.com/Prakhar-Agarwal-byte/moby/testutil" 15 "gotest.tools/v3/assert" 16 is "gotest.tools/v3/assert/cmp" 17 "gotest.tools/v3/poll" 18 "gotest.tools/v3/skip" 19 ) 20 21 func TestServiceUpdateLabel(t *testing.T) { 22 skip.If(t, testEnv.DaemonInfo.OSType != "linux") 23 ctx := setupTest(t) 24 25 d := swarm.NewSwarm(ctx, t, testEnv) 26 defer d.Stop(t) 27 cli := d.NewClientT(t) 28 defer cli.Close() 29 30 serviceName := "TestService_" + t.Name() 31 serviceID := swarm.CreateService(ctx, t, d, swarm.ServiceWithName(serviceName)) 32 service := getService(ctx, t, cli, serviceID) 33 assert.Check(t, is.DeepEqual(service.Spec.Labels, map[string]string{})) 34 35 // add label to empty set 36 service.Spec.Labels["foo"] = "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"})) 42 43 // add label to non-empty set 44 service.Spec.Labels["foo2"] = "bar" 45 _, err = cli.ServiceUpdate(ctx, serviceID, service.Version, service.Spec, types.ServiceUpdateOptions{}) 46 assert.NilError(t, err) 47 poll.WaitOn(t, serviceSpecIsUpdated(ctx, cli, serviceID, service.Version.Index), swarm.ServicePoll) 48 service = getService(ctx, t, cli, serviceID) 49 assert.Check(t, is.DeepEqual(service.Spec.Labels, map[string]string{"foo": "bar", "foo2": "bar"})) 50 51 delete(service.Spec.Labels, "foo2") 52 _, err = cli.ServiceUpdate(ctx, serviceID, service.Version, service.Spec, types.ServiceUpdateOptions{}) 53 assert.NilError(t, err) 54 poll.WaitOn(t, serviceSpecIsUpdated(ctx, cli, serviceID, service.Version.Index), swarm.ServicePoll) 55 service = getService(ctx, t, cli, serviceID) 56 assert.Check(t, is.DeepEqual(service.Spec.Labels, map[string]string{"foo": "bar"})) 57 58 delete(service.Spec.Labels, "foo") 59 _, err = cli.ServiceUpdate(ctx, serviceID, service.Version, service.Spec, types.ServiceUpdateOptions{}) 60 assert.NilError(t, err) 61 poll.WaitOn(t, serviceSpecIsUpdated(ctx, cli, serviceID, service.Version.Index), swarm.ServicePoll) 62 service = getService(ctx, t, cli, serviceID) 63 assert.Check(t, is.DeepEqual(service.Spec.Labels, map[string]string{})) 64 65 // now make sure we can add again 66 service.Spec.Labels["foo"] = "bar" 67 _, err = cli.ServiceUpdate(ctx, serviceID, service.Version, service.Spec, types.ServiceUpdateOptions{}) 68 assert.NilError(t, err) 69 poll.WaitOn(t, serviceSpecIsUpdated(ctx, cli, serviceID, service.Version.Index), swarm.ServicePoll) 70 service = getService(ctx, t, cli, serviceID) 71 assert.Check(t, is.DeepEqual(service.Spec.Labels, map[string]string{"foo": "bar"})) 72 73 err = cli.ServiceRemove(ctx, serviceID) 74 assert.NilError(t, err) 75 } 76 77 func TestServiceUpdateSecrets(t *testing.T) { 78 skip.If(t, testEnv.DaemonInfo.OSType != "linux") 79 ctx := setupTest(t) 80 81 d := swarm.NewSwarm(ctx, t, testEnv) 82 defer d.Stop(t) 83 cli := d.NewClientT(t) 84 defer cli.Close() 85 86 secretName := "TestSecret_" + t.Name() 87 secretTarget := "targetName" 88 resp, err := cli.SecretCreate(ctx, swarmtypes.SecretSpec{ 89 Annotations: swarmtypes.Annotations{ 90 Name: secretName, 91 }, 92 Data: []byte("TESTINGDATA"), 93 }) 94 assert.NilError(t, err) 95 assert.Check(t, resp.ID != "") 96 97 serviceName := "TestService_" + t.Name() 98 serviceID := swarm.CreateService(ctx, t, d, swarm.ServiceWithName(serviceName)) 99 service := getService(ctx, t, cli, serviceID) 100 101 // add secret 102 service.Spec.TaskTemplate.ContainerSpec.Secrets = append(service.Spec.TaskTemplate.ContainerSpec.Secrets, 103 &swarmtypes.SecretReference{ 104 File: &swarmtypes.SecretReferenceFileTarget{ 105 Name: secretTarget, 106 UID: "0", 107 GID: "0", 108 Mode: 0o600, 109 }, 110 SecretID: resp.ID, 111 SecretName: secretName, 112 }, 113 ) 114 _, err = cli.ServiceUpdate(ctx, serviceID, service.Version, service.Spec, types.ServiceUpdateOptions{}) 115 assert.NilError(t, err) 116 poll.WaitOn(t, serviceIsUpdated(ctx, cli, serviceID), swarm.ServicePoll) 117 118 service = getService(ctx, t, cli, serviceID) 119 secrets := service.Spec.TaskTemplate.ContainerSpec.Secrets 120 assert.Assert(t, is.Equal(1, len(secrets))) 121 122 secret := *secrets[0] 123 assert.Check(t, is.Equal(secretName, secret.SecretName)) 124 assert.Check(t, nil != secret.File) 125 assert.Check(t, is.Equal(secretTarget, secret.File.Name)) 126 127 // remove 128 service.Spec.TaskTemplate.ContainerSpec.Secrets = []*swarmtypes.SecretReference{} 129 _, err = cli.ServiceUpdate(ctx, serviceID, service.Version, service.Spec, types.ServiceUpdateOptions{}) 130 assert.NilError(t, err) 131 poll.WaitOn(t, serviceIsUpdated(ctx, cli, serviceID), swarm.ServicePoll) 132 service = getService(ctx, t, cli, serviceID) 133 assert.Check(t, is.Equal(0, len(service.Spec.TaskTemplate.ContainerSpec.Secrets))) 134 135 err = cli.ServiceRemove(ctx, serviceID) 136 assert.NilError(t, err) 137 } 138 139 func TestServiceUpdateConfigs(t *testing.T) { 140 skip.If(t, testEnv.DaemonInfo.OSType != "linux") 141 ctx := setupTest(t) 142 143 d := swarm.NewSwarm(ctx, t, testEnv) 144 defer d.Stop(t) 145 cli := d.NewClientT(t) 146 defer cli.Close() 147 148 configName := "TestConfig_" + t.Name() 149 configTarget := "targetName" 150 resp, err := cli.ConfigCreate(ctx, swarmtypes.ConfigSpec{ 151 Annotations: swarmtypes.Annotations{ 152 Name: configName, 153 }, 154 Data: []byte("TESTINGDATA"), 155 }) 156 assert.NilError(t, err) 157 assert.Check(t, resp.ID != "") 158 159 serviceName := "TestService_" + t.Name() 160 serviceID := swarm.CreateService(ctx, t, d, swarm.ServiceWithName(serviceName)) 161 service := getService(ctx, t, cli, serviceID) 162 163 // add config 164 service.Spec.TaskTemplate.ContainerSpec.Configs = append(service.Spec.TaskTemplate.ContainerSpec.Configs, 165 &swarmtypes.ConfigReference{ 166 File: &swarmtypes.ConfigReferenceFileTarget{ 167 Name: configTarget, 168 UID: "0", 169 GID: "0", 170 Mode: 0o600, 171 }, 172 ConfigID: resp.ID, 173 ConfigName: configName, 174 }, 175 ) 176 _, err = cli.ServiceUpdate(ctx, serviceID, service.Version, service.Spec, types.ServiceUpdateOptions{}) 177 assert.NilError(t, err) 178 poll.WaitOn(t, serviceIsUpdated(ctx, cli, serviceID), swarm.ServicePoll) 179 180 service = getService(ctx, t, cli, serviceID) 181 configs := service.Spec.TaskTemplate.ContainerSpec.Configs 182 assert.Assert(t, is.Equal(1, len(configs))) 183 184 config := *configs[0] 185 assert.Check(t, is.Equal(configName, config.ConfigName)) 186 assert.Check(t, nil != config.File) 187 assert.Check(t, is.Equal(configTarget, config.File.Name)) 188 189 // remove 190 service.Spec.TaskTemplate.ContainerSpec.Configs = []*swarmtypes.ConfigReference{} 191 _, err = cli.ServiceUpdate(ctx, serviceID, service.Version, service.Spec, types.ServiceUpdateOptions{}) 192 assert.NilError(t, err) 193 poll.WaitOn(t, serviceIsUpdated(ctx, cli, serviceID), swarm.ServicePoll) 194 service = getService(ctx, t, cli, serviceID) 195 assert.Check(t, is.Equal(0, len(service.Spec.TaskTemplate.ContainerSpec.Configs))) 196 197 err = cli.ServiceRemove(ctx, serviceID) 198 assert.NilError(t, err) 199 } 200 201 func TestServiceUpdateNetwork(t *testing.T) { 202 skip.If(t, testEnv.DaemonInfo.OSType != "linux") 203 ctx := setupTest(t) 204 205 d := swarm.NewSwarm(ctx, t, testEnv) 206 defer d.Stop(t) 207 cli := d.NewClientT(t) 208 defer cli.Close() 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(ctx, t, d, 219 swarm.ServiceWithReplicas(instances), 220 swarm.ServiceWithName(serviceName), 221 swarm.ServiceWithNetwork(testNet)) 222 223 poll.WaitOn(t, swarm.RunningTasksCount(ctx, cli, serviceID, instances), swarm.ServicePoll) 224 service := getService(ctx, 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(ctx, 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 ctx := setupTest(t) 283 284 d := swarm.NewSwarm(ctx, t, testEnv) 285 defer d.Stop(t) 286 cli := d.NewClientT(t) 287 defer func() { _ = cli.Close() }() 288 var ( 289 serviceID string 290 service swarmtypes.Service 291 ) 292 for i, tc := range tests { 293 tc := tc 294 t.Run(tc.name, func(t *testing.T) { 295 ctx := testutil.StartSpan(ctx, t) 296 if i == 0 { 297 serviceID = swarm.CreateService(ctx, t, d, swarm.ServiceWithPidsLimit(tc.pidsLimit)) 298 } else { 299 service = getService(ctx, t, cli, serviceID) 300 if service.Spec.TaskTemplate.Resources == nil { 301 service.Spec.TaskTemplate.Resources = &swarmtypes.ResourceRequirements{} 302 } 303 if service.Spec.TaskTemplate.Resources.Limits == nil { 304 service.Spec.TaskTemplate.Resources.Limits = &swarmtypes.Limit{} 305 } 306 service.Spec.TaskTemplate.Resources.Limits.Pids = tc.pidsLimit 307 _, err := cli.ServiceUpdate(ctx, serviceID, service.Version, service.Spec, types.ServiceUpdateOptions{}) 308 assert.NilError(t, err) 309 poll.WaitOn(t, serviceIsUpdated(ctx, cli, serviceID), swarm.ServicePoll) 310 } 311 312 poll.WaitOn(t, swarm.RunningTasksCount(ctx, cli, serviceID, 1), swarm.ServicePoll) 313 service = getService(ctx, t, cli, serviceID) 314 container := getServiceTaskContainer(ctx, t, cli, serviceID) 315 assert.Equal(t, service.Spec.TaskTemplate.Resources.Limits.Pids, tc.expected) 316 if tc.expected == 0 { 317 if container.HostConfig.Resources.PidsLimit != nil { 318 t.Fatalf("Expected container.HostConfig.Resources.PidsLimit to be nil") 319 } 320 } else { 321 assert.Assert(t, container.HostConfig.Resources.PidsLimit != nil) 322 assert.Equal(t, *container.HostConfig.Resources.PidsLimit, tc.expected) 323 } 324 }) 325 } 326 327 err := cli.ServiceRemove(ctx, serviceID) 328 assert.NilError(t, err) 329 } 330 331 func getServiceTaskContainer(ctx context.Context, t *testing.T, cli client.APIClient, serviceID string) types.ContainerJSON { 332 t.Helper() 333 tasks, err := cli.TaskList(ctx, types.TaskListOptions{ 334 Filters: filters.NewArgs( 335 filters.Arg("service", serviceID), 336 filters.Arg("desired-state", "running"), 337 ), 338 }) 339 assert.NilError(t, err) 340 assert.Assert(t, len(tasks) > 0) 341 342 ctr, err := cli.ContainerInspect(ctx, tasks[0].Status.ContainerStatus.ContainerID) 343 assert.NilError(t, err) 344 assert.Equal(t, ctr.State.Running, true) 345 return ctr 346 } 347 348 func getService(ctx context.Context, t *testing.T, cli client.ServiceAPIClient, serviceID string) swarmtypes.Service { 349 t.Helper() 350 service, _, err := cli.ServiceInspectWithRaw(ctx, serviceID, types.ServiceInspectOptions{}) 351 assert.NilError(t, err) 352 return service 353 } 354 355 func serviceIsUpdated(ctx context.Context, client client.ServiceAPIClient, serviceID string) func(log poll.LogT) poll.Result { 356 return func(log poll.LogT) poll.Result { 357 service, _, err := client.ServiceInspectWithRaw(ctx, serviceID, types.ServiceInspectOptions{}) 358 switch { 359 case err != nil: 360 return poll.Error(err) 361 case service.UpdateStatus != nil && service.UpdateStatus.State == swarmtypes.UpdateStateCompleted: 362 return poll.Success() 363 default: 364 if service.UpdateStatus != nil { 365 return poll.Continue("waiting for service %s to be updated, state: %s, message: %s", serviceID, service.UpdateStatus.State, service.UpdateStatus.Message) 366 } 367 return poll.Continue("waiting for service %s to be updated", serviceID) 368 } 369 } 370 } 371 372 func serviceSpecIsUpdated(ctx context.Context, client client.ServiceAPIClient, serviceID string, serviceOldVersion uint64) func(log poll.LogT) poll.Result { 373 return func(log poll.LogT) poll.Result { 374 service, _, err := client.ServiceInspectWithRaw(ctx, serviceID, types.ServiceInspectOptions{}) 375 switch { 376 case err != nil: 377 return poll.Error(err) 378 case service.Version.Index > serviceOldVersion: 379 return poll.Success() 380 default: 381 return poll.Continue("waiting for service %s to be updated", serviceID) 382 } 383 } 384 }