raijin-server 0.2.17__py3-none-any.whl → 0.2.19__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/modules/metallb.py +166 -42
- raijin_server/modules/traefik.py +41 -1
- {raijin_server-0.2.17.dist-info → raijin_server-0.2.19.dist-info}/METADATA +1 -1
- {raijin_server-0.2.17.dist-info → raijin_server-0.2.19.dist-info}/RECORD +9 -9
- {raijin_server-0.2.17.dist-info → raijin_server-0.2.19.dist-info}/WHEEL +0 -0
- {raijin_server-0.2.17.dist-info → raijin_server-0.2.19.dist-info}/entry_points.txt +0 -0
- {raijin_server-0.2.17.dist-info → raijin_server-0.2.19.dist-info}/licenses/LICENSE +0 -0
- {raijin_server-0.2.17.dist-info → raijin_server-0.2.19.dist-info}/top_level.txt +0 -0
raijin_server/__init__.py
CHANGED
raijin_server/modules/metallb.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""Provisiona MetalLB (L2) com pool de IPs para LoadBalancer em ambientes bare metal."""
|
|
2
2
|
|
|
3
3
|
import socket
|
|
4
|
+
import time
|
|
4
5
|
|
|
5
6
|
import typer
|
|
6
7
|
|
|
@@ -28,46 +29,168 @@ def _detect_node_name(ctx: ExecutionContext) -> str:
|
|
|
28
29
|
return socket.gethostname()
|
|
29
30
|
|
|
30
31
|
|
|
31
|
-
def
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
"
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
32
|
+
def _uninstall_metallb(ctx: ExecutionContext) -> None:
|
|
33
|
+
"""Remove instalacao anterior do MetalLB completamente."""
|
|
34
|
+
typer.echo("Removendo instalacao anterior do MetalLB...")
|
|
35
|
+
|
|
36
|
+
# Helm uninstall
|
|
37
|
+
run_cmd(
|
|
38
|
+
["helm", "uninstall", "metallb", "-n", "metallb-system"],
|
|
39
|
+
ctx,
|
|
40
|
+
check=False,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
# Remove CRDs que podem ficar orfaos
|
|
44
|
+
run_cmd(
|
|
45
|
+
["kubectl", "delete", "crd",
|
|
46
|
+
"ipaddresspools.metallb.io",
|
|
47
|
+
"l2advertisements.metallb.io",
|
|
48
|
+
"bgpadvertisements.metallb.io",
|
|
49
|
+
"bgppeers.metallb.io",
|
|
50
|
+
"bfdprofiles.metallb.io",
|
|
51
|
+
"communities.metallb.io",
|
|
52
|
+
"servicel2statuses.metallb.io",
|
|
53
|
+
"--ignore-not-found"],
|
|
54
|
+
ctx,
|
|
55
|
+
check=False,
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
# Remove namespace se existir
|
|
59
|
+
run_cmd(
|
|
60
|
+
["kubectl", "delete", "namespace", "metallb-system", "--ignore-not-found"],
|
|
61
|
+
ctx,
|
|
62
|
+
check=False,
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
# Aguarda limpeza
|
|
66
|
+
time.sleep(5)
|
|
42
67
|
|
|
43
68
|
|
|
44
|
-
def
|
|
45
|
-
|
|
69
|
+
def _check_existing_metallb(ctx: ExecutionContext) -> bool:
|
|
70
|
+
"""Verifica se existe instalacao do MetalLB."""
|
|
46
71
|
result = run_cmd(
|
|
47
|
-
[
|
|
48
|
-
"kubectl",
|
|
49
|
-
"-n",
|
|
50
|
-
"metallb-system",
|
|
51
|
-
"get",
|
|
52
|
-
"deploy",
|
|
53
|
-
"-l",
|
|
54
|
-
"app.kubernetes.io/component=webhook",
|
|
55
|
-
"-o",
|
|
56
|
-
"jsonpath={.items[0].metadata.name}",
|
|
57
|
-
],
|
|
72
|
+
["helm", "status", "metallb", "-n", "metallb-system"],
|
|
58
73
|
ctx,
|
|
59
74
|
check=False,
|
|
60
75
|
)
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
76
|
+
return result.returncode == 0
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def _wait_for_pods_running(ctx: ExecutionContext, timeout: int = 180) -> bool:
|
|
80
|
+
"""Aguarda todos os pods do MetalLB estarem Running."""
|
|
81
|
+
typer.echo("Aguardando pods do MetalLB ficarem Running...")
|
|
82
|
+
deadline = time.time() + timeout
|
|
83
|
+
|
|
84
|
+
while time.time() < deadline:
|
|
85
|
+
# Verifica se ha pods pending ou em erro
|
|
86
|
+
result = run_cmd(
|
|
87
|
+
[
|
|
88
|
+
"kubectl", "-n", "metallb-system", "get", "pods",
|
|
89
|
+
"-o", "jsonpath={range .items[*]}{.metadata.name}:{.status.phase}\\n{end}",
|
|
90
|
+
],
|
|
91
|
+
ctx,
|
|
92
|
+
check=False,
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
if result.returncode != 0:
|
|
96
|
+
time.sleep(5)
|
|
97
|
+
continue
|
|
98
|
+
|
|
99
|
+
output = (result.stdout or "").strip()
|
|
100
|
+
if not output:
|
|
101
|
+
time.sleep(5)
|
|
102
|
+
continue
|
|
103
|
+
|
|
104
|
+
pods = []
|
|
105
|
+
for line in output.split("\n"):
|
|
106
|
+
if not line:
|
|
107
|
+
continue
|
|
108
|
+
# Split apenas na ultima ocorrencia de ":" para evitar problemas com nomes
|
|
109
|
+
parts = line.rsplit(":", 1)
|
|
110
|
+
if len(parts) == 2:
|
|
111
|
+
pods.append((parts[0], parts[1]))
|
|
112
|
+
|
|
113
|
+
all_running = all(phase == "Running" for _, phase in pods)
|
|
114
|
+
if all_running and pods:
|
|
115
|
+
typer.secho(f" Todos os {len(pods)} pods Running.", fg=typer.colors.GREEN)
|
|
116
|
+
return True
|
|
117
|
+
|
|
118
|
+
# Mostra status atual
|
|
119
|
+
pending = [name for name, phase in pods if phase != "Running"]
|
|
120
|
+
if pending:
|
|
121
|
+
typer.echo(f" Aguardando: {', '.join(pending[:3])}...")
|
|
122
|
+
|
|
123
|
+
time.sleep(10)
|
|
124
|
+
|
|
125
|
+
# Timeout - mostra diagnostico
|
|
126
|
+
typer.secho(" Timeout esperando pods. Diagnostico:", fg=typer.colors.YELLOW)
|
|
127
|
+
run_cmd(["kubectl", "-n", "metallb-system", "get", "pods", "-o", "wide"], ctx, check=False)
|
|
128
|
+
run_cmd(["kubectl", "-n", "metallb-system", "get", "events", "--sort-by=.lastTimestamp"], ctx, check=False)
|
|
129
|
+
return False
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def _wait_for_webhook_ready(ctx: ExecutionContext, timeout: int = 120) -> bool:
|
|
133
|
+
"""Aguarda webhook estar respondendo."""
|
|
134
|
+
typer.echo("Aguardando webhook do MetalLB...")
|
|
135
|
+
deadline = time.time() + timeout
|
|
136
|
+
|
|
137
|
+
while time.time() < deadline:
|
|
138
|
+
result = run_cmd(
|
|
139
|
+
[
|
|
140
|
+
"kubectl", "-n", "metallb-system", "get", "endpoints",
|
|
141
|
+
"metallb-webhook-service", "-o", "jsonpath={.subsets[0].addresses[0].ip}",
|
|
142
|
+
],
|
|
143
|
+
ctx,
|
|
144
|
+
check=False,
|
|
145
|
+
)
|
|
146
|
+
if result.returncode == 0 and (result.stdout or "").strip():
|
|
147
|
+
typer.secho(" Webhook disponivel.", fg=typer.colors.GREEN)
|
|
148
|
+
return True
|
|
149
|
+
time.sleep(5)
|
|
150
|
+
|
|
151
|
+
typer.secho(" Webhook nao ficou disponivel.", fg=typer.colors.YELLOW)
|
|
152
|
+
return False
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def _apply_pool_with_retry(manifest: str, ctx: ExecutionContext, max_attempts: int = 12) -> bool:
|
|
156
|
+
"""Aplica IPAddressPool/L2Advertisement com retry."""
|
|
157
|
+
typer.echo("Aplicando IPAddressPool e L2Advertisement...")
|
|
158
|
+
|
|
159
|
+
for attempt in range(1, max_attempts + 1):
|
|
160
|
+
result = run_cmd(
|
|
161
|
+
f"echo '{manifest}' | kubectl apply -f -",
|
|
162
|
+
ctx,
|
|
163
|
+
use_shell=True,
|
|
164
|
+
check=False,
|
|
165
|
+
)
|
|
166
|
+
if result.returncode == 0:
|
|
167
|
+
typer.secho(" Pool e L2Advertisement aplicados.", fg=typer.colors.GREEN)
|
|
168
|
+
return True
|
|
169
|
+
|
|
170
|
+
stderr = (result.stderr or "").lower()
|
|
171
|
+
if "webhook" in stderr or "connection refused" in stderr:
|
|
172
|
+
typer.echo(f" Webhook nao pronto, tentativa {attempt}/{max_attempts}...")
|
|
173
|
+
time.sleep(10)
|
|
174
|
+
else:
|
|
175
|
+
typer.secho(f" Erro: {result.stderr}", fg=typer.colors.RED)
|
|
176
|
+
return False
|
|
177
|
+
|
|
178
|
+
return False
|
|
65
179
|
|
|
66
180
|
|
|
67
181
|
def run(ctx: ExecutionContext) -> None:
|
|
68
182
|
require_root(ctx)
|
|
69
183
|
typer.echo("Instalando MetalLB via Helm...")
|
|
70
184
|
|
|
185
|
+
# Prompt opcional de limpeza
|
|
186
|
+
if _check_existing_metallb(ctx):
|
|
187
|
+
cleanup = typer.confirm(
|
|
188
|
+
"Instalacao anterior do MetalLB detectada. Limpar antes de reinstalar?",
|
|
189
|
+
default=False,
|
|
190
|
+
)
|
|
191
|
+
if cleanup:
|
|
192
|
+
_uninstall_metallb(ctx)
|
|
193
|
+
|
|
71
194
|
pool = typer.prompt(
|
|
72
195
|
"Pool de IPs (range ou CIDR) para services LoadBalancer",
|
|
73
196
|
default="192.168.1.100-192.168.1.250",
|
|
@@ -89,12 +212,12 @@ def run(ctx: ExecutionContext) -> None:
|
|
|
89
212
|
"speaker.tolerations[1].key=node-role.kubernetes.io/master",
|
|
90
213
|
"speaker.tolerations[1].operator=Exists",
|
|
91
214
|
"speaker.tolerations[1].effect=NoSchedule",
|
|
92
|
-
#
|
|
215
|
+
# nodeSelector com chave escapada
|
|
93
216
|
f"controller.nodeSelector.kubernetes\\.io/hostname={node_name}",
|
|
94
217
|
f"speaker.nodeSelector.kubernetes\\.io/hostname={node_name}",
|
|
95
218
|
]
|
|
96
219
|
|
|
97
|
-
# Instala
|
|
220
|
+
# Instala do zero
|
|
98
221
|
helm_upgrade_install(
|
|
99
222
|
release="metallb",
|
|
100
223
|
chart="metallb",
|
|
@@ -105,13 +228,16 @@ def run(ctx: ExecutionContext) -> None:
|
|
|
105
228
|
values=values,
|
|
106
229
|
)
|
|
107
230
|
|
|
108
|
-
#
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
231
|
+
# Aguarda pods estarem Running
|
|
232
|
+
if not _wait_for_pods_running(ctx):
|
|
233
|
+
ctx.errors.append("Pods do MetalLB nao subiram - verifique taints/recursos do cluster")
|
|
234
|
+
return
|
|
235
|
+
|
|
236
|
+
# Aguarda webhook
|
|
237
|
+
if not _wait_for_webhook_ready(ctx):
|
|
238
|
+
typer.secho("Continuando mesmo sem confirmacao do webhook...", fg=typer.colors.YELLOW)
|
|
113
239
|
|
|
114
|
-
# Aplica
|
|
240
|
+
# Aplica pool
|
|
115
241
|
manifest = f"""
|
|
116
242
|
apiVersion: metallb.io/v1beta1
|
|
117
243
|
kind: IPAddressPool
|
|
@@ -132,10 +258,8 @@ spec:
|
|
|
132
258
|
- raijin-pool
|
|
133
259
|
"""
|
|
134
260
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
use_shell=True,
|
|
139
|
-
)
|
|
261
|
+
if not _apply_pool_with_retry(manifest, ctx):
|
|
262
|
+
ctx.errors.append("Falha ao aplicar IPAddressPool/L2Advertisement")
|
|
263
|
+
return
|
|
140
264
|
|
|
141
|
-
typer.secho("\n✓ MetalLB
|
|
265
|
+
typer.secho("\n✓ MetalLB instalado. Services LoadBalancer usarao o pool informado.", fg=typer.colors.GREEN, bold=True)
|
raijin_server/modules/traefik.py
CHANGED
|
@@ -7,6 +7,36 @@ import typer
|
|
|
7
7
|
from raijin_server.utils import ExecutionContext, helm_upgrade_install, require_root, run_cmd
|
|
8
8
|
|
|
9
9
|
|
|
10
|
+
def _check_existing_traefik(ctx: ExecutionContext) -> bool:
|
|
11
|
+
"""Verifica se existe instalacao do Traefik."""
|
|
12
|
+
result = run_cmd(
|
|
13
|
+
["helm", "status", "traefik", "-n", "traefik"],
|
|
14
|
+
ctx,
|
|
15
|
+
check=False,
|
|
16
|
+
)
|
|
17
|
+
return result.returncode == 0
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _uninstall_traefik(ctx: ExecutionContext) -> None:
|
|
21
|
+
"""Remove instalacao anterior do Traefik."""
|
|
22
|
+
import time
|
|
23
|
+
typer.echo("Removendo instalacao anterior do Traefik...")
|
|
24
|
+
|
|
25
|
+
run_cmd(
|
|
26
|
+
["helm", "uninstall", "traefik", "-n", "traefik"],
|
|
27
|
+
ctx,
|
|
28
|
+
check=False,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
run_cmd(
|
|
32
|
+
["kubectl", "delete", "namespace", "traefik", "--ignore-not-found"],
|
|
33
|
+
ctx,
|
|
34
|
+
check=False,
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
time.sleep(5)
|
|
38
|
+
|
|
39
|
+
|
|
10
40
|
def _detect_node_name(ctx: ExecutionContext) -> str:
|
|
11
41
|
"""Tenta obter o nome do node via kubectl; fallback para hostname local.
|
|
12
42
|
|
|
@@ -35,6 +65,15 @@ def run(ctx: ExecutionContext) -> None:
|
|
|
35
65
|
require_root(ctx)
|
|
36
66
|
typer.echo("Instalando Traefik via Helm...")
|
|
37
67
|
|
|
68
|
+
# Prompt opcional de limpeza
|
|
69
|
+
if _check_existing_traefik(ctx):
|
|
70
|
+
cleanup = typer.confirm(
|
|
71
|
+
"Instalacao anterior do Traefik detectada. Limpar antes de reinstalar?",
|
|
72
|
+
default=False,
|
|
73
|
+
)
|
|
74
|
+
if cleanup:
|
|
75
|
+
_uninstall_traefik(ctx)
|
|
76
|
+
|
|
38
77
|
acme_email = typer.prompt("Email para ACME/Let's Encrypt", default="admin@example.com")
|
|
39
78
|
dashboard_host = typer.prompt("Host para dashboard (opcional)", default="traefik.local")
|
|
40
79
|
|
|
@@ -56,7 +95,8 @@ def run(ctx: ExecutionContext) -> None:
|
|
|
56
95
|
"tolerations[1].key=node-role.kubernetes.io/master",
|
|
57
96
|
"tolerations[1].operator=Exists",
|
|
58
97
|
"tolerations[1].effect=NoSchedule",
|
|
59
|
-
|
|
98
|
+
# Escapa chave com ponto para evitar parsing incorreto
|
|
99
|
+
f"nodeSelector.kubernetes\\.io/hostname={node_name}",
|
|
60
100
|
]
|
|
61
101
|
|
|
62
102
|
if dashboard_host:
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
raijin_server/__init__.py,sha256=
|
|
1
|
+
raijin_server/__init__.py,sha256=0tXuzf-HrPX7SKpdPAcVDC_u8HnxXWAls7-ufyxhsu0,95
|
|
2
2
|
raijin_server/cli.py,sha256=71nn7QN0f3MJkXcHr0STXmxljr-CaPibzOoiItbOT88,28571
|
|
3
3
|
raijin_server/config.py,sha256=QNiEVvrbW56XgvNn5-h3bkJm46Xc8mjNqPbvixXD8N0,4829
|
|
4
4
|
raijin_server/healthchecks.py,sha256=lzXdFw6S0hOYbUKbqksh4phb04lXgXdTspP1Dsz4dx8,15401
|
|
@@ -20,7 +20,7 @@ raijin_server/modules/kafka.py,sha256=bp8k_IhuAIO6dL0IpK1UxxLZoGih6nJp0ZnzwmiZEj
|
|
|
20
20
|
raijin_server/modules/kong.py,sha256=2EZKYBmBhm_7Nduw9PWrvrekp0VCxQbc2gElpAJqKfg,491
|
|
21
21
|
raijin_server/modules/kubernetes.py,sha256=waSf2cCVnLicN5o3M47MzMzmHHtvKeFXm1__8ynQzA0,11871
|
|
22
22
|
raijin_server/modules/loki.py,sha256=erwFfSiSFOv-Ul3nFdrI2RElPYuqqBPBBa_MJAwyLys,676
|
|
23
|
-
raijin_server/modules/metallb.py,sha256=
|
|
23
|
+
raijin_server/modules/metallb.py,sha256=LBavNo5NJn8CM5x2kVuUHbbsdXYp0fkm5oUWfTTqeio,8471
|
|
24
24
|
raijin_server/modules/minio.py,sha256=BVvsEaJlJUV92_ep7pKsBhSYPjWZrDOB3J6XAWYAHYg,486
|
|
25
25
|
raijin_server/modules/network.py,sha256=QRlYdcryCCPAWG3QQ_W7ld9gJgETI7H8gwntOU7UqFE,4818
|
|
26
26
|
raijin_server/modules/observability_dashboards.py,sha256=fVz0WEOQrUTF5rJ__Nu_onyBuwL_exFmysWMmg8AE9w,7319
|
|
@@ -29,7 +29,7 @@ raijin_server/modules/prometheus.py,sha256=Rs9BREmaoKlyteNdAQZnSIeJfsRO0RQKyyL2g
|
|
|
29
29
|
raijin_server/modules/sanitize.py,sha256=_RnWn1DUuNrzx3NnKEbMvf5iicgjiN_ubwT59e0rYWY,6040
|
|
30
30
|
raijin_server/modules/secrets.py,sha256=xpV3gIMnwQdAI2j69Ck5daIK4wlYJA_1rkWTtSfVNk0,3715
|
|
31
31
|
raijin_server/modules/ssh_hardening.py,sha256=oQdk-EVnEHNMKIWvoFuZzI4jK0nNO8IAY4hkB4pj8zw,4025
|
|
32
|
-
raijin_server/modules/traefik.py,sha256=
|
|
32
|
+
raijin_server/modules/traefik.py,sha256=3DAcdW19jBaA1qFuqfwf054bKQCC4htQrY6dXf_cC0k,3539
|
|
33
33
|
raijin_server/modules/velero.py,sha256=_CV0QQnWr5L-CWXDOiD9Ef4J7GaQT-s9yNBwqp_FLOY,1395
|
|
34
34
|
raijin_server/modules/vpn.py,sha256=hF-0vA17VKTxhQLDBSEeqI5aPQpiaaj4IpUf9l6lr64,8297
|
|
35
35
|
raijin_server/scripts/__init__.py,sha256=deduGfHf8BMVWred4ux5LfBDT2NJ5XYeJAt2sDEU4qs,53
|
|
@@ -37,9 +37,9 @@ raijin_server/scripts/checklist.sh,sha256=j6E0Kmk1EfjLvKK1VpCqzXJAXI_7Bm67LK4ndy
|
|
|
37
37
|
raijin_server/scripts/install.sh,sha256=Y1ickbQ4siQ0NIPs6UgrqUr8WWy7U0LHmaTQbEgavoI,3949
|
|
38
38
|
raijin_server/scripts/log_size_metric.sh,sha256=Iv4SsX8AuCYRou-klYn32mX41xB6j0xJGLBO6riw4rU,1208
|
|
39
39
|
raijin_server/scripts/pre-deploy-check.sh,sha256=XqMo7IMIpwUHF17YEmU0-cVmTDMoCGMBFnmS39FidI4,4912
|
|
40
|
-
raijin_server-0.2.
|
|
41
|
-
raijin_server-0.2.
|
|
42
|
-
raijin_server-0.2.
|
|
43
|
-
raijin_server-0.2.
|
|
44
|
-
raijin_server-0.2.
|
|
45
|
-
raijin_server-0.2.
|
|
40
|
+
raijin_server-0.2.19.dist-info/licenses/LICENSE,sha256=kJsMCjOiRZE0AQNtxWqBa32z9kMAaF4EUxyHj3hKaJo,1105
|
|
41
|
+
raijin_server-0.2.19.dist-info/METADATA,sha256=PeFtZI1UEbt-j_VTPVGQ2T_guRQvkrOlXUddK25CPoI,22476
|
|
42
|
+
raijin_server-0.2.19.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
43
|
+
raijin_server-0.2.19.dist-info/entry_points.txt,sha256=3ZvxDX4pvcjkIRsXAJ69wIfVmKa78LKo-C3QhqN2KVM,56
|
|
44
|
+
raijin_server-0.2.19.dist-info/top_level.txt,sha256=Yz1xneCRtsZOzbPIcTAcrSxd-1p80pohMXYAZ74dpok,14
|
|
45
|
+
raijin_server-0.2.19.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|