@localctl/cli 0.2.0 → 0.3.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.
Files changed (52) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +398 -431
  3. package/assets/manifests/namespace-addons.yaml +4 -4
  4. package/assets/manifests/tls-store.yaml +8 -8
  5. package/bin/localctl.js +2 -2
  6. package/package.json +61 -54
  7. package/src/commands/addons.js +56 -56
  8. package/src/commands/doctor.js +44 -43
  9. package/src/commands/down.js +35 -35
  10. package/src/commands/exec.js +15 -15
  11. package/src/commands/generateManifests.js +12 -12
  12. package/src/commands/hosts.js +23 -23
  13. package/src/commands/logs.js +45 -45
  14. package/src/commands/profiles.js +180 -180
  15. package/src/commands/prune.js +49 -49
  16. package/src/commands/registryInfo.js +18 -18
  17. package/src/commands/reload.js +44 -44
  18. package/src/commands/secrets.js +75 -75
  19. package/src/commands/setup.js +36 -36
  20. package/src/commands/status.js +136 -136
  21. package/src/commands/up.js +123 -123
  22. package/src/index.js +38 -36
  23. package/src/lib/addonDirs.js +43 -43
  24. package/src/lib/addonLoader.js +190 -190
  25. package/src/lib/addons/cluster/logging/addon.yaml +46 -46
  26. package/src/lib/addons/cluster/logging/config/all-logs.json +21 -21
  27. package/src/lib/addons/cluster/monitoring/addon.yaml +33 -33
  28. package/src/lib/addons/mongo/addon.yaml +18 -18
  29. package/src/lib/addons/postgres/addon.yaml +39 -39
  30. package/src/lib/addons/redis/addon.yaml +29 -29
  31. package/src/lib/appSecrets.js +60 -60
  32. package/src/lib/clusterAddons.js +59 -59
  33. package/src/lib/clusterProvision.js +171 -170
  34. package/src/lib/configSchema.js +103 -103
  35. package/src/lib/constants.js +16 -16
  36. package/src/lib/engine.js +36 -20
  37. package/src/lib/exec.js +77 -77
  38. package/src/lib/hostsFile.js +79 -79
  39. package/src/lib/loadConfig.js +51 -51
  40. package/src/lib/logger.js +9 -9
  41. package/src/lib/manifestGen.js +210 -210
  42. package/src/lib/paths.js +41 -41
  43. package/src/lib/podman.js +72 -72
  44. package/src/lib/profileStore.js +134 -134
  45. package/src/lib/projectContext.js +32 -32
  46. package/src/lib/prompt.js +97 -97
  47. package/src/lib/toolInstall.js +30 -30
  48. package/src/lib/ui.js +50 -50
  49. package/src/lib/updateCheck.js +84 -0
  50. package/src/lib/updateCheckWorker.js +46 -0
  51. package/src/lib/workloadReadiness.js +35 -35
  52. package/templates/Tiltfile.tmpl +35 -35
