Add path attributes to indicate when operations should forward (#7175)
This commit is contained in:
parent
0b5c7550a6
commit
fb4edc129e
|
@ -15,7 +15,8 @@ import (
|
||||||
|
|
||||||
"github.com/hashicorp/errwrap"
|
"github.com/hashicorp/errwrap"
|
||||||
log "github.com/hashicorp/go-hclog"
|
log "github.com/hashicorp/go-hclog"
|
||||||
multierror "github.com/hashicorp/go-multierror"
|
"github.com/hashicorp/go-multierror"
|
||||||
|
"github.com/hashicorp/vault/sdk/helper/consts"
|
||||||
"github.com/hashicorp/vault/sdk/helper/entropy"
|
"github.com/hashicorp/vault/sdk/helper/entropy"
|
||||||
"github.com/hashicorp/vault/sdk/helper/errutil"
|
"github.com/hashicorp/vault/sdk/helper/errutil"
|
||||||
"github.com/hashicorp/vault/sdk/helper/license"
|
"github.com/hashicorp/vault/sdk/helper/license"
|
||||||
|
@ -225,6 +226,19 @@ func (b *Backend) HandleRequest(ctx context.Context, req *logical.Request) (*log
|
||||||
|
|
||||||
if path.Operations != nil {
|
if path.Operations != nil {
|
||||||
if op, ok := path.Operations[req.Operation]; ok {
|
if op, ok := path.Operations[req.Operation]; ok {
|
||||||
|
|
||||||
|
// Check whether this operation should be forwarded
|
||||||
|
replState := b.System().ReplicationState()
|
||||||
|
props := op.Properties()
|
||||||
|
|
||||||
|
if props.ForwardPerformanceStandby && replState.HasState(consts.ReplicationPerformanceStandby) {
|
||||||
|
return nil, logical.ErrReadOnly
|
||||||
|
}
|
||||||
|
|
||||||
|
if props.ForwardPerformanceSecondary && !b.System().LocalMount() && replState.HasState(consts.ReplicationPerformanceSecondary) {
|
||||||
|
return nil, logical.ErrReadOnly
|
||||||
|
}
|
||||||
|
|
||||||
callback = op.Handler()
|
callback = op.Handler()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/hashicorp/vault/sdk/helper/consts"
|
||||||
"github.com/hashicorp/vault/sdk/logical"
|
"github.com/hashicorp/vault/sdk/logical"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -93,6 +94,7 @@ func TestBackendHandleRequest(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
system: &logical.StaticSystemView{},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, path := range []string{"foo/bar", "foo/baz/handler", "foo/both/handler"} {
|
for _, path := range []string{"foo/bar", "foo/baz/handler", "foo/both/handler"} {
|
||||||
|
@ -114,6 +116,98 @@ func TestBackendHandleRequest(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestBackendHandleRequest_Forwarding(t *testing.T) {
|
||||||
|
tests := map[string]struct {
|
||||||
|
fwdStandby bool
|
||||||
|
fwdSecondary bool
|
||||||
|
isLocal bool
|
||||||
|
isStandby bool
|
||||||
|
isSecondary bool
|
||||||
|
expectFwd bool
|
||||||
|
}{
|
||||||
|
"no forward": {
|
||||||
|
expectFwd: false,
|
||||||
|
},
|
||||||
|
"no forward, local restricted": {
|
||||||
|
isSecondary: true,
|
||||||
|
fwdSecondary: true,
|
||||||
|
isLocal: true,
|
||||||
|
expectFwd: false,
|
||||||
|
},
|
||||||
|
"no forward, forwarding not requested": {
|
||||||
|
isSecondary: true,
|
||||||
|
isStandby: true,
|
||||||
|
expectFwd: false,
|
||||||
|
},
|
||||||
|
"forward, secondary": {
|
||||||
|
fwdSecondary: true,
|
||||||
|
isSecondary: true,
|
||||||
|
expectFwd: true,
|
||||||
|
},
|
||||||
|
"forward, standby": {
|
||||||
|
fwdStandby: true,
|
||||||
|
isStandby: true,
|
||||||
|
expectFwd: true,
|
||||||
|
},
|
||||||
|
"no forward, only secondary": {
|
||||||
|
fwdSecondary: true,
|
||||||
|
isStandby: true,
|
||||||
|
expectFwd: false,
|
||||||
|
},
|
||||||
|
"no forward, only standby": {
|
||||||
|
fwdStandby: true,
|
||||||
|
isSecondary: true,
|
||||||
|
expectFwd: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, test := range tests {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
var replState consts.ReplicationState
|
||||||
|
if test.isStandby {
|
||||||
|
replState.AddState(consts.ReplicationPerformanceStandby)
|
||||||
|
}
|
||||||
|
if test.isSecondary {
|
||||||
|
replState.AddState(consts.ReplicationPerformanceSecondary)
|
||||||
|
}
|
||||||
|
|
||||||
|
b := &Backend{
|
||||||
|
Paths: []*Path{
|
||||||
|
{
|
||||||
|
Pattern: "foo",
|
||||||
|
Operations: map[logical.Operation]OperationHandler{
|
||||||
|
logical.ReadOperation: &PathOperation{
|
||||||
|
Callback: func(ctx context.Context, req *logical.Request, data *FieldData) (*logical.Response, error) {
|
||||||
|
return nil, nil
|
||||||
|
},
|
||||||
|
ForwardPerformanceSecondary: test.fwdSecondary,
|
||||||
|
ForwardPerformanceStandby: test.fwdStandby,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
system: &logical.StaticSystemView{
|
||||||
|
LocalMountVal: test.isLocal,
|
||||||
|
ReplicationStateVal: replState,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := b.HandleRequest(context.Background(), &logical.Request{
|
||||||
|
Operation: logical.ReadOperation,
|
||||||
|
Path: "foo",
|
||||||
|
})
|
||||||
|
|
||||||
|
if !test.expectFwd && err != nil {
|
||||||
|
t.Fatalf("unexpected err: %v", err)
|
||||||
|
}
|
||||||
|
if test.expectFwd && err != logical.ErrReadOnly {
|
||||||
|
t.Fatalf("expected ErrReadOnly, got: %v", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestBackendHandleRequest_badwrite(t *testing.T) {
|
func TestBackendHandleRequest_badwrite(t *testing.T) {
|
||||||
callback := func(ctx context.Context, req *logical.Request, data *FieldData) (*logical.Response, error) {
|
callback := func(ctx context.Context, req *logical.Request, data *FieldData) (*logical.Response, error) {
|
||||||
return &logical.Response{
|
return &logical.Response{
|
||||||
|
|
|
@ -153,6 +153,14 @@ type OperationProperties struct {
|
||||||
// Deprecated indicates that this operation should be avoided.
|
// Deprecated indicates that this operation should be avoided.
|
||||||
Deprecated bool
|
Deprecated bool
|
||||||
|
|
||||||
|
// ForwardPerformanceStandby indicates that this path should not be processed
|
||||||
|
// on a performance standby node, and should be forwarded to the active node instead.
|
||||||
|
ForwardPerformanceStandby bool
|
||||||
|
|
||||||
|
// ForwardPerformanceSecondary indicates that this path should not be processed
|
||||||
|
// on a performance secondary node, and should be forwarded to the active node instead.
|
||||||
|
ForwardPerformanceSecondary bool
|
||||||
|
|
||||||
// DisplayAttrs provides hints for UI and documentation generators. They
|
// DisplayAttrs provides hints for UI and documentation generators. They
|
||||||
// will be included in OpenAPI output if set.
|
// will be included in OpenAPI output if set.
|
||||||
DisplayAttrs *DisplayAttributes
|
DisplayAttrs *DisplayAttributes
|
||||||
|
@ -206,13 +214,15 @@ type Response struct {
|
||||||
|
|
||||||
// PathOperation is a concrete implementation of OperationHandler.
|
// PathOperation is a concrete implementation of OperationHandler.
|
||||||
type PathOperation struct {
|
type PathOperation struct {
|
||||||
Callback OperationFunc
|
Callback OperationFunc
|
||||||
Summary string
|
Summary string
|
||||||
Description string
|
Description string
|
||||||
Examples []RequestExample
|
Examples []RequestExample
|
||||||
Responses map[int][]Response
|
Responses map[int][]Response
|
||||||
Unpublished bool
|
Unpublished bool
|
||||||
Deprecated bool
|
Deprecated bool
|
||||||
|
ForwardPerformanceSecondary bool
|
||||||
|
ForwardPerformanceStandby bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *PathOperation) Handler() OperationFunc {
|
func (p *PathOperation) Handler() OperationFunc {
|
||||||
|
@ -221,12 +231,14 @@ func (p *PathOperation) Handler() OperationFunc {
|
||||||
|
|
||||||
func (p *PathOperation) Properties() OperationProperties {
|
func (p *PathOperation) Properties() OperationProperties {
|
||||||
return OperationProperties{
|
return OperationProperties{
|
||||||
Summary: strings.TrimSpace(p.Summary),
|
Summary: strings.TrimSpace(p.Summary),
|
||||||
Description: strings.TrimSpace(p.Description),
|
Description: strings.TrimSpace(p.Description),
|
||||||
Responses: p.Responses,
|
Responses: p.Responses,
|
||||||
Examples: p.Examples,
|
Examples: p.Examples,
|
||||||
Unpublished: p.Unpublished,
|
Unpublished: p.Unpublished,
|
||||||
Deprecated: p.Deprecated,
|
Deprecated: p.Deprecated,
|
||||||
|
ForwardPerformanceSecondary: p.ForwardPerformanceSecondary,
|
||||||
|
ForwardPerformanceStandby: p.ForwardPerformanceStandby,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue