Show version tag in UI (#3743)

* Show version tag in UI

* Add new arguments to test cloudbuild configuration

* backend cloudbuild should use commit_sha as argument

* Fix minor bug during dev
This commit is contained in:
Yuan (Bob) Gong 2020-05-13 02:02:21 +08:00 committed by GitHub
parent 731aed4577
commit e8356bc37a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 70 additions and 72 deletions

View File

@ -55,14 +55,16 @@ steps:
waitFor: ["-"]
- name: 'gcr.io/cloud-builders/docker'
args: ['build', '-t', 'gcr.io/$PROJECT_ID/frontend:$COMMIT_SHA',
'--build-arg', 'COMMIT_HASH=$COMMIT_SHA', '-f',
'/workspace/frontend/Dockerfile', '/workspace']
'--build-arg', 'COMMIT_HASH=$COMMIT_SHA',
'--build-arg', 'TAG_NAME=$TAG_NAME',
'-f', '/workspace/frontend/Dockerfile', '/workspace']
id: 'buildFrontend'
waitFor: ['prepareFrontend']
- name: 'gcr.io/cloud-builders/docker'
args: ['build', '-t', 'gcr.io/$PROJECT_ID/api-server:$COMMIT_SHA',
'--build-arg', 'COMMIT_SHA=$COMMIT_SHA', '-f',
'/workspace/backend/Dockerfile', '/workspace']
'--build-arg', 'COMMIT_SHA=$COMMIT_SHA',
'--build-arg', 'TAG_NAME=$TAG_NAME',
'-f', '/workspace/backend/Dockerfile', '/workspace']
id: 'buildApiServer'
waitFor: ['copyPythonSDK']

View File

@ -64,6 +64,8 @@ FROM debian:stretch
ARG COMMIT_SHA=unknown
ENV COMMIT_SHA=${COMMIT_SHA}
ARG TAG_NAME=unknown
ENV TAG_NAME=${TAG_NAME}
WORKDIR /bin

View File

