feature: helm charts repos with certs
[osm/osmclient.git] / osmclient / cli_commands / repo.py
1 # Copyright ETSI Contributors and Others.
2 # All Rights Reserved.
3 #
4 # Licensed under the Apache License, Version 2.0 (the "License"); you may
5 # not use this file except in compliance with the License. You may obtain
6 # a copy of the License at
7 #
8 # http://www.apache.org/licenses/LICENSE-2.0
9 #
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 # License for the specific language governing permissions and limitations
14 # under the License.
15
16 import click
17 from osmclient.common.exceptions import NotFound, ClientException
18 from osmclient.cli_commands import utils
19 from prettytable import PrettyTable
20 import yaml
21 import json
22 import logging
23
24 logger = logging.getLogger("osmclient")
25
26
27 @click.command(name="repo-add", short_help="adds a repo to OSM")
28 @click.argument("name")
29 @click.argument("uri")
30 @click.option(
31 "--type",
32 type=click.Choice(["osm", "helm-chart", "juju-bundle"]),
33 default="osm",
34 help="type of repo (osm for OSM repositories, helm-chart for Helm Charts, juju-bundle for Juju Bundles)",
35 )
36 @click.option("--description", default=None, help="human readable description")
37 @click.option(
38 "--user", default=None, help="OSM repository: The username of the OSM repository"
39 )
40 @click.option(
41 "--password",
42 default=None,
43 help="OSM repository: The password of the OSM repository",
44 )
45 @click.option(
46 "--oci",
47 is_flag=True,
48 help="enable OCI (only for helm-chart repos, default: false, automatically set to true for oci:// URI)",
49 )
50 @click.option(
51 "--ca-file",
52 default=None,
53 help="cert to use when adding a repository (only for helm)",
54 )
55 @click.pass_context
56 def repo_add(ctx, **kwargs):
57 """adds a repo to OSM
58
59 NAME: name of the repo
60 URI: URI of the repo
61 """
62 kwargs = {k: v for k, v in kwargs.items() if v is not None}
63 repo = kwargs
64 repo["url"] = repo.pop("uri")
65 if repo["url"].startswith("oci://"):
66 repo["oci"] = True
67 if "ca_file" in kwargs:
68 try:
69 with open(kwargs["ca_file"], "r") as ca_cert:
70 repo["cacert"] = ca_cert.read()
71 repo.pop("ca_file")
72 except FileNotFoundError:
73 raise ClientException("CA file not found !")
74 except EOFError:
75 raise ClientException("Empty CA file !")
76 except PermissionError:
77 raise ClientException("Can not read CA file ! Insufficient permissions")
78 except Exception as e:
79 raise ClientException(f"Can not read the cert file ! Error: {e}")
80 if repo["type"] in ["helm-chart", "juju-bundle"]:
81 ctx.obj.repo.create(repo["name"], repo)
82 else:
83 ctx.obj.osmrepo.create(repo["name"], repo)
84
85
86 @click.command(name="repo-update", short_help="updates a repo in OSM")
87 @click.argument("name")
88 @click.option("--newname", help="New name for the repo")
89 @click.option("--uri", help="URI of the repo")
90 @click.option("--description", help="human readable description")
91 @click.option(
92 "--oci",
93 is_flag=True,
94 help="enable OCI (only for helm-chart repos, default: false, automatically set to true for oci:// URI)",
95 )
96 @click.pass_context
97 def repo_update(ctx, name, newname, uri, description, oci):
98 """updates a repo in OSM
99
100 NAME: name of the repo
101 """
102 utils.check_client_version(ctx.obj, ctx.command.name)
103 repo = {}
104 if newname:
105 repo["name"] = newname
106 if uri:
107 repo["url"] = uri
108 if uri.startswith("oci://"):
109 repo["oci"] = True
110 if description:
111 repo["description"] = description
112 if oci:
113 repo["oci"] = oci
114 try:
115 ctx.obj.repo.update(name, repo)
116 except NotFound:
117 ctx.obj.osmrepo.update(name, repo)
118
119
120 @click.command(
121 name="repo-index", short_help="Index a repository from a folder with artifacts"
122 )
123 @click.option(
124 "--origin", default=".", help="origin path where the artifacts are located"
125 )
126 @click.option(
127 "--destination", default=".", help="destination path where the index is deployed"
128 )
129 @click.pass_context
130 def repo_index(ctx, origin, destination):
131 """Index a repository
132
133 NAME: name or ID of the repo to be deleted
134 """
135 utils.check_client_version(ctx.obj, ctx.command.name)
136 ctx.obj.osmrepo.repo_index(origin, destination)
137
138
139 @click.command(name="repo-delete", short_help="deletes a repo")
140 @click.argument("name")
141 @click.option(
142 "--force", is_flag=True, help="forces the deletion from the DB (not recommended)"
143 )
144 @click.pass_context
145 def repo_delete(ctx, name, force):
146 """deletes a repo
147
148 NAME: name or ID of the repo to be deleted
149 """
150 logger.debug("")
151 try:
152 ctx.obj.repo.delete(name, force=force)
153 except NotFound:
154 ctx.obj.osmrepo.delete(name, force=force)
155
156
157 @click.command(name="repo-list")
158 @click.option(
159 "--filter",
160 default=None,
161 multiple=True,
162 help="restricts the list to the repos matching the filter",
163 )
164 @click.option("--literal", is_flag=True, help="print literally, no pretty table")
165 @click.pass_context
166 def repo_list(ctx, filter, literal):
167 """list all repos"""
168 # K8s Repositories
169 utils.check_client_version(ctx.obj, ctx.command.name)
170 if filter:
171 filter = "&".join(filter)
172 resp = ctx.obj.repo.list(filter)
173 resp += ctx.obj.osmrepo.list(filter)
174 if literal:
175 print(yaml.safe_dump(resp, indent=4, default_flow_style=False))
176 return
177 table = PrettyTable(["Name", "Id", "Type", "URI", "Description"])
178 for repo in resp:
179 # cluster['k8s-nets'] = json.dumps(yaml.safe_load(cluster['k8s-nets']))
180 table.add_row(
181 [
182 repo["name"],
183 repo["_id"],
184 repo["type"],
185 repo["url"],
186 utils.trunc_text(repo.get("description") or "", 40),
187 ]
188 )
189 table.align = "l"
190 print(table)
191
192
193 @click.command(name="repo-show", short_help="shows the details of a repo")
194 @click.argument("name")
195 @click.option("--literal", is_flag=True, help="print literally, no pretty table")
196 @click.pass_context
197 def repo_show(ctx, name, literal):
198 """shows the details of a repo
199
200 NAME: name or ID of the repo
201 """
202 try:
203 resp = ctx.obj.repo.get(name)
204 except NotFound:
205 resp = ctx.obj.osmrepo.get(name)
206
207 if literal:
208 if resp:
209 print(yaml.safe_dump(resp, indent=4, default_flow_style=False))
210 return
211 table = PrettyTable(["key", "attribute"])
212 if resp:
213 for k, v in list(resp.items()):
214 table.add_row([k, json.dumps(v, indent=2)])
215
216 table.align = "l"
217 print(table)
218
219
220 ########################
221 # Catalogue commands
222 ########################
223
224
225 def pkg_repo_list(ctx, pkgtype, filter, repo, long):
226 resp = ctx.obj.osmrepo.pkg_list(pkgtype, filter, repo)
227 if long:
228 table = PrettyTable(
229 ["nfpkg name", "vendor", "version", "latest", "description", "repository"]
230 )
231 else:
232 table = PrettyTable(["nfpkg name", "repository"])
233 for vnfd in resp:
234 name = vnfd.get("id", vnfd.get("name", "-"))
235 repository = vnfd.get("repository")
236 if long:
237 vendor = vnfd.get("provider", vnfd.get("vendor"))
238 version = vnfd.get("version")
239 description = vnfd.get("description")
240 latest = vnfd.get("latest")
241 table.add_row([name, vendor, version, latest, description, repository])
242 else:
243 table.add_row([name, repository])
244 table.align = "l"
245 print(table)
246
247
248 def pkg_repo_show(ctx, pkgtype, name, repo, version, filter, literal):
249 logger.debug("")
250 if filter:
251 filter = "&".join(filter)
252 resp = ctx.obj.osmrepo.pkg_get(pkgtype, name, repo, version, filter)
253
254 if literal:
255 print(yaml.safe_dump(resp, indent=4, default_flow_style=False))
256 return
257 pkgtype += "d"
258 catalog = pkgtype + "-catalog"
259 full_catalog = pkgtype + ":" + catalog
260 if resp.get(catalog):
261 resp = resp.pop(catalog)[pkgtype][0]
262 elif resp.get(full_catalog):
263 resp = resp.pop(full_catalog)[pkgtype][0]
264
265 table = PrettyTable(["field", "value"])
266 for k, v in list(resp.items()):
267 table.add_row([k, utils.wrap_text(text=json.dumps(v, indent=2), width=100)])
268 table.align = "l"
269 print(table)
270
271
272 @click.command(name="vnfpkg-repo-list", short_help="list all xNF from OSM repositories")
273 @click.option(
274 "--filter",
275 default=None,
276 multiple=True,
277 help="restricts the list to the NFpkg matching the filter",
278 )
279 @click.option(
280 "--repo", default=None, help="restricts the list to a particular OSM repository"
281 )
282 @click.option("--long", is_flag=True, help="get more details")
283 @click.pass_context
284 def nfpkg_repo_list1(ctx, filter, repo, long):
285 """list xNF packages from OSM repositories"""
286 pkgtype = "vnf"
287 pkg_repo_list(ctx, pkgtype, filter, repo, long)
288
289
290 @click.command(name="nfpkg-repo-list", short_help="list all xNF from OSM repositories")
291 @click.option(
292 "--filter",
293 default=None,
294 multiple=True,
295 help="restricts the list to the NFpkg matching the filter",
296 )
297 @click.option(
298 "--repo", default=None, help="restricts the list to a particular OSM repository"
299 )
300 @click.option("--long", is_flag=True, help="get more details")
301 @click.pass_context
302 def nfpkg_repo_list2(ctx, filter, repo, long):
303 """list xNF packages from OSM repositories"""
304 pkgtype = "vnf"
305 pkg_repo_list(ctx, pkgtype, filter, repo, long)
306
307
308 @click.command(name="nsd-repo-list", short_help="list all NS from OSM repositories")
309 @click.option(
310 "--filter",
311 default=None,
312 multiple=True,
313 help="restricts the list to the NS matching the filter",
314 )
315 @click.option(
316 "--repo", default=None, help="restricts the list to a particular OSM repository"
317 )
318 @click.option("--long", is_flag=True, help="get more details")
319 @click.pass_context
320 def nspkg_repo_list(ctx, filter, repo, long):
321 """list xNF packages from OSM repositories"""
322 pkgtype = "ns"
323 pkg_repo_list(ctx, pkgtype, filter, repo, long)
324
325
326 @click.command(name="nspkg-repo-list", short_help="list all NS from OSM repositories")
327 @click.option(
328 "--filter",
329 default=None,
330 multiple=True,
331 help="restricts the list to the NS matching the filter",
332 )
333 @click.option(
334 "--repo", default=None, help="restricts the list to a particular OSM repository"
335 )
336 @click.option("--long", is_flag=True, help="get more details")
337 @click.pass_context
338 def nspkg_repo_list2(ctx, filter, repo, long):
339 """list xNF packages from OSM repositories"""
340 pkgtype = "ns"
341 pkg_repo_list(ctx, pkgtype, filter, repo, long)
342
343
344 @click.command(
345 name="vnfpkg-repo-show",
346 short_help="shows the details of a NF package in an OSM repository",
347 )
348 @click.option("--literal", is_flag=True, help="print literally, no pretty table")
349 @click.option("--repo", required=True, help="Repository name")
350 @click.argument("name")
351 @click.option("--filter", default=None, multiple=True, help="filter by fields")
352 @click.option("--version", default="latest", help="package version")
353 @click.pass_context
354 def vnfd_show1(ctx, name, repo, version, literal=None, filter=None):
355 """shows the content of a VNFD in a repository
356
357 NAME: name or ID of the VNFD/VNFpkg
358 """
359 pkgtype = "vnf"
360 pkg_repo_show(ctx, pkgtype, name, repo, version, filter, literal)
361
362
363 @click.command(
364 name="nsd-repo-show",
365 short_help="shows the details of a NS package in an OSM repository",
366 )
367 @click.option("--literal", is_flag=True, help="print literally, no pretty table")
368 @click.option("--repo", required=True, help="Repository name")
369 @click.argument("name")
370 @click.option("--filter", default=None, multiple=True, help="filter by fields")
371 @click.option("--version", default="latest", help="package version")
372 @click.pass_context
373 def nsd_repo_show(ctx, name, repo, version, literal=None, filter=None):
374 """shows the content of a VNFD in a repository
375
376 NAME: name or ID of the VNFD/VNFpkg
377 """
378 pkgtype = "ns"
379 pkg_repo_show(ctx, pkgtype, name, repo, version, filter, literal)
380
381
382 @click.command(
383 name="nspkg-repo-show",
384 short_help="shows the details of a NS package in an OSM repository",
385 )
386 @click.option("--literal", is_flag=True, help="print literally, no pretty table")
387 @click.option("--repo", required=True, help="Repository name")
388 @click.argument("name")
389 @click.option("--filter", default=None, multiple=True, help="filter by fields")
390 @click.option("--version", default="latest", help="package version")
391 @click.pass_context
392 def nsd_repo_show2(ctx, name, repo, version, literal=None, filter=None):
393 """shows the content of a VNFD in a repository
394
395 NAME: name or ID of the VNFD/VNFpkg
396 """
397 pkgtype = "ns"
398 pkg_repo_show(ctx, pkgtype, name, repo, version, filter, literal)
399
400
401 @click.command(
402 name="nfpkg-repo-show",
403 short_help="shows the details of a NF package in an OSM repository",
404 )
405 @click.option("--literal", is_flag=True, help="print literally, no pretty table")
406 @click.option("--repo", required=True, help="Repository name")
407 @click.argument("name")
408 @click.option("--filter", default=None, multiple=True, help="filter by fields")
409 @click.option("--version", default="latest", help="package version")
410 @click.pass_context
411 def vnfd_show2(ctx, name, repo, version, literal=None, filter=None):
412 """shows the content of a VNFD in a repository
413
414 NAME: name or ID of the VNFD/VNFpkg
415 """
416 pkgtype = "vnf"
417 pkg_repo_show(ctx, pkgtype, name, repo, version, filter, literal)