Skip to content

Commit

Permalink
Add 'fleet create-reference' command. (#999)
Browse files Browse the repository at this point in the history
Also change `--fleet-root` to `--fleet`.
Accept a reference file as argument to --fleet.
  • Loading branch information
floitsch authored Mar 11, 2024
1 parent 8886288 commit cb084e0
Show file tree
Hide file tree
Showing 13 changed files with 230 additions and 69 deletions.
6 changes: 5 additions & 1 deletion src/cli/cli.toit
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,11 @@ main args --config/Config --cache/Cache --ui/Ui:
--options=[
cli.Option "fleet-root"
--type="directory"
--help="Specify the fleet root. Can also be set with the ARTEMIS_FLEET_ROOT environment variable.",
--help="Specify the fleet root. Can also be set with the ARTEMIS_FLEET_ROOT environment variable."
--hidden,
cli.Option "fleet"
--type="directory|reference"
--help="Specify the fleet. Can also be set with the ARTEMIS_FLEET environment variable.",
cli.OptionEnum "output-format"
["text", "json"]
--help="Specify the format used when printing to the console."
Expand Down
120 changes: 75 additions & 45 deletions src/cli/cmds/fleet.toit
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,28 @@ create-fleet-commands config/Config cache/Cache ui/Ui -> List:
--run=:: group-move it config cache ui
group-cmd.add group-move-cmd

create-reference-cmd := cli.Command "create-reference"
--aliases=["create-ref"]
--help="""
Creates a reference file for this fleet.
References can be used for pod-management commands, such as 'pod upload',
but cannot be used for device management commands, such as 'fleet roll-out'.
"""
--options=[
cli.Option "output"
--short-name="o"
--type="file"
--help="The file to write the reference to."
--required,
]
--examples=[
cli.Example "Create a reference file 'my-fleet.ref':"
--arguments="--output=my-fleet.ref",
]
--run=:: create-reference it config cache ui
cmd.add create-reference-cmd

return [cmd]

init parsed/cli.Parsed config/Config cache/Cache ui/Ui:
Expand All @@ -416,7 +438,7 @@ init parsed/cli.Parsed config/Config cache/Cache ui/Ui:

organization-id = default-organization-id

fleet-root := compute-fleet-root parsed config ui
fleet-root := compute-fleet-root-or-ref parsed config ui
with-artemis parsed config cache ui: | artemis/Artemis |
FleetWithDevices.init fleet-root artemis --organization-id=organization-id --ui=ui

Expand Down Expand Up @@ -499,16 +521,17 @@ add-device parsed/cli.Parsed config/Config cache/Cache ui/Ui:
ui.info "Added device $device-id to fleet."

group-list parsed/cli.Parsed config/Config cache/Cache ui/Ui:
fleet-root := compute-fleet-root parsed config ui
fleet-file := Fleet.load-fleet-file fleet-root --ui=ui
ui.do --kind=Ui.RESULT: | printer/Printer |
structured := []
fleet-file.group-pods.do: | name pod-reference/PodReference |
structured.add {
"name": name,
"pod": pod-reference.to-string,
}
printer.emit structured --header={"name": "Group", "pod": "Pod"}
with-devices-fleet parsed config cache ui: | fleet/FleetWithDevices |
fleet-file := Fleet.load-fleet-file fleet.root --ui=ui

ui.do --kind=Ui.RESULT: | printer/Printer |
structured := []
fleet-file.group-pods.do: | name pod-reference/PodReference |
structured.add {
"name": name,
"pod": pod-reference.to-string,
}
printer.emit structured --header={"name": "Group", "pod": "Pod"}

group-create parsed/cli.Parsed config/Config cache/Cache ui/Ui:
pod := parsed["pod"]
Expand All @@ -530,7 +553,7 @@ group-create parsed/cli.Parsed config/Config cache/Cache ui/Ui:
if not fleet.pod-exists pod-reference:
ui.abort "Pod $pod-reference does not exist."

fleet-file := Fleet.load-fleet-file fleet.fleet-root_ --ui=ui
fleet-file := Fleet.load-fleet-file fleet.root --ui=ui
if fleet-file.group-pods.contains name:
ui.abort "Group $name already exists."
fleet-file.group-pods[name] = pod-reference
Expand All @@ -556,7 +579,7 @@ group-update parsed/cli.Parsed config/Config cache/Cache ui/Ui:
executed-actions/List := []

with-devices-fleet parsed config cache ui: | fleet/FleetWithDevices |
fleet-root := fleet.fleet-root_
fleet-root := fleet.root
fleet-file := Fleet.load-fleet-file fleet-root --ui=ui

pod-reference/PodReference? := null
Expand Down Expand Up @@ -600,55 +623,54 @@ group-update parsed/cli.Parsed config/Config cache/Cache ui/Ui:
group-remove parsed/cli.Parsed config/Config cache/Cache ui/Ui:
group := parsed["group"]

fleet-root := compute-fleet-root parsed config ui

fleet-file := Fleet.load-fleet-file fleet-root --ui=ui
if not fleet-file.group-pods.contains group:
ui.info "Group '$group' does not exist."
return
with-devices-fleet parsed config cache ui: | fleet/FleetWithDevices |
fleet-file := Fleet.load-fleet-file fleet.root --ui=ui
if not fleet-file.group-pods.contains group:
ui.info "Group '$group' does not exist."
return

device-file := FleetWithDevices.load-devices-file fleet-root --ui=ui
used-groups := {}
device-file.devices.do: | device/DeviceFleet |
used-groups.add device.group
device-file := FleetWithDevices.load-devices-file fleet.root --ui=ui
used-groups := {}
device-file.devices.do: | device/DeviceFleet |
used-groups.add device.group

if used-groups.contains group:
ui.abort "Group '$group' is in use."
if used-groups.contains group:
ui.abort "Group '$group' is in use."

fleet-file.group-pods.remove group
fleet-file.write
fleet-file.group-pods.remove group
fleet-file.write

ui.info "Removed group $group."
ui.info "Removed group $group."

group-move parsed/cli.Parsed config/Config cache/Cache ui/Ui:
to := parsed["to"]
groups-to-move := parsed["group"]
devices-to-move := parsed["device"]

fleet-root := compute-fleet-root parsed config ui

if groups-to-move.is-empty and devices-to-move.is-empty:
ui.abort "No devices or groups given."

ids-to-move := {}
with-devices-fleet parsed config cache ui: | fleet/FleetWithDevices |
devices-to-move.do: | device |
ids-to-move.add (fleet.resolve-alias device).id

fleet-file := Fleet.load-fleet-file fleet-root --ui=ui
if not fleet-file.group-pods.contains to:
ui.abort "Group '$to' does not exist."
fleet-root := fleet.root

groups-to-move-set := {}
groups-to-move-set.add-all groups-to-move
if groups-to-move.is-empty and devices-to-move.is-empty:
ui.abort "No devices or groups given."

moved-count := move-devices_
--fleet-root=fleet-root
--ids-to-move=ids-to-move
--groups-to-move=groups-to-move-set
--to=to
--ui=ui
ui.info "Moved $moved-count devices to group '$to'."
fleet-file := Fleet.load-fleet-file fleet-root --ui=ui
if not fleet-file.group-pods.contains to:
ui.abort "Group '$to' does not exist."

groups-to-move-set := {}
groups-to-move-set.add-all groups-to-move

moved-count := move-devices_
--fleet-root=fleet-root
--ids-to-move=ids-to-move
--groups-to-move=groups-to-move-set
--to=to
--ui=ui
ui.info "Moved $moved-count devices to group '$to'."

move-devices_ -> int
--fleet-root/string
Expand All @@ -672,3 +694,11 @@ move-devices_ -> int
new-devices-file.write

return moved-count

create-reference parsed/cli.Parsed config/Config cache/Cache ui/Ui:
output := parsed["output"]

with-pod-fleet parsed config cache ui: | fleet/Fleet |
reference := fleet.create-reference
write-json-to-file --pretty output reference
ui.info "Created reference file $output."
27 changes: 17 additions & 10 deletions src/cli/cmds/utils_.toit
Original file line number Diff line number Diff line change
Expand Up @@ -39,25 +39,32 @@ default-organization-from-config config/Config -> uuid.Uuid?:
return uuid.parse organization-id-string

with-devices-fleet parsed/cli.Parsed config/Config cache/Cache ui/Ui [block]:
fleet-root := compute-fleet-root parsed config ui
// If the result of the compute-call isn't a root, but a reference, then
// the constructor call below will throw.
fleet-root := compute-fleet-root-or-ref parsed config ui

with-artemis parsed config cache ui: | artemis/Artemis |
fleet := FleetWithDevices fleet-root artemis --ui=ui --cache=cache --config=config
block.call fleet

with-pod-fleet parsed/cli.Parsed config/Config cache/Cache ui/Ui [block]:
fleet-root := compute-fleet-root parsed config ui
fleet-root-or-ref := compute-fleet-root-or-ref parsed config ui

with-artemis parsed config cache ui: | artemis/Artemis |
fleet := Fleet fleet-root artemis --ui=ui --cache=cache --config=config
fleet := Fleet fleet-root-or-ref artemis --ui=ui --cache=cache --config=config
block.call fleet

compute-fleet-root parsed/cli.Parsed config/Config ui/Ui -> string:
fleet-root := parsed["fleet-root"]
if fleet-root: return fleet-root
fleet-root-env := os.env.get "ARTEMIS_FLEET_ROOT"
if fleet-root-env:
compute-fleet-root-or-ref parsed/cli.Parsed config/Config ui/Ui -> string:
fleet-root := parsed["fleet-root"] // Old deprecated argument.
fleet-root-or-ref := parsed["fleet"]
if fleet-root and fleet-root-or-ref:
ui.abort "The arguments --fleet-root and --fleet are mutually exclusive."
fleet-root-or-ref = fleet-root-or-ref or fleet-root
if fleet-root-or-ref: return fleet-root-or-ref
// For the environment 'ARTEMIS_FLEET' wins.
fleet-env := os.env.get "ARTEMIS_FLEET" or os.env.get "ARTEMIS_FLEET_ROOT"
if fleet-env:
ui.do --kind=Ui.DEBUG: | printer/Printer |
printer.emit "Using fleet-root '$fleet-root-env' provided by environment variable."
return fleet-root-env
printer.emit "Using fleet-root '$fleet-env' provided by environment variable."
return fleet-env
return "."
57 changes: 45 additions & 12 deletions src/cli/fleet.toit
Original file line number Diff line number Diff line change
Expand Up @@ -233,15 +233,15 @@ class Fleet:
artemis_/Artemis
ui_/Ui
cache_/Cache
fleet-root_/string
fleet-root-or-ref_/string

organization-id/uuid.Uuid

constructor fleet-root/string artemis/Artemis --ui/Ui --cache/Cache --config/Config:
fleet-file := load-fleet-file fleet-root --ui=ui
return Fleet fleet-root artemis --ui=ui --cache=cache --config=config --fleet-file=fleet-file
constructor fleet-root-or-ref/string artemis/Artemis --ui/Ui --cache/Cache --config/Config:
fleet-file := load-fleet-file fleet-root-or-ref --ui=ui
return Fleet fleet-root-or-ref artemis --ui=ui --cache=cache --config=config --fleet-file=fleet-file

constructor .fleet-root_ .artemis_ --ui/Ui --cache/Cache --config/Config --fleet-file/FleetFile:
constructor .fleet-root-or-ref_ .artemis_ --ui/Ui --cache/Cache --config/Config --fleet-file/FleetFile:
ui_ = ui
cache_ = cache

Expand All @@ -253,16 +253,40 @@ class Fleet:
if not org:
ui.abort "Organization $organization-id does not exist or is not accessible."

static load-fleet-file fleet-root/string --ui/Ui -> FleetFile:
if not file.is-directory fleet-root:
ui.abort "Fleet root $fleet-root is not a directory."
fleet-path := "$fleet-root/$FLEET-FILE_"
static load-fleet-file fleet-root-or-ref/string --ui/Ui -> FleetFile:
fleet-path/string := ?
must-be-reference/bool := ?
if file.is-file fleet-root-or-ref:
// Must be a reference.
fleet-path = fleet-root-or-ref
must-be-reference = true
else if file.is-directory fleet-root-or-ref:
fleet-path = "$fleet-root-or-ref/$FLEET-FILE_"
must-be-reference = false
else:
ui.abort "Fleet root $fleet-root-or-ref is not a directory or a file."
unreachable

if not file.is-file fleet-path:
ui.error "Fleet root $fleet-root does not contain a $FLEET-FILE_ file."
// Can only happen if the fleet-root-or-ref was a directory.
ui.error "Fleet root $fleet-root-or-ref does not contain a $FLEET-FILE_ file."
ui.error "Use 'init' to initialize a fleet root."
ui.abort

return FleetFile.parse fleet-path --ui=ui
result := FleetFile.parse fleet-path --ui=ui
if must-be-reference and not result.is-reference:
ui.abort "Provided fleet-file is not a reference."
else if not must-be-reference and result.is-reference:
ui.abort "Fleet file in given directory is a reference."

return result

create-reference -> Map:
return {
"id": "$id",
"organization": "$organization-id",
"is-reference": true,
}

/**
Uploads the given $pod to the broker.
Expand Down Expand Up @@ -476,6 +500,9 @@ class FleetWithDevices extends Fleet:
aliases_/Map := {:}

constructor fleet-root/string artemis/Artemis --ui/Ui --cache/Cache --config/Config:
if not file.is-directory fleet-root and file.is-file fleet-root:
ui.abort "Fleet argument for this operation must be a fleet root (directory) and not a reference file: '$fleet-root'."

fleet-file := Fleet.load-fleet-file fleet-root --ui=ui
if fleet-file.is-reference:
ui.abort "Fleet root $fleet-root is a reference fleet and cannot be used for device management."
Expand Down Expand Up @@ -529,8 +556,14 @@ class FleetWithDevices extends Fleet:

return DevicesFile.parse devices-path --ui=ui

/** The root (directory) of this fleet. */
root -> string:
// Since this is a fleet with devices, we know that the $fleet-root-or-ref_ must be a
// directory and not just a ref file.
return fleet-root-or-ref_

write-devices_ -> none:
file := DevicesFile "$fleet-root_/$DEVICES-FILE_" devices_
file := DevicesFile "$fleet-root-or-ref_/$DEVICES-FILE_" devices_
file.write

/**
Expand Down
64 changes: 64 additions & 0 deletions tests/cmd-fleet-create-reference-test.toit
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// Copyright (C) 2024 Toitware ApS.
import encoding.json
import host.directory
import host.file
import expect show *
import uuid show Uuid
import .utils

main args:
with-fleet --count=0 --args=args: | test-cli/TestCli _ fleet-dir/string |
run-test test-cli fleet-dir

run-test test-cli/TestCli fleet-dir/string:
test-cli.run-gold "110-list-groups"
"List the groups "
[
"fleet", "group", "list",
]

test-cli.run-gold "120-list-pods"
"List the pods "
[
"pod", "list",
]

ref-file := "$test-cli.tmp-dir/fleet.ref"
test-cli.run-gold "200-create-ref"
"Create a ref"
[
"fleet", "create-reference", "-o", ref-file
]

// We can still use the ref file to list pods.
test-cli.run-gold "210-list-pods-ref"
"List the pods using the ref"
[
"pod", "list", "--fleet", ref-file
]

// However, we don't have access to the groups anymore.
test-cli.run-gold --expect-exit-1 "220-list-groups-ref"
"Fail when trying to list the groups using the ref"
[
"fleet", "group", "list", "--fleet", ref-file
]

// We can't use a fleet file as reference.
test-cli.run-gold --expect-exit-1 "300-full-fleet-as-ref"
"Fail when trying to use the full fleet as reference"
[
"pod", "list", "--fleet", "$fleet-dir/fleet.json"
]

fake-fleet-dir := "$test-cli.tmp-dir/fake-fleet"
directory.mkdir fake-fleet-dir
fake-fleet-file := "$fake-fleet-dir/fleet.json"
file.write-content --path=fake-fleet-file (file.read-content ref-file)

test-cli.run-gold --expect-exit-1 "310-ref-as-fleet"
"Fail when trying to use a ref as fleet"
[
"pod", "list", "--fleet", fake-fleet-dir
]
Loading

0 comments on commit cb084e0

Please sign in to comment.