raijin-server 0.2.5__py3-none-any.whl → 0.2.7__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 +147 -2
- raijin_server/modules/calico.py +68 -3
- raijin_server/modules/cert_manager.py +363 -29
- raijin_server/modules/full_install.py +228 -1
- raijin_server/modules/prometheus.py +90 -5
- raijin_server/scripts/checklist.sh +0 -0
- raijin_server/scripts/install.sh +3 -2
- raijin_server/scripts/log_size_metric.sh +17 -13
- raijin_server/scripts/pre-deploy-check.sh +2 -1
- raijin_server/utils.py +207 -23
- {raijin_server-0.2.5.dist-info → raijin_server-0.2.7.dist-info}/METADATA +96 -79
- {raijin_server-0.2.5.dist-info → raijin_server-0.2.7.dist-info}/RECORD +16 -16
- {raijin_server-0.2.5.dist-info → raijin_server-0.2.7.dist-info}/WHEEL +0 -0
- {raijin_server-0.2.5.dist-info → raijin_server-0.2.7.dist-info}/entry_points.txt +0 -0
- {raijin_server-0.2.5.dist-info → raijin_server-0.2.7.dist-info}/licenses/LICENSE +0 -0
- {raijin_server-0.2.5.dist-info → raijin_server-0.2.7.dist-info}/top_level.txt +0 -0
|
@@ -18,6 +18,8 @@ from enum import Enum
|
|
|
18
18
|
from pathlib import Path
|
|
19
19
|
from typing import Callable, Optional, List
|
|
20
20
|
|
|
21
|
+
import os
|
|
22
|
+
|
|
21
23
|
import typer
|
|
22
24
|
|
|
23
25
|
from raijin_server.utils import (
|
|
@@ -34,11 +36,14 @@ CHART_REPO = "https://charts.jetstack.io"
|
|
|
34
36
|
CHART_NAME = "cert-manager"
|
|
35
37
|
NAMESPACE = "cert-manager"
|
|
36
38
|
MANIFEST_PATH = Path("/tmp/raijin-cert-manager-issuer.yaml")
|
|
39
|
+
HELM_DATA_DIR = Path("/tmp/raijin-helm")
|
|
40
|
+
HELM_REPO_CONFIG = HELM_DATA_DIR / "repositories.yaml"
|
|
41
|
+
HELM_REPO_CACHE = HELM_DATA_DIR / "cache"
|
|
37
42
|
|
|
38
|
-
# Timeouts
|
|
39
|
-
WEBHOOK_READY_TIMEOUT =
|
|
40
|
-
POD_READY_TIMEOUT =
|
|
41
|
-
CRD_READY_TIMEOUT =
|
|
43
|
+
# Timeouts enxutos (falha rápida em redes rápidas)
|
|
44
|
+
WEBHOOK_READY_TIMEOUT = 240 # 4 minutos
|
|
45
|
+
POD_READY_TIMEOUT = 180 # 3 minutos
|
|
46
|
+
CRD_READY_TIMEOUT = 120 # 2 minutos
|
|
42
47
|
|
|
43
48
|
|
|
44
49
|
class DNSProvider(str, Enum):
|
|
@@ -82,6 +87,17 @@ def _get_acme_server(staging: bool) -> str:
|
|
|
82
87
|
return "https://acme-v02.api.letsencrypt.org/directory"
|
|
83
88
|
|
|
84
89
|
|
|
90
|
+
def _helm_env() -> dict:
|
|
91
|
+
"""Garante diretórios de cache/config do Helm isolados em /tmp para evitar erros de permissão."""
|
|
92
|
+
HELM_DATA_DIR.mkdir(parents=True, exist_ok=True)
|
|
93
|
+
HELM_REPO_CACHE.mkdir(parents=True, exist_ok=True)
|
|
94
|
+
return {
|
|
95
|
+
**os.environ,
|
|
96
|
+
"HELM_REPOSITORY_CONFIG": str(HELM_REPO_CONFIG),
|
|
97
|
+
"HELM_REPOSITORY_CACHE": str(HELM_REPO_CACHE),
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
|
|
85
101
|
# =============================================================================
|
|
86
102
|
# Builders de Manifests YAML
|
|
87
103
|
# =============================================================================
|
|
@@ -432,37 +448,355 @@ def _wait_for_webhook_ready(ctx: ExecutionContext, timeout: int = WEBHOOK_READY_
|
|
|
432
448
|
# Instalação e Configuração
|
|
433
449
|
# =============================================================================
|
|
434
450
|
|
|
435
|
-
def
|
|
436
|
-
"""
|
|
437
|
-
|
|
451
|
+
def _test_helm_repo_connectivity(ctx: ExecutionContext) -> bool:
|
|
452
|
+
"""Testa conectividade com o repositório Helm antes de instalar."""
|
|
453
|
+
if ctx.dry_run:
|
|
454
|
+
return True
|
|
455
|
+
|
|
456
|
+
logger.info("Testando conectividade com charts.jetstack.io")
|
|
457
|
+
typer.echo(" [1/5] Testando conectividade com charts.jetstack.io...")
|
|
438
458
|
|
|
439
459
|
try:
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
+
start = time.time()
|
|
461
|
+
result = subprocess.run(
|
|
462
|
+
["curl", "-sI", "--connect-timeout", "15", f"{CHART_REPO}/index.yaml"],
|
|
463
|
+
capture_output=True,
|
|
464
|
+
text=True,
|
|
465
|
+
timeout=20,
|
|
466
|
+
)
|
|
467
|
+
elapsed = time.time() - start
|
|
468
|
+
|
|
469
|
+
if result.returncode == 0 and "200" in result.stdout:
|
|
470
|
+
logger.info(f"Repositório Helm acessível em {elapsed:.2f}s")
|
|
471
|
+
typer.secho(f" ✓ Repositório Helm acessível ({elapsed:.2f}s)", fg=typer.colors.GREEN)
|
|
472
|
+
return True
|
|
473
|
+
else:
|
|
474
|
+
logger.error(f"Repositório retornou erro: {result.stdout[:200]}")
|
|
475
|
+
typer.secho(f" ✗ Repositório retornou erro: {result.stdout[:100]}", fg=typer.colors.RED)
|
|
476
|
+
return False
|
|
477
|
+
except subprocess.TimeoutExpired:
|
|
478
|
+
logger.error("Timeout ao conectar com charts.jetstack.io")
|
|
479
|
+
typer.secho(" ✗ Timeout ao conectar com charts.jetstack.io (>15s)", fg=typer.colors.RED)
|
|
480
|
+
return False
|
|
481
|
+
except Exception as e:
|
|
482
|
+
logger.error(f"Erro de conectividade: {e}")
|
|
483
|
+
typer.secho(f" ✗ Erro de conectividade: {e}", fg=typer.colors.RED)
|
|
484
|
+
return False
|
|
485
|
+
|
|
486
|
+
|
|
487
|
+
def _test_image_registry_connectivity(ctx: ExecutionContext) -> bool:
|
|
488
|
+
"""Testa conectividade com o registry de imagens."""
|
|
489
|
+
if ctx.dry_run:
|
|
490
|
+
return True
|
|
491
|
+
|
|
492
|
+
logger.info("Testando conectividade com quay.io (registry de imagens)")
|
|
493
|
+
typer.echo(" [2/5] Testando conectividade com quay.io (registry de imagens)...")
|
|
494
|
+
|
|
495
|
+
try:
|
|
496
|
+
start = time.time()
|
|
497
|
+
result = subprocess.run(
|
|
498
|
+
["curl", "-sI", "--connect-timeout", "15", "https://quay.io/v2/"],
|
|
499
|
+
capture_output=True,
|
|
500
|
+
text=True,
|
|
501
|
+
timeout=20,
|
|
502
|
+
)
|
|
503
|
+
elapsed = time.time() - start
|
|
504
|
+
|
|
505
|
+
# quay.io retorna 401 para /v2/ sem auth, mas isso significa que está acessível
|
|
506
|
+
if result.returncode == 0 and ("200" in result.stdout or "401" in result.stdout):
|
|
507
|
+
logger.info(f"Registry quay.io acessível em {elapsed:.2f}s")
|
|
508
|
+
typer.secho(f" ✓ Registry quay.io acessível ({elapsed:.2f}s)", fg=typer.colors.GREEN)
|
|
509
|
+
return True
|
|
510
|
+
else:
|
|
511
|
+
logger.warning(f"Registry pode estar inacessível: {result.stdout[:100]}")
|
|
512
|
+
typer.secho(f" ⚠ Registry pode estar inacessível", fg=typer.colors.YELLOW)
|
|
513
|
+
return True # Não bloqueia, apenas avisa
|
|
514
|
+
except Exception as e:
|
|
515
|
+
logger.warning(f"Não foi possível testar registry: {e}")
|
|
516
|
+
typer.secho(f" ⚠ Não foi possível testar registry: {e}", fg=typer.colors.YELLOW)
|
|
517
|
+
return True # Não bloqueia
|
|
518
|
+
|
|
519
|
+
|
|
520
|
+
def _add_helm_repo(ctx: ExecutionContext) -> bool:
|
|
521
|
+
"""Adiciona e atualiza o repositório Helm."""
|
|
522
|
+
if ctx.dry_run:
|
|
523
|
+
typer.echo(" [3/5] [dry-run] Adicionando repositório Helm jetstack...")
|
|
524
|
+
return True
|
|
525
|
+
|
|
526
|
+
logger.info("Adicionando repositório Helm jetstack")
|
|
527
|
+
typer.echo(" [3/5] Adicionando repositório Helm jetstack...")
|
|
528
|
+
|
|
529
|
+
try:
|
|
530
|
+
start = time.time()
|
|
531
|
+
|
|
532
|
+
# Adiciona repo
|
|
533
|
+
result = subprocess.run(
|
|
534
|
+
["helm", "repo", "add", "jetstack", CHART_REPO, "--force-update"],
|
|
535
|
+
capture_output=True,
|
|
536
|
+
text=True,
|
|
537
|
+
timeout=60,
|
|
538
|
+
env=_helm_env(),
|
|
539
|
+
)
|
|
540
|
+
|
|
541
|
+
if result.returncode != 0:
|
|
542
|
+
logger.error(f"Falha ao adicionar repo: {result.stderr}")
|
|
543
|
+
typer.secho(f" ✗ Falha ao adicionar repo: {result.stderr[:100]}", fg=typer.colors.RED)
|
|
544
|
+
return False
|
|
545
|
+
|
|
546
|
+
elapsed_add = time.time() - start
|
|
547
|
+
logger.info(f"Repo adicionado em {elapsed_add:.2f}s")
|
|
548
|
+
typer.echo(f" Repo adicionado ({elapsed_add:.2f}s)")
|
|
549
|
+
|
|
550
|
+
# Atualiza repo
|
|
551
|
+
typer.echo(" Atualizando índice do repositório...")
|
|
552
|
+
start = time.time()
|
|
553
|
+
|
|
554
|
+
result = subprocess.run(
|
|
555
|
+
["helm", "repo", "update", "jetstack"],
|
|
556
|
+
capture_output=True,
|
|
557
|
+
text=True,
|
|
558
|
+
timeout=120,
|
|
559
|
+
env=_helm_env(),
|
|
560
|
+
)
|
|
561
|
+
|
|
562
|
+
elapsed_update = time.time() - start
|
|
563
|
+
|
|
564
|
+
if result.returncode != 0:
|
|
565
|
+
logger.error(f"Falha ao atualizar repo: {result.stderr}")
|
|
566
|
+
typer.secho(f" ✗ Falha ao atualizar repo: {result.stderr[:100]}", fg=typer.colors.RED)
|
|
567
|
+
return False
|
|
568
|
+
|
|
569
|
+
logger.info(f"Repo atualizado em {elapsed_update:.2f}s")
|
|
570
|
+
typer.secho(f" ✓ Repositório configurado ({elapsed_add + elapsed_update:.2f}s total)", fg=typer.colors.GREEN)
|
|
571
|
+
return True
|
|
572
|
+
|
|
573
|
+
except subprocess.TimeoutExpired:
|
|
574
|
+
logger.error("Timeout ao configurar repositório Helm")
|
|
575
|
+
typer.secho(" ✗ Timeout ao configurar repositório (>60s)", fg=typer.colors.RED)
|
|
576
|
+
return False
|
|
577
|
+
except Exception as e:
|
|
578
|
+
logger.error(f"Erro ao configurar repo: {e}")
|
|
579
|
+
typer.secho(f" ✗ Erro: {e}", fg=typer.colors.RED)
|
|
580
|
+
return False
|
|
581
|
+
|
|
582
|
+
|
|
583
|
+
def _run_helm_install(ctx: ExecutionContext, attempt: int = 1) -> bool:
|
|
584
|
+
"""Executa o helm upgrade --install, com uma tentativa de retry para repo/config."""
|
|
585
|
+
if ctx.dry_run:
|
|
586
|
+
typer.echo(" [4/5] [dry-run] Executando helm upgrade --install...")
|
|
587
|
+
return True
|
|
588
|
+
|
|
589
|
+
logger.info("Executando helm upgrade --install cert-manager")
|
|
590
|
+
typer.echo(" [4/5] Executando helm upgrade --install cert-manager...")
|
|
591
|
+
typer.echo(" (isso pode levar vários minutos)")
|
|
592
|
+
|
|
593
|
+
cmd = [
|
|
594
|
+
"helm", "upgrade", "--install", "cert-manager", "jetstack/cert-manager",
|
|
595
|
+
"--repo", CHART_REPO,
|
|
596
|
+
"-n", NAMESPACE,
|
|
597
|
+
"--create-namespace",
|
|
598
|
+
"--set", "installCRDs=true",
|
|
599
|
+
"--set", "webhook.timeoutSeconds=30",
|
|
600
|
+
"--set", "startupapicheck.timeout=5m",
|
|
601
|
+
"--set", "startupapicheck.enabled=true",
|
|
602
|
+
"--set", "webhook.replicaCount=1",
|
|
603
|
+
"--set", "cainjector.replicaCount=1",
|
|
604
|
+
"--wait",
|
|
605
|
+
"--timeout", "15m",
|
|
606
|
+
"--debug", # Mais logs
|
|
607
|
+
]
|
|
608
|
+
|
|
609
|
+
logger.info(f"Comando: {' '.join(cmd)}")
|
|
610
|
+
|
|
611
|
+
try:
|
|
612
|
+
start = time.time()
|
|
613
|
+
|
|
614
|
+
# Executa com output em tempo real para ver progresso
|
|
615
|
+
process = subprocess.Popen(
|
|
616
|
+
cmd,
|
|
617
|
+
stdout=subprocess.PIPE,
|
|
618
|
+
stderr=subprocess.STDOUT,
|
|
619
|
+
text=True,
|
|
620
|
+
env=_helm_env(),
|
|
460
621
|
)
|
|
622
|
+
|
|
623
|
+
output_lines = []
|
|
624
|
+
last_log_time = time.time()
|
|
625
|
+
|
|
626
|
+
while True:
|
|
627
|
+
line = process.stdout.readline()
|
|
628
|
+
if not line and process.poll() is not None:
|
|
629
|
+
break
|
|
630
|
+
if line:
|
|
631
|
+
output_lines.append(line)
|
|
632
|
+
logger.debug(line.strip())
|
|
633
|
+
|
|
634
|
+
# Mostra progresso a cada 30s
|
|
635
|
+
if time.time() - last_log_time > 30:
|
|
636
|
+
elapsed = int(time.time() - start)
|
|
637
|
+
typer.echo(f" ... ainda instalando ({elapsed}s)")
|
|
638
|
+
last_log_time = time.time()
|
|
639
|
+
|
|
640
|
+
elapsed = time.time() - start
|
|
641
|
+
return_code = process.poll()
|
|
642
|
+
|
|
643
|
+
if return_code == 0:
|
|
644
|
+
logger.info(f"Helm install concluído em {elapsed:.2f}s")
|
|
645
|
+
typer.secho(f" ✓ Helm install concluído ({elapsed:.2f}s)", fg=typer.colors.GREEN)
|
|
646
|
+
return True
|
|
647
|
+
else:
|
|
648
|
+
output = "".join(output_lines[-20:]) # Últimas 20 linhas
|
|
649
|
+
logger.error(f"Helm install falhou (código {return_code}): {output}")
|
|
650
|
+
typer.secho(f" ✗ Helm install falhou (código {return_code})", fg=typer.colors.RED)
|
|
651
|
+
|
|
652
|
+
needs_repo_retry = "repo jetstack not found" in output.lower() or "repositories.yaml" in output.lower()
|
|
653
|
+
if needs_repo_retry and attempt == 1:
|
|
654
|
+
typer.echo(" → Reconfigurando repositório Helm e tentando novamente...")
|
|
655
|
+
if _add_helm_repo(ctx):
|
|
656
|
+
return _run_helm_install(ctx, attempt=2)
|
|
657
|
+
|
|
658
|
+
# Mostra as últimas linhas do erro
|
|
659
|
+
typer.echo("\n Últimas linhas do log:")
|
|
660
|
+
for line in output_lines[-10:]:
|
|
661
|
+
typer.echo(f" {line.strip()}")
|
|
662
|
+
|
|
663
|
+
return False
|
|
664
|
+
|
|
665
|
+
except Exception as e:
|
|
666
|
+
logger.error(f"Erro durante helm install: {e}")
|
|
667
|
+
typer.secho(f" ✗ Erro: {e}", fg=typer.colors.RED)
|
|
668
|
+
return False
|
|
669
|
+
|
|
670
|
+
|
|
671
|
+
def _verify_installation(ctx: ExecutionContext) -> bool:
|
|
672
|
+
"""Verifica se os pods estão rodando."""
|
|
673
|
+
if ctx.dry_run:
|
|
674
|
+
typer.echo(" [5/5] [dry-run] Verificando instalação...")
|
|
461
675
|
return True
|
|
676
|
+
|
|
677
|
+
logger.info("Verificando pods do cert-manager")
|
|
678
|
+
typer.echo(" [5/5] Verificando pods do cert-manager...")
|
|
679
|
+
|
|
680
|
+
try:
|
|
681
|
+
result = subprocess.run(
|
|
682
|
+
["kubectl", "get", "pods", "-n", NAMESPACE, "-o", "wide"],
|
|
683
|
+
capture_output=True,
|
|
684
|
+
text=True,
|
|
685
|
+
timeout=30,
|
|
686
|
+
)
|
|
687
|
+
|
|
688
|
+
if result.returncode == 0:
|
|
689
|
+
typer.echo(f"\n{result.stdout}")
|
|
690
|
+
|
|
691
|
+
# Verifica se todos estão Running
|
|
692
|
+
if "Running" in result.stdout and "0/1" not in result.stdout:
|
|
693
|
+
logger.info("Todos os pods estão Running")
|
|
694
|
+
typer.secho(" ✓ Todos os pods estão Running", fg=typer.colors.GREEN)
|
|
695
|
+
return True
|
|
696
|
+
else:
|
|
697
|
+
logger.warning("Alguns pods podem não estar prontos")
|
|
698
|
+
typer.secho(" ⚠ Alguns pods podem não estar prontos", fg=typer.colors.YELLOW)
|
|
699
|
+
return True # Não falha, o wait_for_webhook vai verificar
|
|
700
|
+
else:
|
|
701
|
+
logger.error(f"Erro ao verificar pods: {result.stderr}")
|
|
702
|
+
return False
|
|
703
|
+
|
|
462
704
|
except Exception as e:
|
|
463
|
-
|
|
464
|
-
|
|
705
|
+
logger.error(f"Erro ao verificar pods: {e}")
|
|
706
|
+
return False
|
|
707
|
+
|
|
708
|
+
|
|
709
|
+
def _install_cert_manager_helm(ctx: ExecutionContext) -> bool:
|
|
710
|
+
"""Instala cert-manager via Helm com logs detalhados."""
|
|
711
|
+
typer.secho("\n📦 Instalando cert-manager via Helm...", fg=typer.colors.CYAN, bold=True)
|
|
712
|
+
logger.info("Iniciando instalação do cert-manager")
|
|
713
|
+
|
|
714
|
+
start_total = time.time()
|
|
715
|
+
|
|
716
|
+
# Etapa 1: Testa conectividade com repo Helm
|
|
717
|
+
if not _test_helm_repo_connectivity(ctx):
|
|
718
|
+
typer.secho(
|
|
719
|
+
"\n⚠ Problema de conectividade com o repositório Helm.",
|
|
720
|
+
fg=typer.colors.YELLOW,
|
|
721
|
+
)
|
|
722
|
+
typer.echo(" Teste manual: curl -sI https://charts.jetstack.io/index.yaml")
|
|
723
|
+
if not typer.confirm("Tentar instalar mesmo assim?", default=False):
|
|
724
|
+
return False
|
|
725
|
+
|
|
726
|
+
# Etapa 2: Testa conectividade com registry de imagens
|
|
727
|
+
_test_image_registry_connectivity(ctx)
|
|
728
|
+
|
|
729
|
+
# Etapa 3: Adiciona e atualiza repo Helm
|
|
730
|
+
if not _add_helm_repo(ctx):
|
|
731
|
+
return False
|
|
732
|
+
|
|
733
|
+
# Etapa 4: Executa helm install
|
|
734
|
+
if not _run_helm_install(ctx):
|
|
735
|
+
_show_diagnostic_info(ctx)
|
|
465
736
|
return False
|
|
737
|
+
|
|
738
|
+
# Etapa 5: Verifica instalação
|
|
739
|
+
_verify_installation(ctx)
|
|
740
|
+
|
|
741
|
+
elapsed_total = time.time() - start_total
|
|
742
|
+
logger.info(f"Instalação do cert-manager concluída em {elapsed_total:.2f}s")
|
|
743
|
+
typer.secho(f"\n✓ Instalação concluída em {elapsed_total:.2f}s", fg=typer.colors.GREEN)
|
|
744
|
+
|
|
745
|
+
return True
|
|
746
|
+
|
|
747
|
+
|
|
748
|
+
def _show_diagnostic_info(ctx: ExecutionContext) -> None:
|
|
749
|
+
"""Mostra informações de diagnóstico quando falha."""
|
|
750
|
+
if ctx.dry_run:
|
|
751
|
+
return
|
|
752
|
+
|
|
753
|
+
typer.secho("\n🔍 Informações de diagnóstico:", fg=typer.colors.YELLOW, bold=True)
|
|
754
|
+
|
|
755
|
+
# Pods
|
|
756
|
+
typer.echo("\n Pods:")
|
|
757
|
+
try:
|
|
758
|
+
result = subprocess.run(
|
|
759
|
+
["kubectl", "get", "pods", "-n", NAMESPACE, "-o", "wide"],
|
|
760
|
+
capture_output=True,
|
|
761
|
+
text=True,
|
|
762
|
+
timeout=15,
|
|
763
|
+
)
|
|
764
|
+
if result.stdout:
|
|
765
|
+
for line in result.stdout.strip().split("\n"):
|
|
766
|
+
typer.echo(f" {line}")
|
|
767
|
+
except Exception:
|
|
768
|
+
typer.echo(" (não foi possível obter pods)")
|
|
769
|
+
|
|
770
|
+
# Eventos recentes
|
|
771
|
+
typer.echo("\n Eventos recentes:")
|
|
772
|
+
try:
|
|
773
|
+
result = subprocess.run(
|
|
774
|
+
["kubectl", "get", "events", "-n", NAMESPACE, "--sort-by=.lastTimestamp"],
|
|
775
|
+
capture_output=True,
|
|
776
|
+
text=True,
|
|
777
|
+
timeout=15,
|
|
778
|
+
)
|
|
779
|
+
if result.stdout:
|
|
780
|
+
lines = result.stdout.strip().split("\n")
|
|
781
|
+
for line in lines[-10:]: # Últimos 10 eventos
|
|
782
|
+
typer.echo(f" {line[:120]}")
|
|
783
|
+
except Exception:
|
|
784
|
+
typer.echo(" (não foi possível obter eventos)")
|
|
785
|
+
|
|
786
|
+
# Helm status
|
|
787
|
+
typer.echo("\n Helm release status:")
|
|
788
|
+
try:
|
|
789
|
+
result = subprocess.run(
|
|
790
|
+
["helm", "status", "cert-manager", "-n", NAMESPACE],
|
|
791
|
+
capture_output=True,
|
|
792
|
+
text=True,
|
|
793
|
+
timeout=15,
|
|
794
|
+
)
|
|
795
|
+
if result.stdout:
|
|
796
|
+
for line in result.stdout.strip().split("\n")[:15]:
|
|
797
|
+
typer.echo(f" {line}")
|
|
798
|
+
except Exception:
|
|
799
|
+
typer.echo(" (não foi possível obter status do Helm)")
|
|
466
800
|
|
|
467
801
|
|
|
468
802
|
def _apply_manifest_with_retry(
|