Skip to content

Commit

Permalink
Rename bucket with a container (#53)
Browse files Browse the repository at this point in the history
Rename term bucket to container (#53)

authored by @kayrus
  • Loading branch information
kayrus authored Mar 1, 2023
1 parent 0b6a261 commit 7cef0c3
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 81 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ jobs:
# From tag 1.25.0
CINDER_CSI_CHART_VERSION: 2.2.0
DOCKER_IMAGE_NAME: velero-plugin-for-openstack
SWIFT_BUCKET_NAME: my-swift-bucket
SWIFT_CONTAINER_NAME: my-swift-container
TESTS_DIRECTORY: tests/actions/integration-tests
steps:
- name: Checkout velero-plugin-for-openstack
Expand Down Expand Up @@ -84,11 +84,11 @@ jobs:
SERVICE_TIMEOUT=200
disable_all_services
enable_service key rabbit mysql s-proxy s-object s-container s-account c-bak c-api c-vol c-sch n-api n-crt n-cpu n-cond n-sch n-api-meta n-sproxy placement-api placement-client
- name: Prepare Swift bucket for velero backups
- name: Prepare Swift container for velero backups
run: |
source "${{ github.workspace }}/devstack/openrc"
SWIFT_TMP_URL_KEY=$(dd if=/dev/urandom | tr -dc A-Za-z0-9 | head -c 40)
swift post "${SWIFT_BUCKET_NAME}"
swift post "${SWIFT_CONTAINER_NAME}"
swift post -m "Temp-URL-Key:${SWIFT_TMP_URL_KEY}"
- name: Install Velero helm chart
run: |
Expand Down
16 changes: 8 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ Initialize velero plugin:
velero install \
--provider "community.openstack.org/openstack" \
--plugins lirt/velero-plugin-for-openstack:v0.4.1 \
--bucket <BUCKET_NAME> \
--bucket <SWIFT_CONTAINER_NAME> \
--no-secret

# Or add plugin to existing velero:
Expand All @@ -199,15 +199,15 @@ Change configuration of `backupstoragelocations.velero.io`:
```yaml
spec:
objectStorage:
bucket: <BUCKET_NAME>
bucket: <CONTAINER_NAME>
provider: community.openstack.org/openstack
# # Optional config
# config:
# cloud: cloud1
# region: fra
# # If you want to enable restic you need to set resticRepoPrefix to this value:
# # resticRepoPrefix: swift:<BUCKET_NAME>:/<PATH>
# resticRepoPrefix: swift:my-awesome-bucket:/restic # Example
# # resticRepoPrefix: swift:<CONTAINER_NAME>:/<PATH>
# resticRepoPrefix: swift:my-awesome-container:/restic # Example
```

Change configuration of `volumesnapshotlocations.velero.io`:
Expand Down Expand Up @@ -236,15 +236,15 @@ credentials:
configuration:
provider: community.openstack.org/openstack
backupStorageLocation:
bucket: my-swift-bucket
bucket: my-swift-container
# caCert: <CERT_CONTENTS_IN_BASE64>
# # Optional config
# config:
# cloud: cloud1
# region: fra
# # If you want to enable restic you need to set resticRepoPrefix to this value:
# # resticRepoPrefix: swift:<BUCKET_NAME>:/<PATH>
# resticRepoPrefix: swift:my-awesome-bucket:/restic # Example
# # resticRepoPrefix: swift:<CONTAINER_NAME>:/<PATH>
# resticRepoPrefix: swift:my-awesome-container:/restic # Example
initContainers:
- name: velero-plugin-openstack
image: lirt/velero-plugin-for-openstack:v0.4.1
Expand Down Expand Up @@ -290,7 +290,7 @@ Volume backups with Velero can also be done using [Restic](https://velero.io/doc

There is a common similarity that `restic` can use Openstack Swift as object storage for backups. Restic way of authentication and implementation is however very different from this repository and it means that some ways of authentication that work here will not work with restic. Please refer to [official restic documentation](https://restic.readthedocs.io/en/latest/030_preparing_a_new_repo.html#openstack-swift) to understand how are you supposed to configure authentication variables with restic.

Recommended way of using this plugin with restic is to use authentication with environment variables and only for 1 cloud and 1 BackupStorageLocation. In the BSL you need to configure `config.resticRepoPrefix: swift:<BUCKET_NAME>:/<PATH>` - for example `config.resticRepoPrefix: swift:my-awesome-bucket:/restic`.
Recommended way of using this plugin with restic is to use authentication with environment variables and only for 1 cloud and 1 BackupStorageLocation. In the BSL you need to configure `config.resticRepoPrefix: swift:<CONTAINER_NAME>:/<PATH>` - for example `config.resticRepoPrefix: swift:my-awesome-container:/restic`.

## Known Issues

Expand Down
97 changes: 46 additions & 51 deletions src/swift/object_store.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"io"
"net/http"
"os"
"regexp"
"time"

"github.com/Lirt/velero-plugin-swift/src/utils"
Expand Down Expand Up @@ -61,72 +60,68 @@ func (o *ObjectStore) Init(config map[string]string) error {
return nil
}

// GetObject returns body of Swift object defined by bucket name and key
func (o *ObjectStore) GetObject(bucket, key string) (io.ReadCloser, error) {
// GetObject returns body of Swift object defined by container name and object
func (o *ObjectStore) GetObject(container, object string) (io.ReadCloser, error) {
log := o.log.WithFields(logrus.Fields{
"bucket": bucket,
"key": key,
"container": container,
"object": object,
})
log.Infof("GetObject")

object := objects.Download(o.client, bucket, key, nil)
if object.Err != nil {
return nil, fmt.Errorf("failed to download contents of key %v from bucket %v: %v", key, bucket, object.Err)
res := objects.Download(o.client, container, object, nil)
if res.Err != nil {
return nil, fmt.Errorf("failed to download contents of %q object from %q container: %v", object, container, res.Err)
}

return object.Body, nil
return res.Body, nil
}

// PutObject uploads new object into bucket
func (o *ObjectStore) PutObject(bucket string, key string, body io.Reader) error {
// PutObject uploads new object into container
func (o *ObjectStore) PutObject(container string, object string, body io.Reader) error {
log := o.log.WithFields(logrus.Fields{
"bucket": bucket,
"key": key,
"container": container,
"object": object,
})
log.Infof("PutObject")

createOpts := objects.CreateOpts{
Content: body,
}

if _, err := objects.Create(o.client, bucket, key, createOpts).Extract(); err != nil {
return fmt.Errorf("failed to create new object in bucket %v with key %v: %v", bucket, key, err)
if _, err := objects.Create(o.client, container, object, createOpts).Extract(); err != nil {
return fmt.Errorf("failed to create new %q object in %q container: %v", object, container, err)
}

return nil
}

// ObjectExists does Get operation and validates result or error to find out if object exists
func (o *ObjectStore) ObjectExists(bucket, key string) (bool, error) {
func (o *ObjectStore) ObjectExists(container, object string) (bool, error) {
log := o.log.WithFields(logrus.Fields{
"bucket": bucket,
"key": key,
"container": container,
"object": object,
})
log.Infof("ObjectExists")

result := objects.Get(o.client, bucket, key, nil)
res := objects.Get(o.client, container, object, nil)

if result.Err != nil {
errResourceNotFound, err := regexp.MatchString("Resource not found", result.Err.Error())
if err != nil {
return false, fmt.Errorf("regexp matching failed: %v", err)
}
if errResourceNotFound {
log.Infof("Key %v in bucket %v doesn't exist yet.", key, bucket)
if res.Err != nil {
if _, ok := res.Err.(gophercloud.ErrDefault404); ok {
log.Infof("%q object doesn't yet exist in %q container.", object, container)
return false, nil
}
return false, fmt.Errorf("cannot Get key %v in bucket %v: %v", key, bucket, result.Err)
return false, fmt.Errorf("cannot Get %q object from %q container: %v", object, container, res.Err)
}

return true, nil
}

// ListCommonPrefixes returns list of objects in bucket, that match specified prefix
func (o *ObjectStore) ListCommonPrefixes(bucket, prefix, delimiter string) ([]string, error) {
// ListCommonPrefixes returns list of objects in container, that match specified prefix
func (o *ObjectStore) ListCommonPrefixes(container, prefix, delimiter string) ([]string, error) {
log := o.log.WithFields(logrus.Fields{
"bucket": bucket,
"delimiter": delimiter,
"container": container,
"prefix": prefix,
"delimiter": delimiter,
})
log.Infof("ListCommonPrefixes")

Expand All @@ -136,14 +131,14 @@ func (o *ObjectStore) ListCommonPrefixes(bucket, prefix, delimiter string) ([]st
Full: true,
}

allPages, err := objects.List(o.client, bucket, opts).AllPages()
allPages, err := objects.List(o.client, container, opts).AllPages()
if err != nil {
return nil, fmt.Errorf("failed to list objects in bucket %v: %v", bucket, err)
return nil, fmt.Errorf("failed to list objects in %q container: %v", container, err)
}

allObjects, err := objects.ExtractInfo(allPages)
if err != nil {
return nil, fmt.Errorf("failed to extract info from objects in bucket %v: %v", bucket, err)
return nil, fmt.Errorf("failed to extract objects info from %q container: %v", container, err)
}

var objNames []string
Expand All @@ -155,51 +150,51 @@ func (o *ObjectStore) ListCommonPrefixes(bucket, prefix, delimiter string) ([]st
}

// ListObjects lists objects with prefix in all containers
func (o *ObjectStore) ListObjects(bucket, prefix string) ([]string, error) {
func (o *ObjectStore) ListObjects(container, prefix string) ([]string, error) {
log := o.log.WithFields(logrus.Fields{
"bucket": bucket,
"prefix": prefix,
"container": container,
"prefix": prefix,
})
log.Infof("ListObjects")

objects, err := o.ListCommonPrefixes(bucket, prefix, "/")
objects, err := o.ListCommonPrefixes(container, prefix, "/")
if err != nil {
return nil, fmt.Errorf("failed to list objects in bucket %v with prefix %v: %v", bucket, prefix, err)
return nil, fmt.Errorf("failed to list objects in %q container with %q prefix: %v", container, prefix, err)
}

return objects, nil
}

// DeleteObject deletes object specified by key from bucket
func (o *ObjectStore) DeleteObject(bucket, key string) error {
// DeleteObject deletes object specified by object from container
func (o *ObjectStore) DeleteObject(container, object string) error {
log := o.log.WithFields(logrus.Fields{
"bucket": bucket,
"key": key,
"container": container,
"object": object,
})
log.Infof("DeleteObject")

_, err := objects.Delete(o.client, bucket, key, nil).Extract()
_, err := objects.Delete(o.client, container, object, nil).Extract()
if err != nil {
return fmt.Errorf("failed to delete object with key %v from bucket %v: %v", key, bucket, err)
return fmt.Errorf("failed to delete %q object from %q container: %v", object, container, err)
}

return nil
}

// CreateSignedURL creates temporary URL for key in bucket
func (o *ObjectStore) CreateSignedURL(bucket, key string, ttl time.Duration) (string, error) {
// CreateSignedURL creates temporary URL for object in container
func (o *ObjectStore) CreateSignedURL(container, object string, ttl time.Duration) (string, error) {
log := o.log.WithFields(logrus.Fields{
"bucket": bucket,
"key": key,
"container": container,
"object": object,
})
log.Infof("CreateSignedURL")

url, err := objects.CreateTempURL(o.client, bucket, key, objects.CreateTempURLOpts{
url, err := objects.CreateTempURL(o.client, container, object, objects.CreateTempURLOpts{
Method: http.MethodGet,
TTL: int(ttl.Seconds()),
})
if err != nil {
return "", fmt.Errorf("failed to create temporary URL for bucket %v with key %v: %v", bucket, key, err)
return "", fmt.Errorf("failed to create temporary URL for %q object in %q container: %v", object, container, err)
}

return url, nil
Expand Down
36 changes: 18 additions & 18 deletions src/swift/object_store_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ import (
"github.com/stretchr/testify/assert"
)

func handleGetObject(t *testing.T, bucket, key string, data []byte) {
th.Mux.HandleFunc(fmt.Sprintf("/%s/%s", bucket, key),
func handleGetObject(t *testing.T, container, object string, data []byte) {
th.Mux.HandleFunc(fmt.Sprintf("/%s/%s", container, object),
func(w http.ResponseWriter, r *http.Request) {
th.TestMethod(t, r, http.MethodGet)
th.TestHeader(t, r, "X-Auth-Token", fakeClient.TokenID)
Expand All @@ -30,8 +30,8 @@ func handleGetObject(t *testing.T, bucket, key string, data []byte) {
})
}

func handlePutObject(t *testing.T, bucket, key string, data []byte) {
th.Mux.HandleFunc(fmt.Sprintf("/%s/%s", bucket, key),
func handlePutObject(t *testing.T, container, object string, data []byte) {
th.Mux.HandleFunc(fmt.Sprintf("/%s/%s", container, object),
func(w http.ResponseWriter, r *http.Request) {
th.TestMethod(t, r, http.MethodPut)
th.TestHeader(t, r, "X-Auth-Token", fakeClient.TokenID)
Expand All @@ -46,8 +46,8 @@ func handlePutObject(t *testing.T, bucket, key string, data []byte) {
})
}

func handleObjectExists(t *testing.T, bucket, key string) {
th.Mux.HandleFunc(fmt.Sprintf("/%s/%s", bucket, key),
func handleObjectExists(t *testing.T, container, object string) {
th.Mux.HandleFunc(fmt.Sprintf("/%s/%s", container, object),
func(w http.ResponseWriter, r *http.Request) {
th.TestMethod(t, r, http.MethodHead)
th.TestHeader(t, r, "X-Auth-Token", fakeClient.TokenID)
Expand All @@ -61,33 +61,33 @@ func TestPutObject(t *testing.T) {
th.SetupHTTP()
defer th.TeardownHTTP()

bucket := "testBucket"
key := "testKey"
container := "testContainer"
object := "testKey"
content := "All code is guilty until proven innocent"
handlePutObject(t, bucket, key, []byte(content))
handlePutObject(t, container, object, []byte(content))

store := ObjectStore{
client: fakeClient.ServiceClient(),
log: logrus.New(),
}
err := store.PutObject(bucket, key, strings.NewReader(content))
err := store.PutObject(container, object, strings.NewReader(content))
assert.Nil(t, err)
}

func TestGetObject(t *testing.T) {
th.SetupHTTP()
defer th.TeardownHTTP()

bucket := "testBucket"
key := "testKey"
container := "testContainer"
object := "testKey"
content := "All code is guilty until proven innocent"
handleGetObject(t, bucket, key, []byte(content))
handleGetObject(t, container, object, []byte(content))

store := ObjectStore{
client: fakeClient.ServiceClient(),
log: logrus.New(),
}
readCloser, err := store.GetObject(bucket, key)
readCloser, err := store.GetObject(container, object)

if !assert.Nil(t, err) {
t.FailNow()
Expand All @@ -99,16 +99,16 @@ func TestObjectExists(t *testing.T) {
th.SetupHTTP()
defer th.TeardownHTTP()

bucket := "testBucket"
key := "testKey"
handleObjectExists(t, bucket, key)
container := "testContainer"
object := "testKey"
handleObjectExists(t, container, object)

store := ObjectStore{
client: fakeClient.ServiceClient(),
log: logrus.New(),
}

_, err := store.ObjectExists(bucket, key)
_, err := store.ObjectExists(container, object)

if !assert.Nil(t, err) {
t.FailNow()
Expand Down
2 changes: 1 addition & 1 deletion tests/actions/integration-tests/velero-helm-values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ configuration:
provider: community.openstack.org/openstack
backupStorageLocation:
name: swift
bucket: my-swift-bucket
bucket: my-swift-container
volumeSnapshotLocation:
name: cinder
initContainers:
Expand Down

0 comments on commit 7cef0c3

Please sign in to comment.