mirror of
				https://github.com/optim-enterprises-bv/vault.git
				synced 2025-10-31 18:48:08 +00:00 
			
		
		
		
	 8d83c5d047
			
		
	
	8d83c5d047
	
	
	
		
			
			* sdk/physical: use permitpool from go-secure-stdlib * physical: use permitpool from go-secure-stdlib * fixup! sdk/physical: use permitpool from go-secure-stdlib * fixup! sdk/physical: use permitpool from go-secure-stdlib
		
			
				
	
	
		
			258 lines
		
	
	
		
			6.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			258 lines
		
	
	
		
			6.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright (c) HashiCorp, Inc.
 | |
| // SPDX-License-Identifier: BUSL-1.1
 | |
| 
 | |
| package swift
 | |
| 
 | |
| import (
 | |
| 	"context"
 | |
| 	"fmt"
 | |
| 	"os"
 | |
| 	"sort"
 | |
| 	"strconv"
 | |
| 	"strings"
 | |
| 	"time"
 | |
| 
 | |
| 	metrics "github.com/armon/go-metrics"
 | |
| 	cleanhttp "github.com/hashicorp/go-cleanhttp"
 | |
| 	log "github.com/hashicorp/go-hclog"
 | |
| 	"github.com/hashicorp/go-secure-stdlib/permitpool"
 | |
| 	"github.com/hashicorp/go-secure-stdlib/strutil"
 | |
| 	"github.com/hashicorp/vault/sdk/physical"
 | |
| 	"github.com/ncw/swift"
 | |
| )
 | |
| 
 | |
| // Verify SwiftBackend satisfies the correct interfaces
 | |
| var _ physical.Backend = (*SwiftBackend)(nil)
 | |
| 
 | |
| // SwiftBackend is a physical backend that stores data
 | |
| // within an OpenStack Swift container.
 | |
| type SwiftBackend struct {
 | |
| 	container  string
 | |
| 	client     *swift.Connection
 | |
| 	logger     log.Logger
 | |
| 	permitPool *permitpool.Pool
 | |
| }
 | |
| 
 | |
| // NewSwiftBackend constructs a Swift backend using a pre-existing
 | |
| // container. Credentials can be provided to the backend, sourced
 | |
| // from the environment.
 | |
