fog / fog-rackspace

Rackspace provider gem for Fog ecosystem
MIT License
8 stars 35 forks source link

Different etags for Mock storage object between head request and get request #25

Open brlanier opened 6 years ago

brlanier commented 6 years ago

When using Fog.Mock! in tests I have noticed that a different etag is returned in the response when using a files.head request versus a file.get request. I don't know enough about what is going on, but I think the Mock class for the head request has a bug or I really don't understand what its trying to do.

Using Fog 1.37.0 but can see the code is the same here for the two files I am looking at.

From my test on the same file here is the metadata for the same file using each request

Head:

<Fog::Storage::Rackspace::File
    key="global/teacher/test_program/u1/g1/default/grade_web_resources/test.pdf",
    content_length=19,
    content_type="application/pdf",
    content_disposition=nil,
    etag="\"ba6969cb26232dd85e5b47c8f3a181bb\"",
    last_modified=2017-08-09 21:28:21 UTC,
    access_control_allow_origin=nil,
    origin=nil,
    content_encoding=nil,
    delete_at=nil,
    delete_after=nil

and using get

<Fog::Storage::Rackspace::File
    key="global/teacher/test_program/u1/g1/default/grade_web_resources/test.pdf",
    content_length=19,
    content_type="application/pdf",
    content_disposition=nil,
    etag="f354d658bc16113d80d596a8b61e87bf",
    last_modified=2017-08-09 21:27:53 UTC,
    access_control_allow_origin=nil,
    origin=nil,
    content_encoding=nil,
    delete_at=nil,
    delete_after=nil

The get request has the correct etag based on what we set prior to saving the file.

Part of the method we are using to push the file

    cloud_file = directory.files.create key: cloud_path
    cloud_file.body = File.open local_path
    cloud_file.etag = checksum local_path
    cloud_file.save

In looking at the rackspace/requests/storage/head_object.rb file, specifically the Mock class, I get stuck when looking at how the etag is generated. It looks like it tries to grab the value from each part of the file object that is stored in the hash key, adding that value to a new hash and then later calculating and md5 digest of that new hash turned into a string. This seems weird that you are creating an md5 digest of a value that is a string of all the md5 digests of each part of the file and using that for the etag. In testing, when inspecting the mock object of the file in question I can see that the @meta entry already contains the correct Etag as set.

...@last_modified=2017-08-10 17:31:12 UTC, @hash="f354d658bc16113d80d596a8b61e87bf", @meta={"Etag"=>"f354d658bc16113d80d596a8b61e87bf"}, @static_manifest=false>

In summary, I can't figure out how to properly test methods that are relying on a head call to test data. We have a method, partly listed above, that creates a cloud file, writes the file body and etag before saving and then does files.head call against the newly uploaded file to verify the file was uploaded with the correct etag and size. That method is failing tests now because the wrong etag is being returned in the head request under Fog.Mock!.

I don't fully understand the process or the best place to make a change, but it seems like the Moc class in get_object is just relying on the .to_headers method to return what is there while head is doing something different.

I would be happy to submit a pull request at some point, but I'm afraid I don't understand enough about the entire process but more specifically why the head_object in the Mock class is creating what seems to be a random etag. If there is no need to have the etag returned with something besides nil, then it seems letting the value that may or may not exist in the @meta value of the object seems the correct way. Not to mention the .to_headers method seems to take care of adding the bytes for each part up.

Sorry for the rambling. Happy to provide more info as needed. Looking for help, clarification or guidance on perhaps the best way or needed way to make the change.