Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GetBucketLifecycleConfiguration deserialization failed #1941

Closed
microyahoo opened this issue Nov 25, 2022 · 11 comments
Closed

GetBucketLifecycleConfiguration deserialization failed #1941

microyahoo opened this issue Nov 25, 2022 · 11 comments
Assignees
Labels
bug This issue is a bug. response-requested Waiting on additional info and feedback. Will move to "closing-soon" in 7 days.

Comments

@microyahoo
Copy link

microyahoo commented Nov 25, 2022

Describe the bug

lcc, err := client.GetBucketLifecycleConfiguration(ctx,                                         
         &s3.GetBucketLifecycleConfigurationInput{                                                   
             Bucket: aws.String(bucket),                                                             
         })                                                                                          
 if err != nil {                                                                                 
        log.Println(err)
}
2022/11/25 13:34:13 operation error S3: GetBucketLifecycleConfiguration, https response error StatusCode: 200, RequestID: tx00000955d58f77184c2cb-00638053d5-3813-os-ztjyqxatbzfjbn8t, HostID: , deserialization failed, failed to decode response body, EOF

s3cmd is OK to get lifecycle

➜ /root/go/src/deeproute.ai/smd ☞ git:(test26) ✗ s3cmd getlifecycle s3://bucket
<?xml version="1.0" ?>
<LifecycleConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
        <Rule>
                <ID>805acc91-d9ce-4aa9-93de-c9936b32544e</ID>
                <Filter>
                        <Prefix>go</Prefix>
                        <Tag>
                                <Key>color</Key>
                                <Value>red</Value>
                        </Tag>
                </Filter>
                <Status>Enabled</Status>
                <Expiration>
                        <Days>100</Days>
                </Expiration>
        </Rule>
</LifecycleConfiguration>

Expected Behavior

return no error

Current Behavior

deserialization failed, failed to decode response body, EOF

Reproduction Steps

  1. set lifecycle for bucket
  2. GetBucketLifecycleConfiguration

Possible Solution

No response

Additional Information/Context

No response

AWS Go SDK V2 Module Versions Used

github.com/aws/aws-sdk-go v1.43.19
github.com/aws/aws-sdk-go-v2 v1.17.1
github.com/aws/aws-sdk-go-v2/config v1.18.3
github.com/aws/aws-sdk-go-v2/credentials v1.13.3
github.com/aws/aws-sdk-go-v2/service/s3 v1.29.4

Compiler and Version used

go version go1.19.3 linux/amd64

Operating System and version

Linux k1 3.10.0-1127.el7.x86_64 #1 SMP Tue Mar 31 23:36:51 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux

@microyahoo microyahoo added bug This issue is a bug. needs-triage This issue or PR still needs to be triaged. labels Nov 25, 2022
@microyahoo
Copy link
Author

microyahoo commented Nov 25, 2022

PutBucketLifecycleConfiguration API will also fail

I1125 08:38:45.247711       1 object_bucket.go:451] os lifecycle rules: [{                                                                   
    Status: Enabled,                                                                                                                         
    Expiration: {                                                                                                                            
      Days: 4,                                                                                                                               
      ExpiredObjectDeleteMarker: false                                                                                                       
    },                                                                                                                                       
    Filter: <nil>,                                                                                                                           
    ID: "798510e0-fe42-4d03-8abc-ad10c76fba69"                                                                                               
  }]                                                                                                                                         
E1125 08:38:45.288627       1 object_bucket.go:459] Failed to set bucket lifecycle: operation error S3: PutBucketLifecycleConfiguration, http
s response error StatusCode: 400, RequestID: tx000008fbb1c934bb81c1c-0063807f15-3813-os-ztjyqxatbzfjbn8t, HostID: 3813-os-ztjyqxatbzfjbn8t-os
-ztjyqxatbzfjbn8t, api error MalformedXML: UnknownError 

@RanVaknin
Copy link
Contributor

Hi @microyahoo ,

I'm not sure why you are running into this error. I'm able to successfully put and retrieve a lifecycle configuration on my bucket.

Example:

