Allow creating TTL'd sessions without a node

This is required to be able to hold cross-DC locks.

Signed-off-by: Johannes Löthberg <johannes.loethberg@elokon.com>
pull/21208/head
Johannes Löthberg 6 months ago
parent 11bcf521ae
commit 22a4f96b9d
No known key found for this signature in database
GPG Key ID: FEBC5EC99474C681

@ -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

@ -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

@ -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)
}

@ -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

@ -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: "")` <EnterpriseAlert inline /> - 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: "")` <EnterpriseAlert inline /> - 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: "")` <EnterpriseAlert inline /> - 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: "")` <EnterpriseAlert inline /> - 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: "")` <EnterpriseAlert inline /> - Specifies the namespace to query.
You can also [specify the namespace through other methods](#methods-to-specify-namespace).

Loading…
Cancel
Save