@localctl/cli 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/README.md +398 -431
- package/assets/manifests/namespace-addons.yaml +4 -4
- package/assets/manifests/tls-store.yaml +8 -8
- package/bin/localctl.js +2 -2
- package/package.json +61 -54
- package/src/commands/addons.js +56 -56
- package/src/commands/doctor.js +44 -43
- package/src/commands/down.js +35 -35
- package/src/commands/exec.js +15 -15
- package/src/commands/generateManifests.js +12 -12
- package/src/commands/hosts.js +23 -23
- package/src/commands/logs.js +45 -45
- package/src/commands/profiles.js +180 -180
- package/src/commands/prune.js +49 -49
- package/src/commands/registryInfo.js +18 -18
- package/src/commands/reload.js +44 -44
- package/src/commands/secrets.js +75 -75
- package/src/commands/setup.js +36 -36
- package/src/commands/status.js +136 -136
- package/src/commands/up.js +123 -123
- package/src/index.js +38 -36
- package/src/lib/addonDirs.js +43 -43
- package/src/lib/addonLoader.js +190 -190
- package/src/lib/addons/cluster/logging/addon.yaml +46 -46
- package/src/lib/addons/cluster/logging/config/all-logs.json +21 -21
- package/src/lib/addons/cluster/monitoring/addon.yaml +33 -33
- package/src/lib/addons/mongo/addon.yaml +18 -18
- package/src/lib/addons/postgres/addon.yaml +39 -39
- package/src/lib/addons/redis/addon.yaml +29 -29
- package/src/lib/appSecrets.js +60 -60
- package/src/lib/clusterAddons.js +59 -59
- package/src/lib/clusterProvision.js +171 -170
- package/src/lib/configSchema.js +103 -103
- package/src/lib/constants.js +16 -16
- package/src/lib/engine.js +36 -20
- package/src/lib/exec.js +77 -77
- package/src/lib/hostsFile.js +79 -79
- package/src/lib/loadConfig.js +51 -51
- package/src/lib/logger.js +9 -9
- package/src/lib/manifestGen.js +210 -210
- package/src/lib/paths.js +41 -41
- package/src/lib/podman.js +72 -72
- package/src/lib/profileStore.js +134 -134
- package/src/lib/projectContext.js +32 -32
- package/src/lib/prompt.js +97 -97
- package/src/lib/toolInstall.js +30 -30
- package/src/lib/ui.js +50 -50
- package/src/lib/updateCheck.js +84 -0
- package/src/lib/updateCheckWorker.js +46 -0
- package/src/lib/workloadReadiness.js +35 -35
- package/templates/Tiltfile.tmpl +35 -35
|
@@ -1,39 +1,39 @@
|
|
|
1
|
-
# Postgres addon - deploys the real Bitnami PostgreSQL chart, configured for local dev.
|
|
2
|
-
# See docs/adding-tools.md for the full addon.yaml reference.
|
|
3
|
-
|
|
4
|
-
chart:
|
|
5
|
-
repo: https://charts.bitnami.com/bitnami
|
|
6
|
-
name: postgresql
|
|
7
|
-
version: "18.12.2"
|
|
8
|
-
|
|
9
|
-
# Generates a random value once per key, stored as a real k8s Secret named "<release>-secret" -
|
|
10
|
-
# never written to disk, never in this file. {{secretName}} below refers to that Secret's name.
|
|
11
|
-
secrets:
|
|
12
|
-
password:
|
|
13
|
-
length: 20
|
|
14
|
-
|
|
15
|
-
# Plain env vars injected into the app's own container automatically.
|
|
16
|
-
env:
|
|
17
|
-
POSTGRES_HOST: "{{serviceHost}}"
|
|
18
|
-
POSTGRES_PORT: "5432"
|
|
19
|
-
POSTGRES_DB: "{{database}}"
|
|
20
|
-
POSTGRES_USER: "localdev"
|
|
21
|
-
|
|
22
|
-
# Unlike `env`, these become a real secretKeyRef - the password itself never passes through a
|
|
23
|
-
# plaintext value anywhere in this pipeline.
|
|
24
|
-
envFromSecret:
|
|
25
|
-
POSTGRES_PASSWORD:
|
|
26
|
-
secret: "{{secretName}}"
|
|
27
|
-
key: password
|
|
28
|
-
|
|
29
|
-
# Passed to `helm template` as -f values.yaml. {{...}} is substituted first: {{database}}/
|
|
30
|
-
# {{storage}}/{{version}} come from the app's own .local/config.json dependency entry (with
|
|
31
|
-
# fallback defaults), {{secretName}} from the generated Secret above.
|
|
32
|
-
values: |
|
|
33
|
-
auth:
|
|
34
|
-
username: localdev
|
|
35
|
-
database: "{{database}}"
|
|
36
|
-
existingSecret: "{{secretName}}"
|
|
37
|
-
primary:
|
|
38
|
-
persistence:
|
|
39
|
-
size: "{{storage}}"
|
|
1
|
+
# Postgres addon - deploys the real Bitnami PostgreSQL chart, configured for local dev.
|
|
2
|
+
# See docs/adding-tools.md for the full addon.yaml reference.
|
|
3
|
+
|
|
4
|
+
chart:
|
|
5
|
+
repo: https://charts.bitnami.com/bitnami
|
|
6
|
+
name: postgresql
|
|
7
|
+
version: "18.12.2"
|
|
8
|
+
|
|
9
|
+
# Generates a random value once per key, stored as a real k8s Secret named "<release>-secret" -
|
|
10
|
+
# never written to disk, never in this file. {{secretName}} below refers to that Secret's name.
|
|
11
|
+
secrets:
|
|
12
|
+
password:
|
|
13
|
+
length: 20
|
|
14
|
+
|
|
15
|
+
# Plain env vars injected into the app's own container automatically.
|
|
16
|
+
env:
|
|
17
|
+
POSTGRES_HOST: "{{serviceHost}}"
|
|
18
|
+
POSTGRES_PORT: "5432"
|
|
19
|
+
POSTGRES_DB: "{{database}}"
|
|
20
|
+
POSTGRES_USER: "localdev"
|
|
21
|
+
|
|
22
|
+
# Unlike `env`, these become a real secretKeyRef - the password itself never passes through a
|
|
23
|
+
# plaintext value anywhere in this pipeline.
|
|
24
|
+
envFromSecret:
|
|
25
|
+
POSTGRES_PASSWORD:
|
|
26
|
+
secret: "{{secretName}}"
|
|
27
|
+
key: password
|
|
28
|
+
|
|
29
|
+
# Passed to `helm template` as -f values.yaml. {{...}} is substituted first: {{database}}/
|
|
30
|
+
# {{storage}}/{{version}} come from the app's own .local/config.json dependency entry (with
|
|
31
|
+
# fallback defaults), {{secretName}} from the generated Secret above.
|
|
32
|
+
values: |
|
|
33
|
+
auth:
|
|
34
|
+
username: localdev
|
|
35
|
+
database: "{{database}}"
|
|
36
|
+
existingSecret: "{{secretName}}"
|
|
37
|
+
primary:
|
|
38
|
+
persistence:
|
|
39
|
+
size: "{{storage}}"
|
|
@@ -1,29 +1,29 @@
|
|
|
1
|
-
# Redis addon - deploys the real Bitnami Redis chart, configured for local dev (standalone, no
|
|
2
|
-
# replicas). See docs/adding-tools.md for the full addon.yaml reference.
|
|
3
|
-
|
|
4
|
-
chart:
|
|
5
|
-
repo: https://charts.bitnami.com/bitnami
|
|
6
|
-
name: redis
|
|
7
|
-
version: "28.2.4"
|
|
8
|
-
|
|
9
|
-
secrets:
|
|
10
|
-
password:
|
|
11
|
-
length: 20
|
|
12
|
-
|
|
13
|
-
env:
|
|
14
|
-
REDIS_HOST: "{{serviceHost}}"
|
|
15
|
-
REDIS_PORT: "6379"
|
|
16
|
-
|
|
17
|
-
envFromSecret:
|
|
18
|
-
REDIS_PASSWORD:
|
|
19
|
-
secret: "{{secretName}}"
|
|
20
|
-
key: password
|
|
21
|
-
|
|
22
|
-
values: |
|
|
23
|
-
architecture: standalone
|
|
24
|
-
auth:
|
|
25
|
-
existingSecret: "{{secretName}}"
|
|
26
|
-
existingSecretPasswordKey: password
|
|
27
|
-
master:
|
|
28
|
-
persistence:
|
|
29
|
-
size: "{{storage}}"
|
|
1
|
+
# Redis addon - deploys the real Bitnami Redis chart, configured for local dev (standalone, no
|
|
2
|
+
# replicas). See docs/adding-tools.md for the full addon.yaml reference.
|
|
3
|
+
|
|
4
|
+
chart:
|
|
5
|
+
repo: https://charts.bitnami.com/bitnami
|
|
6
|
+
name: redis
|
|
7
|
+
version: "28.2.4"
|
|
8
|
+
|
|
9
|
+
secrets:
|
|
10
|
+
password:
|
|
11
|
+
length: 20
|
|
12
|
+
|
|
13
|
+
env:
|
|
14
|
+
REDIS_HOST: "{{serviceHost}}"
|
|
15
|
+
REDIS_PORT: "6379"
|
|
16
|
+
|
|
17
|
+
envFromSecret:
|
|
18
|
+
REDIS_PASSWORD:
|
|
19
|
+
secret: "{{secretName}}"
|
|
20
|
+
key: password
|
|
21
|
+
|
|
22
|
+
values: |
|
|
23
|
+
architecture: standalone
|
|
24
|
+
auth:
|
|
25
|
+
existingSecret: "{{secretName}}"
|
|
26
|
+
existingSecretPasswordKey: password
|
|
27
|
+
master:
|
|
28
|
+
persistence:
|
|
29
|
+
size: "{{storage}}"
|
package/src/lib/appSecrets.js
CHANGED
|
@@ -1,60 +1,60 @@
|
|
|
1
|
-
const { run, capture } = require('./exec');
|
|
2
|
-
|
|
3
|
-
// One Secret per app, holding whatever keys `.local/config.json`'s "secrets" array declares.
|
|
4
|
-
// Unlike addon-generated secrets (random values, never seen by you), these are values *you*
|
|
5
|
-
// supply - an API key, a third-party token - so there's no "generate once" step: you set them
|
|
6
|
-
// with `localctl app secrets set`, and manifestGen.js just references them via secretKeyRef.
|
|
7
|
-
function secretNameFor(cfg) {
|
|
8
|
-
return `${cfg.name}-secrets`;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
function ensureNamespace(namespace) {
|
|
12
|
-
if (capture('kubectl', ['get', 'namespace', namespace]).status !== 0) {
|
|
13
|
-
run('kubectl', ['create', 'namespace', namespace], { stdio: 'ignore', allowFail: true });
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function readAll(cfg) {
|
|
18
|
-
const res = capture('kubectl', ['get', 'secret', secretNameFor(cfg), '-n', cfg.namespace, '-o', 'json']);
|
|
19
|
-
if (res.status !== 0) return {};
|
|
20
|
-
const data = JSON.parse(res.stdout).data || {};
|
|
21
|
-
return Object.fromEntries(Object.entries(data).map(([k, v]) => [k, Buffer.from(v, 'base64').toString('utf8')]));
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
// Rewrites the whole Secret from a plain key->value map. `apply` (not `create`) makes this
|
|
25
|
-
// idempotent - a re-run with the same data is a no-op, a changed one updates in place.
|
|
26
|
-
function writeAll(cfg, data) {
|
|
27
|
-
ensureNamespace(cfg.namespace);
|
|
28
|
-
const doc = {
|
|
29
|
-
apiVersion: 'v1',
|
|
30
|
-
kind: 'Secret',
|
|
31
|
-
metadata: { name: secretNameFor(cfg), namespace: cfg.namespace },
|
|
32
|
-
type: 'Opaque',
|
|
33
|
-
stringData: data,
|
|
34
|
-
};
|
|
35
|
-
run('kubectl', ['apply', '-f', '-'], { stdio: ['pipe', 'inherit', 'inherit'], input: JSON.stringify(doc) });
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function setSecret(cfg, key, value) {
|
|
39
|
-
const data = readAll(cfg);
|
|
40
|
-
data[key] = value;
|
|
41
|
-
writeAll(cfg, data);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
function unsetSecret(cfg, key) {
|
|
45
|
-
const data = readAll(cfg);
|
|
46
|
-
if (!(key in data)) return false;
|
|
47
|
-
delete data[key];
|
|
48
|
-
if (Object.keys(data).length === 0) {
|
|
49
|
-
run('kubectl', ['delete', 'secret', secretNameFor(cfg), '-n', cfg.namespace, '--ignore-not-found=true'], { stdio: 'ignore' });
|
|
50
|
-
} else {
|
|
51
|
-
writeAll(cfg, data);
|
|
52
|
-
}
|
|
53
|
-
return true;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function listKeys(cfg) {
|
|
57
|
-
return Object.keys(readAll(cfg));
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
module.exports = { secretNameFor, setSecret, unsetSecret, listKeys, readAll };
|
|
1
|
+
const { run, capture } = require('./exec');
|
|
2
|
+
|
|
3
|
+
// One Secret per app, holding whatever keys `.local/config.json`'s "secrets" array declares.
|
|
4
|
+
// Unlike addon-generated secrets (random values, never seen by you), these are values *you*
|
|
5
|
+
// supply - an API key, a third-party token - so there's no "generate once" step: you set them
|
|
6
|
+
// with `localctl app secrets set`, and manifestGen.js just references them via secretKeyRef.
|
|
7
|
+
function secretNameFor(cfg) {
|
|
8
|
+
return `${cfg.name}-secrets`;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function ensureNamespace(namespace) {
|
|
12
|
+
if (capture('kubectl', ['get', 'namespace', namespace]).status !== 0) {
|
|
13
|
+
run('kubectl', ['create', 'namespace', namespace], { stdio: 'ignore', allowFail: true });
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function readAll(cfg) {
|
|
18
|
+
const res = capture('kubectl', ['get', 'secret', secretNameFor(cfg), '-n', cfg.namespace, '-o', 'json']);
|
|
19
|
+
if (res.status !== 0) return {};
|
|
20
|
+
const data = JSON.parse(res.stdout).data || {};
|
|
21
|
+
return Object.fromEntries(Object.entries(data).map(([k, v]) => [k, Buffer.from(v, 'base64').toString('utf8')]));
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Rewrites the whole Secret from a plain key->value map. `apply` (not `create`) makes this
|
|
25
|
+
// idempotent - a re-run with the same data is a no-op, a changed one updates in place.
|
|
26
|
+
function writeAll(cfg, data) {
|
|
27
|
+
ensureNamespace(cfg.namespace);
|
|
28
|
+
const doc = {
|
|
29
|
+
apiVersion: 'v1',
|
|
30
|
+
kind: 'Secret',
|
|
31
|
+
metadata: { name: secretNameFor(cfg), namespace: cfg.namespace },
|
|
32
|
+
type: 'Opaque',
|
|
33
|
+
stringData: data,
|
|
34
|
+
};
|
|
35
|
+
run('kubectl', ['apply', '-f', '-'], { stdio: ['pipe', 'inherit', 'inherit'], input: JSON.stringify(doc) });
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function setSecret(cfg, key, value) {
|
|
39
|
+
const data = readAll(cfg);
|
|
40
|
+
data[key] = value;
|
|
41
|
+
writeAll(cfg, data);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function unsetSecret(cfg, key) {
|
|
45
|
+
const data = readAll(cfg);
|
|
46
|
+
if (!(key in data)) return false;
|
|
47
|
+
delete data[key];
|
|
48
|
+
if (Object.keys(data).length === 0) {
|
|
49
|
+
run('kubectl', ['delete', 'secret', secretNameFor(cfg), '-n', cfg.namespace, '--ignore-not-found=true'], { stdio: 'ignore' });
|
|
50
|
+
} else {
|
|
51
|
+
writeAll(cfg, data);
|
|
52
|
+
}
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function listKeys(cfg) {
|
|
57
|
+
return Object.keys(readAll(cfg));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
module.exports = { secretNameFor, setSecret, unsetSecret, listKeys, readAll };
|
package/src/lib/clusterAddons.js
CHANGED
|
@@ -1,59 +1,59 @@
|
|
|
1
|
-
const { run } = require('./exec');
|
|
2
|
-
const { loadAddon } = require('./addonLoader');
|
|
3
|
-
const { findAddonDir, listAddonNames } = require('./addonDirs');
|
|
4
|
-
const { getList, workloadsWithPrefix } = require('./workloadReadiness');
|
|
5
|
-
const { DOMAIN, OBSERVABILITY_NAMESPACE } = require('./constants');
|
|
6
|
-
const ui = require('./ui');
|
|
7
|
-
|
|
8
|
-
// Cluster-wide addons live at <addons-dir>/cluster/<name>/addon.yaml - your own addons dir
|
|
9
|
-
// (~/.localctl/addons, or $LOCALCTL_ADDONS_DIR) is searched before the ones bundled with this
|
|
10
|
-
// CLI, and a name there overrides a built-in one of the same name. See docs/adding-tools.md.
|
|
11
|
-
// Shared by `localctl addons` and `localctl profiles new`'s "carry over which addons?" wizard.
|
|
12
|
-
function addonDirFor(name) {
|
|
13
|
-
const dir = findAddonDir(name, 'cluster');
|
|
14
|
-
if (!dir) {
|
|
15
|
-
throw new Error(`Unknown addon "${name}". Known: ${listAddonNames('cluster').join(', ')}.`);
|
|
16
|
-
}
|
|
17
|
-
return dir;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
function renderClusterAddon(name) {
|
|
21
|
-
const { manifestYaml } = loadAddon(addonDirFor(name), {
|
|
22
|
-
release: name,
|
|
23
|
-
namespace: OBSERVABILITY_NAMESPACE,
|
|
24
|
-
addonName: name,
|
|
25
|
-
extraVars: { domain: DOMAIN },
|
|
26
|
-
});
|
|
27
|
-
return manifestYaml;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function enableClusterAddon(name) {
|
|
31
|
-
run('kubectl', ['apply', '-f', '-'], { stdio: ['pipe', 'inherit', 'inherit'], input: renderClusterAddon(name) });
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
function disableClusterAddon(name) {
|
|
35
|
-
run('kubectl', ['delete', '-f', '-', '--ignore-not-found=true'], { stdio: ['pipe', 'inherit', 'inherit'], input: renderClusterAddon(name) });
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function readyLabel(workloads) {
|
|
39
|
-
if (workloads.length === 0) return ui.dim('-');
|
|
40
|
-
const ready = workloads.reduce((sum, w) => sum + w.ready, 0);
|
|
41
|
-
const desired = workloads.reduce((sum, w) => sum + w.desired, 0);
|
|
42
|
-
return ui.readyLabel(ready, desired);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function clusterAddonInstances() {
|
|
46
|
-
const records = getList(['get', 'configmap', '-A', '-l', 'localctl.dev/addon=true']);
|
|
47
|
-
return records.map((r) => {
|
|
48
|
-
const release = r.metadata.name.replace(/-localctl-addon$/, '');
|
|
49
|
-
return {
|
|
50
|
-
release,
|
|
51
|
-
owner: r.data.owner || null,
|
|
52
|
-
namespace: r.metadata.namespace,
|
|
53
|
-
chart: `${r.data.chart}@${r.data.version}`,
|
|
54
|
-
ready: readyLabel(workloadsWithPrefix(r.metadata.namespace, release)),
|
|
55
|
-
};
|
|
56
|
-
});
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
module.exports = { renderClusterAddon, enableClusterAddon, disableClusterAddon, clusterAddonInstances };
|
|
1
|
+
const { run } = require('./exec');
|
|
2
|
+
const { loadAddon } = require('./addonLoader');
|
|
3
|
+
const { findAddonDir, listAddonNames } = require('./addonDirs');
|
|
4
|
+
const { getList, workloadsWithPrefix } = require('./workloadReadiness');
|
|
5
|
+
const { DOMAIN, OBSERVABILITY_NAMESPACE } = require('./constants');
|
|
6
|
+
const ui = require('./ui');
|
|
7
|
+
|
|
8
|
+
// Cluster-wide addons live at <addons-dir>/cluster/<name>/addon.yaml - your own addons dir
|
|
9
|
+
// (~/.localctl/addons, or $LOCALCTL_ADDONS_DIR) is searched before the ones bundled with this
|
|
10
|
+
// CLI, and a name there overrides a built-in one of the same name. See docs/adding-tools.md.
|
|
11
|
+
// Shared by `localctl addons` and `localctl profiles new`'s "carry over which addons?" wizard.
|
|
12
|
+
function addonDirFor(name) {
|
|
13
|
+
const dir = findAddonDir(name, 'cluster');
|
|
14
|
+
if (!dir) {
|
|
15
|
+
throw new Error(`Unknown addon "${name}". Known: ${listAddonNames('cluster').join(', ')}.`);
|
|
16
|
+
}
|
|
17
|
+
return dir;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function renderClusterAddon(name) {
|
|
21
|
+
const { manifestYaml } = loadAddon(addonDirFor(name), {
|
|
22
|
+
release: name,
|
|
23
|
+
namespace: OBSERVABILITY_NAMESPACE,
|
|
24
|
+
addonName: name,
|
|
25
|
+
extraVars: { domain: DOMAIN },
|
|
26
|
+
});
|
|
27
|
+
return manifestYaml;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function enableClusterAddon(name) {
|
|
31
|
+
run('kubectl', ['apply', '-f', '-'], { stdio: ['pipe', 'inherit', 'inherit'], input: renderClusterAddon(name) });
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function disableClusterAddon(name) {
|
|
35
|
+
run('kubectl', ['delete', '-f', '-', '--ignore-not-found=true'], { stdio: ['pipe', 'inherit', 'inherit'], input: renderClusterAddon(name) });
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function readyLabel(workloads) {
|
|
39
|
+
if (workloads.length === 0) return ui.dim('-');
|
|
40
|
+
const ready = workloads.reduce((sum, w) => sum + w.ready, 0);
|
|
41
|
+
const desired = workloads.reduce((sum, w) => sum + w.desired, 0);
|
|
42
|
+
return ui.readyLabel(ready, desired);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function clusterAddonInstances() {
|
|
46
|
+
const records = getList(['get', 'configmap', '-A', '-l', 'localctl.dev/addon=true']);
|
|
47
|
+
return records.map((r) => {
|
|
48
|
+
const release = r.metadata.name.replace(/-localctl-addon$/, '');
|
|
49
|
+
return {
|
|
50
|
+
release,
|
|
51
|
+
owner: r.data.owner || null,
|
|
52
|
+
namespace: r.metadata.namespace,
|
|
53
|
+
chart: `${r.data.chart}@${r.data.version}`,
|
|
54
|
+
ready: readyLabel(workloadsWithPrefix(r.metadata.namespace, release)),
|
|
55
|
+
};
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
module.exports = { renderClusterAddon, enableClusterAddon, disableClusterAddon, clusterAddonInstances };
|