func main() {

        bucketname := "foo-bucket"
	cfg, err := config.LoadDefaultConfig(context.TODO(), config.WithRegion("us-east-1"), config.WithClientLogMode(aws.LogResponseWithBody|aws.LogRequestWithBody))
	if err != nil {
		log.Fatalf("unable to load SDK config, %v", err)
	}

	client := s3.NewFromConfig(cfg)

	_, err = client.PutBucketLifecycleConfiguration(context.Background(), &s3.PutBucketLifecycleConfigurationInput{
		Bucket: aws.String(bucketname),
		LifecycleConfiguration: &types.BucketLifecycleConfiguration{
			Rules: []types.LifecycleRule{
				{
					ID: aws.String("myrule-1"),
					Filter: &types.LifecycleRuleFilterMemberObjectSizeGreaterThan{
						Value: 1024,
					},
					NoncurrentVersionExpiration: &types.NoncurrentVersionExpiration{NoncurrentDays: 3},
					Expiration: &types.LifecycleExpiration{
						Days: 1,
					},
					Status: "Enabled",
				},
			},
		},
	})
	if err != nil {
		panic(err)
	}

	out, err := client.GetBucketLifecycleConfiguration(context.Background(),
		&s3.GetBucketLifecycleConfigurationInput{
			Bucket: aws.String(bucketname),
		})
	if err != nil {
		panic(err)
	}

	fmt.Println("found: ", len(out.Rules), "rule(s).")
}

output:

$ go run main.go

<?xml version="1.0" encoding="UTF-8"?>
<LifecycleConfiguration
	xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
	<Rule>
		<ID>myrule-1</ID>
		<Filter>
			<ObjectSizeGreaterThan>1024</ObjectSizeGreaterThan>
		</Filter>
		<Status>Enabled</Status>
		<Expiration>
			<Days>1</Days>
		</Expiration>
		<NoncurrentVersionExpiration>
			<NoncurrentDays>3</NoncurrentDays>
		</NoncurrentVersionExpiration>
	</Rule>
</LifecycleConfiguration>

found:  1 rule(s).

My guess is that somehow you uploaded a malformed XML describing the lifecycle configuration. That is you are running into this deserialization error.

Hope my code example helps.
Thanks,
Ran~

@RanVaknin RanVaknin self-assigned this Nov 29, 2022
@RanVaknin RanVaknin added response-requested Waiting on additional info and feedback. Will move to "closing-soon" in 7 days. and removed needs-triage This issue or PR still needs to be triaged. labels Nov 29, 2022
@microyahoo
Copy link
Author

microyahoo commented Nov 29, 2022

Hi @RanVaknin, thanks for your response. The reason for api error MalformedXML: UnknownError is that Filter is not specified in the rule like below. Why does this have to be specified, even if it is empty.

input := &s3.PutBucketLifecycleConfigurationInput{                                                   
        Bucket: aws.String(bucket),                                                                      
        LifecycleConfiguration: &types.BucketLifecycleConfiguration{                                     
            Rules: []types.LifecycleRule{                                                                
                types.LifecycleRule{                                                                     
                    Status: types.ExpirationStatusEnabled,                                               
                    ID:     aws.String("rule-uuid1"),                                                   
                    // Filter: &types.LifecycleRuleFilterMemberPrefix{},                       
                    Expiration: &types.LifecycleExpiration{                                             
                        Days: 2,                                                                        
                    },                                                                                                                       
                },                                                                                       
            },                                                                                           
        },                                                                                               
    }

@microyahoo
Copy link
Author

microyahoo commented Nov 29, 2022

In addition, the following example with replicated tag key will report the error deserialization failed, failed to decode response body, EOF

package main

import (
	"context"
	"fmt"
	"log"

	"github.com/aws/aws-sdk-go-v2/aws"
	"github.com/aws/aws-sdk-go-v2/config"
	"github.com/aws/aws-sdk-go-v2/credentials"
	"github.com/aws/aws-sdk-go-v2/service/s3"
	"github.com/aws/aws-sdk-go-v2/service/s3/types"
	"github.com/aws/aws-sdk-go/aws/awsutil"
)