| func NewSwiftBackend(conf map[string]string, logger log.Logger) (physical.Backend, error) {
 | |
| 	var ok bool
 | |
| 
 | |
| 	username := os.Getenv("OS_USERNAME")
 | |
| 	if username == "" {
 | |
| 		username = conf["username"]
 | |
| 		if username == "" {
 | |
| 			return nil, fmt.Errorf("missing username")
 | |
| 		}
 | |
| 	}
 | |
| 	password := os.Getenv("OS_PASSWORD")
 | |
| 	if password == "" {
 | |
| 		password = conf["password"]
 | |
| 		if password == "" {
 | |
| 			return nil, fmt.Errorf("missing password")
 | |
| 		}
 | |
| 	}
 | |
| 	authUrl := os.Getenv("OS_AUTH_URL")
 | |
| 	if authUrl == "" {
 | |
| 		authUrl = conf["auth_url"]
 | |
| 		if authUrl == "" {
 | |
| 			return nil, fmt.Errorf("missing auth_url")
 | |
| 		}
 | |
| 	}
 | |
| 	container := os.Getenv("OS_CONTAINER")
 | |
| 	if container == "" {
 | |
| 		container = conf["container"]
 | |
| 		if container == "" {
 | |
| 			return nil, fmt.Errorf("missing container")
 | |
| 		}
 | |
| 	}
 | |
| 	project := os.Getenv("OS_PROJECT_NAME")
 | |
| 	if project == "" {
 | |
| 		if project, ok = conf["project"]; !ok {
 | |
| 			// Check for KeyStone naming prior to V3
 | |
| 			project = os.Getenv("OS_TENANT_NAME")
 | |
| 			if project == "" {
 | |
| 				project = conf["tenant"]
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	domain := os.Getenv("OS_USER_DOMAIN_NAME")
 | |
| 	if domain == "" {
 | |
| 		domain = conf["domain"]
 | |
| 	}
 | |
| 	projectDomain := os.Getenv("OS_PROJECT_DOMAIN_NAME")
 | |
| 	if projectDomain == "" {
 | |
| 		projectDomain = conf["project-domain"]
 | |
| 	}
 | |
| 
 | |
| 	region := os.Getenv("OS_REGION_NAME")
 | |
| 	if region == "" {
 | |
| 		region = conf["region"]
 | |
| 	}
 | |
| 	tenantID := os.Getenv("OS_TENANT_ID")
 | |
| 	if tenantID == "" {
 | |
| 		tenantID = conf["tenant_id"]
 | |
| 	}
 | |
| 	trustID := os.Getenv("OS_TRUST_ID")
 | |
| 	if trustID == "" {
 | |
| 		trustID = conf["trust_id"]
 | |
| 	}
 | |
| 	storageUrl := os.Getenv("OS_STORAGE_URL")
 | |
| 	if storageUrl == "" {
 | |
| 		storageUrl = conf["storage_url"]
 | |
| 	}
 | |
| 	authToken := os.Getenv("OS_AUTH_TOKEN")
 | |
| 	if authToken == "" {
 | |
| 		authToken = conf["auth_token"]
 | |
| 	}
 | |
| 
 | |
| 	c := swift.Connection{
 | |
| 		Domain:       domain,
 | |
| 		UserName:     username,
 | |
| 		ApiKey:       password,
 | |
| 		AuthUrl:      authUrl,
 | |
| 		Tenant:       project,
 | |
| 		TenantDomain: projectDomain,
 | |
| 		Region:       region,
 | |
| 		TenantId:     tenantID,
 | |
| 		TrustId:      trustID,
 | |
| 		StorageUrl:   storageUrl,
 | |
| 		AuthToken:    authToken,
 | |
| 		Transport:    cleanhttp.DefaultPooledTransport(),
 | |
| 	}
 | |
| 
 | |
| 	err := c.Authenticate()
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	_, _, err = c.Container(container)
 | |
| 	if err != nil {
 | |
| 		return nil, fmt.Errorf("Unable to access container %q: %w", container, err)
 | |
| 	}
 | |
| 
 | |
| 	maxParStr, ok := conf["max_parallel"]
 | |
| 	var maxParInt int
 | |
| 	if ok {
 | |
| 		maxParInt, err = strconv.Atoi(maxParStr)
 | |
| 		if err != nil {
 | |
| 			return nil, fmt.Errorf("failed parsing max_parallel parameter: %w", err)
 | |
| 		}
 | |
| 		if logger.IsDebug() {
 | |
| 			logger.Debug("max_parallel set", "max_parallel", maxParInt)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	s := &SwiftBackend{
 | |
| 		client:     &c,
 | |
| 		container:  container,
 | |
| 		logger:     logger,
 | |
| 		permitPool: permitpool.New(maxParInt),
 | |
| 	}
 | |
| 	return s, nil
 | |
| }
 | |
| 
 | |
| // Put is used to insert or update an entry
 | |
| func (s *SwiftBackend) Put(ctx context.Context, entry *physical.Entry) error {
 | |
| 	defer metrics.MeasureSince([]string{"swift", "put"}, time.Now())
 | |
| 
 | |
| 	if err := s.permitPool.Acquire(ctx); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	defer s.permitPool.Release()
 | |
| 
 | |
| 	err := s.client.ObjectPutBytes(s.container, entry.Key, entry.Value, "")
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // Get is used to fetch an entry
 | |
| func (s *SwiftBackend) Get(ctx context.Context, key string) (*physical.Entry, error) {
 | |
| 	defer metrics.MeasureSince([]string{"swift", "get"}, time.Now())
 | |
| 
 | |
| 	if err := s.permitPool.Acquire(ctx); err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	defer s.permitPool.Release()
 | |
| 
 | |
| 	// Do a list of names with the key first since eventual consistency means
 | |
| 	// it might be deleted, but a node might return a read of bytes which fails
 | |
| 	// the physical test
 | |
| 	list, err := s.client.ObjectNames(s.container, &swift.ObjectsOpts{Prefix: key})
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	if 0 == len(list) {
 | |
| 		return nil, nil
 | |
| 	}
 | |
| 	data, err := s.client.ObjectGetBytes(s.container, key)
 | |
| 	if err == swift.ObjectNotFound {
 | |
| 		return nil, nil
 | |
| 	}
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	ent := &physical.Entry{
 | |
| 		Key:   key,
 | |
| 		Value: data,
 | |
| 	}
 | |
| 
 | |
| 	return ent, nil
 | |
| }
 | |
| 
 | |
| // Delete is used to permanently delete an entry
 | |
| func (s *SwiftBackend) Delete(ctx context.Context, key string) error {
 | |
| 	defer metrics.MeasureSince([]string{"swift", "delete"}, time.Now())
 | |
| 
 | |
| 	if err := s.permitPool.Acquire(ctx); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	defer s.permitPool.Release()
 | |
| 
 | |
| 	err := s.client.ObjectDelete(s.container, key)
 | |
| 
 | |
| 	if err != nil && err != swift.ObjectNotFound {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // List is used to list all the keys under a given
 | |
| // prefix, up to the next prefix.
 | |
| func (s *SwiftBackend) List(ctx context.Context, prefix string) ([]string, error) {
 | |
| 	defer metrics.MeasureSince([]string{"swift", "list"}, time.Now())
 | |
| 
 | |
| 	if err := s.permitPool.Acquire(ctx); err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	defer s.permitPool.Release()
 | |
| 
 | |
| 	list, err := s.client.ObjectNamesAll(s.container, &swift.ObjectsOpts{Prefix: prefix})
 | |
| 	if nil != err {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	keys := []string{}
 | |
| 	for _, key := range list {
 | |
| 		key := strings.TrimPrefix(key, prefix)
 | |
| 
 | |
| 		if i := strings.Index(key, "/"); i == -1 {
 | |
| 			// Add objects only from the current 'folder'
 | |
| 			keys = append(keys, key)
 | |
| 		} else if i != -1 {
 | |
| 			// Add truncated 'folder' paths
 | |
| 			keys = strutil.AppendIfMissing(keys, key[:i+1])
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	sort.Strings(keys)
 | |
| 
 | |
| 	return keys, nil
 | |
| }
 |