@@ -1,190 +1,190 @@
1
- const fs = require('fs');
2
- const path = require('path');
3
- const os = require('os');
4
- const crypto = require('crypto');
5
- const yaml = require('js-yaml');
6
- const { run, capture } = require('./exec');
7
-
8
- // Generic Helm-based addon loader. An addon is a folder containing an addon.yaml (what chart to
9
- // pull and how to configure it) and, optionally, a config/ folder (literal files - dashboards,
10
- // datasources, init scripts - that don't belong in Helm values but need to reach the chart as a
11
- // ConfigMap). See docs/adding-tools.md for the full addon.yaml reference and a worked example.
12
-
13
- function repoNameFor(url) {
14
- return `localctl-${crypto.createHash('md5').update(url).digest('hex').slice(0, 8)}`;
15
- }
16
-
17
- function ensureRepo(url) {
18
- const name = repoNameFor(url);
19
- // --force-update: idempotent even if re-pointed at a different URL under the same generated name.
20
- run('helm', ['repo', 'add', name, url, '--force-update'], { stdio: 'ignore' });
21
- run('helm', ['repo', 'update', name], { stdio: 'ignore', allowFail: true });
22
- return name;
23
- }
24
-
25
- function substitute(template, vars) {
26
- return String(template).replace(/\{\{\s*([\w.]+)\s*\}\}/g, (match, key) => {
27
- if (!(key in vars)) throw new Error(`Addon references unknown template variable "{{${key}}}"`);
28
- return vars[key];
29
- });
30
- }
31
-
32
- function ensureNamespace(ns) {
33
- if (capture('kubectl', ['get', 'namespace', ns]).status !== 0) {
34
- run('kubectl', ['create', 'namespace', ns], { stdio: 'ignore', allowFail: true });
35
- }
36
- }
37
-
38
- // Generates each secret's value once and stores it as a real k8s Secret; a re-run that finds the
39
- // Secret already there leaves it alone, so app data doesn't lose its password out from under it
40
- // on every `localctl up`.
41
- function ensureSecret(name, namespace, keys) {
42
- ensureNamespace(namespace);
43
- if (capture('kubectl', ['get', 'secret', name, '-n', namespace]).status === 0) return;
44
- const args = ['create', 'secret', 'generic', name, '-n', namespace];
45
- for (const key of keys) {
46
- args.push(`--from-literal=${key}=${crypto.randomBytes(16).toString('hex')}`);
47
- }
48
- run('kubectl', args, { stdio: 'ignore' });
49
- }
50
-
51
- // Turns a folder of literal files (dashboards, datasource definitions, init scripts - whatever
52
- // the chart expects to mount, not a Helm value) into a real ConfigMap.
53
- function ensureConfigMap(name, namespace, configDir) {
54
- ensureNamespace(namespace);
55
- const files = fs.readdirSync(configDir).filter((f) => fs.statSync(path.join(configDir, f)).isFile());
56
- const args = ['create', 'configmap', name, '-n', namespace, '--dry-run=client', '-o', 'yaml'];
57
- files.forEach((f) => args.push(`--from-file=${path.join(configDir, f)}`));
58
- const rendered = capture('kubectl', args);
59
- if (rendered.status !== 0) throw new Error(`Failed to render ConfigMap for ${configDir}: ${rendered.stderr}`);
60
- run('kubectl', ['apply', '-f', '-'], { stdio: ['pipe', 'ignore', 'inherit'], input: rendered.stdout });
61
- }
62
-
63
- function helmTemplate(release, chart, valuesObj, namespace) {
64
- const repoName = ensureRepo(chart.repo);
65
- const tmpValues = path.join(os.tmpdir(), `localctl-addon-values-${process.pid}-${Date.now()}.yaml`);
66
- fs.writeFileSync(tmpValues, yaml.dump(valuesObj));
67
- try {
68
- // --no-hooks: excludes `helm test` pods and similar hook resources from the rendered output -
69
- // we apply this YAML with plain `kubectl apply`, not `helm install`, so nothing would ever
70
- // run `helm test` to use them; left in, they'd just sit in the namespace as inert clutter.
71
- const args = ['template', release, `${repoName}/${chart.name}`, '-f', tmpValues, '-n', namespace, '--no-hooks'];
72
- if (chart.version) args.push('--version', chart.version);
73
- const res = capture('helm', args);
74
- if (res.status !== 0) {
75
- throw new Error(`helm template failed for chart "${chart.name}":\n${res.stderr}`);
76
- }
77
- return res.stdout;
78
- } finally {
79
- fs.rmSync(tmpValues, { force: true });
80
- }
81
- }
82
-
83
- // Every chart names its Service differently (Bitnami's own "dedupe release name vs chart name"
84
- // logic alone means postgresql uses `<release>-postgresql` but redis uses `<release>-master`, no
85
- // chart name at all, when the release name already contains "redis") - predicting it from a
86
- // naming convention is fragile per-chart guesswork. Reading it back out of the chart's own
87
- // rendered output is not: whatever the chart actually created is, by definition, correct.
88
- function findPrimaryService(manifestYaml) {
89
- const docs = yaml.loadAll(manifestYaml).filter(Boolean);
90
- const services = docs.filter((d) => d.kind === 'Service');
91
- return services.find((s) => !/-headless$|-hl$/.test(s.metadata.name)) || services[0] || null;
92
- }
93
-
94
- function findServiceHost(manifestYaml) {
95
- const svc = findPrimaryService(manifestYaml);
96
- return svc ? svc.metadata.name : null;
97
- }
98
-
99
- // Same "read it back, don't guess" reasoning as findServiceHost - a wait-for-dependency init
100
- // container (manifestGen.js) needs a real port, and addon.yaml's own `env` block is free to name
101
- // its port var anything (POSTGRES_PORT, REDIS_PORT, ...), so it's not something to parse generically.
102
- function findServicePort(manifestYaml) {
103
- const svc = findPrimaryService(manifestYaml);
104
- const port = svc && svc.spec.ports && svc.spec.ports[0];
105
- return port ? port.port : null;
106
- }
107
-
108
- // A small tracking ConfigMap bundled into the addon's own manifest output, so its lifecycle is
109
- // tied to the addon's (created and deleted together, no separate cleanup code needed). This is
110
- // what `localctl addons status` reads to list every addon instance in the cluster - it doesn't
111
- // try to guess which workload kind(s) a given chart happens to use (Deployment, StatefulSet,
112
- // DaemonSet all show up in real charts; guessing which is exactly the fragility that bit
113
- // `localctl status`'s own dependency listing once already).
114
- function buildAddonRecord({ release, namespace, addonName, chart, owner }) {
115
- return {
116
- apiVersion: 'v1',
117
- kind: 'ConfigMap',
118
- metadata: {
119
- name: `${release}-localctl-addon`,
120
- namespace,
121
- labels: { 'localctl.dev/addon': 'true' },
122
- },
123
- data: {
124
- addon: addonName,
125
- chart: chart.name,
126
- version: String(chart.version || ''),
127
- owner: owner || '',
128
- },
129
- };
130
- }
131
-
132
- // Loads one addon (given its folder) and returns the rendered manifest YAML, plus any env vars
133
- // it wants injected into the caller's own container (per-app addons only - see manifestGen.js).
134
- function loadAddon(addonDir, { release, namespace, addonName, owner, extraVars = {}, chartVersionOverride }) {
135
- const specPath = path.join(addonDir, 'addon.yaml');
136
- const spec = yaml.load(fs.readFileSync(specPath, 'utf8'));
137
-
138
- const chart = { ...spec.chart, version: chartVersionOverride || spec.chart.version };
139
- const vars = { release, namespace, ...extraVars };
140
-
141
- if (spec.secrets) {
142
- const secretName = `${release}-secret`;
143
- ensureSecret(secretName, namespace, Object.keys(spec.secrets));
144
- vars.secretName = secretName;
145
- }
146
-
147
- const configDir = path.join(addonDir, 'config');
148
- if (spec.config && fs.existsSync(configDir)) {
149
- const configMapName = `${release}-config`;
150
- ensureConfigMap(configMapName, namespace, configDir);
151
- vars.configMapName = configMapName;
152
- }
153
-
154
- const valuesYaml = substitute(spec.values, vars);
155
- const valuesObj = yaml.load(valuesYaml) || {};
156
- const manifestYaml = helmTemplate(release, chart, valuesObj, namespace);
157
- vars.serviceHost = findServiceHost(manifestYaml);
158
- const servicePort = findServicePort(manifestYaml);
159
-
160
- const env = {};
161
- if (spec.env) {
162
- for (const [k, v] of Object.entries(spec.env)) {
163
- env[k] = substitute(v, vars);
164
- }
165
- }
166
-
167
- // Unlike `env` (plain values), these become a real k8s secretKeyRef in the app's Deployment -
168
- // the actual password never passes through a plaintext env value, config file, or `helm` value.
169
- const envFromSecret = {};
170
- if (spec.envFromSecret) {
171
- for (const [k, ref] of Object.entries(spec.envFromSecret)) {
172
- envFromSecret[k] = { secret: substitute(ref.secret, vars), key: substitute(ref.key, vars) };
173
- }
174
- }
175
-
176
- const record = buildAddonRecord({ release, namespace, addonName: addonName || path.basename(addonDir), chart, owner });
177
- // trimEnd() + explicit '\n' before the separator: `helm template` output doesn't always end
178
- // with a trailing newline, and joining on a bare '---\n' in that case glues the separator
179
- // directly onto the last line's content with no line break, corrupting the YAML.
180
- const fullManifestYaml = [manifestYaml.trimEnd(), yaml.dump(record).trimEnd()].join('\n---\n');
181
-
182
- // The addon's real, rendered host+port - not env's POSTGRES_HOST/POSTGRES_PORT (those are only
183
- // as reliable as each addon.yaml's own naming), so a wait-for-dependency init container
184
- // (manifestGen.js) can work for any addon generically, built-in or your own.
185
- const connect = vars.serviceHost && servicePort ? { host: vars.serviceHost, port: servicePort } : null;
186
-
187
- return { manifestYaml: fullManifestYaml, env, envFromSecret, connect };
188
- }
189
-
190
- module.exports = { loadAddon, substitute };
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const os = require('os');
4
+ const crypto = require('crypto');
5
+ const yaml = require('js-yaml');
6
+ const { run, capture } = require('./exec');
7
+
8
+ // Generic Helm-based addon loader. An addon is a folder containing an addon.yaml (what chart to
9
+ // pull and how to configure it) and, optionally, a config/ folder (literal files - dashboards,
10
+ // datasources, init scripts - that don't belong in Helm values but need to reach the chart as a
11
+ // ConfigMap). See docs/adding-tools.md for the full addon.yaml reference and a worked example.
12
+
13
+ function repoNameFor(url) {
14
+ return `localctl-${crypto.createHash('md5').update(url).digest('hex').slice(0, 8)}`;
15
+ }
16
+
17
+ function ensureRepo(url) {
18
+ const name = repoNameFor(url);
19
+ // --force-update: idempotent even if re-pointed at a different URL under the same generated name.
20
+ run('helm', ['repo', 'add', name, url, '--force-update'], { stdio: 'ignore' });
21
+ run('helm', ['repo', 'update', name], { stdio: 'ignore', allowFail: true });
22
+ return name;
23
+ }
24
+
25
+ function substitute(template, vars) {
26
+ return String(template).replace(/\{\{\s*([\w.]+)\s*\}\}/g, (match, key) => {
27
+ if (!(key in vars)) throw new Error(`Addon references unknown template variable "{{${key}}}"`);
28
+ return vars[key];
29
+ });
30
+ }
31
+
32
+ function ensureNamespace(ns) {
33
+ if (capture('kubectl', ['get', 'namespace', ns]).status !== 0) {
34
+ run('kubectl', ['create', 'namespace', ns], { stdio: 'ignore', allowFail: true });
35
+ }
36
+ }
37
+
38
+ // Generates each secret's value once and stores it as a real k8s Secret; a re-run that finds the
39
+ // Secret already there leaves it alone, so app data doesn't lose its password out from under it
40
+ // on every `localctl up`.
41
+ function ensureSecret(name, namespace, keys) {
42
+ ensureNamespace(namespace);
43
+ if (capture('kubectl', ['get', 'secret', name, '-n', namespace]).status === 0) return;
44
+ const args = ['create', 'secret', 'generic', name, '-n', namespace];
45
+ for (const key of keys) {
46
+ args.push(`--from-literal=${key}=${crypto.randomBytes(16).toString('hex')}`);
47
+ }
48
+ run('kubectl', args, { stdio: 'ignore' });
49
+ }
50
+
51
+ // Turns a folder of literal files (dashboards, datasource definitions, init scripts - whatever
52
+ // the chart expects to mount, not a Helm value) into a real ConfigMap.
53
+ function ensureConfigMap(name, namespace, configDir) {
54
+ ensureNamespace(namespace);
55
+ const files = fs.readdirSync(configDir).filter((f) => fs.statSync(path.join(configDir, f)).isFile());
56
+ const args = ['create', 'configmap', name, '-n', namespace, '--dry-run=client', '-o', 'yaml'];
57
+ files.forEach((f) => args.push(`--from-file=${path.join(configDir, f)}`));
58
+ const rendered = capture('kubectl', args);
59
+ if (rendered.status !== 0) throw new Error(`Failed to render ConfigMap for ${configDir}: ${rendered.stderr}`);
60
+ run('kubectl', ['apply', '-f', '-'], { stdio: ['pipe', 'ignore', 'inherit'], input: rendered.stdout });
61
+ }
62
+
63
+ function helmTemplate(release, chart, valuesObj, namespace) {
64
+ const repoName = ensureRepo(chart.repo);
65
+ const tmpValues = path.join(os.tmpdir(), `localctl-addon-values-${process.pid}-${Date.now()}.yaml`);
66
+ fs.writeFileSync(tmpValues, yaml.dump(valuesObj));
67
+ try {
68
+ // --no-hooks: excludes `helm test` pods and similar hook resources from the rendered output -
69
+ // we apply this YAML with plain `kubectl apply`, not `helm install`, so nothing would ever
70
+ // run `helm test` to use them; left in, they'd just sit in the namespace as inert clutter.
71
+ const args = ['template', release, `${repoName}/${chart.name}`, '-f', tmpValues, '-n', namespace, '--no-hooks'];
72
+ if (chart.version) args.push('--version', chart.version);
73
+ const res = capture('helm', args);
74
+ if (res.status !== 0) {
75
+ throw new Error(`helm template failed for chart "${chart.name}":\n${res.stderr}`);
76
+ }
77
+ return res.stdout;
78
+ } finally {
79
+ fs.rmSync(tmpValues, { force: true });
80
+ }
81
+ }
82
+
83
+ // Every chart names its Service differently (Bitnami's own "dedupe release name vs chart name"
84
+ // logic alone means postgresql uses `<release>-postgresql` but redis uses `<release>-master`, no
85
+ // chart name at all, when the release name already contains "redis") - predicting it from a
86
+ // naming convention is fragile per-chart guesswork. Reading it back out of the chart's own
87
+ // rendered output is not: whatever the chart actually created is, by definition, correct.
88
+ function findPrimaryService(manifestYaml) {
89
+ const docs = yaml.loadAll(manifestYaml).filter(Boolean);
90
+ const services = docs.filter((d) => d.kind === 'Service');
91
+ return services.find((s) => !/-headless$|-hl$/.test(s.metadata.name)) || services[0] || null;
92
+ }
93
+
94
+ function findServiceHost(manifestYaml) {
95
+ const svc = findPrimaryService(manifestYaml);
96
+ return svc ? svc.metadata.name : null;
97
+ }
98
+
99
+ // Same "read it back, don't guess" reasoning as findServiceHost - a wait-for-dependency init
100
+ // container (manifestGen.js) needs a real port, and addon.yaml's own `env` block is free to name
101
+ // its port var anything (POSTGRES_PORT, REDIS_PORT, ...), so it's not something to parse generically.
102
+ function findServicePort(manifestYaml) {
103
+ const svc = findPrimaryService(manifestYaml);
104
+ const port = svc && svc.spec.ports && svc.spec.ports[0];
105
+ return port ? port.port : null;
106
+ }
107
+
108
+ // A small tracking ConfigMap bundled into the addon's own manifest output, so its lifecycle is
109
+ // tied to the addon's (created and deleted together, no separate cleanup code needed). This is
110
+ // what `localctl addons status` reads to list every addon instance in the cluster - it doesn't
111
+ // try to guess which workload kind(s) a given chart happens to use (Deployment, StatefulSet,
112
+ // DaemonSet all show up in real charts; guessing which is exactly the fragility that bit
113
+ // `localctl status`'s own dependency listing once already).
114
+ function buildAddonRecord({ release, namespace, addonName, chart, owner }) {
115
+ return {
116
+ apiVersion: 'v1',
117
+ kind: 'ConfigMap',
118
+ metadata: {
119
+ name: `${release}-localctl-addon`,
120
+ namespace,
121
+ labels: { 'localctl.dev/addon': 'true' },
122
+ },
123
+ data: {
124
+ addon: addonName,
125
+ chart: chart.name,
126
+ version: String(chart.version || ''),
127
+ owner: owner || '',
128
+ },
129
+ };
130
+ }
131
+
132
+ // Loads one addon (given its folder) and returns the rendered manifest YAML, plus any env vars
133
+ // it wants injected into the caller's own container (per-app addons only - see manifestGen.js).
134
+ function loadAddon(addonDir, { release, namespace, addonName, owner, extraVars = {}, chartVersionOverride }) {
135
+ const specPath = path.join(addonDir, 'addon.yaml');
136
+ const spec = yaml.load(fs.readFileSync(specPath, 'utf8'));
137
+
138
+ const chart = { ...spec.chart, version: chartVersionOverride || spec.chart.version };
139
+ const vars = { release, namespace, ...extraVars };
140
+
141
+ if (spec.secrets) {
142
+ const secretName = `${release}-secret`;
143
+ ensureSecret(secretName, namespace, Object.keys(spec.secrets));
144
+ vars.secretName = secretName;
145
+ }
146
+
147
+ const configDir = path.join(addonDir, 'config');
148
+ if (spec.config && fs.existsSync(configDir)) {
149
+ const configMapName = `${release}-config`;
150
+ ensureConfigMap(configMapName, namespace, configDir);
151
+ vars.configMapName = configMapName;
152
+ }
153
+
154
+ const valuesYaml = substitute(spec.values, vars);
155
+ const valuesObj = yaml.load(valuesYaml) || {};
156
+ const manifestYaml = helmTemplate(release, chart, valuesObj, namespace);
157
+ vars.serviceHost = findServiceHost(manifestYaml);
158
+ const servicePort = findServicePort(manifestYaml);
159
+
160
+ const env = {};
161
+ if (spec.env) {
162
+ for (const [k, v] of Object.entries(spec.env)) {
163
+ env[k] = substitute(v, vars);
164
+ }
165
+ }
166
+
167
+ // Unlike `env` (plain values), these become a real k8s secretKeyRef in the app's Deployment -
168
+ // the actual password never passes through a plaintext env value, config file, or `helm` value.
169
+ const envFromSecret = {};
170
+ if (spec.envFromSecret) {
171
+ for (const [k, ref] of Object.entries(spec.envFromSecret)) {
172
+ envFromSecret[k] = { secret: substitute(ref.secret, vars), key: substitute(ref.key, vars) };
173
+ }
174
+ }
175
+
176
+ const record = buildAddonRecord({ release, namespace, addonName: addonName || path.basename(addonDir), chart, owner });
177
+ // trimEnd() + explicit '\n' before the separator: `helm template` output doesn't always end
178
+ // with a trailing newline, and joining on a bare '---\n' in that case glues the separator
179
+ // directly onto the last line's content with no line break, corrupting the YAML.
180
+ const fullManifestYaml = [manifestYaml.trimEnd(), yaml.dump(record).trimEnd()].join('\n---\n');
181
+
182
+ // The addon's real, rendered host+port - not env's POSTGRES_HOST/POSTGRES_PORT (those are only
183
+ // as reliable as each addon.yaml's own naming), so a wait-for-dependency init container
184
+ // (manifestGen.js) can work for any addon generically, built-in or your own.
185
+ const connect = vars.serviceHost && servicePort ? { host: vars.serviceHost, port: servicePort } : null;
186
+
187
+ return { manifestYaml: fullManifestYaml, env, envFromSecret, connect };
188
+ }
189
+
190
+ module.exports = { loadAddon, substitute };
@@ -1,46 +1,46 @@
1
- # Cluster-wide logging addon - deploys Grafana Labs' own official loki-stack chart (Loki +
2
- # Promtail + Grafana, all in one). Enabled via `localctl addons enable logging`.
3
- #
4
- # `config` demonstrates the addon config/ folder feature: any file dropped in ./config/ becomes a
5
- # ConfigMap ("<release>-config"), and {{configMapName}} below wires it in as a Grafana dashboard.
6
- # See docs/adding-tools.md.
7
-
8
- chart:
9
- repo: https://grafana.github.io/helm-charts
10
- name: loki-stack
11
- version: "2.10.3"
12
-
13
- config: true
14
-
15
- values: |
16
- loki:
17
- enabled: true
18
- promtail:
19
- enabled: true
20
- grafana:
21
- enabled: true
22
- adminUser: admin
23
- dashboardProviders:
24
- dashboardproviders.yaml:
25
- apiVersion: 1
26
- providers:
27
- - name: default
28
- orgId: 1
29
- folder: ""
30
- type: file
31
- disableDeletion: false
32
- editable: true
33
- options:
34
- path: /var/lib/grafana/dashboards/default
35
- dashboardsConfigMaps:
36
- default: "{{configMapName}}"
37
- ingress:
38
- enabled: true
39
- ingressClassName: traefik
40
- annotations:
41
- traefik.ingress.kubernetes.io/router.entrypoints: web,websecure
42
- hosts:
43
- - "grafana.{{domain}}"
44
- tls:
45
- - hosts:
46
- - "grafana.{{domain}}"
1
+ # Cluster-wide logging addon - deploys Grafana Labs' own official loki-stack chart (Loki +
2
+ # Promtail + Grafana, all in one). Enabled via `localctl addons enable logging`.
3
+ #
4
+ # `config` demonstrates the addon config/ folder feature: any file dropped in ./config/ becomes a
5
+ # ConfigMap ("<release>-config"), and {{configMapName}} below wires it in as a Grafana dashboard.
6
+ # See docs/adding-tools.md.
7
+
8
+ chart:
9
+ repo: https://grafana.github.io/helm-charts
10
+ name: loki-stack
11
+ version: "2.10.3"
12
+
13
+ config: true
14
+
15
+ values: |
16
+ loki:
17
+ enabled: true
18
+ promtail:
19
+ enabled: true
20
+ grafana:
21
+ enabled: true
22
+ adminUser: admin
23
+ dashboardProviders:
24
+ dashboardproviders.yaml:
25
+ apiVersion: 1
26
+ providers:
27
+ - name: default
28
+ orgId: 1
29
+ folder: ""
30
+ type: file
31
+ disableDeletion: false
32
+ editable: true
33
+ options:
34
+ path: /var/lib/grafana/dashboards/default
35
+ dashboardsConfigMaps:
36
+ default: "{{configMapName}}"
37
+ ingress:
38
+ enabled: true
39
+ ingressClassName: traefik
40
+ annotations:
41
+ traefik.ingress.kubernetes.io/router.entrypoints: web,websecure
42
+ hosts:
43
+ - "grafana.{{domain}}"
44
+ tls:
45
+ - hosts:
46
+ - "grafana.{{domain}}"
@@ -1,21 +1,21 @@
1
- {
2
- "title": "All Logs",
3
- "uid": "localctl-all-logs",
4
- "schemaVersion": 39,
5
- "timezone": "browser",
6
- "time": { "from": "now-1h", "to": "now" },
7
- "panels": [
8
- {
9
- "type": "logs",
10
- "title": "Every pod, every namespace",
11
- "gridPos": { "h": 20, "w": 24, "x": 0, "y": 0 },
12
- "datasource": { "type": "loki", "uid": "Loki" },
13
- "targets": [
14
- {
15
- "expr": "{namespace=~\".+\"}",
16
- "refId": "A"
17
- }
18
- ]
19
- }
20
- ]
21
- }
1
+ {
2
+ "title": "All Logs",
3
+ "uid": "localctl-all-logs",
4
+ "schemaVersion": 39,
5
+ "timezone": "browser",
6
+ "time": { "from": "now-1h", "to": "now" },
7
+ "panels": [
8
+ {
9
+ "type": "logs",
10
+ "title": "Every pod, every namespace",
11
+ "gridPos": { "h": 20, "w": 24, "x": 0, "y": 0 },
12
+ "datasource": { "type": "loki", "uid": "Loki" },
13
+ "targets": [
14
+ {
15
+ "expr": "{namespace=~\".+\"}",
16
+ "refId": "A"
17
+ }
18
+ ]
19
+ }
20
+ ]
21
+ }
@@ -1,33 +1,33 @@
1
- # Cluster-wide metrics addon - deploys the real prometheus-community Prometheus chart (server +
2
- # kube-state-metrics + node-exporter). Enabled via `localctl addons enable monitoring`.
3
- #
4
- # No bundled Grafana here (the `logging` addon already brings one) - point it at this Prometheus
5
- # instance yourself as a datasource if you want dashboards
6
- # (http://monitoring-prometheus-server.observability.svc.cluster.local - the chart's own Service
7
- # name, check `kubectl get svc -n observability` if it ever renames itself in a future chart
8
- # version rather than trusting this comment). Keeping the two addons independent means either
9
- # works without the other.
10
-
11
- chart:
12
- repo: https://prometheus-community.github.io/helm-charts
13
- name: prometheus
14
- version: "29.34.0"
15
-
16
- values: |
17
- alertmanager:
18
- enabled: false
19
- prometheus-pushgateway:
20
- enabled: false
21
- server:
22
- persistentVolume:
23
- size: 2Gi
24
- ingress:
25
- enabled: true
26
- ingressClassName: traefik
27
- annotations:
28
- traefik.ingress.kubernetes.io/router.entrypoints: web,websecure
29
- hosts:
30
- - "monitoring.{{domain}}"
31
- tls:
32
- - hosts:
33
- - "monitoring.{{domain}}"
1
+ # Cluster-wide metrics addon - deploys the real prometheus-community Prometheus chart (server +
2
+ # kube-state-metrics + node-exporter). Enabled via `localctl addons enable monitoring`.
3
+ #
4
+ # No bundled Grafana here (the `logging` addon already brings one) - point it at this Prometheus
5
+ # instance yourself as a datasource if you want dashboards
6
+ # (http://monitoring-prometheus-server.observability.svc.cluster.local - the chart's own Service
7
+ # name, check `kubectl get svc -n observability` if it ever renames itself in a future chart
8
+ # version rather than trusting this comment). Keeping the two addons independent means either
9
+ # works without the other.
10
+
11
+ chart:
12
+ repo: https://prometheus-community.github.io/helm-charts
13
+ name: prometheus
14
+ version: "29.34.0"
15
+
16
+ values: |
17
+ alertmanager:
18
+ enabled: false
19
+ prometheus-pushgateway:
20
+ enabled: false
21
+ server:
22
+ persistentVolume:
23
+ size: 2Gi
24
+ ingress:
25
+ enabled: true
26
+ ingressClassName: traefik
27
+ annotations:
28
+ traefik.ingress.kubernetes.io/router.entrypoints: web,websecure
29
+ hosts:
30
+ - "monitoring.{{domain}}"
31
+ tls:
32
+ - hosts:
33
+ - "monitoring.{{domain}}"
@@ -1,18 +1,18 @@
1
- # Mongo addon - deploys the real Bitnami MongoDB chart. Auth disabled by default, matching this
2
- # addon's old behavior (local dev only, not meant to hold anything sensitive) - set auth.enabled
3
- # and add a secrets/envFromSecret block here (see postgres/addon.yaml) if you want it.
4
-
5
- chart:
6
- repo: https://charts.bitnami.com/bitnami
7
- name: mongodb
8
- version: "19.3.1"
9
-
10
- env:
11
- MONGO_HOST: "{{serviceHost}}"
12
- MONGO_PORT: "27017"
13
-
14
- values: |
15
- auth:
16
- enabled: false
17
- persistence:
18
- size: "{{storage}}"
1
+ # Mongo addon - deploys the real Bitnami MongoDB chart. Auth disabled by default, matching this
2
+ # addon's old behavior (local dev only, not meant to hold anything sensitive) - set auth.enabled
3
+ # and add a secrets/envFromSecret block here (see postgres/addon.yaml) if you want it.
4
+
5
+ chart:
6
+ repo: https://charts.bitnami.com/bitnami
7
+ name: mongodb
8
+ version: "19.3.1"
9
+
10
+ env:
11
+ MONGO_HOST: "{{serviceHost}}"
12
+ MONGO_PORT: "27017"
13
+
14
+ values: |
15
+ auth:
16
+ enabled: false
17
+ persistence:
18
+ size: "{{storage}}"