Skip to content

Commit

Permalink
[front-end-server] Allow viewer:tensorboard podTemplateSpec to be cus…
Browse files Browse the repository at this point in the history
…tomizable (kubeflow#1906)

* Allow front-end server to provide custom viewer podTemplateSpec via path/configmap

* Fix JSON.parse input to string
  • Loading branch information
eterna2 authored and k8s-ci-robot committed Aug 22, 2019
1 parent 06b7ad6 commit af456bc
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 26 deletions.
50 changes: 26 additions & 24 deletions frontend/server/k8s-helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,30 @@ const viewerGroup = 'kubeflow.org';
const viewerVersion = 'v1beta1';
const viewerPlural = 'viewers';

export const defaultPodTemplateSpec = {
spec: {
containers: [{
env: [{
name: "GOOGLE_APPLICATION_CREDENTIALS",
value: "/secret/gcp-credentials/user-gcp-sa.json"
}],
volumeMounts: [{
name: "gcp-credentials",
mountPath: "/secret/gcp-credentials/user-gcp-sa.json",
readOnly: true
}]
}],
volumes: [{
name: "gcp-credentials",
volumeSource: {
secret: {
secretName: "user-gcp-sa"
}
}
}]
}
}

export const isInCluster = fs.existsSync(namespaceFilePath);

if (isInCluster) {
Expand All @@ -49,7 +73,7 @@ function getNameOfViewerResource(logdir: string): string {
* Create Tensorboard instance via CRD with the given logdir if there is no
* existing Tensorboard instance.
*/
export async function newTensorboardInstance(logdir: string): Promise<void> {
export async function newTensorboardInstance(logdir: string, podTemplateSpec: Object = defaultPodTemplateSpec): Promise<void> {
if (!k8sV1CustomObjectClient) {
throw new Error('Cannot access kubernetes Custom Object API');
}
Expand All @@ -70,29 +94,7 @@ export async function newTensorboardInstance(logdir: string): Promise<void> {
tensorboardSpec: {
logDir: logdir,
},
podTemplateSpec: {
spec: {
containers: [{
env: [{
name: "GOOGLE_APPLICATION_CREDENTIALS",
value: "/secret/gcp-credentials/user-gcp-sa.json"
}],
volumeMounts: [{
name: "gcp-credentials",
mountPath: "/secret/gcp-credentials/user-gcp-sa.json",
readOnly: true
}]
}],
volumes: [{
name: "gcp-credentials",
volumeSource: {
secret: {
secretName: "user-gcp-sa"
}
}
}]
}
}
podTemplateSpec
}
};
await k8sV1CustomObjectClient.createNamespacedCustomObject(viewerGroup,
Expand Down
11 changes: 9 additions & 2 deletions frontend/server/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ import proxyMiddleware from './proxy-middleware';
import { Storage } from '@google-cloud/storage';
import {Stream} from 'stream';

import {loadJSON} from './utils';

const BASEPATH = '/pipeline';

/** All configurable environment variables can be found here. */
Expand All @@ -50,7 +52,9 @@ const {
/** API service will listen to this host */
ML_PIPELINE_SERVICE_HOST = 'localhost',
/** API service will listen to this port */
ML_PIPELINE_SERVICE_PORT = '3001'
ML_PIPELINE_SERVICE_PORT = '3001',
/** path to viewer:tensorboard pod template spec */
VIEWER_TENSORBOARD_POD_TEMPLATE_SPEC_PATH
} = process.env;

/** construct minio endpoint from host and namespace (optional) */
Expand All @@ -75,6 +79,9 @@ const s3Client = new MinioClient({
secretKey: AWS_SECRET_ACCESS_KEY,
} as any);

/** pod template spec to use for viewer crd */
const podTemplateSpec = loadJSON(VIEWER_TENSORBOARD_POD_TEMPLATE_SPEC_PATH, k8sHelper.defaultPodTemplateSpec)

const app = express() as Application;

app.use(function (req, _, next) {
Expand Down Expand Up @@ -278,7 +285,7 @@ const createTensorboardHandler = async (req, res) => {
}

try {
await k8sHelper.newTensorboardInstance(logdir);
await k8sHelper.newTensorboardInstance(logdir, podTemplateSpec);
const tensorboardAddress = await k8sHelper.waitForTensorboardInstance(logdir, 60 * 1000);
res.send(tensorboardAddress);
} catch (err) {
Expand Down
10 changes: 10 additions & 0 deletions frontend/server/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
// 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 {readFileSync} from 'fs';

export function equalArrays(a1: any[], a2: any[]): boolean {
if (!Array.isArray(a1) || !Array.isArray(a2) || a1.length !== a2.length) {
Expand All @@ -32,3 +33,12 @@ export function generateRandomString(length: number): string {
}
return str;
}

export function loadJSON(filepath: string, defaultValue: Object = {}): Object {
if (!filepath) return defaultValue;
try {
return JSON.parse(readFileSync(filepath, "utf-8"))
} catch (error) {
return defaultValue;
}
}

0 comments on commit af456bc

Please sign in to comment.