Skip to content

Commit

Permalink
feat: add env variables in start session form
Browse files Browse the repository at this point in the history
  • Loading branch information
andre-code committed Oct 11, 2022
1 parent 3e57242 commit d98c125
Show file tree
Hide file tree
Showing 7 changed files with 234 additions and 49 deletions.
3 changes: 2 additions & 1 deletion client/src/api-client/notebook-servers.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ function addNotebookServersMethods(client) {
});
};

client.startNotebook = (namespacePath, projectPath, branchName, commitId, image, options) => {
client.startNotebook = (namespacePath, projectPath, branchName, commitId, image, options, env_variables = {}) => {
const headers = client.getBasicHeaders();
headers.append("Content-Type", "application/json");
const url = `${client.baseUrl}/notebooks/servers`;
Expand All @@ -106,6 +106,7 @@ function addNotebookServersMethods(client) {
commit_sha: commitId,
branch: branchName,
notebook,
environment_variables: env_variables,
...options
};
if (image)
Expand Down
1 change: 1 addition & 0 deletions client/src/model/RenkuModels.js
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,7 @@ const notebooksSchema = new Schema({
commit: { initial: {} },
discard: { initial: false },
options: { initial: {} },
environment_variables: { initial: [{ key: "", value: "" }] },
objectStoresConfiguration: { initial: [] },
includeMergedBranches: { initial: false },
displayedCommits: { initial: 25 },
Expand Down
105 changes: 60 additions & 45 deletions client/src/notebooks/NotebookStart.present.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
* limitations under the License.
*/

import React, { Component, Fragment, useState } from "react";
import React, { Component, Fragment, useEffect, useState } from "react";
import { Link, useLocation } from "react-router-dom";
import {
Badge, Button, ButtonGroup, Col, Collapse, DropdownItem, Form, FormGroup, FormText, Input, Label,
Expand Down Expand Up @@ -46,6 +46,7 @@ import Time from "../utils/helpers/Time";
import LaunchErrorAlert from "./components/LaunchErrorAlert";
import { NotebooksHelper } from "./index";
import { ObjectStoresConfigurationButton, ObjectStoresConfigurationModal } from "./ObjectStoresConfig.present";
import EnvironmentVariables from "./components/EnviromentVariables";


function ProjectSessionLockAlert({ lockStatus }) {
Expand Down Expand Up @@ -123,6 +124,18 @@ function StartNotebookServer(props) {
const location = useLocation();

const [showShareLinkModal, setShowShareLinkModal] = useState(location?.state?.showShareLinkModal ?? false);
const [environmentVariables, setEnvironmentVariables] = useState([ { key: "", value: "" }]);

const setNotebookEnvVariables = (variables) => {
props.handlers.setNotebookEnvVariables(variables);
setEnvironmentVariables(variables);
};

useEffect(() => {
if (props.envVariablesQueryParams)
setEnvironmentVariables(props.envVariablesQueryParams);
}, []); // eslint-disable-line

const toggleShareLinkModal = () => setShowShareLinkModal(!showShareLinkModal);

// Show fetching status when auto-starting
Expand Down Expand Up @@ -152,6 +165,8 @@ function StartNotebookServer(props) {
notebookFilePath={location?.state?.filePath}
toggleShareLinkModal={toggleShareLinkModal}
showShareLinkModal={showShareLinkModal}
setEnvironmentVariables={setNotebookEnvVariables}
environmentVariables={environmentVariables}
{...props} />) :
null;

Expand Down Expand Up @@ -816,54 +831,54 @@ class StartNotebookCommitsOptions extends Component {
}
}

class StartNotebookOptions extends Component {
render() {
const { justStarted } = this.props;
if (justStarted)
return <Label>Starting a new session... <Loader size="14" inline="true" /></Label>;
function StartNotebookOptions(props) {

const { all, fetched } = this.props.notebooks;
const { filters, options } = this.props;
if (!fetched)
return (<Label>Verifying available sessions... <Loader size="14" inline="true" /></Label>);

if (Object.keys(options.global).length === 0 || options.fetching)
return (<Label>Loading session parameters... <Loader size="14" inline="true" /></Label>);

if (Object.keys(all).length > 0) {
const currentCommit = filters.commit?.id;
const currentNotebook = Object.keys(all).find(k => {
const annotations = NotebooksHelper.cleanAnnotations(all[k].annotations, "renku.io");
if (annotations["commit-sha"] === currentCommit)
return true;
return false;
});
if (currentNotebook) {
return [
<StartNotebookOptionsRunning key="notebook-options-running" notebook={all[currentNotebook]}/>,
<ShareLinkSessionModal
key="shareLinkModal"
toggleModal={this.props.toggleShareLinkModal}
showModal={this.props.showShareLinkModal}
notebookFilePath={this.props.notebookFilePath}
{...this.props}
/>
];
}
}
const { justStarted, environmentVariables, setEnvironmentVariables } = props;
if (justStarted)
return <Label>Starting a new session... <Loader size="14" inline="true" /></Label>;

return [
<StartNotebookServerOptions key="options" {...this.props} />,
<ServerOptionLaunch key="button" {...this.props} />,
<ShareLinkSessionModal
key="shareLinkModal"
toggleModal={this.props.toggleShareLinkModal}
showModal={this.props.showShareLinkModal}
{...this.props}
/>
];
const { all, fetched } = props.notebooks;
const { filters, options } = props;
if (!fetched)
return (<Label>Verifying available sessions... <Loader size="14" inline="true" /></Label>);

if (Object.keys(options.global).length === 0 || options.fetching)
return (<Label>Loading session parameters... <Loader size="14" inline="true" /></Label>);

if (Object.keys(all).length > 0) {
const currentCommit = filters.commit?.id;
const currentNotebook = Object.keys(all).find(k => {
const annotations = NotebooksHelper.cleanAnnotations(all[k].annotations, "renku.io");
if (annotations["commit-sha"] === currentCommit)
return true;
return false;
});
if (currentNotebook) {
return [
<StartNotebookOptionsRunning key="notebook-options-running" notebook={all[currentNotebook]}/>,
<ShareLinkSessionModal
key="shareLinkModal"
toggleModal={props.toggleShareLinkModal}
showModal={props.showShareLinkModal}
notebookFilePath={props.notebookFilePath}
{...props}
/>
];
}
}

return [
<StartNotebookServerOptions key="options" {...props} />,
<EnvironmentVariables key="envVariables"
environmentVariables={environmentVariables} setEnvironmentVariables={setEnvironmentVariables} />,
<ServerOptionLaunch key="button" {...props} />,
<ShareLinkSessionModal
key="shareLinkModal"
toggleModal={props.toggleShareLinkModal}
showModal={props.showShareLinkModal}
{...props}
/>
];
}

function Warning(props) {
Expand Down
30 changes: 30 additions & 0 deletions client/src/notebooks/Notebooks.container.js
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,11 @@ class StartNotebookServer extends Component {
this.customNotebookFilePath = currentSearch && this.autostart && currentSearch["notebook"] ?
currentSearch["notebook"] :
null;

const environmentVariables = this.getEnvironmentVariablesFromSearch(currentSearch);
this.customEnvVariables = currentSearch && this.autostart && environmentVariables.length ?
environmentVariables : undefined;

this.state = {
autosavesCommit: false,
autostartReady: false,
Expand All @@ -330,6 +335,7 @@ class StartNotebookServer extends Component {
setIgnorePipeline: this.setIgnorePipeline.bind(this),
setDisplayedCommits: this.setDisplayedCommits.bind(this),
setServerOption: this.setServerOptionFromEvent.bind(this),
setNotebookEnvVariables: this.setNotebookEnvVariables.bind(this),
startServer: this.startServer.bind(this),
setObjectStoresConfiguration: this.setObjectStoresConfiguration.bind(this),
toggleMergedBranches: this.toggleMergedBranches.bind(this),
Expand All @@ -344,6 +350,7 @@ class StartNotebookServer extends Component {
this.coordinator.startNotebookPolling();
this.coordinator.fetchAutosaves();
this.selectNotebookFilePath(this.customNotebookFilePath);
this.setNotebookEnvVariables(this.customEnvVariables);
this.refreshBranches();
}
}
Expand Down Expand Up @@ -417,6 +424,28 @@ class StartNotebookServer extends Component {
this.coordinator.setNotebookFilePath(notebookFilePath);
}

setNotebookEnvVariables(variables) {
const env_variables = {};
if (variables?.length > 0) {
variables.map( variable => {
if (variable.key && variable.value)
env_variables[variable.key] = variable.value;
});
}
this.coordinator.setNotebookEnvironmentVariables(env_variables);
}

getEnvironmentVariablesFromSearch(search) {
const variables = [];
Object.keys(search).map( key => {
if (key.startsWith("env[")) {
const env_key = key.split("env[").pop().split("]").shift();
variables.push({ key: env_key, value: search[key] });
}
});
return variables;
}

async selectBranch(branchName) {
if (this._isMounted) {
// get the useful branchName
Expand Down Expand Up @@ -812,6 +841,7 @@ class StartNotebookServer extends Component {
launchError={this.state.launchError}
lockStatus={this.props.lockStatus}
message={this.props.message}
envVariablesQueryParams={this.customEnvVariables}
{...this.propsToChildProps()}
/>;
}
Expand Down
7 changes: 6 additions & 1 deletion client/src/notebooks/Notebooks.state.js
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,10 @@ class NotebooksCoordinator {
this.model.set(`filters.options.${option}`, value);
}

setNotebookEnvironmentVariables(values) {
this.model.set(`filters.environment_variables`, values);
}

setObjectStoresConfiguration(value) {
this.model.set("filters.objectStoresConfiguration", value);
}
Expand Down Expand Up @@ -1262,8 +1266,9 @@ class NotebooksCoordinator {
const image = projectOptions.image ?
projectOptions.image :
null;
const env_variables = this.model.get("filters.environment_variables");

return this.client.startNotebook(namespace, project, branch, commit, image, options);
return this.client.startNotebook(namespace, project, branch, commit, image, options, env_variables);
}

stopNotebook(serverName, force = false) {
Expand Down
105 changes: 105 additions & 0 deletions client/src/notebooks/components/EnviromentVariables.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/*!
* Copyright 2022 - Swiss Data Science Center (SDSC)
* A partnership between École Polytechnique Fédérale de Lausanne (EPFL) and
* Eidgenössische Technische Hochschule Zürich (ETHZ).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, 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 React, { ChangeEvent } from "react";
import { Button, Col, FormGroup, Input, Label, Row } from "../../utils/ts-wrappers";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faTrash } from "@fortawesome/free-solid-svg-icons";
import { InputLabel } from "../../utils/components/formlabels/FormLabels";

interface EnvironmentVariablesProps {
environmentVariables: EnvVariablesField[];
setEnvironmentVariables: Function;
}

export interface EnvVariablesField {
key: string;
value: string;
}

function EnvironmentVariables({ environmentVariables, setEnvironmentVariables }: EnvironmentVariablesProps) {
const handleFormChange = (index: number, name: "key" | "value", value: string) => {
let data: EnvVariablesField[] = [...environmentVariables];
data[index][name] = value;
setEnvironmentVariables(data);
};

const addFields = () => {
let object = { key: "", value: "" };
setEnvironmentVariables([...environmentVariables, object]);
};

const removeFields = (index: number) => {
let data = [...environmentVariables];
data.splice(index, 1);
setEnvironmentVariables(data);
};

const content = environmentVariables?.map((input, index) => {
return (
<Row key={index}>
<Col xs={5}>
<FormGroup className="field-group">
<Label for="variable" size="sm" className="text-muted d-md-none">Variable</Label>
<Input
name="variable"
value={input.key}
onChange={(event: ChangeEvent<HTMLInputElement>) => handleFormChange(index, "key", event.target.value)}
/>
</FormGroup>
</Col>
<Col xs={5}>
<FormGroup className="field-group">
<Label for="variable" size="sm" className="text-muted d-md-none">Value</Label>
<Input
name="value"
value={input.value}
onChange={(event: ChangeEvent<HTMLInputElement>) => handleFormChange(index, "value", event.target.value)}
/>
</FormGroup>
</Col>
<Col xs={2} className="d-flex align-items-center justify-content-start">
<Button
size="sm"
className="border-0 text-danger bg-transparent mb-3"
onClick={() => removeFields(index)}>
<FontAwesomeIcon icon={faTrash} />
</Button>
</Col>
</Row>
);
});

const header = !environmentVariables?.length ? null :
<Row className="my-2 d-none d-sm-none d-md-flex d-xl-flex">
<Col sm={5}>
<Label className="small">Variable</Label>
</Col>
<Col sm={5}>
<Label className="small">Value</Label>
</Col>
</Row>;

return <div className="mt-3">
<InputLabel text="Environment Variables" isRequired={false} />
{header}
{content}
<div><Button className="btn-outline-rk-green my-1" onClick={addFields}>Add Variable</Button></div>
</div>;
}

export default EnvironmentVariables;
Loading

0 comments on commit d98c125

Please sign in to comment.