Added support for maximum replicas per node to services
Signed-off-by: Olli Janatuinen <olli.janatuinen@gmail.com>
This commit is contained in:
parent
7e7ff2a033
commit
153171e9dd
7 changed files with 58 additions and 0 deletions
|
@ -219,6 +219,12 @@ func (sr *swarmRouter) createService(ctx context.Context, w http.ResponseWriter,
|
|||
// API version 1.40
|
||||
service.TaskTemplate.ContainerSpec.Sysctls = nil
|
||||
}
|
||||
|
||||
if service.TaskTemplate.Placement != nil {
|
||||
// MaxReplicas for docker swarm services weren't supported before
|
||||
// API version 1.40
|
||||
service.TaskTemplate.Placement.MaxReplicas = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -265,6 +271,12 @@ func (sr *swarmRouter) updateService(ctx context.Context, w http.ResponseWriter,
|
|||
// API version 1.40
|
||||
service.TaskTemplate.ContainerSpec.Sysctls = nil
|
||||
}
|
||||
|
||||
if service.TaskTemplate.Placement != nil {
|
||||
// MaxReplicas for docker swarm services weren't supported before
|
||||
// API version 1.40
|
||||
service.TaskTemplate.Placement.MaxReplicas = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2878,6 +2878,11 @@ definitions:
|
|||
SpreadDescriptor: "node.labels.datacenter"
|
||||
- Spread:
|
||||
SpreadDescriptor: "node.labels.rack"
|
||||
MaxReplicas:
|
||||
description: "Maximum number of replicas for per node (default value is 0, which is unlimited)"
|
||||
type: "integer"
|
||||
format: "int64"
|
||||
default: 0
|
||||
Platforms:
|
||||
description: |
|
||||
Platforms stores all the platforms that the service's image can
|
||||
|
|
|
@ -127,6 +127,7 @@ type ResourceRequirements struct {
|
|||
type Placement struct {
|
||||
Constraints []string `json:",omitempty"`
|
||||
Preferences []PlacementPreference `json:",omitempty"`
|
||||
MaxReplicas uint64 `json:",omitempty"`
|
||||
|
||||
// Platforms stores all the platforms that the image can run on.
|
||||
// This field is used in the platform filter for scheduling. If empty,
|
||||
|
|
|
@ -246,6 +246,7 @@ func ServiceSpecToGRPC(s types.ServiceSpec) (swarmapi.ServiceSpec, error) {
|
|||
spec.Task.Placement = &swarmapi.Placement{
|
||||
Constraints: s.TaskTemplate.Placement.Constraints,
|
||||
Preferences: preferences,
|
||||
MaxReplicas: s.TaskTemplate.Placement.MaxReplicas,
|
||||
Platforms: platforms,
|
||||
}
|
||||
}
|
||||
|
@ -472,6 +473,7 @@ func placementFromGRPC(p *swarmapi.Placement) *types.Placement {
|
|||
}
|
||||
r := &types.Placement{
|
||||
Constraints: p.Constraints,
|
||||
MaxReplicas: p.MaxReplicas,
|
||||
}
|
||||
|
||||
for _, pref := range p.Preferences {
|
||||
|
|
|
@ -33,6 +33,10 @@ keywords: "API, Docker, rcli, REST, documentation"
|
|||
* `GET /info` now returns information about `DataPathPort` that is currently used in swarm
|
||||
* `GET /swarm` endpoint now returns DataPathPort info
|
||||
* `POST /containers/create` now takes `KernelMemoryTCP` field to set hard limit for kernel TCP buffer memory.
|
||||
* `GET /service` now returns `MaxReplicas` as part of the `Placement`.
|
||||
* `GET /service/{id}` now returns `MaxReplicas` as part of the `Placement`.
|
||||
* `POST /service/create` and `POST /services/(id or name)/update` now take the field `MaxReplicas`
|
||||
as part of the service `Placement`, allowing to specify maximum replicas per node for the service.
|
||||
|
||||
## V1.39 API changes
|
||||
|
||||
|
|
|
@ -141,6 +141,14 @@ func ServiceWithReplicas(n uint64) ServiceSpecOpt {
|
|||
}
|
||||
}
|
||||
|
||||
// ServiceWithMaxReplicas sets the max replicas for the service
|
||||
func ServiceWithMaxReplicas(n uint64) ServiceSpecOpt {
|
||||
return func(spec *swarmtypes.ServiceSpec) {
|
||||
ensurePlacement(spec)
|
||||
spec.TaskTemplate.Placement.MaxReplicas = n
|
||||
}
|
||||
}
|
||||
|
||||
// ServiceWithName sets the name of the service
|
||||
func ServiceWithName(name string) ServiceSpecOpt {
|
||||
return func(spec *swarmtypes.ServiceSpec) {
|
||||
|
@ -210,3 +218,9 @@ func ensureContainerSpec(spec *swarmtypes.ServiceSpec) {
|
|||
spec.TaskTemplate.ContainerSpec = &swarmtypes.ContainerSpec{}
|
||||
}
|
||||
}
|
||||
|
||||
func ensurePlacement(spec *swarmtypes.ServiceSpec) {
|
||||
if spec.TaskTemplate.Placement == nil {
|
||||
spec.TaskTemplate.Placement = &swarmtypes.Placement{}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -153,6 +153,26 @@ func TestCreateServiceConflict(t *testing.T) {
|
|||
assert.Check(t, is.Contains(string(buf), "service "+serviceName+" already exists"))
|
||||
}
|
||||
|
||||
func TestCreateServiceMaxReplicas(t *testing.T) {
|
||||
defer setupTest(t)()
|
||||
d := swarm.NewSwarm(t, testEnv)
|
||||
defer d.Stop(t)
|
||||
client := d.NewClientT(t)
|
||||
defer client.Close()
|
||||
|
||||
var maxReplicas uint64 = 2
|
||||
serviceSpec := []swarm.ServiceSpecOpt{
|
||||
swarm.ServiceWithReplicas(maxReplicas),
|
||||
swarm.ServiceWithMaxReplicas(maxReplicas),
|
||||
}
|
||||
|
||||
serviceID := swarm.CreateService(t, d, serviceSpec...)
|
||||
poll.WaitOn(t, serviceRunningTasksCount(client, serviceID, maxReplicas), swarm.ServicePoll)
|
||||
|
||||
_, _, err := client.ServiceInspectWithRaw(context.Background(), serviceID, types.ServiceInspectOptions{})
|
||||
assert.NilError(t, err)
|
||||
}
|
||||
|
||||
func TestCreateWithDuplicateNetworkNames(t *testing.T) {
|
||||
skip.If(t, testEnv.DaemonInfo.OSType == "windows")
|
||||
defer setupTest(t)()
|
||||
|
|
Loading…
Reference in a new issue