Skip to content

Commit

Permalink
feat: update to NodeJS 16 (#8)
Browse files Browse the repository at this point in the history
Signed-off-by: Mathew Wicks <5735406+thesuperzapper@users.noreply.github.com>
  • Loading branch information
thesuperzapper authored May 19, 2024
1 parent da9cb36 commit aaa264e
Show file tree
Hide file tree
Showing 11 changed files with 25,199 additions and 6,581 deletions.
2 changes: 2 additions & 0 deletions dashboard/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
dist/
node_modules/
27 changes: 11 additions & 16 deletions dashboard/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,36 +1,31 @@
# Step 1: Builds and tests
FROM node:14.21.3-bullseye AS build
FROM node:16.20.2-bullseye AS build

ARG kubeflowversion
ARG commit

ENV BUILD_VERSION=$kubeflowversion
ENV BUILD_COMMIT=$commit
ENV CHROME_BIN=/usr/bin/chromium
ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true

RUN apt update -qq && apt install -qq -y gnulib

COPY . /centraldashboard
WORKDIR /centraldashboard

RUN BUILDARCH="$(dpkg --print-architecture)" && npm rebuild && \
if [ "$BUILDARCH" = "arm64" ] || \
[ "$BUILDARCH" = "armhf" ]; then \
export CFLAGS=-Wno-error && \
export CXXFLAGS=-Wno-error; \
fi && \
npm install && \
npm run build && \
npm prune --production
RUN npm ci \
&& npm run build \
&& npm prune --production

# Step 2: Packages assets for serving
FROM node:14.21.3-alpine3.17 AS serve
FROM node:16.20.2-alpine AS serve

RUN apk add --no-cache tini

USER node

ENV NODE_ENV=production
WORKDIR /app
COPY --from=build /centraldashboard .
WORKDIR /usr/src/app
COPY --from=build --chown=node:node /centraldashboard .

EXPOSE 8082
ENTRYPOINT ["npm", "start"]
ENTRYPOINT ["/sbin/tini", "--" , "npm", "start"]
35 changes: 25 additions & 10 deletions dashboard/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,31 @@ It provides a jump-off point to all other tool Web UIs.

### Getting Started

Make sure you have installed node 12 or 14.

1. Run `cd dashboard`
2. Run `make build-local` to install npm dependencies
3. Run `npm run dev` to start the development server
- This runs [webpack](https://webpack.js.org/) over the front-end code in the [public](./public) folder.
It also starts the [webpack-dev-server](https://webpack.js.org/configuration/dev-server/) at http://localhost:8081.
- It also starts the Express API server at http://localhost:8082.
Requests from the front-end starting with `/api` are proxied to the Express server.
All other requests are handled by the front-end server which mirrors the production configuration.
Make sure you have installed node 16!

1. We STRONGLY recommend using [nvm](https://github.com/nvm-sh/nvm):
- Uninstall any Homebrew versions with `brew uninstall node` (or `node@XX`)
- Install `nvm` with `curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash`
- Install node `16` with `nvm install 16`
- Use node `16` with `nvm use 16`
- Set node `16` as the default with `nvm alias default 16`
2. Run `cd dashboard`
3. Run `npm install` to install npm dependencies
4. Run `./run_local.sh` to start the development server, this will:
- Run the following kubectl port-forwards:
- `localhost:8081` -> `kfam-api.deploykf-dashboard.svc`
- `localhost:8085` -> `jupyter-web-app-service.kubeflow.svc`
- `localhost:8087` -> `ml-pipeline-ui.kubeflow.svc`
- Run [webpack](https://webpack.js.org/) over the front-end code in the [public](./public) folder
- Run [webpack-dev-server](https://webpack.js.org/configuration/dev-server/) at http://localhost:8081
- Run the Express API server at http://localhost:8082
- Proxy requests from the front-end starting with `/api` to the Express server.
- All other requests are handled by the front-end server which mirrors the production configuration.
- __TIP:__ if you see `bind: address already in use` errors, you might try `pkill -f "kubectl port-forward"` to stop all existing port-forwards
5. Open your browser to `http://localhost:8080` to see the dashboard:
- You will need to inject your requrests with a `kubeflow-userid` header
- You can do this in Chrome by using the [Header Editor](https://chromewebstore.google.com/detail/eningockdidmgiojffjmkdblpjocbhgh) extension
- For example, set the `kubeflow-userid` header to `user1@example.com`

### Server Components

Expand Down
6 changes: 4 additions & 2 deletions dashboard/app/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,10 @@ export class Api {
}

let interval = Interval.Last15m;
if (Interval[req.query.interval] !== undefined) {
interval = Number(Interval[req.query.interval]);
const intervalQuery = req.query.interval as string;
const intervalQueryKey = intervalQuery as keyof typeof Interval;
if (Interval[intervalQueryKey] !== undefined) {
interval = Interval[intervalQueryKey];
}
switch (req.params.type) {
case 'node':
Expand Down
10 changes: 5 additions & 5 deletions dashboard/app/k8s_service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ const APP_API_NAME = 'applications';
/** Wrap Kubernetes API calls in a simpler interface for use in routes. */
export class KubernetesService {
private namespace = process.env.POD_NAMESPACE || 'kubeflow';
private coreAPI: k8s.Core_v1Api;
private customObjectsAPI: k8s.Custom_objectsApi;
private coreAPI: k8s.CoreV1Api;
private customObjectsAPI: k8s.CustomObjectsApi;
private dashboardConfigMap = DASHBOARD_CONFIGMAP;

constructor(private kubeConfig: k8s.KubeConfig) {
Expand All @@ -63,9 +63,9 @@ export class KubernetesService {
if (context && context.namespace) {
this.namespace = context.namespace;
}
this.coreAPI = this.kubeConfig.makeApiClient(k8s.Core_v1Api);
this.coreAPI = this.kubeConfig.makeApiClient(k8s.CoreV1Api);
this.customObjectsAPI =
this.kubeConfig.makeApiClient(k8s.Custom_objectsApi);
this.kubeConfig.makeApiClient(k8s.CustomObjectsApi);
}

/** Retrieves the list of namespaces from the Cluster. */
Expand All @@ -91,7 +91,7 @@ export class KubernetesService {
}

/** Retrieves the list of events for the given Namespace from the Cluster. */
async getEventsForNamespace(namespace: string): Promise<k8s.V1Event[]> {
async getEventsForNamespace(namespace: string): Promise<k8s.CoreV1Event[]> {
try {
const {body} = await this.coreAPI.listNamespacedEvent(namespace);
return body.items;
Expand Down
14 changes: 7 additions & 7 deletions dashboard/app/k8s_service_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import {KubernetesService} from './k8s_service';
describe('KubernetesService', () => {
let mockResponse: jasmine.SpyObj<IncomingMessage>;
let mockKubeConfig: jasmine.SpyObj<k8s.KubeConfig>;
let mockApiClient: jasmine.SpyObj<k8s.Core_v1Api>;
let mockCustomApiClient: jasmine.SpyObj<k8s.Custom_objectsApi>;
let mockApiClient: jasmine.SpyObj<k8s.CoreV1Api>;
let mockCustomApiClient: jasmine.SpyObj<k8s.CustomObjectsApi>;
let k8sService: KubernetesService;

beforeEach(() => {
Expand All @@ -17,13 +17,13 @@ describe('KubernetesService', () => {
'loadFromDefault', 'getContextObject', 'getCurrentContext',
'makeApiClient'
]);
mockApiClient = jasmine.createSpyObj<k8s.Core_v1Api>(
mockApiClient = jasmine.createSpyObj<k8s.CoreV1Api>(
'mockApiClient', ['listNamespace', 'listNamespacedEvent', 'listNode']);
mockCustomApiClient = jasmine.createSpyObj<k8s.Custom_objectsApi>(
mockCustomApiClient = jasmine.createSpyObj<k8s.CustomObjectsApi>(
'mockCustomApiClient', ['listNamespacedCustomObject']);
mockKubeConfig.makeApiClient.withArgs(k8s.Core_v1Api)
mockKubeConfig.makeApiClient.withArgs(k8s.CoreV1Api)
.and.returnValue(mockApiClient);
mockKubeConfig.makeApiClient.withArgs(k8s.Custom_objectsApi)
mockKubeConfig.makeApiClient.withArgs(k8s.CustomObjectsApi)
.and.returnValue(mockCustomApiClient);

k8sService = new KubernetesService(mockKubeConfig);
Expand Down Expand Up @@ -165,7 +165,7 @@ describe('KubernetesService', () => {
]
} as unknown; // needed to work around TS compiler
mockApiClient.listNamespacedEvent.and.returnValue(Promise.resolve(
{response: mockResponse, body: response as k8s.V1EventList}));
{response: mockResponse, body: response as k8s.CoreV1EventList}));

const events = await k8sService.getEventsForNamespace('kubeflow');
const eventNames = events.map((n) => n.metadata.name);
Expand Down
10 changes: 5 additions & 5 deletions dashboard/app/metrics_service.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
/** Time-series interval enumeration. */
export enum Interval {
Last5m,
Last15m,
Last30m,
Last60m,
Last180m
Last5m = 'Last5m',
Last15m = 'Last15m',
Last30m = 'Last30m',
Last60m = 'Last60m',
Last180m = 'Last180m',
}

/** Data-point contained in a time series. */
Expand Down
Loading

0 comments on commit aaa264e

Please sign in to comment.