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 }