raijin-server 0.2.1__py3-none-any.whl → 0.2.3__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 CHANGED
@@ -1,5 +1,5 @@
1
1
  """Pacote principal do CLI Raijin Server."""
2
2
 
3
- __version__ = "0.2.1"
3
+ __version__ = "0.2.3"
4
4
 
5
5
  __all__ = ["__version__"]
@@ -87,17 +87,37 @@ def _install_istioctl(ctx: ExecutionContext) -> None:
87
87
  return
88
88
 
89
89
  typer.echo(f"Instalando istioctl v{ISTIOCTL_VERSION}...")
90
+
91
+ # Limpa tentativas anteriores
92
+ istio_dir = Path(f"/tmp/istio-{ISTIOCTL_VERSION}")
93
+ if istio_dir.exists() and not ctx.dry_run:
94
+ run_cmd(["rm", "-rf", str(istio_dir)], ctx, check=False)
95
+
96
+ # Download do Istio
90
97
  run_cmd(
91
98
  f"curl -L https://istio.io/downloadIstio | ISTIO_VERSION={ISTIOCTL_VERSION} sh -",
92
99
  ctx,
93
100
  use_shell=True,
94
101
  cwd="/tmp",
95
102
  )
103
+
104
+ # Verifica se o download foi bem sucedido
105
+ istioctl_path = Path(f"/tmp/istio-{ISTIOCTL_VERSION}/bin/istioctl")
106
+ if not ctx.dry_run and not istioctl_path.exists():
107
+ typer.secho(
108
+ f"Falha ao baixar istioctl. Arquivo nao encontrado: {istioctl_path}",
109
+ fg=typer.colors.RED,
110
+ )
111
+ typer.secho("Verifique sua conexao de internet e tente novamente.", fg=typer.colors.YELLOW)
112
+ ctx.warnings.append("istioctl nao instalado - download falhou")
113
+ return # Nao falha o modulo inteiro, apenas avisa
114
+
96
115
  run_cmd(
97
- ["mv", f"/tmp/istio-{ISTIOCTL_VERSION}/bin/istioctl", "/usr/local/bin/istioctl"],
116
+ ["mv", str(istioctl_path), "/usr/local/bin/istioctl"],
98
117
  ctx,
99
118
  )
100
119
  run_cmd(["chmod", "+x", "/usr/local/bin/istioctl"], ctx)
120
+ typer.secho("✓ istioctl instalado com sucesso.", fg=typer.colors.GREEN)
101
121
 
102
122
 
103
123
  def _install_velero(ctx: ExecutionContext) -> None:
@@ -62,11 +62,40 @@ def _split_namespaces(raw_value: str) -> Iterable[str]:
62
62
  return [ns.strip() for ns in raw_value.split(",") if ns.strip()]
63
63
 
64
64
 
65
+ def _check_cluster_available(ctx: ExecutionContext) -> bool:
66
+ """Verifica se o cluster Kubernetes esta acessivel."""
67
+ if ctx.dry_run:
68
+ return True
69
+ try:
70
+ result = run_cmd(
71
+ ["kubectl", "cluster-info"],
72
+ ctx,
73
+ check=False,
74
+ retries=1,
75
+ )
76
+ return result.returncode == 0
77
+ except Exception:
78
+ return False
79
+
80
+
65
81
  def run(ctx: ExecutionContext) -> None:
66
82
  require_root(ctx)
67
83
  ensure_tool("kubectl", ctx, install_hint="Instale kubectl ou habilite dry-run.")
68
84
  ensure_tool("curl", ctx, install_hint="Instale curl.")
69
85
 
