pub trait Item: DynClone {
type Error: Error + Send + Sync;
type State: Clone + Debug + Display + PartialEq + Serialize + DeserializeOwned + Send + Sync + 'static;
type StateDiff: Clone + Debug + Display + Serialize + DeserializeOwned + Send + Sync + 'static;
type Params<'exec>: Params<Spec = ParamsSpec<Self::Params<'exec>>> + Clone + Debug + Serialize + DeserializeOwned + Send + Sync + 'static;
type Data<'exec>: Data<'exec>;
// Required methods
fn id(&self) -> &ItemId;
fn setup<'life0, 'life1, 'async_trait>(
&'life0 self,
resources: &'life1 mut Resources<Empty>
) -> Pin<Box<dyn Future<Output = Result<(), Self::Error>> + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait;
fn try_state_current<'life0, 'life1, 'life2, 'life3, 'async_trait>(
fn_ctx: FnCtx<'life0>,
params_partial: &'life1 <Self::Params<'life2> as Params>::Partial,
data: Self::Data<'life3>
) -> Pin<Box<dyn Future<Output = Result<Option<Self::State>, Self::Error>> + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait;
fn state_current<'life0, 'life1, 'life2, 'life3, 'async_trait>(
fn_ctx: FnCtx<'life0>,
params: &'life1 Self::Params<'life2>,
data: Self::Data<'life3>
) -> Pin<Box<dyn Future<Output = Result<Self::State, Self::Error>> + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait;
fn try_state_goal<'life0, 'life1, 'life2, 'life3, 'async_trait>(
fn_ctx: FnCtx<'life0>,
params_partial: &'life1 <Self::Params<'life2> as Params>::Partial,
data: Self::Data<'life3>
) -> Pin<Box<dyn Future<Output = Result<Option<Self::State>, Self::Error>> + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait;
fn state_goal<'life0, 'life1, 'life2, 'life3, 'async_trait>(
fn_ctx: FnCtx<'life0>,
params: &'life1 Self::Params<'life2>,
data: Self::Data<'life3>
) -> Pin<Box<dyn Future<Output = Result<Self::State, Self::Error>> + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait;
fn state_diff<'life0, 'life1, 'life2, 'life3, 'life4, 'async_trait>(
params_partial: &'life0 <Self::Params<'life1> as Params>::Partial,
data: Self::Data<'life2>,
state_a: &'life3 Self::State,
state_b: &'life4 Self::State
) -> Pin<Box<dyn Future<Output = Result<Self::StateDiff, Self::Error>> + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait,
'life4: 'async_trait;
fn state_clean<'life0, 'life1, 'life2, 'async_trait>(
params_partial: &'life0 <Self::Params<'life1> as Params>::Partial,
data: Self::Data<'life2>
) -> Pin<Box<dyn Future<Output = Result<Self::State, Self::Error>> + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait;
fn apply_check<'life0, 'life1, 'life2, 'life3, 'life4, 'life5, 'async_trait>(
params: &'life0 Self::Params<'life1>,
data: Self::Data<'life2>,
state_current: &'life3 Self::State,
state_target: &'life4 Self::State,
diff: &'life5 Self::StateDiff
) -> Pin<Box<dyn Future<Output = Result<ApplyCheck, Self::Error>> + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait,
'life4: 'async_trait,
'life5: 'async_trait;
fn apply_dry<'life0, 'life1, 'life2, 'life3, 'life4, 'life5, 'life6, 'async_trait>(
fn_ctx: FnCtx<'life0>,
params: &'life1 Self::Params<'life2>,
data: Self::Data<'life3>,
state_current: &'life4 Self::State,
state_target: &'life5 Self::State,
diff: &'life6 Self::StateDiff
) -> Pin<Box<dyn Future<Output = Result<Self::State, Self::Error>> + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait,
'life4: 'async_trait,
'life5: 'async_trait,
'life6: 'async_trait;
fn apply<'life0, 'life1, 'life2, 'life3, 'life4, 'life5, 'life6, 'async_trait>(
fn_ctx: FnCtx<'life0>,
params: &'life1 Self::Params<'life2>,
data: Self::Data<'life3>,
state_current: &'life4 Self::State,
state_target: &'life5 Self::State,
diff: &'life6 Self::StateDiff
) -> Pin<Box<dyn Future<Output = Result<Self::State, Self::Error>> + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait,
'life4: 'async_trait,
'life5: 'async_trait,
'life6: 'async_trait;
}
Expand description
Defines all of the data and logic to manage an item.
The item may be simple or complex, ranging from:
- File download.
- Application installation.
- Server launching / initialization.
- Multiple cloud resource management.
The lifecycle functions include:
- Status discovery.
- Execution.
- Backup.
- Restoration.
- Clean up / deletion.
Since the latter four functions are write-operations, their specification includes a dry run function.
§Logical IDs vs Physical IDs
A logical ID is defined by code, and does not change. A physical ID is one generated during execution, which may be random or computed.
§Examples
The following are examples of logical IDs and corresponding physical IDs:
-
If the function creates a file, the ID may be the full file path, or it may be the file name, assuming the file path may be deduced by the clean up logic from
Data
. -
If the function instantiates a virtual machine on a cloud platform, this may be the ID of the instance so that it may be terminated.
Logical ID | Physical ID |
---|---|
app.file_path | /mnt/data/app.zip |
app_server_instance_id | ef34a9a4-0c02-45a6-96ec-a4db06d4980c |
app_server.address | 10.0.0.1 |
Required Associated Types§
sourcetype State: Clone + Debug + Display + PartialEq + Serialize + DeserializeOwned + Send + Sync + 'static
type State: Clone + Debug + Display + PartialEq + Serialize + DeserializeOwned + Send + Sync + 'static
Summary of the managed item’s state.
For an extensive explanation of state, and how to define it, please
see the state concept as well as the State
type.
This type is used to represent the current state of the item (if it exists), the goal state of the item (what is intended to exist), and is used in the diff calculation – what is the difference between the current and goal states.
§Examples
- A file’s state may be its path, and a hash of its contents.
- A server’s state may be its operating system, CPU and memory capacity, IP address, and ID.
sourcetype StateDiff: Clone + Debug + Display + Serialize + DeserializeOwned + Send + Sync + 'static
type StateDiff: Clone + Debug + Display + Serialize + DeserializeOwned + Send + Sync + 'static
Diff between the current and target State
s.
§Design Note
Initially I thought the field-wise diff between two State
s is
suitable, but:
- Externally controlled state may not be known ahead of time.
- It isn’t easy or necessarily goal to compare every single field.
state.apply(diff) = state_goal
may not be meaningful for a field level diff, and theapply
may be a complex process.
sourcetype Params<'exec>: Params<Spec = ParamsSpec<Self::Params<'exec>>> + Clone + Debug + Serialize + DeserializeOwned + Send + Sync + 'static
type Params<'exec>: Params<Spec = ParamsSpec<Self::Params<'exec>>> + Clone + Debug + Serialize + DeserializeOwned + Send + Sync + 'static
Parameters to use this item.
Item consumers must provide for this item to work.
§Examples
-
For a file download item:
- URL of the file.
- Credentials.
-
For a server launch item:
- Image ID.
- Server size.
§Implementors
Peace will automatically save and load these into Resources
when a
command context is built.
Required Methods§
sourcefn id(&self) -> &ItemId
fn id(&self) -> &ItemId
Returns the ID of this full spec.
§Implementors
The ID should be a unique value that does not change over the lifetime of the managed item.
ItemId
s must begin with a letter or underscore, and contain only
letters, numbers, and underscores. The item_id!
macro provides
a compile time check to ensure that these conditions are upheld.
const fn id() -> ItemId {
item_id!("my_item")
}
§Design Note
This is an instance method as logic for an Item
may be used for
multiple tasks. For example, an Item
implemented to download a
file may be instantiated with different files to download, and each
instance of the Item
should have its own ID.
sourcefn setup<'life0, 'life1, 'async_trait>(
&'life0 self,
resources: &'life1 mut Resources<Empty>
) -> Pin<Box<dyn Future<Output = Result<(), Self::Error>> + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn setup<'life0, 'life1, 'async_trait>(
&'life0 self,
resources: &'life1 mut Resources<Empty>
) -> Pin<Box<dyn Future<Output = Result<(), Self::Error>> + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
sourcefn try_state_current<'life0, 'life1, 'life2, 'life3, 'async_trait>(
fn_ctx: FnCtx<'life0>,
params_partial: &'life1 <Self::Params<'life2> as Params>::Partial,
data: Self::Data<'life3>
) -> Pin<Box<dyn Future<Output = Result<Option<Self::State>, Self::Error>> + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait,
fn try_state_current<'life0, 'life1, 'life2, 'life3, 'async_trait>(
fn_ctx: FnCtx<'life0>,
params_partial: &'life1 <Self::Params<'life2> as Params>::Partial,
data: Self::Data<'life3>
) -> Pin<Box<dyn Future<Output = Result<Option<Self::State>, Self::Error>> + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait,
Returns the current state of the managed item, if possible.
This should return Ok(None)
if the state is not able to be queried,
such as when failing to connect to a remote host, instead of returning
an error.
sourcefn state_current<'life0, 'life1, 'life2, 'life3, 'async_trait>(
fn_ctx: FnCtx<'life0>,
params: &'life1 Self::Params<'life2>,
data: Self::Data<'life3>
) -> Pin<Box<dyn Future<Output = Result<Self::State, Self::Error>> + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait,
fn state_current<'life0, 'life1, 'life2, 'life3, 'async_trait>(
fn_ctx: FnCtx<'life0>,
params: &'life1 Self::Params<'life2>,
data: Self::Data<'life3>
) -> Pin<Box<dyn Future<Output = Result<Self::State, Self::Error>> + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait,
Returns the current state of the managed item.
This is expected to successfully discover the current state, so errors will be presented to the user.
sourcefn try_state_goal<'life0, 'life1, 'life2, 'life3, 'async_trait>(
fn_ctx: FnCtx<'life0>,
params_partial: &'life1 <Self::Params<'life2> as Params>::Partial,
data: Self::Data<'life3>
) -> Pin<Box<dyn Future<Output = Result<Option<Self::State>, Self::Error>> + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait,
fn try_state_goal<'life0, 'life1, 'life2, 'life3, 'async_trait>(
fn_ctx: FnCtx<'life0>,
params_partial: &'life1 <Self::Params<'life2> as Params>::Partial,
data: Self::Data<'life3>
) -> Pin<Box<dyn Future<Output = Result<Option<Self::State>, Self::Error>> + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait,
Returns the goal state of the managed item, if possible.
This should return Ok(None)
if the state is not able to be queried,
such as when failing to read a potentially non-existent file to
determine its content hash, instead of returning an error.
sourcefn state_goal<'life0, 'life1, 'life2, 'life3, 'async_trait>(
fn_ctx: FnCtx<'life0>,
params: &'life1 Self::Params<'life2>,
data: Self::Data<'life3>
) -> Pin<Box<dyn Future<Output = Result<Self::State, Self::Error>> + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait,
fn state_goal<'life0, 'life1, 'life2, 'life3, 'async_trait>(
fn_ctx: FnCtx<'life0>,
params: &'life1 Self::Params<'life2>,
data: Self::Data<'life3>
) -> Pin<Box<dyn Future<Output = Result<Self::State, Self::Error>> + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait,
Returns the goal state of the managed item.
This is expected to successfully discover the goal state, so errors will be presented to the user.
§Examples
-
For a file download item, the goal state could be the destination path and a content hash.
-
For a web application service item, the goal state could be the web service is running on the latest version.
sourcefn state_diff<'life0, 'life1, 'life2, 'life3, 'life4, 'async_trait>(
params_partial: &'life0 <Self::Params<'life1> as Params>::Partial,
data: Self::Data<'life2>,
state_a: &'life3 Self::State,
state_b: &'life4 Self::State
) -> Pin<Box<dyn Future<Output = Result<Self::StateDiff, Self::Error>> + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait,
'life4: 'async_trait,
fn state_diff<'life0, 'life1, 'life2, 'life3, 'life4, 'async_trait>(
params_partial: &'life0 <Self::Params<'life1> as Params>::Partial,
data: Self::Data<'life2>,
state_a: &'life3 Self::State,
state_b: &'life4 Self::State
) -> Pin<Box<dyn Future<Output = Result<Self::StateDiff, Self::Error>> + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait,
'life4: 'async_trait,
Returns the difference between two states.
§Implementors
When this type is serialized, it should provide “just enough” /
meaningful information to the user on what has changed. So instead of
including the complete goal [State
], it should only include the
parts that changed.
This function call is intended to be cheap and fast.
§Examples
-
For a file download item, the difference could be the content hash changes from
abcd
toefgh
. -
For a web application service item, the goal state could be the application version changing from 1 to 2.
sourcefn state_clean<'life0, 'life1, 'life2, 'async_trait>(
params_partial: &'life0 <Self::Params<'life1> as Params>::Partial,
data: Self::Data<'life2>
) -> Pin<Box<dyn Future<Output = Result<Self::State, Self::Error>> + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
fn state_clean<'life0, 'life1, 'life2, 'async_trait>(
params_partial: &'life0 <Self::Params<'life1> as Params>::Partial,
data: Self::Data<'life2>
) -> Pin<Box<dyn Future<Output = Result<Self::State, Self::Error>> + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
Returns the representation of a clean State
.
§Implementors
This should return essentially the None
concept of the item
state. The diff between this and the current state will be shown to the
user when they want to see what would be cleaned up by the clean
command.
sourcefn apply_check<'life0, 'life1, 'life2, 'life3, 'life4, 'life5, 'async_trait>(
params: &'life0 Self::Params<'life1>,
data: Self::Data<'life2>,
state_current: &'life3 Self::State,
state_target: &'life4 Self::State,
diff: &'life5 Self::StateDiff
) -> Pin<Box<dyn Future<Output = Result<ApplyCheck, Self::Error>> + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait,
'life4: 'async_trait,
'life5: 'async_trait,
fn apply_check<'life0, 'life1, 'life2, 'life3, 'life4, 'life5, 'async_trait>(
params: &'life0 Self::Params<'life1>,
data: Self::Data<'life2>,
state_current: &'life3 Self::State,
state_target: &'life4 Self::State,
diff: &'life5 Self::StateDiff
) -> Pin<Box<dyn Future<Output = Result<ApplyCheck, Self::Error>> + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait,
'life4: 'async_trait,
'life5: 'async_trait,
Returns whether apply
needs to be executed.
If the current state is already in sync with the target state, then
apply
does not have to be executed.
§Examples
-
For a file download item, if the destination file differs from the file on the server, then the file needs to be downloaded.
-
For a web application service item, if the web service is running, but reports a previous version, then the service may need to be restarted.
§Implementors
This function call is intended to be cheap and fast.
§Parameters
fn_ctx
: Context to send progress updates.params
: Parameters to the item.data
: Runtime data that the function reads from or writes to.state_current
: CurrentState
of the managed item, returned fromstate_current
.state_target
: TargetState
of the managed item, eitherstate_clean
orstate_goal
.state_diff
: GoalState
of the managed item, returned fromstate_diff
.
sourcefn apply_dry<'life0, 'life1, 'life2, 'life3, 'life4, 'life5, 'life6, 'async_trait>(
fn_ctx: FnCtx<'life0>,
params: &'life1 Self::Params<'life2>,
data: Self::Data<'life3>,
state_current: &'life4 Self::State,
state_target: &'life5 Self::State,
diff: &'life6 Self::StateDiff
) -> Pin<Box<dyn Future<Output = Result<Self::State, Self::Error>> + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait,
'life4: 'async_trait,
'life5: 'async_trait,
'life6: 'async_trait,
fn apply_dry<'life0, 'life1, 'life2, 'life3, 'life4, 'life5, 'life6, 'async_trait>(
fn_ctx: FnCtx<'life0>,
params: &'life1 Self::Params<'life2>,
data: Self::Data<'life3>,
state_current: &'life4 Self::State,
state_target: &'life5 Self::State,
diff: &'life6 Self::StateDiff
) -> Pin<Box<dyn Future<Output = Result<Self::State, Self::Error>> + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait,
'life4: 'async_trait,
'life5: 'async_trait,
'life6: 'async_trait,
Dry-run transform of the current state to the target state.
This will only be called if check
returns ExecRequired
.
This should mirror the logic in [apply
], with the following
differences:
-
When state will actually be altered, this would skip the logic.
-
Where there would be IDs received from an external system, a placeholder ID should still be inserted into the runtime data. This should allow subsequent
Item
s that rely on this one to use those placeholders in their logic.
§Implementors
This function call is intended to be read-only and cheap.
§Parameters
fn_ctx
: Context to send progress updates.params
: Parameters to the item.data
: Runtime data that the function reads from or writes to.state_current
: CurrentState
of the managed item, returned fromstate_current
.state_target
: TargetState
of the managed item, eitherstate_clean
orstate_goal
.state_diff
: GoalState
of the managed item, returned fromstate_diff
.
sourcefn apply<'life0, 'life1, 'life2, 'life3, 'life4, 'life5, 'life6, 'async_trait>(
fn_ctx: FnCtx<'life0>,
params: &'life1 Self::Params<'life2>,
data: Self::Data<'life3>,
state_current: &'life4 Self::State,
state_target: &'life5 Self::State,
diff: &'life6 Self::StateDiff
) -> Pin<Box<dyn Future<Output = Result<Self::State, Self::Error>> + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait,
'life4: 'async_trait,
'life5: 'async_trait,
'life6: 'async_trait,
fn apply<'life0, 'life1, 'life2, 'life3, 'life4, 'life5, 'life6, 'async_trait>(
fn_ctx: FnCtx<'life0>,
params: &'life1 Self::Params<'life2>,
data: Self::Data<'life3>,
state_current: &'life4 Self::State,
state_target: &'life5 Self::State,
diff: &'life6 Self::StateDiff
) -> Pin<Box<dyn Future<Output = Result<Self::State, Self::Error>> + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait,
'life4: 'async_trait,
'life5: 'async_trait,
'life6: 'async_trait,
Transforms the current state to the target state.
This will only be called if check
returns ExecRequired
.
§Parameters
fn_ctx
: Context to send progress updates.params
: Parameters to the item.data
: Runtime data that the function reads from or writes to.state_current
: CurrentState
of the managed item, returned fromstate_current
.state_target
: TargetState
of the managed item, eitherstate_clean
orstate_goal
.state_diff
: GoalState
of the managed item, returned fromstate_diff
.