secret/consul: Add support for consul namespaces and admin partitions (#13850)

* Add support for consul namespaces and admin partitions
This commit is contained in:
Robert 2022-02-09 15:44:00 -06:00 committed by GitHub
parent 571804390e
commit d0832a1993
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 460 additions and 76 deletions

View File

@ -5,6 +5,7 @@ import (
"encoding/base64"
"fmt"
"log"
"os"
"reflect"
"testing"
"time"
@ -38,7 +39,7 @@ func testBackendConfigAccess(t *testing.T, version string) {
t.Fatal(err)
}
cleanup, consulConfig := consul.PrepareTestContainer(t, version)
cleanup, consulConfig := consul.PrepareTestContainer(t, version, false)
defer cleanup()
connData := map[string]interface{}{
@ -87,7 +88,7 @@ func TestBackend_Renew_Revoke(t *testing.T) {
t.Parallel()
t.Run("legacy", func(t *testing.T) {
t.Parallel()
testBackendRenewRevoke(t, "")
testBackendRenewRevoke(t, "1.4.4")
})
testBackendRenewRevoke14(t, "")
@ -103,7 +104,7 @@ func testBackendRenewRevoke(t *testing.T, version string) {
t.Fatal(err)
}
cleanup, consulConfig := consul.PrepareTestContainer(t, version)
cleanup, consulConfig := consul.PrepareTestContainer(t, version, false)
defer cleanup()
connData := map[string]interface{}{
@ -196,7 +197,7 @@ func testBackendRenewRevoke(t *testing.T, version string) {
Value: []byte("bar"),
}, nil)
if err == nil {
t.Fatal("expected error")
t.Fatal("err: expected error")
}
}
@ -208,7 +209,7 @@ func testBackendRenewRevoke14(t *testing.T, version string) {
t.Fatal(err)
}
cleanup, consulConfig := consul.PrepareTestContainer(t, version)
cleanup, consulConfig := consul.PrepareTestContainer(t, version, false)
defer cleanup()
connData := map[string]interface{}{
@ -320,7 +321,7 @@ func TestBackend_LocalToken(t *testing.T) {
t.Fatal(err)
}
cleanup, consulConfig := consul.PrepareTestContainer(t, "")
cleanup, consulConfig := consul.PrepareTestContainer(t, "", false)
defer cleanup()
connData := map[string]interface{}{
@ -450,7 +451,7 @@ func TestBackend_Management(t *testing.T) {
})
t.Run("post-1.4.0", func(t *testing.T) {
t.Parallel()
testBackendManagement(t, "")
testBackendManagement(t, "1.4.4")
})
})
}
@ -463,7 +464,7 @@ func testBackendManagement(t *testing.T, version string) {
t.Fatal(err)
}
cleanup, consulConfig := consul.PrepareTestContainer(t, version)
cleanup, consulConfig := consul.PrepareTestContainer(t, version, false)
defer cleanup()
connData := map[string]interface{}{
@ -492,10 +493,9 @@ func TestBackend_Basic(t *testing.T) {
t.Parallel()
t.Run("legacy", func(t *testing.T) {
t.Parallel()
testBackendRenewRevoke(t, "")
testBackendRenewRevoke(t, "1.4.4")
})
testBackendBasic(t, "")
testBackendBasic(t, "1.4.4")
})
})
}
@ -508,7 +508,7 @@ func testBackendBasic(t *testing.T, version string) {
t.Fatal(err)
}
cleanup, consulConfig := consul.PrepareTestContainer(t, version)
cleanup, consulConfig := consul.PrepareTestContainer(t, version, false)
defer cleanup()
connData := map[string]interface{}{
@ -702,6 +702,262 @@ func testAccStepDeletePolicy(t *testing.T, name string) logicaltest.TestStep {
}
}
func TestBackend_Enterprise_Namespace(t *testing.T) {
if _, hasLicense := os.LookupEnv("CONSUL_LICENSE"); !hasLicense {
t.Skip("Skipping: No enterprise license found")
}
testBackendEntNamespace(t)
}
func TestBackend_Enterprise_Partition(t *testing.T) {
if _, hasLicense := os.LookupEnv("CONSUL_LICENSE"); !hasLicense {
t.Skip("Skipping: No enterprise license found")
}
testBackendEntPartition(t)
}
func testBackendEntNamespace(t *testing.T) {
config := logical.TestBackendConfig()
config.StorageView = &logical.InmemStorage{}
b, err := Factory(context.Background(), config)
if err != nil {
t.Fatal(err)
}
cleanup, consulConfig := consul.PrepareTestContainer(t, "", true)
defer cleanup()
connData := map[string]interface{}{
"address": consulConfig.Address(),
"token": consulConfig.Token,
}
req := &logical.Request{
Storage: config.StorageView,
Operation: logical.UpdateOperation,
Path: "config/access",
Data: connData,
}
resp, err := b.HandleRequest(context.Background(), req)
if err != nil {
t.Fatal(err)
}
// Create the role in namespace "ns1"
req.Path = "roles/test-ns"
req.Data = map[string]interface{}{
"policies": []string{"ns-test"},
"lease": "6h",
"consul_namespace": "ns1",
}
resp, err = b.HandleRequest(context.Background(), req)
if err != nil {
t.Fatal(err)
}
req.Operation = logical.ReadOperation
req.Path = "creds/test-ns"
resp, err = b.HandleRequest(context.Background(), req)
if err != nil {
t.Fatal(err)
}
if resp == nil {
t.Fatal("resp nil")
}
if resp.IsError() {
t.Fatalf("resp is error: %v", resp.Error())
}
generatedSecret := resp.Secret
generatedSecret.TTL = 6 * time.Hour
var d struct {
Token string `mapstructure:"token"`
Accessor string `mapstructure:"accessor"`
ConsulNamespace string `mapstructure:"consul_namespace"`
}
if err := mapstructure.Decode(resp.Data, &d); err != nil {
t.Fatal(err)
}
t.Logf("Generated namespace '%s' token: %s with accessor %s", d.ConsulNamespace, d.Token, d.Accessor)
if d.ConsulNamespace != "ns1" {
t.Fatalf("Failed to access namespace")
}
// Build a client and verify that the credentials work
consulapiConfig := consulapi.DefaultNonPooledConfig()
consulapiConfig.Address = connData["address"].(string)
consulapiConfig.Token = d.Token
client, err := consulapi.NewClient(consulapiConfig)
if err != nil {
t.Fatal(err)
}
t.Log("Verifying that the generated token works...")
_, err = client.Catalog(), nil
if err != nil {
t.Fatal(err)
}
req.Operation = logical.RenewOperation
req.Secret = generatedSecret
resp, err = b.HandleRequest(context.Background(), req)
if err != nil {
t.Fatal(err)
}
if resp == nil {
t.Fatal("got nil response from renew")
}
req.Operation = logical.RevokeOperation
resp, err = b.HandleRequest(context.Background(), req)
if err != nil {
t.Fatal(err)
}
// Build a management client and verify that the token does not exist anymore
consulmgmtConfig := consulapi.DefaultNonPooledConfig()
consulmgmtConfig.Address = connData["address"].(string)
consulmgmtConfig.Token = connData["token"].(string)
mgmtclient, err := consulapi.NewClient(consulmgmtConfig)
if err != nil {
t.Fatal(err)
}
q := &consulapi.QueryOptions{
Datacenter: "DC1",
Namespace: "ns1",
}
t.Log("Verifying that the generated token does not exist...")
_, _, err = mgmtclient.ACL().TokenRead(d.Accessor, q)
if err == nil {
t.Fatal("err: expected error")
}
}
func testBackendEntPartition(t *testing.T) {
config := logical.TestBackendConfig()
config.StorageView = &logical.InmemStorage{}
b, err := Factory(context.Background(), config)
if err != nil {
t.Fatal(err)
}
cleanup, consulConfig := consul.PrepareTestContainer(t, "", true)
defer cleanup()
connData := map[string]interface{}{
"address": consulConfig.Address(),
"token": consulConfig.Token,
}
req := &logical.Request{
Storage: config.StorageView,
Operation: logical.UpdateOperation,
Path: "config/access",
Data: connData,
}
resp, err := b.HandleRequest(context.Background(), req)
if err != nil {
t.Fatal(err)
}
// Create the role in partition "part1"
req.Path = "roles/test-part"
req.Data = map[string]interface{}{
"policies": []string{"part-test"},
"lease": "6h",
"partition": "part1",
}
resp, err = b.HandleRequest(context.Background(), req)
if err != nil {
t.Fatal(err)
}
req.Operation = logical.ReadOperation
req.Path = "creds/test-part"
resp, err = b.HandleRequest(context.Background(), req)
if err != nil {
t.Fatal(err)
}
if resp == nil {
t.Fatal("resp nil")
}
if resp.IsError() {
t.Fatalf("resp is error: %v", resp.Error())
}
generatedSecret := resp.Secret
generatedSecret.TTL = 6 * time.Hour
var d struct {
Token string `mapstructure:"token"`
Accessor string `mapstructure:"accessor"`
Partition string `mapstructure:"partition"`
}
if err := mapstructure.Decode(resp.Data, &d); err != nil {
t.Fatal(err)
}
t.Logf("Generated partition '%s' token: %s with accessor %s", d.Partition, d.Token, d.Accessor)
if d.Partition != "part1" {
t.Fatalf("Failed to access partition")
}
// Build a client and verify that the credentials work
consulapiConfig := consulapi.DefaultNonPooledConfig()
consulapiConfig.Address = connData["address"].(string)
consulapiConfig.Token = d.Token
client, err := consulapi.NewClient(consulapiConfig)
if err != nil {
t.Fatal(err)
}
t.Log("Verifying that the generated token works...")
_, err = client.Catalog(), nil
if err != nil {
t.Fatal(err)
}
req.Operation = logical.RenewOperation
req.Secret = generatedSecret
resp, err = b.HandleRequest(context.Background(), req)
if err != nil {
t.Fatal(err)
}
if resp == nil {
t.Fatal("got nil response from renew")
}
req.Operation = logical.RevokeOperation
resp, err = b.HandleRequest(context.Background(), req)
if err != nil {
t.Fatal(err)
}
// Build a management client and verify that the token does not exist anymore
consulmgmtConfig := consulapi.DefaultNonPooledConfig()
consulmgmtConfig.Address = connData["address"].(string)
consulmgmtConfig.Token = connData["token"].(string)
mgmtclient, err := consulapi.NewClient(consulmgmtConfig)
if err != nil {
t.Fatal(err)
}
q := &consulapi.QueryOptions{
Datacenter: "DC1",
Partition: "test1",
}
t.Log("Verifying that the generated token does not exist...")
_, _, err = mgmtclient.ACL().TokenRead(d.Accessor, q)
if err == nil {
t.Fatal("err: expected error")
}
}
const testPolicy = `
key "" {
policy = "write"

View File

@ -26,7 +26,7 @@ func pathRoles(b *backend) *framework.Path {
Fields: map[string]*framework.FieldSchema{
"name": {
Type: framework.TypeString,
Description: "Name of the role",
Description: "Name of the role.",
},
"policy": {
@ -71,6 +71,18 @@ Defaults to 'client'.`,
Description: "Use ttl instead.",
Deprecated: true,
},
"consul_namespace": {
Type: framework.TypeString,
Description: `Indicates which namespace that the token will be
created within. Defaults to 'default'. Available in Consul 1.7 and above.`,
},
"partition": {
Type: framework.TypeString,
Description: `Indicates which admin partition that the token
will be created within. Defaults to 'default'. Available in Consul 1.11 and above.`,
},
},
Callbacks: map[logical.Operation]framework.OperationFunc{
@ -113,11 +125,13 @@ func (b *backend) pathRolesRead(ctx context.Context, req *logical.Request, d *fr
// Generate the response
resp := &logical.Response{
Data: map[string]interface{}{
"lease": int64(result.TTL.Seconds()),
"ttl": int64(result.TTL.Seconds()),
"max_ttl": int64(result.MaxTTL.Seconds()),
"token_type": result.TokenType,
"local": result.Local,
"lease": int64(result.TTL.Seconds()),
"ttl": int64(result.TTL.Seconds()),
"max_ttl": int64(result.MaxTTL.Seconds()),
"token_type": result.TokenType,
"local": result.Local,
"consul_namespace": result.ConsulNamespace,
"partition": result.Partition,
},
}
if result.Policy != "" {
@ -126,16 +140,14 @@ func (b *backend) pathRolesRead(ctx context.Context, req *logical.Request, d *fr
if len(result.Policies) > 0 {
resp.Data["policies"] = result.Policies
}
return resp, nil
}
func (b *backend) pathRolesWrite(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
tokenType := d.Get("token_type").(string)
policy := d.Get("policy").(string)
name := d.Get("name").(string)
policies := d.Get("policies").([]string)
local := d.Get("local").(bool)
if len(policies) == 0 {
switch tokenType {
case "client":
@ -173,13 +185,19 @@ func (b *backend) pathRolesWrite(ctx context.Context, req *logical.Request, d *f
maxTTL = time.Second * time.Duration(maxTTLRaw.(int))
}
name := d.Get("name").(string)
local := d.Get("local").(bool)
namespace := d.Get("consul_namespace").(string)
partition := d.Get("partition").(string)
entry, err := logical.StorageEntryJSON("policy/"+name, roleConfig{
Policy: string(policyRaw),
Policies: policies,
TokenType: tokenType,
TTL: ttl,
MaxTTL: maxTTL,
Local: local,
Policy: string(policyRaw),
Policies: policies,
TokenType: tokenType,
TTL: ttl,
MaxTTL: maxTTL,
Local: local,
ConsulNamespace: namespace,
Partition: partition,
})
if err != nil {
return nil, err
@ -201,10 +219,12 @@ func (b *backend) pathRolesDelete(ctx context.Context, req *logical.Request, d *
}
type roleConfig struct {
Policy string `json:"policy"`
Policies []string `json:"policies"`
TTL time.Duration `json:"lease"`
MaxTTL time.Duration `json:"max_ttl"`
TokenType string `json:"token_type"`
Local bool `json:"local"`
Policy string `json:"policy"`
Policies []string `json:"policies"`
TTL time.Duration `json:"lease"`
MaxTTL time.Duration `json:"max_ttl"`
TokenType string `json:"token_type"`
Local bool `json:"local"`
ConsulNamespace string `json:"consul_namespace"`
Partition string `json:"partition"`
}

View File

@ -20,7 +20,22 @@ func pathToken(b *backend) *framework.Path {
Fields: map[string]*framework.FieldSchema{
"role": {
Type: framework.TypeString,
Description: "Name of the role",
Description: "Name of the role.",
},
"policies": {
Type: framework.TypeCommaStringSlice,
Description: `List of policies to attach to the token.`,
},
"consul_namespace": {
Type: framework.TypeString,
Description: "Namespace to create the token in.",
},
"partition": {
Type: framework.TypeString,
Description: "Admin partition to create the token in.",
},
},
@ -32,7 +47,6 @@ func pathToken(b *backend) *framework.Path {
func (b *backend) pathTokenRead(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
role := d.Get("role").(string)
entry, err := req.Storage.Get(ctx, "policy/"+role)
if err != nil {
return nil, fmt.Errorf("error retrieving role: %w", err)
@ -41,13 +55,13 @@ func (b *backend) pathTokenRead(ctx context.Context, req *logical.Request, d *fr
return logical.ErrorResponse(fmt.Sprintf("role %q not found", role)), nil
}
var result roleConfig
if err := entry.DecodeJSON(&result); err != nil {
var roleConfigData roleConfig
if err := entry.DecodeJSON(&roleConfigData); err != nil {
return nil, err
}
if result.TokenType == "" {
result.TokenType = "client"
if roleConfigData.TokenType == "" {
roleConfigData.TokenType = "client"
}
// Get the consul client
@ -66,12 +80,12 @@ func (b *backend) pathTokenRead(ctx context.Context, req *logical.Request, d *fr
writeOpts = writeOpts.WithContext(ctx)
// Create an ACLEntry for Consul pre 1.4
if (result.Policy != "" && result.TokenType == "client") ||
(result.Policy == "" && result.TokenType == "management") {
if (roleConfigData.Policy != "" && roleConfigData.TokenType == "client") ||
(roleConfigData.Policy == "" && roleConfigData.TokenType == "management") {
token, _, err := c.ACL().Create(&api.ACLEntry{
Name: tokenName,
Type: result.TokenType,
Rules: result.Policy,
Type: roleConfigData.TokenType,
Rules: roleConfigData.Policy,
}, writeOpts)
if err != nil {
return logical.ErrorResponse(err.Error()), nil
@ -84,22 +98,26 @@ func (b *backend) pathTokenRead(ctx context.Context, req *logical.Request, d *fr
"token": token,
"role": role,
})
s.Secret.TTL = result.TTL
s.Secret.MaxTTL = result.MaxTTL
s.Secret.TTL = roleConfigData.TTL
s.Secret.MaxTTL = roleConfigData.MaxTTL
return s, nil
}
// Create an ACLToken for Consul 1.4 and above
policyLink := []*api.ACLTokenPolicyLink{}
for _, policyName := range result.Policies {
policyLink = append(policyLink, &api.ACLTokenPolicyLink{
policyLinks := []*api.ACLTokenPolicyLink{}
for _, policyName := range roleConfigData.Policies {
policyLinks = append(policyLinks, &api.ACLTokenPolicyLink{
Name: policyName,
})
}
token, _, err := c.ACL().TokenCreate(&api.ACLToken{
Description: tokenName,
Policies: policyLink,
Local: result.Local,
Policies: policyLinks,
Local: roleConfigData.Local,
Namespace: roleConfigData.ConsulNamespace,
Partition: roleConfigData.Partition,
}, writeOpts)
if err != nil {
return logical.ErrorResponse(err.Error()), nil
@ -107,16 +125,18 @@ func (b *backend) pathTokenRead(ctx context.Context, req *logical.Request, d *fr
// Use the helper to create the secret
s := b.Secret(SecretTokenType).Response(map[string]interface{}{
"token": token.SecretID,
"accessor": token.AccessorID,
"local": token.Local,
"token": token.SecretID,
"accessor": token.AccessorID,
"local": token.Local,
"consul_namespace": token.Namespace,
"partition": token.Partition,
}, map[string]interface{}{
"token": token.AccessorID,
"role": role,
"version": tokenPolicyType,
})
s.Secret.TTL = result.TTL
s.Secret.MaxTTL = result.MaxTTL
s.Secret.TTL = roleConfigData.TTL
s.Secret.MaxTTL = roleConfigData.MaxTTL
return s, nil
}

3
changelog/13850.txt Normal file
View File

@ -0,0 +1,3 @@
```release-note:improvement
secrets/consul: Add support for consul enterprise namespaces and admin partitions.
```

8
go.mod
View File

@ -57,7 +57,7 @@ require (
github.com/google/go-metrics-stackdriver v0.2.0
github.com/hashicorp/cap v0.1.1
github.com/hashicorp/consul-template v0.27.2-0.20211014231529-4ff55381f1c4
github.com/hashicorp/consul/api v1.11.0
github.com/hashicorp/consul/api v1.12.0
github.com/hashicorp/errwrap v1.1.0
github.com/hashicorp/go-cleanhttp v0.5.2
github.com/hashicorp/go-discover v0.0.0-20210818145131-c573d69da192
@ -283,8 +283,8 @@ require (
github.com/hashicorp/go-version v1.3.0 // indirect
github.com/hashicorp/jsonapi v0.0.0-20210826224640-ee7dae0fb22d // indirect
github.com/hashicorp/logutils v1.0.0 // indirect
github.com/hashicorp/mdns v1.0.1 // indirect
github.com/hashicorp/serf v0.9.5 // indirect
github.com/hashicorp/mdns v1.0.4 // indirect
github.com/hashicorp/serf v0.9.6 // indirect
github.com/hashicorp/vic v1.5.1-0.20190403131502-bbfe86ec9443 // indirect
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d // indirect
github.com/huandu/xstrings v1.3.2 // indirect
@ -305,7 +305,7 @@ require (
github.com/mattn/go-ieproxy v0.0.1 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
github.com/miekg/dns v1.1.40 // indirect
github.com/miekg/dns v1.1.41 // indirect
github.com/mitchellh/hashstructure v1.0.0 // indirect
github.com/mitchellh/iochan v1.0.0 // indirect
github.com/mitchellh/pointerstructure v1.2.0 // indirect

12
go.sum
View File

@ -793,6 +793,8 @@ github.com/hashicorp/consul-template v0.27.2-0.20211014231529-4ff55381f1c4/go.mo
github.com/hashicorp/consul/api v1.4.0/go.mod h1:xc8u05kyMa3Wjr9eEAsIAo3dg8+LywT5E/Cl7cNS5nU=
github.com/hashicorp/consul/api v1.11.0 h1:Hw/G8TtRvOElqxVIhBzXciiSTbapq8hZ2XKZsXk5ZCE=
github.com/hashicorp/consul/api v1.11.0/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M=
github.com/hashicorp/consul/api v1.12.0 h1:k3y1FYv6nuKyNTqj6w9gXOx5r5CfLj/k/euUeBXj1OY=
github.com/hashicorp/consul/api v1.12.0/go.mod h1:6pVBMo0ebnYdt2S3H87XhekM/HHrUoTD2XXb/VrZVy0=
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
github.com/hashicorp/consul/sdk v0.4.0/go.mod h1:fY08Y9z5SvJqevyZNy6WWPXiG3KwBPAvlcdx16zZ0fM=
github.com/hashicorp/consul/sdk v0.4.1-0.20200910203702-bb2b5dd871ca/go.mod h1:fY08Y9z5SvJqevyZNy6WWPXiG3KwBPAvlcdx16zZ0fM=
@ -911,9 +913,13 @@ github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
github.com/hashicorp/mdns v1.0.1 h1:XFSOubp8KWB+Jd2PDyaX5xUd5bhSP/+pTDZVDMzZJM8=
github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY=
github.com/hashicorp/mdns v1.0.4 h1:sY0CMhFmjIPDMlTB+HfymFHCaYLhgifZ0QhjaYKD/UQ=
github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc=
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
github.com/hashicorp/memberlist v0.2.2 h1:5+RffWKwqJ71YPu9mWsF7ZOscZmwfasdA8kbdC7AO2g=
github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE=
github.com/hashicorp/memberlist v0.3.0 h1:8+567mCcFDnS5ADl7lrpxPMWiFCElyUEeW0gtj34fMA=
github.com/hashicorp/memberlist v0.3.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE=
github.com/hashicorp/nomad/api v0.0.0-20211006193434-215bf04bc650 h1:pSi8Q6BuijRU9vK/b4/evBeDMXSFBlOX5CTUo3iY4HY=
github.com/hashicorp/nomad/api v0.0.0-20211006193434-215bf04bc650/go.mod h1:vYHP9jMXk4/T2qNUbWlQ1OHCA1hHLil3nvqSmz8mtgc=
github.com/hashicorp/raft v1.0.1/go.mod h1:DVSAWItjLjTOkVbSpWQ0j0kUADIvDaCtBxIcbNAQLkI=
@ -934,6 +940,8 @@ github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/J
github.com/hashicorp/serf v0.9.4/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk=
github.com/hashicorp/serf v0.9.5 h1:EBWvyu9tcRszt3Bxp3KNssBMP1KuHWyO51lz9+786iM=
github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk=
github.com/hashicorp/serf v0.9.6 h1:uuEX1kLR6aoda1TBttmJQKDLZE1Ob7KN0NPdE7EtCDc=
github.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4=
github.com/hashicorp/vault-plugin-auth-alicloud v0.10.0 h1:ujwHy67QeSwIWN2OLw4K/9ImcZaNU2jeNpWDI17/aQk=
github.com/hashicorp/vault-plugin-auth-alicloud v0.10.0/go.mod h1:GqQnzKRACjoUJCq8cHXJKPIMbFpIwxaLTwz8dyYghvM=
github.com/hashicorp/vault-plugin-auth-azure v0.9.2 h1:Q2+z7tAMfc141CWA/4RemI/VtrnuJ1UMwz80EYP73gA=
@ -1149,6 +1157,8 @@ github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3N
github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
github.com/miekg/dns v1.1.40 h1:pyyPFfGMnciYUk/mXpKkVmeMQjfXqt3FAJ2hy7tPiLA=
github.com/miekg/dns v1.1.40/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
github.com/miekg/dns v1.1.41 h1:WMszZWJG0XmzbK9FEmzH2TVcqYzFesusSIB41b8KHxY=
github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI=
github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
@ -1730,6 +1740,7 @@ golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwY
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8=
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
@ -1859,6 +1870,7 @@ golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20201202213521-69691e467435/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=

View File

@ -26,7 +26,9 @@ func (c *Config) APIConfig() *consulapi.Config {
// the Consul version used will be given by the environment variable
// CONSUL_DOCKER_VERSION, or if that's empty, whatever we've hardcoded as the
// the latest Consul version.
func PrepareTestContainer(t *testing.T, version string) (func(), *Config) {
func PrepareTestContainer(t *testing.T, version string, isEnterprise bool) (func(), *Config) {
t.Helper()
if retAddress := os.Getenv("CONSUL_HTTP_ADDR"); retAddress != "" {
shp, err := docker.NewServiceHostPortParse(retAddress)
if err != nil {
@ -41,21 +43,38 @@ func PrepareTestContainer(t *testing.T, version string) (func(), *Config) {
if consulVersion != "" {
version = consulVersion
} else {
version = "1.7.2" // Latest Consul version, update as new releases come out
version = "1.11.2" // Latest Consul version, update as new releases come out
}
}
if strings.HasPrefix(version, "1.3") {
config = `datacenter = "test" acl_default_policy = "deny" acl_datacenter = "test" acl_master_token = "test"`
}
repo := os.Getenv("CONSUL_DOCKER_REPO")
if repo == "" {
repo = "consul"
name := "consul"
repo := "consul"
var envVars []string
// If running the enterprise container, set the appropriate values below.
if isEnterprise {
version += "-ent"
name = "consul-enterprise"
repo = "hashicorp/consul-enterprise"
license, hasLicense := os.LookupEnv("CONSUL_LICENSE")
envVars = append(envVars, "CONSUL_LICENSE="+license)
if !hasLicense {
t.Fatalf("Failed to find enterprise license")
}
}
if dockerRepo, hasEnvRepo := os.LookupEnv("CONSUL_DOCKER_REPO"); hasEnvRepo {
repo = dockerRepo
}
runner, err := docker.NewServiceRunner(docker.RunOptions{
ContainerName: "consul",
ContainerName: name,
ImageRepo: repo,
ImageTag: version,
Env: envVars,
Cmd: []string{"agent", "-dev", "-client", "0.0.0.0", "-hcl", config},
Ports: []string{"8500/tcp"},
AuthUsername: os.Getenv("CONSUL_DOCKER_USERNAME"),
@ -102,13 +121,12 @@ func PrepareTestContainer(t *testing.T, version string) (func(), *Config) {
Name: "test",
Description: "test",
Rules: `node_prefix "" {
policy = "write"
}
policy = "write"
}
service_prefix "" {
policy = "read"
}
`,
service_prefix "" {
policy = "read"
}`,
}
q := &consulapi.WriteOptions{
Token: consulToken,
@ -117,13 +135,68 @@ func PrepareTestContainer(t *testing.T, version string) (func(), *Config) {
if err != nil {
return nil, err
}
// Configure a namespace and parition if testing enterprise Consul
if isEnterprise {
// Namespaces require Consul 1.7 or newer
namespace := &consulapi.Namespace{
Name: "ns1",
Description: "ns1 test",
}
_, _, err = consul.Namespaces().Create(namespace, q)
if err != nil {
return nil, err
}
nsPolicy := &consulapi.ACLPolicy{
Name: "ns-test",
Description: "namespace test",
Namespace: "ns1",
Rules: `service_prefix "" {
policy = "read"
}`,
}
_, _, err = consul.ACL().PolicyCreate(nsPolicy, q)
if err != nil {
return nil, err
}
// Partitions require Consul 1.11 or newer
partition := &consulapi.Partition{
Name: "part1",
Description: "part1 test",
}
_, _, err = consul.Partitions().Create(ctx, partition, q)
if err != nil {
return nil, err
}
partPolicy := &consulapi.ACLPolicy{
Name: "part-test",
Description: "partition test",
Partition: "part1",
Rules: `service_prefix "" {
policy = "read"
}`,
}
_, _, err = consul.ACL().PolicyCreate(partPolicy, q)
if err != nil {
return nil, err
}
}
return &Config{
ServiceHostPort: *shp,
Token: consulToken,
}, nil
})
if err != nil {
t.Fatalf("Could not start docker Consul: %s", err)
}
return svc.Cleanup, svc.Config.(*Config)
}

View File

@ -12,7 +12,7 @@ import (
)
func MakeConsulBackend(t testing.T, logger hclog.Logger) *vault.PhysicalBackendBundle {
cleanup, config := consul.PrepareTestContainer(t.(*realtesting.T), "")
cleanup, config := consul.PrepareTestContainer(t.(*realtesting.T), "", false)
consulConf := map[string]string{
"address": config.Address(),

View File

@ -157,7 +157,7 @@ func TestConsul_newConsulBackend(t *testing.T) {
}
func TestConsulBackend(t *testing.T) {
cleanup, config := consul.PrepareTestContainer(t, "1.4.4")
cleanup, config := consul.PrepareTestContainer(t, "1.4.4", false)
defer cleanup()
client, err := api.NewClient(config.APIConfig())
@ -187,7 +187,7 @@ func TestConsulBackend(t *testing.T) {
}
func TestConsul_TooLarge(t *testing.T) {
cleanup, config := consul.PrepareTestContainer(t, "1.4.4")
cleanup, config := consul.PrepareTestContainer(t, "1.4.4", false)
defer cleanup()
client, err := api.NewClient(config.APIConfig())
@ -250,7 +250,7 @@ func TestConsul_TooLarge(t *testing.T) {
}
func TestConsulHABackend(t *testing.T) {
cleanup, config := consul.PrepareTestContainer(t, "1.4.4")
cleanup, config := consul.PrepareTestContainer(t, "1.4.4", false)
defer cleanup()
client, err := api.NewClient(config.APIConfig())

View File

@ -50,7 +50,7 @@ func testConsulServiceRegistrationConfig(t *testing.T, conf *consulConf) *servic
// TestConsul_ServiceRegistration tests whether consul ServiceRegistration works
func TestConsul_ServiceRegistration(t *testing.T) {
// Prepare a docker-based consul instance
cleanup, config := consul.PrepareTestContainer(t, "")
cleanup, config := consul.PrepareTestContainer(t, "", false)
defer cleanup()
// Create a consul client