86
+ # Verifica se cluster esta disponivel antes de aplicar
87
+ if not _check_cluster_available(ctx):
88
+ typer.secho(
89
+ "✗ Cluster Kubernetes nao esta acessivel. Execute o modulo 'kubernetes' primeiro.",
90
+ fg=typer.colors.RED,
91
+ )
92
+ typer.secho(
93
+ " Verifique: kubectl cluster-info",
94
+ fg=typer.colors.YELLOW,
95
+ )
96
+ ctx.errors.append("Calico: cluster nao acessivel")
97
+ raise typer.Exit(code=1)
98
+
70
99
  typer.echo("Aplicando Calico como CNI...")
71
100
  pod_cidr = typer.prompt("Pod CIDR (Calico)", default="10.244.0.0/16")
72
101
 
@@ -79,11 +79,40 @@ stringData:
79
79
  """
80
80
 
81
81
 
82
+ def _check_cluster_available(ctx: ExecutionContext) -> bool:
83
+ """Verifica se o cluster Kubernetes esta acessivel."""
84
+ if ctx.dry_run:
85
+ return True
86
+ try:
87
+ import subprocess
88
+ result = subprocess.run(
89
+ ["kubectl", "cluster-info"],
90
+ capture_output=True,
91
+ timeout=30,
92
+ )
93
+ return result.returncode == 0
94
+ except Exception:
95
+ return False
96
+
97
+
82
98
  def run(ctx: ExecutionContext) -> None:
83
99
  require_root(ctx)
84
100
  ensure_tool("helm", ctx, install_hint="Instale helm ou use --dry-run para simular.")
85
101
  ensure_tool("kubectl", ctx, install_hint="Instale kubectl ou use --dry-run para simular.")
86
102
 
103
+ # Verifica se cluster esta disponivel antes de instalar
104
+ if not _check_cluster_available(ctx):
105
+ typer.secho(
106
+ "✗ Cluster Kubernetes nao esta acessivel. Execute o modulo 'kubernetes' primeiro.",
107
+ fg=typer.colors.RED,
108
+ )
109
+ typer.secho(
110
+ " Verifique: kubectl cluster-info",
111
+ fg=typer.colors.YELLOW,
112
+ )
113
+ ctx.errors.append("cert-manager: cluster nao acessivel")
114
+ raise typer.Exit(code=1)
115
+
87
116
  typer.echo("Instalando cert-manager via Helm...")
88
117
  email = typer.prompt("Email para ACME (Let's Encrypt)", default="admin@example.com")
89
118
  solver = typer.prompt("Tipo de desafio (http01/dns01)", default="http01")
@@ -35,6 +35,20 @@ def _cleanup_old_repo(ctx: ExecutionContext) -> None:
35
35
  typer.echo("Nao foi possivel ler/remover repo antigo apt.kubernetes.io (ok prosseguir).")
36
36
 
37
37
 
38
+ def _reset_cluster(ctx: ExecutionContext) -> None:
39
+ """Executa kubeadm reset para limpar instalacao anterior."""
40
+ typer.secho("Limpando instalacao anterior do Kubernetes...", fg=typer.colors.YELLOW)
41
+ run_cmd(["kubeadm", "reset", "-f"], ctx, check=False)
42
+ # Remove configs residuais
43
+ run_cmd(["rm", "-rf", "/etc/kubernetes/manifests", "/etc/kubernetes/pki"], ctx, check=False)
44
+ run_cmd(["rm", "-rf", "/var/lib/etcd"], ctx, check=False)
45
+ run_cmd(["rm", "-rf", "/root/.kube/config"], ctx, check=False)
46
+ # Remove CNI configs
47
+ run_cmd(["rm", "-rf", "/etc/cni/net.d"], ctx, check=False)
48
+ run_cmd(["rm", "-rf", "/var/lib/cni"], ctx, check=False)
49
+ typer.secho("✓ Limpeza concluida.", fg=typer.colors.GREEN)
50
+
51
+
38
52
  def run(ctx: ExecutionContext) -> None:
39
53
  require_root(ctx)
40
54
  typer.echo("Instalando e preparando Kubernetes (kubeadm/kubelet/kubectl)...")
@@ -43,7 +57,10 @@ def run(ctx: ExecutionContext) -> None:
43
57
  kubeconfig_exists = Path("/etc/kubernetes/admin.conf").exists()
44
58
  if kubeconfig_exists and not ctx.dry_run:
45
59
  typer.secho("⚠ Cluster Kubernetes ja parece estar inicializado.", fg=typer.colors.YELLOW)
46
- if not typer.confirm("Deseja continuar mesmo assim? (pode causar problemas)"):
60
+ reset_choice = typer.confirm("Deseja limpar e reinstalar? (recomendado)")
61
+ if reset_choice:
62
+ _reset_cluster(ctx)
63
+ elif not typer.confirm("Deseja continuar sem limpar? (pode causar problemas)"):
47
64
  typer.echo("Operacao cancelada pelo usuario.")
48
65
  return
49
66
 
@@ -130,8 +147,18 @@ def run(ctx: ExecutionContext) -> None:
130
147
  enable_service("kubelet", ctx)
131
148
 
132
149
  # 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)
150
+ # Desabilita IPv6 completamente para evitar erros de preflight e simplificar rede
151
+ sysctl_k8s = """# Kubernetes network settings
152
+ net.ipv4.ip_forward=1
153
+ net.bridge.bridge-nf-call-iptables=1
154
+ net.bridge.bridge-nf-call-ip6tables=1
155
+ # Disable IPv6 completely
156
+ net.ipv6.conf.all.disable_ipv6=1
157
+ net.ipv6.conf.default.disable_ipv6=1
158
+ net.ipv6.conf.lo.disable_ipv6=1
159
+ """
160
+ write_file(Path("/etc/sysctl.d/99-kubernetes.conf"), sysctl_k8s, ctx)
161
+ run_cmd(["sysctl", "--system"], ctx, check=False)
135
162
 
136
163
  # Prompts de configuracao
137
164
  pod_cidr = typer.prompt("Pod CIDR", default="10.244.0.0/16")
@@ -168,7 +195,16 @@ cgroupDriver: systemd
168
195
 
169
196
  ensure_tool("kubeadm", ctx, install_hint="Instale kubeadm (apt install kubeadm).")
170
197
  run_cmd(["kubeadm", "config", "images", "pull"], ctx, check=False)
171
- run_cmd(["kubeadm", "init", "--config", str(cfg_path), "--upload-certs"], ctx)
198
+ # Ignora erros de preflight relacionados a IPv6 (desabilitado)
199
+ run_cmd(
200
+ [
201
+ "kubeadm", "init",
202
+ "--config", str(cfg_path),
203
+ "--upload-certs",
204
+ "--ignore-preflight-errors=FileContent--proc-sys-net-ipv6-conf-default-forwarding",
205
+ ],
206
+ ctx,
207
+ )
172
208
 
173
209
  # Configura kubeconfig para root e sudoer.
174
210
  run_cmd(["mkdir", "-p", "/root/.kube"], ctx)
raijin_server/utils.py CHANGED
@@ -51,9 +51,10 @@ class ExecutionContext:
51
51
 
52
52
  dry_run: bool = False
53
53
  assume_yes: bool = True
54
- max_retries: int = 3
55
- retry_delay: int = 5
56
- timeout: int = 300
54
+ max_retries: int = 5
55
+ retry_delay: int = 10
56
+ retry_backoff: float = 1.5 # Multiplier for exponential backoff
57
+ timeout: int = 600 # 10 min for slow connections
57
58
  errors: list = field(default_factory=list)
58
59
  warnings: list = field(default_factory=list)
59
60
 
@@ -144,14 +145,24 @@ def run_cmd(
144
145
  logger.warning(msg)
145
146
  ctx.warnings.append(msg)
146
147
  if attempt < max_attempts:
147
- time.sleep(ctx.retry_delay)
148
+ backoff_delay = int(ctx.retry_delay * (ctx.retry_backoff ** (attempt - 1)))
149
+ typer.secho(
150
+ f"Timeout! Aguardando {backoff_delay}s antes de tentar novamente...",
151
+ fg=typer.colors.YELLOW,
152
+ )
153
+ time.sleep(backoff_delay)
148
154
  except subprocess.CalledProcessError as e:
149
155
  last_error = e
150
156
  msg = f"Comando falhou com codigo {e.returncode} (tentativa {attempt}/{max_attempts})"
151
157
  logger.error(f"{msg}: {e.stderr if hasattr(e, 'stderr') else ''}")
152
158
  if attempt < max_attempts:
153
- typer.secho(f"Tentando novamente em {ctx.retry_delay}s...", fg=typer.colors.YELLOW)
154
- time.sleep(ctx.retry_delay)
159
+ # Exponential backoff: delay * backoff^(attempt-1)
160
+ backoff_delay = int(ctx.retry_delay * (ctx.retry_backoff ** (attempt - 1)))
161
+ typer.secho(
162
+ f"Tentando novamente em {backoff_delay}s... (possivel instabilidade de rede)",
163
+ fg=typer.colors.YELLOW,
164
+ )
165
+ time.sleep(backoff_delay)
155
166
  else:
156
167
  ctx.errors.append(msg)
157
168
  if check:
@@ -192,8 +203,64 @@ def ensure_tool(name: str, ctx: ExecutionContext, install_hint: str = "") -> Non
192
203
  raise typer.Exit(code=1)
193
204
 
194
205
 
206
+ def _fix_broken_apt_sources(ctx: ExecutionContext) -> None:
207
+ """Corrige repositórios APT quebrados (mirrors brasileiros problemáticos)."""
208
+ if ctx.dry_run:
209
+ typer.echo("[dry-run] Verificando/corrigindo repositórios APT...")
210
+ return
211
+
212
+ sources_list = Path("/etc/apt/sources.list")
213
+
214
+ # Detecta se está usando mirror brasileiro quebrado
215
+ needs_fix = False
216
+ if sources_list.exists():
217
+ content = sources_list.read_text()
218
+ if "br.archive.ubuntu.com" in content or "br.ports.ubuntu.com" in content:
219
+ needs_fix = True
220
+
221
+ if not needs_fix:
222
+ return
223
+
224
+ typer.secho(
225
+ "⚠ Detectado mirror brasileiro possivelmente quebrado. Corrigindo...",
226
+ fg=typer.colors.YELLOW,
227
+ )
228
+ logger.warning("Corrigindo mirror brasileiro quebrado em sources.list")
229
+
230
+ # Backup do original
231
+ backup = sources_list.with_suffix(".list.bak")
232
+ if not backup.exists():
233
+ import shutil as sh
234
+ sh.copy2(sources_list, backup)
235
+
236
+ # Substitui mirror brasileiro pelo principal
237
+ new_content = content.replace("br.archive.ubuntu.com", "archive.ubuntu.com")
238
+ new_content = new_content.replace("br.ports.ubuntu.com", "ports.ubuntu.com")
239
+ sources_list.write_text(new_content)
240
+
241
+ typer.secho("✓ Repositórios corrigidos (backup em sources.list.bak)", fg=typer.colors.GREEN)
242
+
243
+
195
244
  def apt_update(ctx: ExecutionContext) -> None:
196
- run_cmd(["apt-get", "update"], ctx)
245
+ """Executa apt-get update, corrigindo repositórios quebrados se necessário."""
246
+ _fix_broken_apt_sources(ctx)
247
+
248
+ # Tenta o update; se falhar com erro de Release, tenta corrigir
249
+ try:
250
+ run_cmd(["apt-get", "update"], ctx, retries=2)
251
+ except Exception as e:
252
+ error_msg = str(e).lower()
253
+ if "release" in error_msg or "no longer has" in error_msg:
254
+ typer.secho(
255
+ "⚠ Erro de repositório detectado. Tentando fallback...",
256
+ fg=typer.colors.YELLOW,
257
+ )
258
+ # Força correção e tenta novamente
259
+ ctx_temp = ExecutionContext(dry_run=False)
260
+ _fix_broken_apt_sources(ctx_temp)
261
+ run_cmd(["apt-get", "update"], ctx)
262
+ else:
263
+ raise
197
264
 
198
265
 
199
266
  def apt_install(packages: Iterable[str], ctx: ExecutionContext) -> None:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: raijin-server
3
- Version: 0.2.1
3
+ Version: 0.2.3
4
4
  Summary: CLI para automacao de setup e hardening de servidores Ubuntu Server.
5
5
  Home-page: https://example.com/raijin-server
6
6
  Author: Equipe Raijin
@@ -39,11 +39,14 @@ CLI em Python (Typer) para automatizar setup e hardening de servidores Ubuntu Se
39
39
 
40
40
  ## Destaques
41
41
 
42
- - ✅ **Validações de Pré-requisitos**: OS, espaço em disco, memória, conectividade
42
+ - ✅ **Validações de Pré-requisitos**: OS, espaço em disco, memória, conectividade, ambiente Python (venv)
43
43
  - ✅ **Health Checks Automáticos**: Valida serviços após instalação
44
- - ✅ **Retry Inteligente**: Resistente a falhas temporárias de rede
45
- - ✅ **Logging Estruturado**: Logs persistentes para auditoria
44
+ - ✅ **Retry Inteligente com Backoff**: Resistente a falhas temporárias de rede (5 tentativas, backoff exponencial)
45
+ - ✅ **Logging Estruturado**: Logs persistentes com rotação (20MB, 5 backups)
46
46
  - ✅ **Gestão de Dependências**: Garante ordem correta de execução
47
+ - ✅ **Verificação de Cluster**: Módulos que dependem de K8s verificam disponibilidade antes de executar
48
+ - ✅ **Clean Automático**: Opção de limpar instalação anterior ao re-executar kubernetes
49
+ - ✅ **IPv4 Only**: IPv6 desabilitado por padrão para simplificar rede
47
50
  - ✅ **Configuração via Arquivo**: Automação completa com YAML/JSON
48
51
  - ✅ **Idempotência**: Re-execução segura sem quebrar o sistema
49
52
  - ✅ **Modo Dry-run**: Simula execução sem aplicar mudanças
@@ -180,7 +183,9 @@ se deseja pular. Se executar manualmente, basta responder "não" quando pergunta
180
183
 
181
184
  ### Comandos Úteis
182
185
  ```bash
183
- # Versão
186
+ # Versão (flag ou comando)
187
+ raijin-server --version
188
+ raijin-server -V
184
189
  raijin-server version
185
190
 
186
191
  # Monitorar logs
@@ -222,9 +227,9 @@ tail -f /var/log/raijin-server/raijin-server.log
222
227
  - `network`: Netplan com IP fixo e DNS. **OPCIONAL** se IP já configurado pelo provedor ISP.
223
228
  - `firewall`: UFW com regras para SSH/HTTP/HTTPS/K8s.
224
229
  - `vpn`: provisiona WireGuard (servidor + cliente inicial) e libera firewall.
225
- - `kubernetes`: kubeadm init, containerd SystemdCgroup, kubeconfig.
226
- - `calico`: CNI Calico com CIDR custom, default-deny e opcao de liberar egress rotulado.
227
- - `cert_manager`: instala cert-manager e ClusterIssuer ACME (HTTP-01/DNS-01).
230
+ - `kubernetes`: kubeadm init, containerd SystemdCgroup, kubeconfig. **Oferece limpeza automática** se detectar instalação anterior. IPv6 desabilitado por padrão.
231
+ - `calico`: CNI Calico com CIDR custom, default-deny e opcao de liberar egress rotulado. **Verifica cluster ativo** antes de aplicar.
232
+ - `cert_manager`: instala cert-manager e ClusterIssuer ACME (HTTP-01/DNS-01). **Verifica cluster ativo** antes de instalar.
228
233
  - `secrets`: instala sealed-secrets e external-secrets via Helm.
229
234
  - `istio`: istioctl install (perfil raijin) e injeção automática.
230
235
  - `traefik`: IngressController com TLS/ACME.
@@ -1,14 +1,14 @@
1
- raijin_server/__init__.py,sha256=JPePevptTQ8J1tBtg72vlfl2KSqpnUdGTt7dQhMtKmk,94
1
+ raijin_server/__init__.py,sha256=AKDS9uS_9UXF-3BZWSJvcQdgvkhbsxMs1o-P7R5xBE4,94
2
2
  raijin_server/cli.py,sha256=ZgaSkXwXaND7HHeySjIn4GEmTVTqUDttUfqXJ9yZV1E,16772
3
3
  raijin_server/config.py,sha256=Dta2CS1d6RgNiQ84P6dTXk98boFrjzuvhs_fCdlm0I4,4810
4
4
  raijin_server/healthchecks.py,sha256=-mQq-dGZ2id16wvPmiTPjDHw14PBwz_i8AXi307V38k,12411
5
- raijin_server/utils.py,sha256=-zKDIQyVaPBE7k2rj4uTpdLI-YUG-fZhnkhO_5bsEPc,8782
5
+ raijin_server/utils.py,sha256=oQM-NGL_kmlNZejFvxXk85MI_WkcxNfwaw5LeAsKUFU,11476
6
6
  raijin_server/validators.py,sha256=qOZMHgwjHogVf17UPlxfUCpQd9qAGQW7tycd8mUvnEs,9404
7
7
  raijin_server/modules/__init__.py,sha256=e_IbkhLGPcF8to9QUmIESP6fpcTOYcIhaXLKIvqRJMY,920
8
8
  raijin_server/modules/apokolips_demo.py,sha256=gMUpYNaO0V20KoNa4ljyA1W9HJbY__O9AEO64NuWGhE,12365
9
- raijin_server/modules/bootstrap.py,sha256=XiIqaNwDIKVA6nXAM2t_35gYW8Z4oWB7vnJAgNxuLYw,8916
10
- raijin_server/modules/calico.py,sha256=ljSMYlc-U9HoidQHcGgL1VggS_ypnVohyF4DtzXokc8,3273
11
- raijin_server/modules/cert_manager.py,sha256=f3lLnaJBxYcfjh4AozWsOZw-nZ56Hqxk2p9EkMpkHuE,3984
9
+ raijin_server/modules/bootstrap.py,sha256=oVIGNRW_JbgY8zXNHGAIP0vGbbHNHyQexthxo5zhbcw,9762
10
+ raijin_server/modules/calico.py,sha256=a8N7YYv7NoaspPKdhRtwHy3V2mM4cP5xA1H8BwslB18,4139
11
+ raijin_server/modules/cert_manager.py,sha256=bSv5CRbPlH3DHWHBqUNTKVh0C973E4XC8WzGieOHh3A,4882
12
12
  raijin_server/modules/essentials.py,sha256=2xUXCyCQtFGd2DnCKV81N1R6bEJqH8zaet8mLovtQ1I,689
13
13
  raijin_server/modules/firewall.py,sha256=h6AISqiZeTinVT7BjmQIS872qRAFZJLg7meqlth3cfw,757
14
14
  raijin_server/modules/full_install.py,sha256=Mk_SHBrtL4zgjgd2shUuhp4fyDIPdlBVZtC5t8x-1vU,5908
@@ -18,7 +18,7 @@ raijin_server/modules/harness.py,sha256=dhZ89YIhlkuxiRU1deN6wXVWnXm0xeI03PwYf_qg
18
18
  raijin_server/modules/istio.py,sha256=761FOGEzEXWlTLYApQxUWY8l4cnEbnIXbIHK3itk_AQ,522
19
19
  raijin_server/modules/kafka.py,sha256=bp8k_IhuAIO6dL0IpK1UxxLZoGih6nJp0ZnzwmiZEj8,950
20
20
  raijin_server/modules/kong.py,sha256=2EZKYBmBhm_7Nduw9PWrvrekp0VCxQbc2gElpAJqKfg,491
21
- raijin_server/modules/kubernetes.py,sha256=B_J2mHLjMNglksnZk08HSK3z55kNytuq5sVBbjAXbLY,6846
21
+ raijin_server/modules/kubernetes.py,sha256=zHbgCYzzdJJwUGsxJoiyT4HCeJz2HmDSeBR88KP-v4Y,8286
22
22
  raijin_server/modules/loki.py,sha256=erwFfSiSFOv-Ul3nFdrI2RElPYuqqBPBBa_MJAwyLys,676
23
23
  raijin_server/modules/minio.py,sha256=BVvsEaJlJUV92_ep7pKsBhSYPjWZrDOB3J6XAWYAHYg,486
24
24
  raijin_server/modules/network.py,sha256=bwVljaVvTc6FbbD-XtDpqqNL-fXMB9-iWVWsXToBvt4,4804
@@ -36,9 +36,9 @@ raijin_server/scripts/checklist.sh,sha256=j6E0Kmk1EfjLvKK1VpCqzXJAXI_7Bm67LK4ndy
36
36
  raijin_server/scripts/install.sh,sha256=IZOTujOSGmKpznwgL59picsQNVzYkai6FtfFS3Klf34,3908
37
37
  raijin_server/scripts/log_size_metric.sh,sha256=rC2Ck4xnYVJV4Qymu24-indC8bkzfZs4FBqqxGPRl1I,1143
38
38
  raijin_server/scripts/pre-deploy-check.sh,sha256=naPUgKjnKgsh-eGDH2623C7zcr9VjDEw1H0lfYaXW8c,4853
39
- raijin_server-0.2.1.dist-info/licenses/LICENSE,sha256=kJsMCjOiRZE0AQNtxWqBa32z9kMAaF4EUxyHj3hKaJo,1105
40
- raijin_server-0.2.1.dist-info/METADATA,sha256=EV6FCXxKZWHwsPc9HEgQEV03pUUmS-AGQ8JdOVW7rD0,16327
41
- raijin_server-0.2.1.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
42
- raijin_server-0.2.1.dist-info/entry_points.txt,sha256=3ZvxDX4pvcjkIRsXAJ69wIfVmKa78LKo-C3QhqN2KVM,56
43
- raijin_server-0.2.1.dist-info/top_level.txt,sha256=Yz1xneCRtsZOzbPIcTAcrSxd-1p80pohMXYAZ74dpok,14
44
- raijin_server-0.2.1.dist-info/RECORD,,
39
+ raijin_server-0.2.3.dist-info/licenses/LICENSE,sha256=kJsMCjOiRZE0AQNtxWqBa32z9kMAaF4EUxyHj3hKaJo,1105
40
+ raijin_server-0.2.3.dist-info/METADATA,sha256=Bh7AicqZXP3kLYhHxgf1uB3R4PGI4hcC8AYaDAaUg6A,16941
41
+ raijin_server-0.2.3.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
42
+ raijin_server-0.2.3.dist-info/entry_points.txt,sha256=3ZvxDX4pvcjkIRsXAJ69wIfVmKa78LKo-C3QhqN2KVM,56
43
+ raijin_server-0.2.3.dist-info/top_level.txt,sha256=Yz1xneCRtsZOzbPIcTAcrSxd-1p80pohMXYAZ74dpok,14
44
+ raijin_server-0.2.3.dist-info/RECORD,,