Use the faraday follow redirects middleware in the registry clients
🍭 Context
The rails backend interact with the container registry to get information and data about container images and tags.
On tags, the rails backend extracts the created_at
timestamp to return it on APIs (and display it on frontend). Getting the created_at
timestamp involves getting the config
data first. The config
data is basically a json structure where there is a created
key.
The config is stored as a blob on the Container Registry. Rails will thus need to:
- pull/download the config data first
- JSON decode it
- Read the appropriate field
In (1.), the Container Registry can choose to send a redirect response. That response will point to an object storage location. Rails thus need to handle that redirect.
Recently, we rolled out a change in the Container Registry to use the Google Cloud CDN for object storage locations. This means that the redirect location can point to a Google Cloud CDN.
We learned the hard way that an url in Google Cloud CDN can end with a signature (because it's a signed url, similar to signed AWS S3 urls). The signature can end with a trailing =
. Example:
https://redirect.to.here?Foo=Bar&Signature=66dyddgqweg12b43431wd=
Rails uses faraday for all the interactions with the Container Registry. faraday
has a built in params encoder. That encoder will thus transform our example into:
https://redirect.to.here?Foo=Bar&Signature=66dyddgqweg12b43431wd%3D
Now, guess what happens when the Google Cloud CDN receives %3D
instead of =
? Yes,
Since rails can't get the config
structure, it can't read the creation timestamp and ultimately will use nil
as created_at
for tags. This nil
will get translated into Time.zone.now
on the UI. This leads to a super confusing UX where all tags are Published just now
.
The above is exactly #353244 (closed).
💡 Solution Design
Following redirects is a basic feature that http clients such as faraday
usually have.
Fortunately, we have a middleware that fill our needs here.
More context in !81056 (comment 851205352).
Since this change is for the blob download function of the registry client, we're going to gate this change behind a feature flag. We can easily break several features in this function doesn't work properly. Here is the rollout issue: #353291 (closed)
🔬 What does this MR do and why?
- For the
faraday
used to download blobs, use theFaradayMiddleware::FollowRedirects
middleware.- Gate this behind a feature flag. Rollout issue: #353291 (closed)
- Create or update the related specs.
🖼 Screenshots or screen recordings
feature flag disabled
Conditions |
created_at returned properly? |
---|---|
GCS (no CDN) | |
GCS (with CDN) |
|
AWS |
feature flag enabled
Conditions |
created_at returned properly? |
---|---|
GCS (no CDN) | |
GCS (with CDN) |
|
AWS |
🗼 How to set up and validate locally
The way to test this is to enable the registry in GDK but then use the container registry project to build a binary with object storage support.
- Enable the registry in GDK.
-
$ gdk reconfigure && gdk restart
. - Stop the GDK registry :
$ gdk stop registry
- Update
<gdk_root>/registry/config.yml
with:storage: # filesystem: # rootdirectory: /var/lib/registry gcs: bucket: dfernandez-packages keyfile: /Users/david/Downloads/dfernandez-5494dd2c-2bda264f293e.json
- The above is a gcs bucket but you can use whatever object storage driver you want.
- Checkout the container registry project
- Build the binary with the support for the storage driver you want:
$ GO_BUILD_FLAGS='-tags include_gcs' make binaries
- Start the registry with:
$ /bin/registry serve <gdk_root>/registry/config.yml
Now you're ready to test the scenarios of the previous section. You will still need to have the cloud resources (object storage bucket and API keys) at hand.
🚥 MR acceptance checklist
This checklist encourages us to confirm any changes have been analyzed to reduce risks in quality, performance, reliability, security, and maintainability.
-
I have evaluated the MR acceptance checklist for this MR.