Skip to content

Commit

Permalink
feat(KubeConfig): switch to using KubeConfig for config handling (#506
Browse files Browse the repository at this point in the history
)
  • Loading branch information
Silas Boyd-Wickizer committed Jun 25, 2019
1 parent c506587 commit cb94f02
Show file tree
Hide file tree
Showing 13 changed files with 801 additions and 156 deletions.
41 changes: 25 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,9 @@ the cluster's kubeconfig file and that cluster's API specification.

To create the config required to make a client, you can either:

let kubernetes-client load the file automatically through the `KUBECONFIG`
env
let kubernetes-client configure automatically by trying the `KUBECONFIG`
environment variable first, then `~/.kube/config`, then an in-cluster
service account, and lastly settling on a default proxy configuration:

```js
const client = new Client({ version: '1.13' })
Expand All @@ -34,33 +35,41 @@ const client = new Client({ version: '1.13' })
provide your own path to a file:

```js
const { KubeConfig } = require('kubernetes-client')
const kubeconfig = new KubeConfig()
kubeconfig.loadFromFile('~/some/path')
const Request = require('kubernetes-client/backends/request')
const backend = new Request(Request.config.fromKubeconfig('~/some/path'))

const backend = new Request({ kubeconfig })
const client = new Client({ backend, version: '1.13' })
```

provide a kubeconfig object from memory:
provide a configuration object from memory:

```js
// Should match the kubeconfig file format exactly
const kubeconfig = {
apiVersion: 'v1',
clusters: [],
contexts: [],
'current-context': '',
kind: 'Config',
users: []
const config = {
apiVersion: 'v1',
clusters: [],
contexts: [],
'current-context': '',
kind: 'Config',
users: []
}
const { KubeConfig } = require('kubernetes-client')
const kubeconfig = new KubeConfig()
kubeconfig.loadFromString(JSON.stringify(config))

const Request = require('kubernetes-client/backends/request')
const backend = new Request(Request.config.fromKubeconfig(kubeconfig))
const backend = new Request({ kubeconfig })
const client = new Client({ backend, version: '1.13' })
```

and you can also specify the kubeconfig context by passing it as the
second argument to `fromKubeconfig()`:
and you can also specify the context by setting it in the `kubeconfig`
object:

```
const config = Request.config.fromKubeconfig(null, 'dev')
```js
kubeconfig.setCurrentContext('dev')
```

You can also elide the `.version` and pass an OpenAPI specification:
Expand Down
38 changes: 25 additions & 13 deletions backends/request/client.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
'use strict'

const { convertKubeconfig } = require('./config')
const deprecate = require('depd')('kubernetes-client')
const JSONStream = require('json-stream')
const pump = require('pump')
const qs = require('qs')
Expand Down Expand Up @@ -100,26 +102,36 @@ class Request {
*/
constructor (options) {
this.requestOptions = options.request || {}

let convertedOptions
if (!options.kubeconfig) {
deprecate('Request() without a .kubeconfig option, see ' +
'https://github.com/godaddy/kubernetes-client/blob/master/merging-with-kubernetes.md')
convertedOptions = options
} else {
convertedOptions = convertKubeconfig(options.kubeconfig)
}

this.requestOptions.qsStringifyOptions = { indices: false }
this.requestOptions.baseUrl = options.url
this.requestOptions.ca = options.ca
this.requestOptions.cert = options.cert
this.requestOptions.key = options.key
if ('insecureSkipTlsVerify' in options) {
this.requestOptions.strictSSL = !options.insecureSkipTlsVerify
this.requestOptions.baseUrl = convertedOptions.url
this.requestOptions.ca = convertedOptions.ca
this.requestOptions.cert = convertedOptions.cert
this.requestOptions.key = convertedOptions.key
if ('insecureSkipTlsVerify' in convertedOptions) {
this.requestOptions.strictSSL = !convertedOptions.insecureSkipTlsVerify
}
if ('timeout' in options) {
this.requestOptions.timeout = options.timeout
if ('timeout' in convertedOptions) {
this.requestOptions.timeout = convertedOptions.timeout
}

this.authProvider = {
type: null
}
if (options.auth) {
this.requestOptions.auth = options.auth
if (options.auth.provider) {
this.requestOptions.auth = options.auth.request
this.authProvider = options.auth.provider
if (convertedOptions.auth) {
this.requestOptions.auth = convertedOptions.auth
if (convertedOptions.auth.provider) {
this.requestOptions.auth = convertedOptions.auth.request
this.authProvider = convertedOptions.auth.provider
}
}
}
Expand Down
7 changes: 6 additions & 1 deletion backends/request/client.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,14 @@
const { expect } = require('chai')
const nock = require('nock')

const KubeConfig = require('../../lib/config')
const Request = require('./client')

const url = 'http://mock.kube.api'
const kubeconfig = new KubeConfig()
kubeconfig.loadFromClusterAndUser(
{ name: 'cluster', server: url },
{ name: 'user' })

describe('lib.backends.request', () => {
describe('Request', () => {
Expand All @@ -15,7 +20,7 @@ describe('lib.backends.request', () => {
.get('/foo')
.reply(200)

const backend = new Request({ url })
const backend = new Request({ kubeconfig })
backend.http({
method: 'GET',
pathname: '/foo'
Expand Down
109 changes: 109 additions & 0 deletions backends/request/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,115 @@ function getInCluster () {

module.exports.getInCluster = getInCluster

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

//
// Accept a manually specified current-context to take precedence over
// `current-context`
Expand Down
Loading

0 comments on commit cb94f02

Please sign in to comment.