lnp-devopscli 1.0.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.
- dc_cli/__init__.py +3 -0
- dc_cli/groups/__init__.py +0 -0
- dc_cli/groups/bw.py +430 -0
- dc_cli/groups/gl.py +300 -0
- dc_cli/groups/ws.py +1249 -0
- dc_cli/main.py +54 -0
- dc_cli/utils/__init__.py +0 -0
- dc_cli/utils/config.py +83 -0
- dc_cli/utils/gitlab.py +165 -0
- lnp_devopscli-1.0.0.dist-info/METADATA +136 -0
- lnp_devopscli-1.0.0.dist-info/RECORD +14 -0
- lnp_devopscli-1.0.0.dist-info/WHEEL +5 -0
- lnp_devopscli-1.0.0.dist-info/entry_points.txt +3 -0
- lnp_devopscli-1.0.0.dist-info/top_level.txt +1 -0
dc_cli/groups/gl.py
ADDED
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
"""Grupo: dc gl - GitLab commands."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
import sys
|
|
7
|
+
|
|
8
|
+
import click
|
|
9
|
+
|
|
10
|
+
from ..utils.config import get_section, load_dotenv
|
|
11
|
+
from ..utils.gitlab import GitLabAPI
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _get_api() -> GitLabAPI:
|
|
15
|
+
"""Retorna GitLabAPI instanciado com config + token do .env."""
|
|
16
|
+
load_dotenv()
|
|
17
|
+
gl_config = get_section("gl")
|
|
18
|
+
token = os.environ.get("GITLAB_LNP_TOKEN")
|
|
19
|
+
if not token:
|
|
20
|
+
click.echo(
|
|
21
|
+
"[ERRO] GITLAB_LNP_TOKEN nao definido.\n"
|
|
22
|
+
" Exporte a variavel ou adicione no .env.",
|
|
23
|
+
err=True,
|
|
24
|
+
)
|
|
25
|
+
sys.exit(1)
|
|
26
|
+
return GitLabAPI(host=gl_config["host"], token=token)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _get_group_id(group: str | None) -> int:
|
|
30
|
+
"""Resolve group_id do --group ou do default_group no config."""
|
|
31
|
+
gl_config = get_section("gl")
|
|
32
|
+
target = group or gl_config.get("default_group")
|
|
33
|
+
if not target:
|
|
34
|
+
raise click.ClickException(
|
|
35
|
+
"Nenhum grupo especificado. Use --group ou configure gl.default_group no config.yaml."
|
|
36
|
+
)
|
|
37
|
+
api = _get_api()
|
|
38
|
+
return api.resolve_group_id(target)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _get_project_id(project: str | None) -> int:
|
|
42
|
+
"""Resolve project_id do --project ou do default_project no config."""
|
|
43
|
+
gl_config = get_section("gl")
|
|
44
|
+
target = project or gl_config.get("default_project")
|
|
45
|
+
if not target:
|
|
46
|
+
raise click.ClickException(
|
|
47
|
+
"Nenhum projeto especificado. Use --project ou configure gl.default_project no config.yaml."
|
|
48
|
+
)
|
|
49
|
+
api = _get_api()
|
|
50
|
+
return api.resolve_project_id(target)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@click.group(name="gl")
|
|
54
|
+
def gl_group():
|
|
55
|
+
"""GitLab — deploy tokens, grupos e mais."""
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
# ─── dc gl token ────────────────────────────────────────
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@gl_group.group(name="token")
|
|
62
|
+
def token_group():
|
|
63
|
+
"""Gerencia deploy tokens e project access tokens do GitLab."""
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@token_group.command(name="create")
|
|
67
|
+
@click.argument("token_type")
|
|
68
|
+
@click.option(
|
|
69
|
+
"--group", "-g", default=None, help="Group ID ou path (default: config.yaml)"
|
|
70
|
+
)
|
|
71
|
+
def token_create(token_type, group):
|
|
72
|
+
"""Cria deploy token do tipo definido em config.yaml.
|
|
73
|
+
|
|
74
|
+
\b
|
|
75
|
+
Tipos disponiveis sao definidos em config.yaml (gl.token_types).
|
|
76
|
+
Exemplo: dc gl token create image-pull
|
|
77
|
+
"""
|
|
78
|
+
gl_config = get_section("gl")
|
|
79
|
+
token_types = gl_config.get("token_types", {})
|
|
80
|
+
|
|
81
|
+
if token_type not in token_types:
|
|
82
|
+
available = ", ".join(token_types.keys()) if token_types else "(nenhum)"
|
|
83
|
+
raise click.ClickException(
|
|
84
|
+
f"Tipo '{token_type}' nao encontrado. Disponiveis: {available}"
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
token_def = token_types[token_type]
|
|
88
|
+
token_name = token_def["name"]
|
|
89
|
+
scopes = token_def["scopes"]
|
|
90
|
+
|
|
91
|
+
api = _get_api()
|
|
92
|
+
target = group or gl_config.get("default_group")
|
|
93
|
+
if not target:
|
|
94
|
+
raise click.ClickException("Nenhum grupo especificado.")
|
|
95
|
+
|
|
96
|
+
click.echo(f"[>] Resolvendo grupo '{target}'...")
|
|
97
|
+
group_id = api.resolve_group_id(target)
|
|
98
|
+
click.echo(f" id: {group_id}")
|
|
99
|
+
|
|
100
|
+
click.echo(
|
|
101
|
+
f"[>] Criando deploy token '{token_name}' com scopes: {', '.join(scopes)}"
|
|
102
|
+
)
|
|
103
|
+
result = api.create_deploy_token(group_id, token_name, scopes)
|
|
104
|
+
|
|
105
|
+
username = result.get("username", "?")
|
|
106
|
+
token_value = result.get("token", "?")
|
|
107
|
+
|
|
108
|
+
click.echo(f"\n[OK] Deploy token criado:")
|
|
109
|
+
click.echo(f" Username: {username}")
|
|
110
|
+
click.echo(f" Token: {token_value}")
|
|
111
|
+
click.echo(f"\n Para usar no k8s:")
|
|
112
|
+
click.echo(f" kubectl create secret docker-registry gitlab-registry \\")
|
|
113
|
+
click.echo(f" --docker-server=registry.gitlab.com \\")
|
|
114
|
+
click.echo(f' --docker-username="{username}" \\')
|
|
115
|
+
click.echo(f' --docker-password="{token_value}" \\')
|
|
116
|
+
click.echo(f" -n <namespace>")
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
@token_group.command(name="list")
|
|
120
|
+
@click.option(
|
|
121
|
+
"--group", "-g", default=None, help="Group ID ou path (default: config.yaml)"
|
|
122
|
+
)
|
|
123
|
+
def token_list(group):
|
|
124
|
+
"""Lista deploy tokens existentes no grupo."""
|
|
125
|
+
api = _get_api()
|
|
126
|
+
group_id = _get_group_id(group)
|
|
127
|
+
|
|
128
|
+
click.echo(f"[>] Listando deploy tokens do grupo {group_id}...")
|
|
129
|
+
tokens = api.list_deploy_tokens(group_id)
|
|
130
|
+
|
|
131
|
+
if not tokens:
|
|
132
|
+
click.echo("[i] Nenhum deploy token encontrado.")
|
|
133
|
+
return
|
|
134
|
+
|
|
135
|
+
click.echo(f"\n{'ID':<10} {'Nome':<30} {'Username':<35} {'Scopes'}")
|
|
136
|
+
click.echo("-" * 100)
|
|
137
|
+
for t in tokens:
|
|
138
|
+
tid = t.get("id", "?")
|
|
139
|
+
name = t.get("name", "?")
|
|
140
|
+
username = t.get("username", "?")
|
|
141
|
+
scopes = ", ".join(t.get("scopes", []))
|
|
142
|
+
click.echo(f"{tid:<10} {name:<30} {username:<35} {scopes}")
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
@token_group.command(name="revoke")
|
|
146
|
+
@click.argument("token_id", type=int)
|
|
147
|
+
@click.option(
|
|
148
|
+
"--group", "-g", default=None, help="Group ID ou path (default: config.yaml)"
|
|
149
|
+
)
|
|
150
|
+
@click.confirmation_option(prompt="Tem certeza que deseja revogar este token?")
|
|
151
|
+
def token_revoke(token_id, group):
|
|
152
|
+
"""Revoga um deploy token pelo ID."""
|
|
153
|
+
api = _get_api()
|
|
154
|
+
group_id = _get_group_id(group)
|
|
155
|
+
|
|
156
|
+
click.echo(f"[>] Revogando token {token_id} do grupo {group_id}...")
|
|
157
|
+
api.revoke_deploy_token(group_id, token_id)
|
|
158
|
+
click.echo(f"[OK] Token {token_id} revogado.")
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
@token_group.command(name="create-project")
|
|
162
|
+
@click.argument("token_type")
|
|
163
|
+
@click.option(
|
|
164
|
+
"--project",
|
|
165
|
+
"project_arg",
|
|
166
|
+
"-p",
|
|
167
|
+
default=None,
|
|
168
|
+
help="Project ID ou path (default: config.yaml)",
|
|
169
|
+
)
|
|
170
|
+
def token_create_project(token_type, project_arg):
|
|
171
|
+
"""Cria project access token do tipo definido em config.yaml.
|
|
172
|
+
|
|
173
|
+
\b
|
|
174
|
+
Tipos disponiveis sao definidos em config.yaml (gl.project_token_types).
|
|
175
|
+
Exemplo: dc gl token create-project tf-state
|
|
176
|
+
"""
|
|
177
|
+
gl_config = get_section("gl")
|
|
178
|
+
token_types = gl_config.get("project_token_types", {})
|
|
179
|
+
|
|
180
|
+
if token_type not in token_types:
|
|
181
|
+
available = ", ".join(token_types.keys()) if token_types else "(nenhum)"
|
|
182
|
+
raise click.ClickException(
|
|
183
|
+
f"Tipo '{token_type}' nao encontrado. Disponiveis: {available}"
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
token_def = token_types[token_type]
|
|
187
|
+
token_name = token_def["name"]
|
|
188
|
+
scopes = token_def["scopes"]
|
|
189
|
+
access_level = token_def.get("access_level")
|
|
190
|
+
expires_at = token_def.get("expires_at")
|
|
191
|
+
|
|
192
|
+
api = _get_api()
|
|
193
|
+
target = project_arg or gl_config.get("default_project")
|
|
194
|
+
if not target:
|
|
195
|
+
raise click.ClickException("Nenhum projeto especificado.")
|
|
196
|
+
|
|
197
|
+
click.echo(f"[>] Resolvendo projeto '{target}'...")
|
|
198
|
+
project_id = api.resolve_project_id(target)
|
|
199
|
+
click.echo(f" id: {project_id}")
|
|
200
|
+
|
|
201
|
+
click.echo(
|
|
202
|
+
f"[>] Criando project access token '{token_name}' com scopes: {', '.join(scopes)}"
|
|
203
|
+
)
|
|
204
|
+
result = api.create_project_access_token(
|
|
205
|
+
project_id,
|
|
206
|
+
token_name,
|
|
207
|
+
scopes,
|
|
208
|
+
access_level=access_level,
|
|
209
|
+
expires_at=expires_at,
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
token_value = result.get("token", "?")
|
|
213
|
+
username = result.get("username") or "oauth2"
|
|
214
|
+
|
|
215
|
+
click.echo("\n[OK] Project access token criado:")
|
|
216
|
+
click.echo(f" Username: {username}")
|
|
217
|
+
click.echo(f" Token: {token_value}")
|
|
218
|
+
click.echo("\n Para Terraform backend (GitLab state):")
|
|
219
|
+
click.echo(f" TF_HTTP_USERNAME={username}")
|
|
220
|
+
click.echo(f" TF_HTTP_PASSWORD={token_value}")
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
@token_group.command(name="list-project")
|
|
224
|
+
@click.option(
|
|
225
|
+
"--project",
|
|
226
|
+
"project_arg",
|
|
227
|
+
"-p",
|
|
228
|
+
default=None,
|
|
229
|
+
help="Project ID ou path (default: config.yaml)",
|
|
230
|
+
)
|
|
231
|
+
def token_list_project(project_arg):
|
|
232
|
+
"""Lista project access tokens existentes no projeto."""
|
|
233
|
+
api = _get_api()
|
|
234
|
+
project_id = _get_project_id(project_arg)
|
|
235
|
+
|
|
236
|
+
click.echo(f"[>] Listando project access tokens do projeto {project_id}...")
|
|
237
|
+
tokens = api.list_project_access_tokens(project_id)
|
|
238
|
+
|
|
239
|
+
if not tokens:
|
|
240
|
+
click.echo("[i] Nenhum project access token encontrado.")
|
|
241
|
+
return
|
|
242
|
+
|
|
243
|
+
click.echo(f"\n{'ID':<10} {'Nome':<35} {'Scopes'}")
|
|
244
|
+
click.echo("-" * 100)
|
|
245
|
+
for t in tokens:
|
|
246
|
+
tid = t.get("id", "?")
|
|
247
|
+
name = t.get("name", "?")
|
|
248
|
+
scopes = ", ".join(t.get("scopes", []))
|
|
249
|
+
click.echo(f"{tid:<10} {name:<35} {scopes}")
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
@token_group.command(name="revoke-project")
|
|
253
|
+
@click.argument("token_id", type=int)
|
|
254
|
+
@click.option(
|
|
255
|
+
"--project",
|
|
256
|
+
"project_arg",
|
|
257
|
+
"-p",
|
|
258
|
+
default=None,
|
|
259
|
+
help="Project ID ou path (default: config.yaml)",
|
|
260
|
+
)
|
|
261
|
+
@click.confirmation_option(prompt="Tem certeza que deseja revogar este project token?")
|
|
262
|
+
def token_revoke_project(token_id, project_arg):
|
|
263
|
+
"""Revoga um project access token pelo ID."""
|
|
264
|
+
api = _get_api()
|
|
265
|
+
project_id = _get_project_id(project_arg)
|
|
266
|
+
|
|
267
|
+
click.echo(f"[>] Revogando project token {token_id} do projeto {project_id}...")
|
|
268
|
+
api.revoke_project_access_token(project_id, token_id)
|
|
269
|
+
click.echo(f"[OK] Project token {token_id} revogado.")
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
# ─── dc gl group-id ─────────────────────────────────────
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
@gl_group.command(name="group-id")
|
|
276
|
+
@click.argument("search")
|
|
277
|
+
def group_id(search):
|
|
278
|
+
"""Busca group_id pelo nome/path.
|
|
279
|
+
|
|
280
|
+
\b
|
|
281
|
+
Exemplo:
|
|
282
|
+
dc gl group-id lnp-consulting-ti
|
|
283
|
+
dc gl group-id lnp-consulting-ti/saas
|
|
284
|
+
"""
|
|
285
|
+
api = _get_api()
|
|
286
|
+
|
|
287
|
+
click.echo(f"[>] Buscando grupo '{search}'...")
|
|
288
|
+
try:
|
|
289
|
+
gid = api.resolve_group_id(search)
|
|
290
|
+
click.echo(f"[OK] {search} -> id: {gid}")
|
|
291
|
+
except click.ClickException:
|
|
292
|
+
# Mostrar resultados parciais
|
|
293
|
+
groups = api.search_groups(search)
|
|
294
|
+
if groups:
|
|
295
|
+
click.echo(f"\nResultados para '{search}':")
|
|
296
|
+
for g in groups[:10]:
|
|
297
|
+
click.echo(f" {g['full_path']:<50} id: {g['id']}")
|
|
298
|
+
else:
|
|
299
|
+
click.echo(f"[ERRO] Nenhum grupo encontrado para '{search}'.", err=True)
|
|
300
|
+
sys.exit(1)
|