New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

auto-kubernetes-client

Package Overview
Dependencies
Maintainers
1
Versions
14
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

auto-kubernetes-client - npm Package Compare versions

Comparing version 0.2.0 to 0.3.0

examples/watch-pods/index.js

32

examples/list-pods/index.js

@@ -6,5 +6,5 @@ 'use strict';

const K8sClient = require('.');
const K8sClient = require('../..');
const userDir = process.env[(process.platform == 'win32') ? 'USERPROFILE' : 'HOME'];
const userDir = process.env[process.platform === 'win32' ? 'USERPROFILE' : 'HOME'];
const config = {

@@ -17,22 +17,10 @@ url: 'https://192.168.99.100:8443',

K8sClient(config, function(err, client) {
if (err) {
console.error(`Cannot connect to cluster: ${err.message}`);
return;
}
client.namespaces.list(function(err, response, nsList) {
if (err) {
console.error(`Cannot list namespaces: ${err.message}`);
return;
}
return nsList.items.forEach(function(ns) {
return client.ns(ns.metadata.name).pods.list(function(err, response, podList) {
podList.items.forEach(function(pod) {
console.log(`Discovered pod ${pod.metadata.namespace}/${pod.metadata.name}`);
});
});
});
});
K8sClient(config).then(function(client) {
return client.namespaces.list()
.then(nsList => nsList.items.map(ns => client.ns(ns.metadata.name).pods.list()))
.then(podListPromises => Promise.all(podListPromises))
.then(podLists => podLists.reduce((result, podList) => result.concat(podList.items), []))
.then(pods => pods.forEach(pod => console.log(`Discovered pod ${pod.metadata.namespace}/${pod.metadata.name}`)));
}).catch(function(err) {
console.error(`Error: ${err.message}`);
});
{
"name": "auto-kubernetes-client",
"version": "0.2.0",
"version": "0.3.0",
"description": "NodeJS Kubernetes Client with automatic API discoveryEdit",

@@ -8,3 +8,3 @@ "main": "src/index.js",

"install": "check-node-version --package",
"prepublish": "eslint src",
"prepublish": "eslint src examples",
"test": "echo 'No tests yet'"

@@ -31,3 +31,4 @@ },

"flatmap": "0.0.3",
"request": "^2.81.0"
"request": "^2.81.0",
"through2": "^2.0.3"
},

@@ -34,0 +35,0 @@ "engines": {

@@ -51,6 +51,6 @@ # auto-kubernetes-client [![Build Status](https://travis-ci.org/Collaborne/auto-kubernetes-client.svg?branch=master)](https://travis-ci.org/Collaborne/auto-kubernetes-client) [![Greenkeeper badge](https://badges.greenkeeper.io/Collaborne/auto-kubernetes-client.svg)](https://greenkeeper.io/)

Single resources offer resource methods `get`, `update`, `patch` and `delete`.
- Resource methods typically have the signature `method(callback, qs = {})`, where `qs` is a hash for additional query parameters,
and `callback` is a `function(err, response, data)`. `response` contains the full response as provided by the [request library](https://github.com/request/request), and `data` is the parsed response entity.
- The `watch` resource method has the signature `watch(callback)`, and `callback` is a `function(err, change)`. If `change` is
`null` then the watch was interrupted/ended, otherwise it will be an object with a `type` field ('ADDED', 'DELETED', 'MODIFIED'), and the actual object that was modified.
- Resource methods typically have the signature `method(qs = {})`, where `qs` is a hash for additional query parameters,
and return a promise for the parsed response entity.
- The `watch` resource method has the signature `watch()`, and returns an object stream for the observed changes.
Each object has a `type` field ('ADDED', 'DELETED', 'MODIFIED'), and the actual object that was modified.

@@ -57,0 +57,0 @@ ## Examples

@@ -6,250 +6,265 @@ 'use strict';

const flatMap = require('flatmap');
const through2 = require('through2');
/**
* Query the kubernetes server
* Cluster configuration
*
* @param {string} path
* @param {Object} [extraOptions={}]
* @param {Function} [callback=null]
* @typedef Configuration
* @property {string} url
* @property {boolean} insecureSkipTlsVerify
* @property {string} ca
* @property {string} cert
* @property {string} key
* @property {ConfigurationAuth} auth
*/
function doRequest(config, path, extraOptions = {}, callback = null) {
const options = Object.assign({}, config, {
json: true
}, extraOptions)
/**
* Cluster user authentication
*
* @typedef ConfigurationAuth
* @property {string} user
* @property {string} password
* @property {string} bearer
*/
/**
* Client
*
* @typedef Client
*/
return request(url.resolve(config.url, path), options, callback);
}
function wrapCallback(callback) {
return function(err, response, result) {
if (err) {
// Basic error
return callback(err, response, result);
} else if (result && result.apiVersion === 'v1' && result.kind === 'Status' && result.status === 'Failure') {
// k8s error encoded as status
return callback(new Error(result.message), response, result);
} else {
return callback(null, response, result);
}
}
}
module.exports = function connect(config, callback) {
/**
* Connect to the cluster
*
* @param {Configuration} config
* @return {Promise<Client>}
*/
module.exports = function connect(config) {
// Ensure that the config.url ends with a '/'
const k8sRequest = doRequest.bind(this, Object.assign({}, config, { url: config.url.endsWith('/') ? config.url : config.url + '/' }));
const configOptions = Object.assign({}, config, { url: config.url.endsWith('/') ? config.url : config.url + '/' });
k8sRequest('apis', {}, function(err, response, apiGroups) {
function _createApi(groupPath, version) {
// Query that API for all possible operations, and map them.
return new Promise(function(resolve, reject) {
return k8sRequest(groupPath, {}, wrapCallback(function(err, response, apiResources) {
if (err) {
return reject(err);
}
/**
* Query the kubernetes server and return a uncooked response stream
*
* @param {string} path
* @param {Object} [extraOptions={}]
* @return {Stream}
*/
function streamK8sRequest(path, extraOptions = {}) {
const options = Object.assign({}, configOptions, extraOptions);
// TODO: Transform the API information (APIResourceList) into functions.
// Basically we have resources[] with each
// { kind: Bindings, name: bindings, namespaced: true/false }
// For each of these we want to produce a list/watch function under that name,
// and a function with that name that returns an object with get/... for the single thing.
// If namespaced is set then this is appended to the ns() result, otherwise it is directly
// set on the thing.
function createResourceCollection(resource, pathPrefix = '') {
let resourcePath = groupPath + '/';
if (pathPrefix) {
resourcePath += pathPrefix + '/';
}
resourcePath += resource.name;
return request(url.resolve(configOptions.url, path), options);
}
return {
watch: function(callback, resourceVersion = '', qs = {}) {
// Watch calls the callback for each item, so we're switching off JSON mode, and instead
// process each line of the response separately
// Buffer contains usable data from 0..bufferLength
let buffer = Buffer.alloc(0);
let bufferLength = 0;
return k8sRequest(resourcePath, { method: 'GET', json: false, qs: Object.assign({}, qs, { watch: 'true', resourceVersion }) })
.on('error', function(err) {
return callback(err);
})
.on('data', function(data) {
// Find a newline in the buffer: everything up to it together with the current buffer contents is for the callback,
// and the rest forms the new buffer.
let newlineIndex;
let startIndex = 0;
while ((newlineIndex = data.indexOf('\n', startIndex)) !== -1) {
const contents = Buffer.alloc(bufferLength + newlineIndex - startIndex);
buffer.copy(contents, 0, 0, bufferLength);
data.copy(contents, bufferLength, startIndex, newlineIndex);
callback(null, JSON.parse(contents.toString('UTF-8')));
/**
* Query the kubernetes server and return a promise for the result object
*
* Note that the promise does not reject when a 'Status' object is returned; the caller must apply suitable
* checks on its own.
*
* @param {any} path
* @param {any} [extraOptions={}]
* @returns {Promise<>}
*/
function k8sRequest(path, extraOptions = {}) {
const options = Object.assign({}, configOptions, { json: true }, extraOptions);
// Clear the buffer if we used it.
if (bufferLength > 0) {
bufferLength = 0;
}
return new Promise(function(resolve, reject) {
return request(url.resolve(configOptions.url, path), options, function(err, response, data) {
if (err) {
return reject(err);
} else {
return resolve(data);
}
});
});
}
startIndex = newlineIndex + 1;
}
function createApi(name, groupPath, version, preferred) {
// Query that API for all possible operations, and map them.
return k8sRequest(groupPath, {}).then(function(apiResources) {
// TODO: Transform the API information (APIResourceList) into functions.
// Basically we have resources[] with each
// { kind: Bindings, name: bindings, namespaced: true/false }
// For each of these we want to produce a list/watch function under that name,
// and a function with that name that returns an object with get/... for the single thing.
// If namespaced is set then this is appended to the ns() result, otherwise it is directly
// set on the thing.
function createResourceCollection(resource, pathPrefix = '') {
let resourcePath = groupPath + '/';
if (pathPrefix) {
resourcePath += pathPrefix + '/';
}
resourcePath += resource.name;
const restData = data.slice(startIndex);
if (bufferLength + restData.length < buffer.length) {
restData.copy(buffer, bufferLength);
bufferLength += restData.length;
} else {
buffer = bufferLength === 0 ? restData : Buffer.concat([buffer.slice(0, bufferLength), restData]);
bufferLength = buffer.length;
}
})
.on('end', function() {
if (bufferLength > 0) {
callback(JSON.parse(buffer.toString('UTF-8', 0, bufferLength)));
}
return {
watch: function(resourceVersion = '', qs = {}) {
let buffer = Buffer.alloc(0);
let bufferLength = 0;
return callback(null, null);
});
},
const parseJSONStream = through2.obj(function(chunk, enc, callback) {
// Find a newline in the buffer: everything up to it together with the current buffer contents is for the callback,
// and the rest forms the new buffer.
let newlineIndex;
let startIndex = 0;
while ((newlineIndex = chunk.indexOf('\n', startIndex)) !== -1) {
const contents = Buffer.alloc(bufferLength + newlineIndex - startIndex);
buffer.copy(contents, 0, 0, bufferLength);
chunk.copy(contents, bufferLength, startIndex, newlineIndex);
this.push(JSON.parse(contents.toString('UTF-8')));
list: function(callback, qs = {}) {
return k8sRequest(resourcePath, { qs, method: 'GET' }, wrapCallback(callback));
},
// Clear the buffer if we used it.
if (bufferLength > 0) {
bufferLength = 0;
}
create: function(object, callback, qs = {}) {
return k8sRequest(resourcePath, { qs, method: 'POST', body: object }, wrapCallback(callback));
},
startIndex = newlineIndex + 1;
}
deletecollection: function(callback, qs = {}) {
return k8sRequest(resourcePath, { qs, method: 'DELETE' }, wrapCallback(callback));
},
}
}
const restData = chunk.slice(startIndex);
if (bufferLength + restData.length < buffer.length) {
restData.copy(buffer, bufferLength);
bufferLength += restData.length;
} else {
buffer = bufferLength === 0 ? restData : Buffer.concat([buffer.slice(0, bufferLength), restData]);
bufferLength = buffer.length;
}
function createResource(resource, name, pathPrefix = '') {
let resourcePath = groupPath + '/';
if (pathPrefix) {
resourcePath += pathPrefix + '/';
}
resourcePath += resource.name + '/';
resourcePath += name;
return callback();
}, function(callback) {
if (bufferLength > 0) {
this.push(JSON.parse(buffer.toString('UTF-8', 0, bufferLength)));
bufferLength = 0;
}
return {
get: function(callback, qs = {}) {
return k8sRequest(resourcePath, { qs, method: 'GET' }, wrapCallback(callback));
},
return callback();
});
update: function(object, callback, qs = {}) {
return k8sRequest(resourcePath, { qs, method: 'PUT', body: object }, wrapCallback(callback));
},
return streamK8sRequest(resourcePath, { method: 'GET', json: false, qs: Object.assign({}, qs, { watch: 'true', resourceVersion }) })
.pipe(parseJSONStream);
},
patch: function(object, callback, qs = {}) {
return k8sRequest(resourcePath, { qs, method: 'PATCH', body: object }, wrapCallback(callback));
},
list: function(qs = {}) {
return k8sRequest(resourcePath, { qs, method: 'GET' });
},
delete: function(callback, qs = {}) {
return k8sRequest(resourcePath, { qs, method: 'DELETE' }, wrapCallback(callback));
},
};
}
create: function(object, qs = {}) {
return k8sRequest(resourcePath, { qs, method: 'POST', body: object });
},
function createResourceAPI(resource, pathPrefix = '') {
return {
[resource.name.toLowerCase()]: createResourceCollection(resource, pathPrefix),
[resource.kind.toLowerCase()]: function(name) {
return createResource(resource, name, pathPrefix)
}
}
}
deletecollection: function(qs = {}) {
return k8sRequest(resourcePath, { qs, method: 'DELETE' });
},
}
}
const nsResources = {};
const api = {
name: version.groupVersion,
ns: function(namespace) {
// Return adapted nsResources for this namespace
return Object.keys(nsResources).reduce(function(result, resourceKey) {
return Object.assign(result, createResourceAPI(nsResources[resourceKey], `namespaces/${namespace}`));
}, {});
},
function createResource(resource, name, pathPrefix = '') {
let resourcePath = groupPath + '/';
if (pathPrefix) {
resourcePath += pathPrefix + '/';
}
resourcePath += resource.name + '/';
resourcePath += name;
// other properties here represent non-namespaced resources
};
return {
get: function(qs = {}) {
return k8sRequest(resourcePath, { qs, method: 'GET' });
},
apiResources.resources.forEach(function(resource) {
const slashIndex = resource.name.indexOf('/');
if (slashIndex !== -1) {
const subResource = resource.name.substring(slashIndex + 1);
switch (subResource) {
// TODO: Apply suitable additional methods on the resource when we understand the subresource.
// TODO: support minimally 'status' and possibly 'proxy', 'exec'.
default:
// A unknown sub-resource, for now just ignore it.
console.log(`Found unknown sub-resource ${subResource}, ignoring (${JSON.stringify(resource)})`);
return;
}
}
if (resource.namespaced) {
nsResources[resource.name] = resource;
} else {
Object.assign(api, createResourceAPI(resource));
}
});
update: function(object, qs = {}) {
return k8sRequest(resourcePath, { qs, method: 'PUT', body: object });
},
return resolve(api);
}));
});
}
patch: function(object, qs = {}) {
return k8sRequest(resourcePath, { qs, method: 'PATCH', body: object });
},
function _resolveVersion(groupName, versionName) {
const group = apiGroups.groups.find(group => group.name === groupName);
if (!group) {
throw new Error(`No API group ${groupName} available`);
delete: function(qs = {}) {
return k8sRequest(resourcePath, { qs, method: 'DELETE' });
},
};
}
const version = versionName ? group.versions.find(version => version.version === versionName) : group.preferredVersion;
if (!version) {
throw new Error(`No version ${versionName} for API group ${groupName} available`);
function createResourceAPI(resource, pathPrefix = '') {
return {
[resource.name.toLowerCase()]: createResourceCollection(resource, pathPrefix),
[resource.kind.toLowerCase()]: function(name) {
return createResource(resource, name, pathPrefix)
}
}
}
return version;
}
const nsResources = {};
const api = {
name,
version: version.version,
preferred,
ns: function(namespace) {
// Return adapted nsResources for this namespace
return Object.keys(nsResources).reduce(function(result, resourceKey) {
return Object.assign(result, createResourceAPI(nsResources[resourceKey], `namespaces/${namespace}`));
}, {});
},
if (err) {
return callback(err, null);
}
// other properties here represent non-namespaced resources
};
// Initialize the APIs
const apis = {};
function _loadApi(groupPath, version) {
return _createApi(groupPath, version).then(function(api) {
apis[api.name || ''] = api;
return apiResources.resources.reduce(function(api, resource) {
const slashIndex = resource.name.indexOf('/');
if (slashIndex !== -1) {
const subResource = resource.name.substring(slashIndex + 1);
switch (subResource) {
// TODO: Apply suitable additional methods on the resource when we understand the subresource.
// TODO: support minimally 'status' and possibly 'proxy', 'exec'.
default:
// A unknown sub-resource, for now just ignore it.
console.log(`Found unknown sub-resource ${subResource}, ignoring (${JSON.stringify(resource)})`);
}
} else if (resource.namespaced) {
nsResources[resource.name] = resource;
} else {
Object.assign(api, createResourceAPI(resource));
}
return api;
});
}
}, api);
});
}
const _createPromises = flatMap(apiGroups.groups, function(group) {
return group.versions.map(version => _loadApi(`apis/${version.groupVersion}`, version));
const coreVersion = config.version || 'v1';
return k8sRequest('apis').then(function(apiGroups) {
// Initialize the APIs
const apiPromises = flatMap(apiGroups.groups, function(group) {
return group.versions.map(version => createApi(group.name, `apis/${version.groupVersion}`, version, version.version === group.preferredVersion.version));
});
const coreVersion = config.version || 'v1';
const corePath = `api/${coreVersion}`;
_createPromises.push(_loadApi(corePath, { groupVersion: coreVersion, version: coreVersion }));
apiPromises.push(createApi('', `api/${coreVersion}`, { groupVersion: coreVersion, version: coreVersion }, true));
return Promise.all(apiPromises);
}).then(function(apis) {
return apis.reduce(function(result, api) {
result[api.name] = result[api.name] || {};
result[api.name][api.version] = api;
if (api.preferred) {
result[api.name][''] = api;
}
return result;
}, {})
}).then(function(apis) {
const coreApi = Object.assign({}, apis[''][coreVersion]);
delete coreApi.name;
return Promise.all(_createPromises).then(function() {
const coreApi = Object.assign({}, apis[coreVersion]);
delete coreApi.name;
return Object.assign({}, coreApi, {
group: function(groupName, versionName) {
const apiGroup = apis[groupName];
if (!apiGroup) {
throw new Error(`No API group ${groupName} available`);
}
return callback(null, Object.assign({}, coreApi, {
_request: k8sRequest,
_apiGroups: apiGroups,
group: function(groupName, versionName) {
const version = _resolveVersion(groupName, versionName);
const api = apiGroup[versionName || ''];
if (!api) {
throw new Error(`No version ${versionName} for API group ${groupName} available`);
}
const api = apis[version.groupVersion];
if (!api) {
throw new Error(`Unknown group ${groupName}/${versionName}`);
}
return api;
},
}));
}, function(error) {
return callback(error);
})
return api;
},
});
});
}
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc