raijin-server 0.2.41__py3-none-any.whl → 0.3.1__py3-none-any.whl
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.
Potentially problematic release.
This version of raijin-server might be problematic. Click here for more details.
- raijin_server/__init__.py +1 -1
- raijin_server/cli.py +6 -0
- raijin_server/modules/__init__.py +3 -1
- raijin_server/modules/grafana.py +365 -16
- raijin_server/modules/internal_dns.py +446 -0
- raijin_server/modules/kong.py +8 -4
- raijin_server/modules/minio.py +15 -5
- raijin_server/modules/observability_ingress.py +29 -1
- raijin_server/modules/prometheus.py +266 -3
- raijin_server/modules/traefik.py +35 -1
- raijin_server/modules/vpn_client.py +526 -0
- raijin_server-0.3.1.dist-info/METADATA +362 -0
- {raijin_server-0.2.41.dist-info → raijin_server-0.3.1.dist-info}/RECORD +17 -15
- raijin_server-0.2.41.dist-info/METADATA +0 -564
- {raijin_server-0.2.41.dist-info → raijin_server-0.3.1.dist-info}/WHEEL +0 -0
- {raijin_server-0.2.41.dist-info → raijin_server-0.3.1.dist-info}/entry_points.txt +0 -0
- {raijin_server-0.2.41.dist-info → raijin_server-0.3.1.dist-info}/licenses/LICENSE +0 -0
- {raijin_server-0.2.41.dist-info → raijin_server-0.3.1.dist-info}/top_level.txt +0 -0
raijin_server/__init__.py
CHANGED
raijin_server/cli.py
CHANGED
|
@@ -27,6 +27,7 @@ from raijin_server.modules import (
|
|
|
27
27
|
grafana,
|
|
28
28
|
harness,
|
|
29
29
|
hardening,
|
|
30
|
+
internal_dns,
|
|
30
31
|
istio,
|
|
31
32
|
kafka,
|
|
32
33
|
kong,
|
|
@@ -44,6 +45,7 @@ from raijin_server.modules import (
|
|
|
44
45
|
traefik,
|
|
45
46
|
velero,
|
|
46
47
|
vpn,
|
|
48
|
+
vpn_client,
|
|
47
49
|
)
|
|
48
50
|
from raijin_server.utils import ExecutionContext, logger, active_log_file, available_log_files, page_text, ensure_tool
|
|
49
51
|
from raijin_server.validators import validate_system_requirements, check_module_dependencies, MODULE_DEPENDENCIES
|
|
@@ -85,6 +87,8 @@ MODULES: Dict[str, Callable[[ExecutionContext], None]] = {
|
|
|
85
87
|
"essentials": essentials.run,
|
|
86
88
|
"firewall": firewall.run,
|
|
87
89
|
"vpn": vpn.run,
|
|
90
|
+
"vpn_client": vpn_client.run,
|
|
91
|
+
"internal_dns": internal_dns.run,
|
|
88
92
|
"kubernetes": kubernetes.run,
|
|
89
93
|
"calico": calico.run,
|
|
90
94
|
"metallb": metallb.run,
|
|
@@ -120,6 +124,8 @@ MODULE_DESCRIPTIONS: Dict[str, str] = {
|
|
|
120
124
|
"essentials": "Pacotes basicos, repos, utilitarios",
|
|
121
125
|
"firewall": "Regras UFW padrao e serviços basicos",
|
|
122
126
|
"vpn": "Provisiona WireGuard com cliente inicial",
|
|
127
|
+
"vpn_client": "Gerencia clientes VPN (adicionar/remover/listar)",
|
|
128
|
+
"internal_dns": "Configura DNS interno para domínios privados (*.asgard.internal)",
|
|
123
129
|
"kubernetes": "Instala kubeadm/kubelet/kubectl e inicializa cluster",
|
|
124
130
|
"calico": "CNI Calico e politica default deny",
|
|
125
131
|
"metallb": "LoadBalancer em bare metal (pool L2)",
|
|
@@ -21,6 +21,8 @@ __all__ = [
|
|
|
21
21
|
"bootstrap",
|
|
22
22
|
"ssh_hardening",
|
|
23
23
|
"vpn",
|
|
24
|
+
"vpn_client",
|
|
25
|
+
"internal_dns",
|
|
24
26
|
"observability_ingress",
|
|
25
27
|
"observability_dashboards",
|
|
26
28
|
"apokolips_demo",
|
|
@@ -32,4 +34,4 @@ __all__ = [
|
|
|
32
34
|
from raijin_server.modules import calico, essentials, firewall, grafana, harness, hardening, istio
|
|
33
35
|
from raijin_server.modules import kafka, kong, kubernetes, loki, minio, network, observability_dashboards
|
|
34
36
|
from raijin_server.modules import observability_ingress, prometheus, traefik, velero, apokolips_demo, secrets, cert_manager
|
|
35
|
-
from raijin_server.modules import bootstrap, full_install, sanitize, ssh_hardening, vpn
|
|
37
|
+
from raijin_server.modules import bootstrap, full_install, sanitize, ssh_hardening, vpn, vpn_client, internal_dns
|
raijin_server/modules/grafana.py
CHANGED
|
@@ -1,12 +1,27 @@
|
|
|
1
1
|
"""Configuracao do Grafana via Helm com datasource e dashboards provisionados."""
|
|
2
2
|
|
|
3
|
+
import json
|
|
3
4
|
import socket
|
|
5
|
+
import tempfile
|
|
6
|
+
import textwrap
|
|
4
7
|
import time
|
|
5
8
|
from pathlib import Path
|
|
6
9
|
|
|
7
10
|
import typer
|
|
8
11
|
|
|
9
|
-
from raijin_server.utils import
|
|
12
|
+
from raijin_server.utils import (
|
|
13
|
+
ExecutionContext,
|
|
14
|
+
helm_upgrade_install,
|
|
15
|
+
kubectl_create_ns,
|
|
16
|
+
require_root,
|
|
17
|
+
run_cmd,
|
|
18
|
+
write_file,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
LOCAL_PATH_PROVISIONER_URL = (
|
|
22
|
+
"https://raw.githubusercontent.com/rancher/local-path-provisioner/"
|
|
23
|
+
"v0.0.30/deploy/local-path-storage.yaml"
|
|
24
|
+
)
|
|
10
25
|
|
|
11
26
|
|
|
12
27
|
def _detect_node_name(ctx: ExecutionContext) -> str:
|
|
@@ -21,6 +36,269 @@ def _detect_node_name(ctx: ExecutionContext) -> str:
|
|
|
21
36
|
return socket.gethostname()
|
|
22
37
|
|
|
23
38
|
|
|
39
|
+
def _get_default_storage_class(ctx: ExecutionContext) -> str:
|
|
40
|
+
"""Retorna o nome da StorageClass default do cluster, se existir."""
|
|
41
|
+
if ctx.dry_run:
|
|
42
|
+
return ""
|
|
43
|
+
result = run_cmd(
|
|
44
|
+
[
|
|
45
|
+
"kubectl",
|
|
46
|
+
"get",
|
|
47
|
+
"storageclass",
|
|
48
|
+
"-o",
|
|
49
|
+
"jsonpath={.items[?(@.metadata.annotations.storageclass\\.kubernetes\\.io/is-default-class=='true')].metadata.name}",
|
|
50
|
+
],
|
|
51
|
+
ctx,
|
|
52
|
+
check=False,
|
|
53
|
+
)
|
|
54
|
+
if result.returncode == 0 and (result.stdout or "").strip():
|
|
55
|
+
return (result.stdout or "").strip()
|
|
56
|
+
return ""
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _list_storage_classes(ctx: ExecutionContext) -> list:
|
|
60
|
+
"""Lista todas as StorageClasses disponiveis."""
|
|
61
|
+
result = run_cmd(
|
|
62
|
+
["kubectl", "get", "storageclass", "-o", "jsonpath={.items[*].metadata.name}"],
|
|
63
|
+
ctx,
|
|
64
|
+
check=False,
|
|
65
|
+
)
|
|
66
|
+
if result.returncode == 0 and (result.stdout or "").strip():
|
|
67
|
+
return (result.stdout or "").strip().split()
|
|
68
|
+
return []
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _apply_manifest(ctx: ExecutionContext, manifest: str, description: str) -> bool:
|
|
72
|
+
"""Aplica manifest YAML temporario com kubectl."""
|
|
73
|
+
tmp_path = None
|
|
74
|
+
try:
|
|
75
|
+
with tempfile.NamedTemporaryFile("w", delete=False, suffix=".yaml") as tmp:
|
|
76
|
+
tmp.write(manifest)
|
|
77
|
+
tmp.flush()
|
|
78
|
+
tmp_path = Path(tmp.name)
|
|
79
|
+
result = run_cmd(
|
|
80
|
+
["kubectl", "apply", "-f", str(tmp_path)],
|
|
81
|
+
ctx,
|
|
82
|
+
check=False,
|
|
83
|
+
)
|
|
84
|
+
if result.returncode != 0:
|
|
85
|
+
typer.secho(f" Falha ao aplicar {description}.", fg=typer.colors.RED)
|
|
86
|
+
return False
|
|
87
|
+
typer.secho(f" ✓ {description} aplicado.", fg=typer.colors.GREEN)
|
|
88
|
+
return True
|
|
89
|
+
finally:
|
|
90
|
+
if tmp_path and tmp_path.exists():
|
|
91
|
+
tmp_path.unlink(missing_ok=True)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def _patch_local_path_provisioner_tolerations(ctx: ExecutionContext) -> None:
|
|
95
|
+
"""Adiciona tolerations ao local-path-provisioner para rodar em control-plane."""
|
|
96
|
+
typer.echo(" Configurando tolerations no local-path-provisioner...")
|
|
97
|
+
|
|
98
|
+
# Patch no deployment para tolerar control-plane
|
|
99
|
+
patch_deployment = textwrap.dedent(
|
|
100
|
+
"""
|
|
101
|
+
spec:
|
|
102
|
+
template:
|
|
103
|
+
spec:
|
|
104
|
+
tolerations:
|
|
105
|
+
- key: node-role.kubernetes.io/control-plane
|
|
106
|
+
operator: Exists
|
|
107
|
+
effect: NoSchedule
|
|
108
|
+
- key: node-role.kubernetes.io/master
|
|
109
|
+
operator: Exists
|
|
110
|
+
effect: NoSchedule
|
|
111
|
+
"""
|
|
112
|
+
).strip()
|
|
113
|
+
|
|
114
|
+
result = run_cmd(
|
|
115
|
+
[
|
|
116
|
+
"kubectl", "-n", "local-path-storage", "patch", "deployment",
|
|
117
|
+
"local-path-provisioner", "--patch", patch_deployment,
|
|
118
|
+
],
|
|
119
|
+
ctx,
|
|
120
|
+
check=False,
|
|
121
|
+
)
|
|
122
|
+
if result.returncode == 0:
|
|
123
|
+
typer.secho(" ✓ Deployment patched com tolerations.", fg=typer.colors.GREEN)
|
|
124
|
+
|
|
125
|
+
# Patch no ConfigMap para os helper pods
|
|
126
|
+
helper_pod_config = {
|
|
127
|
+
"nodePathMap": [
|
|
128
|
+
{
|
|
129
|
+
"node": "DEFAULT_PATH_FOR_NON_LISTED_NODES",
|
|
130
|
+
"paths": ["/opt/local-path-provisioner"]
|
|
131
|
+
}
|
|
132
|
+
],
|
|
133
|
+
"setupCommand": None,
|
|
134
|
+
"teardownCommand": None,
|
|
135
|
+
"helperPod": {
|
|
136
|
+
"apiVersion": "v1",
|
|
137
|
+
"kind": "Pod",
|
|
138
|
+
"metadata": {},
|
|
139
|
+
"spec": {
|
|
140
|
+
"tolerations": [
|
|
141
|
+
{"key": "node-role.kubernetes.io/control-plane", "operator": "Exists", "effect": "NoSchedule"},
|
|
142
|
+
{"key": "node-role.kubernetes.io/master", "operator": "Exists", "effect": "NoSchedule"}
|
|
143
|
+
],
|
|
144
|
+
"containers": [
|
|
145
|
+
{
|
|
146
|
+
"name": "helper-pod",
|
|
147
|
+
"image": "busybox:stable",
|
|
148
|
+
"imagePullPolicy": "IfNotPresent"
|
|
149
|
+
}
|
|
150
|
+
]
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
config_json_str = json.dumps(helper_pod_config)
|
|
156
|
+
patch_data = json.dumps({"data": {"config.json": config_json_str}})
|
|
157
|
+
|
|
158
|
+
result = run_cmd(
|
|
159
|
+
[
|
|
160
|
+
"kubectl", "-n", "local-path-storage", "patch", "configmap",
|
|
161
|
+
"local-path-config", "--type=merge", "-p", patch_data,
|
|
162
|
+
],
|
|
163
|
+
ctx,
|
|
164
|
+
check=False,
|
|
165
|
+
)
|
|
166
|
+
if result.returncode == 0:
|
|
167
|
+
typer.secho(" ✓ ConfigMap patched para helper pods.", fg=typer.colors.GREEN)
|
|
168
|
+
|
|
169
|
+
# Reinicia o deployment
|
|
170
|
+
run_cmd(
|
|
171
|
+
["kubectl", "-n", "local-path-storage", "rollout", "restart", "deployment/local-path-provisioner"],
|
|
172
|
+
ctx,
|
|
173
|
+
check=False,
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
# Aguarda rollout
|
|
177
|
+
run_cmd(
|
|
178
|
+
[
|
|
179
|
+
"kubectl", "-n", "local-path-storage", "rollout", "status",
|
|
180
|
+
"deployment/local-path-provisioner", "--timeout=60s",
|
|
181
|
+
],
|
|
182
|
+
ctx,
|
|
183
|
+
check=False,
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def _install_local_path_provisioner(ctx: ExecutionContext) -> bool:
|
|
188
|
+
"""Instala local-path-provisioner para usar storage local (NVMe/SSD)."""
|
|
189
|
+
typer.echo("Instalando local-path-provisioner para storage local...")
|
|
190
|
+
|
|
191
|
+
result = run_cmd(
|
|
192
|
+
["kubectl", "apply", "-f", LOCAL_PATH_PROVISIONER_URL],
|
|
193
|
+
ctx,
|
|
194
|
+
check=False,
|
|
195
|
+
)
|
|
196
|
+
if result.returncode != 0:
|
|
197
|
+
typer.secho(" Falha ao instalar local-path-provisioner.", fg=typer.colors.RED)
|
|
198
|
+
return False
|
|
199
|
+
|
|
200
|
+
# Aguarda deployment ficar pronto inicialmente
|
|
201
|
+
typer.echo(" Aguardando local-path-provisioner ficar Ready...")
|
|
202
|
+
run_cmd(
|
|
203
|
+
[
|
|
204
|
+
"kubectl", "-n", "local-path-storage", "rollout", "status",
|
|
205
|
+
"deployment/local-path-provisioner", "--timeout=60s",
|
|
206
|
+
],
|
|
207
|
+
ctx,
|
|
208
|
+
check=False,
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
# Aplica tolerations para control-plane (single-node clusters)
|
|
212
|
+
_patch_local_path_provisioner_tolerations(ctx)
|
|
213
|
+
|
|
214
|
+
typer.secho(" ✓ local-path-provisioner instalado e configurado.", fg=typer.colors.GREEN)
|
|
215
|
+
return True
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def _set_default_storage_class(ctx: ExecutionContext, name: str) -> None:
|
|
219
|
+
"""Define uma StorageClass como default."""
|
|
220
|
+
# Remove default de outras classes primeiro
|
|
221
|
+
existing = _list_storage_classes(ctx)
|
|
222
|
+
for sc in existing:
|
|
223
|
+
if sc != name:
|
|
224
|
+
run_cmd(
|
|
225
|
+
[
|
|
226
|
+
"kubectl", "annotate", "storageclass", sc,
|
|
227
|
+
"storageclass.kubernetes.io/is-default-class-",
|
|
228
|
+
"--overwrite",
|
|
229
|
+
],
|
|
230
|
+
ctx,
|
|
231
|
+
check=False,
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
# Define a nova como default
|
|
235
|
+
run_cmd(
|
|
236
|
+
[
|
|
237
|
+
"kubectl", "annotate", "storageclass", name,
|
|
238
|
+
"storageclass.kubernetes.io/is-default-class=true",
|
|
239
|
+
"--overwrite",
|
|
240
|
+
],
|
|
241
|
+
ctx,
|
|
242
|
+
check=True,
|
|
243
|
+
)
|
|
244
|
+
typer.secho(f" ✓ StorageClass '{name}' definida como default.", fg=typer.colors.GREEN)
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
def _ensure_storage_class(ctx: ExecutionContext) -> str:
|
|
248
|
+
"""Garante que existe uma StorageClass disponivel, instalando local-path se necessario."""
|
|
249
|
+
if ctx.dry_run:
|
|
250
|
+
return "local-path" # Retorna um valor dummy para dry-run
|
|
251
|
+
|
|
252
|
+
default_sc = _get_default_storage_class(ctx)
|
|
253
|
+
available = _list_storage_classes(ctx)
|
|
254
|
+
|
|
255
|
+
# Se ja existir default (qualquer uma), usa ela
|
|
256
|
+
if default_sc:
|
|
257
|
+
typer.echo(f"StorageClass default detectada: {default_sc}")
|
|
258
|
+
# Se for local-path, garante que o provisioner tem tolerations
|
|
259
|
+
if default_sc == "local-path" or "local-path" in available:
|
|
260
|
+
_patch_local_path_provisioner_tolerations(ctx)
|
|
261
|
+
return default_sc
|
|
262
|
+
|
|
263
|
+
# Se local-path estiver disponivel mas nao for default, define como default
|
|
264
|
+
if "local-path" in available:
|
|
265
|
+
typer.echo("StorageClass 'local-path' detectada.")
|
|
266
|
+
_patch_local_path_provisioner_tolerations(ctx)
|
|
267
|
+
_set_default_storage_class(ctx, "local-path")
|
|
268
|
+
return "local-path"
|
|
269
|
+
|
|
270
|
+
# Se houver outras classes disponiveis, pergunta qual usar
|
|
271
|
+
if available:
|
|
272
|
+
typer.echo(f"StorageClasses disponiveis (sem default): {', '.join(available)}")
|
|
273
|
+
choice = typer.prompt(
|
|
274
|
+
f"Qual StorageClass usar? ({'/'.join(available)})",
|
|
275
|
+
default=available[0],
|
|
276
|
+
)
|
|
277
|
+
return choice
|
|
278
|
+
|
|
279
|
+
# Nenhuma StorageClass disponivel - instala local-path automaticamente
|
|
280
|
+
typer.secho(
|
|
281
|
+
"Nenhuma StorageClass encontrada no cluster.",
|
|
282
|
+
fg=typer.colors.YELLOW,
|
|
283
|
+
)
|
|
284
|
+
install = typer.confirm(
|
|
285
|
+
"Instalar local-path-provisioner para usar armazenamento local (NVMe/SSD)?",
|
|
286
|
+
default=True,
|
|
287
|
+
)
|
|
288
|
+
if not install:
|
|
289
|
+
typer.secho(
|
|
290
|
+
"Abortando: Grafana com PVC requer uma StorageClass.",
|
|
291
|
+
fg=typer.colors.RED,
|
|
292
|
+
)
|
|
293
|
+
raise typer.Exit(1)
|
|
294
|
+
|
|
295
|
+
if not _install_local_path_provisioner(ctx):
|
|
296
|
+
raise typer.Exit(1)
|
|
297
|
+
|
|
298
|
+
_set_default_storage_class(ctx, "local-path")
|
|
299
|
+
return "local-path"
|
|
300
|
+
|
|
301
|
+
|
|
24
302
|
def _check_existing_grafana(ctx: ExecutionContext) -> bool:
|
|
25
303
|
"""Verifica se existe instalacao do Grafana."""
|
|
26
304
|
result = run_cmd(
|
|
@@ -101,29 +379,84 @@ def run(ctx: ExecutionContext) -> None:
|
|
|
101
379
|
if cleanup:
|
|
102
380
|
_uninstall_grafana(ctx)
|
|
103
381
|
|
|
382
|
+
# Verifica se existe StorageClass default para sugerir no prompt
|
|
383
|
+
default_sc = _get_default_storage_class(ctx)
|
|
384
|
+
|
|
385
|
+
enable_persistence = typer.confirm(
|
|
386
|
+
"Habilitar PVC para persistencia do Grafana?",
|
|
387
|
+
default=bool(default_sc)
|
|
388
|
+
)
|
|
389
|
+
|
|
390
|
+
# Se habilitou PVC, garante que existe StorageClass disponivel
|
|
391
|
+
if enable_persistence:
|
|
392
|
+
default_sc = _ensure_storage_class(ctx)
|
|
393
|
+
|
|
104
394
|
admin_password = typer.prompt("Senha admin do Grafana", default="admin")
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
395
|
+
|
|
396
|
+
# Ingress público não é recomendado para ferramentas de observabilidade
|
|
397
|
+
enable_ingress = typer.confirm(
|
|
398
|
+
"Habilitar ingress público? (NÃO recomendado - use VPN + port-forward)",
|
|
399
|
+
default=False
|
|
400
|
+
)
|
|
401
|
+
|
|
402
|
+
ingress_host = "grafana.local"
|
|
403
|
+
ingress_class = "traefik"
|
|
404
|
+
tls_secret = "grafana-tls"
|
|
405
|
+
|
|
406
|
+
if enable_ingress:
|
|
407
|
+
typer.secho(
|
|
408
|
+
"\n⚠️ ATENÇÃO: Expor Grafana publicamente é um risco de segurança!",
|
|
409
|
+
fg=typer.colors.YELLOW,
|
|
410
|
+
bold=True,
|
|
411
|
+
)
|
|
412
|
+
typer.secho(
|
|
413
|
+
"Recomendação: Use VPN (raijin vpn) + port-forward para acesso seguro.\n",
|
|
414
|
+
fg=typer.colors.YELLOW,
|
|
415
|
+
)
|
|
416
|
+
ingress_host = typer.prompt("Host para ingress", default="grafana.local")
|
|
417
|
+
ingress_class = typer.prompt("IngressClass", default="traefik")
|
|
418
|
+
tls_secret = typer.prompt("Secret TLS (cert-manager)", default="grafana-tls")
|
|
419
|
+
|
|
420
|
+
persistence_size = "10Gi"
|
|
421
|
+
storage_class = ""
|
|
422
|
+
if enable_persistence:
|
|
423
|
+
storage_class = typer.prompt(
|
|
424
|
+
"StorageClass para PVC",
|
|
425
|
+
default=default_sc or "",
|
|
426
|
+
).strip()
|
|
427
|
+
persistence_size = typer.prompt("Tamanho do storage", default="10Gi")
|
|
109
428
|
|
|
110
429
|
node_name = _detect_node_name(ctx)
|
|
111
430
|
|
|
431
|
+
# Constroi o YAML de persistencia condicionalmente
|
|
432
|
+
persistence_yaml = f"""persistence:
|
|
433
|
+
enabled: {str(enable_persistence).lower()}"""
|
|
434
|
+
|
|
435
|
+
if enable_persistence:
|
|
436
|
+
persistence_yaml += f"""
|
|
437
|
+
size: {persistence_size}"""
|
|
438
|
+
if storage_class:
|
|
439
|
+
persistence_yaml += f"""
|
|
440
|
+
storageClassName: {storage_class}"""
|
|
441
|
+
|
|
112
442
|
values_yaml = f"""adminPassword: {admin_password}
|
|
113
443
|
service:
|
|
114
444
|
type: ClusterIP
|
|
115
445
|
ingress:
|
|
116
|
-
enabled:
|
|
446
|
+
enabled: {str(enable_ingress).lower()}"""
|
|
447
|
+
|
|
448
|
+
if enable_ingress:
|
|
449
|
+
values_yaml += f"""
|
|
117
450
|
ingressClassName: {ingress_class}
|
|
118
451
|
hosts:
|
|
119
452
|
- {ingress_host}
|
|
120
453
|
tls:
|
|
121
454
|
- secretName: {tls_secret}
|
|
122
455
|
hosts:
|
|
123
|
-
- {ingress_host}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
456
|
+
- {ingress_host}"""
|
|
457
|
+
|
|
458
|
+
values_yaml += f"""
|
|
459
|
+
{persistence_yaml}
|
|
127
460
|
tolerations:
|
|
128
461
|
- key: node-role.kubernetes.io/control-plane
|
|
129
462
|
operator: Exists
|
|
@@ -181,7 +514,13 @@ dashboards:
|
|
|
181
514
|
values_path = Path("/tmp/raijin-grafana-values.yaml")
|
|
182
515
|
write_file(values_path, values_yaml, ctx)
|
|
183
516
|
|
|
184
|
-
|
|
517
|
+
kubectl_create_ns("observability", ctx)
|
|
518
|
+
|
|
519
|
+
if not enable_persistence:
|
|
520
|
+
typer.secho(
|
|
521
|
+
"PVC desativado: Grafana usara volume efemero (dados perdidos apos restart).",
|
|
522
|
+
fg=typer.colors.YELLOW,
|
|
523
|
+
)
|
|
185
524
|
|
|
186
525
|
helm_upgrade_install(
|
|
187
526
|
release="grafana",
|
|
@@ -191,15 +530,25 @@ dashboards:
|
|
|
191
530
|
repo_url="https://grafana.github.io/helm-charts",
|
|
192
531
|
ctx=ctx,
|
|
193
532
|
values=[],
|
|
194
|
-
extra_args=["-f", str(values_path)],
|
|
533
|
+
extra_args=["-f", str(values_path), "--wait", "--timeout", "10m", "--atomic"],
|
|
195
534
|
)
|
|
196
535
|
|
|
197
536
|
if not ctx.dry_run:
|
|
198
537
|
_wait_for_grafana_ready(ctx)
|
|
199
538
|
|
|
200
539
|
typer.secho("\n✓ Grafana instalado com sucesso.", fg=typer.colors.GREEN, bold=True)
|
|
201
|
-
|
|
202
|
-
|
|
540
|
+
|
|
541
|
+
if enable_ingress:
|
|
542
|
+
typer.echo(f"\nAcesse: https://{ingress_host}")
|
|
543
|
+
else:
|
|
544
|
+
typer.secho("\n🔒 Acesso Seguro via VPN + Port-Forward:", fg=typer.colors.CYAN, bold=True)
|
|
545
|
+
typer.echo("\n1. Configure VPN (se ainda não tiver):")
|
|
546
|
+
typer.echo(" sudo raijin vpn")
|
|
547
|
+
typer.echo("\n2. Conecte via WireGuard no seu Windows/Mac")
|
|
548
|
+
typer.echo("\n3. Faça port-forward local:")
|
|
549
|
+
typer.echo(" kubectl -n observability port-forward svc/grafana 3000:80")
|
|
550
|
+
typer.echo("\n4. Acesse no navegador:")
|
|
551
|
+
typer.echo(" http://localhost:3000")
|
|
552
|
+
|
|
553
|
+
typer.echo("\nUsuario: admin")
|
|
203
554
|
typer.echo(f"Senha: {admin_password}")
|
|
204
|
-
typer.echo("\nPara port-forward local:")
|
|
205
|
-
typer.echo(" kubectl -n observability port-forward svc/grafana 3000:80")
|