Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 4 additions & 7 deletions src/cfengine_cli/cfengine_wrapper/cfengine_commands.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
from cfbs.commands import build_command
from cf_remote.commands import deploy as deploy_command
from cf_remote.commands import install as install_command
from cf_remote.commands import spawn as spawn_command
from cf_remote.commands import destroy as destroy_command

from cfengine_cli.cfengine_wrapper.cfengine_utils import (
Expand Down Expand Up @@ -31,12 +30,10 @@ def install() -> int: # TODO ENT-14117
return install_command(None, None)


def spawn() -> int: # TODO ENT-14118
return spawn_command(None, None, None, None)


def destroy() -> int: # TODO ENT-14118
return destroy_command(None)
def destroy(groupname, del_all=False) -> int:
if del_all:
return destroy_command(None)
return destroy_command(groupname)


def build() -> int: # TODO ENT-14119
Expand Down
141 changes: 140 additions & 1 deletion src/cfengine_cli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
from cfengine_cli.version import cfengine_cli_version_string
from cfengine_cli import commands
from cfengine_cli.utils import UserError
from cf_remote.commands import spawn, list_boxes, list_platforms, init_cloud_config
from cf_remote.spawn import CFRUserError, Providers
from cfbs.utils import CFBSProgrammerError


Expand Down Expand Up @@ -93,6 +95,66 @@ def _get_arg_parser():
"If omitted and multiple installations are found, you'll be prompted.",
)

sp = subp.add_parser("spawn", help="Spawn hosts in the clouds")
sp.add_argument(
"--list-platforms", help="List supported platforms", action="store_true"
)
sp.add_argument(
"--list-boxes", help="List installed vagrant boxes", action="store_true"
)
sp.add_argument(
"--init-config",
help="Initialize configuration file for spawn functionality",
action="store_true",
)
sp.add_argument("--platform", help="Platform or vagrant box to use", type=str)
sp.add_argument("--count", default=1, help="How many hosts to spawn", type=int)
sp.add_argument(
"--role", help="Role of the hosts", choices=["hub", "hubs", "client", "clients"]
)
sp.add_argument(
"--name", help="Name of the group of hosts (can be used in other commands)"
)
sp.add_argument(
"--append",
help="Append the new VMs to a pre-existing group",
action="store_true",
)
sp.add_argument(
"--provider",
help="VM provider",
type=str,
default="aws",
choices=["aws", "gcp", "vagrant"],
)
sp.add_argument("--cpus", help="Number of CPUs of the vagrant instances", type=int)
sp.add_argument(
"--sync-folder",
help="Root folder of synchronized folders of vagrant instance",
type=str,
)
sp.add_argument(
"--provision",
help="full path to provision shell script for Vagrant VM",
type=str,
)
sp.add_argument("--size", help="Size/type of the instances", type=str)
sp.add_argument(
"--network", help="network/subnet to assign the VMs to (GCP only)", type=str
)
sp.add_argument(
"--no-public-ip",
help="No public IP needed (GCP only; WARNING: The VMs will only be accessible"
+ " from some other VM in the same cloud/network!)",
action="store_true",
)

dp = subp.add_parser("destroy", help="Destroy hosts spawned in the clouds")
dp.add_argument(
"--all", help="Destroy all hosts spawned in the clouds", action="store_true"
)
dp.add_argument("name", help="Name of the group of hosts to destroy", nargs="?")

profile_parser = subp.add_parser(
"profile", help="Parse CFEngine profiling output (cf-agent -Kp)"
)
Expand Down Expand Up @@ -222,6 +284,59 @@ def run_command_with_args(args) -> int:
return cfengine_commands.report(target=args.host)
if args.command == "run":
return cfengine_commands.run(*args.run_args, target=args.host)
if args.command == "spawn":
if args.list_platforms:
return list_platforms()
if args.list_boxes:
return list_boxes()
if args.init_config:
return init_cloud_config()
if args.name and "," in args.name:
raise UserError("Group --name may not contain commas")
if args.role and args.role.endswith("s"):
# role should be singular
args.role = args.role[:-1]
if args.provider == "gcp":
provider = Providers.GCP
elif args.provider == "aws":
provider = Providers.AWS
if args.network:
raise UserError("--network not supported for AWS")
if args.no_public_ip:
raise UserError("--no-public-ip not supported for AWS")
else:
assert args.provider == "vagrant"
provider = Providers.VAGRANT

if provider != Providers.VAGRANT:
if args.cpus:
raise UserError(f"--cpus not supported for {args.provider}")
if args.sync_folder:
raise UserError(f"--sync-folder not supported for {args.provider}")
if args.provision:
raise UserError(f"--provision not supported for {args.provider}")

if args.network and (args.network.count("/") != 1):
raise UserError(
"Invalid network specified, needs to be in the network/subnet format"
)

return spawn(
args.platform,
args.count,
args.role,
args.name,
provider=provider,
size=args.size,
network=args.network,
public_ip=not args.no_public_ip,
extend_group=args.append,
vagrant_cpus=args.cpus,
vagrant_sync_folder=args.sync_folder,
vagrant_provision=args.provision,
)
if args.command == "destroy":
return cfengine_commands.destroy(args.name, del_all=args.all)
if args.command == "dev":
return commands.dev(args.dev_command, args)
if args.command == "profile":
Expand All @@ -234,6 +349,30 @@ def run_command_with_args(args) -> int:
def validate_args(args):
if args.command == "dev" and args.dev_command is None:
raise UserError("Missing subcommand - cfengine dev <subcommand>")
if (
args.command == "spawn"
and not args.list_platforms
and not args.init_config
and not args.list_boxes
):
# The above options don't require any other options/arguments (TODO:
# --provider), but otherwise all have to be given
if not args.platform:
raise UserError("--platform needs to be specified")
if not args.count:
raise UserError("--count needs to be specified")
if not args.role:
raise UserError("--role needs to be specified")
if not args.name:
raise UserError("--name needs to be specified")

if args.command == "destroy":
if not args.all and not args.name:
raise UserError("Either '--all' or 'NAME' must be specified for destroy")
if args.all and args.name:
raise UserError(
"Only one of '--all' or 'NAME' may be specified for destruction"
)


def _main():
Expand All @@ -252,7 +391,7 @@ def main():
exit_code = _main()
assert type(exit_code) is int
sys.exit(exit_code)
except UserError as e:
except (UserError, CFRUserError) as e:
print(str(e))
sys.exit(-1)
# Exceptions below are not expected, print extra info:
Expand Down
Loading