From c1a3d57681e1f69b8e324464b9238f33a4520fae Mon Sep 17 00:00:00 2001 From: Artem Lifshits <55093318+artem-lifshits@users.noreply.github.com> Date: Mon, 3 Jun 2024 15:53:17 +0200 Subject: [PATCH] implement bucket inventories (#675) [OBS]: implement bucket inventories What this PR does / why we need it Inventories feature implementation Acceptance tests === RUN TestOBSInventories tools.go:72: { "StatusCode": 200, "request_id": "0000018FDE20DE304C22D49E48CD208F", "ResponseHeaders": { "content-length": [ "494" ], "content-type": [ "application/xml" ], "date": [ "Mon, 03 Jun 2024 12:44:07 GMT" ], "id-2": [ "32AAAQAAEAABAAAQAAEAABAAAQAAEAABCS14+tvg5whfOPpV/OOi905j8EqMMCi8" ], "request-id": [ "0000018FDE20DE304C22D49E48CD208F" ], "server": [ "OBS" ] }, "XMLName": { "Space": "", "Local": "" }, "Id": "test-id", "IsEnabled": true, "Filter": { "Prefix": "test" }, "Schedule": { "Frequency": "Daily" }, "Destination": { "Format": "CSV", "Bucket": "obs-sdk-test-pxliz", "Prefix": "test" }, "IncludedObjectVersions": "All", "OptionalFields": [ { "Field": "Size" } ] } --- PASS: TestOBSInventories (1.34s) PASS Process finished with the exit code 0 Reviewed-by: Anton Sidelnikov --- acceptance/openstack/obs/v1/obs_test.go | 62 +++++++++++++++++++++++++ openstack/obs/client_bucket.go | 35 ++++++++++++++ openstack/obs/const.go | 2 + openstack/obs/convert.go | 54 +++++++++++++++++++++ openstack/obs/model_base.go | 30 ++++++++++++ openstack/obs/model_bucket.go | 24 ++++++++++ openstack/obs/trait.go | 25 ++++++++++ 7 files changed, 232 insertions(+) diff --git a/acceptance/openstack/obs/v1/obs_test.go b/acceptance/openstack/obs/v1/obs_test.go index 485c53741..14fb1733c 100644 --- a/acceptance/openstack/obs/v1/obs_test.go +++ b/acceptance/openstack/obs/v1/obs_test.go @@ -309,3 +309,65 @@ func TestOBSCustomDomain(t *testing.T) { _, err = client.DeleteBucketCustomDomain(inputDelete) th.AssertNoErr(t, err) } + +func TestOBSInventories(t *testing.T) { + client, err := clients.NewOBSClient() + th.AssertNoErr(t, err) + + var ( + configId = "test-id" + bucketName = strings.ToLower(tools.RandomString("obs-sdk-test-", 5)) + ) + + _, err = client.CreateBucket(&obs.CreateBucketInput{ + Bucket: bucketName, + }) + t.Cleanup(func() { + _, err = client.DeleteBucket(bucketName) + th.AssertNoErr(t, err) + }) + th.AssertNoErr(t, err) + + inventoryOpts := obs.SetBucketInventoryInput{ + Bucket: bucketName, + InventoryConfigId: configId, + BucketInventoryConfiguration: obs.BucketInventoryConfiguration{ + Id: configId, + IsEnabled: true, + Schedule: obs.InventorySchedule{ + Frequency: "Daily", + }, + Destination: obs.InventoryDestination{ + Format: "CSV", + Bucket: bucketName, + Prefix: "test", + }, + Filter: obs.InventoryFilter{ + Prefix: "test", + }, + IncludedObjectVersions: "All", + OptionalFields: []obs.InventoryOptionalFields{ + { + Field: "Size", + }, + }, + }, + } + + _, err = client.SetBucketInventory(&inventoryOpts) + th.AssertNoErr(t, err) + + getResp, err := client.GetBucketInventory(obs.GetBucketInventoryInput{ + BucketName: bucketName, + InventoryConfigId: configId, + }) + th.AssertNoErr(t, err) + + tools.PrintResource(t, getResp) + + _, err = client.DeleteBucketInventory(&obs.DeleteBucketInventoryInput{ + Bucket: bucketName, + InventoryConfigId: configId, + }) + th.AssertNoErr(t, err) +} diff --git a/openstack/obs/client_bucket.go b/openstack/obs/client_bucket.go index 295dd0726..1c02aef72 100644 --- a/openstack/obs/client_bucket.go +++ b/openstack/obs/client_bucket.go @@ -704,3 +704,38 @@ func (obsClient ObsClient) GetBucketCustomDomain(bucketName string) (output *Get } return } + +func (obsClient ObsClient) SetBucketInventory(input *SetBucketInventoryInput) (output *BaseModel, err error) { + if input == nil { + return nil, errors.New("SetBucketInventoryInput is nil") + } + + output = &BaseModel{} + err = obsClient.doActionWithBucket("SetBucketInventory", HTTP_PUT, input.Bucket, input, output) + if err != nil { + output = nil + } + return +} + +func (obsClient ObsClient) DeleteBucketInventory(input *DeleteBucketInventoryInput) (output *BaseModel, err error) { + if input == nil { + return nil, errors.New("DeleteBucketInventoryInput is nil") + } + + output = &BaseModel{} + err = obsClient.doActionWithBucket("DeleteBucketInventory", HTTP_DELETE, input.Bucket, input, output) + if err != nil { + output = nil + } + return +} + +func (obsClient ObsClient) GetBucketInventory(input GetBucketInventoryInput) (output *GetBucketInventoryOutput, err error) { + output = &GetBucketInventoryOutput{} + err = obsClient.doActionWithBucket("GetBucketInventory", HTTP_GET, input.BucketName, input, output) + if err != nil { + output = nil + } + return +} diff --git a/openstack/obs/const.go b/openstack/obs/const.go index be14eccc2..4ba2f24ff 100644 --- a/openstack/obs/const.go +++ b/openstack/obs/const.go @@ -241,6 +241,7 @@ var ( "object-lock": true, "restore": true, "encryption": true, + "inventory": true, "tagging": true, "append": true, "position": true, @@ -667,6 +668,7 @@ const ( SubResourceEncryption SubResourceType = "encryption" SubResourceObjectLock SubResourceType = "object-lock" SubResourceCustomDomain SubResourceType = "customdomain" + SubResourceInventory SubResourceType = "inventory" SubResourceTagging SubResourceType = "tagging" SubResourceDelete SubResourceType = "delete" SubResourceVersions SubResourceType = "versions" diff --git a/openstack/obs/convert.go b/openstack/obs/convert.go index 8657a2a3d..a4f2437ff 100644 --- a/openstack/obs/convert.go +++ b/openstack/obs/convert.go @@ -7,6 +7,7 @@ import ( "io/ioutil" "net/http" "reflect" + "strconv" "strings" "time" ) @@ -973,3 +974,56 @@ func ConvertObjectLockConfigurationToXml(input BucketWormPolicy, returnMd5 bool, } return } + +// ConvertInventoryConfigurationToXml converts BucketInventoryConfiguration value to XML data and returns it +func ConvertInventoryConfigurationToXml(input BucketInventoryConfiguration, returnMd5 bool, isObs bool) (data string, md5 string) { + xml := make([]string, 0, 6) + xml = append(xml, "") + + id := XmlTranscoding(input.Id) + xml = append(xml, fmt.Sprintf("%s", id)) + + isEnabled := XmlTranscoding(strconv.FormatBool(input.IsEnabled)) + xml = append(xml, fmt.Sprintf("%s", isEnabled)) + + if input.Filter.Prefix != "" { + filter := XmlTranscoding(input.Filter.Prefix) + xml = append(xml, fmt.Sprintf("%s", filter)) + } + + if input.Destination.Bucket != "" && input.Destination.Format != "" { + xml = append(xml, "") + bucket := XmlTranscoding(input.Destination.Bucket) + format := XmlTranscoding(input.Destination.Format) + xml = append(xml, fmt.Sprintf("%s", format)) + xml = append(xml, fmt.Sprintf("%s", bucket)) + + if input.Destination.Prefix != "" { + prefix := XmlTranscoding(input.Destination.Prefix) + xml = append(xml, fmt.Sprintf("%s", prefix)) + } + xml = append(xml, "") + } + + schedule := XmlTranscoding(input.Schedule.Frequency) + xml = append(xml, fmt.Sprintf("%s", schedule)) + + inclVersions := XmlTranscoding(input.IncludedObjectVersions) + xml = append(xml, fmt.Sprintf("%s", inclVersions)) + + if len(input.OptionalFields) > 0 { + xml = append(xml, "") + for _, field := range input.OptionalFields { + xml = append(xml, fmt.Sprintf("%s", XmlTranscoding(field.Field))) + } + xml = append(xml, "") + } + + xml = append(xml, "") + + data = strings.Join(xml, "") + if returnMd5 { + md5 = Base64Md5([]byte(data)) + } + return +} diff --git a/openstack/obs/model_base.go b/openstack/obs/model_base.go index bc274ef0a..d5d1cc2f3 100644 --- a/openstack/obs/model_base.go +++ b/openstack/obs/model_base.go @@ -350,3 +350,33 @@ type BucketWormPolicy struct { Days string `xml:"Rule>DefaultRetention>Days,omitempty"` Years string `xml:"Rule>DefaultRetention>Years,omitempty"` } + +// BucketInventoryConfiguration defines the bucket inventory configuration +type BucketInventoryConfiguration struct { + XMLName xml.Name `xml:"InventoryConfiguration"` + Id string `xml:"Id"` + IsEnabled bool `xml:"IsEnabled"` + Filter InventoryFilter `xml:"Filter,omitempty"` + Schedule InventorySchedule `xml:"Schedule"` + Destination InventoryDestination `xml:"Destination"` + IncludedObjectVersions string `xml:"IncludedObjectVersions"` + OptionalFields []InventoryOptionalFields `xml:"OptionalFields,omitempty"` +} + +type InventoryFilter struct { + Prefix string `xml:"Prefix,omitempty"` +} + +type InventorySchedule struct { + Frequency string `xml:"Frequency,omitempty"` +} + +type InventoryDestination struct { + Format string `xml:"Format,omitempty"` + Bucket string `xml:"Bucket,omitempty"` + Prefix string `xml:"Prefix,omitempty"` +} + +type InventoryOptionalFields struct { + Field string `xml:"Field"` +} diff --git a/openstack/obs/model_bucket.go b/openstack/obs/model_bucket.go index 4076dcf3b..4e41fb4ab 100644 --- a/openstack/obs/model_bucket.go +++ b/openstack/obs/model_bucket.go @@ -325,3 +325,27 @@ type Domain struct { DomainName string `xml:"DomainName"` CreateTime string `xml:"CreateTime"` } + +// SetBucketInventoryInput is the input parameter of SetBucketInventory function +type SetBucketInventoryInput struct { + Bucket string `xml:"-"` + InventoryConfigId string `xml:"-"` + BucketInventoryConfiguration +} + +// DeleteBucketInventoryInput is the input parameter of DeleteBucketInventory function +type DeleteBucketInventoryInput struct { + Bucket string `xml:"-"` + InventoryConfigId string `xml:"-"` +} + +// GetBucketInventoryOutput is the result of GetBucketInventory function +type GetBucketInventoryOutput struct { + BaseModel + BucketInventoryConfiguration +} + +type GetBucketInventoryInput struct { + BucketName string `xml:""` + InventoryConfigId string `xml:""` +} diff --git a/openstack/obs/trait.go b/openstack/obs/trait.go index c9f295e73..95614cf42 100644 --- a/openstack/obs/trait.go +++ b/openstack/obs/trait.go @@ -880,3 +880,28 @@ func (input SetWORMPolicyInput) trans(isObs bool) (params map[string]string, hea data, _ = ConvertObjectLockConfigurationToXml(input.BucketWormPolicy, false, isObs) return } + +func (input SetBucketInventoryInput) trans(isObs bool) (params map[string]string, headers map[string][]string, data interface{}, err error) { + params = map[string]string{ + "id": input.InventoryConfigId, + string(SubResourceInventory): ""} + + data, _ = ConvertInventoryConfigurationToXml(input.BucketInventoryConfiguration, false, isObs) + return +} + +func (input DeleteBucketInventoryInput) trans(isObs bool) (params map[string]string, headers map[string][]string, data interface{}, err error) { + params = map[string]string{ + "id": input.InventoryConfigId, + string(SubResourceInventory): ""} + + return +} + +func (input GetBucketInventoryInput) trans(isObs bool) (params map[string]string, headers map[string][]string, data interface{}, err error) { + params = map[string]string{ + "id": input.InventoryConfigId, + string(SubResourceInventory): ""} + + return +}