package service // import "github.com/docker/docker/integration/service" import ( "context" "testing" "github.com/docker/docker/api/types" swarmtypes "github.com/docker/docker/api/types/swarm" "github.com/docker/docker/client" "github.com/docker/docker/integration/internal/network" "github.com/docker/docker/integration/internal/swarm" "gotest.tools/assert" is "gotest.tools/assert/cmp" "gotest.tools/poll" "gotest.tools/skip" ) func TestServiceUpdateLabel(t *testing.T) { skip.If(t, testEnv.DaemonInfo.OSType != "linux") defer setupTest(t)() d := swarm.NewSwarm(t, testEnv) defer d.Stop(t) cli := d.NewClientT(t) defer cli.Close() ctx := context.Background() serviceName := "TestService_" + t.Name() serviceID := swarm.CreateService(t, d, swarm.ServiceWithName(serviceName)) service := getService(t, cli, serviceID) assert.Check(t, is.DeepEqual(service.Spec.Labels, map[string]string{})) // add label to empty set service.Spec.Labels["foo"] = "bar" _, err := cli.ServiceUpdate(ctx, serviceID, service.Version, service.Spec, types.ServiceUpdateOptions{}) assert.NilError(t, err) poll.WaitOn(t, serviceSpecIsUpdated(cli, serviceID, service.Version.Index), swarm.ServicePoll) service = getService(t, cli, serviceID) assert.Check(t, is.DeepEqual(service.Spec.Labels, map[string]string{"foo": "bar"})) // add label to non-empty set service.Spec.Labels["foo2"] = "bar" _, err = cli.ServiceUpdate(ctx, serviceID, service.Version, service.Spec, types.ServiceUpdateOptions{}) assert.NilError(t, err) poll.WaitOn(t, serviceSpecIsUpdated(cli, serviceID, service.Version.Index), swarm.ServicePoll) service = getService(t, cli, serviceID) assert.Check(t, is.DeepEqual(service.Spec.Labels, map[string]string{"foo": "bar", "foo2": "bar"})) delete(service.Spec.Labels, "foo2") _, err = cli.ServiceUpdate(ctx, serviceID, service.Version, service.Spec, types.ServiceUpdateOptions{}) assert.NilError(t, err) poll.WaitOn(t, serviceSpecIsUpdated(cli, serviceID, service.Version.Index), swarm.ServicePoll) service = getService(t, cli, serviceID) assert.Check(t, is.DeepEqual(service.Spec.Labels, map[string]string{"foo": "bar"})) delete(service.Spec.Labels, "foo") _, err = cli.ServiceUpdate(ctx, serviceID, service.Version, service.Spec, types.ServiceUpdateOptions{}) assert.NilError(t, err) poll.WaitOn(t, serviceSpecIsUpdated(cli, serviceID, service.Version.Index), swarm.ServicePoll) service = getService(t, cli, serviceID) assert.Check(t, is.DeepEqual(service.Spec.Labels, map[string]string{})) // now make sure we can add again service.Spec.Labels["foo"] = "bar" _, err = cli.ServiceUpdate(ctx, serviceID, service.Version, service.Spec, types.ServiceUpdateOptions{}) assert.NilError(t, err) poll.WaitOn(t, serviceSpecIsUpdated(cli, serviceID, service.Version.Index), swarm.ServicePoll) service = getService(t, cli, serviceID) assert.Check(t, is.DeepEqual(service.Spec.Labels, map[string]string{"foo": "bar"})) err = cli.ServiceRemove(context.Background(), serviceID) assert.NilError(t, err) } func TestServiceUpdateSecrets(t *testing.T) { skip.If(t, testEnv.DaemonInfo.OSType != "linux") defer setupTest(t)() d := swarm.NewSwarm(t, testEnv) defer d.Stop(t) cli := d.NewClientT(t) defer cli.Close() ctx := context.Background() secretName := "TestSecret_" + t.Name() secretTarget := "targetName" resp, err := cli.SecretCreate(ctx, swarmtypes.SecretSpec{ Annotations: swarmtypes.Annotations{ Name: secretName, }, Data: []byte("TESTINGDATA"), }) assert.NilError(t, err) assert.Check(t, resp.ID != "") serviceName := "TestService_" + t.Name() serviceID := swarm.CreateService(t, d, swarm.ServiceWithName(serviceName)) service := getService(t, cli, serviceID) // add secret service.Spec.TaskTemplate.ContainerSpec.Secrets = append(service.Spec.TaskTemplate.ContainerSpec.Secrets, &swarmtypes.SecretReference{ File: &swarmtypes.SecretReferenceFileTarget{ Name: secretTarget, UID: "0", GID: "0", Mode: 0600, }, SecretID: resp.ID, SecretName: secretName, }, ) _, err = cli.ServiceUpdate(ctx, serviceID, service.Version, service.Spec, types.ServiceUpdateOptions{}) assert.NilError(t, err) poll.WaitOn(t, serviceIsUpdated(cli, serviceID), swarm.ServicePoll) service = getService(t, cli, serviceID) secrets := service.Spec.TaskTemplate.ContainerSpec.Secrets assert.Assert(t, is.Equal(1, len(secrets))) secret := *secrets[0] assert.Check(t, is.Equal(secretName, secret.SecretName)) assert.Check(t, nil != secret.File) assert.Check(t, is.Equal(secretTarget, secret.File.Name)) // remove service.Spec.TaskTemplate.ContainerSpec.Secrets = []*swarmtypes.SecretReference{} _, err = cli.ServiceUpdate(ctx, serviceID, service.Version, service.Spec, types.ServiceUpdateOptions{}) assert.NilError(t, err) poll.WaitOn(t, serviceIsUpdated(cli, serviceID), swarm.ServicePoll) service = getService(t, cli, serviceID) assert.Check(t, is.Equal(0, len(service.Spec.TaskTemplate.ContainerSpec.Secrets))) err = cli.ServiceRemove(context.Background(), serviceID) assert.NilError(t, err) } func TestServiceUpdateConfigs(t *testing.T) { skip.If(t, testEnv.DaemonInfo.OSType != "linux") defer setupTest(t)() d := swarm.NewSwarm(t, testEnv) defer d.Stop(t) cli := d.NewClientT(t) defer cli.Close() ctx := context.Background() configName := "TestConfig_" + t.Name() configTarget := "targetName" resp, err := cli.ConfigCreate(ctx, swarmtypes.ConfigSpec{ Annotations: swarmtypes.Annotations{ Name: configName, }, Data: []byte("TESTINGDATA"), }) assert.NilError(t, err) assert.Check(t, resp.ID != "") serviceName := "TestService_" + t.Name() serviceID := swarm.CreateService(t, d, swarm.ServiceWithName(serviceName)) service := getService(t, cli, serviceID) // add config service.Spec.TaskTemplate.ContainerSpec.Configs = append(service.Spec.TaskTemplate.ContainerSpec.Configs, &swarmtypes.ConfigReference{ File: &swarmtypes.ConfigReferenceFileTarget{ Name: configTarget, UID: "0", GID: "0", Mode: 0600, }, ConfigID: resp.ID, ConfigName: configName, }, ) _, err = cli.ServiceUpdate(ctx, serviceID, service.Version, service.Spec, types.ServiceUpdateOptions{}) assert.NilError(t, err) poll.WaitOn(t, serviceIsUpdated(cli, serviceID), swarm.ServicePoll) service = getService(t, cli, serviceID) configs := service.Spec.TaskTemplate.ContainerSpec.Configs assert.Assert(t, is.Equal(1, len(configs))) config := *configs[0] assert.Check(t, is.Equal(configName, config.ConfigName)) assert.Check(t, nil != config.File) assert.Check(t, is.Equal(configTarget, config.File.Name)) // remove service.Spec.TaskTemplate.ContainerSpec.Configs = []*swarmtypes.ConfigReference{} _, err = cli.ServiceUpdate(ctx, serviceID, service.Version, service.Spec, types.ServiceUpdateOptions{}) assert.NilError(t, err) poll.WaitOn(t, serviceIsUpdated(cli, serviceID), swarm.ServicePoll) service = getService(t, cli, serviceID) assert.Check(t, is.Equal(0, len(service.Spec.TaskTemplate.ContainerSpec.Configs))) err = cli.ServiceRemove(context.Background(), serviceID) assert.NilError(t, err) } func TestServiceUpdateNetwork(t *testing.T) { skip.If(t, testEnv.DaemonInfo.OSType != "linux") defer setupTest(t)() d := swarm.NewSwarm(t, testEnv) defer d.Stop(t) cli := d.NewClientT(t) defer cli.Close() ctx := context.Background() // Create a overlay network testNet := "testNet" + t.Name() overlayID := network.CreateNoError(ctx, t, cli, testNet, network.WithDriver("overlay")) var instances uint64 = 1 // Create service with the overlay network serviceName := "TestServiceUpdateNetworkRM_" + t.Name() serviceID := swarm.CreateService(t, d, swarm.ServiceWithReplicas(instances), swarm.ServiceWithName(serviceName), swarm.ServiceWithNetwork(testNet)) poll.WaitOn(t, swarm.RunningTasksCount(cli, serviceID, instances), swarm.ServicePoll) service := getService(t, cli, serviceID) netInfo, err := cli.NetworkInspect(ctx, testNet, types.NetworkInspectOptions{ Verbose: true, Scope: "swarm", }) assert.NilError(t, err) assert.Assert(t, len(netInfo.Containers) == 2, "Expected 2 endpoints, one for container and one for LB Sandbox") //Remove network from service service.Spec.TaskTemplate.Networks = []swarmtypes.NetworkAttachmentConfig{} _, err = cli.ServiceUpdate(ctx, serviceID, service.Version, service.Spec, types.ServiceUpdateOptions{}) assert.NilError(t, err) poll.WaitOn(t, serviceIsUpdated(cli, serviceID), swarm.ServicePoll) netInfo, err = cli.NetworkInspect(ctx, testNet, types.NetworkInspectOptions{ Verbose: true, Scope: "swarm", }) assert.NilError(t, err) assert.Assert(t, len(netInfo.Containers) == 0, "Load balancing endpoint still exists in network") err = cli.NetworkRemove(ctx, overlayID) assert.NilError(t, err) err = cli.ServiceRemove(ctx, serviceID) assert.NilError(t, err) } func getService(t *testing.T, cli client.ServiceAPIClient, serviceID string) swarmtypes.Service { t.Helper() service, _, err := cli.ServiceInspectWithRaw(context.Background(), serviceID, types.ServiceInspectOptions{}) assert.NilError(t, err) return service } func serviceIsUpdated(client client.ServiceAPIClient, serviceID string) func(log poll.LogT) poll.Result { return func(log poll.LogT) poll.Result { service, _, err := client.ServiceInspectWithRaw(context.Background(), serviceID, types.ServiceInspectOptions{}) switch { case err != nil: return poll.Error(err) case service.UpdateStatus != nil && service.UpdateStatus.State == swarmtypes.UpdateStateCompleted: return poll.Success() default: if service.UpdateStatus != nil { return poll.Continue("waiting for service %s to be updated, state: %s, message: %s", serviceID, service.UpdateStatus.State, service.UpdateStatus.Message) } return poll.Continue("waiting for service %s to be updated", serviceID) } } } func serviceSpecIsUpdated(client client.ServiceAPIClient, serviceID string, serviceOldVersion uint64) func(log poll.LogT) poll.Result { return func(log poll.LogT) poll.Result { service, _, err := client.ServiceInspectWithRaw(context.Background(), serviceID, types.ServiceInspectOptions{}) switch { case err != nil: return poll.Error(err) case service.Version.Index > serviceOldVersion: return poll.Success() default: return poll.Continue("waiting for service %s to be updated", serviceID) } } }