gopkg.in/goose.v2@v2.0.1/testservices/swiftservice/service.go (about)

     1  // Swift double testing service - internal direct API implementation
     2  
     3  package swiftservice
     4  
     5  import (
     6  	"fmt"
     7  	"net/url"
     8  	"sort"
     9  	"strings"
    10  	"sync"
    11  	"time"
    12  
    13  	"gopkg.in/goose.v2/swift"
    14  	"gopkg.in/goose.v2/testservices"
    15  	"gopkg.in/goose.v2/testservices/identityservice"
    16  )
    17  
    18  type object map[string][]byte
    19  
    20  var _ testservices.HttpService = (*Swift)(nil)
    21  var _ identityservice.ServiceProvider = (*Swift)(nil)
    22  
    23  type Swift struct {
    24  	testservices.ServiceInstance
    25  
    26  	mu         sync.Mutex // protects the remaining fields
    27  	containers map[string]object
    28  }
    29  
    30  // New creates an instance of the Swift object, given the parameters.
    31  func New(hostURL, versionPath, tenantId, region string, identityService, fallbackIdentity identityservice.IdentityService) *Swift {
    32  	URL, err := url.Parse(hostURL)
    33  	if err != nil {
    34  		panic(err)
    35  	}
    36  	hostname := URL.Host
    37  	if !strings.HasSuffix(hostname, "/") {
    38  		hostname += "/"
    39  	}
    40  	swift := &Swift{
    41  		containers: make(map[string]object),
    42  		ServiceInstance: testservices.ServiceInstance{
    43  			IdentityService:         identityService,
    44  			FallbackIdentityService: fallbackIdentity,
    45  			Scheme:                  URL.Scheme,
    46  			Hostname:                hostname,
    47  			VersionPath:             versionPath,
    48  			TenantId:                tenantId,
    49  			Region:                  region,
    50  		},
    51  	}
    52  	if identityService != nil {
    53  		identityService.RegisterServiceProvider("swift", "object-store", swift)
    54  	}
    55  	return swift
    56  }
    57  
    58  func Stop() {
    59  	// noop
    60  }
    61  
    62  func (s *Swift) endpointURL(path string) string {
    63  	// Note: We're using the Openstack Style endpoints for this testservice
    64  	// 	Openstack object-storage endpoint url:
    65  	// 	http://<ip addr>:<port>/swift/v1
    66  	// 	Rackspace object-storage endpoint url:
    67  	// 	https://<rackspace addr>/v1/MossoCloudFS_<tenant id>
    68  	ep := s.Scheme + "://" + s.Hostname + "swift/" + s.VersionPath
    69  	if path != "" {
    70  		ep += "/" + strings.TrimLeft(path, "/")
    71  	}
    72  	return ep
    73  }
    74  
    75  func (s *Swift) Endpoints() []identityservice.Endpoint {
    76  	ep := identityservice.Endpoint{
    77  		AdminURL:    s.endpointURL(""),
    78  		InternalURL: s.endpointURL(""),
    79  		PublicURL:   s.endpointURL(""),
    80  		Region:      s.Region,
    81  	}
    82  	return []identityservice.Endpoint{ep}
    83  }
    84  
    85  func (s *Swift) V3Endpoints() []identityservice.V3Endpoint {
    86  	url := s.endpointURL("")
    87  	return identityservice.NewV3Endpoints(url, url, url, s.RegionID)
    88  }
    89  
    90  // HasContainer verifies the given container exists or not.
    91  func (s *Swift) HasContainer(name string) bool {
    92  	s.mu.Lock()
    93  	_, ok := s.containers[name]
    94  	s.mu.Unlock()
    95  	return ok
    96  }
    97  
    98  // GetObject retrieves a given object from its container, returning
    99  // the object data or an error.
   100  func (s *Swift) GetObject(container, name string) ([]byte, error) {
   101  	if err := s.ProcessFunctionHook(s, container, name); err != nil {
   102  		return nil, err
   103  	}
   104  	s.mu.Lock()
   105  	data, ok := s.containers[container][name]
   106  	s.mu.Unlock()
   107  	if !ok {
   108  		return nil, fmt.Errorf("no such object %q in container %q", name, container)
   109  	}
   110  	return data, nil
   111  }
   112  
   113  // AddContainer creates a new container with the given name, if it
   114  // does not exist. Otherwise an error is returned.
   115  func (s *Swift) AddContainer(name string) error {
   116  	if err := s.ProcessFunctionHook(s, name); err != nil {
   117  		return err
   118  	}
   119  	if s.HasContainer(name) {
   120  		return fmt.Errorf("container already exists %q", name)
   121  	}
   122  	s.mu.Lock()
   123  	s.containers[name] = make(object)
   124  	s.mu.Unlock()
   125  	return nil
   126  }
   127  
   128  // ListContainer lists the objects in the given container.
   129  // params contains filtering attributes: prefix, delimiter, marker.
   130  // Only prefix is currently supported.
   131  func (s *Swift) ListContainer(name string, params map[string]string) ([]swift.ContainerContents, error) {
   132  	if err := s.ProcessFunctionHook(s, name); err != nil {
   133  		return nil, err
   134  	}
   135  	if ok := s.HasContainer(name); !ok {
   136  		return nil, fmt.Errorf("no such container %q", name)
   137  	}
   138  	s.mu.Lock()
   139  	items := s.containers[name]
   140  	s.mu.Unlock()
   141  	sorted := make([]string, 0, len(items))
   142  	prefix := params["prefix"]
   143  	for filename := range items {
   144  		if prefix != "" && !strings.HasPrefix(filename, prefix) {
   145  			continue
   146  		}
   147  		sorted = append(sorted, filename)
   148  	}
   149  	sort.Strings(sorted)
   150  	contents := make([]swift.ContainerContents, len(sorted))
   151  	var i = 0
   152  	for _, filename := range sorted {
   153  		contents[i] = swift.ContainerContents{
   154  			Name:         filename,
   155  			Hash:         "", // not implemented
   156  			LengthBytes:  len(items[filename]),
   157  			ContentType:  "application/octet-stream",
   158  			LastModified: time.Now().Format("2006-01-02 15:04:05"), //not implemented
   159  		}
   160  		i++
   161  	}
   162  	return contents, nil
   163  }
   164  
   165  // AddObject creates a new object with the given name in the specified
   166  // container, setting the object's data. It's an error if the object
   167  // already exists. If the container does not exist, it will be
   168  // created.
   169  func (s *Swift) AddObject(container, name string, data []byte) error {
   170  	if err := s.ProcessFunctionHook(s, container, name); err != nil {
   171  		return err
   172  	}
   173  	if _, err := s.GetObject(container, name); err == nil {
   174  		return fmt.Errorf(
   175  			"object %q in container %q already exists",
   176  			name,
   177  			container)
   178  	}
   179  	if ok := s.HasContainer(container); !ok {
   180  		if err := s.AddContainer(container); err != nil {
   181  			return err
   182  		}
   183  	}
   184  	s.mu.Lock()
   185  	s.containers[container][name] = data
   186  	s.mu.Unlock()
   187  	return nil
   188  }
   189  
   190  // RemoveContainer deletes an existing container with the given name.
   191  func (s *Swift) RemoveContainer(name string) error {
   192  	if err := s.ProcessFunctionHook(s, name); err != nil {
   193  		return err
   194  	}
   195  	if ok := s.HasContainer(name); !ok {
   196  		return fmt.Errorf("no such container %q", name)
   197  	}
   198  	s.mu.Lock()
   199  	delete(s.containers, name)
   200  	s.mu.Unlock()
   201  	return nil
   202  }
   203  
   204  // RemoveObject deletes an existing object in a given container.
   205  func (s *Swift) RemoveObject(container, name string) error {
   206  	if err := s.ProcessFunctionHook(s, container, name); err != nil {
   207  		return err
   208  	}
   209  	if _, err := s.GetObject(container, name); err != nil {
   210  		return err
   211  	}
   212  	s.mu.Lock()
   213  	delete(s.containers[container], name)
   214  	s.mu.Unlock()
   215  	return nil
   216  }
   217  
   218  // GetURL returns the full URL, which can be used to GET the
   219  // object. An error occurs if the object does not exist.
   220  func (s *Swift) GetURL(container, object string) (string, error) {
   221  	if err := s.ProcessFunctionHook(s, container, object); err != nil {
   222  		return "", err
   223  	}
   224  	if _, err := s.GetObject(container, object); err != nil {
   225  		return "", err
   226  	}
   227  	return s.endpointURL(fmt.Sprintf("%s/%s", container, object)), nil
   228  }