1
0
Fork 0
mirror of https://github.com/moby/moby.git synced 2022-11-09 12:21:53 -05:00

Merge pull request #27369 from cezarsa/hc

Add --health-* flags to service create and update
This commit is contained in:
Vincent Demeester 2016-10-28 21:59:52 +02:00 committed by GitHub
commit f860289131
21 changed files with 1526 additions and 450 deletions

View file

@ -3,20 +3,22 @@ package swarm
import (
"time"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/mount"
)
// ContainerSpec represents the spec of a container.
type ContainerSpec struct {
Image string `json:",omitempty"`
Labels map[string]string `json:",omitempty"`
Command []string `json:",omitempty"`
Args []string `json:",omitempty"`
Env []string `json:",omitempty"`
Dir string `json:",omitempty"`
User string `json:",omitempty"`
Groups []string `json:",omitempty"`
TTY bool `json:",omitempty"`
Mounts []mount.Mount `json:",omitempty"`
StopGracePeriod *time.Duration `json:",omitempty"`
Image string `json:",omitempty"`
Labels map[string]string `json:",omitempty"`
Command []string `json:",omitempty"`
Args []string `json:",omitempty"`
Env []string `json:",omitempty"`
Dir string `json:",omitempty"`
User string `json:",omitempty"`
Groups []string `json:",omitempty"`
TTY bool `json:",omitempty"`
Mounts []mount.Mount `json:",omitempty"`
StopGracePeriod *time.Duration `json:",omitempty"`
Healthcheck *container.HealthConfig `json:",omitempty"`
}

View file

