diff --git a/agent/agent.go b/agent/agent.go index 3721f2916a..295366b2e8 100644 --- a/agent/agent.go +++ b/agent/agent.go @@ -1,6 +1,7 @@ package agent import ( + "bytes" "fmt" "math/rand" "reflect" @@ -44,6 +45,8 @@ type Agent struct { stopOnce sync.Once // only allow stop to be called once closed chan struct{} // only closed in run err error // read only after closed is closed + + nodeUpdatePeriod time.Duration } // New returns a new agent, ready for task dispatch. @@ -53,14 +56,15 @@ func New(config *Config) (*Agent, error) { } a := &Agent{ - config: config, - sessionq: make(chan sessionOperation), - started: make(chan struct{}), - leaving: make(chan struct{}), - left: make(chan struct{}), - stopped: make(chan struct{}), - closed: make(chan struct{}), - ready: make(chan struct{}), + config: config, + sessionq: make(chan sessionOperation), + started: make(chan struct{}), + leaving: make(chan struct{}), + left: make(chan struct{}), + stopped: make(chan struct{}), + closed: make(chan struct{}), + ready: make(chan struct{}), + nodeUpdatePeriod: nodeUpdatePeriod, } a.worker = newWorker(config.DB, config.Executor, a) @@ -182,13 +186,15 @@ func (a *Agent) run(ctx context.Context) { log.G(ctx).Debug("(*Agent).run") defer log.G(ctx).Debug("(*Agent).run exited") + nodeTLSInfo := a.config.NodeTLSInfo + // get the node description - nodeDescription, err := a.nodeDescriptionWithHostname(ctx) + nodeDescription, err := a.nodeDescriptionWithHostname(ctx, nodeTLSInfo) if err != nil { log.G(ctx).WithError(err).WithField("agent", a.config.Executor).Error("agent: node description unavailable") } // nodeUpdateTicker is used to periodically check for updates to node description - nodeUpdateTicker := time.NewTicker(nodeUpdatePeriod) + nodeUpdateTicker := time.NewTicker(a.nodeUpdatePeriod) defer nodeUpdateTicker.Stop() var ( @@ -214,6 +220,35 @@ func (a *Agent) run(ctx context.Context) { a.worker.Listen(ctx, reporter) + updateNode := func() { + // skip updating if the registration isn't finished + if registered != nil { + return + } + // get the current node description + newNodeDescription, err := a.nodeDescriptionWithHostname(ctx, nodeTLSInfo) + if err != nil { + log.G(ctx).WithError(err).WithField("agent", a.config.Executor).Error("agent: updated node description unavailable") + } + + // if newNodeDescription is nil, it will cause a panic when + // trying to create a session. Typically this can happen + // if the engine goes down + if newNodeDescription == nil { + return + } + + // if the node description has changed, update it to the new one + // and close the session. The old session will be stopped and a + // new one will be created with the updated description + if !reflect.DeepEqual(nodeDescription, newNodeDescription) { + nodeDescription = newNodeDescription + // close the session + log.G(ctx).Info("agent: found node update") + session.sendError(nil) + } + } + for { select { case operation := <-sessionq: @@ -247,7 +282,7 @@ func (a *Agent) run(ctx context.Context) { } } case msg := <-session.messages: - if err := a.handleSessionMessage(ctx, msg); err != nil { + if err := a.handleSessionMessage(ctx, msg, nodeTLSInfo); err != nil { log.G(ctx).WithError(err).Error("session message handler failed") } case sub := <-session.subscriptions: @@ -305,33 +340,17 @@ func (a *Agent) run(ctx context.Context) { } session = newSession(ctx, a, delay, session.sessionID, nodeDescription) registered = session.registered - case <-nodeUpdateTicker.C: - // skip this case if the registration isn't finished - if registered != nil { - continue - } - // get the current node description - newNodeDescription, err := a.nodeDescriptionWithHostname(ctx) - if err != nil { - log.G(ctx).WithError(err).WithField("agent", a.config.Executor).Error("agent: updated node description unavailable") - } - - // if newNodeDescription is nil, it will cause a panic when - // trying to create a session. Typically this can happen - // if the engine goes down - if newNodeDescription == nil { - continue - } - - // if the node description has changed, update it to the new one - // and close the session. The old session will be stopped and a - // new one will be created with the updated description - if !reflect.DeepEqual(nodeDescription, newNodeDescription) { - nodeDescription = newNodeDescription - // close the session - log.G(ctx).Info("agent: found node update") - session.sendError(nil) + case ev := <-a.config.NotifyTLSChange: + // the TLS info has changed, so force a check to see if we need to restart the session + if tlsInfo, ok := ev.(*api.NodeTLSInfo); ok { + nodeTLSInfo = tlsInfo + updateNode() + nodeUpdateTicker.Stop() + nodeUpdateTicker = time.NewTicker(a.nodeUpdatePeriod) } + case <-nodeUpdateTicker.C: + // periodically check to see whether the node information has changed, and if so, restart the session + updateNode() case <-a.stopped: // TODO(stevvooe): Wait on shutdown and cleanup. May need to pump // this loop a few times. @@ -347,7 +366,7 @@ func (a *Agent) run(ctx context.Context) { } } -func (a *Agent) handleSessionMessage(ctx context.Context, message *api.SessionMessage) error { +func (a *Agent) handleSessionMessage(ctx context.Context, message *api.SessionMessage, nti *api.NodeTLSInfo) error { seen := map[api.Peer]struct{}{} for _, manager := range message.Managers { if manager.Peer.Addr == "" { @@ -358,18 +377,28 @@ func (a *Agent) handleSessionMessage(ctx context.Context, message *api.SessionMe seen[*manager.Peer] = struct{}{} } - if message.Node != nil { - if a.node == nil || !nodesEqual(a.node, message.Node) { - if a.config.NotifyNodeChange != nil { - a.config.NotifyNodeChange <- message.Node.Copy() - } - a.node = message.Node.Copy() - if err := a.config.Executor.Configure(ctx, a.node); err != nil { - log.G(ctx).WithError(err).Error("node configure failed") - } + var changes *NodeChanges + if message.Node != nil && (a.node == nil || !nodesEqual(a.node, message.Node)) { + if a.config.NotifyNodeChange != nil { + changes = &NodeChanges{Node: message.Node.Copy()} + } + a.node = message.Node.Copy() + if err := a.config.Executor.Configure(ctx, a.node); err != nil { + log.G(ctx).WithError(err).Error("node configure failed") + } + } + if len(message.RootCA) > 0 && !bytes.Equal(message.RootCA, nti.TrustRoot) { + if changes == nil { + changes = &NodeChanges{RootCert: message.RootCA} + } else { + changes.RootCert = message.RootCA } } + if changes != nil { + a.config.NotifyNodeChange <- changes + } + // prune managers not in list. for peer := range a.config.ConnBroker.Remotes().Weights() { if _, ok := seen[peer]; !ok { @@ -517,12 +546,15 @@ func (a *Agent) Publisher(ctx context.Context, subscriptionID string) (exec.LogP } // nodeDescriptionWithHostname retrieves node description, and overrides hostname if available -func (a *Agent) nodeDescriptionWithHostname(ctx context.Context) (*api.NodeDescription, error) { +func (a *Agent) nodeDescriptionWithHostname(ctx context.Context, tlsInfo *api.NodeTLSInfo) (*api.NodeDescription, error) { desc, err := a.config.Executor.Describe(ctx) - // Override hostname - if a.config.Hostname != "" && desc != nil { - desc.Hostname = a.config.Hostname + // Override hostname and TLS info + if desc != nil { + if a.config.Hostname != "" && desc != nil { + desc.Hostname = a.config.Hostname + } + desc.TLSInfo = tlsInfo } return desc, err } diff --git a/agent/agent_test.go b/agent/agent_test.go index 2e01a0b3c9..9ef53cbfbd 100644 --- a/agent/agent_test.go +++ b/agent/agent_test.go @@ -1,40 +1,23 @@ package agent import ( + "errors" "testing" "time" - "github.com/docker/swarmkit/agent/exec" + events "github.com/docker/go-events" + agentutils "github.com/docker/swarmkit/agent/testutils" "github.com/docker/swarmkit/api" "github.com/docker/swarmkit/ca" "github.com/docker/swarmkit/ca/testutils" "github.com/docker/swarmkit/connectionbroker" + raftutils "github.com/docker/swarmkit/manager/state/raft/testutils" "github.com/docker/swarmkit/remotes" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "golang.org/x/net/context" ) -// NoopExecutor is a dummy executor that implements enough to get the agent started. -type NoopExecutor struct { -} - -func (e *NoopExecutor) Describe(ctx context.Context) (*api.NodeDescription, error) { - return &api.NodeDescription{}, nil -} - -func (e *NoopExecutor) Configure(ctx context.Context, node *api.Node) error { - return nil -} - -func (e *NoopExecutor) Controller(t *api.Task) (exec.Controller, error) { - return nil, exec.ErrRuntimeUnsupported -} - -func (e *NoopExecutor) SetNetworkBootstrapKeys([]*api.EncryptionKey) error { - return nil -} - func TestAgent(t *testing.T) { // TODO(stevvooe): The current agent is fairly monolithic, making it hard // to test without implementing or mocking an entire master. We'd like to @@ -73,10 +56,11 @@ func TestAgentStartStop(t *testing.T) { defer cleanup() agent, err := New(&Config{ - Executor: &NoopExecutor{}, + Executor: &agentutils.TestExecutor{}, ConnBroker: connectionbroker.New(remotes), Credentials: agentSecurityConfig.ClientTLSCreds, DB: db, + NodeTLSInfo: &api.NodeTLSInfo{}, }) require.NoError(t, err) assert.NotNil(t, agent) @@ -93,69 +77,252 @@ func TestAgentStartStop(t *testing.T) { assert.NoError(t, agent.Stop(ctx)) } -func TestHandleSessionMessage(t *testing.T) { - // TODO(rajdeepd): The current agent is fairly monolithic, hence we - // have to test message handling this way. Needs to be refactored - ctx, _ := context.WithTimeout(context.Background(), 5000*time.Millisecond) - agent, cleanup := agentTestEnv(t) +func TestHandleSessionMessageNetworkManagerChanges(t *testing.T) { + nodeChangeCh := make(chan *NodeChanges, 1) + defer close(nodeChangeCh) + tester := agentTestEnv(t, nodeChangeCh, nil) + defer tester.cleanup() - defer cleanup() + currSession, closedSessions := tester.dispatcher.GetSessions() + require.NotNil(t, currSession) + require.NotNil(t, currSession.Description) + require.Empty(t, closedSessions) var messages = []*api.SessionMessage{ - {SessionID: "sm1", Node: &api.Node{}, + { Managers: []*api.WeightedPeer{ {&api.Peer{NodeID: "node1", Addr: "10.0.0.1"}, 1.0}}, - NetworkBootstrapKeys: []*api.EncryptionKey{{}}}, - {SessionID: "sm1", Node: &api.Node{}, + NetworkBootstrapKeys: []*api.EncryptionKey{{}}, + }, + { Managers: []*api.WeightedPeer{ {&api.Peer{NodeID: "node1", Addr: ""}, 1.0}}, - NetworkBootstrapKeys: []*api.EncryptionKey{{}}}, - {SessionID: "sm1", Node: &api.Node{}, + NetworkBootstrapKeys: []*api.EncryptionKey{{}}, + }, + { Managers: []*api.WeightedPeer{ {&api.Peer{NodeID: "node1", Addr: "10.0.0.1"}, 1.0}}, - NetworkBootstrapKeys: nil}, - {SessionID: "sm1", Node: &api.Node{}, + NetworkBootstrapKeys: nil, + }, + { Managers: []*api.WeightedPeer{ {&api.Peer{NodeID: "", Addr: "10.0.0.1"}, 1.0}}, - NetworkBootstrapKeys: []*api.EncryptionKey{{}}}, - {SessionID: "sm1", Node: &api.Node{}, + NetworkBootstrapKeys: []*api.EncryptionKey{{}}, + }, + { Managers: []*api.WeightedPeer{ {&api.Peer{NodeID: "node1", Addr: "10.0.0.1"}, 0.0}}, - NetworkBootstrapKeys: []*api.EncryptionKey{{}}}, + NetworkBootstrapKeys: []*api.EncryptionKey{{}}, + }, } for _, m := range messages { - err := agent.handleSessionMessage(ctx, m) - if err != nil { - t.Fatal("err should be nil") + m.SessionID = currSession.SessionID + tester.dispatcher.SessionMessageChannel() <- m + select { + case nodeChange := <-nodeChangeCh: + require.FailNow(t, "there should be no node changes with these messages: %v", nodeChange) + case <-time.After(100 * time.Millisecond): } } + + currSession, closedSessions = tester.dispatcher.GetSessions() + require.NotEmpty(t, currSession) + require.Empty(t, closedSessions) +} + +func TestHandleSessionMessageNodeChanges(t *testing.T) { + nodeChangeCh := make(chan *NodeChanges, 1) + defer close(nodeChangeCh) + tester := agentTestEnv(t, nodeChangeCh, nil) + defer tester.cleanup() + + currSession, closedSessions := tester.dispatcher.GetSessions() + require.NotNil(t, currSession) + require.NotNil(t, currSession.Description) + require.Empty(t, closedSessions) + + var testcases = []struct { + msg *api.SessionMessage + change *NodeChanges + errorMsg string + }{ + { + msg: &api.SessionMessage{ + Node: &api.Node{}, + }, + change: &NodeChanges{Node: &api.Node{}}, + errorMsg: "the node changed, but no notification of node change", + }, + { + msg: &api.SessionMessage{ + RootCA: []byte("new root CA"), + }, + change: &NodeChanges{RootCert: []byte("new root CA")}, + errorMsg: "the root cert changed, but no notification of node change", + }, + { + msg: &api.SessionMessage{ + Node: &api.Node{ID: "something"}, + RootCA: []byte("new root CA"), + }, + change: &NodeChanges{ + Node: &api.Node{ID: "something"}, + RootCert: []byte("new root CA"), + }, + errorMsg: "the root cert and node both changed, but no notification of node change", + }, + { + msg: &api.SessionMessage{ + Node: &api.Node{ID: "something"}, + RootCA: tester.testCA.RootCA.Certs, + }, + errorMsg: "while a node and root cert were provided, nothing has changed so no node changed", + }, + } + + for _, tc := range testcases { + tc.msg.SessionID = currSession.SessionID + tester.dispatcher.SessionMessageChannel() <- tc.msg + if tc.change != nil { + select { + case nodeChange := <-nodeChangeCh: + require.Equal(t, tc.change, nodeChange, tc.errorMsg) + case <-time.After(100 * time.Millisecond): + require.FailNow(t, tc.errorMsg) + } + } else { + select { + case nodeChange := <-nodeChangeCh: + require.FailNow(t, "%s: but got change: %v", tc.errorMsg, nodeChange) + case <-time.After(100 * time.Millisecond): + } + } + } + + currSession, closedSessions = tester.dispatcher.GetSessions() + require.NotEmpty(t, currSession) + require.Empty(t, closedSessions) } -func agentTestEnv(t *testing.T) (*Agent, func()) { +// when the node description changes, the session is restarted and propagated up to the dispatcher +func TestSessionRestartedOnNodeDescriptionChange(t *testing.T) { + tlsCh := make(chan events.Event, 1) + defer close(tlsCh) + tester := agentTestEnv(t, nil, tlsCh) + defer tester.cleanup() + + currSession, closedSessions := tester.dispatcher.GetSessions() + require.NotNil(t, currSession) + require.NotNil(t, currSession.Description) + require.Empty(t, closedSessions) + + tester.executor.UpdateNodeDescription(&api.NodeDescription{ + Hostname: "testAgent", + }) + var gotSession *api.SessionRequest + require.NoError(t, raftutils.PollFuncWithTimeout(nil, func() error { + gotSession, closedSessions = tester.dispatcher.GetSessions() + if gotSession == nil || len(closedSessions) != 1 { + return errors.New("session has not been restarted yet") + } + return nil + }, 1*time.Second)) + require.NotEqual(t, currSession, gotSession) + require.NotNil(t, gotSession.Description) + require.Equal(t, "testAgent", gotSession.Description.Hostname) + currSession = gotSession + + newTLSInfo := &api.NodeTLSInfo{ + TrustRoot: testutils.ECDSA256SHA256Cert, + CertIssuerPublicKey: []byte("public key"), + CertIssuerSubject: []byte("subject"), + } + tlsCh <- newTLSInfo + require.NoError(t, raftutils.PollFuncWithTimeout(nil, func() error { + gotSession, closedSessions = tester.dispatcher.GetSessions() + if gotSession == nil || len(closedSessions) != 2 { + return errors.New("session has not been restarted yet") + } + return nil + }, 1*time.Second)) + require.NotEqual(t, currSession, gotSession) + require.NotNil(t, gotSession.Description) + require.Equal(t, "testAgent", gotSession.Description.Hostname) + require.Equal(t, newTLSInfo, gotSession.Description.TLSInfo) +} + +type agentTester struct { + agent *Agent + dispatcher *agentutils.MockDispatcher + executor *agentutils.TestExecutor + cleanup func() + testCA *testutils.TestCA +} + +func agentTestEnv(t *testing.T, nodeChangeCh chan *NodeChanges, tlsChangeCh chan events.Event) *agentTester { var cleanup []func() tc := testutils.NewTestCA(t) - cleanup = append(cleanup, func() { tc.Stop() }) + cleanup = append(cleanup, tc.Stop) agentSecurityConfig, err := tc.NewNodeConfig(ca.WorkerRole) require.NoError(t, err) + managerSecurityConfig, err := tc.NewNodeConfig(ca.ManagerRole) + require.NoError(t, err) - addr := "localhost:4949" - remotes := remotes.NewRemotes(api.Peer{Addr: addr}) + mockDispatcher, mockDispatcherStop := agentutils.NewMockDispatcher(t, managerSecurityConfig) + cleanup = append(cleanup, mockDispatcherStop) + + remotes := remotes.NewRemotes(api.Peer{Addr: mockDispatcher.Addr}) db, cleanupStorage := storageTestEnv(t) cleanup = append(cleanup, func() { cleanupStorage() }) + executor := &agentutils.TestExecutor{} + agent, err := New(&Config{ - Executor: &NoopExecutor{}, - ConnBroker: connectionbroker.New(remotes), - Credentials: agentSecurityConfig.ClientTLSCreds, - DB: db, + Executor: executor, + ConnBroker: connectionbroker.New(remotes), + Credentials: agentSecurityConfig.ClientTLSCreds, + DB: db, + NotifyNodeChange: nodeChangeCh, + NotifyTLSChange: tlsChangeCh, + NodeTLSInfo: &api.NodeTLSInfo{ + TrustRoot: tc.RootCA.Certs, + CertIssuerPublicKey: agentSecurityConfig.IssuerInfo().PublicKey, + CertIssuerSubject: agentSecurityConfig.IssuerInfo().Subject, + }, }) require.NoError(t, err) - return agent, func() { - for i := len(cleanup) - 1; i >= 0; i-- { - cleanup[i]() - } + agent.nodeUpdatePeriod = 200 * time.Millisecond + + go agent.Start(context.Background()) + cleanup = append(cleanup, func() { + agent.Stop(context.Background()) + }) + + getErr := make(chan error) + go func() { + getErr <- agent.Err(context.Background()) + }() + select { + case err := <-getErr: + require.FailNow(t, "starting agent errored with: %v", err) + case <-agent.Ready(): + case <-time.After(5 * time.Second): + require.FailNow(t, "agent not ready within 5 seconds") + } + + return &agentTester{ + agent: agent, + dispatcher: mockDispatcher, + executor: executor, + testCA: tc, + cleanup: func() { + // go in reverse order + for i := len(cleanup) - 1; i >= 0; i-- { + cleanup[i]() + } + }, } } diff --git a/agent/config.go b/agent/config.go index de9359842e..70c94ecb65 100644 --- a/agent/config.go +++ b/agent/config.go @@ -2,6 +2,7 @@ package agent import ( "github.com/boltdb/bolt" + "github.com/docker/go-events" "github.com/docker/swarmkit/agent/exec" "github.com/docker/swarmkit/api" "github.com/docker/swarmkit/connectionbroker" @@ -9,6 +10,13 @@ import ( "google.golang.org/grpc/credentials" ) +// NodeChanges encapsulates changes that should be made to the node as per session messages +// from the dispatcher +type NodeChanges struct { + Node *api.Node + RootCert []byte +} + // Config provides values for an Agent. type Config struct { // Hostname the name of host for agent instance. @@ -25,10 +33,16 @@ type Config struct { DB *bolt.DB // NotifyNodeChange channel receives new node changes from session messages. - NotifyNodeChange chan<- *api.Node + NotifyNodeChange chan<- *NodeChanges + + // NotifyTLSChange channel sends new TLS information changes, which can cause a session to restart + NotifyTLSChange <-chan events.Event // Credentials is credentials for grpc connection to manager. Credentials credentials.TransportCredentials + + // NodeTLSInfo contains the starting node TLS info to bootstrap into the agent + NodeTLSInfo *api.NodeTLSInfo } func (c *Config) validate() error { @@ -44,5 +58,9 @@ func (c *Config) validate() error { return errors.New("agent: database required") } + if c.NodeTLSInfo == nil { + return errors.New("agent: Node TLS info is required") + } + return nil } diff --git a/agent/testutils/fakes.go b/agent/testutils/fakes.go new file mode 100644 index 0000000000..31625bbac8 --- /dev/null +++ b/agent/testutils/fakes.go @@ -0,0 +1,219 @@ +package testutils + +import ( + "net" + "sync" + "testing" + "time" + + "google.golang.org/grpc" + + "github.com/docker/swarmkit/agent/exec" + "github.com/docker/swarmkit/api" + "github.com/docker/swarmkit/ca" + "github.com/stretchr/testify/require" + "golang.org/x/net/context" +) + +// TestExecutor is executor for integration tests +type TestExecutor struct { + mu sync.Mutex + desc *api.NodeDescription +} + +// Describe just returns empty NodeDescription. +func (e *TestExecutor) Describe(ctx context.Context) (*api.NodeDescription, error) { + e.mu.Lock() + defer e.mu.Unlock() + if e.desc == nil { + return &api.NodeDescription{}, nil + } + return e.desc.Copy(), nil +} + +// Configure does nothing. +func (e *TestExecutor) Configure(ctx context.Context, node *api.Node) error { + return nil +} + +// SetNetworkBootstrapKeys does nothing. +func (e *TestExecutor) SetNetworkBootstrapKeys([]*api.EncryptionKey) error { + return nil +} + +// Controller returns TestController. +func (e *TestExecutor) Controller(t *api.Task) (exec.Controller, error) { + return &TestController{ + ch: make(chan struct{}), + }, nil +} + +// UpdateNodeDescription sets the node description on the test executor +func (e *TestExecutor) UpdateNodeDescription(newDesc *api.NodeDescription) { + e.mu.Lock() + defer e.mu.Unlock() + e.desc = newDesc +} + +// TestController is dummy channel based controller for tests. +type TestController struct { + ch chan struct{} + closeOnce sync.Once +} + +// Update does nothing. +func (t *TestController) Update(ctx context.Context, task *api.Task) error { + return nil +} + +// Prepare does nothing. +func (t *TestController) Prepare(ctx context.Context) error { + return nil +} + +// Start does nothing. +func (t *TestController) Start(ctx context.Context) error { + return nil +} + +// Wait waits on internal channel. +func (t *TestController) Wait(ctx context.Context) error { + select { + case <-t.ch: + case <-ctx.Done(): + } + return nil +} + +// Shutdown closes internal channel +func (t *TestController) Shutdown(ctx context.Context) error { + t.closeOnce.Do(func() { + close(t.ch) + }) + return nil +} + +// Terminate closes internal channel if it wasn't closed before. +func (t *TestController) Terminate(ctx context.Context) error { + t.closeOnce.Do(func() { + close(t.ch) + }) + return nil +} + +// Remove does nothing. +func (t *TestController) Remove(ctx context.Context) error { + return nil +} + +// Close does nothing. +func (t *TestController) Close() error { + t.closeOnce.Do(func() { + close(t.ch) + }) + return nil +} + +// MockDispatcher is a fake dispatcher that one agent at a time can connect to +type MockDispatcher struct { + mu sync.Mutex + sessionCh chan *api.SessionMessage + openSession *api.SessionRequest + closedSessions []*api.SessionRequest + + Addr string +} + +// UpdateTaskStatus is not implemented +func (m *MockDispatcher) UpdateTaskStatus(context.Context, *api.UpdateTaskStatusRequest) (*api.UpdateTaskStatusResponse, error) { + panic("not implemented") +} + +// Tasks keeps an open stream until canceled +func (m *MockDispatcher) Tasks(_ *api.TasksRequest, stream api.Dispatcher_TasksServer) error { + select { + case <-stream.Context().Done(): + } + return nil +} + +// Assignments keeps an open stream until canceled +func (m *MockDispatcher) Assignments(_ *api.AssignmentsRequest, stream api.Dispatcher_AssignmentsServer) error { + select { + case <-stream.Context().Done(): + } + return nil +} + +// Heartbeat always successfully heartbeats +func (m *MockDispatcher) Heartbeat(context.Context, *api.HeartbeatRequest) (*api.HeartbeatResponse, error) { + return &api.HeartbeatResponse{Period: time.Second * 5}, nil +} + +// Session allows a session to be established, and sends the node info +func (m *MockDispatcher) Session(r *api.SessionRequest, stream api.Dispatcher_SessionServer) error { + m.mu.Lock() + m.openSession = r + m.mu.Unlock() + defer func() { + m.mu.Lock() + defer m.mu.Unlock() + m.closedSessions = append(m.closedSessions, m.openSession) + m.openSession = nil + }() + + // send the initial message first + if err := stream.Send(&api.SessionMessage{ + SessionID: r.SessionID, + Managers: []*api.WeightedPeer{ + { + Peer: &api.Peer{Addr: m.Addr}, + }, + }, + }); err != nil { + return err + } + + ctx := stream.Context() + for { + select { + case msg := <-m.sessionCh: + msg.SessionID = r.SessionID + if err := stream.Send(msg); err != nil { + return err + } + case <-ctx.Done(): + return nil + } + } +} + +// GetSessions return all the established and closed sessions +func (m *MockDispatcher) GetSessions() (*api.SessionRequest, []*api.SessionRequest) { + m.mu.Lock() + defer m.mu.Unlock() + return m.openSession, m.closedSessions +} + +// SessionMessageChannel returns a writable channel to inject session messages +func (m *MockDispatcher) SessionMessageChannel() chan<- *api.SessionMessage { + return m.sessionCh +} + +// NewMockDispatcher starts and returns a mock dispatcher instance that can be connected to +func NewMockDispatcher(t *testing.T, secConfig *ca.SecurityConfig) (*MockDispatcher, func()) { + l, err := net.Listen("tcp", "127.0.0.1:0") + addr := l.Addr().String() + require.NoError(t, err) + + serverOpts := []grpc.ServerOption{grpc.Creds(secConfig.ServerTLSCreds)} + s := grpc.NewServer(serverOpts...) + + m := &MockDispatcher{ + Addr: addr, + sessionCh: make(chan *api.SessionMessage, 1), + } + api.RegisterDispatcherServer(s, m) + go s.Serve(l) + return m, s.Stop +} diff --git a/api/dispatcher.pb.go b/api/dispatcher.pb.go index ce8d7ddbe4..98be17ad9a 100644 --- a/api/dispatcher.pb.go +++ b/api/dispatcher.pb.go @@ -154,6 +154,8 @@ type SessionMessage struct { // Symmetric encryption key distributed by the lead manager. Used by agents // for securing network bootstrapping and communication. NetworkBootstrapKeys []*EncryptionKey `protobuf:"bytes,4,rep,name=network_bootstrap_keys,json=networkBootstrapKeys" json:"network_bootstrap_keys,omitempty"` + // Which root certificates to trust + RootCA []byte `protobuf:"bytes,5,opt,name=RootCA,proto3" json:"RootCA,omitempty"` } func (m *SessionMessage) Reset() { *m = SessionMessage{} } @@ -513,6 +515,10 @@ func (m *SessionMessage) CopyFrom(src interface{}) { } } + if o.RootCA != nil { + m.RootCA = make([]byte, len(o.RootCA)) + copy(m.RootCA, o.RootCA) + } } func (m *HeartbeatRequest) Copy() *HeartbeatRequest { @@ -1148,6 +1154,12 @@ func (m *SessionMessage) MarshalTo(dAtA []byte) (int, error) { i += n } } + if len(m.RootCA) > 0 { + dAtA[i] = 0x2a + i++ + i = encodeVarintDispatcher(dAtA, i, uint64(len(m.RootCA))) + i += copy(dAtA[i:], m.RootCA) + } return i, nil } @@ -1860,6 +1872,10 @@ func (m *SessionMessage) Size() (n int) { n += 1 + l + sovDispatcher(uint64(l)) } } + l = len(m.RootCA) + if l > 0 { + n += 1 + l + sovDispatcher(uint64(l)) + } return n } @@ -2045,6 +2061,7 @@ func (this *SessionMessage) String() string { `Node:` + strings.Replace(fmt.Sprintf("%v", this.Node), "Node", "Node", 1) + `,`, `Managers:` + strings.Replace(fmt.Sprintf("%v", this.Managers), "WeightedPeer", "WeightedPeer", 1) + `,`, `NetworkBootstrapKeys:` + strings.Replace(fmt.Sprintf("%v", this.NetworkBootstrapKeys), "EncryptionKey", "EncryptionKey", 1) + `,`, + `RootCA:` + fmt.Sprintf("%v", this.RootCA) + `,`, `}`, }, "") return s @@ -2457,6 +2474,37 @@ func (m *SessionMessage) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field RootCA", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDispatcher + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthDispatcher + } + postIndex := iNdEx + byteLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.RootCA = append(m.RootCA[:0], dAtA[iNdEx:postIndex]...) + if m.RootCA == nil { + m.RootCA = []byte{} + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipDispatcher(dAtA[iNdEx:]) @@ -3630,65 +3678,66 @@ var ( func init() { proto.RegisterFile("dispatcher.proto", fileDescriptorDispatcher) } var fileDescriptorDispatcher = []byte{ - // 949 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x9c, 0x55, 0xcd, 0x6e, 0xe3, 0x54, - 0x14, 0x8e, 0xd3, 0xd4, 0x6d, 0x4f, 0xda, 0x12, 0x2e, 0xa3, 0xc1, 0x58, 0x9a, 0x34, 0xb8, 0x4c, - 0x54, 0x69, 0x8a, 0x3b, 0x84, 0x9f, 0x0d, 0x55, 0xa1, 0x69, 0x22, 0x35, 0x9a, 0x49, 0xa7, 0xba, - 0xcd, 0xcc, 0x2c, 0x23, 0x27, 0x3e, 0xb8, 0x26, 0x8d, 0xaf, 0xf1, 0xbd, 0x99, 0x21, 0x48, 0x48, - 0x48, 0x30, 0x12, 0x62, 0x85, 0x58, 0x75, 0xc3, 0x2b, 0xf0, 0x1c, 0x15, 0x2b, 0x96, 0xac, 0x0a, - 0x93, 0x07, 0xe0, 0x01, 0x58, 0x21, 0xdb, 0xd7, 0x49, 0xc8, 0x24, 0x9d, 0xb4, 0xab, 0xc4, 0xe7, - 0x7c, 0xdf, 0x39, 0x9f, 0xbf, 0x7b, 0x7c, 0x2e, 0xe4, 0x6c, 0x97, 0xfb, 0x96, 0x68, 0x9f, 0x62, - 0x60, 0xfa, 0x01, 0x13, 0x8c, 0x10, 0x9b, 0xb5, 0x3b, 0x18, 0x98, 0xfc, 0xb9, 0x15, 0x74, 0x3b, - 0xae, 0x30, 0x9f, 0x7d, 0xa0, 0x67, 0x45, 0xdf, 0x47, 0x1e, 0x03, 0xf4, 0x35, 0xd6, 0xfa, 0x12, - 0xdb, 0x22, 0x79, 0xbc, 0xe5, 0x30, 0x87, 0x45, 0x7f, 0x77, 0xc2, 0x7f, 0x32, 0xfa, 0x96, 0x7f, - 0xd6, 0x73, 0x5c, 0x6f, 0x27, 0xfe, 0x91, 0xc1, 0xbc, 0xc3, 0x98, 0x73, 0x86, 0x3b, 0xd1, 0x53, - 0xab, 0xf7, 0xc5, 0x8e, 0xdd, 0x0b, 0x2c, 0xe1, 0x32, 0x99, 0x37, 0x5e, 0x28, 0xb0, 0x7e, 0x82, - 0x9c, 0xbb, 0xcc, 0xa3, 0xf8, 0x55, 0x0f, 0xb9, 0x20, 0x55, 0xc8, 0xda, 0xc8, 0xdb, 0x81, 0xeb, - 0x87, 0x38, 0x4d, 0x29, 0x28, 0x5b, 0xd9, 0xd2, 0xa6, 0xf9, 0xaa, 0x46, 0xf3, 0x88, 0xd9, 0x58, - 0x19, 0x41, 0xe9, 0x38, 0x8f, 0x6c, 0x03, 0xf0, 0xb8, 0x70, 0xd3, 0xb5, 0xb5, 0x74, 0x41, 0xd9, - 0x5a, 0x29, 0xaf, 0x0d, 0x2e, 0x37, 0x56, 0x64, 0xbb, 0x5a, 0x85, 0xae, 0x48, 0x40, 0xcd, 0x36, - 0xbe, 0x4f, 0x0f, 0x75, 0xd4, 0x91, 0x73, 0xcb, 0xc1, 0x89, 0x02, 0xca, 0xd5, 0x05, 0xc8, 0x36, - 0x64, 0x3c, 0x66, 0x63, 0xd4, 0x28, 0x5b, 0xd2, 0x66, 0xc9, 0xa5, 0x11, 0x8a, 0xec, 0xc2, 0x72, - 0xd7, 0xf2, 0x2c, 0x07, 0x03, 0xae, 0x2d, 0x14, 0x16, 0xb6, 0xb2, 0xa5, 0xc2, 0x34, 0xc6, 0x53, - 0x74, 0x9d, 0x53, 0x81, 0xf6, 0x31, 0x62, 0x40, 0x87, 0x0c, 0xf2, 0x14, 0x6e, 0x7b, 0x28, 0x9e, - 0xb3, 0xa0, 0xd3, 0x6c, 0x31, 0x26, 0xb8, 0x08, 0x2c, 0xbf, 0xd9, 0xc1, 0x3e, 0xd7, 0x32, 0x51, - 0xad, 0x77, 0xa7, 0xd5, 0xaa, 0x7a, 0xed, 0xa0, 0x1f, 0x59, 0xf3, 0x00, 0xfb, 0xf4, 0x96, 0x2c, - 0x50, 0x4e, 0xf8, 0x0f, 0xb0, 0xcf, 0x8d, 0xcf, 0x21, 0x77, 0x88, 0x56, 0x20, 0x5a, 0x68, 0x89, - 0xe4, 0x38, 0xae, 0x65, 0x83, 0x71, 0x0c, 0x6f, 0x8e, 0x55, 0xe0, 0x3e, 0xf3, 0x38, 0x92, 0x4f, - 0x41, 0xf5, 0x31, 0x70, 0x99, 0x2d, 0x0f, 0xf3, 0x1d, 0x33, 0x9e, 0x0a, 0x33, 0x99, 0x0a, 0xb3, - 0x22, 0xa7, 0xa2, 0xbc, 0x7c, 0x71, 0xb9, 0x91, 0x3a, 0xff, 0x6b, 0x43, 0xa1, 0x92, 0x62, 0xfc, - 0x9c, 0x86, 0xb7, 0x1f, 0xfb, 0xb6, 0x25, 0xb0, 0x61, 0xf1, 0xce, 0x89, 0xb0, 0x44, 0x8f, 0xdf, - 0x48, 0x1b, 0x79, 0x02, 0x4b, 0xbd, 0xa8, 0x50, 0xe2, 0xf9, 0xee, 0x34, 0x9f, 0x66, 0xf4, 0x32, - 0x47, 0x91, 0x18, 0x41, 0x93, 0x62, 0x3a, 0x83, 0xdc, 0x64, 0x92, 0x6c, 0xc2, 0x92, 0xb0, 0x78, - 0x67, 0x24, 0x0b, 0x06, 0x97, 0x1b, 0x6a, 0x08, 0xab, 0x55, 0xa8, 0x1a, 0xa6, 0x6a, 0x36, 0xf9, - 0x04, 0x54, 0x1e, 0x91, 0xe4, 0xd4, 0xe4, 0xa7, 0xe9, 0x19, 0x53, 0x22, 0xd1, 0x86, 0x0e, 0xda, - 0xab, 0x2a, 0x63, 0xaf, 0x8d, 0x5d, 0x58, 0x0d, 0xa3, 0x37, 0xb3, 0xc8, 0xd8, 0x93, 0xec, 0xe4, - 0x1b, 0x30, 0x61, 0x31, 0xd4, 0xca, 0x35, 0x25, 0x32, 0x4c, 0x9b, 0x25, 0x90, 0xc6, 0x30, 0xa3, - 0x0c, 0x64, 0x9f, 0x73, 0xd7, 0xf1, 0xba, 0xe8, 0x89, 0x1b, 0x6a, 0xf8, 0x06, 0x60, 0x54, 0x83, - 0x98, 0x90, 0x09, 0x4b, 0xcb, 0xc9, 0x99, 0x29, 0xe0, 0x30, 0x45, 0x23, 0x1c, 0xf9, 0x08, 0x54, - 0x8e, 0xed, 0x00, 0x85, 0xf4, 0x54, 0x9f, 0xc6, 0x38, 0x89, 0x10, 0x87, 0x29, 0x2a, 0xb1, 0x65, - 0x15, 0x32, 0xae, 0xc0, 0xae, 0xf1, 0x22, 0x0d, 0xb9, 0x51, 0xf3, 0x83, 0x53, 0xcb, 0x73, 0x90, - 0xec, 0x01, 0x58, 0xc3, 0x98, 0x14, 0x32, 0xf5, 0xa8, 0x46, 0x4c, 0x3a, 0xc6, 0x20, 0x75, 0x50, - 0xad, 0x76, 0xb4, 0xcb, 0x42, 0x49, 0xeb, 0xa5, 0x8f, 0xaf, 0xe6, 0xc6, 0x5d, 0xc7, 0x02, 0xfb, - 0x11, 0x99, 0xca, 0x22, 0x46, 0x6b, 0x5c, 0x62, 0x9c, 0x23, 0x45, 0x50, 0x1f, 0x1f, 0x57, 0xf6, - 0x1b, 0xd5, 0x5c, 0x4a, 0xd7, 0x7f, 0xfa, 0xb5, 0x70, 0x7b, 0x12, 0x21, 0xc7, 0xb2, 0x08, 0x2a, - 0xad, 0xd6, 0x1f, 0x3d, 0xa9, 0xe6, 0x94, 0xe9, 0x38, 0x8a, 0x5d, 0xf6, 0x0c, 0x8d, 0x7f, 0x95, - 0xff, 0x1d, 0x64, 0x32, 0x0e, 0x9f, 0x41, 0x26, 0xbc, 0x16, 0x22, 0x0f, 0xd6, 0x4b, 0xf7, 0xae, - 0x7e, 0x8f, 0x84, 0x65, 0x36, 0xfa, 0x3e, 0xd2, 0x88, 0x48, 0xee, 0x00, 0x58, 0xbe, 0x7f, 0xe6, - 0x22, 0x6f, 0x0a, 0x16, 0x2f, 0x65, 0xba, 0x22, 0x23, 0x0d, 0x16, 0xa6, 0x03, 0xe4, 0xbd, 0x33, - 0xc1, 0x9b, 0xae, 0xa7, 0x2d, 0xc4, 0x69, 0x19, 0xa9, 0x79, 0x64, 0x0f, 0x96, 0xda, 0x91, 0x39, - 0xc9, 0xa2, 0x7b, 0x6f, 0x1e, 0x27, 0x69, 0x42, 0x32, 0xee, 0x42, 0x26, 0xd4, 0x42, 0x56, 0x61, - 0xf9, 0xe0, 0x51, 0xfd, 0xf8, 0x61, 0x35, 0xf4, 0x8b, 0xbc, 0x01, 0xd9, 0xda, 0xd1, 0x01, 0xad, - 0xd6, 0xab, 0x47, 0x8d, 0xfd, 0x87, 0x39, 0xa5, 0x74, 0xbe, 0x08, 0x50, 0x19, 0xde, 0x91, 0xe4, - 0x6b, 0x58, 0x92, 0x73, 0x4a, 0x8c, 0xe9, 0xc3, 0x34, 0x7e, 0x7d, 0xe9, 0x57, 0x61, 0xa4, 0x23, - 0xc6, 0xe6, 0xef, 0xbf, 0xfd, 0x73, 0x9e, 0xbe, 0x03, 0xab, 0x11, 0xe6, 0xfd, 0x70, 0x11, 0x63, - 0x00, 0x6b, 0xf1, 0x93, 0x5c, 0xf3, 0xf7, 0x15, 0xf2, 0x2d, 0xac, 0x0c, 0x97, 0x29, 0x99, 0xfa, - 0xae, 0x93, 0xdb, 0x5a, 0xbf, 0xfb, 0x1a, 0x94, 0xdc, 0x12, 0xf3, 0x08, 0x20, 0xbf, 0x28, 0x90, - 0x9b, 0xdc, 0x33, 0xe4, 0xde, 0x35, 0x76, 0xa6, 0xbe, 0x3d, 0x1f, 0xf8, 0x3a, 0xa2, 0x7a, 0xb0, - 0x18, 0x6d, 0x28, 0x52, 0x98, 0xb5, 0x0a, 0x86, 0xdd, 0x67, 0x23, 0x92, 0x73, 0x28, 0xce, 0xd1, - 0xf1, 0xc7, 0xb4, 0x72, 0x5f, 0x21, 0x3f, 0x28, 0x90, 0x1d, 0x1b, 0x6d, 0x52, 0x7c, 0xcd, 0xec, - 0x27, 0x1a, 0x8a, 0xf3, 0x7d, 0x23, 0x73, 0x4e, 0x44, 0x59, 0xbb, 0x78, 0x99, 0x4f, 0xfd, 0xf9, - 0x32, 0x9f, 0xfa, 0x6e, 0x90, 0x57, 0x2e, 0x06, 0x79, 0xe5, 0x8f, 0x41, 0x5e, 0xf9, 0x7b, 0x90, - 0x57, 0x5a, 0x6a, 0x74, 0x97, 0x7e, 0xf8, 0x5f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x70, 0xa6, 0xd5, - 0x2c, 0xde, 0x09, 0x00, 0x00, + // 964 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x9c, 0x55, 0x4f, 0x6f, 0xe3, 0x44, + 0x14, 0xcf, 0x64, 0x53, 0xb7, 0x7d, 0x69, 0x4b, 0x18, 0x56, 0xc5, 0x58, 0xda, 0x34, 0xb8, 0x6c, + 0x55, 0x69, 0x8b, 0xbb, 0x84, 0x3f, 0x17, 0xaa, 0x42, 0xd2, 0x44, 0x6a, 0xb4, 0x9b, 0x6e, 0x35, + 0xcd, 0xee, 0x1e, 0x23, 0x27, 0x1e, 0x5c, 0x93, 0xc6, 0x63, 0x3c, 0x93, 0x5d, 0x82, 0x84, 0xc4, + 0x81, 0x95, 0x10, 0x27, 0xc4, 0xa9, 0x12, 0xe2, 0x2b, 0xf0, 0x39, 0x2a, 0x4e, 0x1c, 0x39, 0x15, + 0x36, 0x1f, 0x80, 0x0f, 0xc0, 0x09, 0x79, 0x3c, 0x4e, 0x42, 0x36, 0xe9, 0xa6, 0x3d, 0xd9, 0xf3, + 0xe6, 0xf7, 0x7b, 0xef, 0xe7, 0x37, 0xcf, 0xbf, 0x81, 0x9c, 0xe3, 0xf1, 0xc0, 0x16, 0xed, 0x53, + 0x1a, 0x5a, 0x41, 0xc8, 0x04, 0xc3, 0xd8, 0x61, 0xed, 0x0e, 0x0d, 0x2d, 0xfe, 0xdc, 0x0e, 0xbb, + 0x1d, 0x4f, 0x58, 0xcf, 0x3e, 0x30, 0xb2, 0xa2, 0x1f, 0x50, 0x1e, 0x03, 0x8c, 0x55, 0xd6, 0xfa, + 0x92, 0xb6, 0x45, 0xb2, 0xbc, 0xed, 0x32, 0x97, 0xc9, 0xd7, 0xdd, 0xe8, 0x4d, 0x45, 0xdf, 0x0a, + 0xce, 0x7a, 0xae, 0xe7, 0xef, 0xc6, 0x0f, 0x15, 0xcc, 0xbb, 0x8c, 0xb9, 0x67, 0x74, 0x57, 0xae, + 0x5a, 0xbd, 0x2f, 0x76, 0x9d, 0x5e, 0x68, 0x0b, 0x8f, 0xa9, 0x7d, 0xf3, 0x05, 0x82, 0xb5, 0x13, + 0xca, 0xb9, 0xc7, 0x7c, 0x42, 0xbf, 0xea, 0x51, 0x2e, 0x70, 0x15, 0xb2, 0x0e, 0xe5, 0xed, 0xd0, + 0x0b, 0x22, 0x9c, 0x8e, 0x0a, 0x68, 0x3b, 0x5b, 0xdc, 0xb4, 0x5e, 0xd5, 0x68, 0x1d, 0x31, 0x87, + 0x56, 0x46, 0x50, 0x32, 0xce, 0xc3, 0x3b, 0x00, 0x3c, 0x4e, 0xdc, 0xf4, 0x1c, 0x3d, 0x5d, 0x40, + 0xdb, 0xcb, 0xe5, 0xd5, 0xc1, 0xe5, 0xc6, 0xb2, 0x2a, 0x57, 0xab, 0x90, 0x65, 0x05, 0xa8, 0x39, + 0xe6, 0x2f, 0xe9, 0xa1, 0x8e, 0x3a, 0xe5, 0xdc, 0x76, 0xe9, 0x44, 0x02, 0x74, 0x75, 0x02, 0xbc, + 0x03, 0x19, 0x9f, 0x39, 0x54, 0x16, 0xca, 0x16, 0xf5, 0x59, 0x72, 0x89, 0x44, 0xe1, 0x3d, 0x58, + 0xea, 0xda, 0xbe, 0xed, 0xd2, 0x90, 0xeb, 0xb7, 0x0a, 0xb7, 0xb6, 0xb3, 0xc5, 0xc2, 0x34, 0xc6, + 0x53, 0xea, 0xb9, 0xa7, 0x82, 0x3a, 0xc7, 0x94, 0x86, 0x64, 0xc8, 0xc0, 0x4f, 0x61, 0xdd, 0xa7, + 0xe2, 0x39, 0x0b, 0x3b, 0xcd, 0x16, 0x63, 0x82, 0x8b, 0xd0, 0x0e, 0x9a, 0x1d, 0xda, 0xe7, 0x7a, + 0x46, 0xe6, 0x7a, 0x77, 0x5a, 0xae, 0xaa, 0xdf, 0x0e, 0xfb, 0xb2, 0x35, 0x0f, 0x68, 0x9f, 0xdc, + 0x56, 0x09, 0xca, 0x09, 0xff, 0x01, 0xed, 0x73, 0xbc, 0x0e, 0x1a, 0x61, 0x4c, 0x1c, 0x94, 0xf4, + 0x85, 0x02, 0xda, 0x5e, 0x21, 0x6a, 0x65, 0x7e, 0x0e, 0xb9, 0x43, 0x6a, 0x87, 0xa2, 0x45, 0x6d, + 0x91, 0x1c, 0xd3, 0xb5, 0xda, 0x63, 0x1e, 0xc3, 0x9b, 0x63, 0x19, 0x78, 0xc0, 0x7c, 0x4e, 0xf1, + 0xa7, 0xa0, 0x05, 0x34, 0xf4, 0x98, 0xa3, 0x0e, 0xf9, 0x1d, 0x2b, 0x9e, 0x16, 0x2b, 0x99, 0x16, + 0xab, 0xa2, 0xa6, 0xa5, 0xbc, 0x74, 0x71, 0xb9, 0x91, 0x3a, 0xff, 0x6b, 0x03, 0x11, 0x45, 0x31, + 0x7f, 0x4a, 0xc3, 0xdb, 0x8f, 0x03, 0xc7, 0x16, 0xb4, 0x61, 0xf3, 0xce, 0x89, 0xb0, 0x45, 0x8f, + 0xdf, 0x48, 0x1b, 0x7e, 0x02, 0x8b, 0x3d, 0x99, 0x28, 0x39, 0x8b, 0xbd, 0x69, 0xfd, 0x9b, 0x51, + 0xcb, 0x1a, 0x45, 0x62, 0x04, 0x49, 0x92, 0x19, 0x0c, 0x72, 0x93, 0x9b, 0x78, 0x13, 0x16, 0x85, + 0xcd, 0x3b, 0x23, 0x59, 0x30, 0xb8, 0xdc, 0xd0, 0x22, 0x58, 0xad, 0x42, 0xb4, 0x68, 0xab, 0xe6, + 0xe0, 0x4f, 0x40, 0xe3, 0x92, 0xa4, 0xa6, 0x29, 0x3f, 0x4d, 0xcf, 0x98, 0x12, 0x85, 0x36, 0x0d, + 0xd0, 0x5f, 0x55, 0x19, 0xf7, 0xda, 0xdc, 0x83, 0x95, 0x28, 0x7a, 0xb3, 0x16, 0x99, 0xfb, 0x8a, + 0x9d, 0xfc, 0x1b, 0x16, 0x2c, 0x44, 0x5a, 0xb9, 0x8e, 0x64, 0xc3, 0xf4, 0x59, 0x02, 0x49, 0x0c, + 0x33, 0xcb, 0x80, 0x4b, 0x9c, 0x7b, 0xae, 0xdf, 0xa5, 0xbe, 0xb8, 0xa1, 0x86, 0x6f, 0x00, 0x46, + 0x39, 0xb0, 0x05, 0x99, 0x28, 0xb5, 0x9a, 0x9c, 0x99, 0x02, 0x0e, 0x53, 0x44, 0xe2, 0xf0, 0x47, + 0xa0, 0x71, 0xda, 0x0e, 0xa9, 0x50, 0x3d, 0x35, 0xa6, 0x31, 0x4e, 0x24, 0xe2, 0x30, 0x45, 0x14, + 0xb6, 0xac, 0x41, 0xc6, 0x13, 0xb4, 0x6b, 0xbe, 0x48, 0x43, 0x6e, 0x54, 0xfc, 0xe0, 0xd4, 0xf6, + 0x5d, 0x8a, 0xf7, 0x01, 0xec, 0x61, 0x4c, 0x09, 0x99, 0x7a, 0x54, 0x23, 0x26, 0x19, 0x63, 0xe0, + 0x3a, 0x68, 0x76, 0x5b, 0x7a, 0x5c, 0x24, 0x69, 0xad, 0xf8, 0xf1, 0xd5, 0xdc, 0xb8, 0xea, 0x58, + 0xa0, 0x24, 0xc9, 0x44, 0x25, 0x31, 0x5b, 0xe3, 0x12, 0xe3, 0x3d, 0xbc, 0x05, 0xda, 0xe3, 0xe3, + 0x4a, 0xa9, 0x51, 0xcd, 0xa5, 0x0c, 0xe3, 0xc7, 0x5f, 0x0b, 0xeb, 0x93, 0x08, 0x35, 0x96, 0x5b, + 0xa0, 0x91, 0x6a, 0xfd, 0xd1, 0x93, 0x6a, 0x0e, 0x4d, 0xc7, 0x11, 0xda, 0x65, 0xcf, 0xa8, 0xf9, + 0x2f, 0xfa, 0xdf, 0x41, 0x26, 0xe3, 0xf0, 0x19, 0x64, 0xa2, 0xeb, 0x42, 0xf6, 0x60, 0xad, 0x78, + 0xef, 0xea, 0xef, 0x48, 0x58, 0x56, 0xa3, 0x1f, 0x50, 0x22, 0x89, 0xf8, 0x0e, 0x80, 0x1d, 0x04, + 0x67, 0x1e, 0xe5, 0x4d, 0xc1, 0x62, 0xb3, 0x26, 0xcb, 0x2a, 0xd2, 0x60, 0xd1, 0x76, 0x48, 0x79, + 0xef, 0x4c, 0xf0, 0xa6, 0xe7, 0xeb, 0xb7, 0xe2, 0x6d, 0x15, 0xa9, 0xf9, 0x78, 0x1f, 0x16, 0xdb, + 0xb2, 0x39, 0x89, 0x01, 0xbe, 0x37, 0x4f, 0x27, 0x49, 0x42, 0x32, 0xef, 0x42, 0x26, 0xd2, 0x82, + 0x57, 0x60, 0xe9, 0xe0, 0x51, 0xfd, 0xf8, 0x61, 0x35, 0xea, 0x17, 0x7e, 0x03, 0xb2, 0xb5, 0xa3, + 0x03, 0x52, 0xad, 0x57, 0x8f, 0x1a, 0xa5, 0x87, 0x39, 0x54, 0x3c, 0x5f, 0x00, 0xa8, 0x0c, 0xef, + 0x4e, 0xfc, 0x35, 0x2c, 0xaa, 0x39, 0xc5, 0xe6, 0xf4, 0x61, 0x1a, 0xbf, 0xd6, 0x8c, 0xab, 0x30, + 0xaa, 0x23, 0xe6, 0xe6, 0xef, 0xbf, 0xfd, 0x73, 0x9e, 0xbe, 0x03, 0x2b, 0x12, 0xf3, 0x7e, 0x64, + 0xd0, 0x34, 0x84, 0xd5, 0x78, 0xa5, 0xec, 0xff, 0x3e, 0xc2, 0xdf, 0xc2, 0xf2, 0xd0, 0x4c, 0xf1, + 0xd4, 0x6f, 0x9d, 0x74, 0x6b, 0xe3, 0xee, 0x6b, 0x50, 0xca, 0x25, 0xe6, 0x11, 0x80, 0x7f, 0x46, + 0x90, 0x9b, 0xf4, 0x19, 0x7c, 0xef, 0x1a, 0x9e, 0x69, 0xec, 0xcc, 0x07, 0xbe, 0x8e, 0xa8, 0x1e, + 0x2c, 0x48, 0x87, 0xc2, 0x85, 0x59, 0x56, 0x30, 0xac, 0x3e, 0x1b, 0x91, 0x9c, 0xc3, 0xd6, 0x1c, + 0x15, 0x7f, 0x48, 0xa3, 0xfb, 0x08, 0x7f, 0x8f, 0x20, 0x3b, 0x36, 0xda, 0x78, 0xeb, 0x35, 0xb3, + 0x9f, 0x68, 0xd8, 0x9a, 0xef, 0x1f, 0x99, 0x73, 0x22, 0xca, 0xfa, 0xc5, 0xcb, 0x7c, 0xea, 0xcf, + 0x97, 0xf9, 0xd4, 0x77, 0x83, 0x3c, 0xba, 0x18, 0xe4, 0xd1, 0x1f, 0x83, 0x3c, 0xfa, 0x7b, 0x90, + 0x47, 0x2d, 0x4d, 0xde, 0xa5, 0x1f, 0xfe, 0x17, 0x00, 0x00, 0xff, 0xff, 0xdb, 0x8f, 0x76, 0x01, + 0xf6, 0x09, 0x00, 0x00, } diff --git a/api/dispatcher.proto b/api/dispatcher.proto index ffa2464a48..3045ca6745 100644 --- a/api/dispatcher.proto +++ b/api/dispatcher.proto @@ -126,6 +126,9 @@ message SessionMessage { // Symmetric encryption key distributed by the lead manager. Used by agents // for securing network bootstrapping and communication. repeated EncryptionKey network_bootstrap_keys = 4; + + // Which root certificates to trust + bytes RootCA = 5; } // HeartbeatRequest provides identifying properties for a single heartbeat. diff --git a/api/types.pb.go b/api/types.pb.go index 271c1794a0..958d73f091 100644 --- a/api/types.pb.go +++ b/api/types.pb.go @@ -29,6 +29,7 @@ PluginDescription EngineDescription NodeDescription + NodeTLSInfo RaftMemberStatus NodeStatus Image @@ -336,7 +337,7 @@ func (x RaftMemberStatus_Reachability) String() string { return proto.EnumName(RaftMemberStatus_Reachability_name, int32(x)) } func (RaftMemberStatus_Reachability) EnumDescriptor() ([]byte, []int) { - return fileDescriptorTypes, []int{9, 0} + return fileDescriptorTypes, []int{10, 0} } // TODO(aluzzardi) These should be using `gogoproto.enumvalue_customname`. @@ -369,7 +370,7 @@ var NodeStatus_State_value = map[string]int32{ func (x NodeStatus_State) String() string { return proto.EnumName(NodeStatus_State_name, int32(x)) } -func (NodeStatus_State) EnumDescriptor() ([]byte, []int) { return fileDescriptorTypes, []int{10, 0} } +func (NodeStatus_State) EnumDescriptor() ([]byte, []int) { return fileDescriptorTypes, []int{11, 0} } type Mount_MountType int32 @@ -393,7 +394,7 @@ var Mount_MountType_value = map[string]int32{ func (x Mount_MountType) String() string { return proto.EnumName(Mount_MountType_name, int32(x)) } -func (Mount_MountType) EnumDescriptor() ([]byte, []int) { return fileDescriptorTypes, []int{12, 0} } +func (Mount_MountType) EnumDescriptor() ([]byte, []int) { return fileDescriptorTypes, []int{13, 0} } type Mount_BindOptions_MountPropagation int32 @@ -427,7 +428,7 @@ func (x Mount_BindOptions_MountPropagation) String() string { return proto.EnumName(Mount_BindOptions_MountPropagation_name, int32(x)) } func (Mount_BindOptions_MountPropagation) EnumDescriptor() ([]byte, []int) { - return fileDescriptorTypes, []int{12, 0, 0} + return fileDescriptorTypes, []int{13, 0, 0} } type RestartPolicy_RestartCondition int32 @@ -453,7 +454,7 @@ func (x RestartPolicy_RestartCondition) String() string { return proto.EnumName(RestartPolicy_RestartCondition_name, int32(x)) } func (RestartPolicy_RestartCondition) EnumDescriptor() ([]byte, []int) { - return fileDescriptorTypes, []int{13, 0} + return fileDescriptorTypes, []int{14, 0} } type UpdateConfig_FailureAction int32 @@ -479,7 +480,7 @@ func (x UpdateConfig_FailureAction) String() string { return proto.EnumName(UpdateConfig_FailureAction_name, int32(x)) } func (UpdateConfig_FailureAction) EnumDescriptor() ([]byte, []int) { - return fileDescriptorTypes, []int{14, 0} + return fileDescriptorTypes, []int{15, 0} } // UpdateOrder controls the order of operations when rolling out an @@ -506,7 +507,7 @@ func (x UpdateConfig_UpdateOrder) String() string { return proto.EnumName(UpdateConfig_UpdateOrder_name, int32(x)) } func (UpdateConfig_UpdateOrder) EnumDescriptor() ([]byte, []int) { - return fileDescriptorTypes, []int{14, 1} + return fileDescriptorTypes, []int{15, 1} } type UpdateStatus_UpdateState int32 @@ -544,7 +545,7 @@ func (x UpdateStatus_UpdateState) String() string { return proto.EnumName(UpdateStatus_UpdateState_name, int32(x)) } func (UpdateStatus_UpdateState) EnumDescriptor() ([]byte, []int) { - return fileDescriptorTypes, []int{15, 0} + return fileDescriptorTypes, []int{16, 0} } // AddressFamily specifies the network address family that @@ -572,7 +573,7 @@ func (x IPAMConfig_AddressFamily) String() string { return proto.EnumName(IPAMConfig_AddressFamily_name, int32(x)) } func (IPAMConfig_AddressFamily) EnumDescriptor() ([]byte, []int) { - return fileDescriptorTypes, []int{20, 0} + return fileDescriptorTypes, []int{21, 0} } type PortConfig_Protocol int32 @@ -594,7 +595,7 @@ var PortConfig_Protocol_value = map[string]int32{ func (x PortConfig_Protocol) String() string { return proto.EnumName(PortConfig_Protocol_name, int32(x)) } -func (PortConfig_Protocol) EnumDescriptor() ([]byte, []int) { return fileDescriptorTypes, []int{21, 0} } +func (PortConfig_Protocol) EnumDescriptor() ([]byte, []int) { return fileDescriptorTypes, []int{22, 0} } // PublishMode controls how ports are published on the swarm. type PortConfig_PublishMode int32 @@ -622,7 +623,7 @@ func (x PortConfig_PublishMode) String() string { return proto.EnumName(PortConfig_PublishMode_name, int32(x)) } func (PortConfig_PublishMode) EnumDescriptor() ([]byte, []int) { - return fileDescriptorTypes, []int{21, 1} + return fileDescriptorTypes, []int{22, 1} } type IssuanceStatus_State int32 @@ -662,7 +663,7 @@ var IssuanceStatus_State_value = map[string]int32{ func (x IssuanceStatus_State) String() string { return proto.EnumName(IssuanceStatus_State_name, int32(x)) } -func (IssuanceStatus_State) EnumDescriptor() ([]byte, []int) { return fileDescriptorTypes, []int{26, 0} } +func (IssuanceStatus_State) EnumDescriptor() ([]byte, []int) { return fileDescriptorTypes, []int{27, 0} } type ExternalCA_CAProtocol int32 @@ -681,7 +682,7 @@ func (x ExternalCA_CAProtocol) String() string { return proto.EnumName(ExternalCA_CAProtocol_name, int32(x)) } func (ExternalCA_CAProtocol) EnumDescriptor() ([]byte, []int) { - return fileDescriptorTypes, []int{28, 0} + return fileDescriptorTypes, []int{29, 0} } // Encryption algorithm that can implemented using this key @@ -702,7 +703,7 @@ func (x EncryptionKey_Algorithm) String() string { return proto.EnumName(EncryptionKey_Algorithm_name, int32(x)) } func (EncryptionKey_Algorithm) EnumDescriptor() ([]byte, []int) { - return fileDescriptorTypes, []int{41, 0} + return fileDescriptorTypes, []int{42, 0} } type MaybeEncryptedRecord_Algorithm int32 @@ -725,7 +726,7 @@ func (x MaybeEncryptedRecord_Algorithm) String() string { return proto.EnumName(MaybeEncryptedRecord_Algorithm_name, int32(x)) } func (MaybeEncryptedRecord_Algorithm) EnumDescriptor() ([]byte, []int) { - return fileDescriptorTypes, []int{46, 0} + return fileDescriptorTypes, []int{47, 0} } // Version tracks the last time an object in the store was updated. @@ -828,12 +829,26 @@ type NodeDescription struct { Resources *Resources `protobuf:"bytes,3,opt,name=resources" json:"resources,omitempty"` // Information about the Docker Engine on the node. Engine *EngineDescription `protobuf:"bytes,4,opt,name=engine" json:"engine,omitempty"` + // Information on the node's TLS setup + TLSInfo *NodeTLSInfo `protobuf:"bytes,5,opt,name=tls_info,json=tlsInfo" json:"tls_info,omitempty"` } func (m *NodeDescription) Reset() { *m = NodeDescription{} } func (*NodeDescription) ProtoMessage() {} func (*NodeDescription) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{8} } +type NodeTLSInfo struct { + // Information about which root certs the node trusts + TrustRoot []byte `protobuf:"bytes,1,opt,name=trust_root,json=trustRoot,proto3" json:"trust_root,omitempty"` + // Information about the node's current TLS certificate + CertIssuerSubject []byte `protobuf:"bytes,2,opt,name=cert_issuer_subject,json=certIssuerSubject,proto3" json:"cert_issuer_subject,omitempty"` + CertIssuerPublicKey []byte `protobuf:"bytes,3,opt,name=cert_issuer_public_key,json=certIssuerPublicKey,proto3" json:"cert_issuer_public_key,omitempty"` +} + +func (m *NodeTLSInfo) Reset() { *m = NodeTLSInfo{} } +func (*NodeTLSInfo) ProtoMessage() {} +func (*NodeTLSInfo) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{9} } + type RaftMemberStatus struct { Leader bool `protobuf:"varint,1,opt,name=leader,proto3" json:"leader,omitempty"` Reachability RaftMemberStatus_Reachability `protobuf:"varint,2,opt,name=reachability,proto3,enum=docker.swarmkit.v1.RaftMemberStatus_Reachability" json:"reachability,omitempty"` @@ -842,7 +857,7 @@ type RaftMemberStatus struct { func (m *RaftMemberStatus) Reset() { *m = RaftMemberStatus{} } func (*RaftMemberStatus) ProtoMessage() {} -func (*RaftMemberStatus) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{9} } +func (*RaftMemberStatus) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{10} } type NodeStatus struct { State NodeStatus_State `protobuf:"varint,1,opt,name=state,proto3,enum=docker.swarmkit.v1.NodeStatus_State" json:"state,omitempty"` @@ -853,7 +868,7 @@ type NodeStatus struct { func (m *NodeStatus) Reset() { *m = NodeStatus{} } func (*NodeStatus) ProtoMessage() {} -func (*NodeStatus) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{10} } +func (*NodeStatus) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{11} } type Image struct { // reference is a docker image reference. This can include a rpository, tag @@ -864,7 +879,7 @@ type Image struct { func (m *Image) Reset() { *m = Image{} } func (*Image) ProtoMessage() {} -func (*Image) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{11} } +func (*Image) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{12} } // Mount describes volume mounts for a container. // @@ -899,7 +914,7 @@ type Mount struct { func (m *Mount) Reset() { *m = Mount{} } func (*Mount) ProtoMessage() {} -func (*Mount) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{12} } +func (*Mount) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{13} } // BindOptions specifies options that are specific to a bind mount. type Mount_BindOptions struct { @@ -909,7 +924,7 @@ type Mount_BindOptions struct { func (m *Mount_BindOptions) Reset() { *m = Mount_BindOptions{} } func (*Mount_BindOptions) ProtoMessage() {} -func (*Mount_BindOptions) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{12, 0} } +func (*Mount_BindOptions) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{13, 0} } // VolumeOptions contains parameters for mounting the volume. type Mount_VolumeOptions struct { @@ -926,7 +941,7 @@ type Mount_VolumeOptions struct { func (m *Mount_VolumeOptions) Reset() { *m = Mount_VolumeOptions{} } func (*Mount_VolumeOptions) ProtoMessage() {} -func (*Mount_VolumeOptions) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{12, 1} } +func (*Mount_VolumeOptions) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{13, 1} } type Mount_TmpfsOptions struct { // Size sets the size of the tmpfs, in bytes. @@ -944,7 +959,7 @@ type Mount_TmpfsOptions struct { func (m *Mount_TmpfsOptions) Reset() { *m = Mount_TmpfsOptions{} } func (*Mount_TmpfsOptions) ProtoMessage() {} -func (*Mount_TmpfsOptions) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{12, 2} } +func (*Mount_TmpfsOptions) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{13, 2} } type RestartPolicy struct { Condition RestartPolicy_RestartCondition `protobuf:"varint,1,opt,name=condition,proto3,enum=docker.swarmkit.v1.RestartPolicy_RestartCondition" json:"condition,omitempty"` @@ -962,7 +977,7 @@ type RestartPolicy struct { func (m *RestartPolicy) Reset() { *m = RestartPolicy{} } func (*RestartPolicy) ProtoMessage() {} -func (*RestartPolicy) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{13} } +func (*RestartPolicy) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{14} } // UpdateConfig specifies the rate and policy of updates. // TODO(aluzzardi): Consider making this a oneof with RollingStrategy and LockstepStrategy. @@ -1002,7 +1017,7 @@ type UpdateConfig struct { func (m *UpdateConfig) Reset() { *m = UpdateConfig{} } func (*UpdateConfig) ProtoMessage() {} -func (*UpdateConfig) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{14} } +func (*UpdateConfig) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{15} } // UpdateStatus is the status of an update in progress. type UpdateStatus struct { @@ -1026,7 +1041,7 @@ type UpdateStatus struct { func (m *UpdateStatus) Reset() { *m = UpdateStatus{} } func (*UpdateStatus) ProtoMessage() {} -func (*UpdateStatus) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{15} } +func (*UpdateStatus) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{16} } // Container specific status. type ContainerStatus struct { @@ -1037,7 +1052,7 @@ type ContainerStatus struct { func (m *ContainerStatus) Reset() { *m = ContainerStatus{} } func (*ContainerStatus) ProtoMessage() {} -func (*ContainerStatus) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{16} } +func (*ContainerStatus) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{17} } // PortStatus specifies the actual allocated runtime state of a list // of port configs. @@ -1047,7 +1062,7 @@ type PortStatus struct { func (m *PortStatus) Reset() { *m = PortStatus{} } func (*PortStatus) ProtoMessage() {} -func (*PortStatus) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{17} } +func (*PortStatus) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{18} } type TaskStatus struct { // Note: can't use stdtime because this field is nullable. @@ -1084,7 +1099,7 @@ type TaskStatus struct { func (m *TaskStatus) Reset() { *m = TaskStatus{} } func (*TaskStatus) ProtoMessage() {} -func (*TaskStatus) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{18} } +func (*TaskStatus) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{19} } type isTaskStatus_RuntimeStatus interface { isTaskStatus_RuntimeStatus() @@ -1186,7 +1201,7 @@ type NetworkAttachmentConfig struct { func (m *NetworkAttachmentConfig) Reset() { *m = NetworkAttachmentConfig{} } func (*NetworkAttachmentConfig) ProtoMessage() {} -func (*NetworkAttachmentConfig) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{19} } +func (*NetworkAttachmentConfig) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{20} } // IPAMConfig specifies parameters for IP Address Management. type IPAMConfig struct { @@ -1207,7 +1222,7 @@ type IPAMConfig struct { func (m *IPAMConfig) Reset() { *m = IPAMConfig{} } func (*IPAMConfig) ProtoMessage() {} -func (*IPAMConfig) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{20} } +func (*IPAMConfig) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{21} } // PortConfig specifies an exposed port which can be // addressed using the given name. This can be later queried @@ -1233,7 +1248,7 @@ type PortConfig struct { func (m *PortConfig) Reset() { *m = PortConfig{} } func (*PortConfig) ProtoMessage() {} -func (*PortConfig) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{21} } +func (*PortConfig) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{22} } // Driver is a generic driver type to be used throughout the API. For now, a // driver is simply a name and set of options. The field contents depend on the @@ -1246,7 +1261,7 @@ type Driver struct { func (m *Driver) Reset() { *m = Driver{} } func (*Driver) ProtoMessage() {} -func (*Driver) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{22} } +func (*Driver) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{23} } type IPAMOptions struct { Driver *Driver `protobuf:"bytes,1,opt,name=driver" json:"driver,omitempty"` @@ -1255,7 +1270,7 @@ type IPAMOptions struct { func (m *IPAMOptions) Reset() { *m = IPAMOptions{} } func (*IPAMOptions) ProtoMessage() {} -func (*IPAMOptions) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{23} } +func (*IPAMOptions) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{24} } // Peer should be used anywhere where we are describing a remote peer. type Peer struct { @@ -1265,7 +1280,7 @@ type Peer struct { func (m *Peer) Reset() { *m = Peer{} } func (*Peer) ProtoMessage() {} -func (*Peer) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{24} } +func (*Peer) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{25} } // WeightedPeer should be used anywhere where we are describing a remote peer // with a weight. @@ -1276,7 +1291,7 @@ type WeightedPeer struct { func (m *WeightedPeer) Reset() { *m = WeightedPeer{} } func (*WeightedPeer) ProtoMessage() {} -func (*WeightedPeer) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{25} } +func (*WeightedPeer) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{26} } type IssuanceStatus struct { State IssuanceStatus_State `protobuf:"varint,1,opt,name=state,proto3,enum=docker.swarmkit.v1.IssuanceStatus_State" json:"state,omitempty"` @@ -1288,7 +1303,7 @@ type IssuanceStatus struct { func (m *IssuanceStatus) Reset() { *m = IssuanceStatus{} } func (*IssuanceStatus) ProtoMessage() {} -func (*IssuanceStatus) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{26} } +func (*IssuanceStatus) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{27} } type AcceptancePolicy struct { Policies []*AcceptancePolicy_RoleAdmissionPolicy `protobuf:"bytes,1,rep,name=policies" json:"policies,omitempty"` @@ -1296,7 +1311,7 @@ type AcceptancePolicy struct { func (m *AcceptancePolicy) Reset() { *m = AcceptancePolicy{} } func (*AcceptancePolicy) ProtoMessage() {} -func (*AcceptancePolicy) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{27} } +func (*AcceptancePolicy) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{28} } type AcceptancePolicy_RoleAdmissionPolicy struct { Role NodeRole `protobuf:"varint,1,opt,name=role,proto3,enum=docker.swarmkit.v1.NodeRole" json:"role,omitempty"` @@ -1311,7 +1326,7 @@ type AcceptancePolicy_RoleAdmissionPolicy struct { func (m *AcceptancePolicy_RoleAdmissionPolicy) Reset() { *m = AcceptancePolicy_RoleAdmissionPolicy{} } func (*AcceptancePolicy_RoleAdmissionPolicy) ProtoMessage() {} func (*AcceptancePolicy_RoleAdmissionPolicy) Descriptor() ([]byte, []int) { - return fileDescriptorTypes, []int{27, 0} + return fileDescriptorTypes, []int{28, 0} } type AcceptancePolicy_RoleAdmissionPolicy_Secret struct { @@ -1326,7 +1341,7 @@ func (m *AcceptancePolicy_RoleAdmissionPolicy_Secret) Reset() { } func (*AcceptancePolicy_RoleAdmissionPolicy_Secret) ProtoMessage() {} func (*AcceptancePolicy_RoleAdmissionPolicy_Secret) Descriptor() ([]byte, []int) { - return fileDescriptorTypes, []int{27, 0, 0} + return fileDescriptorTypes, []int{28, 0, 0} } type ExternalCA struct { @@ -1343,7 +1358,7 @@ type ExternalCA struct { func (m *ExternalCA) Reset() { *m = ExternalCA{} } func (*ExternalCA) ProtoMessage() {} -func (*ExternalCA) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{28} } +func (*ExternalCA) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{29} } type CAConfig struct { // NodeCertExpiry is the duration certificates should be issued for @@ -1368,7 +1383,7 @@ type CAConfig struct { func (m *CAConfig) Reset() { *m = CAConfig{} } func (*CAConfig) ProtoMessage() {} -func (*CAConfig) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{29} } +func (*CAConfig) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{30} } // OrchestrationConfig defines cluster-level orchestration settings. type OrchestrationConfig struct { @@ -1379,7 +1394,7 @@ type OrchestrationConfig struct { func (m *OrchestrationConfig) Reset() { *m = OrchestrationConfig{} } func (*OrchestrationConfig) ProtoMessage() {} -func (*OrchestrationConfig) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{30} } +func (*OrchestrationConfig) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{31} } // TaskDefaults specifies default values for task creation. type TaskDefaults struct { @@ -1393,7 +1408,7 @@ type TaskDefaults struct { func (m *TaskDefaults) Reset() { *m = TaskDefaults{} } func (*TaskDefaults) ProtoMessage() {} -func (*TaskDefaults) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{31} } +func (*TaskDefaults) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{32} } // DispatcherConfig defines cluster-level dispatcher settings. type DispatcherConfig struct { @@ -1405,7 +1420,7 @@ type DispatcherConfig struct { func (m *DispatcherConfig) Reset() { *m = DispatcherConfig{} } func (*DispatcherConfig) ProtoMessage() {} -func (*DispatcherConfig) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{32} } +func (*DispatcherConfig) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{33} } // RaftConfig defines raft settings for the cluster. type RaftConfig struct { @@ -1427,7 +1442,7 @@ type RaftConfig struct { func (m *RaftConfig) Reset() { *m = RaftConfig{} } func (*RaftConfig) ProtoMessage() {} -func (*RaftConfig) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{33} } +func (*RaftConfig) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{34} } type EncryptionConfig struct { // AutoLockManagers specifies whether or not managers TLS keys and raft data @@ -1438,7 +1453,7 @@ type EncryptionConfig struct { func (m *EncryptionConfig) Reset() { *m = EncryptionConfig{} } func (*EncryptionConfig) ProtoMessage() {} -func (*EncryptionConfig) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{34} } +func (*EncryptionConfig) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{35} } type SpreadOver struct { SpreadDescriptor string `protobuf:"bytes,1,opt,name=spread_descriptor,json=spreadDescriptor,proto3" json:"spread_descriptor,omitempty"` @@ -1446,7 +1461,7 @@ type SpreadOver struct { func (m *SpreadOver) Reset() { *m = SpreadOver{} } func (*SpreadOver) ProtoMessage() {} -func (*SpreadOver) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{35} } +func (*SpreadOver) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{36} } type PlacementPreference struct { // Types that are valid to be assigned to Preference: @@ -1456,7 +1471,7 @@ type PlacementPreference struct { func (m *PlacementPreference) Reset() { *m = PlacementPreference{} } func (*PlacementPreference) ProtoMessage() {} -func (*PlacementPreference) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{36} } +func (*PlacementPreference) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{37} } type isPlacementPreference_Preference interface { isPlacementPreference_Preference() @@ -1551,7 +1566,7 @@ type Placement struct { func (m *Placement) Reset() { *m = Placement{} } func (*Placement) ProtoMessage() {} -func (*Placement) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{37} } +func (*Placement) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{38} } // JoinToken contains the join tokens for workers and managers. type JoinTokens struct { @@ -1563,7 +1578,7 @@ type JoinTokens struct { func (m *JoinTokens) Reset() { *m = JoinTokens{} } func (*JoinTokens) ProtoMessage() {} -func (*JoinTokens) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{38} } +func (*JoinTokens) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{39} } type RootCA struct { // CAKey is the root CA private key. @@ -1584,7 +1599,7 @@ type RootCA struct { func (m *RootCA) Reset() { *m = RootCA{} } func (*RootCA) ProtoMessage() {} -func (*RootCA) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{39} } +func (*RootCA) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{40} } type Certificate struct { Role NodeRole `protobuf:"varint,1,opt,name=role,proto3,enum=docker.swarmkit.v1.NodeRole" json:"role,omitempty"` @@ -1597,7 +1612,7 @@ type Certificate struct { func (m *Certificate) Reset() { *m = Certificate{} } func (*Certificate) ProtoMessage() {} -func (*Certificate) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{40} } +func (*Certificate) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{41} } // Symmetric keys to encrypt inter-agent communication. type EncryptionKey struct { @@ -1613,7 +1628,7 @@ type EncryptionKey struct { func (m *EncryptionKey) Reset() { *m = EncryptionKey{} } func (*EncryptionKey) ProtoMessage() {} -func (*EncryptionKey) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{41} } +func (*EncryptionKey) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{42} } // ManagerStatus provides informations about the state of a manager in the cluster. type ManagerStatus struct { @@ -1630,7 +1645,7 @@ type ManagerStatus struct { func (m *ManagerStatus) Reset() { *m = ManagerStatus{} } func (*ManagerStatus) ProtoMessage() {} -func (*ManagerStatus) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{42} } +func (*ManagerStatus) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{43} } // SecretReference is the linkage between a service and a secret that it uses. type SecretReference struct { @@ -1650,7 +1665,7 @@ type SecretReference struct { func (m *SecretReference) Reset() { *m = SecretReference{} } func (*SecretReference) ProtoMessage() {} -func (*SecretReference) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{43} } +func (*SecretReference) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{44} } type isSecretReference_Target interface { isSecretReference_Target() @@ -1748,7 +1763,7 @@ type SecretReference_FileTarget struct { func (m *SecretReference_FileTarget) Reset() { *m = SecretReference_FileTarget{} } func (*SecretReference_FileTarget) ProtoMessage() {} func (*SecretReference_FileTarget) Descriptor() ([]byte, []int) { - return fileDescriptorTypes, []int{43, 0} + return fileDescriptorTypes, []int{44, 0} } // BlacklistedCertificate is a record for a blacklisted certificate. It does not @@ -1762,7 +1777,7 @@ type BlacklistedCertificate struct { func (m *BlacklistedCertificate) Reset() { *m = BlacklistedCertificate{} } func (*BlacklistedCertificate) ProtoMessage() {} -func (*BlacklistedCertificate) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{44} } +func (*BlacklistedCertificate) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{45} } // HealthConfig holds configuration settings for the HEALTHCHECK feature. type HealthConfig struct { @@ -1792,7 +1807,7 @@ type HealthConfig struct { func (m *HealthConfig) Reset() { *m = HealthConfig{} } func (*HealthConfig) ProtoMessage() {} -func (*HealthConfig) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{45} } +func (*HealthConfig) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{46} } type MaybeEncryptedRecord struct { Algorithm MaybeEncryptedRecord_Algorithm `protobuf:"varint,1,opt,name=algorithm,proto3,enum=docker.swarmkit.v1.MaybeEncryptedRecord_Algorithm" json:"algorithm,omitempty"` @@ -1802,7 +1817,7 @@ type MaybeEncryptedRecord struct { func (m *MaybeEncryptedRecord) Reset() { *m = MaybeEncryptedRecord{} } func (*MaybeEncryptedRecord) ProtoMessage() {} -func (*MaybeEncryptedRecord) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{46} } +func (*MaybeEncryptedRecord) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{47} } type RootRotation struct { CACert []byte `protobuf:"bytes,1,opt,name=ca_cert,json=caCert,proto3" json:"ca_cert,omitempty"` @@ -1813,7 +1828,7 @@ type RootRotation struct { func (m *RootRotation) Reset() { *m = RootRotation{} } func (*RootRotation) ProtoMessage() {} -func (*RootRotation) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{47} } +func (*RootRotation) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{48} } func init() { proto.RegisterType((*Version)(nil), "docker.swarmkit.v1.Version") @@ -1825,6 +1840,7 @@ func init() { proto.RegisterType((*PluginDescription)(nil), "docker.swarmkit.v1.PluginDescription") proto.RegisterType((*EngineDescription)(nil), "docker.swarmkit.v1.EngineDescription") proto.RegisterType((*NodeDescription)(nil), "docker.swarmkit.v1.NodeDescription") + proto.RegisterType((*NodeTLSInfo)(nil), "docker.swarmkit.v1.NodeTLSInfo") proto.RegisterType((*RaftMemberStatus)(nil), "docker.swarmkit.v1.RaftMemberStatus") proto.RegisterType((*NodeStatus)(nil), "docker.swarmkit.v1.NodeStatus") proto.RegisterType((*Image)(nil), "docker.swarmkit.v1.Image") @@ -2070,6 +2086,37 @@ func (m *NodeDescription) CopyFrom(src interface{}) { m.Engine = &EngineDescription{} github_com_docker_swarmkit_api_deepcopy.Copy(m.Engine, o.Engine) } + if o.TLSInfo != nil { + m.TLSInfo = &NodeTLSInfo{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.TLSInfo, o.TLSInfo) + } +} + +func (m *NodeTLSInfo) Copy() *NodeTLSInfo { + if m == nil { + return nil + } + o := &NodeTLSInfo{} + o.CopyFrom(m) + return o +} + +func (m *NodeTLSInfo) CopyFrom(src interface{}) { + + o := src.(*NodeTLSInfo) + *m = *o + if o.TrustRoot != nil { + m.TrustRoot = make([]byte, len(o.TrustRoot)) + copy(m.TrustRoot, o.TrustRoot) + } + if o.CertIssuerSubject != nil { + m.CertIssuerSubject = make([]byte, len(o.CertIssuerSubject)) + copy(m.CertIssuerSubject, o.CertIssuerSubject) + } + if o.CertIssuerPublicKey != nil { + m.CertIssuerPublicKey = make([]byte, len(o.CertIssuerPublicKey)) + copy(m.CertIssuerPublicKey, o.CertIssuerPublicKey) + } } func (m *RaftMemberStatus) Copy() *RaftMemberStatus { @@ -3351,6 +3398,52 @@ func (m *NodeDescription) MarshalTo(dAtA []byte) (int, error) { } i += n5 } + if m.TLSInfo != nil { + dAtA[i] = 0x2a + i++ + i = encodeVarintTypes(dAtA, i, uint64(m.TLSInfo.Size())) + n6, err := m.TLSInfo.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n6 + } + return i, nil +} + +func (m *NodeTLSInfo) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *NodeTLSInfo) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.TrustRoot) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintTypes(dAtA, i, uint64(len(m.TrustRoot))) + i += copy(dAtA[i:], m.TrustRoot) + } + if len(m.CertIssuerSubject) > 0 { + dAtA[i] = 0x12 + i++ + i = encodeVarintTypes(dAtA, i, uint64(len(m.CertIssuerSubject))) + i += copy(dAtA[i:], m.CertIssuerSubject) + } + if len(m.CertIssuerPublicKey) > 0 { + dAtA[i] = 0x1a + i++ + i = encodeVarintTypes(dAtA, i, uint64(len(m.CertIssuerPublicKey))) + i += copy(dAtA[i:], m.CertIssuerPublicKey) + } return i, nil } @@ -3498,31 +3591,31 @@ func (m *Mount) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0x2a i++ i = encodeVarintTypes(dAtA, i, uint64(m.BindOptions.Size())) - n6, err := m.BindOptions.MarshalTo(dAtA[i:]) + n7, err := m.BindOptions.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n6 + i += n7 } if m.VolumeOptions != nil { dAtA[i] = 0x32 i++ i = encodeVarintTypes(dAtA, i, uint64(m.VolumeOptions.Size())) - n7, err := m.VolumeOptions.MarshalTo(dAtA[i:]) + n8, err := m.VolumeOptions.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n7 + i += n8 } if m.TmpfsOptions != nil { dAtA[i] = 0x3a i++ i = encodeVarintTypes(dAtA, i, uint64(m.TmpfsOptions.Size())) - n8, err := m.TmpfsOptions.MarshalTo(dAtA[i:]) + n9, err := m.TmpfsOptions.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n8 + i += n9 } return i, nil } @@ -3596,11 +3689,11 @@ func (m *Mount_VolumeOptions) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0x1a i++ i = encodeVarintTypes(dAtA, i, uint64(m.DriverConfig.Size())) - n9, err := m.DriverConfig.MarshalTo(dAtA[i:]) + n10, err := m.DriverConfig.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n9 + i += n10 } return i, nil } @@ -3657,11 +3750,11 @@ func (m *RestartPolicy) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0x12 i++ i = encodeVarintTypes(dAtA, i, uint64(m.Delay.Size())) - n10, err := m.Delay.MarshalTo(dAtA[i:]) + n11, err := m.Delay.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n10 + i += n11 } if m.MaxAttempts != 0 { dAtA[i] = 0x18 @@ -3672,11 +3765,11 @@ func (m *RestartPolicy) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0x22 i++ i = encodeVarintTypes(dAtA, i, uint64(m.Window.Size())) - n11, err := m.Window.MarshalTo(dAtA[i:]) + n12, err := m.Window.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n11 + i += n12 } return i, nil } @@ -3704,11 +3797,11 @@ func (m *UpdateConfig) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0x12 i++ i = encodeVarintTypes(dAtA, i, uint64(github_com_gogo_protobuf_types.SizeOfStdDuration(m.Delay))) - n12, err := github_com_gogo_protobuf_types.StdDurationMarshalTo(m.Delay, dAtA[i:]) + n13, err := github_com_gogo_protobuf_types.StdDurationMarshalTo(m.Delay, dAtA[i:]) if err != nil { return 0, err } - i += n12 + i += n13 if m.FailureAction != 0 { dAtA[i] = 0x18 i++ @@ -3718,11 +3811,11 @@ func (m *UpdateConfig) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0x22 i++ i = encodeVarintTypes(dAtA, i, uint64(m.Monitor.Size())) - n13, err := m.Monitor.MarshalTo(dAtA[i:]) + n14, err := m.Monitor.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n13 + i += n14 } if m.MaxFailureRatio != 0 { dAtA[i] = 0x2d @@ -3761,21 +3854,21 @@ func (m *UpdateStatus) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0x12 i++ i = encodeVarintTypes(dAtA, i, uint64(m.StartedAt.Size())) - n14, err := m.StartedAt.MarshalTo(dAtA[i:]) + n15, err := m.StartedAt.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n14 + i += n15 } if m.CompletedAt != nil { dAtA[i] = 0x1a i++ i = encodeVarintTypes(dAtA, i, uint64(m.CompletedAt.Size())) - n15, err := m.CompletedAt.MarshalTo(dAtA[i:]) + n16, err := m.CompletedAt.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n15 + i += n16 } if len(m.Message) > 0 { dAtA[i] = 0x22 @@ -3869,11 +3962,11 @@ func (m *TaskStatus) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0xa i++ i = encodeVarintTypes(dAtA, i, uint64(m.Timestamp.Size())) - n16, err := m.Timestamp.MarshalTo(dAtA[i:]) + n17, err := m.Timestamp.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n16 + i += n17 } if m.State != 0 { dAtA[i] = 0x10 @@ -3893,21 +3986,21 @@ func (m *TaskStatus) MarshalTo(dAtA []byte) (int, error) { i += copy(dAtA[i:], m.Err) } if m.RuntimeStatus != nil { - nn17, err := m.RuntimeStatus.MarshalTo(dAtA[i:]) + nn18, err := m.RuntimeStatus.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += nn17 + i += nn18 } if m.PortStatus != nil { dAtA[i] = 0x32 i++ i = encodeVarintTypes(dAtA, i, uint64(m.PortStatus.Size())) - n18, err := m.PortStatus.MarshalTo(dAtA[i:]) + n19, err := m.PortStatus.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n18 + i += n19 } return i, nil } @@ -3918,11 +4011,11 @@ func (m *TaskStatus_Container) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0x2a i++ i = encodeVarintTypes(dAtA, i, uint64(m.Container.Size())) - n19, err := m.Container.MarshalTo(dAtA[i:]) + n20, err := m.Container.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n19 + i += n20 } return i, nil } @@ -4142,11 +4235,11 @@ func (m *IPAMOptions) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0xa i++ i = encodeVarintTypes(dAtA, i, uint64(m.Driver.Size())) - n20, err := m.Driver.MarshalTo(dAtA[i:]) + n21, err := m.Driver.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n20 + i += n21 } if len(m.Configs) > 0 { for _, msg := range m.Configs { @@ -4212,11 +4305,11 @@ func (m *WeightedPeer) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0xa i++ i = encodeVarintTypes(dAtA, i, uint64(m.Peer.Size())) - n21, err := m.Peer.MarshalTo(dAtA[i:]) + n22, err := m.Peer.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n21 + i += n22 } if m.Weight != 0 { dAtA[i] = 0x10 @@ -4319,11 +4412,11 @@ func (m *AcceptancePolicy_RoleAdmissionPolicy) MarshalTo(dAtA []byte) (int, erro dAtA[i] = 0x1a i++ i = encodeVarintTypes(dAtA, i, uint64(m.Secret.Size())) - n22, err := m.Secret.MarshalTo(dAtA[i:]) + n23, err := m.Secret.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n22 + i += n23 } return i, nil } @@ -4429,11 +4522,11 @@ func (m *CAConfig) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0xa i++ i = encodeVarintTypes(dAtA, i, uint64(m.NodeCertExpiry.Size())) - n23, err := m.NodeCertExpiry.MarshalTo(dAtA[i:]) + n24, err := m.NodeCertExpiry.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n23 + i += n24 } if len(m.ExternalCAs) > 0 { for _, msg := range m.ExternalCAs { @@ -4509,11 +4602,11 @@ func (m *TaskDefaults) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0xa i++ i = encodeVarintTypes(dAtA, i, uint64(m.LogDriver.Size())) - n24, err := m.LogDriver.MarshalTo(dAtA[i:]) + n25, err := m.LogDriver.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n24 + i += n25 } return i, nil } @@ -4537,11 +4630,11 @@ func (m *DispatcherConfig) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0xa i++ i = encodeVarintTypes(dAtA, i, uint64(m.HeartbeatPeriod.Size())) - n25, err := m.HeartbeatPeriod.MarshalTo(dAtA[i:]) + n26, err := m.HeartbeatPeriod.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n25 + i += n26 } return i, nil } @@ -4657,11 +4750,11 @@ func (m *PlacementPreference) MarshalTo(dAtA []byte) (int, error) { var l int _ = l if m.Preference != nil { - nn26, err := m.Preference.MarshalTo(dAtA[i:]) + nn27, err := m.Preference.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += nn26 + i += nn27 } return i, nil } @@ -4672,11 +4765,11 @@ func (m *PlacementPreference_Spread) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0xa i++ i = encodeVarintTypes(dAtA, i, uint64(m.Spread.Size())) - n27, err := m.Spread.MarshalTo(dAtA[i:]) + n28, err := m.Spread.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n27 + i += n28 } return i, nil } @@ -4791,20 +4884,20 @@ func (m *RootCA) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0x22 i++ i = encodeVarintTypes(dAtA, i, uint64(m.JoinTokens.Size())) - n28, err := m.JoinTokens.MarshalTo(dAtA[i:]) + n29, err := m.JoinTokens.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n28 + i += n29 if m.RootRotation != nil { dAtA[i] = 0x2a i++ i = encodeVarintTypes(dAtA, i, uint64(m.RootRotation.Size())) - n29, err := m.RootRotation.MarshalTo(dAtA[i:]) + n30, err := m.RootRotation.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n29 + i += n30 } if m.LastForcedRotation != 0 { dAtA[i] = 0x30 @@ -4843,11 +4936,11 @@ func (m *Certificate) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0x1a i++ i = encodeVarintTypes(dAtA, i, uint64(m.Status.Size())) - n30, err := m.Status.MarshalTo(dAtA[i:]) + n31, err := m.Status.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n30 + i += n31 if len(m.Certificate) > 0 { dAtA[i] = 0x22 i++ @@ -4975,11 +5068,11 @@ func (m *SecretReference) MarshalTo(dAtA []byte) (int, error) { i += copy(dAtA[i:], m.SecretName) } if m.Target != nil { - nn31, err := m.Target.MarshalTo(dAtA[i:]) + nn32, err := m.Target.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += nn31 + i += nn32 } return i, nil } @@ -4990,11 +5083,11 @@ func (m *SecretReference_File) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0x1a i++ i = encodeVarintTypes(dAtA, i, uint64(m.File.Size())) - n32, err := m.File.MarshalTo(dAtA[i:]) + n33, err := m.File.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n32 + i += n33 } return i, nil } @@ -5058,11 +5151,11 @@ func (m *BlacklistedCertificate) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0xa i++ i = encodeVarintTypes(dAtA, i, uint64(m.Expiry.Size())) - n33, err := m.Expiry.MarshalTo(dAtA[i:]) + n34, err := m.Expiry.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n33 + i += n34 } return i, nil } @@ -5101,21 +5194,21 @@ func (m *HealthConfig) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0x12 i++ i = encodeVarintTypes(dAtA, i, uint64(m.Interval.Size())) - n34, err := m.Interval.MarshalTo(dAtA[i:]) + n35, err := m.Interval.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n34 + i += n35 } if m.Timeout != nil { dAtA[i] = 0x1a i++ i = encodeVarintTypes(dAtA, i, uint64(m.Timeout.Size())) - n35, err := m.Timeout.MarshalTo(dAtA[i:]) + n36, err := m.Timeout.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n35 + i += n36 } if m.Retries != 0 { dAtA[i] = 0x20 @@ -5126,11 +5219,11 @@ func (m *HealthConfig) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0x2a i++ i = encodeVarintTypes(dAtA, i, uint64(m.StartPeriod.Size())) - n36, err := m.StartPeriod.MarshalTo(dAtA[i:]) + n37, err := m.StartPeriod.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n36 + i += n37 } return i, nil } @@ -5378,6 +5471,28 @@ func (m *NodeDescription) Size() (n int) { l = m.Engine.Size() n += 1 + l + sovTypes(uint64(l)) } + if m.TLSInfo != nil { + l = m.TLSInfo.Size() + n += 1 + l + sovTypes(uint64(l)) + } + return n +} + +func (m *NodeTLSInfo) Size() (n int) { + var l int + _ = l + l = len(m.TrustRoot) + if l > 0 { + n += 1 + l + sovTypes(uint64(l)) + } + l = len(m.CertIssuerSubject) + if l > 0 { + n += 1 + l + sovTypes(uint64(l)) + } + l = len(m.CertIssuerPublicKey) + if l > 0 { + n += 1 + l + sovTypes(uint64(l)) + } return n } @@ -6327,6 +6442,19 @@ func (this *NodeDescription) String() string { `Platform:` + strings.Replace(fmt.Sprintf("%v", this.Platform), "Platform", "Platform", 1) + `,`, `Resources:` + strings.Replace(fmt.Sprintf("%v", this.Resources), "Resources", "Resources", 1) + `,`, `Engine:` + strings.Replace(fmt.Sprintf("%v", this.Engine), "EngineDescription", "EngineDescription", 1) + `,`, + `TLSInfo:` + strings.Replace(fmt.Sprintf("%v", this.TLSInfo), "NodeTLSInfo", "NodeTLSInfo", 1) + `,`, + `}`, + }, "") + return s +} +func (this *NodeTLSInfo) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&NodeTLSInfo{`, + `TrustRoot:` + fmt.Sprintf("%v", this.TrustRoot) + `,`, + `CertIssuerSubject:` + fmt.Sprintf("%v", this.CertIssuerSubject) + `,`, + `CertIssuerPublicKey:` + fmt.Sprintf("%v", this.CertIssuerPublicKey) + `,`, `}`, }, "") return s @@ -8155,6 +8283,182 @@ func (m *NodeDescription) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TLSInfo", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.TLSInfo == nil { + m.TLSInfo = &NodeTLSInfo{} + } + if err := m.TLSInfo.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTypes(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthTypes + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *NodeTLSInfo) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: NodeTLSInfo: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: NodeTLSInfo: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TrustRoot", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + byteLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.TrustRoot = append(m.TrustRoot[:0], dAtA[iNdEx:postIndex]...) + if m.TrustRoot == nil { + m.TrustRoot = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CertIssuerSubject", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + byteLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.CertIssuerSubject = append(m.CertIssuerSubject[:0], dAtA[iNdEx:postIndex]...) + if m.CertIssuerSubject == nil { + m.CertIssuerSubject = []byte{} + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CertIssuerPublicKey", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + byteLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.CertIssuerPublicKey = append(m.CertIssuerPublicKey[:0], dAtA[iNdEx:postIndex]...) + if m.CertIssuerPublicKey == nil { + m.CertIssuerPublicKey = []byte{} + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipTypes(dAtA[iNdEx:]) @@ -14466,276 +14770,284 @@ var ( func init() { proto.RegisterFile("types.proto", fileDescriptorTypes) } var fileDescriptorTypes = []byte{ - // 4333 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xac, 0x5a, 0x4f, 0x6c, 0x24, 0xc7, - 0x5a, 0xf7, 0xfc, 0xf5, 0xcc, 0x37, 0x63, 0xbb, 0xb7, 0xd6, 0xd9, 0x78, 0x27, 0x1b, 0x7b, 0xd2, - 0xc9, 0xbe, 0xfc, 0x79, 0xd1, 0x64, 0xd7, 0xfb, 0x12, 0x6d, 0x12, 0x5e, 0x92, 0xf9, 0xe7, 0xf5, - 0xbc, 0xb5, 0x67, 0x46, 0x35, 0xe3, 0xdd, 0x97, 0x03, 0xb4, 0xca, 0xdd, 0xe5, 0x71, 0xc7, 0x3d, - 0x5d, 0x43, 0x77, 0x8f, 0xbd, 0x03, 0x42, 0xac, 0x38, 0x00, 0xf2, 0x89, 0x23, 0x12, 0xf2, 0x09, - 0x4e, 0x1c, 0xb8, 0x70, 0x40, 0x70, 0x21, 0x48, 0x1c, 0x72, 0xe3, 0x01, 0x12, 0x7a, 0x02, 0xc9, - 0x10, 0x23, 0x71, 0x43, 0x70, 0x79, 0xe2, 0x02, 0x12, 0xaa, 0x3f, 0xdd, 0xd3, 0xf6, 0x8e, 0xed, - 0x0d, 0xe1, 0x62, 0x77, 0x7d, 0xf5, 0xfb, 0xbe, 0xaa, 0xfa, 0xea, 0xab, 0xaa, 0xdf, 0x57, 0x35, - 0x50, 0x08, 0x26, 0x23, 0xea, 0x57, 0x46, 0x1e, 0x0b, 0x18, 0x42, 0x16, 0x33, 0x0f, 0xa8, 0x57, - 0xf1, 0x8f, 0x88, 0x37, 0x3c, 0xb0, 0x83, 0xca, 0xe1, 0xfd, 0xd2, 0xda, 0x80, 0xb1, 0x81, 0x43, - 0x3f, 0x10, 0x88, 0xdd, 0xf1, 0xde, 0x07, 0x81, 0x3d, 0xa4, 0x7e, 0x40, 0x86, 0x23, 0xa9, 0x54, - 0x5a, 0xbd, 0x08, 0xb0, 0xc6, 0x1e, 0x09, 0x6c, 0xe6, 0xaa, 0xfa, 0xe5, 0x01, 0x1b, 0x30, 0xf1, - 0xf9, 0x01, 0xff, 0x92, 0x52, 0x7d, 0x0d, 0xe6, 0x9f, 0x50, 0xcf, 0xb7, 0x99, 0x8b, 0x96, 0x21, - 0x63, 0xbb, 0x16, 0x7d, 0xb6, 0x92, 0x28, 0x27, 0xde, 0x49, 0x63, 0x59, 0xd0, 0xef, 0x01, 0xb4, - 0xf8, 0x47, 0xd3, 0x0d, 0xbc, 0x09, 0xd2, 0x20, 0x75, 0x40, 0x27, 0x02, 0x91, 0xc7, 0xfc, 0x93, - 0x4b, 0x0e, 0x89, 0xb3, 0x92, 0x94, 0x92, 0x43, 0xe2, 0xe8, 0xdf, 0x26, 0xa0, 0x50, 0x75, 0x5d, - 0x16, 0x88, 0xd6, 0x7d, 0x84, 0x20, 0xed, 0x92, 0x21, 0x55, 0x4a, 0xe2, 0x1b, 0xd5, 0x21, 0xeb, - 0x90, 0x5d, 0xea, 0xf8, 0x2b, 0xc9, 0x72, 0xea, 0x9d, 0xc2, 0xfa, 0x0f, 0x2b, 0x2f, 0x0e, 0xb9, - 0x12, 0x33, 0x52, 0xd9, 0x12, 0x68, 0xd1, 0x09, 0xac, 0x54, 0xd1, 0x67, 0x30, 0x6f, 0xbb, 0x96, - 0x6d, 0x52, 0x7f, 0x25, 0x2d, 0xac, 0xac, 0xce, 0xb2, 0x32, 0xed, 0x7d, 0x2d, 0xfd, 0xcd, 0xe9, - 0xda, 0x1c, 0x0e, 0x95, 0x4a, 0x1f, 0x43, 0x21, 0x66, 0x76, 0xc6, 0xd8, 0x96, 0x21, 0x73, 0x48, - 0x9c, 0x31, 0x55, 0xa3, 0x93, 0x85, 0x4f, 0x92, 0x0f, 0x13, 0xfa, 0x97, 0x90, 0xc7, 0xd4, 0x67, - 0x63, 0xcf, 0xa4, 0x3e, 0x7a, 0x17, 0xf2, 0x2e, 0x71, 0x99, 0x61, 0x8e, 0xc6, 0xbe, 0x50, 0x4f, - 0xd5, 0x8a, 0x67, 0xa7, 0x6b, 0xb9, 0x36, 0x71, 0x59, 0xbd, 0xbb, 0xe3, 0xe3, 0x1c, 0xaf, 0xae, - 0x8f, 0xc6, 0x3e, 0x7a, 0x03, 0x8a, 0x43, 0x3a, 0x64, 0xde, 0xc4, 0xd8, 0x9d, 0x04, 0xd4, 0x17, - 0x86, 0x53, 0xb8, 0x20, 0x65, 0x35, 0x2e, 0xd2, 0x7f, 0x2f, 0x01, 0xcb, 0xa1, 0x6d, 0x4c, 0x7f, - 0x75, 0x6c, 0x7b, 0x74, 0x48, 0xdd, 0xc0, 0x47, 0x1f, 0x42, 0xd6, 0xb1, 0x87, 0x76, 0x20, 0xdb, - 0x28, 0xac, 0xbf, 0x3e, 0x6b, 0xb4, 0x51, 0xaf, 0xb0, 0x02, 0xa3, 0x2a, 0x14, 0x3d, 0xea, 0x53, - 0xef, 0x50, 0x7a, 0x52, 0x34, 0x79, 0xad, 0xf2, 0x39, 0x15, 0x7d, 0x03, 0x72, 0x5d, 0x87, 0x04, - 0x7b, 0xcc, 0x1b, 0x22, 0x1d, 0x8a, 0xc4, 0x33, 0xf7, 0xed, 0x80, 0x9a, 0xc1, 0xd8, 0x0b, 0x67, - 0xf5, 0x9c, 0x0c, 0xdd, 0x82, 0x24, 0x93, 0x0d, 0xe5, 0x6b, 0xd9, 0xb3, 0xd3, 0xb5, 0x64, 0xa7, - 0x87, 0x93, 0xcc, 0xd7, 0x3f, 0x85, 0x1b, 0x5d, 0x67, 0x3c, 0xb0, 0xdd, 0x06, 0xf5, 0x4d, 0xcf, - 0x1e, 0x71, 0xeb, 0x3c, 0x3c, 0x78, 0xec, 0x87, 0xe1, 0xc1, 0xbf, 0xa3, 0x90, 0x49, 0x4e, 0x43, - 0x46, 0xff, 0x9d, 0x24, 0xdc, 0x68, 0xba, 0x03, 0xdb, 0xa5, 0x71, 0xed, 0xbb, 0xb0, 0x48, 0x85, - 0xd0, 0x38, 0x94, 0x61, 0xac, 0xec, 0x2c, 0x48, 0x69, 0x18, 0xdb, 0xad, 0x0b, 0xf1, 0x76, 0x7f, - 0xd6, 0xf0, 0x5f, 0xb0, 0x3e, 0x33, 0xea, 0x9a, 0x30, 0x3f, 0x12, 0x83, 0xf0, 0x57, 0x52, 0xc2, - 0xd6, 0xdd, 0x59, 0xb6, 0x5e, 0x18, 0x67, 0x18, 0x7c, 0x4a, 0xf7, 0xfb, 0x04, 0xdf, 0xbf, 0x26, - 0x60, 0xa9, 0xcd, 0xac, 0x73, 0x7e, 0x28, 0x41, 0x6e, 0x9f, 0xf9, 0x41, 0x6c, 0xa1, 0x45, 0x65, - 0xf4, 0x10, 0x72, 0x23, 0x35, 0x7d, 0x6a, 0xf6, 0xef, 0xcc, 0xee, 0xb2, 0xc4, 0xe0, 0x08, 0x8d, - 0x3e, 0x85, 0xbc, 0x17, 0xc6, 0xc4, 0x4a, 0xea, 0x65, 0x02, 0x67, 0x8a, 0x47, 0x3f, 0x86, 0xac, - 0x9c, 0x84, 0x95, 0xb4, 0xd0, 0xbc, 0xfb, 0x52, 0x3e, 0xc7, 0x4a, 0x49, 0xff, 0x79, 0x02, 0x34, - 0x4c, 0xf6, 0x82, 0x6d, 0x3a, 0xdc, 0xa5, 0x5e, 0x2f, 0x20, 0xc1, 0xd8, 0x47, 0xb7, 0x20, 0xeb, - 0x50, 0x62, 0x51, 0x4f, 0x0c, 0x32, 0x87, 0x55, 0x09, 0xed, 0xf0, 0x20, 0x27, 0xe6, 0x3e, 0xd9, - 0xb5, 0x1d, 0x3b, 0x98, 0x88, 0x61, 0x2e, 0xce, 0x9e, 0xe5, 0x8b, 0x36, 0x2b, 0x38, 0xa6, 0x88, - 0xcf, 0x99, 0x41, 0x2b, 0x30, 0x3f, 0xa4, 0xbe, 0x4f, 0x06, 0x54, 0x8c, 0x3e, 0x8f, 0xc3, 0xa2, - 0xfe, 0x29, 0x14, 0xe3, 0x7a, 0xa8, 0x00, 0xf3, 0x3b, 0xed, 0xc7, 0xed, 0xce, 0xd3, 0xb6, 0x36, - 0x87, 0x96, 0xa0, 0xb0, 0xd3, 0xc6, 0xcd, 0x6a, 0x7d, 0xb3, 0x5a, 0xdb, 0x6a, 0x6a, 0x09, 0xb4, - 0x00, 0xf9, 0x69, 0x31, 0xa9, 0xff, 0x69, 0x02, 0x80, 0x4f, 0xa0, 0x1a, 0xd4, 0x27, 0x90, 0xf1, - 0x03, 0x12, 0xc8, 0x89, 0x5b, 0x5c, 0x7f, 0x6b, 0x56, 0xaf, 0xa7, 0xf0, 0x0a, 0xff, 0x47, 0xb1, - 0x54, 0x89, 0xf7, 0x30, 0x79, 0xae, 0x87, 0x7c, 0x0d, 0x11, 0xcb, 0xf2, 0x54, 0xc7, 0xc5, 0xb7, - 0xfe, 0x29, 0x64, 0x84, 0xf6, 0xf9, 0xee, 0xe6, 0x20, 0xdd, 0xe0, 0x5f, 0x09, 0x94, 0x87, 0x0c, - 0x6e, 0x56, 0x1b, 0x5f, 0x6a, 0x49, 0xa4, 0x41, 0xb1, 0xd1, 0xea, 0xd5, 0x3b, 0xed, 0x76, 0xb3, - 0xde, 0x6f, 0x36, 0xb4, 0x94, 0x7e, 0x17, 0x32, 0xad, 0x21, 0xb7, 0x7c, 0x87, 0x47, 0xc5, 0x1e, - 0xf5, 0xa8, 0x6b, 0x86, 0xc1, 0x36, 0x15, 0xe8, 0x3f, 0xcb, 0x43, 0x66, 0x9b, 0x8d, 0xdd, 0x00, - 0xad, 0xc7, 0x56, 0xf6, 0xe2, 0xec, 0xcd, 0x59, 0x00, 0x2b, 0xfd, 0xc9, 0x88, 0xaa, 0x95, 0x7f, - 0x0b, 0xb2, 0x32, 0x7e, 0xd4, 0x70, 0x54, 0x89, 0xcb, 0x03, 0xe2, 0x0d, 0x68, 0xa0, 0xc6, 0xa3, - 0x4a, 0xe8, 0x1d, 0xc8, 0x79, 0x94, 0x58, 0xcc, 0x75, 0x26, 0x22, 0xcc, 0x72, 0x72, 0xeb, 0xc5, - 0x94, 0x58, 0x1d, 0xd7, 0x99, 0xe0, 0xa8, 0x16, 0x6d, 0x42, 0x71, 0xd7, 0x76, 0x2d, 0x83, 0x8d, - 0xe4, 0x3e, 0x98, 0xb9, 0x3c, 0x28, 0x65, 0xaf, 0x6a, 0xb6, 0x6b, 0x75, 0x24, 0x18, 0x17, 0x76, - 0xa7, 0x05, 0xd4, 0x86, 0xc5, 0x43, 0xe6, 0x8c, 0x87, 0x34, 0xb2, 0x95, 0x15, 0xb6, 0xde, 0xbe, - 0xdc, 0xd6, 0x13, 0x81, 0x0f, 0xad, 0x2d, 0x1c, 0xc6, 0x8b, 0xe8, 0x31, 0x2c, 0x04, 0xc3, 0xd1, - 0x9e, 0x1f, 0x99, 0x9b, 0x17, 0xe6, 0x7e, 0x70, 0x85, 0xc3, 0x38, 0x3c, 0xb4, 0x56, 0x0c, 0x62, - 0xa5, 0xd2, 0x6f, 0xa5, 0xa0, 0x10, 0xeb, 0x39, 0xea, 0x41, 0x61, 0xe4, 0xb1, 0x11, 0x19, 0x88, - 0xbd, 0x5c, 0xcd, 0xc5, 0xfd, 0x97, 0x1a, 0x75, 0xa5, 0x3b, 0x55, 0xc4, 0x71, 0x2b, 0xfa, 0x49, - 0x12, 0x0a, 0xb1, 0x4a, 0xf4, 0x1e, 0xe4, 0x70, 0x17, 0xb7, 0x9e, 0x54, 0xfb, 0x4d, 0x6d, 0xae, - 0x74, 0xe7, 0xf8, 0xa4, 0xbc, 0x22, 0xac, 0xc5, 0x0d, 0x74, 0x3d, 0xfb, 0x90, 0x87, 0xde, 0x3b, - 0x30, 0x1f, 0x42, 0x13, 0xa5, 0xd7, 0x8e, 0x4f, 0xca, 0xaf, 0x5e, 0x84, 0xc6, 0x90, 0xb8, 0xb7, - 0x59, 0xc5, 0xcd, 0x86, 0x96, 0x9c, 0x8d, 0xc4, 0xbd, 0x7d, 0xe2, 0x51, 0x0b, 0xfd, 0x00, 0xb2, - 0x0a, 0x98, 0x2a, 0x95, 0x8e, 0x4f, 0xca, 0xb7, 0x2e, 0x02, 0xa7, 0x38, 0xdc, 0xdb, 0xaa, 0x3e, - 0x69, 0x6a, 0xe9, 0xd9, 0x38, 0xdc, 0x73, 0xc8, 0x21, 0x45, 0x6f, 0x41, 0x46, 0xc2, 0x32, 0xa5, - 0xdb, 0xc7, 0x27, 0xe5, 0x57, 0x5e, 0x30, 0xc7, 0x51, 0xa5, 0x95, 0xdf, 0xfd, 0xc3, 0xd5, 0xb9, - 0xbf, 0xf8, 0xa3, 0x55, 0xed, 0x62, 0x75, 0xe9, 0xbf, 0x13, 0xb0, 0x70, 0x6e, 0xca, 0x91, 0x0e, - 0x59, 0x97, 0x99, 0x6c, 0x24, 0xb7, 0xf8, 0x5c, 0x0d, 0xce, 0x4e, 0xd7, 0xb2, 0x6d, 0x56, 0x67, - 0xa3, 0x09, 0x56, 0x35, 0xe8, 0xf1, 0x85, 0x43, 0xea, 0xc1, 0x4b, 0xc6, 0xd3, 0xcc, 0x63, 0xea, - 0x73, 0x58, 0xb0, 0x3c, 0xfb, 0x90, 0x7a, 0x86, 0xc9, 0xdc, 0x3d, 0x7b, 0xa0, 0xb6, 0xef, 0xd2, - 0x2c, 0x9b, 0x0d, 0x01, 0xc4, 0x45, 0xa9, 0x50, 0x17, 0xf8, 0xef, 0x71, 0x40, 0x95, 0x9e, 0x40, - 0x31, 0x1e, 0xa1, 0xe8, 0x75, 0x00, 0xdf, 0xfe, 0x35, 0xaa, 0x38, 0x8f, 0x60, 0x48, 0x38, 0xcf, - 0x25, 0x82, 0xf1, 0xa0, 0xb7, 0x21, 0x3d, 0x64, 0x96, 0xb4, 0xb3, 0x50, 0xbb, 0xc9, 0xcf, 0xc9, - 0x7f, 0x3c, 0x5d, 0x2b, 0x30, 0xbf, 0xb2, 0x61, 0x3b, 0x74, 0x9b, 0x59, 0x14, 0x0b, 0x80, 0x7e, - 0x08, 0x69, 0xbe, 0x55, 0xa0, 0xd7, 0x20, 0x5d, 0x6b, 0xb5, 0x1b, 0xda, 0x5c, 0xe9, 0xc6, 0xf1, - 0x49, 0x79, 0x41, 0xb8, 0x84, 0x57, 0xf0, 0xd8, 0x45, 0x6b, 0x90, 0x7d, 0xd2, 0xd9, 0xda, 0xd9, - 0xe6, 0xe1, 0x75, 0xf3, 0xf8, 0xa4, 0xbc, 0x14, 0x55, 0x4b, 0xa7, 0xa1, 0xd7, 0x21, 0xd3, 0xdf, - 0xee, 0x6e, 0xf4, 0xb4, 0x64, 0x09, 0x1d, 0x9f, 0x94, 0x17, 0xa3, 0x7a, 0xd1, 0xe7, 0xd2, 0x0d, - 0x35, 0xab, 0xf9, 0x48, 0xae, 0xff, 0x22, 0x09, 0x0b, 0x98, 0x93, 0x6d, 0x2f, 0xe8, 0x32, 0xc7, - 0x36, 0x27, 0xa8, 0x0b, 0x79, 0x93, 0xb9, 0x96, 0x1d, 0x5b, 0x53, 0xeb, 0x97, 0x1c, 0x8c, 0x53, - 0xad, 0xb0, 0x54, 0x0f, 0x35, 0xf1, 0xd4, 0x08, 0xfa, 0x00, 0x32, 0x16, 0x75, 0xc8, 0x44, 0x9d, - 0xd0, 0xb7, 0x2b, 0x92, 0xce, 0x57, 0x42, 0x3a, 0x5f, 0x69, 0x28, 0x3a, 0x8f, 0x25, 0x4e, 0x50, - 0x49, 0xf2, 0xcc, 0x20, 0x41, 0x40, 0x87, 0xa3, 0x40, 0x1e, 0xcf, 0x69, 0x5c, 0x18, 0x92, 0x67, - 0x55, 0x25, 0x42, 0xf7, 0x21, 0x7b, 0x64, 0xbb, 0x16, 0x3b, 0x52, 0x27, 0xf0, 0x15, 0x46, 0x15, - 0x50, 0x3f, 0xe6, 0xa7, 0xee, 0x85, 0x6e, 0x72, 0x7f, 0xb7, 0x3b, 0xed, 0x66, 0xe8, 0x6f, 0x55, - 0xdf, 0x71, 0xdb, 0xcc, 0xe5, 0x6b, 0x05, 0x3a, 0x6d, 0x63, 0xa3, 0xda, 0xda, 0xda, 0xc1, 0xdc, - 0xe7, 0xcb, 0xc7, 0x27, 0x65, 0x2d, 0x82, 0x6c, 0x10, 0xdb, 0xe1, 0x94, 0xf0, 0x36, 0xa4, 0xaa, - 0xed, 0x2f, 0xb5, 0x64, 0x49, 0x3b, 0x3e, 0x29, 0x17, 0xa3, 0xea, 0xaa, 0x3b, 0x99, 0x2e, 0xa3, - 0x8b, 0xed, 0xea, 0x7f, 0x93, 0x82, 0xe2, 0xce, 0xc8, 0x22, 0x01, 0x95, 0x31, 0x89, 0xca, 0x50, - 0x18, 0x11, 0x8f, 0x38, 0x0e, 0x75, 0x6c, 0x7f, 0xa8, 0x12, 0x95, 0xb8, 0x08, 0x7d, 0xfc, 0xb2, - 0x6e, 0xac, 0xe5, 0x78, 0x9c, 0xfd, 0xfe, 0x3f, 0xaf, 0x25, 0x42, 0x87, 0xee, 0xc0, 0xe2, 0x9e, - 0xec, 0xad, 0x41, 0x4c, 0x31, 0xb1, 0x29, 0x31, 0xb1, 0x95, 0x59, 0x13, 0x1b, 0xef, 0x56, 0x45, - 0x0d, 0xb2, 0x2a, 0xb4, 0xf0, 0xc2, 0x5e, 0xbc, 0x88, 0x1e, 0xc0, 0xfc, 0x90, 0xb9, 0x76, 0xc0, - 0xbc, 0xeb, 0x67, 0x21, 0x44, 0xa2, 0xf7, 0xe0, 0x06, 0x9f, 0xdc, 0xb0, 0x3f, 0xa2, 0x5a, 0x9c, - 0x58, 0x49, 0xbc, 0x34, 0x24, 0xcf, 0x54, 0x83, 0x98, 0x8b, 0x51, 0x0d, 0x32, 0xcc, 0xe3, 0x94, - 0x28, 0x2b, 0xba, 0xfb, 0xfe, 0xb5, 0xdd, 0x95, 0x85, 0x0e, 0xd7, 0xc1, 0x52, 0x55, 0xff, 0x08, - 0x16, 0xce, 0x0d, 0x82, 0x33, 0x81, 0x6e, 0x75, 0xa7, 0xd7, 0xd4, 0xe6, 0x50, 0x11, 0x72, 0xf5, - 0x4e, 0xbb, 0xdf, 0x6a, 0xef, 0x70, 0x2a, 0x53, 0x84, 0x1c, 0xee, 0x6c, 0x6d, 0xd5, 0xaa, 0xf5, - 0xc7, 0x5a, 0x52, 0xaf, 0x40, 0x21, 0x66, 0x0d, 0x2d, 0x02, 0xf4, 0xfa, 0x9d, 0xae, 0xb1, 0xd1, - 0xc2, 0xbd, 0xbe, 0x24, 0x42, 0xbd, 0x7e, 0x15, 0xf7, 0x95, 0x20, 0xa1, 0xff, 0x47, 0x32, 0x9c, - 0x51, 0xc5, 0x7d, 0x6a, 0xe7, 0xb9, 0xcf, 0x15, 0x9d, 0x57, 0xec, 0x67, 0x5a, 0x88, 0x38, 0xd0, - 0xc7, 0x00, 0x22, 0x70, 0xa8, 0x65, 0x90, 0x40, 0x4d, 0x7c, 0xe9, 0x05, 0x27, 0xf7, 0xc3, 0x7c, - 0x19, 0xe7, 0x15, 0xba, 0x1a, 0xa0, 0x1f, 0x43, 0xd1, 0x64, 0xc3, 0x91, 0x43, 0x95, 0x72, 0xea, - 0x5a, 0xe5, 0x42, 0x84, 0xaf, 0x06, 0x71, 0xf6, 0x95, 0x3e, 0xcf, 0x0f, 0x7f, 0x3b, 0x11, 0x7a, - 0x66, 0x06, 0xe1, 0x2a, 0x42, 0x6e, 0xa7, 0xdb, 0xa8, 0xf6, 0x5b, 0xed, 0x47, 0x5a, 0x02, 0x01, - 0x64, 0x85, 0xab, 0x1b, 0x5a, 0x92, 0x13, 0xc5, 0x7a, 0x67, 0xbb, 0xbb, 0xd5, 0x14, 0x94, 0x0b, - 0x2d, 0x83, 0x16, 0x3a, 0xdb, 0x10, 0x8e, 0x6c, 0x36, 0xb4, 0x34, 0xba, 0x09, 0x4b, 0x91, 0x54, - 0x69, 0x66, 0xd0, 0x2d, 0x40, 0x91, 0x70, 0x6a, 0x22, 0xab, 0xff, 0x06, 0x2c, 0xd5, 0x99, 0x1b, - 0x10, 0xdb, 0x8d, 0x48, 0xf4, 0x3a, 0x1f, 0xb4, 0x12, 0x19, 0xb6, 0x25, 0xf7, 0xf4, 0xda, 0xd2, - 0xd9, 0xe9, 0x5a, 0x21, 0x82, 0xb6, 0x1a, 0x7c, 0xa4, 0x61, 0xc1, 0xe2, 0xeb, 0x77, 0x64, 0x5b, - 0xc2, 0xb9, 0x99, 0xda, 0xfc, 0xd9, 0xe9, 0x5a, 0xaa, 0xdb, 0x6a, 0x60, 0x2e, 0x43, 0xaf, 0x41, - 0x9e, 0x3e, 0xb3, 0x03, 0xc3, 0xe4, 0x7b, 0x38, 0x77, 0x60, 0x06, 0xe7, 0xb8, 0xa0, 0xce, 0xb7, - 0xec, 0x1a, 0x40, 0x97, 0x79, 0x81, 0x6a, 0xf9, 0x47, 0x90, 0x19, 0x31, 0x4f, 0x64, 0xb0, 0x97, - 0xe6, 0xeb, 0x1c, 0x2e, 0x03, 0x15, 0x4b, 0xb0, 0xfe, 0x97, 0x49, 0x80, 0x3e, 0xf1, 0x0f, 0x94, - 0x91, 0x87, 0x90, 0x8f, 0xee, 0x3e, 0x54, 0x2a, 0x7c, 0xe5, 0x6c, 0x47, 0x60, 0xf4, 0x20, 0x0c, - 0x36, 0x99, 0x1e, 0xcc, 0x4c, 0x65, 0xc2, 0x86, 0x66, 0x31, 0xec, 0xf3, 0x39, 0x00, 0x3f, 0x12, - 0xa9, 0xe7, 0xa9, 0x99, 0xe7, 0x9f, 0xa8, 0x2e, 0x8e, 0x05, 0xe9, 0x34, 0x45, 0x30, 0xdf, 0x9c, - 0xd5, 0xc8, 0x85, 0x19, 0xd9, 0x9c, 0xc3, 0x53, 0x3d, 0xf4, 0x39, 0x14, 0xf8, 0xb8, 0x0d, 0x5f, - 0xd4, 0x29, 0x6e, 0x79, 0xa9, 0xab, 0xa4, 0x05, 0x0c, 0xa3, 0xe8, 0xbb, 0xa6, 0xc1, 0xa2, 0x37, - 0x76, 0xf9, 0xb0, 0x95, 0x0d, 0xdd, 0x86, 0x57, 0xdb, 0x34, 0x38, 0x62, 0xde, 0x41, 0x35, 0x08, - 0x88, 0xb9, 0x3f, 0xa4, 0xae, 0xf2, 0x71, 0x8c, 0x58, 0x27, 0xce, 0x11, 0xeb, 0x15, 0x98, 0x27, - 0x8e, 0x4d, 0x7c, 0x2a, 0xd9, 0x48, 0x1e, 0x87, 0x45, 0x4e, 0xff, 0x79, 0x32, 0x41, 0x7d, 0x9f, - 0xca, 0x14, 0x38, 0x8f, 0xa7, 0x02, 0xfd, 0xef, 0x93, 0x00, 0xad, 0x6e, 0x75, 0x5b, 0x99, 0x6f, - 0x40, 0x76, 0x8f, 0x0c, 0x6d, 0x67, 0x72, 0xd5, 0x02, 0x9f, 0xe2, 0x2b, 0x55, 0x69, 0x68, 0x43, - 0xe8, 0x60, 0xa5, 0x2b, 0xb2, 0x82, 0xf1, 0xae, 0x4b, 0x83, 0x28, 0x2b, 0x10, 0x25, 0x4e, 0x41, - 0x3c, 0xe2, 0x46, 0x33, 0x23, 0x0b, 0xbc, 0xeb, 0x03, 0x12, 0xd0, 0x23, 0x32, 0x09, 0x57, 0xa5, - 0x2a, 0xa2, 0x4d, 0x9e, 0x2d, 0xf8, 0xd4, 0x3b, 0xa4, 0xd6, 0x4a, 0x46, 0x84, 0xe0, 0x75, 0xfd, - 0xc1, 0x0a, 0x2e, 0xc9, 0x55, 0xa4, 0x5d, 0xfa, 0x54, 0x30, 0x82, 0x69, 0xd5, 0x77, 0x4a, 0xe0, - 0xef, 0xc1, 0xc2, 0xb9, 0x71, 0xbe, 0x90, 0x8e, 0xb5, 0xba, 0x4f, 0x7e, 0xa4, 0xa5, 0xd5, 0xd7, - 0x47, 0x5a, 0x56, 0xff, 0xe3, 0x94, 0x5c, 0x47, 0xca, 0xab, 0xb3, 0xaf, 0xd4, 0x72, 0x22, 0xfa, - 0x4d, 0xe6, 0xa8, 0xf8, 0x7e, 0xfb, 0xea, 0xe5, 0xc5, 0xe9, 0xbd, 0x80, 0xe3, 0x48, 0x11, 0xad, - 0x41, 0x41, 0xce, 0xbf, 0xc1, 0xe3, 0x49, 0xb8, 0x75, 0x01, 0x83, 0x14, 0x71, 0x4d, 0x74, 0x17, - 0x16, 0x47, 0xe3, 0x5d, 0xc7, 0xf6, 0xf7, 0xa9, 0x25, 0x31, 0x69, 0x81, 0x59, 0x88, 0xa4, 0x02, - 0xb6, 0x0d, 0x45, 0x25, 0x30, 0x04, 0xb5, 0xcb, 0x88, 0x0e, 0xbd, 0x77, 0x5d, 0x87, 0xa4, 0x8a, - 0x60, 0x7c, 0x85, 0xd1, 0xb4, 0xa0, 0x37, 0x20, 0x17, 0x76, 0x16, 0xad, 0x40, 0xaa, 0x5f, 0xef, - 0x6a, 0x73, 0xa5, 0xa5, 0xe3, 0x93, 0x72, 0x21, 0x14, 0xf7, 0xeb, 0x5d, 0x5e, 0xb3, 0xd3, 0xe8, - 0x6a, 0x89, 0xf3, 0x35, 0x3b, 0x8d, 0x6e, 0x29, 0xcd, 0x29, 0x86, 0xbe, 0x07, 0x85, 0x58, 0x0b, - 0xe8, 0x4d, 0x98, 0x6f, 0xb5, 0x1f, 0xe1, 0x66, 0xaf, 0xa7, 0xcd, 0x95, 0x6e, 0x1d, 0x9f, 0x94, - 0x51, 0xac, 0xb6, 0xe5, 0x0e, 0xf8, 0xfc, 0xa0, 0xd7, 0x21, 0xbd, 0xd9, 0xe1, 0x47, 0x97, 0xe4, - 0x92, 0x31, 0xc4, 0x26, 0xf3, 0x83, 0xd2, 0x4d, 0xc5, 0x5d, 0xe2, 0x86, 0xf5, 0x3f, 0x48, 0x40, - 0x56, 0x52, 0xea, 0x99, 0x13, 0x55, 0x85, 0xf9, 0x30, 0xd1, 0x93, 0x3c, 0xff, 0xed, 0xcb, 0x39, - 0x79, 0x45, 0x51, 0x68, 0x19, 0x7e, 0xa1, 0x5e, 0xe9, 0x13, 0x28, 0xc6, 0x2b, 0xbe, 0x53, 0xf0, - 0xfd, 0x3a, 0x14, 0x78, 0x7c, 0x87, 0xdc, 0x7c, 0x1d, 0xb2, 0x92, 0xf6, 0x47, 0x5b, 0xe9, 0xe5, - 0x09, 0x82, 0x42, 0xa2, 0x87, 0x30, 0x2f, 0x93, 0x8a, 0xf0, 0x0a, 0x6c, 0xf5, 0xea, 0x55, 0x84, - 0x43, 0xb8, 0xfe, 0x39, 0xa4, 0xbb, 0x94, 0x7a, 0xdc, 0xf7, 0x2e, 0xb3, 0xe8, 0xf4, 0xf4, 0x51, - 0xf9, 0x90, 0x45, 0x5b, 0x0d, 0x9e, 0x0f, 0x59, 0xb4, 0x65, 0x45, 0x37, 0x18, 0xc9, 0xd8, 0x0d, - 0x46, 0x1f, 0x8a, 0x4f, 0xa9, 0x3d, 0xd8, 0x0f, 0xa8, 0x25, 0x0c, 0xbd, 0x0f, 0xe9, 0x11, 0x8d, - 0x3a, 0xbf, 0x32, 0x33, 0xc0, 0x28, 0xf5, 0xb0, 0x40, 0xf1, 0x7d, 0xe4, 0x48, 0x68, 0xab, 0x8b, - 0x57, 0x55, 0xd2, 0xff, 0x2e, 0x09, 0x8b, 0x2d, 0xdf, 0x1f, 0x13, 0xd7, 0x0c, 0x89, 0xc9, 0x67, - 0xe7, 0x89, 0xc9, 0x3b, 0x33, 0x47, 0x78, 0x4e, 0xe5, 0xfc, 0xc5, 0x8c, 0x3a, 0x1c, 0x92, 0xd1, - 0xe1, 0xa0, 0xff, 0x7b, 0x22, 0xbc, 0x7d, 0xb9, 0x1b, 0x5b, 0xee, 0xa5, 0x95, 0xe3, 0x93, 0xf2, - 0x72, 0xdc, 0x12, 0xdd, 0x71, 0x0f, 0x5c, 0x76, 0xe4, 0xa2, 0x37, 0x20, 0x83, 0x9b, 0xed, 0xe6, - 0x53, 0x2d, 0x21, 0xc3, 0xf3, 0x1c, 0x08, 0x53, 0x97, 0x1e, 0x71, 0x4b, 0xdd, 0x66, 0xbb, 0xc1, - 0x89, 0x44, 0x72, 0x86, 0xa5, 0x2e, 0x75, 0x2d, 0xdb, 0x1d, 0xa0, 0x37, 0x21, 0xdb, 0xea, 0xf5, - 0x76, 0x44, 0x7e, 0xfc, 0xea, 0xf1, 0x49, 0xf9, 0xe6, 0x39, 0x14, 0x2f, 0x50, 0x8b, 0x83, 0x38, - 0x8b, 0xe7, 0x14, 0x63, 0x06, 0x88, 0xd3, 0x43, 0x09, 0xc2, 0x9d, 0x3e, 0x4f, 0xde, 0x33, 0x33, - 0x40, 0x98, 0xf1, 0xbf, 0x6a, 0xb9, 0xfd, 0x53, 0x12, 0xb4, 0xaa, 0x69, 0xd2, 0x51, 0xc0, 0xeb, - 0x55, 0xe2, 0xd4, 0x87, 0xdc, 0x88, 0x7f, 0xd9, 0x34, 0x24, 0x01, 0x0f, 0x67, 0x5e, 0xfd, 0x5f, - 0xd0, 0xab, 0x60, 0xe6, 0xd0, 0xaa, 0x35, 0xb4, 0x7d, 0xdf, 0x66, 0xae, 0x94, 0xe1, 0xc8, 0x52, - 0xe9, 0x3f, 0x13, 0x70, 0x73, 0x06, 0x02, 0xdd, 0x83, 0xb4, 0xc7, 0x9c, 0x70, 0x0e, 0xef, 0x5c, - 0x76, 0xb1, 0xc6, 0x55, 0xb1, 0x40, 0xa2, 0x55, 0x00, 0x32, 0x0e, 0x18, 0x11, 0xed, 0x8b, 0xd9, - 0xcb, 0xe1, 0x98, 0x04, 0x3d, 0x85, 0xac, 0x4f, 0x4d, 0x8f, 0x86, 0x54, 0xf1, 0xf3, 0xff, 0x6b, - 0xef, 0x2b, 0x3d, 0x61, 0x06, 0x2b, 0x73, 0xa5, 0x0a, 0x64, 0xa5, 0x84, 0x87, 0xbd, 0x45, 0x02, - 0x22, 0x3a, 0x5d, 0xc4, 0xe2, 0x9b, 0x47, 0x13, 0x71, 0x06, 0x61, 0x34, 0x11, 0x67, 0xa0, 0xff, - 0x75, 0x12, 0xa0, 0xf9, 0x2c, 0xa0, 0x9e, 0x4b, 0x9c, 0x7a, 0x15, 0x35, 0x63, 0xbb, 0xbf, 0x1c, - 0xed, 0xbb, 0x33, 0xaf, 0x5b, 0x23, 0x8d, 0x4a, 0xbd, 0x3a, 0x63, 0xff, 0xbf, 0x0d, 0xa9, 0xb1, - 0xa7, 0x5e, 0x73, 0x24, 0xcd, 0xdb, 0xc1, 0x5b, 0x98, 0xcb, 0x50, 0x73, 0xba, 0x6d, 0xa5, 0x2e, - 0x7f, 0xb3, 0x89, 0x35, 0x30, 0x73, 0xeb, 0xe2, 0x2b, 0xdf, 0x24, 0x86, 0x49, 0xd5, 0xc9, 0x51, - 0x94, 0x2b, 0xbf, 0x5e, 0xad, 0x53, 0x2f, 0xc0, 0x59, 0x93, 0xf0, 0xff, 0xdf, 0x6b, 0x7f, 0x7b, - 0x1f, 0x60, 0x3a, 0x34, 0xb4, 0x0a, 0x99, 0xfa, 0x46, 0xaf, 0xb7, 0xa5, 0xcd, 0xc9, 0x0d, 0x7c, - 0x5a, 0x25, 0xc4, 0xfa, 0x9f, 0x27, 0x21, 0x57, 0xaf, 0xaa, 0x63, 0xb5, 0x0e, 0x9a, 0xd8, 0x95, - 0x78, 0xef, 0x0c, 0xfa, 0x6c, 0x64, 0x7b, 0x13, 0xb5, 0xb1, 0x5c, 0x91, 0xb3, 0x2d, 0x72, 0x15, - 0xde, 0xeb, 0xa6, 0x50, 0x40, 0x18, 0x8a, 0x54, 0x39, 0xc1, 0x30, 0x49, 0xb8, 0xc7, 0xaf, 0x5e, - 0xed, 0x2c, 0xc9, 0xbe, 0xa7, 0x65, 0x1f, 0x17, 0x42, 0x23, 0x75, 0xe2, 0xa3, 0x8f, 0x61, 0xc9, - 0xb7, 0x07, 0xae, 0xed, 0x0e, 0x8c, 0xd0, 0x79, 0x29, 0xe1, 0xbc, 0x1b, 0x67, 0xa7, 0x6b, 0x0b, - 0x3d, 0x59, 0xa5, 0x7c, 0xb8, 0xa0, 0x90, 0x75, 0xe1, 0x4a, 0xf4, 0x11, 0x2c, 0xc6, 0x54, 0xb9, - 0x17, 0xa5, 0xdb, 0xb5, 0xb3, 0xd3, 0xb5, 0x62, 0xa4, 0xf9, 0x98, 0x4e, 0x70, 0x31, 0x52, 0x7c, - 0x4c, 0xc5, 0xf5, 0xc2, 0x1e, 0xf3, 0x4c, 0x6a, 0x78, 0x62, 0x4d, 0x8b, 0x13, 0x3c, 0x8d, 0x0b, - 0x42, 0x26, 0x97, 0xb9, 0xfe, 0x04, 0x6e, 0x76, 0x3c, 0x73, 0x9f, 0xfa, 0x81, 0x74, 0x85, 0xf2, - 0xe2, 0xe7, 0x70, 0x27, 0x20, 0xfe, 0x81, 0xb1, 0x6f, 0xfb, 0x01, 0xf3, 0x26, 0x86, 0x47, 0x03, - 0xea, 0xf2, 0x7a, 0x43, 0xbc, 0x48, 0xa9, 0xfb, 0x9f, 0xdb, 0x1c, 0xb3, 0x29, 0x21, 0x38, 0x44, - 0x6c, 0x71, 0x80, 0xde, 0x82, 0x22, 0x67, 0xe1, 0x0d, 0xba, 0x47, 0xc6, 0x4e, 0xc0, 0x47, 0x0f, - 0x0e, 0x1b, 0x18, 0x2f, 0x7d, 0x4c, 0xe5, 0x1d, 0x36, 0x90, 0x9f, 0xfa, 0x4f, 0x41, 0x6b, 0xd8, - 0xfe, 0x88, 0x04, 0xe6, 0x7e, 0x78, 0xb1, 0x85, 0x1a, 0xa0, 0xed, 0x53, 0xe2, 0x05, 0xbb, 0x94, - 0x04, 0xc6, 0x88, 0x7a, 0x36, 0xb3, 0xae, 0x9f, 0xe5, 0xa5, 0x48, 0xa5, 0x2b, 0x34, 0xf4, 0xff, - 0x4a, 0x00, 0x60, 0xb2, 0x17, 0x32, 0xb2, 0x1f, 0xc2, 0x0d, 0xdf, 0x25, 0x23, 0x7f, 0x9f, 0x05, - 0x86, 0xed, 0x06, 0xd4, 0x3b, 0x24, 0x8e, 0xba, 0x9f, 0xd0, 0xc2, 0x8a, 0x96, 0x92, 0xa3, 0xf7, - 0x01, 0x1d, 0x50, 0x3a, 0x32, 0x98, 0x63, 0x19, 0x61, 0xa5, 0x7c, 0x2f, 0x4b, 0x63, 0x8d, 0xd7, - 0x74, 0x1c, 0xab, 0x17, 0xca, 0x51, 0x0d, 0x56, 0xf9, 0xf0, 0xa9, 0x1b, 0x78, 0x36, 0xf5, 0x8d, - 0x3d, 0xe6, 0x19, 0xbe, 0xc3, 0x8e, 0x8c, 0x3d, 0xe6, 0x38, 0xec, 0x88, 0x7a, 0xe1, 0xd5, 0x4f, - 0xc9, 0x61, 0x83, 0xa6, 0x04, 0x6d, 0x30, 0xaf, 0xe7, 0xb0, 0xa3, 0x8d, 0x10, 0xc1, 0x69, 0xdb, - 0x74, 0xcc, 0x81, 0x6d, 0x1e, 0x84, 0xb4, 0x2d, 0x92, 0xf6, 0x6d, 0xf3, 0x00, 0xbd, 0x09, 0x0b, - 0xd4, 0xa1, 0xe2, 0x06, 0x40, 0xa2, 0x32, 0x02, 0x55, 0x0c, 0x85, 0x1c, 0xa4, 0x7f, 0x01, 0x5a, - 0xd3, 0x35, 0xbd, 0xc9, 0x28, 0x36, 0xe7, 0xef, 0x03, 0xe2, 0x9b, 0xa4, 0xe1, 0x30, 0xf3, 0xc0, - 0x18, 0x12, 0x97, 0x0c, 0x78, 0xbf, 0xe4, 0x1b, 0x8d, 0xc6, 0x6b, 0xb6, 0x98, 0x79, 0xb0, 0xad, - 0xe4, 0xfa, 0xc7, 0x00, 0xbd, 0x91, 0x47, 0x89, 0xd5, 0xe1, 0x6c, 0x82, 0xbb, 0x4e, 0x94, 0x0c, - 0x4b, 0x3d, 0x03, 0x31, 0x4f, 0x2d, 0x75, 0x4d, 0x56, 0x34, 0x22, 0xb9, 0xfe, 0xcb, 0x70, 0xb3, - 0xeb, 0x10, 0x53, 0x3c, 0x89, 0x76, 0xa3, 0x47, 0x07, 0xf4, 0x10, 0xb2, 0x12, 0xaa, 0x66, 0x72, - 0xe6, 0x72, 0x9b, 0xb6, 0xb9, 0x39, 0x87, 0x15, 0xbe, 0x56, 0x04, 0x98, 0xda, 0xd1, 0x9f, 0x41, - 0x3e, 0x32, 0x8f, 0xca, 0xc0, 0x53, 0x60, 0x1e, 0xdd, 0xb6, 0xab, 0x72, 0xd6, 0x3c, 0x8e, 0x8b, - 0x50, 0x0b, 0x0a, 0xa3, 0x48, 0xf9, 0x4a, 0x3a, 0x37, 0xa3, 0xd3, 0x38, 0xae, 0xab, 0x7f, 0x06, - 0xf0, 0x13, 0x66, 0xbb, 0x7d, 0x76, 0x40, 0x5d, 0xf1, 0xce, 0xc5, 0xb3, 0x35, 0x1a, 0x3a, 0x42, - 0x95, 0x44, 0x32, 0x2a, 0xbd, 0x18, 0x3d, 0xf7, 0xc8, 0xa2, 0xfe, 0x57, 0x49, 0xc8, 0x62, 0xc6, - 0x82, 0x7a, 0x15, 0x95, 0x21, 0xab, 0x96, 0xba, 0x38, 0x42, 0x6a, 0xf9, 0xb3, 0xd3, 0xb5, 0x8c, - 0x5c, 0xe3, 0x19, 0x53, 0x2c, 0xee, 0xd8, 0x26, 0x9c, 0xbc, 0x6c, 0x13, 0x46, 0xf7, 0xa0, 0xa8, - 0x40, 0xc6, 0x3e, 0xf1, 0xf7, 0x65, 0x8e, 0x55, 0x5b, 0x3c, 0x3b, 0x5d, 0x03, 0x89, 0xdc, 0x24, - 0xfe, 0x3e, 0x06, 0x89, 0xe6, 0xdf, 0xa8, 0x09, 0x85, 0xaf, 0x98, 0xed, 0x1a, 0x81, 0x18, 0x84, - 0xba, 0xee, 0x9a, 0x39, 0x15, 0xd3, 0xa1, 0xaa, 0x77, 0x51, 0xf8, 0x6a, 0x3a, 0xf8, 0x26, 0x2c, - 0x78, 0x8c, 0x05, 0x72, 0xe7, 0xb1, 0x99, 0xab, 0x32, 0xe9, 0xf2, 0xcc, 0x0b, 0x56, 0xc6, 0x02, - 0xac, 0x70, 0xb8, 0xe8, 0xc5, 0x4a, 0xe8, 0x1e, 0x2c, 0x3b, 0xc4, 0x0f, 0x0c, 0xb1, 0x65, 0x59, - 0x53, 0x6b, 0x59, 0xb1, 0x5a, 0x10, 0xaf, 0xdb, 0x10, 0x55, 0xa1, 0x86, 0xfe, 0x0f, 0x09, 0x28, - 0xf0, 0xc1, 0xd8, 0x7b, 0xb6, 0xc9, 0x79, 0xda, 0x77, 0xa7, 0x0f, 0xb7, 0x21, 0x65, 0xfa, 0x9e, - 0x72, 0xaa, 0x38, 0x3f, 0xeb, 0x3d, 0x8c, 0xb9, 0x0c, 0x7d, 0x01, 0x59, 0x95, 0xd1, 0x4b, 0xe6, - 0xa0, 0x5f, 0xcf, 0x28, 0x95, 0x6f, 0x94, 0x9e, 0x88, 0xc7, 0x69, 0xef, 0xe4, 0x3e, 0x8e, 0xe3, - 0x22, 0x74, 0x0b, 0x92, 0xa6, 0x74, 0x97, 0x7a, 0x78, 0xaf, 0xb7, 0x71, 0xd2, 0x74, 0xf5, 0xbf, - 0x4d, 0xc0, 0xc2, 0x74, 0xcd, 0xf2, 0x08, 0xb8, 0x03, 0x79, 0x7f, 0xbc, 0xeb, 0x4f, 0xfc, 0x80, - 0x0e, 0xc3, 0x37, 0xbc, 0x48, 0x80, 0x5a, 0x90, 0x27, 0xce, 0x80, 0x79, 0x76, 0xb0, 0x3f, 0x54, - 0xc9, 0xe4, 0xec, 0xd3, 0x3e, 0x6e, 0xb3, 0x52, 0x0d, 0x55, 0xf0, 0x54, 0x3b, 0x3c, 0xba, 0xc5, - 0x71, 0x25, 0x8f, 0xee, 0x37, 0xa0, 0xe8, 0x90, 0xa1, 0xb8, 0xe2, 0x08, 0xec, 0xa1, 0x1c, 0x47, - 0x1a, 0x17, 0x94, 0xac, 0x6f, 0x0f, 0xa9, 0xae, 0x43, 0x3e, 0x32, 0x86, 0x96, 0xa0, 0x50, 0x6d, - 0xf6, 0x8c, 0xfb, 0xeb, 0x0f, 0x8d, 0x47, 0xf5, 0x6d, 0x6d, 0x4e, 0xd1, 0xcb, 0x3f, 0x4b, 0xc0, - 0x82, 0xda, 0x51, 0x14, 0x65, 0x7f, 0x13, 0xe6, 0x3d, 0xb2, 0x17, 0x84, 0x49, 0x45, 0x5a, 0x46, - 0x35, 0xdf, 0xa4, 0x79, 0x52, 0xc1, 0xab, 0x66, 0x27, 0x15, 0xb1, 0x57, 0xe5, 0xd4, 0x95, 0xaf, - 0xca, 0xe9, 0xff, 0x97, 0x57, 0x65, 0xfd, 0x4f, 0x92, 0xb0, 0xa4, 0xd8, 0x5f, 0xb4, 0x81, 0xbd, - 0x0b, 0x79, 0x49, 0x04, 0xa7, 0x29, 0x91, 0x78, 0xc8, 0x94, 0xb8, 0x56, 0x03, 0xe7, 0x64, 0x75, - 0xcb, 0xe2, 0x39, 0xba, 0x82, 0xc6, 0x7e, 0x23, 0x01, 0x52, 0xd4, 0xe6, 0x09, 0x66, 0x03, 0xd2, - 0x7b, 0xb6, 0x43, 0x55, 0x9c, 0xcd, 0xbc, 0xbe, 0xbe, 0xd0, 0xbc, 0x78, 0x68, 0xe9, 0x8b, 0x2c, - 0x7f, 0x73, 0x0e, 0x0b, 0xed, 0xd2, 0x6f, 0x02, 0x4c, 0xa5, 0x33, 0x13, 0x59, 0x4e, 0x16, 0xd5, - 0x9d, 0x60, 0x48, 0x16, 0x5b, 0x0d, 0xcc, 0x65, 0xbc, 0x6a, 0x60, 0x5b, 0x6a, 0xcb, 0x10, 0x55, - 0x8f, 0x78, 0xd5, 0xc0, 0xb6, 0xa2, 0xd7, 0x9e, 0xf4, 0x35, 0xaf, 0x3d, 0xb5, 0x5c, 0x78, 0x33, - 0xa5, 0x6f, 0xc1, 0xad, 0x9a, 0x43, 0xcc, 0x03, 0xc7, 0xf6, 0x03, 0x6a, 0xc5, 0x57, 0xe8, 0x3a, - 0x64, 0xcf, 0xf1, 0xb4, 0xab, 0x2e, 0x02, 0x15, 0x52, 0xff, 0xb7, 0x04, 0x14, 0x37, 0x29, 0x71, - 0x82, 0xfd, 0xe9, 0x6d, 0x4a, 0x40, 0xfd, 0x40, 0x6d, 0xf0, 0xe2, 0x1b, 0x7d, 0x08, 0xb9, 0xe8, - 0x18, 0xbf, 0xf6, 0x45, 0x26, 0x82, 0xa2, 0x07, 0x30, 0xcf, 0x63, 0x9a, 0x8d, 0xc3, 0xfc, 0xe0, - 0xaa, 0xcb, 0x7e, 0x85, 0xe4, 0x9b, 0xba, 0x47, 0xc5, 0xb9, 0x2d, 0x9c, 0x92, 0xc1, 0x61, 0x11, - 0xfd, 0x12, 0x14, 0xc5, 0x5d, 0x75, 0x48, 0x53, 0x32, 0xd7, 0xd9, 0x2c, 0xc8, 0xe7, 0x26, 0x49, - 0x51, 0xfe, 0x27, 0x01, 0xcb, 0xdb, 0x64, 0xb2, 0x4b, 0xd5, 0x32, 0xa5, 0x16, 0xa6, 0x26, 0xf3, - 0x2c, 0xd4, 0x8d, 0x2f, 0xef, 0x2b, 0x5e, 0xaf, 0x66, 0x29, 0xcf, 0x5e, 0xe5, 0x61, 0xce, 0x92, - 0x8c, 0xe5, 0x2c, 0xcb, 0x90, 0x71, 0x99, 0x6b, 0x52, 0xb5, 0xf6, 0x65, 0x41, 0xb7, 0xe3, 0x4b, - 0xbb, 0x14, 0x3d, 0x2c, 0x89, 0x67, 0xa1, 0x36, 0x0b, 0xa2, 0xd6, 0xd0, 0x17, 0x50, 0xea, 0x35, - 0xeb, 0xb8, 0xd9, 0xaf, 0x75, 0x7e, 0x6a, 0xf4, 0xaa, 0x5b, 0xbd, 0xea, 0xfa, 0x3d, 0xa3, 0xdb, - 0xd9, 0xfa, 0xf2, 0xfe, 0x83, 0x7b, 0x1f, 0x6a, 0x89, 0x52, 0xf9, 0xf8, 0xa4, 0x7c, 0xa7, 0x5d, - 0xad, 0x6f, 0xc9, 0x58, 0xde, 0x65, 0xcf, 0x7a, 0xc4, 0xf1, 0xc9, 0xfa, 0xbd, 0x2e, 0x73, 0x26, - 0x1c, 0xa3, 0x9f, 0x24, 0xa0, 0x18, 0x3f, 0x1f, 0xe2, 0xc7, 0x5e, 0xe2, 0xd2, 0x63, 0x6f, 0x7a, - 0x7a, 0x26, 0x2f, 0x39, 0x3d, 0x37, 0x60, 0xd9, 0xf4, 0x98, 0xef, 0x1b, 0x9c, 0x30, 0x53, 0xeb, - 0x02, 0x25, 0x7f, 0xe5, 0xec, 0x74, 0xed, 0x46, 0x9d, 0xd7, 0xf7, 0x44, 0xb5, 0x32, 0x7f, 0xc3, - 0x8c, 0x89, 0x44, 0x4b, 0xef, 0xfd, 0x22, 0x05, 0xf9, 0xe8, 0xba, 0x99, 0x2f, 0x19, 0x9e, 0xeb, - 0x2b, 0x57, 0x44, 0xf2, 0x36, 0x3d, 0x42, 0x6f, 0x4c, 0xb3, 0xfc, 0x2f, 0xe4, 0xfb, 0x5a, 0x54, - 0x1d, 0x66, 0xf8, 0x6f, 0x41, 0xae, 0xda, 0xeb, 0xb5, 0x1e, 0xb5, 0x9b, 0x0d, 0xed, 0xeb, 0x44, - 0xe9, 0x95, 0xe3, 0x93, 0xf2, 0x8d, 0x08, 0x54, 0xf5, 0x65, 0x4f, 0x05, 0xaa, 0x5e, 0x6f, 0x76, - 0xfb, 0xcd, 0x86, 0xf6, 0x3c, 0x79, 0x11, 0x25, 0xb2, 0x56, 0xf1, 0x4a, 0x9e, 0xef, 0xe2, 0x66, - 0xb7, 0x8a, 0x79, 0x83, 0x5f, 0x27, 0xe5, 0xe5, 0xc3, 0xb4, 0x45, 0x8f, 0x8e, 0x88, 0xc7, 0xdb, - 0x5c, 0x0d, 0x7f, 0x2d, 0xf2, 0x3c, 0x25, 0x5f, 0x52, 0xa7, 0x77, 0xe7, 0x94, 0x58, 0x13, 0xde, - 0x9a, 0x78, 0xb4, 0x10, 0x66, 0x52, 0x17, 0x5a, 0xeb, 0xf1, 0x40, 0xe5, 0x56, 0x74, 0x98, 0xc7, - 0x3b, 0xed, 0x36, 0x07, 0x3d, 0x4f, 0x5f, 0x18, 0x1d, 0x1e, 0xbb, 0x3c, 0x23, 0x41, 0x77, 0x21, - 0x17, 0xbe, 0x69, 0x68, 0x5f, 0xa7, 0x2f, 0x74, 0xa8, 0x1e, 0x3e, 0xc8, 0x88, 0x06, 0x37, 0x77, - 0xfa, 0xe2, 0xc7, 0x2c, 0xcf, 0x33, 0x17, 0x1b, 0xdc, 0x1f, 0x07, 0x16, 0x3b, 0x72, 0xf9, 0x04, - 0xab, 0x7b, 0x8e, 0xaf, 0x33, 0x32, 0x29, 0x8c, 0x30, 0xea, 0x92, 0xe3, 0x2d, 0xc8, 0xe1, 0xe6, - 0x4f, 0xe4, 0xef, 0x5e, 0x9e, 0x67, 0x2f, 0xd8, 0xc1, 0xf4, 0x2b, 0x6a, 0xaa, 0xd6, 0x3a, 0xb8, - 0xbb, 0x59, 0x15, 0x2e, 0xbf, 0x88, 0xea, 0x78, 0xa3, 0x7d, 0xe2, 0x52, 0x6b, 0xfa, 0x9c, 0x1c, - 0x55, 0xbd, 0xf7, 0x2b, 0x90, 0x0b, 0x69, 0x03, 0x5a, 0x85, 0xec, 0xd3, 0x0e, 0x7e, 0xdc, 0xc4, - 0xda, 0x9c, 0xf4, 0x61, 0x58, 0xf3, 0x54, 0x12, 0xbe, 0x32, 0xcc, 0x6f, 0x57, 0xdb, 0xd5, 0x47, - 0x4d, 0x1c, 0x5e, 0x41, 0x86, 0x00, 0x75, 0xf6, 0x95, 0x34, 0xd5, 0x40, 0x64, 0xb3, 0xb6, 0xf2, - 0xcd, 0xb7, 0xab, 0x73, 0x3f, 0xff, 0x76, 0x75, 0xee, 0xf9, 0xd9, 0x6a, 0xe2, 0x9b, 0xb3, 0xd5, - 0xc4, 0xcf, 0xce, 0x56, 0x13, 0xff, 0x72, 0xb6, 0x9a, 0xd8, 0xcd, 0x8a, 0x1d, 0xe3, 0xc1, 0xff, - 0x06, 0x00, 0x00, 0xff, 0xff, 0x45, 0x07, 0xa3, 0xee, 0x6e, 0x2a, 0x00, 0x00, + // 4449 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xac, 0x7a, 0x4d, 0x6c, 0x23, 0xc7, + 0x72, 0xbf, 0xf8, 0x29, 0xb2, 0x48, 0x49, 0xb3, 0xbd, 0xf2, 0x5a, 0x4b, 0xaf, 0x25, 0x7a, 0xec, + 0x7d, 0xfe, 0x78, 0x06, 0xbd, 0xab, 0x7d, 0x36, 0xd6, 0xf6, 0xff, 0xd9, 0xe6, 0x97, 0x56, 0x7c, + 0x2b, 0x91, 0x44, 0x93, 0xda, 0x7d, 0x3e, 0xfc, 0x33, 0x18, 0xcd, 0xb4, 0xa8, 0xb1, 0x86, 0xd3, + 0xcc, 0x4c, 0x53, 0x5a, 0x26, 0x08, 0xb2, 0xc8, 0x21, 0x09, 0x74, 0x4a, 0x6e, 0x01, 0x02, 0x9d, + 0x92, 0x53, 0x10, 0xe4, 0x92, 0x43, 0x90, 0x5c, 0xe2, 0x00, 0x39, 0xf8, 0x96, 0x97, 0x04, 0x08, + 0x1e, 0x12, 0x40, 0x89, 0x75, 0xc8, 0x2d, 0x48, 0x2e, 0x0f, 0xb9, 0x24, 0x40, 0xd0, 0x1f, 0x33, + 0x1c, 0x69, 0x29, 0x69, 0x1d, 0xe7, 0x22, 0x4d, 0x57, 0xfd, 0xaa, 0xba, 0xbb, 0xba, 0xba, 0xbb, + 0xaa, 0x9a, 0x50, 0x60, 0x93, 0x11, 0x09, 0x2a, 0x23, 0x9f, 0x32, 0x8a, 0x90, 0x4d, 0xad, 0x03, + 0xe2, 0x57, 0x82, 0x23, 0xd3, 0x1f, 0x1e, 0x38, 0xac, 0x72, 0x78, 0xbf, 0xb4, 0x36, 0xa0, 0x74, + 0xe0, 0x92, 0x0f, 0x04, 0x62, 0x77, 0xbc, 0xf7, 0x01, 0x73, 0x86, 0x24, 0x60, 0xe6, 0x70, 0x24, + 0x85, 0x4a, 0xab, 0x17, 0x01, 0xf6, 0xd8, 0x37, 0x99, 0x43, 0x3d, 0xc5, 0x5f, 0x1e, 0xd0, 0x01, + 0x15, 0x9f, 0x1f, 0xf0, 0x2f, 0x49, 0xd5, 0xd7, 0x60, 0xfe, 0x09, 0xf1, 0x03, 0x87, 0x7a, 0x68, + 0x19, 0x32, 0x8e, 0x67, 0x93, 0x67, 0x2b, 0x89, 0x72, 0xe2, 0x9d, 0x34, 0x96, 0x0d, 0xfd, 0x1e, + 0x40, 0x8b, 0x7f, 0x34, 0x3d, 0xe6, 0x4f, 0x90, 0x06, 0xa9, 0x03, 0x32, 0x11, 0x88, 0x3c, 0xe6, + 0x9f, 0x9c, 0x72, 0x68, 0xba, 0x2b, 0x49, 0x49, 0x39, 0x34, 0x5d, 0xfd, 0xdb, 0x04, 0x14, 0xaa, + 0x9e, 0x47, 0x99, 0xe8, 0x3d, 0x40, 0x08, 0xd2, 0x9e, 0x39, 0x24, 0x4a, 0x48, 0x7c, 0xa3, 0x3a, + 0x64, 0x5d, 0x73, 0x97, 0xb8, 0xc1, 0x4a, 0xb2, 0x9c, 0x7a, 0xa7, 0xb0, 0xfe, 0xc3, 0xca, 0x8b, + 0x53, 0xae, 0xc4, 0x94, 0x54, 0xb6, 0x04, 0x5a, 0x0c, 0x02, 0x2b, 0x51, 0xf4, 0x19, 0xcc, 0x3b, + 0x9e, 0xed, 0x58, 0x24, 0x58, 0x49, 0x0b, 0x2d, 0xab, 0xb3, 0xb4, 0x4c, 0x47, 0x5f, 0x4b, 0x7f, + 0x73, 0xba, 0x36, 0x87, 0x43, 0xa1, 0xd2, 0xc7, 0x50, 0x88, 0xa9, 0x9d, 0x31, 0xb7, 0x65, 0xc8, + 0x1c, 0x9a, 0xee, 0x98, 0xa8, 0xd9, 0xc9, 0xc6, 0x27, 0xc9, 0x87, 0x09, 0xfd, 0x4b, 0xc8, 0x63, + 0x12, 0xd0, 0xb1, 0x6f, 0x91, 0x00, 0xbd, 0x0b, 0x79, 0xcf, 0xf4, 0xa8, 0x61, 0x8d, 0xc6, 0x81, + 0x10, 0x4f, 0xd5, 0x8a, 0x67, 0xa7, 0x6b, 0xb9, 0xb6, 0xe9, 0xd1, 0x7a, 0x77, 0x27, 0xc0, 0x39, + 0xce, 0xae, 0x8f, 0xc6, 0x01, 0x7a, 0x03, 0x8a, 0x43, 0x32, 0xa4, 0xfe, 0xc4, 0xd8, 0x9d, 0x30, + 0x12, 0x08, 0xc5, 0x29, 0x5c, 0x90, 0xb4, 0x1a, 0x27, 0xe9, 0xbf, 0x93, 0x80, 0xe5, 0x50, 0x37, + 0x26, 0xbf, 0x3c, 0x76, 0x7c, 0x32, 0x24, 0x1e, 0x0b, 0xd0, 0x87, 0x90, 0x75, 0x9d, 0xa1, 0xc3, + 0x64, 0x1f, 0x85, 0xf5, 0xd7, 0x67, 0xcd, 0x36, 0x1a, 0x15, 0x56, 0x60, 0x54, 0x85, 0xa2, 0x4f, + 0x02, 0xe2, 0x1f, 0x4a, 0x4b, 0x8a, 0x2e, 0xaf, 0x15, 0x3e, 0x27, 0xa2, 0x6f, 0x40, 0xae, 0xeb, + 0x9a, 0x6c, 0x8f, 0xfa, 0x43, 0xa4, 0x43, 0xd1, 0xf4, 0xad, 0x7d, 0x87, 0x11, 0x8b, 0x8d, 0xfd, + 0x70, 0x55, 0xcf, 0xd1, 0xd0, 0x2d, 0x48, 0x52, 0xd9, 0x51, 0xbe, 0x96, 0x3d, 0x3b, 0x5d, 0x4b, + 0x76, 0x7a, 0x38, 0x49, 0x03, 0xfd, 0x53, 0xb8, 0xd1, 0x75, 0xc7, 0x03, 0xc7, 0x6b, 0x90, 0xc0, + 0xf2, 0x9d, 0x11, 0xd7, 0xce, 0xdd, 0x83, 0xfb, 0x7e, 0xe8, 0x1e, 0xfc, 0x3b, 0x72, 0x99, 0xe4, + 0xd4, 0x65, 0xf4, 0xdf, 0x4a, 0xc2, 0x8d, 0xa6, 0x37, 0x70, 0x3c, 0x12, 0x97, 0xbe, 0x0b, 0x8b, + 0x44, 0x10, 0x8d, 0x43, 0xe9, 0xc6, 0x4a, 0xcf, 0x82, 0xa4, 0x86, 0xbe, 0xdd, 0xba, 0xe0, 0x6f, + 0xf7, 0x67, 0x4d, 0xff, 0x05, 0xed, 0x33, 0xbd, 0xae, 0x09, 0xf3, 0x23, 0x31, 0x89, 0x60, 0x25, + 0x25, 0x74, 0xdd, 0x9d, 0xa5, 0xeb, 0x85, 0x79, 0x86, 0xce, 0xa7, 0x64, 0xbf, 0x8f, 0xf3, 0xfd, + 0x71, 0x12, 0x96, 0xda, 0xd4, 0x3e, 0x67, 0x87, 0x12, 0xe4, 0xf6, 0x69, 0xc0, 0x62, 0x1b, 0x2d, + 0x6a, 0xa3, 0x87, 0x90, 0x1b, 0xa9, 0xe5, 0x53, 0xab, 0x7f, 0x67, 0xf6, 0x90, 0x25, 0x06, 0x47, + 0x68, 0xf4, 0x29, 0xe4, 0xfd, 0xd0, 0x27, 0x56, 0x52, 0x2f, 0xe3, 0x38, 0x53, 0x3c, 0xfa, 0x31, + 0x64, 0xe5, 0x22, 0xac, 0xa4, 0x85, 0xe4, 0xdd, 0x97, 0xb2, 0x39, 0x56, 0x42, 0xe8, 0x11, 0xe4, + 0x98, 0x1b, 0x18, 0x8e, 0xb7, 0x47, 0x57, 0x32, 0x42, 0xc1, 0xda, 0x2c, 0x05, 0xdc, 0x10, 0xfd, + 0xad, 0x5e, 0xcb, 0xdb, 0xa3, 0xb5, 0xc2, 0xd9, 0xe9, 0xda, 0xbc, 0x6a, 0xe0, 0x79, 0xe6, 0x06, + 0xfc, 0x43, 0xff, 0xdd, 0x04, 0x14, 0x62, 0x28, 0xf4, 0x3a, 0x00, 0xf3, 0xc7, 0x01, 0x33, 0x7c, + 0x4a, 0x99, 0x30, 0x56, 0x11, 0xe7, 0x05, 0x05, 0x53, 0xca, 0x50, 0x05, 0x6e, 0x5a, 0xc4, 0x67, + 0x86, 0x13, 0x04, 0x63, 0xe2, 0x1b, 0xc1, 0x78, 0xf7, 0x2b, 0x62, 0x31, 0x61, 0xb8, 0x22, 0xbe, + 0xc1, 0x59, 0x2d, 0xc1, 0xe9, 0x49, 0x06, 0x7a, 0x00, 0xb7, 0xe2, 0xf8, 0xd1, 0x78, 0xd7, 0x75, + 0x2c, 0x83, 0x2f, 0x66, 0x4a, 0x88, 0xdc, 0x9c, 0x8a, 0x74, 0x05, 0xef, 0x31, 0x99, 0xe8, 0x3f, + 0x4f, 0x80, 0x86, 0xcd, 0x3d, 0xb6, 0x4d, 0x86, 0xbb, 0xc4, 0xef, 0x31, 0x93, 0x8d, 0x03, 0x74, + 0x0b, 0xb2, 0x2e, 0x31, 0x6d, 0xe2, 0x8b, 0x41, 0xe5, 0xb0, 0x6a, 0xa1, 0x1d, 0xbe, 0x83, 0x4d, + 0x6b, 0xdf, 0xdc, 0x75, 0x5c, 0x87, 0x4d, 0xc4, 0x50, 0x16, 0x67, 0xbb, 0xf0, 0x45, 0x9d, 0x15, + 0x1c, 0x13, 0xc4, 0xe7, 0xd4, 0xa0, 0x15, 0x98, 0x1f, 0x92, 0x20, 0x30, 0x07, 0x44, 0x8c, 0x34, + 0x8f, 0xc3, 0xa6, 0xfe, 0x29, 0x14, 0xe3, 0x72, 0xa8, 0x00, 0xf3, 0x3b, 0xed, 0xc7, 0xed, 0xce, + 0xd3, 0xb6, 0x36, 0x87, 0x96, 0xa0, 0xb0, 0xd3, 0xc6, 0xcd, 0x6a, 0x7d, 0xb3, 0x5a, 0xdb, 0x6a, + 0x6a, 0x09, 0xb4, 0x00, 0xf9, 0x69, 0x33, 0xa9, 0xff, 0x69, 0x02, 0x80, 0x9b, 0x5b, 0x4d, 0xea, + 0x13, 0xc8, 0x04, 0xcc, 0x64, 0xd2, 0x2b, 0x17, 0xd7, 0xdf, 0xba, 0x6c, 0x0d, 0xd5, 0x78, 0xf9, + 0x3f, 0x82, 0xa5, 0x48, 0x7c, 0x84, 0xc9, 0x73, 0x23, 0xe4, 0x07, 0x84, 0x69, 0xdb, 0xbe, 0x1a, + 0xb8, 0xf8, 0xd6, 0x3f, 0x85, 0x8c, 0x90, 0x3e, 0x3f, 0xdc, 0x1c, 0xa4, 0x1b, 0xfc, 0x2b, 0x81, + 0xf2, 0x90, 0xc1, 0xcd, 0x6a, 0xe3, 0x4b, 0x2d, 0x89, 0x34, 0x28, 0x36, 0x5a, 0xbd, 0x7a, 0xa7, + 0xdd, 0x6e, 0xd6, 0xfb, 0xcd, 0x86, 0x96, 0xd2, 0xef, 0x42, 0xa6, 0x35, 0xe4, 0x9a, 0xef, 0x70, + 0x97, 0xdf, 0x23, 0x3e, 0xf1, 0xac, 0x70, 0x27, 0x4d, 0x09, 0xfa, 0xcf, 0xf2, 0x90, 0xd9, 0xa6, + 0x63, 0x8f, 0xa1, 0xf5, 0xd8, 0xb1, 0xb5, 0x38, 0xfb, 0xe6, 0x11, 0xc0, 0x4a, 0x7f, 0x32, 0x22, + 0xea, 0x58, 0xbb, 0x05, 0x59, 0xb9, 0x39, 0xd4, 0x74, 0x54, 0x8b, 0xd3, 0x99, 0xe9, 0x0f, 0x08, + 0x53, 0xf3, 0x51, 0x2d, 0xf4, 0x0e, 0xe4, 0x7c, 0x62, 0xda, 0xd4, 0x73, 0x27, 0x62, 0x0f, 0xe5, + 0xe4, 0xbd, 0x82, 0x89, 0x69, 0x77, 0x3c, 0x77, 0x82, 0x23, 0x2e, 0xda, 0x84, 0xe2, 0xae, 0xe3, + 0xd9, 0x06, 0x1d, 0xc9, 0x43, 0x3e, 0x73, 0xf9, 0x8e, 0x93, 0xa3, 0xaa, 0x39, 0x9e, 0xdd, 0x91, + 0x60, 0x5c, 0xd8, 0x9d, 0x36, 0x50, 0x1b, 0x16, 0x0f, 0xa9, 0x3b, 0x1e, 0x92, 0x48, 0x57, 0x56, + 0xe8, 0x7a, 0xfb, 0x72, 0x5d, 0x4f, 0x04, 0x3e, 0xd4, 0xb6, 0x70, 0x18, 0x6f, 0xa2, 0xc7, 0xb0, + 0xc0, 0x86, 0xa3, 0xbd, 0x20, 0x52, 0x37, 0x2f, 0xd4, 0xfd, 0xe0, 0x0a, 0x83, 0x71, 0x78, 0xa8, + 0xad, 0xc8, 0x62, 0xad, 0xd2, 0x6f, 0xa4, 0xa0, 0x10, 0x1b, 0x39, 0xea, 0x41, 0x61, 0xe4, 0xd3, + 0x91, 0x39, 0x10, 0x17, 0x95, 0x5a, 0x8b, 0xfb, 0x2f, 0x35, 0xeb, 0x4a, 0x77, 0x2a, 0x88, 0xe3, + 0x5a, 0xf4, 0x93, 0x24, 0x14, 0x62, 0x4c, 0xf4, 0x1e, 0xe4, 0x70, 0x17, 0xb7, 0x9e, 0x54, 0xfb, + 0x4d, 0x6d, 0xae, 0x74, 0xe7, 0xf8, 0xa4, 0xbc, 0x22, 0xb4, 0xc5, 0x15, 0x74, 0x7d, 0xe7, 0x90, + 0xbb, 0xde, 0x3b, 0x30, 0x1f, 0x42, 0x13, 0xa5, 0xd7, 0x8e, 0x4f, 0xca, 0xaf, 0x5e, 0x84, 0xc6, + 0x90, 0xb8, 0xb7, 0x59, 0xc5, 0xcd, 0x86, 0x96, 0x9c, 0x8d, 0xc4, 0xbd, 0x7d, 0xd3, 0x27, 0x36, + 0xfa, 0x01, 0x64, 0x15, 0x30, 0x55, 0x2a, 0x1d, 0x9f, 0x94, 0x6f, 0x5d, 0x04, 0x4e, 0x71, 0xb8, + 0xb7, 0x55, 0x7d, 0xd2, 0xd4, 0xd2, 0xb3, 0x71, 0xb8, 0xe7, 0x9a, 0x87, 0x04, 0xbd, 0x05, 0x19, + 0x09, 0xcb, 0x94, 0x6e, 0x1f, 0x9f, 0x94, 0x5f, 0x79, 0x41, 0x1d, 0x47, 0x95, 0x56, 0x7e, 0xfb, + 0x0f, 0x56, 0xe7, 0xfe, 0xe2, 0x0f, 0x57, 0xb5, 0x8b, 0xec, 0xd2, 0x7f, 0x25, 0x60, 0xe1, 0xdc, + 0x92, 0x23, 0x1d, 0xb2, 0x1e, 0xb5, 0xe8, 0x48, 0xde, 0x5f, 0xb9, 0x1a, 0x9c, 0x9d, 0xae, 0x65, + 0xdb, 0xb4, 0x4e, 0x47, 0x13, 0xac, 0x38, 0xe8, 0xf1, 0x85, 0x1b, 0xf8, 0xc1, 0x4b, 0xfa, 0xd3, + 0xcc, 0x3b, 0xf8, 0x73, 0x58, 0xb0, 0x7d, 0xe7, 0x90, 0xf8, 0x86, 0x45, 0xbd, 0x3d, 0x67, 0xa0, + 0xee, 0xa6, 0xd2, 0x2c, 0x9d, 0x0d, 0x01, 0xc4, 0x45, 0x29, 0x50, 0x17, 0xf8, 0xef, 0x71, 0xfb, + 0x96, 0x9e, 0x40, 0x31, 0xee, 0xa1, 0xfc, 0x3a, 0x09, 0x9c, 0x5f, 0x21, 0x2a, 0xa0, 0x13, 0xe1, + 0x1f, 0xce, 0x73, 0x8a, 0x08, 0xe7, 0xd0, 0xdb, 0x90, 0x1e, 0x52, 0x5b, 0xea, 0x59, 0xa8, 0xdd, + 0xe4, 0x41, 0xc0, 0x3f, 0x9e, 0xae, 0x15, 0x68, 0x50, 0xd9, 0x70, 0x5c, 0xb2, 0x4d, 0x6d, 0x82, + 0x05, 0x40, 0x3f, 0x84, 0x34, 0x3f, 0x2a, 0xd0, 0x6b, 0x90, 0xae, 0xb5, 0xda, 0x0d, 0x6d, 0xae, + 0x74, 0xe3, 0xf8, 0xa4, 0xbc, 0x20, 0x4c, 0xc2, 0x19, 0xdc, 0x77, 0xd1, 0x1a, 0x64, 0x9f, 0x74, + 0xb6, 0x76, 0xb6, 0xb9, 0x7b, 0xdd, 0x3c, 0x3e, 0x29, 0x2f, 0x45, 0x6c, 0x69, 0x34, 0xf4, 0x3a, + 0x64, 0xfa, 0xdb, 0xdd, 0x8d, 0x9e, 0x96, 0x2c, 0xa1, 0xe3, 0x93, 0xf2, 0x62, 0xc4, 0x17, 0x63, + 0x2e, 0xdd, 0x50, 0xab, 0x9a, 0x8f, 0xe8, 0xfa, 0x2f, 0x92, 0xb0, 0x80, 0x79, 0x26, 0xe1, 0xb3, + 0x2e, 0x75, 0x1d, 0x6b, 0x82, 0xba, 0x90, 0xb7, 0xa8, 0x67, 0x3b, 0xb1, 0x3d, 0xb5, 0x7e, 0xc9, + 0xad, 0x3f, 0x95, 0x0a, 0x5b, 0xf5, 0x50, 0x12, 0x4f, 0x95, 0xa0, 0x0f, 0x20, 0x63, 0x13, 0xd7, + 0x9c, 0xa8, 0xf0, 0xe3, 0x76, 0x45, 0xe6, 0x2a, 0x95, 0x30, 0x57, 0xa9, 0x34, 0x54, 0xae, 0x82, + 0x25, 0x4e, 0xc4, 0xc9, 0xe6, 0x33, 0xc3, 0x64, 0x8c, 0x0c, 0x47, 0x4c, 0xc6, 0x1e, 0x69, 0x5c, + 0x18, 0x9a, 0xcf, 0xaa, 0x8a, 0x84, 0xee, 0x43, 0xf6, 0xc8, 0xf1, 0x6c, 0x7a, 0xa4, 0xc2, 0x8b, + 0x2b, 0x94, 0x2a, 0xa0, 0x7e, 0xcc, 0x6f, 0xdd, 0x0b, 0xc3, 0xe4, 0xf6, 0x6e, 0x77, 0xda, 0xcd, + 0xd0, 0xde, 0x8a, 0xdf, 0xf1, 0xda, 0xd4, 0xe3, 0x7b, 0x05, 0x3a, 0x6d, 0x63, 0xa3, 0xda, 0xda, + 0xda, 0xc1, 0xdc, 0xe6, 0xcb, 0xc7, 0x27, 0x65, 0x2d, 0x82, 0x6c, 0x98, 0x8e, 0xcb, 0xe3, 0xdd, + 0xdb, 0x90, 0xaa, 0xb6, 0xbf, 0xd4, 0x92, 0x25, 0xed, 0xf8, 0xa4, 0x5c, 0x8c, 0xd8, 0x55, 0x6f, + 0x32, 0xdd, 0x46, 0x17, 0xfb, 0xd5, 0xff, 0x26, 0x05, 0xc5, 0x9d, 0x91, 0x6d, 0x32, 0x22, 0x7d, + 0x12, 0x95, 0xa1, 0x30, 0x32, 0x7d, 0xd3, 0x75, 0x89, 0xeb, 0x04, 0x43, 0x95, 0x85, 0xc5, 0x49, + 0xe8, 0xe3, 0x97, 0x35, 0x63, 0x2d, 0xc7, 0xfd, 0xec, 0xf7, 0xfe, 0x79, 0x2d, 0x11, 0x1a, 0x74, + 0x07, 0x16, 0xf7, 0xe4, 0x68, 0x0d, 0xd3, 0x12, 0x0b, 0x9b, 0x12, 0x0b, 0x5b, 0x99, 0xb5, 0xb0, + 0xf1, 0x61, 0x55, 0xd4, 0x24, 0xab, 0x42, 0x0a, 0x2f, 0xec, 0xc5, 0x9b, 0xe8, 0x01, 0xcc, 0x0f, + 0xa9, 0xe7, 0x30, 0xea, 0x5f, 0xbf, 0x0a, 0x21, 0x12, 0xbd, 0x07, 0x37, 0xf8, 0xe2, 0x86, 0xe3, + 0x11, 0x6c, 0x71, 0x63, 0x25, 0xf1, 0xd2, 0xd0, 0x7c, 0xa6, 0x3a, 0xc4, 0x9c, 0x8c, 0x6a, 0x90, + 0xa1, 0x3e, 0x0f, 0x89, 0xb2, 0x62, 0xb8, 0xef, 0x5f, 0x3b, 0x5c, 0xd9, 0xe8, 0x70, 0x19, 0x2c, + 0x45, 0xf5, 0x8f, 0x60, 0xe1, 0xdc, 0x24, 0x78, 0x24, 0xd0, 0xad, 0xee, 0xf4, 0x9a, 0xda, 0x1c, + 0x2a, 0x42, 0xae, 0xde, 0x69, 0xf7, 0x5b, 0xed, 0x1d, 0x1e, 0xca, 0x14, 0x21, 0x87, 0x3b, 0x5b, + 0x5b, 0xb5, 0x6a, 0xfd, 0xb1, 0x96, 0xd4, 0x2b, 0x50, 0x88, 0x69, 0x43, 0x8b, 0x00, 0xbd, 0x7e, + 0xa7, 0x6b, 0x6c, 0xb4, 0x70, 0xaf, 0x2f, 0x03, 0xa1, 0x5e, 0xbf, 0x8a, 0xfb, 0x8a, 0x90, 0xd0, + 0xff, 0x3d, 0x19, 0xae, 0xa8, 0x8a, 0x7d, 0x6a, 0xe7, 0x63, 0x9f, 0x2b, 0x06, 0xaf, 0xa2, 0x9f, + 0x69, 0x23, 0x8a, 0x81, 0x3e, 0x06, 0x10, 0x8e, 0x43, 0x6c, 0xc3, 0x64, 0x6a, 0xe1, 0x4b, 0x2f, + 0x18, 0xb9, 0x1f, 0x16, 0x03, 0x70, 0x5e, 0xa1, 0xab, 0x0c, 0xfd, 0x18, 0x8a, 0x16, 0x1d, 0x8e, + 0x5c, 0xa2, 0x84, 0x53, 0xd7, 0x0a, 0x17, 0x22, 0x7c, 0x95, 0xc5, 0xa3, 0xaf, 0xf4, 0xf9, 0xf8, + 0xf0, 0x37, 0x13, 0xa1, 0x65, 0x66, 0x04, 0x5c, 0x45, 0xc8, 0xed, 0x74, 0x1b, 0xd5, 0x7e, 0xab, + 0xfd, 0x48, 0x4b, 0x20, 0x80, 0xac, 0x30, 0x75, 0x43, 0x4b, 0xf2, 0x40, 0xb1, 0xde, 0xd9, 0xee, + 0x6e, 0x35, 0x45, 0xc8, 0x85, 0x96, 0x41, 0x0b, 0x8d, 0x6d, 0x08, 0x43, 0x36, 0x1b, 0x5a, 0x1a, + 0xdd, 0x84, 0xa5, 0x88, 0xaa, 0x24, 0x33, 0xe8, 0x16, 0xa0, 0x88, 0x38, 0x55, 0x91, 0xd5, 0x7f, + 0x0d, 0x96, 0xea, 0xd4, 0x63, 0xa6, 0xe3, 0x45, 0x41, 0xf4, 0x3a, 0x9f, 0xb4, 0x22, 0x19, 0x8e, + 0x2d, 0xcf, 0xf4, 0xda, 0xd2, 0xd9, 0xe9, 0x5a, 0x21, 0x82, 0xb6, 0x1a, 0x7c, 0xa6, 0x61, 0xc3, + 0xe6, 0xfb, 0x77, 0xe4, 0xd8, 0xc2, 0xb8, 0x99, 0xda, 0xfc, 0xd9, 0xe9, 0x5a, 0xaa, 0xdb, 0x6a, + 0x60, 0x4e, 0x43, 0xaf, 0x41, 0x9e, 0x3c, 0x73, 0x98, 0x61, 0xf1, 0x33, 0x9c, 0x1b, 0x30, 0x83, + 0x73, 0x9c, 0x50, 0xe7, 0x47, 0x76, 0x0d, 0xa0, 0x4b, 0x7d, 0xa6, 0x7a, 0xfe, 0x11, 0x64, 0x46, + 0xd4, 0x17, 0xe9, 0xf9, 0xa5, 0xc5, 0x08, 0x0e, 0x97, 0x8e, 0x8a, 0x25, 0x58, 0xff, 0xcb, 0x24, + 0x40, 0xdf, 0x0c, 0x0e, 0x94, 0x92, 0x87, 0x90, 0x8f, 0x0a, 0x3b, 0x2a, 0xcf, 0xbf, 0x72, 0xb5, + 0x23, 0x30, 0x7a, 0x10, 0x3a, 0x9b, 0x4c, 0x0f, 0x66, 0xe6, 0x69, 0x61, 0x47, 0xb3, 0x22, 0xec, + 0xf3, 0x39, 0x00, 0xbf, 0x12, 0x89, 0xef, 0xab, 0x95, 0xe7, 0x9f, 0xa8, 0x2e, 0xae, 0x05, 0x69, + 0x34, 0x15, 0x60, 0xbe, 0x39, 0xab, 0x93, 0x0b, 0x2b, 0xb2, 0x39, 0x87, 0xa7, 0x72, 0xe8, 0x73, + 0x28, 0xf0, 0x79, 0x1b, 0x81, 0xe0, 0xa9, 0xd8, 0xf2, 0x52, 0x53, 0x49, 0x0d, 0x18, 0x46, 0xd1, + 0x77, 0x4d, 0x83, 0x45, 0x7f, 0xec, 0xf1, 0x69, 0x2b, 0x1d, 0xba, 0x03, 0xaf, 0xb6, 0x09, 0x3b, + 0xa2, 0xfe, 0x41, 0x95, 0x31, 0xd3, 0xda, 0x1f, 0x12, 0x4f, 0xd9, 0x38, 0x16, 0x58, 0x27, 0xce, + 0x05, 0xd6, 0x2b, 0x30, 0x6f, 0xba, 0x8e, 0x19, 0x10, 0x19, 0x8d, 0xe4, 0x71, 0xd8, 0xe4, 0xe1, + 0x3f, 0x4f, 0x26, 0x48, 0x10, 0x10, 0x99, 0xdf, 0xe7, 0xf1, 0x94, 0xa0, 0xff, 0x7d, 0x12, 0xa0, + 0xd5, 0xad, 0x6e, 0x2b, 0xf5, 0x0d, 0xc8, 0xee, 0x99, 0x43, 0xc7, 0x9d, 0x5c, 0xb5, 0xc1, 0xa7, + 0xf8, 0x4a, 0x55, 0x2a, 0xda, 0x10, 0x32, 0x58, 0xc9, 0x8a, 0xac, 0x60, 0xbc, 0xeb, 0x11, 0x16, + 0x65, 0x05, 0xa2, 0xc5, 0x43, 0x10, 0xdf, 0xf4, 0xa2, 0x95, 0x91, 0x0d, 0x3e, 0xf4, 0x81, 0xc9, + 0xc8, 0x91, 0x39, 0x09, 0x77, 0xa5, 0x6a, 0xa2, 0x4d, 0x9e, 0x2d, 0x04, 0xc4, 0x3f, 0x24, 0xf6, + 0x4a, 0x46, 0xb8, 0xe0, 0x75, 0xe3, 0xc1, 0x0a, 0x2e, 0x83, 0xab, 0x48, 0xba, 0xf4, 0xa9, 0x88, + 0x08, 0xa6, 0xac, 0xef, 0x54, 0x9d, 0xb8, 0x07, 0x0b, 0xe7, 0xe6, 0xf9, 0x42, 0x3a, 0xd6, 0xea, + 0x3e, 0xf9, 0x91, 0x96, 0x56, 0x5f, 0x1f, 0x69, 0x59, 0xfd, 0x8f, 0x52, 0x72, 0x1f, 0x29, 0xab, + 0xce, 0xae, 0x17, 0xe6, 0x84, 0xf7, 0x5b, 0xd4, 0x55, 0xfe, 0xfd, 0xf6, 0xd5, 0xdb, 0x8b, 0x87, + 0xf7, 0x02, 0x8e, 0x23, 0x41, 0xb4, 0x06, 0x05, 0xb9, 0xfe, 0x06, 0xf7, 0x27, 0x61, 0xd6, 0x05, + 0x0c, 0x92, 0xc4, 0x25, 0xd1, 0x5d, 0x58, 0x14, 0xe9, 0x7b, 0xb0, 0x4f, 0x6c, 0x89, 0x49, 0x0b, + 0xcc, 0x42, 0x44, 0x15, 0xb0, 0x6d, 0x28, 0x2a, 0x82, 0x21, 0x42, 0xbb, 0x8c, 0x18, 0xd0, 0x7b, + 0xd7, 0x0d, 0x48, 0x8a, 0x88, 0x88, 0xaf, 0x30, 0x9a, 0x36, 0xf4, 0x06, 0xe4, 0xc2, 0xc1, 0xa2, + 0x15, 0x48, 0xf5, 0xeb, 0x5d, 0x6d, 0xae, 0xb4, 0x74, 0x7c, 0x52, 0x2e, 0x84, 0xe4, 0x7e, 0xbd, + 0xcb, 0x39, 0x3b, 0x8d, 0xae, 0x96, 0x38, 0xcf, 0xd9, 0x69, 0x74, 0x4b, 0x69, 0x1e, 0x62, 0xe8, + 0x7b, 0x50, 0x88, 0xf5, 0x80, 0xde, 0x84, 0xf9, 0x56, 0xfb, 0x11, 0x6e, 0xf6, 0x7a, 0xda, 0x5c, + 0xe9, 0xd6, 0xf1, 0x49, 0x19, 0xc5, 0xb8, 0x2d, 0x6f, 0xc0, 0xd7, 0x07, 0xbd, 0x0e, 0xe9, 0xcd, + 0x0e, 0xbf, 0xba, 0x64, 0x2c, 0x19, 0x43, 0x6c, 0xd2, 0x80, 0x95, 0x6e, 0xaa, 0xd8, 0x25, 0xae, + 0x58, 0xff, 0xfd, 0x04, 0x64, 0x65, 0x48, 0x3d, 0x73, 0xa1, 0xaa, 0x30, 0x1f, 0x26, 0x7a, 0x32, + 0xce, 0x7f, 0xfb, 0xf2, 0x98, 0xbc, 0xa2, 0x42, 0x68, 0xe9, 0x7e, 0xa1, 0x5c, 0xe9, 0x13, 0x28, + 0xc6, 0x19, 0xdf, 0xc9, 0xf9, 0x7e, 0x15, 0x0a, 0xdc, 0xbf, 0xc3, 0xd8, 0x7c, 0x1d, 0xb2, 0x32, + 0xec, 0x8f, 0x8e, 0xd2, 0xcb, 0x13, 0x04, 0x85, 0x44, 0x0f, 0x61, 0x5e, 0x26, 0x15, 0x61, 0x7d, + 0x6f, 0xf5, 0xea, 0x5d, 0x84, 0x43, 0xb8, 0xfe, 0x39, 0xa4, 0xbb, 0x84, 0xf8, 0xdc, 0xf6, 0x1e, + 0xb5, 0xc9, 0xf4, 0xf6, 0x51, 0xf9, 0x90, 0x4d, 0x5a, 0x0d, 0x9e, 0x0f, 0xd9, 0xa4, 0x65, 0x47, + 0x15, 0x8c, 0x64, 0xac, 0x82, 0xd1, 0x87, 0xe2, 0x53, 0xe2, 0x0c, 0xf6, 0x19, 0xb1, 0x85, 0xa2, + 0xf7, 0x21, 0x3d, 0x22, 0xd1, 0xe0, 0x57, 0x66, 0x3a, 0x18, 0x21, 0x3e, 0x16, 0x28, 0x7e, 0x8e, + 0x1c, 0x09, 0x69, 0x55, 0x55, 0x56, 0x2d, 0xfd, 0xef, 0x92, 0xb0, 0xd8, 0x0a, 0x82, 0xb1, 0xe9, + 0x59, 0x61, 0x60, 0xf2, 0xd9, 0xf9, 0xc0, 0xe4, 0x9d, 0x99, 0x33, 0x3c, 0x27, 0x72, 0xbe, 0x30, + 0xa3, 0x2e, 0x87, 0x64, 0x74, 0x39, 0xe8, 0xff, 0x96, 0x08, 0xab, 0x2f, 0x77, 0x63, 0xdb, 0xbd, + 0xb4, 0x72, 0x7c, 0x52, 0x5e, 0x8e, 0x6b, 0x22, 0x3b, 0xde, 0x81, 0x47, 0x8f, 0x3c, 0xf4, 0x06, + 0x64, 0x70, 0xb3, 0xdd, 0x7c, 0xaa, 0x25, 0xa4, 0x7b, 0x9e, 0x03, 0x61, 0xe2, 0x91, 0x23, 0xae, + 0xa9, 0xdb, 0x6c, 0x37, 0x78, 0x20, 0x91, 0x9c, 0xa1, 0xa9, 0x4b, 0x3c, 0xdb, 0xf1, 0x06, 0xe8, + 0x4d, 0xc8, 0xb6, 0x7a, 0xbd, 0x1d, 0x91, 0x1f, 0xbf, 0x7a, 0x7c, 0x52, 0xbe, 0x79, 0x0e, 0x25, + 0x2a, 0x6f, 0x36, 0x07, 0xf1, 0x28, 0x9e, 0x87, 0x18, 0x33, 0x40, 0x3c, 0x3c, 0x94, 0x20, 0xdc, + 0xe9, 0xf3, 0xe4, 0x3d, 0x33, 0x03, 0x84, 0x29, 0xff, 0xab, 0xb6, 0xdb, 0x3f, 0x25, 0x41, 0xab, + 0x5a, 0x16, 0x19, 0x31, 0xce, 0x57, 0x89, 0x53, 0x1f, 0x72, 0x23, 0xfe, 0xe5, 0x90, 0x30, 0x08, + 0x78, 0x38, 0xf3, 0x5d, 0xe3, 0x82, 0x5c, 0x05, 0x53, 0x97, 0x54, 0xed, 0xa1, 0x13, 0x04, 0x0e, + 0xf5, 0x24, 0x0d, 0x47, 0x9a, 0x4a, 0xff, 0x91, 0x80, 0x9b, 0x33, 0x10, 0xe8, 0x1e, 0xa4, 0x7d, + 0xea, 0x86, 0x6b, 0x78, 0xe7, 0xb2, 0xc2, 0x1a, 0x17, 0xc5, 0x02, 0x89, 0x56, 0x01, 0xcc, 0x31, + 0xa3, 0xa6, 0xe8, 0x5f, 0xac, 0x5e, 0x0e, 0xc7, 0x28, 0xe8, 0x29, 0x64, 0x03, 0x62, 0xf9, 0x24, + 0x0c, 0x15, 0x3f, 0xff, 0xdf, 0x8e, 0xbe, 0xd2, 0x13, 0x6a, 0xb0, 0x52, 0x57, 0xaa, 0x40, 0x56, + 0x52, 0xb8, 0xdb, 0xdb, 0x26, 0x33, 0x55, 0xd9, 0x55, 0x7c, 0x73, 0x6f, 0x32, 0xdd, 0x41, 0xe8, + 0x4d, 0xa6, 0x3b, 0xd0, 0xff, 0x3a, 0x09, 0xd0, 0x7c, 0xc6, 0x88, 0xef, 0x99, 0x6e, 0xbd, 0x8a, + 0x9a, 0xb1, 0xd3, 0x5f, 0xce, 0xf6, 0xdd, 0x99, 0xb5, 0xe4, 0x48, 0xa2, 0x52, 0xaf, 0xce, 0x38, + 0xff, 0x6f, 0x43, 0x6a, 0xec, 0xab, 0xa7, 0x2a, 0x19, 0xe6, 0xed, 0xe0, 0x2d, 0xcc, 0x69, 0xa8, + 0x39, 0x3d, 0xb6, 0x52, 0x97, 0x3f, 0x48, 0xc5, 0x3a, 0x98, 0x79, 0x74, 0xf1, 0x9d, 0x6f, 0x99, + 0x86, 0x45, 0xd4, 0xcd, 0x51, 0x94, 0x3b, 0xbf, 0x5e, 0xad, 0x13, 0x9f, 0xe1, 0xac, 0x65, 0xf2, + 0xff, 0xdf, 0xeb, 0x7c, 0x7b, 0x1f, 0x60, 0x3a, 0x35, 0xb4, 0x0a, 0x99, 0xfa, 0x46, 0xaf, 0xb7, + 0xa5, 0xcd, 0xc9, 0x03, 0x7c, 0xca, 0x12, 0x64, 0xfd, 0xcf, 0x93, 0x90, 0xab, 0x57, 0xd5, 0xb5, + 0x5a, 0x07, 0x4d, 0x9c, 0x4a, 0xa2, 0x58, 0x4d, 0x9e, 0x8d, 0x1c, 0x7f, 0xa2, 0x0e, 0x96, 0x2b, + 0x72, 0xb6, 0x45, 0x2e, 0xc2, 0x47, 0xdd, 0x14, 0x02, 0x08, 0x43, 0x91, 0x28, 0x23, 0x18, 0x96, + 0x19, 0x9e, 0xf1, 0xab, 0x57, 0x1b, 0x4b, 0x46, 0xdf, 0xd3, 0x76, 0x80, 0x0b, 0xa1, 0x92, 0xba, + 0x19, 0xa0, 0x8f, 0x61, 0x29, 0x70, 0x06, 0x9e, 0xe3, 0x0d, 0x8c, 0xd0, 0x78, 0xa2, 0x72, 0x5e, + 0xbb, 0x71, 0x76, 0xba, 0xb6, 0xd0, 0x93, 0x2c, 0x65, 0xc3, 0x05, 0x85, 0xac, 0x0b, 0x53, 0xa2, + 0x8f, 0x60, 0x31, 0x26, 0xca, 0xad, 0x28, 0xcd, 0xae, 0x9d, 0x9d, 0xae, 0x15, 0x23, 0xc9, 0xc7, + 0x64, 0x82, 0x8b, 0x91, 0xe0, 0x63, 0x22, 0xca, 0x0b, 0x7b, 0xd4, 0xb7, 0x88, 0xe1, 0x8b, 0x3d, + 0x2d, 0x6e, 0xf0, 0x34, 0x2e, 0x08, 0x9a, 0xdc, 0xe6, 0xfa, 0x13, 0xb8, 0xd9, 0xf1, 0xad, 0x7d, + 0x12, 0x30, 0x69, 0x0a, 0x65, 0xc5, 0xcf, 0xe1, 0x0e, 0x33, 0x83, 0x03, 0x63, 0xdf, 0x09, 0x18, + 0xf5, 0x27, 0x86, 0x4f, 0x18, 0xf1, 0x38, 0xdf, 0x10, 0xcf, 0x6d, 0xaa, 0xfe, 0x73, 0x9b, 0x63, + 0x36, 0x25, 0x04, 0x87, 0x88, 0x2d, 0x0e, 0xd0, 0x5b, 0x50, 0xe4, 0x51, 0x78, 0x83, 0xec, 0x99, + 0x63, 0x97, 0xf1, 0xd9, 0x83, 0x4b, 0x07, 0xc6, 0x4b, 0x5f, 0x53, 0x79, 0x97, 0x0e, 0xe4, 0xa7, + 0xfe, 0x53, 0xd0, 0x1a, 0x4e, 0x30, 0x32, 0x99, 0xb5, 0x1f, 0x16, 0xb6, 0x50, 0x03, 0xb4, 0x7d, + 0x62, 0xfa, 0x6c, 0x97, 0x98, 0xcc, 0x18, 0x11, 0xdf, 0xa1, 0xf6, 0xf5, 0xab, 0xbc, 0x14, 0x89, + 0x74, 0x85, 0x84, 0xfe, 0x9f, 0x09, 0x00, 0x6c, 0xee, 0x85, 0x11, 0xd9, 0x0f, 0xe1, 0x46, 0xe0, + 0x99, 0xa3, 0x60, 0x9f, 0x32, 0xc3, 0xf1, 0x18, 0xf1, 0x0f, 0x4d, 0x57, 0xd5, 0x27, 0xb4, 0x90, + 0xd1, 0x52, 0x74, 0xf4, 0x3e, 0xa0, 0x03, 0x42, 0x46, 0x06, 0x75, 0x6d, 0x23, 0x64, 0xca, 0xc7, + 0xc0, 0x34, 0xd6, 0x38, 0xa7, 0xe3, 0xda, 0xbd, 0x90, 0x8e, 0x6a, 0xb0, 0xca, 0xa7, 0x4f, 0x3c, + 0xe6, 0x3b, 0x24, 0x30, 0xf6, 0xa8, 0x6f, 0x04, 0x2e, 0x3d, 0x32, 0xf6, 0xa8, 0xeb, 0xd2, 0x23, + 0xe2, 0x87, 0xa5, 0x9f, 0x92, 0x4b, 0x07, 0x4d, 0x09, 0xda, 0xa0, 0x7e, 0xcf, 0xa5, 0x47, 0x1b, + 0x21, 0x82, 0x87, 0x6d, 0xd3, 0x39, 0x33, 0xc7, 0x3a, 0x08, 0xc3, 0xb6, 0x88, 0xda, 0x77, 0xac, + 0x03, 0xf4, 0x26, 0x2c, 0x10, 0x97, 0x88, 0x0a, 0x80, 0x44, 0x65, 0x04, 0xaa, 0x18, 0x12, 0x39, + 0x48, 0xff, 0x02, 0xb4, 0xa6, 0x67, 0xf9, 0x93, 0x51, 0x6c, 0xcd, 0xdf, 0x07, 0xc4, 0x0f, 0x49, + 0xc3, 0xa5, 0xd6, 0x81, 0x31, 0x34, 0x3d, 0x73, 0xc0, 0xc7, 0x25, 0xdf, 0x68, 0x34, 0xce, 0xd9, + 0xa2, 0xd6, 0xc1, 0xb6, 0xa2, 0xeb, 0x1f, 0x03, 0xf4, 0x46, 0x3e, 0x31, 0xed, 0x0e, 0x8f, 0x26, + 0xb8, 0xe9, 0x44, 0xcb, 0xb0, 0xd5, 0x1b, 0x17, 0xf5, 0xd5, 0x56, 0xd7, 0x24, 0xa3, 0x11, 0xd1, + 0xf5, 0xff, 0x0f, 0x37, 0xbb, 0xae, 0x69, 0x89, 0xf7, 0xde, 0x6e, 0xf4, 0xe8, 0x80, 0x1e, 0x42, + 0x56, 0x42, 0xd5, 0x4a, 0xce, 0xdc, 0x6e, 0xd3, 0x3e, 0x37, 0xe7, 0xb0, 0xc2, 0xd7, 0x8a, 0x00, + 0x53, 0x3d, 0xfa, 0x33, 0xc8, 0x47, 0xea, 0x51, 0x19, 0x78, 0x0a, 0xcc, 0xbd, 0xdb, 0xf1, 0x54, + 0xce, 0x9a, 0xc7, 0x71, 0x12, 0x6a, 0x41, 0x61, 0x14, 0x09, 0x5f, 0x19, 0xce, 0xcd, 0x18, 0x34, + 0x8e, 0xcb, 0xea, 0x9f, 0x01, 0xfc, 0x84, 0x3a, 0x5e, 0x9f, 0x1e, 0x10, 0x4f, 0xbc, 0x73, 0xf1, + 0x6c, 0x8d, 0x84, 0x86, 0x50, 0x2d, 0x91, 0x8c, 0x4a, 0x2b, 0x46, 0xcf, 0x3d, 0xb2, 0xa9, 0xff, + 0x55, 0x12, 0xb2, 0x98, 0x52, 0x56, 0xaf, 0xa2, 0x32, 0x64, 0xd5, 0x56, 0x17, 0x57, 0x48, 0x2d, + 0x7f, 0x76, 0xba, 0x96, 0x91, 0x7b, 0x3c, 0x63, 0x89, 0xcd, 0x1d, 0x3b, 0x84, 0x93, 0x97, 0x1d, + 0xc2, 0xe8, 0x1e, 0x14, 0x15, 0xc8, 0xd8, 0x37, 0x83, 0x7d, 0x99, 0x63, 0xd5, 0x16, 0xcf, 0x4e, + 0xd7, 0x40, 0x22, 0x37, 0xcd, 0x60, 0x1f, 0x83, 0x44, 0xf3, 0x6f, 0xd4, 0x84, 0xc2, 0x57, 0xd4, + 0xf1, 0x0c, 0x26, 0x26, 0xa1, 0xca, 0x5d, 0x33, 0x97, 0x62, 0x3a, 0x55, 0xf5, 0xe8, 0x0b, 0x5f, + 0x4d, 0x27, 0xdf, 0x84, 0x05, 0x9f, 0x52, 0x26, 0x4f, 0x1e, 0x87, 0x7a, 0x2a, 0x93, 0x2e, 0xcf, + 0x2c, 0xb0, 0x52, 0xca, 0xb0, 0xc2, 0xe1, 0xa2, 0x1f, 0x6b, 0xa1, 0x7b, 0xb0, 0xec, 0x9a, 0x01, + 0x33, 0xc4, 0x91, 0x65, 0x4f, 0xb5, 0x65, 0xc5, 0x6e, 0x41, 0x9c, 0xb7, 0x21, 0x58, 0xa1, 0x84, + 0xfe, 0x0f, 0x09, 0x28, 0xf0, 0xc9, 0x38, 0x7b, 0x8e, 0xc5, 0xe3, 0xb4, 0xef, 0x1e, 0x3e, 0xdc, + 0x86, 0x94, 0x15, 0xf8, 0xca, 0xa8, 0xe2, 0xfe, 0xac, 0xf7, 0x30, 0xe6, 0x34, 0xf4, 0x05, 0x64, + 0x55, 0x46, 0x2f, 0x23, 0x07, 0xfd, 0xfa, 0x88, 0x52, 0xd9, 0x46, 0xc9, 0x09, 0x7f, 0x9c, 0x8e, + 0x4e, 0x9e, 0xe3, 0x38, 0x4e, 0x42, 0xb7, 0x20, 0x69, 0x49, 0x73, 0xa9, 0x5f, 0x15, 0xd4, 0xdb, + 0x38, 0x69, 0x79, 0xfa, 0xdf, 0x26, 0x60, 0x61, 0xba, 0x67, 0xb9, 0x07, 0xdc, 0x81, 0x7c, 0x30, + 0xde, 0x0d, 0x26, 0x01, 0x23, 0xc3, 0xf0, 0x0d, 0x2f, 0x22, 0xa0, 0x16, 0xe4, 0x4d, 0x77, 0x40, + 0x7d, 0x87, 0xed, 0x0f, 0x55, 0x32, 0x39, 0xfb, 0xb6, 0x8f, 0xeb, 0xac, 0x54, 0x43, 0x11, 0x3c, + 0x95, 0x0e, 0xaf, 0x6e, 0xf9, 0xd0, 0x2b, 0xae, 0xee, 0x37, 0xa0, 0xe8, 0x9a, 0x43, 0x51, 0xe2, + 0x60, 0xce, 0x50, 0xce, 0x23, 0x8d, 0x0b, 0x8a, 0xd6, 0x77, 0x86, 0x44, 0xd7, 0x21, 0x1f, 0x29, + 0x43, 0x4b, 0x50, 0xa8, 0x36, 0x7b, 0xc6, 0xfd, 0xf5, 0x87, 0xc6, 0xa3, 0xfa, 0xb6, 0x36, 0xa7, + 0xc2, 0xcb, 0x3f, 0x4b, 0xc0, 0x82, 0x3a, 0x51, 0x54, 0xc8, 0xfe, 0x26, 0xcc, 0xfb, 0xe6, 0x1e, + 0x0b, 0x93, 0x8a, 0xb4, 0xf4, 0x6a, 0x7e, 0x48, 0xf3, 0xa4, 0x82, 0xb3, 0x66, 0x27, 0x15, 0xb1, + 0x57, 0xe5, 0xd4, 0x95, 0xaf, 0xca, 0xe9, 0xff, 0x93, 0x57, 0x65, 0xfd, 0x4f, 0x92, 0xb0, 0xa4, + 0xa2, 0xbf, 0xe8, 0x00, 0x7b, 0x17, 0xf2, 0x32, 0x10, 0x9c, 0xa6, 0x44, 0xe2, 0x21, 0x53, 0xe2, + 0x5a, 0x0d, 0x9c, 0x93, 0xec, 0x96, 0xcd, 0x73, 0x74, 0x05, 0x8d, 0xfd, 0x00, 0x04, 0x24, 0xa9, + 0xcd, 0x13, 0xcc, 0x06, 0xa4, 0xf7, 0x1c, 0x97, 0x28, 0x3f, 0x9b, 0x59, 0xbe, 0xbe, 0xd0, 0xbd, + 0x78, 0x68, 0xe9, 0x8b, 0x2c, 0x7f, 0x73, 0x0e, 0x0b, 0xe9, 0xd2, 0xaf, 0x03, 0x4c, 0xa9, 0x33, + 0x13, 0x59, 0x1e, 0x2c, 0xaa, 0x9a, 0x60, 0x18, 0x2c, 0xb6, 0x1a, 0x98, 0xd3, 0x38, 0x6b, 0xe0, + 0xd8, 0xea, 0xc8, 0x10, 0xac, 0x47, 0x9c, 0x35, 0x70, 0xec, 0xe8, 0xb5, 0x27, 0x7d, 0xcd, 0x6b, + 0x4f, 0x2d, 0x17, 0x56, 0xa6, 0xf4, 0x2d, 0xb8, 0x55, 0x73, 0x4d, 0xeb, 0xc0, 0x75, 0x02, 0x46, + 0xec, 0xf8, 0x0e, 0x5d, 0x87, 0xec, 0xb9, 0x38, 0xed, 0xaa, 0x42, 0xa0, 0x42, 0xea, 0xff, 0x9a, + 0x80, 0xe2, 0x26, 0x31, 0x5d, 0xb6, 0x3f, 0xad, 0xa6, 0x30, 0x12, 0x30, 0x75, 0xc0, 0x8b, 0x6f, + 0xf4, 0x21, 0xe4, 0xa2, 0x6b, 0xfc, 0xda, 0x17, 0x99, 0x08, 0x8a, 0x1e, 0xc0, 0x3c, 0xf7, 0x69, + 0x3a, 0x0e, 0xf3, 0x83, 0xab, 0x8a, 0xfd, 0x0a, 0xc9, 0x0f, 0x75, 0x9f, 0x88, 0x7b, 0x5b, 0x18, + 0x25, 0x83, 0xc3, 0x26, 0xfa, 0x7f, 0x50, 0x14, 0xb5, 0xea, 0x30, 0x4c, 0xc9, 0x5c, 0xa7, 0xb3, + 0x20, 0x9f, 0x9b, 0x64, 0x88, 0xf2, 0xdf, 0x09, 0x58, 0xde, 0x36, 0x27, 0xbb, 0x44, 0x6d, 0x53, + 0x62, 0x63, 0x62, 0x51, 0xdf, 0x46, 0xdd, 0xf8, 0xf6, 0xbe, 0xe2, 0xf5, 0x6a, 0x96, 0xf0, 0xec, + 0x5d, 0x1e, 0xe6, 0x2c, 0xc9, 0x58, 0xce, 0xb2, 0x0c, 0x19, 0x8f, 0x7a, 0x16, 0x51, 0x7b, 0x5f, + 0x36, 0x74, 0x27, 0xbe, 0xb5, 0x4b, 0xd1, 0xc3, 0x92, 0x78, 0x16, 0x6a, 0x53, 0x16, 0xf5, 0x86, + 0xbe, 0x80, 0x52, 0xaf, 0x59, 0xc7, 0xcd, 0x7e, 0xad, 0xf3, 0x53, 0xa3, 0x57, 0xdd, 0xea, 0x55, + 0xd7, 0xef, 0x19, 0xdd, 0xce, 0xd6, 0x97, 0xf7, 0x1f, 0xdc, 0xfb, 0x50, 0x4b, 0x94, 0xca, 0xc7, + 0x27, 0xe5, 0x3b, 0xed, 0x6a, 0x7d, 0x4b, 0xfa, 0xf2, 0x2e, 0x7d, 0xd6, 0x33, 0xdd, 0xc0, 0x5c, + 0xbf, 0xd7, 0xa5, 0xee, 0x84, 0x63, 0xf4, 0x93, 0x04, 0x14, 0xe3, 0xf7, 0x43, 0xfc, 0xda, 0x4b, + 0x5c, 0x7a, 0xed, 0x4d, 0x6f, 0xcf, 0xe4, 0x25, 0xb7, 0xe7, 0x06, 0x2c, 0x5b, 0x3e, 0x0d, 0x02, + 0x83, 0x07, 0xcc, 0xc4, 0xbe, 0x10, 0x92, 0xbf, 0x72, 0x76, 0xba, 0x76, 0xa3, 0xce, 0xf9, 0x3d, + 0xc1, 0x56, 0xea, 0x6f, 0x58, 0x31, 0x92, 0xe8, 0xe9, 0xbd, 0x5f, 0xa4, 0x20, 0x1f, 0x95, 0x9b, + 0xf9, 0x96, 0xe1, 0xb9, 0xbe, 0x32, 0x45, 0x44, 0x6f, 0x93, 0x23, 0xf4, 0xc6, 0x34, 0xcb, 0xff, + 0x42, 0xbe, 0xaf, 0x45, 0xec, 0x30, 0xc3, 0x7f, 0x0b, 0x72, 0xd5, 0x5e, 0xaf, 0xf5, 0xa8, 0xdd, + 0x6c, 0x68, 0x5f, 0x27, 0x4a, 0xaf, 0x1c, 0x9f, 0x94, 0x6f, 0x44, 0xa0, 0x6a, 0x20, 0x47, 0x2a, + 0x50, 0xf5, 0x7a, 0xb3, 0xdb, 0x6f, 0x36, 0xb4, 0xe7, 0xc9, 0x8b, 0x28, 0x91, 0xb5, 0x8a, 0x57, + 0xf2, 0x7c, 0x17, 0x37, 0xbb, 0x55, 0xcc, 0x3b, 0xfc, 0x3a, 0x29, 0x8b, 0x0f, 0xd3, 0x1e, 0x7d, + 0x32, 0x32, 0x7d, 0xde, 0xe7, 0x6a, 0xf8, 0x6b, 0x91, 0xe7, 0x29, 0xf9, 0x92, 0x3a, 0xad, 0x9d, + 0x13, 0xd3, 0x9e, 0xf0, 0xde, 0xc4, 0xa3, 0x85, 0x50, 0x93, 0xba, 0xd0, 0x5b, 0x8f, 0x3b, 0x2a, + 0xd7, 0xa2, 0xc3, 0x3c, 0xde, 0x69, 0xb7, 0x39, 0xe8, 0x79, 0xfa, 0xc2, 0xec, 0xf0, 0xd8, 0xe3, + 0x19, 0x09, 0xba, 0x0b, 0xb9, 0xf0, 0x4d, 0x43, 0xfb, 0x3a, 0x7d, 0x61, 0x40, 0xf5, 0xf0, 0x41, + 0x46, 0x74, 0xb8, 0xb9, 0xd3, 0x17, 0x3f, 0x66, 0x79, 0x9e, 0xb9, 0xd8, 0xe1, 0xfe, 0x98, 0xd9, + 0xf4, 0xc8, 0xe3, 0x0b, 0xac, 0xea, 0x1c, 0x5f, 0x67, 0x64, 0x52, 0x18, 0x61, 0x54, 0x91, 0xe3, + 0x2d, 0xc8, 0xe1, 0xe6, 0x4f, 0xe4, 0xef, 0x5e, 0x9e, 0x67, 0x2f, 0xe8, 0xc1, 0xe4, 0x2b, 0x62, + 0xa9, 0xde, 0x3a, 0xb8, 0xbb, 0x59, 0x15, 0x26, 0xbf, 0x88, 0xea, 0xf8, 0xa3, 0x7d, 0xd3, 0x23, + 0xf6, 0xf4, 0x39, 0x39, 0x62, 0xbd, 0xf7, 0x4b, 0x90, 0x0b, 0xc3, 0x06, 0xb4, 0x0a, 0xd9, 0xa7, + 0x1d, 0xfc, 0xb8, 0x89, 0xb5, 0x39, 0x69, 0xc3, 0x90, 0xf3, 0x54, 0x06, 0x7c, 0x65, 0x98, 0xdf, + 0xae, 0xb6, 0xab, 0x8f, 0x9a, 0x38, 0x2c, 0x41, 0x86, 0x00, 0x75, 0xf7, 0x95, 0x34, 0xd5, 0x41, + 0xa4, 0xb3, 0xb6, 0xf2, 0xcd, 0xb7, 0xab, 0x73, 0x3f, 0xff, 0x76, 0x75, 0xee, 0xf9, 0xd9, 0x6a, + 0xe2, 0x9b, 0xb3, 0xd5, 0xc4, 0xcf, 0xce, 0x56, 0x13, 0xff, 0x72, 0xb6, 0x9a, 0xd8, 0xcd, 0x8a, + 0x13, 0xe3, 0xc1, 0xff, 0x04, 0x00, 0x00, 0xff, 0xff, 0x71, 0x7c, 0x7a, 0x32, 0x4b, 0x2b, 0x00, + 0x00, } diff --git a/api/types.proto b/api/types.proto index 8420ad4839..367a7096a6 100644 --- a/api/types.proto +++ b/api/types.proto @@ -86,6 +86,18 @@ message NodeDescription { // Information about the Docker Engine on the node. EngineDescription engine = 4; + + // Information on the node's TLS setup + NodeTLSInfo tls_info = 5 [(gogoproto.customname) = "TLSInfo"]; +} + +message NodeTLSInfo { + // Information about which root certs the node trusts + bytes trust_root = 1; + + // Information about the node's current TLS certificate + bytes cert_issuer_subject = 2; + bytes cert_issuer_public_key = 3; } message RaftMemberStatus { diff --git a/ca/config.go b/ca/config.go index 6fcc5cb362..90db943082 100644 --- a/ca/config.go +++ b/ca/config.go @@ -19,6 +19,7 @@ import ( "github.com/docker/swarmkit/connectionbroker" "github.com/docker/swarmkit/identity" "github.com/docker/swarmkit/log" + "github.com/docker/swarmkit/watch" "github.com/opencontainers/go-digest" "github.com/pkg/errors" "google.golang.org/grpc/credentials" @@ -78,6 +79,9 @@ type SecurityConfig struct { ServerTLSCreds *MutableTLSCreds ClientTLSCreds *MutableTLSCreds + + // An optional queue for anyone interested in subscribing to SecurityConfig updates + queue *watch.Queue } // CertificateUpdate represents a change in the underlying TLS configuration being returned by @@ -158,6 +162,13 @@ func (s *SecurityConfig) UpdateRootCA(rootCA *RootCA, externalCARootPool *x509.C return s.updateTLSCredentials(s.certificate, s.issuerInfo) } +// SetWatch allows you to set a watch on the security config, in order to be notified of any changes +func (s *SecurityConfig) SetWatch(q *watch.Queue) { + s.mu.Lock() + defer s.mu.Unlock() + s.queue = q +} + // IssuerInfo returns the issuer subject and issuer public key func (s *SecurityConfig) IssuerInfo() *IssuerInfo { s.mu.Lock() @@ -165,8 +176,7 @@ func (s *SecurityConfig) IssuerInfo() *IssuerInfo { return s.issuerInfo } -// updateTLSCredentials updates the client, server, and TLS credentials on a security config. This function expects -// something else to have taken out a lock on the SecurityConfig. +// This function expects something else to have taken out a lock on the SecurityConfig. func (s *SecurityConfig) updateTLSCredentials(certificate *tls.Certificate, issuerInfo *IssuerInfo) error { certs := []tls.Certificate{*certificate} clientConfig, err := NewClientTLSConfig(certs, s.rootCA.Pool, ManagerRole) @@ -197,6 +207,13 @@ func (s *SecurityConfig) updateTLSCredentials(certificate *tls.Certificate, issu s.certificate = certificate s.issuerInfo = issuerInfo + if s.queue != nil { + s.queue.Publish(&api.NodeTLSInfo{ + TrustRoot: s.rootCA.Certs, + CertIssuerPublicKey: s.issuerInfo.PublicKey, + CertIssuerSubject: s.issuerInfo.Subject, + }) + } return nil } @@ -445,6 +462,7 @@ func RenewTLSConfigNow(ctx context.Context, s *SecurityConfig, connBroker *conne log.WithError(err).Errorf("failed to renew the certificate") return err } + s.mu.Lock() defer s.mu.Unlock() return s.updateTLSCredentials(tlsKeyPair, issuerInfo) diff --git a/ca/config_test.go b/ca/config_test.go index 485a7181cb..ceb2387307 100644 --- a/ca/config_test.go +++ b/ca/config_test.go @@ -17,9 +17,11 @@ import ( cfconfig "github.com/cloudflare/cfssl/config" "github.com/cloudflare/cfssl/helpers" + "github.com/docker/swarmkit/api" "github.com/docker/swarmkit/ca" "github.com/docker/swarmkit/ca/testutils" "github.com/docker/swarmkit/manager/state/store" + "github.com/docker/swarmkit/watch" "github.com/pkg/errors" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -396,6 +398,57 @@ func TestSecurityConfigUpdateRootCA(t *testing.T) { } } +func TestSecurityConfigSetWatch(t *testing.T) { + tc := testutils.NewTestCA(t) + defer tc.Stop() + + secConfig, err := tc.NewNodeConfig(ca.ManagerRole) + require.NoError(t, err) + issuer := secConfig.IssuerInfo() + + w := watch.NewQueue() + defer w.Close() + secConfig.SetWatch(w) + + configWatch, configCancel := w.Watch() + defer configCancel() + + require.NoError(t, ca.RenewTLSConfigNow(context.Background(), secConfig, tc.ConnBroker)) + select { + case ev := <-configWatch: + nodeTLSInfo, ok := ev.(*api.NodeTLSInfo) + require.True(t, ok) + require.Equal(t, &api.NodeTLSInfo{ + TrustRoot: tc.RootCA.Certs, + CertIssuerPublicKey: issuer.PublicKey, + CertIssuerSubject: issuer.Subject, + }, nodeTLSInfo) + case <-time.After(time.Second): + require.FailNow(t, "on TLS certificate update, we should have gotten a security config update") + } + + require.NoError(t, secConfig.UpdateRootCA(&tc.RootCA, tc.RootCA.Pool)) + select { + case ev := <-configWatch: + nodeTLSInfo, ok := ev.(*api.NodeTLSInfo) + require.True(t, ok) + require.Equal(t, &api.NodeTLSInfo{ + TrustRoot: tc.RootCA.Certs, + CertIssuerPublicKey: issuer.PublicKey, + CertIssuerSubject: issuer.Subject, + }, nodeTLSInfo) + case <-time.After(time.Second): + require.FailNow(t, "on TLS certificate update, we should have gotten a security config update") + } + + configCancel() + w.Close() + + // ensure that we can still update tls certs and roots without error even though the watch is closed + require.NoError(t, secConfig.UpdateRootCA(&tc.RootCA, tc.RootCA.Pool)) + require.NoError(t, ca.RenewTLSConfigNow(context.Background(), secConfig, tc.ConnBroker)) +} + // enforce that no matter what order updating the root CA and updating TLS credential happens, we // end up with a security config that has updated certs, and an updated root pool func TestRenewTLSConfigUpdateRootCARace(t *testing.T) { diff --git a/ca/server.go b/ca/server.go index 63f0b7e38c..f3fa9cb6c8 100644 --- a/ca/server.go +++ b/ca/server.go @@ -60,6 +60,9 @@ type Server struct { lastSeenClusterRootCA *api.RootCA lastSeenExternalCAs []*api.ExternalCA secConfigMu sync.Mutex + + // before we update the security config with the new root CA, we need to be able to save the root certs + rootPaths CertPaths } // DefaultCAConfig returns the default CA Config, with a default expiration. @@ -70,13 +73,14 @@ func DefaultCAConfig() api.CAConfig { } // NewServer creates a CA API server. -func NewServer(store *store.MemoryStore, securityConfig *SecurityConfig) *Server { +func NewServer(store *store.MemoryStore, securityConfig *SecurityConfig, rootCAPaths CertPaths) *Server { return &Server{ store: store, securityConfig: securityConfig, pending: make(map[string]*api.Node), started: make(chan struct{}), reconciliationRetryInterval: defaultReconciliationRetryInterval, + rootPaths: rootCAPaths, } } @@ -582,6 +586,9 @@ func (s *Server) UpdateRootCA(ctx context.Context, cluster *api.Cluster) error { if err != nil { return errors.Wrap(err, "invalid Root CA object in cluster") } + if err := SaveRootCA(updatedRootCA, s.rootPaths); err != nil { + return errors.Wrap(err, "unable to save new root CA certificates") + } externalCARootPool := updatedRootCA.Pool if rCA.RootRotation != nil { diff --git a/ca/server_test.go b/ca/server_test.go index e3f2500c95..4bc50e6b7a 100644 --- a/ca/server_test.go +++ b/ca/server_test.go @@ -4,6 +4,8 @@ import ( "bytes" "crypto/x509" "fmt" + "io/ioutil" + "os" "testing" "time" @@ -542,4 +544,10 @@ func TestCAServerUpdateRootCA(t *testing.T) { require.Equal(t, ca.ErrNoExternalCAURLs, err) } } + + // If we can't save the root cert, we can't update the root CA even if it's completely valid + require.NoError(t, os.RemoveAll(tc.TempDir)) + require.NoError(t, ioutil.WriteFile(tc.TempDir, []byte("cant create directory if this is file"), 0700)) + tc.CAServer.UpdateRootCA(context.Background(), fakeClusterSpec(testutils.ECDSA256SHA256Cert, testutils.ECDSA256Key, nil, nil)) + require.Equal(t, tc.RootCA.Certs, tc.ServingSecurityConfig.RootCA().Certs) } diff --git a/ca/testutils/cautils.go b/ca/testutils/cautils.go index 36d1b24d39..fe80d49242 100644 --- a/ca/testutils/cautils.go +++ b/ca/testutils/cautils.go @@ -173,11 +173,9 @@ func NewTestCAFromRootCA(t *testing.T, tempBaseDir string, rootCA ca.RootCA, krw serverOpts := []grpc.ServerOption{grpc.Creds(managerConfig.ServerTLSCreds)} grpcServer := grpc.NewServer(serverOpts...) - managerToken := ca.GenerateJoinToken(&rootCA) - workerToken := ca.GenerateJoinToken(&rootCA) + clusterObj := createClusterObject(t, s, organization, &rootCA, externalCAs...) - createClusterObject(t, s, organization, workerToken, managerToken, externalCAs...) - caServer := ca.NewServer(s, managerConfig) + caServer := ca.NewServer(s, managerConfig, paths.RootCA) caServer.SetReconciliationRetryInterval(50 * time.Millisecond) api.RegisterCAServer(grpcServer, caServer) api.RegisterNodeCAServer(grpcServer, caServer) @@ -235,8 +233,8 @@ func NewTestCAFromRootCA(t *testing.T, tempBaseDir string, rootCA ca.RootCA, krw Server: grpcServer, ServingSecurityConfig: managerConfig, CAServer: caServer, - WorkerToken: workerToken, - ManagerToken: managerToken, + WorkerToken: clusterObj.RootCA.JoinTokens.Worker, + ManagerToken: clusterObj.RootCA.JoinTokens.Manager, ConnBroker: connectionbroker.New(remotes), KeyReadWriter: krw, watchCancel: clusterWatchCancel, @@ -335,27 +333,33 @@ func genSecurityConfig(s *store.MemoryStore, rootCA ca.RootCA, krw *ca.KeyReadWr }) } -func createClusterObject(t *testing.T, s *store.MemoryStore, clusterID, workerToken, managerToken string, externalCAs ...*api.ExternalCA) { - assert.NoError(t, s.Update(func(tx store.Tx) error { - store.CreateCluster(tx, &api.Cluster{ - ID: clusterID, - Spec: api.ClusterSpec{ - Annotations: api.Annotations{ - Name: store.DefaultClusterName, - }, - CAConfig: api.CAConfig{ - ExternalCAs: externalCAs, - }, +func createClusterObject(t *testing.T, s *store.MemoryStore, clusterID string, rootCA *ca.RootCA, externalCAs ...*api.ExternalCA) *api.Cluster { + cluster := &api.Cluster{ + ID: clusterID, + Spec: api.ClusterSpec{ + Annotations: api.Annotations{ + Name: store.DefaultClusterName, }, - RootCA: api.RootCA{ - JoinTokens: api.JoinTokens{ - Worker: workerToken, - Manager: managerToken, - }, + CAConfig: api.CAConfig{ + ExternalCAs: externalCAs, + }, + }, + RootCA: api.RootCA{ + CACert: rootCA.Certs, + JoinTokens: api.JoinTokens{ + Worker: ca.GenerateJoinToken(rootCA), + Manager: ca.GenerateJoinToken(rootCA), }, - }) + }, + } + if s, err := rootCA.Signer(); err == nil && !External { + cluster.RootCA.CAKey = s.Key + } + assert.NoError(t, s.Update(func(tx store.Tx) error { + store.CreateCluster(tx, cluster) return nil })) + return cluster } // CreateRootCertAndKey returns a generated certificate and key for a root CA diff --git a/codecov.yml b/codecov.yml index b03ce3d879..aa409fa9e0 100644 --- a/codecov.yml +++ b/codecov.yml @@ -8,3 +8,5 @@ coverage: enabled: yes target: 0 changes: false +ignore: + -**/testutils diff --git a/integration/exec.go b/integration/exec.go deleted file mode 100644 index 43d1e0de10..0000000000 --- a/integration/exec.go +++ /dev/null @@ -1,94 +0,0 @@ -package integration - -import ( - "sync" - - "github.com/docker/swarmkit/agent/exec" - "github.com/docker/swarmkit/api" - "golang.org/x/net/context" -) - -// TestExecutor is executor for integration tests -type TestExecutor struct { -} - -// Describe just returns empty NodeDescription. -func (e *TestExecutor) Describe(ctx context.Context) (*api.NodeDescription, error) { - return &api.NodeDescription{}, nil -} - -// Configure does nothing. -func (e *TestExecutor) Configure(ctx context.Context, node *api.Node) error { - return nil -} - -// SetNetworkBootstrapKeys does nothing. -func (e *TestExecutor) SetNetworkBootstrapKeys([]*api.EncryptionKey) error { - return nil -} - -// Controller returns TestController. -func (e *TestExecutor) Controller(t *api.Task) (exec.Controller, error) { - return &TestController{ - ch: make(chan struct{}), - }, nil -} - -// TestController is dummy channel based controller for tests. -type TestController struct { - ch chan struct{} - closeOnce sync.Once -} - -// Update does nothing. -func (t *TestController) Update(ctx context.Context, task *api.Task) error { - return nil -} - -// Prepare does nothing. -func (t *TestController) Prepare(ctx context.Context) error { - return nil -} - -// Start does nothing. -func (t *TestController) Start(ctx context.Context) error { - return nil -} - -// Wait waits on internal channel. -func (t *TestController) Wait(ctx context.Context) error { - select { - case <-t.ch: - case <-ctx.Done(): - } - return nil -} - -// Shutdown closes internal channel -func (t *TestController) Shutdown(ctx context.Context) error { - t.closeOnce.Do(func() { - close(t.ch) - }) - return nil -} - -// Terminate closes internal channel if it wasn't closed before. -func (t *TestController) Terminate(ctx context.Context) error { - t.closeOnce.Do(func() { - close(t.ch) - }) - return nil -} - -// Remove does nothing. -func (t *TestController) Remove(ctx context.Context) error { - return nil -} - -// Close does nothing. -func (t *TestController) Close() error { - t.closeOnce.Do(func() { - close(t.ch) - }) - return nil -} diff --git a/integration/node.go b/integration/node.go index 64dad8409a..d751bf01f9 100644 --- a/integration/node.go +++ b/integration/node.go @@ -9,6 +9,7 @@ import ( "google.golang.org/grpc" + agentutils "github.com/docker/swarmkit/agent/testutils" "github.com/docker/swarmkit/api" "github.com/docker/swarmkit/ca" "github.com/docker/swarmkit/identity" @@ -40,7 +41,7 @@ func newTestNode(joinAddr, joinToken string, lateBind bool, rootCA *ca.RootCA) ( ListenControlAPI: cAddr, JoinAddr: joinAddr, StateDir: tmpDir, - Executor: &TestExecutor{}, + Executor: &agentutils.TestExecutor{}, JoinToken: joinToken, } if !lateBind { diff --git a/manager/controlapi/ca_rotation_test.go b/manager/controlapi/ca_rotation_test.go index 562f7bb844..911451b93e 100644 --- a/manager/controlapi/ca_rotation_test.go +++ b/manager/controlapi/ca_rotation_test.go @@ -66,7 +66,7 @@ func getSecurityConfig(t *testing.T, localRootCA *ca.RootCA, cluster *api.Cluste secConfig, err := localRootCA.CreateSecurityConfig(context.Background(), ca.NewKeyReadWriter(paths.Node, nil, nil), ca.CertificateRequestConfig{}) require.NoError(t, err) - require.NoError(t, ca.NewServer(store.NewMemoryStore(nil), secConfig).UpdateRootCA(context.Background(), cluster)) + require.NoError(t, ca.NewServer(store.NewMemoryStore(nil), secConfig, paths.RootCA).UpdateRootCA(context.Background(), cluster)) return secConfig } diff --git a/manager/controlapi/server_test.go b/manager/controlapi/server_test.go index 16844fb240..934bd6cb29 100644 --- a/manager/controlapi/server_test.go +++ b/manager/controlapi/server_test.go @@ -48,7 +48,7 @@ func newTestServer(t *testing.T) *testServer { ts.Store = store.NewMemoryStore(&stateutils.MockProposer{}) assert.NotNil(t, ts.Store) - ts.Server = NewServer(ts.Store, nil, securityConfig, ca.NewServer(ts.Store, securityConfig), nil) + ts.Server = NewServer(ts.Store, nil, securityConfig, ca.NewServer(ts.Store, securityConfig, tc.Paths.RootCA), nil) assert.NotNil(t, ts.Server) temp, err := ioutil.TempFile("", "test-socket") diff --git a/manager/dirty_test.go b/manager/dirty_test.go index 52814946f5..259cedcb2f 100644 --- a/manager/dirty_test.go +++ b/manager/dirty_test.go @@ -45,6 +45,7 @@ func TestIsStateDirty(t *testing.T) { SecurityConfig: managerSecurityConfig, AutoLockManagers: true, UnlockKey: []byte("kek"), + RootCAPaths: tc.Paths.RootCA, }) assert.NoError(t, err) assert.NotNil(t, m) diff --git a/manager/dispatcher/dispatcher.go b/manager/dispatcher/dispatcher.go index eeec4657b4..c3b4785db7 100644 --- a/manager/dispatcher/dispatcher.go +++ b/manager/dispatcher/dispatcher.go @@ -102,20 +102,29 @@ type nodeUpdate struct { description *api.NodeDescription } +// clusterUpdate is an object that stores an update to the cluster that should trigger +// a new session message. These are pointers to indicate the difference between +// "there is no update" and "update this to nil" +type clusterUpdate struct { + managerUpdate *[]*api.WeightedPeer + bootstrapKeyUpdate *[]*api.EncryptionKey + rootCAUpdate *[]byte +} + // Dispatcher is responsible for dispatching tasks and tracking agent health. type Dispatcher struct { mu sync.Mutex wg sync.WaitGroup nodes *nodeStore store *store.MemoryStore - mgrQueue *watch.Queue lastSeenManagers []*api.WeightedPeer networkBootstrapKeys []*api.EncryptionKey - keyMgrQueue *watch.Queue + lastSeenRootCert []byte config *Config cluster Cluster ctx context.Context cancel context.CancelFunc + clusterUpdateQueue *watch.Queue taskUpdates map[string]*api.TaskStatus // indexed by task ID taskUpdatesLock sync.Mutex @@ -196,6 +205,7 @@ func (d *Dispatcher) Run(ctx context.Context) error { if clusters[0].NetworkBootstrapKeys != nil { d.networkBootstrapKeys = clusters[0].NetworkBootstrapKeys } + d.lastSeenRootCert = clusters[0].RootCA.CACert } return nil }, @@ -205,9 +215,8 @@ func (d *Dispatcher) Run(ctx context.Context) error { d.mu.Unlock() return err } - // set queues here to guarantee that Close will close them - d.mgrQueue = watch.NewQueue() - d.keyMgrQueue = watch.NewQueue() + // set queue here to guarantee that Close will close it + d.clusterUpdateQueue = watch.NewQueue() peerWatcher, peerCancel := d.cluster.SubscribePeers() defer peerCancel() @@ -231,7 +240,7 @@ func (d *Dispatcher) Run(ctx context.Context) error { d.mu.Lock() d.lastSeenManagers = mgrs d.mu.Unlock() - d.mgrQueue.Publish(mgrs) + d.clusterUpdateQueue.Publish(clusterUpdate{managerUpdate: &mgrs}) } batchTimer := time.NewTimer(maxBatchInterval) @@ -259,9 +268,13 @@ func (d *Dispatcher) Run(ctx context.Context) error { d.nodes.updatePeriod(d.config.HeartbeatPeriod, d.config.HeartbeatEpsilon, d.config.GracePeriodMultiplier) } } + d.lastSeenRootCert = cluster.Cluster.RootCA.CACert d.networkBootstrapKeys = cluster.Cluster.NetworkBootstrapKeys d.mu.Unlock() - d.keyMgrQueue.Publish(cluster.Cluster.NetworkBootstrapKeys) + d.clusterUpdateQueue.Publish(clusterUpdate{ + bootstrapKeyUpdate: &cluster.Cluster.NetworkBootstrapKeys, + rootCAUpdate: &cluster.Cluster.RootCA.CACert, + }) case <-ctx.Done(): return nil } @@ -286,8 +299,7 @@ func (d *Dispatcher) Stop() error { d.processUpdatesCond.Broadcast() d.processUpdatesLock.Unlock() - d.mgrQueue.Close() - d.keyMgrQueue.Close() + d.clusterUpdateQueue.Close() d.wg.Wait() @@ -1284,6 +1296,12 @@ func (d *Dispatcher) getNetworkBootstrapKeys() []*api.EncryptionKey { return d.networkBootstrapKeys } +func (d *Dispatcher) getRootCACert() []byte { + d.mu.Lock() + defer d.mu.Unlock() + return d.lastSeenRootCert +} + // Session is a stream which controls agent connection. // Each message contains list of backup Managers with weights. Also there is // a special boolean field Disconnect which if true indicates that node should @@ -1355,14 +1373,13 @@ func (d *Dispatcher) Session(r *api.SessionRequest, stream api.Dispatcher_Sessio Node: nodeObj, Managers: d.getManagers(), NetworkBootstrapKeys: d.getNetworkBootstrapKeys(), + RootCA: d.getRootCACert(), }); err != nil { return err } - managerUpdates, mgrCancel := d.mgrQueue.Watch() - defer mgrCancel() - keyMgrUpdates, keyMgrCancel := d.keyMgrQueue.Watch() - defer keyMgrCancel() + clusterUpdatesCh, clusterCancel := d.clusterUpdateQueue.Watch() + defer clusterCancel() // disconnectNode is a helper forcibly shutdown connection disconnectNode := func() error { @@ -1396,11 +1413,21 @@ func (d *Dispatcher) Session(r *api.SessionRequest, stream api.Dispatcher_Sessio disconnect bool mgrs []*api.WeightedPeer netKeys []*api.EncryptionKey + rootCert []byte ) select { - case ev := <-managerUpdates: - mgrs = ev.([]*api.WeightedPeer) + case ev := <-clusterUpdatesCh: + update := ev.(clusterUpdate) + if update.managerUpdate != nil { + mgrs = *update.managerUpdate + } + if update.bootstrapKeyUpdate != nil { + netKeys = *update.bootstrapKeyUpdate + } + if update.rootCAUpdate != nil { + rootCert = *update.rootCAUpdate + } case ev := <-nodeUpdates: nodeObj = ev.(api.EventUpdateNode).Node case <-stream.Context().Done(): @@ -1409,8 +1436,6 @@ func (d *Dispatcher) Session(r *api.SessionRequest, stream api.Dispatcher_Sessio disconnect = true case <-dctx.Done(): disconnect = true - case ev := <-keyMgrUpdates: - netKeys = ev.([]*api.EncryptionKey) } if mgrs == nil { mgrs = d.getManagers() @@ -1418,12 +1443,16 @@ func (d *Dispatcher) Session(r *api.SessionRequest, stream api.Dispatcher_Sessio if netKeys == nil { netKeys = d.getNetworkBootstrapKeys() } + if rootCert == nil { + rootCert = d.getRootCACert() + } if err := stream.Send(&api.SessionMessage{ SessionID: sessionID, Node: nodeObj, Managers: mgrs, NetworkBootstrapKeys: netKeys, + RootCA: rootCert, }); err != nil { return err } diff --git a/manager/dispatcher/dispatcher_test.go b/manager/dispatcher/dispatcher_test.go index 588cedbce7..c4738e05ac 100644 --- a/manager/dispatcher/dispatcher_test.go +++ b/manager/dispatcher/dispatcher_test.go @@ -2,8 +2,10 @@ package dispatcher import ( "crypto/tls" + "errors" "fmt" "net" + "sync" "testing" "time" @@ -17,9 +19,12 @@ import ( "github.com/docker/swarmkit/api" "github.com/docker/swarmkit/ca" "github.com/docker/swarmkit/ca/testutils" + "github.com/docker/swarmkit/identity" raftutils "github.com/docker/swarmkit/manager/state/raft/testutils" "github.com/docker/swarmkit/manager/state/store" + digest "github.com/opencontainers/go-digest" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) type grpcDispatcher struct { @@ -30,6 +35,7 @@ type grpcDispatcher struct { dispatcherServer *Dispatcher conns []*grpc.ClientConn testCA *testutils.TestCA + testCluster *testCluster } func (gd *grpcDispatcher) Close() { @@ -43,32 +49,73 @@ func (gd *grpcDispatcher) Close() { } type testCluster struct { - addr string - store *store.MemoryStore + mu sync.Mutex + addr string + store *store.MemoryStore + subscriptions map[string]chan events.Event + peers []*api.Peer + members map[uint64]*api.RaftMember } -func (t *testCluster) GetMemberlist() map[uint64]*api.RaftMember { - return map[uint64]*api.RaftMember{ - 1: { - NodeID: "1", - Addr: t.addr, +func newTestCluster(addr string, s *store.MemoryStore) *testCluster { + return &testCluster{ + addr: addr, + store: s, + subscriptions: make(map[string]chan events.Event), + peers: []*api.Peer{ + { + Addr: addr, + NodeID: "1", + }, + }, + members: map[uint64]*api.RaftMember{ + 1: { + NodeID: "1", + Addr: addr, + }, }, } } +func (t *testCluster) GetMemberlist() map[uint64]*api.RaftMember { + t.mu.Lock() + defer t.mu.Unlock() + return t.members +} + func (t *testCluster) SubscribePeers() (chan events.Event, func()) { + t.mu.Lock() + defer t.mu.Unlock() ch := make(chan events.Event, 1) - ch <- []*api.Peer{ - { - Addr: t.addr, - NodeID: "1", - }, - } + id := identity.NewID() + t.subscriptions[id] = ch + ch <- t.peers return ch, func() { + t.mu.Lock() + defer t.mu.Unlock() + delete(t.subscriptions, id) close(ch) } } +func (t *testCluster) addMember(addr string) { + t.mu.Lock() + defer t.mu.Unlock() + id := uint64(len(t.members) + 1) + strID := fmt.Sprintf("%d", id) + t.members[id] = &api.RaftMember{ + NodeID: strID, + Addr: addr, + } + t.peers = append(t.peers, &api.Peer{ + Addr: addr, + NodeID: strID, + }) + for _, ch := range t.subscriptions { + ch <- t.peers + } +} + func (t *testCluster) MemoryStore() *store.MemoryStore { return t.store } @@ -96,7 +143,7 @@ func startDispatcher(c *Config) (*grpcDispatcher, error) { serverOpts := []grpc.ServerOption{grpc.Creds(managerSecurityConfig.ServerTLSCreds)} s := grpc.NewServer(serverOpts...) - tc := &testCluster{addr: l.Addr().String(), store: tca.MemoryStore} + tc := newTestCluster(l.Addr().String(), tca.MemoryStore) d := New(tc, c) authorize := func(ctx context.Context, roles []string) error { @@ -154,6 +201,7 @@ func startDispatcher(c *Config) (*grpcDispatcher, error) { conns: conns, grpcServer: s, testCA: tca, + testCluster: tc, }, nil } @@ -1339,3 +1387,77 @@ func TestOldTasksNoCert(t *testing.T) { assert.Nil(t, resp) assert.EqualError(t, err, "rpc error: code = 7 desc = Permission denied: unauthorized peer role: rpc error: code = 7 desc = no client certificates in request") } + +func TestClusterUpdatesSendMessages(t *testing.T) { + cfg := DefaultConfig() + cfg.RateLimitPeriod = 0 + gd, err := startDispatcher(cfg) + require.NoError(t, err) + defer gd.Close() + + stream, err := gd.Clients[0].Session(context.Background(), &api.SessionRequest{}) + require.NoError(t, err) + defer stream.CloseSend() + + var msg *api.SessionMessage + { + msg, err = stream.Recv() + require.NoError(t, err) + require.NotEmpty(t, msg.SessionID) + require.NotNil(t, msg.Node) + require.Len(t, msg.Managers, 1) + require.Empty(t, msg.NetworkBootstrapKeys) + require.Equal(t, gd.testCA.RootCA.Certs, msg.RootCA) + } + + // changing the network bootstrap keys results in a new message with updated keys + expected := msg.Copy() + expected.NetworkBootstrapKeys = []*api.EncryptionKey{ + {Key: []byte("network key1")}, + {Key: []byte("network key2")}, + } + require.NoError(t, gd.Store.Update(func(tx store.Tx) error { + cluster := store.GetCluster(tx, gd.testCA.Organization) + if cluster == nil { + return errors.New("no cluster") + } + cluster.NetworkBootstrapKeys = expected.NetworkBootstrapKeys + return store.UpdateCluster(tx, cluster) + })) + time.Sleep(100 * time.Millisecond) + { + msg, err = stream.Recv() + require.NoError(t, err) + require.Equal(t, expected, msg) + } + + // changing the peers results in a new message with updated managers + gd.testCluster.addMember("1.1.1.1") + time.Sleep(100 * time.Millisecond) + { + msg, err = stream.Recv() + require.NoError(t, err) + require.Len(t, msg.Managers, 2) + expected.Managers = msg.Managers + require.Equal(t, expected, msg) + } + + // changing the rootCA cert and has in the cluster results in a new message with an updated cert + expected = msg.Copy() + expected.RootCA = testutils.ECDSA256SHA256Cert + require.NoError(t, gd.Store.Update(func(tx store.Tx) error { + cluster := store.GetCluster(tx, gd.testCA.Organization) + if cluster == nil { + return errors.New("no cluster") + } + cluster.RootCA.CACert = testutils.ECDSA256SHA256Cert + cluster.RootCA.CACertHash = digest.FromBytes(testutils.ECDSA256SHA256Cert).String() + return store.UpdateCluster(tx, cluster) + })) + time.Sleep(100 * time.Millisecond) + { + msg, err = stream.Recv() + require.NoError(t, err) + require.Equal(t, expected, msg) + } +} diff --git a/manager/manager.go b/manager/manager.go index cb3cb43fdb..7b436b16f2 100644 --- a/manager/manager.go +++ b/manager/manager.go @@ -65,6 +65,9 @@ type RemoteAddrs struct { type Config struct { SecurityConfig *ca.SecurityConfig + // RootCAPaths is the path to which new root certs should be save + RootCAPaths ca.CertPaths + // ExternalCAs is a list of initial CAs to which a manager node // will make certificate signing requests for node certificates. ExternalCAs []*api.ExternalCA @@ -210,7 +213,7 @@ func New(config *Config) (*Manager, error) { m := &Manager{ config: *config, - caserver: ca.NewServer(raftNode.MemoryStore(), config.SecurityConfig), + caserver: ca.NewServer(raftNode.MemoryStore(), config.SecurityConfig, config.RootCAPaths), dispatcher: dispatcher.New(raftNode, dispatcher.DefaultConfig()), logbroker: logbroker.New(raftNode.MemoryStore()), server: grpc.NewServer(opts...), diff --git a/manager/manager_test.go b/manager/manager_test.go index 48e6b78fd7..8569de3101 100644 --- a/manager/manager_test.go +++ b/manager/manager_test.go @@ -62,6 +62,7 @@ func TestManager(t *testing.T) { SecurityConfig: managerSecurityConfig, AutoLockManagers: true, UnlockKey: []byte("kek"), + RootCAPaths: tc.Paths.RootCA, }) assert.NoError(t, err) assert.NotNil(t, m) @@ -222,6 +223,7 @@ func TestManagerLockUnlock(t *testing.T) { ControlAPI: temp.Name(), StateDir: stateDir, SecurityConfig: managerSecurityConfig, + RootCAPaths: tc.Paths.RootCA, // start off without any encryption }) require.NoError(t, err) @@ -431,6 +433,7 @@ func TestManagerUpdatesSecurityConfig(t *testing.T) { ControlAPI: temp.Name(), StateDir: stateDir, SecurityConfig: managerSecurityConfig, + RootCAPaths: tc.Paths.RootCA, }) require.NoError(t, err) require.NotNil(t, m) @@ -539,6 +542,7 @@ func TestManagerEncryptsDecryptsRootKeyMaterial(t *testing.T) { ControlAPI: temp.Name(), StateDir: stateDir, SecurityConfig: managerSecurityConfig, + RootCAPaths: tc.Paths.RootCA, } done := make(chan error) defer close(done) diff --git a/node/node.go b/node/node.go index 0551d5b93f..efa75504e9 100644 --- a/node/node.go +++ b/node/node.go @@ -1,6 +1,7 @@ package node import ( + "bytes" "crypto/tls" "encoding/json" "io/ioutil" @@ -25,6 +26,7 @@ import ( "github.com/docker/swarmkit/manager" "github.com/docker/swarmkit/manager/encryption" "github.com/docker/swarmkit/remotes" + "github.com/docker/swarmkit/watch" "github.com/docker/swarmkit/xnet" grpc_prometheus "github.com/grpc-ecosystem/go-grpc-prometheus" "github.com/pkg/errors" @@ -129,7 +131,7 @@ type Node struct { err error agent *agent.Agent manager *manager.Manager - notifyNodeChange chan *api.Node // used to send role updates from the dispatcher api on promotion/demotion + notifyNodeChange chan *agent.NodeChanges // used by the agent to relay node updates from the dispatcher Session stream to (*Node).run unlockKey []byte } @@ -172,7 +174,7 @@ func New(c *Config) (*Node, error) { stopped: make(chan struct{}), closed: make(chan struct{}), ready: make(chan struct{}), - notifyNodeChange: make(chan *api.Node, 1), + notifyNodeChange: make(chan *agent.NodeChanges, 1), unlockKey: c.UnlockKey, } @@ -235,7 +237,8 @@ func (n *Node) run(ctx context.Context) (err error) { } }(ctx) - securityConfig, err := n.loadSecurityConfig(ctx) + paths := ca.NewConfigPaths(filepath.Join(n.config.StateDir, certDirectory)) + securityConfig, err := n.loadSecurityConfig(ctx, paths) if err != nil { return err } @@ -274,24 +277,44 @@ func (n *Node) run(ctx context.Context) (err error) { select { case <-agentDone: return - case node := <-n.notifyNodeChange: - // If the server is sending us a ForceRenewal State, renew - if node.Certificate.Status.State == api.IssuanceStateRotate { - renewCert() - continue - } + case nodeChanges := <-n.notifyNodeChange: n.Lock() - // If we got a role change, renew - role := ca.WorkerRole - if node.Role == api.NodeRoleManager { - role = ca.ManagerRole + currentRole := n.role + n.Unlock() + + if nodeChanges.Node != nil { + role := ca.WorkerRole + if nodeChanges.Node.Role == api.NodeRoleManager { + role = ca.ManagerRole + } + + // If the server is sending us a ForceRenewal State, or if the new node role doesn't match our current role, renew + if currentRole != role || nodeChanges.Node.Certificate.Status.State == api.IssuanceStateRotate { + renewCert() + } } - if n.role == role { - n.Unlock() - continue + + if nodeChanges.RootCert != nil { + // We only want to update the root CA if this is a worker node. Manager nodes directly watch the raft + // store and update the root CA, with the necessary signer, from the raft store (since the managers + // need the CA key as well to potentially issue new TLS certificates). + if currentRole == ca.ManagerRole || bytes.Equal(nodeChanges.RootCert, securityConfig.RootCA().Certs) { + continue + } + newRootCA, err := ca.NewRootCA(nodeChanges.RootCert, nil, nil, ca.DefaultNodeCertExpiration, nil) + if err != nil { + log.G(ctx).WithError(err).Error("invalid new root certificate from the dispatcher") + continue + } + if err := ca.SaveRootCA(newRootCA, paths.RootCA); err != nil { + log.G(ctx).WithError(err).Error("could not save new root certificate from the dispatcher") + continue + } + if err := securityConfig.UpdateRootCA(&newRootCA, newRootCA.Pool); err != nil { + log.G(ctx).WithError(err).Error("could not use new root CA from dispatcher") + continue + } } - n.Unlock() - renewCert() } } }() @@ -322,12 +345,12 @@ func (n *Node) run(ctx context.Context) (err error) { var managerErr error var agentErr error go func() { - managerErr = n.superviseManager(ctx, securityConfig, managerReady, forceCertRenewal) // store err and loop + managerErr = n.superviseManager(ctx, securityConfig, paths.RootCA, managerReady, forceCertRenewal) // store err and loop wg.Done() cancel() }() go func() { - agentErr = n.runAgent(ctx, db, securityConfig.ClientTLSCreds, agentReady) + agentErr = n.runAgent(ctx, db, securityConfig, agentReady) wg.Done() cancel() close(agentDone) @@ -401,7 +424,7 @@ func (n *Node) Err(ctx context.Context) error { } } -func (n *Node) runAgent(ctx context.Context, db *bolt.DB, creds credentials.TransportCredentials, ready chan<- struct{}) error { +func (n *Node) runAgent(ctx context.Context, db *bolt.DB, securityConfig *ca.SecurityConfig, ready chan<- struct{}) error { waitCtx, waitCancel := context.WithCancel(ctx) remotesCh := n.remotes.WaitSelect(ctx) controlCh := n.ListenControlSocket(waitCtx) @@ -428,13 +451,28 @@ waitPeer: default: } + secChangeQueue := watch.NewQueue() + defer secChangeQueue.Close() + secChangesCh, secChangesCancel := secChangeQueue.Watch() + defer secChangesCancel() + securityConfig.SetWatch(secChangeQueue) + + rootCA := securityConfig.RootCA() + issuer := securityConfig.IssuerInfo() + a, err := agent.New(&agent.Config{ Hostname: n.config.Hostname, ConnBroker: n.connBroker, Executor: n.config.Executor, DB: db, NotifyNodeChange: n.notifyNodeChange, - Credentials: creds, + NotifyTLSChange: secChangesCh, + Credentials: securityConfig.ClientTLSCreds, + NodeTLSInfo: &api.NodeTLSInfo{ + TrustRoot: rootCA.Certs, + CertIssuerPublicKey: issuer.PublicKey, + CertIssuerSubject: issuer.Subject, + }, }) if err != nil { return err @@ -565,8 +603,7 @@ func (n *Node) Remotes() []api.Peer { return remotes } -func (n *Node) loadSecurityConfig(ctx context.Context) (*ca.SecurityConfig, error) { - paths := ca.NewConfigPaths(filepath.Join(n.config.StateDir, certDirectory)) +func (n *Node) loadSecurityConfig(ctx context.Context, paths *ca.SecurityConfigPaths) (*ca.SecurityConfig, error) { var securityConfig *ca.SecurityConfig krw := ca.NewKeyReadWriter(paths.Node, n.unlockKey, &manager.RaftDEKData{}) @@ -717,7 +754,7 @@ func (n *Node) waitRole(ctx context.Context, role string) error { return nil } -func (n *Node) runManager(ctx context.Context, securityConfig *ca.SecurityConfig, ready chan struct{}, workerRole <-chan struct{}) (bool, error) { +func (n *Node) runManager(ctx context.Context, securityConfig *ca.SecurityConfig, rootPaths ca.CertPaths, ready chan struct{}, workerRole <-chan struct{}) (bool, error) { var remoteAPI *manager.RemoteAddrs if n.config.ListenRemoteAPI != "" { remoteAPI = &manager.RemoteAddrs{ @@ -741,6 +778,7 @@ func (n *Node) runManager(ctx context.Context, securityConfig *ca.SecurityConfig UnlockKey: n.unlockKey, Availability: n.config.Availability, PluginGetter: n.config.PluginGetter, + RootCAPaths: rootPaths, }) if err != nil { return false, err @@ -789,7 +827,7 @@ func (n *Node) runManager(ctx context.Context, securityConfig *ca.SecurityConfig return clearData, nil } -func (n *Node) superviseManager(ctx context.Context, securityConfig *ca.SecurityConfig, ready chan struct{}, forceCertRenewal chan struct{}) error { +func (n *Node) superviseManager(ctx context.Context, securityConfig *ca.SecurityConfig, rootPaths ca.CertPaths, ready chan struct{}, forceCertRenewal chan struct{}) error { for { if err := n.waitRole(ctx, ca.ManagerRole); err != nil { return err @@ -803,7 +841,7 @@ func (n *Node) superviseManager(ctx context.Context, securityConfig *ca.Security } }() - wasRemoved, err := n.runManager(ctx, securityConfig, ready, workerRole) + wasRemoved, err := n.runManager(ctx, securityConfig, rootPaths, ready, workerRole) if err != nil { waitRoleCancel() return errors.Wrap(err, "manager stopped") diff --git a/node/node_test.go b/node/node_test.go index 125430e346..562ac0a475 100644 --- a/node/node_test.go +++ b/node/node_test.go @@ -1,17 +1,25 @@ package node import ( + "bytes" "crypto/x509" "encoding/pem" + "fmt" "io/ioutil" "os" "path/filepath" + "sync" "testing" + "time" + "github.com/cloudflare/cfssl/helpers" + "github.com/docker/swarmkit/agent" + agentutils "github.com/docker/swarmkit/agent/testutils" "github.com/docker/swarmkit/api" "github.com/docker/swarmkit/ca" cautils "github.com/docker/swarmkit/ca/testutils" "github.com/docker/swarmkit/identity" + "github.com/docker/swarmkit/manager/state/raft/testutils" "github.com/docker/swarmkit/manager/state/store" "github.com/pkg/errors" "github.com/stretchr/testify/require" @@ -33,7 +41,7 @@ func TestLoadSecurityConfigNewNode(t *testing.T) { AutoLockManagers: autoLockManagers, }) require.NoError(t, err) - securityConfig, err := node.loadSecurityConfig(context.Background()) + securityConfig, err := node.loadSecurityConfig(context.Background(), paths) require.NoError(t, err) require.NotNil(t, securityConfig) @@ -63,7 +71,7 @@ func TestLoadSecurityConfigPartialCertsOnDisk(t *testing.T) { StateDir: tempdir, }) require.NoError(t, err) - securityConfig, err := node.loadSecurityConfig(context.Background()) + securityConfig, err := node.loadSecurityConfig(context.Background(), paths) require.NoError(t, err) require.NotNil(t, securityConfig) @@ -81,7 +89,7 @@ func TestLoadSecurityConfigPartialCertsOnDisk(t *testing.T) { StateDir: tempdir, }) require.NoError(t, err) - securityConfig, err = node.loadSecurityConfig(context.Background()) + securityConfig, err = node.loadSecurityConfig(context.Background(), paths) require.NoError(t, err) require.NotNil(t, securityConfig) @@ -122,7 +130,7 @@ func TestLoadSecurityConfigLoadFromDisk(t *testing.T) { UnlockKey: []byte("passphrase"), }) require.NoError(t, err) - securityConfig, err := node.loadSecurityConfig(context.Background()) + securityConfig, err := node.loadSecurityConfig(context.Background(), paths) require.NoError(t, err) require.NotNil(t, securityConfig) @@ -133,7 +141,7 @@ func TestLoadSecurityConfigLoadFromDisk(t *testing.T) { JoinToken: tc.ManagerToken, }) require.NoError(t, err) - _, err = node.loadSecurityConfig(context.Background()) + _, err = node.loadSecurityConfig(context.Background(), paths) require.Equal(t, ErrInvalidUnlockKey, err) // Invalid CA @@ -147,7 +155,7 @@ func TestLoadSecurityConfigLoadFromDisk(t *testing.T) { UnlockKey: []byte("passphrase"), }) require.NoError(t, err) - _, err = node.loadSecurityConfig(context.Background()) + _, err = node.loadSecurityConfig(context.Background(), paths) require.IsType(t, x509.UnknownAuthorityError{}, errors.Cause(err)) } @@ -167,7 +175,7 @@ func TestLoadSecurityConfigDownloadAllCerts(t *testing.T) { JoinAddr: "127.0.0.1:12", }) require.NoError(t, err) - _, err = node.loadSecurityConfig(context.Background()) + _, err = node.loadSecurityConfig(context.Background(), paths) require.Error(t, err) tc := cautils.NewTestCA(t) @@ -182,7 +190,7 @@ func TestLoadSecurityConfigDownloadAllCerts(t *testing.T) { JoinToken: tc.ManagerToken, }) require.NoError(t, err) - _, err = node.loadSecurityConfig(context.Background()) + _, err = node.loadSecurityConfig(context.Background(), paths) require.NoError(t, err) // the TLS key and cert were written to disk unencrypted @@ -226,7 +234,7 @@ func TestLoadSecurityConfigDownloadAllCerts(t *testing.T) { JoinToken: tc.ManagerToken, }) require.NoError(t, err) - _, err = node.loadSecurityConfig(context.Background()) + _, err = node.loadSecurityConfig(context.Background(), paths) require.NoError(t, err) // make sure the CA cert has not been replaced @@ -240,3 +248,129 @@ func TestLoadSecurityConfigDownloadAllCerts(t *testing.T) { _, _, err = ca.NewKeyReadWriter(paths.Node, []byte("passphrase"), nil).Read() require.NoError(t, err) } + +func TestManagerIgnoresDispatcherRootCAUpdate(t *testing.T) { + tmpDir, err := ioutil.TempDir("", "manager-root-ca-update") + require.NoError(t, err) + + // don't bother with a listening socket + cAddr := filepath.Join(tmpDir, "control.sock") + cfg := &Config{ + ListenControlAPI: cAddr, + StateDir: tmpDir, + Executor: &agentutils.TestExecutor{}, + } + + node, err := New(cfg) + require.NoError(t, err) + + var nodeErr error + wg := &sync.WaitGroup{} + wg.Add(1) + go func() { + nodeErr = node.Start(context.Background()) + wg.Done() + }() + + select { + case <-node.Ready(): + case <-time.After(5 * time.Second): + require.FailNow(t, "node did not ready in time") + } + + certPath := filepath.Join(tmpDir, certDirectory, "swarm-root-ca.crt") + currentCACerts, err := ioutil.ReadFile(certPath) + require.NoError(t, err) + parsedCerts, err := helpers.ParseCertificatesPEM(currentCACerts) + require.NoError(t, err) + require.Len(t, parsedCerts, 1) + + // fake an update from the dispatcher, because the dispatcher is actually the local manager + node.notifyNodeChange <- &agent.NodeChanges{ + RootCert: append(currentCACerts, cautils.ECDSA256SHA256Cert...), + } + + // the node root CA certificates do not change + time.Sleep(250 * time.Millisecond) + caCerts, err := ioutil.ReadFile(certPath) + require.NoError(t, err) + require.Equal(t, currentCACerts, caCerts) + + require.NoError(t, node.Stop(context.Background())) + wg.Wait() + require.NoError(t, nodeErr) +} + +func TestAgentRespectsDispatcherRootCAUpdate(t *testing.T) { + tmpDir, err := ioutil.TempDir("", "manager-root-ca-update") + require.NoError(t, err) + + // bootstrap worker TLS certificates + paths := ca.NewConfigPaths(filepath.Join(tmpDir, certDirectory)) + rootCA, err := ca.CreateRootCA("rootCN") + require.NoError(t, err) + require.NoError(t, ca.SaveRootCA(rootCA, paths.RootCA)) + managerSecConfig, err := rootCA.CreateSecurityConfig(context.Background(), + ca.NewKeyReadWriter(paths.Node, nil, nil), ca.CertificateRequestConfig{}) + require.NoError(t, err) + + _, _, err = rootCA.IssueAndSaveNewCertificates(ca.NewKeyReadWriter(paths.Node, nil, nil), "workerNode", + ca.WorkerRole, managerSecConfig.ServerTLSCreds.Organization()) + require.NoError(t, err) + + mockDispatcher, cleanup := agentutils.NewMockDispatcher(t, managerSecConfig) + defer cleanup() + + cfg := &Config{ + StateDir: tmpDir, + Executor: &agentutils.TestExecutor{}, + JoinAddr: mockDispatcher.Addr, + } + node, err := New(cfg) + require.NoError(t, err) + + var nodeErr error + wg := &sync.WaitGroup{} + wg.Add(1) + go func() { + nodeErr = node.Start(context.Background()) + wg.Done() + }() + + select { + case <-node.Ready(): + case <-time.After(5 * time.Second): + require.FailNow(t, "node did not ready in time") + } + + currentCACerts, err := ioutil.ReadFile(paths.RootCA.Cert) + require.NoError(t, err) + parsedCerts, err := helpers.ParseCertificatesPEM(currentCACerts) + require.NoError(t, err) + require.Len(t, parsedCerts, 1) + + // fake an update from the dispatcher + node.notifyNodeChange <- &agent.NodeChanges{ + RootCert: append(currentCACerts, cautils.ECDSA256SHA256Cert...), + } + + require.NoError(t, testutils.PollFuncWithTimeout(nil, func() error { + caCerts, err := ioutil.ReadFile(paths.RootCA.Cert) + require.NoError(t, err) + if bytes.Equal(currentCACerts, caCerts) { + return errors.New("new certificates have not been replaced yet") + } + parsedCerts, err := helpers.ParseCertificatesPEM(caCerts) + if err != nil { + return err + } + if len(parsedCerts) != 2 { + return fmt.Errorf("expecting 2 new certificates, got %d", len(parsedCerts)) + } + return nil + }, time.Second)) + + require.NoError(t, node.Stop(context.Background())) + wg.Wait() + require.NoError(t, nodeErr) +}