Skip to content

Commit 216e3c2

Browse files
authored
Merge pull request #208 from SimonThalvorsen/main
Changed how the arg_parse/validation of `cf-remote` functions is done and sets req cf-remote version >= 9.4
2 parents 932584d + 7ca7353 commit 216e3c2

4 files changed

Lines changed: 51 additions & 211 deletions

File tree

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ readme = "README.md"
1010
license = {file = "LICENSE"}
1111
requires-python = ">=3.10"
1212
dependencies = [
13-
"cf-remote>=0.7.3",
13+
"cf-remote>=0.9.4",
1414
"cfbs>=5.5.0",
1515
"tree-sitter-cfengine>=1.1.12",
1616
"tree-sitter>=0.25",

src/cfengine_cli/cfengine_wrapper/arg_parse.py

Lines changed: 22 additions & 151 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,21 @@
11
import argparse
22

3+
from cf_remote.args import (
4+
add_save_args,
5+
add_deploy_args,
6+
add_install_args,
7+
add_uninstall_args,
8+
add_spawn_args,
9+
add_destroy_args,
10+
)
11+
312

413
def parse_wrapper_args(subp: argparse._SubParsersAction):
514

6-
sp = subp.add_parser(
7-
"save", help="Save host(s) with a group name to use in other commands"
8-
)
9-
sp.add_argument(
10-
"--role",
11-
help="Role of the hosts",
12-
choices=["hub", "hubs", "client", "clients"],
13-
required=True,
14-
)
15-
sp.add_argument(
16-
"--name",
17-
help="Name of the group of hosts (can be used in other commands)",
18-
required=True,
19-
)
20-
sp.add_argument(
21-
"--hosts",
22-
"-H",
23-
help="SSH usernames and IPs for SSH and CFEngine in the form of user@ip",
24-
required=True,
15+
add_save_args(
16+
subp.add_parser(
17+
"save", help="Save host(s) with a group name to use in other commands"
18+
)
2519
)
2620

2721
sp = subp.add_parser(
@@ -40,18 +34,13 @@ def parse_wrapper_args(subp: argparse._SubParsersAction):
4034
help="""Build a policy set from a CFEngine Build project.
4135
A wrapper around the cfbs `build`-function.""",
4236
)
43-
sp = subp.add_parser(
37+
38+
deploy_parser = subp.add_parser(
4439
"deploy",
4540
help="""Deploy policy-set (masterfiles) to hub.
4641
A wrapper around the cf-remote `deploy`-function with some added niceties.""",
4742
)
48-
sp.add_argument("--hub", help="Hub(s) to deploy to", type=str)
49-
sp.add_argument(
50-
"masterfiles",
51-
help="Policy-set location (tarball URL or local path to tarball / directory)",
52-
type=str,
53-
nargs="?",
54-
)
43+
add_deploy_args(deploy_parser)
5544

5645
install_parser = subp.add_parser(
5746
"install",
@@ -64,78 +53,14 @@ def parse_wrapper_args(subp: argparse._SubParsersAction):
6453
help="Specify version",
6554
type=str,
6655
)
67-
# install_parser._option_string_actions.get("--version").help = "absdfsf"
68-
# TODO: Update cf-remote/cfbs to have more modular arg-parsing, then we can import
69-
# and override any differences? technically illegal since _option_string_actions,
70-
# but will save ~ 200-1000 loc depending on how much we import into cfengine-cli
71-
72-
install_parser.add_argument(
73-
"--edition",
74-
"-E",
75-
choices=["community", "enterprise"],
76-
help="Enterprise or community packages",
77-
type=str,
78-
)
79-
install_parser.add_argument(
80-
"--package", help="Local path to package or URL to download", type=str
81-
)
82-
install_parser.add_argument(
83-
"--hub-package",
84-
help="Local path to package or URL to download for --hub",
85-
type=str,
86-
)
87-
install_parser.add_argument(
88-
"--client-package",
89-
help="Local path to package or URL to download for --clients",
90-
type=str,
91-
)
92-
install_parser.add_argument(
93-
"--bootstrap", "-B", help="cf-agent --bootstrap argument", type=str
94-
)
95-
install_parser.add_argument(
96-
"--clients", "-c", help="Where to install client package", type=str
97-
)
98-
install_parser.add_argument("--hub", help="Where to install hub package", type=str)
99-
install_parser.add_argument(
100-
"--demo",
101-
help="Use defaults to make demos smoother (NOT secure)",
102-
action="store_true",
103-
)
104-
install_parser.add_argument(
105-
"--call-collect",
106-
help="Enable call collect in --demo def.json",
107-
action="store_true",
108-
)
109-
install_parser.add_argument(
110-
"--remote-download",
111-
help="Package will be downloaded directly to the target machine",
112-
action="store_true",
113-
)
114-
install_parser.add_argument(
115-
"--trust-keys",
116-
help="Comma-separated list of paths to keys hosts should trust"
117-
+ " (implies '--trust-server no' when boostraping)",
118-
type=str,
119-
)
120-
install_parser.add_argument(
121-
"--insecure",
122-
help="Ignore mismatching checksums when downloading urls",
123-
action="store_true",
124-
)
56+
add_install_args(install_parser)
12557

12658
uninstall_parser = subp.add_parser(
12759
"uninstall",
12860
help="Uninstall CFEngine on the given hosts",
12961
description="A wrapper around the cf-remote `uninstall` function",
13062
)
131-
uninstall_parser.add_argument(
132-
"--purge", help="Complete uninstallation", action="store_true"
133-
)
134-
uninstall_parser.add_argument(
135-
"--clients", "-c", help="Where to uninstall", type=str
136-
)
137-
uninstall_parser.add_argument("--hub", help="Where to uninstall", type=str)
138-
uninstall_parser.add_argument("--hosts", "-H", help="Where to uninstall", type=str)
63+
add_uninstall_args(uninstall_parser)
13964

14065
report_parser = subp.add_parser(
14166
"report",
@@ -184,73 +109,19 @@ def parse_wrapper_args(subp: argparse._SubParsersAction):
184109
"If omitted and multiple installations are found, you'll be prompted.",
185110
)
186111

187-
sp = subp.add_parser(
112+
spawn_parser = subp.add_parser(
188113
"spawn",
189114
help="Spawn hosts in the clouds",
190115
description="A wrapper around the cf-remote `spawn`-function",
191116
)
192-
sp.add_argument(
193-
"--list-platforms", help="List supported platforms", action="store_true"
194-
)
195-
sp.add_argument(
196-
"--list-boxes", help="List installed vagrant boxes", action="store_true"
197-
)
198-
sp.add_argument(
199-
"--init-config",
200-
help="Initialize configuration file for spawn functionality",
201-
action="store_true",
202-
)
203-
sp.add_argument("--platform", help="Platform or vagrant box to use", type=str)
204-
sp.add_argument("--count", default=1, help="How many hosts to spawn", type=int)
205-
sp.add_argument(
206-
"--role", help="Role of the hosts", choices=["hub", "hubs", "client", "clients"]
207-
)
208-
sp.add_argument(
209-
"--name", help="Name of the group of hosts (can be used in other commands)"
210-
)
211-
sp.add_argument(
212-
"--append",
213-
help="Append the new VMs to a pre-existing group",
214-
action="store_true",
215-
)
216-
sp.add_argument(
217-
"--provider",
218-
help="VM provider",
219-
type=str,
220-
default="aws",
221-
choices=["aws", "gcp", "vagrant"],
222-
)
223-
sp.add_argument("--cpus", help="Number of CPUs of the vagrant instances", type=int)
224-
sp.add_argument(
225-
"--sync-folder",
226-
help="Root folder of synchronized folders of vagrant instance",
227-
type=str,
228-
)
229-
sp.add_argument(
230-
"--provision",
231-
help="full path to provision shell script for Vagrant VM",
232-
type=str,
233-
)
234-
sp.add_argument("--size", help="Size/type of the instances", type=str)
235-
sp.add_argument(
236-
"--network", help="network/subnet to assign the VMs to (GCP only)", type=str
237-
)
238-
sp.add_argument(
239-
"--no-public-ip",
240-
help="No public IP needed (GCP only; WARNING: The VMs will only be accessible"
241-
+ " from some other VM in the same cloud/network!)",
242-
action="store_true",
243-
)
117+
add_spawn_args(spawn_parser)
244118

245-
dp = subp.add_parser(
119+
destroy_parser = subp.add_parser(
246120
"destroy",
247121
help="Destroy hosts spawned in the clouds",
248122
description="A wrapper around the cf-remote `destroy`-function",
249123
)
250-
dp.add_argument(
251-
"--all", help="Destroy all hosts spawned in the clouds", action="store_true"
252-
)
253-
dp.add_argument("name", help="Name of the group of hosts to destroy", nargs="?")
124+
add_destroy_args(destroy_parser)
254125

255126
profile_parser = subp.add_parser(
256127
"profile", help="Parse CFEngine profiling output (cf-agent -Kp)"

src/cfengine_cli/main.py

Lines changed: 23 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
from cf_remote import log
99
from cf_remote.main import resolve_hosts
10-
from cf_remote.utils import is_package_url, strip_user
10+
from cf_remote.utils import strip_user, CFRExitError
1111
from cfengine_cli.cfengine_wrapper import cfengine_commands
1212
from cfengine_cli.cfengine_wrapper.arg_parse import parse_wrapper_args
1313
from cfengine_cli.version import cfengine_cli_version_string
@@ -22,6 +22,14 @@
2222
uninstall,
2323
)
2424
from cf_remote.spawn import CFRUserError, Providers
25+
from cf_remote.validate import (
26+
validate_edition_args,
27+
validate_install_args,
28+
validate_uninstall_args,
29+
validate_spawn_args,
30+
validate_deploy_args,
31+
validate_destroy_args,
32+
)
2533
from cfbs.utils import CFBSProgrammerError
2634

2735

@@ -323,39 +331,19 @@ def run_command_with_args(args) -> int:
323331
def validate_args(args):
324332
if args.command == "dev" and args.dev_command is None:
325333
raise UserError("Missing subcommand - cfengine dev <subcommand>")
326-
if (
327-
args.command == "spawn"
328-
and not args.list_platforms
329-
and not args.init_config
330-
and not args.list_boxes
331-
):
332-
# The above options don't require any other options/arguments (TODO:
333-
# --provider), but otherwise all have to be given
334-
if not args.platform:
335-
raise UserError("--platform needs to be specified")
336-
if not args.count:
337-
raise UserError("--count needs to be specified")
338-
if not args.role:
339-
raise UserError("--role needs to be specified")
340-
if not args.name:
341-
raise UserError("--name needs to be specified")
334+
335+
if args.command == "spawn":
336+
validate_spawn_args(args)
342337

343338
if args.command == "destroy":
344-
if not args.all and not args.name:
345-
raise UserError("Either '--all' or 'NAME' must be specified for destroy")
339+
validate_destroy_args(args)
346340
if args.all and args.name:
347341
raise UserError(
348342
"Only one of '--all' or 'NAME' may be specified for destruction"
349343
)
350-
if args.command in ["install"]: # , "packages", "list", "download"]:
351-
if args.edition:
352-
args.edition = args.edition.lower()
353-
if args.edition == "core":
354-
args.edition = "community"
355-
if args.edition not in ["enterprise", "community"]:
356-
raise UserError("--edition must be either community or enterprise")
357-
else:
358-
args.edition = "enterprise"
344+
345+
if args.command == "install":
346+
validate_edition_args(args)
359347

360348
if "hosts" in args and args.hosts:
361349
log.debug(f"validate_args, hosts in args, args.hosts='{args.hosts}'")
@@ -371,33 +359,14 @@ def validate_args(args):
371359
log.debug(f"validate_args, hubs in args, args.hub='{args.hub}'")
372360
args.hub = resolve_hosts(args.hub)
373361

374-
if args.command in ["uninstall"] and not (args.hosts or args.hub or args.clients):
375-
raise UserError("Use --hosts, --hub or --clients to specify remote hosts")
362+
if args.command == "uninstall":
363+
validate_uninstall_args(args)
376364

377365
if args.command == "install":
378-
if args.call_collect and not args.demo:
379-
raise UserError("--call-collect must be used with --demo")
380-
if not args.clients and not args.hub:
381-
raise UserError("Specify hosts using --hub and --clients")
382-
if args.hub and args.clients and args.package:
383-
raise UserError(
384-
"Use --hub-package / --client-package instead to distinguish between hosts"
385-
)
386-
if args.package and (args.hub_package or args.client_package):
387-
raise UserError(
388-
"--package cannot be used in combination with --hub-package / --client-package"
389-
)
390-
if args.package and not is_package_url(args.package):
391-
if not os.path.exists(os.path.expanduser(args.package)):
392-
raise UserError("Package/directory '%s' does not exist" % args.package)
393-
if args.hub_package and not is_package_url(args.hub_package):
394-
if not os.path.isfile(args.hub_package):
395-
raise UserError("Hub package '%s' does not exist" % args.hub_package)
396-
if args.client_package and not is_package_url(args.client_package):
397-
if not os.path.isfile(args.client_package):
398-
raise UserError(
399-
"Client package '%s' does not exist" % args.client_package
400-
)
366+
validate_install_args(args)
367+
368+
if args.command == "deploy":
369+
validate_deploy_args(args)
401370

402371

403372
def _main():
@@ -416,7 +385,7 @@ def main():
416385
exit_code = _main()
417386
assert type(exit_code) is int
418387
sys.exit(exit_code)
419-
except (UserError, CFRUserError) as e:
388+
except (UserError, CFRUserError, CFRExitError) as e:
420389
print(str(e))
421390
sys.exit(-1)
422391
# Exceptions below are not expected, print extra info:

uv.lock

Lines changed: 5 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)