@ -8,6 +8,7 @@ import (
"strings"
"time"
"github.com/docker/docker/api/types/container"
mounttypes "github.com/docker/docker/api/types/mount"
"github.com/docker/docker/api/types/swarm"
"github.com/docker/docker/opts"
@ -68,6 +69,25 @@ func (c *nanoCPUs) Value() int64 {
return int64(*c)
}
// PositiveDurationOpt is an option type for time.Duration that uses a pointer.
// It bahave similarly to DurationOpt but only allows positive duration values.
type PositiveDurationOpt struct {
DurationOpt
}
// Set a new value on the option. Setting a negative duration value will cause
// an error to be returned.
func (d *PositiveDurationOpt) Set(s string) error {
err := d.DurationOpt.Set(s)
if err != nil {
return err
}
if *d.DurationOpt.value < 0 {
return fmt.Errorf("duration cannot be negative")
}
return nil
}
// DurationOpt is an option type for time.Duration that uses a pointer. This
// allows us to get nil values outside, instead of defaulting to 0
type DurationOpt struct {
@ -377,6 +397,47 @@ func (ldo *logDriverOptions) toLogDriver() *swarm.Driver {
}
}
type healthCheckOptions struct {
cmd string
interval PositiveDurationOpt
timeout PositiveDurationOpt
retries int
noHealthcheck bool
}
func (opts *healthCheckOptions) toHealthConfig() (*container.HealthConfig, error) {
var healthConfig *container.HealthConfig
haveHealthSettings := opts.cmd != "" ||
opts.interval.Value() != nil ||
opts.timeout.Value() != nil ||
opts.retries != 0
if opts.noHealthcheck {
if haveHealthSettings {
return nil, fmt.Errorf("--%s conflicts with --health-* options", flagNoHealthcheck)
}
healthConfig = &container.HealthConfig{Test: []string{"NONE"}}
} else if haveHealthSettings {
var test []string
if opts.cmd != "" {
test = []string{"CMD-SHELL", opts.cmd}
}
var interval, timeout time.Duration
if ptr := opts.interval.Value(); ptr != nil {
interval = *ptr
}
if ptr := opts.timeout.Value(); ptr != nil {
timeout = *ptr
}
healthConfig = &container.HealthConfig{
Test: test,
Interval: interval,
Timeout: timeout,
Retries: opts.retries,
}
}
return healthConfig, nil
}
// ValidatePort validates a string is in the expected format for a port definition
func ValidatePort(value string) (string, error) {
portMappings, err := nat.ParsePortSpec(value)
@ -416,6 +477,8 @@ type serviceOptions struct {
registryAuth bool
logDriver logDriverOptions
healthcheck healthCheckOptions
}
func newServiceOptions() *serviceOptions {
@ -490,6 +553,12 @@ func (opts *serviceOptions) ToService() (swarm.ServiceSpec, error) {
EndpointSpec: opts.endpoint.ToEndpointSpec(),
}
healthConfig, err := opts.healthcheck.toHealthConfig()
if err != nil {
return service, err
}
service.TaskTemplate.ContainerSpec.Healthcheck = healthConfig
switch opts.mode {
case "global":
if opts.replicas.Value() != nil {
@ -541,6 +610,12 @@ func addServiceFlags(cmd *cobra.Command, opts *serviceOptions) {
flags.StringVar(&opts.logDriver.name, flagLogDriver, "", "Logging driver for service")
flags.Var(&opts.logDriver.opts, flagLogOpt, "Logging driver options")
flags.StringVar(&opts.healthcheck.cmd, flagHealthCmd, "", "Command to run to check health")
flags.Var(&opts.healthcheck.interval, flagHealthInterval, "Time between running the check")
flags.Var(&opts.healthcheck.timeout, flagHealthTimeout, "Maximum time to allow one check to run")
flags.IntVar(&opts.healthcheck.retries, flagHealthRetries, 0, "Consecutive failures needed to report unhealthy")
flags.BoolVar(&opts.healthcheck.noHealthcheck, flagNoHealthcheck, false, "Disable any container-specified HEALTHCHECK")
}
const (
@ -589,4 +664,9 @@ const (
flagRegistryAuth = "with-registry-auth"
flagLogDriver = "log-driver"
flagLogOpt = "log-opt"
flagHealthCmd = "health-cmd"
flagHealthInterval = "health-interval"
flagHealthRetries = "health-retries"
flagHealthTimeout = "health-timeout"
flagNoHealthcheck = "no-healthcheck"
)

View file

@ -1,9 +1,11 @@
package service
import (
"reflect"
"testing"
"time"
"github.com/docker/docker/api/types/container"
mounttypes "github.com/docker/docker/api/types/mount"
"github.com/docker/docker/pkg/testutil/assert"
)
@ -40,6 +42,15 @@ func TestDurationOptSetAndValue(t *testing.T) {
var duration DurationOpt
assert.NilError(t, duration.Set("300s"))
assert.Equal(t, *duration.Value(), time.Duration(300*10e8))
assert.NilError(t, duration.Set("-300s"))
assert.Equal(t, *duration.Value(), time.Duration(-300*10e8))
}
func TestPositiveDurationOptSetAndValue(t *testing.T) {
var duration PositiveDurationOpt
assert.NilError(t, duration.Set("300s"))
assert.Equal(t, *duration.Value(), time.Duration(300*10e8))
assert.Error(t, duration.Set("-300s"), "cannot be negative")
}
func TestUint64OptString(t *testing.T) {
@ -201,3 +212,41 @@ func TestMountOptTypeConflict(t *testing.T) {
assert.Error(t, m.Set("type=bind,target=/foo,source=/foo,volume-nocopy=true"), "cannot mix")
assert.Error(t, m.Set("type=volume,target=/foo,source=/foo,bind-propagation=rprivate"), "cannot mix")
}
func TestHealthCheckOptionsToHealthConfig(t *testing.T) {
dur := time.Second
opt := healthCheckOptions{
cmd: "curl",
interval: PositiveDurationOpt{DurationOpt{value: &dur}},
timeout: PositiveDurationOpt{DurationOpt{value: &dur}},
retries: 10,
}
config, err := opt.toHealthConfig()
assert.NilError(t, err)
assert.Equal(t, reflect.DeepEqual(config, &container.HealthConfig{
Test: []string{"CMD-SHELL", "curl"},
Interval: time.Second,
Timeout: time.Second,
Retries: 10,
}), true)
}
func TestHealthCheckOptionsToHealthConfigNoHealthcheck(t *testing.T) {
opt := healthCheckOptions{
noHealthcheck: true,
}
config, err := opt.toHealthConfig()
assert.NilError(t, err)
assert.Equal(t, reflect.DeepEqual(config, &container.HealthConfig{
Test: []string{"NONE"},
}), true)
}
func TestHealthCheckOptionsToHealthConfigConflict(t *testing.T) {
opt := healthCheckOptions{
cmd: "curl",
noHealthcheck: true,
}
_, err := opt.toHealthConfig()
assert.Error(t, err, "--no-healthcheck conflicts with --health-* options")
}

View file

@ -9,6 +9,7 @@ import (
"golang.org/x/net/context"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
mounttypes "github.com/docker/docker/api/types/mount"
"github.com/docker/docker/api/types/swarm"
"github.com/docker/docker/cli"
@ -266,6 +267,10 @@ func updateService(flags *pflag.FlagSet, spec *swarm.ServiceSpec) error {
spec.TaskTemplate.ForceUpdate++
}
if err := updateHealthcheck(flags, cspec); err != nil {
return err
}
return nil
}
@ -537,3 +542,48 @@ func updateLogDriver(flags *pflag.FlagSet, taskTemplate *swarm.TaskSpec) error {
return nil
}
func updateHealthcheck(flags *pflag.FlagSet, containerSpec *swarm.ContainerSpec) error {
if !anyChanged(flags, flagNoHealthcheck, flagHealthCmd, flagHealthInterval, flagHealthRetries, flagHealthTimeout) {
return nil
}
if containerSpec.Healthcheck == nil {
containerSpec.Healthcheck = &container.HealthConfig{}
}
noHealthcheck, err := flags.GetBool(flagNoHealthcheck)
if err != nil {
return err
}
if noHealthcheck {
if !anyChanged(flags, flagHealthCmd, flagHealthInterval, flagHealthRetries, flagHealthTimeout) {
containerSpec.Healthcheck = &container.HealthConfig{
Test: []string{"NONE"},
}
return nil
}
return fmt.Errorf("--%s conflicts with --health-* options", flagNoHealthcheck)
}
if len(containerSpec.Healthcheck.Test) > 0 && containerSpec.Healthcheck.Test[0] == "NONE" {
containerSpec.Healthcheck.Test = nil
}
if flags.Changed(flagHealthInterval) {
val := *flags.Lookup(flagHealthInterval).Value.(*PositiveDurationOpt).Value()
containerSpec.Healthcheck.Interval = val
}
if flags.Changed(flagHealthTimeout) {
val := *flags.Lookup(flagHealthTimeout).Value.(*PositiveDurationOpt).Value()
containerSpec.Healthcheck.Timeout = val
}
if flags.Changed(flagHealthRetries) {
containerSpec.Healthcheck.Retries, _ = flags.GetInt(flagHealthRetries)
}
if flags.Changed(flagHealthCmd) {
cmd, _ := flags.GetString(flagHealthCmd)
if cmd != "" {
containerSpec.Healthcheck.Test = []string{"CMD-SHELL", cmd}
} else {
containerSpec.Healthcheck.Test = nil
}
}
return nil
}

View file

@ -1,9 +1,12 @@
package service
import (
"reflect"
"sort"
"testing"
"time"
"github.com/docker/docker/api/types/container"
mounttypes "github.com/docker/docker/api/types/mount"
"github.com/docker/docker/api/types/swarm"
"github.com/docker/docker/pkg/testutil/assert"
@ -196,3 +199,79 @@ func TestUpdatePortsConflictingFlags(t *testing.T) {
err := updatePorts(flags, &portConfigs)
assert.Error(t, err, "conflicting port mapping")
}
func TestUpdateHealthcheckTable(t *testing.T) {
type test struct {
flags [][2]string
initial *container.HealthConfig
expected *container.HealthConfig
err string
}
testCases := []test{
{
flags: [][2]string{{"no-healthcheck", "true"}},
initial: &container.HealthConfig{Test: []string{"CMD-SHELL", "cmd1"}, Retries: 10},
expected: &container.HealthConfig{Test: []string{"NONE"}},
},
{
flags: [][2]string{{"health-cmd", "cmd1"}},
initial: &container.HealthConfig{Test: []string{"NONE"}},
expected: &container.HealthConfig{Test: []string{"CMD-SHELL", "cmd1"}},
},
{
flags: [][2]string{{"health-retries", "10"}},
initial: &container.HealthConfig{Test: []string{"NONE"}},
expected: &container.HealthConfig{Retries: 10},
},
{
flags: [][2]string{{"health-retries", "10"}},
initial: &container.HealthConfig{Test: []string{"CMD", "cmd1"}},
expected: &container.HealthConfig{Test: []string{"CMD", "cmd1"}, Retries: 10},
},
{
flags: [][2]string{{"health-interval", "1m"}},
initial: &container.HealthConfig{Test: []string{"CMD", "cmd1"}},
expected: &container.HealthConfig{Test: []string{"CMD", "cmd1"}, Interval: time.Minute},
},
{
flags: [][2]string{{"health-cmd", ""}},
initial: &container.HealthConfig{Test: []string{"CMD", "cmd1"}, Retries: 10},
expected: &container.HealthConfig{Retries: 10},
},
{
flags: [][2]string{{"health-retries", "0"}},
initial: &container.HealthConfig{Test: []string{"CMD", "cmd1"}, Retries: 10},
expected: &container.HealthConfig{Test: []string{"CMD", "cmd1"}},
},
{
flags: [][2]string{{"health-cmd", "cmd1"}, {"no-healthcheck", "true"}},
err: "--no-healthcheck conflicts with --health-* options",
},
{
flags: [][2]string{{"health-interval", "10m"}, {"no-healthcheck", "true"}},
err: "--no-healthcheck conflicts with --health-* options",
},
{
flags: [][2]string{{"health-timeout", "1m"}, {"no-healthcheck", "true"}},
err: "--no-healthcheck conflicts with --health-* options",
},
}
for i, c := range testCases {
flags := newUpdateCommand(nil).Flags()
for _, flag := range c.flags {
flags.Set(flag[0], flag[1])
}
cspec := &swarm.ContainerSpec{
Healthcheck: c.initial,
}
err := updateHealthcheck(flags, cspec)
if c.err != "" {
assert.Error(t, err, c.err)
} else {
assert.NilError(t, err)
if !reflect.DeepEqual(cspec.Healthcheck, c.expected) {
t.Errorf("incorrect result for test %d, expected health config:\n\t%#v\ngot:\n\t%#v", i, c.expected, cspec.Healthcheck)
}
}
}
}

View file

@ -2574,6 +2574,10 @@ _docker_service_update() {
--env -e
--force
--group-add
--health-cmd
--health-interval
--health-retries
--health-timeout
--label -l
--limit-cpu
--limit-memory
@ -2581,6 +2585,7 @@ _docker_service_update() {
--log-opt
--mount
--network
--no-healthcheck
--publish -p
--replicas
--reserve-cpu

View file

@ -1089,6 +1089,10 @@ __docker_service_subcommand() {
"($help)--endpoint-mode=[Placement constraints]:mode:(dnsrr vip)"
"($help)*"{-e=,--env=}"[Set environment variables]:env: "
"($help)*--group-add=[Add additional user groups to the container]:group:_groups"
"($help)--health-cmd=[Command to run to check health]:command: "
"($help)--health-interval=[Time between running the check]:time: "
"($help)--health-retries=[Consecutive failures needed to report unhealthy]:retries:(1 2 3 4 5)"
"($help)--health-timeout=[Maximum time to allow one check to run]:time: "
"($help)*--label=[Service labels]:label: "
"($help)--limit-cpu=[Limit CPUs]:value: "
"($help)--limit-memory=[Limit Memory]:value: "
@ -1096,6 +1100,7 @@ __docker_service_subcommand() {
"($help)*--log-opt=[Logging driver options]:log driver options:__docker_log_options"
"($help)*--mount=[Attach a mount to the service]:mount: "
"($help)*--network=[Network attachments]:network: "
"($help)--no-healthcheck[Disable any container-specified HEALTHCHECK]"
"($help)*"{-p=,--publish=}"[Publish a port as a node port]:port: "
"($help)--replicas=[Number of tasks]:replicas: "
"($help)--reserve-cpu=[Reserve CPUs]:value: "

View file

@ -4,6 +4,7 @@ import (
"fmt"
"strings"
container "github.com/docker/docker/api/types/container"
mounttypes "github.com/docker/docker/api/types/mount"
types "github.com/docker/docker/api/types/swarm"
swarmapi "github.com/docker/swarmkit/api"
@ -56,6 +57,11 @@ func containerSpecFromGRPC(c *swarmapi.ContainerSpec) types.ContainerSpec {
grace, _ := ptypes.Duration(c.StopGracePeriod)
containerSpec.StopGracePeriod = &grace
}
if c.Healthcheck != nil {
containerSpec.Healthcheck = healthConfigFromGRPC(c.Healthcheck)
}
return containerSpec
}
@ -115,5 +121,29 @@ func containerToGRPC(c types.ContainerSpec) (*swarmapi.ContainerSpec, error) {
containerSpec.Mounts = append(containerSpec.Mounts, mount)
}
if c.Healthcheck != nil {
containerSpec.Healthcheck = healthConfigToGRPC(c.Healthcheck)
}
return containerSpec, nil
}
func healthConfigFromGRPC(h *swarmapi.HealthConfig) *container.HealthConfig {
interval, _ := ptypes.Duration(h.Interval)
timeout, _ := ptypes.Duration(h.Timeout)
return &container.HealthConfig{
Test: h.Test,
Interval: interval,
Timeout: timeout,
Retries: int(h.Retries),
}
}
func healthConfigToGRPC(h *container.HealthConfig) *swarmapi.HealthConfig {
return &swarmapi.HealthConfig{
Test: h.Test,
Interval: ptypes.DurationProto(h.Interval),
Timeout: ptypes.DurationProto(h.Timeout),
Retries: int32(h.Retries),
}
}

View file

@ -18,6 +18,7 @@ import (
"github.com/docker/docker/reference"
"github.com/docker/swarmkit/agent/exec"
"github.com/docker/swarmkit/api"
"github.com/docker/swarmkit/protobuf/ptypes"
)
const (
@ -124,12 +125,13 @@ func (c *containerConfig) image() string {
func (c *containerConfig) config() *enginecontainer.Config {
config := &enginecontainer.Config{
Labels: c.labels(),
User: c.spec().User,
Env: c.spec().Env,
WorkingDir: c.spec().Dir,
Image: c.image(),
Volumes: c.volumes(),
Labels: c.labels(),
User: c.spec().User,
Env: c.spec().Env,
WorkingDir: c.spec().Dir,
Image: c.image(),
Volumes: c.volumes(),
Healthcheck: c.healthcheck(),
}
if len(c.spec().Command) > 0 {
@ -224,6 +226,21 @@ func (c *containerConfig) binds() []string {
return r
}
func (c *containerConfig) healthcheck() *enginecontainer.HealthConfig {
hcSpec := c.spec().Healthcheck
if hcSpec == nil {
return nil
}
interval, _ := ptypes.Duration(hcSpec.Interval)
timeout, _ := ptypes.Duration(hcSpec.Timeout)
return &enginecontainer.HealthConfig{
Test: hcSpec.Test,
Interval: interval,
Timeout: timeout,
Retries: int(hcSpec.Retries),
}
}
func getMountMask(m *api.Mount) string {
var maskOpts []string
if m.ReadOnly {

View file

@ -27,6 +27,10 @@ Options:
-e, --env value Set environment variables (default [])
--env-file value Read in a file of environment variables (default [])
--group-add value Add additional user groups to the container (default [])
--health-cmd string Command to run to check health
--health-interval duration Time between running the check
--health-retries int Consecutive failures needed to report unhealthy
--health-timeout duration Maximum time to allow one check to run
--help Print usage
-l, --label value Service labels (default [])
--limit-cpu value Limit CPUs (default 0.000)
@ -37,6 +41,7 @@ Options:
--mount value Attach a mount to the service
--name string Service name
--network value Network attachments (default [])
--no-healthcheck Disable any container-specified HEALTHCHECK
-p, --publish value Publish a port as a node port (default [])
--replicas value Number of tasks (default none)
--reserve-cpu value Reserve CPUs (default 0.000)

View file

@ -32,6 +32,10 @@ Options:
--force Force update even if no changes require it
--group-add value Add additional user groups to the container (default [])
--group-rm value Remove previously added user groups from the container (default [])
--health-cmd string Command to run to check health
--health-interval duration Time between running the check
--health-retries int Consecutive failures needed to report unhealthy
--health-timeout duration Maximum time to allow one check to run
--help Print usage
--image string Service image tag
--label-add value Add or update service labels (default [])
@ -42,6 +46,7 @@ Options:
--log-opt value Logging driver options (default [])
--mount-add value Add or update a mount on a service
--mount-rm value Remove a mount by its target path (default [])
--no-healthcheck Disable any container-specified HEALTHCHECK
--publish-add value Add or update a published port (default [])
--publish-rm value Remove a published port by its target port (default [])
--replicas value Number of tasks (default none)

View file

@ -147,7 +147,7 @@ clone git github.com/docker/containerd 52ef1ceb4b660c42cf4ea9013180a5663968d4c7
clone git github.com/tonistiigi/fifo 8c56881ce5e63e19e2dfc495c8af0fb90916467d
# cluster
clone git github.com/docker/swarmkit 0ec7c6ee4b3185ec4e3d6bd65f8f5542b1761421
clone git github.com/docker/swarmkit 72981f443024da2c57d54b915eae0477be6dada5
clone git github.com/golang/mock bd3c8e81be01eef76d4b503f5e687d2d1354d2d9
clone git github.com/gogo/protobuf v0.3
clone git github.com/cloudflare/cfssl 7fb22c8cba7ecaf98e4082d22d65800cf45e042a

View file

@ -54,6 +54,13 @@ type ContainerStatuser interface {
ContainerStatus(ctx context.Context) (*api.ContainerStatus, error)
}
// PortStatuser reports status of ports which are allocated by the executor
type PortStatuser interface {
// PortStatus returns the status on a list of PortConfigs
// which are managed at the host level by the controller.
PortStatus(ctx context.Context) (*api.PortStatus, error)
}
// Resolve attempts to get a controller from the executor and reports the
// correct status depending on the tasks current state according to the result.
//
@ -131,6 +138,7 @@ func Do(ctx context.Context, task *api.Task, ctlr Controller) (*api.TaskStatus,
// this particular method. Eventually, we assemble this as part of a defer.
var (
containerStatus *api.ContainerStatus
portStatus *api.PortStatus
exitCode int
)
@ -230,6 +238,21 @@ func Do(ctx context.Context, task *api.Task, ctlr Controller) (*api.TaskStatus,
status.RuntimeStatus = &api.TaskStatus_Container{
Container: containerStatus,
}
if portStatus == nil {
pctlr, ok := ctlr.(PortStatuser)
if !ok {
return
}
var err error
portStatus, err = pctlr.PortStatus(ctx)
if err != nil && !contextDoneError(err) {
log.G(ctx).WithError(err).Error("container port status unavailable")
}
}
status.PortStatus = portStatus
}()
if task.DesiredState == api.TaskStateShutdown {

View file

@ -141,3 +141,35 @@ func (_m *MockContainerStatuser) ContainerStatus(ctx context.Context) (*api.Cont
func (_mr *_MockContainerStatuserRecorder) ContainerStatus(arg0 interface{}) *gomock.Call {
return _mr.mock.ctrl.RecordCall(_mr.mock, "ContainerStatus", arg0)
}
// Mock of PortStatuser interface
type MockPortStatuser struct {
ctrl *gomock.Controller
recorder *_MockPortStatuserRecorder
}
// Recorder for MockPortStatuser (not exported)
type _MockPortStatuserRecorder struct {
mock *MockPortStatuser
}
func NewMockPortStatuser(ctrl *gomock.Controller) *MockPortStatuser {
mock := &MockPortStatuser{ctrl: ctrl}
mock.recorder = &_MockPortStatuserRecorder{mock}
return mock
}
func (_m *MockPortStatuser) EXPECT() *_MockPortStatuserRecorder {
return _m.recorder
}
func (_m *MockPortStatuser) PortStatus(ctx context.Context) (*api.PortStatus, error) {
ret := _m.ctrl.Call(_m, "PortStatus", ctx)
ret0, _ := ret[0].(*api.PortStatus)
ret1, _ := ret[1].(error)
return ret0, ret1
}
func (_mr *_MockPortStatuserRecorder) PortStatus(arg0 interface{}) *gomock.Call {
return _mr.mock.ctrl.RecordCall(_mr.mock, "PortStatus", arg0)
}

View file

@ -489,6 +489,11 @@ type ContainerSpec struct {
Secrets []*SecretReference `protobuf:"bytes,12,rep,name=secrets" json:"secrets,omitempty"`
// DNSConfig allows one to specify DNS related configuration in resolv.conf
DNSConfig *ContainerSpec_DNSConfig `protobuf:"bytes,15,opt,name=dns_config,json=dnsConfig" json:"dns_config,omitempty"`
// Healthcheck describes how to check the container is healthy. If the
// container is considered unhealthy, it will be destroyed, its creating
// task will exit and a new task will be rescheduled elsewhere. A container
// is considered unhealthy after `Retries` number of consecutive failures.
Healthcheck *HealthConfig `protobuf:"bytes,16,opt,name=healthcheck" json:"healthcheck,omitempty"`
}
func (m *ContainerSpec) Reset() { *m = ContainerSpec{} }
@ -756,6 +761,7 @@ func (m *ContainerSpec) Copy() *ContainerSpec {
StopGracePeriod: m.StopGracePeriod.Copy(),
PullOptions: m.PullOptions.Copy(),
DNSConfig: m.DNSConfig.Copy(),
Healthcheck: m.Healthcheck.Copy(),
}
if m.Labels != nil {
@ -1035,7 +1041,7 @@ func (this *ContainerSpec) GoString() string {
if this == nil {
return "nil"
}
s := make([]string, 0, 19)
s := make([]string, 0, 20)
s = append(s, "&api.ContainerSpec{")
s = append(s, "Image: "+fmt.Sprintf("%#v", this.Image)+",\n")
keysForLabels := make([]string, 0, len(this.Labels))
@ -1074,6 +1080,9 @@ func (this *ContainerSpec) GoString() string {
if this.DNSConfig != nil {
s = append(s, "DNSConfig: "+fmt.Sprintf("%#v", this.DNSConfig)+",\n")
}
if this.Healthcheck != nil {
s = append(s, "Healthcheck: "+fmt.Sprintf("%#v", this.Healthcheck)+",\n")
}
s = append(s, "}")
return strings.Join(s, "")
}
@ -1681,6 +1690,18 @@ func (m *ContainerSpec) MarshalTo(data []byte) (int, error) {
}
i += n18
}
if m.Healthcheck != nil {
data[i] = 0x82
i++
data[i] = 0x1
i++
i = encodeVarintSpecs(data, i, uint64(m.Healthcheck.Size()))
n19, err := m.Healthcheck.MarshalTo(data[i:])
if err != nil {
return 0, err
}
i += n19
}
return i, nil
}
@ -1826,20 +1847,20 @@ func (m *NetworkSpec) MarshalTo(data []byte) (int, error) {
data[i] = 0xa
i++
i = encodeVarintSpecs(data, i, uint64(m.Annotations.Size()))
n19, err := m.Annotations.MarshalTo(data[i:])
n20, err := m.Annotations.MarshalTo(data[i:])
if err != nil {
return 0, err
}
i += n19
i += n20
if m.DriverConfig != nil {
data[i] = 0x12
i++
i = encodeVarintSpecs(data, i, uint64(m.DriverConfig.Size()))
n20, err := m.DriverConfig.MarshalTo(data[i:])
n21, err := m.DriverConfig.MarshalTo(data[i:])
if err != nil {
return 0, err
}
i += n20
i += n21
}
if m.Ipv6Enabled {
data[i] = 0x18
@ -1865,11 +1886,11 @@ func (m *NetworkSpec) MarshalTo(data []byte) (int, error) {
data[i] = 0x2a
i++
i = encodeVarintSpecs(data, i, uint64(m.IPAM.Size()))
n21, err := m.IPAM.MarshalTo(data[i:])
n22, err := m.IPAM.MarshalTo(data[i:])
if err != nil {
return 0, err
}
i += n21
i += n22
}
if m.Attachable {
data[i] = 0x30
@ -1902,59 +1923,59 @@ func (m *ClusterSpec) MarshalTo(data []byte) (int, error) {
data[i] = 0xa
i++
i = encodeVarintSpecs(data, i, uint64(m.Annotations.Size()))
n22, err := m.Annotations.MarshalTo(data[i:])
if err != nil {
return 0, err
}
i += n22
data[i] = 0x12
i++
i = encodeVarintSpecs(data, i, uint64(m.AcceptancePolicy.Size()))
n23, err := m.AcceptancePolicy.MarshalTo(data[i:])
n23, err := m.Annotations.MarshalTo(data[i:])
if err != nil {
return 0, err
}
i += n23
data[i] = 0x1a
data[i] = 0x12
i++
i = encodeVarintSpecs(data, i, uint64(m.Orchestration.Size()))
n24, err := m.Orchestration.MarshalTo(data[i:])
i = encodeVarintSpecs(data, i, uint64(m.AcceptancePolicy.Size()))
n24, err := m.AcceptancePolicy.MarshalTo(data[i:])
if err != nil {
return 0, err
}
i += n24
data[i] = 0x22
data[i] = 0x1a
i++
i = encodeVarintSpecs(data, i, uint64(m.Raft.Size()))
n25, err := m.Raft.MarshalTo(data[i:])
i = encodeVarintSpecs(data, i, uint64(m.Orchestration.Size()))
n25, err := m.Orchestration.MarshalTo(data[i:])
if err != nil {
return 0, err
}
i += n25
data[i] = 0x2a
data[i] = 0x22
i++
i = encodeVarintSpecs(data, i, uint64(m.Dispatcher.Size()))
n26, err := m.Dispatcher.MarshalTo(data[i:])
i = encodeVarintSpecs(data, i, uint64(m.Raft.Size()))
n26, err := m.Raft.MarshalTo(data[i:])
if err != nil {
return 0, err
}
i += n26
data[i] = 0x32
data[i] = 0x2a
i++
i = encodeVarintSpecs(data, i, uint64(m.CAConfig.Size()))
n27, err := m.CAConfig.MarshalTo(data[i:])
i = encodeVarintSpecs(data, i, uint64(m.Dispatcher.Size()))
n27, err := m.Dispatcher.MarshalTo(data[i:])
if err != nil {
return 0, err
}
i += n27
data[i] = 0x3a
data[i] = 0x32
i++
i = encodeVarintSpecs(data, i, uint64(m.TaskDefaults.Size()))
n28, err := m.TaskDefaults.MarshalTo(data[i:])
i = encodeVarintSpecs(data, i, uint64(m.CAConfig.Size()))
n28, err := m.CAConfig.MarshalTo(data[i:])
if err != nil {
return 0, err
}
i += n28
data[i] = 0x3a
i++
i = encodeVarintSpecs(data, i, uint64(m.TaskDefaults.Size()))
n29, err := m.TaskDefaults.MarshalTo(data[i:])
if err != nil {
return 0, err
}
i += n29
return i, nil
}
@ -1976,11 +1997,11 @@ func (m *SecretSpec) MarshalTo(data []byte) (int, error) {
data[i] = 0xa
i++
i = encodeVarintSpecs(data, i, uint64(m.Annotations.Size()))
n29, err := m.Annotations.MarshalTo(data[i:])
n30, err := m.Annotations.MarshalTo(data[i:])
if err != nil {
return 0, err
}
i += n29
i += n30
if len(m.Data) > 0 {
data[i] = 0x12
i++
@ -2235,6 +2256,10 @@ func (m *ContainerSpec) Size() (n int) {
l = m.DNSConfig.Size()
n += 1 + l + sovSpecs(uint64(l))
}
if m.Healthcheck != nil {
l = m.Healthcheck.Size()
n += 2 + l + sovSpecs(uint64(l))
}
return n
}
@ -2500,6 +2525,7 @@ func (this *ContainerSpec) String() string {
`TTY:` + fmt.Sprintf("%v", this.TTY) + `,`,
`Hostname:` + fmt.Sprintf("%v", this.Hostname) + `,`,
`DNSConfig:` + strings.Replace(fmt.Sprintf("%v", this.DNSConfig), "ContainerSpec_DNSConfig", "ContainerSpec_DNSConfig", 1) + `,`,
`Healthcheck:` + strings.Replace(fmt.Sprintf("%v", this.Healthcheck), "HealthConfig", "HealthConfig", 1) + `,`,
`}`,
}, "")
return s
@ -4047,6 +4073,39 @@ func (m *ContainerSpec) Unmarshal(data []byte) error {
return err
}
iNdEx = postIndex
case 16:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Healthcheck", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowSpecs
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := data[iNdEx]
iNdEx++
msglen |= (int(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthSpecs
}
postIndex := iNdEx + msglen
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.Healthcheck == nil {
m.Healthcheck = &HealthConfig{}
}
if err := m.Healthcheck.Unmarshal(data[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipSpecs(data[iNdEx:])
@ -5069,103 +5128,105 @@ var (
func init() { proto.RegisterFile("specs.proto", fileDescriptorSpecs) }
var fileDescriptorSpecs = []byte{
// 1563 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xac, 0x57, 0xcd, 0x6e, 0x23, 0xc7,
0x11, 0xe6, 0x88, 0x14, 0x7f, 0x6a, 0xc8, 0x5d, 0x6e, 0xc3, 0x3f, 0xb3, 0xb4, 0x43, 0x72, 0xe9,
0x8d, 0x23, 0xc7, 0x88, 0x36, 0x61, 0x02, 0x67, 0x9d, 0x8d, 0x91, 0xf0, 0x2f, 0x5a, 0x46, 0x91,
0x4c, 0xb4, 0xe4, 0x05, 0xf6, 0x44, 0xb4, 0x66, 0x5a, 0xe4, 0x40, 0xc3, 0xe9, 0x49, 0x4f, 0x0f,
0x0d, 0xdd, 0x72, 0x34, 0xf6, 0x90, 0x37, 0xd0, 0x29, 0x40, 0xde, 0x20, 0xef, 0xb0, 0xc7, 0x1c,
0x73, 0x12, 0x2c, 0x3e, 0x41, 0x80, 0xbc, 0x40, 0xd0, 0x3d, 0x3d, 0xe4, 0x30, 0x1e, 0x59, 0x06,
0xa2, 0x5b, 0x77, 0xcd, 0xf7, 0x55, 0x77, 0x57, 0x7d, 0xac, 0x2a, 0x82, 0x19, 0x06, 0xd4, 0x0e,
// 1586 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xac, 0x57, 0xdd, 0x6e, 0xe3, 0xc6,
0x15, 0x16, 0x2d, 0x59, 0x3f, 0x87, 0xd2, 0xae, 0x76, 0x90, 0x1f, 0xae, 0x92, 0x4a, 0x5a, 0x65,
0x9b, 0x3a, 0x0d, 0xea, 0x6d, 0xd5, 0x22, 0xdd, 0x74, 0x1b, 0xb4, 0xfa, 0xab, 0x57, 0x75, 0xed,
0x08, 0x63, 0x67, 0x81, 0xbd, 0x12, 0xc6, 0xe4, 0x58, 0x22, 0x4c, 0x71, 0xd8, 0xe1, 0x50, 0x81,
0xef, 0x7a, 0x19, 0xec, 0x45, 0xdf, 0xc0, 0x57, 0x2d, 0xfa, 0x06, 0x7d, 0x87, 0xbd, 0xec, 0x65,
0xaf, 0x8c, 0x5a, 0x4f, 0x50, 0xa0, 0x2f, 0x50, 0xcc, 0x70, 0x28, 0x51, 0x0d, 0x1d, 0x07, 0xa8,
0xef, 0x66, 0x0e, 0xbf, 0xef, 0x70, 0xe6, 0x9c, 0x8f, 0xe7, 0x1c, 0x82, 0x19, 0x06, 0xd4, 0x0e,
0xf7, 0x03, 0xce, 0x04, 0x43, 0xc8, 0x61, 0xf6, 0x05, 0xe5, 0xfb, 0xe1, 0xd7, 0x84, 0x2f, 0x2e,
0x5c, 0xb1, 0xbf, 0xfc, 0x45, 0xc3, 0x14, 0x97, 0x01, 0xd5, 0x80, 0xc6, 0x3b, 0x33, 0x36, 0x63,
0x5c, 0xb1, 0xbf, 0xfc, 0x59, 0xc3, 0x14, 0x97, 0x01, 0xd5, 0x80, 0xc6, 0x3b, 0x33, 0x36, 0x63,
0x6a, 0xf9, 0x4c, 0xae, 0xb4, 0xf5, 0x7d, 0x27, 0xe2, 0x44, 0xb8, 0xcc, 0x7f, 0x96, 0x2c, 0xe2,
0x0f, 0x9d, 0xbf, 0x16, 0xa0, 0x7c, 0xcc, 0x1c, 0x7a, 0x12, 0x50, 0x1b, 0x1d, 0x80, 0x49, 0x7c,
0x9f, 0x09, 0x05, 0x08, 0x2d, 0xa3, 0x6d, 0xec, 0x99, 0xdd, 0xd6, 0xfe, 0x77, 0x8f, 0xdc, 0xef,
0x6d, 0x60, 0xfd, 0xc2, 0xdb, 0xeb, 0x56, 0x0e, 0xa7, 0x99, 0xe8, 0xe7, 0x50, 0xe0, 0xcc, 0xa3,
0xd6, 0x4e, 0xdb, 0xd8, 0x7b, 0xd0, 0xfd, 0x30, 0xcb, 0x83, 0x3c, 0x14, 0x33, 0x8f, 0x62, 0x85,
0x44, 0x07, 0x00, 0x0b, 0xba, 0x38, 0xa3, 0x3c, 0x9c, 0xbb, 0x81, 0x95, 0x57, 0xbc, 0x9f, 0xdc,
0xc6, 0x93, 0x97, 0xdd, 0x3f, 0x5a, 0xc3, 0x71, 0x8a, 0x8a, 0x8e, 0xa0, 0x4a, 0x96, 0xc4, 0xf5,
0xc8, 0x99, 0xeb, 0xb9, 0xe2, 0xd2, 0x2a, 0x28, 0x57, 0x9f, 0x7c, 0xaf, 0xab, 0x5e, 0x8a, 0x80,
0xb7, 0xe8, 0x1d, 0x07, 0x60, 0x73, 0x10, 0xfa, 0x18, 0x4a, 0x93, 0xd1, 0xf1, 0x70, 0x7c, 0x7c,
0x50, 0xcf, 0x35, 0x1e, 0xbf, 0xb9, 0x6a, 0xbf, 0x2b, 0x7d, 0x6c, 0x00, 0x13, 0xea, 0x3b, 0xae,
0x3f, 0x43, 0x7b, 0x50, 0xee, 0x0d, 0x06, 0xa3, 0xc9, 0xe9, 0x68, 0x58, 0x37, 0x1a, 0x8d, 0x37,
0x57, 0xed, 0xf7, 0xb6, 0x81, 0x3d, 0xdb, 0xa6, 0x81, 0xa0, 0x4e, 0xa3, 0xf0, 0xcd, 0xdf, 0x9a,
0xb9, 0xce, 0x37, 0x06, 0x54, 0xd3, 0x97, 0x40, 0x1f, 0x43, 0xb1, 0x37, 0x38, 0x1d, 0xbf, 0x1a,
0xd5, 0x73, 0x1b, 0x7a, 0x1a, 0xd1, 0xb3, 0x85, 0xbb, 0xa4, 0xe8, 0x29, 0xec, 0x4e, 0x7a, 0x5f,
0x9d, 0x8c, 0xea, 0xc6, 0xe6, 0x3a, 0x69, 0xd8, 0x84, 0x44, 0xa1, 0x42, 0x0d, 0x71, 0x6f, 0x7c,
0x5c, 0xdf, 0xc9, 0x46, 0x0d, 0x39, 0x71, 0x7d, 0x7d, 0x95, 0x9b, 0x3c, 0x98, 0x27, 0x94, 0x2f,
0x5d, 0xfb, 0x9e, 0x35, 0xf1, 0x19, 0x14, 0x04, 0x09, 0x2f, 0x94, 0x26, 0xcc, 0x6c, 0x4d, 0x9c,
0x92, 0xf0, 0x42, 0x1e, 0xaa, 0xe9, 0x0a, 0x2f, 0x95, 0xc1, 0x69, 0xe0, 0xb9, 0x36, 0x11, 0xd4,
0x51, 0xca, 0x30, 0xbb, 0x3f, 0xce, 0x62, 0xe3, 0x35, 0x4a, 0xdf, 0xff, 0x65, 0x0e, 0xa7, 0xa8,
0xe8, 0x05, 0x14, 0x67, 0x1e, 0x3b, 0x23, 0x9e, 0xd2, 0x84, 0xd9, 0x7d, 0x92, 0xe5, 0xe4, 0x40,
0x21, 0x36, 0x0e, 0x34, 0x05, 0x3d, 0x87, 0x62, 0x14, 0x38, 0x44, 0x50, 0xab, 0xa8, 0xc8, 0xed,
0x2c, 0xf2, 0x57, 0x0a, 0x31, 0x60, 0xfe, 0xb9, 0x3b, 0xc3, 0x1a, 0x8f, 0x0e, 0xa1, 0xec, 0x53,
0xf1, 0x35, 0xe3, 0x17, 0xa1, 0x55, 0x6a, 0xe7, 0xf7, 0xcc, 0xee, 0xa7, 0x99, 0x62, 0x8c, 0x31,
0x3d, 0x21, 0x88, 0x3d, 0x5f, 0x50, 0x5f, 0xc4, 0x6e, 0xfa, 0x3b, 0x96, 0x81, 0xd7, 0x0e, 0xd0,
0x6f, 0xa1, 0x4c, 0x7d, 0x27, 0x60, 0xae, 0x2f, 0xac, 0xf2, 0xed, 0x17, 0x19, 0x69, 0x8c, 0x0c,
0x26, 0x5e, 0x33, 0xfa, 0x45, 0x28, 0x2c, 0x98, 0x43, 0x3b, 0xcf, 0xe0, 0xd1, 0x77, 0x82, 0x85,
0x1a, 0x50, 0xd6, 0xc1, 0x8a, 0xb3, 0x5c, 0xc0, 0xeb, 0x7d, 0xe7, 0x21, 0xd4, 0xb6, 0x02, 0xa3,
0xca, 0x46, 0x92, 0x2d, 0xd4, 0x83, 0x8a, 0xcd, 0x7c, 0x41, 0x5c, 0x9f, 0x72, 0x2d, 0x90, 0xcc,
0xd8, 0x0e, 0x12, 0x90, 0x64, 0xbd, 0xcc, 0xe1, 0x0d, 0x0b, 0xfd, 0x01, 0x2a, 0x9c, 0x86, 0x2c,
0xe2, 0x36, 0x0d, 0xb5, 0x42, 0xf6, 0xb2, 0x73, 0x1c, 0x83, 0x30, 0xfd, 0x73, 0xe4, 0x72, 0x2a,
0xe3, 0x14, 0xe2, 0x0d, 0x15, 0xbd, 0x80, 0x12, 0xa7, 0xa1, 0x20, 0x5c, 0x7c, 0x5f, 0x92, 0x71,
0x0c, 0x99, 0x30, 0xcf, 0xb5, 0x2f, 0x71, 0xc2, 0x40, 0x2f, 0xa0, 0x12, 0x78, 0xc4, 0x56, 0x5e,
0xad, 0x5d, 0x45, 0xff, 0x51, 0x16, 0x7d, 0x92, 0x80, 0xf0, 0x06, 0x8f, 0x3e, 0x07, 0xf0, 0xd8,
0x6c, 0xea, 0x70, 0x77, 0x49, 0xb9, 0x16, 0x49, 0x23, 0x8b, 0x3d, 0x54, 0x08, 0x5c, 0xf1, 0xd8,
0x2c, 0x5e, 0xa2, 0x83, 0xff, 0x4b, 0x21, 0x29, 0x75, 0x1c, 0x02, 0x90, 0xf5, 0x57, 0xad, 0x8f,
0x4f, 0x7e, 0x90, 0x2b, 0x9d, 0x91, 0x14, 0x1d, 0x3d, 0x81, 0xea, 0x39, 0xe3, 0x36, 0x9d, 0x6a,
0xdd, 0x57, 0x94, 0x26, 0x4c, 0x65, 0x8b, 0x85, 0xde, 0xaf, 0x40, 0x89, 0x47, 0xbe, 0x70, 0x17,
0xb4, 0x73, 0x08, 0xef, 0x66, 0x3a, 0x45, 0x5d, 0xa8, 0xae, 0xd3, 0x3c, 0x75, 0x1d, 0xa5, 0x8f,
0x4a, 0xff, 0xe1, 0xea, 0xba, 0x65, 0xae, 0xf5, 0x30, 0x1e, 0x62, 0x73, 0x0d, 0x1a, 0x3b, 0x9d,
0x6f, 0x8b, 0x50, 0xdb, 0x12, 0x0b, 0x7a, 0x07, 0x76, 0xdd, 0x05, 0x99, 0xd1, 0x98, 0x8e, 0xe3,
0x0d, 0x1a, 0x41, 0xd1, 0x23, 0x67, 0xd4, 0x93, 0x92, 0x91, 0x61, 0xfb, 0xd9, 0x9d, 0xaa, 0xdb,
0xff, 0x93, 0xc2, 0x8f, 0x7c, 0xc1, 0x2f, 0xb1, 0x26, 0x23, 0x0b, 0x4a, 0x36, 0x5b, 0x2c, 0x88,
0x2f, 0xcb, 0x4b, 0x7e, 0xaf, 0x82, 0x93, 0x2d, 0x42, 0x50, 0x20, 0x7c, 0x16, 0x5a, 0x05, 0x65,
0x56, 0x6b, 0x54, 0x87, 0x3c, 0xf5, 0x97, 0xd6, 0xae, 0x32, 0xc9, 0xa5, 0xb4, 0x38, 0x6e, 0x9c,
0xf3, 0x0a, 0x96, 0x4b, 0xc9, 0x8b, 0x42, 0xca, 0xad, 0x92, 0x32, 0xa9, 0x35, 0xfa, 0x35, 0x14,
0x17, 0x2c, 0xf2, 0x45, 0x68, 0x95, 0xd5, 0x65, 0x1f, 0x67, 0x5d, 0xf6, 0x48, 0x22, 0x74, 0xf9,
0xd3, 0x70, 0xf4, 0x12, 0x1e, 0x85, 0x82, 0x05, 0xd3, 0x19, 0x27, 0x36, 0x9d, 0x06, 0x94, 0xbb,
0xcc, 0x51, 0xd9, 0xb8, 0xa5, 0x8a, 0x0e, 0x75, 0x87, 0xc7, 0x0f, 0x25, 0xed, 0x40, 0xb2, 0x26,
0x8a, 0x84, 0x26, 0x50, 0x0d, 0x22, 0xcf, 0x9b, 0xb2, 0x20, 0x2e, 0xe6, 0xa0, 0x9c, 0xfc, 0x80,
0xa8, 0x4d, 0x22, 0xcf, 0xfb, 0x32, 0x26, 0x61, 0x33, 0xd8, 0x6c, 0xd0, 0x7b, 0x50, 0x9c, 0x71,
0x16, 0x05, 0xa1, 0x65, 0xaa, 0x78, 0xe8, 0x1d, 0xfa, 0x02, 0x4a, 0x21, 0xb5, 0x39, 0x15, 0xa1,
0x55, 0x55, 0xaf, 0xfd, 0x28, 0xeb, 0x90, 0x13, 0x05, 0xc1, 0xf4, 0x9c, 0x72, 0xea, 0xdb, 0x14,
0x27, 0x1c, 0xf4, 0x18, 0xf2, 0x42, 0x5c, 0x5a, 0xb5, 0xb6, 0xb1, 0x57, 0xee, 0x97, 0x56, 0xd7,
0xad, 0xfc, 0xe9, 0xe9, 0x6b, 0x2c, 0x6d, 0xb2, 0x4c, 0xcd, 0x59, 0x28, 0x7c, 0xb2, 0xa0, 0xd6,
0x03, 0x15, 0xde, 0xf5, 0x1e, 0xbd, 0x06, 0x70, 0xfc, 0x70, 0x6a, 0xab, 0xdf, 0x85, 0xf5, 0x50,
0xbd, 0xee, 0xd3, 0xbb, 0x5f, 0x37, 0x3c, 0x3e, 0xd1, 0xc5, 0xb6, 0xb6, 0xba, 0x6e, 0x55, 0xd6,
0x5b, 0x5c, 0x71, 0xfc, 0x30, 0x5e, 0x36, 0x3e, 0x07, 0x33, 0x25, 0x1d, 0x99, 0xf2, 0x0b, 0x7a,
0xa9, 0xd5, 0x28, 0x97, 0x52, 0xa1, 0x4b, 0xe2, 0x45, 0xf1, 0xcc, 0x53, 0xc1, 0xf1, 0xe6, 0x37,
0x3b, 0xcf, 0x8d, 0x46, 0x17, 0xcc, 0x54, 0xfc, 0xd0, 0x47, 0x50, 0xe3, 0x74, 0xe6, 0x86, 0x82,
0x5f, 0x4e, 0x49, 0x24, 0xe6, 0xd6, 0xef, 0x15, 0xa1, 0x9a, 0x18, 0x7b, 0x91, 0x98, 0x37, 0xa6,
0xb0, 0xb9, 0x06, 0x6a, 0x83, 0x29, 0x9f, 0x17, 0x52, 0xbe, 0xa4, 0x5c, 0x16, 0x67, 0x19, 0xe9,
0xb4, 0x49, 0xa6, 0x21, 0xa4, 0x84, 0xdb, 0x73, 0xf5, 0x43, 0xa8, 0x60, 0xbd, 0x93, 0xca, 0x4e,
0x72, 0xad, 0x95, 0xad, 0xb7, 0x9d, 0xff, 0x18, 0x50, 0x4d, 0x77, 0x09, 0x34, 0x88, 0x7b, 0x83,
0x7a, 0xd2, 0x83, 0xee, 0xb3, 0xbb, 0xba, 0x8a, 0xaa, 0xc4, 0x5e, 0x24, 0x9d, 0x1d, 0xc9, 0x49,
0x4e, 0x91, 0xd1, 0xaf, 0x60, 0x37, 0x60, 0x5c, 0x24, 0xbf, 0xc7, 0x66, 0x66, 0xf5, 0x64, 0x3c,
0xa9, 0x5c, 0x31, 0xb8, 0x33, 0x87, 0x07, 0xdb, 0xde, 0xd0, 0x53, 0xc8, 0xbf, 0x1a, 0x4f, 0xea,
0xb9, 0xc6, 0x07, 0x6f, 0xae, 0xda, 0xef, 0x6f, 0x7f, 0x7c, 0xe5, 0x72, 0x11, 0x11, 0x6f, 0x3c,
0x41, 0x3f, 0x85, 0xdd, 0xe1, 0xf1, 0x09, 0xc6, 0x75, 0xa3, 0xd1, 0x7a, 0x73, 0xd5, 0xfe, 0x60,
0x1b, 0x27, 0x3f, 0xb1, 0xc8, 0x77, 0x30, 0x3b, 0x5b, 0x0f, 0x37, 0xff, 0xd8, 0x01, 0x53, 0x97,
0xa9, 0xfb, 0x1d, 0x6e, 0x7e, 0x07, 0xb5, 0xb8, 0xf2, 0x27, 0xe2, 0xdb, 0xb9, 0xb3, 0x01, 0x54,
0x63, 0x82, 0xce, 0xf1, 0x13, 0xa8, 0xba, 0xc1, 0xf2, 0xb3, 0x29, 0xf5, 0xc9, 0x99, 0xa7, 0xe7,
0x9c, 0x32, 0x36, 0xa5, 0x6d, 0x14, 0x9b, 0xa4, 0xf2, 0x5d, 0x5f, 0x50, 0xee, 0xeb, 0x09, 0xa6,
0x8c, 0xd7, 0x7b, 0xf4, 0x05, 0x14, 0xdc, 0x80, 0x2c, 0x74, 0xd7, 0xca, 0x7c, 0xc1, 0x78, 0xd2,
0x3b, 0xd2, 0x1a, 0xec, 0x97, 0x57, 0xd7, 0xad, 0x82, 0x34, 0x60, 0x45, 0x43, 0xcd, 0xa4, 0x71,
0xc8, 0x93, 0x54, 0x21, 0x2b, 0xe3, 0x94, 0xa5, 0xf3, 0xf7, 0x02, 0x98, 0x03, 0x2f, 0x0a, 0x85,
0x2e, 0xc7, 0xf7, 0x16, 0xb7, 0xd7, 0xf0, 0x88, 0xa8, 0x51, 0x98, 0xf8, 0xb2, 0xb6, 0xa9, 0x86,
0xac, 0x63, 0xf7, 0x34, 0xd3, 0xdd, 0x1a, 0x1c, 0x37, 0xef, 0x7e, 0x51, 0xfa, 0xb4, 0x0c, 0x5c,
0x27, 0xff, 0xf3, 0x05, 0x9d, 0x40, 0x8d, 0x71, 0x7b, 0x4e, 0x43, 0x11, 0x97, 0x43, 0x3d, 0x3a,
0x66, 0xfe, 0xa9, 0xf8, 0x32, 0x0d, 0xd4, 0xb5, 0x20, 0xbe, 0xed, 0xb6, 0x0f, 0xf4, 0x1c, 0x0a,
0x9c, 0x9c, 0x27, 0xc3, 0x45, 0xa6, 0xbe, 0x31, 0x39, 0x17, 0x5b, 0x2e, 0x14, 0x03, 0xfd, 0x11,
0xc0, 0x71, 0xc3, 0x80, 0x08, 0x7b, 0x4e, 0xb9, 0xce, 0x53, 0xe6, 0x13, 0x87, 0x6b, 0xd4, 0x96,
0x97, 0x14, 0x1b, 0x1d, 0x42, 0xc5, 0x26, 0x89, 0xd2, 0x8a, 0xb7, 0x77, 0x82, 0x41, 0x4f, 0xbb,
0xa8, 0x4b, 0x17, 0xab, 0xeb, 0x56, 0x39, 0xb1, 0xe0, 0xb2, 0x4d, 0xb4, 0xf2, 0x0e, 0xa1, 0x26,
0xe7, 0xec, 0xa9, 0x43, 0xcf, 0x49, 0xe4, 0x89, 0x50, 0x35, 0xad, 0x5b, 0xe6, 0x4a, 0x39, 0xf2,
0x0d, 0x35, 0x4e, 0xdf, 0xab, 0x2a, 0x52, 0xb6, 0x8e, 0x0b, 0x10, 0x17, 0xf5, 0xfb, 0x95, 0x09,
0x82, 0x82, 0x43, 0x04, 0x51, 0xca, 0xa8, 0x62, 0xb5, 0xee, 0x7f, 0xf8, 0xf6, 0xa6, 0x99, 0xfb,
0xd7, 0x4d, 0x33, 0xf7, 0xef, 0x9b, 0xa6, 0xf1, 0x97, 0x55, 0xd3, 0x78, 0xbb, 0x6a, 0x1a, 0xff,
0x5c, 0x35, 0x8d, 0x6f, 0x57, 0x4d, 0xe3, 0xac, 0xa8, 0xfe, 0xde, 0xfe, 0xf2, 0xbf, 0x01, 0x00,
0x00, 0xff, 0xff, 0x2a, 0x90, 0x7c, 0x40, 0x3d, 0x0f, 0x00, 0x00,
0x07, 0x9d, 0x3f, 0x17, 0xa0, 0x7c, 0xcc, 0x1c, 0x7a, 0x12, 0x50, 0x1b, 0x1d, 0x80, 0x49, 0x7c,
0x9f, 0x09, 0x05, 0x08, 0x2d, 0xa3, 0x6d, 0xec, 0x99, 0xdd, 0xd6, 0xfe, 0xb7, 0x5f, 0xb9, 0xdf,
0xdb, 0xc0, 0xfa, 0x85, 0xb7, 0xd7, 0xad, 0x1c, 0x4e, 0x33, 0xd1, 0x4f, 0xa1, 0xc0, 0x99, 0x47,
0xad, 0x9d, 0xb6, 0xb1, 0xf7, 0xa0, 0xfb, 0x61, 0x96, 0x07, 0xf9, 0x52, 0xcc, 0x3c, 0x8a, 0x15,
0x12, 0x1d, 0x00, 0x2c, 0xe8, 0xe2, 0x8c, 0xf2, 0x70, 0xee, 0x06, 0x56, 0x5e, 0xf1, 0x7e, 0x74,
0x1b, 0x4f, 0x1e, 0x76, 0xff, 0x68, 0x0d, 0xc7, 0x29, 0x2a, 0x3a, 0x82, 0x2a, 0x59, 0x12, 0xd7,
0x23, 0x67, 0xae, 0xe7, 0x8a, 0x4b, 0xab, 0xa0, 0x5c, 0x7d, 0xf2, 0x9d, 0xae, 0x7a, 0x29, 0x02,
0xde, 0xa2, 0x77, 0x1c, 0x80, 0xcd, 0x8b, 0xd0, 0xc7, 0x50, 0x9a, 0x8c, 0x8e, 0x87, 0xe3, 0xe3,
0x83, 0x7a, 0xae, 0xf1, 0xf8, 0xcd, 0x55, 0xfb, 0x5d, 0xe9, 0x63, 0x03, 0x98, 0x50, 0xdf, 0x71,
0xfd, 0x19, 0xda, 0x83, 0x72, 0x6f, 0x30, 0x18, 0x4d, 0x4e, 0x47, 0xc3, 0xba, 0xd1, 0x68, 0xbc,
0xb9, 0x6a, 0xbf, 0xb7, 0x0d, 0xec, 0xd9, 0x36, 0x0d, 0x04, 0x75, 0x1a, 0x85, 0x6f, 0xfe, 0xd2,
0xcc, 0x75, 0xbe, 0x31, 0xa0, 0x9a, 0x3e, 0x04, 0xfa, 0x18, 0x8a, 0xbd, 0xc1, 0xe9, 0xf8, 0xd5,
0xa8, 0x9e, 0xdb, 0xd0, 0xd3, 0x88, 0x9e, 0x2d, 0xdc, 0x25, 0x45, 0x4f, 0x61, 0x77, 0xd2, 0xfb,
0xea, 0x64, 0x54, 0x37, 0x36, 0xc7, 0x49, 0xc3, 0x26, 0x24, 0x0a, 0x15, 0x6a, 0x88, 0x7b, 0xe3,
0xe3, 0xfa, 0x4e, 0x36, 0x6a, 0xc8, 0x89, 0xeb, 0xeb, 0xa3, 0xdc, 0xe4, 0xc1, 0x3c, 0xa1, 0x7c,
0xe9, 0xda, 0xf7, 0xac, 0x89, 0xcf, 0xa0, 0x20, 0x48, 0x78, 0xa1, 0x34, 0x61, 0x66, 0x6b, 0xe2,
0x94, 0x84, 0x17, 0xf2, 0xa5, 0x9a, 0xae, 0xf0, 0x52, 0x19, 0x9c, 0x06, 0x9e, 0x6b, 0x13, 0x41,
0x1d, 0xa5, 0x0c, 0xb3, 0xfb, 0xc3, 0x2c, 0x36, 0x5e, 0xa3, 0xf4, 0xf9, 0x5f, 0xe6, 0x70, 0x8a,
0x8a, 0x5e, 0x40, 0x71, 0xe6, 0xb1, 0x33, 0xe2, 0x29, 0x4d, 0x98, 0xdd, 0x27, 0x59, 0x4e, 0x0e,
0x14, 0x62, 0xe3, 0x40, 0x53, 0xd0, 0x73, 0x28, 0x46, 0x81, 0x43, 0x04, 0xb5, 0x8a, 0x8a, 0xdc,
0xce, 0x22, 0x7f, 0xa5, 0x10, 0x03, 0xe6, 0x9f, 0xbb, 0x33, 0xac, 0xf1, 0xe8, 0x10, 0xca, 0x3e,
0x15, 0x5f, 0x33, 0x7e, 0x11, 0x5a, 0xa5, 0x76, 0x7e, 0xcf, 0xec, 0x7e, 0x9a, 0x29, 0xc6, 0x18,
0xd3, 0x13, 0x82, 0xd8, 0xf3, 0x05, 0xf5, 0x45, 0xec, 0xa6, 0xbf, 0x63, 0x19, 0x78, 0xed, 0x00,
0xfd, 0x1a, 0xca, 0xd4, 0x77, 0x02, 0xe6, 0xfa, 0xc2, 0x2a, 0xdf, 0x7e, 0x90, 0x91, 0xc6, 0xc8,
0x60, 0xe2, 0x35, 0xa3, 0x5f, 0x84, 0xc2, 0x82, 0x39, 0xb4, 0xf3, 0x0c, 0x1e, 0x7d, 0x2b, 0x58,
0xa8, 0x01, 0x65, 0x1d, 0xac, 0x38, 0xcb, 0x05, 0xbc, 0xde, 0x77, 0x1e, 0x42, 0x6d, 0x2b, 0x30,
0xaa, 0x6c, 0x24, 0xd9, 0x42, 0x3d, 0xa8, 0xd8, 0xcc, 0x17, 0xc4, 0xf5, 0x29, 0xd7, 0x02, 0xc9,
0x8c, 0xed, 0x20, 0x01, 0x49, 0xd6, 0xcb, 0x1c, 0xde, 0xb0, 0xd0, 0xef, 0xa0, 0xc2, 0x69, 0xc8,
0x22, 0x6e, 0xd3, 0x50, 0x2b, 0x64, 0x2f, 0x3b, 0xc7, 0x31, 0x08, 0xd3, 0x3f, 0x46, 0x2e, 0xa7,
0x32, 0x4e, 0x21, 0xde, 0x50, 0xd1, 0x0b, 0x28, 0x71, 0x1a, 0x0a, 0xc2, 0xc5, 0x77, 0x25, 0x19,
0xc7, 0x90, 0x09, 0xf3, 0x5c, 0xfb, 0x12, 0x27, 0x0c, 0xf4, 0x02, 0x2a, 0x81, 0x47, 0x6c, 0xe5,
0xd5, 0xda, 0x55, 0xf4, 0x1f, 0x64, 0xd1, 0x27, 0x09, 0x08, 0x6f, 0xf0, 0xe8, 0x73, 0x00, 0x8f,
0xcd, 0xa6, 0x0e, 0x77, 0x97, 0x94, 0x6b, 0x91, 0x34, 0xb2, 0xd8, 0x43, 0x85, 0xc0, 0x15, 0x8f,
0xcd, 0xe2, 0x25, 0x3a, 0xf8, 0xbf, 0x14, 0x92, 0x52, 0xc7, 0x21, 0x00, 0x59, 0x3f, 0xd5, 0xfa,
0xf8, 0xe4, 0x7b, 0xb9, 0xd2, 0x19, 0x49, 0xd1, 0xd1, 0x13, 0xa8, 0x9e, 0x33, 0x6e, 0xd3, 0xa9,
0xd6, 0x7d, 0x45, 0x69, 0xc2, 0x54, 0xb6, 0x58, 0xe8, 0xfd, 0x0a, 0x94, 0x78, 0xe4, 0x0b, 0x77,
0x41, 0x3b, 0x87, 0xf0, 0x6e, 0xa6, 0x53, 0xd4, 0x85, 0xea, 0x3a, 0xcd, 0x53, 0xd7, 0x51, 0xfa,
0xa8, 0xf4, 0x1f, 0xae, 0xae, 0x5b, 0xe6, 0x5a, 0x0f, 0xe3, 0x21, 0x36, 0xd7, 0xa0, 0xb1, 0xd3,
0xf9, 0x6b, 0x09, 0x6a, 0x5b, 0x62, 0x41, 0xef, 0xc0, 0xae, 0xbb, 0x20, 0x33, 0x1a, 0xd3, 0x71,
0xbc, 0x41, 0x23, 0x28, 0x7a, 0xe4, 0x8c, 0x7a, 0x52, 0x32, 0x32, 0x6c, 0x3f, 0xb9, 0x53, 0x75,
0xfb, 0x7f, 0x50, 0xf8, 0x91, 0x2f, 0xf8, 0x25, 0xd6, 0x64, 0x64, 0x41, 0xc9, 0x66, 0x8b, 0x05,
0xf1, 0x65, 0x79, 0xc9, 0xef, 0x55, 0x70, 0xb2, 0x45, 0x08, 0x0a, 0x84, 0xcf, 0x42, 0xab, 0xa0,
0xcc, 0x6a, 0x8d, 0xea, 0x90, 0xa7, 0xfe, 0xd2, 0xda, 0x55, 0x26, 0xb9, 0x94, 0x16, 0xc7, 0x8d,
0x73, 0x5e, 0xc1, 0x72, 0x29, 0x79, 0x51, 0x48, 0xb9, 0x55, 0x52, 0x26, 0xb5, 0x46, 0xbf, 0x84,
0xe2, 0x82, 0x45, 0xbe, 0x08, 0xad, 0xb2, 0x3a, 0xec, 0xe3, 0xac, 0xc3, 0x1e, 0x49, 0x84, 0x2e,
0x7f, 0x1a, 0x8e, 0x5e, 0xc2, 0xa3, 0x50, 0xb0, 0x60, 0x3a, 0xe3, 0xc4, 0xa6, 0xd3, 0x80, 0x72,
0x97, 0x39, 0x2a, 0x1b, 0xb7, 0x54, 0xd1, 0xa1, 0xee, 0xf0, 0xf8, 0xa1, 0xa4, 0x1d, 0x48, 0xd6,
0x44, 0x91, 0xd0, 0x04, 0xaa, 0x41, 0xe4, 0x79, 0x53, 0x16, 0xc4, 0xc5, 0x1c, 0x94, 0x93, 0xef,
0x11, 0xb5, 0x49, 0xe4, 0x79, 0x5f, 0xc6, 0x24, 0x6c, 0x06, 0x9b, 0x0d, 0x7a, 0x0f, 0x8a, 0x33,
0xce, 0xa2, 0x20, 0xb4, 0x4c, 0x15, 0x0f, 0xbd, 0x43, 0x5f, 0x40, 0x29, 0xa4, 0x36, 0xa7, 0x22,
0xb4, 0xaa, 0xea, 0xb6, 0x1f, 0x65, 0xbd, 0xe4, 0x44, 0x41, 0x30, 0x3d, 0xa7, 0x9c, 0xfa, 0x36,
0xc5, 0x09, 0x07, 0x3d, 0x86, 0xbc, 0x10, 0x97, 0x56, 0xad, 0x6d, 0xec, 0x95, 0xfb, 0xa5, 0xd5,
0x75, 0x2b, 0x7f, 0x7a, 0xfa, 0x1a, 0x4b, 0x9b, 0x2c, 0x53, 0x73, 0x16, 0x0a, 0x9f, 0x2c, 0xa8,
0xf5, 0x40, 0x85, 0x77, 0xbd, 0x47, 0xaf, 0x01, 0x1c, 0x3f, 0x9c, 0xda, 0xea, 0xbb, 0xb0, 0x1e,
0xaa, 0xdb, 0x7d, 0x7a, 0xf7, 0xed, 0x86, 0xc7, 0x27, 0xba, 0xd8, 0xd6, 0x56, 0xd7, 0xad, 0xca,
0x7a, 0x8b, 0x2b, 0x8e, 0x1f, 0xc6, 0x4b, 0xd4, 0x07, 0x73, 0x4e, 0x89, 0x27, 0xe6, 0xf6, 0x9c,
0xda, 0x17, 0x56, 0xfd, 0xf6, 0xda, 0xfb, 0x52, 0xc1, 0xb4, 0x87, 0x34, 0xa9, 0xf1, 0x39, 0x98,
0x29, 0xf9, 0x49, 0xd9, 0x5c, 0xd0, 0x4b, 0xad, 0x68, 0xb9, 0x94, 0x2a, 0x5f, 0x12, 0x2f, 0x8a,
0xe7, 0xa6, 0x0a, 0x8e, 0x37, 0xbf, 0xda, 0x79, 0x6e, 0x34, 0xba, 0x60, 0xa6, 0x72, 0x80, 0x3e,
0x82, 0x1a, 0xa7, 0x33, 0x37, 0x14, 0xfc, 0x72, 0x4a, 0x22, 0x31, 0xb7, 0x7e, 0xab, 0x08, 0xd5,
0xc4, 0xd8, 0x8b, 0xc4, 0xbc, 0x31, 0x85, 0xcd, 0x55, 0x50, 0x1b, 0x4c, 0x19, 0xa2, 0x90, 0xf2,
0x25, 0xe5, 0xb2, 0xc0, 0xcb, 0x6c, 0xa5, 0x4d, 0x32, 0x95, 0x21, 0x25, 0xdc, 0x9e, 0xab, 0x8f,
0xa9, 0x82, 0xf5, 0x4e, 0x7e, 0x1d, 0x89, 0x5e, 0xf4, 0xd7, 0xa1, 0xb7, 0x9d, 0xff, 0x18, 0x50,
0x4d, 0x77, 0x1a, 0x34, 0x88, 0xfb, 0x8b, 0xba, 0xd2, 0x83, 0xee, 0xb3, 0xbb, 0x3a, 0x93, 0xaa,
0xe6, 0x5e, 0x24, 0x9d, 0x1d, 0xc9, 0x69, 0x50, 0x91, 0xd1, 0x2f, 0x60, 0x37, 0x60, 0x5c, 0x24,
0xdf, 0x74, 0x33, 0xb3, 0x02, 0x33, 0x9e, 0x54, 0xbf, 0x18, 0xdc, 0x99, 0xc3, 0x83, 0x6d, 0x6f,
0xe8, 0x29, 0xe4, 0x5f, 0x8d, 0x27, 0xf5, 0x5c, 0xe3, 0x83, 0x37, 0x57, 0xed, 0xf7, 0xb7, 0x1f,
0xbe, 0x72, 0xb9, 0x88, 0x88, 0x37, 0x9e, 0xa0, 0x1f, 0xc3, 0xee, 0xf0, 0xf8, 0x04, 0xe3, 0xba,
0xd1, 0x68, 0xbd, 0xb9, 0x6a, 0x7f, 0xb0, 0x8d, 0x93, 0x8f, 0x58, 0xe4, 0x3b, 0x98, 0x9d, 0xad,
0x07, 0xa4, 0xbf, 0xef, 0x80, 0xa9, 0x4b, 0xdd, 0xfd, 0x0e, 0x48, 0xbf, 0x81, 0x5a, 0xdc, 0x3d,
0x12, 0x01, 0xef, 0xdc, 0xd9, 0x44, 0xaa, 0x31, 0x41, 0xe7, 0xf8, 0x09, 0x54, 0xdd, 0x60, 0xf9,
0xd9, 0x94, 0xfa, 0xe4, 0xcc, 0xd3, 0xb3, 0x52, 0x19, 0x9b, 0xd2, 0x36, 0x8a, 0x4d, 0xf2, 0xeb,
0x71, 0x7d, 0x41, 0xb9, 0xaf, 0xa7, 0xa0, 0x32, 0x5e, 0xef, 0xd1, 0x17, 0x50, 0x70, 0x03, 0xb2,
0xd0, 0x9d, 0x2f, 0xf3, 0x06, 0xe3, 0x49, 0xef, 0x48, 0x6b, 0xb0, 0x5f, 0x5e, 0x5d, 0xb7, 0x0a,
0xd2, 0x80, 0x15, 0x0d, 0x35, 0x93, 0xe6, 0x23, 0xdf, 0xa4, 0x8a, 0x61, 0x19, 0xa7, 0x2c, 0x9d,
0xbf, 0x15, 0xc0, 0x1c, 0x78, 0x51, 0x28, 0x74, 0x49, 0xbf, 0xb7, 0xb8, 0xbd, 0x86, 0x47, 0x44,
0x8d, 0xd3, 0xc4, 0x97, 0xf5, 0x51, 0x35, 0x75, 0x1d, 0xbb, 0xa7, 0x99, 0xee, 0xd6, 0xe0, 0x78,
0x00, 0xe8, 0x17, 0xa5, 0x4f, 0xcb, 0xc0, 0x75, 0xf2, 0x3f, 0x4f, 0xd0, 0x09, 0xd4, 0x18, 0xb7,
0xe7, 0x34, 0x14, 0x71, 0x49, 0xd5, 0xe3, 0x67, 0xe6, 0x8f, 0xc9, 0x97, 0x69, 0xa0, 0xae, 0x27,
0xf1, 0x69, 0xb7, 0x7d, 0xa0, 0xe7, 0x50, 0xe0, 0xe4, 0x3c, 0x19, 0x50, 0x32, 0xf5, 0x8d, 0xc9,
0xb9, 0xd8, 0x72, 0xa1, 0x18, 0xe8, 0xf7, 0x00, 0x8e, 0x1b, 0x06, 0x44, 0xd8, 0x73, 0xca, 0x75,
0x9e, 0x32, 0xaf, 0x38, 0x5c, 0xa3, 0xb6, 0xbc, 0xa4, 0xd8, 0xe8, 0x10, 0x2a, 0x36, 0x49, 0x94,
0x56, 0xbc, 0xbd, 0x9b, 0x0c, 0x7a, 0xda, 0x45, 0x5d, 0xba, 0x58, 0x5d, 0xb7, 0xca, 0x89, 0x05,
0x97, 0x6d, 0xa2, 0x95, 0x77, 0x08, 0x35, 0x39, 0xab, 0x4f, 0x1d, 0x7a, 0x4e, 0x22, 0x4f, 0x84,
0xaa, 0xf1, 0xdd, 0x52, 0x1f, 0xe5, 0xd8, 0x38, 0xd4, 0x38, 0x7d, 0xae, 0xaa, 0x48, 0xd9, 0x3a,
0x2e, 0x40, 0xdc, 0x18, 0xee, 0x57, 0x26, 0x08, 0x0a, 0x0e, 0x11, 0x44, 0x29, 0xa3, 0x8a, 0xd5,
0xba, 0xff, 0xe1, 0xdb, 0x9b, 0x66, 0xee, 0x9f, 0x37, 0xcd, 0xdc, 0xbf, 0x6f, 0x9a, 0xc6, 0x9f,
0x56, 0x4d, 0xe3, 0xed, 0xaa, 0x69, 0xfc, 0x63, 0xd5, 0x34, 0xfe, 0xb5, 0x6a, 0x1a, 0x67, 0x45,
0xf5, 0x8b, 0xfc, 0xf3, 0xff, 0x06, 0x00, 0x00, 0xff, 0xff, 0xc3, 0x59, 0xc9, 0x0e, 0x81, 0x0f,
0x00, 0x00,
}

View file

@ -225,6 +225,12 @@ message ContainerSpec {
// DNSConfig allows one to specify DNS related configuration in resolv.conf
DNSConfig dns_config = 15 [(gogoproto.customname) = "DNSConfig"];
// Healthcheck describes how to check the container is healthy. If the
// container is considered unhealthy, it will be destroyed, its creating
// task will exit and a new task will be rescheduled elsewhere. A container
// is considered unhealthy after `Retries` number of consecutive failures.
HealthConfig healthcheck = 16;
}
// EndpointSpec defines the properties that can be configured to

File diff suppressed because it is too large Load diff

View file

@ -408,6 +408,12 @@ message ContainerStatus {
int32 exit_code = 3;
}
// PortStatus specifies the actual allocated runtime state of a list
// of port configs.
message PortStatus {
repeated PortConfig ports = 1;
}
message TaskStatus {
Timestamp timestamp = 1;
@ -438,6 +444,10 @@ message TaskStatus {
oneof runtime_status {
ContainerStatus container = 5;
}
// HostPorts provides a list of ports allocated at the host
// level.
PortStatus port_status = 6;
}
// NetworkAttachmentConfig specifies how a service should be attached to a particular network.
@ -807,3 +817,26 @@ message BlacklistedCertificate {
// was issued for the given CN.
Timestamp expiry = 1;
}
// HealthConfig holds configuration settings for the HEALTHCHECK feature.
message HealthConfig {
// Test is the test to perform to check that the container is healthy.
// An empty slice means to inherit the default.
// The options are:
// {} : inherit healthcheck
// {"NONE"} : disable healthcheck
// {"CMD", args...} : exec arguments directly
// {"CMD-SHELL", command} : run command with system's default shell
repeated string test = 1;
// Interval is the time to wait between checks. Zero means inherit.
Duration interval = 2;
// Timeout is the time to wait before considering the check to have hung.
// Zero means inherit.
Duration timeout = 3;
// Retries is the number of consecutive failures needed to consider a
// container as unhealthy. Zero means inherit.
int32 retries = 4;
}

View file

@ -489,6 +489,28 @@ func taskUpdateEndpoint(t *api.Task, endpoint *api.Endpoint) {
t.Endpoint = endpoint.Copy()
}
func isIngressNetworkNeeded(s *api.Service) bool {
if s == nil {
return false
}
if s.Spec.Endpoint == nil {
return false
}
for _, p := range s.Spec.Endpoint.Ports {
// The service to which this task belongs is trying to
// expose ports with PublishMode as Ingress to the
// external world. Automatically attach the task to
// the ingress network.
if p.PublishMode == api.PublishModeIngress {
return true
}
}
return false
}
func (a *Allocator) taskCreateNetworkAttachments(t *api.Task, s *api.Service) {
// If task network attachments have already been filled in no
// need to do anything else.
@ -497,11 +519,7 @@ func (a *Allocator) taskCreateNetworkAttachments(t *api.Task, s *api.Service) {
}
var networks []*api.NetworkAttachment
// The service to which this task belongs is trying to expose
// ports to the external world. Automatically attach the task
// to the ingress network.
if s != nil && s.Spec.Endpoint != nil && len(s.Spec.Endpoint.Ports) != 0 {
if isIngressNetworkNeeded(s) {
networks = append(networks, &api.NetworkAttachment{Network: a.netCtx.ingressNetwork})
}
@ -638,7 +656,7 @@ func (a *Allocator) allocateService(ctx context.Context, s *api.Service) error {
// The service is trying to expose ports to the external
// world. Automatically attach the service to the ingress
// network only if it is not already done.
if len(s.Spec.Endpoint.Ports) != 0 {
if isIngressNetworkNeeded(s) {
var found bool
for _, vip := range s.Endpoint.VirtualIPs {
if vip.NetworkID == nc.ingressNetwork.ID {
@ -668,7 +686,7 @@ func (a *Allocator) allocateService(ctx context.Context, s *api.Service) error {
// If the service doesn't expose ports any more and if we have
// any lingering virtual IP references for ingress network
// clean them up here.
if s.Spec.Endpoint == nil || len(s.Spec.Endpoint.Ports) == 0 {
if !isIngressNetworkNeeded(s) {
if s.Endpoint != nil {
for i, vip := range s.Endpoint.VirtualIPs {
if vip.NetworkID == nc.ingressNetwork.ID {
@ -755,7 +773,10 @@ func (a *Allocator) allocateTask(ctx context.Context, t *api.Task) (err error) {
return
}
taskUpdateEndpoint(t, s.Endpoint)
if s.Endpoint != nil {
taskUpdateEndpoint(t, s.Endpoint)
taskUpdated = true
}
}
for _, na := range t.Networks {
@ -781,6 +802,7 @@ func (a *Allocator) allocateTask(ctx context.Context, t *api.Task) (err error) {
taskUpdated = true
}
})
if err != nil {
return err
}

View file

@ -128,8 +128,12 @@ func (pa *portAllocator) serviceAllocatePorts(s *api.Service) (err error) {
for _, portConfig := range portConfigs {
// Make a copy of port config to create runtime state
portState := portConfig.Copy()
if err = pa.portSpaces[portState.Protocol].allocate(portState); err != nil {
return
// Do an actual allocation only if the PublishMode is Ingress
if portConfig.PublishMode == api.PublishModeIngress {
if err = pa.portSpaces[portState.Protocol].allocate(portState); err != nil {
return
}
}
if s.Endpoint == nil {
@ -148,6 +152,12 @@ func (pa *portAllocator) serviceDeallocatePorts(s *api.Service) {
}
for _, portState := range s.Endpoint.Ports {
// Do an actual free only if the PublishMode is
// Ingress
if portState.PublishMode != api.PublishModeIngress {
continue
}
pa.portSpaces[portState.Protocol].free(portState)
}
@ -177,6 +187,11 @@ func (pa *portAllocator) isPortsAllocated(s *api.Service) bool {
}
for i, portConfig := range s.Spec.Endpoint.Ports {
// Ignore ports which are not PublishModeIngress
if portConfig.PublishMode != api.PublishModeIngress {
continue
}
// The port configuration slice and port state slice
// are expected to be in the same order.
portState := s.Endpoint.Ports[i]

View file

@ -131,11 +131,13 @@ func New(config *Config) (*Manager, error) {
// externally-reachable address.
tcpAddr := config.AdvertiseAddr
var tcpAddrPort string
if tcpAddr == "" {
// Otherwise, we know we are joining an existing swarm. Use a
// wildcard address to trigger remote autodetection of our
// address.
_, tcpAddrPort, err := net.SplitHostPort(config.ProtoAddr["tcp"])
var err error
_, tcpAddrPort, err = net.SplitHostPort(config.ProtoAddr["tcp"])
if err != nil {
return nil, fmt.Errorf("missing or invalid listen address %s", config.ProtoAddr["tcp"])
}
@ -189,7 +191,7 @@ func New(config *Config) (*Manager, error) {
} else if err != nil {
return nil, err
}
if proto == "tcp" {
if proto == "tcp" && tcpAddrPort == "0" {
// in case of 0 port
tcpAddr = l.Addr().String()
}