"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.findObject = exports.findHomeDir = exports.bufferFromFileOrString = exports.makeAbsolutePath = exports.Config = exports.KubeConfig = void 0; const tslib_1 = require("tslib"); const execa = require("execa"); const fs = require("fs"); const yaml = require("js-yaml"); const net = require("net"); const path = require("path"); const shelljs = require("shelljs"); const api = tslib_1.__importStar(require("./api")); const azure_auth_1 = require("./azure_auth"); const config_types_1 = require("./config_types"); const exec_auth_1 = require("./exec_auth"); const file_auth_1 = require("./file_auth"); const gcp_auth_1 = require("./gcp_auth"); const oidc_auth_1 = require("./oidc_auth"); // fs.existsSync was removed in node 10 function fileExists(filepath) { try { fs.accessSync(filepath); return true; } catch (ignore) { return false; } } class KubeConfig { constructor() { this.contexts = []; this.clusters = []; this.users = []; } getContexts() { return this.contexts; } getClusters() { return this.clusters; } getUsers() { return this.users; } getCurrentContext() { return this.currentContext; } setCurrentContext(context) { this.currentContext = context; } getContextObject(name) { if (!this.contexts) { return null; } return findObject(this.contexts, name, 'context'); } getCurrentCluster() { const context = this.getCurrentContextObject(); if (!context) { return null; } return this.getCluster(context.cluster); } getCluster(name) { return findObject(this.clusters, name, 'cluster'); } getCurrentUser() { const ctx = this.getCurrentContextObject(); if (!ctx) { return null; } return this.getUser(ctx.user); } getUser(name) { return findObject(this.users, name, 'user'); } loadFromFile(file, opts) { const rootDirectory = path.dirname(file); this.loadFromString(fs.readFileSync(file, 'utf8'), opts); this.makePathsAbsolute(rootDirectory); } async applytoHTTPSOptions(opts) { const user = this.getCurrentUser(); await this.applyOptions(opts); if (user && user.username) { opts.auth = `${user.username}:${user.password}`; } } async applyToRequest(opts) { const cluster = this.getCurrentCluster(); const user = this.getCurrentUser(); await this.applyOptions(opts); if (cluster && cluster.skipTLSVerify) { opts.strictSSL = false; } if (user && user.username) { opts.auth = { password: user.password, username: user.username, }; } } loadFromString(config, opts) { const obj = yaml.load(config); this.clusters = config_types_1.newClusters(obj.clusters, opts); this.contexts = config_types_1.newContexts(obj.contexts, opts); this.users = config_types_1.newUsers(obj.users, opts); this.currentContext = obj['current-context']; } loadFromOptions(options) { this.clusters = options.clusters; this.contexts = options.contexts; this.users = options.users; this.currentContext = options.currentContext; } loadFromClusterAndUser(cluster, user) { this.clusters = [cluster]; this.users = [user]; this.currentContext = 'loaded-context'; this.contexts = [ { cluster: cluster.name, user: user.name, name: this.currentContext, }, ]; } loadFromCluster(pathPrefix = '') { const host = process.env.KUBERNETES_SERVICE_HOST; const port = process.env.KUBERNETES_SERVICE_PORT; const clusterName = 'inCluster'; const userName = 'inClusterUser'; const contextName = 'inClusterContext'; let scheme = 'https'; if (port === '80' || port === '8080' || port === '8001') { scheme = 'http'; } // Wrap raw IPv6 addresses in brackets. let serverHost = host; if (host && net.isIPv6(host)) { serverHost = `[${host}]`; } this.clusters = [ { name: clusterName, caFile: `${pathPrefix}${Config.SERVICEACCOUNT_CA_PATH}`, server: `${scheme}://${serverHost}:${port}`, skipTLSVerify: false, }, ]; this.users = [ { name: userName, authProvider: { name: 'tokenFile', config: { tokenFile: `${pathPrefix}${Config.SERVICEACCOUNT_TOKEN_PATH}`, }, }, }, ]; const namespaceFile = `${pathPrefix}${Config.SERVICEACCOUNT_NAMESPACE_PATH}`; let namespace; if (fileExists(namespaceFile)) { namespace = fs.readFileSync(namespaceFile, 'utf8'); } this.contexts = [ { cluster: clusterName, name: contextName, user: userName, namespace, }, ]; this.currentContext = contextName; } mergeConfig(config, preserveContext = false) { if (!preserveContext) { this.currentContext = config.currentContext; } config.clusters.forEach((cluster) => { this.addCluster(cluster); }); config.users.forEach((user) => { this.addUser(user); }); config.contexts.forEach((ctx) => { this.addContext(ctx); }); } addCluster(cluster) { if (!this.clusters) { this.clusters = []; } this.clusters.forEach((c, ix) => { if (c.name === cluster.name) { throw new Error(`Duplicate cluster: ${c.name}`); } }); this.clusters.push(cluster); } addUser(user) { if (!this.users) { this.users = []; } this.users.forEach((c, ix) => { if (c.name === user.name) { throw new Error(`Duplicate user: ${c.name}`); } }); this.users.push(user); } addContext(ctx) { if (!this.contexts) { this.contexts = []; } this.contexts.forEach((c, ix) => { if (c.name === ctx.name) { throw new Error(`Duplicate context: ${c.name}`); } }); this.contexts.push(ctx); } loadFromDefault(opts, contextFromStartingConfig = false) { if (process.env.KUBECONFIG && process.env.KUBECONFIG.length > 0) { const files = process.env.KUBECONFIG.split(path.delimiter).filter((filename) => filename); this.loadFromFile(files[0], opts); for (let i = 1; i < files.length; i++) { const kc = new KubeConfig(); kc.loadFromFile(files[i], opts); this.mergeConfig(kc, contextFromStartingConfig); } return; } const home = findHomeDir(); if (home) { const config = path.join(home, '.kube', 'config'); if (fileExists(config)) { this.loadFromFile(config, opts); return; } } if (process.platform === 'win32' && shelljs.which('wsl.exe')) { try { const envKubeconfigPathResult = execa.sync('wsl.exe', ['bash', '-ic', 'printenv KUBECONFIG']); if (envKubeconfigPathResult.exitCode === 0 && envKubeconfigPathResult.stdout.length > 0) { const result = execa.sync('wsl.exe', ['cat', envKubeconfigPathResult.stdout]); if (result.exitCode === 0) { this.loadFromString(result.stdout, opts); return; } if (result.exitCode === 0) { this.loadFromString(result.stdout, opts); return; } } } catch (err) { // Falling back to default kubeconfig } try { const result = execa.sync('wsl.exe', ['cat', '~/.kube/config']); if (result.exitCode === 0) { this.loadFromString(result.stdout, opts); return; } } catch (err) { // Falling back to alternative auth } } if (fileExists(Config.SERVICEACCOUNT_TOKEN_PATH)) { this.loadFromCluster(); return; } this.loadFromClusterAndUser({ name: 'cluster', server: 'http://localhost:8080' }, { name: 'user' }); } makeApiClient(apiClientType) { const cluster = this.getCurrentCluster(); if (!cluster) { throw new Error('No active cluster!'); } const apiClient = new apiClientType(cluster.server); apiClient.setDefaultAuthentication(this); return apiClient; } makePathsAbsolute(rootDirectory) { this.clusters.forEach((cluster) => { if (cluster.caFile) { cluster.caFile = makeAbsolutePath(rootDirectory, cluster.caFile); } }); this.users.forEach((user) => { if (user.certFile) { user.certFile = makeAbsolutePath(rootDirectory, user.certFile); } if (user.keyFile) { user.keyFile = makeAbsolutePath(rootDirectory, user.keyFile); } }); } exportConfig() { const configObj = { apiVersion: 'v1', kind: 'Config', clusters: this.clusters.map(config_types_1.exportCluster), users: this.users.map(config_types_1.exportUser), contexts: this.contexts.map(config_types_1.exportContext), preferences: {}, 'current-context': this.getCurrentContext(), }; return JSON.stringify(configObj); } getCurrentContextObject() { return this.getContextObject(this.currentContext); } applyHTTPSOptions(opts) { const cluster = this.getCurrentCluster(); const user = this.getCurrentUser(); if (!user) { return; } if (cluster != null && cluster.skipTLSVerify) { opts.rejectUnauthorized = false; } const ca = cluster != null ? bufferFromFileOrString(cluster.caFile, cluster.caData) : null; if (ca) { opts.ca = ca; } const cert = bufferFromFileOrString(user.certFile, user.certData); if (cert) { opts.cert = cert; } const key = bufferFromFileOrString(user.keyFile, user.keyData); if (key) { opts.key = key; } } async applyAuthorizationHeader(opts) { const user = this.getCurrentUser(); if (!user) { return; } const authenticator = KubeConfig.authenticators.find((elt) => { return elt.isAuthProvider(user); }); if (!opts.headers) { opts.headers = {}; } if (authenticator) { await authenticator.applyAuthentication(user, opts); } if (user.token) { opts.headers.Authorization = `Bearer ${user.token}`; } } async applyOptions(opts) { this.applyHTTPSOptions(opts); await this.applyAuthorizationHeader(opts); } } exports.KubeConfig = KubeConfig; KubeConfig.authenticators = [ new azure_auth_1.AzureAuth(), new gcp_auth_1.GoogleCloudPlatformAuth(), new exec_auth_1.ExecAuth(), new file_auth_1.FileAuth(), new oidc_auth_1.OpenIDConnectAuth(), ]; // This class is deprecated and will eventually be removed. class Config { static fromFile(filename) { return Config.apiFromFile(filename, api.CoreV1Api); } static fromCluster() { return Config.apiFromCluster(api.CoreV1Api); } static defaultClient() { return Config.apiFromDefaultClient(api.CoreV1Api); } static apiFromFile(filename, apiClientType) { const kc = new KubeConfig(); kc.loadFromFile(filename); return kc.makeApiClient(apiClientType); } static apiFromCluster(apiClientType) { const kc = new KubeConfig(); kc.loadFromCluster(); const cluster = kc.getCurrentCluster(); if (!cluster) { throw new Error('No active cluster!'); } const k8sApi = new apiClientType(cluster.server); k8sApi.setDefaultAuthentication(kc); return k8sApi; } static apiFromDefaultClient(apiClientType) { const kc = new KubeConfig(); kc.loadFromDefault(); return kc.makeApiClient(apiClientType); } } exports.Config = Config; Config.SERVICEACCOUNT_ROOT = '/var/run/secrets/kubernetes.io/serviceaccount'; Config.SERVICEACCOUNT_CA_PATH = Config.SERVICEACCOUNT_ROOT + '/ca.crt'; Config.SERVICEACCOUNT_TOKEN_PATH = Config.SERVICEACCOUNT_ROOT + '/token'; Config.SERVICEACCOUNT_NAMESPACE_PATH = Config.SERVICEACCOUNT_ROOT + '/namespace'; function makeAbsolutePath(root, file) { if (!root || path.isAbsolute(file)) { return file; } return path.join(root, file); } exports.makeAbsolutePath = makeAbsolutePath; // This is public really only for testing. function bufferFromFileOrString(file, data) { if (file) { return fs.readFileSync(file); } if (data) { return Buffer.from(data, 'base64'); } return null; } exports.bufferFromFileOrString = bufferFromFileOrString; // Only public for testing. function findHomeDir() { if (process.env.HOME) { try { fs.accessSync(process.env.HOME); return process.env.HOME; // tslint:disable-next-line:no-empty } catch (ignore) { } } if (process.platform !== 'win32') { return null; } if (process.env.HOMEDRIVE && process.env.HOMEPATH) { const dir = path.join(process.env.HOMEDRIVE, process.env.HOMEPATH); try { fs.accessSync(dir); return dir; // tslint:disable-next-line:no-empty } catch (ignore) { } } if (process.env.USERPROFILE) { try { fs.accessSync(process.env.USERPROFILE); return process.env.USERPROFILE; // tslint:disable-next-line:no-empty } catch (ignore) { } } return null; } exports.findHomeDir = findHomeDir; // Only really public for testing... function findObject(list, name, key) { if (!list) { return null; } for (const obj of list) { if (obj.name === name) { if (obj[key]) { obj[key].name = name; return obj[key]; } return obj; } } return null; } exports.findObject = findObject; //# sourceMappingURL=config.js.map