raijin-server 0.2.2__py3-none-any.whl → 0.2.4__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.
- raijin_server/__init__.py +1 -1
- raijin_server/cli.py +77 -0
- raijin_server/healthchecks.py +61 -2
- raijin_server/modules/apokolips_demo.py +40 -4
- raijin_server/modules/cert_manager.py +949 -71
- raijin_server/modules/full_install.py +44 -1
- raijin_server/utils.py +57 -1
- {raijin_server-0.2.2.dist-info → raijin_server-0.2.4.dist-info}/METADATA +31 -1
- {raijin_server-0.2.2.dist-info → raijin_server-0.2.4.dist-info}/RECORD +13 -13
- {raijin_server-0.2.2.dist-info → raijin_server-0.2.4.dist-info}/WHEEL +0 -0
- {raijin_server-0.2.2.dist-info → raijin_server-0.2.4.dist-info}/entry_points.txt +0 -0
- {raijin_server-0.2.2.dist-info → raijin_server-0.2.4.dist-info}/licenses/LICENSE +0 -0
- {raijin_server-0.2.2.dist-info → raijin_server-0.2.4.dist-info}/top_level.txt +0 -0
raijin_server/__init__.py
CHANGED
raijin_server/cli.py
CHANGED
|
@@ -470,6 +470,83 @@ def sanitize(ctx: typer.Context) -> None:
|
|
|
470
470
|
_run_module(ctx, "sanitize")
|
|
471
471
|
|
|
472
472
|
|
|
473
|
+
# ============================================================================
|
|
474
|
+
# Subcomandos Cert-Manager
|
|
475
|
+
# ============================================================================
|
|
476
|
+
cert_app = typer.Typer(help="Comandos para gerenciamento do cert-manager")
|
|
477
|
+
app.add_typer(cert_app, name="cert")
|
|
478
|
+
|
|
479
|
+
|
|
480
|
+
@cert_app.command(name="install")
|
|
481
|
+
def cert_install(ctx: typer.Context) -> None:
|
|
482
|
+
"""Instala cert-manager e configura ClusterIssuer interativamente."""
|
|
483
|
+
_run_module(ctx, "cert_manager")
|
|
484
|
+
|
|
485
|
+
|
|
486
|
+
@cert_app.command(name="status")
|
|
487
|
+
def cert_status(ctx: typer.Context) -> None:
|
|
488
|
+
"""Exibe status detalhado do cert-manager, pods, webhook e certificados."""
|
|
489
|
+
exec_ctx = ctx.obj or ExecutionContext()
|
|
490
|
+
cert_manager.status(exec_ctx)
|
|
491
|
+
|
|
492
|
+
|
|
493
|
+
@cert_app.command(name="diagnose")
|
|
494
|
+
def cert_diagnose(ctx: typer.Context) -> None:
|
|
495
|
+
"""Executa diagnóstico completo para troubleshooting do cert-manager."""
|
|
496
|
+
exec_ctx = ctx.obj or ExecutionContext()
|
|
497
|
+
cert_manager.diagnose(exec_ctx)
|
|
498
|
+
|
|
499
|
+
|
|
500
|
+
@cert_app.command(name="list-certs")
|
|
501
|
+
def cert_list(ctx: typer.Context) -> None:
|
|
502
|
+
"""Lista todos os certificados no cluster."""
|
|
503
|
+
import subprocess
|
|
504
|
+
|
|
505
|
+
typer.secho("\n📜 Certificados no Cluster", fg=typer.colors.CYAN, bold=True)
|
|
506
|
+
try:
|
|
507
|
+
result = subprocess.run(
|
|
508
|
+
[
|
|
509
|
+
"kubectl", "get", "certificates", "-A",
|
|
510
|
+
"-o", "wide"
|
|
511
|
+
],
|
|
512
|
+
capture_output=False,
|
|
513
|
+
timeout=15,
|
|
514
|
+
)
|
|
515
|
+
if result.returncode != 0:
|
|
516
|
+
typer.secho("Nenhum certificado encontrado ou erro ao listar.", fg=typer.colors.YELLOW)
|
|
517
|
+
except Exception as e:
|
|
518
|
+
typer.secho(f"Erro: {e}", fg=typer.colors.RED)
|
|
519
|
+
|
|
520
|
+
|
|
521
|
+
@cert_app.command(name="list-issuers")
|
|
522
|
+
def cert_list_issuers(ctx: typer.Context) -> None:
|
|
523
|
+
"""Lista todos os ClusterIssuers e Issuers."""
|
|
524
|
+
import subprocess
|
|
525
|
+
|
|
526
|
+
typer.secho("\n🔐 ClusterIssuers", fg=typer.colors.CYAN, bold=True)
|
|
527
|
+
try:
|
|
528
|
+
subprocess.run(
|
|
529
|
+
["kubectl", "get", "clusterissuers", "-o", "wide"],
|
|
530
|
+
timeout=15,
|
|
531
|
+
)
|
|
532
|
+
except Exception:
|
|
533
|
+
pass
|
|
534
|
+
|
|
535
|
+
typer.secho("\n🔐 Issuers (por namespace)", fg=typer.colors.CYAN, bold=True)
|
|
536
|
+
try:
|
|
537
|
+
subprocess.run(
|
|
538
|
+
["kubectl", "get", "issuers", "-A", "-o", "wide"],
|
|
539
|
+
timeout=15,
|
|
540
|
+
)
|
|
541
|
+
except Exception:
|
|
542
|
+
pass
|
|
543
|
+
|
|
544
|
+
|
|
545
|
+
# ============================================================================
|
|
546
|
+
# Comandos Existentes
|
|
547
|
+
# ============================================================================
|
|
548
|
+
|
|
549
|
+
|
|
473
550
|
@app.command(name="bootstrap")
|
|
474
551
|
def bootstrap_cmd(ctx: typer.Context) -> None:
|
|
475
552
|
"""Instala todas as ferramentas necessarias: helm, kubectl, istioctl, velero, containerd."""
|
raijin_server/healthchecks.py
CHANGED
|
@@ -264,8 +264,67 @@ def verify_helm_chart(release: str, namespace: str, ctx: ExecutionContext) -> bo
|
|
|
264
264
|
|
|
265
265
|
|
|
266
266
|
def verify_cert_manager(ctx: ExecutionContext) -> bool:
|
|
267
|
-
"""Health check para cert-manager."""
|
|
268
|
-
|
|
267
|
+
"""Health check completo para cert-manager."""
|
|
268
|
+
logger.info("Verificando health check: cert-manager")
|
|
269
|
+
typer.secho("\n=== Health Check: Cert-Manager ===", fg=typer.colors.CYAN)
|
|
270
|
+
|
|
271
|
+
all_ok = True
|
|
272
|
+
|
|
273
|
+
# Verifica release Helm
|
|
274
|
+
ok, status = check_helm_release("cert-manager", CERT_NS, ctx)
|
|
275
|
+
if ok:
|
|
276
|
+
typer.secho(f" ✓ Release cert-manager: {status}", fg=typer.colors.GREEN)
|
|
277
|
+
else:
|
|
278
|
+
typer.secho(f" ✗ Release cert-manager: {status}", fg=typer.colors.RED)
|
|
279
|
+
return False
|
|
280
|
+
|
|
281
|
+
# Verifica pods
|
|
282
|
+
if not check_k8s_pods_in_namespace(CERT_NS, ctx, timeout=180):
|
|
283
|
+
all_ok = False
|
|
284
|
+
|
|
285
|
+
# Verifica CRDs
|
|
286
|
+
if not ctx.dry_run:
|
|
287
|
+
try:
|
|
288
|
+
import subprocess
|
|
289
|
+
result = subprocess.run(
|
|
290
|
+
["kubectl", "get", "crd", "certificates.cert-manager.io"],
|
|
291
|
+
capture_output=True,
|
|
292
|
+
timeout=10,
|
|
293
|
+
)
|
|
294
|
+
if result.returncode == 0:
|
|
295
|
+
typer.secho(" ✓ CRDs instalados", fg=typer.colors.GREEN)
|
|
296
|
+
else:
|
|
297
|
+
typer.secho(" ✗ CRDs não encontrados", fg=typer.colors.RED)
|
|
298
|
+
all_ok = False
|
|
299
|
+
except Exception as e:
|
|
300
|
+
typer.secho(f" ✗ Erro ao verificar CRDs: {e}", fg=typer.colors.RED)
|
|
301
|
+
all_ok = False
|
|
302
|
+
|
|
303
|
+
# Verifica webhook ready
|
|
304
|
+
if not ctx.dry_run:
|
|
305
|
+
try:
|
|
306
|
+
import subprocess
|
|
307
|
+
result = subprocess.run(
|
|
308
|
+
[
|
|
309
|
+
"kubectl", "get", "deployment", "cert-manager-webhook",
|
|
310
|
+
"-n", CERT_NS,
|
|
311
|
+
"-o", "jsonpath={.status.readyReplicas}"
|
|
312
|
+
],
|
|
313
|
+
capture_output=True,
|
|
314
|
+
text=True,
|
|
315
|
+
timeout=10,
|
|
316
|
+
)
|
|
317
|
+
ready = result.returncode == 0 and result.stdout.strip() and int(result.stdout.strip()) >= 1
|
|
318
|
+
if ready:
|
|
319
|
+
typer.secho(" ✓ Webhook pronto", fg=typer.colors.GREEN)
|
|
320
|
+
else:
|
|
321
|
+
typer.secho(" ✗ Webhook não está pronto", fg=typer.colors.RED)
|
|
322
|
+
all_ok = False
|
|
323
|
+
except Exception as e:
|
|
324
|
+
typer.secho(f" ✗ Erro ao verificar webhook: {e}", fg=typer.colors.RED)
|
|
325
|
+
all_ok = False
|
|
326
|
+
|
|
327
|
+
return all_ok
|
|
269
328
|
|
|
270
329
|
|
|
271
330
|
def verify_secrets(ctx: ExecutionContext) -> bool:
|
|
@@ -244,7 +244,15 @@ def _resolve_tls_secret() -> str | None:
|
|
|
244
244
|
return secret.strip() or None
|
|
245
245
|
|
|
246
246
|
|
|
247
|
-
def
|
|
247
|
+
def _resolve_ip_access() -> bool:
|
|
248
|
+
"""Pergunta se deseja acesso via IP direto (para testes)."""
|
|
249
|
+
env_ip = os.environ.get("APOKOLIPS_IP_ACCESS")
|
|
250
|
+
if env_ip:
|
|
251
|
+
return env_ip.strip().lower() in ("1", "true", "yes")
|
|
252
|
+
return typer.confirm("Habilitar acesso via IP direto? (apenas para testes)", default=True)
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
def _build_manifest(host: str, tls_secret: str | None, ip_access: bool = False) -> str:
|
|
248
256
|
html_block = indent(HTML_TEMPLATE.strip("\n"), " " * 4)
|
|
249
257
|
tls_block = ""
|
|
250
258
|
if tls_secret:
|
|
@@ -254,6 +262,20 @@ def _build_manifest(host: str, tls_secret: str | None) -> str:
|
|
|
254
262
|
f" - {host}\n"
|
|
255
263
|
f" secretName: {tls_secret}\n"
|
|
256
264
|
)
|
|
265
|
+
|
|
266
|
+
# Regra adicional para acesso via IP (sem host)
|
|
267
|
+
ip_rule = ""
|
|
268
|
+
if ip_access:
|
|
269
|
+
ip_rule = """
|
|
270
|
+
- http:
|
|
271
|
+
paths:
|
|
272
|
+
- path: /
|
|
273
|
+
pathType: Prefix
|
|
274
|
+
backend:
|
|
275
|
+
service:
|
|
276
|
+
name: apokolips-demo
|
|
277
|
+
port:
|
|
278
|
+
number: 80"""
|
|
257
279
|
|
|
258
280
|
template = """\
|
|
259
281
|
apiVersion: v1
|
|
@@ -340,12 +362,13 @@ spec:
|
|
|
340
362
|
service:
|
|
341
363
|
name: apokolips-demo
|
|
342
364
|
port:
|
|
343
|
-
number:
|
|
365
|
+
number: 80__IP_RULE__
|
|
344
366
|
__TLS__
|
|
345
367
|
"""
|
|
346
368
|
|
|
347
369
|
manifest = template.format(namespace=NAMESPACE, host=host)
|
|
348
370
|
manifest = manifest.replace("__HTML__", html_block)
|
|
371
|
+
manifest = manifest.replace("__IP_RULE__", ip_rule)
|
|
349
372
|
manifest = manifest.replace("__TLS__", tls_block.rstrip())
|
|
350
373
|
return f"{manifest.strip()}\n"
|
|
351
374
|
|
|
@@ -354,7 +377,8 @@ def run(ctx: ExecutionContext) -> None:
|
|
|
354
377
|
ensure_tool("kubectl", ctx, install_hint="Instale kubectl para aplicar o manifesto do site.")
|
|
355
378
|
host = _resolve_host()
|
|
356
379
|
tls_secret = _resolve_tls_secret()
|
|
357
|
-
|
|
380
|
+
ip_access = _resolve_ip_access()
|
|
381
|
+
manifest = _build_manifest(host, tls_secret, ip_access)
|
|
358
382
|
|
|
359
383
|
typer.echo("Gerando manifesto Apokolips...")
|
|
360
384
|
write_file(TMP_MANIFEST, manifest, ctx)
|
|
@@ -369,10 +393,22 @@ def run(ctx: ExecutionContext) -> None:
|
|
|
369
393
|
typer.echo(f" • Host: {host}")
|
|
370
394
|
if tls_secret:
|
|
371
395
|
typer.echo(f" • Secret TLS: {tls_secret}")
|
|
396
|
+
if ip_access:
|
|
397
|
+
typer.secho(" • Acesso via IP: HABILITADO (apenas para testes)", fg=typer.colors.YELLOW)
|
|
398
|
+
|
|
372
399
|
typer.echo("\nTestes sugeridos:")
|
|
373
|
-
|
|
400
|
+
if ip_access:
|
|
401
|
+
typer.echo(" # Acesso direto via IP (teste):")
|
|
402
|
+
typer.echo(" curl http://<IP_DO_SERVIDOR>/")
|
|
403
|
+
typer.echo("")
|
|
404
|
+
typer.echo(f" # Acesso via DNS (produção):")
|
|
405
|
+
typer.echo(f" curl -H 'Host: {host}' http://<IP_DO_LOAD_BALANCER>/")
|
|
374
406
|
typer.echo(f" kubectl -n {NAMESPACE} get ingress {NAMESPACE}")
|
|
375
407
|
typer.echo(f" kubectl -n {NAMESPACE} get pods")
|
|
376
408
|
|
|
409
|
+
if ip_access:
|
|
410
|
+
typer.secho("\n⚠️ Lembre-se de desabilitar o acesso via IP após configurar o DNS!", fg=typer.colors.YELLOW)
|
|
411
|
+
typer.echo(" Rode novamente com APOKOLIPS_IP_ACCESS=false ou responda 'não' na pergunta.")
|
|
412
|
+
|
|
377
413
|
typer.echo("\nPara remover:")
|
|
378
414
|
typer.echo(f" kubectl delete namespace {NAMESPACE}")
|