github.com/leeclow-ops/gophercloud@v1.2.1/acceptance/openstack/objectstorage/v1/objects_test.go (about) 1 //go:build acceptance 2 // +build acceptance 3 4 package v1 5 6 import ( 7 "bytes" 8 "io/ioutil" 9 "net/http" 10 "strings" 11 "testing" 12 13 "github.com/leeclow-ops/gophercloud/acceptance/clients" 14 "github.com/leeclow-ops/gophercloud/acceptance/tools" 15 "github.com/leeclow-ops/gophercloud/openstack/objectstorage/v1/containers" 16 "github.com/leeclow-ops/gophercloud/openstack/objectstorage/v1/objects" 17 th "github.com/leeclow-ops/gophercloud/testhelper" 18 ) 19 20 // numObjects is the number of objects to create for testing. 21 var numObjects = 2 22 23 func TestObjects(t *testing.T) { 24 client, err := clients.NewObjectStorageV1Client() 25 if err != nil { 26 t.Fatalf("Unable to create client: %v", err) 27 } 28 29 // Make a slice of length numObjects to hold the random object names. 30 oNames := make([]string, numObjects) 31 for i := 0; i < len(oNames); i++ { 32 oNames[i] = tools.RandomString("test-object-", 8) 33 } 34 35 // Create a container to hold the test objects. 36 cName := tools.RandomString("test-container-", 8) 37 header, err := containers.Create(client, cName, nil).Extract() 38 th.AssertNoErr(t, err) 39 t.Logf("Create object headers: %+v\n", header) 40 41 // Defer deletion of the container until after testing. 42 defer func() { 43 res := containers.Delete(client, cName) 44 th.AssertNoErr(t, res.Err) 45 }() 46 47 // Create a slice of buffers to hold the test object content. 48 oContents := make([]*bytes.Buffer, numObjects) 49 for i := 0; i < numObjects; i++ { 50 oContents[i] = bytes.NewBuffer([]byte(tools.RandomString("", 10))) 51 createOpts := objects.CreateOpts{ 52 Content: oContents[i], 53 } 54 res := objects.Create(client, cName, oNames[i], createOpts) 55 th.AssertNoErr(t, res.Err) 56 } 57 // Delete the objects after testing. 58 defer func() { 59 for i := 0; i < numObjects; i++ { 60 res := objects.Delete(client, cName, oNames[i], nil) 61 th.AssertNoErr(t, res.Err) 62 } 63 }() 64 65 // List all created objects 66 listOpts := objects.ListOpts{ 67 Full: true, 68 Prefix: "test-object-", 69 } 70 71 allPages, err := objects.List(client, cName, listOpts).AllPages() 72 if err != nil { 73 t.Fatalf("Unable to list objects: %v", err) 74 } 75 76 ons, err := objects.ExtractNames(allPages) 77 if err != nil { 78 t.Fatalf("Unable to extract objects: %v", err) 79 } 80 th.AssertEquals(t, len(ons), len(oNames)) 81 82 ois, err := objects.ExtractInfo(allPages) 83 if err != nil { 84 t.Fatalf("Unable to extract object info: %v", err) 85 } 86 th.AssertEquals(t, len(ois), len(oNames)) 87 88 // Create temporary URL, download its contents and compare with what was originally created. 89 // Downloading the URL validates it (this cannot be done in unit tests). 90 objURLs := make([]string, numObjects) 91 for i := 0; i < numObjects; i++ { 92 objURLs[i], err = objects.CreateTempURL(client, cName, oNames[i], objects.CreateTempURLOpts{ 93 Method: http.MethodGet, 94 TTL: 180, 95 }) 96 th.AssertNoErr(t, err) 97 98 resp, err := http.Get(objURLs[i]) 99 th.AssertNoErr(t, err) 100 101 body, err := ioutil.ReadAll(resp.Body) 102 th.AssertNoErr(t, err) 103 th.AssertDeepEquals(t, oContents[i].Bytes(), body) 104 resp.Body.Close() 105 } 106 107 // Copy the contents of one object to another. 108 copyOpts := objects.CopyOpts{ 109 Destination: cName + "/" + oNames[1], 110 } 111 copyres := objects.Copy(client, cName, oNames[0], copyOpts) 112 th.AssertNoErr(t, copyres.Err) 113 114 // Download one of the objects that was created above. 115 downloadres := objects.Download(client, cName, oNames[0], nil) 116 th.AssertNoErr(t, downloadres.Err) 117 118 o1Content, err := downloadres.ExtractContent() 119 th.AssertNoErr(t, err) 120 121 // Download the another object that was create above. 122 downloadOpts := objects.DownloadOpts{ 123 Newest: true, 124 } 125 downloadres = objects.Download(client, cName, oNames[1], downloadOpts) 126 th.AssertNoErr(t, downloadres.Err) 127 o2Content, err := downloadres.ExtractContent() 128 th.AssertNoErr(t, err) 129 130 // Compare the two object's contents to test that the copy worked. 131 th.AssertEquals(t, string(o2Content), string(o1Content)) 132 133 // Update an object's metadata. 134 metadata := map[string]string{ 135 "Gophercloud-Test": "objects", 136 } 137 138 disposition := "inline" 139 cType := "text/plain" 140 updateOpts := &objects.UpdateOpts{ 141 Metadata: metadata, 142 ContentDisposition: &disposition, 143 ContentType: &cType, 144 } 145 updateres := objects.Update(client, cName, oNames[0], updateOpts) 146 th.AssertNoErr(t, updateres.Err) 147 148 // Delete the object's metadata after testing. 149 defer func() { 150 temp := make([]string, len(metadata)) 151 i := 0 152 for k := range metadata { 153 temp[i] = k 154 i++ 155 } 156 empty := "" 157 cType := "application/octet-stream" 158 iTrue := true 159 updateOpts = &objects.UpdateOpts{ 160 RemoveMetadata: temp, 161 ContentDisposition: &empty, 162 ContentType: &cType, 163 DetectContentType: &iTrue, 164 } 165 res := objects.Update(client, cName, oNames[0], updateOpts) 166 th.AssertNoErr(t, res.Err) 167 168 // Retrieve an object's metadata. 169 getOpts := objects.GetOpts{ 170 Newest: true, 171 } 172 resp := objects.Get(client, cName, oNames[0], getOpts) 173 om, err := resp.ExtractMetadata() 174 th.AssertNoErr(t, err) 175 if len(om) > 0 { 176 t.Errorf("Expected custom metadata to be empty, found: %v", metadata) 177 } 178 object, err := resp.Extract() 179 th.AssertNoErr(t, err) 180 th.AssertEquals(t, empty, object.ContentDisposition) 181 th.AssertEquals(t, cType, object.ContentType) 182 }() 183 184 // Retrieve an object's metadata. 185 getOpts := objects.GetOpts{ 186 Newest: true, 187 } 188 resp := objects.Get(client, cName, oNames[0], getOpts) 189 om, err := resp.ExtractMetadata() 190 th.AssertNoErr(t, err) 191 for k := range metadata { 192 if om[k] != metadata[strings.Title(k)] { 193 t.Errorf("Expected custom metadata with key: %s", k) 194 return 195 } 196 } 197 198 object, err := resp.Extract() 199 th.AssertNoErr(t, err) 200 th.AssertEquals(t, disposition, object.ContentDisposition) 201 th.AssertEquals(t, cType, object.ContentType) 202 } 203 204 func TestObjectsListSubdir(t *testing.T) { 205 client, err := clients.NewObjectStorageV1Client() 206 if err != nil { 207 t.Fatalf("Unable to create client: %v", err) 208 } 209 210 // Create a random subdirectory name. 211 cSubdir1 := tools.RandomString("test-subdir-", 8) 212 cSubdir2 := tools.RandomString("test-subdir-", 8) 213 214 // Make a slice of length numObjects to hold the random object names. 215 oNames1 := make([]string, numObjects) 216 for i := 0; i < len(oNames1); i++ { 217 oNames1[i] = cSubdir1 + "/" + tools.RandomString("test-object-", 8) 218 } 219 220 oNames2 := make([]string, numObjects) 221 for i := 0; i < len(oNames2); i++ { 222 oNames2[i] = cSubdir2 + "/" + tools.RandomString("test-object-", 8) 223 } 224 225 // Create a container to hold the test objects. 226 cName := tools.RandomString("test-container-", 8) 227 _, err = containers.Create(client, cName, nil).Extract() 228 th.AssertNoErr(t, err) 229 230 // Defer deletion of the container until after testing. 231 defer func() { 232 t.Logf("Deleting container %s", cName) 233 res := containers.Delete(client, cName) 234 th.AssertNoErr(t, res.Err) 235 }() 236 237 // Create a slice of buffers to hold the test object content. 238 oContents1 := make([]*bytes.Buffer, numObjects) 239 for i := 0; i < numObjects; i++ { 240 oContents1[i] = bytes.NewBuffer([]byte(tools.RandomString("", 10))) 241 createOpts := objects.CreateOpts{ 242 Content: oContents1[i], 243 } 244 res := objects.Create(client, cName, oNames1[i], createOpts) 245 th.AssertNoErr(t, res.Err) 246 } 247 // Delete the objects after testing. 248 defer func() { 249 for i := 0; i < numObjects; i++ { 250 t.Logf("Deleting object %s", oNames1[i]) 251 res := objects.Delete(client, cName, oNames1[i], nil) 252 th.AssertNoErr(t, res.Err) 253 } 254 }() 255 256 oContents2 := make([]*bytes.Buffer, numObjects) 257 for i := 0; i < numObjects; i++ { 258 oContents2[i] = bytes.NewBuffer([]byte(tools.RandomString("", 10))) 259 createOpts := objects.CreateOpts{ 260 Content: oContents2[i], 261 } 262 res := objects.Create(client, cName, oNames2[i], createOpts) 263 th.AssertNoErr(t, res.Err) 264 } 265 // Delete the objects after testing. 266 defer func() { 267 for i := 0; i < numObjects; i++ { 268 t.Logf("Deleting object %s", oNames2[i]) 269 res := objects.Delete(client, cName, oNames2[i], nil) 270 th.AssertNoErr(t, res.Err) 271 } 272 }() 273 274 listOpts := objects.ListOpts{ 275 Full: true, 276 Delimiter: "/", 277 } 278 279 allPages, err := objects.List(client, cName, listOpts).AllPages() 280 if err != nil { 281 t.Fatal(err) 282 } 283 284 allObjects, err := objects.ExtractNames(allPages) 285 if err != nil { 286 t.Fatal(err) 287 } 288 289 t.Logf("%#v\n", allObjects) 290 expected := []string{cSubdir1, cSubdir2} 291 for _, e := range expected { 292 var valid bool 293 for _, a := range allObjects { 294 if e+"/" == a { 295 valid = true 296 } 297 } 298 if !valid { 299 t.Fatalf("could not find %s in results", e) 300 } 301 } 302 303 listOpts = objects.ListOpts{ 304 Full: true, 305 Delimiter: "/", 306 Prefix: cSubdir2, 307 } 308 309 allPages, err = objects.List(client, cName, listOpts).AllPages() 310 if err != nil { 311 t.Fatal(err) 312 } 313 314 allObjects, err = objects.ExtractNames(allPages) 315 if err != nil { 316 t.Fatal(err) 317 } 318 319 th.AssertEquals(t, allObjects[0], cSubdir2+"/") 320 t.Logf("%#v\n", allObjects) 321 } 322 323 func TestObjectsBulkDelete(t *testing.T) { 324 client, err := clients.NewObjectStorageV1Client() 325 if err != nil { 326 t.Fatalf("Unable to create client: %v", err) 327 } 328 329 // Create a random subdirectory name. 330 cSubdir1 := tools.RandomString("test-subdir-", 8) 331 cSubdir2 := tools.RandomString("test-subdir-", 8) 332 333 // Make a slice of length numObjects to hold the random object names. 334 oNames1 := make([]string, numObjects) 335 for i := 0; i < len(oNames1); i++ { 336 oNames1[i] = cSubdir1 + "/" + tools.RandomString("test-object-", 8) 337 } 338 339 oNames2 := make([]string, numObjects) 340 for i := 0; i < len(oNames2); i++ { 341 oNames2[i] = cSubdir2 + "/" + tools.RandomString("test-object-", 8) 342 } 343 344 // Create a container to hold the test objects. 345 cName := tools.RandomString("test-container-", 8) 346 _, err = containers.Create(client, cName, nil).Extract() 347 th.AssertNoErr(t, err) 348 349 // Defer deletion of the container until after testing. 350 defer func() { 351 t.Logf("Deleting container %s", cName) 352 res := containers.Delete(client, cName) 353 th.AssertNoErr(t, res.Err) 354 }() 355 356 // Create a slice of buffers to hold the test object content. 357 oContents1 := make([]*bytes.Buffer, numObjects) 358 for i := 0; i < numObjects; i++ { 359 oContents1[i] = bytes.NewBuffer([]byte(tools.RandomString("", 10))) 360 createOpts := objects.CreateOpts{ 361 Content: oContents1[i], 362 } 363 res := objects.Create(client, cName, oNames1[i], createOpts) 364 th.AssertNoErr(t, res.Err) 365 } 366 367 oContents2 := make([]*bytes.Buffer, numObjects) 368 for i := 0; i < numObjects; i++ { 369 oContents2[i] = bytes.NewBuffer([]byte(tools.RandomString("", 10))) 370 createOpts := objects.CreateOpts{ 371 Content: oContents2[i], 372 } 373 res := objects.Create(client, cName, oNames2[i], createOpts) 374 th.AssertNoErr(t, res.Err) 375 } 376 377 // Delete the objects after testing. 378 expectedResp := objects.BulkDeleteResponse{ 379 ResponseStatus: "200 OK", 380 Errors: [][]string{}, 381 NumberDeleted: numObjects * 2, 382 } 383 384 resp, err := objects.BulkDelete(client, cName, append(oNames1, oNames2...)).Extract() 385 th.AssertNoErr(t, err) 386 th.AssertDeepEquals(t, *resp, expectedResp) 387 388 // Verify deletion 389 listOpts := objects.ListOpts{ 390 Full: true, 391 Delimiter: "/", 392 } 393 394 allPages, err := objects.List(client, cName, listOpts).AllPages() 395 if err != nil { 396 t.Fatal(err) 397 } 398 399 allObjects, err := objects.ExtractNames(allPages) 400 if err != nil { 401 t.Fatal(err) 402 } 403 404 th.AssertEquals(t, len(allObjects), 0) 405 }