From 2253ce284fe428bf7cda3d5b85b31349c91acf40 Mon Sep 17 00:00:00 2001 From: faxioman Date: Tue, 19 May 2020 10:22:27 +0200 Subject: [PATCH] Use of the kubeless http trigger instead of a custom created ingress (#206) --- lib/deploy.js | 108 ++++++++++++++++------- lib/get-info.js | 28 +++--- lib/ingress.js | 169 ------------------------------------ lib/remove.js | 29 +++---- package.json | 2 +- test/kubelessDeploy.test.js | 67 +++++++++----- test/kubelessInfo.test.js | 34 +++----- test/kubelessRemove.test.js | 50 ----------- test/lib/mocks.js | 48 ++++------ 9 files changed, 178 insertions(+), 357 deletions(-) delete mode 100644 lib/ingress.js diff --git a/lib/deploy.js b/lib/deploy.js index 32fc2d2..ea1ff64 100644 --- a/lib/deploy.js +++ b/lib/deploy.js @@ -21,8 +21,8 @@ const BbPromise = require('bluebird'); const Api = require('kubernetes-client'); const CRD = require('./crd'); const helpers = require('./helpers'); -const ingressHelper = require('./ingress'); const moment = require('moment'); +const url = require('url'); /** Supported message queue types */ const MQTypes = Object.freeze({ kafka: 'Kafka', nats: 'NATS' }); @@ -434,6 +434,49 @@ function deployScheduleTrigger(name, namespace, schedule, options) { }); } +function deployHttpTrigger(name, namespace, path, options) { + if (options.ingress.enableTlsAcme && options.ingress.tlsSecretName) { + throw new Error('Cannot specify both enableTlsAcme and tlsSecretName'); + } + // fix for issue https://github.com/kubeless/kubeless/issues/1124 + const baseAnnotations = { + 'kubernetes.io/ingress.class': options.ingress.class || 'nginx', + }; + // end fix + const trigger = { + apiVersion: 'kubeless.io/v1beta1', + kind: 'HTTPTrigger', + metadata: { + name, + namespace, + labels: { + 'created-by': 'kubeless', + }, + annotations: Object.assign(baseAnnotations, options.ingress.additionalAnnotations), + }, + spec: { + 'basic-auth-secret': options.ingress.basicAuthSecretName || '', + 'cors-enable': options.ingress.cors || false, + 'function-name': name, + gateway: options.ingress.class || 'nginx', // not working: https://github.com/kubeless/kubeless/issues/1124 + 'host-name': options.hostname, + path: path.replace(/^\//, ''), + tls: options.ingress.enableTlsAcme || false, + 'tls-secret': options.ingress.tlsSecretName || '', + }, + }; + const httpTriggerApi = new CRD('apis/kubeless.io', 'v1beta1', namespace, 'httptriggers'); + options.log(`Creating http trigger for: ${trigger.metadata.name}`); + return httpTriggerApi.getItem(trigger.metadata.name) + .then((res) => { + if (res.code === 404) { + return httpTriggerApi.post({ body: trigger }); + } + options.log('Updating existing http trigger'); + return httpTriggerApi.put(trigger.metadata.name, { body: trigger }); + }); +} + function deployFunction(f, namespace, runtime, contentType, options) { const functionsApi = new CRD('apis/kubeless.io', 'v1beta1', namespace, 'functions'); let environment = options.environment ? parseEnv(options.environment) : null; @@ -527,10 +570,27 @@ function handleMQTDeployment(trigger, name, namespace, options) { } function deployTrigger(event, funcName, namespace, service, options) { - let triggerPromise = new BbPromise((r) => r()); + let triggerPromise; + let config; + let defaultHostname; switch (event.type) { case 'http': - // TODO: Rely on Kubeless httptrigger object when it support paths + if (_.isEmpty(event.path)) { + throw new Error('You should specify a path for the trigger event'); + } + config = helpers.loadKubeConfig(); + // eslint-disable-next-line max-len + defaultHostname = `${url.parse(helpers.getKubernetesAPIURL(config)).hostname}.${options.defaultDNSResolution || 'nip.io'}`; + triggerPromise = deployHttpTrigger( + funcName, + namespace, + event.path, + { + log: options.log, + ingress: options.ingress || {}, + hostname: event.hostname || options.hostname || defaultHostname, + } + ); break; case 'trigger': if (_.isEmpty(event.trigger)) { @@ -586,38 +646,20 @@ function deploy(functions, runtime, service, options) { if (res.code && res.code !== 200) { errors.push(res.message); } - counter++; - helpers.checkFinished(counter, elements, errors, resolve, reject, { - onSuccess: () => ingressHelper.addIngressRuleIfNecessary(service, functions, { - verbose: options.verbose, - log: options.log, - hostname: options.hostname, - defaultDNSResolution: options.defaultDNSResolution, - ingress: options.ingress, - namespace: ns, - }), + _.each(description.events, event => { + deployTrigger(event, description.id, ns, service, opts) + .catch(triggerErr => errors.push(triggerErr)) + .then((tr) => { + if (tr && tr.code && tr.code !== 200) { + errors.push(tr.message); + } + counter++; + helpers.checkFinished(counter, elements, errors, resolve, reject); + }); }); + counter++; + helpers.checkFinished(counter, elements, errors, resolve, reject); }); - _.each(description.events, event => { - deployTrigger(event, description.id, ns, service, opts) - .catch(triggerErr => errors.push(triggerErr)) - .then((res) => { - if (res && res.code && res.code !== 200) { - errors.push(res.message); - } - counter++; - helpers.checkFinished(counter, elements, errors, resolve, reject, { - onSuccess: () => ingressHelper.addIngressRuleIfNecessary(service, functions, { - verbose: options.verbose, - log: options.log, - hostname: options.hostname, - defaultDNSResolution: options.defaultDNSResolution, - ingress: options.ingress, - namespace: ns, - }), - }); - }); - }); } else { counter++; opts.log( diff --git a/lib/get-info.js b/lib/get-info.js index aad15f2..c9cfb81 100644 --- a/lib/get-info.js +++ b/lib/get-info.js @@ -93,13 +93,24 @@ function info(functions, service, options) { }); const core = new Api.Core(connectionOptions); const functionsApi = new CRD('apis/kubeless.io', 'v1beta1', namespace, 'functions'); - const extensions = new Api.Extensions(connectionOptions); + const httpTriggerApi = new CRD('apis/kubeless.io', 'v1beta1', namespace, 'httptriggers'); core.ns.services.get((err, servicesInfo) => { if (err) reject(new Error(err)); functionsApi.getItem(f).catch((ferr) => reject(ferr)).then(fDesc => { - extensions.ns.ingress(service).get((ierr, fIngress) => { - if (ierr && !ierr.message.match(/not found/)) { - reject(new Error(ierr)); + let tErr; + let httpTriggerDesc; + httpTriggerApi.getItem(f).catch((ex) => { + tErr = ex; + }).then(res => { + if (res && res.kind === 'Status') { + tErr = res; + } else { + httpTriggerDesc = res; + } + }).finally(() => { + // eslint-disable-next-line max-len + if (tErr && (tErr.code && tErr.code !== 404)) { + reject(new Error(tErr)); } const functionService = _.find( servicesInfo.items, @@ -112,12 +123,9 @@ function info(functions, service, options) { opts.log(`Not found any information about the function "${f}"`); } else { let url = null; - if (fIngress) { - const rule = _.find(fIngress.spec.rules, - r => _.some(r.http.paths, p => p.backend.serviceName === f) - ); - const path = _.find(rule.http.paths, p => p.backend.serviceName === f).path; - url = `${rule.host || 'API_URL'}${path}`; + if (httpTriggerDesc) { + // eslint-disable-next-line max-len + url = `${httpTriggerDesc.spec['host-name'] || 'API_URL'}/${httpTriggerDesc.spec.path}`; } const fService = { name: functionService.metadata.name, diff --git a/lib/ingress.js b/lib/ingress.js deleted file mode 100644 index a93ddef..0000000 --- a/lib/ingress.js +++ /dev/null @@ -1,169 +0,0 @@ -/* - Copyright 2017 Bitnami. - - 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. -*/ - -'use strict'; - -const _ = require('lodash'); -const BbPromise = require('bluebird'); -const Api = require('kubernetes-client'); -const helpers = require('./helpers'); -const url = require('url'); - -function addIngressRuleIfNecessary(ruleName, functions, options) { - const opts = _.defaults({}, options, { - verbose: false, - log: console.log, - namespace: 'default', - hostname: null, - defaultDNSResolution: 'nip.io', - ingress: _.defaults({ - class: 'nginx', - additionalAnnotations: {}, - tlsConfig: undefined, - name: undefined, - }), - }); - const config = helpers.loadKubeConfig(); - const extensions = new Api.Extensions(helpers.getConnectionOptions( - config, { namespace: options.namespace }) - ); - const defaultHostname = - `${url.parse(helpers.getKubernetesAPIURL(config)).hostname}.${opts.defaultDNSResolution}`; - const rules = []; - _.each(functions, (description) => { - _.each(description.events, event => { - if (event.type === 'http') { - const fpath = event.path || '/'; - if (event.path !== '/' || !_.isEmpty(event.hostname)) { - const hostname = event.hostname || opts.hostname || defaultHostname; - const absolutePath = _.startsWith(fpath, '/') ? - fpath : - `/${fpath}`; - const previousRule = _.findIndex(rules, r => r.host === hostname); - if (previousRule >= 0) { - rules[previousRule].http.paths.push({ - path: absolutePath, - backend: { serviceName: description.id, servicePort: 8080 }, - }); - } else { - rules.push({ - host: hostname, - http: { - paths: [{ - path: absolutePath, - backend: { serviceName: description.id, servicePort: 8080 }, - }], - }, - }); - } - } - } - }); - }); - return new BbPromise((resolve, reject) => { - if (!_.isEmpty(rules)) { - // Found a path to deploy the function - const ingressDef = { - kind: 'Ingress', - metadata: { - name: ruleName, - annotations: _.merge({ - 'kubernetes.io/ingress.class': opts.ingress.class, - [`${ - opts.ingress.name || opts.ingress.class - }.ingress.kubernetes.io/rewrite-target`]: '/', - }, opts.ingress.additionalAnnotations), - }, - spec: { rules, tls: opts.ingress.tlsConfig }, - }; - extensions.ns.ingress.get(ruleName, (err) => { - if (err === null) { - // Update existing ingress rule - extensions.ns.ingress(ruleName).put({ body: ingressDef }, (ingErr, res) => { - if (ingErr) { - reject( - 'Unable to deploy the ingress rule. ' + - `Received: ${ingErr.message}` - ); - } else { - if (opts.verbose) { - opts.log(`Updated Ingress rule ${ruleName}`); - } - resolve(res); - } - }); - } else { - // Create new - extensions.ns.ingress.post({ body: ingressDef }, (ingErr, res) => { - if (ingErr) { - reject( - 'Unable to deploy the ingress rule. ' + - `Received: ${ingErr.message}` - ); - } else { - if (opts.verbose) { - opts.log(`Deployed Ingress rule ${ruleName}`); - } - resolve(res); - } - }); - } - }); - } else { - if (opts.verbose) { - opts.log('Skipping ingress rule generation'); - } - resolve(); - } - }); -} - -function removeIngressRule(ruleName, namespace, options) { - const opts = _.defaults({}, options, { - verbose: false, - log: console.log, - }); - const extensions = opts.apiExtensions || - new Api.Extensions(helpers.getConnectionOptions(helpers.loadKubeConfig(), { - namespace, - })); - return new BbPromise((resolve, reject) => { - try { - extensions.ns.ingress.delete(ruleName, (err, res) => { - if (err) { - if (err.message.match(/not found/)) { - // Ingress rule doesn't exists - resolve(); - } else { - reject(err); - } - } else { - if (opts.verbose) { - opts.log(`Removed Ingress rule ${ruleName}`); - } - resolve(res); - } - }); - } catch (e) { - resolve(); - } - }); -} - -module.exports = { - addIngressRuleIfNecessary, - removeIngressRule, -}; diff --git a/lib/remove.js b/lib/remove.js index 1c423a2..9b1d930 100644 --- a/lib/remove.js +++ b/lib/remove.js @@ -20,7 +20,6 @@ const _ = require('lodash'); const BbPromise = require('bluebird'); const CRD = require('./crd'); const helpers = require('./helpers'); -const ingressHelper = require('./ingress'); function apiDeleteTrigger(triggerName, namespace, triggerType) { const triggerApi = new CRD('apis/kubeless.io', 'v1beta1', namespace, triggerType); @@ -28,10 +27,18 @@ function apiDeleteTrigger(triggerName, namespace, triggerType) { } function removeTrigger(event, funcName, namespace, service, options) { - let triggerPromise = new BbPromise((r) => r()); + let triggerPromise; switch (_.keys(event)[0]) { case 'http': - // TODO: Rely on Kubeless httptrigger object when it support paths + if (_.isEmpty(event.http)) { + throw new Error('You should specify a path for the trigger event'); + } + options.log(`Deleting http trigger for ${funcName}`); + triggerPromise = apiDeleteTrigger( + funcName, + namespace, + 'httptriggers' + ); break; case 'trigger': { if (_.isEmpty(event.trigger)) { @@ -116,13 +123,7 @@ function removeFunction(functions, service, options) { }).then(res => { checkResult(res, desc.id, errors, { log: opts.log, verbose: opts.verbose }); counter++; - helpers.checkFinished(counter, elements, errors, resolve, reject, { - onSuccess: () => ingressHelper.removeIngressRule( - service, - namespace, - { verbose: options.verbose, log: options.log, apiExtensions: options.apiExtensions } - ), - }); + helpers.checkFinished(counter, elements, errors, resolve, reject); }); _.each(desc.events, event => { removeTrigger(event, desc.id, namespace, service, opts).catch(err => { @@ -130,13 +131,7 @@ function removeFunction(functions, service, options) { }).then((res) => { checkResult(res, desc.id, errors, { log: opts.log, verbose: opts.verbose }); counter++; - helpers.checkFinished(counter, elements, errors, resolve, reject, { - onSuccess: () => ingressHelper.removeIngressRule( - service, - namespace, - { verbose: options.verbose, log: options.log, apiExtensions: options.apiExtensions } - ), - }); + helpers.checkFinished(counter, elements, errors, resolve, reject); }); }); }); diff --git a/package.json b/package.json index 08af9c5..3c2551c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "serverless-kubeless", - "version": "0.9.3", + "version": "0.10.0", "description": "This plugin enables support for Kubeless within the [Serverless Framework](https://github.com/serverless).", "main": "index.js", "directories": { diff --git a/test/kubelessDeploy.test.js b/test/kubelessDeploy.test.js index 750ce02..957d9c6 100644 --- a/test/kubelessDeploy.test.js +++ b/test/kubelessDeploy.test.js @@ -1198,14 +1198,17 @@ describe('KubelessDeploy', () => { depsFile, serverlessWithCustomPath ); - mocks.createDeploymentNocks( - config.clusters[0].cluster.server, functionName, defaultFuncSpec()); - mocks.createIngressNocks( + nock(config.clusters[0].cluster.server) + .get(`/apis/kubeless.io/v1beta1/namespaces/default/httptriggers/${functionName}`) + .reply(404, JSON.stringify({ code: 404 })); + mocks.createTriggerNocks( config.clusters[0].cluster.server, functionName, '1.2.3.4.nip.io', '/test' ); + mocks.createDeploymentNocks( + config.clusters[0].cluster.server, functionName, defaultFuncSpec()); return expect( // eslint-disable-line no-unused-expressions kubelessDeploy.deployFunction() ).to.be.fulfilled; @@ -1213,7 +1216,7 @@ describe('KubelessDeploy', () => { it('should deploy a function with a specific hostname', () => { const serverlessWithCustomPath = _.cloneDeep(serverlessWithFunction); serverlessWithCustomPath.service.functions[functionName].events = [{ - http: { }, + http: { path: '/' }, }]; serverlessWithCustomPath.service.provider.hostname = 'test.com'; kubelessDeploy = instantiateKubelessDeploy( @@ -1221,14 +1224,17 @@ describe('KubelessDeploy', () => { depsFile, serverlessWithCustomPath ); - mocks.createDeploymentNocks( - config.clusters[0].cluster.server, functionName, defaultFuncSpec()); - mocks.createIngressNocks( + nock(config.clusters[0].cluster.server) + .get(`/apis/kubeless.io/v1beta1/namespaces/default/httptriggers/${functionName}`) + .reply(404, JSON.stringify({ code: 404 })); + mocks.createTriggerNocks( config.clusters[0].cluster.server, functionName, 'test.com', '/' ); + mocks.createDeploymentNocks( + config.clusters[0].cluster.server, functionName, defaultFuncSpec()); return expect( // eslint-disable-line no-unused-expressions kubelessDeploy.deployFunction() ).to.be.fulfilled; @@ -1244,14 +1250,17 @@ describe('KubelessDeploy', () => { depsFile, serverlessWithCustomPath ); - mocks.createDeploymentNocks( - config.clusters[0].cluster.server, functionName, defaultFuncSpec()); - mocks.createIngressNocks( + nock(config.clusters[0].cluster.server) + .get(`/apis/kubeless.io/v1beta1/namespaces/default/httptriggers/${functionName}`) + .reply(404, JSON.stringify({ code: 404 })); + mocks.createTriggerNocks( config.clusters[0].cluster.server, functionName, 'test.com', '/test' ); + mocks.createDeploymentNocks( + config.clusters[0].cluster.server, functionName, defaultFuncSpec()); return expect( // eslint-disable-line no-unused-expressions kubelessDeploy.deployFunction() ).to.be.fulfilled; @@ -1266,14 +1275,17 @@ describe('KubelessDeploy', () => { depsFile, serverlessWithCustomPath ); - mocks.createDeploymentNocks( - config.clusters[0].cluster.server, functionName, defaultFuncSpec()); - mocks.createIngressNocks( + nock(config.clusters[0].cluster.server) + .get(`/apis/kubeless.io/v1beta1/namespaces/default/httptriggers/${functionName}`) + .reply(404, JSON.stringify({ code: 404 })); + mocks.createTriggerNocks( config.clusters[0].cluster.server, functionName, 'test.com', '/test' ); + mocks.createDeploymentNocks( + config.clusters[0].cluster.server, functionName, defaultFuncSpec()); return expect( // eslint-disable-line no-unused-expressions kubelessDeploy.deployFunction() ).to.be.fulfilled; @@ -1289,16 +1301,19 @@ describe('KubelessDeploy', () => { depsFile, serverlessWithCustomPath ); - mocks.createDeploymentNocks( - config.clusters[0].cluster.server, - functionName, defaultFuncSpec(), { namespace: 'myns' }); - mocks.createIngressNocks( + nock(config.clusters[0].cluster.server) + .get(`/apis/kubeless.io/v1beta1/namespaces/myns/httptriggers/${functionName}`) + .reply(404, JSON.stringify({ code: 404 })); + mocks.createTriggerNocks( config.clusters[0].cluster.server, functionName, '1.2.3.4.nip.io', '/test', { namespace: 'myns' } ); + mocks.createDeploymentNocks( + config.clusters[0].cluster.server, + functionName, defaultFuncSpec(), { namespace: 'myns' }); return expect( // eslint-disable-line no-unused-expressions kubelessDeploy.deployFunction() ).to.be.fulfilled; @@ -1313,14 +1328,17 @@ describe('KubelessDeploy', () => { depsFile, serverlessWithCustomPath ); - mocks.createDeploymentNocks( - config.clusters[0].cluster.server, functionName, defaultFuncSpec()); - mocks.createIngressNocks( + nock(config.clusters[0].cluster.server) + .get(`/apis/kubeless.io/v1beta1/namespaces/default/httptriggers/${functionName}`) + .reply(404, JSON.stringify({ code: 404 })); + mocks.createTriggerNocks( config.clusters[0].cluster.server, functionName, '1.2.3.4.nip.io', '/test' ); + mocks.createDeploymentNocks( + config.clusters[0].cluster.server, functionName, defaultFuncSpec()); return expect( // eslint-disable-line no-unused-expressions kubelessDeploy.deployFunction() ).to.be.fulfilled; @@ -1336,14 +1354,17 @@ describe('KubelessDeploy', () => { depsFile, serverlessWithCustomPath ); - mocks.createDeploymentNocks( - config.clusters[0].cluster.server, functionName, defaultFuncSpec()); - mocks.createIngressNocks( + nock(config.clusters[0].cluster.server) + .get(`/apis/kubeless.io/v1beta1/namespaces/default/httptriggers/${functionName}`) + .reply(404, JSON.stringify({ code: 404 })); + mocks.createTriggerNocks( config.clusters[0].cluster.server, functionName, '1.2.3.4.xip.io', '/test' ); + mocks.createDeploymentNocks( + config.clusters[0].cluster.server, functionName, defaultFuncSpec()); return expect( // eslint-disable-line no-unused-expressions kubelessDeploy.deployFunction() ).to.be.fulfilled; diff --git a/test/kubelessInfo.test.js b/test/kubelessInfo.test.js index 6b577cd..970d3f0 100644 --- a/test/kubelessInfo.test.js +++ b/test/kubelessInfo.test.js @@ -140,29 +140,18 @@ describe('KubelessInfo', () => { .reply(200, _.find(allFunctions, (ff) => ff.metadata.name === f.id)); }); - - // Mock call to get.ingress + // Mock call to get.httptrigger per namespace _.each(functions, f => { - nock(config.clusters[0].cluster.server) - .get(`/apis/extensions/v1beta1/namespaces/${f.namespace}/ingresses/${serviceName}`) - .reply(200, f.path ? { - spec: { - rules: [{ - host: '1.2.3.4.nip.io', - http: { - paths: [{ - path: f.path, - backend: { serviceName: f.id }, - }], - }, - }], - }, - status: { - loadBalancer: { - ingress: [{ ip: '1.2.3.4' }], + if (f.path) { + nock(config.clusters[0].cluster.server) + .get(`/apis/kubeless.io/v1beta1/namespaces/${f.namespace}/httptriggers/${f.id}`) + .reply(200, { + spec: { + 'host-name': '1.2.3.4.nip.io', + path: f.path.replace(/^\//, ''), }, - }, - } : null); + }); + } }); } function infoMock(f) { @@ -252,9 +241,6 @@ describe('KubelessInfo', () => { nock(config.clusters[0].cluster.server) .get('/apis/kubeless.io/v1beta1/namespaces/custom-1/functions/my-function-1') .reply(404, { code: 404 }); - nock(config.clusters[0].cluster.server) - .get(`/apis/extensions/v1beta1/namespaces/custom-1/ingresses/${serviceName}`) - .reply(404, 'not found'); const serverlessWithNS = getServerlessObj({ service: { service: serviceName, diff --git a/test/kubelessRemove.test.js b/test/kubelessRemove.test.js index 96c5ab0..bb428e4 100644 --- a/test/kubelessRemove.test.js +++ b/test/kubelessRemove.test.js @@ -22,7 +22,6 @@ const BbPromise = require('bluebird'); const chaiAsPromised = require('chai-as-promised'); const expect = require('chai').expect; const fs = require('fs'); -const helpers = require('../lib/helpers'); const mocks = require('./lib/mocks'); const moment = require('moment'); const nock = require('nock'); @@ -32,7 +31,6 @@ const sinon = require('sinon'); const rm = require('./lib/rm'); const KubelessRemove = require('../remove/kubelessRemove'); -const remove = require('../lib/remove'); const serverless = require('./lib/serverless')(); require('chai').use(chaiAsPromised); @@ -207,53 +205,5 @@ describe('KubelessRemove', () => { }) ).to.be.fulfilled; }); - it('should remove the ingress controller if exists', () => { - nock(config.clusters[0].cluster.server) - .delete('/apis/kubeless.io/v1beta1/namespaces/default/functions/myFunction') - .reply(200, {}); - const apiExtensions = new Api.Extensions( - helpers.getConnectionOptions(helpers.loadKubeConfig(), { namespace: 'default' }) - ); - const functions = [{ - id: 'myFunction', - handler: 'function.hello', - events: [{ http: { path: '/test' } }], - }]; - const serviceName = 'test'; - return expect( // eslint-disable-line no-unused-expressions - remove(functions, serviceName, { - apiExtensions, log: () => {}, - }).then(() => { - expect(Api.Extensions.prototype.delete.calledOnce).to.be.eql(true); - expect( - Api.Extensions.prototype.delete.firstCall.args[0].path[0] - ).to.be.eql('/apis/extensions/v1beta1/namespaces/default/ingresses'); - }) - ).to.be.fulfilled; - }); - it('should remove the ingress controller if exists (with a different namespace)', () => { - nock(config.clusters[0].cluster.server) - .delete('/apis/kubeless.io/v1beta1/namespaces/test/functions/myFunction') - .reply(200, {}); - const apiExtensions = new Api.Extensions( - helpers.getConnectionOptions(helpers.loadKubeConfig(), { namespace: 'test' }) - ); - const functions = [{ - id: 'myFunction', - handler: 'function.hello', - events: [{ http: { path: '/test' } }], - }]; - const serviceName = 'test'; - return expect( // eslint-disable-line no-unused-expressions - remove(functions, serviceName, { - apiExtensions, namespace: 'test', log: () => { }, - }).then(() => { - expect(Api.Extensions.prototype.delete.calledOnce).to.be.eql(true); - expect( - Api.Extensions.prototype.delete.firstCall.args[0].path[0] - ).to.be.eql('/apis/extensions/v1beta1/namespaces/test/ingresses'); - }) - ).to.be.fulfilled; - }); }); }); diff --git a/test/lib/mocks.js b/test/lib/mocks.js index a10361d..959f0db 100644 --- a/test/lib/mocks.js +++ b/test/lib/mocks.js @@ -38,24 +38,6 @@ function thirdPartyResources(kubelessDeploy, namespace) { return result; } -function extensions(kubelessDeploy, namespace) { - const result = { - namespaces: { - namespace: namespace || 'default', - }, - ns: { - ingress: { - post: sinon.stub().callsFake((body, callback) => { - callback(null, { statusCode: 200 }); - }), - }, - }, - addResource: sinon.stub(), - }; - sinon.stub(kubelessDeploy, 'getExtensions').returns(result); - return result; -} - function kubeConfig(cwd) { fs.mkdirSync(path.join(cwd, '.kube')); fs.writeFileSync( @@ -158,26 +140,33 @@ function createDeploymentNocks(endpoint, func, funcSpec, options) { })); } -function createIngressNocks(endpoint, func, hostname, p, options) { +function createTriggerNocks(endpoint, func, hostname, p, options) { const opts = _.defaults({}, options, { namespace: 'default', }); nock(endpoint) - .post(`/apis/extensions/v1beta1/namespaces/${opts.namespace}/ingresses`, { - kind: 'Ingress', + .post(`/apis/kubeless.io/v1beta1/namespaces/${opts.namespace}/httptriggers/`, { + apiVersion: 'kubeless.io/v1beta1', + kind: 'HTTPTrigger', metadata: { + name: func, + namespace: opts.namespace, annotations: { 'kubernetes.io/ingress.class': 'nginx', - 'nginx.ingress.kubernetes.io/rewrite-target': '/', + }, + labels: { + 'created-by': 'kubeless', }, }, spec: { - rules: [{ - host: hostname, - http: { - paths: [{ path: p, backend: { serviceName: func, servicePort: 8080 } }], - }, - }], + 'host-name': hostname, + 'basic-auth-secret': '', + 'cors-enable': false, + 'function-name': func, + gateway: 'nginx', + path: p.replace(/^\//, ''), + tls: false, + 'tls-secret': '', }, }) .reply(200, { message: 'OK' }); @@ -185,9 +174,8 @@ function createIngressNocks(endpoint, func, hostname, p, options) { module.exports = { thirdPartyResources, - extensions, kubeConfig, restoreKubeConfig, createDeploymentNocks, - createIngressNocks, + createTriggerNocks, };