isoloom_plan/configure/resources.rs
1//! The resources of a resolved recipe: each step of the model with nothing left to resolve. A
2//! step that needs no resolving (a file, a user, a command) is its authored type, so the model
3//! has one definition of it; the others carry what their machine's OS and ports make of them.
4
5use std::num::NonZeroU16;
6
7use isoloom_spec::model::Protocol;
8use isoloom_spec::syntax::{AbsPath, AptWord, Argv, HttpsUrl, ProjectPath, RepositoryKey, RepositoryName, UnitName, UserName};
9
10/// A package to install, its version pinned when given (an entry's always is, from `lock.yml`).
11pub type PinnedPackage = isoloom_spec::syntax::Package;
12/// A project file or folder copied as it is.
13pub type CopyPlan = isoloom_spec::syntax::Copy;
14/// A folder, a removal, a link or a pinned download.
15pub type FilePlan = isoloom_spec::syntax::FileStep;
16/// A project file whose placeholders [`super::fill`] fills in, placed in the machine.
17pub type TemplatePlan = isoloom_spec::syntax::Template;
18/// A local user (its literal password hashed when rendered for Linux).
19pub type UserPlan = isoloom_spec::syntax::UserStep;
20/// A local group.
21pub type GroupPlan = isoloom_spec::syntax::GroupStep;
22/// A command run once, behind its guard.
23pub type CommandPlan = isoloom_spec::syntax::CommandStep;
24
25/// One resource of a recipe, in the order the recipe lists it: no renderer reorders, groups or
26/// parallelizes them.
27#[derive(Debug, Clone, PartialEq, Eq)]
28#[non_exhaustive]
29pub enum Resource {
30 /// A vendor's package repository.
31 Repository(RepositoryPlan),
32 /// Packages, installed together.
33 Packages(Vec<PinnedPackage>),
34 /// A project file or folder.
35 Copy(CopyPlan),
36 /// A folder, a removal, a link or a download.
37 File(FilePlan),
38 /// A template, filled in at generation.
39 Template(TemplatePlan),
40 /// A user.
41 User(UserPlan),
42 /// A group.
43 Group(GroupPlan),
44 /// A daemon.
45 Service(ServicePlan),
46 /// A command, behind its guard.
47 Command(CommandPlan),
48 /// Ports opened in the OS's firewall.
49 Firewall(Vec<PortSpec>),
50 /// A project script.
51 Run(RunPlan),
52 /// A restart.
53 Reboot,
54 /// An Ansible playbook of the project.
55 Playbook(ProjectPath),
56}
57
58impl Resource {
59 /// The step kind it comes from, as written (`repository`, `run`).
60 #[must_use]
61 pub fn kind(&self) -> &'static str {
62 match self {
63 Resource::Repository(_) => "repository",
64 Resource::Packages(_) => "packages",
65 Resource::Copy(_) => "copy",
66 Resource::File(_) => "file",
67 Resource::Template(_) => "template",
68 Resource::User(_) => "user",
69 Resource::Group(_) => "group",
70 Resource::Service(_) => "service",
71 Resource::Command(_) => "command",
72 Resource::Firewall(_) => "firewall",
73 Resource::Run(_) => "run",
74 Resource::Reboot => "reboot",
75 Resource::Playbook(_) => "playbook",
76 }
77 }
78}
79
80/// The package manager a repository is added to: the OS family's.
81#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
82#[non_exhaustive]
83pub enum RepositoryKind {
84 /// A deb822 source and its keyring (Debian's family).
85 Apt,
86 /// A `.repo` file and its key (Red Hat's family).
87 Rpm,
88 /// A `repositories` line and its key (Alpine).
89 Apk,
90}
91
92/// A repository, its suite and components resolved (apt's) and its key pinned.
93#[derive(Debug, Clone, PartialEq, Eq)]
94pub struct RepositoryPlan {
95 /// Its name: it names the source and key files.
96 pub name: RepositoryName,
97 /// The package manager it is added to.
98 pub kind: RepositoryKind,
99 /// Its address.
100 pub url: HttpsUrl,
101 /// Its signing key, pinned by SHA-256.
102 pub key: RepositoryKey,
103 /// The suite (apt: the step's, else the OS's codename).
104 pub suite: Option<AptWord>,
105 /// The components (apt: the step's, else `main`; empty for the others).
106 pub components: Vec<AptWord>,
107 /// Its priority over the OS's own packages.
108 pub priority: Option<i32>,
109}
110
111/// A daemon, its unit resolved for the family.
112#[derive(Debug, Clone, PartialEq, Eq)]
113pub struct ServicePlan {
114 /// The unit, as the family names it (`ssh` on Debian for the table's `ssh`, `sshd` on Rocky).
115 pub unit: UnitName,
116 /// Where its unit comes from.
117 pub source: ServiceSource,
118 /// Started at boot and now (else disabled and stopped).
119 pub enabled: bool,
120}
121
122/// Where a service's unit comes from.
123#[derive(Debug, Clone, PartialEq, Eq)]
124#[non_exhaustive]
125pub enum ServiceSource {
126 /// A package installs it.
127 Package,
128 /// Isoloom defines it from the step's `run`.
129 Defined {
130 /// What runs, in the foreground.
131 run: Argv,
132 /// The user it runs as (root when `None`).
133 user: Option<UserName>,
134 /// The folder it runs in.
135 cwd: Option<AbsPath>,
136 },
137}
138
139/// A port, by number and transport.
140#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
141pub struct PortSpec {
142 /// The number.
143 pub port: NonZeroU16,
144 /// The transport.
145 pub protocol: Protocol,
146}
147
148/// A project script, and the shell that runs it.
149#[derive(Debug, Clone, PartialEq, Eq)]
150pub struct RunPlan {
151 /// The script.
152 pub script: ProjectPath,
153 /// A path whose existence means it already ran.
154 pub creates: Option<AbsPath>,
155 /// What runs it.
156 pub shell: Shell,
157}
158
159/// What runs a script.
160#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
161#[non_exhaustive]
162pub enum Shell {
163 /// `sh` (Linux).
164 Posix,
165 /// PowerShell (Windows).
166 PowerShell,
167}