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

kubebox

Package Overview
Dependencies
Maintainers
1
Versions
29
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

kubebox - npm Package Compare versions

Comparing version 0.0.5 to 0.0.7

.vscode/extensions.json

24

.vscode/launch.json
{
// Use IntelliSense to learn about possible Node.js debug attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
// Use IntelliSense to find out which attributes exist for node debugging
// Use hover for the description of the existing attributes
// For further information visit https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "chrome",
"name": "Node (launch)",
"type": "node",
"request": "launch",
"name": "Launch Chrome",
"file": "${workspaceRoot}/docs/index.html"
"program": "${workspaceRoot}/index.js",
"cwd": "${workspaceRoot}",
"console": "integratedTerminal"
},
{
"name": "Kukebox",
"name": "Node (attach)",
"type": "node",
"request": "attach",
"port": 5858
},
{
"name": "Chrome",
"type": "chrome",
"request": "launch",
"url": "http://localhost:8080/index.html",
"webRoot": "${workspaceRoot}/",
"sourceMaps": true
}
]
}

@@ -10,4 +10,14 @@ 'use strict';

this.master_api = master_api;
this.apis = [];
}
get master_api() {
return this._master_api;
}
set master_api(master_api) {
this.apis = [];
this._master_api = master_api;
}
get headers() {

@@ -21,2 +31,6 @@ return this.master_api.headers;

get openshift() {
return this.apis.some(path => path === '/oapi' || path === '/oapi/v1');
}
get_apis() {

@@ -23,0 +37,0 @@ return Object.assign({

'use strict';
const http = require('http'),
https = require('https');
https = require('https'),
os = require('os'),
URI = require('urijs');
module.exports.get = function (options, generator, async = true) {
return generator ? getStream(options, generator, async) : getBody(options);
if (generator) {
if (os.platform() === 'browser' && WebSocket) {
return getWebSocketStream(options, generator, async);
} else {
return getStream(options, generator, async);
}
} else {
return getBody(options);
}
};

@@ -35,8 +45,68 @@

function getWebSocketStream(options, generator, async = true) {
let cancellation = Function.prototype;
const promise = new Promise((resolve, reject) => {
const url = new URI(options.path)
.protocol((options.protocol || 'http').startsWith('https') ? 'wss' : 'ws')
.hostname(options.hostname)
.port(options.port);
if (options.headers['Authorization']) {
url.addQuery('access_token', options.headers['Authorization'].substring(7));
}
const socket = new WebSocket(url.toString(), ['binary.k8s.io']);
socket.binaryType = 'arraybuffer';
socket.addEventListener('error', event => {
reject(Error(`WebSocket connection failed to ${event.target.url}`));
});
socket.addEventListener('open', event => {
let clientAbort;
cancellation = () => {
clientAbort = true;
socket.close();
};
const gen = generator();
gen.next();
socket.addEventListener('message', event => {
const res = gen.next(new Buffer(event.data, 'binary'));
if (res.done) {
socket.close();
event.body = res.value;
// ignored for async as it's already been resolved
resolve(event);
}
});
socket.addEventListener('close', event => {
if (!clientAbort) {
const res = gen.next();
// the generator may have already returned from the 'data' event
if (!async && !res.done) {
event.body = res.value;
resolve(event);
}
}
// ignored if the generator is done already
gen.return();
});
if (async) {
resolve(event);
}
});
});
return { promise, cancellation: () => cancellation() };
}
// TODO: add wrapper method getStreamAsync instead of a boolean flag
function getStream(options, generator, async = true) {
let cancellation = Function.prototype;
const promise = new Promise((resolve, reject) => {
const promise = new Promise((resolve, reject) => {
let clientAbort, serverAbort;
const client = (options.protocol || 'http').startsWith('https') ? https : http;
const client = (options.protocol || 'http').startsWith('https') ? https : http;
const request = client.get(options)

@@ -51,3 +121,3 @@ .on('error', error => {

if (response.statusCode >= 400) {
const error = new Error(`Failed to get resource ${options.path}, status code: ${response.statusCode}`);
const error = new Error(`Failed to get resource ${options.path}, status code: ${response.statusCode}`);
// standard promises don't handle multi-parameters reject callbacks

@@ -110,3 +180,3 @@ error.response = response;

if (response.statusCode !== 101) {
const error = new Error(`Failed to upgrade resource ${options.path}, status code: ${response.statusCode}`);
const error = new Error(`Failed to upgrade resource ${options.path}, status code: ${response.statusCode}`);
// standard promises don't handle multi-parameters reject callbacks

@@ -157,3 +227,3 @@ error.response = response;

});
return {promise, cancellation: () => cancellation()};
return { promise, cancellation: () => cancellation() };
}

@@ -211,3 +281,3 @@

if (opcode === 0x8) {
return {opcode};
return { opcode };
}

@@ -236,3 +306,3 @@

}
return {FIN, opcode, length, payload};
return { FIN, opcode, length, payload };
}

@@ -6,222 +6,55 @@ 'use strict';

const blessed = require('blessed'),
Client = require('./client'),
contrib = require('blessed-contrib'),
duration = require('moment-duration-format'),
fs = require('fs'),
get = require('./http-then').get,
moment = require('moment'),
os = require('os'),
path = require('path'),
task = require('./task'),
URI = require('urijs'),
util = require('./util'),
yaml = require('js-yaml');
const Client = require('./client'),
contrib = require('blessed-contrib'),
get = require('./http-then').get,
os = require('os'),
URI = require('urijs');
const { call, delay, wait } = require('./promise');
const KubeConfig = require('./config/manager');
const login = require('./ui/login'),
const Dashboard = require('./ui/dashboard'),
login = require('./ui/login'),
namespaces = require('./ui/namespaces');
const { isNotEmpty } = require('./util');
const { call, wait } = require('./promise');
class Kubebox {
constructor(screen) {
// TODO: better management of the current Kube config
const session = {
apis : [],
cancellations : new task.Cancellations(),
namespace : null,
pod : null,
pods : {},
get openshift() {
return this.apis.some(path => path === '/oapi' || path === '/oapi/v1');
}
};
let current_namespace;
const { debug, log } = require('./ui/debug');
const kube_config = os.platform() !== 'browser'
? getKubeConfig(process.argv[2] || process.env.KUBERNETES_MASTER)
: [];
const kube_config = new KubeConfig({ debug });
const client = new Client();
if (kube_config.length) {
client.master_api = getMasterApi(kube_config[0]);
session.namespace = kube_config[0].context.namespace;
}
client.master_api = kube_config.current_context.getMasterApi();
current_namespace = kube_config.current_context.namespace.name;
screen.key(['l', 'C-l'], (ch, key) => reauthenticate()
.then(dashboard)
.catch(error => console.error(error.stack))
);
const dashboard = new Dashboard(screen, client, debug);
const grid = new contrib.grid({ rows: 12, cols: 12, screen: screen });
const pods_table = grid.set(0, 0, 6, 6, blessed.listtable, {
border : 'line',
align : 'left',
keys : true,
tags : true,
shrink : false,
noCellBorders : true,
// FIXME: margin isn't incremented for child list in scrollable list table
scrollbar : {
ch : ' ',
style : { bg: 'white' },
track : {
style : { bg: 'black' }
}
},
style : {
border : { fg: 'white' },
header : { fg: 'blue', bold: true },
cell : { fg: 'white', selected: { bg: 'blue' } }
}
});
pods_table.on('select', (item, i) => {
// empty table!
if (i === 0) return;
// FIXME: logs resources are not available for pods in non running state
const name = session.pods.items[i - 1].metadata.name;
if (name === session.pod)
return;
session.cancellations.run('dashboard.logs');
session.pod = name;
// just to update the table with the new selection
setTableData(session.pods);
// and reset the logs widget label until the log request succeeds
pod_log.setLabel('Logs');
pod_log.logLines = [];
pod_log.setItems([]);
screen.render();
const logger = function*(sinceTime) {
let log, timestamp;
try {
while (log = yield) {
// skip empty data frame payload on connect!
if (log.length === 0) continue;
log = log.toString('utf8');
const i = log.indexOf(' ');
timestamp = log.substring(0, i);
const msg = log.substring(i + 1);
// avoid scanning the whole buffer if the timestamp differs from the since time
if (!timestamp.startsWith(sinceTime) || !pod_log.logLines.includes(msg))
pod_log.log(msg);
}
} catch (e) {
// HTTP chunked transfer-encoding / streaming requests abort on timeout instead of being ended.
// WebSocket upgraded requests end when timed out on OpenShift.
}
// wait 1s and retry the pod log follow request from the latest timestamp if any
delay(1000)
.then(() => get(client.get_pod(session.namespace, name)))
.then(response => JSON.parse(response.body.toString('utf8')))
.then(pod => {
// TODO: checks should be done at the level of the container (like CrashLoopBackOff)
// check if the pod is not terminated (otherwise the connection closes)
if (pod.status.phase !== 'Running') return;
// check if the pod is not terminating
if (pod.metadata.deletionTimestamp) {
pod_log.setLabel(`Logs {grey-fg}[${name}]{/grey-fg} {red-fg}TERMINATING{/red-fg}`);
} else {
// TODO: max number of retries window
// re-follow log from the latest timestamp received
const { promise, cancellation } = get(client.follow_log(session.namespace, name, timestamp), timestamp
? function*() {
// sub-second info from the 'sinceTime' parameter are not taken into account
// so just strip the info and add a 'startsWith' check to avoid duplicates
yield* logger(timestamp.substring(0, timestamp.indexOf('.')));
}
: logger);
session.cancellations.add('dashboard.logs', cancellation);
return promise.then(() => debug.log(`Following log for pod ${session.pod} ...`));
}
})
.catch(error => {
// the pod might have already been deleted?
if (!error.response || error.response.statusCode !== 404)
console.error(error.stack);
});
};
// FIXME: deal with multi-containers pod
const { promise, cancellation } = get(client.follow_log(session.namespace, name), logger);
session.cancellations.add('dashboard.logs', cancellation);
promise
.then(() => debug.log(`Following log for pod ${session.pod} ...`))
.then(() => pod_log.setLabel(`Logs {grey-fg}[${name}]{/grey-fg}`))
.then(() => screen.render())
.catch(error => console.error(error.stack));
});
// work-around for https://github.com/chjj/blessed/issues/175
pods_table.on('remove', () => pods_table.removeLabel());
pods_table.on('prerender', () => pods_table.setLabel('Pods'));
function setTableData(pods) {
const selected = pods_table.selected;
pods_table.setData(pods.items.reduce((data, pod) => {
data.push([
pod.metadata.name === session.pod ? `{blue-fg}${pod.metadata.name}{/blue-fg}` : pod.metadata.name,
// TODO: be more fine grained for the status
// TODO: add a visual hint depending on the status
pod.status.phase,
// FIXME: negative duration is displayed when pod starts as clocks may not be synced
util.formatDuration(moment.duration(moment().diff(moment(pod.status.startTime))))
]);
return data;
}, [['NAME', 'STATUS', 'AGE']]));
pods_table.select(selected);
}
// TODO: enable user scrolling
const pod_log = grid.set(6, 0, 6, 12, contrib.log, {
border : 'line',
align : 'left',
label : 'Logs',
tags : true,
style : {
border : { fg: 'white' }
},
bufferLength: 50
});
// TODO: enable user scrolling and add timestamps
const debug = grid.set(0, 0, 12, 12, contrib.log, {
label : 'Logs',
style : {
fg : 'white',
border : { fg: 'white' }
},
bufferLength: 100
});
const log = message => new Promise(resolve => {
debug.log(message);
resolve();
});
// FIXME: the namespace selection handle should only be active
// when the connection is established to the cluster
screen.key(['n'], () => {
namespaces.prompt(screen, session, client)
namespaces.prompt(screen, client, { current_namespace })
.then(namespace => {
if (namespace === session.namespace)
return;
resetDashboard();
if (namespace === current_namespace) return;
dashboard.reset();
// switch dashboard to new namespace
session.namespace = namespace;
session.pod = null;
debug.log(`Switching to namespace ${session.namespace}`);
current_namespace = namespace;
debug.log(`Switching to namespace ${current_namespace}`);
screen.render();
return dashboard().catch(error => console.error(error.stack));
});
return dashboard.run(current_namespace);
})
.catch(error => console.error(error.stack));
});
screen.key(['l', 'C-l'], (ch, key) =>
logging().catch(error => console.error(error.stack))
);
const carousel = new contrib.carousel(
[
screen => {
// TODO: restore selection if any
screen.append(pods_table);
screen.append(pod_log);
pod_log.setScrollPerc(100);
pods_table.focus();
dashboard.render();
},

@@ -241,9 +74,7 @@ screen => {

if (client.master_api) {
// TODO: display login prompt with message on error
if (isNotEmpty(client.master_api)) {
connect().catch(error => console.error(error.stack));
} else {
login.prompt(screen, kube_config)
.then(updateSessionAfterLogin)
.then(connect)
.catch(error => console.error(error.stack));
logging().catch(error => console.error(error.stack));
}

@@ -254,42 +85,47 @@

return get(client.get_apis())
.then(response => session.apis = JSON.parse(response.body.toString('utf8')).paths)
.then(response => client.apis = JSON.parse(response.body.toString('utf8')).paths)
.catch(error => debug.log(`Unable to retrieve available APIs: ${error.message}`))
.then(dashboard)
.then(_ => current_namespace
? Promise.resolve(current_namespace)
: namespaces.prompt(screen, client, { promptAfterRequest : true })
.then(namespace => current_namespace = namespace))
.then(dashboard.run)
.catch(error => error.response && [401, 403].includes(error.response.statusCode)
? log(`Authentication required for ${client.url} (openshift)`)
.then(_ => authenticate(login))
.then(_ => connect())
.then(_ => login ? authenticate(login).then(_ => connect()) : logging())
: Promise.reject(error));
}
function authenticate(credentials) {
if (!session.openshift)
throw Error(`No authentication available for: ${client.url}`);
function logging() {
return login.prompt(screen, kube_config)
// it may be better to reset the dashboard when authentication has succeeded
.then(call(dashboard.reset))
.then(updateSessionAfterLogin)
.then(connect);
}
// TODO: display an error message in the login prompt when authentication has failed
return (credentials ? Promise.resolve(credentials)
// TODO: it may be better to reset the dashboard when authentication has succeeded
: login.prompt(screen, kube_config).then(call(resetDashboard)))
.then(updateSessionAfterLogin)
.then(credentials => util.isEmpty(credentials.token)
// try retrieving an OAuth access token from the OpenShift OAuth server
? os.platform() === 'browser'
? get(client.oauth_authorize_web(credentials))
.then(response => {
const path = URI.parse(response.url).path;
if (response.statusCode === 200 && path === '/oauth/token/display') {
return response.body.toString('utf8').match(/<code>(.*)<\/code>/)[1];
} else if (path === '/login') {
const error = Error('Authentication failed!');
// fake authentication error to emulate the implicit grant flow
response.statusCode = 401;
error.response = response;
throw error;
} else {
throw Error('Unsupported authentication!');
}
})
: get(client.oauth_authorize(credentials))
.then(response => response.headers.location.match(/access_token=([^&]+)/)[1])
: credentials.token)
function authenticate(login) {
if (!client.openshift)
return Promise.reject(Error(`No authentication available for: ${client.url}`));
return (isNotEmpty(login.token) ? Promise.resolve(login.token)
// try retrieving an OAuth access token from the OpenShift OAuth server
: os.platform() === 'browser'
? get(client.oauth_authorize_web(login))
.then(response => {
const path = URI.parse(response.url).path;
if (response.statusCode === 200 && path === '/oauth/token/display') {
return response.body.toString('utf8').match(/<code>(.*)<\/code>/)[1];
} else if (path === '/login') {
const error = Error('Authentication failed!');
// fake authentication error to emulate the implicit grant flow
response.statusCode = 401;
error.response = response;
throw error;
} else {
throw Error('Unsupported authentication!');
}
})
: get(client.oauth_authorize(login))
.then(response => response.headers.location.match(/access_token=([^&]+)/)[1]))
// test it authenticates ok

@@ -306,107 +142,10 @@ .then(token => get(client.get_user(token))

.then(wait(1000))
.then(reauthenticate)
.then(logging)
: Promise.reject(error));
}
function reauthenticate() {
delete session.namespace;
return authenticate();
}
function dashboard() {
return (session.namespace ? Promise.resolve(session.namespace)
: namespaces.prompt(screen, session, client, { promptAfterRequest : true })
.then(namespace => session.namespace = namespace))
.then(_ => get(client.get_pods(session.namespace)))
.then(response => {
session.pods = JSON.parse(response.body.toString('utf8'));
session.pods.items = session.pods.items || [];
})
.then(() => setTableData(session.pods))
.then(() => debug.log(`Watching for pods changes in namespace ${session.namespace} ...`))
.then(() => screen.render())
.then(() => {
const id = setInterval(refreshPodAges, 1000);
session.cancellations.add('dashboard.refreshPodAges', () => clearInterval(id));
})
.then(() => {
const { promise, cancellation } = get(client.watch_pods(session.namespace,session.pods.metadata.resourceVersion), updatePodTable);
session.cancellations.add('dashboard', cancellation);
return promise;
});
}
function resetDashboard() {
// cancel current running tasks and open requests
session.cancellations.run('dashboard');
// reset dashboard widgets
pods_table.clearItems();
pod_log.setLabel('Logs');
pod_log.logLines = [];
pod_log.setItems([]);
screen.render();
}
function* updatePodTable() {
const index = object => session.pods.items.findIndex(pod => pod.metadata.uid === object.metadata.uid);
let change;
try {
while (change = yield) {
change = JSON.parse(change);
switch (change.type) {
case 'ADDED':
session.pods.items.push(change.object);
break;
case 'MODIFIED':
session.pods.items[index(change.object)] = change.object;
break;
case 'DELETED':
session.pods.items.splice(index(change.object), 1);
if (change.object.metadata.name === session.pod) {
// check if that's the selected pod and clean the selection
pod_log.setLabel(`Logs {grey-fg}[${session.pod}]{/grey-fg} {red-fg}DELETED{/red-fg}`);
session.pod = null;
}
break;
}
setTableData(session.pods);
screen.render();
}
} catch (e) {
// HTTP chunked transfer-encoding / streaming watch requests abort on timeout when the 'timeoutSeconds'
// request parameter is greater than the '--min-request-timeout' server API option,
// otherwise the connections just end normally (http://kubernetes.io/docs/admin/kube-apiserver/).
// WebSocket upgraded watch requests (idle?) end when timed out on Kubernetes.
}
// retry the pods list watch request
session.cancellations.run('dashboard.refreshPodAges');
dashboard().catch(error => console.error(error.stack));
}
function refreshPodAges() {
session.pods.items.forEach(pod => moment(pod.status.startTime).add(1, 's').toISOString());
// we may want to avoid recreating the whole table data
setTableData(session.pods);
screen.render();
}
function updateSessionAfterLogin(login) {
const config = findClusterByUrl(kube_config, login.cluster);
if (!config) {
client.master_api = getBaseMasterApi(login.cluster);
} else {
// override token, server URL and user but keep the rest of the config
if (!util.isEmpty(login.token)) {
config.user.token = login.token;
}
config.cluster.server = login.cluster;
// use new master_api
const master_api = getMasterApi(config);
client.master_api = master_api;
// TODO: if only the token is defined, get username from token and replace here
if (!util.isEmpty(login.username)) {
config.context.user = login.username + '/' + master_api.hostname + ':' + master_api.port;
}
session.namespace = config.context.namespace;
}
kube_config.updateOrInsertContext(login);
client.master_api = kube_config.current_context.getMasterApi();
current_namespace = kube_config.current_context.namespace.name;
return login;

@@ -417,109 +156,2 @@ }

// TODO: support client access information provided as CLI options
// CLI option -> Kube config context -> prompt user
// TODO: better context disambiguation workflow
// see:
// - http://kubernetes.io/docs/user-guide/accessing-the-cluster/
// - http://kubernetes.io/docs/user-guide/kubeconfig-file/
function getKubeConfig(master) {
// TODO: check if the file exists and can be read first
const kube = yaml.safeLoad(fs.readFileSync(path.join(os.homedir(), '.kube/config'), 'utf8'));
const configs = [];
if (!master) {
const current = kube['current-context'];
if (current) {
const context = kube.contexts.find(item => item.name === current).context;
configs.push({
context : context,
cluster : kube.clusters.find(item => item.name === context.cluster).cluster
});
}
// TODO: better deal with the case no current context is set
// - in case the master passed as argument matches the current context
} else {
const cluster = findClusterByUrl(kube.clusters, master);
if (cluster) {
configs.push({
// FIXME: there can be multiple contexts for the same cluster
// that could be disambiguated in updateSessionAfterLogin
context : (kube.contexts.find(item => item.context.cluster === cluster.name) || {}).context || {},
cluster : cluster.cluster
});
} else {
configs.push({
context : {},
cluster : { server: master }
});
}
}
kube.clusters.filter(cluster => cluster.cluster !== configs[0].cluster)
.forEach(cluster => configs.push({
cluster : cluster.cluster,
context : (kube.contexts.find(item => item.context.cluster === cluster.name) || {}).context || {},
}));
configs.forEach(config => config.user = (kube.users.find(user => user.name === config.context.user) || {}).user || {});
return configs;
}
function findClusterByUrl(clusters, master) {
const uri = URI(master);
let matches = clusters.filter(item => URI(item.cluster.server).hostname() === uri.hostname());
if (matches.length > 1) {
matches = matches.filter(item => {
const server = URI(item.cluster.server);
return server.protocol() === uri.protocol() && server.port() === uri.port();
});
}
if (matches.length > 1)
throw Error(`Multiple clusters found for server: ${master}!`);
return matches.length === 1 ? matches[0] : null;
}
function getMasterApi({ cluster, user }) {
const api = getBaseMasterApi(cluster.server);
if (user['client-certificate']) {
api.cert = fs.readFileSync(user['client-certificate']);
}
if (user['client-certificate-data']) {
api.cert = Buffer.from(user['client-certificate-data'], 'base64');
}
if (user['client-key']) {
api.key = fs.readFileSync(user['client-key']);
}
if (user['client-key-data']) {
api.key = Buffer.from(user['client-key-data'], 'base64');
}
if (user.token) {
api.headers['Authorization'] = `Bearer ${user.token}`;
}
if (cluster['insecure-skip-tls-verify']) {
api.rejectUnauthorized = false;
}
if (cluster['certificate-authority']) {
api.ca = fs.readFileSync(cluster['certificate-authority']);
}
if (cluster['certificate-authority-data']) {
api.ca = Buffer.from(cluster['certificate-authority-data'], 'base64');
}
return api;
}
function getBaseMasterApi(url) {
const { protocol, hostname, port } = URI.parse(url);
const api = {
protocol : protocol + ':', hostname, port,
headers : {
'Accept' : 'application/json, text/plain, */*'
},
get url() {
return this.protocol + '//' + this.hostname + (this.port ? ':' + this.port : '');
}
}
return api;
}
module.exports = Kubebox;

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

function login_form(kube_config) {
function login_form(kube_config, screen) {
const form = blessed.form({
parent : screen,
name : 'form',
keys : true,

@@ -42,4 +44,5 @@ mouse : true,

const cluster = blessed.textbox({
const url = blessed.textbox({
parent : form,
name : 'url',
inputOnFocus : true,

@@ -52,7 +55,6 @@ mouse : true,

top : 0,
// FIXME: use the current config
value : kube_config.length ? kube_config[0].cluster.server : ''
value : kube_config.current_context.cluster.server
});
// retain key grabbing as text areas reset it after input reading
cluster.on('blur', () => form.screen.grabKeys = true);
url.on('blur', () => form.screen.grabKeys = true);

@@ -69,2 +71,3 @@ blessed.text({

parent : form,
name : 'username',
inputOnFocus : true,

@@ -77,4 +80,3 @@ mouse : true,

top : 2,
// FIXME: use the current config
value : kube_config.length ? kube_config[0].context.user.split('/')[0] : ''
value : kube_config.current_context.user.username
});

@@ -86,4 +88,2 @@ // retain key grabbing as text areas reset it after input reading

parent : form,
mouse : true,
keys : true,
left : 1,

@@ -97,2 +97,3 @@ top : 3,

parent : form,
name : 'password',
inputOnFocus : true,

@@ -112,4 +113,2 @@ mouse : true,

parent : form,
mouse : true,
keys : true,
left : 1,

@@ -123,2 +122,3 @@ top : 4,

parent : form,
name : 'token',
inputOnFocus : true,

@@ -131,4 +131,3 @@ mouse : true,

top : 4,
// FIXME: use the current config
value : kube_config.length ? kube_config[0].user.token : ''
value : kube_config.current_context.user.token
});

@@ -158,2 +157,16 @@ // retain key grabbing as text areas reset it after input reading

// Reset the focus stack when clicking on a form element
const focusOnclick = element => element.on('click', () => form._selected = element);
focusOnclick(username);
focusOnclick(password);
focusOnclick(token);
focusOnclick(url);
// This is a hack to not 'rewind' the focus stack on 'blur'
username.options.inputOnFocus = false;
password.options.inputOnFocus = false;
token.options.inputOnFocus = false;
url.options.inputOnFocus = false;
return {

@@ -164,3 +177,3 @@ form,

token : () => token.value,
cluster : () => cluster.value
url : () => url.value
};

@@ -173,3 +186,3 @@ }

screen.grabKeys = true;
const { form, username, password, token, cluster } = login_form(kube_config);
const { form, username, password, token, url } = login_form(kube_config, screen);
screen.append(form);

@@ -185,3 +198,3 @@ form.focusNext();

fulfill({
cluster : cluster(),
url : url(),
username : username(),

@@ -188,0 +201,0 @@ password : password(),

@@ -17,2 +17,3 @@ 'use strict';

tags : true,
mouse : true,
border : { type: 'line' },

@@ -36,3 +37,3 @@ scrollbar : {

function prompt(screen, session, client, { promptAfterRequest } = { promptAfterRequest : false }) {
function prompt(screen, client, { current_namespace, promptAfterRequest } = { promptAfterRequest : false }) {
return new Promise(function(fulfill, reject) {

@@ -42,5 +43,5 @@ const list = namespaces_list();

// TODO: watch for namespace changes when the selection list is open
// TODO: watch for namespaces changes when the selection list is open
function request_namespaces() {
return get(session.openshift ? client.get_projects() : client.get_namespaces())
return get(client.openshift ? client.get_projects() : client.get_namespaces())
.then(response => JSON.parse(response.body.toString('utf8')))

@@ -50,3 +51,3 @@ // TODO: display a message in case the user has access to no namespaces

.then(namespaces => list.setItems(namespaces.items.reduce((data, namespace) => {
data.push(namespace.metadata.name === session.namespace
data.push(namespace.metadata.name === current_namespace
? `{blue-fg}${namespace.metadata.name}{/blue-fg}`

@@ -89,3 +90,3 @@ : namespace.metadata.name);

// Force the user to select a namespace
if (item || session.namespace) {
if (item || current_namespace) {
close_namespaces_list();

@@ -96,4 +97,4 @@ }

list.on('cancel', () => {
if (session.namespace) {
fulfill(session.namespace);
if (current_namespace) {
fulfill(current_namespace);
}

@@ -100,0 +101,0 @@ });

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

module.exports.formatDuration = function(duration) {
module.exports.isNotEmpty = str => str && str.length > 0;
module.exports.formatDuration = function (duration) {
if (duration.years() > 0)

@@ -8,0 +10,0 @@ return duration.format('y[y] M[M]');

@@ -5,3 +5,3 @@ {

"author": "Antonin Stefanutti",
"version": "0.0.5",
"version": "0.0.7",
"license": "MIT",

@@ -11,8 +11,14 @@ "homepage": "https://github.com/astefanutti/kubebox",

"bin": {
"kubebox": "./bin/kubebox"
"kubebox": "index.js"
},
"scripts": {
"start": "./bin/kubebox"
"start": "node index.js",
"browserify": "browserify -r ./lib/kubebox.js:kubebox -r blessed -i pty.js -i term.js -i map-canvas -i marked-terminal -i picture-tube -o docs/kubebox.js",
"browserify-debug": "browserify --debug -r ./lib/kubebox.js:kubebox -r blessed -i pty.js -i term.js -i map-canvas -i marked-terminal -i picture-tube | exorcist docs/kubebox.js.map > docs/kubebox.js",
"bundle": "browserify index.js -o bundle.js -i pty.js -i term.js -i map-canvas -i marked-terminal -i picture-tube --bare",
"executable": "nexe package.json"
},
"preferGlobal": true,
"pkg": {
"scripts": "node_modules/blessed/lib/widgets/*.js"
},
"repository": {

@@ -23,4 +29,6 @@ "type": "git",

"devDependencies": {
"xterm": "~2.8.1",
"browserify": "~14.4.0"
"browserify": "~14.4.0",
"exorcist": "^0.4.0",
"nexe": "~1.1.3",
"xterm": "~2.9.0"
},

@@ -37,3 +45,27 @@ "dependencies": {

"node": ">=6.0.0"
},
"nexe": {
"input": "./index.js",
"output": "kubebox^$",
"temp": ".nexe",
"browserify": {
"requires": [],
"excludes": [
"pty.js",
"term.js",
"map-canvas",
"marked-terminal",
"picture-tube"
],
"paths": []
},
"runtime": {
"nodeConfigureOpts": [
"--fully-static"
],
"framework": "node",
"version": "7.0.0",
"ignoreFlags": true
}
}
}

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

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