@ -138,7 +138,7 @@ func startHttpProxy(resourceManager *resource.ResourceManager) {
topMux.HandleFunc("/apis/v1beta1/pipelines/upload", pipelineUploadServer.UploadPipeline)
topMux.HandleFunc("/apis/v1beta1/pipelines/upload_version", pipelineUploadServer.UploadPipelineVersion)
topMux.HandleFunc("/apis/v1beta1/healthz", func(w http.ResponseWriter, r *http.Request) {
io.WriteString(w, `{"commit_sha":"`+common.GetStringConfig("COMMIT_SHA")+`"}`)
io.WriteString(w, `{"commit_sha":"`+common.GetStringConfigWithDefault("COMMIT_SHA", "unknown")+`", "tag_name":"`+common.GetStringConfigWithDefault("TAG_NAME", "unknown")+`"}`)
})
topMux.Handle("/apis/", mux)

View File

@ -2,6 +2,8 @@ FROM node:12.14.1 as build
ARG COMMIT_HASH
ENV COMMIT_HASH=${COMMIT_HASH}
ARG TAG_NAME
ENV TAG_NAME=${TAG_NAME}
ARG DATE
@ -16,7 +18,8 @@ RUN npm run build
RUN mkdir -p ./server/dist && \
echo ${COMMIT_HASH} > ./server/dist/COMMIT_HASH && \
echo ${DATE} > ./server/dist/BUILD_DATE
echo ${DATE} > ./server/dist/BUILD_DATE && \
echo ${TAG_NAME} > ./server/dist/TAG_NAME
# Generate the dependency licenses files (one for the UI and one for the webserver),
# concatenate them to one file under ./src/server

View File

@ -1,2 +1,3 @@
./BUILD_DATE
./COMMIT_HASH
BUILD_DATE
COMMIT_HASH
TAG_NAME

View File

@ -11,9 +11,6 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import * as os from 'os';
import * as fs from 'fs';
import * as path from 'path';
import { PassThrough } from 'stream';
import express from 'express';
@ -27,6 +24,7 @@ import { loadConfigs } from './configs';
import * as minioHelper from './minio-helper';
import { TEST_ONLY as K8S_TEST_EXPORT } from './k8s-helper';
import { Server } from 'http';
import { commonSetup } from './integration-tests/test-helper';
jest.mock('minio');
jest.mock('node-fetch');
@ -38,48 +36,17 @@ jest.mock('./minio-helper');
const mockedFetch: jest.Mock = fetch as any;
beforeEach(() => {
const consoleInfoSpy = jest.spyOn(global.console, 'info');
consoleInfoSpy.mockImplementation(() => null);
const consoleLogSpy = jest.spyOn(global.console, 'log');
consoleLogSpy.mockImplementation(() => null);
});
afterEach(() => {
jest.restoreAllMocks();
jest.resetAllMocks();
});
describe('UIServer apis', () => {
let app: UIServer;
const indexHtmlPath = path.resolve(os.tmpdir(), 'index.html');
const argv = ['node', 'dist/server.js', os.tmpdir(), '3000'];
const buildDate = new Date().toISOString();
const tagName = '1.0.0';
const commitHash = 'abcdefg';
const indexHtmlContent = `
<html>
<head>
<script>
window.KFP_FLAGS.DEPLOYMENT=null
</script>
<script id="kubeflow-client-placeholder"></script>
</head>
</html>`;
beforeAll(() => {
fs.writeFileSync(path.resolve(__dirname, 'BUILD_DATE'), buildDate);
fs.writeFileSync(path.resolve(__dirname, 'COMMIT_HASH'), commitHash);
fs.writeFileSync(indexHtmlPath, indexHtmlContent);
});
afterAll(() => {
fs.unlinkSync(path.resolve(__dirname, 'BUILD_DATE'));
fs.unlinkSync(path.resolve(__dirname, 'COMMIT_HASH'));
fs.unlinkSync(indexHtmlPath);
});
const { argv, buildDate, indexHtmlContent } = commonSetup({ tagName, commitHash });
beforeEach(() => {
jest.resetAllMocks();
const consoleInfoSpy = jest.spyOn(global.console, 'info');
consoleInfoSpy.mockImplementation(() => null);
const consoleLogSpy = jest.spyOn(global.console, 'log');
consoleLogSpy.mockImplementation(() => null);
});
afterEach(() => {
@ -157,6 +124,7 @@ describe('UIServer apis', () => {
apiServerReady: false,
buildDate,
frontendCommitHash: commitHash,
frontendTagName: tagName,
},
done,
);
@ -167,6 +135,7 @@ describe('UIServer apis', () => {
json: () =>
Promise.resolve({
commit_sha: 'commit_sha',
tag_name: '1.0.0',
}),
}));
@ -178,9 +147,11 @@ describe('UIServer apis', () => {
200,
{
apiServerCommitHash: 'commit_sha',
apiServerTagName: '1.0.0',
apiServerReady: true,
buildDate,
frontendCommitHash: commitHash,
frontendTagName: tagName,
},
done,
);

View File

@ -19,9 +19,11 @@ import { Handler } from 'express';
/** HealthzStats describes the ml-pipeline ui server state. */
export interface HealthzStats {
apiServerCommitHash: string;
apiServerTagName: string;
apiServerReady: boolean;
buildDate: string;
frontendCommitHash: string;
frontendTagName: string;
}
/**
@ -41,15 +43,20 @@ export function getHealthzEndpoint(apiServerAddress: string, apiVersionPrefix: s
export function getBuildMetadata(currentDir: string = path.resolve(__dirname)) {
const buildDatePath = path.join(currentDir, 'BUILD_DATE');
const commitHashPath = path.join(currentDir, 'COMMIT_HASH');
const tagNamePath = path.join(currentDir, 'TAG_NAME');
const buildDate = fs.existsSync(buildDatePath)
? fs.readFileSync(buildDatePath, 'utf-8').trim()
: '';
const frontendCommitHash = fs.existsSync(commitHashPath)
? fs.readFileSync(commitHashPath, 'utf-8').trim()
: '';
const frontendTagName = fs.existsSync(tagNamePath)
? fs.readFileSync(tagNamePath, 'utf-8').trim()
: '';
return {
buildDate,
frontendCommitHash,
frontendTagName,
};
}
@ -71,6 +78,7 @@ export function getHealthzHandler(options: {
healthzStats.apiServerReady = true;
const serverStatus = await response.json();
healthzStats.apiServerCommitHash = serverStatus.commit_sha;
healthzStats.apiServerTagName = serverStatus.tag_name;
} catch (e) {
healthzStats.apiServerReady = false;
}

View File

@ -2,11 +2,14 @@ import * as path from 'path';
import * as os from 'os';
import * as fs from 'fs';
export function commonSetup() {
export function commonSetup(
options: { commitHash?: string; tagName?: string } = {},
): { argv: string[]; buildDate: string; indexHtmlPath: string; indexHtmlContent: string } {
const indexHtmlPath = path.resolve(os.tmpdir(), 'index.html');
const argv = ['node', 'dist/server.js', os.tmpdir(), '3000'];
const buildDate = new Date().toISOString();
const commitHash = 'abcdefg';
const commitHash = options.commitHash || 'abcdefg';
const tagName = options.tagName || '1.0.0';
const indexHtmlContent = `
<html>
<head>
@ -18,23 +21,19 @@ export function commonSetup() {
</html>`;
beforeAll(() => {
fs.writeFileSync(path.resolve(__dirname, 'BUILD_DATE'), buildDate);
fs.writeFileSync(path.resolve(__dirname, 'COMMIT_HASH'), commitHash);
console.log('beforeAll, writing files');
fs.writeFileSync(path.resolve(__dirname, '..', 'BUILD_DATE'), buildDate);
fs.writeFileSync(path.resolve(__dirname, '..', 'COMMIT_HASH'), commitHash);
fs.writeFileSync(path.resolve(__dirname, '..', 'TAG_NAME'), tagName);
fs.writeFileSync(indexHtmlPath, indexHtmlContent);
});
afterAll(() => {
fs.unlinkSync(path.resolve(__dirname, 'BUILD_DATE'));
fs.unlinkSync(path.resolve(__dirname, 'COMMIT_HASH'));
fs.unlinkSync(indexHtmlPath);
});
beforeEach(() => {
jest.resetAllMocks();
jest.restoreAllMocks();
});
return { argv };
return { argv, buildDate, indexHtmlPath, indexHtmlContent };
}
export function buildQuery(queriesMap: { [key: string]: string | undefined }): string {

View File

@ -232,9 +232,11 @@ describe('SideNav', () => {
it('populates the display build information using the response from the healthz endpoint', async () => {
const buildInfo = {
apiServerCommitHash: '0a7b9e38f2b9bcdef4bbf3234d971e1635b50cd5',
apiServerTagName: '1.0.0',
apiServerReady: true,
buildDate: 'Tue Oct 23 14:23:53 UTC 2018',
frontendCommitHash: '302e93ce99099173f387c7e0635476fe1b69ea98',
frontendTagName: '1.0.0-rc1',
};
buildInfoSpy.mockImplementationOnce(() => buildInfo);
@ -243,6 +245,7 @@ describe('SideNav', () => {
expect(tree).toMatchSnapshot();
expect(tree.state('displayBuildInfo')).toEqual({
tagName: buildInfo.apiServerTagName,
commitHash: buildInfo.apiServerCommitHash.substring(0, 7),
commitUrl:
'https://www.github.com/kubeflow/pipelines/commit/' + buildInfo.apiServerCommitHash,
@ -306,12 +309,13 @@ describe('SideNav', () => {
`);
});
it('displays the frontend commit hash if the api server hash is not returned', async () => {
it('displays the frontend tag name if the api server hash is not returned', async () => {
const buildInfo = {
apiServerReady: true,
// No apiServerCommitHash
// No apiServerCommitHash or apiServerTagName
buildDate: 'Tue Oct 23 14:23:53 UTC 2018',
frontendCommitHash: '302e93ce99099173f387c7e0635476fe1b69ea98',
frontendTagName: '1.0.0',
};
buildInfoSpy.mockImplementationOnce(() => buildInfo);
@ -321,6 +325,7 @@ describe('SideNav', () => {
expect(tree.state('displayBuildInfo')).toEqual(
expect.objectContaining({
commitHash: buildInfo.frontendCommitHash.substring(0, 7),
tagName: buildInfo.frontendTagName,
}),
);
});
@ -345,7 +350,7 @@ describe('SideNav', () => {
);
});
it("displays 'unknown' if the frontend and api server commit hashes are not returned", async () => {
it("displays 'unknown' if the frontend and api server tag names/commit hashes are not returned", async () => {
const buildInfo = {
apiServerReady: true,
// No apiServerCommitHash
@ -360,6 +365,7 @@ describe('SideNav', () => {
expect(tree.state('displayBuildInfo')).toEqual(
expect.objectContaining({
commitHash: 'unknown',
tagName: 'unknown',
}),
);
});

View File

@ -180,6 +180,7 @@ interface DisplayBuildInfo {
commitHash: string;
commitUrl: string;
date: string;
tagName: string;
}
interface SideNavProps extends RouterProps {
@ -222,10 +223,13 @@ export class SideNav extends React.Component<SideNavInternalProps, SideNavState>
async function fetchBuildInfo() {
const buildInfo = await Apis.getBuildInfo();
const commitHash = buildInfo.apiServerCommitHash || buildInfo.frontendCommitHash || '';
const tagName = buildInfo.apiServerTagName || buildInfo.frontendTagName || '';
return {
tagName: tagName || 'unknown',
commitHash: commitHash ? commitHash.substring(0, 7) : 'unknown',
commitUrl:
'https://www.github.com/kubeflow/pipelines' + (commitHash ? `/commit/${commitHash}` : ''),
'https://www.github.com/kubeflow/pipelines' +
(commitHash && commitHash !== 'unknown' ? `/commit/${commitHash}` : ''),
date: buildInfo.buildDate
? new Date(buildInfo.buildDate).toLocaleDateString('en-US')
: 'unknown',
@ -533,19 +537,19 @@ export class SideNav extends React.Component<SideNavInternalProps, SideNavState>
)}
{displayBuildInfo && (
<Tooltip
title={'Build date: ' + displayBuildInfo.date}
title={`Build date: ${displayBuildInfo.date}, Commit hash: ${displayBuildInfo.commitHash}`}
enterDelay={300}
placement={'top-start'}
>
<div className={css.envMetadata}>
<span>Build commit: </span>
<span>Version: </span>
<a
href={displayBuildInfo.commitUrl}
className={classes(css.link, commonCss.unstyled)}
rel='noopener'
target='_blank'
>
{displayBuildInfo.commitHash}
{displayBuildInfo.tagName}
</a>
</div>
</Tooltip>

View File

@ -208,13 +208,13 @@ exports[`SideNav populates the display build information using the response from
<WithStyles(Tooltip)
enterDelay={300}
placement="top-start"
title="Build date: 10/23/2018"
title="Build date: 10/23/2018, Commit hash: 0a7b9e3"
>
<div
className="envMetadata"
>
<span>
Build commit:
Version:
</span>
<a
className="link unstyled"
@ -222,7 +222,7 @@ exports[`SideNav populates the display build information using the response from
rel="noopener"
target="_blank"
>
0a7b9e3
1.0.0
</a>
</div>
</WithStyles(Tooltip)>

View File

@ -36,9 +36,11 @@ export interface ListRequest {
export interface BuildInfo {
apiServerCommitHash?: string;
apiServerTagName?: string;
apiServerReady?: boolean;
buildDate?: string;
frontendCommitHash?: string;
frontendTagName?: string;
}
// Hack types from https://github.com/microsoft/TypeScript/issues/1897#issuecomment-557057387

View File

@ -1,7 +1,7 @@
steps:
- id: "api-server"
name: "gcr.io/cloud-builders/docker"
args: [ 'build', '-t', '$_GCR_BASE/api-server', '-f', 'backend/Dockerfile', '.' ]
args: ['build', '-t', '$_GCR_BASE/api-server', '-f', 'backend/Dockerfile', '--build-arg', 'COMMIT_SHA=abcdefg', '--build-arg', 'TAG_NAME=1.0-dev', '.']
timeout: 1800s # 30min
options:
machineType: N1_HIGHCPU_8 # This is cpu intensive, use a better machine.

View File

@ -12,7 +12,7 @@ steps:
- id: "frontend"
name: "gcr.io/cloud-builders/docker"
args:
["build", "-t", "$_GCR_BASE/frontend", "-f", "frontend/Dockerfile", "."]
["build", "-t", "$_GCR_BASE/frontend", "-f", "frontend/Dockerfile", '--build-arg', 'COMMIT_HASH=abcdefg', '--build-arg', 'TAG_NAME=test', "."]
waitFor: ["-"]
- id: "viewer-crd-controller"
name: "gcr.io/cloud-builders/docker"

View File

@ -2,7 +2,7 @@ steps:
- id: "frontend"
name: "gcr.io/cloud-builders/docker"
args:
["build", "-t", "$_GCR_BASE/frontend", "-f", "frontend/Dockerfile", "."]
["build", "-t", "$_GCR_BASE/frontend", "-f", "frontend/Dockerfile", '--build-arg', 'COMMIT_HASH=abcdefg', '--build-arg', 'TAG_NAME=1.0-dev', "."]
images:
- "$_GCR_BASE/frontend"
timeout: 1800s # 30min