PAWS XXXXV (You are now leaving S3. Please have your customs forms ready.)

Well I manages to plow though the final few S3Control actions the other day so today I move into the much more complex CloudFront API.

This one was to say the least a little daunting as the XML calls are massive some with as many as 60 nodes. Well as usually I just blundered right into it without any sort of plan except I figured I better do the harderst one first CreateDistribution.

Well as usual the first thing I ran into was someting I have not seen before.

Invalid Iteration

on the XML.

It took a little while to figure out that I was sending blocks like this
 <CacheBehaviors>
    <Items>
      <CacheBehavior>
        <AllowedMethods>
          <Items>  /this one is extra
            <CachedMethods>
              <Items>
                <Method>GET</Method>
              </Items>
              <Quantity>1</Quantity>
            </CachedMethods>
            <Method>GET</Method>
          </Items>
          <Quantity>1</Quantity>
        </AllowedMethods>
but what the API was expecting was a block like this
<CacheBehaviors>
      <Items>
         <CacheBehavior>
            <AllowedMethods>
               <CachedMethods>
                  <Items>
                     <Method>string</Method>
                  </Items>
                  <Quantity>integer</Quantity>
               </CachedMethods>
               <Items>
                  <Method>string</Method>
               </Items>
               <Quantity>integer</Quantity>
            </AllowedMethods>
I eventually realized what the API was looking for where blocks like this
<CacheBehavior>
  <AllowedMethods>
    <CachedMethods>
      <Items>
        <Method>GET1</Method>
…
      </Items>
      <Quantity>7</Quantity>
    </CachedMethods>
    <4Items>
      <Method>GET</Method>
…
    </Items>
    <Quantity>7</Quantity>
  </AllowedMethods>
Though the patch to fix this was simple enough

  elsif ( $attribute->type_constraint eq 'ArrayRef[Str|Undef]' ) {

            my $location = 'member';
            $location = $attribute->request_name
              if ($attribute->can('request_name'));
            my $temp_xml .= (
                join '',
                map { sprintf '<%s>%s</%s>', $location, $_, $location }
                  @{ $attribute->get_value($value) }
            );
            $temp_xml = "<$att_name>" . $temp_xml . "</$att_name>"
              unless ( $attribute->does('Flatten') );
            $xml .= $temp_xml;
        }

I just had to use a temp value for XML in the 'else' part of the condition that was failing like I have done before in the same sub.

It was a rather long journey adding in many many lines of debugging to find out where to add the patch.

In the end I at least got the call not to return an invalid XML error.

However by working on the above action I discovered that to do anything with CloudfFront your AWS Identity has to have a very high access level.

I did not want to spend a long time figuring our exactly what group or role I needed so I decided it was best to drop the call out to 'CreateDistribution' for now and get at least one of the less complex ones working first.

The good old adage, get one working and then grow from there seem apt.

keeping this in mind I creates a CloudFront object via the AWS management console and then started to work on the simple 'GetDistribution' action. On my frist run I got


bless( {
                 'ETag' => 'E22WXSE7DVIZBC',
                 '_request_id' => '781f4525-2d9a-11ea-b662-c9c2384ce829'
               }, 'Paws::CloudFront::GetDistributionResult' );
returned. It worked but in the content part of my response I got a whole great wack of XML so I think I am going to be spending some time with the 'RestXMLResponse.pm' code with this one.

The generated class was correct


package Paws::CloudFront::GetDistributionResult;
  use Moose;
  has Distribution => (is => 'ro', isa => 'Paws::CloudFront::Distribution');
  has ETag => (is => 'ro', isa => 'Str', traits => ['ParamInHeader'], header_name => 'ETag');
  has _request_id => (is => 'ro', isa => 'Str');
1;
and the XML in the content of the response looked ok;
<Distribution xmlns="http://cloudfront.amazonaws.com/doc/2019-03-26/">
    <Id>E2F696NMEQJ5SH</Id>
   <ARN>arn:aws:cloudfront::985173205561:distribution/E2F696NMEQJ5SH</ARN>
  <Status>Deployed</Status>
…
So the problem must be in the good old 'response_to_object' sub of RestXMLResponse.pm.

I have been here many times before. It did not take long to find the problem as it is one I have seen before.

Our XML parser is dropping off the root tag again. I get this


 {
   'ActiveTrustedSigners' => {
                             'Enabled' => 'false',
                             'Quantity' => '0'
                           },
…

hash rather than this


 {
   'Distribution'=>  {
          'ActiveTrustedSigners' => {
                           'Enabled' => 'false',
                           'Quantity' => '0'
                                    },
…

Fortunately in the boto there is this


 "GetDistributionResult":{
      …
   "payload":"Distribution"
    },

That payload will help me here, but as seen above there is no reference to 'payload' in my generated class.

Me thinks I will have to change the template again?

I played a little in the restxml/callresult_class.tt and I found that it might just be an '[% END%]' tag in the wrong spot and by moving a few things about I was now getting this;


package Paws::CloudFront::GetDistributionResult;
  use Moose;
  has Distribution => (is => 'ro', isa => 'Paws::CloudFront::Distribution');
  use MooseX::ClassAttribute;
  class_has _payload => (is => 'ro', default => 'Distribution');
    has ETag => (is => 'ro', isa => 'Str', traits => ['ParamInHeader'], header_name => 'ETag');
  has _request_id => (is => 'ro', isa => 'Str');

sort of out of order but should work if I add this patch to 'RestXmlCaller.pm'


         } else {
-        $unserialized_struct = eval { $self->unserialize_response( $content ) };
+               if ( $ret_class->can('_payload')){
+           $unserialized_struct->{$ret_class->_payload}= eval { $self->unserialize_response( $content ) };
+           }
+               else {
+           $unserialized_struct = eval { $self->unserialize_response( $content ) };
+           }
                if ($@){

and when I ran it I got the correct unserialized hash;


{
 'Distribution'=>  {
          'ActiveTrustedSigners' => {
                                      'Enabled' => 'false',
                                      'Quantity' => '0'
                                    },
…
from the XML.

However I ran into a completely new fail starting with

Attribute (Items) does not pass the type constraint because: Validation failed for 'ArrayRef[Str|Undef]' with value [ { Method: ARRAY(0x5184cf0) } ] at /wwwveh/lib/x86_64-linux-thread-multi/Moose/Object.pm line 24 ...

and then many lines of Moose Poop, when I tried to coerce this new hash into an object.

So it looks like I have my next post all lined up.

36c109b26d5d4a68abd5739c6b519ba5.jpg

Leave a comment

About byterock

user-pic Long time Perl guy, a few CPAN mods allot of work on DBD::Oracle and a few YAPC presentations