Skip to content
This repository was archived by the owner on Dec 9, 2024. It is now read-only.
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
459 changes: 40 additions & 419 deletions deploy/kubelessDeploy.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion examples/event-trigger-python/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ The topic in which the function will be listening is defined in the `events` sec
$ npm install
$ serverless deploy
$ kubeless topic publish --topic hello_topic --data 'hello world!' # push a message into the queue
$ serverless logs -f hello
$ serverless logs -f events
hello world!
```
141 changes: 6 additions & 135 deletions info/kubelessInfo.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,10 @@
'use strict';

const _ = require('lodash');
const Api = require('kubernetes-client');
const BbPromise = require('bluebird');
const chalk = require('chalk');
const getInfo = require('../lib/get-info');
const helpers = require('../lib/helpers');

function toMultipleWords(word) {
return word.replace(/([A-Z])/, ' $1').replace(/^./, (l) => l.toUpperCase());
}

class KubelessInfo {
constructor(serverless, options) {
this.serverless = serverless;
Expand Down Expand Up @@ -62,136 +57,12 @@ class KubelessInfo {
return BbPromise.resolve();
}

formatMessage(service, f, options) {
if (options && !options.color) chalk.enabled = false;
let message = '';
message += `\n${chalk.yellow.underline(`Service Information "${service.name}"`)}\n`;
message += `${chalk.yellow('Cluster IP: ')} ${service.ip}\n`;
message += `${chalk.yellow('Type: ')} ${service.type}\n`;
message += `${chalk.yellow('Ports: ')}\n`;
_.each(service.ports, (port) => {
// Ports can have variable properties
_.each(port, (value, key) => {
message += ` ${chalk.yellow(`${toMultipleWords(key)}: `)} ${value}\n`;
});
});
if (this.options.verbose) {
message += `${chalk.yellow('Metadata')}\n`;
message += ` ${chalk.yellow('Self Link: ')} ${service.selfLink}\n`;
message += ` ${chalk.yellow('UID: ')} ${service.uid}\n`;
message += ` ${chalk.yellow('Timestamp: ')} ${service.timestamp}\n`;
}
message += `${chalk.yellow.underline('Function Info')}\n`;
if (f.url) {
message += `${chalk.yellow('URL: ')} ${f.url}\n`;
}
if (f.annotations && f.annotations['kubeless.serverless.com/description']) {
message += `${chalk.yellow('Description:')} ` +
`${f.annotations['kubeless.serverless.com/description']}\n`;
}
if (f.labels) {
message += `${chalk.yellow('Labels:\n')}`;
_.each(f.labels, (v, k) => {
message += `${chalk.yellow(` ${k}:`)} ${v}\n`;
});
}
message += `${chalk.yellow('Handler: ')} ${f.handler}\n`;
message += `${chalk.yellow('Runtime: ')} ${f.runtime}\n`;
if (f.type === 'PubSub' && !_.isEmpty(f.topic)) {
message += `${chalk.yellow('Topic Trigger:')} ${f.topic}\n`;
} else {
message += `${chalk.yellow('Trigger: ')} ${f.type}\n`;
}
message += `${chalk.yellow('Dependencies: ')} ${f.deps}`;
if (this.options.verbose) {
message += `\n${chalk.yellow('Metadata:')}\n`;
message += ` ${chalk.yellow('Self Link: ')} ${f.selfLink}\n`;
message += ` ${chalk.yellow('UID: ')} ${f.uid}\n`;
message += ` ${chalk.yellow('Timestamp: ')} ${f.timestamp}`;
}
return message;
}

infoFunction(options) {
let counter = 0;
let message = '';
return new BbPromise((resolve) => {
_.each(this.serverless.service.functions, (desc, f) => {
const connectionOptions = helpers.getConnectionOptions(helpers.loadKubeConfig(), {
namespace: desc.namespace || this.serverless.service.provider.namespace,
});
const core = new Api.Core(connectionOptions);
const thirdPartyResources = new Api.ThirdPartyResources(connectionOptions);
const extensions = new Api.Extensions(connectionOptions);
thirdPartyResources.addResource('functions');
core.services.get((err, servicesInfo) => {
if (err) throw new this.serverless.classes.Error(err);
thirdPartyResources.ns.functions.get((ferr, functionsInfo) => {
if (ferr) throw new this.serverless.classes.Error(ferr);
extensions.ns.ingress.get((ierr, ingressInfo) => {
if (ierr) throw this.serverless.classes.Error(ierr);
const fDesc = _.find(functionsInfo.items, item => item.metadata.name === f);
const functionService = _.find(
servicesInfo.items,
(service) => (
service.metadata.labels &&
service.metadata.labels.function === f
)
);
if (_.isEmpty(functionService) || _.isEmpty(fDesc)) {
this.serverless.cli.consoleLog(
`Not found any information about the function "${f}"`
);
} else {
const fIngress = _.find(ingressInfo.items, item => (
item.metadata.labels && item.metadata.labels.function === f
));
let url = null;
if (fIngress) {
url = `${fIngress.spec.rules[0].host || 'API_URL'}` +
`${fIngress.spec.rules[0].http.paths[0].path}`;
}
const service = {
name: functionService.metadata.name,
ip: functionService.spec.clusterIP,
type: functionService.spec.type,
ports: functionService.spec.ports,
selfLink: functionService.metadata.selfLink,
uid: functionService.metadata.uid,
timestamp: functionService.metadata.creationTimestamp,
};
const func = {
name: f,
url,
handler: fDesc.spec.handler,
runtime: fDesc.spec.runtime,
topic: fDesc.spec.topic,
type: fDesc.spec.type,
deps: fDesc.spec.deps,
annotations: fDesc.metadata.annotations,
labels: fDesc.metadata.labels,
selfLink: fDesc.metadata.selfLink,
uid: fDesc.metadata.uid,
timestamp: fDesc.metadata.creationTimestamp,
};
message += this.formatMessage(
service,
func,
_.defaults({}, options, { color: true })
);
}
counter++;
if (counter === _.keys(this.serverless.service.functions).length) {
if (!_.isEmpty(message)) {
this.serverless.cli.consoleLog(message);
}
resolve(message);
}
});
});
});
});
});
return getInfo(this.serverless.service.functions, _.defaults({}, options, {
namespace: this.serverless.service.provider.namespace,
verbose: this.options.verbose,
log: this.serverless.cli.consoleLog,
}));
}
}

Expand Down
102 changes: 12 additions & 90 deletions invoke/kubelessInvoke.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@
const _ = require('lodash');
const BbPromise = require('bluebird');
const path = require('path');
const request = require('request');
const helpers = require('../lib/helpers');
const invoke = require('../lib/invoke');

class KubelessInvoke {
constructor(serverless, options) {
Expand All @@ -36,46 +36,7 @@ class KubelessInvoke {
};
}

getData(data) {
let result = null;
const d = data || this.options.data;
try {
if (!_.isEmpty(d)) {
try {
// Try to parse data as JSON
result = {
body: JSON.parse(d),
json: true,
};
} catch (e) {
// Assume data is a string
result = {
body: d,
};
}
} else if (this.options.path) {
const absolutePath = path.isAbsolute(this.options.path) ?
this.options.path :
path.join(this.serverless.config.servicePath, this.options.path);
if (!this.serverless.utils.fileExistsSync(absolutePath)) {
throw new this.serverless.classes.Error('The file you provided does not exist.');
}
result = {
body: this.serverless.utils.readFileSync(absolutePath),
json: true,
};
}
} catch (e) {
throw new this.serverless.classes.Error(
`Unable to parse data given in the arguments: \n${e.message}`
);
}
return result;
}

validate() {
// Parse data to ensure it has a correct format
this.getData();
const unsupportedOptions = ['stage', 'region', 'type'];
helpers.warnUnsupportedOptions(
unsupportedOptions,
Expand All @@ -93,58 +54,19 @@ class KubelessInvoke {
invokeFunction(func, data) {
const f = func || this.options.function;
this.serverless.cli.log(`Calling function: ${f}...`);
const config = helpers.loadKubeConfig();
const APIRootUrl = helpers.getKubernetesAPIURL(config);
const namespace = this.serverless.service.functions[f].namespace ||
this.serverless.service.provider.namespace ||
helpers.getDefaultNamespace(config);
const url = `${APIRootUrl}/api/v1/proxy/namespaces/${namespace}/services/${f}/`;
const connectionOptions = Object.assign(
helpers.getConnectionOptions(helpers.loadKubeConfig()),
{ url }
);
const requestData = this.getData(data);
if (this.serverless.service.functions[f].sequence) {
let promise = null;
_.each(this.serverless.service.functions[f].sequence.slice(), sequenceFunction => {
if (promise) {
promise = promise.then(
result => this.invokeFunction(sequenceFunction, result.body)
);
} else {
promise = this.invokeFunction(sequenceFunction, data);
}
});
return new BbPromise((resolve, reject) => promise.then(
(response) => resolve(response),
err => reject(err)
));
let dataPath = this.options.path;
if (dataPath && !path.isAbsolute(dataPath)) {
dataPath = path.join(this.serverless.config.servicePath, dataPath);
}
return new BbPromise((resolve, reject) => {
const parseReponse = (err, response) => {
if (err) {
reject(new this.serverless.classes.Error(err.message, err.statusCode));
} else {
if (response.statusCode !== 200) {
reject(new this.serverless.classes.Error(response.statusMessage, response.statusCode));
}
resolve(response);
}
};
if (_.isEmpty(requestData)) {
// There is no data to send, sending a GET request
request.get(connectionOptions, parseReponse);
} else {
// Sending request data with a POST
request.post(
Object.assign(
connectionOptions,
requestData
),
parseReponse
);
return invoke(
f,
data || this.options.data,
_.map(this.serverless.service.functions, (desc, ff) => _.assign({}, desc, { id: ff })),
{
namespace: this.serverless.service.provider.namespace,
path: dataPath,
}
});
);
}

log(response) {
Expand Down
Loading