Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update to Kubeless 0.5.0 #245

Merged
merged 3 commits into from
Apr 17, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 24 additions & 4 deletions dashboard/src/actions/functions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import { Dispatch } from "redux";
import { createAction, getReturnOfExpression } from "typesafe-actions";

import Function from "../shared/Function";
import { IFunction, IStoreState } from "../shared/types";
import KubelessConfig from "../shared/KubelessConfig";
import { IFunction, IRuntime, IStoreState } from "../shared/types";

export const requestFunctions = createAction("REQUEST_FUNCTIONS");
export const receiveFunctions = createAction("RECEIVE_FUNCTIONS", (functions: IFunction[]) => ({
Expand All @@ -17,9 +18,19 @@ export const setPodName = createAction("SET_FUNCTION_POD_NAME", (name: string) =
name,
type: "SET_FUNCTION_POD_NAME",
}));
const allActions = [requestFunctions, receiveFunctions, selectFunction, setPodName].map(
getReturnOfExpression,
);
export const requestRuntimes = createAction("REQUEST_RUNTIMES");
export const receiveRuntimes = createAction("RECEIVE_RUNTIMES", (runtimes: IRuntime[]) => ({
runtimes,
type: "RECEIVE_RUNTIMES",
}));
const allActions = [
requestFunctions,
receiveFunctions,
selectFunction,
setPodName,
requestRuntimes,
receiveRuntimes,
].map(getReturnOfExpression);
export type FunctionsAction = typeof allActions[number];

export function fetchFunctions(namespace: string) {
Expand Down Expand Up @@ -69,3 +80,12 @@ export function getPodName(fn: IFunction) {
return name;
};
}

export function fetchRuntimes() {
return async (dispatch: Dispatch<IStoreState>) => {
dispatch(requestRuntimes());
const runtimeList = await KubelessConfig.getRuntimes();
dispatch(receiveRuntimes(runtimeList));
return runtimeList;
};
}
3 changes: 3 additions & 0 deletions dashboard/src/components/FunctionIcon/FunctionIcon.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as React from "react";

import nodeIcon from "../../img/node.png";
import phpIcon from "../../img/php.png";
import pythonIcon from "../../img/python.png";
import rubyIcon from "../../img/ruby.png";
import placeholder from "../../placeholder.png";
Expand All @@ -18,6 +19,8 @@ class FunctionIcon extends React.Component<IFunctionIconProps> {
src = nodeIcon;
} else if (runtime.match(/ruby/)) {
src = rubyIcon;
} else if (runtime.match(/php/)) {
src = phpIcon;
} else if (runtime.match(/python/)) {
src = pythonIcon;
}
Expand Down
64 changes: 37 additions & 27 deletions dashboard/src/components/FunctionList/FunctionDeployButton.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import * as crypto from "crypto";
import * as Moniker from "moniker-native";
import * as React from "react";
import AceEditor from "react-ace";
Expand All @@ -7,21 +8,12 @@ import "brace/mode/json";
import "brace/mode/ruby";
import "brace/mode/text";

import { IFunction } from "../../shared/types";

// TODO: fetch this from the API/ConfigMap
const Runtimes = {
"Nodejs (6)": "nodejs6",
"Nodejs (8)": "nodejs8",
"Python (2.7)": "python2.7",
"Python (3.4)": "python3.4",
"Python (3.6)": "python3.6",
"Ruby (2.4)": "ruby2.4",
};
import { IFunction, IRuntime } from "../../shared/types";

interface IFunctionDeployButtonProps {
deployFunction: (n: string, ns: string, spec: IFunction["spec"]) => Promise<any>;
navigateToFunction: (n: string, ns: string) => Promise<any>;
runtimes: IRuntime[];
}

interface IFunctionDeployButtonState {
Expand All @@ -38,11 +30,11 @@ class FunctionDeployButton extends React.Component<
> {
public state: IFunctionDeployButtonState = {
functionSpec: {
checksum: "",
deps: "",
function: "",
handler: "hello.handler",
runtime: "nodejs6",
type: "HTTP",
},
modalIsOpen: false,
name: "",
Expand All @@ -66,6 +58,13 @@ class FunctionDeployButton extends React.Component<

public render() {
const { functionSpec: f, name, namespace } = this.state;
const runtimes = {};
this.props.runtimes.forEach(r => {
r.versions.forEach(version => {
const target = r.ID + version.version;
runtimes[`${r.ID} (${version.version})`] = target;
});
});
return (
<div className="FunctionDeployButton">
<button className="button button-accent" onClick={this.openModal}>
Expand Down Expand Up @@ -105,8 +104,8 @@ class FunctionDeployButton extends React.Component<
<div>
<label htmlFor="runtimes">Runtimes</label>
<select onChange={this.handleRuntimeChange} value={f.runtime}>
{Object.keys(Runtimes).map(r => (
<option key={Runtimes[r]} value={Runtimes[r]}>
{Object.keys(runtimes).map(r => (
<option key={runtimes[r]} value={runtimes[r]}>
{r}
</option>
))}
Expand Down Expand Up @@ -142,7 +141,7 @@ class FunctionDeployButton extends React.Component<

private runtimeToDepsMode() {
const { functionSpec: { runtime } } = this.state;
if (runtime.match(/node/)) {
if (runtime.match(/node|php/)) {
return "json";
} else if (runtime.match(/ruby/)) {
return "ruby";
Expand All @@ -154,14 +153,13 @@ class FunctionDeployButton extends React.Component<

private runtimeToDepsDescription() {
const { functionSpec: { runtime } } = this.state;
if (runtime.match(/node/)) {
return "package.json";
} else if (runtime.match(/ruby/)) {
return "Gemfile";
} else if (runtime.match(/python/)) {
return "requirements.txt";
}
return "";
let deps = "";
this.props.runtimes.forEach(r => {
if (runtime.match(r.ID)) {
deps = r.depName;
}
});
return deps;
}

private openModal = () => {
Expand All @@ -180,6 +178,12 @@ class FunctionDeployButton extends React.Component<
e.preventDefault();
const { deployFunction, navigateToFunction } = this.props;
const { functionSpec, name, namespace } = this.state;
const functionSha256 = crypto
.createHash("sha256")
.update(functionSpec.function, "utf8")
.digest()
.toString("hex");
functionSpec.checksum = `sha256:${functionSha256}`;
try {
await deployFunction(name, namespace, functionSpec);
navigateToFunction(name, namespace);
Expand Down Expand Up @@ -220,18 +224,24 @@ class FunctionDeployButton extends React.Component<
const fnName = handler.split(".").pop();
if (runtime.match(/node/)) {
return `module.exports = {
${fnName}: function(req, res) {
res.end("Hello World");
${fnName}: function(event, context) {
return "Hello World";
}
};
`;
} else if (runtime.match(/ruby/)) {
return `def ${fnName}(request)
return `def ${fnName}(event, context)
"Hello World"
end
`;
} else if (runtime.match(/php/)) {
return `<?php
function ${fnName}($event, $context) {
return "hello world";
}
`;
} else if (runtime.match(/python/)) {
return `def ${fnName}():
return `def ${fnName}(event, context):
return "Hello World"
`;
}
Expand Down
8 changes: 6 additions & 2 deletions dashboard/src/components/FunctionList/FunctionList.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,25 @@
import * as React from "react";

import { IFunction } from "../../shared/types";
import { IFunction, IRuntime } from "../../shared/types";
import { CardGrid } from "../Card";
import FunctionDeployButton from "./FunctionDeployButton";
import FunctionListItem from "./FunctionListItem";

interface IFunctionListProps {
functions: IFunction[];
runtimes: IRuntime[];
namespace: string;
fetchFunctions: (namespace: string) => Promise<any>;
fetchRuntimes: () => Promise<any>;
deployFunction: (n: string, ns: string, spec: IFunction["spec"]) => Promise<any>;
navigateToFunction: (n: string, ns: string) => any;
}

class FunctionList extends React.Component<IFunctionListProps> {
public componentDidMount() {
const { namespace, fetchFunctions } = this.props;
const { namespace, fetchFunctions, fetchRuntimes } = this.props;
fetchFunctions(namespace);
fetchRuntimes();
}

public render() {
Expand All @@ -34,6 +37,7 @@ class FunctionList extends React.Component<IFunctionListProps> {
<FunctionDeployButton
deployFunction={this.props.deployFunction}
navigateToFunction={this.props.navigateToFunction}
runtimes={this.props.runtimes}
/>
</div>
</div>
Expand Down
1 change: 0 additions & 1 deletion dashboard/src/components/FunctionList/FunctionListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ class FunctionListItem extends React.Component<IFunctionListItemProps> {
<div className="ChartListItem__content">
<h3 className="ChartListItem__content__title">{f.metadata.name}</h3>
<div className="ChartListItem__content__info text-r">
<p className="margin-reset type-color-light-blue">type: {f.spec.type}</p>
<span
className={`ChartListItem__content__repo padding-tiny
padding-h-normal type-small margin-t-small`}
Expand Down
3 changes: 3 additions & 0 deletions dashboard/src/components/FunctionView/FunctionEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import * as React from "react";
import AceEditor from "react-ace";

import "brace/mode/javascript";
import "brace/mode/php";
import "brace/mode/python";
import "brace/mode/ruby";
import "brace/theme/xcode";
Expand Down Expand Up @@ -36,6 +37,8 @@ class FunctionEditor extends React.Component<IFunctionEditorProps> {
return "javascript";
} else if (runtime.match(/ruby/)) {
return "ruby";
} else if (runtime.match(/php/)) {
return "php";
} else if (runtime.match(/python/)) {
return "python";
}
Expand Down
1 change: 0 additions & 1 deletion dashboard/src/components/FunctionView/FunctionInfo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ class FunctionInfo extends React.Component<IFunctionInfoProps> {
<ul className="remove-style margin-reset padding-reset type-small">
<li>handler: {f.spec.handler}</li>
<li>runtime: {f.spec.runtime}</li>
<li>type: {f.spec.type}</li>
</ul>
</CardFooter>
</Card>
Expand Down
6 changes: 6 additions & 0 deletions dashboard/src/components/FunctionView/FunctionView.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import * as crypto from "crypto";
import * as React from "react";

import { IDeploymentStatus, IFunction, IResource } from "../../shared/types";
Expand Down Expand Up @@ -143,6 +144,11 @@ class FunctionView extends React.Component<IFunctionViewProps, IFunctionViewStat
...f,
spec: {
...f.spec,
checksum: `sha256:${crypto
.createHash("sha256")
.update(this.state.functionCode, "utf8")
.digest()
.toString("hex")}`,
function: this.state.functionCode,
},
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,11 @@ interface IRouteProps {
};
}

function mapStateToProps(
{ functions: { items } }: IStoreState,
{ match: { params } }: IRouteProps,
) {
function mapStateToProps({ functions }: IStoreState, { match: { params } }: IRouteProps) {
return {
functions: items,
functions: functions.items,
namespace: params.namespace,
runtimes: functions.runtimes,
};
}

Expand All @@ -29,6 +27,7 @@ function mapDispatchToProps(dispatch: Dispatch<IStoreState>) {
deployFunction: (name: string, namespace: string, spec: IFunction["spec"]) =>
dispatch(actions.functions.createFunction(name, namespace, spec)),
fetchFunctions: (namespace: string) => dispatch(actions.functions.fetchFunctions(namespace)),
fetchRuntimes: (namespace: string) => dispatch(actions.functions.fetchRuntimes()),
navigateToFunction: (name: string, namespace: string) =>
dispatch(push(`/functions/${namespace}/${name}`)),
};
Expand Down
Binary file added dashboard/src/img/php.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 8 additions & 1 deletion dashboard/src/reducers/functions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ import { getType } from "typesafe-actions";

import actions from "../actions";
import { FunctionsAction } from "../actions/functions";
import { IFunction } from "../shared/types";
import { IFunction, IRuntime } from "../shared/types";

export interface IFunctionState {
isFetching: boolean;
items: IFunction[];
runtimes: IRuntime[];
selected: {
function?: IFunction;
podName?: string;
Expand All @@ -16,6 +17,7 @@ export interface IFunctionState {
const initialState: IFunctionState = {
isFetching: false,
items: [],
runtimes: [],
selected: {},
};

Expand All @@ -39,6 +41,11 @@ const functionsReducer = (
case getType(actions.functions.setPodName):
const { name } = action;
return { ...state, isFetching: false, selected: { ...state.selected, podName: name } };
case getType(actions.functions.receiveRuntimes):
const { runtimes } = action;
return { ...state, isFetching: false, runtimes };
case getType(actions.functions.requestRuntimes):
return { ...state, isFetching: true };
default:
return state;
}
Expand Down
23 changes: 23 additions & 0 deletions dashboard/src/shared/KubelessConfig.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import axios from "axios";

import { IKubelessConfigMap } from "./types";

export default class Config {
public static async get() {
const { data: config } = await axios.get<IKubelessConfigMap>(Config.SelfLink);
return config;
}

public static async getRuntimes() {
const config = await this.get();
return JSON.parse(config.data["runtime-images"]);
}

private static Name: string = "kubeless-config";
private static Namespace: string = "kubeless";
private static APIBase: string = "/api/kube";
private static APIEndpoint: string = `${Config.APIBase}/api/v1`;
private static SelfLink: string = `${Config.APIEndpoint}/namespaces/${
Config.Namespace
}/configmaps/${Config.Name}`;
}
Loading