Skip to main content

edgehog_device_runtime_containers/resource/
mod.rs

1// This file is part of Edgehog.
2//
3// Copyright 2025 SECO Mind Srl
4//
5// Licensed under the Apache License, Version 2.0 (the "License");
6// you may not use this file except in compliance with the License.
7// You may obtain a copy of the License at
8//
9//    http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
16//
17// SPDX-License-Identifier: Apache-2.0
18
19//! Resource for the service.
20//!
21//! Handles and generalizes the operation on the various type of resources.
22
23use std::future::Future;
24
25use async_trait::async_trait;
26use tracing::debug;
27use uuid::Uuid;
28
29use crate::{
30    error::DockerError,
31    properties::{Client, PropertyError},
32    store::{StateStore, StoreError},
33    Docker,
34};
35
36pub(crate) mod container;
37pub(crate) mod deployment;
38pub(crate) mod device_mapping;
39pub(crate) mod image;
40pub(crate) mod network;
41pub(crate) mod volume;
42
43/// Error returned from a Resource operation.
44#[derive(Debug, thiserror::Error, displaydoc::Display)]
45pub enum ResourceError {
46    /// couldn't publish resource property
47    Property(#[from] PropertyError),
48    /// couldn't complete the store operation
49    Store(#[from] StoreError),
50    /// couldn't complete docker operation
51    Docker(#[source] DockerError),
52    /// couldn't fetch the {resource} with id {id}
53    Missing {
54        /// Id of the resource
55        id: Uuid,
56        /// Type of the resource
57        resource: &'static str,
58    },
59}
60
61#[derive(Debug)]
62pub(crate) struct Context<'a, D> {
63    /// Id of the resource
64    pub(crate) id: Uuid,
65    pub(crate) store: &'a mut StateStore,
66    pub(crate) device: &'a mut D,
67    pub(crate) client: &'a Docker,
68}
69
70/// State of the object for the request.
71#[derive(Debug, Clone, Copy, PartialEq, Eq)]
72pub(crate) enum State {
73    // The resource was published and saved to the state file
74    Missing,
75    // The resource was created
76    Created,
77}
78
79impl<T> From<T> for ResourceError
80where
81    T: Into<DockerError>,
82{
83    fn from(value: T) -> Self {
84        ResourceError::Docker(value.into())
85    }
86}
87
88type Result<T> = std::result::Result<T, ResourceError>;
89
90#[async_trait]
91pub(crate) trait Resource<D>: Sized
92where
93    D: Client + Sync + 'static,
94{
95    async fn publish(ctx: Context<'_, D>) -> Result<()>;
96}
97
98pub(crate) trait Create<D>: Resource<D>
99where
100    D: Client + Send + Sync + 'static,
101{
102    fn fetch(ctx: &mut Context<'_, D>) -> impl Future<Output = Result<(State, Self)>> + Send;
103
104    fn create(&mut self, ctx: &mut Context<'_, D>) -> impl Future<Output = Result<()>> + Send;
105
106    fn delete(&mut self, ctx: &mut Context<'_, D>) -> impl Future<Output = Result<()>> + Send;
107
108    fn unset(&mut self, ctx: &mut Context<'_, D>) -> impl Future<Output = Result<()>> + Send;
109
110    fn refresh(ctx: &mut Context<'_, D>) -> impl Future<Output = Result<()>> + Send;
111
112    async fn up(mut ctx: Context<'_, D>) -> Result<Self> {
113        let (state, mut resource) = Self::fetch(&mut ctx).await?;
114
115        match state {
116            State::Missing => {
117                debug!("resource missing, creating it");
118
119                resource.create(&mut ctx).await?;
120            }
121            State::Created => {
122                debug!("resource already created");
123            }
124        }
125
126        Ok(resource)
127    }
128
129    async fn down(mut ctx: Context<'_, D>) -> Result<()> {
130        let (state, mut resource) = Self::fetch(&mut ctx).await?;
131
132        match state {
133            State::Missing => {
134                debug!("resource already missing");
135            }
136            State::Created => {
137                debug!("resource found, deleting it");
138
139                resource.delete(&mut ctx).await?
140            }
141        }
142
143        resource.unset(&mut ctx).await?;
144
145        Ok(())
146    }
147}