diff --git a/api/resources.go b/api/resources.go index d9bd49476aed..b5ada2d9ec14 100644 --- a/api/resources.go +++ b/api/resources.go @@ -119,6 +119,7 @@ type NetworkResource struct { DNS *DNSConfig `hcl:"dns,block"` ReservedPorts []Port `hcl:"reserved_ports,block"` DynamicPorts []Port `hcl:"port,block"` + Hostname string `hcl:"hostname,optional"` // COMPAT(0.13) // XXX Deprecated. Please do not use. The field will be removed in Nomad diff --git a/client/allocrunner/alloc_runner_hooks.go b/client/allocrunner/alloc_runner_hooks.go index 07a184ffe422..9624e633c730 100644 --- a/client/allocrunner/alloc_runner_hooks.go +++ b/client/allocrunner/alloc_runner_hooks.go @@ -133,6 +133,14 @@ func (ar *allocRunner) initRunnerHooks(config *clientconfig.Config) error { return fmt.Errorf("failed to initialize network configurator: %v", err) } + // Create a new taskenv.Builder which is used and mutated by networkHook. + envBuilder := taskenv.NewBuilder( + config.Node, ar.Alloc(), nil, config.Region).SetAllocDir(ar.allocDir.AllocDir) + + // Create a taskenv.TaskEnv which is used for read only purposes by the + // newNetworkHook. + builtTaskEnv := envBuilder.Build() + // Create the alloc directory hook. This is run first to ensure the // directory path exists for other hooks. alloc := ar.Alloc() @@ -142,13 +150,13 @@ func (ar *allocRunner) initRunnerHooks(config *clientconfig.Config) error { newUpstreamAllocsHook(hookLogger, ar.prevAllocWatcher), newDiskMigrationHook(hookLogger, ar.prevAllocMigrator, ar.allocDir), newAllocHealthWatcherHook(hookLogger, alloc, hs, ar.Listener(), ar.consulClient), - newNetworkHook(hookLogger, ns, alloc, nm, nc, ar), + newNetworkHook(hookLogger, ns, alloc, nm, nc, ar, builtTaskEnv), newGroupServiceHook(groupServiceHookConfig{ alloc: alloc, consul: ar.consulClient, consulNamespace: alloc.ConsulNamespace(), restarter: ar, - taskEnvBuilder: taskenv.NewBuilder(config.Node, ar.Alloc(), nil, config.Region).SetAllocDir(ar.allocDir.AllocDir), + taskEnvBuilder: envBuilder, networkStatusGetter: ar, logger: hookLogger, }), diff --git a/client/allocrunner/network_hook.go b/client/allocrunner/network_hook.go index 70f82111c6d8..b540d4b5e569 100644 --- a/client/allocrunner/network_hook.go +++ b/client/allocrunner/network_hook.go @@ -5,14 +5,25 @@ import ( "fmt" hclog "github.com/hashicorp/go-hclog" + "github.com/hashicorp/nomad/client/taskenv" "github.com/hashicorp/nomad/nomad/structs" "github.com/hashicorp/nomad/plugins/drivers" + "github.com/miekg/dns" ) -// We create a pause container to own the network namespace, and the -// NetworkIsolationSpec we get back from CreateNetwork has this label set as -// the container ID. We'll use this to generate a hostname for the task. -const dockerNetSpecLabelKey = "docker_sandbox_container_id" +const ( + // dockerNetSpecLabelKey is the label added when we create a pause + // container to own the network namespace, and the NetworkIsolationSpec we + // get back from CreateNetwork has this label set as the container ID. + // We'll use this to generate a hostname for the task in the event the user + // did not specify a custom one. Please see dockerNetSpecHostnameKey. + dockerNetSpecLabelKey = "docker_sandbox_container_id" + + // dockerNetSpecHostnameKey is the label added when we create a pause + // container and the task group network include a user supplied hostname + // parameter. + dockerNetSpecHostnameKey = "docker_sandbox_hostname" +) type networkIsolationSetter interface { SetNetworkIsolation(*drivers.NetworkIsolationSpec) @@ -61,6 +72,9 @@ type networkHook struct { // the alloc network has been created networkConfigurator NetworkConfigurator + // taskEnv is used to perform interpolation within the network blocks. + taskEnv *taskenv.TaskEnv + logger hclog.Logger } @@ -69,13 +83,16 @@ func newNetworkHook(logger hclog.Logger, alloc *structs.Allocation, netManager drivers.DriverNetworkManager, netConfigurator NetworkConfigurator, - networkStatusSetter networkStatusSetter) *networkHook { + networkStatusSetter networkStatusSetter, + taskEnv *taskenv.TaskEnv, +) *networkHook { return &networkHook{ isolationSetter: ns, networkStatusSetter: networkStatusSetter, alloc: alloc, manager: netManager, networkConfigurator: netConfigurator, + taskEnv: taskEnv, logger: logger, } } @@ -95,8 +112,24 @@ func (h *networkHook) Prerun() error { return nil } - spec, created, err := h.manager.CreateNetwork(h.alloc.ID) + // Perform our networks block interpolation. + interpolatedNetworks := taskenv.InterpolateNetworks(h.taskEnv, tg.Networks) + + // Interpolated values need to be validated. It is also possible a user + // supplied hostname avoids the validation on job registrations because it + // looks like it includes interpolation, when it doesn't. + if interpolatedNetworks[0].Hostname != "" { + if _, ok := dns.IsDomainName(interpolatedNetworks[0].Hostname); !ok { + return fmt.Errorf("network hostname %q is not a valid DNS name", interpolatedNetworks[0].Hostname) + } + } + + // Our network create request. + networkCreateReq := drivers.NetworkCreateRequest{ + Hostname: interpolatedNetworks[0].Hostname, + } + spec, created, err := h.manager.CreateNetwork(h.alloc.ID, &networkCreateReq) if err != nil { return fmt.Errorf("failed to create network for alloc: %v", err) } @@ -111,18 +144,31 @@ func (h *networkHook) Prerun() error { if err != nil { return fmt.Errorf("failed to configure networking for alloc: %v", err) } - if hostname, ok := spec.Labels[dockerNetSpecLabelKey]; ok { + + // If the driver set the sandbox hostname label, then we will use that + // to set the HostsConfig.Hostname. Otherwise, identify the sandbox + // container ID which will have been used to set the network namespace + // hostname. + if hostname, ok := spec.Labels[dockerNetSpecHostnameKey]; ok { + h.spec.HostsConfig = &drivers.HostsConfig{ + Address: status.Address, + Hostname: hostname, + } + } else if hostname, ok := spec.Labels[dockerNetSpecLabelKey]; ok { + + // the docker_sandbox_container_id is the full ID of the pause + // container, whereas we want the shortened name that dockerd sets + // as the pause container's hostname. if len(hostname) > 12 { - // the docker_sandbox_container_id is the full ID of the pause - // container, whereas we want the shortened name that dockerd - // sets as the pause container's hostname hostname = hostname[:12] } + h.spec.HostsConfig = &drivers.HostsConfig{ Address: status.Address, Hostname: hostname, } } + h.networkStatusSetter.SetNetworkStatus(status) } return nil diff --git a/client/allocrunner/network_hook_test.go b/client/allocrunner/network_hook_test.go index a15084cd36a7..c5dee542c91f 100644 --- a/client/allocrunner/network_hook_test.go +++ b/client/allocrunner/network_hook_test.go @@ -4,6 +4,7 @@ import ( "testing" "github.com/hashicorp/nomad/client/allocrunner/interfaces" + "github.com/hashicorp/nomad/client/taskenv" "github.com/hashicorp/nomad/helper/testlog" "github.com/hashicorp/nomad/nomad/mock" "github.com/hashicorp/nomad/nomad/structs" @@ -56,7 +57,7 @@ func TestNetworkHook_Prerun_Postrun(t *testing.T) { destroyCalled := false nm := &testutils.MockDriver{ MockNetworkManager: testutils.MockNetworkManager{ - CreateNetworkF: func(allocID string) (*drivers.NetworkIsolationSpec, bool, error) { + CreateNetworkF: func(allocID string, req *drivers.NetworkCreateRequest) (*drivers.NetworkIsolationSpec, bool, error) { require.Equal(t, alloc.ID, allocID) return spec, false, nil }, @@ -79,8 +80,10 @@ func TestNetworkHook_Prerun_Postrun(t *testing.T) { } require := require.New(t) + envBuilder := taskenv.NewBuilder(mock.Node(), alloc, nil, alloc.Job.Region) + logger := testlog.HCLogger(t) - hook := newNetworkHook(logger, setter, alloc, nm, &hostNetworkConfigurator{}, statusSetter) + hook := newNetworkHook(logger, setter, alloc, nm, &hostNetworkConfigurator{}, statusSetter, envBuilder.Build()) require.NoError(hook.Prerun()) require.True(setter.called) require.False(destroyCalled) @@ -91,11 +94,10 @@ func TestNetworkHook_Prerun_Postrun(t *testing.T) { setter.called = false destroyCalled = false alloc.Job.TaskGroups[0].Networks[0].Mode = "host" - hook = newNetworkHook(logger, setter, alloc, nm, &hostNetworkConfigurator{}, statusSetter) + hook = newNetworkHook(logger, setter, alloc, nm, &hostNetworkConfigurator{}, statusSetter, envBuilder.Build()) require.NoError(hook.Prerun()) require.False(setter.called) require.False(destroyCalled) require.NoError(hook.Postrun()) require.False(destroyCalled) - } diff --git a/client/allocrunner/network_manager_linux.go b/client/allocrunner/network_manager_linux.go index 0d6ef3e98dc1..a4a08ce29cea 100644 --- a/client/allocrunner/network_manager_linux.go +++ b/client/allocrunner/network_manager_linux.go @@ -26,8 +26,18 @@ func newNetworkManager(alloc *structs.Allocation, driverManager drivermanager.Ma tgNetMode = tg.Networks[0].Mode } + groupIsolationMode := netModeToIsolationMode(tgNetMode) + + // Setting the hostname is only possible where the task groups networking + // mode is group; meaning bridge or none. + if len(tg.Networks) > 0 && + (groupIsolationMode != drivers.NetIsolationModeGroup && tg.Networks[0].Hostname != "") { + return nil, fmt.Errorf("hostname cannot be set on task group using %q networking mode", + groupIsolationMode) + } + // networkInitiator tracks the task driver which needs to create the network - // to check for multiple drivers needing the create the network + // to check for multiple drivers needing to create the network. var networkInitiator string // driverCaps tracks which drivers we've checked capabilities for so as not @@ -80,6 +90,14 @@ func newNetworkManager(alloc *structs.Allocation, driverManager drivermanager.Ma nm = netManager networkInitiator = task.Name + } else if tg.Networks[0].Hostname != "" { + // TODO jrasell: remove once the default linux network manager + // supports setting the hostname in bridged mode. This currently + // indicates only Docker supports this, which is true unless a + // custom driver can which means this check still holds as true as + // we can tell. + // Please see: https://github.com/hashicorp/nomad/issues/11180 + return nil, fmt.Errorf("hostname is not currently supported on driver %s", task.Driver) } // mark this driver's capabilities as checked @@ -92,7 +110,10 @@ func newNetworkManager(alloc *structs.Allocation, driverManager drivermanager.Ma // defaultNetworkManager creates a network namespace for the alloc type defaultNetworkManager struct{} -func (*defaultNetworkManager) CreateNetwork(allocID string) (*drivers.NetworkIsolationSpec, bool, error) { +// CreateNetwork is the CreateNetwork implementation of the +// drivers.DriverNetworkManager interface function. It does not currently +// support setting the hostname of the network namespace. +func (*defaultNetworkManager) CreateNetwork(allocID string, _ *drivers.NetworkCreateRequest) (*drivers.NetworkIsolationSpec, bool, error) { netns, err := nsutil.NewNS(allocID) if err != nil { // when a client restarts, the namespace will already exist and diff --git a/client/allocrunner/network_manager_linux_test.go b/client/allocrunner/network_manager_linux_test.go index eab55dc5a2b4..ac2f97c8fdc4 100644 --- a/client/allocrunner/network_manager_linux_test.go +++ b/client/allocrunner/network_manager_linux_test.go @@ -164,6 +164,92 @@ func TestNewNetworkManager(t *testing.T) { err: true, errContains: "want to initiate networking but only one", }, + { + name: "hostname set in bridged mode", + alloc: &structs.Allocation{ + TaskGroup: "group", + Job: &structs.Job{ + TaskGroups: []*structs.TaskGroup{ + { + Name: "group", + Networks: []*structs.NetworkResource{ + { + Mode: "bridge", + Hostname: "foobar", + }, + }, + Tasks: []*structs.Task{ + { + Name: "task1", + Driver: "mustinit1", + Resources: &structs.Resources{}, + }, + }, + }, + }, + }, + }, + mustInit: true, + err: false, + }, + { + name: "hostname set in host mode", + alloc: &structs.Allocation{ + TaskGroup: "group", + Job: &structs.Job{ + TaskGroups: []*structs.TaskGroup{ + { + Name: "group", + Networks: []*structs.NetworkResource{ + { + Mode: "host", + Hostname: "foobar", + }, + }, + Tasks: []*structs.Task{ + { + Name: "task1", + Driver: "group1", + Resources: &structs.Resources{}, + }, + }, + }, + }, + }, + }, + mustInit: false, + err: true, + errContains: `hostname cannot be set on task group using "host" networking mode`, + }, + { + name: "hostname set using exec driver", + alloc: &structs.Allocation{ + TaskGroup: "group", + Job: &structs.Job{ + TaskGroups: []*structs.TaskGroup{ + { + Name: "group", + Networks: []*structs.NetworkResource{ + { + Mode: "bridge", + Hostname: "foobar", + }, + }, + Tasks: []*structs.Task{ + { + Name: "task1", + Driver: "group1", + Resources: &structs.Resources{}, + }, + }, + }, + }, + }, + }, + mustInit: false, + err: true, + errContains: "hostname is not currently supported on driver group1", + }, } { t.Run(tc.name, func(t *testing.T) { require := require.New(t) diff --git a/client/taskenv/network.go b/client/taskenv/network.go new file mode 100644 index 000000000000..3bbafaa0c111 --- /dev/null +++ b/client/taskenv/network.go @@ -0,0 +1,29 @@ +package taskenv + +import ( + "github.com/hashicorp/nomad/nomad/structs" +) + +// InterpolateNetworks returns an interpolated copy of the task group networks +// with values from the task's environment. +// +// Current interoperable fields: +// - Hostname +func InterpolateNetworks(taskEnv *TaskEnv, networks structs.Networks) structs.Networks { + + // Guard against not having a valid taskEnv. This can be the case if the + // PreKilling or Exited hook is run before Poststart. + if taskEnv == nil || networks == nil { + return nil + } + + // Create a copy of the networks array, so we can manipulate the copy. + interpolated := networks.Copy() + + // Iterate the copy and perform the interpolation. + for i := range interpolated { + interpolated[i].Hostname = taskEnv.ReplaceEnv(interpolated[i].Hostname) + } + + return interpolated +} diff --git a/client/taskenv/network_test.go b/client/taskenv/network_test.go new file mode 100644 index 000000000000..60589b6f1c3d --- /dev/null +++ b/client/taskenv/network_test.go @@ -0,0 +1,45 @@ +package taskenv + +import ( + "testing" + + "github.com/hashicorp/nomad/nomad/structs" + "github.com/stretchr/testify/assert" +) + +func Test_InterpolateNetworks(t *testing.T) { + testCases := []struct { + inputTaskEnv *TaskEnv + inputNetworks structs.Networks + expectedOutputNetworks structs.Networks + name string + }{ + { + inputTaskEnv: testEnv, + inputNetworks: structs.Networks{ + {Hostname: "my-little-pony"}, + }, + expectedOutputNetworks: structs.Networks{ + {Hostname: "my-little-pony"}, + }, + name: "non-interpolated hostname", + }, + { + inputTaskEnv: testEnv, + inputNetworks: structs.Networks{ + {Hostname: "${foo}-cache-${baz}"}, + }, + expectedOutputNetworks: structs.Networks{ + {Hostname: "bar-cache-blah"}, + }, + name: "interpolated hostname", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + actualOutput := InterpolateNetworks(tc.inputTaskEnv, tc.inputNetworks) + assert.Equal(t, tc.expectedOutputNetworks, actualOutput, tc.name) + }) + } +} diff --git a/command/agent/job_endpoint.go b/command/agent/job_endpoint.go index 65967985797a..512331e4ab03 100644 --- a/command/agent/job_endpoint.go +++ b/command/agent/job_endpoint.go @@ -1200,10 +1200,11 @@ func ApiNetworkResourceToStructs(in []*api.NetworkResource) []*structs.NetworkRe out = make([]*structs.NetworkResource, len(in)) for i, nw := range in { out[i] = &structs.NetworkResource{ - Mode: nw.Mode, - CIDR: nw.CIDR, - IP: nw.IP, - MBits: nw.Megabits(), + Mode: nw.Mode, + CIDR: nw.CIDR, + IP: nw.IP, + Hostname: nw.Hostname, + MBits: nw.Megabits(), } if nw.DNS != nil { diff --git a/command/agent/job_endpoint_test.go b/command/agent/job_endpoint_test.go index 9834ab20de15..470c488f07c2 100644 --- a/command/agent/job_endpoint_test.go +++ b/command/agent/job_endpoint_test.go @@ -2210,8 +2210,9 @@ func TestJobs_ApiJobToStructsJob(t *testing.T) { MemoryMB: helper.IntToPtr(10), Networks: []*api.NetworkResource{ { - IP: "10.10.11.1", - MBits: helper.IntToPtr(10), + IP: "10.10.11.1", + MBits: helper.IntToPtr(10), + Hostname: "foobar", ReservedPorts: []api.Port{ { Label: "http", @@ -2602,8 +2603,9 @@ func TestJobs_ApiJobToStructsJob(t *testing.T) { MemoryMB: 10, Networks: []*structs.NetworkResource{ { - IP: "10.10.11.1", - MBits: 10, + IP: "10.10.11.1", + MBits: 10, + Hostname: "foobar", ReservedPorts: []structs.Port{ { Label: "http", diff --git a/drivers/docker/network.go b/drivers/docker/network.go index d0900d37e0c2..30a49f6eba52 100644 --- a/drivers/docker/network.go +++ b/drivers/docker/network.go @@ -7,12 +7,21 @@ import ( "github.com/hashicorp/nomad/plugins/drivers" ) -// dockerNetSpecLabelKey is used when creating a parent container for -// shared networking. It is a label whos value identifies the container ID of -// the parent container so tasks can configure their network mode accordingly -const dockerNetSpecLabelKey = "docker_sandbox_container_id" +const ( + // dockerNetSpecLabelKey is the label added when we create a pause + // container to own the network namespace, and the NetworkIsolationSpec we + // get back from CreateNetwork has this label set as the container ID. + // We'll use this to generate a hostname for the task in the event the user + // did not specify a custom one. Please see dockerNetSpecHostnameKey. + dockerNetSpecLabelKey = "docker_sandbox_container_id" + + // dockerNetSpecHostnameKey is the label added when we create a pause + // container and the task group network include a user supplied hostname + // parameter. + dockerNetSpecHostnameKey = "docker_sandbox_hostname" +) -func (d *Driver) CreateNetwork(allocID string) (*drivers.NetworkIsolationSpec, bool, error) { +func (d *Driver) CreateNetwork(allocID string, createSpec *drivers.NetworkCreateRequest) (*drivers.NetworkIsolationSpec, bool, error) { // Initialize docker API clients client, _, err := d.dockerClients() if err != nil { @@ -32,19 +41,26 @@ func (d *Driver) CreateNetwork(allocID string) (*drivers.NetworkIsolationSpec, b return nil, false, err } - config, err := d.createSandboxContainerConfig(allocID) + config, err := d.createSandboxContainerConfig(allocID, createSpec) if err != nil { return nil, false, err } - specFromContainer := func(c *docker.Container) *drivers.NetworkIsolationSpec { - return &drivers.NetworkIsolationSpec{ + specFromContainer := func(c *docker.Container, hostname string) *drivers.NetworkIsolationSpec { + spec := &drivers.NetworkIsolationSpec{ Mode: drivers.NetIsolationModeGroup, Path: c.NetworkSettings.SandboxKey, Labels: map[string]string{ dockerNetSpecLabelKey: c.ID, }, } + + // If the user supplied a hostname, set the label. + if hostname != "" { + spec.Labels[dockerNetSpecHostnameKey] = hostname + } + + return spec } // We want to return a flag that tells us if the container already @@ -55,7 +71,7 @@ func (d *Driver) CreateNetwork(allocID string) (*drivers.NetworkIsolationSpec, b return nil, false, err } if container != nil && container.State.Running { - return specFromContainer(container), false, nil + return specFromContainer(container, createSpec.Hostname), false, nil } container, err = d.createContainer(client, *config, d.config.InfraImage) @@ -76,7 +92,7 @@ func (d *Driver) CreateNetwork(allocID string) (*drivers.NetworkIsolationSpec, b return nil, false, err } - return specFromContainer(container), true, nil + return specFromContainer(container, createSpec.Hostname), true, nil } func (d *Driver) DestroyNetwork(allocID string, spec *drivers.NetworkIsolationSpec) error { @@ -92,17 +108,18 @@ func (d *Driver) DestroyNetwork(allocID string, spec *drivers.NetworkIsolationSp } // createSandboxContainerConfig creates a docker container configuration which -// starts a container with an empty network namespace -func (d *Driver) createSandboxContainerConfig(allocID string) (*docker.CreateContainerOptions, error) { +// starts a container with an empty network namespace. +func (d *Driver) createSandboxContainerConfig(allocID string, createSpec *drivers.NetworkCreateRequest) (*docker.CreateContainerOptions, error) { return &docker.CreateContainerOptions{ Name: fmt.Sprintf("nomad_init_%s", allocID), Config: &docker.Config{ - Image: d.config.InfraImage, + Image: d.config.InfraImage, + Hostname: createSpec.Hostname, }, HostConfig: &docker.HostConfig{ - // set the network mode to none which creates a network namespace with - // only a loopback interface + // Set the network mode to none which creates a network namespace + // with only a loopback interface. NetworkMode: "none", }, }, nil diff --git a/drivers/docker/network_test.go b/drivers/docker/network_test.go new file mode 100644 index 000000000000..9fada5bf4527 --- /dev/null +++ b/drivers/docker/network_test.go @@ -0,0 +1,66 @@ +package docker + +import ( + "github.com/hashicorp/nomad/plugins/drivers" + "testing" + + docker "github.com/fsouza/go-dockerclient" + "github.com/stretchr/testify/assert" +) + +func TestDriver_createSandboxContainerConfig(t *testing.T) { + testCases := []struct { + inputAllocID string + inputNetworkCreateRequest *drivers.NetworkCreateRequest + expectedOutputOpts *docker.CreateContainerOptions + name string + }{ + { + inputAllocID: "768b5e8c-a52e-825c-d564-51100230eb62", + inputNetworkCreateRequest: &drivers.NetworkCreateRequest{ + Hostname: "", + }, + expectedOutputOpts: &docker.CreateContainerOptions{ + Name: "nomad_init_768b5e8c-a52e-825c-d564-51100230eb62", + Config: &docker.Config{ + Image: "gcr.io/google_containers/pause-amd64:3.1", + }, + HostConfig: &docker.HostConfig{ + NetworkMode: "none", + }, + }, + name: "no input hostname", + }, + { + inputAllocID: "768b5e8c-a52e-825c-d564-51100230eb62", + inputNetworkCreateRequest: &drivers.NetworkCreateRequest{ + Hostname: "linux", + }, + expectedOutputOpts: &docker.CreateContainerOptions{ + Name: "nomad_init_768b5e8c-a52e-825c-d564-51100230eb62", + Config: &docker.Config{ + Image: "gcr.io/google_containers/pause-amd64:3.1", + Hostname: "linux", + }, + HostConfig: &docker.HostConfig{ + NetworkMode: "none", + }, + }, + name: "supplied input hostname", + }, + } + + d := &Driver{ + config: &DriverConfig{ + InfraImage: "gcr.io/google_containers/pause-amd64:3.1", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + actualOutput, err := d.createSandboxContainerConfig(tc.inputAllocID, tc.inputNetworkCreateRequest) + assert.Nil(t, err, tc.name) + assert.Equal(t, tc.expectedOutputOpts, actualOutput, tc.name) + }) + } +} diff --git a/e2e/networking/inputs/docker_bridged_hostname.nomad b/e2e/networking/inputs/docker_bridged_hostname.nomad new file mode 100644 index 000000000000..bac6fa808faa --- /dev/null +++ b/e2e/networking/inputs/docker_bridged_hostname.nomad @@ -0,0 +1,24 @@ +job "networking" { + datacenters = ["dc1", "dc2"] + + constraint { + attribute = "${attr.kernel.name}" + value = "linux" + } + + group "bridged" { + network { + hostname = "mylittlepony" + mode = "bridge" + } + + task "sleep" { + driver = "docker" + config { + image = "busybox:1" + command = "/bin/sleep" + args = ["300"] + } + } + } +} diff --git a/e2e/networking/inputs/docker_bridged_hostname_interpolation.nomad b/e2e/networking/inputs/docker_bridged_hostname_interpolation.nomad new file mode 100644 index 000000000000..2d09ed6ec9a1 --- /dev/null +++ b/e2e/networking/inputs/docker_bridged_hostname_interpolation.nomad @@ -0,0 +1,24 @@ +job "networking" { + datacenters = ["dc1", "dc2"] + + constraint { + attribute = "${attr.kernel.name}" + value = "linux" + } + + group "bridged" { + network { + hostname = "mylittlepony-${NOMAD_ALLOC_INDEX}" + mode = "bridge" + } + + task "sleep" { + driver = "docker" + config { + image = "busybox:1" + command = "/bin/sleep" + args = ["300"] + } + } + } +} diff --git a/e2e/networking/networking.go b/e2e/networking/networking.go index 705a3965b47c..90c86e66a7b0 100644 --- a/e2e/networking/networking.go +++ b/e2e/networking/networking.go @@ -1,14 +1,94 @@ package networking import ( + "os" + "strings" + "github.com/hashicorp/nomad/e2e/e2eutil" "github.com/hashicorp/nomad/e2e/framework" + "github.com/hashicorp/nomad/helper/uuid" ) +type NetworkingE2ETest struct { + framework.TC + jobIDs []string +} + func init() { framework.AddSuites(&framework.TestSuite{ Component: "Networking", CanRunLocal: true, - Cases: []framework.TestCase{e2eutil.NewE2EJob("networking/inputs/basic.nomad")}, + Cases: []framework.TestCase{ + e2eutil.NewE2EJob("networking/inputs/basic.nomad"), + new(NetworkingE2ETest), + }, }) } + +func (tc *NetworkingE2ETest) BeforeAll(f *framework.F) { + e2eutil.WaitForLeader(f.T(), tc.Nomad()) + e2eutil.WaitForNodesReady(f.T(), tc.Nomad(), 1) +} + +func (tc *NetworkingE2ETest) AfterEach(f *framework.F) { + if os.Getenv("NOMAD_TEST_SKIPCLEANUP") == "1" { + return + } + + for _, jobID := range tc.jobIDs { + _, err := e2eutil.Command("nomad", "job", "stop", "-purge", jobID) + f.NoError(err) + } + tc.jobIDs = []string{} + + _, err := e2eutil.Command("nomad", "system", "gc") + f.NoError(err) +} + +func (tc *NetworkingE2ETest) TestNetworking_DockerBridgedHostname(f *framework.F) { + + jobID := "test-networking-" + uuid.Generate()[0:8] + f.NoError(e2eutil.Register(jobID, "networking/inputs/docker_bridged_hostname.nomad")) + tc.jobIDs = append(tc.jobIDs, jobID) + f.NoError(e2eutil.WaitForAllocStatusExpected(jobID, "default", []string{"running"}), + "job should be running with 1 alloc") + + // Grab the allocations for the job. + allocs, _, err := tc.Nomad().Jobs().Allocations(jobID, false, nil) + f.NoError(err, "failed to get allocs for job") + f.Len(allocs, 1, "job should have one alloc") + + // Run the hostname command within the allocation. + hostnameOutput, err := e2eutil.AllocExec(allocs[0].ID, "sleep", "hostname", "default", nil) + f.NoError(err, "failed to run hostname exec command") + f.Equal("mylittlepony", strings.TrimSpace(hostnameOutput), "incorrect hostname set within container") + + // Check the /etc/hosts file for the correct IP address and hostname entry. + hostsOutput, err := e2eutil.AllocExec(allocs[0].ID, "sleep", "cat /etc/hosts", "default", nil) + f.NoError(err, "failed to run hostname exec command") + f.Contains(hostsOutput, "mylittlepony", "/etc/hosts doesn't contain hostname entry") +} + +func (tc *NetworkingE2ETest) TestNetworking_DockerBridgedHostnameInterpolation(f *framework.F) { + + jobID := "test-networking-" + uuid.Generate()[0:8] + f.NoError(e2eutil.Register(jobID, "networking/inputs/docker_bridged_hostname_interpolation.nomad")) + tc.jobIDs = append(tc.jobIDs, jobID) + f.NoError(e2eutil.WaitForAllocStatusExpected(jobID, "default", []string{"running"}), + "job should be running with 1 alloc") + + // Grab the allocations for the job. + allocs, _, err := tc.Nomad().Jobs().Allocations(jobID, false, nil) + f.NoError(err, "failed to get allocs for job") + f.Len(allocs, 1, "job should have one alloc") + + // Run the hostname command within the allocation. + hostnameOutput, err := e2eutil.AllocExec(allocs[0].ID, "sleep", "hostname", "default", nil) + f.NoError(err, "failed to run hostname exec command") + f.Equal("mylittlepony-0", strings.TrimSpace(hostnameOutput), "incorrect hostname set within container") + + // Check the /etc/hosts file for the correct IP address and hostname entry. + hostsOutput, err := e2eutil.AllocExec(allocs[0].ID, "sleep", "cat /etc/hosts", "default", nil) + f.NoError(err, "failed to run hostname exec command") + f.Contains(hostsOutput, "mylittlepony-0", "/etc/hosts doesn't contain hostname entry") +} diff --git a/go.mod b/go.mod index f93a173bc6d4..ccebc25de238 100644 --- a/go.mod +++ b/go.mod @@ -93,6 +93,7 @@ require ( github.com/kr/pty v1.1.5 github.com/kr/text v0.2.0 github.com/mattn/go-colorable v0.1.7 + github.com/miekg/dns v1.1.26 github.com/mitchellh/cli v1.1.0 github.com/mitchellh/colorstring v0.0.0-20150917214807-8631ce90f286 github.com/mitchellh/copystructure v1.1.1 diff --git a/jobspec/parse_network.go b/jobspec/parse_network.go index 6b7e9474e8d8..795f38a3eb99 100644 --- a/jobspec/parse_network.go +++ b/jobspec/parse_network.go @@ -23,6 +23,7 @@ func ParseNetwork(o *ast.ObjectList) (*api.NetworkResource, error) { "mbits", "dns", "port", + "hostname", } if err := checkHCLKeys(o.Items[0].Val, valid); err != nil { return nil, multierror.Prefix(err, "network ->") diff --git a/jobspec/test-fixtures/tg-network-with-hostname.hcl b/jobspec/test-fixtures/tg-network-with-hostname.hcl new file mode 100644 index 000000000000..0f84ab0163a6 --- /dev/null +++ b/jobspec/test-fixtures/tg-network-with-hostname.hcl @@ -0,0 +1,34 @@ +job "foo" { + datacenters = ["dc1"] + + group "bar" { + count = 3 + shutdown_delay = "14s" + + network { + mode = "bridge" + hostname = "foobar" + + port "http" { + static = 80 + to = 8080 + host_network = "public" + } + } + + task "bar" { + driver = "raw_exec" + + config { + command = "bash" + args = ["-c", "echo hi"] + } + + resources { + network { + mbits = 10 + } + } + } + } +} diff --git a/nomad/plan_normalization_test.go b/nomad/plan_normalization_test.go index 1082c44e50f9..ba427d423754 100644 --- a/nomad/plan_normalization_test.go +++ b/nomad/plan_normalization_test.go @@ -62,5 +62,5 @@ func TestPlanNormalize(t *testing.T) { } optimizedLogSize := buf.Len() - assert.Less(t, float64(optimizedLogSize)/float64(unoptimizedLogSize), 0.66) + assert.Less(t, float64(optimizedLogSize)/float64(unoptimizedLogSize), 0.67) } diff --git a/nomad/structs/diff_test.go b/nomad/structs/diff_test.go index 6eabcdc8b004..76221d5a208c 100644 --- a/nomad/structs/diff_test.go +++ b/nomad/structs/diff_test.go @@ -3422,10 +3422,11 @@ func TestTaskGroupDiff(t *testing.T) { Old: &TaskGroup{ Networks: Networks{ { - Device: "foo", - CIDR: "foo", - IP: "foo", - MBits: 100, + Device: "foo", + CIDR: "foo", + IP: "foo", + MBits: 100, + Hostname: "foo", ReservedPorts: []Port{ { Label: "foo", @@ -3438,10 +3439,11 @@ func TestTaskGroupDiff(t *testing.T) { New: &TaskGroup{ Networks: Networks{ { - Device: "bar", - CIDR: "bar", - IP: "bar", - MBits: 200, + Device: "bar", + CIDR: "bar", + IP: "bar", + MBits: 200, + Hostname: "bar", DynamicPorts: []Port{ { Label: "bar", @@ -3462,6 +3464,12 @@ func TestTaskGroupDiff(t *testing.T) { Type: DiffTypeAdded, Name: "Network", Fields: []*FieldDiff{ + { + Type: DiffTypeAdded, + Name: "Hostname", + Old: "", + New: "bar", + }, { Type: DiffTypeAdded, Name: "MBits", @@ -3518,6 +3526,12 @@ func TestTaskGroupDiff(t *testing.T) { Type: DiffTypeDeleted, Name: "Network", Fields: []*FieldDiff{ + { + Type: DiffTypeDeleted, + Name: "Hostname", + Old: "foo", + New: "", + }, { Type: DiffTypeDeleted, Name: "MBits", diff --git a/nomad/structs/structs.go b/nomad/structs/structs.go index d0753cbe6d52..e47af63d2deb 100644 --- a/nomad/structs/structs.go +++ b/nomad/structs/structs.go @@ -25,15 +25,10 @@ import ( "strings" "time" - "github.com/hashicorp/nomad/lib/cpuset" - "github.com/hashicorp/cronexpr" "github.com/hashicorp/go-msgpack/codec" "github.com/hashicorp/go-multierror" "github.com/hashicorp/go-version" - "github.com/mitchellh/copystructure" - "golang.org/x/crypto/blake2b" - "github.com/hashicorp/nomad/acl" "github.com/hashicorp/nomad/command/agent/host" "github.com/hashicorp/nomad/command/agent/pprof" @@ -41,8 +36,12 @@ import ( "github.com/hashicorp/nomad/helper/args" "github.com/hashicorp/nomad/helper/constraints/semver" "github.com/hashicorp/nomad/helper/uuid" + "github.com/hashicorp/nomad/lib/cpuset" "github.com/hashicorp/nomad/lib/kheap" psstructs "github.com/hashicorp/nomad/plugins/shared/structs" + "github.com/miekg/dns" + "github.com/mitchellh/copystructure" + "golang.org/x/crypto/blake2b" ) var ( @@ -2542,6 +2541,7 @@ type NetworkResource struct { Device string // Name of the device CIDR string // CIDR block of addresses IP string // Host IP address + Hostname string `json:",omitempty"` // Hostname of the network namespace MBits int // Throughput DNS *DNSConfig // DNS Configuration ReservedPorts []Port // Host Reserved ports @@ -2550,7 +2550,7 @@ type NetworkResource struct { func (nr *NetworkResource) Hash() uint32 { var data []byte - data = append(data, []byte(fmt.Sprintf("%s%s%s%s%d", nr.Mode, nr.Device, nr.CIDR, nr.IP, nr.MBits))...) + data = append(data, []byte(fmt.Sprintf("%s%s%s%s%s%d", nr.Mode, nr.Device, nr.CIDR, nr.IP, nr.Hostname, nr.MBits))...) for i, port := range nr.ReservedPorts { data = append(data, []byte(fmt.Sprintf("r%d%s%d%d", i, port.Label, port.Value, port.To))...) @@ -6317,7 +6317,18 @@ func (tg *TaskGroup) validateNetworks() error { mErr.Errors = append(mErr.Errors, err) } } + + // Validate the hostname field to be a valid DNS name. If the parameter + // looks like it includes an interpolation value, we skip this. It + // would be nice to validate additional parameters, but this isn't the + // right place. + if net.Hostname != "" && !strings.Contains(net.Hostname, "${") { + if _, ok := dns.IsDomainName(net.Hostname); !ok { + mErr.Errors = append(mErr.Errors, errors.New("Hostname is not a valid DNS name")) + } + } } + // Check for duplicate tasks or port labels, and no duplicated static ports for _, task := range tg.Tasks { if task.Resources == nil { diff --git a/nomad/structs/structs_test.go b/nomad/structs/structs_test.go index 2409f7021fb9..ab14ba01a160 100644 --- a/nomad/structs/structs_test.go +++ b/nomad/structs/structs_test.go @@ -1442,6 +1442,33 @@ func TestTaskGroupNetwork_Validate(t *testing.T) { }, }, }, + { + TG: &TaskGroup{ + Tasks: []*Task{ + {Driver: "docker"}, + }, + Networks: []*NetworkResource{ + { + Mode: "bridge", + Hostname: "foobar", + }, + }, + }, + }, + { + TG: &TaskGroup{ + Tasks: []*Task{ + {Name: "hostname-invalid-dns-name"}, + }, + Networks: []*NetworkResource{ + { + Mode: "bridge", + Hostname: "............", + }, + }, + }, + ErrContains: "Hostname is not a valid DNS name", + }, } for i := range cases { @@ -1599,6 +1626,56 @@ func TestTask_Validate_Resources(t *testing.T) { } } +func TestNetworkResource_Copy(t *testing.T) { + testCases := []struct { + inputNetworkResource *NetworkResource + name string + }{ + { + inputNetworkResource: nil, + name: "nil input check", + }, + { + inputNetworkResource: &NetworkResource{ + Mode: "bridge", + Device: "eth0", + CIDR: "10.0.0.1/8", + IP: "10.1.1.13", + Hostname: "foobar", + MBits: 1000, + DNS: &DNSConfig{ + Servers: []string{"8.8.8.8", "8.8.4.4"}, + Searches: []string{"example.com"}, + Options: []string{"ndot:2"}, + }, + ReservedPorts: []Port{ + { + Label: "foo", + Value: 1313, + To: 1313, + HostNetwork: "private", + }, + }, + DynamicPorts: []Port{ + { + Label: "bar", + To: 1414, + HostNetwork: "public", + }, + }, + }, + name: "fully populated input check", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + output := tc.inputNetworkResource.Copy() + assert.Equal(t, tc.inputNetworkResource, output, tc.name) + }) + } +} + func TestTask_Validate_Services(t *testing.T) { s1 := &Service{ Name: "service-name", @@ -2559,13 +2636,11 @@ func TestConstraint_Validate(t *testing.T) { } func TestAffinity_Validate(t *testing.T) { - type tc struct { affinity *Affinity err error name string } - testCases := []tc{ { affinity: &Affinity{}, @@ -3484,7 +3559,6 @@ func TestReschedulePolicy_Validate(t *testing.T) { ReschedulePolicy *ReschedulePolicy errors []error } - testCases := []testCase{ { desc: "Nil", @@ -4061,7 +4135,6 @@ func TestAllocation_Terminated(t *testing.T) { DesiredStatus string Terminated bool } - harness := []desiredState{ { ClientStatus: AllocClientStatusPending, @@ -4105,7 +4178,6 @@ func TestAllocation_ShouldReschedule(t *testing.T) { RescheduleTrackers []*RescheduleEvent ShouldReschedule bool } - fail := time.Now() harness := []testCase{ @@ -4238,7 +4310,6 @@ func TestAllocation_LastEventTime(t *testing.T) { taskState map[string]*TaskState expectedLastEventTime time.Time } - t1 := time.Now().UTC() testCases := []testCase{ @@ -4903,7 +4974,6 @@ func TestRescheduleTracker_Copy(t *testing.T) { original *RescheduleTracker expected *RescheduleTracker } - cases := []testCase{ {nil, nil}, {&RescheduleTracker{Events: []*RescheduleEvent{ @@ -5074,7 +5144,6 @@ func TestScalingPolicy_Validate(t *testing.T) { input *ScalingPolicy expectedErr string } - cases := []testCase{ { name: "full horizontal policy", @@ -5706,7 +5775,6 @@ func TestSpread_Validate(t *testing.T) { err error name string } - testCases := []tc{ { spread: &Spread{}, diff --git a/plugins/drivers/driver.go b/plugins/drivers/driver.go index c84d42166252..f47dc5a2815b 100644 --- a/plugins/drivers/driver.go +++ b/plugins/drivers/driver.go @@ -90,7 +90,7 @@ type ExecOptions struct { // network namespace for which tasks can join. This only needs to be implemented // if the driver MUST create the network namespace type DriverNetworkManager interface { - CreateNetwork(allocID string) (*NetworkIsolationSpec, bool, error) + CreateNetwork(allocID string, request *NetworkCreateRequest) (*NetworkIsolationSpec, bool, error) DestroyNetwork(allocID string, spec *NetworkIsolationSpec) error } @@ -211,6 +211,15 @@ type HostsConfig struct { Address string } +// NetworkCreateRequest contains all the relevant information when creating a +// network via DriverNetworkManager.CreateNetwork. +type NetworkCreateRequest struct { + + // Hostname is the hostname the user has specified that the network should + // be configured with. + Hostname string +} + // MountConfigSupport is an enum that defaults to "all" for backwards // compatibility with community drivers. type MountConfigSupport int32 diff --git a/plugins/drivers/proto/driver.pb.go b/plugins/drivers/proto/driver.pb.go index b682661b373a..66407ef55f2b 100644 --- a/plugins/drivers/proto/driver.pb.go +++ b/plugins/drivers/proto/driver.pb.go @@ -1657,7 +1657,9 @@ func (m *ExecTaskStreamingResponse) GetResult() *ExitResult { type CreateNetworkRequest struct { // AllocID of the allocation the network is associated with - AllocId string `protobuf:"bytes,1,opt,name=alloc_id,json=allocId,proto3" json:"alloc_id,omitempty"` + AllocId string `protobuf:"bytes,1,opt,name=alloc_id,json=allocId,proto3" json:"alloc_id,omitempty"` + // Hostname of the network namespace + Hostname string `protobuf:"bytes,2,opt,name=hostname,proto3" json:"hostname,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -1695,6 +1697,13 @@ func (m *CreateNetworkRequest) GetAllocId() string { return "" } +func (m *CreateNetworkRequest) GetHostname() string { + if m != nil { + return m.Hostname + } + return "" +} + type CreateNetworkResponse struct { IsolationSpec *NetworkIsolationSpec `protobuf:"bytes,1,opt,name=isolation_spec,json=isolationSpec,proto3" json:"isolation_spec,omitempty"` // created indicates that the network namespace is newly created @@ -3673,25 +3682,25 @@ func init() { } var fileDescriptor_4a8f45747846a74d = []byte{ - // 3771 bytes of a gzipped FileDescriptorProto + // 3776 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x5a, 0xcd, 0x6f, 0x1b, 0x49, - 0x76, 0x57, 0xb3, 0xf9, 0xf9, 0x48, 0x51, 0xad, 0xb2, 0x6c, 0xd3, 0xdc, 0x24, 0xe3, 0xed, 0x60, - 0x02, 0x61, 0x77, 0x86, 0x9e, 0xd1, 0x22, 0xe3, 0xf1, 0xac, 0x67, 0x3d, 0x1c, 0x8a, 0xb6, 0x34, + 0x76, 0x57, 0xb3, 0xf9, 0xf9, 0x48, 0x51, 0xad, 0xb2, 0x6c, 0xd3, 0x9c, 0x24, 0xe3, 0xed, 0x60, + 0x02, 0x61, 0x77, 0x86, 0x9e, 0xd5, 0x22, 0xe3, 0xf1, 0xac, 0x67, 0x3d, 0x1c, 0x8a, 0xb6, 0x34, 0x96, 0x28, 0xa5, 0x48, 0xc1, 0xeb, 0x38, 0x3b, 0x9d, 0x16, 0xbb, 0x4c, 0xb5, 0xc5, 0xfe, 0x98, 0xae, 0xa6, 0x2c, 0x6d, 0x10, 0x24, 0xd8, 0x00, 0xc1, 0x06, 0x48, 0x90, 0x5c, 0x26, 0x7b, 0xc9, 0x69, 0x81, 0x9c, 0xf2, 0x0f, 0x04, 0x1b, 0xec, 0x29, 0x87, 0xfc, 0x13, 0xb9, 0xe4, 0x96, 0x63, 0x72, 0xca, 0x35, 0xa8, 0x8f, 0x6e, 0x76, 0x93, 0xf4, 0xb8, 0x49, 0xf9, 0xc4, 0x7e, 0xf5, 0xf1, 0xab, 0xc7, 0xf7, 0x5e, 0xbd, 0x7a, 0xf5, 0xea, 0x81, 0xee, 0x8f, 0x27, 0x23, 0xdb, 0xa5, 0xf7, - 0xac, 0xc0, 0xbe, 0x20, 0x01, 0xbd, 0xe7, 0x07, 0x5e, 0xe8, 0x49, 0xaa, 0xc5, 0x09, 0xf4, 0xfe, + 0xac, 0xc0, 0xbe, 0x20, 0x01, 0xbd, 0xe7, 0x07, 0x5e, 0xe8, 0x49, 0xaa, 0xc5, 0x09, 0xf4, 0xc1, 0x99, 0x49, 0xcf, 0xec, 0xa1, 0x17, 0xf8, 0x2d, 0xd7, 0x73, 0x4c, 0xab, 0x25, 0xe7, 0xb4, 0xe4, 0x1c, 0x31, 0xac, 0xf9, 0x7b, 0x23, 0xcf, 0x1b, 0x8d, 0x89, 0x40, 0x38, 0x9d, 0xbc, 0xbc, 0x67, - 0x4d, 0x02, 0x33, 0xb4, 0x3d, 0x57, 0xf6, 0xbf, 0x37, 0xdb, 0x1f, 0xda, 0x0e, 0xa1, 0xa1, 0xe9, - 0xf8, 0x72, 0xc0, 0xfb, 0x11, 0x2f, 0xf4, 0xcc, 0x0c, 0x88, 0x75, 0xef, 0x6c, 0x38, 0xa6, 0x3e, - 0x19, 0xb2, 0x5f, 0x83, 0x7d, 0xc8, 0x61, 0x1f, 0xcc, 0x0c, 0xa3, 0x61, 0x30, 0x19, 0x86, 0x11, + 0x4d, 0x02, 0x33, 0xb4, 0x3d, 0x57, 0xf6, 0xbf, 0x3f, 0xdb, 0x1f, 0xda, 0x0e, 0xa1, 0xa1, 0xe9, + 0xf8, 0x72, 0xc0, 0x07, 0x11, 0x2f, 0xf4, 0xcc, 0x0c, 0x88, 0x75, 0xef, 0x6c, 0x38, 0xa6, 0x3e, + 0x19, 0xb2, 0x5f, 0x83, 0x7d, 0xc8, 0x61, 0x1f, 0xce, 0x0c, 0xa3, 0x61, 0x30, 0x19, 0x86, 0x11, 0xe7, 0x66, 0x18, 0x06, 0xf6, 0xe9, 0x24, 0x24, 0x62, 0xb4, 0x7e, 0x07, 0x6e, 0x0f, 0x4c, 0x7a, 0xde, 0xf1, 0xdc, 0x97, 0xf6, 0xa8, 0x3f, 0x3c, 0x23, 0x8e, 0x89, 0xc9, 0x37, 0x13, 0x42, 0x43, 0xfd, 0x4f, 0xa0, 0x31, 0xdf, 0x45, 0x7d, 0xcf, 0xa5, 0x04, 0x7d, 0x01, 0x79, 0xb6, 0x64, 0x43, - 0xb9, 0xab, 0x6c, 0x57, 0x77, 0x3e, 0x68, 0xbd, 0x49, 0x04, 0x82, 0x87, 0x96, 0x64, 0xb5, 0xd5, + 0xb9, 0xab, 0x6c, 0x57, 0x77, 0x3e, 0x6c, 0xbd, 0x49, 0x04, 0x82, 0x87, 0x96, 0x64, 0xb5, 0xd5, 0xf7, 0xc9, 0x10, 0xf3, 0x99, 0xfa, 0x4d, 0xb8, 0xd1, 0x31, 0x7d, 0xf3, 0xd4, 0x1e, 0xdb, 0xa1, 0x4d, 0x68, 0xb4, 0xe8, 0x04, 0xb6, 0xd2, 0xcd, 0x72, 0xc1, 0x9f, 0x41, 0x6d, 0x98, 0x68, 0x97, 0x0b, 0x3f, 0x68, 0x65, 0x92, 0x7d, 0x6b, 0x97, 0x53, 0x29, 0xe0, 0x14, 0x9c, 0xbe, 0x05, 0xe8, @@ -3699,217 +3708,217 @@ var fileDescriptor_4a8f45747846a74d = []byte{ 0xc9, 0xcc, 0x2b, 0x80, 0x58, 0x8e, 0x8c, 0x15, 0x75, 0xbb, 0xba, 0xf3, 0x55, 0x46, 0x56, 0x16, 0xe0, 0xb5, 0xda, 0x31, 0x58, 0xd7, 0x0d, 0x83, 0x2b, 0x9c, 0x40, 0x47, 0x5f, 0x43, 0xf1, 0x8c, 0x98, 0xe3, 0xf0, 0xac, 0x91, 0xbb, 0xab, 0x6c, 0xd7, 0x77, 0x1e, 0x5f, 0x63, 0x9d, 0x3d, 0x0e, - 0xd4, 0x0f, 0xcd, 0x90, 0x60, 0x89, 0x8a, 0x3e, 0x04, 0x24, 0xbe, 0x0c, 0x8b, 0xd0, 0x61, 0x60, + 0xd4, 0x0f, 0xcd, 0x90, 0x60, 0x89, 0x8a, 0x3e, 0x02, 0x24, 0xbe, 0x0c, 0x8b, 0xd0, 0x61, 0x60, 0xfb, 0xcc, 0x24, 0x1b, 0xea, 0x5d, 0x65, 0xbb, 0x82, 0x37, 0x45, 0xcf, 0xee, 0xb4, 0xa3, 0xe9, 0xc3, 0xc6, 0x0c, 0xb7, 0x48, 0x03, 0xf5, 0x9c, 0x5c, 0x71, 0x8d, 0x54, 0x30, 0xfb, 0x44, 0x4f, - 0xa0, 0x70, 0x61, 0x8e, 0x27, 0x84, 0xb3, 0x5c, 0xdd, 0xf9, 0xf8, 0x6d, 0xe6, 0x21, 0x4d, 0x74, - 0x2a, 0x07, 0x2c, 0xe6, 0x7f, 0x96, 0xfb, 0x54, 0xd1, 0x1f, 0x40, 0x35, 0xc1, 0x37, 0xaa, 0x03, - 0x9c, 0xf4, 0x76, 0xbb, 0x83, 0x6e, 0x67, 0xd0, 0xdd, 0xd5, 0xd6, 0xd0, 0x3a, 0x54, 0x4e, 0x7a, - 0x7b, 0xdd, 0xf6, 0xc1, 0x60, 0xef, 0xb9, 0xa6, 0xa0, 0x2a, 0x94, 0x22, 0x22, 0xa7, 0x5f, 0x02, - 0xc2, 0x64, 0xe8, 0x5d, 0x90, 0x80, 0x19, 0xb2, 0xd4, 0x2a, 0xba, 0x0d, 0xa5, 0xd0, 0xa4, 0xe7, - 0x86, 0x6d, 0x49, 0x9e, 0x8b, 0x8c, 0xdc, 0xb7, 0xd0, 0x3e, 0x14, 0xcf, 0x4c, 0xd7, 0x1a, 0xbf, - 0x9d, 0xef, 0xb4, 0xa8, 0x19, 0xf8, 0x1e, 0x9f, 0x88, 0x25, 0x00, 0xb3, 0xee, 0xd4, 0xca, 0x42, - 0x01, 0xfa, 0x73, 0xd0, 0xfa, 0xa1, 0x19, 0x84, 0x49, 0x76, 0xba, 0x90, 0x67, 0xeb, 0x4b, 0x8b, - 0x5e, 0x66, 0x4d, 0xb1, 0x33, 0x31, 0x9f, 0xae, 0xff, 0x6f, 0x0e, 0x36, 0x13, 0xd8, 0xd2, 0x52, - 0x9f, 0x41, 0x31, 0x20, 0x74, 0x32, 0x0e, 0x39, 0x7c, 0x7d, 0xe7, 0x51, 0x46, 0xf8, 0x39, 0xa4, - 0x16, 0xe6, 0x30, 0x58, 0xc2, 0xa1, 0x6d, 0xd0, 0xc4, 0x0c, 0x83, 0x04, 0x81, 0x17, 0x18, 0x0e, - 0x1d, 0x71, 0xa9, 0x55, 0x70, 0x5d, 0xb4, 0x77, 0x59, 0xf3, 0x21, 0x1d, 0x25, 0xa4, 0xaa, 0x5e, - 0x53, 0xaa, 0xc8, 0x04, 0xcd, 0x25, 0xe1, 0x6b, 0x2f, 0x38, 0x37, 0x98, 0x68, 0x03, 0xdb, 0x22, - 0x8d, 0x3c, 0x07, 0xfd, 0x24, 0x23, 0x68, 0x4f, 0x4c, 0x3f, 0x92, 0xb3, 0xf1, 0x86, 0x9b, 0x6e, - 0xd0, 0x7f, 0x08, 0x45, 0xf1, 0x4f, 0x99, 0x25, 0xf5, 0x4f, 0x3a, 0x9d, 0x6e, 0xbf, 0xaf, 0xad, - 0xa1, 0x0a, 0x14, 0x70, 0x77, 0x80, 0x99, 0x85, 0x55, 0xa0, 0xf0, 0xb8, 0x3d, 0x68, 0x1f, 0x68, - 0x39, 0xfd, 0x07, 0xb0, 0xf1, 0xcc, 0xb4, 0xc3, 0x2c, 0xc6, 0xa5, 0x7b, 0xa0, 0x4d, 0xc7, 0x4a, - 0xed, 0xec, 0xa7, 0xb4, 0x93, 0x5d, 0x34, 0xdd, 0x4b, 0x3b, 0x9c, 0xd1, 0x87, 0x06, 0x2a, 0x09, - 0x02, 0xa9, 0x02, 0xf6, 0xa9, 0xbf, 0x86, 0x8d, 0x7e, 0xe8, 0xf9, 0x99, 0x2c, 0xff, 0x47, 0x50, - 0x62, 0xa7, 0x8d, 0x37, 0x09, 0xa5, 0xe9, 0xdf, 0x69, 0x89, 0xd3, 0xa8, 0x15, 0x9d, 0x46, 0xad, - 0x5d, 0x79, 0x5a, 0xe1, 0x68, 0x24, 0xba, 0x05, 0x45, 0x6a, 0x8f, 0x5c, 0x73, 0x2c, 0xbd, 0x85, - 0xa4, 0x74, 0xc4, 0x8c, 0x3c, 0x5a, 0x58, 0x1a, 0x7e, 0x07, 0xd0, 0x2e, 0xa1, 0x61, 0xe0, 0x5d, - 0x65, 0xe2, 0x67, 0x0b, 0x0a, 0x2f, 0xbd, 0x60, 0x28, 0x36, 0x62, 0x19, 0x0b, 0x82, 0x6d, 0xaa, - 0x14, 0x88, 0xc4, 0xfe, 0x10, 0xd0, 0xbe, 0xcb, 0xce, 0x94, 0x6c, 0x8a, 0xf8, 0x87, 0x1c, 0xdc, - 0x48, 0x8d, 0x97, 0xca, 0x58, 0x7d, 0x1f, 0x32, 0xc7, 0x34, 0xa1, 0x62, 0x1f, 0xa2, 0x23, 0x28, - 0x8a, 0x11, 0x52, 0x92, 0xf7, 0x97, 0x00, 0x12, 0xc7, 0x94, 0x84, 0x93, 0x30, 0x0b, 0x8d, 0x5e, - 0x7d, 0xb7, 0x46, 0xff, 0x1a, 0xb4, 0xe8, 0x7f, 0xd0, 0xb7, 0xea, 0xe6, 0x2b, 0xb8, 0x31, 0xf4, - 0xc6, 0x63, 0x32, 0x64, 0xd6, 0x60, 0xd8, 0x6e, 0x48, 0x82, 0x0b, 0x73, 0xfc, 0x76, 0xbb, 0x41, - 0xd3, 0x59, 0xfb, 0x72, 0x92, 0xfe, 0x02, 0x36, 0x13, 0x0b, 0x4b, 0x45, 0x3c, 0x86, 0x02, 0x65, - 0x0d, 0x52, 0x13, 0x1f, 0x2d, 0xa9, 0x09, 0x8a, 0xc5, 0x74, 0xfd, 0x86, 0x00, 0xef, 0x5e, 0x10, - 0x37, 0xfe, 0x5b, 0xfa, 0x2e, 0x6c, 0xf6, 0xb9, 0x99, 0x66, 0xb2, 0xc3, 0xa9, 0x89, 0xe7, 0x52, - 0x26, 0xbe, 0x05, 0x28, 0x89, 0x22, 0x0d, 0xf1, 0x0a, 0x36, 0xba, 0x97, 0x64, 0x98, 0x09, 0xb9, - 0x01, 0xa5, 0xa1, 0xe7, 0x38, 0xa6, 0x6b, 0x35, 0x72, 0x77, 0xd5, 0xed, 0x0a, 0x8e, 0xc8, 0xe4, - 0x5e, 0x54, 0xb3, 0xee, 0x45, 0xfd, 0xef, 0x14, 0xd0, 0xa6, 0x6b, 0x4b, 0x41, 0x32, 0xee, 0x43, - 0x8b, 0x01, 0xb1, 0xb5, 0x6b, 0x58, 0x52, 0xb2, 0x3d, 0x72, 0x17, 0xa2, 0x9d, 0x04, 0x41, 0xc2, - 0x1d, 0xa9, 0xd7, 0x74, 0x47, 0xfa, 0x1e, 0xfc, 0x4e, 0xc4, 0x4e, 0x3f, 0x0c, 0x88, 0xe9, 0xd8, - 0xee, 0x68, 0xff, 0xe8, 0xc8, 0x27, 0x82, 0x71, 0x84, 0x20, 0x6f, 0x99, 0xa1, 0x29, 0x19, 0xe3, - 0xdf, 0x6c, 0xd3, 0x0f, 0xc7, 0x1e, 0x8d, 0x37, 0x3d, 0x27, 0xf4, 0xff, 0x50, 0xa1, 0x31, 0x07, - 0x15, 0x89, 0xf7, 0x05, 0x14, 0x28, 0x09, 0x27, 0xbe, 0x34, 0x95, 0x6e, 0x66, 0x86, 0x17, 0xe3, - 0xb5, 0xfa, 0x0c, 0x0c, 0x0b, 0x4c, 0x34, 0x82, 0x72, 0x18, 0x5e, 0x19, 0xd4, 0xfe, 0x79, 0x14, - 0x10, 0x1c, 0x5c, 0x17, 0x7f, 0x40, 0x02, 0xc7, 0x76, 0xcd, 0x71, 0xdf, 0xfe, 0x39, 0xc1, 0xa5, - 0x30, 0xbc, 0x62, 0x1f, 0xe8, 0x39, 0x33, 0x78, 0xcb, 0x76, 0xa5, 0xd8, 0x3b, 0xab, 0xae, 0x92, - 0x10, 0x30, 0x16, 0x88, 0xcd, 0x03, 0x28, 0xf0, 0xff, 0xb4, 0x8a, 0x21, 0x6a, 0xa0, 0x86, 0xe1, - 0x15, 0x67, 0xaa, 0x8c, 0xd9, 0x67, 0xf3, 0x21, 0xd4, 0x92, 0xff, 0x80, 0x19, 0xd2, 0x19, 0xb1, - 0x47, 0x67, 0xc2, 0xc0, 0x0a, 0x58, 0x52, 0x4c, 0x93, 0xaf, 0x6d, 0x4b, 0x86, 0xac, 0x05, 0x2c, - 0x08, 0xfd, 0x5f, 0x73, 0x70, 0x67, 0x81, 0x64, 0xa4, 0xb1, 0xbe, 0x48, 0x19, 0xeb, 0x3b, 0x92, - 0x42, 0x64, 0xf1, 0x2f, 0x52, 0x16, 0xff, 0x0e, 0xc1, 0xd9, 0xb6, 0xb9, 0x05, 0x45, 0x72, 0x69, - 0x87, 0xc4, 0x92, 0xa2, 0x92, 0x54, 0x62, 0x3b, 0xe5, 0xaf, 0xbb, 0x9d, 0x3e, 0x86, 0xad, 0x4e, - 0x40, 0xcc, 0x90, 0x48, 0x57, 0x1e, 0xd9, 0xff, 0x1d, 0x28, 0x9b, 0xe3, 0xb1, 0x37, 0x9c, 0xaa, - 0xb5, 0xc4, 0xe9, 0x7d, 0x4b, 0xff, 0x56, 0x81, 0x9b, 0x33, 0x73, 0xa4, 0xa4, 0x4f, 0xa1, 0x6e, - 0x53, 0x6f, 0xcc, 0xff, 0x84, 0x91, 0xb8, 0xc5, 0xfd, 0x78, 0xb9, 0xe3, 0x64, 0x3f, 0xc2, 0xe0, - 0x97, 0xba, 0x75, 0x3b, 0x49, 0x72, 0xab, 0xe2, 0x8b, 0x5b, 0x72, 0x37, 0x47, 0xa4, 0xfe, 0x8f, - 0x0a, 0xdc, 0x94, 0xa7, 0x78, 0xe6, 0x3f, 0xb3, 0x80, 0xe5, 0xdc, 0xbb, 0x66, 0x59, 0x6f, 0xc0, - 0xad, 0x59, 0xbe, 0xa4, 0x5f, 0xff, 0xbf, 0x3c, 0xa0, 0xf9, 0x1b, 0x24, 0xfa, 0x3e, 0xd4, 0x28, - 0x71, 0x2d, 0x43, 0x9c, 0x09, 0xe2, 0xb8, 0x2a, 0xe3, 0x2a, 0x6b, 0x13, 0x87, 0x03, 0x65, 0x6e, - 0x8e, 0x5c, 0x4a, 0x6e, 0xcb, 0x98, 0x7f, 0xa3, 0x33, 0xa8, 0xbd, 0xa4, 0x46, 0xbc, 0x36, 0x37, - 0x9a, 0x7a, 0x66, 0xd7, 0x35, 0xcf, 0x47, 0xeb, 0x71, 0x3f, 0xfe, 0x5f, 0xb8, 0xfa, 0x92, 0xc6, - 0x04, 0xfa, 0xa5, 0x02, 0xb7, 0xa3, 0xd0, 0x61, 0x2a, 0x3e, 0xc7, 0xb3, 0x08, 0x6d, 0xe4, 0xef, - 0xaa, 0xdb, 0xf5, 0x9d, 0xe3, 0x6b, 0xc8, 0x6f, 0xae, 0xf1, 0xd0, 0xb3, 0x08, 0xbe, 0xe9, 0x2e, - 0x68, 0xa5, 0xa8, 0x05, 0x37, 0x9c, 0x09, 0x0d, 0x0d, 0x61, 0x05, 0x86, 0x1c, 0xd4, 0x28, 0x70, - 0xb9, 0x6c, 0xb2, 0xae, 0x94, 0xad, 0xa2, 0x73, 0x58, 0x77, 0xbc, 0x89, 0x1b, 0x1a, 0x43, 0x7e, - 0xc7, 0xa1, 0x8d, 0xe2, 0x52, 0x97, 0xdf, 0x05, 0x52, 0x3a, 0x64, 0x70, 0xe2, 0xc6, 0x44, 0x71, - 0xcd, 0x49, 0x50, 0x4c, 0x91, 0x01, 0x71, 0xbc, 0x90, 0x18, 0xcc, 0x27, 0xd2, 0x46, 0x49, 0x28, - 0x52, 0xb4, 0xb1, 0xed, 0x4f, 0xf5, 0x16, 0x54, 0x13, 0x62, 0x46, 0x65, 0xc8, 0xf7, 0x8e, 0x7a, - 0x5d, 0x6d, 0x0d, 0x01, 0x14, 0x3b, 0x7b, 0xf8, 0xe8, 0x68, 0x20, 0x6e, 0x06, 0xfb, 0x87, 0xed, - 0x27, 0x5d, 0x2d, 0xa7, 0x77, 0xa1, 0x96, 0x5c, 0x10, 0x21, 0xa8, 0x9f, 0xf4, 0x9e, 0xf6, 0x8e, - 0x9e, 0xf5, 0x8c, 0xc3, 0xa3, 0x93, 0xde, 0x80, 0xdd, 0x29, 0xea, 0x00, 0xed, 0xde, 0xf3, 0x29, - 0xbd, 0x0e, 0x95, 0xde, 0x51, 0x44, 0x2a, 0xcd, 0x9c, 0xa6, 0xe8, 0xff, 0xae, 0xc2, 0xd6, 0x22, - 0xd9, 0x23, 0x0b, 0xf2, 0x4c, 0x8f, 0xf2, 0x56, 0xf7, 0xee, 0xd5, 0xc8, 0xd1, 0x99, 0xf9, 0xfa, - 0xa6, 0x74, 0xe3, 0x15, 0xcc, 0xbf, 0x91, 0x01, 0xc5, 0xb1, 0x79, 0x4a, 0xc6, 0xb4, 0xa1, 0xf2, - 0xbc, 0xc7, 0x93, 0xeb, 0xac, 0x7d, 0xc0, 0x91, 0x44, 0xd2, 0x43, 0xc2, 0xa2, 0x01, 0x54, 0xcf, - 0x3c, 0x1a, 0x52, 0x21, 0x3a, 0xe9, 0x3b, 0x77, 0x32, 0xae, 0xb2, 0x37, 0x9d, 0x89, 0x93, 0x30, - 0xcd, 0x07, 0x50, 0x4d, 0x2c, 0xb6, 0x20, 0x67, 0xb1, 0x95, 0xcc, 0x59, 0x54, 0x92, 0x09, 0x88, - 0x47, 0xf3, 0x3a, 0x60, 0x32, 0x62, 0x46, 0xb0, 0x77, 0xd4, 0x1f, 0x88, 0xdb, 0xe1, 0x13, 0x7c, - 0x74, 0x72, 0xac, 0x29, 0xac, 0x71, 0xd0, 0xee, 0x3f, 0xd5, 0x72, 0xb1, 0x8d, 0xa8, 0x7a, 0x07, - 0xaa, 0x09, 0xbe, 0x50, 0x13, 0xca, 0x8c, 0x33, 0xd7, 0x74, 0x88, 0x64, 0x20, 0xa6, 0x99, 0xdf, - 0x34, 0x2d, 0x2b, 0x20, 0x94, 0x4a, 0x3e, 0x22, 0x52, 0x7f, 0x01, 0x95, 0xdd, 0x5e, 0x5f, 0x42, - 0x34, 0xa0, 0x44, 0x49, 0xc0, 0xfe, 0x37, 0xcf, 0x3e, 0x55, 0x70, 0x44, 0x32, 0x70, 0x4a, 0xcc, - 0x60, 0x78, 0x46, 0xa8, 0x3c, 0xcf, 0x63, 0x9a, 0xcd, 0xf2, 0x78, 0x16, 0x47, 0xe8, 0xae, 0x82, - 0x23, 0x52, 0xff, 0x9f, 0x12, 0xc0, 0x34, 0xa3, 0x80, 0xea, 0x90, 0x8b, 0x7d, 0x70, 0xce, 0xb6, - 0x98, 0x1d, 0x70, 0x6e, 0xa5, 0x1d, 0x70, 0x4e, 0x77, 0xe0, 0xa6, 0x43, 0x47, 0xbe, 0x39, 0x3c, - 0x37, 0x64, 0x22, 0x40, 0x6c, 0x55, 0xee, 0xcf, 0x6a, 0xf8, 0x86, 0xec, 0x94, 0x3b, 0x51, 0xe0, - 0x1e, 0x80, 0x4a, 0xdc, 0x0b, 0xee, 0x7b, 0xaa, 0x3b, 0x9f, 0x2d, 0x9d, 0xe9, 0x68, 0x75, 0xdd, - 0x0b, 0x61, 0x2b, 0x0c, 0x06, 0x19, 0x00, 0x16, 0xb9, 0xb0, 0x87, 0xc4, 0x60, 0xa0, 0x05, 0x0e, - 0xfa, 0xc5, 0xf2, 0xa0, 0xbb, 0x1c, 0x23, 0x86, 0xae, 0x58, 0x11, 0x8d, 0x7a, 0x50, 0x09, 0x08, - 0xf5, 0x26, 0xc1, 0x90, 0x08, 0x07, 0x94, 0xfd, 0x32, 0x82, 0xa3, 0x79, 0x78, 0x0a, 0x81, 0x76, - 0xa1, 0xc8, 0xfd, 0x0e, 0xf3, 0x30, 0xea, 0x77, 0xa6, 0x4d, 0xd3, 0x60, 0xdc, 0x93, 0x60, 0x39, - 0x17, 0x3d, 0x81, 0x92, 0x60, 0x91, 0x36, 0xca, 0x1c, 0xe6, 0xc3, 0xac, 0x4e, 0x91, 0xcf, 0xc2, - 0xd1, 0x6c, 0xa6, 0xd5, 0x09, 0x25, 0x41, 0xa3, 0x22, 0xb4, 0xca, 0xbe, 0xd1, 0xf7, 0xa0, 0x22, - 0xce, 0x60, 0xcb, 0x0e, 0x1a, 0x20, 0x8c, 0x93, 0x37, 0xec, 0xda, 0x01, 0x7a, 0x0f, 0xaa, 0x22, - 0x9e, 0x32, 0xb8, 0x57, 0xa8, 0xf2, 0x6e, 0x10, 0x4d, 0xc7, 0xcc, 0x37, 0x88, 0x01, 0x24, 0x08, - 0xc4, 0x80, 0x5a, 0x3c, 0x80, 0x04, 0x01, 0x1f, 0xf0, 0x07, 0xb0, 0xc1, 0xa3, 0xd0, 0x51, 0xe0, - 0x4d, 0x7c, 0x83, 0xdb, 0xd4, 0x3a, 0x1f, 0xb4, 0xce, 0x9a, 0x9f, 0xb0, 0xd6, 0x1e, 0x33, 0xae, - 0x3b, 0x50, 0x7e, 0xe5, 0x9d, 0x8a, 0x01, 0x75, 0xb1, 0x0f, 0x5e, 0x79, 0xa7, 0x51, 0x57, 0x1c, - 0x25, 0x6c, 0xa4, 0xa3, 0x84, 0x6f, 0xe0, 0xd6, 0xfc, 0x71, 0xc7, 0xa3, 0x05, 0xed, 0xfa, 0xd1, - 0xc2, 0x96, 0xbb, 0xc8, 0x0f, 0x7f, 0x09, 0xaa, 0xe5, 0xd2, 0xc6, 0xe6, 0x52, 0xc6, 0x11, 0xef, - 0x63, 0xcc, 0x26, 0x37, 0x3f, 0x81, 0x72, 0x64, 0x7d, 0xcb, 0xf8, 0xa5, 0xe6, 0x43, 0xa8, 0xa7, - 0x6d, 0x77, 0x29, 0xaf, 0xf6, 0xcf, 0x39, 0xa8, 0xc4, 0x56, 0x8a, 0x5c, 0xb8, 0xc1, 0xa5, 0xc8, - 0x42, 0x34, 0x63, 0x6a, 0xf4, 0x22, 0x30, 0xfc, 0x3c, 0xe3, 0xff, 0x6a, 0x47, 0x08, 0xf2, 0x16, - 0x2a, 0x77, 0x00, 0x8a, 0x91, 0xa7, 0xeb, 0x7d, 0x0d, 0x1b, 0x63, 0xdb, 0x9d, 0x5c, 0x26, 0xd6, - 0x12, 0x11, 0xdd, 0x1f, 0x66, 0x5c, 0xeb, 0x80, 0xcd, 0x9e, 0xae, 0x51, 0x1f, 0xa7, 0x68, 0xb4, - 0x07, 0x05, 0xdf, 0x0b, 0xc2, 0xe8, 0x90, 0xca, 0x7a, 0x7c, 0x1c, 0x7b, 0x41, 0x78, 0x68, 0xfa, - 0x3e, 0xbb, 0x98, 0x08, 0x00, 0xfd, 0xdb, 0x1c, 0xdc, 0x5a, 0xfc, 0xc7, 0x50, 0x0f, 0xd4, 0xa1, - 0x3f, 0x91, 0x42, 0x7a, 0xb8, 0xac, 0x90, 0x3a, 0xfe, 0x64, 0xca, 0x3f, 0x03, 0x42, 0xcf, 0xa0, - 0xe8, 0x10, 0xc7, 0x0b, 0xae, 0xa4, 0x2c, 0x1e, 0x2d, 0x0b, 0x79, 0xc8, 0x67, 0x4f, 0x51, 0x25, - 0x1c, 0xc2, 0x50, 0x96, 0xd6, 0x4b, 0xa5, 0x9f, 0x5c, 0x32, 0x75, 0x14, 0x41, 0xe2, 0x18, 0x47, - 0xff, 0x04, 0x6e, 0x2e, 0xfc, 0x2b, 0xe8, 0x77, 0x01, 0x86, 0xfe, 0xc4, 0xe0, 0xa9, 0x7d, 0x61, - 0x41, 0x2a, 0xae, 0x0c, 0xfd, 0x49, 0x9f, 0x37, 0xe8, 0x2f, 0xa0, 0xf1, 0x26, 0x7e, 0x99, 0xf7, - 0x11, 0x1c, 0x1b, 0xce, 0x29, 0x97, 0x81, 0x8a, 0xcb, 0xa2, 0xe1, 0xf0, 0x14, 0xe9, 0xb0, 0x1e, - 0x75, 0x9a, 0x97, 0x6c, 0x80, 0xca, 0x07, 0x54, 0xe5, 0x00, 0xf3, 0xf2, 0xf0, 0x54, 0xff, 0x55, - 0x0e, 0x36, 0x66, 0x58, 0x66, 0xd7, 0x33, 0xe1, 0xf1, 0xa2, 0x8b, 0xaf, 0xa0, 0x98, 0xfb, 0x1b, - 0xda, 0x56, 0x94, 0x32, 0xe5, 0xdf, 0xfc, 0xe0, 0xf3, 0x65, 0x3a, 0x33, 0x67, 0xfb, 0x6c, 0xfb, - 0x38, 0xa7, 0x76, 0x48, 0x79, 0x14, 0x52, 0xc0, 0x82, 0x40, 0xcf, 0xa1, 0x1e, 0x10, 0x7e, 0xe0, - 0x5a, 0x86, 0xb0, 0xb2, 0xc2, 0x52, 0x56, 0x26, 0x39, 0x64, 0xc6, 0x86, 0xd7, 0x23, 0x24, 0x46, - 0x51, 0xf4, 0x0c, 0xd6, 0xad, 0x2b, 0xd7, 0x74, 0xec, 0xa1, 0x44, 0x2e, 0xae, 0x8c, 0x5c, 0x93, - 0x40, 0x1c, 0x58, 0x7f, 0x00, 0xd5, 0x44, 0x27, 0xfb, 0x63, 0x3c, 0xdc, 0x92, 0x32, 0x11, 0x44, - 0xda, 0x5b, 0x14, 0xa4, 0xb7, 0xd0, 0x4f, 0xa1, 0x9a, 0xd8, 0x17, 0xcb, 0x4c, 0x65, 0xf2, 0x0c, - 0x3d, 0x2e, 0xcf, 0x02, 0xce, 0x85, 0x1e, 0xba, 0x0d, 0x25, 0x16, 0xea, 0x18, 0xb6, 0xcf, 0x25, - 0x5a, 0xc1, 0x45, 0x46, 0xee, 0xfb, 0xfa, 0x6f, 0x72, 0x50, 0x4f, 0x6f, 0xe9, 0xc8, 0x8e, 0x7c, - 0x12, 0xd8, 0x9e, 0x95, 0xb0, 0xa3, 0x63, 0xde, 0xc0, 0x6c, 0x85, 0x75, 0x7f, 0x33, 0xf1, 0x42, - 0x33, 0xb2, 0x95, 0xa1, 0x3f, 0xf9, 0x23, 0x46, 0xcf, 0xd8, 0xa0, 0x3a, 0x63, 0x83, 0xe8, 0x03, - 0x40, 0xd2, 0x94, 0xc6, 0xb6, 0x63, 0x87, 0xc6, 0xe9, 0x55, 0x48, 0x84, 0x8e, 0x55, 0xac, 0x89, - 0x9e, 0x03, 0xd6, 0xf1, 0x25, 0x6b, 0x67, 0x86, 0xe7, 0x79, 0x8e, 0x41, 0x87, 0x5e, 0x40, 0x0c, - 0xd3, 0x7a, 0xc5, 0x6f, 0x2d, 0x2a, 0xae, 0x7a, 0x9e, 0xd3, 0x67, 0x6d, 0x6d, 0xeb, 0x15, 0x3b, - 0xf9, 0x86, 0xfe, 0x84, 0x92, 0xd0, 0x60, 0x3f, 0x3c, 0x58, 0xa8, 0x60, 0x10, 0x4d, 0x1d, 0x7f, - 0x42, 0xd1, 0xef, 0xc3, 0x7a, 0x34, 0x80, 0x1f, 0x7e, 0xf2, 0xd4, 0xad, 0xc9, 0x21, 0xbc, 0x0d, - 0xe9, 0x50, 0x3b, 0x26, 0xc1, 0x90, 0xb8, 0xe1, 0xc0, 0x1e, 0x9e, 0xb3, 0xf3, 0x5d, 0xd9, 0x56, - 0x70, 0xaa, 0xed, 0xab, 0x7c, 0xb9, 0xa4, 0x95, 0x71, 0xb4, 0x9a, 0x43, 0x1c, 0xaa, 0xff, 0x0c, - 0x0a, 0x3c, 0x44, 0x60, 0x32, 0xe1, 0xc7, 0x2b, 0x3f, 0x7d, 0x65, 0x68, 0xc9, 0x1a, 0xf8, 0xd9, - 0xfb, 0x3d, 0xa8, 0x70, 0xd9, 0x27, 0x22, 0x7a, 0x1e, 0x77, 0xf2, 0xce, 0x26, 0x94, 0x03, 0x62, - 0x5a, 0x9e, 0x3b, 0x8e, 0x12, 0x3e, 0x31, 0xad, 0x7f, 0x03, 0x45, 0x71, 0xce, 0x5c, 0x03, 0xff, - 0x43, 0x40, 0xe2, 0x7f, 0x33, 0x7d, 0x3a, 0x36, 0xa5, 0x32, 0x0a, 0xe5, 0xaf, 0x8c, 0xa2, 0xe7, - 0x78, 0xda, 0xa1, 0xff, 0xa7, 0x22, 0xe2, 0x51, 0xf1, 0xfe, 0xc3, 0x02, 0x57, 0x66, 0xe4, 0xec, - 0xb6, 0x2c, 0x12, 0x4d, 0x11, 0x89, 0xf6, 0xa1, 0x28, 0xc3, 0xce, 0xdc, 0xaa, 0xcf, 0x67, 0x12, - 0x20, 0x4a, 0x3b, 0x13, 0x79, 0x21, 0x5f, 0x36, 0xed, 0x4c, 0x44, 0xda, 0x99, 0xb0, 0xdb, 0xa4, - 0x0c, 0x88, 0x05, 0x5c, 0x9e, 0xc7, 0xc3, 0x55, 0x2b, 0xce, 0xed, 0x13, 0xfd, 0xbf, 0x95, 0xd8, - 0x4d, 0x45, 0x39, 0x78, 0xf4, 0x35, 0x94, 0xd9, 0x8e, 0x37, 0x1c, 0xd3, 0x97, 0x2f, 0xca, 0x9d, - 0xd5, 0xd2, 0xfb, 0xd1, 0x21, 0x26, 0xc2, 0xd9, 0x92, 0x2f, 0x28, 0xe6, 0xee, 0xd8, 0x55, 0x22, - 0x72, 0x77, 0xec, 0x1b, 0xbd, 0x0f, 0x75, 0x73, 0x12, 0x7a, 0x86, 0x69, 0x5d, 0x90, 0x20, 0xb4, - 0x29, 0x91, 0xba, 0x5f, 0x67, 0xad, 0xed, 0xa8, 0xb1, 0xf9, 0x19, 0xd4, 0x92, 0x98, 0x6f, 0x0b, - 0x33, 0x0a, 0xc9, 0x30, 0xe3, 0x4f, 0x01, 0xa6, 0xf9, 0x2c, 0x66, 0x23, 0xe4, 0xd2, 0x66, 0xb7, - 0x7a, 0x79, 0x77, 0x2d, 0xe0, 0x32, 0x6b, 0xe8, 0xb0, 0xfb, 0x54, 0x3a, 0xd9, 0x5e, 0x88, 0x92, - 0xed, 0x6c, 0x33, 0xb3, 0xfd, 0x77, 0x6e, 0x8f, 0xc7, 0x71, 0x8e, 0xad, 0xe2, 0x79, 0xce, 0x53, - 0xde, 0xa0, 0xff, 0x36, 0x27, 0x6c, 0x45, 0x3c, 0x9b, 0x64, 0xba, 0xbb, 0xbc, 0x2b, 0x55, 0x3f, - 0x00, 0xa0, 0xa1, 0x19, 0xb0, 0x98, 0xc9, 0x8c, 0xb2, 0x7c, 0xcd, 0xb9, 0x6c, 0xfd, 0x20, 0xaa, - 0xe3, 0xc0, 0x15, 0x39, 0xba, 0x1d, 0xa2, 0xcf, 0xa1, 0x36, 0xf4, 0x1c, 0x7f, 0x4c, 0xe4, 0xe4, - 0xc2, 0x5b, 0x27, 0x57, 0xe3, 0xf1, 0xed, 0x30, 0x91, 0x5b, 0x2c, 0x5e, 0x37, 0xb7, 0xf8, 0x1b, - 0x45, 0xbc, 0xfe, 0x24, 0x1f, 0x9f, 0xd0, 0x68, 0x41, 0x85, 0xc3, 0x93, 0x15, 0x5f, 0xb2, 0xbe, - 0xab, 0xbc, 0xa1, 0xf9, 0x79, 0x96, 0x7a, 0x82, 0x37, 0x47, 0xb1, 0xff, 0xa6, 0x42, 0x25, 0x7e, - 0xf8, 0x99, 0xd3, 0xfd, 0xa7, 0x50, 0x89, 0x8b, 0x68, 0xa4, 0x83, 0xf8, 0x4e, 0xf5, 0xc4, 0x83, - 0xd1, 0x4b, 0x40, 0xe6, 0x68, 0x14, 0x47, 0xa7, 0xc6, 0x84, 0x9a, 0xa3, 0xe8, 0xd9, 0xed, 0xd3, - 0x25, 0xe4, 0x10, 0x1d, 0x67, 0x27, 0x6c, 0x3e, 0xd6, 0xcc, 0xd1, 0x28, 0xd5, 0x82, 0xfe, 0x0c, - 0x6e, 0xa6, 0xd7, 0x30, 0x4e, 0xaf, 0x0c, 0xdf, 0xb6, 0xe4, 0x1d, 0x79, 0x6f, 0xd9, 0xb7, 0xaf, - 0x56, 0x0a, 0xfe, 0xcb, 0xab, 0x63, 0xdb, 0x12, 0x32, 0x47, 0xc1, 0x5c, 0x47, 0xf3, 0x2f, 0xe0, - 0xf6, 0x1b, 0x86, 0x2f, 0xd0, 0x41, 0x2f, 0x5d, 0xd3, 0xb1, 0xba, 0x10, 0x12, 0xda, 0xfb, 0xb5, - 0x22, 0x9e, 0xe8, 0xd2, 0x32, 0x69, 0x27, 0xc3, 0xea, 0x7b, 0x19, 0xd7, 0xe9, 0x1c, 0x9f, 0x08, - 0x78, 0x1e, 0x49, 0x7f, 0x35, 0x13, 0x49, 0x67, 0x8d, 0x9f, 0x44, 0x40, 0x2a, 0x80, 0x24, 0x82, - 0xfe, 0x2f, 0x2a, 0x94, 0x23, 0x74, 0x7e, 0xc3, 0xbd, 0xa2, 0x21, 0x71, 0x8c, 0x38, 0xfd, 0xa6, - 0x60, 0x10, 0x4d, 0x3c, 0x29, 0xf4, 0x3d, 0xa8, 0xb0, 0x8b, 0xb4, 0xe8, 0xce, 0xf1, 0xee, 0x32, - 0x6b, 0xe0, 0x9d, 0xef, 0x41, 0x35, 0xf4, 0x42, 0x73, 0x6c, 0x84, 0xfc, 0x78, 0x57, 0xc5, 0x6c, - 0xde, 0xc4, 0x0f, 0x77, 0xf4, 0x43, 0xd8, 0x0c, 0xcf, 0x02, 0x2f, 0x0c, 0xc7, 0x2c, 0xb4, 0xe4, - 0x81, 0x8e, 0x88, 0x4b, 0xf2, 0x58, 0x8b, 0x3b, 0x44, 0x00, 0x44, 0x99, 0xf7, 0x9e, 0x0e, 0x66, - 0xa6, 0xcb, 0x9d, 0x48, 0x1e, 0xaf, 0xc7, 0xad, 0xcc, 0xb4, 0xd9, 0xe1, 0xe9, 0x8b, 0x00, 0x82, - 0xfb, 0x0a, 0x05, 0x47, 0x24, 0x32, 0x60, 0xc3, 0x21, 0x26, 0x9d, 0x04, 0xc4, 0x32, 0x5e, 0xda, - 0x64, 0x6c, 0x89, 0xc4, 0x44, 0x3d, 0xf3, 0xed, 0x20, 0x12, 0x4b, 0xeb, 0x31, 0x9f, 0x8d, 0xeb, - 0x11, 0x9c, 0xa0, 0x59, 0xe4, 0x20, 0xbe, 0xd0, 0x06, 0x54, 0xfb, 0xcf, 0xfb, 0x83, 0xee, 0xa1, - 0x71, 0x78, 0xb4, 0xdb, 0x95, 0x65, 0x3b, 0xfd, 0x2e, 0x16, 0xa4, 0xc2, 0xfa, 0x07, 0x47, 0x83, - 0xf6, 0x81, 0x31, 0xd8, 0xef, 0x3c, 0xed, 0x6b, 0x39, 0x74, 0x13, 0x36, 0x07, 0x7b, 0xf8, 0x68, - 0x30, 0x38, 0xe8, 0xee, 0x1a, 0xc7, 0x5d, 0xbc, 0x7f, 0xb4, 0xdb, 0xd7, 0x54, 0x84, 0xa0, 0x3e, - 0x6d, 0x1e, 0xec, 0x1f, 0x76, 0xb5, 0x3c, 0xaa, 0x42, 0xe9, 0xb8, 0x8b, 0x3b, 0xdd, 0xde, 0x40, - 0x2b, 0xe8, 0xbf, 0x52, 0xa1, 0x9a, 0xd0, 0x22, 0x33, 0xe4, 0x80, 0x8a, 0x6b, 0x48, 0x1e, 0xb3, - 0x4f, 0xfe, 0xcc, 0x68, 0x0e, 0xcf, 0x84, 0x76, 0xf2, 0x58, 0x10, 0xfc, 0xea, 0x61, 0x5e, 0x26, - 0xf6, 0x79, 0x1e, 0x97, 0x1d, 0xf3, 0x52, 0x80, 0x7c, 0x1f, 0x6a, 0xe7, 0x24, 0x70, 0xc9, 0x58, - 0xf6, 0x0b, 0x8d, 0x54, 0x45, 0x9b, 0x18, 0xb2, 0x0d, 0x9a, 0x1c, 0x32, 0x85, 0x11, 0xea, 0xa8, - 0x8b, 0xf6, 0xc3, 0x08, 0x6c, 0x0b, 0x0a, 0xa2, 0xbb, 0x24, 0xd6, 0xe7, 0x04, 0x3b, 0xa6, 0xe8, - 0x6b, 0xd3, 0xe7, 0x21, 0x5f, 0x1e, 0xf3, 0x6f, 0x74, 0x3a, 0xaf, 0x9f, 0x22, 0xd7, 0xcf, 0x83, - 0xe5, 0xcd, 0xf9, 0x4d, 0x2a, 0x3a, 0x8b, 0x55, 0x54, 0x02, 0x15, 0x47, 0xb5, 0x2e, 0x9d, 0x76, - 0x67, 0x8f, 0xa9, 0x65, 0x1d, 0x2a, 0x87, 0xed, 0x9f, 0x1a, 0x27, 0x7d, 0x9e, 0xd5, 0x46, 0x1a, - 0xd4, 0x9e, 0x76, 0x71, 0xaf, 0x7b, 0x20, 0x5b, 0x54, 0xb4, 0x05, 0x9a, 0x6c, 0x99, 0x8e, 0xcb, - 0x33, 0x04, 0xf1, 0x59, 0x40, 0x65, 0xc8, 0xf7, 0x9f, 0xb5, 0x8f, 0xb5, 0xa2, 0xfe, 0x5f, 0x39, - 0xd8, 0x10, 0xc7, 0x42, 0xfc, 0x2a, 0xff, 0xe6, 0x57, 0xc9, 0x64, 0x96, 0x27, 0x97, 0xce, 0xf2, - 0x44, 0x41, 0x28, 0x3f, 0xd5, 0xd5, 0x69, 0x10, 0xca, 0xb3, 0x43, 0x29, 0x8f, 0x9f, 0x5f, 0xc6, - 0xe3, 0x37, 0xa0, 0xe4, 0x10, 0x1a, 0xeb, 0xad, 0x82, 0x23, 0x12, 0xd9, 0x50, 0x35, 0x5d, 0xd7, - 0x0b, 0x4d, 0x91, 0x3a, 0x2d, 0x2e, 0x75, 0x18, 0xce, 0xfc, 0xe3, 0x56, 0x7b, 0x8a, 0x24, 0x1c, - 0x73, 0x12, 0xbb, 0xf9, 0x13, 0xd0, 0x66, 0x07, 0x2c, 0x73, 0x1c, 0xfe, 0xe0, 0xe3, 0xe9, 0x69, - 0x48, 0xd8, 0xbe, 0x90, 0x6f, 0x0e, 0xda, 0x1a, 0x23, 0xf0, 0x49, 0xaf, 0xb7, 0xdf, 0x7b, 0xa2, - 0x29, 0x08, 0xa0, 0xd8, 0xfd, 0xe9, 0xfe, 0xa0, 0xbb, 0xab, 0xe5, 0x76, 0x7e, 0xbd, 0x09, 0x45, - 0xc1, 0x24, 0xfa, 0x56, 0x46, 0x02, 0xc9, 0x8a, 0x4f, 0xf4, 0x93, 0xa5, 0x23, 0xea, 0x54, 0x15, - 0x69, 0xf3, 0xd1, 0xca, 0xf3, 0xe5, 0xeb, 0xdb, 0x1a, 0xfa, 0x1b, 0x05, 0x6a, 0xa9, 0x97, 0xb7, - 0xac, 0xa9, 0xe3, 0x05, 0x05, 0xa6, 0xcd, 0x1f, 0xaf, 0x34, 0x37, 0xe6, 0xe5, 0x97, 0x0a, 0x54, - 0x13, 0xa5, 0x95, 0xe8, 0xc1, 0x2a, 0xe5, 0x98, 0x82, 0x93, 0xcf, 0x56, 0xaf, 0xe4, 0xd4, 0xd7, - 0x3e, 0x52, 0xd0, 0x5f, 0x2b, 0x50, 0x4d, 0x14, 0x19, 0x66, 0x66, 0x65, 0xbe, 0x24, 0x32, 0x33, - 0x2b, 0x8b, 0x6a, 0x1a, 0xd7, 0xd0, 0x5f, 0x2a, 0x50, 0x89, 0x0b, 0x06, 0xd1, 0xfd, 0xe5, 0x4b, - 0x0c, 0x05, 0x13, 0x9f, 0xae, 0x5a, 0x9b, 0xa8, 0xaf, 0xa1, 0x3f, 0x87, 0x72, 0x54, 0x5d, 0x87, - 0xb2, 0x9e, 0x5e, 0x33, 0xa5, 0x7b, 0xcd, 0xfb, 0x4b, 0xcf, 0x4b, 0x2e, 0x1f, 0x95, 0xbc, 0x65, - 0x5e, 0x7e, 0xa6, 0x38, 0xaf, 0x79, 0x7f, 0xe9, 0x79, 0xf1, 0xf2, 0xcc, 0x12, 0x12, 0x95, 0x71, - 0x99, 0x2d, 0x61, 0xbe, 0x24, 0x2f, 0xb3, 0x25, 0x2c, 0x2a, 0xc4, 0x13, 0x8c, 0x24, 0x6a, 0xeb, - 0x32, 0x33, 0x32, 0x5f, 0xbf, 0x97, 0x99, 0x91, 0x05, 0xa5, 0x7c, 0xfa, 0x1a, 0xfa, 0x85, 0x92, - 0xbc, 0x17, 0xdc, 0x5f, 0xba, 0x84, 0x6c, 0x49, 0x93, 0x9c, 0x2b, 0x62, 0xe3, 0x1b, 0xf4, 0x17, - 0x32, 0x8b, 0x21, 0x2a, 0xd0, 0xd0, 0x32, 0x60, 0xa9, 0xa2, 0xb5, 0xe6, 0x27, 0xab, 0x1d, 0x36, - 0x9c, 0x89, 0xbf, 0x52, 0x00, 0xa6, 0xb5, 0x6a, 0x99, 0x99, 0x98, 0x2b, 0x92, 0x6b, 0x3e, 0x58, - 0x61, 0x66, 0x72, 0x83, 0x44, 0xb5, 0x34, 0x99, 0x37, 0xc8, 0x4c, 0x2d, 0x5d, 0xe6, 0x0d, 0x32, - 0x5b, 0x07, 0xa7, 0xaf, 0xa1, 0x7f, 0x52, 0x60, 0x73, 0xae, 0x96, 0x07, 0x3d, 0xba, 0x66, 0x39, - 0x57, 0xf3, 0x8b, 0xd5, 0x01, 0x22, 0xd6, 0xb6, 0x95, 0x8f, 0x14, 0xf4, 0xb7, 0x0a, 0xac, 0xa7, - 0xeb, 0x1f, 0x32, 0x9f, 0x52, 0x0b, 0xaa, 0x82, 0x9a, 0x0f, 0x57, 0x9b, 0x1c, 0x4b, 0xeb, 0xef, - 0x15, 0xa8, 0xa7, 0x4b, 0x61, 0xd0, 0xc3, 0xe5, 0xdc, 0xc2, 0x0c, 0x43, 0x9f, 0xaf, 0x38, 0x3b, - 0xe2, 0xe8, 0xcb, 0xd2, 0x1f, 0x17, 0x44, 0xf4, 0x56, 0xe4, 0x3f, 0x3f, 0xfa, 0xff, 0x00, 0x00, - 0x00, 0xff, 0xff, 0x2c, 0x87, 0xc3, 0x62, 0x98, 0x33, 0x00, 0x00, + 0xa0, 0x70, 0x61, 0x8e, 0x27, 0x84, 0xb3, 0x5c, 0xdd, 0xf9, 0xe1, 0xdb, 0xcc, 0x43, 0x9a, 0xe8, + 0x54, 0x0e, 0x58, 0xcc, 0xff, 0x2c, 0xf7, 0xa9, 0xa2, 0x3f, 0x80, 0x6a, 0x82, 0x6f, 0x54, 0x07, + 0x38, 0xe9, 0xed, 0x76, 0x07, 0xdd, 0xce, 0xa0, 0xbb, 0xab, 0xad, 0xa1, 0x75, 0xa8, 0x9c, 0xf4, + 0xf6, 0xba, 0xed, 0x83, 0xc1, 0xde, 0x73, 0x4d, 0x41, 0x55, 0x28, 0x45, 0x44, 0x4e, 0xbf, 0x04, + 0x84, 0xc9, 0xd0, 0xbb, 0x20, 0x01, 0x33, 0x64, 0xa9, 0x55, 0x74, 0x1b, 0x4a, 0xa1, 0x49, 0xcf, + 0x0d, 0xdb, 0x92, 0x3c, 0x17, 0x19, 0xb9, 0x6f, 0xa1, 0x7d, 0x28, 0x9e, 0x99, 0xae, 0x35, 0x7e, + 0x3b, 0xdf, 0x69, 0x51, 0x33, 0xf0, 0x3d, 0x3e, 0x11, 0x4b, 0x00, 0x66, 0xdd, 0xa9, 0x95, 0x85, + 0x02, 0xf4, 0xe7, 0xa0, 0xf5, 0x43, 0x33, 0x08, 0x93, 0xec, 0x74, 0x21, 0xcf, 0xd6, 0x97, 0x16, + 0xbd, 0xcc, 0x9a, 0x62, 0x67, 0x62, 0x3e, 0x5d, 0xff, 0xdf, 0x1c, 0x6c, 0x26, 0xb0, 0xa5, 0xa5, + 0x3e, 0x83, 0x62, 0x40, 0xe8, 0x64, 0x1c, 0x72, 0xf8, 0xfa, 0xce, 0xa3, 0x8c, 0xf0, 0x73, 0x48, + 0x2d, 0xcc, 0x61, 0xb0, 0x84, 0x43, 0xdb, 0xa0, 0x89, 0x19, 0x06, 0x09, 0x02, 0x2f, 0x30, 0x1c, + 0x3a, 0xe2, 0x52, 0xab, 0xe0, 0xba, 0x68, 0xef, 0xb2, 0xe6, 0x43, 0x3a, 0x4a, 0x48, 0x55, 0xbd, + 0xa6, 0x54, 0x91, 0x09, 0x9a, 0x4b, 0xc2, 0xd7, 0x5e, 0x70, 0x6e, 0x30, 0xd1, 0x06, 0xb6, 0x45, + 0x1a, 0x79, 0x0e, 0xfa, 0x49, 0x46, 0xd0, 0x9e, 0x98, 0x7e, 0x24, 0x67, 0xe3, 0x0d, 0x37, 0xdd, + 0xa0, 0xff, 0x00, 0x8a, 0xe2, 0x9f, 0x32, 0x4b, 0xea, 0x9f, 0x74, 0x3a, 0xdd, 0x7e, 0x5f, 0x5b, + 0x43, 0x15, 0x28, 0xe0, 0xee, 0x00, 0x33, 0x0b, 0xab, 0x40, 0xe1, 0x71, 0x7b, 0xd0, 0x3e, 0xd0, + 0x72, 0xfa, 0xf7, 0x61, 0xe3, 0x99, 0x69, 0x87, 0x59, 0x8c, 0x4b, 0xf7, 0x40, 0x9b, 0x8e, 0x95, + 0xda, 0xd9, 0x4f, 0x69, 0x27, 0xbb, 0x68, 0xba, 0x97, 0x76, 0x38, 0xa3, 0x0f, 0x0d, 0x54, 0x12, + 0x04, 0x52, 0x05, 0xec, 0x53, 0x7f, 0x0d, 0x1b, 0xfd, 0xd0, 0xf3, 0x33, 0x59, 0xfe, 0x8f, 0xa0, + 0xc4, 0x4e, 0x1b, 0x6f, 0x12, 0x4a, 0xd3, 0xbf, 0xd3, 0x12, 0xa7, 0x51, 0x2b, 0x3a, 0x8d, 0x5a, + 0xbb, 0xf2, 0xb4, 0xc2, 0xd1, 0x48, 0x74, 0x0b, 0x8a, 0xd4, 0x1e, 0xb9, 0xe6, 0x58, 0x7a, 0x0b, + 0x49, 0xe9, 0x88, 0x19, 0x79, 0xb4, 0xb0, 0x34, 0xfc, 0x0e, 0xa0, 0x5d, 0x42, 0xc3, 0xc0, 0xbb, + 0xca, 0xc4, 0xcf, 0x16, 0x14, 0x5e, 0x7a, 0xc1, 0x50, 0x6c, 0xc4, 0x32, 0x16, 0x04, 0xdb, 0x54, + 0x29, 0x10, 0x89, 0xfd, 0x11, 0xa0, 0x7d, 0x97, 0x9d, 0x29, 0xd9, 0x14, 0xf1, 0x0f, 0x39, 0xb8, + 0x91, 0x1a, 0x2f, 0x95, 0xb1, 0xfa, 0x3e, 0x64, 0x8e, 0x69, 0x42, 0xc5, 0x3e, 0x44, 0x47, 0x50, + 0x14, 0x23, 0xa4, 0x24, 0xef, 0x2f, 0x01, 0x24, 0x8e, 0x29, 0x09, 0x27, 0x61, 0x16, 0x1a, 0xbd, + 0xfa, 0x6e, 0x8d, 0xfe, 0x35, 0x68, 0xd1, 0xff, 0xa0, 0x6f, 0xd5, 0xcd, 0x57, 0x70, 0x63, 0xe8, + 0x8d, 0xc7, 0x64, 0xc8, 0xac, 0xc1, 0xb0, 0xdd, 0x90, 0x04, 0x17, 0xe6, 0xf8, 0xed, 0x76, 0x83, + 0xa6, 0xb3, 0xf6, 0xe5, 0x24, 0xfd, 0x05, 0x6c, 0x26, 0x16, 0x96, 0x8a, 0x78, 0x0c, 0x05, 0xca, + 0x1a, 0xa4, 0x26, 0x3e, 0x5e, 0x52, 0x13, 0x14, 0x8b, 0xe9, 0xfa, 0x0d, 0x01, 0xde, 0xbd, 0x20, + 0x6e, 0xfc, 0xb7, 0xf4, 0x5d, 0xd8, 0xec, 0x73, 0x33, 0xcd, 0x64, 0x87, 0x53, 0x13, 0xcf, 0xa5, + 0x4c, 0x7c, 0x0b, 0x50, 0x12, 0x45, 0x1a, 0xe2, 0x15, 0x6c, 0x74, 0x2f, 0xc9, 0x30, 0x13, 0x72, + 0x03, 0x4a, 0x43, 0xcf, 0x71, 0x4c, 0xd7, 0x6a, 0xe4, 0xee, 0xaa, 0xdb, 0x15, 0x1c, 0x91, 0xc9, + 0xbd, 0xa8, 0x66, 0xdd, 0x8b, 0xfa, 0xdf, 0x29, 0xa0, 0x4d, 0xd7, 0x96, 0x82, 0x64, 0xdc, 0x87, + 0x16, 0x03, 0x62, 0x6b, 0xd7, 0xb0, 0xa4, 0x64, 0x7b, 0xe4, 0x2e, 0x44, 0x3b, 0x09, 0x82, 0x84, + 0x3b, 0x52, 0xaf, 0xe9, 0x8e, 0xf4, 0x3d, 0xf8, 0x9d, 0x88, 0x9d, 0x7e, 0x18, 0x10, 0xd3, 0xb1, + 0xdd, 0xd1, 0xfe, 0xd1, 0x91, 0x4f, 0x04, 0xe3, 0x08, 0x41, 0xde, 0x32, 0x43, 0x53, 0x32, 0xc6, + 0xbf, 0xd9, 0xa6, 0x1f, 0x8e, 0x3d, 0x1a, 0x6f, 0x7a, 0x4e, 0xe8, 0xff, 0xa1, 0x42, 0x63, 0x0e, + 0x2a, 0x12, 0xef, 0x0b, 0x28, 0x50, 0x12, 0x4e, 0x7c, 0x69, 0x2a, 0xdd, 0xcc, 0x0c, 0x2f, 0xc6, + 0x6b, 0xf5, 0x19, 0x18, 0x16, 0x98, 0x68, 0x04, 0xe5, 0x30, 0xbc, 0x32, 0xa8, 0xfd, 0xf3, 0x28, + 0x20, 0x38, 0xb8, 0x2e, 0xfe, 0x80, 0x04, 0x8e, 0xed, 0x9a, 0xe3, 0xbe, 0xfd, 0x73, 0x82, 0x4b, + 0x61, 0x78, 0xc5, 0x3e, 0xd0, 0x73, 0x66, 0xf0, 0x96, 0xed, 0x4a, 0xb1, 0x77, 0x56, 0x5d, 0x25, + 0x21, 0x60, 0x2c, 0x10, 0x9b, 0x07, 0x50, 0xe0, 0xff, 0x69, 0x15, 0x43, 0xd4, 0x40, 0x0d, 0xc3, + 0x2b, 0xce, 0x54, 0x19, 0xb3, 0xcf, 0xe6, 0x43, 0xa8, 0x25, 0xff, 0x01, 0x33, 0xa4, 0x33, 0x62, + 0x8f, 0xce, 0x84, 0x81, 0x15, 0xb0, 0xa4, 0x98, 0x26, 0x5f, 0xdb, 0x96, 0x0c, 0x59, 0x0b, 0x58, + 0x10, 0xfa, 0xbf, 0xe6, 0xe0, 0xce, 0x02, 0xc9, 0x48, 0x63, 0x7d, 0x91, 0x32, 0xd6, 0x77, 0x24, + 0x85, 0xc8, 0xe2, 0x5f, 0xa4, 0x2c, 0xfe, 0x1d, 0x82, 0xb3, 0x6d, 0x73, 0x0b, 0x8a, 0xe4, 0xd2, + 0x0e, 0x89, 0x25, 0x45, 0x25, 0xa9, 0xc4, 0x76, 0xca, 0x5f, 0x77, 0x3b, 0x1d, 0xc2, 0x56, 0x27, + 0x20, 0x66, 0x48, 0xa4, 0x2b, 0x8f, 0xec, 0xff, 0x0e, 0x94, 0xcd, 0xf1, 0xd8, 0x1b, 0x4e, 0xd5, + 0x5a, 0xe2, 0xf4, 0xbe, 0x85, 0x9a, 0x50, 0x3e, 0xf3, 0x68, 0xe8, 0x9a, 0x0e, 0x91, 0xce, 0x2b, + 0xa6, 0xf5, 0x6f, 0x15, 0xb8, 0x39, 0x83, 0x27, 0xb5, 0x70, 0x0a, 0x75, 0x9b, 0x7a, 0x63, 0xfe, + 0x07, 0x8d, 0xc4, 0x0d, 0xef, 0xc7, 0xcb, 0x1d, 0x35, 0xfb, 0x11, 0x06, 0xbf, 0xf0, 0xad, 0xdb, + 0x49, 0x92, 0x5b, 0x1c, 0x5f, 0xdc, 0x92, 0x3b, 0x3d, 0x22, 0xf5, 0x7f, 0x54, 0xe0, 0xa6, 0x3c, + 0xe1, 0xb3, 0xff, 0xd1, 0x79, 0x96, 0x73, 0xef, 0x9a, 0x65, 0xbd, 0x01, 0xb7, 0x66, 0xf9, 0x92, + 0x3e, 0xff, 0xff, 0xf2, 0x80, 0xe6, 0x6f, 0x97, 0xe8, 0x7b, 0x50, 0xa3, 0xc4, 0xb5, 0x0c, 0x71, + 0x5e, 0x88, 0xa3, 0xac, 0x8c, 0xab, 0xac, 0x4d, 0x1c, 0x1c, 0x94, 0xb9, 0x40, 0x72, 0x29, 0xb9, + 0x2d, 0x63, 0xfe, 0x8d, 0xce, 0xa0, 0xf6, 0x92, 0x1a, 0xf1, 0xda, 0xdc, 0xa0, 0xea, 0x99, 0xdd, + 0xda, 0x3c, 0x1f, 0xad, 0xc7, 0xfd, 0xf8, 0x7f, 0xe1, 0xea, 0x4b, 0x1a, 0x13, 0xe8, 0x97, 0x0a, + 0xdc, 0x8e, 0xc2, 0x8a, 0xa9, 0xf8, 0x1c, 0xcf, 0x22, 0xb4, 0x91, 0xbf, 0xab, 0x6e, 0xd7, 0x77, + 0x8e, 0xaf, 0x21, 0xbf, 0xb9, 0xc6, 0x43, 0xcf, 0x22, 0xf8, 0xa6, 0xbb, 0xa0, 0x95, 0xa2, 0x16, + 0xdc, 0x70, 0x26, 0x34, 0x34, 0x84, 0x15, 0x18, 0x72, 0x50, 0xa3, 0xc0, 0xe5, 0xb2, 0xc9, 0xba, + 0x52, 0xb6, 0x8a, 0xce, 0x61, 0xdd, 0xf1, 0x26, 0x6e, 0x68, 0x0c, 0xf9, 0xfd, 0x87, 0x36, 0x8a, + 0x4b, 0x5d, 0x8c, 0x17, 0x48, 0xe9, 0x90, 0xc1, 0x89, 0xdb, 0x14, 0xc5, 0x35, 0x27, 0x41, 0x31, + 0x45, 0x06, 0xc4, 0xf1, 0x42, 0x62, 0x30, 0x7f, 0x49, 0x1b, 0x25, 0xa1, 0x48, 0xd1, 0xc6, 0x5c, + 0x03, 0xd5, 0x5b, 0x50, 0x4d, 0x88, 0x19, 0x95, 0x21, 0xdf, 0x3b, 0xea, 0x75, 0xb5, 0x35, 0x04, + 0x50, 0xec, 0xec, 0xe1, 0xa3, 0xa3, 0x81, 0xb8, 0x35, 0xec, 0x1f, 0xb6, 0x9f, 0x74, 0xb5, 0x9c, + 0xde, 0x85, 0x5a, 0x72, 0x41, 0x84, 0xa0, 0x7e, 0xd2, 0x7b, 0xda, 0x3b, 0x7a, 0xd6, 0x33, 0x0e, + 0x8f, 0x4e, 0x7a, 0x03, 0x76, 0xdf, 0xa8, 0x03, 0xb4, 0x7b, 0xcf, 0xa7, 0xf4, 0x3a, 0x54, 0x7a, + 0x47, 0x11, 0xa9, 0x34, 0x73, 0x9a, 0xa2, 0xff, 0xbb, 0x0a, 0x5b, 0x8b, 0x64, 0x8f, 0x2c, 0xc8, + 0x33, 0x3d, 0xca, 0x1b, 0xdf, 0xbb, 0x57, 0x23, 0x47, 0x67, 0xe6, 0xeb, 0x9b, 0xd2, 0xc5, 0x57, + 0x30, 0xff, 0x46, 0x06, 0x14, 0xc7, 0xe6, 0x29, 0x19, 0xd3, 0x86, 0xca, 0x73, 0x22, 0x4f, 0xae, + 0xb3, 0xf6, 0x01, 0x47, 0x12, 0x09, 0x11, 0x09, 0x8b, 0x06, 0x50, 0x65, 0x4e, 0x8c, 0x0a, 0xd1, + 0x49, 0xbf, 0xba, 0x93, 0x71, 0x95, 0xbd, 0xe9, 0x4c, 0x9c, 0x84, 0x69, 0x3e, 0x80, 0x6a, 0x62, + 0xb1, 0x05, 0xf9, 0x8c, 0xad, 0x64, 0x3e, 0xa3, 0x92, 0x4c, 0x4e, 0x3c, 0x9a, 0xd7, 0x01, 0x93, + 0x11, 0x33, 0x82, 0xbd, 0xa3, 0xfe, 0x40, 0xdc, 0x1c, 0x9f, 0xe0, 0xa3, 0x93, 0x63, 0x4d, 0x61, + 0x8d, 0x83, 0x76, 0xff, 0xa9, 0x96, 0x8b, 0x6d, 0x44, 0xd5, 0x3b, 0x50, 0x4d, 0xf0, 0x95, 0xf2, + 0xda, 0x4a, 0xda, 0x6b, 0x33, 0xbf, 0x69, 0x5a, 0x56, 0x40, 0x28, 0x95, 0x7c, 0x44, 0xa4, 0xfe, + 0x02, 0x2a, 0xbb, 0xbd, 0xbe, 0x84, 0x68, 0x40, 0x89, 0x92, 0x80, 0xfd, 0x6f, 0x9e, 0x99, 0xaa, + 0xe0, 0x88, 0x64, 0xe0, 0x94, 0x98, 0xc1, 0xf0, 0x8c, 0x50, 0x79, 0xd6, 0xc7, 0x34, 0x9b, 0xe5, + 0xf1, 0x0c, 0x8f, 0xd0, 0x5d, 0x05, 0x47, 0xa4, 0xfe, 0x3f, 0x25, 0x80, 0x69, 0xb6, 0x01, 0xd5, + 0x21, 0x17, 0xfb, 0xe0, 0x9c, 0x6d, 0x31, 0x3b, 0x48, 0x9c, 0x31, 0xfc, 0x1b, 0xed, 0xc0, 0x4d, + 0x87, 0x8e, 0x7c, 0x73, 0x78, 0x6e, 0xc8, 0x24, 0x81, 0xd8, 0xaa, 0xdc, 0x9f, 0xd5, 0xf0, 0x0d, + 0xd9, 0x29, 0x77, 0xa2, 0xc0, 0x3d, 0x00, 0x95, 0xb8, 0x17, 0xdc, 0xf7, 0x54, 0x77, 0x3e, 0x5b, + 0x3a, 0x0b, 0xd2, 0xea, 0xba, 0x17, 0xc2, 0x56, 0x18, 0x0c, 0x32, 0x00, 0x2c, 0x72, 0x61, 0x0f, + 0x89, 0xc1, 0x40, 0x0b, 0x1c, 0xf4, 0x8b, 0xe5, 0x41, 0x77, 0x39, 0x46, 0x0c, 0x5d, 0xb1, 0x22, + 0x1a, 0xf5, 0xa0, 0x12, 0x10, 0xea, 0x4d, 0x82, 0x21, 0x11, 0x0e, 0x28, 0xfb, 0x45, 0x05, 0x47, + 0xf3, 0xf0, 0x14, 0x02, 0xed, 0x42, 0x91, 0xfb, 0x1d, 0xe6, 0x61, 0xd4, 0xef, 0x4c, 0xa9, 0xa6, + 0xc1, 0xb8, 0x27, 0xc1, 0x72, 0x2e, 0x7a, 0x02, 0x25, 0xc1, 0x22, 0x6d, 0x94, 0x39, 0xcc, 0x47, + 0x59, 0x9d, 0x22, 0x9f, 0x85, 0xa3, 0xd9, 0x4c, 0xab, 0x13, 0x4a, 0x82, 0x46, 0x45, 0x68, 0x95, + 0x7d, 0xa3, 0xf7, 0xa0, 0x22, 0xce, 0x60, 0xcb, 0x0e, 0x1a, 0x20, 0x8c, 0x93, 0x37, 0xec, 0xda, + 0x01, 0x7a, 0x1f, 0xaa, 0x22, 0xd6, 0x32, 0xb8, 0x57, 0xa8, 0xf2, 0x6e, 0x10, 0x4d, 0xc7, 0xcc, + 0x37, 0x88, 0x01, 0x24, 0x08, 0xc4, 0x80, 0x5a, 0x3c, 0x80, 0x04, 0x01, 0x1f, 0xf0, 0x07, 0xb0, + 0xc1, 0x23, 0xd4, 0x51, 0xe0, 0x4d, 0x7c, 0x83, 0xdb, 0xd4, 0x3a, 0x1f, 0xb4, 0xce, 0x9a, 0x9f, + 0xb0, 0xd6, 0x1e, 0x33, 0xae, 0x3b, 0x50, 0x7e, 0xe5, 0x9d, 0x8a, 0x01, 0x75, 0xb1, 0x0f, 0x5e, + 0x79, 0xa7, 0x51, 0x57, 0x1c, 0x25, 0x6c, 0xa4, 0xa3, 0x84, 0x6f, 0xe0, 0xd6, 0xfc, 0x71, 0xc7, + 0xa3, 0x05, 0xed, 0xfa, 0xd1, 0xc2, 0x96, 0xbb, 0xc8, 0x0f, 0x7f, 0x09, 0xaa, 0xe5, 0xd2, 0xc6, + 0xe6, 0x52, 0xc6, 0x11, 0xef, 0x63, 0xcc, 0x26, 0x37, 0x3f, 0x81, 0x72, 0x64, 0x7d, 0xcb, 0xf8, + 0xa5, 0xe6, 0x43, 0xa8, 0xa7, 0x6d, 0x77, 0x29, 0xaf, 0xf6, 0xcf, 0x39, 0xa8, 0xc4, 0x56, 0x8a, + 0x5c, 0xb8, 0xc1, 0xa5, 0xc8, 0x42, 0x34, 0x63, 0x6a, 0xf4, 0x22, 0x30, 0xfc, 0x3c, 0xe3, 0xff, + 0x6a, 0x47, 0x08, 0xf2, 0x86, 0x2a, 0x77, 0x00, 0x8a, 0x91, 0xa7, 0xeb, 0x7d, 0x0d, 0x1b, 0x63, + 0xdb, 0x9d, 0x5c, 0x26, 0xd6, 0x12, 0x11, 0xdd, 0x1f, 0x66, 0x5c, 0xeb, 0x80, 0xcd, 0x9e, 0xae, + 0x51, 0x1f, 0xa7, 0x68, 0xb4, 0x07, 0x05, 0xdf, 0x0b, 0xc2, 0xe8, 0x90, 0xca, 0x7a, 0x7c, 0x1c, + 0x7b, 0x41, 0x78, 0x68, 0xfa, 0x3e, 0xbb, 0xb4, 0x08, 0x00, 0xfd, 0xdb, 0x1c, 0xdc, 0x5a, 0xfc, + 0xc7, 0x50, 0x0f, 0xd4, 0xa1, 0x3f, 0x91, 0x42, 0x7a, 0xb8, 0xac, 0x90, 0x3a, 0xfe, 0x64, 0xca, + 0x3f, 0x03, 0x42, 0xcf, 0xa0, 0xe8, 0x10, 0xc7, 0x0b, 0xae, 0xa4, 0x2c, 0x1e, 0x2d, 0x0b, 0x79, + 0xc8, 0x67, 0x4f, 0x51, 0x25, 0x1c, 0xc2, 0x50, 0x96, 0xd6, 0x4b, 0xa5, 0x9f, 0x5c, 0x32, 0xad, + 0x14, 0x41, 0xe2, 0x18, 0x47, 0xff, 0x04, 0x6e, 0x2e, 0xfc, 0x2b, 0xe8, 0x77, 0x01, 0x86, 0xfe, + 0xc4, 0xe0, 0x69, 0x7f, 0x61, 0x41, 0x2a, 0xae, 0x0c, 0xfd, 0x49, 0x9f, 0x37, 0xe8, 0x2f, 0xa0, + 0xf1, 0x26, 0x7e, 0x99, 0xf7, 0x11, 0x1c, 0x1b, 0xce, 0x29, 0x97, 0x81, 0x8a, 0xcb, 0xa2, 0xe1, + 0xf0, 0x14, 0xe9, 0xb0, 0x1e, 0x75, 0x9a, 0x97, 0x6c, 0x80, 0xca, 0x07, 0x54, 0xe5, 0x00, 0xf3, + 0xf2, 0xf0, 0x54, 0xff, 0x55, 0x0e, 0x36, 0x66, 0x58, 0x66, 0x57, 0x37, 0xe1, 0xf1, 0xa2, 0x4b, + 0xb1, 0xa0, 0x98, 0xfb, 0x1b, 0xda, 0x56, 0x94, 0x4e, 0xe5, 0xdf, 0xfc, 0xe0, 0xf3, 0x65, 0xaa, + 0x33, 0x67, 0xfb, 0x6c, 0xfb, 0x38, 0xa7, 0x76, 0x48, 0x79, 0x14, 0x52, 0xc0, 0x82, 0x40, 0xcf, + 0xa1, 0x1e, 0x10, 0x7e, 0xe0, 0x5a, 0x86, 0xb0, 0xb2, 0xc2, 0x52, 0x56, 0x26, 0x39, 0x64, 0xc6, + 0x86, 0xd7, 0x23, 0x24, 0x46, 0x51, 0xf4, 0x0c, 0xd6, 0xad, 0x2b, 0xd7, 0x74, 0xec, 0xa1, 0x44, + 0x2e, 0xae, 0x8c, 0x5c, 0x93, 0x40, 0x1c, 0x58, 0x7f, 0x00, 0xd5, 0x44, 0x27, 0xfb, 0x63, 0x3c, + 0xdc, 0x92, 0x32, 0x11, 0x44, 0xda, 0x5b, 0x14, 0xa4, 0xb7, 0xd0, 0x4f, 0xa1, 0x9a, 0xd8, 0x17, + 0xcb, 0x4c, 0x65, 0xf2, 0x0c, 0x3d, 0x2e, 0xcf, 0x02, 0xce, 0x85, 0x1e, 0xba, 0x0d, 0x25, 0x16, + 0xea, 0x18, 0xb6, 0xcf, 0x25, 0x5a, 0xc1, 0x45, 0x46, 0xee, 0xfb, 0xfa, 0x6f, 0x72, 0x50, 0x4f, + 0x6f, 0xe9, 0xc8, 0x8e, 0x7c, 0x12, 0xd8, 0x9e, 0x95, 0xb0, 0xa3, 0x63, 0xde, 0xc0, 0x6c, 0x85, + 0x75, 0x7f, 0x33, 0xf1, 0x42, 0x33, 0xb2, 0x95, 0xa1, 0x3f, 0xf9, 0x23, 0x46, 0xcf, 0xd8, 0xa0, + 0x3a, 0x63, 0x83, 0xe8, 0x43, 0x40, 0xd2, 0x94, 0xc6, 0xb6, 0x63, 0x87, 0xc6, 0xe9, 0x55, 0x48, + 0x84, 0x8e, 0x55, 0xac, 0x89, 0x9e, 0x03, 0xd6, 0xf1, 0x25, 0x6b, 0x67, 0x86, 0xe7, 0x79, 0x8e, + 0x41, 0x87, 0x5e, 0x40, 0x0c, 0xd3, 0x7a, 0xc5, 0x6f, 0x2d, 0x2a, 0xae, 0x7a, 0x9e, 0xd3, 0x67, + 0x6d, 0x6d, 0xeb, 0x15, 0x3b, 0xf9, 0x86, 0xfe, 0x84, 0x92, 0xd0, 0x60, 0x3f, 0x3c, 0x58, 0xa8, + 0x60, 0x10, 0x4d, 0x1d, 0x7f, 0x42, 0xd1, 0xef, 0xc3, 0x7a, 0x34, 0x80, 0x1f, 0x7e, 0xf2, 0xd4, + 0xad, 0xc9, 0x21, 0xbc, 0x0d, 0xe9, 0x50, 0x3b, 0x26, 0xc1, 0x90, 0xb8, 0xe1, 0xc0, 0x1e, 0x9e, + 0xb3, 0xf3, 0x5d, 0xd9, 0x56, 0x70, 0xaa, 0xed, 0xab, 0x7c, 0xb9, 0xa4, 0x95, 0x71, 0xb4, 0x9a, + 0x43, 0x1c, 0xaa, 0xff, 0x0c, 0x0a, 0x3c, 0x44, 0x60, 0x32, 0xe1, 0xc7, 0x2b, 0x3f, 0x7d, 0x65, + 0x68, 0xc9, 0x1a, 0xf8, 0xd9, 0xfb, 0x1e, 0x54, 0xb8, 0xec, 0x13, 0x11, 0x3d, 0x8f, 0x3b, 0x79, + 0x67, 0x13, 0xca, 0x01, 0x31, 0x2d, 0xcf, 0x1d, 0x47, 0xc9, 0xa0, 0x98, 0xd6, 0xbf, 0x81, 0xa2, + 0x38, 0x67, 0xae, 0x81, 0xff, 0x11, 0x20, 0xf1, 0xbf, 0x99, 0x3e, 0x1d, 0x9b, 0x52, 0x19, 0x85, + 0xf2, 0x17, 0x48, 0xd1, 0x73, 0x3c, 0xed, 0xd0, 0xff, 0x53, 0x11, 0xf1, 0xa8, 0x78, 0x1b, 0x62, + 0x81, 0x2b, 0x33, 0x72, 0x76, 0x5b, 0x16, 0x49, 0xa8, 0x88, 0x44, 0xfb, 0x50, 0x94, 0x61, 0x67, + 0x6e, 0xd5, 0xa7, 0x35, 0x09, 0x10, 0xa5, 0xa4, 0x89, 0xbc, 0x90, 0x2f, 0x9b, 0x92, 0x26, 0x22, + 0x25, 0x4d, 0xd8, 0x6d, 0x52, 0x06, 0xc4, 0x02, 0x2e, 0xcf, 0xe3, 0xe1, 0xaa, 0x15, 0xe7, 0xfd, + 0x89, 0xfe, 0xdf, 0x4a, 0xec, 0xa6, 0xa2, 0xfc, 0x3c, 0xfa, 0x1a, 0xca, 0x6c, 0xc7, 0x1b, 0x8e, + 0xe9, 0xcb, 0xd7, 0xe6, 0xce, 0x6a, 0xa9, 0xff, 0xe8, 0x10, 0x13, 0xe1, 0x6c, 0xc9, 0x17, 0x14, + 0x73, 0x77, 0xec, 0x2a, 0x11, 0xb9, 0x3b, 0xf6, 0x8d, 0x3e, 0x80, 0xba, 0x39, 0x09, 0x3d, 0xc3, + 0xb4, 0x2e, 0x48, 0x10, 0xda, 0x94, 0x48, 0xdd, 0xaf, 0xb3, 0xd6, 0x76, 0xd4, 0xd8, 0xfc, 0x0c, + 0x6a, 0x49, 0xcc, 0xb7, 0x85, 0x19, 0x85, 0x64, 0x98, 0xf1, 0xa7, 0x00, 0xd3, 0x5c, 0x17, 0xb3, + 0x11, 0x72, 0x69, 0xb3, 0x5b, 0xbd, 0xbc, 0xbb, 0x16, 0x70, 0x99, 0x35, 0x74, 0xd8, 0x7d, 0x2a, + 0x9d, 0x88, 0x2f, 0x44, 0x89, 0x78, 0xb6, 0x99, 0xd9, 0xfe, 0x3b, 0xb7, 0xc7, 0xe3, 0x38, 0xff, + 0x56, 0xf1, 0x3c, 0xe7, 0x29, 0x6f, 0xd0, 0x7f, 0x9b, 0x13, 0xb6, 0x22, 0x9e, 0x54, 0x32, 0xdd, + 0x5d, 0xde, 0x95, 0xaa, 0x1f, 0x00, 0xd0, 0xd0, 0x0c, 0x58, 0xcc, 0x64, 0x46, 0x19, 0xc0, 0xe6, + 0x5c, 0x26, 0x7f, 0x10, 0xd5, 0x78, 0xe0, 0x8a, 0x1c, 0xdd, 0x0e, 0xd1, 0xe7, 0x50, 0x1b, 0x7a, + 0x8e, 0x3f, 0x26, 0x72, 0x72, 0xe1, 0xad, 0x93, 0xab, 0xf1, 0xf8, 0x76, 0x98, 0xc8, 0x3b, 0x16, + 0xaf, 0x9b, 0x77, 0xfc, 0x8d, 0x22, 0x5e, 0x86, 0x92, 0x0f, 0x53, 0x68, 0xb4, 0xa0, 0xfa, 0xe1, + 0xc9, 0x8a, 0xaf, 0x5c, 0xdf, 0x55, 0xfa, 0xd0, 0xfc, 0x3c, 0x4b, 0xad, 0xc1, 0x9b, 0xa3, 0xd8, + 0x7f, 0x53, 0xa1, 0x12, 0x3f, 0x0a, 0xcd, 0xe9, 0xfe, 0x53, 0xa8, 0xc4, 0x05, 0x36, 0xd2, 0x41, + 0x7c, 0xa7, 0x7a, 0xe2, 0xc1, 0xe8, 0x25, 0x20, 0x73, 0x34, 0x8a, 0xa3, 0x53, 0x63, 0x42, 0xcd, + 0x51, 0xf4, 0x24, 0xf7, 0xe9, 0x12, 0x72, 0x88, 0x8e, 0xb3, 0x13, 0x36, 0x1f, 0x6b, 0xe6, 0x68, + 0x94, 0x6a, 0x41, 0x7f, 0x06, 0x37, 0xd3, 0x6b, 0x18, 0xa7, 0x57, 0x86, 0x6f, 0x5b, 0xf2, 0x8e, + 0xbc, 0xb7, 0xec, 0xbb, 0x58, 0x2b, 0x05, 0xff, 0xe5, 0xd5, 0xb1, 0x6d, 0x09, 0x99, 0xa3, 0x60, + 0xae, 0xa3, 0xf9, 0x17, 0x70, 0xfb, 0x0d, 0xc3, 0x17, 0xe8, 0xa0, 0x97, 0xae, 0xf7, 0x58, 0x5d, + 0x08, 0x09, 0xed, 0xfd, 0x5a, 0x11, 0xcf, 0x77, 0x69, 0x99, 0xb4, 0x93, 0x61, 0xf5, 0xbd, 0x8c, + 0xeb, 0x74, 0x8e, 0x4f, 0x04, 0x3c, 0x8f, 0xa4, 0xbf, 0x9a, 0x89, 0xa4, 0xb3, 0xc6, 0x4f, 0x22, + 0x20, 0x15, 0x40, 0x12, 0x41, 0xff, 0x17, 0x15, 0xca, 0x11, 0x3a, 0xbf, 0xe1, 0x5e, 0xd1, 0x90, + 0x38, 0x46, 0x9c, 0x7e, 0x53, 0x30, 0x88, 0x26, 0x9e, 0x14, 0x7a, 0x0f, 0x2a, 0xec, 0x22, 0x2d, + 0xba, 0x73, 0xbc, 0xbb, 0xcc, 0x1a, 0x78, 0xe7, 0xfb, 0x50, 0x0d, 0xbd, 0xd0, 0x1c, 0x1b, 0x21, + 0x3f, 0xde, 0x55, 0x31, 0x9b, 0x37, 0xf1, 0xc3, 0x1d, 0xfd, 0x00, 0x36, 0xc3, 0xb3, 0xc0, 0x0b, + 0xc3, 0x31, 0x0b, 0x2d, 0x79, 0xa0, 0x23, 0xe2, 0x92, 0x3c, 0xd6, 0xe2, 0x0e, 0x11, 0x00, 0x51, + 0xe6, 0xbd, 0xa7, 0x83, 0x99, 0xe9, 0x72, 0x27, 0x92, 0xc7, 0xeb, 0x71, 0x2b, 0x33, 0x6d, 0x76, + 0x78, 0xfa, 0x22, 0x80, 0xe0, 0xbe, 0x42, 0xc1, 0x11, 0x89, 0x0c, 0xd8, 0x70, 0x88, 0x49, 0x27, + 0x01, 0xb1, 0x8c, 0x97, 0x36, 0x19, 0x5b, 0x22, 0x31, 0x51, 0xcf, 0x7c, 0x3b, 0x88, 0xc4, 0xd2, + 0x7a, 0xcc, 0x67, 0xe3, 0x7a, 0x04, 0x27, 0x68, 0x16, 0x39, 0x88, 0x2f, 0xb4, 0x01, 0xd5, 0xfe, + 0xf3, 0xfe, 0xa0, 0x7b, 0x68, 0x1c, 0x1e, 0xed, 0x76, 0x65, 0x49, 0x4f, 0xbf, 0x8b, 0x05, 0xa9, + 0xb0, 0xfe, 0xc1, 0xd1, 0xa0, 0x7d, 0x60, 0x0c, 0xf6, 0x3b, 0x4f, 0xfb, 0x5a, 0x0e, 0xdd, 0x84, + 0xcd, 0xc1, 0x1e, 0x3e, 0x1a, 0x0c, 0x0e, 0xba, 0xbb, 0xc6, 0x71, 0x17, 0xef, 0x1f, 0xed, 0xf6, + 0x35, 0x15, 0x21, 0xa8, 0x4f, 0x9b, 0x07, 0xfb, 0x87, 0x5d, 0x2d, 0x8f, 0xaa, 0x50, 0x3a, 0xee, + 0xe2, 0x4e, 0xb7, 0x37, 0xd0, 0x0a, 0xfa, 0xaf, 0x54, 0xa8, 0x26, 0xb4, 0xc8, 0x0c, 0x39, 0xa0, + 0xe2, 0x1a, 0x92, 0xc7, 0xec, 0x93, 0x3f, 0x41, 0x9a, 0xc3, 0x33, 0xa1, 0x9d, 0x3c, 0x16, 0x04, + 0xbf, 0x7a, 0x98, 0x97, 0x89, 0x7d, 0x9e, 0xc7, 0x65, 0xc7, 0xbc, 0x14, 0x20, 0xdf, 0x83, 0xda, + 0x39, 0x09, 0x5c, 0x32, 0x96, 0xfd, 0x42, 0x23, 0x55, 0xd1, 0x26, 0x86, 0x6c, 0x83, 0x26, 0x87, + 0x4c, 0x61, 0x84, 0x3a, 0xea, 0xa2, 0xfd, 0x30, 0x02, 0xdb, 0x82, 0x82, 0xe8, 0x2e, 0x89, 0xf5, + 0x39, 0xc1, 0x8e, 0x29, 0xfa, 0xda, 0xf4, 0x79, 0xc8, 0x97, 0xc7, 0xfc, 0x1b, 0x9d, 0xce, 0xeb, + 0xa7, 0xc8, 0xf5, 0xf3, 0x60, 0x79, 0x73, 0x7e, 0x93, 0x8a, 0xce, 0x62, 0x15, 0x95, 0x40, 0xc5, + 0x51, 0x1d, 0x4c, 0xa7, 0xdd, 0xd9, 0x63, 0x6a, 0x59, 0x87, 0xca, 0x61, 0xfb, 0xa7, 0xc6, 0x49, + 0x9f, 0x67, 0xb5, 0x91, 0x06, 0xb5, 0xa7, 0x5d, 0xdc, 0xeb, 0x1e, 0xc8, 0x16, 0x15, 0x6d, 0x81, + 0x26, 0x5b, 0xa6, 0xe3, 0xf2, 0x0c, 0x41, 0x7c, 0x16, 0x50, 0x19, 0xf2, 0xfd, 0x67, 0xed, 0x63, + 0xad, 0xa8, 0xff, 0x57, 0x0e, 0x36, 0xc4, 0xb1, 0x10, 0xbf, 0xd8, 0xbf, 0xf9, 0xc5, 0x32, 0x99, + 0xe5, 0xc9, 0xa5, 0xb3, 0x3c, 0x51, 0x10, 0xca, 0x4f, 0x75, 0x75, 0x1a, 0x84, 0xf2, 0xec, 0x50, + 0xca, 0xe3, 0xe7, 0x97, 0xf1, 0xf8, 0x0d, 0x28, 0x39, 0x84, 0xc6, 0x7a, 0xab, 0xe0, 0x88, 0x44, + 0x36, 0x54, 0x4d, 0xd7, 0xf5, 0x42, 0x53, 0xa4, 0x4e, 0x8b, 0x4b, 0x1d, 0x86, 0x33, 0xff, 0xb8, + 0xd5, 0x9e, 0x22, 0x09, 0xc7, 0x9c, 0xc4, 0x6e, 0xfe, 0x04, 0xb4, 0xd9, 0x01, 0xcb, 0x1c, 0x87, + 0xdf, 0xff, 0xe1, 0xf4, 0x34, 0x24, 0x6c, 0x5f, 0xc8, 0x37, 0x07, 0x6d, 0x8d, 0x11, 0xf8, 0xa4, + 0xd7, 0xdb, 0xef, 0x3d, 0xd1, 0x14, 0x04, 0x50, 0xec, 0xfe, 0x74, 0x7f, 0xd0, 0xdd, 0xd5, 0x72, + 0x3b, 0xbf, 0xde, 0x84, 0xa2, 0x60, 0x12, 0x7d, 0x2b, 0x23, 0x81, 0x64, 0x35, 0x28, 0xfa, 0xc9, + 0xd2, 0x11, 0x75, 0xaa, 0xc2, 0xb4, 0xf9, 0x68, 0xe5, 0xf9, 0xf2, 0xf5, 0x6d, 0x0d, 0xfd, 0x8d, + 0x02, 0xb5, 0xd4, 0xcb, 0x5b, 0xd6, 0xd4, 0xf1, 0x82, 0xe2, 0xd3, 0xe6, 0x8f, 0x57, 0x9a, 0x1b, + 0xf3, 0xf2, 0x4b, 0x05, 0xaa, 0x89, 0xb2, 0x4b, 0xf4, 0x60, 0x95, 0x52, 0x4d, 0xc1, 0xc9, 0x67, + 0xab, 0x57, 0x79, 0xea, 0x6b, 0x1f, 0x2b, 0xe8, 0xaf, 0x15, 0xa8, 0x26, 0x0a, 0x10, 0x33, 0xb3, + 0x32, 0x5f, 0x2e, 0x99, 0x99, 0x95, 0x45, 0xf5, 0x8e, 0x6b, 0xe8, 0x2f, 0x15, 0xa8, 0xc4, 0xc5, + 0x84, 0xe8, 0xfe, 0xf2, 0xe5, 0x87, 0x82, 0x89, 0x4f, 0x57, 0xad, 0x5b, 0xd4, 0xd7, 0xd0, 0x9f, + 0x43, 0x39, 0xaa, 0xbc, 0x43, 0x59, 0x4f, 0xaf, 0x99, 0xb2, 0xbe, 0xe6, 0xfd, 0xa5, 0xe7, 0x25, + 0x97, 0x8f, 0xca, 0xe1, 0x32, 0x2f, 0x3f, 0x53, 0xb8, 0xd7, 0xbc, 0xbf, 0xf4, 0xbc, 0x78, 0x79, + 0x66, 0x09, 0x89, 0xaa, 0xb9, 0xcc, 0x96, 0x30, 0x5f, 0xae, 0x97, 0xd9, 0x12, 0x16, 0x15, 0xe9, + 0x09, 0x46, 0x12, 0x75, 0x77, 0x99, 0x19, 0x99, 0xaf, 0xed, 0xcb, 0xcc, 0xc8, 0x82, 0x32, 0x3f, + 0x7d, 0x0d, 0xfd, 0x42, 0x49, 0xde, 0x0b, 0xee, 0x2f, 0x5d, 0x5e, 0xb6, 0xa4, 0x49, 0xce, 0x15, + 0xb8, 0xf1, 0x0d, 0xfa, 0x0b, 0x99, 0xc5, 0x10, 0xd5, 0x69, 0x68, 0x19, 0xb0, 0x54, 0x41, 0x5b, + 0xf3, 0x93, 0xd5, 0x0e, 0x1b, 0xce, 0xc4, 0x5f, 0x29, 0x00, 0xd3, 0x3a, 0xb6, 0xcc, 0x4c, 0xcc, + 0x15, 0xd0, 0x35, 0x1f, 0xac, 0x30, 0x33, 0xb9, 0x41, 0xa2, 0x3a, 0x9b, 0xcc, 0x1b, 0x64, 0xa6, + 0xce, 0x2e, 0xf3, 0x06, 0x99, 0xad, 0x91, 0xd3, 0xd7, 0xd0, 0x3f, 0x29, 0xb0, 0x39, 0x57, 0xe7, + 0x83, 0x1e, 0x5d, 0xb3, 0xd4, 0xab, 0xf9, 0xc5, 0xea, 0x00, 0x11, 0x6b, 0xdb, 0xca, 0xc7, 0x0a, + 0xfa, 0x5b, 0x05, 0xd6, 0xd3, 0xf5, 0x0f, 0x99, 0x4f, 0xa9, 0x05, 0x15, 0x43, 0xcd, 0x87, 0xab, + 0x4d, 0x8e, 0xa5, 0xf5, 0xf7, 0x0a, 0xd4, 0xd3, 0xa5, 0x30, 0xe8, 0xe1, 0x72, 0x6e, 0x61, 0x86, + 0xa1, 0xcf, 0x57, 0x9c, 0x1d, 0x71, 0xf4, 0x65, 0xe9, 0x8f, 0x0b, 0x22, 0x7a, 0x2b, 0xf2, 0x9f, + 0x1f, 0xfd, 0x7f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x76, 0x8b, 0xb6, 0x52, 0xb4, 0x33, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. diff --git a/plugins/drivers/proto/driver.proto b/plugins/drivers/proto/driver.proto index dc4b2f061460..f315d3215dbd 100644 --- a/plugins/drivers/proto/driver.proto +++ b/plugins/drivers/proto/driver.proto @@ -329,6 +329,9 @@ message CreateNetworkRequest { // AllocID of the allocation the network is associated with string alloc_id = 1; + + // Hostname of the network namespace + string hostname = 2; } message CreateNetworkResponse { diff --git a/plugins/drivers/server.go b/plugins/drivers/server.go index f79690e444a0..15896801e645 100644 --- a/plugins/drivers/server.go +++ b/plugins/drivers/server.go @@ -388,7 +388,7 @@ func (b *driverPluginServer) CreateNetwork(ctx context.Context, req *proto.Creat return nil, fmt.Errorf("CreateNetwork RPC not supported by driver") } - spec, created, err := nm.CreateNetwork(req.AllocId) + spec, created, err := nm.CreateNetwork(req.GetAllocId(), networkCreateRequestFromProto(req)) if err != nil { return nil, err } @@ -397,7 +397,6 @@ func (b *driverPluginServer) CreateNetwork(ctx context.Context, req *proto.Creat IsolationSpec: NetworkIsolationSpecToProto(spec), Created: created, }, nil - } func (b *driverPluginServer) DestroyNetwork(ctx context.Context, req *proto.DestroyNetworkRequest) (*proto.DestroyNetworkResponse, error) { diff --git a/plugins/drivers/testutils/testing.go b/plugins/drivers/testutils/testing.go index 8e2ea66799a6..8088381db6c9 100644 --- a/plugins/drivers/testutils/testing.go +++ b/plugins/drivers/testutils/testing.go @@ -199,12 +199,12 @@ type MockDriver struct { } type MockNetworkManager struct { - CreateNetworkF func(string) (*drivers.NetworkIsolationSpec, bool, error) + CreateNetworkF func(string, *drivers.NetworkCreateRequest) (*drivers.NetworkIsolationSpec, bool, error) DestroyNetworkF func(string, *drivers.NetworkIsolationSpec) error } -func (m *MockNetworkManager) CreateNetwork(id string) (*drivers.NetworkIsolationSpec, bool, error) { - return m.CreateNetworkF(id) +func (m *MockNetworkManager) CreateNetwork(allocID string, req *drivers.NetworkCreateRequest) (*drivers.NetworkIsolationSpec, bool, error) { + return m.CreateNetworkF(allocID, req) } func (m *MockNetworkManager) DestroyNetwork(id string, spec *drivers.NetworkIsolationSpec) error { return m.DestroyNetworkF(id, spec) diff --git a/plugins/drivers/utils.go b/plugins/drivers/utils.go index 9b1f4988f60f..7639ef4c32cd 100644 --- a/plugins/drivers/utils.go +++ b/plugins/drivers/utils.go @@ -635,6 +635,15 @@ func netIsolationModeFromProto(pb proto.NetworkIsolationSpec_NetworkIsolationMod } } +func networkCreateRequestFromProto(pb *proto.CreateNetworkRequest) *NetworkCreateRequest { + if pb == nil { + return nil + } + return &NetworkCreateRequest{ + Hostname: pb.GetHostname(), + } +} + func NetworkIsolationSpecToProto(spec *NetworkIsolationSpec) *proto.NetworkIsolationSpec { if spec == nil { return nil diff --git a/plugins/drivers/utils_test.go b/plugins/drivers/utils_test.go index 5f46dc8a1978..f12cac479fce 100644 --- a/plugins/drivers/utils_test.go +++ b/plugins/drivers/utils_test.go @@ -5,6 +5,8 @@ import ( "github.com/hashicorp/nomad/helper/uuid" "github.com/hashicorp/nomad/nomad/structs" + "github.com/hashicorp/nomad/plugins/drivers/proto" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -103,3 +105,34 @@ func TestTaskConfigRoundTrip(t *testing.T) { require.EqualValues(t, input, parsed) } + +func Test_networkCreateRequestFromProto(t *testing.T) { + testCases := []struct { + inputPB *proto.CreateNetworkRequest + expectedOutput *NetworkCreateRequest + name string + }{ + { + inputPB: nil, + expectedOutput: nil, + name: "nil safety", + }, + { + inputPB: &proto.CreateNetworkRequest{ + AllocId: "59598b74-86e9-16ee-eb54-24c62935cc7c", + Hostname: "foobar", + }, + expectedOutput: &NetworkCreateRequest{ + Hostname: "foobar", + }, + name: "generic 1", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + actualOutput := networkCreateRequestFromProto(tc.inputPB) + assert.Equal(t, tc.expectedOutput, actualOutput, tc.name) + }) + } +} diff --git a/scheduler/util.go b/scheduler/util.go index de009e6fcf52..099340946a3f 100644 --- a/scheduler/util.go +++ b/scheduler/util.go @@ -571,6 +571,10 @@ func networkUpdated(netA, netB []*structs.NetworkResource) bool { return true } + if an.Hostname != bn.Hostname { + return true + } + if !reflect.DeepEqual(an.DNS, bn.DNS) { return true } diff --git a/scheduler/util_test.go b/scheduler/util_test.go index d50eebae30af..9bb3ff81fea8 100644 --- a/scheduler/util_test.go +++ b/scheduler/util_test.go @@ -887,6 +887,16 @@ func TestNetworkUpdated(t *testing.T) { }, updated: true, }, + { + name: "hostname updated", + a: []*structs.NetworkResource{ + {Hostname: "foo"}, + }, + b: []*structs.NetworkResource{ + {Hostname: "bar"}, + }, + updated: true, + }, } for i := range cases {