Set namespace on vault client if VAULT_NAMESPACE env is set (#6867)

This commit is contained in:
poornas 2018-11-27 14:42:32 -08:00 committed by kannappanr
parent b65cf281fd
commit 45bb11e020
25 changed files with 649 additions and 204 deletions

View File

@ -192,6 +192,11 @@ func NewVault(kmsConf KMSConfig) (KMS, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
if ns, ok := os.LookupEnv("VAULT_NAMESPACE"); ok {
c.SetNamespace(ns)
}
accessToken, leaseDuration, err := getVaultAccessToken(c, config.Auth.AppRole.ID, config.Auth.AppRole.Secret) accessToken, leaseDuration, err := getVaultAccessToken(c, config.Auth.AppRole.ID, config.Auth.AppRole.Secret)
if err != nil { if err != nil {
return nil, err return nil, err

View File

@ -50,6 +50,8 @@ vault write -f auth/approle/role/my-role/secret-id
The AppRole ID, AppRole Secret Id, Vault endpoint and Vault key name can now be used to start minio server with Vault as KMS. The AppRole ID, AppRole Secret Id, Vault endpoint and Vault key name can now be used to start minio server with Vault as KMS.
Note: If [Vault Namespaces](https://learn.hashicorp.com/vault/operations/namespaces) are in use, VAULT_NAMESPACE variable needs to be set before setting approle and transit secrets engine.
### 3. Environment variables ### 3. Environment variables
You'll need the Vault endpoint, AppRole ID, AppRole SecretID and encryption key-ring name defined in step 2.2 You'll need the Vault endpoint, AppRole ID, AppRole SecretID and encryption key-ring name defined in step 2.2
@ -67,6 +69,10 @@ Optionally set `MINIO_SSE_VAULT_CAPATH` as the path to a directory of PEM-encode
export MINIO_SSE_VAULT_CAPATH=/home/user/custom-pems export MINIO_SSE_VAULT_CAPATH=/home/user/custom-pems
``` ```
Optionally set `VAULT_NAMESPACE` if AppRole and Transit Secrets engine have been scoped to Vault Namespace
```
export VAULT_NAMESPACE=ns1
```
### 4. Test your setup ### 4. Test your setup
To test this setup, start minio server with environment variables set in Step 3, and server is ready to handle SSE-S3 requests. To test this setup, start minio server with environment variables set in Step 3, and server is ready to handle SSE-S3 requests.

View File

@ -271,4 +271,5 @@ type TokenCreateRequest struct {
DisplayName string `json:"display_name"` DisplayName string `json:"display_name"`
NumUses int `json:"num_uses"` NumUses int `json:"num_uses"`
Renewable *bool `json:"renewable,omitempty"` Renewable *bool `json:"renewable,omitempty"`
Type string `json:"type"`
} }

View File

@ -17,8 +17,9 @@ import (
"github.com/hashicorp/errwrap" "github.com/hashicorp/errwrap"
"github.com/hashicorp/go-cleanhttp" "github.com/hashicorp/go-cleanhttp"
retryablehttp "github.com/hashicorp/go-retryablehttp" "github.com/hashicorp/go-retryablehttp"
"github.com/hashicorp/go-rootcerts" "github.com/hashicorp/go-rootcerts"
"github.com/hashicorp/vault/helper/consts"
"github.com/hashicorp/vault/helper/parseutil" "github.com/hashicorp/vault/helper/parseutil"
"golang.org/x/net/http2" "golang.org/x/net/http2"
"golang.org/x/time/rate" "golang.org/x/time/rate"
@ -120,7 +121,7 @@ type TLSConfig struct {
func DefaultConfig() *Config { func DefaultConfig() *Config {
config := &Config{ config := &Config{
Address: "https://127.0.0.1:8200", Address: "https://127.0.0.1:8200",
HttpClient: cleanhttp.DefaultClient(), HttpClient: cleanhttp.DefaultPooledClient(),
} }
config.HttpClient.Timeout = time.Second * 60 config.HttpClient.Timeout = time.Second * 60
@ -464,6 +465,19 @@ func (c *Client) SetMFACreds(creds []string) {
c.mfaCreds = creds c.mfaCreds = creds
} }
// SetNamespace sets the namespace supplied either via the environment
// variable or via the command line.
func (c *Client) SetNamespace(namespace string) {
c.modifyLock.Lock()
defer c.modifyLock.Unlock()
if c.headers == nil {
c.headers = make(http.Header)
}
c.headers.Set(consts.NamespaceHeaderName, namespace)
}
// Token returns the access token being used by this client. It will // Token returns the access token being used by this client. It will
// return the empty string if there is no token set. // return the empty string if there is no token set.
func (c *Client) Token() string { func (c *Client) Token() string {
@ -490,6 +504,26 @@ func (c *Client) ClearToken() {
c.token = "" c.token = ""
} }
// Headers gets the current set of headers used for requests. This returns a
// copy; to modify it make modifications locally and use SetHeaders.
func (c *Client) Headers() http.Header {
c.modifyLock.RLock()
defer c.modifyLock.RUnlock()
if c.headers == nil {
return nil
}
ret := make(http.Header)
for k, v := range c.headers {
for _, val := range v {
ret[k] = append(ret[k], val)
}
}
return ret
}
// SetHeaders sets the headers to be used for future requests. // SetHeaders sets the headers to be used for future requests.
func (c *Client) SetHeaders(headers http.Header) { func (c *Client) SetHeaders(headers http.Header) {
c.modifyLock.Lock() c.modifyLock.Lock()
@ -512,6 +546,10 @@ func (c *Client) SetBackoff(backoff retryablehttp.Backoff) {
// underlying http.Client is used; modifying the client from more than one // underlying http.Client is used; modifying the client from more than one
// goroutine at once may not be safe, so modify the client as needed and then // goroutine at once may not be safe, so modify the client as needed and then
// clone. // clone.
//
// Also, only the client's config is currently copied; this means items not in
// the api.Config struct, such as policy override and wrapping function
// behavior, must currently then be set as desired on the new client.
func (c *Client) Clone() (*Client, error) { func (c *Client) Clone() (*Client, error) {
c.modifyLock.RLock() c.modifyLock.RLock()
c.config.modifyLock.RLock() c.config.modifyLock.RLock()

View File

@ -5,6 +5,7 @@ import (
"context" "context"
"fmt" "fmt"
"io" "io"
"net/url"
"os" "os"
"github.com/hashicorp/errwrap" "github.com/hashicorp/errwrap"
@ -46,8 +47,26 @@ func (c *Client) Logical() *Logical {
} }
func (c *Logical) Read(path string) (*Secret, error) { func (c *Logical) Read(path string) (*Secret, error) {
return c.ReadWithData(path, nil)
}
func (c *Logical) ReadWithData(path string, data map[string][]string) (*Secret, error) {
r := c.c.NewRequest("GET", "/v1/"+path) r := c.c.NewRequest("GET", "/v1/"+path)
var values url.Values
for k, v := range data {
if values == nil {
values = make(url.Values)
}
for _, val := range v {
values.Add(k, val)
}
}
if values != nil {
r.Params = values
}
ctx, cancelFunc := context.WithCancel(context.Background()) ctx, cancelFunc := context.WithCancel(context.Background())
defer cancelFunc() defer cancelFunc()
resp, err := c.c.RawRequestWithContext(ctx, r) resp, err := c.c.RawRequestWithContext(ctx, r)

View File

@ -8,6 +8,8 @@ import (
"net/http" "net/http"
"net/url" "net/url"
"github.com/hashicorp/vault/helper/consts"
retryablehttp "github.com/hashicorp/go-retryablehttp" retryablehttp "github.com/hashicorp/go-retryablehttp"
) )
@ -124,7 +126,7 @@ func (r *Request) toRetryableHTTP() (*retryablehttp.Request, error) {
} }
if len(r.ClientToken) != 0 { if len(r.ClientToken) != 0 {
req.Header.Set("X-Vault-Token", r.ClientToken) req.Header.Set(consts.AuthHeaderName, r.ClientToken)
} }
if len(r.WrapTTL) != 0 { if len(r.WrapTTL) != 0 {

View File

@ -2,6 +2,7 @@ package api
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"github.com/mitchellh/mapstructure" "github.com/mitchellh/mapstructure"
@ -25,17 +26,24 @@ func (c *Sys) AuditHash(path string, input string) (string, error) {
} }
defer resp.Body.Close() defer resp.Body.Close()
type d struct { secret, err := ParseSecret(resp.Body)
Hash string `json:"hash"`
}
var result d
err = resp.DecodeJSON(&result)
if err != nil { if err != nil {
return "", err return "", err
} }
if secret == nil || secret.Data == nil {
return "", errors.New("data from server response is empty")
}
return result.Hash, err hash, ok := secret.Data["hash"]
if !ok {
return "", errors.New("hash not found in response data")
}
hashStr, ok := hash.(string)
if !ok {
return "", errors.New("could not parse hash in response data")
}
return hashStr, nil
} }
func (c *Sys) ListAudit() (map[string]*Audit, error) { func (c *Sys) ListAudit() (map[string]*Audit, error) {
@ -50,30 +58,19 @@ func (c *Sys) ListAudit() (map[string]*Audit, error) {
} }
defer resp.Body.Close() defer resp.Body.Close()
var result map[string]interface{} secret, err := ParseSecret(resp.Body)
err = resp.DecodeJSON(&result)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if secret == nil || secret.Data == nil {
return nil, errors.New("data from server response is empty")
}
mounts := map[string]*Audit{} mounts := map[string]*Audit{}
for k, v := range result { err = mapstructure.Decode(secret.Data, &mounts)
switch v.(type) {
case map[string]interface{}:
default:
continue
}
var res Audit
err = mapstructure.Decode(v, &res)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Not a mount, some other api.Secret data
if res.Type == "" {
continue
}
mounts[k] = &res
}
return mounts, nil return mounts, nil
} }
@ -124,16 +121,16 @@ func (c *Sys) DisableAudit(path string) error {
// documentation. Please refer to that documentation for more details. // documentation. Please refer to that documentation for more details.
type EnableAuditOptions struct { type EnableAuditOptions struct {
Type string `json:"type"` Type string `json:"type" mapstructure:"type"`
Description string `json:"description"` Description string `json:"description" mapstructure:"description"`
Options map[string]string `json:"options"` Options map[string]string `json:"options" mapstructure:"options"`
Local bool `json:"local"` Local bool `json:"local" mapstructure:"local"`
} }
type Audit struct { type Audit struct {
Path string Type string `json:"type" mapstructure:"type"`
Type string Description string `json:"description" mapstructure:"description"`
Description string Options map[string]string `json:"options" mapstructure:"options"`
Options map[string]string Local bool `json:"local" mapstructure:"local"`
Local bool Path string `json:"path" mapstructure:"path"`
} }

View File

@ -2,6 +2,7 @@ package api
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"github.com/mitchellh/mapstructure" "github.com/mitchellh/mapstructure"
@ -18,30 +19,19 @@ func (c *Sys) ListAuth() (map[string]*AuthMount, error) {
} }
defer resp.Body.Close() defer resp.Body.Close()
var result map[string]interface{} secret, err := ParseSecret(resp.Body)
err = resp.DecodeJSON(&result)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if secret == nil || secret.Data == nil {
return nil, errors.New("data from server response is empty")
}
mounts := map[string]*AuthMount{} mounts := map[string]*AuthMount{}
for k, v := range result { err = mapstructure.Decode(secret.Data, &mounts)
switch v.(type) {
case map[string]interface{}:
default:
continue
}
var res AuthMount
err = mapstructure.Decode(v, &res)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Not a mount, some other api.Secret data
if res.Type == "" {
continue
}
mounts[k] = &res
}
return mounts, nil return mounts, nil
} }
@ -83,46 +73,8 @@ func (c *Sys) DisableAuth(path string) error {
return err return err
} }
// Structures for the requests/resposne are all down here. They aren't // Rather than duplicate, we can use modern Go's type aliasing
// individually documented because the map almost directly to the raw HTTP API type EnableAuthOptions = MountInput
// documentation. Please refer to that documentation for more details. type AuthConfigInput = MountConfigInput
type AuthMount = MountOutput
type EnableAuthOptions struct { type AuthConfigOutput = MountConfigOutput
Type string `json:"type"`
Description string `json:"description"`
Config AuthConfigInput `json:"config"`
Local bool `json:"local"`
PluginName string `json:"plugin_name,omitempty"`
SealWrap bool `json:"seal_wrap" mapstructure:"seal_wrap"`
Options map[string]string `json:"options" mapstructure:"options"`
}
type AuthConfigInput struct {
DefaultLeaseTTL string `json:"default_lease_ttl" mapstructure:"default_lease_ttl"`
MaxLeaseTTL string `json:"max_lease_ttl" mapstructure:"max_lease_ttl"`
PluginName string `json:"plugin_name,omitempty" mapstructure:"plugin_name"`
AuditNonHMACRequestKeys []string `json:"audit_non_hmac_request_keys,omitempty" mapstructure:"audit_non_hmac_request_keys"`
AuditNonHMACResponseKeys []string `json:"audit_non_hmac_response_keys,omitempty" mapstructure:"audit_non_hmac_response_keys"`
ListingVisibility string `json:"listing_visibility,omitempty" mapstructure:"listing_visibility"`
PassthroughRequestHeaders []string `json:"passthrough_request_headers,omitempty" mapstructure:"passthrough_request_headers"`
}
type AuthMount struct {
Type string `json:"type" mapstructure:"type"`
Description string `json:"description" mapstructure:"description"`
Accessor string `json:"accessor" mapstructure:"accessor"`
Config AuthConfigOutput `json:"config" mapstructure:"config"`
Local bool `json:"local" mapstructure:"local"`
SealWrap bool `json:"seal_wrap" mapstructure:"seal_wrap"`
Options map[string]string `json:"options" mapstructure:"options"`
}
type AuthConfigOutput struct {
DefaultLeaseTTL int `json:"default_lease_ttl" mapstructure:"default_lease_ttl"`
MaxLeaseTTL int `json:"max_lease_ttl" mapstructure:"max_lease_ttl"`
PluginName string `json:"plugin_name,omitempty" mapstructure:"plugin_name"`
AuditNonHMACRequestKeys []string `json:"audit_non_hmac_request_keys,omitempty" mapstructure:"audit_non_hmac_request_keys"`
AuditNonHMACResponseKeys []string `json:"audit_non_hmac_response_keys,omitempty" mapstructure:"audit_non_hmac_response_keys"`
ListingVisibility string `json:"listing_visibility,omitempty" mapstructure:"listing_visibility"`
PassthroughRequestHeaders []string `json:"passthrough_request_headers,omitempty" mapstructure:"passthrough_request_headers"`
}

View File

@ -2,7 +2,10 @@ package api
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"github.com/mitchellh/mapstructure"
) )
func (c *Sys) CapabilitiesSelf(path string) ([]string, error) { func (c *Sys) CapabilitiesSelf(path string) ([]string, error) {
@ -33,22 +36,29 @@ func (c *Sys) Capabilities(token, path string) ([]string, error) {
} }
defer resp.Body.Close() defer resp.Body.Close()
var result map[string]interface{} secret, err := ParseSecret(resp.Body)
err = resp.DecodeJSON(&result) if err != nil {
return nil, err
}
if secret == nil || secret.Data == nil {
return nil, errors.New("data from server response is empty")
}
var res []string
err = mapstructure.Decode(secret.Data[path], &res)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if result["capabilities"] == nil { if len(res) == 0 {
return nil, nil _, ok := secret.Data["capabilities"]
if ok {
err = mapstructure.Decode(secret.Data["capabilities"], &res)
if err != nil {
return nil, err
} }
var capabilities []string
capabilitiesRaw, ok := result["capabilities"].([]interface{})
if !ok {
return nil, fmt.Errorf("error interpreting returned capabilities")
} }
for _, capability := range capabilitiesRaw {
capabilities = append(capabilities, capability.(string))
} }
return capabilities, nil
return res, nil
} }

View File

@ -1,6 +1,11 @@
package api package api
import "context" import (
"context"
"errors"
"github.com/mitchellh/mapstructure"
)
func (c *Sys) CORSStatus() (*CORSResponse, error) { func (c *Sys) CORSStatus() (*CORSResponse, error) {
r := c.c.NewRequest("GET", "/v1/sys/config/cors") r := c.c.NewRequest("GET", "/v1/sys/config/cors")
@ -13,8 +18,20 @@ func (c *Sys) CORSStatus() (*CORSResponse, error) {
} }
defer resp.Body.Close() defer resp.Body.Close()
secret, err := ParseSecret(resp.Body)
if err != nil {
return nil, err
}
if secret == nil || secret.Data == nil {
return nil, errors.New("data from server response is empty")
}
var result CORSResponse var result CORSResponse
err = resp.DecodeJSON(&result) err = mapstructure.Decode(secret.Data, &result)
if err != nil {
return nil, err
}
return &result, err return &result, err
} }
@ -32,8 +49,20 @@ func (c *Sys) ConfigureCORS(req *CORSRequest) (*CORSResponse, error) {
} }
defer resp.Body.Close() defer resp.Body.Close()
secret, err := ParseSecret(resp.Body)
if err != nil {
return nil, err
}
if secret == nil || secret.Data == nil {
return nil, errors.New("data from server response is empty")
}
var result CORSResponse var result CORSResponse
err = resp.DecodeJSON(&result) err = mapstructure.Decode(secret.Data, &result)
if err != nil {
return nil, err
}
return &result, err return &result, err
} }
@ -48,18 +77,29 @@ func (c *Sys) DisableCORS() (*CORSResponse, error) {
} }
defer resp.Body.Close() defer resp.Body.Close()
var result CORSResponse secret, err := ParseSecret(resp.Body)
err = resp.DecodeJSON(&result) if err != nil {
return &result, err return nil, err
}
if secret == nil || secret.Data == nil {
return nil, errors.New("data from server response is empty")
}
var result CORSResponse
err = mapstructure.Decode(secret.Data, &result)
if err != nil {
return nil, err
}
return &result, err
} }
type CORSRequest struct { type CORSRequest struct {
AllowedOrigins string `json:"allowed_origins"` AllowedOrigins string `json:"allowed_origins" mapstructure:"allowed_origins"`
Enabled bool `json:"enabled"` Enabled bool `json:"enabled" mapstructure:"enabled"`
} }
type CORSResponse struct { type CORSResponse struct {
AllowedOrigins string `json:"allowed_origins"` AllowedOrigins string `json:"allowed_origins" mapstructure:"allowed_origins"`
Enabled bool `json:"enabled"` Enabled bool `json:"enabled" mapstructure:"enabled"`
} }

