Deployment actions
The detail of a deployment gives its actions: what a client can do to it, in the order to show them. Each action has an identifier, a title, a description, whether it is enabled and why not, an optional confirmation question, a scope and a kind. The scope is the whole deployment or one instance. The kind is a toggle, which stays in the desired state, or an operation, which runs one time; an action with a procedure of its own has no kind. A client shows each action as a control. Deploy, export and delete have procedures of their own; each other action goes through one generic action procedure. Thus a client needs no new code for a new action (FR-012 of 001).
Migrations and Domains give the actions on a migration and on a domain.
Suspend and resume
Suspend stops the instances of a deployment and removes their names from DNS; the data stays. Resume starts them again. The detail gives both actions, and the one for the current state is not enabled; after a failed build, both are enabled.
A suspend is a value in the desired state, not a command to the instances (FR-013 of 001). Each build gets it, also a destroy, so no build starts a suspended deployment again. The toggle builds the configuration and source of the last successful build, so it does not deploy a configuration that only failed. A submit and an export are refused while the deployment is suspended: an export makes the snapshot of each app in its instance, thus it cannot run while the instances are stopped. An export suspends the deployment (see Export of a migration).
The schema of a revision declares which actions it supports and for which deployment methods. The detail gives a toggle only when the built-in schema names the method of the deployment, or when the deployment is suspended, so that no other deployment causes a schema build. Until the schema of the deployed source is known, the action shows as enabled; a revision that cannot do it refuses the action as an invalid configuration.
| Case | Error |
|---|---|
| unknown action, or no such deployment | not_found |
| nothing is deployed | conflict |
| already in the state that the action sets | conflict |
| a build of the deployment runs | queue_full |
| the deployed revision cannot do the action | invalid_configuration |
Operations of an application
An operation is a command that runs one time in the node of an application, for example the change of the role of an account (FR-018 of 001). It does not change the desired state and it is not a build: the state of the deployment and its last successful build stay the same. A command that changes lasting state that a deploy can revert must be a toggle, not an operation. The modules of the application declare its operations, one module for each command, so a new operation needs no code in api or in a front-end. A command that can run code of the caller or read a path of the node has no module, so no client can start it. A command also has no module when the identity provider or the front-end of the application does the same work. A comment in the modules of the application tells which commands are absent, and why.
The action of an operation has the identifier <application>.<operation>, the scope of that instance, the group of the command, the JSON Schema of its input, and whether the operation is sensitive. The group is the first word of the command line below the program, for example accounts for accounts-rotate, so a client can show many operations in groups. A client makes its form from the schema with the same code as the form of a configuration. The detail gives the operations of the enabled applications, as the schema of the deployed source declares them. Thus an old pin does not show an operation that it cannot do. The rules of the toggles apply: only a deployment method that the built-in schema names causes a schema build, and until the schema of the deployed source is known, the built-in operations show as pending.
The client sends the identifier and the input to the generic action procedure. api checks the input against the schema of the deployed source, and the effect makes the command line from the module of the command at that source. The effect keeps the input out of the store and out of the log. api then runs the command and waits for it to stop, at most five minutes. A non-zero exit code of the command is a result, not an error. The result holds the exit code and the last 64 KiB of the output.
A log of the deployment keeps the last 100 runs: the operation, who started it, the time, the exit code and the output. The output of a sensitive operation stays out of the log, so the client shows it one time only. The input stays out of the log and out of the audit events; an event has the action deployment_<application>.<operation>. Because the output can hold personal data, a destroy of the deployment removes its log, and a backup of the database has the retention of the other personal data.
An operation does not stop a build that starts after it. A deploy that restarts the node makes the operation fail, and the exit code tells so.
The operations backup-now, backup-list, backup-restore, backup-record-erasure and backup-confirm-erasures of each application work on the backups of its node. backup-now and backup-restore start systemd units and stop at once, so the timeout of a run does not limit a backup or a restore, and the work continues when api restarts. backup-list shows the snapshots, the result of the last restore, and the erasures to do again after a restore. A restore stops the services of the application first, and starts the services that it stopped when it ends, also after a failure. backup-record-erasure writes the time and the reference of an erasure of personal data to the node, and no other data of the person. A restore from a snapshot that is older than a recorded erasure adds that erasure to a list, which backup-list shows until backup-confirm-erasures clears it. The restore does not do the erasures again: they need the restored application, and the operator does them.
| Case | Error |
|---|---|
| no such deployment, or the revision has no such operation | not_found |
| nothing is deployed, or the deployment is suspended | conflict |
| the application is not enabled | conflict |
| a build of the deployment runs | queue_full |
| the deployed revision cannot do operations | invalid_configuration |
| the input does not agree with the schema | invalid_configuration |
| the command did not start | runtime_error |
Objects left behind
A step of a destroy, or of a replace, can fail and let the run continue. The object of the step then stays, and the build ends with exit code 4 (FR-019 of 001). api keeps a record of each such object in the table left_behind, by deployment. A later build that writes the object again removes the record. The table has no link to the deployment, thus the records stay when the deployment is gone.
| Call | Result |
|---|---|
GET /deployments/left-behind/{deployment_id} |
the records, without the inputs of the removals |
POST /deployments/left-behind/{deployment_id} |
runs the retry, and gives the counts of the results |
DELETE /deployments/left-behind/{entry_id}/{deployment_id} |
removes one record, and does not touch the object |
The part state-lifecycle of the technical design tells, for each effect, what a failure leaves and whether a second run is safe.
A record is retryable when its step gave a note with a kind and an environment. core/effects/tf/common/left-behind-kinds.nix has the variables that each kind can keep, and none of them holds a secret. The credentials come from the deploy credentials of the retry job. The retry is a node effect (retry-left-behind.sh), not a build, thus it does not change the state of the deployment.
A removal that succeeds removes its records. A removal that fails keeps them, with the last line of its output as the error. A removal that cannot reach its service fails. A release of a zone removes its record only when the DNS service showed that it no longer holds the zone for the deployment. The retry does not take options of ssh from a record. A retry of a zone or of a SPIRE name needs the deployment in its record, and the retry refuses a record without it, or with a different deployment than that of the retry. A retry of a SPIRE name uses the Incus server, port and project of the runtime settings of the deployment now, and it refuses a record with a different one, because a different server can show that the instance is gone when it is not. A retry of a SPIRE name refuses a name whose instance exists, and it gives the owner to spire-retire-name, which refuses a name that another deployment uses now.
| Case | Error |
|---|---|
| a build of the deployment is not finished | conflict |
| no such record for this deployment | not_found |