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

use cache busting for KP bundles #64414

Merged
merged 29 commits into from
Apr 29, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
0f88c86
convert into TS
mshustov Apr 24, 2020
5c2bc55
load plugin scripts in html body
mshustov Apr 24, 2020
3ab5f29
use buildNum as a unique Id for cache busting
mshustov Apr 24, 2020
f2c8d51
add tests for caching
mshustov Apr 27, 2020
9f46398
fix tests
mshustov Apr 27, 2020
5e5c145
remove the last TODO. url should be inlined with assetss server
mshustov Apr 27, 2020
03b1584
this logic handled by publicPathMap on the client
mshustov Apr 27, 2020
5082c6e
cache kbn-shared-deps as well
mshustov Apr 27, 2020
c0332e1
attempt to fix karma tests
mshustov Apr 27, 2020
3de245b
Merge branch 'master' into js-cachable-assets
mshustov Apr 27, 2020
9f45612
Merge branch 'master' of github.com:elastic/kibana into pr/64414
spalger Apr 27, 2020
c66b362
always run file through replace stream
spalger Apr 27, 2020
b1d7133
place buildHash at begining of path, include all static files
spalger Apr 27, 2020
db23f0b
Merge branch 'master' of github.com:elastic/kibana into pr/64414
spalger Apr 27, 2020
4ad5f1c
update bundles_route tests to inject buildNum everywhere
spalger Apr 27, 2020
73635fb
Merge branch 'master' of github.com:elastic/kibana into pr/64414
spalger Apr 27, 2020
0e6f64c
Merge branch 'master' of github.com:elastic/kibana into pr/64414
spalger Apr 27, 2020
b46fcd5
fix karma config to point to right prefix
spalger Apr 28, 2020
b6e7a63
use isDist naming throughout
spalger Apr 28, 2020
240553f
explain magic number with variables
spalger Apr 28, 2020
7c35408
restore replacePublicPath option from #64226
spalger Apr 28, 2020
0beb6ab
replace one more instance of replacePublicPath
spalger Apr 28, 2020
604ab26
use promisify instead of bluebird + non-null assertions
spalger Apr 28, 2020
50dc863
Merge branch 'master' of github.com:elastic/kibana into pr/64414
spalger Apr 28, 2020
94690da
remove one more magic number
spalger Apr 28, 2020
5c7c3f1
Merge branch 'master' into js-cachable-assets
elasticmachine Apr 28, 2020
6770a4a
Merge branch 'master' of github.com:elastic/kibana into pr/64414
spalger Apr 29, 2020
f06d044
Merge branch 'master' of github.com:elastic/kibana into pr/64414
spalger Apr 29, 2020
2943a61
Merge branch 'master' into js-cachable-assets
elasticmachine Apr 29, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 9 additions & 6 deletions src/legacy/ui/ui_render/ui_render_mixin.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,12 @@ export function uiRenderMixin(kbnServer, server, config) {
? await uiSettings.get('theme:darkMode')
: false;

const buildHash = server.newPlatform.env.packageInfo.buildNum;
const basePath = config.get('server.basePath');
const regularBundlePath = `${basePath}/bundles`;
const dllBundlePath = `${basePath}/built_assets/dlls`;

const regularBundlePath = `${basePath}/${buildHash}/bundles`;
const dllBundlePath = `${basePath}/${buildHash}/built_assets/dlls`;

const dllStyleChunks = DllCompiler.getRawDllConfig().chunks.map(
chunk => `${dllBundlePath}/vendors${chunk}.style.dll.css`
);
Expand All @@ -108,15 +111,15 @@ export function uiRenderMixin(kbnServer, server, config) {

const styleSheetPaths = [
...(isCore ? [] : dllStyleChunks),
`${basePath}/bundles/kbn-ui-shared-deps/${UiSharedDeps.baseCssDistFilename}`,
`${regularBundlePath}/kbn-ui-shared-deps/${UiSharedDeps.baseCssDistFilename}`,
...(darkMode
? [
`${basePath}/bundles/kbn-ui-shared-deps/${UiSharedDeps.darkCssDistFilename}`,
`${regularBundlePath}/kbn-ui-shared-deps/${UiSharedDeps.darkCssDistFilename}`,
`${basePath}/node_modules/@kbn/ui-framework/dist/kui_dark.css`,
`${regularBundlePath}/dark_theme.style.css`,
]
: [
`${basePath}/bundles/kbn-ui-shared-deps/${UiSharedDeps.lightCssDistFilename}`,
`${regularBundlePath}/kbn-ui-shared-deps/${UiSharedDeps.lightCssDistFilename}`,
`${basePath}/node_modules/@kbn/ui-framework/dist/kui_light.css`,
`${regularBundlePath}/light_theme.style.css`,
]),
Expand All @@ -131,7 +134,7 @@ export function uiRenderMixin(kbnServer, server, config) {
)
.map(path =>
path.localPath.endsWith('.scss')
? `${basePath}/built_assets/css/${path.publicPath}`
? `${basePath}/${buildHash}/built_assets/css/${path.publicPath}`
: `${basePath}/${path.publicPath}`
)
.reverse(),
Expand Down
119 changes: 101 additions & 18 deletions src/optimize/bundles_route/__tests__/bundles_route.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import { PUBLIC_PATH_PLACEHOLDER } from '../../public_path_placeholder';

const chance = new Chance();
const outputFixture = resolve(__dirname, './fixtures/output');
const pluginNoPlaceholderFixture = resolve(__dirname, './fixtures/plugin/no_placeholder');

const randomWordsCache = new Set();
const uniqueRandomWord = () => {
Expand All @@ -58,6 +59,9 @@ describe('optimizer/bundle route', () => {
dllBundlesPath = outputFixture,
basePublicPath = '',
builtCssPath = outputFixture,
npUiPluginPublicDirs = [],
buildHash = '1234',
isDist = false,
} = options;

const server = new Hapi.Server();
Expand All @@ -69,6 +73,9 @@ describe('optimizer/bundle route', () => {
dllBundlesPath,
basePublicPath,
builtCssPath,
npUiPluginPublicDirs,
buildHash,
isDist,
})
);

Expand Down Expand Up @@ -158,7 +165,7 @@ describe('optimizer/bundle route', () => {
it('responds with exact file data', async () => {
const server = createServer();
const response = await server.inject({
url: '/bundles/image.png',
url: '/1234/bundles/image.png',
});

expect(response.statusCode).to.be(200);
Expand All @@ -173,7 +180,7 @@ describe('optimizer/bundle route', () => {
it('responds with no content-length and exact file data', async () => {
const server = createServer();
const response = await server.inject({
url: '/bundles/no_placeholder.js',
url: '/1234/bundles/no_placeholder.js',
});

expect(response.statusCode).to.be(200);
Expand All @@ -187,12 +194,12 @@ describe('optimizer/bundle route', () => {
});

describe('js file with placeholder', () => {
it('responds with no content-length and modified file data', async () => {
it('responds with no content-length and modifiedfile data ', async () => {
const basePublicPath = `/${uniqueRandomWord()}`;
const server = createServer({ basePublicPath });

const response = await server.inject({
url: '/bundles/with_placeholder.js',
url: '/1234/bundles/with_placeholder.js',
});

expect(response.statusCode).to.be(200);
Expand All @@ -204,7 +211,7 @@ describe('optimizer/bundle route', () => {
);
expect(response.result.indexOf(source)).to.be(-1);
expect(response.result).to.be(
replaceAll(source, PUBLIC_PATH_PLACEHOLDER, `${basePublicPath}/bundles/`)
replaceAll(source, PUBLIC_PATH_PLACEHOLDER, `${basePublicPath}/1234/bundles/`)
);
});
});
Expand All @@ -213,7 +220,7 @@ describe('optimizer/bundle route', () => {
it('responds with no content-length and exact file data', async () => {
const server = createServer();
const response = await server.inject({
url: '/bundles/no_placeholder.css',
url: '/1234/bundles/no_placeholder.css',
});

expect(response.statusCode).to.be(200);
Expand All @@ -231,7 +238,7 @@ describe('optimizer/bundle route', () => {
const server = createServer({ basePublicPath });

const response = await server.inject({
url: '/bundles/with_placeholder.css',
url: '/1234/bundles/with_placeholder.css',
});

expect(response.statusCode).to.be(200);
Expand All @@ -240,7 +247,7 @@ describe('optimizer/bundle route', () => {
expect(response.headers).to.have.property('content-type', 'text/css; charset=utf-8');
expect(response.result.indexOf(source)).to.be(-1);
expect(response.result).to.be(
replaceAll(source, PUBLIC_PATH_PLACEHOLDER, `${basePublicPath}/bundles/`)
replaceAll(source, PUBLIC_PATH_PLACEHOLDER, `${basePublicPath}/1234/bundles/`)
);
});
});
Expand All @@ -250,7 +257,7 @@ describe('optimizer/bundle route', () => {
const server = createServer();

const response = await server.inject({
url: '/bundles/../outside_output.js',
url: '/1234/bundles/../outside_output.js',
});

expect(response.statusCode).to.be(404);
Expand All @@ -267,7 +274,7 @@ describe('optimizer/bundle route', () => {
const server = createServer();

const response = await server.inject({
url: '/bundles/non_existent.js',
url: '/1234/bundles/non_existent.js',
});

expect(response.statusCode).to.be(404);
Expand All @@ -286,7 +293,7 @@ describe('optimizer/bundle route', () => {
});

const response = await server.inject({
url: '/bundles/with_placeholder.js',
url: '/1234/bundles/with_placeholder.js',
});

expect(response.statusCode).to.be(404);
Expand All @@ -306,31 +313,31 @@ describe('optimizer/bundle route', () => {

sinon.assert.notCalled(createHash);
const resp1 = await server.inject({
url: '/bundles/no_placeholder.js',
url: '/1234/bundles/no_placeholder.js',
});

sinon.assert.calledOnce(createHash);
createHash.resetHistory();
expect(resp1.statusCode).to.be(200);

const resp2 = await server.inject({
url: '/bundles/no_placeholder.js',
url: '/1234/bundles/no_placeholder.js',
});

sinon.assert.notCalled(createHash);
expect(resp2.statusCode).to.be(200);
});

it('is unique per basePublicPath although content is the same', async () => {
it('is unique per basePublicPath although content is the same (by default)', async () => {
const basePublicPath1 = `/${uniqueRandomWord()}`;
const basePublicPath2 = `/${uniqueRandomWord()}`;

const [resp1, resp2] = await Promise.all([
createServer({ basePublicPath: basePublicPath1 }).inject({
url: '/bundles/no_placeholder.js',
url: '/1234/bundles/no_placeholder.js',
}),
createServer({ basePublicPath: basePublicPath2 }).inject({
url: '/bundles/no_placeholder.js',
url: '/1234/bundles/no_placeholder.js',
}),
]);

Expand All @@ -349,13 +356,13 @@ describe('optimizer/bundle route', () => {
it('responds with 304 when etag and last modified are sent back', async () => {
const server = createServer();
const resp = await server.inject({
url: '/bundles/with_placeholder.js',
url: '/1234/bundles/with_placeholder.js',
});

expect(resp.statusCode).to.be(200);

const resp2 = await server.inject({
url: '/bundles/with_placeholder.js',
url: '/1234/bundles/with_placeholder.js',
headers: {
'if-modified-since': resp.headers['last-modified'],
'if-none-match': resp.headers.etag,
Expand All @@ -366,4 +373,80 @@ describe('optimizer/bundle route', () => {
expect(resp2.result).to.have.length(0);
});
});

describe('kibana platform assets', () => {
describe('caching', () => {
describe('for non-distributable mode', () => {
it('uses "etag" header to invalidate cache', async () => {
const basePublicPath = `/${uniqueRandomWord()}`;

const npUiPluginPublicDirs = [
{
id: 'no_placeholder',
path: pluginNoPlaceholderFixture,
},
];
const responce = await createServer({ basePublicPath, npUiPluginPublicDirs }).inject({
url: '/1234/bundles/plugin/no_placeholder/no_placeholder.plugin.js',
});

expect(responce.statusCode).to.be(200);

expect(responce.headers.etag).to.be.a('string');
expect(responce.headers['cache-control']).to.be('must-revalidate');
});

it('creates the same "etag" header for the same content with the same basePath', async () => {
const npUiPluginPublicDirs = [
{
id: 'no_placeholder',
path: pluginNoPlaceholderFixture,
},
];
const [resp1, resp2] = await Promise.all([
createServer({ basePublicPath: '', npUiPluginPublicDirs }).inject({
url: '/1234/bundles/plugin/no_placeholder/no_placeholder.plugin.js',
}),
createServer({ basePublicPath: '', npUiPluginPublicDirs }).inject({
url: '/1234/bundles/plugin/no_placeholder/no_placeholder.plugin.js',
}),
]);

expect(resp1.statusCode).to.be(200);
expect(resp2.statusCode).to.be(200);

expect(resp1.rawPayload).to.eql(resp2.rawPayload);

expect(resp1.headers.etag).to.be.a('string');
expect(resp2.headers.etag).to.be.a('string');
expect(resp1.headers.etag).to.eql(resp2.headers.etag);
});
});

describe('for distributable mode', () => {
it('commands to cache assets for each release for a year', async () => {
const basePublicPath = `/${uniqueRandomWord()}`;

const npUiPluginPublicDirs = [
{
id: 'no_placeholder',
path: pluginNoPlaceholderFixture,
},
];
const responce = await createServer({
basePublicPath,
npUiPluginPublicDirs,
isDist: true,
}).inject({
url: '/1234/bundles/plugin/no_placeholder/no_placeholder.plugin.js',
});

expect(responce.statusCode).to.be(200);

expect(responce.headers.etag).to.be(undefined);
expect(responce.headers['cache-control']).to.be('max-age=31536000');
});
});
});
});
});
Loading