raijin-server 0.3.3__py3-none-any.whl → 0.3.6__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.
@@ -1,19 +1,20 @@
1
- raijin_server/__init__.py,sha256=PxBToT7REJri8rHFGLoeIfEApLUM9TrOogmxCLdnomI,94
2
- raijin_server/cli.py,sha256=rqkAQCU5imi52YJCIeEuZqWo8bWYkVErOQh3JpKIDok,38149
1
+ raijin_server/__init__.py,sha256=DizNqR7krrm4UEAK6a2xeOG3wuV7xlSekBC-mCSHadw,94
2
+ raijin_server/cli.py,sha256=74ZqEPhg9R9jxNFZRV37CHrwm-dBKmYdT_MxdK4FKKA,37585
3
3
  raijin_server/config.py,sha256=QNiEVvrbW56XgvNn5-h3bkJm46Xc8mjNqPbvixXD8N0,4829
4
4
  raijin_server/healthchecks.py,sha256=lzXdFw6S0hOYbUKbqksh4phb04lXgXdTspP1Dsz4dx8,15401
5
5
  raijin_server/module_manager.py,sha256=Wmhj603CN0XGUVr7_Fo8CHzKd9yIbS9x5BJLqDj78kw,10259
6
6
  raijin_server/utils.py,sha256=9RnGnPoUTYOpMVRLNa4P4lIQrJNQLkSkPUxycZRGv78,20827
7
7
  raijin_server/validators.py,sha256=EATYPy2pllAb6IX4gUZKnELvospWwyGV3DHrzxb_RMg,11761
8
- raijin_server/modules/__init__.py,sha256=ojxAdnJfXifNUVa4WuLVh97jHqeVzIi6DZ_fAtXB9tM,984
8
+ raijin_server/modules/__init__.py,sha256=BCTLuNtmvn8IWqGNQZQBRokqAv-KwUZP4_kyxkyHyN4,896
9
9
  raijin_server/modules/apokolips_demo.py,sha256=8ltsXRbVDwlDwLMIvh02NG-FeAfBWw_v6lh7IGOyNqs,13725
10
10
  raijin_server/modules/bootstrap.py,sha256=oVIGNRW_JbgY8zXNHGAIP0vGbbHNHyQexthxo5zhbcw,9762
11
11
  raijin_server/modules/calico.py,sha256=TTPF1bLFdAKb3IVOqFqRxNblULkRmMMRylsIBp4w8I8,6700
12
12
  raijin_server/modules/cert_manager.py,sha256=XkFlXJjiP4_9It_PJaFcVYMS-QKTzzFAt839QQ9qNsg,50223
13
13
  raijin_server/modules/essentials.py,sha256=2xUXCyCQtFGd2DnCKV81N1R6bEJqH8zaet8mLovtQ1I,689
14
14
  raijin_server/modules/firewall.py,sha256=h6AISqiZeTinVT7BjmQIS872qRAFZJLg7meqlth3cfw,757
15
- raijin_server/modules/full_install.py,sha256=xiKe2GLuZ97c4YdTmhP-kwDVuJJ9Xq3dlgcLlqSPeYM,15518
16
- raijin_server/modules/grafana.py,sha256=r4U6FJZ9OeTk4d3LDJT0NbN8wumB3REMtd3E3PRL_oE,17383
15
+ raijin_server/modules/full_install.py,sha256=M4SV4OA-r41xhpMmZQvUieRugiq0faLa4f0N499ksd4,15035
16
+ raijin_server/modules/grafana.py,sha256=8YbKG-UL19lwTkH1-ZxpUetOZd-CLI4_kPsuZblNaWI,18080
17
+ raijin_server/modules/harbor.py,sha256=ix0qnu_FugzbFnnTRZ3Dyy6UmKdm3nD0ubZ4SZYHkWE,21940
17
18
  raijin_server/modules/hardening.py,sha256=4hz3ifkMhPlXa2n7gPxN0gitQgzALZ-073vuU3LM4RI,1616
18
19
  raijin_server/modules/harness.py,sha256=uWTxTVJlY_VB6xi4ftMtTSaIb96HA8WJQS-RbyxU45M,5391
19
20
  raijin_server/modules/internal_dns.py,sha256=Jynngq0TEEUo3jkAR4m8F1ihF10rkQuKHVP-gZYyDFY,15191
@@ -21,15 +22,13 @@ raijin_server/modules/istio.py,sha256=o0K5-Fw4LRs-kbAVgwzYxHzEt_aPFJG8suqOqvg274
21
22
  raijin_server/modules/kafka.py,sha256=n7ZpLPWv6sKBJhdBiPe7VgeDB24YiCIOWvOQkWwt03Y,5664
22
23
  raijin_server/modules/kong.py,sha256=_w1VIkND6zZuUwIl_CNDxbwWdzaEdXZEO_Iqc1ngPwQ,13654
23
24
  raijin_server/modules/kubernetes.py,sha256=9E6zV0zGQWZW92NVpxwYctpi-4JDmi6YzF3tKRI4HlU,13343
24
- raijin_server/modules/loki.py,sha256=aNiUpnOFppZMXoQwYhn7IoPMzwUz4aHi6pbiqj1PRjc,5022
25
+ raijin_server/modules/loki.py,sha256=fRoXNghwffW6afE_a3sKMhjPJ9DIWhGMTTPM-ltNr2E,5766
25
26
  raijin_server/modules/metallb.py,sha256=uUuklc_RsQ-W2qDVRMQAxQm9HKGEqso444b1IwBpM6w,8554
26
- raijin_server/modules/minio.py,sha256=QbladHGefZBZ8l3f9D7t45nwfwVcuAgHi78E4Ygi300,17614
27
+ raijin_server/modules/minio.py,sha256=ZoxugJvvuGLzViDfEzrVCRZUevoiFwcEy0PNyn0My4w,18918
27
28
  raijin_server/modules/network.py,sha256=QRlYdcryCCPAWG3QQ_W7ld9gJgETI7H8gwntOU7UqFE,4818
28
- raijin_server/modules/observability_dashboards.py,sha256=fVz0WEOQrUTF5rJ__Nu_onyBuwL_exFmysWMmg8AE9w,7319
29
- raijin_server/modules/observability_ingress.py,sha256=S4MtJKahiZ1qSx0P71P3IhKvq4RY-g01Z4IogW3c1hs,7045
30
- raijin_server/modules/prometheus.py,sha256=y5sy_mH1YeQWTOt5CNqqj5JD92-GMxbWQaaZKwx505U,19728
29
+ raijin_server/modules/prometheus.py,sha256=lyhaqLIfMl0GtQ2b2Hre7_A47HrHBB5gspmnWtwXZ4Y,21880
31
30
  raijin_server/modules/sanitize.py,sha256=_RnWn1DUuNrzx3NnKEbMvf5iicgjiN_ubwT59e0rYWY,6040
32
- raijin_server/modules/secrets.py,sha256=d4j12feQL8m_4-hYN5FfboQHvBc75TFeGno3OzrXokE,9266
31
+ raijin_server/modules/secrets.py,sha256=C5k10ODo6Ai_e6Ei-JiWTOa-cOh-5d2__PhTQA0UmjI,19452
33
32
  raijin_server/modules/ssh_hardening.py,sha256=Zd0dlylUBr01SkrI1CS05-0DB9xIto5rWH1bUVs80ow,5422
34
33
  raijin_server/modules/traefik.py,sha256=omziywss4o-8t64Kj-upLqbXdFYm2JwqOoOukDUmqxY,5008
35
34
  raijin_server/modules/velero.py,sha256=yDtqd6yUu0L5wzLCjYXqvvxB_RyaAoZtntb6HoHVAOo,5642
