Skip to content

Commit

Permalink
Cluster management front end UI (#66)
Browse files Browse the repository at this point in the history
Cluster management front end UI

Signed-off-by: Mohammed Abdi <mohammed.munir.abdi@ibm.com>
  • Loading branch information
mamy-CS authored and lumjjb committed Aug 9, 2021
1 parent 60263db commit cb0bab9
Show file tree
Hide file tree
Showing 24 changed files with 3,328 additions and 1,624 deletions.
3,439 changes: 1,887 additions & 1,552 deletions tornjak-frontend/package-lock.json

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions tornjak-frontend/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import "bootstrap/dist/css/bootstrap.min.css";
import { BrowserRouter as Router, Route } from "react-router-dom";
import NavigationBar from "./components/navbar";
import SelectServer from "./components/select-server";
import ClusterList from "./components/cluster-list";
import ClusterManagement from "./components/cluster-management";
import AgentList from "./components/agent-list";
import CreateJoinToken from "./components/agent-create-join-token";
import EntryList from "./components/entry-list";
Expand All @@ -27,10 +29,12 @@ function App() {
<SelectServer />
<br />
<Route path="/" exact component={AgentList} />
<Route path="/clusters" exact component={ClusterList} />
<Route path="/agents" exact component={AgentList} />
<Route path="/entries" exact component={EntryList} />
<Route path="/entry/create" exact component={EntryCreate} />
<Route path="/agent/createjointoken" exact component={CreateJoinToken} />
<Route path="/cluster/clustermanagement" exact component={ClusterManagement} />
<Route path="/tornjak/serverinfo" exact component={TornjakServerInfo} />
<Route path="/server/manage" exact component={ServerManagement} />
<br /><br /><br />
Expand Down
2 changes: 1 addition & 1 deletion tornjak-frontend/src/components/agent-create-join-token.js
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ class CreateJoinToken extends Component {
return
}
axios.post(endpoint, cjtData)
.then(res => this.setState({ message: "Requst:" + JSON.stringify(cjtData,null, ' ')+ "\n\nSuccess:" + JSON.stringify(res.data, null, ' ')}))
.then(res => this.setState({ message: "Request:" + JSON.stringify(cjtData,null, ' ')+ "\n\nSuccess:" + JSON.stringify(res.data, null, ' ')}))
.catch(err => this.setState({ message: "ERROR:" + err + (typeof (err.response) !== "undefined" ? err.response.data : "")}))
//window.location = '/';
}
Expand Down
7 changes: 3 additions & 4 deletions tornjak-frontend/src/components/agent-list.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React, { Component } from 'react';
import { connect } from 'react-redux';
import IsManager from './is_manager';
import Table from "tables/agents-list-table";
import { selectors, workloadSelectors} from './selector-info';
import { selectors, workloadSelectors} from '../data/data';
import TornjakApi from './tornjak-api-helpers';
import {
serverSelectedFunc,
Expand Down Expand Up @@ -71,7 +71,7 @@ class AgentList extends Component {
if(prevProps.globalTornjakServerInfo !== this.props.globalTornjakServerInfo)
{
this.TornjakApi.populateServerInfo(this.props.globalTornjakServerInfo, this.props.serverInfoUpdateFunc);
this.TornjakApi.refreshSelectorsState(this.props.globalServerSelected, this.props.agentworkloadSelectorInfoFunc);
this.TornjakApi.refreshLocalSelectorsState(this.props.agentworkloadSelectorInfoFunc);
}
}
}
Expand All @@ -92,15 +92,14 @@ class AgentList extends Component {
render() {
return (
<div>
<h3>Agent List</h3>
<h3>Agents List</h3>
{this.props.globalErrorMessage !== "OK" &&
<div className="alert-primary" role="alert">
<pre>
{this.props.globalErrorMessage}
</pre>
</div>
}
{IsManager}
<br /><br />
<div className="indvidual-list-table">
<Table data={this.agentList()} id="table-1" />
Expand Down
328 changes: 328 additions & 0 deletions tornjak-frontend/src/components/cluster-create.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,328 @@
import React, { Component } from 'react';
import { connect } from 'react-redux';
import axios from 'axios';
import { Dropdown, TextInput, MultiSelect, TextArea } from 'carbon-components-react';
import GetApiServerUri from './helpers';
import IsManager from './is_manager';
import TornjakApi from './tornjak-api-helpers';
import { clusterType } from '../data/data';
import './style.css';
import {
clusterTypeInfoFunc,
serverSelectedFunc,
selectorInfoFunc,
agentsListUpdateFunc,
tornjakMessageFunc,
tornjakServerInfoUpdateFunc,
serverInfoUpdateFunc
} from 'redux/actions';

class ClusterCreate extends Component {
constructor(props) {
super(props);
this.TornjakApi = new TornjakApi();
this.onChangeClusterName = this.onChangeClusterName.bind(this);
this.onChangeClusterType = this.onChangeClusterType.bind(this);
this.onChangeManualClusterType = this.onChangeManualClusterType.bind(this);
this.onChangeClusterDomainName = this.onChangeClusterDomainName.bind(this);
this.onChangeClusterManagedBy = this.onChangeClusterManagedBy.bind(this);
this.onChangeAgentsList = this.onChangeAgentsList.bind(this);
this.onSubmit = this.onSubmit.bind(this);

this.state = {
clusterName: "",
clusterType: "",
clusterDomainName: "",
clusterManagedBy: "",
clusterAgentsList: "",
clusterTypeList: this.props.clusterTypeList,
clusterTypeManualEntryOption: "----Select this option and Enter Custom Cluster Type Below----",
clusterTypeManualEntry: false,
message: "",
statusOK: "",
selectedServer: "",
agentsListDisplay: "Select Agents",
assignedAgentsListDisplay: "",
}
}

componentDidMount() {
this.props.clusterTypeInfoFunc(clusterType); //set cluster type info
if (IsManager) {
if (this.props.globalServerSelected !== "" && (this.props.globalErrorMessage === "OK" || this.props.globalErrorMessage === "")) {
this.TornjakApi.populateAgentsUpdate(this.props.globalServerSelected, this.props.agentsListUpdateFunc, this.props.tornjakMessageFunc);
this.TornjakApi.populateTornjakServerInfo(this.props.globalServerSelected, this.props.tornjakServerInfoUpdateFunc, this.props.tornjakMessageFunc);
this.setState({ selectedServer: this.props.globalServerSelected });
}
} else {
this.TornjakApi.populateLocalAgentsUpdate(this.props.agentsListUpdateFunc, this.props.tornjakMessageFunc);
this.TornjakApi.populateLocalTornjakServerInfo(this.props.tornjakServerInfoUpdateFunc, this.props.tornjakMessageFunc);
this.TornjakApi.populateServerInfo(this.props.globalTornjakServerInfo, this.props.serverInfoUpdateFunc);
}
}

componentDidUpdate(prevProps, prevState) {
if (IsManager) {
if (prevProps.globalServerSelected !== this.props.globalServerSelected) {
this.setState({ selectedServer: this.props.globalServerSelected });
}
}
}

onChangeAgentsList = selected => {
var sid = selected.selectedItems, agents = "", agentsDisplay = "", assignedAgentsDisplay = "";
let localAgentsIdList = [];
for (let i = 0; i < sid.length; i++) {
localAgentsIdList[i] = sid[i].label;
}
agents = localAgentsIdList;
agentsDisplay = localAgentsIdList.toString();
assignedAgentsDisplay = localAgentsIdList.join("\n");
if (agentsDisplay.length === 0) {
agentsDisplay = "Select Agents"
}
this.setState({
clusterAgentsList: agents,
agentsListDisplay: agentsDisplay,
assignedAgentsListDisplay: assignedAgentsDisplay,
});
}

onChangeClusterName(e) {
var sid = e.target.value;
this.setState({
clusterName: sid
});
return
}

onChangeClusterType = selected => {
var sid = selected.selectedItem;
if (sid === this.state.clusterTypeManualEntryOption) {
this.setState({
clusterTypeManualEntry: true,
clusterType: sid,
});
} else {
this.setState({
clusterType: sid,
clusterTypeManualEntry: false
});
}
return
}

onChangeManualClusterType(e) {
var sid = e.target.value;
this.setState({
clusterType: sid
});
return
}

onChangeClusterDomainName(e) {
var sid = e.target.value;
this.setState({
clusterDomainName: sid
});
return
}

onChangeClusterManagedBy(e) {
var sid = e.target.value;
this.setState({
clusterManagedBy: sid
});
return
}

getApiEntryCreateEndpoint() {
if (!IsManager) {
return GetApiServerUri('/api/tornjak/clusters/create')
} else if (IsManager && this.state.selectedServer !== "") {
return GetApiServerUri('/manager-api/tornjak/clusters/create') + "/" + this.state.selectedServer
} else {
this.setState({ message: "Error: No server selected" })
return ""
}
}

onSubmit(e) {
var agentsList = [];
e.preventDefault();

if (this.state.clusterName.length === 0) {
this.setState({ message: "ERROR: Cluster Name Can Not Be Empty - Enter Cluster Name" });
return
}

if (this.state.clusterType.length === 0) {
this.setState({ message: "ERROR: Cluster Type Can Not Be Empty - Choose/ Enter Cluster Type" });
return
}

if (this.state.clusterAgentsList !== "") {
agentsList = this.state.clusterAgentsList;
}

var cjtData = {
"cluster": {
"name": this.state.clusterName,
"platformType": this.state.clusterType,
"domainName": this.state.clusterDomainName,
"managedBy": this.state.clusterManagedBy,
"agentsList": agentsList
}
}

let endpoint = this.getApiEntryCreateEndpoint();
if (endpoint === "") {
return
}
axios.post(endpoint, cjtData)
.then(
res => this.setState({
message: "Request:" + JSON.stringify(cjtData, null, ' ') + "\n\nSuccess:" + JSON.stringify(res.data, null, ' '),
statusOK: "OK",
})
)
.catch(
err => this.setState({
message: "ERROR:" + err.response.data,
statusOK: "ERROR"
})
)
}
render() {
const ClusterType = this.props.clusterTypeList;
return (
<div>
<div className="cluster-create">
<div className="create-create-title">
<h3>Create Cluster</h3>
</div>
<form onSubmit={this.onSubmit}>
<br /><br />
<div className="entry-form">
<div className="clustername-input-field">
<TextInput
aria-required="true"
helperText="i.e. exampleabc"
id="clusterNameInputField"
invalidText="A valid value is required - refer to helper text below"
labelText="Cluster Name [*required]"
placeholder="Enter CLUSTER NAME"
onChange={this.onChangeClusterName}
required />
</div>
<div className="clustertype-drop-down">
<Dropdown
aria-required="true"
ariaLabel="clustertype-drop-down"
id="clustertype-drop-down"
items={ClusterType}
label="Select Cluster Type"
titleText="Cluster Type [*required]"
onChange={this.onChangeClusterType}
required />
<p className="cluster-helper">i.e. Kubernetes, VMs...</p>
</div>
{this.state.clusterTypeManualEntry === true &&
<div className="clustertype-manual-input-field">
<TextInput
helperText="i.e. Kubernetes, VMs..."
id="clusterTypeManualInputField"
invalidText="A valid value is required - refer to helper text below"
labelText="Cluster Type - Manual Entry"
placeholder="Enter Cluster Type"
onChange={(e) => {
this.onChangeManualClusterType(e);
}}
/>
</div>}
<div className="cluster-domain-name-input-field">
<TextInput
helperText="i.e. example.org"
id="clusterDomainNameInputField"
invalidText="A valid value is required - refer to helper text below"
labelText="Cluster Domain Name/ URL"
placeholder="Enter CLUSTER DOMAIN NAME/ URL"
onChange={this.onChangeClusterDomainName}
/>
</div>
<div className="cluster-managed-by-input-field">
<TextInput
helperText="i.e. person-A"
id="clusterNameInputField"
invalidText="A valid value is required - refer to helper text below"
labelText="Cluster Managed By"
placeholder="Enter CLUSTER MANAGED BY"
onChange={this.onChangeClusterManagedBy}
/>
</div>
<div className="agents-multiselect">
<MultiSelect.Filterable
titleText="Assign Agents To Cluster"
helperText="i.e. spiffe://example.org/agent/myagent1..."
placeholder={this.state.agentsListDisplay}
ariaLabel="selectors-multiselect"
id="selectors-multiselect"
items={this.props.agentsList}
label={this.state.agentsListDisplay}
onChange={this.onChangeAgentsList}
/>
</div>
<div className="selectors-textArea">
<TextArea
cols={50}
helperText="i.e. spiffe://example.org/agent/myagent1..."
id="selectors-textArea"
invalidText="A valid value is required"
labelText="Assigned Agents"
placeholder="Assigned agents will be populated here - Refer to Assign Agents To Cluster"
defaultValue={this.state.assignedAgentsListDisplay}
rows={8}
disabled
/>
</div>
<div className="form-group">
<input type="submit" value="Create Cluster" className="btn btn-primary" />
</div>
<div>
{this.state.statusOK === "OK" &&
<p className="success-message">--ENTRY SUCCESSFULLY CREATED--</p>
}
{(this.state.statusOK === "ERROR") &&
<p className="failed-message">--ENTRY CREATION FAILED--</p>
}
</div>
<div className="alert-primary" role="alert">
<pre>
{this.state.message}
</pre>
</div>
</div>
</form>
</div>
</div>
)
}
}


const mapStateToProps = (state) => ({
globalClusterTypeInfo: state.clusters.globalClusterTypeInfo,
globalServerSelected: state.servers.globalServerSelected,
globalSelectorInfo: state.servers.globalSelectorInfo,
globalAgentsList: state.agents.globalAgentsList,
globalServerInfo: state.servers.globalServerInfo,
globalTornjakServerInfo: state.servers.globalTornjakServerInfo,
globalErrorMessage: state.tornjak.globalErrorMessage,
globalWorkloadSelectorInfo: state.servers.globalWorkloadSelectorInfo,
globalAgentsWorkLoadAttestorInfo: state.agents.globalAgentsWorkLoadAttestorInfo,
})

export default connect(
mapStateToProps,
{ clusterTypeInfoFunc, serverSelectedFunc, selectorInfoFunc, agentsListUpdateFunc, tornjakMessageFunc, tornjakServerInfoUpdateFunc, serverInfoUpdateFunc }
)(ClusterCreate)
Loading

0 comments on commit cb0bab9

Please sign in to comment.