Add path attributes to indicate when operations should forward (#7175)

This commit is contained in:
Jim Kalafut 2020-01-07 14:04:08 -08:00 committed by GitHub
parent 0b5c7550a6
commit fb4edc129e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 134 additions and 14 deletions

View File

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

View File

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

View File

@ -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
@ -213,6 +221,8 @@ type PathOperation struct {
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 {
@ -227,6 +237,8 @@ func (p *PathOperation) Properties() OperationProperties {
Examples: p.Examples, Examples: p.Examples,
Unpublished: p.Unpublished, Unpublished: p.Unpublished,
Deprecated: p.Deprecated, Deprecated: p.Deprecated,
ForwardPerformanceSecondary: p.ForwardPerformanceSecondary,
ForwardPerformanceStandby: p.ForwardPerformanceStandby,
} }
} }