From cb8da7a4563e32a6fb75f7b81447e709acd7a612 Mon Sep 17 00:00:00 2001 From: Lance Ball Date: Thu, 24 May 2018 09:53:30 -0400 Subject: [PATCH] feat: add v1beta1 api endpoints (#70) This commit adds the set of v1beta1 endpoint URLs documented here: https://docs.openshift.com/container-platform/3.7/rest_api/index.html The only functional endpoint at the moment is for deployments. All of the others simply expose the endpoint URL but do expose any functional behavior. I've added a `client.apis` object which maps to the the 3 semi-supported APIs at the moment. ```js client.apis.oapi // { version: config.apiVersion, baseUrl: client.apiUrl } client.apis.kube // { version: config.apiVersion, baseUrl: client.kubeUrl } client.apis.v1beta1 // function endpoints() returning array of endpoint URLs ``` Fixes: https://github.com/bucharest-gold/openshift-rest-client/issues/69 --- lib/config-loader.js | 21 ++++ lib/deployments.js | 151 +++++++++++++++++++++++++++++ lib/openshift-rest-client.js | 38 ++++---- test/deployment-test.js | 179 +++++++++++++++++++++++++++++++++++ 4 files changed, 371 insertions(+), 18 deletions(-) create mode 100644 lib/deployments.js create mode 100644 test/deployment-test.js diff --git a/lib/config-loader.js b/lib/config-loader.js index 6418580..ffef1ef 100644 --- a/lib/config-loader.js +++ b/lib/config-loader.js @@ -36,6 +36,27 @@ function loadConfig (client) { client.apiUrl = `${config.cluster}/oapi/${config.apiVersion}`; client.kubeUrl = `${config.cluster}/api/${config.apiVersion}`; + client.apis = { + oapi: { + version: config.apiVersion, + baseUrl: client.apiUrl + }, + kube: { + version: config.apiVersion, + baseUrl: client.kubeUrl + }, + v1beta1: { + endpoints: function endpoints () { + return ['apps', 'extensions', 'policy', + 'authentication.k8s.io', 'authentication.k8s.io', 'rbac.authorization.k8s.io', + 'certificates.k8s.io', 'storage.k8s.io'] + .map(name => { + return { name, url: `/apis/${name}/v1beta1` }; + }).reduce((map, obj) => { map[obj.name] = obj.url; return map; }, {}); + } + } + }; + privates.get(client).config = config; return client; diff --git a/lib/deployments.js b/lib/deployments.js new file mode 100644 index 0000000..e7dac0c --- /dev/null +++ b/lib/deployments.js @@ -0,0 +1,151 @@ +'use strict'; + +/* + * + * Copyright Red Hat, Inc. and individual contributors. + * + * 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. + * + */ + +const request = require('./common-request'); +const privates = require('./private-map'); + +function findAll (client) { + return function findAll (options = {}) { + const clientConfig = privates.get(client).config; + const url = + `${clientConfig.cluster}${client.apis.v1beta1.endpoints() + .apps}/namespaces/${clientConfig + .context.namespace}/deployments`; + + const req = { + method: 'GET', + url, + qs: options.qs + }; + + return request(client, req); + }; +} + +function find (client) { + return function find (deploymentName, options = {}) { + if (!deploymentName) { + return Promise.reject(new Error('Deployment Name is required')); + } + + const clientConfig = privates.get(client).config; + const url = + `${clientConfig.cluster}${client.apis.v1beta1.endpoints() + .apps}/namespaces/${clientConfig.context.namespace}/deployments/${deploymentName}`; + + const req = { + method: 'GET', + url + }; + + return request(client, req); + }; +} + +function create (client) { + return function create (deployment, options = {}) { + const clientConfig = privates.get(client).config; + const url = + `${clientConfig.cluster}${client.apis.v1beta1.endpoints() + .apps}/namespaces/${clientConfig.context.namespace}/deployments`; + + const req = { + method: 'POST', + url, + json: false, + body: JSON.stringify(deployment) + }; + + return request(client, req).then(body => { + return JSON.parse(body); + }); + }; +} + +function update (client) { + return function create (name, deployment, options = {}) { + if (!name) { + return Promise.reject(new Error('Deployment Name is required')); + } + + const clientConfig = privates.get(client).config; + const url = + `${clientConfig.cluster}${client.apis.v1beta1.endpoints() + .apps}/namespaces/${clientConfig.context.namespace}/deployments/${name}`; + + const req = { + method: 'PUT', + json: false, + url, + body: JSON.stringify(deployment) + }; + + return request(client, req).then(body => { + return JSON.parse(body); + }); + }; +} + +function remove (client) { + return function remove (name, options = {}) { + if (!name) { + return Promise.reject(new Error('Deployment Name is required')); + } + const clientConfig = privates.get(client).config; + const url = + `${clientConfig.cluster}${client.apis.v1beta1.endpoints() + .apps}/namespaces/${clientConfig.context.namespace}/deployments/${name}`; + + const req = { + method: 'DELETE', + url, + body: options.body, + qs: options.qs + }; + + return request(client, req); + }; +} + +function removeAll (client) { + return function removeAll (options = {}) { + const clientConfig = privates.get(client).config; + const url = + `${clientConfig.cluster}${client.apis.v1beta1.endpoints() + .apps}/namespaces/${clientConfig.context.namespace}/deployments`; + + const req = { + method: 'DELETE', + url, + qs: options.qs + }; + + return request(client, req); + }; +} + +module.exports = { + findAll: findAll, + find: find, + create: create, + update: update, + remove: remove, + removeAll: removeAll +}; diff --git a/lib/openshift-rest-client.js b/lib/openshift-rest-client.js index 6f07dd8..9cb2470 100644 --- a/lib/openshift-rest-client.js +++ b/lib/openshift-rest-client.js @@ -24,8 +24,9 @@ const loadConfig = require('./config-loader'); const buildconfigs = require('./build-configs'); const builds = require('./builds'); const clusterrolebindings = require('./cluster-role-bindings'); -const configMaps = require('./configmaps'); +const configmaps = require('./configmaps'); const deploymentconfigs = require('./deployment-config'); +const deployments = require('./deployments'); const events = require('./events'); const groups = require('./groups'); const imagestreams = require('./imagestreams'); @@ -65,23 +66,24 @@ function openshiftClient (settings = {}) { const client = {}; Object.assign(client, bindModule(client, { - buildconfigs: buildconfigs, - builds: builds, - clusterrolebindings: clusterrolebindings, - configmaps: configMaps, - deploymentconfigs: deploymentconfigs, - events: events, - groups: groups, - imagestreams: imagestreams, - persistentvolumeclaims: persistentvolumeclaims, - pods: pods, - projectrequests: projectrequests, - projects: projects, - replicationcontrollers: replicationcontrollers, - rolebindings: rolebindings, - routes: routes, - secrets: secrets, - services: services + buildconfigs, + builds, + clusterrolebindings, + configmaps, + deployments, + deploymentconfigs, + events, + groups, + imagestreams, + persistentvolumeclaims, + pods, + projectrequests, + projects, + replicationcontrollers, + rolebindings, + routes, + secrets, + services })); client.settings = settings; diff --git a/test/deployment-test.js b/test/deployment-test.js new file mode 100644 index 0000000..17e4a6c --- /dev/null +++ b/test/deployment-test.js @@ -0,0 +1,179 @@ +'use strict'; + +const test = require('tape'); +const nock = require('nock'); + +const openshiftRestClient = require('../'); +const privates = require('../lib/private-map'); + +const settings = { + config: { + apiVersion: 'v1beta1', + context: + { cluster: '192-168-99-100:8443', + namespace: 'for-node-client-testing', + user: 'developer/192-168-99-100:8443' }, + user: { token: 'zVBd1ZFeJqEAILJgimm4-gZJauaw3PW4EVqV_peEZ3U' }, + cluster: 'https://192.168.99.100:8443' } +}; + +test('find - deployments - basic findAll', (t) => { + openshiftRestClient(settings).then(client => { + t.equal(typeof client.deployments.findAll, 'function', 'There is a findAll method on the deployments object'); + + const clientConfig = privates.get(client).config; + const url = `${client.apis.v1beta1.endpoints().apps}/namespaces/${clientConfig.context.namespace}/deployments`; + + nock(clientConfig.cluster) + .matchHeader('authorization', `Bearer ${clientConfig.user.token}`) // taken from the config + .get(url).reply(200, {kind: 'DeploymentList'}); + + const findResult = client.deployments.findAll().then(deploymentList => { + t.equal(deploymentList.kind, 'DeploymentList', 'returns an object with DeploymentList'); + t.end(); + }); + + t.equal(findResult instanceof Promise, true, 'should return a Promise'); + }); +}); + +test('find - deployments - basic find', (t) => { + openshiftRestClient(settings).then(client => { + t.equal(typeof client.deployments.find, 'function', 'There is a find method on the deployments object'); + + const clientConfig = privates.get(client).config; + const deploymentName = 'cool-deploymentconfig-name-1'; + const url = `${client.apis.v1beta1.endpoints().apps}/namespaces/${clientConfig.context.namespace}/deployments/${deploymentName}`; + + nock(clientConfig.cluster) + .matchHeader('authorization', `Bearer ${clientConfig.user.token}`) // taken from the config + .get(url) + .reply(200, {kind: 'Deployment'}); + + const findResult = client.deployments.find(deploymentName).then(deployment => { + t.equal(deployment.kind, 'Deployment', 'returns an object with DeploymentConfig'); + t.end(); + }); + + t.equal(findResult instanceof Promise, true, 'should return a Promise'); + }); +}); + +test('find - deployments - find with no deployment name', (t) => { + openshiftRestClient(settings).then(client => { + client.deployments.find().catch(err => { + t.equal(err.message, 'Deployment Name is required', 'error message should return'); + t.end(); + }); + }); +}); + +test('create - deployment', (t) => { + openshiftRestClient(settings).then(client => { + t.equal(typeof client.deploymentconfigs.create, 'function', 'There is a create method on the deploymentconfigs object'); + + const clientConfig = privates.get(client).config; + const url = `${client.apis.v1beta1.endpoints().apps}/namespaces/${clientConfig.context.namespace}/deployments`; + const deployment = { + kind: 'Deployment' + }; + + nock(clientConfig.cluster) + .matchHeader('authorization', `Bearer ${clientConfig.user.token}`) // taken from the config + .post(url) + .reply(200, {kind: 'Deployment'}); + + const createResult = client.deployments.create(deployment).then(deployment => { + t.equal(deployment.kind, 'Deployment', 'returns an object with Deployment'); + t.end(); + }); + + t.equal(createResult instanceof Promise, true, 'should return a Promise'); + }); +}); + +test('update - deployment', (t) => { + openshiftRestClient(settings).then(client => { + t.equal(typeof client.deployments.create, 'function', 'There is a create method on the deployments object'); + + const clientConfig = privates.get(client).config; + const deployment = { + kind: 'Deployment' + }; + const deploymentName = 'cool-deploymentconfig-name-1'; + const url = `${client.apis.v1beta1.endpoints().apps}/namespaces/${clientConfig.context.namespace}/deployments/${deploymentName}`; + + nock(clientConfig.cluster) + .matchHeader('authorization', `Bearer ${clientConfig.user.token}`) // taken from the config + .put(url) + .reply(200, {kind: 'Deployment'}); + + const createResult = client.deployments.update(deploymentName, deployment).then(updated => { + t.equal(updated.kind, 'Deployment', 'returns an object with Deployment'); + t.end(); + }); + + t.equal(createResult instanceof Promise, true, 'should return a Promise'); + }); +}); + +test('update - deployment with no deployment name', (t) => { + openshiftRestClient(settings).then((client) => { + client.deployments.update().catch((err) => { + t.equal(err.message, 'Deployment Name is required', 'error message should return'); + t.end(); + }); + }); +}); + +test('remove - deployments - basic removeAll', (t) => { + openshiftRestClient(settings).then((client) => { + t.equal(typeof client.deployments.removeAll, 'function', 'There is a removeAll method on the deployments object'); + + const clientConfig = privates.get(client).config; + const url = `${client.apis.v1beta1.endpoints().apps}/namespaces/${clientConfig.context.namespace}/deployments`; + + nock(clientConfig.cluster) + .matchHeader('authorization', `Bearer ${clientConfig.user.token}`) // taken from the config + .delete(url) + .reply(200, {kind: 'Status'}); + + const removeResult = client.deployments.removeAll().then(deploymentList => { + t.equal(deploymentList.kind, 'Status', 'returns an object with Status'); + t.end(); + }); + + t.equal(removeResult instanceof Promise, true, 'should return a Promise'); + }); +}); + +test('remove - deployments - basic remove', (t) => { + openshiftRestClient(settings).then((client) => { + t.equal(typeof client.deployments.remove, 'function', 'There is a remove method on the deployments object'); + + const clientConfig = privates.get(client).config; + const deploymentName = 'cool-deploymentconfig-name-1'; + const url = `${client.apis.v1beta1.endpoints().apps}/namespaces/${clientConfig.context.namespace}/deployments/${deploymentName}`; + + nock(clientConfig.cluster) + .matchHeader('authorization', `Bearer ${clientConfig.user.token}`) // taken from the config + .delete(url) + .reply(200, {kind: 'Status'}); + + const removeResult = client.deployments.remove(deploymentName).then(status => { + t.equal(status.kind, 'Status', 'returns an object with Status'); + t.end(); + }); + + t.equal(removeResult instanceof Promise, true, 'should return a Promise'); + }); +}); + +// test('remove - deploymentconfigs - remove - no deploymentconfig name', (t) => { +// openshiftRestClient(settings).then((client) => { +// client.deploymentconfigs.remove().catch((err) => { +// t.equal(err.message, 'Deployment Config Name is required', 'error message should return'); +// t.end(); +// }); +// }); +// });