View File

@ -119,4 +119,6 @@ type GenerateRootStatusResponse struct {
EncodedToken string `json:"encoded_token"` EncodedToken string `json:"encoded_token"`
EncodedRootToken string `json:"encoded_root_token"` EncodedRootToken string `json:"encoded_root_token"`
PGPFingerprint string `json:"pgp_fingerprint"` PGPFingerprint string `json:"pgp_fingerprint"`
OTP string `json:"otp"`
OTPLength int `json:"otp_length"`
} }

View File

@ -11,6 +11,7 @@ func (c *Sys) Health() (*HealthResponse, error) {
r.Params.Add("sealedcode", "299") r.Params.Add("sealedcode", "299")
r.Params.Add("standbycode", "299") r.Params.Add("standbycode", "299")
r.Params.Add("drsecondarycode", "299") r.Params.Add("drsecondarycode", "299")
r.Params.Add("performancestandbycode", "299")
ctx, cancelFunc := context.WithCancel(context.Background()) ctx, cancelFunc := context.WithCancel(context.Background())
defer cancelFunc() defer cancelFunc()
@ -35,4 +36,5 @@ type HealthResponse struct {
Version string `json:"version"` Version string `json:"version"`
ClusterName string `json:"cluster_name,omitempty"` ClusterName string `json:"cluster_name,omitempty"`
ClusterID string `json:"cluster_id,omitempty"` ClusterID string `json:"cluster_id,omitempty"`
LastWAL uint64 `json:"last_wal,omitempty"`
} }

