fix: resolve CLI serialization failures and add README
Two Akka.NET deserialization bugs prevented CLI commands from reaching ManagementActor: IReadOnlyList<string> in AuthenticatedUser serialized as a compiler-generated internal type unknown to the server, and ManagementSuccess.Data carried server-side assembly types the CLI couldn't resolve on receipt. Fixed by using string[] for roles and pre-serializing response data to JSON in ManagementActor before sending. Adds full CLI reference documentation covering all 10 command groups.
This commit is contained in:
558
src/ScadaLink.CLI/README.md
Normal file
558
src/ScadaLink.CLI/README.md
Normal file
@@ -0,0 +1,558 @@
|
||||
# ScadaLink CLI
|
||||
|
||||
Command-line tool for managing the ScadaLink SCADA system. Connects to a Central node via Akka.NET ClusterClient and routes commands through the ManagementActor.
|
||||
|
||||
## Installation
|
||||
|
||||
```sh
|
||||
dotnet build src/ScadaLink.CLI
|
||||
```
|
||||
|
||||
The output binary is `scadalink` (or `scadalink.exe` on Windows).
|
||||
|
||||
## Connection
|
||||
|
||||
Every command requires a connection to a running Central node. Contact points can be supplied three ways, evaluated in this priority order:
|
||||
|
||||
1. `--contact-points` flag on the command line
|
||||
2. `SCADALINK_CONTACT_POINTS` environment variable
|
||||
3. `contactPoints` array in `~/.scadalink/config.json`
|
||||
|
||||
```sh
|
||||
scadalink --contact-points akka.tcp://scadalink@central-host:8081 <command>
|
||||
```
|
||||
|
||||
For a two-node HA cluster, supply both nodes comma-separated:
|
||||
|
||||
```sh
|
||||
scadalink --contact-points akka.tcp://scadalink@node1:8081,akka.tcp://scadalink@node2:8082 <command>
|
||||
```
|
||||
|
||||
## Global Options
|
||||
|
||||
These options are accepted by the root command and inherited by all subcommands.
|
||||
|
||||
| Option | Description |
|
||||
|--------|-------------|
|
||||
| `--contact-points <value>` | Comma-separated Akka cluster contact point URIs |
|
||||
| `--username <value>` | LDAP username (reserved for future auth integration) |
|
||||
| `--password <value>` | LDAP password (reserved for future auth integration) |
|
||||
| `--format <json\|table>` | Output format (default: `json`) |
|
||||
|
||||
## Configuration File
|
||||
|
||||
`~/.scadalink/config.json` is loaded at startup. All fields are optional.
|
||||
|
||||
```json
|
||||
{
|
||||
"contactPoints": ["akka.tcp://scadalink@central-host:8081"],
|
||||
"ldap": {
|
||||
"server": "ldap.company.com",
|
||||
"port": 636,
|
||||
"useTls": true
|
||||
},
|
||||
"defaultFormat": "json"
|
||||
}
|
||||
```
|
||||
|
||||
## Environment Variables
|
||||
|
||||
| Variable | Description |
|
||||
|----------|-------------|
|
||||
| `SCADALINK_CONTACT_POINTS` | Comma-separated contact point URIs (overrides config file) |
|
||||
| `SCADALINK_LDAP_SERVER` | LDAP server hostname (overrides config file) |
|
||||
| `SCADALINK_FORMAT` | Default output format (overrides config file) |
|
||||
|
||||
## Output
|
||||
|
||||
All commands write JSON to stdout on success. Errors are written as JSON to stderr:
|
||||
|
||||
```json
|
||||
{ "error": "Human-readable message", "code": "ERROR_CODE" }
|
||||
```
|
||||
|
||||
Exit codes:
|
||||
|
||||
| Code | Meaning |
|
||||
|------|---------|
|
||||
| `0` | Success |
|
||||
| `1` | Command error |
|
||||
| `2` | Authorization failure |
|
||||
|
||||
---
|
||||
|
||||
## Command Reference
|
||||
|
||||
### `template` — Manage templates
|
||||
|
||||
#### `template list`
|
||||
|
||||
List all templates with their full attribute, alarm, script, and composition definitions.
|
||||
|
||||
```sh
|
||||
scadalink --contact-points <uri> template list
|
||||
```
|
||||
|
||||
#### `template get`
|
||||
|
||||
Get a single template by ID.
|
||||
|
||||
```sh
|
||||
scadalink --contact-points <uri> template get --id <int>
|
||||
```
|
||||
|
||||
| Option | Required | Description |
|
||||
|--------|----------|-------------|
|
||||
| `--id` | yes | Template ID |
|
||||
|
||||
#### `template create`
|
||||
|
||||
Create a new template, optionally inheriting from a parent.
|
||||
|
||||
```sh
|
||||
scadalink --contact-points <uri> template create --name <string> [--description <string>] [--parent-id <int>]
|
||||
```
|
||||
|
||||
| Option | Required | Description |
|
||||
|--------|----------|-------------|
|
||||
| `--name` | yes | Template name |
|
||||
| `--description` | no | Template description |
|
||||
| `--parent-id` | no | Parent template ID for inheritance |
|
||||
|
||||
#### `template delete`
|
||||
|
||||
Delete a template by ID.
|
||||
|
||||
```sh
|
||||
scadalink --contact-points <uri> template delete --id <int>
|
||||
```
|
||||
|
||||
| Option | Required | Description |
|
||||
|--------|----------|-------------|
|
||||
| `--id` | yes | Template ID |
|
||||
|
||||
---
|
||||
|
||||
### `instance` — Manage instances
|
||||
|
||||
#### `instance list`
|
||||
|
||||
List instances, with optional filters.
|
||||
|
||||
```sh
|
||||
scadalink --contact-points <uri> instance list [--site-id <int>] [--template-id <int>] [--search <string>]
|
||||
```
|
||||
|
||||
| Option | Required | Description |
|
||||
|--------|----------|-------------|
|
||||
| `--site-id` | no | Filter by site ID |
|
||||
| `--template-id` | no | Filter by template ID |
|
||||
| `--search` | no | Search term matched against instance name |
|
||||
|
||||
#### `instance create`
|
||||
|
||||
Create a new instance of a template at a site.
|
||||
|
||||
```sh
|
||||
scadalink --contact-points <uri> instance create --name <string> --template-id <int> --site-id <int> [--area-id <int>]
|
||||
```
|
||||
|
||||
| Option | Required | Description |
|
||||
|--------|----------|-------------|
|
||||
| `--name` | yes | Unique instance name |
|
||||
| `--template-id` | yes | Template to instantiate |
|
||||
| `--site-id` | yes | Site where the instance will run |
|
||||
| `--area-id` | no | Area within the site |
|
||||
|
||||
#### `instance deploy`
|
||||
|
||||
Deploy an instance to its site. Acquires the per-instance operation lock.
|
||||
|
||||
```sh
|
||||
scadalink --contact-points <uri> instance deploy --id <int>
|
||||
```
|
||||
|
||||
| Option | Required | Description |
|
||||
|--------|----------|-------------|
|
||||
| `--id` | yes | Instance ID |
|
||||
|
||||
#### `instance enable`
|
||||
|
||||
Enable a previously disabled instance.
|
||||
|
||||
```sh
|
||||
scadalink --contact-points <uri> instance enable --id <int>
|
||||
```
|
||||
|
||||
| Option | Required | Description |
|
||||
|--------|----------|-------------|
|
||||
| `--id` | yes | Instance ID |
|
||||
|
||||
#### `instance disable`
|
||||
|
||||
Disable a running instance without deleting it.
|
||||
|
||||
```sh
|
||||
scadalink --contact-points <uri> instance disable --id <int>
|
||||
```
|
||||
|
||||
| Option | Required | Description |
|
||||
|--------|----------|-------------|
|
||||
| `--id` | yes | Instance ID |
|
||||
|
||||
#### `instance delete`
|
||||
|
||||
Delete an instance. The instance must be disabled first.
|
||||
|
||||
```sh
|
||||
scadalink --contact-points <uri> instance delete --id <int>
|
||||
```
|
||||
|
||||
| Option | Required | Description |
|
||||
|--------|----------|-------------|
|
||||
| `--id` | yes | Instance ID |
|
||||
|
||||
---
|
||||
|
||||
### `site` — Manage sites
|
||||
|
||||
#### `site list`
|
||||
|
||||
List all registered sites.
|
||||
|
||||
```sh
|
||||
scadalink --contact-points <uri> site list
|
||||
```
|
||||
|
||||
#### `site create`
|
||||
|
||||
Register a new site.
|
||||
|
||||
```sh
|
||||
scadalink --contact-points <uri> site create --name <string> --identifier <string> [--description <string>]
|
||||
```
|
||||
|
||||
| Option | Required | Description |
|
||||
|--------|----------|-------------|
|
||||
| `--name` | yes | Human-readable site name |
|
||||
| `--identifier` | yes | Unique machine identifier used for cluster routing (e.g. `site-a`) |
|
||||
| `--description` | no | Site description |
|
||||
|
||||
#### `site delete`
|
||||
|
||||
Delete a site. Fails if any instances are assigned to it.
|
||||
|
||||
```sh
|
||||
scadalink --contact-points <uri> site delete --id <int>
|
||||
```
|
||||
|
||||
| Option | Required | Description |
|
||||
|--------|----------|-------------|
|
||||
| `--id` | yes | Site ID |
|
||||
|
||||
#### `site deploy-artifacts`
|
||||
|
||||
Push compiled artifacts to one or all sites.
|
||||
|
||||
```sh
|
||||
scadalink --contact-points <uri> site deploy-artifacts [--site-id <int>]
|
||||
```
|
||||
|
||||
| Option | Required | Description |
|
||||
|--------|----------|-------------|
|
||||
| `--site-id` | no | Target site ID; omit to deploy to all sites |
|
||||
|
||||
---
|
||||
|
||||
### `deploy` — Deployment operations
|
||||
|
||||
#### `deploy instance`
|
||||
|
||||
Deploy a single instance (same as `instance deploy`).
|
||||
|
||||
```sh
|
||||
scadalink --contact-points <uri> deploy instance --id <int>
|
||||
```
|
||||
|
||||
| Option | Required | Description |
|
||||
|--------|----------|-------------|
|
||||
| `--id` | yes | Instance ID |
|
||||
|
||||
#### `deploy artifacts`
|
||||
|
||||
Deploy compiled artifacts to one or all sites (same as `site deploy-artifacts`).
|
||||
|
||||
```sh
|
||||
scadalink --contact-points <uri> deploy artifacts [--site-id <int>]
|
||||
```
|
||||
|
||||
| Option | Required | Description |
|
||||
|--------|----------|-------------|
|
||||
| `--site-id` | no | Target site ID; omit for all sites |
|
||||
|
||||
#### `deploy status`
|
||||
|
||||
Query deployment records, with optional filters and pagination.
|
||||
|
||||
```sh
|
||||
scadalink --contact-points <uri> deploy status [--instance-id <int>] [--status <string>] [--page <int>] [--page-size <int>]
|
||||
```
|
||||
|
||||
| Option | Required | Default | Description |
|
||||
|--------|----------|---------|-------------|
|
||||
| `--instance-id` | no | — | Filter by instance ID |
|
||||
| `--status` | no | — | Filter by deployment status string |
|
||||
| `--page` | no | `1` | Page number |
|
||||
| `--page-size` | no | `50` | Results per page |
|
||||
|
||||
---
|
||||
|
||||
### `data-connection` — Manage data connections
|
||||
|
||||
#### `data-connection list`
|
||||
|
||||
List all configured data connections.
|
||||
|
||||
```sh
|
||||
scadalink --contact-points <uri> data-connection list
|
||||
```
|
||||
|
||||
#### `data-connection create`
|
||||
|
||||
Create a new data connection definition.
|
||||
|
||||
```sh
|
||||
scadalink --contact-points <uri> data-connection create --name <string> --protocol <string> [--configuration <json>]
|
||||
```
|
||||
|
||||
| Option | Required | Description |
|
||||
|--------|----------|-------------|
|
||||
| `--name` | yes | Connection name |
|
||||
| `--protocol` | yes | Protocol identifier (e.g. `OpcUa`) |
|
||||
| `--configuration` | no | Protocol-specific configuration as a JSON string |
|
||||
|
||||
#### `data-connection delete`
|
||||
|
||||
Delete a data connection.
|
||||
|
||||
```sh
|
||||
scadalink --contact-points <uri> data-connection delete --id <int>
|
||||
```
|
||||
|
||||
| Option | Required | Description |
|
||||
|--------|----------|-------------|
|
||||
| `--id` | yes | Data connection ID |
|
||||
|
||||
#### `data-connection assign`
|
||||
|
||||
Assign a data connection to a site.
|
||||
|
||||
```sh
|
||||
scadalink --contact-points <uri> data-connection assign --connection-id <int> --site-id <int>
|
||||
```
|
||||
|
||||
| Option | Required | Description |
|
||||
|--------|----------|-------------|
|
||||
| `--connection-id` | yes | Data connection ID |
|
||||
| `--site-id` | yes | Site ID |
|
||||
|
||||
---
|
||||
|
||||
### `external-system` — Manage external HTTP systems
|
||||
|
||||
#### `external-system list`
|
||||
|
||||
List all external system definitions.
|
||||
|
||||
```sh
|
||||
scadalink --contact-points <uri> external-system list
|
||||
```
|
||||
|
||||
#### `external-system create`
|
||||
|
||||
Register an external HTTP system that scripts can call.
|
||||
|
||||
```sh
|
||||
scadalink --contact-points <uri> external-system create --name <string> --endpoint-url <url> --auth-type <string> [--auth-config <json>]
|
||||
```
|
||||
|
||||
| Option | Required | Description |
|
||||
|--------|----------|-------------|
|
||||
| `--name` | yes | Display name |
|
||||
| `--endpoint-url` | yes | Base URL of the external system |
|
||||
| `--auth-type` | yes | Authentication type: `ApiKey` or `BasicAuth` |
|
||||
| `--auth-config` | no | Auth credentials as a JSON string |
|
||||
|
||||
#### `external-system delete`
|
||||
|
||||
Delete an external system definition.
|
||||
|
||||
```sh
|
||||
scadalink --contact-points <uri> external-system delete --id <int>
|
||||
```
|
||||
|
||||
| Option | Required | Description |
|
||||
|--------|----------|-------------|
|
||||
| `--id` | yes | External system ID |
|
||||
|
||||
---
|
||||
|
||||
### `notification` — Manage notification lists
|
||||
|
||||
#### `notification list`
|
||||
|
||||
List all notification lists.
|
||||
|
||||
```sh
|
||||
scadalink --contact-points <uri> notification list
|
||||
```
|
||||
|
||||
#### `notification create`
|
||||
|
||||
Create a notification list with one or more recipients.
|
||||
|
||||
```sh
|
||||
scadalink --contact-points <uri> notification create --name <string> --emails <email1,email2,...>
|
||||
```
|
||||
|
||||
| Option | Required | Description |
|
||||
|--------|----------|-------------|
|
||||
| `--name` | yes | Notification list name |
|
||||
| `--emails` | yes | Comma-separated list of recipient email addresses |
|
||||
|
||||
#### `notification delete`
|
||||
|
||||
Delete a notification list.
|
||||
|
||||
```sh
|
||||
scadalink --contact-points <uri> notification delete --id <int>
|
||||
```
|
||||
|
||||
| Option | Required | Description |
|
||||
|--------|----------|-------------|
|
||||
| `--id` | yes | Notification list ID |
|
||||
|
||||
---
|
||||
|
||||
### `security` — Security settings
|
||||
|
||||
#### `security api-key list`
|
||||
|
||||
List all inbound API keys.
|
||||
|
||||
```sh
|
||||
scadalink --contact-points <uri> security api-key list
|
||||
```
|
||||
|
||||
#### `security api-key create`
|
||||
|
||||
Create a new inbound API key. The generated key value is returned in the response and not stored in plaintext — save it immediately.
|
||||
|
||||
```sh
|
||||
scadalink --contact-points <uri> security api-key create --name <string>
|
||||
```
|
||||
|
||||
| Option | Required | Description |
|
||||
|--------|----------|-------------|
|
||||
| `--name` | yes | Descriptive label for the key |
|
||||
|
||||
#### `security api-key delete`
|
||||
|
||||
Revoke and delete an API key.
|
||||
|
||||
```sh
|
||||
scadalink --contact-points <uri> security api-key delete --id <int>
|
||||
```
|
||||
|
||||
| Option | Required | Description |
|
||||
|--------|----------|-------------|
|
||||
| `--id` | yes | API key ID |
|
||||
|
||||
#### `security role-mapping list`
|
||||
|
||||
List all LDAP group → role mappings.
|
||||
|
||||
```sh
|
||||
scadalink --contact-points <uri> security role-mapping list
|
||||
```
|
||||
|
||||
#### `security role-mapping create`
|
||||
|
||||
Map an LDAP group to a ScadaLink role.
|
||||
|
||||
```sh
|
||||
scadalink --contact-points <uri> security role-mapping create --ldap-group <string> --role <string>
|
||||
```
|
||||
|
||||
| Option | Required | Description |
|
||||
|--------|----------|-------------|
|
||||
| `--ldap-group` | yes | LDAP group distinguished name or CN |
|
||||
| `--role` | yes | ScadaLink role: `Admin`, `Design`, or `Deployment` |
|
||||
|
||||
#### `security role-mapping delete`
|
||||
|
||||
Remove an LDAP role mapping.
|
||||
|
||||
```sh
|
||||
scadalink --contact-points <uri> security role-mapping delete --id <int>
|
||||
```
|
||||
|
||||
| Option | Required | Description |
|
||||
|--------|----------|-------------|
|
||||
| `--id` | yes | Mapping ID |
|
||||
|
||||
---
|
||||
|
||||
### `health` — Health monitoring
|
||||
|
||||
#### `health summary`
|
||||
|
||||
Return the current health state for all known sites as a JSON object keyed by site identifier.
|
||||
|
||||
```sh
|
||||
scadalink --contact-points <uri> health summary
|
||||
```
|
||||
|
||||
#### `health site`
|
||||
|
||||
Return the health state for a single site.
|
||||
|
||||
```sh
|
||||
scadalink --contact-points <uri> health site --identifier <string>
|
||||
```
|
||||
|
||||
| Option | Required | Description |
|
||||
|--------|----------|-------------|
|
||||
| `--identifier` | yes | Site identifier (e.g. `site-a`) |
|
||||
|
||||
---
|
||||
|
||||
### `audit-log` — Audit log queries
|
||||
|
||||
#### `audit-log query`
|
||||
|
||||
Query the central audit log with optional filters and pagination.
|
||||
|
||||
```sh
|
||||
scadalink --contact-points <uri> audit-log query [options]
|
||||
```
|
||||
|
||||
| Option | Required | Default | Description |
|
||||
|--------|----------|---------|-------------|
|
||||
| `--user` | no | — | Filter by username |
|
||||
| `--entity-type` | no | — | Filter by entity type (e.g. `Template`, `Instance`) |
|
||||
| `--action` | no | — | Filter by action (e.g. `Create`, `Delete`) |
|
||||
| `--from` | no | — | Start timestamp in ISO 8601 format |
|
||||
| `--to` | no | — | End timestamp in ISO 8601 format |
|
||||
| `--page` | no | `1` | Page number |
|
||||
| `--page-size` | no | `50` | Results per page |
|
||||
|
||||
---
|
||||
|
||||
## Architecture Notes
|
||||
|
||||
The CLI connects to the Central cluster using Akka.NET's `ClusterClient`. It does not join the cluster — it contacts the `ClusterClientReceptionist` on one of the configured Central nodes and sends commands to the `ManagementActor` at path `/user/management`.
|
||||
|
||||
The connection is established per-command invocation and torn down cleanly via `CoordinatedShutdown` when the command completes.
|
||||
|
||||
Role enforcement is applied by the ManagementActor on the server side. The current CLI placeholder user carries `Admin`, `Design`, and `Deployment` roles; production use will integrate LDAP authentication via `--username` / `--password`.
|
||||
Reference in New Issue
Block a user