From 79bd2323f1750adbf91eec6b7aa48610df230b95 Mon Sep 17 00:00:00 2001 From: Adam Dyess Date: Wed, 25 Sep 2024 13:32:59 -0500 Subject: [PATCH] Ensure that .kube/config is written out once microk8s is active (#85) --- dist/bootstrap/index.js | 47 +++++++++++++++++++++++--------------- src/bootstrap/index.ts | 49 +++++++++++++++++++++++++--------------- tests/test_deploy_k8s.py | 8 +++++++ 3 files changed, 68 insertions(+), 36 deletions(-) diff --git a/dist/bootstrap/index.js b/dist/bootstrap/index.js index 0480161..e988fc5 100644 --- a/dist/bootstrap/index.js +++ b/dist/bootstrap/index.js @@ -5493,36 +5493,35 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); const core = __importStar(__nccwpck_require__(2186)); const exec = __importStar(__nccwpck_require__(1514)); const os = __importStar(__nccwpck_require__(2087)); +const fs = __importStar(__nccwpck_require__(5747)); const utils_1 = __nccwpck_require__(2828); const semver_1 = __importDefault(__nccwpck_require__(1383)); const ts_dedent_1 = __importDefault(__nccwpck_require__(3604)); const ignoreFail = { "ignoreReturnCode": true }; const user = os.userInfo().username; -const os_release = () => __awaiter(void 0, void 0, void 0, function* () { - // Read os-release file into an object +const checkOutput = (cmd, args, options) => __awaiter(void 0, void 0, void 0, function* () { let stdout_buf = ''; - const options = { - listeners: { - stdout: (data) => { stdout_buf += data.toString(); } - } + options = options || {}; + options.listeners = { + stdout: (data) => { stdout_buf += data.toString(); } }; - yield exec.exec('cat', ['/etc/os-release'], options); + yield exec.exec(cmd, args, options); + return stdout_buf; +}); +const os_release = () => __awaiter(void 0, void 0, void 0, function* () { + // Read os-release file into an object + const output = yield checkOutput('cat', ['/etc/os-release']); let data = {}; - stdout_buf.split('\n').forEach(function (line) { + output.split('\n').forEach(function (line) { let [key, value] = line.split("=", 2); data[key] = value; }); return data; }); const snap_version = (snap_name) => __awaiter(void 0, void 0, void 0, function* () { - let stdout_buf = ''; - const options = { - listeners: { - stdout: (data) => { stdout_buf += data.toString(); } - } - }; - yield exec.exec("snap", ["list", snap_name], options); - const lines = stdout_buf.split('\n'); + // Get the version of a snap + const output = yield checkOutput("snap", ["list", snap_name, "--color=never"]); + const lines = output.split('\n'); if (lines.length < 2) { throw new Error(`snap ${snap_name} not found`); } @@ -5532,6 +5531,18 @@ const snap_version = (snap_name) => __awaiter(void 0, void 0, void 0, function* } return snap_line[1]; }); +const microk8sKubeConfig = () => __awaiter(void 0, void 0, void 0, function* () { + // Get kubeconfig from microk8s + let kubeconfig = ""; + let options = { + silent: true, + listeners: { + stdout: (data) => { kubeconfig += data.toString(); } + } + }; + yield exec_as_microk8s("microk8s config", options); + fs.writeFileSync(`${os.homedir()}/.kube/config`, kubeconfig, { encoding: 'utf8' }); +}); const docker_lxd_clash = () => __awaiter(void 0, void 0, void 0, function* () { // Work-around clash between docker and lxd on jammy // https://github.com/docker/for-linux/issues/1034 @@ -5635,7 +5646,7 @@ function microk8s_init(channel, addons, container_registry_url) { ; if (semver_1.default.lt(mk8s_ver, '1.24.0')) { yield exec_as_microk8s("microk8s kubectl create serviceaccount test-sa"); - if (!(yield retry_until_rc("microk8s kubectl get secrets | grep -q test-sa-token-"))) { + if (!(yield retry_until_rc("bash -c 'microk8s kubectl get secrets | grep -q test-sa-token-'"))) { core.setFailed("Timed out waiting for test SA token"); return false; } @@ -5871,7 +5882,7 @@ function run() { yield snap("install kubectl --classic"); yield exec.exec("mkdir", ["-p", `${HOME}/.kube`]); if (provider === "microk8s") { - yield exec_as_microk8s(`microk8s config > ${HOME}/.kube/config`); + yield microk8sKubeConfig(); } core.endGroup(); } diff --git a/src/bootstrap/index.ts b/src/bootstrap/index.ts index 8aba37b..8327c3e 100644 --- a/src/bootstrap/index.ts +++ b/src/bootstrap/index.ts @@ -1,6 +1,7 @@ import * as core from '@actions/core'; import * as exec from '@actions/exec'; import * as os from 'os' +import * as fs from 'fs'; import { retryAsyncDecorator } from 'ts-retry/lib/cjs/retry/utils'; import semver from 'semver'; import dedent from 'ts-dedent'; @@ -14,17 +15,21 @@ declare var process : { const ignoreFail: exec.ExecOptions = {"ignoreReturnCode": true} const user = os.userInfo().username -const os_release = async () => { - // Read os-release file into an object +const checkOutput = async (cmd: string, args?: string[], options?: exec.ExecOptions) => { let stdout_buf = ''; - const options = { - listeners: { - stdout: (data: Buffer) => { stdout_buf += data.toString() } - } + options = options || {}; + options.listeners = { + stdout: (data: Buffer) => { stdout_buf += data.toString() } }; - await exec.exec('cat', ['/etc/os-release'], options); + await exec.exec(cmd, args, options); + return stdout_buf +} + +const os_release = async () => { + // Read os-release file into an object + const output = await checkOutput('cat', ['/etc/os-release']); let data: { [name:string]: string} = {}; - stdout_buf.split('\n').forEach(function(line){ + output.split('\n').forEach(function(line){ let [key, value] = line.split("=", 2); data[key] = value }) @@ -32,14 +37,9 @@ const os_release = async () => { } const snap_version = async (snap_name: string) => { - let stdout_buf = ''; - const options = { - listeners: { - stdout: (data: Buffer) => { stdout_buf += data.toString() } - } - }; - await exec.exec("snap", ["list", snap_name], options); - const lines = stdout_buf.split('\n'); + // Get the version of a snap + const output = await checkOutput("snap", ["list", snap_name, "--color=never"]); + const lines = output.split('\n'); if (lines.length < 2) { throw new Error(`snap ${snap_name} not found`) } @@ -50,6 +50,19 @@ const snap_version = async (snap_name: string) => { return snap_line[1] } +const microk8sKubeConfig = async () => { + // Get kubeconfig from microk8s + let kubeconfig = "" + let options = { + silent: true, + listeners: { + stdout: (data: Buffer) => { kubeconfig += data.toString() } + } + }; + await exec_as_microk8s("microk8s config", options); + fs.writeFileSync(`${os.homedir()}/.kube/config`, kubeconfig, {encoding: 'utf8'}); +} + const docker_lxd_clash = async () => { // Work-around clash between docker and lxd on jammy // https://github.com/docker/for-linux/issues/1034 @@ -158,7 +171,7 @@ async function microk8s_init(channel, addons, container_registry_url:string) { }; if (semver.lt(mk8s_ver, '1.24.0')) { await exec_as_microk8s("microk8s kubectl create serviceaccount test-sa"); - if (! await retry_until_rc("microk8s kubectl get secrets | grep -q test-sa-token-")) { + if (! await retry_until_rc("bash -c 'microk8s kubectl get secrets | grep -q test-sa-token-'")) { core.setFailed("Timed out waiting for test SA token"); return false; }; @@ -394,7 +407,7 @@ async function run() { await snap("install kubectl --classic"); await exec.exec("mkdir", ["-p", `${HOME}/.kube`]); if (provider === "microk8s") { - await exec_as_microk8s(`microk8s config > ${HOME}/.kube/config`); + await microk8sKubeConfig(); } core.endGroup(); } catch(error: any) { diff --git a/tests/test_deploy_k8s.py b/tests/test_deploy_k8s.py index 3487e4a..e8d9e36 100644 --- a/tests/test_deploy_k8s.py +++ b/tests/test_deploy_k8s.py @@ -1,7 +1,15 @@ import pytest +from subprocess import check_output @pytest.mark.abort_on_fail async def test_build(ops_test): await ops_test.model.deploy("ch:minio") await ops_test.model.wait_for_idle() + + +@pytest.mark.usefixtures("ops_test") +async def test_kubectl(): + # Confirms that kubectl has access to a functional kubeconfig @ $HOME/.kube/config + out = check_output(["kubectl", "get", "pods", "-A"], text=True) + assert "kube-system" in out, "should see some kube-system pods" \ No newline at end of file