Skip to content

Commit

Permalink
Cp062 (#3853)
Browse files Browse the repository at this point in the history
* Backend changes for Central Dashboard to support multi-user isolation. (#3729)

* [Central Dashboard v2] Multi-User Isolation PR (#3503)

* Multi-User Isolation:
- Updated base lib with readBinding -> readBindings, proper return types
- Integrated payload info in env-info
- Cluster Admin detection

Feature Updates:
- Auth forwarding
- Profile Controller caching
- Base URL detection, fixed API base
- Minor Controller refactor
- Resolved PR comments
    - Service discovery now happens via internal hostname for service
- Fixed linter options and code errors

Changes:
- API updated to fallback on K8s listNamespaces if getEnv fails
- Fallback payload ORM written for uniform data passed to UI
- UI ORM to roll namespaces like: {namespace, user, role}
- Namespace selector upgraded to support new namespace object
- Namespace selector has indicators for when a namespace is owned

Short-term Support added for #3555
- Env:IAP_HEADER for auth header that contains IAP token
- Env:IAP_PREFIX for auth domain
- Updated API interface to handle dynamic IAP_HEADER values

Massive Refactor:
- profile_route_handlers.ts added to provide middleware for auth / profiles
- API.ts changed significantly to rely on `req.user`
- All routes have access to the user object
- Conditionally load the registration page if no profiles exist for user
- The point above is WIP

Registration Flow 0.5
- Registration-Page element added
- All html are now dynamically generated from base.pug
- A modular html generation function added in Webpack with bundle linking support
- Modularized js files per page to share base.js
- base pug has a nested pug interpolator for custom elements to be added from webpack
- LF requirement removed
- Base HTML files removed
- Updated Env Variables to be GCP agnostic in user management
- TS specific refactor added for classes
- Global Promise Uncaught handler added
- Promise handling improved in middleware flow
- Added fallback behavior for non KFAM flows

Registration flow 1.0
- Registration detection
- API linking
- Fast Registration flow
- Main-App integration, with resync
- Themed to the tee with the mocks by @ajayalfred
- Incomplete change

Registration Screen Complete:
- Iron-a11y-keys added
- `Profile` -> `workgroup` rename
- JSON serializer added for user data
- Removed authRoute from server
- Added an intermediate PageLoader till registration or main-page loads
- API fetch for workgroup info on page load
- Interactive carousel navigation added to registration flow
- Animated API response added for success
- Invalidation and UI indicators for failed API call
- Regex enforced validation for namespace name with maxlength enforcement
- Shared checkmark component added
- `sleep` added as a new method in utilities mixin

Final Features:
- Added footer links
- Refactor backend to work with Kfam.
    - Simplifies the backend to add a user-aware middleware to enrich
the Request object with information about whether the cluster has
auth enabled and what the user's identity is.
    - Adds unit tests for all interactions with Kfam.
- Linked @prodonjs changes to front-end

Manage-Users:
- Vaadin charts and paper-toast added
- Profile Controller promise handler created
- `/get-contributors` api route created
- Namespace icon added
- Manage Users page added
- Dashboard made aware of cluster admin status and `isolationMode`
- `Manage Users` sidebar entry only when isolationMode is multi
- New `empty` macro added to utilities mixin
- Paper-chip element added
- TSLint remove any warnings

Major updates 2.0:
- Reverted all webpack changes
- Updated failing api_tests.ts
- Corrected the fields being used to/from server for registration
- Removed base files since we do not have multiple entrypoints
- Welcome user toast added after successful namespace registration
- Corrected sidebar entries for Manage-Users
- Manage Users contrib action color now toggles based on status (error support)
- Rollback errors when successfully added contributor
- Namespace selector badge swapped with `(owned)` indicator
- Namespace selector now has corrected namespace icon
- Resolved all @ajayalfred and @prodonjs comments
- Registration Flow buttons moved to the right, flexed to the bottom of the container
- Updated namespace creation regex to what the server replies with
- Corrected flow to trigger completion event 2s after flow complete (for text animation to show)
- Empty mixin corrected to support Events, Strings, Error, Sets
- Paper-chip border-color updated
- Paper-chip ripple updated to accent color
- Fixed all tests
- Addressed all of @prodonjs' comments
- Fixed a jsdoc issue where a `*` was missing

Diverged manage-users page

* Fixed last test:
- Corrected logic for onHasWorkgroupResponse
- Fixed test for env-info

* Multi-User Isolation 2.0 - Manage Users (#3758)

* Multi-User Isolation:
- Updated base lib with readBinding -> readBindings, proper return types
- Integrated payload info in env-info
- Cluster Admin detection

Feature Updates:
- Auth forwarding
- Profile Controller caching
- Base URL detection, fixed API base
- Minor Controller refactor
- Resolved PR comments
    - Service discovery now happens via internal hostname for service
- Fixed linter options and code errors

Changes:
- API updated to fallback on K8s listNamespaces if getEnv fails
- Fallback payload ORM written for uniform data passed to UI
- UI ORM to roll namespaces like: {namespace, user, role}
- Namespace selector upgraded to support new namespace object
- Namespace selector has indicators for when a namespace is owned

Short-term Support added for #3555
- Env:IAP_HEADER for auth header that contains IAP token
- Env:IAP_PREFIX for auth domain
- Updated API interface to handle dynamic IAP_HEADER values

Massive Refactor:
- profile_route_handlers.ts added to provide middleware for auth / profiles
- API.ts changed significantly to rely on `req.user`
- All routes have access to the user object
- Conditionally load the registration page if no profiles exist for user
- The point above is WIP

Registration Flow 0.5
- Registration-Page element added
- All html are now dynamically generated from base.pug
- A modular html generation function added in Webpack with bundle linking support
- Modularized js files per page to share base.js
- base pug has a nested pug interpolator for custom elements to be added from webpack
- LF requirement removed
- Base HTML files removed
- Updated Env Variables to be GCP agnostic in user management
- TS specific refactor added for classes
- Global Promise Uncaught handler added
- Promise handling improved in middleware flow
- Added fallback behavior for non KFAM flows

Registration flow 1.0
- Registration detection
- API linking
- Fast Registration flow
- Main-App integration, with resync
- Themed to the tee with the mocks by @ajayalfred
- Incomplete change

Registration Screen Complete:
- Iron-a11y-keys added
- `Profile` -> `workgroup` rename
- JSON serializer added for user data
- Removed authRoute from server
- Added an intermediate PageLoader till registration or main-page loads
- API fetch for workgroup info on page load
- Interactive carousel navigation added to registration flow
- Animated API response added for success
- Invalidation and UI indicators for failed API call
- Regex enforced validation for namespace name with maxlength enforcement
- Shared checkmark component added
- `sleep` added as a new method in utilities mixin

Final Features:
- Added footer links
- Refactor backend to work with Kfam.
    - Simplifies the backend to add a user-aware middleware to enrich
the Request object with information about whether the cluster has
auth enabled and what the user's identity is.
    - Adds unit tests for all interactions with Kfam.
- Linked @prodonjs changes to front-end

Manage-Users:
- Vaadin charts and paper-toast added
- Profile Controller promise handler created
- `/get-contributors` api route created
- Namespace icon added
- Manage Users page added
- Dashboard made aware of cluster admin status and `isolationMode`
- `Manage Users` sidebar entry only when isolationMode is multi
- New `empty` macro added to utilities mixin
- Paper-chip element added
- TSLint remove any warnings

Major updates 2.0:
- Reverted all webpack changes
- Updated failing api_tests.ts
- Corrected the fields being used to/from server for registration
- Removed base files since we do not have multiple entrypoints
- Welcome user toast added after successful namespace registration
- Corrected sidebar entries for Manage-Users
- Manage Users contrib action color now toggles based on status (error support)
- Rollback errors when successfully added contributor
- Namespace selector badge swapped with `(owned)` indicator
- Namespace selector now has corrected namespace icon
- Resolved all @ajayalfred and @prodonjs comments
- Registration Flow buttons moved to the right, flexed to the bottom of the container
- Updated namespace creation regex to what the server replies with
- Corrected flow to trigger completion event 2s after flow complete (for text animation to show)
- Empty mixin corrected to support Events, Strings, Error, Sets
- Paper-chip border-color updated
- Paper-chip ripple updated to accent color
- Fixed all tests
- Addressed all of @prodonjs' comments
- Fixed a jsdoc issue where a `*` was missing

* Major Changes:
- New Return type from API SimpleBinding, removes front-end complexity by mapping all Bindings to Simple
- Manage Users page completed with the cluster admin overview table
- Ajax calls only fire when user and status conditions are fully met
- Style updates - Corrected Namespace selector

* Finalized Manage-User:
- Delete Contributor added
- Better error handling
- Add contributor passing auth, and deleting erroneous headers
- Paper-chip events finalized
- Flow complete

* Corrected logic to use ownedNamespace than any namespace to trigger registration flow

* Addressed all comments from @prodonjs:
- Moved all workgroup related routes to /api/workgroup
- Added a 404 to API
- Reverted req.user.auth in favor of all headers (explained in PR)

* Updated css for add contribs box to not grow after first input

* Fixed all comments and all tests

* Fixed semicolon issue, builds work as expected

* Removed debug statements and updated RoleMap, removed extraneous code

* Fixed last semicolon issue

* All UX comments updates:
- Text on Manage Contributors updated, membership roles clarified
- Page renamed to Contributors instead of users
- Font updates, title changes, weight changes, spacing changes
- Checkmark animation with api message hidden
- Shared macro, `fire` added to dispach custom events
- Sidebar now hides when in Manage Contributors
- All paper-inputs changed to md2-input, with some interop
- Strong validators implemented

* Fixed all of Jason's comments:
- Migrated all profile specific code and functionality to api_contributors
- /api/has-workgroup -> /api/workgroup/exists
- Refactored tests
- Fixed error response logic

* Another set of review done:
- ContributorAPI -> ContributorApi
- Privitized NO_ROUTES conditional router
- Types made into ProperCase
- Nits, and spaces fixed, css beautification done
- `.fire()` changed to `.fireEvent()`

* Added a middleware to block all workgroup calls if not autoried

* Fixed tests, static method removed

* MUI Basic Auth: (#3842)

- Added support for communicating with Profile Controller for namespaces
- Renamed api_contributors to api_workgroup
- Updated nomenclature, removed unnecessary deps
- Fixed tests to work with new response structure

* MUI Style and Nomenclature: (#3843)

- Table left padding removed as per spec
- Headers boldened with Hacky CSS
- Updated Membership and info to be lowecase
- Updated membership table with new design
- Removed commented code
  • Loading branch information
kunmingg authored and k8s-ci-robot committed Aug 8, 2019
1 parent d74d12c commit 6558315
Show file tree
Hide file tree
Showing 35 changed files with 3,146 additions and 501 deletions.
3 changes: 2 additions & 1 deletion components/centraldashboard/.eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
}
],
"multiline-comment-style": "error",
"require-jsdoc": "off"
"require-jsdoc": "off",
"linebreak-style": "off"
}
}
3 changes: 0 additions & 3 deletions components/centraldashboard/app/.jasmine.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@
"spec_files": [
"**/*_test.ts"
],
"helpers": [
"helpers/**/*.ts"
],
"stopSpecOnExpectationFailure": false,
"random": true
}
115 changes: 82 additions & 33 deletions components/centraldashboard/app/api.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,39 @@
import express from 'express';
import {Router, Request, Response, NextFunction} from 'express';

import {KubernetesService, PlatformInfo} from './k8s_service';
import {Interval, MetricsService} from './metrics_service';
import {
WorkgroupApi,
SimpleBinding,
} from './api_workgroup';

interface EnvironmentInfo {
namespaces: string[];
namespaces: SimpleBinding[];
platform: PlatformInfo;
user: string;
isClusterAdmin: boolean;
}

const IAP_HEADER = 'X-Goog-Authenticated-User-Email';
const IAP_PREFIX = 'accounts.google.com:';
export const ERRORS = {
operation_not_supported: 'Operation not supported'
};

export function apiError(a: {res: Response, error: string, code?: number}) {
const {res, error} = a;
const code = a.code || 400;
return res.status(code).json({
error,
});
}

export class Api {
private platformInfo: PlatformInfo;

constructor(
private k8sService: KubernetesService,
private metricsService?: MetricsService) {}
private workgroupApi?: WorkgroupApi,
private metricsService?: MetricsService,
) {}

/** Retrieves and memoizes the PlatformInfo. */
private async getPlatformInfo(): Promise<PlatformInfo> {
Expand All @@ -28,43 +44,63 @@ export class Api {
}

/**
* Retrieves user information from headers.
* Supports:
* GCP IAP (https://cloud.google.com/iap/docs/identity-howto)
* Builds EnvironmentInfo for the case with identity awareness
*/
private getUser(req: express.Request): string {
let email = 'anonymous@kubeflow.org';
if (req.header(IAP_HEADER)) {
email = req.header(IAP_HEADER).slice(IAP_PREFIX.length);
}
return email;
private async getProfileAwareEnv(user: User.User): Promise<EnvironmentInfo> {
const [platform, {namespaces, isClusterAdmin}] = await Promise.all([
this.getPlatformInfo(),
this.workgroupApi.getWorkgroupInfo(
user,
),
]);
return {user: user.email, platform, namespaces, isClusterAdmin};
}

/**
* Builds EnvironmentInfo for the case without identity awareness
*/
private async getBasicEnvironment(user: User.User): Promise<EnvironmentInfo> {
const [platform, namespaces] = await Promise.all([
this.getPlatformInfo(),
this.workgroupApi.getAllWorkgroups(user.email),
]);
return {
user: user.email,
platform,
namespaces,
isClusterAdmin: true,
};
}

/**
* Returns the Express router for the API routes.
*/
routes(): express.Router {
return express.Router()
routes(): Router {
return Router()
.get(
'/env-info',
async (req: express.Request, res: express.Response) => {
const [platform, user, namespaces] = await Promise.all([
this.getPlatformInfo(),
this.getUser(req),
this.k8sService.getNamespaces(),
]);
res.json({
platform,
user,
namespaces: namespaces.map((n) => n.metadata.name),
});
async (req: Request, res: Response) => {
try {
if (req.user.hasAuth) {
return res.json(await this.getProfileAwareEnv(req.user));
}
res.json(await this.getBasicEnvironment(req.user));
} catch (err) {
const code = (err.response && err.response.statusCode) || 400;
const error =
err.body || 'Unexpected error getting environment info';
console.log(`Unable to get environment info: ${error}${err.stack?'\n'+err.stack:''}`);
apiError({res, code, error});
}
})
.get(
'/metrics/:type((node|podcpu|podmem))',
async (req: express.Request, res: express.Response) => {
async (req: Request, res: Response) => {
if (!this.metricsService) {
res.sendStatus(405);
return;
return apiError({
res, code: 405,
error: ERRORS.operation_not_supported,
});
}

let interval = Interval.Last15m;
Expand All @@ -89,14 +125,27 @@ export class Api {
})
.get(
'/namespaces',
async (_: express.Request, res: express.Response) => {
async (_: Request, res: Response) => {
res.json(await this.k8sService.getNamespaces());
})
.get(
'/activities/:namespace',
async (req: express.Request, res: express.Response) => {
async (req: Request, res: Response) => {
res.json(await this.k8sService.getEventsForNamespace(
req.params.namespace));
});
})
.use('/workgroup', this.workgroupApi
? this.workgroupApi.routes()
: (req: Request, res: Response, next: NextFunction) => {
next();
}
)
.use((req: Request, res: Response) =>
apiError({
res,
error: `Could not find the route you're looking for`,
code: 404,
})
);
}
}
Loading

0 comments on commit 6558315

Please sign in to comment.