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).