@@ -40,9 +39,9 @@ raijin_server/scripts/checklist.sh,sha256=j6E0Kmk1EfjLvKK1VpCqzXJAXI_7Bm67LK4ndy
40
39
  raijin_server/scripts/install.sh,sha256=Y1ickbQ4siQ0NIPs6UgrqUr8WWy7U0LHmaTQbEgavoI,3949
41
40
  raijin_server/scripts/log_size_metric.sh,sha256=Iv4SsX8AuCYRou-klYn32mX41xB6j0xJGLBO6riw4rU,1208
42
41
  raijin_server/scripts/pre-deploy-check.sh,sha256=XqMo7IMIpwUHF17YEmU0-cVmTDMoCGMBFnmS39FidI4,4912
43
- raijin_server-0.3.3.dist-info/licenses/LICENSE,sha256=kJsMCjOiRZE0AQNtxWqBa32z9kMAaF4EUxyHj3hKaJo,1105
44
- raijin_server-0.3.3.dist-info/METADATA,sha256=dtdbYJo209BpoUkIJ5PswDw_1ik5tyQxfFupiiTNRZE,8829
45
- raijin_server-0.3.3.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
46
- raijin_server-0.3.3.dist-info/entry_points.txt,sha256=3ZvxDX4pvcjkIRsXAJ69wIfVmKa78LKo-C3QhqN2KVM,56
47
- raijin_server-0.3.3.dist-info/top_level.txt,sha256=Yz1xneCRtsZOzbPIcTAcrSxd-1p80pohMXYAZ74dpok,14
48
- raijin_server-0.3.3.dist-info/RECORD,,
42
+ raijin_server-0.3.6.dist-info/licenses/LICENSE,sha256=kJsMCjOiRZE0AQNtxWqBa32z9kMAaF4EUxyHj3hKaJo,1105
43
+ raijin_server-0.3.6.dist-info/METADATA,sha256=egpj4eEFQ3JJAA-bT3Iz5ebOc57e5JpyuQ2a8-ruAD4,8829
44
+ raijin_server-0.3.6.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
45
+ raijin_server-0.3.6.dist-info/entry_points.txt,sha256=3ZvxDX4pvcjkIRsXAJ69wIfVmKa78LKo-C3QhqN2KVM,56
46
+ raijin_server-0.3.6.dist-info/top_level.txt,sha256=Yz1xneCRtsZOzbPIcTAcrSxd-1p80pohMXYAZ74dpok,14
47
+ raijin_server-0.3.6.dist-info/RECORD,,
@@ -1,233 +0,0 @@
1
- """Provisiona dashboards opinativos do Grafana e alertas padrao do Prometheus/Alertmanager."""
2
-
3
- from __future__ import annotations
4
-
5
- import json
6
- import textwrap
7
- from pathlib import Path
8
-
9
- import typer
10
-
11
- from raijin_server.utils import (
12
- ExecutionContext,
13
- kubectl_apply,
14
- kubectl_create_ns,
15
- require_root,
16
- write_file,
17
- )
18
-
19
- MANIFEST_PATH = Path("/tmp/raijin-observability-dashboards.yaml")
20
-
21
- CLUSTER_DASHBOARD = {
22
- "title": "Raijin Cluster Overview",
23
- "uid": "raijin-cluster",
24
- "timezone": "browser",
25
- "schemaVersion": 39,
26
- "panels": [
27
- {
28
- "type": "timeseries",
29
- "title": "Node CPU %",
30
- "datasource": "Prometheus",
31
- "targets": [
32
- {
33
- "expr": '100 - (avg by(instance) (irate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)',
34
- "legendFormat": "{{instance}}",
35
- }
36
- ],
37
- },
38
- {
39
- "type": "timeseries",
40
- "title": "Node Memory %",
41
- "datasource": "Prometheus",
42
- "targets": [
43
- {
44
- "expr": '((node_memory_MemTotal_bytes - node_memory_MemAvailable_bytes) / node_memory_MemTotal_bytes) * 100',
45
- "legendFormat": "{{instance}}",
46
- }
47
- ],
48
- },
49
- {
50
- "type": "stat",
51
- "title": "API Server Errors (5m)",
52
- "datasource": "Prometheus",
53
- "targets": [
54
- {
55
- "expr": 'sum(rate(apiserver_request_total{code=~"5.."}[5m]))',
56
- }
57
- ],
58
- },
59
- ],
60
- }
61
-
62
- SLO_DASHBOARD = {
63
- "title": "Raijin SLO - Workloads",
64
- "uid": "raijin-slo",
65
- "schemaVersion": 39,
66
- "panels": [
67
- {
68
- "type": "timeseries",
69
- "title": "Pod Restarts (1h)",
70
- "datasource": "Prometheus",
71
- "targets": [
72
- {
73
- "expr": 'increase(kube_pod_container_status_restarts_total[1h])',
74
- "legendFormat": "{{namespace}}/{{pod}}",
75
- }
76
- ],
77
- },
78
- {
79
- "type": "timeseries",
80
- "title": "Ingress HTTP 5xx Rate",
81
- "datasource": "Prometheus",
82
- "targets": [
83
- {
84
- "expr": 'sum(rate(traefik_service_requests_total{code=~"5.."}[5m])) by (service)',
85
- "legendFormat": "{{service}}",
86
- }
87
- ],
88
- },
89
- {
90
- "type": "gauge",
91
- "title": "Cluster CPU Pressure",
92
- "datasource": "Prometheus",
93
- "targets": [
94
- {
95
- "expr": 'avg(node_pressure_cpu_waiting_seconds_total)',
96
- }
97
- ],
98
- },
99
- ],
100
- }
101
-
102
-
103
- def _json_block(obj: dict) -> str:
104
- return json.dumps(obj, indent=2, separators=(",", ": "))
105
-
106
-
107
- def _alertmanager_block(contact_email: str, webhook_url: str) -> str:
108
- receivers = ["receivers:", " - name: default"]
109
- if contact_email:
110
- receivers.append(" email_configs:")
111
- receivers.append(f" - to: {contact_email}")
112
- receivers.append(" send_resolved: true")
113
- if webhook_url:
114
- receivers.append(" webhook_configs:")
115
- receivers.append(f" - url: {webhook_url}")
116
- receivers.append(" send_resolved: true")
117
- receivers_str = "\n".join(receivers) if len(receivers) > 2 else "receivers:\n - name: default"
118
- return textwrap.dedent(
119
- f"""
120
- route:
121
- receiver: default
122
- group_by: ['alertname']
123
- group_wait: 30s
124
- group_interval: 5m
125
- repeat_interval: 4h
126
- {receivers_str}
127
- """
128
- ).strip()
129
-
130
-
131
- def _build_manifest(namespace: str, contact_email: str, webhook_url: str) -> str:
132
- cluster_json = textwrap.indent(_json_block(CLUSTER_DASHBOARD), " ")
133
- slo_json = textwrap.indent(_json_block(SLO_DASHBOARD), " ")
134
- alertmanager_yaml = textwrap.indent(_alertmanager_block(contact_email, webhook_url), " ")
135
-
136
- prometheus_rules = textwrap.dedent(
137
- f"""
138
- apiVersion: monitoring.coreos.com/v1
139
- kind: PrometheusRule
140
- metadata:
141
- name: raijin-default-alerts
142
- namespace: {namespace}
143
- spec:
144
- groups:
145
- - name: raijin-cluster-health
146
- rules:
147
- - alert: NodeDown
148
- expr: kube_node_status_condition{{condition="Ready",status="true"}} == 0
149
- for: 5m
150
- labels:
151
- severity: critical
152
- annotations:
153
- summary: "Node fora do cluster"
154
- description: "O node {{ $labels.node }} nao reporta Ready ha 5 minutos."
155
- - alert: HighNodeCPU
156
- expr: avg(node_load1) by (instance) > count(node_cpu_seconds_total{{mode="system"}}) by (instance)
157
- for: 10m
158
- labels:
159
- severity: warning
160
- annotations:
161
- summary: "CPU elevada em {{ $labels.instance }}"
162
- description: "Load1 superior ao numero de cores ha 10 minutos."
163
- - alert: APIServerErrorRate
164
- expr: sum(rate(apiserver_request_total{{code=~"5.."}}[5m])) > 5
165
- for: 5m
166
- labels:
167
- severity: warning
168
- annotations:
169
- summary: "API Server retornando 5xx"
170
- description: "Taxa de erros 5xx do API Server acima de 5 req/s."
171
- """
172
- ).strip()
173
-
174
- grafana_cm = textwrap.dedent(
175
- f"""
176
- apiVersion: v1
177
- kind: ConfigMap
178
- metadata:
179
- name: grafana-dashboards-raijin
180
- namespace: {namespace}
181
- labels:
182
- grafana_dashboard: "1"
183
- data:
184
- cluster-overview.json: |
185
- {cluster_json}
186
- slo-workloads.json: |
187
- {slo_json}
188
- """
189
- ).strip()
190
-
191
- alertmanager_cm = textwrap.dedent(
192
- f"""
193
- apiVersion: v1
194
- kind: ConfigMap
195
- metadata:
196
- name: alertmanager-raijin
197
- namespace: {namespace}
198
- labels:
199
- app.kubernetes.io/name: alertmanager
200
- data:
201
- alertmanager.yml: |
202
- {alertmanager_yaml}
203
- """
204
- ).strip()
205
-
206
- documents = "\n---\n".join([grafana_cm, alertmanager_cm, prometheus_rules])
207
- return documents + "\n"
208
-
209
-
210
- def run(ctx: ExecutionContext) -> None:
211
- require_root(ctx)
212
- typer.echo("Aplicando dashboards e alertas opinativos...")
213
-
214
- namespace = typer.prompt("Namespace de observabilidade", default="observability")
215
- contact_email = typer.prompt(
216
- "Email para receber alertas (ENTER para ignorar)",
217
- default="",
218
- )
219
- webhook_url = typer.prompt(
220
- "Webhook HTTP (Slack/Teams) para Alertmanager (ENTER para ignorar)",
221
- default="",
222
- )
223
-
224
- kubectl_create_ns(namespace, ctx)
225
-
226
- manifest = _build_manifest(namespace, contact_email, webhook_url)
227
- write_file(MANIFEST_PATH, manifest, ctx)
228
- kubectl_apply(str(MANIFEST_PATH), ctx)
229
-
230
- typer.secho("Dashboards e alertas aplicados no namespace de observabilidade.", fg=typer.colors.GREEN)
231
- typer.echo("Grafana: ConfigMap 'grafana-dashboards-raijin'")
232
- typer.echo("Alertmanager: ConfigMap 'alertmanager-raijin'")
233
- typer.echo("Prometheus: PrometheusRule 'raijin-default-alerts'")
@@ -1,246 +0,0 @@
1
- """Provisiona ingressos seguros para Grafana, Prometheus e Alertmanager."""
2
-
3
- from __future__ import annotations
4
-
5
- import base64
6
- import subprocess
7
- from pathlib import Path
8
- from typing import Dict, List
9
-
10
- import typer
11
-
12
- from raijin_server.utils import (
13
- ExecutionContext,
14
- kubectl_apply,
15
- kubectl_create_ns,
16
- require_root,
17
- write_file,
18
- )
19
-
20
- MANIFEST_PATH = Path("/tmp/raijin-observability-ingress.yaml")
21
-
22
- COMPONENTS: List[Dict[str, object]] = [
23
- {
24
- "key": "grafana",
25
- "label": "Grafana",
26
- "service": "grafana",
27
- "port": 80,
28
- "default_host": "grafana.example.com",
29
- "default_tls": "grafana-tls",
30
- "auth_secret": "grafana-basic-auth",
31
- "middleware": "grafana-auth",
32
- },
33
- {
34
- "key": "prometheus",
35
- "label": "Prometheus",
36
- "service": "kube-prometheus-stack-prometheus",
37
- "port": 9090,
38
- "default_host": "prometheus.example.com",
39
- "default_tls": "prometheus-tls",
40
- "auth_secret": "prometheus-basic-auth",
41
- "middleware": "prometheus-auth",
42
- },
43
- {
44
- "key": "alertmanager",
45
- "label": "Alertmanager",
46
- "service": "kube-prometheus-stack-alertmanager",
47
- "port": 9093,
48
- "default_host": "alerts.example.com",
49
- "default_tls": "alertmanager-tls",
50
- "auth_secret": "alertmanager-basic-auth",
51
- "middleware": "alertmanager-auth",
52
- },
53
- ]
54
-
55
-
56
- def _generate_htpasswd(username: str, password: str) -> str:
57
- try:
58
- result = subprocess.run(
59
- ["openssl", "passwd", "-6", password],
60
- capture_output=True,
61
- text=True,
62
- check=True,
63
- )
64
- hashed = result.stdout.strip()
65
- except Exception:
66
- import crypt
67
-
68
- hashed = crypt.crypt(password, crypt.mksalt(crypt.METHOD_SHA512))
69
- return f"{username}:{hashed}"
70
-
71
-
72
- def _build_manifest(
73
- namespace: str,
74
- ingress_class: str,
75
- services: List[Dict[str, object]],
76
- encoded_users: str,
77
- issuer_name: str,
78
- issuer_kind: str,
79
- ) -> str:
80
- documents: List[str] = []
81
-
82
- for svc in services:
83
- host = svc["host"]
84
- tls_secret = svc["tls_secret"]
85
- auth_secret = svc["auth_secret"]
86
- middleware = svc["middleware"]
87
- service_name = svc["service"]
88
- port = svc["port"]
89
- name = svc["key"]
90
-
91
- documents.append(
92
- f"""apiVersion: v1
93
- kind: Secret
94
- metadata:
95
- name: {auth_secret}
96
- namespace: {namespace}
97
- type: Opaque
98
- data:
99
- users: {encoded_users}
100
- """
101
- )
102
-
103
- documents.append(
104
- f"""apiVersion: traefik.containo.us/v1alpha1
105
- kind: Middleware
106
- metadata:
107
- name: {middleware}
108
- namespace: {namespace}
109
- spec:
110
- basicAuth:
111
- secret: {auth_secret}
112
- removeHeader: true
113
- """
114
- )
115
-
116
- if issuer_name:
117
- documents.append(
118
- f"""apiVersion: cert-manager.io/v1
119
- kind: Certificate
120
- metadata:
121
- name: {name}-ingress-cert
122
- namespace: {namespace}
123
- spec:
124
- secretName: {tls_secret}
125
- dnsNames:
126
- - {host}
127
- issuerRef:
128
- name: {issuer_name}
129
- kind: {issuer_kind}
130
- """
131
- )
132
-
133
- documents.append(
134
- f"""apiVersion: networking.k8s.io/v1
135
- kind: Ingress
136
- metadata:
137
- name: {name}-secure
138
- namespace: {namespace}
139
- annotations:
140
- traefik.ingress.kubernetes.io/router.middlewares: {namespace}-{middleware}@kubernetescrd
141
- spec:
142
- ingressClassName: {ingress_class}
143
- rules:
144
- - host: {host}
145
- http:
146
- paths:
147
- - path: /
148
- pathType: Prefix
149
- backend:
150
- service:
151
- name: {service_name}
152
- port:
153
- number: {port}
154
- tls:
155
- - secretName: {tls_secret}
156
- hosts:
157
- - {host}
158
- """
159
- )
160
-
161
- return "---\n".join(documents)
162
-
163
-
164
- def run(ctx: ExecutionContext) -> None:
165
- require_root(ctx)
166
-
167
- typer.secho("⚠️ AVISO DE SEGURANÇA", fg=typer.colors.RED, bold=True)
168
- typer.secho(
169
- "\nExpor ferramentas de observabilidade publicamente é um RISCO DE SEGURANÇA significativo!",
170
- fg=typer.colors.YELLOW,
171
- )
172
- typer.secho(
173
- "Mesmo com TLS e BasicAuth, você estará expondo informações sensíveis do cluster.\n",
174
- fg=typer.colors.YELLOW,
175
- )
176
- typer.secho("✅ RECOMENDAÇÃO FORTE:", fg=typer.colors.GREEN, bold=True)
177
- typer.echo("1. Configure VPN: sudo raijin vpn")
178
- typer.echo("2. Use port-forward via VPN para acesso seguro")
179
- typer.echo("3. Mantenha os dashboards APENAS na rede interna\n")
180
-
181
- proceed = typer.confirm(
182
- "Você REALMENTE quer expor os dashboards publicamente?",
183
- default=False
184
- )
185
-
186
- if not proceed:
187
- typer.secho("\n✓ Boa decisão! Use VPN para acesso seguro.", fg=typer.colors.GREEN)
188
- typer.echo("\nPara acessar via VPN + port-forward:")
189
- typer.echo(" kubectl -n observability port-forward svc/grafana 3000:80")
190
- typer.echo(" kubectl -n observability port-forward svc/kube-prometheus-stack-prometheus 9090:9090")
191
- typer.echo(" kubectl -n observability port-forward svc/kube-prometheus-stack-alertmanager 9093:9093")
192
- raise typer.Exit(0)
193
-
194
- typer.echo("\nProvisionando ingress seguro para observabilidade...")
195
-
196
- namespace = typer.prompt("Namespace dos componentes", default="observability")
197
- ingress_class = typer.prompt("IngressClass dedicada", default="traefik")
198
- username = typer.prompt("Usuario para basic auth", default="observability")
199
- password = typer.prompt(
200
- "Senha para basic auth",
201
- hide_input=True,
202
- confirmation_prompt=True,
203
- )
204
-
205
- configured: List[Dict[str, object]] = []
206
- for comp in COMPONENTS:
207
- host = typer.prompt(f"Host para {comp['label']}", default=comp["default_host"])
208
- tls_secret = typer.prompt(
209
- f"Secret TLS para {comp['label']}",
210
- default=comp["default_tls"],
211
- )
212
- configured.append({**comp, "host": host, "tls_secret": tls_secret})
213
-
214
- issue_certs = typer.confirm("Gerar Certificates via cert-manager?", default=True)
215
- issuer_name = ""
216
- issuer_kind = "ClusterIssuer"
217
- if issue_certs:
218
- issuer_name = typer.prompt("Nome do issuer (cert-manager)", default="letsencrypt-prod")
219
- issuer_kind = typer.prompt("Tipo do issuer (Issuer/ClusterIssuer)", default="ClusterIssuer")
220
-
221
- secret_line = _generate_htpasswd(username, password)
222
- encoded_users = base64.b64encode(secret_line.encode()).decode()
223
-
224
- kubectl_create_ns(namespace, ctx)
225
-
226
- manifest = _build_manifest(
227
- namespace,
228
- ingress_class,
229
- configured,
230
- encoded_users,
231
- issuer_name,
232
- issuer_kind,
233
- )
234
-
235
- write_file(MANIFEST_PATH, manifest, ctx)
236
- kubectl_apply(str(MANIFEST_PATH), ctx)
237
-
238
- typer.secho("Ingress seguro aplicado com sucesso.", fg=typer.colors.GREEN)
239
- typer.echo("Hosts publicados:")
240
- for svc in configured:
241
- typer.echo(f" - {svc['label']}: https://{svc['host']}")
242
- if not issuer_name:
243
- typer.secho(
244
- "Certificados nao foram criados automaticamente. Certifique-se de que os secrets TLS existem.",
245
- fg=typer.colors.YELLOW,
246
- )