View File

@ -23,4 +23,7 @@ type LeaderResponse struct {
IsSelf bool `json:"is_self"` IsSelf bool `json:"is_self"`
LeaderAddress string `json:"leader_address"` LeaderAddress string `json:"leader_address"`
LeaderClusterAddress string `json:"leader_cluster_address"` LeaderClusterAddress string `json:"leader_cluster_address"`
PerfStandby bool `json:"performance_standby"`
PerfStandbyLastRemoteWAL uint64 `json:"performance_standby_last_remote_wal"`
LastWAL uint64 `json:"last_wal"`
} }

View File

@ -2,6 +2,7 @@ package api
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"github.com/mitchellh/mapstructure" "github.com/mitchellh/mapstructure"
@ -18,30 +19,19 @@ func (c *Sys) ListMounts() (map[string]*MountOutput, error) {
} }
defer resp.Body.Close() defer resp.Body.Close()
var result map[string]interface{} secret, err := ParseSecret(resp.Body)
err = resp.DecodeJSON(&result)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if secret == nil || secret.Data == nil {
return nil, errors.New("data from server response is empty")
}
mounts := map[string]*MountOutput{} mounts := map[string]*MountOutput{}
for k, v := range result { err = mapstructure.Decode(secret.Data, &mounts)
switch v.(type) {
case map[string]interface{}:
default:
continue
}
var res MountOutput
err = mapstructure.Decode(v, &res)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Not a mount, some other api.Secret data
if res.Type == "" {
continue
}
mounts[k] = &res
}
return mounts, nil return mounts, nil
} }
@ -121,8 +111,16 @@ func (c *Sys) MountConfig(path string) (*MountConfigOutput, error) {
} }
defer resp.Body.Close() defer resp.Body.Close()
secret, err := ParseSecret(resp.Body)
if err != nil {
return nil, err
}
if secret == nil || secret.Data == nil {
return nil, errors.New("data from server response is empty")
}
var result MountConfigOutput var result MountConfigOutput
err = resp.DecodeJSON(&result) err = mapstructure.Decode(secret.Data, &result)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -134,10 +132,13 @@ type MountInput struct {
Type string `json:"type"` Type string `json:"type"`
Description string `json:"description"` Description string `json:"description"`
Config MountConfigInput `json:"config"` Config MountConfigInput `json:"config"`
Options map[string]string `json:"options"`
Local bool `json:"local"` Local bool `json:"local"`
PluginName string `json:"plugin_name,omitempty"`
SealWrap bool `json:"seal_wrap" mapstructure:"seal_wrap"` SealWrap bool `json:"seal_wrap" mapstructure:"seal_wrap"`
Options map[string]string `json:"options"`
// Deprecated: Newer server responses should be returning this information in the
// Type field (json: "type") instead.
PluginName string `json:"plugin_name,omitempty"`
} }
type MountConfigInput struct { type MountConfigInput struct {
@ -146,11 +147,14 @@ type MountConfigInput struct {
Description *string `json:"description,omitempty" mapstructure:"description"` Description *string `json:"description,omitempty" mapstructure:"description"`
MaxLeaseTTL string `json:"max_lease_ttl" mapstructure:"max_lease_ttl"` MaxLeaseTTL string `json:"max_lease_ttl" mapstructure:"max_lease_ttl"`
ForceNoCache bool `json:"force_no_cache" mapstructure:"force_no_cache"` ForceNoCache bool `json:"force_no_cache" mapstructure:"force_no_cache"`
PluginName string `json:"plugin_name,omitempty" mapstructure:"plugin_name"`
AuditNonHMACRequestKeys []string `json:"audit_non_hmac_request_keys,omitempty" mapstructure:"audit_non_hmac_request_keys"` AuditNonHMACRequestKeys []string `json:"audit_non_hmac_request_keys,omitempty" mapstructure:"audit_non_hmac_request_keys"`
AuditNonHMACResponseKeys []string `json:"audit_non_hmac_response_keys,omitempty" mapstructure:"audit_non_hmac_response_keys"` AuditNonHMACResponseKeys []string `json:"audit_non_hmac_response_keys,omitempty" mapstructure:"audit_non_hmac_response_keys"`
ListingVisibility string `json:"listing_visibility,omitempty" mapstructure:"listing_visibility"` ListingVisibility string `json:"listing_visibility,omitempty" mapstructure:"listing_visibility"`
PassthroughRequestHeaders []string `json:"passthrough_request_headers,omitempty" mapstructure:"passthrough_request_headers"` PassthroughRequestHeaders []string `json:"passthrough_request_headers,omitempty" mapstructure:"passthrough_request_headers"`
TokenType string `json:"token_type,omitempty" mapstructure:"token_type"`
// Deprecated: This field will always be blank for newer server responses.
PluginName string `json:"plugin_name,omitempty" mapstructure:"plugin_name"`
} }
type MountOutput struct { type MountOutput struct {
@ -167,9 +171,12 @@ type MountConfigOutput struct {
DefaultLeaseTTL int `json:"default_lease_ttl" mapstructure:"default_lease_ttl"` DefaultLeaseTTL int `json:"default_lease_ttl" mapstructure:"default_lease_ttl"`
MaxLeaseTTL int `json:"max_lease_ttl" mapstructure:"max_lease_ttl"` MaxLeaseTTL int `json:"max_lease_ttl" mapstructure:"max_lease_ttl"`
ForceNoCache bool `json:"force_no_cache" mapstructure:"force_no_cache"` ForceNoCache bool `json:"force_no_cache" mapstructure:"force_no_cache"`
PluginName string `json:"plugin_name,omitempty" mapstructure:"plugin_name"`
AuditNonHMACRequestKeys []string `json:"audit_non_hmac_request_keys,omitempty" mapstructure:"audit_non_hmac_request_keys"` AuditNonHMACRequestKeys []string `json:"audit_non_hmac_request_keys,omitempty" mapstructure:"audit_non_hmac_request_keys"`
AuditNonHMACResponseKeys []string `json:"audit_non_hmac_response_keys,omitempty" mapstructure:"audit_non_hmac_response_keys"` AuditNonHMACResponseKeys []string `json:"audit_non_hmac_response_keys,omitempty" mapstructure:"audit_non_hmac_response_keys"`
ListingVisibility string `json:"listing_visibility,omitempty" mapstructure:"listing_visibility"` ListingVisibility string `json:"listing_visibility,omitempty" mapstructure:"listing_visibility"`
PassthroughRequestHeaders []string `json:"passthrough_request_headers,omitempty" mapstructure:"passthrough_request_headers"` PassthroughRequestHeaders []string `json:"passthrough_request_headers,omitempty" mapstructure:"passthrough_request_headers"`
TokenType string `json:"token_type,omitempty" mapstructure:"token_type"`
// Deprecated: This field will always be blank for newer server responses.
PluginName string `json:"plugin_name,omitempty" mapstructure:"plugin_name"`
} }

View File

@ -2,24 +2,46 @@ package api
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"net/http" "net/http"
"github.com/hashicorp/vault/helper/consts"
"github.com/mitchellh/mapstructure"
) )
// ListPluginsInput is used as input to the ListPlugins function. // ListPluginsInput is used as input to the ListPlugins function.
type ListPluginsInput struct{} type ListPluginsInput struct {
// Type of the plugin. Required.
Type consts.PluginType `json:"type"`
}
// ListPluginsResponse is the response from the ListPlugins call. // ListPluginsResponse is the response from the ListPlugins call.
type ListPluginsResponse struct { type ListPluginsResponse struct {
// PluginsByType is the list of plugins by type.
PluginsByType map[consts.PluginType][]string `json:"types"`
// Names is the list of names of the plugins. // Names is the list of names of the plugins.
Names []string //
// Deprecated: Newer server responses should be returning PluginsByType (json:
// "types") instead.
Names []string `json:"names"`
} }
// ListPlugins lists all plugins in the catalog and returns their names as a // ListPlugins lists all plugins in the catalog and returns their names as a
// list of strings. // list of strings.
func (c *Sys) ListPlugins(i *ListPluginsInput) (*ListPluginsResponse, error) { func (c *Sys) ListPlugins(i *ListPluginsInput) (*ListPluginsResponse, error) {
path := "/v1/sys/plugins/catalog" path := ""
req := c.c.NewRequest("LIST", path) method := ""
if i.Type == consts.PluginTypeUnknown {
path = "/v1/sys/plugins/catalog"
method = "GET"
} else {
path = fmt.Sprintf("/v1/sys/plugins/catalog/%s", i.Type)
method = "LIST"
}
req := c.c.NewRequest(method, path)
ctx, cancelFunc := context.WithCancel(context.Background()) ctx, cancelFunc := context.WithCancel(context.Background())
defer cancelFunc() defer cancelFunc()
@ -29,6 +51,23 @@ func (c *Sys) ListPlugins(i *ListPluginsInput) (*ListPluginsResponse, error) {
} }
defer resp.Body.Close() defer resp.Body.Close()
secret, err := ParseSecret(resp.Body)
if err != nil {
return nil, err
}
if secret == nil || secret.Data == nil {
return nil, errors.New("data from server response is empty")
}
if resp.StatusCode == 405 && req.Method == "GET" {
// We received an Unsupported Operation response from Vault, indicating
// Vault of an older version that doesn't support the READ method yet.
req.Method = "LIST"
resp, err := c.c.RawRequestWithContext(ctx, req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
var result struct { var result struct {
Data struct { Data struct {
Keys []string `json:"keys"` Keys []string `json:"keys"`
@ -37,13 +76,51 @@ func (c *Sys) ListPlugins(i *ListPluginsInput) (*ListPluginsResponse, error) {
if err := resp.DecodeJSON(&result); err != nil { if err := resp.DecodeJSON(&result); err != nil {
return nil, err return nil, err
} }
return &ListPluginsResponse{Names: result.Data.Keys}, nil return &ListPluginsResponse{Names: result.Data.Keys}, nil
} }
result := &ListPluginsResponse{
PluginsByType: make(map[consts.PluginType][]string),
}
if i.Type == consts.PluginTypeUnknown {
for pluginTypeStr, pluginsRaw := range secret.Data {
pluginType, err := consts.ParsePluginType(pluginTypeStr)
if err != nil {
return nil, err
}
pluginsIfc, ok := pluginsRaw.([]interface{})
if !ok {
return nil, fmt.Errorf("unable to parse plugins for %q type", pluginTypeStr)
}
plugins := make([]string, len(pluginsIfc))
for i, nameIfc := range pluginsIfc {
name, ok := nameIfc.(string)
if !ok {
}
plugins[i] = name
}
result.PluginsByType[pluginType] = plugins
}
} else {
var respKeys []string
if err := mapstructure.Decode(secret.Data["keys"], &respKeys); err != nil {
return nil, err
}
result.PluginsByType[i.Type] = respKeys
}
return result, nil
}
// GetPluginInput is used as input to the GetPlugin function. // GetPluginInput is used as input to the GetPlugin function.
type GetPluginInput struct { type GetPluginInput struct {
Name string `json:"-"` Name string `json:"-"`
// Type of the plugin. Required.
Type consts.PluginType `json:"type"`
} }
// GetPluginResponse is the response from the GetPlugin call. // GetPluginResponse is the response from the GetPlugin call.
@ -55,8 +132,9 @@ type GetPluginResponse struct {
SHA256 string `json:"sha256"` SHA256 string `json:"sha256"`
} }
// GetPlugin retrieves information about the plugin.
func (c *Sys) GetPlugin(i *GetPluginInput) (*GetPluginResponse, error) { func (c *Sys) GetPlugin(i *GetPluginInput) (*GetPluginResponse, error) {
path := fmt.Sprintf("/v1/sys/plugins/catalog/%s", i.Name) path := catalogPathByType(i.Type, i.Name)
req := c.c.NewRequest(http.MethodGet, path) req := c.c.NewRequest(http.MethodGet, path)
ctx, cancelFunc := context.WithCancel(context.Background()) ctx, cancelFunc := context.WithCancel(context.Background())
@ -68,13 +146,13 @@ func (c *Sys) GetPlugin(i *GetPluginInput) (*GetPluginResponse, error) {
defer resp.Body.Close() defer resp.Body.Close()
var result struct { var result struct {
Data GetPluginResponse Data *GetPluginResponse
} }
err = resp.DecodeJSON(&result) err = resp.DecodeJSON(&result)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &result.Data, err return result.Data, err
} }
// RegisterPluginInput is used as input to the RegisterPlugin function. // RegisterPluginInput is used as input to the RegisterPlugin function.
@ -82,6 +160,9 @@ type RegisterPluginInput struct {
// Name is the name of the plugin. Required. // Name is the name of the plugin. Required.
Name string `json:"-"` Name string `json:"-"`
// Type of the plugin. Required.
Type consts.PluginType `json:"type"`
// Args is the list of args to spawn the process with. // Args is the list of args to spawn the process with.
Args []string `json:"args,omitempty"` Args []string `json:"args,omitempty"`
@ -94,8 +175,9 @@ type RegisterPluginInput struct {
// RegisterPlugin registers the plugin with the given information. // RegisterPlugin registers the plugin with the given information.
func (c *Sys) RegisterPlugin(i *RegisterPluginInput) error { func (c *Sys) RegisterPlugin(i *RegisterPluginInput) error {
path := fmt.Sprintf("/v1/sys/plugins/catalog/%s", i.Name) path := catalogPathByType(i.Type, i.Name)
req := c.c.NewRequest(http.MethodPut, path) req := c.c.NewRequest(http.MethodPut, path)
if err := req.SetJSONBody(i); err != nil { if err := req.SetJSONBody(i); err != nil {
return err return err
} }
@ -113,12 +195,15 @@ func (c *Sys) RegisterPlugin(i *RegisterPluginInput) error {
type DeregisterPluginInput struct { type DeregisterPluginInput struct {
// Name is the name of the plugin. Required. // Name is the name of the plugin. Required.
Name string `json:"-"` Name string `json:"-"`
// Type of the plugin. Required.
Type consts.PluginType `json:"type"`
} }
// DeregisterPlugin removes the plugin with the given name from the plugin // DeregisterPlugin removes the plugin with the given name from the plugin
// catalog. // catalog.
func (c *Sys) DeregisterPlugin(i *DeregisterPluginInput) error { func (c *Sys) DeregisterPlugin(i *DeregisterPluginInput) error {
path := fmt.Sprintf("/v1/sys/plugins/catalog/%s", i.Name) path := catalogPathByType(i.Type, i.Name)
req := c.c.NewRequest(http.MethodDelete, path) req := c.c.NewRequest(http.MethodDelete, path)
ctx, cancelFunc := context.WithCancel(context.Background()) ctx, cancelFunc := context.WithCancel(context.Background())
@ -129,3 +214,15 @@ func (c *Sys) DeregisterPlugin(i *DeregisterPluginInput) error {
} }
return err return err
} }
// catalogPathByType is a helper to construct the proper API path by plugin type
func catalogPathByType(pluginType consts.PluginType, name string) string {
path := fmt.Sprintf("/v1/sys/plugins/catalog/%s/%s", pluginType, name)
// Backwards compat, if type is not provided then use old path
if pluginType == consts.PluginTypeUnknown {
path = fmt.Sprintf("/v1/sys/plugins/catalog/%s", name)
}
return path
}

View File

@ -2,11 +2,14 @@ package api
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"github.com/mitchellh/mapstructure"
) )
func (c *Sys) ListPolicies() ([]string, error) { func (c *Sys) ListPolicies() ([]string, error) {
r := c.c.NewRequest("GET", "/v1/sys/policy") r := c.c.NewRequest("LIST", "/v1/sys/policies/acl")
ctx, cancelFunc := context.WithCancel(context.Background()) ctx, cancelFunc := context.WithCancel(context.Background())
defer cancelFunc() defer cancelFunc()
@ -16,29 +19,25 @@ func (c *Sys) ListPolicies() ([]string, error) {
} }
defer resp.Body.Close() defer resp.Body.Close()
var result map[string]interface{} secret, err := ParseSecret(resp.Body)
err = resp.DecodeJSON(&result) if err != nil {
return nil, err
}
if secret == nil || secret.Data == nil {
return nil, errors.New("data from server response is empty")
}
var result []string
err = mapstructure.Decode(secret.Data["keys"], &result)
if err != nil { if err != nil {
return nil, err return nil, err
} }
var ok bool return result, err
if _, ok = result["policies"]; !ok {
return nil, fmt.Errorf("policies not found in response")
}
listRaw := result["policies"].([]interface{})
var policies []string
for _, val := range listRaw {
policies = append(policies, val.(string))
}
return policies, err
} }
func (c *Sys) GetPolicy(name string) (string, error) { func (c *Sys) GetPolicy(name string) (string, error) {
r := c.c.NewRequest("GET", fmt.Sprintf("/v1/sys/policy/%s", name)) r := c.c.NewRequest("GET", fmt.Sprintf("/v1/sys/policies/acl/%s", name))
ctx, cancelFunc := context.WithCancel(context.Background()) ctx, cancelFunc := context.WithCancel(context.Background())
defer cancelFunc() defer cancelFunc()
@ -53,16 +52,15 @@ func (c *Sys) GetPolicy(name string) (string, error) {
return "", err return "", err
} }
var result map[string]interface{} secret, err := ParseSecret(resp.Body)
err = resp.DecodeJSON(&result)
if err != nil { if err != nil {
return "", err return "", err
} }
if secret == nil || secret.Data == nil {
if rulesRaw, ok := result["rules"]; ok { return "", errors.New("data from server response is empty")
return rulesRaw.(string), nil
} }
if policyRaw, ok := result["policy"]; ok {
if policyRaw, ok := secret.Data["policy"]; ok {
return policyRaw.(string), nil return policyRaw.(string), nil
} }
@ -71,10 +69,10 @@ func (c *Sys) GetPolicy(name string) (string, error) {
func (c *Sys) PutPolicy(name, rules string) error { func (c *Sys) PutPolicy(name, rules string) error {
body := map[string]string{ body := map[string]string{
"rules": rules, "policy": rules,
} }
r := c.c.NewRequest("PUT", fmt.Sprintf("/v1/sys/policy/%s", name)) r := c.c.NewRequest("PUT", fmt.Sprintf("/v1/sys/policies/acl/%s", name))
if err := r.SetJSONBody(body); err != nil { if err := r.SetJSONBody(body); err != nil {
return err return err
} }
@ -91,7 +89,7 @@ func (c *Sys) PutPolicy(name, rules string) error {
} }
func (c *Sys) DeletePolicy(name string) error { func (c *Sys) DeletePolicy(name string) error {
r := c.c.NewRequest("DELETE", fmt.Sprintf("/v1/sys/policy/%s", name)) r := c.c.NewRequest("DELETE", fmt.Sprintf("/v1/sys/policies/acl/%s", name))
ctx, cancelFunc := context.WithCancel(context.Background()) ctx, cancelFunc := context.WithCancel(context.Background())
defer cancelFunc() defer cancelFunc()

View File

@ -1,6 +1,11 @@
package api package api
import "context" import (
"context"
"errors"
"github.com/mitchellh/mapstructure"
)
func (c *Sys) RekeyStatus() (*RekeyStatusResponse, error) { func (c *Sys) RekeyStatus() (*RekeyStatusResponse, error) {
r := c.c.NewRequest("GET", "/v1/sys/rekey/init") r := c.c.NewRequest("GET", "/v1/sys/rekey/init")
@ -211,8 +216,20 @@ func (c *Sys) RekeyRetrieveBackup() (*RekeyRetrieveResponse, error) {
} }
defer resp.Body.Close() defer resp.Body.Close()
secret, err := ParseSecret(resp.Body)
if err != nil {
return nil, err
}
if secret == nil || secret.Data == nil {
return nil, errors.New("data from server response is empty")
}
var result RekeyRetrieveResponse var result RekeyRetrieveResponse
err = resp.DecodeJSON(&result) err = mapstructure.Decode(secret.Data, &result)
if err != nil {
return nil, err
}
return &result, err return &result, err
} }
@ -227,8 +244,20 @@ func (c *Sys) RekeyRetrieveRecoveryBackup() (*RekeyRetrieveResponse, error) {
} }
defer resp.Body.Close() defer resp.Body.Close()
secret, err := ParseSecret(resp.Body)
if err != nil {
return nil, err
}
if secret == nil || secret.Data == nil {
return nil, errors.New("data from server response is empty")
}
var result RekeyRetrieveResponse var result RekeyRetrieveResponse
err = resp.DecodeJSON(&result) err = mapstructure.Decode(secret.Data, &result)
if err != nil {
return nil, err
}
return &result, err return &result, err
} }
@ -340,9 +369,9 @@ type RekeyUpdateResponse struct {
} }
type RekeyRetrieveResponse struct { type RekeyRetrieveResponse struct {
Nonce string `json:"nonce"` Nonce string `json:"nonce" mapstructure:"nonce"`
Keys map[string][]string `json:"keys"` Keys map[string][]string `json:"keys" mapstructure:"keys"`
KeysB64 map[string][]string `json:"keys_base64"` KeysB64 map[string][]string `json:"keys_base64" mapstructure:"keys_base64"`
} }
type RekeyVerificationStatusResponse struct { type RekeyVerificationStatusResponse struct {

View File

@ -2,6 +2,8 @@ package api
import ( import (
"context" "context"
"encoding/json"
"errors"
"time" "time"
) )
@ -28,9 +30,45 @@ func (c *Sys) KeyStatus() (*KeyStatus, error) {
} }
defer resp.Body.Close() defer resp.Body.Close()
result := new(KeyStatus) secret, err := ParseSecret(resp.Body)
err = resp.DecodeJSON(result) if err != nil {
return result, err return nil, err
}
if secret == nil || secret.Data == nil {
return nil, errors.New("data from server response is empty")
}
var result KeyStatus
termRaw, ok := secret.Data["term"]
if !ok {
return nil, errors.New("term not found in response")
}
term, ok := termRaw.(json.Number)
if !ok {
return nil, errors.New("could not convert term to a number")
}
term64, err := term.Int64()
if err != nil {
return nil, err
}
result.Term = int(term64)
installTimeRaw, ok := secret.Data["install_time"]
if !ok {
return nil, errors.New("install_time not found in response")
}
installTimeStr, ok := installTimeRaw.(string)
if !ok {
return nil, errors.New("could not convert install_time to a string")
}
installTime, err := time.Parse(time.RFC3339Nano, installTimeStr)
if err != nil {
return nil, err
}
result.InstallTime = installTime
return &result, err
} }
type KeyStatus struct { type KeyStatus struct {

View File

@ -41,6 +41,15 @@ func (c *Sys) Unseal(shard string) (*SealStatusResponse, error) {
return sealStatusRequest(c, r) return sealStatusRequest(c, r)
} }
func (c *Sys) UnsealWithOptions(opts *UnsealOpts) (*SealStatusResponse, error) {
r := c.c.NewRequest("PUT", "/v1/sys/unseal")
if err := r.SetJSONBody(opts); err != nil {
return nil, err
}
return sealStatusRequest(c, r)
}
func sealStatusRequest(c *Sys, r *Request) (*SealStatusResponse, error) { func sealStatusRequest(c *Sys, r *Request) (*SealStatusResponse, error) {
ctx, cancelFunc := context.WithCancel(context.Background()) ctx, cancelFunc := context.WithCancel(context.Background())
defer cancelFunc() defer cancelFunc()
@ -57,13 +66,21 @@ func sealStatusRequest(c *Sys, r *Request) (*SealStatusResponse, error) {
type SealStatusResponse struct { type SealStatusResponse struct {
Type string `json:"type"` Type string `json:"type"`
Initialized bool `json:"initialized"`
Sealed bool `json:"sealed"` Sealed bool `json:"sealed"`
T int `json:"t"` T int `json:"t"`
N int `json:"n"` N int `json:"n"`
Progress int `json:"progress"` Progress int `json:"progress"`
Nonce string `json:"nonce"` Nonce string `json:"nonce"`
Version string `json:"version"` Version string `json:"version"`
Migration bool `json:"migration"`
ClusterName string `json:"cluster_name,omitempty"` ClusterName string `json:"cluster_name,omitempty"`
ClusterID string `json:"cluster_id,omitempty"` ClusterID string `json:"cluster_id,omitempty"`
RecoverySeal bool `json:"recovery_seal"` RecoverySeal bool `json:"recovery_seal"`
} }
type UnsealOpts struct {
Key string `json:"key"`
Reset bool `json:"reset"`
Migrate bool `json:"migrate"`
}

View File

@ -8,8 +8,8 @@ func (c *Sys) StepDown() error {
ctx, cancelFunc := context.WithCancel(context.Background()) ctx, cancelFunc := context.WithCancel(context.Background())
defer cancelFunc() defer cancelFunc()
resp, err := c.c.RawRequestWithContext(ctx, r) resp, err := c.c.RawRequestWithContext(ctx, r)
if err == nil { if resp != nil && resp.Body != nil {
defer resp.Body.Close() resp.Body.Close()
} }
return err return err
} }

View File

@ -0,0 +1,14 @@
package consts
const (
// ExpirationRestoreWorkerCount specifies the number of workers to use while
// restoring leases into the expiration manager
ExpirationRestoreWorkerCount = 64
// NamespaceHeaderName is the header set to specify which namespace the
// request is indented for.
NamespaceHeaderName = "X-Vault-Namespace"
// AuthHeaderName is the name of the header containing the token.
AuthHeaderName = "X-Vault-Token"
)

View File

@ -0,0 +1,16 @@
package consts
import "errors"
var (
// ErrSealed is returned if an operation is performed on a sealed barrier.
// No operation is expected to succeed before unsealing
ErrSealed = errors.New("Vault is sealed")
// ErrStandby is returned if an operation is performed on a standby Vault.
// No operation is expected to succeed until active.
ErrStandby = errors.New("Vault is in standby mode")
// Used when .. is used in a path
ErrPathContainsParentReferences = errors.New("path cannot contain parent references")
)

View File

@ -0,0 +1,59 @@
package consts
import "fmt"
var PluginTypes = []PluginType{
PluginTypeUnknown,
PluginTypeCredential,
PluginTypeDatabase,
PluginTypeSecrets,
}
type PluginType uint32
// This is a list of PluginTypes used by Vault.
// If we need to add any in the future, it would
// be best to add them to the _end_ of the list below
// because they resolve to incrementing numbers,
// which may be saved in state somewhere. Thus if
// the name for one of those numbers changed because
// a value were added to the middle, that could cause
// the wrong plugin types to be read from storage
// for a given underlying number. Example of the problem
// here: https://play.golang.org/p/YAaPw5ww3er
const (
PluginTypeUnknown PluginType = iota
PluginTypeCredential
PluginTypeDatabase
PluginTypeSecrets
)
func (p PluginType) String() string {
switch p {
case PluginTypeUnknown:
return "unknown"
case PluginTypeCredential:
return "auth"
case PluginTypeDatabase:
return "database"
case PluginTypeSecrets:
return "secret"
default:
return "unsupported"
}
}
func ParsePluginType(pluginType string) (PluginType, error) {
switch pluginType {
case "unknown":
return PluginTypeUnknown, nil
case "auth":
return PluginTypeCredential, nil
case "database":
return PluginTypeDatabase, nil
case "secret":
return PluginTypeSecrets, nil
default:
return PluginTypeUnknown, fmt.Errorf("%q is not a supported plugin type", pluginType)
}
}

View File

@ -0,0 +1,87 @@
package consts
import "time"
type ReplicationState uint32
var ReplicationStaleReadTimeout = 2 * time.Second
const (
_ ReplicationState = iota
OldReplicationPrimary
OldReplicationSecondary
OldReplicationBootstrapping
// Don't add anything here. Adding anything to this Old block would cause
// the rest of the values to change below. This was done originally to
// ensure no overlap between old and new values.
ReplicationUnknown ReplicationState = 0
ReplicationPerformancePrimary ReplicationState = 1 << iota
ReplicationPerformanceSecondary
OldSplitReplicationBootstrapping
ReplicationDRPrimary
ReplicationDRSecondary
ReplicationPerformanceBootstrapping
ReplicationDRBootstrapping
ReplicationPerformanceDisabled
ReplicationDRDisabled
ReplicationPerformanceStandby
)
func (r ReplicationState) string() string {
switch r {
case ReplicationPerformanceSecondary:
return "secondary"
case ReplicationPerformancePrimary:
return "primary"
case ReplicationPerformanceBootstrapping:
return "bootstrapping"
case ReplicationPerformanceDisabled:
return "disabled"
case ReplicationDRPrimary:
return "primary"
case ReplicationDRSecondary:
return "secondary"
case ReplicationDRBootstrapping:
return "bootstrapping"
case ReplicationDRDisabled:
return "disabled"
}
return "unknown"
}
func (r ReplicationState) GetDRString() string {
switch {
case r.HasState(ReplicationDRBootstrapping):
return ReplicationDRBootstrapping.string()
case r.HasState(ReplicationDRPrimary):
return ReplicationDRPrimary.string()
case r.HasState(ReplicationDRSecondary):
return ReplicationDRSecondary.string()
case r.HasState(ReplicationDRDisabled):
return ReplicationDRDisabled.string()
default:
return "unknown"
}
}
func (r ReplicationState) GetPerformanceString() string {
switch {
case r.HasState(ReplicationPerformanceBootstrapping):
return ReplicationPerformanceBootstrapping.string()
case r.HasState(ReplicationPerformancePrimary):
return ReplicationPerformancePrimary.string()
case r.HasState(ReplicationPerformanceSecondary):
return ReplicationPerformanceSecondary.string()
case r.HasState(ReplicationPerformanceDisabled):
return ReplicationPerformanceDisabled.string()
default:
return "unknown"
}
}
func (r ReplicationState) HasState(flag ReplicationState) bool { return r&flag != 0 }
func (r *ReplicationState) AddState(flag ReplicationState) { *r |= flag }
func (r *ReplicationState) ClearState(flag ReplicationState) { *r &= ^flag }
func (r *ReplicationState) ToggleState(flag ReplicationState) { *r ^= flag }

12
vendor/vendor.json vendored
View File

@ -438,10 +438,10 @@
"revisionTime": "2016-01-19T13:13:26-08:00" "revisionTime": "2016-01-19T13:13:26-08:00"
}, },
{ {
"checksumSHA1": "nBmnbC438E5CNF1kRCnivCnqsEM=", "checksumSHA1": "fmBqxy85Q0iaiJ144S8+nYUn0rs=",
"path": "github.com/hashicorp/vault/api", "path": "github.com/hashicorp/vault/api",
"revision": "f348177b5d55dbd38d039b814bf34cbc9cfc093b", "revision": "d4367e581fe117356c4d4ac9a8c8c4e514457449",
"revisionTime": "2018-07-27T12:33:20Z" "revisionTime": "2018-11-21T18:10:53Z"
}, },
{ {
"checksumSHA1": "bSdPFOHaTwEvM4PIvn0PZfn75jM=", "checksumSHA1": "bSdPFOHaTwEvM4PIvn0PZfn75jM=",
@ -449,6 +449,12 @@
"revision": "f348177b5d55dbd38d039b814bf34cbc9cfc093b", "revision": "f348177b5d55dbd38d039b814bf34cbc9cfc093b",
"revisionTime": "2018-07-27T12:33:20Z" "revisionTime": "2018-07-27T12:33:20Z"
}, },
{
"checksumSHA1": "MQKcgExxbVHltqyTjVbDuHlkfLw=",
"path": "github.com/hashicorp/vault/helper/consts",
"revision": "d4367e581fe117356c4d4ac9a8c8c4e514457449",
"revisionTime": "2018-11-21T18:10:53Z"
},
{ {
"checksumSHA1": "RlqPBLOexQ0jj6jomhiompWKaUg=", "checksumSHA1": "RlqPBLOexQ0jj6jomhiompWKaUg=",
"path": "github.com/hashicorp/vault/helper/hclutil", "path": "github.com/hashicorp/vault/helper/hclutil",