core: propagate remote task handles

Add a new driver capability: RemoteTasks.

When a task is run by a driver with RemoteTasks set, its TaskHandle will
be propagated to the server in its allocation's TaskState. If the task
is replaced due to a down node or draining, its TaskHandle will be
propagated to its replacement allocation.

This allows tasks to be scheduled in remote systems whose lifecycles are
disconnected from the Nomad node's lifecycle.

See https://github.com/hashicorp/nomad-driver-ecs for an example ECS
remote task driver.
This commit is contained in:
Michael Schurter
2020-12-17 15:21:46 -08:00
parent fb5e898131
commit d50fb2a00e
28 changed files with 1431 additions and 347 deletions

View File

@@ -915,6 +915,15 @@ type TaskState struct {
StartedAt time.Time
FinishedAt time.Time
Events []*TaskEvent
// Experimental - TaskHandle is based on drivers.TaskHandle and used
// by remote task drivers to migrate task handles between allocations.
TaskHandle *TaskHandle
}
type TaskHandle struct {
Version int
DriverState []byte
}
const (

View File

@@ -13,20 +13,22 @@ import (
// NewDriverHandle returns a handle for task operations on a specific task
func NewDriverHandle(driver drivers.DriverPlugin, taskID string, task *structs.Task, net *drivers.DriverNetwork) *DriverHandle {
return &DriverHandle{
driver: driver,
net: net,
taskID: taskID,
task: task,
driver: driver,
net: net,
taskID: taskID,
killSignal: task.KillSignal,
killTimeout: task.KillTimeout,
}
}
// DriverHandle encapsulates a driver plugin client and task identifier and exposes
// an api to perform driver operations on the task
type DriverHandle struct {
driver drivers.DriverPlugin
net *drivers.DriverNetwork
task *structs.Task
taskID string
driver drivers.DriverPlugin
net *drivers.DriverNetwork
taskID string
killSignal string
killTimeout time.Duration
}
func (h *DriverHandle) ID() string {
@@ -37,12 +39,13 @@ func (h *DriverHandle) WaitCh(ctx context.Context) (<-chan *drivers.ExitResult,
return h.driver.WaitTask(ctx, h.taskID)
}
func (h *DriverHandle) Update(task *structs.Task) error {
return nil
// SetKillSignal allows overriding the signal sent to kill the task.
func (h *DriverHandle) SetKillSignal(signal string) {
h.killSignal = signal
}
func (h *DriverHandle) Kill() error {
return h.driver.StopTask(h.taskID, h.task.KillTimeout, h.task.KillSignal)
return h.driver.StopTask(h.taskID, h.killTimeout, h.killSignal)
}
func (h *DriverHandle) Stats(ctx context.Context, interval time.Duration) (<-chan *cstructs.TaskResourceUsage, error) {

View File

@@ -0,0 +1,124 @@
package taskrunner
import (
"context"
hclog "github.com/hashicorp/go-hclog"
"github.com/hashicorp/nomad/client/allocrunner/interfaces"
"github.com/hashicorp/nomad/nomad/structs"
"github.com/hashicorp/nomad/plugins/drivers"
)
var _ interfaces.TaskPrestartHook = (*remoteTaskHook)(nil)
var _ interfaces.TaskPreKillHook = (*remoteTaskHook)(nil)
// remoteTaskHook reattaches to remotely executing tasks.
type remoteTaskHook struct {
tr *TaskRunner
logger hclog.Logger
}
func newRemoteTaskHook(tr *TaskRunner, logger hclog.Logger) interfaces.TaskHook {
h := &remoteTaskHook{
tr: tr,
}
h.logger = logger.Named(h.Name())
return h
}
func (h *remoteTaskHook) Name() string {
return "remote_task"
}
// Prestart performs 2 remote task driver related tasks:
// 1. If there is no local handle, see if there is a handle propagated from a
// previous alloc to be restored.
// 2. If the alloc is lost make sure the task signal is set to detach instead
// of kill.
func (h *remoteTaskHook) Prestart(ctx context.Context, req *interfaces.TaskPrestartRequest, resp *interfaces.TaskPrestartResponse) error {
if h.tr.getDriverHandle() != nil {
// Driver handle already exists so don't try to load remote
// task handle
return nil
}
h.tr.stateLock.Lock()
th := drivers.NewTaskHandleFromState(h.tr.state)
h.tr.stateLock.Unlock()
// Task handle will be nil if there was no previous allocation or if
// this is a destructive update
if th == nil {
resp.Done = true
return nil
}
// The task config is unique per invocation so recreate it here
th.Config = h.tr.buildTaskConfig()
if err := h.tr.driver.RecoverTask(th); err != nil {
// Soft error here to let a new instance get started instead of
// failing the task since retrying is unlikely to help.
h.logger.Error("error recovering task state", "error", err)
return nil
}
taskInfo, err := h.tr.driver.InspectTask(th.Config.ID)
if err != nil {
// Soft error here to let a new instance get started instead of
// failing the task since retrying is unlikely to help.
h.logger.Error("error inspecting recovered task state", "error", err)
return nil
}
h.tr.setDriverHandle(NewDriverHandle(h.tr.driver, th.Config.ID, h.tr.Task(), taskInfo.NetworkOverride))
h.tr.stateLock.Lock()
h.tr.localState.TaskHandle = th
h.tr.localState.DriverNetwork = taskInfo.NetworkOverride
h.tr.stateLock.Unlock()
// Ensure the signal is set according to the allocation's state
h.setSignal(h.tr.Alloc())
// Emit TaskStarted manually since the normal task runner logic will
// treat this task like a restored task and skip emitting started.
h.tr.UpdateState(structs.TaskStateRunning, structs.NewTaskEvent(structs.TaskStarted))
return nil
}
// PreKilling tells the remote task driver to detach a remote task instead of
// stopping it.
func (h *remoteTaskHook) PreKilling(ctx context.Context, req *interfaces.TaskPreKillRequest, resp *interfaces.TaskPreKillResponse) error {
alloc := h.tr.Alloc()
h.setSignal(alloc)
return nil
}
// setSignal to detach if the allocation is lost or draining. Safe to call
// multiple times as it only transitions to using detach -- never back to kill.
func (h *remoteTaskHook) setSignal(alloc *structs.Allocation) {
driverHandle := h.tr.getDriverHandle()
if driverHandle == nil {
// Nothing to do exit early
return
}
switch {
case alloc.ClientStatus == structs.AllocClientStatusLost:
// Continue on; lost allocs should just detach
h.logger.Debug("detaching from remote task since alloc was lost")
case alloc.DesiredTransition.ShouldMigrate():
// Continue on; migrating allocs should just detach
h.logger.Debug("detaching from remote task since alloc was drained")
default:
// Nothing to do exit early
return
}
// Set DetachSignal to indicate to the remote task driver that it
// should detach this remote task and ignore it.
driverHandle.SetKillSignal(drivers.DetachSignal)
}

View File

@@ -19,6 +19,7 @@ var _ interfaces.TaskPoststartHook = &serviceHook{}
var _ interfaces.TaskPreKillHook = &serviceHook{}
var _ interfaces.TaskExitedHook = &serviceHook{}
var _ interfaces.TaskStopHook = &serviceHook{}
var _ interfaces.TaskUpdateHook = &serviceHook{}
type serviceHookConfig struct {
alloc *structs.Allocation

View File

@@ -377,7 +377,8 @@ func NewTaskRunner(config *Config) (*TaskRunner, error) {
return nil, err
}
// Initialize the runners hooks.
// Initialize the runners hooks. Must come after initDriver so hooks
// can use tr.driverCapabilities
tr.initHooks()
// Initialize base labels
@@ -496,6 +497,7 @@ func (tr *TaskRunner) Run() {
tr.logger.Info("task failed to restore; waiting to contact server before restarting")
select {
case <-tr.killCtx.Done():
tr.logger.Info("task killed while waiting for server contact")
case <-tr.shutdownCtx.Done():
return
case <-tr.serversContactedCh:
@@ -637,11 +639,12 @@ MAIN:
}
func (tr *TaskRunner) shouldShutdown() bool {
if tr.alloc.ClientTerminalStatus() {
alloc := tr.Alloc()
if alloc.ClientTerminalStatus() {
return true
}
if !tr.IsPoststopTask() && tr.alloc.ServerTerminalStatus() {
if !tr.IsPoststopTask() && alloc.ServerTerminalStatus() {
return true
}
@@ -1142,6 +1145,12 @@ func (tr *TaskRunner) UpdateState(state string, event *structs.TaskEvent) {
tr.logger.Error("error persisting task state", "error", err, "event", event, "state", state)
}
// Store task handle for remote tasks
if tr.driverCapabilities != nil && tr.driverCapabilities.RemoteTasks {
tr.logger.Trace("storing remote task handle state")
tr.localState.TaskHandle.Store(tr.state)
}
// Notify the alloc runner of the transition
tr.stateUpdater.TaskStateUpdated()
}

View File

@@ -152,6 +152,12 @@ func (tr *TaskRunner) initHooks() {
consul: tr.consulServiceClient,
logger: hookLogger,
}))
// If this task driver has remote capabilities, add the remote task
// hook.
if tr.driverCapabilities.RemoteTasks {
tr.runnerHooks = append(tr.runnerHooks, newRemoteTaskHook(tr, hookLogger))
}
}
func (tr *TaskRunner) emitHookError(err error, hookName string) {

View File

@@ -2239,7 +2239,6 @@ func (c *Client) runAllocs(update *allocUpdates) {
// Update the existing allocations
for _, update := range diff.updated {
c.logger.Trace("updating alloc", "alloc_id", update.ID, "index", update.AllocModifyIndex)
c.updateAlloc(update)
}

View File

@@ -32,6 +32,7 @@ import (
_ "github.com/hashicorp/nomad/e2e/periodic"
_ "github.com/hashicorp/nomad/e2e/podman"
_ "github.com/hashicorp/nomad/e2e/quotas"
_ "github.com/hashicorp/nomad/e2e/remotetasks"
_ "github.com/hashicorp/nomad/e2e/rescheduling"
_ "github.com/hashicorp/nomad/e2e/scaling"
_ "github.com/hashicorp/nomad/e2e/scalingpolicies"

View File

@@ -0,0 +1,43 @@
variable "subnets" {
type = list(string)
description = "Subnet IDs task will run in."
}
variable "security_groups" {
type = list(string)
description = "Security Group IDs task will run in."
}
job "nomad-ecs-e2e" {
datacenters = ["dc1"]
group "ecs-remote-task-e2e" {
restart {
attempts = 0
mode = "fail"
}
reschedule {
delay = "5s"
}
task "http-server" {
driver = "ecs"
kill_timeout = "1m" // increased from default to accomodate ECS.
config {
task {
launch_type = "FARGATE"
task_definition = "nomad-rtd-e2e"
network_configuration {
aws_vpc_configuration {
assign_public_ip = "ENABLED"
security_groups = var.security_groups
subnets = var.subnets
}
}
}
}
}
}
}

View File

@@ -0,0 +1,433 @@
package remotetasks
import (
"fmt"
"os"
"testing"
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/ecs"
"github.com/hashicorp/nomad/api"
"github.com/hashicorp/nomad/e2e/e2eutil"
"github.com/hashicorp/nomad/e2e/framework"
"github.com/hashicorp/nomad/helper/uuid"
"github.com/hashicorp/nomad/jobspec2"
"github.com/hashicorp/nomad/plugins/base"
"github.com/hashicorp/nomad/testutil"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
const (
ecsTaskStatusDeactivating = "DEACTIVATING"
ecsTaskStatusStopping = "STOPPING"
ecsTaskStatusDeprovisioning = "DEPROVISIONING"
ecsTaskStatusStopped = "STOPPED"
ecsTaskStatusRunning = "RUNNING"
)
type RemoteTasksTest struct {
framework.TC
jobIDs []string
}
func init() {
framework.AddSuites(&framework.TestSuite{
Component: "RemoteTasks",
CanRunLocal: true,
Cases: []framework.TestCase{
new(RemoteTasksTest),
},
})
}
func (tc *RemoteTasksTest) BeforeAll(f *framework.F) {
e2eutil.WaitForLeader(f.T(), tc.Nomad())
e2eutil.WaitForNodesReady(f.T(), tc.Nomad(), 2)
}
func (tc *RemoteTasksTest) AfterEach(f *framework.F) {
nomadClient := tc.Nomad()
// Mark all nodes eligible
nodesAPI := tc.Nomad().Nodes()
nodes, _, _ := nodesAPI.List(nil)
for _, node := range nodes {
nodesAPI.ToggleEligibility(node.ID, true, nil)
}
jobs := nomadClient.Jobs()
// Stop all jobs in test
for _, id := range tc.jobIDs {
jobs.Deregister(id, true, nil)
}
tc.jobIDs = []string{}
// Garbage collect
nomadClient.System().GarbageCollect()
}
// TestECSJob asserts an ECS job may be started and is cleaned up when stopped.
func (tc *RemoteTasksTest) TestECSJob(f *framework.F) {
t := f.T()
ecsClient := ecsOrSkip(t, tc.Nomad())
jobID := "ecsjob-" + uuid.Generate()[0:8]
tc.jobIDs = append(tc.jobIDs, jobID)
_, allocs := registerECSJobs(t, tc.Nomad(), jobID)
require.Len(t, allocs, 1)
allocID := allocs[0].ID
e2eutil.WaitForAllocsRunning(t, tc.Nomad(), []string{allocID})
// We need to go from Allocation -> ECS ARN, so grab the updated
// allocation's task state.
arn := arnForAlloc(t, tc.Nomad().Allocations(), allocID)
// Use ARN to lookup status of ECS task in AWS
ensureECSRunning(t, ecsClient, arn)
t.Logf("Task %s is running!", arn)
// Stop the job
e2eutil.WaitForJobStopped(t, tc.Nomad(), jobID)
// Ensure it is stopped in ECS
input := ecs.DescribeTasksInput{
Cluster: aws.String("nomad-rtd-e2e"),
Tasks: []*string{aws.String(arn)},
}
testutil.WaitForResult(func() (bool, error) {
resp, err := ecsClient.DescribeTasks(&input)
if err != nil {
return false, err
}
status := *resp.Tasks[0].LastStatus
return status == ecsTaskStatusStopped, fmt.Errorf("ecs task is not stopped: %s", status)
}, func(err error) {
t.Fatalf("error retrieving ecs task status: %v", err)
})
}
// TestECSDrain asserts an ECS job may be started, drained from one node, and
// is managed by a new node without stopping and restarting the remote task.
func (tc *RemoteTasksTest) TestECSDrain(f *framework.F) {
t := f.T()
ecsClient := ecsOrSkip(t, tc.Nomad())
jobID := "ecsjob-" + uuid.Generate()[0:8]
tc.jobIDs = append(tc.jobIDs, jobID)
_, allocs := registerECSJobs(t, tc.Nomad(), jobID)
require.Len(t, allocs, 1)
origNode := allocs[0].NodeID
origAlloc := allocs[0].ID
e2eutil.WaitForAllocsRunning(t, tc.Nomad(), []string{origAlloc})
arn := arnForAlloc(t, tc.Nomad().Allocations(), origAlloc)
ensureECSRunning(t, ecsClient, arn)
t.Logf("Task %s is running! Now to drain the node.", arn)
// Drain the node
_, err := tc.Nomad().Nodes().UpdateDrain(
origNode,
&api.DrainSpec{Deadline: 30 * time.Second},
false,
nil,
)
require.NoError(t, err, "error draining original node")
// Wait for new alloc to be running
var newAlloc *api.AllocationListStub
qopts := &api.QueryOptions{}
testutil.WaitForResult(func() (bool, error) {
allocs, resp, err := tc.Nomad().Jobs().Allocations(jobID, false, qopts)
if err != nil {
return false, fmt.Errorf("error retrieving allocations for job: %w", err)
}
qopts.WaitIndex = resp.LastIndex
if len(allocs) > 2 {
return false, fmt.Errorf("expected 1 or 2 allocs but found %d", len(allocs))
}
for _, alloc := range allocs {
if alloc.ID == origAlloc {
// This is the old alloc, skip it
continue
}
newAlloc = alloc
if newAlloc.ClientStatus == "running" {
break
}
}
if newAlloc == nil {
return false, fmt.Errorf("no new alloc found")
}
if newAlloc.ClientStatus != "running" {
return false, fmt.Errorf("expected new alloc (%s) to be running but found: %s",
newAlloc.ID, newAlloc.ClientStatus)
}
return true, nil
}, func(err error) {
t.Fatalf("error waiting for new alloc to be running: %v", err)
})
// Make sure the ARN hasn't changed by looking up the new alloc's ARN
newARN := arnForAlloc(t, tc.Nomad().Allocations(), newAlloc.ID)
assert.Equal(t, arn, newARN, "unexpected new ARN")
}
// TestECSDeployment asserts a new ECS task is started when an ECS job is
// deployed.
func (tc *RemoteTasksTest) TestECSDeployment(f *framework.F) {
t := f.T()
ecsClient := ecsOrSkip(t, tc.Nomad())
jobID := "ecsjob-" + uuid.Generate()[0:8]
tc.jobIDs = append(tc.jobIDs, jobID)
job, origAllocs := registerECSJobs(t, tc.Nomad(), jobID)
require.Len(t, origAllocs, 1)
origAllocID := origAllocs[0].ID
e2eutil.WaitForAllocsRunning(t, tc.Nomad(), []string{origAllocID})
// We need to go from Allocation -> ECS ARN, so grab the updated
// allocation's task state.
origARN := arnForAlloc(t, tc.Nomad().Allocations(), origAllocID)
// Use ARN to lookup status of ECS task in AWS
ensureECSRunning(t, ecsClient, origARN)
t.Logf("Task %s is running! Updating...", origARN)
// Force a deployment by updating meta
job.Meta = map[string]string{
"updated": time.Now().Format(time.RFC3339Nano),
}
// Register updated job
resp, _, err := tc.Nomad().Jobs().Register(job, nil)
require.NoError(t, err, "error registering updated job")
require.NotEmpty(t, resp.EvalID, "no eval id created when registering updated job")
// Wait for new alloc to be running
var newAlloc *api.AllocationListStub
testutil.WaitForResult(func() (bool, error) {
allocs, _, err := tc.Nomad().Jobs().Allocations(jobID, false, nil)
if err != nil {
return false, err
}
for _, a := range allocs {
if a.ID == origAllocID {
if a.ClientStatus == "complete" {
// Original alloc stopped as expected!
continue
}
// Original alloc is still running
newAlloc = nil
return false, fmt.Errorf("original alloc not yet terminal. "+
"client status: %s; desired status: %s",
a.ClientStatus, a.DesiredStatus)
}
if a.ClientStatus != "running" {
return false, fmt.Errorf("new alloc is not running: %s", a.ClientStatus)
}
if newAlloc != nil {
return false, fmt.Errorf("found 2 replacement allocs: %s and %s",
a.ID, newAlloc.ID)
}
newAlloc = a
}
return newAlloc != nil, fmt.Errorf("no new alloc found for updated job")
}, func(err error) {
require.NoError(t, err, "error waiting for updated alloc")
})
newARN := arnForAlloc(t, tc.Nomad().Allocations(), newAlloc.ID)
t.Logf("Task %s is updated!", newARN)
require.NotEqual(t, origARN, newARN, "expected new ARN")
// Ensure original ARN is stopped in ECS
input := ecs.DescribeTasksInput{
Cluster: aws.String("nomad-rtd-e2e"),
Tasks: []*string{aws.String(origARN)},
}
testutil.WaitForResult(func() (bool, error) {
resp, err := ecsClient.DescribeTasks(&input)
if err != nil {
return false, err
}
status := *resp.Tasks[0].LastStatus
return status == ecsTaskStatusStopped, fmt.Errorf("original ecs task is not stopped: %s", status)
}, func(err error) {
t.Fatalf("error retrieving ecs task status for original ARN: %v", err)
})
}
// ecsOrSkip returns an AWS ECS client or skips the test if ECS is unreachable
// by the test runner or the ECS remote task driver isn't healthy.
func ecsOrSkip(t *testing.T, nomadClient *api.Client) *ecs.ECS {
awsSession := session.Must(session.NewSession())
ecsClient := ecs.New(awsSession, aws.NewConfig().WithRegion("us-east-1"))
_, err := ecsClient.ListClusters(&ecs.ListClustersInput{})
if err != nil {
t.Skipf("Skipping ECS Remote Task Driver Task. Error querying AWS ECS API: %v", err)
}
testutil.WaitForResult(func() (bool, error) {
nodes, _, err := nomadClient.Nodes().List(nil)
if err != nil {
return false, fmt.Errorf("error retrieving node listing: %w", err)
}
notReady := 0
notEligible := 0
noECS := 0
notHealthy := 0
ready := 0
for _, n := range nodes {
if n.Status != "ready" {
notReady++
continue
}
if n.SchedulingEligibility != "eligible" {
notEligible++
continue
}
ecsDriver, ok := n.Drivers["ecs"]
if !ok {
noECS++
continue
}
if !ecsDriver.Healthy {
notHealthy++
continue
}
ready++
}
return ready > 1, fmt.Errorf("expected 2 nodes with healthy ecs drivers but found: "+
"not_ready=%d ineligible=%d no_driver=%d unhealthy=%d ok=%d",
notReady, notEligible, noECS, notHealthy, ready)
}, func(err error) {
if err != nil {
t.Skipf("Skipping Remote Task Driver tests due to: %v", err)
}
})
return ecsClient
}
// arnForAlloc retrieves the ARN for a running allocation.
func arnForAlloc(t *testing.T, allocAPI *api.Allocations, allocID string) string {
t.Logf("Retrieving ARN for alloc=%s", allocID)
ecsState := struct {
ARN string
}{}
testutil.WaitForResult(func() (bool, error) {
alloc, _, err := allocAPI.Info(allocID, nil)
if err != nil {
return false, err
}
state := alloc.TaskStates["http-server"]
if state == nil {
return false, fmt.Errorf("no task state for http-server (%d task states)", len(alloc.TaskStates))
}
if state.TaskHandle == nil {
return false, fmt.Errorf("no task handle for http-server")
}
if len(state.TaskHandle.DriverState) == 0 {
return false, fmt.Errorf("no driver state for task handle")
}
if err := base.MsgPackDecode(state.TaskHandle.DriverState, &ecsState); err != nil {
return false, fmt.Errorf("error decoding driver state: %w", err)
}
if ecsState.ARN == "" {
return false, fmt.Errorf("ARN is empty despite DriverState being %d bytes", len(state.TaskHandle.DriverState))
}
return true, nil
}, func(err error) {
t.Fatalf("error getting ARN: %v", err)
})
t.Logf("Retrieved ARN=%s for alloc=%s", ecsState.ARN, allocID)
return ecsState.ARN
}
// ensureECSRunning asserts that the given ARN is a running ECS task.
func ensureECSRunning(t *testing.T, ecsClient *ecs.ECS, arn string) {
t.Logf("Ensuring ARN=%s is running", arn)
input := ecs.DescribeTasksInput{
Cluster: aws.String("nomad-rtd-e2e"),
Tasks: []*string{aws.String(arn)},
}
testutil.WaitForResult(func() (bool, error) {
resp, err := ecsClient.DescribeTasks(&input)
if err != nil {
return false, err
}
status := *resp.Tasks[0].LastStatus
return status == ecsTaskStatusRunning, fmt.Errorf("ecs task is not running: %s", status)
}, func(err error) {
t.Fatalf("error retrieving ecs task status: %v", err)
})
t.Logf("ARN=%s is running", arn)
}
// registerECSJobs registers an ECS job and returns it and its allocation
// stubs.
func registerECSJobs(t *testing.T, nomadClient *api.Client, jobID string) (*api.Job, []*api.AllocationListStub) {
const (
jobPath = "remotetasks/input/ecs.nomad"
varPath = "remotetasks/input/ecs.vars"
)
jobBytes, err := os.ReadFile(jobPath)
require.NoError(t, err, "error reading job file")
job, err := jobspec2.ParseWithConfig(&jobspec2.ParseConfig{
Path: jobPath,
Body: jobBytes,
VarFiles: []string{varPath},
Strict: true,
})
require.NoErrorf(t, err, "error parsing jobspec from %s with var file %s", jobPath, varPath)
job.ID = &jobID
job.Name = &jobID
// Register job
resp, _, err := nomadClient.Jobs().Register(job, nil)
require.NoError(t, err, "error registering job")
require.NotEmpty(t, resp.EvalID, "no eval id created when registering job")
var allocs []*api.AllocationListStub
testutil.WaitForResult(func() (bool, error) {
allocs, _, err = nomadClient.Jobs().Allocations(jobID, false, nil)
if err != nil {
return false, err
}
return len(allocs) > 0, fmt.Errorf("no allocs found")
}, func(err error) {
require.NoErrorf(t, err, "error retrieving allocations for %s", jobID)
})
return job, allocs
}

View File

@@ -2,99 +2,98 @@
# Manual edits may be lost in future updates.
provider "registry.terraform.io/hashicorp/aws" {
version = "3.22.0"
version = "3.37.0"
hashes = [
"h1:8aWXjFcmEi64P0TMHOCQXWws+/SmvJQrNvHlzdktKOM=",
"h1:f/Tz8zv1Zb78ZaiyJkQ0MGIViZwbYrLuQk3kojPM91c=",
"zh:4a9a66caf1964cdd3b61fb3ebb0da417195a5529cb8e496f266b0778335d11c8",
"zh:514f2f006ae68db715d86781673faf9483292deab235c7402ff306e0e92ea11a",
"zh:5277b61109fddb9011728f6650ef01a639a0590aeffe34ed7de7ba10d0c31803",
"zh:67784dc8c8375ab37103eea1258c3334ee92be6de033c2b37e3a2a65d0005142",
"zh:76d4c8be2ca4a3294fb51fb58de1fe03361d3bc403820270cc8e71a04c5fa806",
"zh:8f90b1cfdcf6e8fb1a9d0382ecaa5056a3a84c94e313fbf9e92c89de271cdede",
"zh:d0ac346519d0df124df89be2d803eb53f373434890f6ee3fb37112802f9eac59",
"zh:d6256feedada82cbfb3b1dd6dd9ad02048f23120ab50e6146a541cb11a108cc1",
"zh:db2fe0d2e77c02e9a74e1ed694aa352295a50283f9a1cf896e5be252af14e9f4",
"zh:eda61e889b579bd90046939a5b40cf5dc9031fb5a819fc3e4667a78bd432bdb2",
"h1:Tf6Os+utUxE8rEr/emCXLFEDdCb0Y6rsN4Ee84+aDCQ=",
"zh:064c9b21bcd69be7a8631ccb3eccb8690c6a9955051145920803ef6ce6fc06bf",
"zh:277dd05750187a41282cf6e066e882eac0dd0056e3211d125f94bf62c19c4b8b",
"zh:47050211f72dcbf3d99c82147abd2eefbb7238efb94d5188979f60de66c8a3df",
"zh:4a4e0d070399a050847545721dae925c192a2d6354802fdfbea73769077acca5",
"zh:4cbc46f79239c85d69389f9e91ca9a9ebf6a8a937cfada026c5a037fd09130fb",
"zh:6548dcb1ac4a388ed46034a5317fa74b3b0b0f68eec03393f2d4d09342683f95",
"zh:75b4a82596aa525d95b0b2847fe648368c6e2b054059c4dc4dcdee01d374b592",
"zh:75cf5cc674b61c82300667a82650f56722618b119ab0526b47b5ecbb4bbf49d0",
"zh:93c896682359039960c38eb5a4b29d1cc06422f228db0572b90330427e2a21ec",
"zh:c7256663aedbc9de121316b6d0623551386a476fc12b8eb77e88532ce15de354",
"zh:e995c32f49c23b5938200386e08b2a3fd69cf5102b5299366c0608bbeac68429",
]
}
provider "registry.terraform.io/hashicorp/external" {
version = "2.0.0"
version = "2.1.0"
hashes = [
"h1:6S7hqjmUnoAZ5D/0F1VlJZKSJsUIBh7Ro0tLjGpKO0g=",
"h1:Q5xqryWI3tCY8yr+fugq7dz4Qz+8g4GaW9ZS8dc6Ob8=",
"zh:07949780dd6a1d43e7b46950f6e6976581d9724102cb5388d3411a1b6f476bde",
"zh:0a4f4636ff93f0644affa8474465dd8c9252946437ad025b28fc9f6603534a24",
"zh:0dd7e05a974c649950d1a21d7015d3753324ae52ebdd1744b144bc409ca4b3e8",
"zh:2b881032b9aa9d227ac712f614056d050bcdcc67df0dc79e2b2cb76a197059ad",
"zh:38feb4787b4570335459ca75a55389df1a7570bdca8cdf5df4c2876afe3c14b4",
"zh:40f7e0aaef3b1f4c2ca2bb1189e3fe9af8c296da129423986d1d99ccc8cfb86c",
"zh:56b361f64f0f0df5c4f958ae2f0e6f8ba192f35b720b9d3ae1be068fabcf73d9",
"zh:5fadb5880cd31c2105f635ded92b9b16f918c1dd989627a4ce62c04939223909",
"zh:61fa0be9c14c8c4109cfb7be8d54a80c56d35dbae49d3231cddb59831e7e5a4d",
"zh:853774bf97fbc4a784d5af5a4ca0090848430781ae6cfc586adeb48f7c44af79",
"h1:wbtDfLeawmv6xVT1W0w0fctRCb4ABlaD3JTxwb1jXag=",
"zh:0d83ffb72fbd08986378204a7373d8c43b127049096eaf2765bfdd6b00ad9853",
"zh:7577d6edc67b1e8c2cf62fe6501192df1231d74125d90e51d570d586d95269c5",
"zh:9c669ded5d5affa4b2544952c4b6588dfed55260147d24ced02dca3a2829f328",
"zh:a404d46f2831f90633947ab5d57e19dbfe35b3704104ba6ec80bcf50b058acfd",
"zh:ae1caea1c936d459ceadf287bb5c5bd67b5e2a7819df6f5c4114b7305df7f822",
"zh:afb4f805477694a4b9dde86b268d2c0821711c8aab1c6088f5f992228c4c06fb",
"zh:b993b4a1de8a462643e78f4786789e44ce5064b332fee1cb0d6250ed085561b8",
"zh:c84b2c13fa3ea2c0aa7291243006d560ce480a5591294b9001ce3742fc9c5791",
"zh:c8966f69b7eccccb771704fd5335923692eccc9e0e90cb95d14538fe2e92a3b8",
"zh:d5fe68850d449b811e633a300b114d0617df6d450305e8251643b4d143dc855b",
"zh:ddebfd1e674ba336df09b1f27bbaa0e036c25b7a7087dc8081443f6e5954028b",
]
}
provider "registry.terraform.io/hashicorp/local" {
version = "2.0.0"
version = "2.1.0"
hashes = [
"h1:EC6eh7avwx1rF56h3RZcxgEp/14ihi7Sk/4J3Hn4nIE=",
"h1:pO1ANXtOCRfecKsY9Hn4UsXoPBLv6LFiDIEiS1MZ09E=",
"zh:34ce8b79493ace8333d094752b579ccc907fa9392a2c1d6933a6c95d0786d3f1",
"zh:5c5a19c4f614a4ffb68bae0b0563f3860115cf7539b8adc21108324cfdc10092",
"zh:67ddb1ca2cd3e1a8f948302597ceb967f19d2eeb2d125303493667388fe6330e",
"zh:68e6b16f3a8e180fcba1a99754118deb2d82331b51f6cca39f04518339bfdfa6",
"zh:8393a12eb11598b2799d51c9b0a922a3d9fadda5a626b94a1b4914086d53120e",
"zh:90daea4b2010a86f2aca1e3a9590e0b3ddcab229c2bd3685fae76a832e9e836f",
"zh:99308edc734a0ac9149b44f8e316ca879b2670a1cae387a8ae754c180b57cdb4",
"zh:c76594db07a9d1a73372a073888b672df64adb455d483c2426cc220eda7e092e",
"zh:dc09c1fb36c6a706bdac96cce338952888c8423978426a09f5df93031aa88b84",
"zh:deda88134e9780319e8de91b3745520be48ead6ec38cb662694d09185c3dac70",
"h1:EYZdckuGU3n6APs97nS2LxZm3dDtGqyM4qaIvsmac8o=",
"zh:0f1ec65101fa35050978d483d6e8916664b7556800348456ff3d09454ac1eae2",
"zh:36e42ac19f5d68467aacf07e6adcf83c7486f2e5b5f4339e9671f68525fc87ab",
"zh:6db9db2a1819e77b1642ec3b5e95042b202aee8151a0256d289f2e141bf3ceb3",
"zh:719dfd97bb9ddce99f7d741260b8ece2682b363735c764cac83303f02386075a",
"zh:7598bb86e0378fd97eaa04638c1a4c75f960f62f69d3662e6d80ffa5a89847fe",
"zh:ad0a188b52517fec9eca393f1e2c9daea362b33ae2eb38a857b6b09949a727c1",
"zh:c46846c8df66a13fee6eff7dc5d528a7f868ae0dcf92d79deaac73cc297ed20c",
"zh:dc1a20a2eec12095d04bf6da5321f535351a594a636912361db20eb2a707ccc4",
"zh:e57ab4771a9d999401f6badd8b018558357d3cbdf3d33cc0c4f83e818ca8e94b",
"zh:ebdcde208072b4b0f8d305ebf2bfdc62c926e0717599dcf8ec2fd8c5845031c3",
"zh:ef34c52b68933bedd0868a13ccfd59ff1c820f299760b3c02e008dc95e2ece91",
]
}
provider "registry.terraform.io/hashicorp/null" {
version = "3.0.0"
version = "3.1.0"
hashes = [
"h1:V1tzrSG6t3e7zWvUwRbGbhsWU2Jd/anrJpOl9XM+R/8=",
"h1:ysHGBhBNkIiJLEpthB/IVCLpA1Qoncp3KbCTFGFZTO0=",
"zh:05fb7eab469324c97e9b73a61d2ece6f91de4e9b493e573bfeda0f2077bc3a4c",
"zh:1688aa91885a395c4ae67636d411475d0b831e422e005dcf02eedacaafac3bb4",
"zh:24a0b1292e3a474f57c483a7a4512d797e041bc9c2fbaac42fe12e86a7fb5a3c",
"zh:2fc951bd0d1b9b23427acc93be09b6909d72871e464088171da60fbee4fdde03",
"zh:6db825759425599a326385a68acc6be2d9ba0d7d6ef587191d0cdc6daef9ac63",
"zh:85985763d02618993c32c294072cc6ec51f1692b803cb506fcfedca9d40eaec9",
"zh:a53186599c57058be1509f904da512342cfdc5d808efdaf02dec15f0f3cb039a",
"zh:c2e07b49b6efa676bdc7b00c06333ea1792a983a5720f9e2233db27323d2707c",
"zh:cdc8fe1096103cf5374751e2e8408ec4abd2eb67d5a1c5151fe2c7ecfd525bef",
"zh:dbdef21df0c012b0d08776f3d4f34eb0f2f229adfde07ff252a119e52c0f65b7",
"h1:vpC6bgUQoJ0znqIKVFevOdq+YQw42bRq0u+H3nto8nA=",
"zh:02a1675fd8de126a00460942aaae242e65ca3380b5bb192e8773ef3da9073fd2",
"zh:53e30545ff8926a8e30ad30648991ca8b93b6fa496272cd23b26763c8ee84515",
"zh:5f9200bf708913621d0f6514179d89700e9aa3097c77dac730e8ba6e5901d521",
"zh:9ebf4d9704faba06b3ec7242c773c0fbfe12d62db7d00356d4f55385fc69bfb2",
"zh:a6576c81adc70326e4e1c999c04ad9ca37113a6e925aefab4765e5a5198efa7e",
"zh:a8a42d13346347aff6c63a37cda9b2c6aa5cc384a55b2fe6d6adfa390e609c53",
"zh:c797744d08a5307d50210e0454f91ca4d1c7621c68740441cf4579390452321d",
"zh:cecb6a304046df34c11229f20a80b24b1603960b794d68361a67c5efe58e62b8",
"zh:e1371aa1e502000d9974cfaff5be4cfa02f47b17400005a16f14d2ef30dc2a70",
"zh:fc39cc1fe71234a0b0369d5c5c7f876c71b956d23d7d6f518289737a001ba69b",
"zh:fea4227271ebf7d9e2b61b89ce2328c7262acd9fd190e1fd6d15a591abfa848e",
]
}
provider "registry.terraform.io/hashicorp/random" {
version = "3.0.0"
version = "3.1.0"
hashes = [
"h1:grDzxfnOdFXi90FRIIwP/ZrCzirJ/SfsGBe6cE0Shg4=",
"h1:yhHJpb4IfQQfuio7qjUXuUFTU/s+ensuEpm23A+VWz0=",
"zh:0fcb00ff8b87dcac1b0ee10831e47e0203a6c46aafd76cb140ba2bab81f02c6b",
"zh:123c984c0e04bad910c421028d18aa2ca4af25a153264aef747521f4e7c36a17",
"zh:287443bc6fd7fa9a4341dec235589293cbcc6e467a042ae225fd5d161e4e68dc",
"zh:2c1be5596dd3cca4859466885eaedf0345c8e7628503872610629e275d71b0d2",
"zh:684a2ef6f415287944a3d966c4c8cee82c20e393e096e2f7cdcb4b2528407f6b",
"zh:7625ccbc6ff17c2d5360ff2af7f9261c3f213765642dcd84e84ae02a3768fd51",
"zh:9a60811ab9e6a5bfa6352fbb943bb530acb6198282a49373283a8fa3aa2b43fc",
"zh:c73e0eaeea6c65b1cf5098b101d51a2789b054201ce7986a6d206a9e2dacaefd",
"zh:e8f9ed41ac83dbe407de9f0206ef1148204a0d51ba240318af801ffb3ee5f578",
"zh:fbdd0684e62563d3ac33425b0ac9439d543a3942465f4b26582bcfabcb149515",
"h1:BZMEPucF+pbu9gsPk0G0BHx7YP04+tKdq2MrRDF1EDM=",
"zh:2bbb3339f0643b5daa07480ef4397bd23a79963cc364cdfbb4e86354cb7725bc",
"zh:3cd456047805bf639fbf2c761b1848880ea703a054f76db51852008b11008626",
"zh:4f251b0eda5bb5e3dc26ea4400dba200018213654b69b4a5f96abee815b4f5ff",
"zh:7011332745ea061e517fe1319bd6c75054a314155cb2c1199a5b01fe1889a7e2",
"zh:738ed82858317ccc246691c8b85995bc125ac3b4143043219bd0437adc56c992",
"zh:7dbe52fac7bb21227acd7529b487511c91f4107db9cc4414f50d04ffc3cab427",
"zh:a3a9251fb15f93e4cfc1789800fc2d7414bbc18944ad4c5c98f466e6477c42bc",
"zh:a543ec1a3a8c20635cf374110bd2f87c07374cf2c50617eee2c669b3ceeeaa9f",
"zh:d9ab41d556a48bd7059f0810cf020500635bfc696c9fc3adab5ea8915c1d886b",
"zh:d9e13427a7d011dbd654e591b0337e6074eef8c3b9bb11b2e39eaaf257044fd7",
"zh:f7605bd1437752114baf601bdf6931debe6dc6bfe3006eb7e9bb9080931dca8a",
]
}
provider "registry.terraform.io/hashicorp/template" {
version = "2.2.0"
hashes = [
"h1:0wlehNaxBX7GJQnPfQwTNvvAf38Jm0Nv7ssKGMaG6Og=",
"h1:94qn780bi1qjrbC3uQtjJh3Wkfwd5+tTtJHOb7KTg9w=",
"zh:01702196f0a0492ec07917db7aaa595843d8f171dc195f4c988d2ffca2a06386",
"zh:09aae3da826ba3d7df69efeb25d146a1de0d03e951d35019a0f80e4f58c89b53",
@@ -110,19 +109,19 @@ provider "registry.terraform.io/hashicorp/template" {
}
provider "registry.terraform.io/hashicorp/tls" {
version = "3.0.0"
version = "3.1.0"
hashes = [
"h1:AcQGOAD5xa4KE9gYw5g7R6UU8a77Yn/afPvch4N86lQ=",
"h1:LtCEW5v1E5Eo49+kQOsKHRYf9Hc8ZR0jTpK+mXszPHs=",
"zh:05eac573a1fe53227bcc6b01daf6ddf0b73456f97f56f316f1b3114a4771e175",
"zh:09390dad764c76f0fd59cae4dad296e3e39487e06de3a4bc0df73916c6bb2f17",
"zh:142d0bc4722ab088b7ca124b0eb44206b9d100f51035c162d50ef552e09813d0",
"zh:2c391743dd20f43329c0d0d49dec7827970d788115593c0e32a57050c0a85337",
"zh:525b12fc87369c0e6d347afe6c77668aebf56cfa078bb0f1f01cc2ee01ac7016",
"zh:5583d81b7a05c6d49a4c445e1ee62e82facb07bb9204998a836b7b522a51db8d",
"zh:925e3acc70e18ed1cd296d337fc3e0ca43ac6f5bf2e660f24de750c7754f91aa",
"zh:a291457d25b207fd28fb4fad9209ebb591e25cfc507ca1cb0fb8b2e255be1969",
"zh:bbf9e2718752aebfbd7c6b8e196eb2e52730b66befed2ea1954f9ff1c199295e",
"zh:f4b333c467ae02c1a238ac57465fe66405f6e2a6cfeb4eded9bc321c5652a1bf",
"h1:fUJX8Zxx38e2kBln+zWr1Tl41X+OuiE++REjrEyiOM4=",
"zh:3d46616b41fea215566f4a957b6d3a1aa43f1f75c26776d72a98bdba79439db6",
"zh:623a203817a6dafa86f1b4141b645159e07ec418c82fe40acd4d2a27543cbaa2",
"zh:668217e78b210a6572e7b0ecb4134a6781cc4d738f4f5d09eb756085b082592e",
"zh:95354df03710691773c8f50a32e31fca25f124b7f3d6078265fdf3c4e1384dca",
"zh:9f97ab190380430d57392303e3f36f4f7835c74ea83276baa98d6b9a997c3698",
"zh:a16f0bab665f8d933e95ca055b9c8d5707f1a0dd8c8ecca6c13091f40dc1e99d",
"zh:be274d5008c24dc0d6540c19e22dbb31ee6bfdd0b2cddd4d97f3cd8a8d657841",
"zh:d5faa9dce0a5fc9d26b2463cea5be35f8586ab75030e7fa4d4920cd73ee26989",
"zh:e9b672210b7fb410780e7b429975adcc76dd557738ecc7c890ea18942eb321a5",
"zh:eb1f8368573d2370605d6dbf60f9aaa5b64e55741d96b5fb026dbfe91de67c0d",
"zh:fc1e12b713837b85daf6c3bb703d7795eaf1c5177aebae1afcf811dd7009f4b0",
]
}

View File

@@ -27,6 +27,14 @@ plugin "nomad-driver-podman" {
}
}
plugin "nomad-driver-ecs" {
config {
enabled = true
cluster = "nomad-rtd-e2e"
region = "us-east-1"
}
}
vault {
enabled = true
address = "http://active.vault.service.consul:8200"

View File

@@ -25,6 +25,14 @@ plugin "nomad-driver-podman" {
}
}
plugin "nomad-driver-ecs" {
config {
enabled = true
cluster = "nomad-rtd-e2e"
region = "us-east-1"
}
}
vault {
enabled = true
address = "http://active.vault.service.consul:8200"

View File

@@ -20,6 +20,14 @@ plugin "nomad-driver-podman" {
}
}
plugin "nomad-driver-ecs" {
config {
enabled = true
cluster = "nomad-rtd-e2e"
region = "us-east-1"
}
}
vault {
enabled = true
address = "http://active.vault.service.consul:8200"

View File

@@ -0,0 +1,21 @@
[
{
"command": [
"/bin/sh -c \"echo '<html> <head> <title>Amazon ECS Sample App</title> <style>body {margin-top: 40px; background-color: #333;} </style> </head><body> <div style=color:white;text-align:center> <h1>Amazon ECS Sample App</h1> <h2>Congratulations!</h2> <p>Your application is now running on a container in Amazon ECS.</p> </div></body></html>' > /usr/local/apache2/htdocs/index.html && httpd-foreground\""
],
"entryPoint": [
"sh",
"-c"
],
"essential": true,
"image": "httpd:2.4",
"name": "nomad-remote-driver-demo",
"portMappings": [
{
"containerPort": 80,
"hostPort": 80,
"protocol": "tcp"
}
]
}
]

29
e2e/terraform/ecs.tf Normal file
View File

@@ -0,0 +1,29 @@
# Nomad ECS Remote Task Driver E2E
resource "aws_ecs_cluster" "nomad_rtd_e2e" {
name = "nomad-rtd-e2e"
}
resource "aws_ecs_task_definition" "nomad_rtd_e2e" {
family = "nomad-rtd-e2e"
container_definitions = file("ecs-task.json")
# Don't need a network for e2e tests
network_mode = "awsvpc"
requires_compatibilities = ["FARGATE"]
cpu = 256
memory = 512
}
data "template_file" "ecs_vars_hcl" {
template = <<EOT
security_groups = ["${aws_security_group.primary.id}"]
subnets = ["${data.aws_subnet.default.id}"]
EOT
}
resource "local_file" "ecs_vars_hcl" {
content = data.template_file.ecs_vars_hcl.rendered
filename = "${path.module}/../remotetasks/input/ecs.vars"
file_permission = "0664"
}

View File

@@ -131,6 +131,13 @@ sudo chmod +x "${NOMAD_PLUGIN_DIR}/nomad-driver-podman"
sudo mv /tmp/linux/io.podman.service /etc/systemd/system/io.podman.service
sudo mv /tmp/linux/io.podman.socket /etc/systemd/system/io.podman.socket
if [ -a "/tmp/linux/nomad-driver-ecs" ]; then
echo "Installing nomad-driver-ecs"
sudo install --mode=0755 --owner=ubuntu /tmp/linux/nomad-driver-ecs "$NOMAD_PLUGIN_DIR"
else
echo "nomad-driver-ecs not found: skipping install"
fi
echo "Configuring dnsmasq"
# disable systemd-resolved and configure dnsmasq to forward local requests to

View File

@@ -7406,6 +7406,34 @@ type AllocState struct {
Time time.Time
}
// TaskHandle is optional handle to a task propogated to the servers for use
// by remote tasks. Since remote tasks are not implicitly lost when the node
// they are assigned to is down, their state is migrated to the replacement
// allocation.
//
// Minimal set of fields from plugins/drivers/task_handle.go:TaskHandle
type TaskHandle struct {
// Version of driver state. Used by the driver to gracefully handle
// plugin upgrades.
Version int
// Driver-specific state containing a handle to the remote task.
DriverState []byte
}
func (h *TaskHandle) Copy() *TaskHandle {
if h == nil {
return nil
}
newTH := TaskHandle{
Version: h.Version,
DriverState: make([]byte, len(h.DriverState)),
}
copy(newTH.DriverState, h.DriverState)
return &newTH
}
// Set of possible states for a task.
const (
TaskStatePending = "pending" // The task is waiting to be run.
@@ -7439,6 +7467,10 @@ type TaskState struct {
// Series of task events that transition the state of the task.
Events []*TaskEvent
// Experimental - TaskHandle is based on drivers.TaskHandle and used
// by remote task drivers to migrate task handles between allocations.
TaskHandle *TaskHandle
}
// NewTaskState returns a TaskState initialized in the Pending state.
@@ -7460,16 +7492,18 @@ func (ts *TaskState) Copy() *TaskState {
if ts == nil {
return nil
}
copy := new(TaskState)
*copy = *ts
newTS := new(TaskState)
*newTS = *ts
if ts.Events != nil {
copy.Events = make([]*TaskEvent, len(ts.Events))
newTS.Events = make([]*TaskEvent, len(ts.Events))
for i, e := range ts.Events {
copy.Events[i] = e.Copy()
newTS.Events[i] = e.Copy()
}
}
return copy
newTS.TaskHandle = ts.TaskHandle.Copy()
return newTS
}
// Successful returns whether a task finished successfully. This doesn't really

View File

@@ -73,6 +73,7 @@ func (d *driverPluginClient) Capabilities() (*Capabilities, error) {
}
caps.MountConfigs = MountConfigSupport(resp.Capabilities.MountConfigs)
caps.RemoteTasks = resp.Capabilities.RemoteTasks
}
return caps, nil

View File

@@ -31,6 +31,13 @@ const (
// handle is from a driver that existed before driver plugins (v0.9). The
// driver should take appropriate action to handle the old driver state.
Pre09TaskHandleVersion = 0
// DetachSignal is a special signal sent to remote task drivers when a
// task should be detached instead of killed. This allows a remote task
// to be left running and transferred to a replacement allocation in
// cases like down or drained nodes causing the original allocation to
// be terminal.
DetachSignal = "DETACH"
)
// DriverPlugin is the interface with drivers will implement. It is also
@@ -158,6 +165,12 @@ type Capabilities struct {
// MountConfigs tells Nomad which mounting config options the driver supports.
MountConfigs MountConfigSupport
// RemoteTasks indicates this driver runs tasks on remote systems
// instead of locally. The Nomad client can use this information to
// adjust behavior such as propogating task handles between allocations
// to avoid downtime when a client is lost.
RemoteTasks bool
}
func (c *Capabilities) HasNetIsolationMode(m NetIsolationMode) bool {

View File

@@ -1836,10 +1836,13 @@ type DriverCapabilities struct {
NetworkIsolationModes []NetworkIsolationSpec_NetworkIsolationMode `protobuf:"varint,4,rep,packed,name=network_isolation_modes,json=networkIsolationModes,proto3,enum=hashicorp.nomad.plugins.drivers.proto.NetworkIsolationSpec_NetworkIsolationMode" json:"network_isolation_modes,omitempty"`
MustCreateNetwork bool `protobuf:"varint,5,opt,name=must_create_network,json=mustCreateNetwork,proto3" json:"must_create_network,omitempty"`
// MountConfigs indicates whether the driver supports mount configurations.
MountConfigs DriverCapabilities_MountConfigs `protobuf:"varint,6,opt,name=mount_configs,json=mountConfigs,proto3,enum=hashicorp.nomad.plugins.drivers.proto.DriverCapabilities_MountConfigs" json:"mount_configs,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
MountConfigs DriverCapabilities_MountConfigs `protobuf:"varint,6,opt,name=mount_configs,json=mountConfigs,proto3,enum=hashicorp.nomad.plugins.drivers.proto.DriverCapabilities_MountConfigs" json:"mount_configs,omitempty"`
// remote_tasks indicates whether the driver executes tasks remotely such
// on cloud runtimes like AWS ECS.
RemoteTasks bool `protobuf:"varint,7,opt,name=remote_tasks,json=remoteTasks,proto3" json:"remote_tasks,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *DriverCapabilities) Reset() { *m = DriverCapabilities{} }
@@ -1909,6 +1912,13 @@ func (m *DriverCapabilities) GetMountConfigs() DriverCapabilities_MountConfigs {
return DriverCapabilities_UNKNOWN_MOUNTS
}
func (m *DriverCapabilities) GetRemoteTasks() bool {
if m != nil {
return m.RemoteTasks
}
return false
}
type NetworkIsolationSpec struct {
Mode NetworkIsolationSpec_NetworkIsolationMode `protobuf:"varint,1,opt,name=mode,proto3,enum=hashicorp.nomad.plugins.drivers.proto.NetworkIsolationSpec_NetworkIsolationMode" json:"mode,omitempty"`
Path string `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"`
@@ -3607,239 +3617,240 @@ func init() {
}
var fileDescriptor_4a8f45747846a74d = []byte{
// 3707 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x5a, 0x4f, 0x6f, 0x1b, 0x49,
0x76, 0x57, 0xf3, 0x9f, 0xc8, 0x47, 0x8a, 0x6a, 0x95, 0x65, 0x0f, 0xcd, 0x49, 0x32, 0xde, 0x0e,
0x26, 0x10, 0x76, 0x67, 0xe8, 0x19, 0x2d, 0x32, 0x1e, 0xcf, 0x7a, 0xd6, 0xc3, 0xa1, 0x68, 0x4b,
0x63, 0x89, 0x52, 0x8a, 0x14, 0xbc, 0x8e, 0xb3, 0xd3, 0x69, 0x75, 0x97, 0xc9, 0xb6, 0xd8, 0x7f,
0xdc, 0x5d, 0x94, 0xa5, 0x0d, 0x82, 0x04, 0x1b, 0x20, 0xd8, 0x00, 0x09, 0x92, 0xcb, 0x64, 0x2f,
0x39, 0x2d, 0x90, 0x53, 0xbe, 0x40, 0xb0, 0xc1, 0x9e, 0xf3, 0x05, 0x72, 0xcc, 0x25, 0xb7, 0x5c,
0x02, 0x24, 0xdf, 0x20, 0xa8, 0x3f, 0xdd, 0xec, 0x26, 0xe9, 0x75, 0x93, 0xf2, 0xa9, 0xfb, 0xbd,
0xaa, 0xfa, 0xd5, 0xeb, 0xf7, 0x5e, 0xd5, 0x7b, 0x55, 0xfd, 0x40, 0xf3, 0xc7, 0x93, 0xa1, 0xed,
0x86, 0x77, 0xad, 0xc0, 0xbe, 0x20, 0x41, 0x78, 0xd7, 0x0f, 0x3c, 0xea, 0x49, 0xaa, 0xc5, 0x09,
0xf4, 0xe1, 0xc8, 0x08, 0x47, 0xb6, 0xe9, 0x05, 0x7e, 0xcb, 0xf5, 0x1c, 0xc3, 0x6a, 0xc9, 0x31,
0x2d, 0x39, 0x46, 0x74, 0x6b, 0xfe, 0xde, 0xd0, 0xf3, 0x86, 0x63, 0x22, 0x10, 0xce, 0x26, 0x2f,
0xee, 0x5a, 0x93, 0xc0, 0xa0, 0xb6, 0xe7, 0xca, 0xf6, 0x0f, 0x66, 0xdb, 0xa9, 0xed, 0x90, 0x90,
0x1a, 0x8e, 0x2f, 0x3b, 0x7c, 0x18, 0xc9, 0x12, 0x8e, 0x8c, 0x80, 0x58, 0x77, 0x47, 0xe6, 0x38,
0xf4, 0x89, 0xc9, 0x9e, 0x3a, 0x7b, 0x91, 0xdd, 0x3e, 0x9a, 0xe9, 0x16, 0xd2, 0x60, 0x62, 0xd2,
0x48, 0x72, 0x83, 0xd2, 0xc0, 0x3e, 0x9b, 0x50, 0x22, 0x7a, 0x6b, 0xb7, 0xe1, 0xbd, 0x81, 0x11,
0x9e, 0x77, 0x3c, 0xf7, 0x85, 0x3d, 0xec, 0x9b, 0x23, 0xe2, 0x18, 0x98, 0xbc, 0x9a, 0x90, 0x90,
0x6a, 0x7f, 0x02, 0x8d, 0xf9, 0xa6, 0xd0, 0xf7, 0xdc, 0x90, 0xa0, 0xaf, 0xa0, 0xc0, 0xa6, 0x6c,
0x28, 0x77, 0x94, 0x9d, 0xea, 0xee, 0x47, 0xad, 0x37, 0xa9, 0x40, 0xc8, 0xd0, 0x92, 0xa2, 0xb6,
0xfa, 0x3e, 0x31, 0x31, 0x1f, 0xa9, 0xdd, 0x84, 0x1b, 0x1d, 0xc3, 0x37, 0xce, 0xec, 0xb1, 0x4d,
0x6d, 0x12, 0x46, 0x93, 0x4e, 0x60, 0x3b, 0xcd, 0x96, 0x13, 0xfe, 0x14, 0x6a, 0x66, 0x82, 0x2f,
0x27, 0xbe, 0xdf, 0xca, 0xa4, 0xfb, 0xd6, 0x1e, 0xa7, 0x52, 0xc0, 0x29, 0x38, 0x6d, 0x1b, 0xd0,
0x23, 0xdb, 0x1d, 0x92, 0xc0, 0x0f, 0x6c, 0x97, 0x46, 0xc2, 0xfc, 0x26, 0x0f, 0x37, 0x52, 0x6c,
0x29, 0xcc, 0x4b, 0x80, 0x58, 0x8f, 0x4c, 0x94, 0xfc, 0x4e, 0x75, 0xf7, 0x9b, 0x8c, 0xa2, 0x2c,
0xc0, 0x6b, 0xb5, 0x63, 0xb0, 0xae, 0x4b, 0x83, 0x2b, 0x9c, 0x40, 0x47, 0xdf, 0x42, 0x69, 0x44,
0x8c, 0x31, 0x1d, 0x35, 0x72, 0x77, 0x94, 0x9d, 0xfa, 0xee, 0xa3, 0x6b, 0xcc, 0xb3, 0xcf, 0x81,
0xfa, 0xd4, 0xa0, 0x04, 0x4b, 0x54, 0xf4, 0x31, 0x20, 0xf1, 0xa6, 0x5b, 0x24, 0x34, 0x03, 0xdb,
0x67, 0x2e, 0xd9, 0xc8, 0xdf, 0x51, 0x76, 0x2a, 0x78, 0x4b, 0xb4, 0xec, 0x4d, 0x1b, 0x9a, 0x3e,
0x6c, 0xce, 0x48, 0x8b, 0x54, 0xc8, 0x9f, 0x93, 0x2b, 0x6e, 0x91, 0x0a, 0x66, 0xaf, 0xe8, 0x31,
0x14, 0x2f, 0x8c, 0xf1, 0x84, 0x70, 0x91, 0xab, 0xbb, 0x9f, 0xbe, 0xcd, 0x3d, 0xa4, 0x8b, 0x4e,
0xf5, 0x80, 0xc5, 0xf8, 0x2f, 0x72, 0x9f, 0x2b, 0xda, 0x7d, 0xa8, 0x26, 0xe4, 0x46, 0x75, 0x80,
0xd3, 0xde, 0x5e, 0x77, 0xd0, 0xed, 0x0c, 0xba, 0x7b, 0xea, 0x1a, 0xda, 0x80, 0xca, 0x69, 0x6f,
0xbf, 0xdb, 0x3e, 0x1c, 0xec, 0x3f, 0x53, 0x15, 0x54, 0x85, 0xf5, 0x88, 0xc8, 0x69, 0x97, 0x80,
0x30, 0x31, 0xbd, 0x0b, 0x12, 0x30, 0x47, 0x96, 0x56, 0x45, 0xef, 0xc1, 0x3a, 0x35, 0xc2, 0x73,
0xdd, 0xb6, 0xa4, 0xcc, 0x25, 0x46, 0x1e, 0x58, 0xe8, 0x00, 0x4a, 0x23, 0xc3, 0xb5, 0xc6, 0x6f,
0x97, 0x3b, 0xad, 0x6a, 0x06, 0xbe, 0xcf, 0x07, 0x62, 0x09, 0xc0, 0xbc, 0x3b, 0x35, 0xb3, 0x30,
0x80, 0xf6, 0x0c, 0xd4, 0x3e, 0x35, 0x02, 0x9a, 0x14, 0xa7, 0x0b, 0x05, 0x36, 0xbf, 0xf4, 0xe8,
0x65, 0xe6, 0x14, 0x2b, 0x13, 0xf3, 0xe1, 0xda, 0xff, 0xe5, 0x60, 0x2b, 0x81, 0x2d, 0x3d, 0xf5,
0x29, 0x94, 0x02, 0x12, 0x4e, 0xc6, 0x94, 0xc3, 0xd7, 0x77, 0x1f, 0x66, 0x84, 0x9f, 0x43, 0x6a,
0x61, 0x0e, 0x83, 0x25, 0x1c, 0xda, 0x01, 0x55, 0x8c, 0xd0, 0x49, 0x10, 0x78, 0x81, 0xee, 0x84,
0x43, 0xae, 0xb5, 0x0a, 0xae, 0x0b, 0x7e, 0x97, 0xb1, 0x8f, 0xc2, 0x61, 0x42, 0xab, 0xf9, 0x6b,
0x6a, 0x15, 0x19, 0xa0, 0xba, 0x84, 0xbe, 0xf6, 0x82, 0x73, 0x9d, 0xa9, 0x36, 0xb0, 0x2d, 0xd2,
0x28, 0x70, 0xd0, 0xcf, 0x32, 0x82, 0xf6, 0xc4, 0xf0, 0x63, 0x39, 0x1a, 0x6f, 0xba, 0x69, 0x86,
0xf6, 0x03, 0x28, 0x89, 0x2f, 0x65, 0x9e, 0xd4, 0x3f, 0xed, 0x74, 0xba, 0xfd, 0xbe, 0xba, 0x86,
0x2a, 0x50, 0xc4, 0xdd, 0x01, 0x66, 0x1e, 0x56, 0x81, 0xe2, 0xa3, 0xf6, 0xa0, 0x7d, 0xa8, 0xe6,
0xb4, 0xef, 0xc3, 0xe6, 0x53, 0xc3, 0xa6, 0x59, 0x9c, 0x4b, 0xf3, 0x40, 0x9d, 0xf6, 0x95, 0xd6,
0x39, 0x48, 0x59, 0x27, 0xbb, 0x6a, 0xba, 0x97, 0x36, 0x9d, 0xb1, 0x87, 0x0a, 0x79, 0x12, 0x04,
0xd2, 0x04, 0xec, 0x55, 0x7b, 0x0d, 0x9b, 0x7d, 0xea, 0xf9, 0x99, 0x3c, 0xff, 0x87, 0xb0, 0xce,
0xa2, 0x8d, 0x37, 0xa1, 0xd2, 0xf5, 0x6f, 0xb7, 0x44, 0x34, 0x6a, 0x45, 0xd1, 0xa8, 0xb5, 0x27,
0xa3, 0x15, 0x8e, 0x7a, 0xa2, 0x5b, 0x50, 0x0a, 0xed, 0xa1, 0x6b, 0x8c, 0xe5, 0x6e, 0x21, 0x29,
0x0d, 0x31, 0x27, 0x8f, 0x26, 0x96, 0x8e, 0xdf, 0x01, 0xb4, 0x47, 0x42, 0x1a, 0x78, 0x57, 0x99,
0xe4, 0xd9, 0x86, 0xe2, 0x0b, 0x2f, 0x30, 0xc5, 0x42, 0x2c, 0x63, 0x41, 0xb0, 0x45, 0x95, 0x02,
0x91, 0xd8, 0x1f, 0x03, 0x3a, 0x70, 0x59, 0x4c, 0xc9, 0x66, 0x88, 0x7f, 0xc8, 0xc1, 0x8d, 0x54,
0x7f, 0x69, 0x8c, 0xd5, 0xd7, 0x21, 0xdb, 0x98, 0x26, 0xa1, 0x58, 0x87, 0xe8, 0x18, 0x4a, 0xa2,
0x87, 0xd4, 0xe4, 0xbd, 0x25, 0x80, 0x44, 0x98, 0x92, 0x70, 0x12, 0x66, 0xa1, 0xd3, 0xe7, 0xdf,
0xad, 0xd3, 0xbf, 0x06, 0x35, 0xfa, 0x8e, 0xf0, 0xad, 0xb6, 0xf9, 0x06, 0x6e, 0x98, 0xde, 0x78,
0x4c, 0x4c, 0xe6, 0x0d, 0xba, 0xed, 0x52, 0x12, 0x5c, 0x18, 0xe3, 0xb7, 0xfb, 0x0d, 0x9a, 0x8e,
0x3a, 0x90, 0x83, 0xb4, 0xe7, 0xb0, 0x95, 0x98, 0x58, 0x1a, 0xe2, 0x11, 0x14, 0x43, 0xc6, 0x90,
0x96, 0xf8, 0x64, 0x49, 0x4b, 0x84, 0x58, 0x0c, 0xd7, 0x6e, 0x08, 0xf0, 0xee, 0x05, 0x71, 0xe3,
0xcf, 0xd2, 0xf6, 0x60, 0xab, 0xcf, 0xdd, 0x34, 0x93, 0x1f, 0x4e, 0x5d, 0x3c, 0x97, 0x72, 0xf1,
0x6d, 0x40, 0x49, 0x14, 0xe9, 0x88, 0x57, 0xb0, 0xd9, 0xbd, 0x24, 0x66, 0x26, 0xe4, 0x06, 0xac,
0x9b, 0x9e, 0xe3, 0x18, 0xae, 0xd5, 0xc8, 0xdd, 0xc9, 0xef, 0x54, 0x70, 0x44, 0x26, 0xd7, 0x62,
0x3e, 0xeb, 0x5a, 0xd4, 0xfe, 0x4e, 0x01, 0x75, 0x3a, 0xb7, 0x54, 0x24, 0x93, 0x9e, 0x5a, 0x0c,
0x88, 0xcd, 0x5d, 0xc3, 0x92, 0x92, 0xfc, 0x68, 0xbb, 0x10, 0x7c, 0x12, 0x04, 0x89, 0xed, 0x28,
0x7f, 0xcd, 0xed, 0x48, 0xdb, 0x87, 0xdf, 0x89, 0xc4, 0xe9, 0xd3, 0x80, 0x18, 0x8e, 0xed, 0x0e,
0x0f, 0x8e, 0x8f, 0x7d, 0x22, 0x04, 0x47, 0x08, 0x0a, 0x96, 0x41, 0x0d, 0x29, 0x18, 0x7f, 0x67,
0x8b, 0xde, 0x1c, 0x7b, 0x61, 0xbc, 0xe8, 0x39, 0xa1, 0xfd, 0x7b, 0x1e, 0x1a, 0x73, 0x50, 0x91,
0x7a, 0x9f, 0x43, 0x31, 0x24, 0x74, 0xe2, 0x4b, 0x57, 0xe9, 0x66, 0x16, 0x78, 0x31, 0x5e, 0xab,
0xcf, 0xc0, 0xb0, 0xc0, 0x44, 0x43, 0x28, 0x53, 0x7a, 0xa5, 0x87, 0xf6, 0xcf, 0xa2, 0x84, 0xe0,
0xf0, 0xba, 0xf8, 0x03, 0x12, 0x38, 0xb6, 0x6b, 0x8c, 0xfb, 0xf6, 0xcf, 0x08, 0x5e, 0xa7, 0xf4,
0x8a, 0xbd, 0xa0, 0x67, 0xcc, 0xe1, 0x2d, 0xdb, 0x95, 0x6a, 0xef, 0xac, 0x3a, 0x4b, 0x42, 0xc1,
0x58, 0x20, 0x36, 0x0f, 0xa1, 0xc8, 0xbf, 0x69, 0x15, 0x47, 0x54, 0x21, 0x4f, 0xe9, 0x15, 0x17,
0xaa, 0x8c, 0xd9, 0x6b, 0xf3, 0x01, 0xd4, 0x92, 0x5f, 0xc0, 0x1c, 0x69, 0x44, 0xec, 0xe1, 0x48,
0x38, 0x58, 0x11, 0x4b, 0x8a, 0x59, 0xf2, 0xb5, 0x6d, 0xc9, 0x94, 0xb5, 0x88, 0x05, 0xa1, 0xfd,
0x6b, 0x0e, 0x6e, 0x2f, 0xd0, 0x8c, 0x74, 0xd6, 0xe7, 0x29, 0x67, 0x7d, 0x47, 0x5a, 0x88, 0x3c,
0xfe, 0x79, 0xca, 0xe3, 0xdf, 0x21, 0x38, 0x5b, 0x36, 0xb7, 0xa0, 0x44, 0x2e, 0x6d, 0x4a, 0x2c,
0xa9, 0x2a, 0x49, 0x25, 0x96, 0x53, 0xe1, 0xba, 0xcb, 0xe9, 0x53, 0xd8, 0xee, 0x04, 0xc4, 0xa0,
0x44, 0x6e, 0xe5, 0x91, 0xff, 0xdf, 0x86, 0xb2, 0x31, 0x1e, 0x7b, 0xe6, 0xd4, 0xac, 0xeb, 0x9c,
0x3e, 0xb0, 0xb4, 0xef, 0x14, 0xb8, 0x39, 0x33, 0x46, 0x6a, 0xfa, 0x0c, 0xea, 0x76, 0xe8, 0x8d,
0xf9, 0x47, 0xe8, 0x89, 0x53, 0xdc, 0x8f, 0x96, 0x0b, 0x27, 0x07, 0x11, 0x06, 0x3f, 0xd4, 0x6d,
0xd8, 0x49, 0x92, 0x7b, 0x15, 0x9f, 0xdc, 0x92, 0xab, 0x39, 0x22, 0xb5, 0x7f, 0x54, 0xe0, 0xa6,
0x8c, 0xe2, 0x99, 0x3f, 0x66, 0x81, 0xc8, 0xb9, 0x77, 0x2d, 0xb2, 0xd6, 0x80, 0x5b, 0xb3, 0x72,
0xc9, 0x7d, 0xfd, 0x3f, 0x0a, 0x80, 0xe6, 0x4f, 0x90, 0xe8, 0x7b, 0x50, 0x0b, 0x89, 0x6b, 0xe9,
0x22, 0x26, 0x88, 0x70, 0x55, 0xc6, 0x55, 0xc6, 0x13, 0xc1, 0x21, 0x64, 0xdb, 0x1c, 0xb9, 0x94,
0xd2, 0x96, 0x31, 0x7f, 0x47, 0x23, 0xa8, 0xbd, 0x08, 0xf5, 0x78, 0x6e, 0xee, 0x34, 0xf5, 0xcc,
0x5b, 0xd7, 0xbc, 0x1c, 0xad, 0x47, 0xfd, 0xf8, 0xbb, 0x70, 0xf5, 0x45, 0x18, 0x13, 0xe8, 0x17,
0x0a, 0xbc, 0x17, 0xa5, 0x0e, 0x53, 0xf5, 0x39, 0x9e, 0x45, 0xc2, 0x46, 0xe1, 0x4e, 0x7e, 0xa7,
0xbe, 0x7b, 0x72, 0x0d, 0xfd, 0xcd, 0x31, 0x8f, 0x3c, 0x8b, 0xe0, 0x9b, 0xee, 0x02, 0x6e, 0x88,
0x5a, 0x70, 0xc3, 0x99, 0x84, 0x54, 0x17, 0x5e, 0xa0, 0xcb, 0x4e, 0x8d, 0x22, 0xd7, 0xcb, 0x16,
0x6b, 0x4a, 0xf9, 0x2a, 0x3a, 0x87, 0x0d, 0xc7, 0x9b, 0xb8, 0x54, 0x37, 0xf9, 0x19, 0x27, 0x6c,
0x94, 0x96, 0x3a, 0xfc, 0x2e, 0xd0, 0xd2, 0x11, 0x83, 0x13, 0x27, 0xa6, 0x10, 0xd7, 0x9c, 0x04,
0xa5, 0xb5, 0xa0, 0x9a, 0xd0, 0x21, 0x2a, 0x43, 0xa1, 0x77, 0xdc, 0xeb, 0xaa, 0x6b, 0x08, 0xa0,
0xd4, 0xd9, 0xc7, 0xc7, 0xc7, 0x03, 0x91, 0xf6, 0x1f, 0x1c, 0xb5, 0x1f, 0x77, 0xd5, 0x9c, 0xd6,
0x85, 0x5a, 0x12, 0x0d, 0x21, 0xa8, 0x9f, 0xf6, 0x9e, 0xf4, 0x8e, 0x9f, 0xf6, 0xf4, 0xa3, 0xe3,
0xd3, 0xde, 0x80, 0x1d, 0x18, 0xea, 0x00, 0xed, 0xde, 0xb3, 0x29, 0xbd, 0x01, 0x95, 0xde, 0x71,
0x44, 0x2a, 0xcd, 0x9c, 0xaa, 0x68, 0xff, 0x93, 0x83, 0xed, 0x45, 0x8a, 0x45, 0x16, 0x14, 0x98,
0x91, 0xe4, 0x91, 0xed, 0xdd, 0xdb, 0x88, 0xa3, 0x33, 0xdf, 0xf4, 0x0d, 0xb9, 0x47, 0x57, 0x30,
0x7f, 0x47, 0x3a, 0x94, 0xc6, 0xc6, 0x19, 0x19, 0x87, 0x8d, 0x3c, 0xbf, 0xd4, 0x78, 0x7c, 0x9d,
0xb9, 0x0f, 0x39, 0x92, 0xb8, 0xd1, 0x90, 0xb0, 0xcd, 0xfb, 0x50, 0x4d, 0xb0, 0x17, 0x5c, 0x1d,
0x6c, 0x27, 0xaf, 0x0e, 0x2a, 0xc9, 0x7b, 0x80, 0x87, 0xf3, 0xda, 0x62, 0x5f, 0xc3, 0xcc, 0xb5,
0x7f, 0xdc, 0x1f, 0x88, 0x43, 0xda, 0x63, 0x7c, 0x7c, 0x7a, 0xa2, 0x2a, 0x8c, 0x39, 0x68, 0xf7,
0x9f, 0xa8, 0xb9, 0xd8, 0x9a, 0x79, 0xed, 0x39, 0x54, 0xf6, 0x7a, 0x7d, 0x61, 0x34, 0xb6, 0x41,
0x85, 0x24, 0x60, 0x9f, 0xc0, 0xef, 0x6f, 0x2a, 0x38, 0x22, 0x51, 0x13, 0xca, 0x21, 0x31, 0x02,
0x73, 0x44, 0x42, 0x19, 0x11, 0x63, 0x9a, 0x8d, 0xf2, 0xf8, 0x3d, 0x88, 0x50, 0x50, 0x05, 0x47,
0xa4, 0xf6, 0xbf, 0xeb, 0x00, 0xd3, 0x33, 0x39, 0xaa, 0x43, 0x2e, 0xde, 0xc5, 0x72, 0xb6, 0xc5,
0x94, 0xed, 0x1a, 0x4e, 0xf4, 0x55, 0xfc, 0x1d, 0xed, 0xc2, 0x4d, 0x27, 0x1c, 0xfa, 0x86, 0x79,
0xae, 0xcb, 0xa3, 0xb4, 0x70, 0x76, 0xbe, 0x23, 0xd4, 0xf0, 0x0d, 0xd9, 0x28, 0x7d, 0x59, 0xe0,
0x1e, 0x42, 0x9e, 0xb8, 0x17, 0x7c, 0xf5, 0x56, 0x77, 0xbf, 0x58, 0xfa, 0xae, 0xa0, 0xd5, 0x75,
0x2f, 0x84, 0x41, 0x18, 0x0c, 0xd2, 0x01, 0x2c, 0x72, 0x61, 0x9b, 0x44, 0x67, 0xa0, 0x45, 0x0e,
0xfa, 0xd5, 0xf2, 0xa0, 0x7b, 0x1c, 0x23, 0x86, 0xae, 0x58, 0x11, 0x8d, 0x7a, 0x50, 0x09, 0x48,
0xe8, 0x4d, 0x02, 0x93, 0x88, 0x25, 0x9c, 0x3d, 0x9d, 0xc7, 0xd1, 0x38, 0x3c, 0x85, 0x40, 0x7b,
0x50, 0xe2, 0x2b, 0x37, 0x6c, 0xac, 0x73, 0x61, 0x3f, 0xca, 0x08, 0xc6, 0x97, 0x2b, 0x96, 0x63,
0xd1, 0x63, 0x58, 0x17, 0x22, 0x86, 0x8d, 0x32, 0x87, 0xf9, 0x38, 0xeb, 0xb6, 0xc2, 0x47, 0xe1,
0x68, 0x34, 0xb3, 0xea, 0x24, 0x24, 0x41, 0xa3, 0x22, 0xac, 0xca, 0xde, 0xd1, 0xfb, 0x50, 0x11,
0x51, 0xcc, 0xb2, 0x83, 0x06, 0xf0, 0x06, 0x11, 0xd6, 0xf6, 0xec, 0x00, 0x7d, 0x00, 0x55, 0x91,
0x91, 0xe8, 0x7c, 0xe9, 0x55, 0x79, 0x33, 0x08, 0xd6, 0x09, 0x5b, 0x80, 0xa2, 0x03, 0x09, 0x02,
0xd1, 0xa1, 0x16, 0x77, 0x20, 0x41, 0xc0, 0x3b, 0xfc, 0x01, 0x6c, 0xf2, 0x3c, 0x6e, 0x18, 0x78,
0x13, 0x5f, 0xe7, 0x3e, 0xb5, 0xc1, 0x3b, 0x6d, 0x30, 0xf6, 0x63, 0xc6, 0xed, 0x31, 0xe7, 0xba,
0x0d, 0xe5, 0x97, 0xde, 0x99, 0xe8, 0x50, 0x17, 0xc1, 0xf4, 0xa5, 0x77, 0x16, 0x35, 0xc5, 0x71,
0x76, 0x33, 0x1d, 0x67, 0x5f, 0xc1, 0xad, 0xf9, 0x80, 0xc1, 0xe3, 0xad, 0x7a, 0xfd, 0x78, 0xbb,
0xed, 0x2e, 0xda, 0xec, 0xbe, 0x86, 0xbc, 0xe5, 0x86, 0x8d, 0xad, 0xa5, 0x9c, 0x23, 0x5e, 0xc7,
0x98, 0x0d, 0x6e, 0x7e, 0x06, 0xe5, 0xc8, 0xfb, 0x96, 0xd9, 0x52, 0x9a, 0x0f, 0xa0, 0x9e, 0xf6,
0xdd, 0xa5, 0x36, 0xa4, 0x7f, 0xce, 0x41, 0x25, 0xf6, 0x52, 0xe4, 0xc2, 0x0d, 0xae, 0x45, 0x96,
0xe4, 0xe8, 0x53, 0xa7, 0x17, 0xa9, 0xd5, 0x97, 0x19, 0xbf, 0xab, 0x1d, 0x21, 0xc8, 0x73, 0x9c,
0x5c, 0x01, 0x28, 0x46, 0x9e, 0xce, 0xf7, 0x2d, 0x6c, 0x8e, 0x6d, 0x77, 0x72, 0x99, 0x98, 0x4b,
0xe4, 0x44, 0x7f, 0x98, 0x71, 0xae, 0x43, 0x36, 0x7a, 0x3a, 0x47, 0x7d, 0x9c, 0xa2, 0xd1, 0x3e,
0x14, 0x7d, 0x2f, 0xa0, 0x51, 0x24, 0xd8, 0xcd, 0x88, 0x7a, 0xe2, 0x05, 0xf4, 0xc8, 0xf0, 0x7d,
0x96, 0xda, 0x0b, 0x00, 0xed, 0xbb, 0x1c, 0xdc, 0x5a, 0xfc, 0x61, 0xa8, 0x07, 0x79, 0xd3, 0x9f,
0x48, 0x25, 0x3d, 0x58, 0x56, 0x49, 0x1d, 0x7f, 0x32, 0x95, 0x9f, 0x01, 0xa1, 0xa7, 0x50, 0x72,
0x88, 0xe3, 0x05, 0x57, 0x52, 0x17, 0x0f, 0x97, 0x85, 0x3c, 0xe2, 0xa3, 0xa7, 0xa8, 0x12, 0x0e,
0x61, 0x28, 0x4b, 0xef, 0x0d, 0xe5, 0x3e, 0xb9, 0xe4, 0xe5, 0x4b, 0x04, 0x89, 0x63, 0x1c, 0xed,
0x33, 0xb8, 0xb9, 0xf0, 0x53, 0xd0, 0xef, 0x02, 0x98, 0xfe, 0x44, 0xe7, 0x97, 0xe3, 0xc2, 0x83,
0xf2, 0xb8, 0x62, 0xfa, 0x93, 0x3e, 0x67, 0x68, 0xcf, 0xa1, 0xf1, 0x26, 0x79, 0xd9, 0xee, 0x23,
0x24, 0xd6, 0x9d, 0x33, 0xae, 0x83, 0x3c, 0x2e, 0x0b, 0xc6, 0xd1, 0x19, 0xd2, 0x60, 0x23, 0x6a,
0x34, 0x2e, 0x59, 0x87, 0x3c, 0xef, 0x50, 0x95, 0x1d, 0x8c, 0xcb, 0xa3, 0x33, 0xed, 0x97, 0x39,
0xd8, 0x9c, 0x11, 0x99, 0x1d, 0x70, 0xc4, 0x8e, 0x17, 0x1d, 0x1d, 0x05, 0xc5, 0xb6, 0x3f, 0xd3,
0xb6, 0xa2, 0x4b, 0x47, 0xfe, 0xce, 0x03, 0x9f, 0x2f, 0x2f, 0x04, 0x73, 0xb6, 0xcf, 0x96, 0x8f,
0x73, 0x66, 0xd3, 0x90, 0x9f, 0x81, 0x8a, 0x58, 0x10, 0xe8, 0x19, 0xd4, 0x03, 0xc2, 0x03, 0xae,
0xa5, 0x0b, 0x2f, 0x2b, 0x2e, 0xe5, 0x65, 0x52, 0x42, 0xe6, 0x6c, 0x78, 0x23, 0x42, 0x62, 0x54,
0x88, 0x9e, 0xc2, 0x86, 0x75, 0xe5, 0x1a, 0x8e, 0x6d, 0x4a, 0xe4, 0xd2, 0xca, 0xc8, 0x35, 0x09,
0xc4, 0x81, 0xb5, 0xfb, 0x50, 0x4d, 0x34, 0xb2, 0x0f, 0xe3, 0x39, 0x8d, 0xd4, 0x89, 0x20, 0xd2,
0xbb, 0x45, 0x51, 0xee, 0x16, 0xda, 0x19, 0x54, 0x13, 0xeb, 0x62, 0x99, 0xa1, 0x4c, 0x9f, 0xd4,
0xe3, 0xfa, 0x2c, 0xe2, 0x1c, 0xf5, 0xd8, 0x39, 0x7e, 0xe4, 0x85, 0x54, 0xb7, 0x7d, 0xae, 0xd1,
0x0a, 0x2e, 0x31, 0xf2, 0xc0, 0xd7, 0x7e, 0x9d, 0x83, 0x7a, 0x7a, 0x49, 0x47, 0x7e, 0xe4, 0x93,
0xc0, 0xf6, 0xac, 0x84, 0x1f, 0x9d, 0x70, 0x06, 0xf3, 0x15, 0xd6, 0xfc, 0x6a, 0xe2, 0x51, 0x23,
0xf2, 0x15, 0xd3, 0x9f, 0xfc, 0x11, 0xa3, 0x67, 0x7c, 0x30, 0x3f, 0xe3, 0x83, 0xe8, 0x23, 0x40,
0xd2, 0x95, 0xc6, 0xb6, 0x63, 0x53, 0xfd, 0xec, 0x8a, 0x12, 0x61, 0xe3, 0x3c, 0x56, 0x45, 0xcb,
0x21, 0x6b, 0xf8, 0x9a, 0xf1, 0x99, 0xe3, 0x79, 0x9e, 0xa3, 0x87, 0xa6, 0x17, 0x10, 0xdd, 0xb0,
0x5e, 0xf2, 0xbc, 0x3f, 0x8f, 0xab, 0x9e, 0xe7, 0xf4, 0x19, 0xaf, 0x6d, 0xbd, 0x64, 0x91, 0xcf,
0xf4, 0x27, 0x21, 0xa1, 0x3a, 0x7b, 0xf0, 0x64, 0xa1, 0x82, 0x41, 0xb0, 0x3a, 0xfe, 0x24, 0x44,
0xbf, 0x0f, 0x1b, 0x51, 0x07, 0x1e, 0xfc, 0x64, 0xd4, 0xad, 0xc9, 0x2e, 0x9c, 0x87, 0x34, 0xa8,
0x9d, 0x90, 0xc0, 0x24, 0x2e, 0x1d, 0xd8, 0xe6, 0x39, 0x8b, 0xef, 0xca, 0x8e, 0x82, 0x53, 0xbc,
0x6f, 0x0a, 0xe5, 0x75, 0xb5, 0x8c, 0xa3, 0xd9, 0x1c, 0xe2, 0x84, 0xda, 0x4f, 0xa1, 0xc8, 0x53,
0x04, 0xa6, 0x13, 0x1e, 0x5e, 0x79, 0xf4, 0x15, 0xe6, 0x29, 0x33, 0x06, 0x8f, 0xbd, 0xef, 0x43,
0x85, 0xeb, 0x3e, 0x91, 0x36, 0x97, 0x19, 0x83, 0x37, 0x36, 0xa1, 0x1c, 0x10, 0xc3, 0xf2, 0xdc,
0x71, 0x74, 0x65, 0x12, 0xd3, 0xda, 0x2b, 0x28, 0x89, 0x38, 0x73, 0x0d, 0xfc, 0x8f, 0x01, 0x89,
0xef, 0x66, 0xf6, 0x74, 0xec, 0x30, 0x94, 0x59, 0x28, 0xff, 0x4f, 0x27, 0x5a, 0x4e, 0xa6, 0x0d,
0xda, 0x7f, 0x2a, 0x22, 0x1f, 0x15, 0x7f, 0x50, 0x58, 0xe2, 0xca, 0x9c, 0x9c, 0x9d, 0x37, 0xc5,
0x55, 0x4d, 0x44, 0xa2, 0x03, 0x28, 0xc9, 0xb4, 0x33, 0xb7, 0xea, 0x0f, 0x28, 0x09, 0x10, 0x5d,
0xdc, 0x12, 0x79, 0xa4, 0x5d, 0xf6, 0xe2, 0x96, 0x88, 0x8b, 0x5b, 0xc2, 0x0e, 0xd6, 0x32, 0x21,
0x16, 0x70, 0x05, 0x9e, 0x0f, 0x57, 0xad, 0xf8, 0x76, 0x9c, 0x68, 0xff, 0xad, 0xc4, 0xdb, 0x54,
0x74, 0x8b, 0x8d, 0xbe, 0x85, 0x32, 0x5b, 0xf1, 0xba, 0x63, 0xf8, 0xf2, 0x9f, 0x6c, 0x67, 0xb5,
0x0b, 0xf2, 0x28, 0x88, 0x89, 0x74, 0x76, 0xdd, 0x17, 0x14, 0xdb, 0xee, 0x0c, 0x6b, 0xba, 0xdd,
0xb1, 0x77, 0xf4, 0x21, 0xd4, 0x8d, 0x09, 0xf5, 0x74, 0xc3, 0xba, 0x20, 0x01, 0xb5, 0x43, 0x22,
0x6d, 0xbf, 0xc1, 0xb8, 0xed, 0x88, 0xd9, 0xfc, 0x02, 0x6a, 0x49, 0xcc, 0xb7, 0xa5, 0x19, 0xc5,
0x64, 0x9a, 0xf1, 0xa7, 0x00, 0xd3, 0x1b, 0x21, 0xe6, 0x23, 0xe4, 0xd2, 0x66, 0xe7, 0x62, 0x79,
0x40, 0x2c, 0xe2, 0x32, 0x63, 0x74, 0xd8, 0x51, 0x28, 0x7d, 0x5d, 0x5d, 0x8c, 0xae, 0xab, 0xd9,
0x62, 0x66, 0xeb, 0xef, 0xdc, 0x1e, 0x8f, 0xe3, 0x5b, 0xaa, 0x8a, 0xe7, 0x39, 0x4f, 0x38, 0x43,
0xfb, 0x4d, 0x4e, 0xf8, 0x8a, 0xf8, 0xf1, 0x90, 0xe9, 0xec, 0xf2, 0xae, 0x4c, 0x7d, 0x1f, 0x20,
0xa4, 0x46, 0xc0, 0x72, 0x26, 0x23, 0xba, 0x27, 0x6b, 0xce, 0xdd, 0x77, 0x0f, 0xa2, 0x4a, 0x08,
0x5c, 0x91, 0xbd, 0xdb, 0x14, 0x7d, 0x09, 0x35, 0xd3, 0x73, 0xfc, 0x31, 0x91, 0x83, 0x8b, 0x6f,
0x1d, 0x5c, 0x8d, 0xfb, 0xb7, 0x69, 0xe2, 0x76, 0xae, 0x74, 0xdd, 0xdb, 0xb9, 0x5f, 0x2b, 0xe2,
0xff, 0x49, 0xf2, 0xf7, 0x0d, 0x1a, 0x2e, 0xa8, 0x11, 0x78, 0xbc, 0xe2, 0xbf, 0xa0, 0xdf, 0x56,
0x20, 0xd0, 0xfc, 0x32, 0xcb, 0x1f, 0xf9, 0x37, 0x67, 0xb1, 0xff, 0x96, 0x87, 0x4a, 0xfc, 0xeb,
0x64, 0xce, 0xf6, 0x9f, 0x43, 0x25, 0x2e, 0x43, 0x91, 0x1b, 0xc4, 0x6f, 0x35, 0x4f, 0xdc, 0x19,
0xbd, 0x00, 0x64, 0x0c, 0x87, 0x71, 0x76, 0xaa, 0x4f, 0x42, 0x63, 0x18, 0xfd, 0xb8, 0xfa, 0x7c,
0x09, 0x3d, 0x44, 0xe1, 0xec, 0x94, 0x8d, 0xc7, 0xaa, 0x31, 0x1c, 0xa6, 0x38, 0xe8, 0xcf, 0xe0,
0x66, 0x7a, 0x0e, 0xfd, 0xec, 0x4a, 0xf7, 0x6d, 0x4b, 0x9e, 0x91, 0xf7, 0x97, 0xfd, 0x7b, 0xd4,
0x4a, 0xc1, 0x7f, 0x7d, 0x75, 0x62, 0x5b, 0x42, 0xe7, 0x28, 0x98, 0x6b, 0x68, 0xfe, 0x05, 0xbc,
0xf7, 0x86, 0xee, 0x0b, 0x6c, 0xd0, 0x4b, 0x57, 0x45, 0xac, 0xae, 0x84, 0x84, 0xf5, 0x7e, 0xa5,
0x88, 0x9f, 0x5c, 0x69, 0x9d, 0xb4, 0x93, 0x69, 0xf5, 0xdd, 0x8c, 0xf3, 0x74, 0x4e, 0x4e, 0x05,
0x3c, 0xcf, 0xa4, 0xbf, 0x99, 0xc9, 0xa4, 0xb3, 0xe6, 0x4f, 0x22, 0x21, 0x15, 0x40, 0x12, 0x41,
0xfb, 0x97, 0x3c, 0x94, 0x23, 0x74, 0x7e, 0xc2, 0xbd, 0x0a, 0x29, 0x71, 0xf4, 0xf8, 0x8e, 0x4b,
0xc1, 0x20, 0x58, 0xfc, 0x3e, 0xe7, 0x7d, 0xa8, 0xb0, 0x83, 0xb4, 0x68, 0xce, 0xf1, 0xe6, 0x32,
0x63, 0xf0, 0xc6, 0x0f, 0xa0, 0x4a, 0x3d, 0x6a, 0x8c, 0x75, 0xca, 0xc3, 0x7b, 0x5e, 0x8c, 0xe6,
0x2c, 0x1e, 0xdc, 0xd1, 0x0f, 0x60, 0x8b, 0x8e, 0x02, 0x8f, 0xd2, 0x31, 0x4b, 0x2d, 0x79, 0xa2,
0x23, 0xf2, 0x92, 0x02, 0x56, 0xe3, 0x06, 0x91, 0x00, 0x85, 0x6c, 0xf7, 0x9e, 0x76, 0x66, 0xae,
0xcb, 0x37, 0x91, 0x02, 0xde, 0x88, 0xb9, 0xcc, 0xb5, 0x59, 0xf0, 0xf4, 0x45, 0x02, 0xc1, 0xf7,
0x0a, 0x05, 0x47, 0x24, 0xd2, 0x61, 0xd3, 0x21, 0x46, 0x38, 0x09, 0x88, 0xa5, 0xbf, 0xb0, 0xc9,
0xd8, 0x12, 0x17, 0x13, 0xf5, 0xcc, 0xa7, 0x83, 0x48, 0x2d, 0xad, 0x47, 0x7c, 0x34, 0xae, 0x47,
0x70, 0x82, 0x66, 0x99, 0x83, 0x78, 0x43, 0x9b, 0x50, 0xed, 0x3f, 0xeb, 0x0f, 0xba, 0x47, 0xfa,
0xd1, 0xf1, 0x5e, 0x57, 0x16, 0xbe, 0xf4, 0xbb, 0x58, 0x90, 0x0a, 0x6b, 0x1f, 0x1c, 0x0f, 0xda,
0x87, 0xfa, 0xe0, 0xa0, 0xf3, 0xa4, 0xaf, 0xe6, 0xd0, 0x4d, 0xd8, 0x1a, 0xec, 0xe3, 0xe3, 0xc1,
0xe0, 0xb0, 0xbb, 0xa7, 0x9f, 0x74, 0xf1, 0xc1, 0xf1, 0x5e, 0x5f, 0xcd, 0x23, 0x04, 0xf5, 0x29,
0x7b, 0x70, 0x70, 0xd4, 0x55, 0x0b, 0xa8, 0x0a, 0xeb, 0x27, 0x5d, 0xdc, 0xe9, 0xf6, 0x06, 0x6a,
0x51, 0xfb, 0x65, 0x1e, 0xaa, 0x09, 0x2b, 0x32, 0x47, 0x0e, 0x42, 0x71, 0x0c, 0x29, 0x60, 0xf6,
0xca, 0x7f, 0xd4, 0x19, 0xe6, 0x48, 0x58, 0xa7, 0x80, 0x05, 0xc1, 0x8f, 0x1e, 0xc6, 0x65, 0x62,
0x9d, 0x17, 0x70, 0xd9, 0x31, 0x2e, 0x05, 0xc8, 0xf7, 0xa0, 0x76, 0x4e, 0x02, 0x97, 0x8c, 0x65,
0xbb, 0xb0, 0x48, 0x55, 0xf0, 0x44, 0x97, 0x1d, 0x50, 0x65, 0x97, 0x29, 0x8c, 0x30, 0x47, 0x5d,
0xf0, 0x8f, 0x22, 0xb0, 0x6d, 0x28, 0x8a, 0xe6, 0x75, 0x31, 0x3f, 0x27, 0x58, 0x98, 0x0a, 0x5f,
0x1b, 0x3e, 0x4f, 0xf9, 0x0a, 0x98, 0xbf, 0xa3, 0xb3, 0x79, 0xfb, 0x94, 0xb8, 0x7d, 0xee, 0x2f,
0xef, 0xce, 0x6f, 0x32, 0xd1, 0x28, 0x36, 0xd1, 0x3a, 0xe4, 0x71, 0x54, 0x2d, 0xd2, 0x69, 0x77,
0xf6, 0x99, 0x59, 0x36, 0xa0, 0x72, 0xd4, 0xfe, 0x89, 0x7e, 0xda, 0xe7, 0x57, 0xc7, 0x48, 0x85,
0xda, 0x93, 0x2e, 0xee, 0x75, 0x0f, 0x25, 0x27, 0x8f, 0xb6, 0x41, 0x95, 0x9c, 0x69, 0xbf, 0x02,
0x43, 0x10, 0xaf, 0x45, 0x54, 0x86, 0x42, 0xff, 0x69, 0xfb, 0x44, 0x2d, 0x69, 0xff, 0x95, 0x83,
0x4d, 0x11, 0x16, 0xe2, 0xff, 0xda, 0x6f, 0xfe, 0xaf, 0x97, 0xbc, 0xe5, 0xc9, 0xa5, 0x6f, 0x79,
0xa2, 0x24, 0x94, 0x47, 0xf5, 0xfc, 0x34, 0x09, 0xe5, 0xb7, 0x43, 0xa9, 0x1d, 0xbf, 0xb0, 0xcc,
0x8e, 0xdf, 0x80, 0x75, 0x87, 0x84, 0xb1, 0xdd, 0x2a, 0x38, 0x22, 0x91, 0x0d, 0x55, 0xc3, 0x75,
0x3d, 0x6a, 0x88, 0xab, 0xd3, 0xd2, 0x52, 0xc1, 0x70, 0xe6, 0x8b, 0x5b, 0xed, 0x29, 0x92, 0xd8,
0x98, 0x93, 0xd8, 0xcd, 0x1f, 0x83, 0x3a, 0xdb, 0x61, 0x99, 0x70, 0xf8, 0xfd, 0x4f, 0xa7, 0xd1,
0x90, 0xb0, 0x75, 0x21, 0x2f, 0xf6, 0xd5, 0x35, 0x46, 0xe0, 0xd3, 0x5e, 0xef, 0xa0, 0xf7, 0x58,
0x55, 0x10, 0x40, 0xa9, 0xfb, 0x93, 0x83, 0x41, 0x77, 0x4f, 0xcd, 0xed, 0xfe, 0x6a, 0x0b, 0x4a,
0x42, 0x48, 0xf4, 0x9d, 0xcc, 0x04, 0x92, 0x35, 0x93, 0xe8, 0xc7, 0x4b, 0x67, 0xd4, 0xa9, 0x3a,
0xcc, 0xe6, 0xc3, 0x95, 0xc7, 0xcb, 0xff, 0x57, 0x6b, 0xe8, 0x6f, 0x14, 0xa8, 0xa5, 0xfe, 0x5d,
0x65, 0xbd, 0x3a, 0x5e, 0x50, 0xa2, 0xd9, 0xfc, 0xd1, 0x4a, 0x63, 0x63, 0x59, 0x7e, 0xa1, 0x40,
0x35, 0x51, 0x9c, 0x88, 0xee, 0xaf, 0x52, 0xd0, 0x28, 0x24, 0xf9, 0x62, 0xf5, 0x5a, 0x48, 0x6d,
0xed, 0x13, 0x05, 0xfd, 0xb5, 0x02, 0xd5, 0x44, 0x99, 0x5e, 0x66, 0x51, 0xe6, 0x8b, 0x0a, 0x33,
0x8b, 0xb2, 0xa8, 0x2a, 0x70, 0x0d, 0xfd, 0xa5, 0x02, 0x95, 0xb8, 0xe4, 0x0e, 0xdd, 0x5b, 0xbe,
0x48, 0x4f, 0x08, 0xf1, 0xf9, 0xaa, 0xd5, 0x7d, 0xda, 0x1a, 0xfa, 0x73, 0x28, 0x47, 0xf5, 0x69,
0x28, 0x6b, 0xf4, 0x9a, 0x29, 0x7e, 0x6b, 0xde, 0x5b, 0x7a, 0x5c, 0x72, 0xfa, 0xa8, 0x68, 0x2c,
0xf3, 0xf4, 0x33, 0xe5, 0x6d, 0xcd, 0x7b, 0x4b, 0x8f, 0x8b, 0xa7, 0x67, 0x9e, 0x90, 0xa8, 0x2d,
0xcb, 0xec, 0x09, 0xf3, 0x45, 0x6d, 0x99, 0x3d, 0x61, 0x51, 0x29, 0x9b, 0x10, 0x24, 0x51, 0x9d,
0x96, 0x59, 0x90, 0xf9, 0x0a, 0xb8, 0xcc, 0x82, 0x2c, 0x28, 0x86, 0xd3, 0xd6, 0xd0, 0xcf, 0x95,
0xe4, 0xb9, 0xe0, 0xde, 0xd2, 0x45, 0x58, 0x4b, 0xba, 0xe4, 0x5c, 0x19, 0x18, 0x5f, 0xa0, 0x3f,
0x97, 0xb7, 0x18, 0xa2, 0x86, 0x0b, 0x2d, 0x03, 0x96, 0x2a, 0xfb, 0x6a, 0x7e, 0xb6, 0x5a, 0xb0,
0xe1, 0x42, 0xfc, 0x95, 0x02, 0x30, 0xad, 0xf6, 0xca, 0x2c, 0xc4, 0x5c, 0x99, 0x59, 0xf3, 0xfe,
0x0a, 0x23, 0x93, 0x0b, 0x24, 0xaa, 0x46, 0xc9, 0xbc, 0x40, 0x66, 0xaa, 0xd1, 0x32, 0x2f, 0x90,
0xd9, 0x4a, 0x32, 0x6d, 0x0d, 0xfd, 0x93, 0x02, 0x5b, 0x73, 0xd5, 0x30, 0xe8, 0xe1, 0x35, 0x0b,
0xa2, 0x9a, 0x5f, 0xad, 0x0e, 0x10, 0x89, 0xb6, 0xa3, 0x7c, 0xa2, 0xa0, 0xbf, 0x55, 0x60, 0x23,
0x5d, 0x41, 0x90, 0x39, 0x4a, 0x2d, 0xa8, 0xab, 0x69, 0x3e, 0x58, 0x6d, 0x70, 0xac, 0xad, 0xbf,
0x57, 0xa0, 0x9e, 0x2e, 0x26, 0x41, 0x0f, 0x96, 0xdb, 0x16, 0x66, 0x04, 0xfa, 0x72, 0xc5, 0xd1,
0x91, 0x44, 0x5f, 0xaf, 0xff, 0x71, 0x51, 0x64, 0x6f, 0x25, 0xfe, 0xf8, 0xe1, 0xff, 0x07, 0x00,
0x00, 0xff, 0xff, 0x47, 0xb7, 0xdf, 0xf9, 0xda, 0x32, 0x00, 0x00,
// 3726 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x5a, 0xcd, 0x6f, 0x1b, 0x49,
0x76, 0x57, 0xf3, 0x4b, 0xe4, 0xa3, 0x44, 0xb5, 0xca, 0xb2, 0x87, 0xe6, 0x24, 0x19, 0x6f, 0x07,
0x13, 0x08, 0xbb, 0x33, 0xf4, 0x8c, 0x16, 0x19, 0x8f, 0x67, 0x3d, 0xeb, 0xe1, 0x50, 0xb4, 0xa5,
0xb1, 0x44, 0x29, 0x45, 0x0a, 0x5e, 0xc7, 0xd9, 0xe9, 0xb4, 0xd8, 0x65, 0xaa, 0x2d, 0xf6, 0xc7,
0x74, 0x15, 0x65, 0x69, 0x83, 0x20, 0xc1, 0x06, 0x08, 0x36, 0x40, 0x82, 0xe4, 0x32, 0xd9, 0x4b,
0x4e, 0x0b, 0xe4, 0x94, 0x7f, 0x20, 0xd8, 0x60, 0xce, 0xf9, 0x27, 0x72, 0xc9, 0x2d, 0x97, 0x00,
0xc9, 0x29, 0xd7, 0xa0, 0x3e, 0xba, 0xd9, 0x4d, 0xd2, 0xe3, 0x26, 0xe5, 0x53, 0xf7, 0x7b, 0x55,
0xf5, 0xab, 0xd7, 0xef, 0xbd, 0xaa, 0xf7, 0xaa, 0xfa, 0x81, 0x11, 0x8c, 0xc6, 0x43, 0xc7, 0xa3,
0x77, 0xed, 0xd0, 0xb9, 0x20, 0x21, 0xbd, 0x1b, 0x84, 0x3e, 0xf3, 0x15, 0xd5, 0x14, 0x04, 0x7a,
0xff, 0xcc, 0xa2, 0x67, 0xce, 0xc0, 0x0f, 0x83, 0xa6, 0xe7, 0xbb, 0x96, 0xdd, 0x54, 0x63, 0x9a,
0x6a, 0x8c, 0xec, 0xd6, 0xf8, 0xbd, 0xa1, 0xef, 0x0f, 0x47, 0x44, 0x22, 0x9c, 0x8e, 0x5f, 0xdc,
0xb5, 0xc7, 0xa1, 0xc5, 0x1c, 0xdf, 0x53, 0xed, 0xef, 0x4d, 0xb7, 0x33, 0xc7, 0x25, 0x94, 0x59,
0x6e, 0xa0, 0x3a, 0xbc, 0x1f, 0xc9, 0x42, 0xcf, 0xac, 0x90, 0xd8, 0x77, 0xcf, 0x06, 0x23, 0x1a,
0x90, 0x01, 0x7f, 0x9a, 0xfc, 0x45, 0x75, 0xfb, 0x60, 0xaa, 0x1b, 0x65, 0xe1, 0x78, 0xc0, 0x22,
0xc9, 0x2d, 0xc6, 0x42, 0xe7, 0x74, 0xcc, 0x88, 0xec, 0x6d, 0xdc, 0x86, 0x77, 0xfa, 0x16, 0x3d,
0x6f, 0xfb, 0xde, 0x0b, 0x67, 0xd8, 0x1b, 0x9c, 0x11, 0xd7, 0xc2, 0xe4, 0x9b, 0x31, 0xa1, 0xcc,
0xf8, 0x13, 0xa8, 0xcf, 0x36, 0xd1, 0xc0, 0xf7, 0x28, 0x41, 0x5f, 0x40, 0x81, 0x4f, 0x59, 0xd7,
0xee, 0x68, 0xdb, 0xd5, 0x9d, 0x0f, 0x9a, 0xaf, 0x53, 0x81, 0x94, 0xa1, 0xa9, 0x44, 0x6d, 0xf6,
0x02, 0x32, 0xc0, 0x62, 0xa4, 0x71, 0x13, 0x6e, 0xb4, 0xad, 0xc0, 0x3a, 0x75, 0x46, 0x0e, 0x73,
0x08, 0x8d, 0x26, 0x1d, 0xc3, 0x56, 0x9a, 0xad, 0x26, 0xfc, 0x39, 0xac, 0x0d, 0x12, 0x7c, 0x35,
0xf1, 0xfd, 0x66, 0x26, 0xdd, 0x37, 0x77, 0x05, 0x95, 0x02, 0x4e, 0xc1, 0x19, 0x5b, 0x80, 0x1e,
0x39, 0xde, 0x90, 0x84, 0x41, 0xe8, 0x78, 0x2c, 0x12, 0xe6, 0xbb, 0x3c, 0xdc, 0x48, 0xb1, 0x95,
0x30, 0x2f, 0x01, 0x62, 0x3d, 0x72, 0x51, 0xf2, 0xdb, 0xd5, 0x9d, 0xaf, 0x32, 0x8a, 0x32, 0x07,
0xaf, 0xd9, 0x8a, 0xc1, 0x3a, 0x1e, 0x0b, 0xaf, 0x70, 0x02, 0x1d, 0x7d, 0x0d, 0xa5, 0x33, 0x62,
0x8d, 0xd8, 0x59, 0x3d, 0x77, 0x47, 0xdb, 0xae, 0xed, 0x3c, 0xba, 0xc6, 0x3c, 0x7b, 0x02, 0xa8,
0xc7, 0x2c, 0x46, 0xb0, 0x42, 0x45, 0x1f, 0x02, 0x92, 0x6f, 0xa6, 0x4d, 0xe8, 0x20, 0x74, 0x02,
0xee, 0x92, 0xf5, 0xfc, 0x1d, 0x6d, 0xbb, 0x82, 0x37, 0x65, 0xcb, 0xee, 0xa4, 0xa1, 0x11, 0xc0,
0xc6, 0x94, 0xb4, 0x48, 0x87, 0xfc, 0x39, 0xb9, 0x12, 0x16, 0xa9, 0x60, 0xfe, 0x8a, 0x1e, 0x43,
0xf1, 0xc2, 0x1a, 0x8d, 0x89, 0x10, 0xb9, 0xba, 0xf3, 0xf1, 0x9b, 0xdc, 0x43, 0xb9, 0xe8, 0x44,
0x0f, 0x58, 0x8e, 0xff, 0x2c, 0xf7, 0xa9, 0x66, 0xdc, 0x87, 0x6a, 0x42, 0x6e, 0x54, 0x03, 0x38,
0xe9, 0xee, 0x76, 0xfa, 0x9d, 0x76, 0xbf, 0xb3, 0xab, 0xaf, 0xa0, 0x75, 0xa8, 0x9c, 0x74, 0xf7,
0x3a, 0xad, 0x83, 0xfe, 0xde, 0x33, 0x5d, 0x43, 0x55, 0x58, 0x8d, 0x88, 0x9c, 0x71, 0x09, 0x08,
0x93, 0x81, 0x7f, 0x41, 0x42, 0xee, 0xc8, 0xca, 0xaa, 0xe8, 0x1d, 0x58, 0x65, 0x16, 0x3d, 0x37,
0x1d, 0x5b, 0xc9, 0x5c, 0xe2, 0xe4, 0xbe, 0x8d, 0xf6, 0xa1, 0x74, 0x66, 0x79, 0xf6, 0xe8, 0xcd,
0x72, 0xa7, 0x55, 0xcd, 0xc1, 0xf7, 0xc4, 0x40, 0xac, 0x00, 0xb8, 0x77, 0xa7, 0x66, 0x96, 0x06,
0x30, 0x9e, 0x81, 0xde, 0x63, 0x56, 0xc8, 0x92, 0xe2, 0x74, 0xa0, 0xc0, 0xe7, 0x57, 0x1e, 0xbd,
0xc8, 0x9c, 0x72, 0x65, 0x62, 0x31, 0xdc, 0xf8, 0xdf, 0x1c, 0x6c, 0x26, 0xb0, 0x95, 0xa7, 0x3e,
0x85, 0x52, 0x48, 0xe8, 0x78, 0xc4, 0x04, 0x7c, 0x6d, 0xe7, 0x61, 0x46, 0xf8, 0x19, 0xa4, 0x26,
0x16, 0x30, 0x58, 0xc1, 0xa1, 0x6d, 0xd0, 0xe5, 0x08, 0x93, 0x84, 0xa1, 0x1f, 0x9a, 0x2e, 0x1d,
0x0a, 0xad, 0x55, 0x70, 0x4d, 0xf2, 0x3b, 0x9c, 0x7d, 0x48, 0x87, 0x09, 0xad, 0xe6, 0xaf, 0xa9,
0x55, 0x64, 0x81, 0xee, 0x11, 0xf6, 0xca, 0x0f, 0xcf, 0x4d, 0xae, 0xda, 0xd0, 0xb1, 0x49, 0xbd,
0x20, 0x40, 0x3f, 0xc9, 0x08, 0xda, 0x95, 0xc3, 0x8f, 0xd4, 0x68, 0xbc, 0xe1, 0xa5, 0x19, 0xc6,
0x8f, 0xa0, 0x24, 0xbf, 0x94, 0x7b, 0x52, 0xef, 0xa4, 0xdd, 0xee, 0xf4, 0x7a, 0xfa, 0x0a, 0xaa,
0x40, 0x11, 0x77, 0xfa, 0x98, 0x7b, 0x58, 0x05, 0x8a, 0x8f, 0x5a, 0xfd, 0xd6, 0x81, 0x9e, 0x33,
0x7e, 0x08, 0x1b, 0x4f, 0x2d, 0x87, 0x65, 0x71, 0x2e, 0xc3, 0x07, 0x7d, 0xd2, 0x57, 0x59, 0x67,
0x3f, 0x65, 0x9d, 0xec, 0xaa, 0xe9, 0x5c, 0x3a, 0x6c, 0xca, 0x1e, 0x3a, 0xe4, 0x49, 0x18, 0x2a,
0x13, 0xf0, 0x57, 0xe3, 0x15, 0x6c, 0xf4, 0x98, 0x1f, 0x64, 0xf2, 0xfc, 0x1f, 0xc3, 0x2a, 0x8f,
0x36, 0xfe, 0x98, 0x29, 0xd7, 0xbf, 0xdd, 0x94, 0xd1, 0xa8, 0x19, 0x45, 0xa3, 0xe6, 0xae, 0x8a,
0x56, 0x38, 0xea, 0x89, 0x6e, 0x41, 0x89, 0x3a, 0x43, 0xcf, 0x1a, 0xa9, 0xdd, 0x42, 0x51, 0x06,
0xe2, 0x4e, 0x1e, 0x4d, 0xac, 0x1c, 0xbf, 0x0d, 0x68, 0x97, 0x50, 0x16, 0xfa, 0x57, 0x99, 0xe4,
0xd9, 0x82, 0xe2, 0x0b, 0x3f, 0x1c, 0xc8, 0x85, 0x58, 0xc6, 0x92, 0xe0, 0x8b, 0x2a, 0x05, 0xa2,
0xb0, 0x3f, 0x04, 0xb4, 0xef, 0xf1, 0x98, 0x92, 0xcd, 0x10, 0xff, 0x90, 0x83, 0x1b, 0xa9, 0xfe,
0xca, 0x18, 0xcb, 0xaf, 0x43, 0xbe, 0x31, 0x8d, 0xa9, 0x5c, 0x87, 0xe8, 0x08, 0x4a, 0xb2, 0x87,
0xd2, 0xe4, 0xbd, 0x05, 0x80, 0x64, 0x98, 0x52, 0x70, 0x0a, 0x66, 0xae, 0xd3, 0xe7, 0xdf, 0xae,
0xd3, 0xbf, 0x02, 0x3d, 0xfa, 0x0e, 0xfa, 0x46, 0xdb, 0x7c, 0x05, 0x37, 0x06, 0xfe, 0x68, 0x44,
0x06, 0xdc, 0x1b, 0x4c, 0xc7, 0x63, 0x24, 0xbc, 0xb0, 0x46, 0x6f, 0xf6, 0x1b, 0x34, 0x19, 0xb5,
0xaf, 0x06, 0x19, 0xcf, 0x61, 0x33, 0x31, 0xb1, 0x32, 0xc4, 0x23, 0x28, 0x52, 0xce, 0x50, 0x96,
0xf8, 0x68, 0x41, 0x4b, 0x50, 0x2c, 0x87, 0x1b, 0x37, 0x24, 0x78, 0xe7, 0x82, 0x78, 0xf1, 0x67,
0x19, 0xbb, 0xb0, 0xd9, 0x13, 0x6e, 0x9a, 0xc9, 0x0f, 0x27, 0x2e, 0x9e, 0x4b, 0xb9, 0xf8, 0x16,
0xa0, 0x24, 0x8a, 0x72, 0xc4, 0x2b, 0xd8, 0xe8, 0x5c, 0x92, 0x41, 0x26, 0xe4, 0x3a, 0xac, 0x0e,
0x7c, 0xd7, 0xb5, 0x3c, 0xbb, 0x9e, 0xbb, 0x93, 0xdf, 0xae, 0xe0, 0x88, 0x4c, 0xae, 0xc5, 0x7c,
0xd6, 0xb5, 0x68, 0xfc, 0x9d, 0x06, 0xfa, 0x64, 0x6e, 0xa5, 0x48, 0x2e, 0x3d, 0xb3, 0x39, 0x10,
0x9f, 0x7b, 0x0d, 0x2b, 0x4a, 0xf1, 0xa3, 0xed, 0x42, 0xf2, 0x49, 0x18, 0x26, 0xb6, 0xa3, 0xfc,
0x35, 0xb7, 0x23, 0x63, 0x0f, 0x7e, 0x27, 0x12, 0xa7, 0xc7, 0x42, 0x62, 0xb9, 0x8e, 0x37, 0xdc,
0x3f, 0x3a, 0x0a, 0x88, 0x14, 0x1c, 0x21, 0x28, 0xd8, 0x16, 0xb3, 0x94, 0x60, 0xe2, 0x9d, 0x2f,
0xfa, 0xc1, 0xc8, 0xa7, 0xf1, 0xa2, 0x17, 0x84, 0xf1, 0xef, 0x79, 0xa8, 0xcf, 0x40, 0x45, 0xea,
0x7d, 0x0e, 0x45, 0x4a, 0xd8, 0x38, 0x50, 0xae, 0xd2, 0xc9, 0x2c, 0xf0, 0x7c, 0xbc, 0x66, 0x8f,
0x83, 0x61, 0x89, 0x89, 0x86, 0x50, 0x66, 0xec, 0xca, 0xa4, 0xce, 0x2f, 0xa2, 0x84, 0xe0, 0xe0,
0xba, 0xf8, 0x7d, 0x12, 0xba, 0x8e, 0x67, 0x8d, 0x7a, 0xce, 0x2f, 0x08, 0x5e, 0x65, 0xec, 0x8a,
0xbf, 0xa0, 0x67, 0xdc, 0xe1, 0x6d, 0xc7, 0x53, 0x6a, 0x6f, 0x2f, 0x3b, 0x4b, 0x42, 0xc1, 0x58,
0x22, 0x36, 0x0e, 0xa0, 0x28, 0xbe, 0x69, 0x19, 0x47, 0xd4, 0x21, 0xcf, 0xd8, 0x95, 0x10, 0xaa,
0x8c, 0xf9, 0x6b, 0xe3, 0x01, 0xac, 0x25, 0xbf, 0x80, 0x3b, 0xd2, 0x19, 0x71, 0x86, 0x67, 0xd2,
0xc1, 0x8a, 0x58, 0x51, 0xdc, 0x92, 0xaf, 0x1c, 0x5b, 0xa5, 0xac, 0x45, 0x2c, 0x09, 0xe3, 0x5f,
0x73, 0x70, 0x7b, 0x8e, 0x66, 0x94, 0xb3, 0x3e, 0x4f, 0x39, 0xeb, 0x5b, 0xd2, 0x42, 0xe4, 0xf1,
0xcf, 0x53, 0x1e, 0xff, 0x16, 0xc1, 0xf9, 0xb2, 0xb9, 0x05, 0x25, 0x72, 0xe9, 0x30, 0x62, 0x2b,
0x55, 0x29, 0x2a, 0xb1, 0x9c, 0x0a, 0xd7, 0x5d, 0x4e, 0x1f, 0xc3, 0x56, 0x3b, 0x24, 0x16, 0x23,
0x6a, 0x2b, 0x8f, 0xfc, 0xff, 0x36, 0x94, 0xad, 0xd1, 0xc8, 0x1f, 0x4c, 0xcc, 0xba, 0x2a, 0xe8,
0x7d, 0xdb, 0xf8, 0x56, 0x83, 0x9b, 0x53, 0x63, 0x94, 0xa6, 0x4f, 0xa1, 0xe6, 0x50, 0x7f, 0x24,
0x3e, 0xc2, 0x4c, 0x9c, 0xe2, 0x7e, 0xb2, 0x58, 0x38, 0xd9, 0x8f, 0x30, 0xc4, 0xa1, 0x6e, 0xdd,
0x49, 0x92, 0xc2, 0xab, 0xc4, 0xe4, 0xb6, 0x5a, 0xcd, 0x11, 0x69, 0xfc, 0xa3, 0x06, 0x37, 0x55,
0x14, 0xcf, 0xfc, 0x31, 0x73, 0x44, 0xce, 0xbd, 0x6d, 0x91, 0x8d, 0x3a, 0xdc, 0x9a, 0x96, 0x4b,
0xed, 0xeb, 0xff, 0x57, 0x00, 0x34, 0x7b, 0x82, 0x44, 0x3f, 0x80, 0x35, 0x4a, 0x3c, 0xdb, 0x94,
0x31, 0x41, 0x86, 0xab, 0x32, 0xae, 0x72, 0x9e, 0x0c, 0x0e, 0x94, 0x6f, 0x73, 0xe4, 0x52, 0x49,
0x5b, 0xc6, 0xe2, 0x1d, 0x9d, 0xc1, 0xda, 0x0b, 0x6a, 0xc6, 0x73, 0x0b, 0xa7, 0xa9, 0x65, 0xde,
0xba, 0x66, 0xe5, 0x68, 0x3e, 0xea, 0xc5, 0xdf, 0x85, 0xab, 0x2f, 0x68, 0x4c, 0xa0, 0x5f, 0x69,
0xf0, 0x4e, 0x94, 0x3a, 0x4c, 0xd4, 0xe7, 0xfa, 0x36, 0xa1, 0xf5, 0xc2, 0x9d, 0xfc, 0x76, 0x6d,
0xe7, 0xf8, 0x1a, 0xfa, 0x9b, 0x61, 0x1e, 0xfa, 0x36, 0xc1, 0x37, 0xbd, 0x39, 0x5c, 0x8a, 0x9a,
0x70, 0xc3, 0x1d, 0x53, 0x66, 0x4a, 0x2f, 0x30, 0x55, 0xa7, 0x7a, 0x51, 0xe8, 0x65, 0x93, 0x37,
0xa5, 0x7c, 0x15, 0x9d, 0xc3, 0xba, 0xeb, 0x8f, 0x3d, 0x66, 0x0e, 0xc4, 0x19, 0x87, 0xd6, 0x4b,
0x0b, 0x1d, 0x7e, 0xe7, 0x68, 0xe9, 0x90, 0xc3, 0xc9, 0x13, 0x13, 0xc5, 0x6b, 0x6e, 0x82, 0xe2,
0x86, 0x0c, 0x89, 0xeb, 0x33, 0x62, 0xf2, 0x3d, 0x91, 0xd6, 0x57, 0xa5, 0x21, 0x25, 0x8f, 0x2f,
0x7f, 0x6a, 0x34, 0xa1, 0x9a, 0x50, 0x33, 0x2a, 0x43, 0xa1, 0x7b, 0xd4, 0xed, 0xe8, 0x2b, 0x08,
0xa0, 0xd4, 0xde, 0xc3, 0x47, 0x47, 0x7d, 0x79, 0x32, 0xd8, 0x3f, 0x6c, 0x3d, 0xee, 0xe8, 0x39,
0xa3, 0x03, 0x6b, 0xc9, 0x09, 0x11, 0x82, 0xda, 0x49, 0xf7, 0x49, 0xf7, 0xe8, 0x69, 0xd7, 0x3c,
0x3c, 0x3a, 0xe9, 0xf6, 0xf9, 0x99, 0xa2, 0x06, 0xd0, 0xea, 0x3e, 0x9b, 0xd0, 0xeb, 0x50, 0xe9,
0x1e, 0x45, 0xa4, 0xd6, 0xc8, 0xe9, 0x9a, 0xf1, 0xdf, 0x39, 0xd8, 0x9a, 0xa7, 0x7b, 0x64, 0x43,
0x81, 0xdb, 0x51, 0x9d, 0xea, 0xde, 0xbe, 0x19, 0x05, 0x3a, 0x77, 0xdf, 0xc0, 0x52, 0xdb, 0x78,
0x05, 0x8b, 0x77, 0x64, 0x42, 0x69, 0x64, 0x9d, 0x92, 0x11, 0xad, 0xe7, 0xc5, 0xbd, 0xc7, 0xe3,
0xeb, 0xcc, 0x7d, 0x20, 0x90, 0xe4, 0xa5, 0x87, 0x82, 0x6d, 0xdc, 0x87, 0x6a, 0x82, 0x3d, 0xe7,
0x76, 0x61, 0x2b, 0x79, 0xbb, 0x50, 0x49, 0x5e, 0x15, 0x3c, 0x9c, 0xd5, 0x16, 0xff, 0x1a, 0x6e,
0xae, 0xbd, 0xa3, 0x5e, 0x5f, 0x9e, 0xe3, 0x1e, 0xe3, 0xa3, 0x93, 0x63, 0x5d, 0xe3, 0xcc, 0x7e,
0xab, 0xf7, 0x44, 0xcf, 0xc5, 0xd6, 0xcc, 0x1b, 0xcf, 0xa1, 0xb2, 0xdb, 0xed, 0x49, 0xa3, 0xf1,
0x3d, 0x8c, 0x92, 0x90, 0x7f, 0x82, 0xb8, 0xe2, 0xa9, 0xe0, 0x88, 0x44, 0x0d, 0x28, 0x53, 0x62,
0x85, 0x83, 0x33, 0x42, 0x55, 0xd0, 0x8c, 0x69, 0x3e, 0xca, 0x17, 0x57, 0x25, 0x52, 0x41, 0x15,
0x1c, 0x91, 0xc6, 0xff, 0xac, 0x02, 0x4c, 0x8e, 0xed, 0xa8, 0x06, 0xb9, 0x78, 0xa3, 0xcb, 0x39,
0x36, 0x57, 0xb6, 0x67, 0xb9, 0xd1, 0x57, 0x89, 0x77, 0xb4, 0x03, 0x37, 0x5d, 0x3a, 0x0c, 0xac,
0xc1, 0xb9, 0xa9, 0x4e, 0xdb, 0x72, 0x3d, 0x88, 0x4d, 0x63, 0x0d, 0xdf, 0x50, 0x8d, 0xca, 0xdd,
0x25, 0xee, 0x01, 0xe4, 0x89, 0x77, 0x21, 0x16, 0x78, 0x75, 0xe7, 0xb3, 0x85, 0xaf, 0x13, 0x9a,
0x1d, 0xef, 0x42, 0x1a, 0x84, 0xc3, 0x20, 0x13, 0xc0, 0x26, 0x17, 0xce, 0x80, 0x98, 0x1c, 0xb4,
0x28, 0x40, 0xbf, 0x58, 0x1c, 0x74, 0x57, 0x60, 0xc4, 0xd0, 0x15, 0x3b, 0xa2, 0x51, 0x17, 0x2a,
0x21, 0xa1, 0xfe, 0x38, 0x1c, 0x10, 0xb9, 0xca, 0xb3, 0x67, 0xfc, 0x38, 0x1a, 0x87, 0x27, 0x10,
0x68, 0x17, 0x4a, 0x62, 0x71, 0xf3, 0x65, 0x9c, 0xff, 0xde, 0xbb, 0xc9, 0x34, 0x98, 0x58, 0xae,
0x58, 0x8d, 0x45, 0x8f, 0x61, 0x55, 0x8a, 0x48, 0xeb, 0x65, 0x01, 0xf3, 0x61, 0xd6, 0x9d, 0x47,
0x8c, 0xc2, 0xd1, 0x68, 0x6e, 0xd5, 0x31, 0x25, 0x61, 0xbd, 0x22, 0xad, 0xca, 0xdf, 0xd1, 0xbb,
0x50, 0x91, 0x81, 0xce, 0x76, 0xc2, 0x3a, 0x88, 0x06, 0x19, 0xf9, 0x76, 0x9d, 0x10, 0xbd, 0x07,
0x55, 0x99, 0xb4, 0x98, 0x62, 0xe9, 0x55, 0x45, 0x33, 0x48, 0xd6, 0x31, 0x5f, 0x80, 0xb2, 0x03,
0x09, 0x43, 0xd9, 0x61, 0x2d, 0xee, 0x40, 0xc2, 0x50, 0x74, 0xf8, 0x03, 0xd8, 0x10, 0xa9, 0xde,
0x30, 0xf4, 0xc7, 0x81, 0x29, 0x7c, 0x6a, 0x5d, 0x74, 0x5a, 0xe7, 0xec, 0xc7, 0x9c, 0xdb, 0xe5,
0xce, 0x75, 0x1b, 0xca, 0x2f, 0xfd, 0x53, 0xd9, 0xa1, 0x26, 0xe3, 0xed, 0x4b, 0xff, 0x34, 0x6a,
0x8a, 0x43, 0xf1, 0x46, 0x3a, 0x14, 0x7f, 0x03, 0xb7, 0x66, 0x63, 0x8a, 0x08, 0xc9, 0xfa, 0xf5,
0x43, 0xf2, 0x96, 0x37, 0x6f, 0xb3, 0xfb, 0x12, 0xf2, 0xb6, 0x47, 0xeb, 0x9b, 0x0b, 0x39, 0x47,
0xbc, 0x8e, 0x31, 0x1f, 0xdc, 0xf8, 0x04, 0xca, 0x91, 0xf7, 0x2d, 0xb2, 0xa5, 0x34, 0x1e, 0x40,
0x2d, 0xed, 0xbb, 0x0b, 0x6d, 0x48, 0xff, 0x9c, 0x83, 0x4a, 0xec, 0xa5, 0xc8, 0x83, 0x1b, 0x42,
0x8b, 0x3c, 0x0f, 0x32, 0x27, 0x4e, 0x2f, 0xb3, 0xaf, 0xcf, 0x33, 0x7e, 0x57, 0x2b, 0x42, 0x50,
0x47, 0x3d, 0xb5, 0x02, 0x50, 0x8c, 0x3c, 0x99, 0xef, 0x6b, 0xd8, 0x18, 0x39, 0xde, 0xf8, 0x32,
0x31, 0x97, 0x4c, 0x9b, 0xfe, 0x30, 0xe3, 0x5c, 0x07, 0x7c, 0xf4, 0x64, 0x8e, 0xda, 0x28, 0x45,
0xa3, 0x3d, 0x28, 0x06, 0x7e, 0xc8, 0xa2, 0x48, 0xb0, 0x93, 0x11, 0xf5, 0xd8, 0x0f, 0xd9, 0xa1,
0x15, 0x04, 0x3c, 0xfb, 0x97, 0x00, 0xc6, 0xb7, 0x39, 0xb8, 0x35, 0xff, 0xc3, 0x50, 0x17, 0xf2,
0x83, 0x60, 0xac, 0x94, 0xf4, 0x60, 0x51, 0x25, 0xb5, 0x83, 0xf1, 0x44, 0x7e, 0x0e, 0x84, 0x9e,
0x42, 0xc9, 0x25, 0xae, 0x1f, 0x5e, 0x29, 0x5d, 0x3c, 0x5c, 0x14, 0xf2, 0x50, 0x8c, 0x9e, 0xa0,
0x2a, 0x38, 0x84, 0xa1, 0xac, 0xbc, 0x97, 0xaa, 0x7d, 0x72, 0xc1, 0xfb, 0x99, 0x08, 0x12, 0xc7,
0x38, 0xc6, 0x27, 0x70, 0x73, 0xee, 0xa7, 0xa0, 0xdf, 0x05, 0x18, 0x04, 0x63, 0x53, 0xdc, 0x9f,
0x4b, 0x0f, 0xca, 0xe3, 0xca, 0x20, 0x18, 0xf7, 0x04, 0xc3, 0x78, 0x0e, 0xf5, 0xd7, 0xc9, 0xcb,
0x77, 0x1f, 0x29, 0xb1, 0xe9, 0x9e, 0x0a, 0x1d, 0xe4, 0x71, 0x59, 0x32, 0x0e, 0x4f, 0x91, 0x01,
0xeb, 0x51, 0xa3, 0x75, 0xc9, 0x3b, 0xe4, 0x45, 0x87, 0xaa, 0xea, 0x60, 0x5d, 0x1e, 0x9e, 0x1a,
0xbf, 0xce, 0xc1, 0xc6, 0x94, 0xc8, 0xfc, 0x0c, 0x24, 0x77, 0xbc, 0xe8, 0x74, 0x29, 0x29, 0xbe,
0xfd, 0x0d, 0x1c, 0x3b, 0xba, 0x97, 0x14, 0xef, 0x22, 0xf0, 0x05, 0xea, 0xce, 0x30, 0xe7, 0x04,
0x7c, 0xf9, 0xb8, 0xa7, 0x0e, 0xa3, 0xe2, 0x98, 0x54, 0xc4, 0x92, 0x40, 0xcf, 0xa0, 0x16, 0x12,
0x11, 0x70, 0x6d, 0x53, 0x7a, 0x59, 0x71, 0x21, 0x2f, 0x53, 0x12, 0x72, 0x67, 0xc3, 0xeb, 0x11,
0x12, 0xa7, 0x28, 0x7a, 0x0a, 0xeb, 0xf6, 0x95, 0x67, 0xb9, 0xce, 0x40, 0x21, 0x97, 0x96, 0x46,
0x5e, 0x53, 0x40, 0x02, 0xd8, 0xb8, 0x0f, 0xd5, 0x44, 0x23, 0xff, 0x30, 0x91, 0xd3, 0x28, 0x9d,
0x48, 0x22, 0xbd, 0x5b, 0x14, 0xd5, 0x6e, 0x61, 0x9c, 0x42, 0x35, 0xb1, 0x2e, 0x16, 0x19, 0xca,
0xf5, 0xc9, 0x7c, 0xa1, 0xcf, 0x22, 0xce, 0x31, 0x9f, 0x1f, 0xf5, 0xcf, 0x7c, 0xca, 0x4c, 0x27,
0x10, 0x1a, 0xad, 0xe0, 0x12, 0x27, 0xf7, 0x03, 0xe3, 0xb7, 0x39, 0xa8, 0xa5, 0x97, 0x74, 0xe4,
0x47, 0x01, 0x09, 0x1d, 0xdf, 0x4e, 0xf8, 0xd1, 0xb1, 0x60, 0x70, 0x5f, 0xe1, 0xcd, 0xdf, 0x8c,
0x7d, 0x66, 0x45, 0xbe, 0x32, 0x08, 0xc6, 0x7f, 0xc4, 0xe9, 0x29, 0x1f, 0xcc, 0x4f, 0xf9, 0x20,
0xfa, 0x00, 0x90, 0x72, 0xa5, 0x91, 0xe3, 0x3a, 0xcc, 0x3c, 0xbd, 0x62, 0x44, 0xda, 0x38, 0x8f,
0x75, 0xd9, 0x72, 0xc0, 0x1b, 0xbe, 0xe4, 0x7c, 0xee, 0x78, 0xbe, 0xef, 0x9a, 0x74, 0xe0, 0x87,
0xc4, 0xb4, 0xec, 0x97, 0xe2, 0x68, 0x90, 0xc7, 0x55, 0xdf, 0x77, 0x7b, 0x9c, 0xd7, 0xb2, 0x5f,
0xf2, 0xc8, 0x37, 0x08, 0xc6, 0x94, 0x30, 0x93, 0x3f, 0x44, 0xb2, 0x50, 0xc1, 0x20, 0x59, 0xed,
0x60, 0x4c, 0xd1, 0xef, 0xc3, 0x7a, 0xd4, 0x41, 0x04, 0x3f, 0x15, 0x75, 0xd7, 0x54, 0x17, 0xc1,
0x43, 0x06, 0xac, 0x1d, 0x93, 0x70, 0x40, 0x3c, 0xd6, 0x77, 0x06, 0xe7, 0x3c, 0xbe, 0x6b, 0xdb,
0x1a, 0x4e, 0xf1, 0xbe, 0x2a, 0x94, 0x57, 0xf5, 0x32, 0x8e, 0x66, 0x73, 0x89, 0x4b, 0x8d, 0x9f,
0x43, 0x51, 0xa4, 0x08, 0x5c, 0x27, 0x22, 0xbc, 0x8a, 0xe8, 0x2b, 0xcd, 0x53, 0xe6, 0x0c, 0x11,
0x7b, 0xdf, 0x85, 0x8a, 0xd0, 0x7d, 0x22, 0x6d, 0x2e, 0x73, 0x86, 0x68, 0x6c, 0x40, 0x39, 0x24,
0x96, 0xed, 0x7b, 0xa3, 0xe8, 0x56, 0x25, 0xa6, 0x8d, 0x6f, 0xa0, 0x24, 0xe3, 0xcc, 0x35, 0xf0,
0x3f, 0x04, 0x24, 0xbf, 0x9b, 0xdb, 0xd3, 0x75, 0x28, 0x55, 0x59, 0xa8, 0xf8, 0x95, 0x27, 0x5b,
0x8e, 0x27, 0x0d, 0xc6, 0x7f, 0x68, 0x32, 0x1f, 0x95, 0x3f, 0x59, 0x78, 0xe2, 0xca, 0x9d, 0x9c,
0x1f, 0x49, 0xe5, 0x6d, 0x4e, 0x44, 0xa2, 0x7d, 0x28, 0xa9, 0xb4, 0x33, 0xb7, 0xec, 0x3f, 0x2a,
0x05, 0x10, 0xdd, 0xed, 0x12, 0x75, 0xea, 0x5d, 0xf4, 0x6e, 0x97, 0xc8, 0xbb, 0x5d, 0xc2, 0x8f,
0x6c, 0x2a, 0x21, 0x96, 0x70, 0x05, 0x91, 0x0f, 0x57, 0xed, 0xf8, 0x02, 0x9d, 0x18, 0xff, 0xa5,
0xc5, 0xdb, 0x54, 0x74, 0xd1, 0x8d, 0xbe, 0x86, 0x32, 0x5f, 0xf1, 0xa6, 0x6b, 0x05, 0xea, 0xb7,
0x6d, 0x7b, 0xb9, 0x3b, 0xf4, 0x28, 0x88, 0xc9, 0x74, 0x76, 0x35, 0x90, 0x14, 0xdf, 0xee, 0x2c,
0x7b, 0xb2, 0xdd, 0xf1, 0x77, 0xf4, 0x3e, 0xd4, 0xac, 0x31, 0xf3, 0x4d, 0xcb, 0xbe, 0x20, 0x21,
0x73, 0x28, 0x51, 0xb6, 0x5f, 0xe7, 0xdc, 0x56, 0xc4, 0x6c, 0x7c, 0x06, 0x6b, 0x49, 0xcc, 0x37,
0xa5, 0x19, 0xc5, 0x64, 0x9a, 0xf1, 0xa7, 0x00, 0x93, 0x4b, 0x23, 0xee, 0x23, 0xe4, 0xd2, 0xe1,
0x47, 0x67, 0x75, 0x40, 0x2c, 0xe2, 0x32, 0x67, 0xb4, 0xf9, 0x51, 0x28, 0x7d, 0xa3, 0x5d, 0x8c,
0x6e, 0xb4, 0xf9, 0x62, 0xe6, 0xeb, 0xef, 0xdc, 0x19, 0x8d, 0xe2, 0x8b, 0xac, 0x8a, 0xef, 0xbb,
0x4f, 0x04, 0xc3, 0xf8, 0x2e, 0x27, 0x7d, 0x45, 0xfe, 0x9b, 0xc8, 0x74, 0x76, 0x79, 0x5b, 0xa6,
0xbe, 0x0f, 0x40, 0x99, 0x15, 0xf2, 0x9c, 0xc9, 0x8a, 0xae, 0xd2, 0x1a, 0x33, 0x57, 0xe2, 0xfd,
0xa8, 0x58, 0x02, 0x57, 0x54, 0xef, 0x16, 0x43, 0x9f, 0xc3, 0xda, 0xc0, 0x77, 0x83, 0x11, 0x51,
0x83, 0x8b, 0x6f, 0x1c, 0x5c, 0x8d, 0xfb, 0xb7, 0x58, 0xe2, 0x02, 0xaf, 0x74, 0xdd, 0x0b, 0xbc,
0xdf, 0x6a, 0xf2, 0x17, 0x4b, 0xf2, 0x0f, 0x0f, 0x1a, 0xce, 0x29, 0x23, 0x78, 0xbc, 0xe4, 0xef,
0xa2, 0xef, 0xab, 0x21, 0x68, 0x7c, 0x9e, 0xe5, 0xa7, 0xfd, 0xeb, 0xb3, 0xd8, 0x7f, 0xcb, 0x43,
0x25, 0xfe, 0xbb, 0x32, 0x63, 0xfb, 0x4f, 0xa1, 0x12, 0x57, 0xaa, 0xa8, 0x0d, 0xe2, 0x7b, 0xcd,
0x13, 0x77, 0x46, 0x2f, 0x00, 0x59, 0xc3, 0x61, 0x9c, 0x9d, 0x9a, 0x63, 0x6a, 0x0d, 0xa3, 0x7f,
0x5b, 0x9f, 0x2e, 0xa0, 0x87, 0x28, 0x9c, 0x9d, 0xf0, 0xf1, 0x58, 0xb7, 0x86, 0xc3, 0x14, 0x07,
0xfd, 0x19, 0xdc, 0x4c, 0xcf, 0x61, 0x9e, 0x5e, 0x99, 0x81, 0x63, 0xab, 0x33, 0xf2, 0xde, 0xa2,
0x3f, 0x98, 0x9a, 0x29, 0xf8, 0x2f, 0xaf, 0x8e, 0x1d, 0x5b, 0xea, 0x1c, 0x85, 0x33, 0x0d, 0x8d,
0xbf, 0x80, 0x77, 0x5e, 0xd3, 0x7d, 0x8e, 0x0d, 0xba, 0xe9, 0xc2, 0x89, 0xe5, 0x95, 0x90, 0xb0,
0xde, 0x6f, 0x34, 0xf9, 0x1f, 0x2c, 0xad, 0x93, 0x56, 0x32, 0xad, 0xbe, 0x9b, 0x71, 0x9e, 0xf6,
0xf1, 0x89, 0x84, 0x17, 0x99, 0xf4, 0x57, 0x53, 0x99, 0x74, 0xd6, 0xfc, 0x49, 0x26, 0xa4, 0x12,
0x48, 0x21, 0x18, 0xff, 0x92, 0x87, 0x72, 0x84, 0x2e, 0x4e, 0xb8, 0x57, 0x94, 0x11, 0xd7, 0x8c,
0xef, 0xb8, 0x34, 0x0c, 0x92, 0x25, 0xee, 0x73, 0xde, 0x85, 0x0a, 0x3f, 0x48, 0xcb, 0xe6, 0x9c,
0x68, 0x2e, 0x73, 0x86, 0x68, 0x7c, 0x0f, 0xaa, 0xcc, 0x67, 0xd6, 0xc8, 0x64, 0x22, 0xbc, 0xe7,
0xe5, 0x68, 0xc1, 0x12, 0xc1, 0x1d, 0xfd, 0x08, 0x36, 0xd9, 0x59, 0xe8, 0x33, 0x36, 0xe2, 0xa9,
0xa5, 0x48, 0x74, 0x64, 0x5e, 0x52, 0xc0, 0x7a, 0xdc, 0x20, 0x13, 0x20, 0xca, 0x77, 0xef, 0x49,
0x67, 0xee, 0xba, 0x62, 0x13, 0x29, 0xe0, 0xf5, 0x98, 0xcb, 0x5d, 0x9b, 0x07, 0xcf, 0x40, 0x26,
0x10, 0x62, 0xaf, 0xd0, 0x70, 0x44, 0x22, 0x13, 0x36, 0x5c, 0x62, 0xd1, 0x71, 0x48, 0x6c, 0xf3,
0x85, 0x43, 0x46, 0xb6, 0xbc, 0x98, 0xa8, 0x65, 0x3e, 0x1d, 0x44, 0x6a, 0x69, 0x3e, 0x12, 0xa3,
0x71, 0x2d, 0x82, 0x93, 0x34, 0xcf, 0x1c, 0xe4, 0x1b, 0xda, 0x80, 0x6a, 0xef, 0x59, 0xaf, 0xdf,
0x39, 0x34, 0x0f, 0x8f, 0x76, 0x3b, 0xaa, 0x36, 0xa6, 0xd7, 0xc1, 0x92, 0xd4, 0x78, 0x7b, 0xff,
0xa8, 0xdf, 0x3a, 0x30, 0xfb, 0xfb, 0xed, 0x27, 0x3d, 0x3d, 0x87, 0x6e, 0xc2, 0x66, 0x7f, 0x0f,
0x1f, 0xf5, 0xfb, 0x07, 0x9d, 0x5d, 0xf3, 0xb8, 0x83, 0xf7, 0x8f, 0x76, 0x7b, 0x7a, 0x1e, 0x21,
0xa8, 0x4d, 0xd8, 0xfd, 0xfd, 0xc3, 0x8e, 0x5e, 0x40, 0x55, 0x58, 0x3d, 0xee, 0xe0, 0x76, 0xa7,
0xdb, 0xd7, 0x8b, 0xc6, 0xaf, 0xf3, 0x50, 0x4d, 0x58, 0x91, 0x3b, 0x72, 0x48, 0xe5, 0x31, 0xa4,
0x80, 0xf9, 0xab, 0xf8, 0x97, 0x67, 0x0d, 0xce, 0xa4, 0x75, 0x0a, 0x58, 0x12, 0xe2, 0xe8, 0x61,
0x5d, 0x26, 0xd6, 0x79, 0x01, 0x97, 0x5d, 0xeb, 0x52, 0x82, 0xfc, 0x00, 0xd6, 0xce, 0x49, 0xe8,
0x91, 0x91, 0x6a, 0x97, 0x16, 0xa9, 0x4a, 0x9e, 0xec, 0xb2, 0x0d, 0xba, 0xea, 0x32, 0x81, 0x91,
0xe6, 0xa8, 0x49, 0xfe, 0x61, 0x04, 0xb6, 0x05, 0x45, 0xd9, 0xbc, 0x2a, 0xe7, 0x17, 0x04, 0x0f,
0x53, 0xf4, 0x95, 0x15, 0x88, 0x94, 0xaf, 0x80, 0xc5, 0x3b, 0x3a, 0x9d, 0xb5, 0x4f, 0x49, 0xd8,
0xe7, 0xfe, 0xe2, 0xee, 0xfc, 0x3a, 0x13, 0x9d, 0xc5, 0x26, 0x5a, 0x85, 0x3c, 0x8e, 0x0a, 0x4a,
0xda, 0xad, 0xf6, 0x1e, 0x37, 0xcb, 0x3a, 0x54, 0x0e, 0x5b, 0x3f, 0x33, 0x4f, 0x7a, 0xe2, 0xea,
0x18, 0xe9, 0xb0, 0xf6, 0xa4, 0x83, 0xbb, 0x9d, 0x03, 0xc5, 0xc9, 0xa3, 0x2d, 0xd0, 0x15, 0x67,
0xd2, 0xaf, 0xc0, 0x11, 0xe4, 0x6b, 0x11, 0x95, 0xa1, 0xd0, 0x7b, 0xda, 0x3a, 0xd6, 0x4b, 0xc6,
0x7f, 0xe6, 0x60, 0x43, 0x86, 0x85, 0xf8, 0xd7, 0xf7, 0xeb, 0x7f, 0xfd, 0x25, 0x6f, 0x79, 0x72,
0xe9, 0x5b, 0x9e, 0x28, 0x09, 0x15, 0x51, 0x3d, 0x3f, 0x49, 0x42, 0xc5, 0xed, 0x50, 0x6a, 0xc7,
0x2f, 0x2c, 0xb2, 0xe3, 0xd7, 0x61, 0xd5, 0x25, 0x34, 0xb6, 0x5b, 0x05, 0x47, 0x24, 0x72, 0xa0,
0x6a, 0x79, 0x9e, 0xcf, 0x2c, 0x79, 0x75, 0x5a, 0x5a, 0x28, 0x18, 0x4e, 0x7d, 0x71, 0xb3, 0x35,
0x41, 0x92, 0x1b, 0x73, 0x12, 0xbb, 0xf1, 0x53, 0xd0, 0xa7, 0x3b, 0x2c, 0x12, 0x0e, 0x7f, 0xf8,
0xf1, 0x24, 0x1a, 0x12, 0xbe, 0x2e, 0xd4, 0xc5, 0xbe, 0xbe, 0xc2, 0x09, 0x7c, 0xd2, 0xed, 0xee,
0x77, 0x1f, 0xeb, 0x1a, 0x02, 0x28, 0x75, 0x7e, 0xb6, 0xdf, 0xef, 0xec, 0xea, 0xb9, 0x9d, 0xdf,
0x6c, 0x42, 0x49, 0x0a, 0x89, 0xbe, 0x55, 0x99, 0x40, 0xb2, 0xac, 0x12, 0xfd, 0x74, 0xe1, 0x8c,
0x3a, 0x55, 0xaa, 0xd9, 0x78, 0xb8, 0xf4, 0x78, 0xf5, 0x8b, 0x6b, 0x05, 0xfd, 0x8d, 0x06, 0x6b,
0xa9, 0xdf, 0x5b, 0x59, 0xaf, 0x8e, 0xe7, 0x54, 0x71, 0x36, 0x7e, 0xb2, 0xd4, 0xd8, 0x58, 0x96,
0x5f, 0x69, 0x50, 0x4d, 0xd4, 0x2f, 0xa2, 0xfb, 0xcb, 0xd4, 0x3c, 0x4a, 0x49, 0x3e, 0x5b, 0xbe,
0x5c, 0xd2, 0x58, 0xf9, 0x48, 0x43, 0x7f, 0xad, 0x41, 0x35, 0x51, 0xc9, 0x97, 0x59, 0x94, 0xd9,
0xba, 0xc3, 0xcc, 0xa2, 0xcc, 0x2b, 0x1c, 0x5c, 0x41, 0x7f, 0xa9, 0x41, 0x25, 0xae, 0xca, 0x43,
0xf7, 0x16, 0xaf, 0xe3, 0x93, 0x42, 0x7c, 0xba, 0x6c, 0x01, 0xa0, 0xb1, 0x82, 0xfe, 0x1c, 0xca,
0x51, 0x09, 0x1b, 0xca, 0x1a, 0xbd, 0xa6, 0xea, 0xe3, 0x1a, 0xf7, 0x16, 0x1e, 0x97, 0x9c, 0x3e,
0xaa, 0x2b, 0xcb, 0x3c, 0xfd, 0x54, 0x05, 0x5c, 0xe3, 0xde, 0xc2, 0xe3, 0xe2, 0xe9, 0xb9, 0x27,
0x24, 0xca, 0xcf, 0x32, 0x7b, 0xc2, 0x6c, 0xdd, 0x5b, 0x66, 0x4f, 0x98, 0x57, 0xed, 0x26, 0x05,
0x49, 0x14, 0xb0, 0x65, 0x16, 0x64, 0xb6, 0x48, 0x2e, 0xb3, 0x20, 0x73, 0xea, 0xe5, 0x8c, 0x15,
0xf4, 0x4b, 0x2d, 0x79, 0x2e, 0xb8, 0xb7, 0x70, 0x9d, 0xd6, 0x82, 0x2e, 0x39, 0x53, 0x29, 0x26,
0x16, 0xe8, 0x2f, 0xd5, 0x2d, 0x86, 0x2c, 0xf3, 0x42, 0x8b, 0x80, 0xa5, 0x2a, 0xc3, 0x1a, 0x9f,
0x2c, 0x17, 0x6c, 0x84, 0x10, 0x7f, 0xa5, 0x01, 0x4c, 0x0a, 0xc2, 0x32, 0x0b, 0x31, 0x53, 0x89,
0xd6, 0xb8, 0xbf, 0xc4, 0xc8, 0xe4, 0x02, 0x89, 0x0a, 0x56, 0x32, 0x2f, 0x90, 0xa9, 0x82, 0xb5,
0xcc, 0x0b, 0x64, 0xba, 0xd8, 0xcc, 0x58, 0x41, 0xff, 0xa4, 0xc1, 0xe6, 0x4c, 0xc1, 0x0c, 0x7a,
0x78, 0xcd, 0x9a, 0xa9, 0xc6, 0x17, 0xcb, 0x03, 0x44, 0xa2, 0x6d, 0x6b, 0x1f, 0x69, 0xe8, 0x6f,
0x35, 0x58, 0x4f, 0x17, 0x19, 0x64, 0x8e, 0x52, 0x73, 0x4a, 0x6f, 0x1a, 0x0f, 0x96, 0x1b, 0x1c,
0x6b, 0xeb, 0xef, 0x35, 0xa8, 0xa5, 0xeb, 0x4d, 0xd0, 0x83, 0xc5, 0xb6, 0x85, 0x29, 0x81, 0x3e,
0x5f, 0x72, 0x74, 0x24, 0xd1, 0x97, 0xab, 0x7f, 0x5c, 0x94, 0xd9, 0x5b, 0x49, 0x3c, 0x7e, 0xfc,
0xff, 0x01, 0x00, 0x00, 0xff, 0xff, 0x6e, 0xc4, 0x84, 0xf7, 0xfd, 0x32, 0x00, 0x00,
}
// Reference imports to suppress errors if they are not otherwise used.

View File

@@ -382,6 +382,10 @@ message DriverCapabilities {
}
// MountConfigs indicates whether the driver supports mount configurations.
MountConfigs mount_configs = 6;
// remote_tasks indicates whether the driver executes tasks remotely such
// on cloud runtimes like AWS ECS.
bool remote_tasks = 7;
}
message NetworkIsolationSpec {

View File

@@ -43,6 +43,7 @@ func (b *driverPluginServer) Capabilities(ctx context.Context, req *proto.Capabi
Exec: caps.Exec,
MustCreateNetwork: caps.MustInitiateNetwork,
NetworkIsolationModes: []proto.NetworkIsolationSpec_NetworkIsolationMode{},
RemoteTasks: caps.RemoteTasks,
},
}

View File

@@ -1,6 +1,7 @@
package drivers
import (
"github.com/hashicorp/nomad/nomad/structs"
"github.com/hashicorp/nomad/plugins/base"
)
@@ -45,3 +46,34 @@ func (h *TaskHandle) Copy() *TaskHandle {
copy(handle.DriverState, h.DriverState)
return handle
}
// Store this TaskHandle on the given TaskState.
func (h *TaskHandle) Store(ts *structs.TaskState) {
if h == nil || len(h.DriverState) == 0 {
// No handle or state, clear existing state
ts.TaskHandle = nil
return
}
ds := make([]byte, len(h.DriverState))
copy(ds, h.DriverState)
ts.TaskHandle = &structs.TaskHandle{
Version: h.Version,
DriverState: ds,
}
}
// NewTaskHandleFromState returns the TaskHandle stored in a TaskState or nil
// if no handle was stored.
func NewTaskHandleFromState(ts *structs.TaskState) *TaskHandle {
if ts.TaskHandle == nil {
return nil
}
th := TaskHandle{
Version: ts.TaskHandle.Version,
DriverState: make([]byte, len(ts.TaskHandle.DriverState)),
}
copy(th.DriverState, ts.TaskHandle.DriverState)
return &th
}

View File

@@ -606,6 +606,10 @@ func (s *GenericScheduler) computePlacements(destructive, place []placementResul
if missing.IsRescheduling() {
updateRescheduleTracker(alloc, prevAllocation, now)
}
// If the allocation has task handles,
// copy them to the new allocation
propagateTaskState(alloc, prevAllocation, missing.PreviousLost())
}
// If we are placing a canary and we found a match, add the canary
@@ -643,6 +647,44 @@ func (s *GenericScheduler) computePlacements(destructive, place []placementResul
return nil
}
// propagateTaskState copies task handles from previous allocations to
// replacement allocations when the previous allocation is being drained or was
// lost. Remote task drivers rely on this to reconnect to remote tasks when the
// allocation managing them changes due to a down or draining node.
//
// The previous allocation will be marked as lost as part of this plan, so its
// ClientStatus is not yet lost.
func propagateTaskState(newAlloc, prev *structs.Allocation, prevLost bool) {
// Don't transfer state from client terminal allocs
if prev.ClientTerminalStatus() {
return
}
// If previous allocation is not lost and not draining, do not copy
// task handles.
if !prevLost && !prev.DesiredTransition.ShouldMigrate() {
return
}
newAlloc.TaskStates = make(map[string]*structs.TaskState, len(newAlloc.AllocatedResources.Tasks))
for taskName, prevState := range prev.TaskStates {
if prevState.TaskHandle == nil {
// No task handle, skip
continue
}
if _, ok := newAlloc.AllocatedResources.Tasks[taskName]; !ok {
// Task dropped in update, skip
continue
}
// Copy state
newState := structs.NewTaskState()
newState.TaskHandle = prevState.TaskHandle.Copy()
newAlloc.TaskStates[taskName] = newState
}
}
// getSelectOptions sets up preferred nodes and penalty nodes
func getSelectOptions(prevAllocation *structs.Allocation, preferredNode *structs.Node) *SelectOptions {
selectOptions := &SelectOptions{}

View File

@@ -3327,6 +3327,98 @@ func TestServiceSched_NodeDrain_Queued_Allocations(t *testing.T) {
}
}
// TestServiceSched_NodeDrain_TaskHandle asserts that allocations with task
// handles have them propagated to replacement allocations when drained.
func TestServiceSched_NodeDrain_TaskHandle(t *testing.T) {
h := NewHarness(t)
node := mock.Node()
require.NoError(t, h.State.UpsertNode(structs.MsgTypeTestSetup, h.NextIndex(), node))
// Create some nodes
for i := 0; i < 10; i++ {
node := mock.Node()
require.NoError(t, h.State.UpsertNode(structs.MsgTypeTestSetup, h.NextIndex(), node))
}
// Generate a fake job with allocations and an update policy.
job := mock.Job()
require.NoError(t, h.State.UpsertJob(structs.MsgTypeTestSetup, h.NextIndex(), job))
var allocs []*structs.Allocation
for i := 0; i < 10; i++ {
alloc := mock.Alloc()
alloc.Job = job
alloc.JobID = job.ID
alloc.NodeID = node.ID
alloc.Name = fmt.Sprintf("my-job.web[%d]", i)
alloc.DesiredTransition.Migrate = helper.BoolToPtr(true)
alloc.TaskStates = map[string]*structs.TaskState{
"web": &structs.TaskState{
TaskHandle: &structs.TaskHandle{
Version: 1,
DriverState: []byte("test-driver-state"),
},
},
}
allocs = append(allocs, alloc)
}
require.NoError(t, h.State.UpsertAllocs(structs.MsgTypeTestSetup, h.NextIndex(), allocs))
node.DrainStrategy = mock.DrainNode().DrainStrategy
require.NoError(t, h.State.UpsertNode(structs.MsgTypeTestSetup, h.NextIndex(), node))
// Create a mock evaluation to deal with drain
eval := &structs.Evaluation{
Namespace: structs.DefaultNamespace,
ID: uuid.Generate(),
Priority: 50,
TriggeredBy: structs.EvalTriggerNodeUpdate,
JobID: job.ID,
NodeID: node.ID,
Status: structs.EvalStatusPending,
}
require.NoError(t, h.State.UpsertEvals(structs.MsgTypeTestSetup, h.NextIndex(), []*structs.Evaluation{eval}))
// Process the evaluation
err := h.Process(NewServiceScheduler, eval)
require.NoError(t, err)
// Ensure a single plan
require.Len(t, h.Plans, 1)
plan := h.Plans[0]
// Ensure the plan evicted all allocs
require.Len(t, plan.NodeUpdate[node.ID], len(allocs))
// Ensure the plan allocated
var planned []*structs.Allocation
for _, allocList := range plan.NodeAllocation {
planned = append(planned, allocList...)
}
require.Len(t, planned, len(allocs))
// Lookup the allocations by JobID
ws := memdb.NewWatchSet()
out, err := h.State.AllocsByJob(ws, job.Namespace, job.ID, false)
require.NoError(t, err)
// Ensure all allocations placed
out, _ = structs.FilterTerminalAllocs(out)
require.Len(t, out, len(allocs))
// Ensure task states were propagated
for _, a := range out {
require.NotEmpty(t, a.TaskStates)
require.NotEmpty(t, a.TaskStates["web"])
require.NotNil(t, a.TaskStates["web"].TaskHandle)
assert.Equal(t, 1, a.TaskStates["web"].TaskHandle.Version)
assert.Equal(t, []byte("test-driver-state"), a.TaskStates["web"].TaskHandle.DriverState)
}
h.AssertEvalStatus(t, structs.EvalStatusComplete)
}
func TestServiceSched_RetryLimit(t *testing.T) {
h := NewHarness(t)
h.Planner = &RejectPlan{h}
@@ -6075,3 +6167,121 @@ func TestServiceSched_CSIVolumesPerAlloc(t *testing.T) {
}
}
// TestPropagateTaskState asserts that propagateTaskState only copies state
// when the previous allocation is lost or draining.
func TestPropagateTaskState(t *testing.T) {
t.Parallel()
const taskName = "web"
taskHandle := &structs.TaskHandle{
Version: 1,
DriverState: []byte("driver-state"),
}
cases := []struct {
name string
prevAlloc *structs.Allocation
prevLost bool
copied bool
}{
{
name: "LostWithState",
prevAlloc: &structs.Allocation{
ClientStatus: structs.AllocClientStatusRunning,
DesiredTransition: structs.DesiredTransition{},
TaskStates: map[string]*structs.TaskState{
taskName: &structs.TaskState{
TaskHandle: taskHandle,
},
},
},
prevLost: true,
copied: true,
},
{
name: "DrainedWithState",
prevAlloc: &structs.Allocation{
ClientStatus: structs.AllocClientStatusRunning,
DesiredTransition: structs.DesiredTransition{
Migrate: helper.BoolToPtr(true),
},
TaskStates: map[string]*structs.TaskState{
taskName: &structs.TaskState{
TaskHandle: taskHandle,
},
},
},
prevLost: false,
copied: true,
},
{
name: "LostWithoutState",
prevAlloc: &structs.Allocation{
ClientStatus: structs.AllocClientStatusRunning,
DesiredTransition: structs.DesiredTransition{},
TaskStates: map[string]*structs.TaskState{
taskName: &structs.TaskState{},
},
},
prevLost: true,
copied: false,
},
{
name: "DrainedWithoutState",
prevAlloc: &structs.Allocation{
ClientStatus: structs.AllocClientStatusRunning,
DesiredTransition: structs.DesiredTransition{
Migrate: helper.BoolToPtr(true),
},
TaskStates: map[string]*structs.TaskState{
taskName: &structs.TaskState{},
},
},
prevLost: false,
copied: false,
},
{
name: "TerminalWithState",
prevAlloc: &structs.Allocation{
ClientStatus: structs.AllocClientStatusComplete,
DesiredTransition: structs.DesiredTransition{},
TaskStates: map[string]*structs.TaskState{
taskName: &structs.TaskState{
TaskHandle: taskHandle,
},
},
},
prevLost: false,
copied: false,
},
}
for i := range cases {
tc := cases[i]
t.Run(tc.name, func(t *testing.T) {
newAlloc := &structs.Allocation{
// Required by propagateTaskState and populated
// by the scheduler's node iterator.
AllocatedResources: &structs.AllocatedResources{
Tasks: map[string]*structs.AllocatedTaskResources{
taskName: nil, // value isn't used
},
},
}
propagateTaskState(newAlloc, tc.prevAlloc, tc.prevLost)
if tc.copied {
// Assert state was copied
require.NotNil(t, newAlloc.TaskStates)
require.Contains(t, newAlloc.TaskStates, taskName)
require.Equal(t, taskHandle, newAlloc.TaskStates[taskName].TaskHandle)
} else {
// Assert state was *not* copied
require.Empty(t, newAlloc.TaskStates,
"expected task states not to be copied")
}
})
}
}

View File

@@ -396,12 +396,13 @@ func (a *allocReconciler) computeGroup(group string, all allocSet) bool {
// reschedulable later and mark the allocations for in place updating
a.handleDelayedReschedules(rescheduleLater, all, tg.Name)
// Create a structure for choosing names. Seed with the taken names which is
// the union of untainted and migrating nodes (includes canaries)
nameIndex := newAllocNameIndex(a.jobID, group, tg.Count, untainted.union(migrate, rescheduleNow))
// Create a structure for choosing names. Seed with the taken names
// which is the union of untainted, rescheduled, allocs on migrating
// nodes, and allocs on down nodes (includes canaries)
nameIndex := newAllocNameIndex(a.jobID, group, tg.Count, untainted.union(migrate, rescheduleNow, lost))
// Stop any unneeded allocations and update the untainted set to not
// included stopped allocations.
// include stopped allocations.
canaryState := dstate != nil && dstate.DesiredCanaries != 0 && !dstate.Promoted
stop := a.computeStop(tg, nameIndex, untainted, migrate, lost, canaries, canaryState, lostLaterEvals)
desiredChanges.Stop += uint64(len(stop))
@@ -452,9 +453,10 @@ func (a *allocReconciler) computeGroup(group string, all allocSet) bool {
// * Not placing any canaries
// * If there are any canaries that they have been promoted
// * There is no delayed stop_after_client_disconnect alloc, which delays scheduling for the whole group
// * An alloc was lost
var place []allocPlaceResult
if len(lostLater) == 0 {
place = a.computePlacements(tg, nameIndex, untainted, migrate, rescheduleNow, canaryState)
place = a.computePlacements(tg, nameIndex, untainted, migrate, rescheduleNow, canaryState, lost)
if !existingDeployment {
dstate.DesiredTotal += len(place)
}
@@ -705,8 +707,11 @@ func (a *allocReconciler) computeLimit(group *structs.TaskGroup, untainted, dest
// computePlacement returns the set of allocations to place given the group
// definition, the set of untainted, migrating and reschedule allocations for the group.
//
// Placements will meet or exceed group count.
func (a *allocReconciler) computePlacements(group *structs.TaskGroup,
nameIndex *allocNameIndex, untainted, migrate allocSet, reschedule allocSet, canaryState bool) []allocPlaceResult {
nameIndex *allocNameIndex, untainted, migrate allocSet, reschedule allocSet,
canaryState bool, lost allocSet) []allocPlaceResult {
// Add rescheduled placement results
var place []allocPlaceResult
@@ -720,13 +725,31 @@ func (a *allocReconciler) computePlacements(group *structs.TaskGroup,
downgradeNonCanary: canaryState && !alloc.DeploymentStatus.IsCanary(),
minJobVersion: alloc.Job.Version,
lost: false,
})
}
// Hot path the nothing to do case
// Add replacements for lost allocs up to group.Count
existing := len(untainted) + len(migrate) + len(reschedule)
if existing >= group.Count {
return place
for _, alloc := range lost {
if existing >= group.Count {
// Reached desired count, do not replace remaining lost
// allocs
break
}
existing++
place = append(place, allocPlaceResult{
name: alloc.Name,
taskGroup: group,
previousAlloc: alloc,
reschedule: false,
canary: alloc.DeploymentStatus.IsCanary(),
downgradeNonCanary: canaryState && !alloc.DeploymentStatus.IsCanary(),
minJobVersion: alloc.Job.Version,
lost: true,
})
}
// Add remaining placement results
@@ -749,8 +772,7 @@ func (a *allocReconciler) computePlacements(group *structs.TaskGroup,
func (a *allocReconciler) computeStop(group *structs.TaskGroup, nameIndex *allocNameIndex,
untainted, migrate, lost, canaries allocSet, canaryState bool, followupEvals map[string]string) allocSet {
// Mark all lost allocations for stop. Previous allocation doesn't matter
// here since it is on a lost node
// Mark all lost allocations for stop.
var stop allocSet
stop = stop.union(lost)
a.markDelayed(lost, structs.AllocClientStatusLost, allocLost, followupEvals)

View File

@@ -35,6 +35,9 @@ type placementResult interface {
// stopped and if so the status description.
StopPreviousAlloc() (bool, string)
// PreviousLost is true if the previous allocation was lost.
PreviousLost() bool
// DowngradeNonCanary indicates that placement should use the latest stable job
// with the MinJobVersion, rather than the current deployment version
DowngradeNonCanary() bool
@@ -58,6 +61,7 @@ type allocPlaceResult struct {
taskGroup *structs.TaskGroup
previousAlloc *structs.Allocation
reschedule bool
lost bool
downgradeNonCanary bool
minJobVersion uint64
@@ -71,6 +75,7 @@ func (a allocPlaceResult) IsRescheduling() bool { return a.re
func (a allocPlaceResult) StopPreviousAlloc() (bool, string) { return false, "" }
func (a allocPlaceResult) DowngradeNonCanary() bool { return a.downgradeNonCanary }
func (a allocPlaceResult) MinJobVersion() uint64 { return a.minJobVersion }
func (a allocPlaceResult) PreviousLost() bool { return a.lost }
// allocDestructiveResult contains the information required to do a destructive
// update. Destructive changes should be applied atomically, as in the old alloc
@@ -92,6 +97,7 @@ func (a allocDestructiveResult) StopPreviousAlloc() (bool, string) {
}
func (a allocDestructiveResult) DowngradeNonCanary() bool { return false }
func (a allocDestructiveResult) MinJobVersion() uint64 { return 0 }
func (a allocDestructiveResult) PreviousLost() bool { return false }
// allocMatrix is a mapping of task groups to their allocation set.
type allocMatrix map[string]allocSet