Skip to content
This repository has been archived by the owner on Dec 9, 2024. It is now read-only.

Commit

Permalink
Merge pull request #57 from andresmgot/hostname
Browse files Browse the repository at this point in the history
Allow to use hostname when deploying an http function
  • Loading branch information
andresmgot authored Sep 1, 2017
2 parents bf060c1 + 444bb7c commit ff2743e
Show file tree
Hide file tree
Showing 18 changed files with 396 additions and 73 deletions.
29 changes: 19 additions & 10 deletions deploy/kubelessDeploy.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const helpers = require('../lib/helpers');
const JSZip = require('jszip');
const moment = require('moment');
const path = require('path');
const url = require('url');

function getFunctionDescription(
funcName,
Expand Down Expand Up @@ -102,7 +103,7 @@ function getFunctionDescription(
return funcs;
}

function getIngressDescription(funcName, funcPath) {
function getIngressDescription(funcName, funcPath, funcHost) {
return {
kind: 'Ingress',
metadata: {
Expand All @@ -115,6 +116,7 @@ function getIngressDescription(funcName, funcPath) {
},
spec: {
rules: [{
host: funcHost,
http: {
paths: [{
path: funcPath,
Expand Down Expand Up @@ -225,8 +227,7 @@ class KubelessDeploy {
const loop = setInterval(() => {
if (retries > 3) {
this.serverless.cli.log(
`Giving up, the deployment of the function ${funcName} seems to have failed. ` +
'Check the kubeless-controller pod logs for more info'
`Giving up, unable to retrieve the status of the ${funcName} deployment. `
);
clearInterval(loop);
return;
Expand Down Expand Up @@ -330,17 +331,24 @@ class KubelessDeploy {
});
}

addIngressRuleIfNecessary(funcName, eventType, eventPath, namespace) {
addIngressRuleIfNecessary(funcName, eventType, eventPath, eventHostname, namespace) {
const config = helpers.loadKubeConfig();
const extensions = this.getExtensions(helpers.getConnectionOptions(
helpers.loadKubeConfig(), { namespace })
config, { namespace })
);
const fpath = eventPath || '/';
const hostname = eventHostname ||
`${url.parse(helpers.getKubernetesAPIURL(config)).hostname}.nip.io`;
return new BbPromise((resolve, reject) => {
if (eventType === 'http' && eventPath && eventPath !== '/') {
if (
eventType === 'http' &&
((!_.isEmpty(eventPath) && eventPath !== '/') || !_.isEmpty(eventHostname))
) {
// Found a path to deploy the function
const absolutePath = _.startsWith(eventPath, '/') ?
eventPath :
`/${eventPath}`;
const ingressDef = getIngressDescription(funcName, absolutePath);
const absolutePath = _.startsWith(fpath, '/') ?
fpath :
`/${fpath}`;
const ingressDef = getIngressDescription(funcName, absolutePath, hostname);
extensions.ns.ingress.post({ body: ingressDef }, (err) => {
if (err) {
reject(
Expand Down Expand Up @@ -433,6 +441,7 @@ class KubelessDeploy {
name,
eventType,
event[eventType].path,
this.serverless.service.provider.hostname || event[eventType].hostname,
connectionOptions.namespace
);
})
Expand Down
8 changes: 4 additions & 4 deletions examples/http-custom-path/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ In this example we will deploy a function that will be available under the path
We will need to have an Ingress controller deployed in order to be able to deploy your function in a specific path. If you don't have it yet you can deploy one executing:

```
curl -sL https://raw.githubusercontent.com/kubeless/kubeless/0.0.20/manifests/ingress/ingress-controller.yaml | kubectl create -f -
curl -sL https://raw.githubusercontent.com/kubeless/kubeless/master/manifests/ingress/ingress-controller-http-only.yaml | kubectl create -f -
```

## Deployment
Expand All @@ -22,7 +22,7 @@ Serverless: Waiting for function hello to be fully deployed. Pods status: {"wait
Serverless: Function hello succesfully deployed
```

As we can see in the logs an Ingress Rule has been deployed to run our function at `/hello`. We can know the specific URL in which the function will be listening executing `serverless info`:
As we can see in the logs an Ingress Rule has been deployed to run our function at `/hello`. If no host is specified, by default it will use `API_URL.nip.io` being `API_URL` the URL/IP of the Kubernetes IP. We can know the specific URL in which the function will be listening executing `serverless info`:
```console
$ serverless info
Service Information "hello"
Expand All @@ -34,7 +34,7 @@ Ports:
Target Port: 8080
Node Port: 31444
Function Info
URL: 192.168.99.100/hello
URL: 192.168.99.100.nip.io/hello
Handler: handler.hello
Runtime: python2.7
Trigger: HTTP
Expand All @@ -43,7 +43,7 @@ Dependencies:

Depending on the Ingress configuration the URL may be redirected to use the HTTPS protocol. You can call your function with a browser or executing:
```console
$ curl -kL 192.168.99.100/hello
$ curl 192.168.99.100.nip.io/hello
hello world
```

Expand Down
4 changes: 0 additions & 4 deletions examples/todo-app/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,6 @@ You'll find two directories here. Deploy them in the following order:
1. The [backend](backend) directory contains the whole Serverless service and it's corresponding function code.
2. The [frontend](frontend) directory contains the frontend you can connect to your backend to use the Todos service through your web browser.

# Known issue

When deploying this applicaiton with the default ingress controller the web browser may reject the self-signed certificate used. To be able to use the application go to `https://API_URL` (where API_URL is the URL of your cluster) and add the certificate to the white list.

# Source

This is a modified version of Philipp Muens Todo example from his serverless [book](https://github.com/pmuens/serverless-book/blob/master/06-serverless-by-example/02-a-serverless-todo-application.md). Modified to run on [Kubeless](https://github.com/kubeless/kubeless)
4 changes: 2 additions & 2 deletions examples/todo-app/backend/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ Do the following to deploy and use the backend:
1. Install kubeless following the instruction from the main [README.md](../../../README.md)
2. Install an Ingress Controller in case you still don't have one:
```
$ curl -sL https://raw.githubusercontent.com/kubeless/kubeless/0.0.20/manifests/ingress/ingress-controller.yaml | kubectl create -f -
$ curl -sL https://raw.githubusercontent.com/kubeless/kubeless/master/manifests/ingress/ingress-controller-http-only.yaml | kubectl create -f -
```
3. Deploy a MongoDB service. It will be used to store the state of our application:
```console
$ curl -sL https://raw.githubusercontent.com/bitnami/bitnami-docker-mongodb/master/kubernetes.yml | kubectl create -f -
```
4. Run `npm install` to install the used npm packages
3. Run `serverless deploy` to deploy the `todo` service in our kubernetes cluster
5. Run `serverless deploy` to deploy the `todo` service in our kubernetes cluster
```console
$ serverless deploy
Serverless: Packaging service...
Expand Down
7 changes: 1 addition & 6 deletions examples/todo-app/backend/todos-create.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,7 @@ const url = 'mongodb://mongodb:27017/todo_app';

module.exports = {
create: (req, res) => new Promise((resolve, reject) => {
res.header('Access-Control-Allow-Origin', '*');
const body = [];
req.on('data', d => body.push(d));
req.on('end', () => {
const data = JSON.parse(Buffer.concat(body));
const data = req.body;
data.id = uuid.v1();
data.updatedAt = new Date().getTime();
MongoClient.connect(url, (cerr, db) => {
Expand All @@ -29,7 +25,6 @@ module.exports = {
}
});
}
});
});
}),
};
1 change: 0 additions & 1 deletion examples/todo-app/backend/todos-delete.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ const url = 'mongodb://mongodb:27017/todo_app';

module.exports = {
delete: (req, res) => new Promise((resolve, reject) => {
res.header('Access-Control-Allow-Origin', '*');
MongoClient.connect(url, (err, db) => {
if (err) {
reject(err);
Expand Down
1 change: 0 additions & 1 deletion examples/todo-app/backend/todos-read-all.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ const url = 'mongodb://mongodb:27017/todo_app';

module.exports = {
readAll: (req, res) => new Promise((resolve, reject) => {
res.header('Access-Control-Allow-Origin', '*');
MongoClient.connect(url, (err, db) => {
if (err) {
reject(err);
Expand Down
1 change: 0 additions & 1 deletion examples/todo-app/backend/todos-read-one.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ const url = 'mongodb://mongodb:27017/todo_app';

module.exports = {
readOne: (req, res) => new Promise((resolve, reject) => {
res.header('Access-Control-Allow-Origin', '*');
MongoClient.connect(url, (err, db) => {
if (err) {
reject(err);
Expand Down
65 changes: 30 additions & 35 deletions examples/todo-app/backend/todos-update.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,41 +9,36 @@ const url = 'mongodb://mongodb:27017/todo_app';

module.exports = {
update: (req, res) => new Promise((resolve, reject) => {
res.header('Access-Control-Allow-Origin', '*');
const body = [];
req.on('data', (d) => body.push(d));
req.on('end', () => {
const data = JSON.parse(Buffer.concat(body));
MongoClient.connect(url, (err, db) => {
if (err) {
reject(err);
} else {
db.collection('todos', (errC, doc) => {
if (errC) {
reject(errC);
} else {
doc.find().toArray((ferr, docEntries) => {
if (ferr) {
reject(ferr);
} else {
const entry = _.find(docEntries, e => e.id === data.id);
const newEntry = _.cloneDeep(entry);
_.assign(newEntry, data, { id: uuid.v1(), updatedAt: new Date().getTime() });
doc.updateOne(entry, { $set: newEntry }, (uerr) => {
if (uerr) {
reject(uerr);
} else {
res.end(JSON.stringify(newEntry));
db.close();
resolve();
}
});
}
});
}
});
}
});
const data = req.body;
MongoClient.connect(url, (err, db) => {
if (err) {
reject(err);
} else {
db.collection('todos', (errC, doc) => {
if (errC) {
reject(errC);
} else {
doc.find().toArray((ferr, docEntries) => {
if (ferr) {
reject(ferr);
} else {
const entry = _.find(docEntries, e => e.id === data.id);
const newEntry = _.cloneDeep(entry);
_.assign(newEntry, data, { id: uuid.v1(), updatedAt: new Date().getTime() });
doc.updateOne(entry, { $set: newEntry }, (uerr) => {
if (uerr) {
reject(uerr);
} else {
res.end(JSON.stringify(newEntry));
db.close();
resolve();
}
});
}
});
}
});
}
});
}),
};
4 changes: 2 additions & 2 deletions examples/todo-app/frontend/react-redux/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Do the following to setup and use the frontend

1. Make sure that you've deployed the backend of the `todo` application
2. Run `npm install` to install the used npm packages
3. Go to `app/js/actions/index.js` and update the `API_URL` with the endpoint of your deployed `todo` Serverless service (e.g. `https://192.168.99.100`)
* NOTE: Check the [known issue](../../README.md#known-issue) for web browsers and the self-signed certificate
3. Go to `app/js/actions/index.js` and update the `API_URL` with the endpoint of your deployed `todo` Serverless service (e.g. `http://192.168.99.100.nip.io`)
* Note: You can find the application hostname executing `serverless info` in the backend folder and checking the field `URL` of any function.
4. Run `npm start`
5. Open up a browser on [localhost:8080](http://localhost:8080) and play around with the application
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export const API_URL = 'https://backend';
export const API_URL = 'http://backend';
console.log(process.env);

if (!API_URL) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ import {

export function createTodo(todo) {
return (dispatch) => fetch(`${API_URL}/create`, {
headers: {
'Content-Type': 'application/json'
},
method: 'POST',
body: JSON.stringify(todo),
})
Expand Down Expand Up @@ -59,6 +62,9 @@ export function getTodo(todo) {

export function updateTodo(todo) {
return (dispatch) => fetch(`${API_URL}/update?id=${todo.id}`, {
headers: {
'Content-Type': 'application/json'
},
method: 'POST',
body: JSON.stringify(todo),
})
Expand Down
2 changes: 1 addition & 1 deletion info/kubelessInfo.js
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ class KubelessInfo {
));
let url = null;
if (fIngress) {
url = `${fIngress.status.loadBalancer.ingress[0].ip || 'API_URL'}` +
url = `${fIngress.spec.rules[0].host || 'API_URL'}` +
`${fIngress.spec.rules[0].http.paths[0].path}`;
}
const service = {
Expand Down
Loading

0 comments on commit ff2743e

Please sign in to comment.