├── .gitignore ├── public └── kubeasy.gif ├── bin └── index.js ├── .travis.yml ├── .prettierrc ├── src ├── constants │ └── index.js ├── index.js ├── hooks │ ├── index.js │ ├── tests │ │ ├── pods.test.js │ │ └── pods.json │ ├── logs.js │ ├── activeCluster.js │ └── pods.js ├── actions │ └── index.js └── widgets │ ├── index.js │ ├── actions.js │ ├── activeCluster.js │ ├── logs.js │ ├── bash.js │ └── pods.js ├── package.json └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | node_modules/ 3 | -------------------------------------------------------------------------------- /public/kubeasy.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcenacp/kubeasy/HEAD/public/kubeasy.gif -------------------------------------------------------------------------------- /bin/index.js: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env node 2 | 3 | const { kubeasy } = require('../src/'); 4 | 5 | kubeasy(); 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '11.14.0' 4 | cache: 5 | directories: 6 | - 'node_modules' 7 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "bracketSpacing": true, 3 | "jsxBracketSameLine": false, 4 | "printWidth": 100, 5 | "singleQuote": true, 6 | "trailingComma": "all" 7 | } -------------------------------------------------------------------------------- /src/constants/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | LONG_TIMEOUT: 30000, 3 | SHORT_TIMEOUT: 500, 4 | 5 | ACTIVE_CLUSTER: 'activeCluster', 6 | ACTIVE_POD: 'activePod', 7 | LOGS: 'logs', 8 | PODS: 'pods', 9 | }; 10 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | const EventEmitter = require('events'); 2 | 3 | const { initHooks } = require('./hooks'); 4 | const { initWidgets } = require('./widgets'); 5 | 6 | const kubeasy = () => { 7 | const state = {}; 8 | const middleware = new EventEmitter(); 9 | initWidgets(middleware, state); 10 | initHooks(middleware, state); 11 | }; 12 | 13 | module.exports = { 14 | kubeasy, 15 | }; 16 | -------------------------------------------------------------------------------- /src/hooks/index.js: -------------------------------------------------------------------------------- 1 | const ActiveClusterHook = require('./activeCluster'); 2 | const PodsHook = require('./pods'); 3 | const LogsHook = require('./logs'); 4 | 5 | const initHooks = (middleware, state) => { 6 | const Hooks = [LogsHook, PodsHook, ActiveClusterHook]; 7 | Hooks.forEach(Hook => { 8 | const hook = new Hook(middleware, state); 9 | hook.get(); 10 | hook.set(); 11 | }); 12 | }; 13 | 14 | module.exports = { 15 | initHooks, 16 | }; 17 | -------------------------------------------------------------------------------- /src/hooks/tests/pods.test.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const pods = require('./pods.json'); 4 | const PodsWidget = require('../pods.js'); 5 | 6 | describe('[HOOKS] Pods', () => { 7 | describe('formatData', () => { 8 | it('should extract status', () => { 9 | const podsWidget = new PodsWidget(); 10 | const expectedData = [['pod-1-xxxxx', 'Ready']]; 11 | 12 | assert.deepEqual(podsWidget.formatData(pods), expectedData); 13 | }); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /src/actions/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | DEBUG: 'DEBUG', 3 | 4 | // ACTIVE_CLUSTER 5 | GET_ACTIVE_CLUSTER: 'GET_ACTIVE_CLUSTER', 6 | HIDE_ACTIVE_CLUSTER: 'HIDE_ACTIVE_CLUSTER', 7 | SET_ACTIVE_CLUSTER: 'SET_ACTIVE_CLUSTER', 8 | SHOW_ACTIVE_CLUSTER: 'SHOW_ACTIVE_CLUSTER', 9 | UPDATE_ACTIVE_CLUSTER: 'UPDATE_ACTIVE_CLUSTER', 10 | 11 | // BASH 12 | GET_BASH: 'GET_BASH', 13 | 14 | // LOGS 15 | GET_LOGS: 'GET_LOGS', 16 | HIDE_LOGS: 'HIDE_LOGS', 17 | SET_LOGS: 'SET_LOGS', 18 | SHOW_LOGS: 'SHOW_LOGS', 19 | UPDATE_LOGS: 'UPDATE_LOGS', 20 | 21 | // PODS 22 | GET_PODS: 'GET_PODS', 23 | HIDE_PODS: 'SET_PODS', 24 | SET_PODS: 'SET_PODS', 25 | SHOW_PODS: 'SET_PODS', 26 | UPDATE_PODS: 'UPDATE_PODS', 27 | }; 28 | -------------------------------------------------------------------------------- /src/widgets/index.js: -------------------------------------------------------------------------------- 1 | const blessed = require('blessed'); 2 | const _ = require('lodash'); 3 | 4 | const ActionsWidget = require('./actions'); 5 | const ActiveClusterWidget = require('./activeCluster'); 6 | const BashWidget = require('./bash'); 7 | const LogsWidget = require('./logs'); 8 | const PodsWidget = require('./pods'); 9 | 10 | const initWidgets = (middleware, state) => { 11 | const screen = blessed.screen({ smartCSR: true }); 12 | const Widgets = [ActionsWidget, BashWidget, LogsWidget, PodsWidget, ActiveClusterWidget]; 13 | Widgets.forEach(Widget => { 14 | new Widget(middleware, state, screen); 15 | }); 16 | screen.key(['C-c'], () => process.exit(0)); 17 | screen.render(); 18 | }; 19 | 20 | module.exports = { 21 | initWidgets, 22 | }; 23 | -------------------------------------------------------------------------------- /src/hooks/logs.js: -------------------------------------------------------------------------------- 1 | const { exec } = require('child_process'); 2 | 3 | const { GET_LOGS, SET_LOGS, UPDATE_LOGS } = require('../actions'); 4 | const { ACTIVE_POD, LOGS, SHORT_TIMEOUT } = require('../constants'); 5 | 6 | class LogsHook { 7 | constructor(middleware, state) { 8 | this.middleware = middleware; 9 | this.state = state; 10 | } 11 | 12 | get() { 13 | this.middleware.on(GET_LOGS, () => { 14 | const podId = this.state[ACTIVE_POD]; 15 | exec(`kubectl logs ${podId}`, (error, stdout) => { 16 | this.middleware.emit(SET_LOGS, stdout); 17 | }); 18 | }); 19 | } 20 | 21 | set() { 22 | this.middleware.on(SET_LOGS, data => { 23 | const logsData = data.toString(); 24 | if (this.state[LOGS] !== logsData) { 25 | this.state[LOGS] = logsData; 26 | this.middleware.emit(UPDATE_LOGS); 27 | } 28 | }); 29 | } 30 | } 31 | 32 | module.exports = LogsHook; 33 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "kubeasy", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "src/index.js", 6 | "scripts": { 7 | "dev": "nodemon bin/index.js", 8 | "lint": "prettier **/*.js --list-different", 9 | "start": "node bin/index.js", 10 | "test": "mocha src/**/*.test.js", 11 | "test:watch": "mocha --watch src/**/*.test.js" 12 | }, 13 | "bin": { 14 | "kubeasy": "bin/index.js" 15 | }, 16 | "repository": { 17 | "type": "git", 18 | "url": "git+https://github.com/marcenacp/kubeasy.git" 19 | }, 20 | "author": "", 21 | "license": "MIT", 22 | "bugs": { 23 | "url": "https://github.com/marcenacp/kubeasy/issues" 24 | }, 25 | "homepage": "https://github.com/marcenacp/kubeasy#readme", 26 | "dependencies": { 27 | "blessed": "^0.1.81", 28 | "blessed-xterm": "^1.1.20", 29 | "lodash": "^4.17.11", 30 | "xterm": "^3.8.1" 31 | }, 32 | "devDependencies": { 33 | "mocha": "^6.1.4", 34 | "nodemon": "^1.19.0", 35 | "prettier": "^1.17.1" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/hooks/activeCluster.js: -------------------------------------------------------------------------------- 1 | const { exec } = require('child_process'); 2 | 3 | const { 4 | GET_ACTIVE_CLUSTER, 5 | GET_LOGS, 6 | GET_PODS, 7 | SET_ACTIVE_CLUSTER, 8 | SET_LOGS, 9 | SET_PODS, 10 | UPDATE_ACTIVE_CLUSTER, 11 | } = require('../actions'); 12 | 13 | const { ACTIVE_CLUSTER, SHORT_TIMEOUT } = require('../constants'); 14 | 15 | class ActiveClusterHook { 16 | constructor(middleware, state) { 17 | this.middleware = middleware; 18 | this.state = state; 19 | } 20 | 21 | get() { 22 | setInterval(() => { 23 | exec('kubectl config current-context', (error, stdout) => { 24 | this.middleware.emit(SET_ACTIVE_CLUSTER, stdout); 25 | }); 26 | }, SHORT_TIMEOUT); 27 | } 28 | 29 | set() { 30 | this.middleware.on(SET_ACTIVE_CLUSTER, data => { 31 | if (this.state[ACTIVE_CLUSTER] !== data) { 32 | this.state[ACTIVE_CLUSTER] = data; 33 | this.middleware.emit(UPDATE_ACTIVE_CLUSTER); 34 | } 35 | }); 36 | } 37 | } 38 | 39 | module.exports = ActiveClusterHook; 40 | -------------------------------------------------------------------------------- /src/widgets/actions.js: -------------------------------------------------------------------------------- 1 | const blessed = require('blessed'); 2 | 3 | class ActionsWidget { 4 | constructor(middleware, state, screen) { 5 | this.middleware = middleware; 6 | this.screen = screen; 7 | this.state = state; 8 | 9 | this.create(); 10 | this.render(); 11 | } 12 | 13 | render() {} 14 | 15 | hide() {} 16 | 17 | create() { 18 | this.widget = blessed.listbar({ 19 | mouse: true, 20 | align: 'left', 21 | alwaysScroll: true, 22 | commands: { 23 | 'Logs [L]': {}, 24 | 'Shell [S]': {}, 25 | 'Return [Echap]': {}, 26 | 'Quit [Ctrl-C]': {}, 27 | }, 28 | select: null, 29 | height: Math.floor(this.screen.height * 0.2), 30 | keys: false, 31 | label: '( Actions )', 32 | left: Math.floor(this.screen.width * 0.3), 33 | scrollable: true, 34 | top: 0, 35 | width: Math.floor(this.screen.width * 0.7), 36 | style: { 37 | focus: { border: { fg: 'blue' } }, 38 | border: { fg: 'blue' }, 39 | }, 40 | border: { 41 | type: 'line', 42 | }, 43 | }); 44 | this.screen.append(this.widget); 45 | } 46 | } 47 | 48 | module.exports = ActionsWidget; 49 | -------------------------------------------------------------------------------- /src/widgets/activeCluster.js: -------------------------------------------------------------------------------- 1 | const blessed = require('blessed'); 2 | 3 | const { UPDATE_ACTIVE_CLUSTER } = require('../actions'); 4 | const { ACTIVE_CLUSTER } = require('../constants'); 5 | 6 | class ActiveClusterWidget { 7 | constructor(middleware, state, screen) { 8 | this.previousCluster = ''; 9 | 10 | this.middleware = middleware; 11 | this.screen = screen; 12 | this.state = state; 13 | 14 | this.create(); 15 | this.render(); 16 | } 17 | 18 | render() { 19 | this.middleware.on(UPDATE_ACTIVE_CLUSTER, () => { 20 | const activeCluster = this.state[ACTIVE_CLUSTER]; 21 | if (activeCluster !== this.previousCluster) { 22 | this.widget.setContent(activeCluster); 23 | } 24 | }); 25 | } 26 | 27 | hide() {} 28 | 29 | create() { 30 | this.widget = blessed.box({ 31 | align: 'left', 32 | alwaysScroll: true, 33 | content: 'Loading...', 34 | height: Math.floor(this.screen.height * 0.2), 35 | keys: true, 36 | label: '( Active cluster )', 37 | left: 0, 38 | scrollable: true, 39 | top: 0, 40 | width: Math.floor(this.screen.width * 0.3), 41 | style: { 42 | selected: { bg: 'blue' }, 43 | focus: { border: { fg: 'blue' } }, 44 | border: { fg: 'blue' }, 45 | }, 46 | border: { 47 | type: 'line', 48 | }, 49 | }); 50 | this.screen.append(this.widget); 51 | } 52 | } 53 | 54 | module.exports = ActiveClusterWidget; 55 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # kubeasy 2 | 3 | [![Build Status](https://travis-ci.org/marcenacp/kubeasy.svg?branch=master)](https://travis-ci.org/marcenacp/kubeasy) 4 | 5 | `kubeasy` is an interactive wrapper around [kubectl](https://kubernetes.io/docs/reference/kubectl/kubectl/) 6 | to help you manage your Kubernetes clusters. 7 | 8 | ![](./public/kubeasy.gif) 9 | 10 | ## Installation 11 | 12 | - Install [kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl/) 13 | 14 | - Install `kubeasy` by downloading the latest version from Github 15 | ```bash 16 | git clone https://github.com/marcenacp/kubeasy.git 17 | cd kubeasy/ 18 | npm install 19 | npm link 20 | ``` 21 | 22 | - Use it with the command: `kubeasy` 23 | 24 | ## Development 25 | 26 | ### Development pattern 27 | 28 | I use [blessed](https://github.com/chjj/blessed) to render the command-line interface, 29 | and Node's [EventEmitter](https://nodejs.org/api/events.html) to propagate data between hooks and widgets. 30 | 31 | - **Hooks** (in `src/hooks`) get data from Kubernetes using `kubectl` and store it in the state. 32 | 33 | - **Widgets** (in `src/widgets`) watch inputs from user and render the command-line interface according to the state. 34 | 35 | ### Useful commands 36 | 37 | - `npm run dev`: launch application with [nodemon](https://github.com/remy/nodemon) 38 | - `npm run test`: launch unit tests and check syntax in Javascript files using [prettier](https://github.com/prettier/prettier) (run in CI) 39 | - `npm run test:watch`: launch unit tests in watch mode 40 | -------------------------------------------------------------------------------- /src/widgets/logs.js: -------------------------------------------------------------------------------- 1 | const blessed = require('blessed'); 2 | 3 | const { UPDATE_LOGS } = require('../actions'); 4 | const { LOGS } = require('../constants'); 5 | 6 | class LogsWidget { 7 | constructor(middleware, state, screen) { 8 | this.middleware = middleware; 9 | this.state = state; 10 | this.screen = screen; 11 | 12 | this.create(); 13 | this.render(); 14 | } 15 | 16 | render() { 17 | this.middleware.on(UPDATE_LOGS, () => { 18 | this.widget.setContent(this.state[LOGS]); 19 | this.widget.setFront(); 20 | this.widget.show(); 21 | this.widget.focus(); 22 | this.screen.render(); 23 | }); 24 | } 25 | 26 | hide() {} 27 | 28 | create() { 29 | this.widget = blessed.log({ 30 | align: 'left', 31 | alwaysScroll: false, 32 | content: 'Loading...', 33 | height: '70%', 34 | keys: true, 35 | label: '( Logs )', 36 | left: 'center', 37 | parent: this.screen, 38 | scrollable: true, 39 | top: 'center', 40 | width: '70%', 41 | style: { 42 | selected: { bg: 'green' }, 43 | focus: { border: { fg: 'green' } }, 44 | }, 45 | border: { 46 | type: 'line', 47 | }, 48 | hover: { 49 | bg: 'blue', 50 | }, 51 | scrollbar: { 52 | fg: 'blue', 53 | ch: '|', 54 | }, 55 | }); 56 | this.widget.hide(); 57 | this.widget.on('keypress', (_, key) => { 58 | if (key.name === 'escape') { 59 | this.widget.hide(); 60 | this.screen.render(); 61 | } 62 | }); 63 | } 64 | } 65 | 66 | module.exports = LogsWidget; 67 | -------------------------------------------------------------------------------- /src/widgets/bash.js: -------------------------------------------------------------------------------- 1 | const XTerm = require('blessed-xterm'); 2 | 3 | const { GET_BASH } = require('../actions'); 4 | const { ACTIVE_POD } = require('../constants'); 5 | 6 | class BashWidget { 7 | constructor(middleware, state, screen) { 8 | this.middleware = middleware; 9 | this.state = state; 10 | this.screen = screen; 11 | 12 | this.create(); 13 | this.render(); 14 | } 15 | 16 | render() { 17 | this.middleware.on(GET_BASH, () => { 18 | const terminal = new XTerm({ 19 | align: 'left', 20 | args: [], 21 | border: 'line', 22 | cursorType: 'block', 23 | cwd: process.cwd(), 24 | env: process.env, 25 | height: Math.floor(this.screen.height * 0.7), 26 | label: '( Bash )', 27 | left: 'center', 28 | scrollback: 1000, 29 | shell: process.env.SHELL || 'sh', 30 | top: 'center', 31 | width: Math.floor(this.screen.width * 0.7), 32 | style: { 33 | fg: 'default', 34 | bg: 'default', 35 | border: { fg: 'default' }, 36 | focus: { border: { fg: 'green' } }, 37 | scrolling: { border: { fg: 'red' } }, 38 | }, 39 | }); 40 | terminal.focus(); 41 | const podId = this.state[ACTIVE_POD]; 42 | terminal.injectInput(`kubectl exec -it ${podId} -- bash\n`); 43 | this.screen.append(terminal); 44 | terminal.on('keypress', (_, key) => { 45 | if (key.name === 'escape') { 46 | terminal.destroy(); 47 | } 48 | }); 49 | }); 50 | } 51 | 52 | hide() {} 53 | 54 | create() {} 55 | } 56 | 57 | module.exports = BashWidget; 58 | -------------------------------------------------------------------------------- /src/hooks/pods.js: -------------------------------------------------------------------------------- 1 | const { exec } = require('child_process'); 2 | const _ = require('lodash'); 3 | 4 | const { SET_PODS, UPDATE_ACTIVE_CLUSTER, UPDATE_PODS } = require('../actions'); 5 | const { LONG_TIMEOUT, PODS } = require('../constants'); 6 | 7 | class PodsHook { 8 | constructor(middleware, state) { 9 | this.middleware = middleware; 10 | this.state = state; 11 | } 12 | 13 | exec() { 14 | exec('kubectl get pods -o=json', (error, stdout) => { 15 | this.middleware.emit(SET_PODS, JSON.parse(stdout)); 16 | }); 17 | } 18 | 19 | get() { 20 | setInterval(this.exec.bind(this), LONG_TIMEOUT); 21 | this.middleware.on(UPDATE_ACTIVE_CLUSTER, this.exec.bind(this)); 22 | } 23 | 24 | formatData(data) { 25 | const LAST_TRANSITION_TIME = 'lastTransitionTime'; 26 | const { items } = data; 27 | return items.map(item => { 28 | const name = _.get(item, 'metadata.name', ''); 29 | const conditions = _.get(item, 'status.conditions', []) 30 | .filter(condition => !!_.get(condition, LAST_TRANSITION_TIME)) 31 | .map(condition => ({ 32 | ...condition, 33 | [LAST_TRANSITION_TIME]: new Date(_.get(condition, LAST_TRANSITION_TIME)), 34 | })) 35 | .sort( 36 | (condition1, condition2) => 37 | _.get(condition2, LAST_TRANSITION_TIME) - _.get(condition1, LAST_TRANSITION_TIME), 38 | ); 39 | const latestCondition = _.get(conditions, `[0].type`, ''); 40 | 41 | return [name, latestCondition]; 42 | }); 43 | } 44 | 45 | set() { 46 | this.middleware.on(SET_PODS, data => { 47 | const formattedData = this.formatData(data); 48 | 49 | if (!_.isEqual(this.state[PODS], formattedData)) { 50 | this.state[PODS] = formattedData; 51 | this.middleware.emit(UPDATE_PODS); 52 | } 53 | }); 54 | } 55 | } 56 | 57 | module.exports = PodsHook; 58 | -------------------------------------------------------------------------------- /src/widgets/pods.js: -------------------------------------------------------------------------------- 1 | const blessed = require('blessed'); 2 | const _ = require('lodash'); 3 | 4 | const { GET_BASH, GET_LOGS, UPDATE_PODS } = require('../actions'); 5 | const { ACTIVE_POD, PODS } = require('../constants'); 6 | 7 | class PodsWidget { 8 | constructor(middleware, state, screen) { 9 | this.middleware = middleware; 10 | this.state = state; 11 | this.screen = screen; 12 | 13 | this.create(); 14 | this.render(); 15 | } 16 | 17 | render() { 18 | this.middleware.on(UPDATE_PODS, () => { 19 | this.widget.focus(); 20 | this.widget.setData([['NAME', 'STATUS'], ...this.state[PODS]]); 21 | }); 22 | } 23 | 24 | hide() {} 25 | 26 | create() { 27 | this.widget = blessed.listtable({ 28 | align: 'center', 29 | border: 'line', 30 | data: null, 31 | height: Math.floor(this.screen.height * 0.8), 32 | interactive: true, 33 | keys: true, 34 | label: '( Pods )', 35 | left: 0, 36 | mouse: true, 37 | parent: this.screen, 38 | tags: true, 39 | top: Math.floor(this.screen.height * 0.2), 40 | width: '100%', 41 | style: { 42 | border: { fg: 'blue' }, 43 | header: { 44 | fg: 'blue', 45 | bold: true, 46 | }, 47 | cell: { 48 | fg: 'magenta', 49 | selected: { 50 | bg: 'blue', 51 | }, 52 | }, 53 | }, 54 | hover: { 55 | bg: 'blue', 56 | }, 57 | }); 58 | this.widget.on('keypress', (event, key) => { 59 | this.state[ACTIVE_POD] = _.get(this.state, `[${PODS}][${this.widget.selected - 1}][0]`); 60 | if (key.name === 'l') { 61 | this.middleware.emit(GET_LOGS); 62 | } 63 | if (key.name === 's') { 64 | this.middleware.emit(GET_BASH); 65 | } 66 | }); 67 | } 68 | } 69 | 70 | module.exports = PodsWidget; 71 | -------------------------------------------------------------------------------- /src/hooks/tests/pods.json: -------------------------------------------------------------------------------- 1 | { 2 | "apiVersion": "v1", 3 | "items": [ 4 | { 5 | "apiVersion": "v1", 6 | "kind": "Pod", 7 | "metadata": { 8 | "annotations": { 9 | "kubernetes.io/limit-ranger": "LimitRanger plugin set: cpu request for container pod-1-name" 10 | }, 11 | "creationTimestamp": "2019-06-07T10:55:58Z", 12 | "generateName": "pod-1-name", 13 | "labels": { 14 | "io.kompose.service": "pod-1" 15 | }, 16 | "name": "pod-1-xxxxx", 17 | "namespace": "default", 18 | "ownerReferences": [ 19 | { 20 | "apiVersion": "apps/v1", 21 | "blockOwnerDeletion": true, 22 | "controller": true, 23 | "kind": "ReplicaSet" 24 | } 25 | ], 26 | "selfLink": "/api/v1/namespaces/default/pods/pod-1-xxxxx" 27 | }, 28 | "spec": { 29 | "containers": [ 30 | { 31 | "args": [ 32 | "start" 33 | ], 34 | "env": [ 35 | { 36 | "name": "ENVIRONMENT_VARIABLE_1", 37 | "value": "environment_variable_1" 38 | }, 39 | { 40 | "name": "ENVIRONMENT_VARIABLE_2", 41 | "value": "environment_variable_2" 42 | } 43 | ], 44 | "image": "gcr.io/production/pod-1:latest", 45 | "imagePullPolicy": "Always", 46 | "name": "pod-name-1", 47 | "ports": [ 48 | { 49 | "containerPort": 4000, 50 | "protocol": "TCP" 51 | } 52 | ], 53 | "resources": { 54 | "requests": { 55 | "cpu": "100m" 56 | } 57 | }, 58 | "terminationMessagePath": "/dev/termination-log", 59 | "terminationMessagePolicy": "File", 60 | "volumeMounts": [ 61 | { 62 | "mountPath": "/var/run/secrets/kubernetes.io/serviceaccount", 63 | "name": "default-token-mn6st", 64 | "readOnly": true 65 | } 66 | ] 67 | } 68 | ], 69 | "dnsPolicy": "ClusterFirst", 70 | "nodeName": "node-name-1", 71 | "priority": 0, 72 | "restartPolicy": "Always", 73 | "schedulerName": "default-scheduler", 74 | "securityContext": {}, 75 | "serviceAccount": "default", 76 | "serviceAccountName": "default", 77 | "terminationGracePeriodSeconds": 30, 78 | "tolerations": [ 79 | { 80 | "effect": "NoExecute", 81 | "key": "node.kubernetes.io/not-ready", 82 | "operator": "Exists", 83 | "tolerationSeconds": 300 84 | }, 85 | { 86 | "effect": "NoExecute", 87 | "key": "node.kubernetes.io/unreachable", 88 | "operator": "Exists", 89 | "tolerationSeconds": 300 90 | } 91 | ], 92 | "volumes": [ 93 | { 94 | "name": "default-token-mn6st", 95 | "secret": { 96 | "defaultMode": 420, 97 | "secretName": "default-token-mn6st" 98 | } 99 | } 100 | ] 101 | }, 102 | "status": { 103 | "conditions": [ 104 | { 105 | "lastProbeTime": null, 106 | "lastTransitionTime": "2019-06-07T10:55:58Z", 107 | "status": "True", 108 | "type": "Initialized" 109 | }, 110 | { 111 | "lastProbeTime": null, 112 | "lastTransitionTime": "2019-06-07T10:57:31Z", 113 | "status": "True", 114 | "type": "Ready" 115 | }, 116 | { 117 | "lastProbeTime": null, 118 | "lastTransitionTime": null, 119 | "status": "True", 120 | "type": "ContainersReady" 121 | }, 122 | { 123 | "lastProbeTime": null, 124 | "lastTransitionTime": "2019-06-07T10:55:58Z", 125 | "status": "True", 126 | "type": "PodScheduled" 127 | } 128 | ], 129 | "containerStatuses": [ 130 | { 131 | "containerID": "LONG_HASH", 132 | "image": "gcr.io/production/pod-1:latest", 133 | "imageID": "LONG_HASH", 134 | "lastState": {}, 135 | "name": "pod-1-name", 136 | "ready": true, 137 | "restartCount": 0, 138 | "state": { 139 | "running": { 140 | "startedAt": "2019-06-07T10:57:30Z" 141 | } 142 | } 143 | } 144 | ], 145 | "hostIP": "127.0.0.1", 146 | "phase": "Running", 147 | "podIP": "127.0.0.1", 148 | "qosClass": "Burstable", 149 | "startTime": "2019-06-07T10:55:58Z" 150 | } 151 | } 152 | ], 153 | "kind": "List", 154 | "metadata": { 155 | "resourceVersion": "", 156 | "selfLink": "" 157 | } 158 | } 159 | --------------------------------------------------------------------------------