mirror of https://github.com/hashicorp/consul
Browse Source
* feat(dataplane): allow token and tenancy information for proxied DNS * changelogpull/21008/head
Dan Stough
7 months ago
committed by
GitHub
12 changed files with 376 additions and 68 deletions
@ -0,0 +1,4 @@
|
||||
```release-note:improvement |
||||
dns: DNS-over-grpc when using `consul-dataplane` now accepts partition, namespace, token as metadata to default those query parameters. |
||||
`consul-dataplane` v1.5+ will send this information automatically. |
||||
``` |
@ -0,0 +1,56 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
package dns |
||||
|
||||
import ( |
||||
"context" |
||||
"fmt" |
||||
|
||||
"github.com/mitchellh/mapstructure" |
||||
"google.golang.org/grpc/metadata" |
||||
) |
||||
|
||||
// Context is used augment a DNS message with Consul-specific metadata.
|
||||
type Context struct { |
||||
Token string `mapstructure:"x-consul-token,omitempty"` |
||||
DefaultNamespace string `mapstructure:"x-consul-namespace,omitempty"` |
||||
DefaultPartition string `mapstructure:"x-consul-partition,omitempty"` |
||||
} |
||||
|
||||
// NewContextFromGRPCContext returns the request context using the gRPC metadata attached to the
|
||||
// given context. If there is no gRPC metadata, it returns an empty context.
|
||||
func NewContextFromGRPCContext(ctx context.Context) (Context, error) { |
||||
if ctx == nil { |
||||
return Context{}, nil |
||||
} |
||||
|
||||
reqCtx := Context{} |
||||
md, ok := metadata.FromIncomingContext(ctx) |
||||
if !ok { |
||||
return reqCtx, nil |
||||
} |
||||
|
||||
m := map[string]string{} |
||||
for k, v := range md { |
||||
m[k] = v[0] |
||||
} |
||||
|
||||
decoderConfig := &mapstructure.DecoderConfig{ |
||||
Metadata: nil, |
||||
Result: &reqCtx, |
||||
WeaklyTypedInput: true, |
||||
} |
||||
|
||||
decoder, err := mapstructure.NewDecoder(decoderConfig) |
||||
if err != nil { |
||||
return Context{}, fmt.Errorf("error creating mapstructure decoder: %w", err) |
||||
} |
||||
|
||||
err = decoder.Decode(m) |
||||
if err != nil { |
||||
return Context{}, fmt.Errorf("error decoding metadata: %w", err) |
||||
} |
||||
|
||||
return reqCtx, nil |
||||
} |
@ -0,0 +1,70 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
package dns |
||||
|
||||
import ( |
||||
"context" |
||||
"testing" |
||||
|
||||
"github.com/stretchr/testify/require" |
||||
"google.golang.org/grpc/metadata" |
||||
) |
||||
|
||||
func TestNewContextFromGRPCContext(t *testing.T) { |
||||
t.Parallel() |
||||
|
||||
md := metadata.MD{} |
||||
testMeta := map[string]string{ |
||||
"x-consul-token": "test-token", |
||||
"x-consul-namespace": "test-namespace", |
||||
"x-consul-partition": "test-partition", |
||||
} |
||||
|
||||
for k, v := range testMeta { |
||||
md.Set(k, v) |
||||
} |
||||
testGRPCContext := metadata.NewIncomingContext(context.Background(), md) |
||||
|
||||
testCases := []struct { |
||||
name string |
||||
grpcCtx context.Context |
||||
expected *Context |
||||
error error |
||||
}{ |
||||
{ |
||||
name: "nil grpc context", |
||||
grpcCtx: nil, |
||||
expected: &Context{}, |
||||
}, |
||||
{ |
||||
name: "grpc context w/o metadata", |
||||
grpcCtx: context.Background(), |
||||
expected: &Context{}, |
||||
}, |
||||
{ |
||||
name: "grpc context w/ kitchen sink", |
||||
grpcCtx: testGRPCContext, |
||||
expected: &Context{ |
||||
Token: "test-token", |
||||
DefaultNamespace: "test-namespace", |
||||
DefaultPartition: "test-partition", |
||||
}, |
||||
}, |
||||
} |
||||
|
||||
for _, tc := range testCases { |
||||
t.Run(tc.name, func(t *testing.T) { |
||||
ctx, err := NewContextFromGRPCContext(tc.grpcCtx) |
||||
if tc.error != nil { |
||||
require.Error(t, err) |
||||
require.Equal(t, Context{}, &ctx) |
||||
require.Equal(t, tc.error, err) |
||||
return |
||||
} |
||||
|
||||
require.NotNil(t, ctx) |
||||
require.Equal(t, tc.expected, &ctx) |
||||
}) |
||||
} |
||||
} |
Loading…
Reference in new issue