func main() {
	const (
		accessKey = "REDACTED"
		secretKey = "REDACTED"
		bucket    = "REDACTED"
		endpoint  = "http://127.0.0.1:80"
	)
	appCreds := aws.NewCredentialsCache(credentials.NewStaticCredentialsProvider(
		accessKey, secretKey, ""))
	cfg, err := config.LoadDefaultConfig(context.TODO(),
		config.WithCredentialsProvider(appCreds),
		config.WithEndpointResolver(
			aws.EndpointResolverFunc(
				func(service, region string) (aws.Endpoint, error) {
					return aws.Endpoint{
						Source: aws.EndpointSourceCustom,
						URL:    endpoint,
					}, nil
				})),
	)
	if err != nil {
		log.Fatal(err)
	}

	// Create an S3 service client
	client := s3.NewFromConfig(cfg, func(o *s3.Options) {
		o.UsePathStyle = true
	})

	// lifecycle
	ctx := context.Background()
	lcc, err := client.GetBucketLifecycleConfiguration(ctx,
		&s3.GetBucketLifecycleConfigurationInput{
			Bucket: aws.String(bucket),
		})
	if err != nil {
		log.Println(err)
	}
	fmt.Printf("get bucket lifecycle configuration: %s\n", awsutil.Prettify(lcc))

	input := &s3.PutBucketLifecycleConfigurationInput{
		Bucket: aws.String(bucket),
		LifecycleConfiguration: &types.BucketLifecycleConfiguration{
			Rules: []types.LifecycleRule{
				types.LifecycleRule{
					Status: types.ExpirationStatusEnabled,
					ID:     aws.String("rule-uuid1"),
					Filter: &types.LifecycleRuleFilterMemberAnd{
						Value: types.LifecycleRuleAndOperator{
							Prefix: aws.String("prefix"),
							Tags: []types.Tag{
								{
									Key:   aws.String("color"),
									Value: aws.String("red"),
								},
								{
									Key:   aws.String("color"),
									Value: aws.String("blue"),
								},
							},
						},
					},
					Expiration: &types.LifecycleExpiration{
						Days: 2,
					},
				},
			},
		},
	}
	fmt.Printf("lifecycle input: %s\n", awsutil.Prettify(input))
	lccOutput, err := client.PutBucketLifecycleConfiguration(ctx, input)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("lifecycle configuration output: %s\n", awsutil.Prettify(lccOutput))

	lcc, err = client.GetBucketLifecycleConfiguration(ctx,
		&s3.GetBucketLifecycleConfigurationInput{
			Bucket: aws.String(bucket),
		})
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("get lifecycle configuration: %s\n", awsutil.Prettify(lcc))
}

output:

🍺 /root/go/src/github.com/microyahoo/go-exercises/s3-examples ☞ git:(master) ✗ go run test_s3.go       
2022/11/29 14:20:07 operation error S3: GetBucketLifecycleConfiguration, https response error StatusCode: 404, RequestID: tx00000cc56de0884bdea0b-006385a497-133cd7-os-ztjyqxatbzfjbn8t, HostID: 133cd7-os-ztjyqxatbzfjbn8t-os-ztjyqxatbzfjbn8t, api error NoSuchLifecycleConfiguration: UnknownError
get bucket lifecycle configuration: <invalid value>
lifecycle input: {
  Bucket: "zhengliang",
  ChecksumAlgorithm: ,
  LifecycleConfiguration: {
    Rules: [{
        Status: Enabled,
        Expiration: {
          Days: 2,
          ExpiredObjectDeleteMarker: false
        },
        Filter: &{{0 0 0xc00011a280 [{0xc00011a290 0xc00011a2a0 {}} {0xc00011a2b0 0xc00011a2c0 {}}] {}} {}},
        ID: "rule-uuid1"
      }]
  }
}
lifecycle configuration output: {
  ResultMetadata: {

  }
}
2022/11/29 14:20:07 operation error S3: GetBucketLifecycleConfiguration, https response error StatusCode: 200, RequestID: tx00000dcbe144593552126-006385a497-133cd7-os-ztjyqxatbzfjbn8t, HostID: , deserialization failed, failed to decode response body, EOF
exit status 1

After running the example, view the lifecycle through s3cmd as shown below

/root/go/src/github.com/microyahoo/go-exercises/s3-examples ☞ git:(master) ✗ s3cmd getlifecycle s3://zhengliang
<?xml version="1.0" ?>
<LifecycleConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
	<Rule>
		<ID>rule-uuid1</ID>
		<Filter>
			<Prefix>prefix</Prefix>
			<Tag>
				<Key>color</Key>
				<Value>red</Value>
			</Tag>
		</Filter>
		<Status>Enabled</Status>
		<Expiration>
			<Days>2</Days>
		</Expiration>
	</Rule>
</LifecycleConfiguration>

@RanVaknin
Copy link
Contributor

RanVaknin commented Nov 29, 2022

@microyahoo ,

You included your secret and access key. I have redacted them from your code snippet. For your own security please omit that info in the future.

Regarding your code. Again, I'm not sure how you are able to put that request, since your list of tags has the same tag twice. When running your code I get the following exception:

panic: operation error S3: PutBucketLifecycleConfiguration, https response error StatusCode: 400, RequestID: REDACTED, HostID: REDACTED, api error InvalidRequest: Duplicate Tag Keys are not allowed.

I'm really confused as to how this executes more than anything. I'd say this is why you are running into a malformed XML. When I omit the duplicate I get a valid response:

<?xml version="1.0" encoding="UTF-8"?>
<LifecycleConfiguration
	xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
	<Rule>
		<ID>rule-uuid1</ID>
		<Filter>
			<And>
				<Prefix>prefix</Prefix>
				<Tag>
					<Key>color</Key>
					<Value>red</Value>
				</Tag>
			</And>
		</Filter>
		<Status>Enabled</Status>
		<Expiration>
			<Days>2</Days>
		</Expiration>
	</Rule>
</LifecycleConfiguration>

