raijin-server 0.1.0__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.
@@ -0,0 +1,131 @@
1
+ """Instalacao completa e automatizada do ambiente produtivo."""
2
+
3
+ import typer
4
+
5
+ from raijin_server.utils import ExecutionContext, require_root
6
+ from raijin_server.modules import (
7
+ bootstrap,
8
+ essentials,
9
+ hardening,
10
+ network,
11
+ firewall,
12
+ kubernetes,
13
+ calico,
14
+ prometheus,
15
+ grafana,
16
+ loki,
17
+ traefik,
18
+ )
19
+
20
+
21
+ # Ordem de execucao dos modulos para instalacao completa
22
+ INSTALL_SEQUENCE = [
23
+ ("bootstrap", bootstrap.run, "Instalacao de ferramentas (helm, kubectl, containerd, etc.)"),
24
+ ("essentials", essentials.run, "Pacotes essenciais e NTP"),
25
+ ("hardening", hardening.run, "Seguranca do sistema (fail2ban, sysctl, auditd)"),
26
+ ("network", network.run, "Configuracao de rede (IP fixo)"),
27
+ ("firewall", firewall.run, "Firewall UFW"),
28
+ ("kubernetes", kubernetes.run, "Cluster Kubernetes (kubeadm)"),
29
+ ("calico", calico.run, "CNI Calico + NetworkPolicy"),
30
+ ("prometheus", prometheus.run, "Monitoramento Prometheus"),
31
+ ("grafana", grafana.run, "Dashboards Grafana"),
32
+ ("loki", loki.run, "Logs centralizados Loki"),
33
+ ("traefik", traefik.run, "Ingress Controller Traefik"),
34
+ ]
35
+
36
+
37
+ def run(ctx: ExecutionContext) -> None:
38
+ """Executa instalacao completa do ambiente produtivo."""
39
+ require_root(ctx)
40
+
41
+ typer.secho(
42
+ "\n" + "=" * 60,
43
+ fg=typer.colors.CYAN,
44
+ )
45
+ typer.secho(
46
+ " RAIJIN SERVER - Instalacao Completa Automatizada",
47
+ fg=typer.colors.CYAN,
48
+ bold=True,
49
+ )
50
+ typer.secho(
51
+ "=" * 60 + "\n",
52
+ fg=typer.colors.CYAN,
53
+ )
54
+
55
+ # Mostra sequencia de instalacao
56
+ typer.echo("Sequencia de instalacao:")
57
+ for i, (name, _, desc) in enumerate(INSTALL_SEQUENCE, 1):
58
+ typer.echo(f" {i:2}. {name:15} - {desc}")
59
+
60
+ typer.echo("")
61
+
62
+ if not ctx.dry_run:
63
+ if not typer.confirm("Deseja continuar com a instalacao completa?", default=True):
64
+ typer.echo("Instalacao cancelada.")
65
+ raise typer.Exit(code=0)
66
+
67
+ total = len(INSTALL_SEQUENCE)
68
+ failed = []
69
+ succeeded = []
70
+
71
+ for i, (name, handler, desc) in enumerate(INSTALL_SEQUENCE, 1):
72
+ typer.secho(
73
+ f"\n{'='*60}",
74
+ fg=typer.colors.CYAN,
75
+ )
76
+ typer.secho(
77
+ f"[{i}/{total}] {name.upper()}: {desc}",
78
+ fg=typer.colors.CYAN,
79
+ bold=True,
80
+ )
81
+ typer.secho(
82
+ f"{'='*60}\n",
83
+ fg=typer.colors.CYAN,
84
+ )
85
+
86
+ try:
87
+ handler(ctx)
88
+ succeeded.append(name)
89
+ typer.secho(f"✓ {name} concluido com sucesso", fg=typer.colors.GREEN)
90
+ except KeyboardInterrupt:
91
+ typer.secho(f"\n⚠ Instalacao interrompida pelo usuario no modulo '{name}'", fg=typer.colors.YELLOW)
92
+ raise typer.Exit(code=130)
93
+ except Exception as e:
94
+ failed.append((name, str(e)))
95
+ typer.secho(f"✗ {name} falhou: {e}", fg=typer.colors.RED)
96
+
97
+ if not ctx.dry_run:
98
+ if not typer.confirm("Continuar com os proximos modulos?", default=True):
99
+ break
100
+
101
+ # Resumo final
102
+ typer.echo("\n" + "=" * 60)
103
+ typer.secho("RESUMO DA INSTALACAO", fg=typer.colors.CYAN, bold=True)
104
+ typer.echo("=" * 60)
105
+
106
+ if succeeded:
107
+ typer.secho(f"\n✓ Modulos instalados com sucesso ({len(succeeded)}):", fg=typer.colors.GREEN)
108
+ for name in succeeded:
109
+ typer.echo(f" - {name}")
110
+
111
+ if failed:
112
+ typer.secho(f"\n✗ Modulos com falha ({len(failed)}):", fg=typer.colors.RED)
113
+ for name, error in failed:
114
+ typer.echo(f" - {name}: {error}")
115
+
116
+ if not failed:
117
+ typer.secho(
118
+ "\n✓ INSTALACAO COMPLETA COM SUCESSO!",
119
+ fg=typer.colors.GREEN,
120
+ bold=True,
121
+ )
122
+ typer.echo("\nProximos passos:")
123
+ typer.echo(" 1. Verifique o cluster: kubectl get nodes")
124
+ typer.echo(" 2. Acesse Grafana: kubectl port-forward svc/grafana 3000:80 -n observability")
125
+ typer.echo(" 3. Adicione workers: kubeadm token create --print-join-command")
126
+ else:
127
+ typer.secho(
128
+ f"\n⚠ Instalacao concluida com {len(failed)} erro(s). Verifique os logs.",
129
+ fg=typer.colors.YELLOW,
130
+ bold=True,
131
+ )
@@ -0,0 +1,69 @@
1
+ """Configuracao do Grafana via Helm com datasource e dashboards provisionados."""
2
+
3
+ from pathlib import Path
4
+
5
+ import typer
6
+
7
+ from raijin_server.utils import ExecutionContext, helm_upgrade_install, require_root, write_file
8
+
9
+
10
+ def run(ctx: ExecutionContext) -> None:
11
+ require_root(ctx)
12
+ typer.echo("Instalando Grafana via Helm...")
13
+
14
+ admin_password = typer.prompt("Senha admin do Grafana", default="admin")
15
+ ingress_host = typer.prompt("Host para acessar o Grafana", default="grafana.local")
16
+ ingress_class = typer.prompt("IngressClass", default="traefik")
17
+ tls_secret = typer.prompt("Secret TLS (cert-manager)", default="grafana-tls")
18
+
19
+ values_yaml = f"""adminPassword: {admin_password}
20
+ service:
21
+ type: ClusterIP
22
+ ingress:
23
+ enabled: true
24
+ ingressClassName: {ingress_class}
25
+ hosts:
26
+ - {ingress_host}
27
+ tls:
28
+ - secretName: {tls_secret}
29
+ hosts:
30
+ - {ingress_host}
31
+ persistence:
32
+ enabled: true
33
+ size: 10Gi
34
+ datasources:
35
+ datasources.yaml:
36
+ apiVersion: 1
37
+ datasources:
38
+ - name: Prometheus
39
+ type: prometheus
40
+ access: proxy
41
+ url: http://kube-prometheus-stack-prometheus.observability.svc:9090
42
+ isDefault: true
43
+ jsonData:
44
+ timeInterval: 30s
45
+ dashboards:
46
+ default:
47
+ kubernetes:
48
+ gnetId: 6417
49
+ revision: 1
50
+ datasource: Prometheus
51
+ node-exporter:
52
+ gnetId: 1860
53
+ revision: 27
54
+ datasource: Prometheus
55
+ """
56
+
57
+ values_path = Path("/tmp/raijin-grafana-values.yaml")
58
+ write_file(values_path, values_yaml, ctx)
59
+
60
+ helm_upgrade_install(
61
+ release="grafana",
62
+ chart="grafana",
63
+ namespace="observability",
64
+ repo="grafana",
65
+ repo_url="https://grafana.github.io/helm-charts",
66
+ ctx=ctx,
67
+ values=[],
68
+ extra_args=["-f", str(values_path)],
69
+ )
@@ -0,0 +1,47 @@
1
+ """Tarefas de hardening do sistema."""
2
+
3
+ import subprocess
4
+
5
+ import typer
6
+
7
+ from raijin_server.utils import ExecutionContext, apt_install, apt_update, enable_service, require_root, run_cmd
8
+
9
+
10
+ def run(ctx: ExecutionContext) -> None:
11
+ require_root(ctx)
12
+ typer.echo("Aplicando hardening basico...")
13
+
14
+ apt_update(ctx)
15
+ apt_install(["fail2ban", "unattended-upgrades", "auditd"], ctx)
16
+
17
+ enable_service("fail2ban", ctx)
18
+ # Tenta iniciar auditd mas nao falha o fluxo se o kernel estiver sem auditing.
19
+ run_cmd(["systemctl", "enable", "--now", "auditd"], ctx, check=False)
20
+ if not ctx.dry_run:
21
+ status = subprocess.run(
22
+ ["systemctl", "is-active", "auditd"],
23
+ capture_output=True,
24
+ text=True,
25
+ )
26
+ if status.returncode != 0 or status.stdout.strip() != "active":
27
+ typer.secho(
28
+ "auditd nao ficou ativo. Verifique se o kernel esta com auditing habilitado (audit=1 no boot).",
29
+ fg=typer.colors.YELLOW,
30
+ )
31
+ run_cmd(
32
+ ["dpkg-reconfigure", "--priority=low", "unattended-upgrades"],
33
+ ctx,
34
+ env={"DEBIAN_FRONTEND": "noninteractive"},
35
+ )
36
+
37
+ # Reforca parametros de rede em tempo de execucao. Ajuste conforme necessidade.
38
+ sysctls = [
39
+ "net.ipv4.conf.all.rp_filter=1",
40
+ "net.ipv4.conf.default.rp_filter=1",
41
+ "net.ipv4.conf.all.accept_redirects=0",
42
+ "net.ipv4.conf.default.accept_redirects=0",
43
+ "net.ipv4.conf.all.send_redirects=0",
44
+ "net.ipv4.ip_forward=0",
45
+ ]
46
+ for param in sysctls:
47
+ run_cmd(["sysctl", "-w", param], ctx)
@@ -0,0 +1,47 @@
1
+ """Instalacao do Harness Delegate via Helm."""
2
+
3
+ import typer
4
+
5
+ from raijin_server.utils import ExecutionContext, ensure_tool, require_root, run_cmd
6
+
7
+
8
+ def run(ctx: ExecutionContext) -> None:
9
+ require_root(ctx)
10
+ ensure_tool("helm", ctx, install_hint="Instale helm para implantar o delegate.")
11
+
12
+ typer.echo("Instalando Harness Delegate via Helm...")
13
+ account_id = typer.prompt("Harness accountId")
14
+ org_id = typer.prompt("Org ID", default="default")
15
+ project_id = typer.prompt("Project ID", default="default")
16
+ delegate_name = typer.prompt("Delegate name", default="raijin-delegate")
17
+ namespace = typer.prompt("Namespace", default="harness-delegate")
18
+ delegate_token = typer.prompt("Delegate token", hide_input=True)
19
+
20
+ run_cmd(
21
+ ["helm", "repo", "add", "harness", "https://app.harness.io/storage/harness-download/delegate-helm-chart/"],
22
+ ctx,
23
+ )
24
+ run_cmd(["helm", "repo", "update"], ctx)
25
+
26
+ cmd = [
27
+ "helm",
28
+ "upgrade",
29
+ "--install",
30
+ delegate_name,
31
+ "harness/harness-delegate-ng",
32
+ "-n",
33
+ namespace,
34
+ "--create-namespace",
35
+ "--set",
36
+ f"delegateName={delegate_name}",
37
+ "--set",
38
+ f"accountId={account_id}",
39
+ "--set",
40
+ f"delegateToken={delegate_token}",
41
+ "--set",
42
+ f"orgId={org_id}",
43
+ "--set",
44
+ f"projectId={project_id}",
45
+ ]
46
+
47
+ run_cmd(cmd, ctx, mask_output=True, display_override="helm upgrade --install <delegate> harness/harness-delegate-ng ...")
@@ -0,0 +1,13 @@
1
+ """Instalacao do Istio usando istioctl."""
2
+
3
+ import typer
4
+
5
+ from raijin_server.utils import ExecutionContext, ensure_tool, require_root, run_cmd
6
+
7
+
8
+ def run(ctx: ExecutionContext) -> None:
9
+ require_root(ctx)
10
+ ensure_tool("istioctl", ctx, install_hint="Instale o binario do Istio CLI.")
11
+ typer.echo("Instalando Istio (perfil raijin)...")
12
+ run_cmd(["istioctl", "install", "--set", "profile=raijin", "-y"], ctx)
13
+ run_cmd(["kubectl", "label", "namespace", "default", "istio-injection=enabled", "--overwrite"], ctx)
@@ -0,0 +1,34 @@
1
+ """Deploy do Apache Kafka via Helm (Bitnami OCI)."""
2
+
3
+ import typer
4
+
5
+ from raijin_server.utils import ExecutionContext, helm_upgrade_install, require_root
6
+
7
+
8
+ def run(ctx: ExecutionContext) -> None:
9
+ require_root(ctx)
10
+ typer.echo("Instalando Kafka (Bitnami) via Helm OCI...")
11
+
12
+ replicas = typer.prompt("Numero de brokers", default="3")
13
+ disk_size = typer.prompt("Storage por broker", default="20Gi")
14
+
15
+ # Bitnami charts migraram para OCI. Usamos a referencia OCI diretamente.
16
+ chart_ref = "oci://registry-1.docker.io/bitnamicharts/kafka"
17
+
18
+ values = [
19
+ f"replicaCount={replicas}",
20
+ "zookeeper.enabled=true",
21
+ f"persistence.size={disk_size}",
22
+ "metrics.kafka.enabled=true",
23
+ "metrics.jmx.enabled=true",
24
+ ]
25
+
26
+ helm_upgrade_install(
27
+ release="kafka",
28
+ chart=chart_ref,
29
+ namespace="kafka",
30
+ repo=None,
31
+ repo_url=None,
32
+ ctx=ctx,
33
+ values=values,
34
+ )
@@ -0,0 +1,19 @@
1
+ """Configuracao do Kong via Helm."""
2
+
3
+ import typer
4
+
5
+ from raijin_server.utils import ExecutionContext, helm_upgrade_install, require_root
6
+
7
+
8
+ def run(ctx: ExecutionContext) -> None:
9
+ require_root(ctx)
10
+ typer.echo("Instalando Kong via Helm...")
11
+ helm_upgrade_install(
12
+ release="kong",
13
+ chart="kong",
14
+ namespace="kong",
15
+ repo="kong",
16
+ repo_url="https://charts.konghq.com",
17
+ ctx=ctx,
18
+ values=["ingressController.installCRDs=false"],
19
+ )
@@ -0,0 +1,187 @@
1
+ """Preparacao de cluster Kubernetes com kubeadm."""
2
+
3
+ import os
4
+ from pathlib import Path
5
+
6
+ import typer
7
+
8
+ from raijin_server.utils import (
9
+ ExecutionContext,
10
+ apt_install,
11
+ apt_update,
12
+ enable_service,
13
+ ensure_tool,
14
+ require_root,
15
+ run_cmd,
16
+ write_file,
17
+ )
18
+
19
+
20
+ def _cleanup_old_repo(ctx: ExecutionContext) -> None:
21
+ """Remove repo legado apt.kubernetes.io se existir para evitar erro 404."""
22
+
23
+ repo_file = Path("/etc/apt/sources.list.d/kubernetes.list")
24
+ if ctx.dry_run:
25
+ typer.echo("[dry-run] checando repos antigos em /etc/apt/sources.list.d/kubernetes.list")
26
+ return
27
+
28
+ if repo_file.exists():
29
+ try:
30
+ content = repo_file.read_text()
31
+ if "apt.kubernetes.io" in content:
32
+ repo_file.unlink()
33
+ typer.echo("Repo antigo apt.kubernetes.io removido.")
34
+ except Exception:
35
+ typer.echo("Nao foi possivel ler/remover repo antigo apt.kubernetes.io (ok prosseguir).")
36
+
37
+
38
+ def run(ctx: ExecutionContext) -> None:
39
+ require_root(ctx)
40
+ typer.echo("Instalando e preparando Kubernetes (kubeadm/kubelet/kubectl)...")
41
+
42
+ # Verifica se cluster ja foi inicializado
43
+ kubeconfig_exists = Path("/etc/kubernetes/admin.conf").exists()
44
+ if kubeconfig_exists and not ctx.dry_run:
45
+ typer.secho("⚠ Cluster Kubernetes ja parece estar inicializado.", fg=typer.colors.YELLOW)
46
+ if not typer.confirm("Deseja continuar mesmo assim? (pode causar problemas)"):
47
+ typer.echo("Operacao cancelada pelo usuario.")
48
+ return
49
+
50
+ _cleanup_old_repo(ctx)
51
+ apt_update(ctx)
52
+ apt_install(
53
+ [
54
+ "apt-transport-https",
55
+ "ca-certificates",
56
+ "curl",
57
+ "gnupg",
58
+ "lsb-release",
59
+ "containerd",
60
+ ],
61
+ ctx,
62
+ )
63
+
64
+ # Novo repo oficial (pkgs.k8s.io) substitui apt.kubernetes.io, que nao tem Release em distros recentes.
65
+ repo_version = "v1.30"
66
+ key_url = f"https://pkgs.k8s.io/core:/stable:/{repo_version}/deb/Release.key"
67
+ key_tmp = Path("/tmp/kubernetes-release.key")
68
+ key_path = Path("/etc/apt/keyrings/kubernetes-apt-keyring.gpg")
69
+ repo_entry = f"deb [signed-by={key_path}] https://pkgs.k8s.io/core:/stable:/{repo_version}/deb/ /"
70
+
71
+ # Verifica se ja tem a chave instalada
72
+ if not key_path.exists() or ctx.dry_run:
73
+ typer.echo("Baixando chave GPG do Kubernetes (pkgs.k8s.io)...")
74
+ run_cmd(
75
+ [
76
+ "curl",
77
+ "-fsSL",
78
+ "--retry",
79
+ "3",
80
+ "--retry-delay",
81
+ "2",
82
+ "-o",
83
+ str(key_tmp),
84
+ key_url,
85
+ ],
86
+ ctx,
87
+ )
88
+
89
+ run_cmd(["mkdir", "-p", "/etc/apt/keyrings"], ctx)
90
+
91
+ if not ctx.dry_run:
92
+ if not key_tmp.exists() or key_tmp.stat().st_size == 0:
93
+ typer.secho("Falha ao baixar chave GPG do Kubernetes. Verifique conectividade.", fg=typer.colors.RED)
94
+ raise typer.Exit(code=1)
95
+ run_cmd(["gpg", "--yes", "--dearmor", "-o", str(key_path), str(key_tmp)], ctx)
96
+ if not key_path.exists() or key_path.stat().st_size == 0:
97
+ typer.secho("Chave GPG nao foi gravada corretamente.", fg=typer.colors.RED)
98
+ raise typer.Exit(code=1)
99
+ key_tmp.unlink(missing_ok=True)
100
+ else:
101
+ typer.echo("Chave GPG do Kubernetes ja existe, pulando download.")
102
+
103
+ repo_file = Path("/etc/apt/sources.list.d/kubernetes.list")
104
+ if not repo_file.exists() or ctx.dry_run:
105
+ run_cmd(
106
+ f'echo "{repo_entry}" | tee /etc/apt/sources.list.d/kubernetes.list',
107
+ ctx,
108
+ use_shell=True,
109
+ )
110
+
111
+ apt_update(ctx)
112
+ apt_install(["kubelet", "kubeadm", "kubectl"], ctx)
113
+
114
+ # Hold das versoes para evitar upgrades automaticos
115
+ run_cmd(["apt-mark", "hold", "kubelet", "kubeadm", "kubectl"], ctx, check=False)
116
+
117
+ # Configura containerd com SystemdCgroup.
118
+ run_cmd(["mkdir", "-p", "/etc/containerd"], ctx)
119
+
120
+ # Verifica se config do containerd ja existe
121
+ containerd_config = Path("/etc/containerd/config.toml")
122
+ if not containerd_config.exists() or ctx.dry_run:
123
+ run_cmd("containerd config default > /etc/containerd/config.toml", ctx, use_shell=True)
124
+
125
+ # Aplica SystemdCgroup
126
+ run_cmd("sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml", ctx, use_shell=True)
127
+ run_cmd(["systemctl", "restart", "containerd"], ctx, check=False)
128
+
129
+ enable_service("containerd", ctx)
130
+ enable_service("kubelet", ctx)
131
+
132
+ # kubeadm exige ip_forward=1; sobrepoe ajuste de hardening para fase de cluster.
133
+ write_file(Path("/etc/sysctl.d/99-kubernetes.conf"), "net.ipv4.ip_forward=1\n", ctx)
134
+ run_cmd(["sysctl", "-w", "net.ipv4.ip_forward=1"], ctx, check=False)
135
+
136
+ # Prompts de configuracao
137
+ pod_cidr = typer.prompt("Pod CIDR", default="10.244.0.0/16")
138
+ service_cidr = typer.prompt("Service CIDR", default="10.96.0.0/12")
139
+ cluster_name = typer.prompt("Nome do cluster", default="raijin")
140
+ advertise_address = typer.prompt("API advertise address", default="0.0.0.0")
141
+
142
+ kubeadm_config = f"""apiVersion: kubeadm.k8s.io/v1beta3
143
+ kind: ClusterConfiguration
144
+ clusterName: {cluster_name}
145
+ kubernetesVersion: stable
146
+ controlPlaneEndpoint: {advertise_address}:6443
147
+ networking:
148
+ podSubnet: {pod_cidr}
149
+ serviceSubnet: {service_cidr}
150
+ apiServer:
151
+ extraArgs:
152
+ authorization-mode: Node,RBAC
153
+ timeoutForControlPlane: 4m0s
154
+ controllerManager: {{}}
155
+ scheduler: {{}}
156
+ ---
157
+ apiVersion: kubeproxy.config.k8s.io/v1alpha1
158
+ kind: KubeProxyConfiguration
159
+ mode: ipvs
160
+ ---
161
+ apiVersion: kubelet.config.k8s.io/v1beta1
162
+ kind: KubeletConfiguration
163
+ cgroupDriver: systemd
164
+ """
165
+
166
+ cfg_path = Path("/etc/kubernetes/kubeadm-config.yaml")
167
+ write_file(cfg_path, kubeadm_config, ctx)
168
+
169
+ ensure_tool("kubeadm", ctx, install_hint="Instale kubeadm (apt install kubeadm).")
170
+ run_cmd(["kubeadm", "config", "images", "pull"], ctx, check=False)
171
+ run_cmd(["kubeadm", "init", "--config", str(cfg_path), "--upload-certs"], ctx)
172
+
173
+ # Configura kubeconfig para root e sudoer.
174
+ run_cmd(["mkdir", "-p", "/root/.kube"], ctx)
175
+ run_cmd(["cp", "/etc/kubernetes/admin.conf", "/root/.kube/config"], ctx)
176
+ run_cmd("chown $(id -u):$(id -g) /root/.kube/config", ctx, use_shell=True)
177
+
178
+ sudo_user = os.environ.get("SUDO_USER")
179
+ if sudo_user:
180
+ user_home = Path(f"/home/{sudo_user}")
181
+ kube_dir = user_home / ".kube"
182
+ run_cmd(["mkdir", "-p", str(kube_dir)], ctx)
183
+ run_cmd(["cp", "/etc/kubernetes/admin.conf", str(kube_dir / "config")], ctx)
184
+ run_cmd(["chown", f"{sudo_user}:{sudo_user}", str(kube_dir / "config")], ctx)
185
+
186
+ typer.echo("Comando de join para workers:")
187
+ run_cmd(["kubeadm", "token", "create", "--print-join-command"], ctx, check=False)
@@ -0,0 +1,27 @@
1
+ """Configuracao do Loki via Helm."""
2
+
3
+ import typer
4
+
5
+ from raijin_server.utils import ExecutionContext, helm_upgrade_install, require_root
6
+
7
+
8
+ def run(ctx: ExecutionContext) -> None:
9
+ require_root(ctx)
10
+ typer.echo("Instalando Loki Stack via Helm...")
11
+
12
+ values = [
13
+ "promtail.enabled=true",
14
+ "loki.persistence.enabled=true",
15
+ "loki.persistence.size=20Gi",
16
+ "loki.retentionPeriod=168h", # 7 dias
17
+ ]
18
+
19
+ helm_upgrade_install(
20
+ release="loki",
21
+ chart="loki-stack",
22
+ namespace="observability",
23
+ repo="grafana",
24
+ repo_url="https://grafana.github.io/helm-charts",
25
+ ctx=ctx,
26
+ values=values,
27
+ )
@@ -0,0 +1,19 @@
1
+ """Deploy do MinIO via Helm."""
2
+
3
+ import typer
4
+
5
+ from raijin_server.utils import ExecutionContext, helm_upgrade_install, require_root
6
+
7
+
8
+ def run(ctx: ExecutionContext) -> None:
9
+ require_root(ctx)
10
+ typer.echo("Instalando MinIO via Helm (modo standalone)...")
11
+ helm_upgrade_install(
12
+ release="minio",
13
+ chart="minio",
14
+ namespace="minio",
15
+ repo="minio",
16
+ repo_url="https://charts.min.io/",
17
+ ctx=ctx,
18
+ values=["mode=standalone"],
19
+ )
@@ -0,0 +1,57 @@
1
+ """Configuracao de rede (IP fixo) via Netplan."""
2
+
3
+ import os
4
+ from pathlib import Path
5
+
6
+ import typer
7
+
8
+ from raijin_server.utils import ExecutionContext, require_root, run_cmd, write_file
9
+
10
+
11
+ def _is_wsl() -> bool:
12
+ if os.environ.get("WSL_DISTRO_NAME"):
13
+ return True
14
+ try:
15
+ data = Path("/proc/version").read_text().lower()
16
+ return "microsoft" in data or "wsl" in data
17
+ except OSError:
18
+ return False
19
+
20
+
21
+ def run(ctx: ExecutionContext) -> None:
22
+ require_root(ctx)
23
+ typer.echo("Configurando IP fixo (Netplan)...")
24
+
25
+ wsl = _is_wsl()
26
+ if wsl:
27
+ typer.secho(
28
+ "Ambiente WSL detectado: netplan pode nao ter efeito ou quebrar rede. Use apenas para gerar arquivo.",
29
+ fg=typer.colors.YELLOW,
30
+ )
31
+
32
+ iface = typer.prompt("Interface", default="ens18")
33
+ address = typer.prompt("Endereco CIDR", default="192.168.0.10/24")
34
+ gateway = typer.prompt("Gateway", default="192.168.0.1")
35
+ dns = typer.prompt("DNS (separe por virgula)", default="1.1.1.1,8.8.8.8")
36
+
37
+ dns_list = ",".join([item.strip() for item in dns.split(",") if item.strip()])
38
+ netplan_content = f"""network:
39
+ version: 2
40
+ renderer: networkd
41
+ ethernets:
42
+ {iface}:
43
+ dhcp4: false
44
+ addresses: [{address}]
45
+ gateway4: {gateway}
46
+ nameservers:
47
+ addresses: [{dns_list}]
48
+ """
49
+
50
+ target = Path("/etc/netplan/01-raijin-static.yaml")
51
+ write_file(target, netplan_content, ctx)
52
+
53
+ apply_now = typer.confirm("Aplicar netplan agora?", default=not wsl)
54
+ if apply_now:
55
+ run_cmd(["netplan", "apply"], ctx)
56
+ else:
57
+ typer.echo("Netplan nao aplicado (apenas arquivo gerado).")