kubernetes-client
Version:
Simplified Kubernetes API client.
345 lines (297 loc) • 9.7 kB
JavaScript
/* eslint no-process-env: 0 no-sync:0 */
const deprecate = require('depd')('kubernetes-client')
const fs = require('fs')
const path = require('path')
const yaml = require('js-yaml')
const merge = require('deepmerge')
const root = process.env.KUBERNETES_CLIENT_SERVICEACCOUNT_ROOT || '/var/run/secrets/kubernetes.io/serviceaccount/'
const caPath = path.join(root, 'ca.crt')
const tokenPath = path.join(root, 'token')
const namespacePath = path.join(root, 'namespace')
function convertKubeconfig (kubeconfig) {
const context = kubeconfig.getCurrentContext()
const cluster = kubeconfig.getCurrentCluster()
const user = kubeconfig.getCurrentUser()
const namespace = context.namespace
let ca
let insecureSkipTlsVerify = false
if (cluster) {
if (cluster.caFile) {
ca = fs.readFileSync(path.normalize(cluster.caFile))
} else if (cluster.caData) {
ca = Buffer.from(cluster.caData, 'base64').toString()
}
insecureSkipTlsVerify = cluster.skipTLSVerify
}
let cert
let key
let auth = {}
if (user) {
if (user.certFile) {
cert = fs.readFileSync(path.normalize(user.certFile))
} else if (user.certData) {
cert = Buffer.from(user.certData, 'base64').toString()
}
if (user.keyFile) {
key = fs.readFileSync(path.normalize(user.keyFile))
} else if (user.keyData) {
key = Buffer.from(user.keyData, 'base64').toString()
}
if (user.token) {
auth.bearer = user.token
}
if (user.authProvider) {
const config = user.authProvider.config
// if we can't determine the type, just fail later (or don't refresh).
let type = null
let token = null
if (config['cmd-path']) {
type = 'cmd'
token = config['access-token']
} else if (config['idp-issuer-url']) {
type = 'openid'
token = config['id-token']
}
// If we have just an access-token, allow that... will expire later though.
if (config['access-token'] && !type) {
token = config['access-token']
}
auth = {
request: {
bearer: token
},
provider: {
config,
type
}
}
}
if (user.exec) {
const env = {}
if (user.exec.env) {
user.exec.env.forEach(variable => {
env[variable.name] = variable.value
})
}
let args = ''
if (user.exec.args) {
args = user.exec.args.join(' ')
}
auth = {
provider: {
type: 'cmd',
config: {
'cmd-args': args,
'cmd-path': user.exec.command,
'token-key': 'status.token',
'cmd-env': env
}
}
}
}
if (user.username) auth.user = user.username
if (user.password) auth.pass = user.password
}
return {
url: cluster.server,
auth: Object.keys(auth).length ? auth : null,
ca,
insecureSkipTlsVerify,
namespace,
cert,
key
}
}
module.exports.convertKubeconfig = convertKubeconfig
function defaultConfigPaths () {
if (process.env.KUBECONFIG) {
// From https://kubernetes.io/docs/tasks/access-application-cluster/configure-access-multiple-clusters/#set-the-kubeconfig-environment-variable
// KUBECONFIG can support multiple config files.
const delimiter = process.platform === 'win32' ? ';' : ':'
return process.env.KUBECONFIG.split(delimiter)
}
const homeDir = process.env[(process.platform === 'win32') ? 'USERPROFILE' : 'HOME']
return [path.join(homeDir, '.kube', 'config')]
}
/**
* Returns with in cluster config
* Based on: https://github.com/kubernetes/client-go/blob/124670e99da15091e13916f0ad4b2b2df2a39cd5/rest/config.go#L274
* and http://kubernetes.io/docs/user-guide/accessing-the-cluster/#accessing-the-api-from-a-pod
*
* @function getInCluster
* @returns {Object} { url, cert, auth, namespace }
*/
function getInCluster () {
const host = process.env.KUBERNETES_SERVICE_HOST
const port = process.env.KUBERNETES_SERVICE_PORT
if (!host || !port) {
throw new TypeError(
'Unable to load in-cluster configuration, KUBERNETES_SERVICE_HOST' +
' and KUBERNETES_SERVICE_PORT must be defined')
}
const ca = fs.readFileSync(caPath, 'utf8')
const bearer = fs.readFileSync(tokenPath, 'utf8')
const namespace = fs.readFileSync(namespacePath, 'utf8')
return {
url: `https://${host}:${port}`,
ca,
auth: { bearer },
namespace
}
}
module.exports.getInCluster = deprecate.function(
getInCluster,
'getInCluster see https://github.com/godaddy/kubernetes-client/blob/master/merging-with-kubernetes.md#request-kubeconfig-')
//
// Accept a manually specified current-context to take precedence over
// `current-context`
//
/* eslint-disable complexity, max-statements */
function fromKubeconfig (kubeconfig, current) {
if (!kubeconfig) kubeconfig = loadKubeconfig()
// if kubeconfig is provided as a path to the yaml file,
// or array of paths to the yaml files,
// automatically load it.
if (typeof kubeconfig === 'string' || Array.isArray(kubeconfig)) {
kubeconfig = loadKubeconfig(kubeconfig)
}
current = current || kubeconfig['current-context']
const context = kubeconfig.contexts
.find(item => item.name === current).context
const cluster = kubeconfig.clusters
.find(item => item.name === context.cluster).cluster
const userConfig = kubeconfig.users
.find(user => user.name === context.user)
const user = userConfig ? userConfig.user : null
const namespace = context.namespace
let ca
let insecureSkipTlsVerify = false
if (cluster) {
if (cluster['certificate-authority']) {
ca = fs.readFileSync(path.normalize(cluster['certificate-authority']))
} else if (cluster['certificate-authority-data']) {
ca = Buffer.from(cluster['certificate-authority-data'], 'base64').toString()
}
if (cluster['insecure-skip-tls-verify']) {
insecureSkipTlsVerify = cluster['insecure-skip-tls-verify']
}
}
let cert
let key
let auth = {}
if (user) {
if (user['client-certificate']) {
cert = fs.readFileSync(path.normalize(user['client-certificate']))
} else if (user['client-certificate-data']) {
cert = Buffer.from(user['client-certificate-data'], 'base64').toString()
}
if (user['client-key']) {
key = fs.readFileSync(path.normalize(user['client-key']))
} else if (user['client-key-data']) {
key = Buffer.from(user['client-key-data'], 'base64').toString()
}
if (user.token) {
auth.bearer = user.token
}
if (user['auth-provider']) {
const config = user['auth-provider'].config
// if we can't determine the type, just fail later (or don't refresh).
let type = null
let token = null
if (config['cmd-path']) {
type = 'cmd'
token = config['access-token']
} else if (config['idp-issuer-url']) {
type = 'openid'
token = config['id-token']
}
// If we have just an access-token, allow that... will expire later though.
if (config['access-token'] && !type) {
token = config['access-token']
}
auth = {
request: {
bearer: token
},
provider: {
config,
type
}
}
}
if (user.exec) {
const env = {}
if (user.exec.env) {
user.exec.env.forEach(variable => {
env[variable.name] = variable.value
})
}
let args = ''
if (user.exec.args) {
args = user.exec.args.join(' ')
}
auth = {
provider: {
type: 'cmd',
config: {
'cmd-args': args,
'cmd-path': user.exec.command,
'token-key': 'status.token',
'cmd-env': env
}
}
}
}
if (user.username) auth.user = user.username
if (user.password) auth.pass = user.password
}
return {
url: cluster.server,
namespace,
auth: Object.keys(auth).length ? auth : null,
ca,
insecureSkipTlsVerify,
key,
cert
}
}
/* eslint-enable complexity, max-statements */
module.exports.fromKubeconfig = deprecate.function(
fromKubeconfig,
'fromKubeconfig see https://github.com/godaddy/kubernetes-client/blob/master/merging-with-kubernetes.md#request-kubeconfig-')
function mapCertificates (cfgPath, config) {
const configDir = path.dirname(cfgPath)
if (config.clusters) {
config.clusters.filter(cluster => cluster.cluster['certificate-authority']).forEach(cluster => {
cluster.cluster['certificate-authority'] = path.resolve(configDir, cluster.cluster['certificate-authority'])
})
}
if (config.users) {
config.users.filter(user => user.user['client-certificate']).forEach(user => {
user.user['client-certificate'] = path.resolve(configDir, user.user['client-certificate'])
})
config.users.filter(user => user.user['client-key']).forEach(user => {
user.user['client-key'] = path.resolve(configDir, user.user['client-key'])
})
}
return config
}
function loadKubeconfig (cfgPath) {
let cfgPaths
if (!cfgPath) {
cfgPaths = defaultConfigPaths()
} else if (Array.isArray(cfgPath)) {
cfgPaths = cfgPath
} else {
cfgPaths = [cfgPath]
}
const configs = cfgPaths.map(cfgPath => {
const config = yaml.safeLoad(fs.readFileSync(cfgPath))
return mapCertificates(cfgPath, config)
})
return merge.all(configs)
}
module.exports.loadKubeconfig = deprecate.function(
loadKubeconfig,
'loadKubeconfig see https://github.com/godaddy/kubernetes-client/blob/master/merging-with-kubernetes.md#request-kubeconfig-')