From the CLI it doesn't seem like you are getting the same policy as the one shown in your code. Your <Filter> tag is missing an <And> tag inside it to reflect this line Filter: &types.LifecycleRuleFilterMemberAnd. Did you create that policy using the console?
My suggestion to check this would be to delete the policy you currently have and re-run this code.

Also, I would enable request and response logging on your client. That will give you some more information on what is being sent and received over the wire so you can further debug this behavior. You can do that by including these lines in your config:

	cfg, err := config.LoadDefaultConfig(context.TODO(), config.WithRegion("us-east-1"), config.WithClientLogMode(aws.LogResponseWithBody|aws.LogRequestWithBody))

Please let me know if that helps.

Thank you,
Ran~

@RanVaknin RanVaknin added response-requested Waiting on additional info and feedback. Will move to "closing-soon" in 7 days. and removed response-requested Waiting on additional info and feedback. Will move to "closing-soon" in 7 days. labels Nov 29, 2022
@microyahoo
Copy link
Author

microyahoo commented Nov 30, 2022

You included your secret and access key. I have redacted them from your code snippet. For your own security please omit that info in the future.

Hi @RanVaknin, thanks for you reminder. I will pay attention later.

Regarding your code. Again, I'm not sure how you are able to put that request, since your list of tags has the same tag twice. When running your code I get the following exception:

The underlying object storage I use is ceph rgw, is it related to the underlying implementation? It comes to normal after remove duplicated tag.

Did you create that policy using the console?

No, I create it using sdk.

Also, I would enable request and response logging on your client. That will give you some more information on what is being sent and received over the wire so you can further debug this behavior. You can do that by including these lines in your config:

The following is output after including config
< redacted for security reasons >

@microyahoo
Copy link
Author

Hi @RanVaknin, thanks for your response. The reason for api error MalformedXML: UnknownError is that Filter is not specified in the rule like below. Why does this have to be specified, even if it is empty.

input := &s3.PutBucketLifecycleConfigurationInput{                                                   
        Bucket: aws.String(bucket),                                                                      
        LifecycleConfiguration: &types.BucketLifecycleConfiguration{                                     
            Rules: []types.LifecycleRule{                                                                
                types.LifecycleRule{                                                                     
                    Status: types.ExpirationStatusEnabled,                                               
                    ID:     aws.String("rule-uuid1"),                                                   
                    // Filter: &types.LifecycleRuleFilterMemberPrefix{},                       
                    Expiration: &types.LifecycleExpiration{                                             
                        Days: 2,                                                                        
                    },                                                                                                                       
                },                                                                                       
            },                                                                                           
        },                                                                                               
    }

Please help to confirm why does Filter have to be specified, even if it is empty.

@RanVaknin
Copy link
Contributor

RanVaknin commented Nov 30, 2022

Hi @microyahoo ,

I deleted your logs because it had your credentials in them and were not redacted 🥲 . Before I deleted them I saw that after you fixed the duplicate tag you got a 200. So I assume that this part is solved.

Regarding your question:

Please help to confirm why does Filter have to be specified, even if it is empty.

From the S3 docs:

Filter
The Filter is used to identify objects that a Lifecycle Rule applies to. A Filter must have exactly one of Prefix, Tag, or And specified. Filter is required if the LifecycleRule does not contain a Prefix element.
Type: LifecycleRuleFilter data type

Regarding you using ceph rgw, this is the first time I have heard about this tool. As a rule of thumb we don't guarantee compatibility with 3rd party tools - for this exact reason. It seems like there is no validation when you upload an XML that doesn't adhere to s3 schema. I suggest using the SDK to put your lifecycle configuration and consulting the published docs.

Let me know if you need anything else.
Thank you!
Ran~

@github-actions github-actions bot removed the response-requested Waiting on additional info and feedback. Will move to "closing-soon" in 7 days. label Dec 1, 2022
@RanVaknin
Copy link
Contributor

I have responded on this discussion thread with my findings. Please refer to it for clarifications.

@RanVaknin RanVaknin added the response-requested Waiting on additional info and feedback. Will move to "closing-soon" in 7 days. label Dec 1, 2022
@microyahoo
Copy link
Author

Thanks for your nice explanation, I understand now.

About ceph radosgw, please refer the link radosgw

@github-actions
Copy link

github-actions bot commented Dec 1, 2022

⚠️COMMENT VISIBILITY WARNING⚠️

Comments on closed issues are hard for our team to see.
If you need more assistance, please either tag a team member or open a new issue that references this one.
If you wish to keep having a conversation with other community members under this issue feel free to do so.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug This issue is a bug. response-requested Waiting on additional info and feedback. Will move to "closing-soon" in 7 days.
Projects
None yet
Development

No branches or pull requests

2 participants