diff --git a/agent/consul/session_endpoint.go b/agent/consul/session_endpoint.go index f2f8ab7740..218199c1e0 100644 --- a/agent/consul/session_endpoint.go +++ b/agent/consul/session_endpoint.go @@ -57,8 +57,8 @@ func (s *Session) Apply(args *structs.SessionRequest, reply *string) error { if args.Session.ID == "" && args.Op == structs.SessionDestroy { return fmt.Errorf("Must provide ID") } - if args.Session.Node == "" && args.Op == structs.SessionCreate { - return fmt.Errorf("Must provide Node") + if args.Session.Node == "" && len(args.Session.NodeChecks) > 0 && args.Op == structs.SessionCreate { + return fmt.Errorf("Must provide Node when NodeChecks are specified") } // The entMeta to populate is the one in the Session struct, not SessionRequest diff --git a/agent/consul/state/session.go b/agent/consul/state/session.go index d57b05947d..2518a715ab 100644 --- a/agent/consul/state/session.go +++ b/agent/consul/state/session.go @@ -47,7 +47,7 @@ func sessionsTableSchema() *memdb.TableSchema { }, indexNode: { Name: indexNode, - AllowMissing: false, + AllowMissing: true, Unique: false, Indexer: nodeSessionsIndexer(), }, @@ -251,13 +251,17 @@ func sessionCreateTxn(tx WriteTxn, idx uint64, sess *structs.Session) error { sess.CreateIndex = idx sess.ModifyIndex = idx - // Check that the node exists - node, err := tx.First(tableNodes, indexID, Query{Value: sess.Node, EnterpriseMeta: *structs.DefaultEnterpriseMetaInPartition(sess.PartitionOrDefault())}) - if err != nil { - return fmt.Errorf("failed node lookup: %s", err) - } - if node == nil { - return ErrMissingNode + // We only require a node to be specified if node checks were also passed + // in. This is necessary to allow for manually-renewed cross-datacenter + // sessions. + if sess.Node != "" && len(sess.NodeChecks) > 0 { + node, err := tx.First(tableNodes, indexID, Query{Value: sess.Node, EnterpriseMeta: *structs.DefaultEnterpriseMetaInPartition(sess.PartitionOrDefault())}) + if err != nil { + return fmt.Errorf("failed node lookup: %s", err) + } + if node == nil { + return ErrMissingNode + } } // Verify that all session checks exist diff --git a/agent/consul/state/session_test.go b/agent/consul/state/session_test.go index 08f7ad09d0..0716cab577 100644 --- a/agent/consul/state/session_test.go +++ b/agent/consul/state/session_test.go @@ -53,7 +53,11 @@ func TestStateStore_SessionCreate_SessionGet(t *testing.T) { } // Registering with an unknown node is disallowed - sess = &structs.Session{ID: testUUID()} + sess = &structs.Session{ + ID: testUUID(), + Node: "nonextant-node", + NodeChecks: []string{string(structs.SerfCheckID)}, + } if err := s.SessionCreate(1, sess); err != ErrMissingNode { t.Fatalf("expected %#v, got: %#v", ErrMissingNode, err) } diff --git a/agent/session_endpoint.go b/agent/session_endpoint.go index 90c3fa32ba..1c0b2c4e23 100644 --- a/agent/session_endpoint.go +++ b/agent/session_endpoint.go @@ -50,8 +50,19 @@ func (s *HTTPHandlers) SessionCreate(resp http.ResponseWriter, req *http.Request fixupEmptySessionChecks(&args.Session) - if (s.agent.config.Datacenter != args.Datacenter) && (!s.agent.config.ServerMode) { - return nil, fmt.Errorf("cross datacenter lock must be created at server agent") + if s.agent.config.Datacenter != args.Datacenter && !s.agent.config.ServerMode { + if len(args.Session.NodeChecks) > 0 { + return nil, fmt.Errorf("cross-datacenter sessions on client agents cannot be created with NodeChecks") + } + + if args.Session.TTL != "" { + ttl, _ := time.ParseDuration(args.Session.TTL) + if ttl == 0 { + return nil, fmt.Errorf("cross-datacenter sessions on client agents cannot be created without a TTL") + } + } + + args.Session.Node = "" } // Create the session, get the ID diff --git a/website/content/api-docs/session.mdx b/website/content/api-docs/session.mdx index 30dd005a1c..a98fbf0f44 100644 --- a/website/content/api-docs/session.mdx +++ b/website/content/api-docs/session.mdx @@ -10,7 +10,7 @@ The `/session` endpoints create, destroy, and query sessions in Consul. ## Create Session -This endpoint initializes a new session. Sessions must be associated with a +This endpoint initializes a new session. Sessions may be associated with a node and may be associated with any number of checks. | Method | Path | Produces | @@ -35,7 +35,9 @@ The table below shows this endpoint's support for - `dc` `(string: "")` - Specifies the datacenter to query. This will default to the datacenter of the agent being queried. This is specified as part of the - URL as a query parameter. Using this parameter across datacenters is not recommended. + URL as a query parameter. Using this parameter across datacenters on client + nodes requires that you specify a TTL, specify the `Node` as `null`, and pass + in an empty `NodeChecks` array. ### JSON Request Body Schema @@ -146,7 +148,6 @@ The table below shows this endpoint's support for - `dc` `(string: "")` - Specifies the datacenter to query. This will default to the datacenter of the agent being queried. - Using this parameter across datacenters is not recommended. - `ns` `(string: "")` - Specifies the namespace to query. You can also [specify the namespace through other methods](#methods-to-specify-namespace). @@ -191,7 +192,6 @@ The table below shows this endpoint's support for - `dc` `(string: "")` - Specifies the datacenter to query. This defaults to the datacenter of the agent being queried. - Using this parameter across datacenters is not recommended. - `ns` `(string: "")` - Specifies the namespace to query. You can also [specify the namespace through other methods](#methods-to-specify-namespace). @@ -254,7 +254,6 @@ The table below shows this endpoint's support for - `dc` `(string: "")` - Specifies the datacenter to query. This will default to the datacenter of the agent being queried. - Using this parameter across datacenters is not recommended. - `ns` `(string: "")` - Specifies the namespace to query. You can also [specify the namespace through other methods](#methods-to-specify-namespace). @@ -313,7 +312,6 @@ The table below shows this endpoint's support for - `dc` `(string: "")` - Specifies the datacenter to query. This will default to the datacenter of the agent being queried. - Using this parameter across datacenters is not recommended. - `ns` `(string: "")` - Specifies the namespace to query. You can also [specify the namespace through other methods](#methods-to-specify-namespace). @@ -375,7 +373,6 @@ The table below shows this endpoint's support for - `dc` `(string: "")` - Specifies the datacenter to query. This will default to the datacenter of the agent being queried. - Using this parameter across datacenters is not recommended. - `ns` `(string: "")` - Specifies the namespace to query. You can also [specify the namespace through other methods](#methods-to-specify-namespace).