-
Notifications
You must be signed in to change notification settings - Fork 74
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
[Threescale 7522] Fix CDN urls for fonts assets #3072
Conversation
The CDN url must be hardcoded at settings.yml:asset_host during assets compilation in order to get static assets like CSS files point to the CDN. Otherwise, the assets are generated with relative paths and are not loaded from the CDN, even when settings.yml:asset_host is set during runtime. We don't want to have to provide any CDN url when building porta docker images in order to get the assets correctly precompiled. To avoid that, this trick assumes the assets are generated with relative paths (settings.yml:asset_host = null during compilation time) and prepends the CDN url in runtime. By default, settings.yml:asset_host is set to RAILS_ASSET_HOST, which makes this env variable the only source of truth for CDN urls.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome detective work!
My suggestion is to handle only assets compiled with asset_host == null
. We can also edit the precompile task to fail unless it is not blank. Or better alter the value during compilation to prevent mistakes. This will simplify the pull request.
If we cannot alter the setting during compilation, then we may merge this change only once we fully migrate off SaaS legacy. To avoid having to deal with that environment.
Also we need a test that will verify the asset_host
is used in all - master, provider and dev portals.
I haven't checked but please make sure that the updated gems are supported in legacy. You can deploy to preview to double check.
app/views/layouts/provider.html.slim
Outdated
@@ -6,6 +6,9 @@ html[lang="en" class="pf-m-redhat-font"] | |||
= content_for?(:title) ? yield(:title) : default_title | |||
| | Red Hat 3scale API Management | |||
= csrf_meta_tag | |||
// Is this the entrypoint of the app? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's not the only layout that is used in the app... probably need to add this in others as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I updated it in all layouts
Co-authored-by: Aleksandar N. Kostadinov <akostadinov@gmail.com>
1e00e85
to
f29bb2d
Compare
If we do so, SaaS would stop loading assets from the CDN. At the end, it's really only this line that we could remove if we force /config/webpack/environment.js#L54
That's also OK, we could wait until the migration is done and push a version of this PR without that line linked above. I don't have a strong opinion.
I tested it locally. But yes, I'll try to figure out how to write tests for this.
How can I check that? other than deploying to preview? |
yes, that's how, also you can install ruby 2.4 locally and bundle install without test and dev deps.
I would say this, but I don't want you to get into conflicts. Especially the whole TS work going on recently. So I would say, add a TODO description what to do after legacy infra is decommissioned and remove the line afterwards. Or if you prefer, you can wait. |
I'll wait until the Typescript PR is merged, then I'll fix any conflict, remove any code to support legacy SaaS and wait until the migration to OCP is done to merge this. |
We're not going to merge until SaaS is moved to OCP
end | ||
|
||
def rails_asset_host_url | ||
asset_host_enabled = Rails.configuration.asset_host.present? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure I get it - there are two asset_host
values in different places in config tree? 🤔
UPDATE: Didn't scroll enough 😅
If I understand correctly Rails.configuration.asset_host
is coming from config/environments/{env}.rb
, and Rails.configuration.three_scale.asset_host
comes from settings.yml
.
From the change and the comment in webpack config, it seems to me that we always assume that this asset_host should be empty on compilation time?
If so, shouldn't we force it to be empty, so we don't have double-host issues?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I understand correctly Rails.configuration.asset_host is coming from config/environments/{env}.rb, and Rails.configuration.three_scale.asset_host comes from settings.yml.
Yes.
From the change and the comment in webpack config, it seems to me that we always assume that this asset_host should be empty on compilation time?
Yes.
If so, shouldn't we force it to be empty, so we don't have double-host issues?
Do you mean force it empty during compilation time? It's the same @akostadinov said. Yes, I agree.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added a callback that returns an error during assets compilation if the asset host is set. I couldn't find an easy way to just unset the asset host transparently. That would need more time but I can try if you guys think it's needed.
In order to be able to load both Sprockets and Webpacker assets from a CDN, the asset host property must be empty during the webpack assets compilation time. This ensures urls are generated as relative paths and webpack can safely prepend the CDN url to them.
This reverts commit 2c3e4a8
I removed the webpacker update from this PR and moved it to #3083 |
|
||
def rails_asset_host_url | ||
asset_host_enabled = Rails.configuration.asset_host.present? | ||
asset_host_url = Rails.configuration.three_scale.asset_host.presence |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is still not clear for me why we are using both settings...
Rails.configuration.asset_host
and Rails.configuration.three_scale.asset_host
Shouldn't we use just one?...
I guess Rails.configuration.asset_host
is the one that should define the actual value, as it's env-specific, and will use the value in Rails.configuration.three_scale.asset_host
, if present.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The relevant piece of code is here: /config/environments/production.rb#L109-L121
We could just set Rails.configuration.asset_host
to Rails.configuration.three_scale.asset_host
and it would work. The first was converted into a lambda function here. I can't get too much info from the commit message: what other assets besides ours are we loading through javascript_include_tag
? Maybe we can remove the lambda function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm, I am not sure about the reason for having this function there.
What I mean is: do you think we can we just rely on Rails.configuration.asset_host
in this helper?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need both variables, asset_host_enabled
tells us when the CDN is enabled, it can be disabled when DISABLE_DIGEST == 1
. In that case we don't care about the value of RAILS_ASSETS
: all assets loaded by rails won't include the CDN url, so the ones loaded by webpack shouldn't neither.
It's also possible that asset_host_enabled
is true, but there's not provided asset url (asset_host_url = RAILS_ASSET_HOST = <empty>
), then the situation is the same, rails won't load any asset and webpack shouldn't neither. So both variables must be true to go ahead and return the CDN url for webpack.
If we rely only on the value of asset_host_enabled
, then we need to check for the value of asset_host_url
anyway, to decide if we prepend the https://
prefix or not. Now it's done in a way all possibilities are checked in one line:
asset_host_enabled | asset_host_url | return value |
---|---|---|
false | '' | '' |
false | 'whtaever.cloudfront.net' | '' |
true | '' | '' |
true | 'whtaever.cloudfront.net' | 'https://whtaever.cloudfront.net' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In this case, does it make sense to rely only on asset_host_url
, if it is set, then prepend. WDYT? Or is asset_host_enabled
a rails thing that is tricky to ignore?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we rely only in asset_host_url
, the second row in the table would fail, it would return 'https://whtaever.cloudfront.net/'
instead of ''
. Since asset_host_enabled == false
, it will prepend the URL to webpack assets like fonts and JS files, but it won't use the CDN for all other assets. I think that's not the expected behaviour, if the admin disables the CDN, then it must be disabled for all assets.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If admin wants to disable CDN, admin could empty asset_host_url
. Or there is some other issue related to that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It can be disabled in two ways: by DISABLE_DIGEST == 1
or by RAILS_ASSET_HOST == ''
. If the admin disables the CDN by leaving RAILS_ASSET_HOST
empty, that will work, but if they want to enable it solely by RAILS_ASSET_HOST
it might not work because DISABLE_DIGEST
could be 1, and then it would only enable the CDN for webpack but not for Sprockets. We need to enforce that no matter how the assets are disabled or enabled, they must be disabled or enabled for both webpack and sprockets.
This PR is stale because it has not received activity for more than 14 days. Remove stale label or comment or this will be closed in 7 days. |
This is not stale but actively waiting for SaaS to be migrated to OCP |
This PR is stale because it has not received activity for more than 14 days. Remove stale label or comment or this will be closed in 7 days. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh no! My review was Pending all this time!! 🤦🏻 I hope it still makes sense.
return '' unless asset_host_enabled && asset_host_url | ||
|
||
"#{request.protocol}#{asset_host_url}" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think there's no need to check the string twice. Would this be equivalent?
return '' unless asset_host_enabled && asset_host_url | |
"#{request.protocol}#{asset_host_url}" | |
return '' unless asset_host_url = Rails.configuration.three_scale.asset_host.presence | |
"#{request.protocol}#{asset_host_url}" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not checking the string twice because only asset_host_url
is a string, asset_host_enabled
is a boolean. This comment explains why both are required.
config/environments/test.rb
Outdated
@@ -64,6 +64,16 @@ | |||
|
|||
config.assets.compile = ENV.fetch('SKIP_ASSETS', '0') == '0' | |||
|
|||
config.asset_host = ->(source) do | |||
# does it exist in /public/assets ? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure what this comment is for. Is it a question or a clarification? If the latter, I think the code is clear enough. However, I would use a self-explanatory name such as exist_in_public_assets
before resorting to comments.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, I'll fix this
config/webpacker.yml
Outdated
@@ -8,7 +8,7 @@ default: &default | |||
|
|||
# Additional paths webpack should lookup modules | |||
# ['app/assets', 'engine/foo/app/assets'] | |||
resolved_paths: ['app/javascript/src'] | |||
additional_paths: ['app/javascript/src'] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this due to the upgrade?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
config/webpack/environment.js
Outdated
|
||
output.publicPath = ''; | ||
Object.assign(fileLoader.use[0].options, { | ||
publicPath, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a bit confusing. Is this supposed to be the previous or the current value of publicPath
? If it's the same value please use the same reference for both. Also for readability's sake I'd suggest not mixing up config
and loaders
: first update publicPath
in one block then get the loader and mutate it in second block. These config files tend to get messy over time if we're not super tidy.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done (or at least I tried 😛)
@@ -0,0 +1,32 @@ | |||
Feature: Asset host |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tag can be added at Feature level once.
Feature: Asset host | |
@javascript | |
Feature: Asset host |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK
lib/tasks/webpacker.rake
Outdated
namespace :webpacker do | ||
|
||
task :check_asset_host do | ||
if Rails.configuration.three_scale.asset_host.presence |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if Rails.configuration.three_scale.asset_host.presence | |
if Rails.configuration.three_scale.asset_host.present? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No the same, presence
returns nil or the original value, present?
return a boolean.
https://stackoverflow.com/questions/19637499/what-is-the-point-of-objectpresence-in-rails
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Still looks better wit h the ?
IMO :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, now I think about it, maybe it should return a boolean...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Honestly noticed that but was too lazy to comment on it :) Thought it was too small of a detail.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Applied
|
||
task :check_asset_host do | ||
if Rails.configuration.three_scale.asset_host.presence | ||
STDERR.puts "*** Asset host must be null during compilation time. See https://github.com/3scale/porta/pull/3072 ***" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the message should be more practical/meaningful. What about we say:
*** Asset host is not supported. Run <command> and try again. ***
With some instruction to remove it? My reasoning is not everyone will be familiar with rake, webpack or with the assets pipeline or they will simply don't what to know about it and will just want to run the app.
And pointing to the PR in a comment, aimed to developers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But it's not true that Asset host is not supported, it only needs to be empty during the compilation time, but it can have a value in runtime.
@@ -44,7 +44,7 @@ | |||
"eslint-plugin-promise": "^4.1.1", | |||
"eslint-plugin-react": "^7.13.0", | |||
"eslint-plugin-standard": "^4.0.0", | |||
"file-loader": "^1.1.6", | |||
"file-loader": "^6.2.0", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is a bold move 😄 No breaking changes or caveats?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't you trust me? 😢
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't try this thoroughly, I only used it in my development environment and my dev cluster and noticed no incidences, also all tests pass. So how could I be sure I'm not breaking anything?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can probably read through the changelog for any breaking changes I guess.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the list of breaking changes:
- use md4 by default for hashing (chore(deps): update webpack-contrib/file-loader#369) (ad39022)
- minimum required nodejs version is 10.13.0
- rename the esModules option to esModule
- switch to ES modules by default (the option esModule is true by default)
- removed the useRelativePath option. It is dangerously and break url when you use multiple entry points.
- drop support for webpack < 4
Only the nodejs version seems relevant for me. Which one is installed in SaaS?
@asset_host = 'cdn.3scale.test.localhost' | ||
end | ||
|
||
attr_reader :request |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
attr_reader :request | |
attr_reader :request, :asset_host |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why?
Rails.configuration.asset_host = nil | ||
Rails.configuration.three_scale.asset_host = @asset_host |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rails.configuration.asset_host = nil | |
Rails.configuration.three_scale.asset_host = @asset_host | |
Rails.configuration.expects(:asset_host).returns(nil) | |
Rails.configuration.three_scale.expects(:asset_host).returns(@asset_host) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I applied this, also for other tests
f13e51b
to
90fb76b
Compare
This PR is stale because it has not received activity for more than 14 days. Remove stale label or comment or this will be closed in 7 days. |
This PR is stale because it has not received activity for more than 14 days. Remove stale label or comment or this will be closed in 7 days. |
This PR is stale because it has not received activity for more than 14 days. Remove stale label or comment or this will be closed in 7 days. |
Closing in favor of #3199 |
Detailed info about this commit in it's original PR: #3072
What this PR does / why we need it:
In the STG-SaaS porta instance, Setting the
RAILS_ASSET_HOST
variable makes all resources to be loaded from the CDN, except the fonts.Which issue(s) this PR fixes
THREESCALE-7522
Verification steps
RAILS_ASSET_HOST
and restart the serverSpecial notes for your reviewer:
Where the fonts urls come from:
__webpack_public_path__
is given in runtime, the CSS stubs are generated using the URL atsettings.yml:asset_host
during compilation time. Thanks to webpacker, I guess.The problem:
settings.yml:asset_host == null
and thus setting relative urls for fonts by default.__webpack_public_path__
variable is either empty or not available, then the JS generates a CSS code pointing to fonts using realtive urls, and thus being loaded from the server.Implemented solution:
I used a simplification of this workaround, which:
settings.yml:asset_host
, thus generating relative paths.Observations:
__webpack_public_path__
. Then I used the variablewindow.rails_asset_host
instead, which allows us to simplify the solution and set the the value only once atprovider.html.slim
.window.rails_asset_host
must be set at the entry point of the app, before including any other JS. I placed it atapp/views/layouts/provider.html.slim
, not sure if it's the right place.WEBPACKER_ASSET_HOST
, which I use to determine whether the user is compiling the assets with a default URL. I think it could probably work without the update, so if the update causes problem to any of you, I could try to remove it.postTransformPublicPath
option, that I need. Again, if this causes problems, I could probably use an older version, as long as it provides the mentioned option.