mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-26 02:11:53 +00:00
Feature/remove bollard (#2396)
* wip * remove bollard, add podman feature * fix error message parsing * fix subcommand * fix typo * use com.docker.network.bridge.name for podman * fix parse error * handle podman network interface nuance * add libyajl2 * use podman repos * manually add criu * do not manually require criu * remove docker images during cleanup stage * force removal * increase img size * Update startos-iso.yaml * don't remove docker
This commit is contained in:
8
.github/workflows/startos-iso.yaml
vendored
8
.github/workflows/startos-iso.yaml
vendored
@@ -12,6 +12,9 @@ on:
|
|||||||
- dev
|
- dev
|
||||||
- unstable
|
- unstable
|
||||||
- dev-unstable
|
- dev-unstable
|
||||||
|
- podman
|
||||||
|
- dev-podman
|
||||||
|
- dev-unstable-podman
|
||||||
runner:
|
runner:
|
||||||
type: choice
|
type: choice
|
||||||
description: Runner
|
description: Runner
|
||||||
@@ -39,7 +42,7 @@ on:
|
|||||||
|
|
||||||
env:
|
env:
|
||||||
NODEJS_VERSION: "18.15.0"
|
NODEJS_VERSION: "18.15.0"
|
||||||
ENVIRONMENT: '${{ fromJson(format(''["{0}", ""]'', github.event.inputs.environment || ''dev''))[github.event.inputs.environment == ''NONE''] }}'
|
ENVIRONMENT: '${{ fromJson(format(''["{0}", ""]'', github.event.inputs.environment || ''dev-podman''))[github.event.inputs.environment == ''NONE''] }}'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
all:
|
all:
|
||||||
@@ -139,6 +142,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
repository: Start9Labs/startos-image-recipes
|
repository: Start9Labs/startos-image-recipes
|
||||||
path: startos-image-recipes
|
path: startos-image-recipes
|
||||||
|
ref: feature/podman
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
@@ -166,7 +170,7 @@ jobs:
|
|||||||
|
|
||||||
- run: "mv embassy-os-deb/embassyos_0.3.x-1_*.deb startos-image-recipes/overlays/deb/"
|
- run: "mv embassy-os-deb/embassyos_0.3.x-1_*.deb startos-image-recipes/overlays/deb/"
|
||||||
|
|
||||||
- run: "rm -rf embassy-os-deb ${{ steps.npm-cache-dir.outputs.dir }} $HOME/.cargo"
|
- run: "sudo rm -rf embassy-os-deb ${{ steps.npm-cache-dir.outputs.dir }} $HOME/.cargo"
|
||||||
|
|
||||||
- name: Run iso build
|
- name: Run iso build
|
||||||
working-directory: startos-image-recipes
|
working-directory: startos-image-recipes
|
||||||
|
|||||||
1
backend/Cargo.lock
generated
1
backend/Cargo.lock
generated
@@ -4665,7 +4665,6 @@ dependencies = [
|
|||||||
"base64 0.13.1",
|
"base64 0.13.1",
|
||||||
"base64ct",
|
"base64ct",
|
||||||
"basic-cookies",
|
"basic-cookies",
|
||||||
"bollard",
|
|
||||||
"bytes",
|
"bytes",
|
||||||
"chrono",
|
"chrono",
|
||||||
"ciborium",
|
"ciborium",
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ avahi-alias = ["avahi"]
|
|||||||
cli = []
|
cli = []
|
||||||
sdk = []
|
sdk = []
|
||||||
daemon = []
|
daemon = []
|
||||||
|
podman = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
aes = { version = "0.7.5", features = ["ctr"] }
|
aes = { version = "0.7.5", features = ["ctr"] }
|
||||||
@@ -50,7 +51,6 @@ base32 = "0.4.0"
|
|||||||
base64 = "0.13.0"
|
base64 = "0.13.0"
|
||||||
base64ct = "1.5.1"
|
base64ct = "1.5.1"
|
||||||
basic-cookies = "0.1.4"
|
basic-cookies = "0.1.4"
|
||||||
bollard = "0.13.0"
|
|
||||||
bytes = "1"
|
bytes = "1"
|
||||||
chrono = { version = "0.4.19", features = ["serde"] }
|
chrono = { version = "0.4.19", features = ["serde"] }
|
||||||
clap = "3.2.8"
|
clap = "3.2.8"
|
||||||
|
|||||||
@@ -27,6 +27,9 @@ alias 'rust-musl-builder'='docker run $USE_TTY --rm -e "OS_ARCH=$OS_ARCH" -v "$H
|
|||||||
|
|
||||||
cd ..
|
cd ..
|
||||||
FLAGS=""
|
FLAGS=""
|
||||||
|
if [[ "$ENVIRONMENT" =~ (^|-)podman($|-) ]]; then
|
||||||
|
FLAGS="podman,$FLAGS"
|
||||||
|
fi
|
||||||
if [[ "$ENVIRONMENT" =~ (^|-)unstable($|-) ]]; then
|
if [[ "$ENVIRONMENT" =~ (^|-)unstable($|-) ]]; then
|
||||||
FLAGS="unstable,$FLAGS"
|
FLAGS="unstable,$FLAGS"
|
||||||
fi
|
fi
|
||||||
@@ -56,7 +59,7 @@ else
|
|||||||
fi
|
fi
|
||||||
for ARCH in x86_64 aarch64
|
for ARCH in x86_64 aarch64
|
||||||
do
|
do
|
||||||
rust-musl-builder sh -c "(cd libs && cargo build --release --features $FLAGS --locked --bin embassy_container_init)"
|
rust-musl-builder sh -c "(cd libs && cargo build --release --locked --bin embassy_container_init)"
|
||||||
if test $? -ne 0; then
|
if test $? -ne 0; then
|
||||||
fail=true
|
fail=true
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -4,9 +4,7 @@ use std::ops::Deref;
|
|||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::Duration;
|
|
||||||
|
|
||||||
use bollard::Docker;
|
|
||||||
use helpers::to_tmp_path;
|
use helpers::to_tmp_path;
|
||||||
use josekit::jwk::Jwk;
|
use josekit::jwk::Jwk;
|
||||||
use patch_db::json_ptr::JsonPointer;
|
use patch_db::json_ptr::JsonPointer;
|
||||||
@@ -111,7 +109,6 @@ pub struct RpcContextSeed {
|
|||||||
pub db: PatchDb,
|
pub db: PatchDb,
|
||||||
pub secret_store: PgPool,
|
pub secret_store: PgPool,
|
||||||
pub account: RwLock<AccountInfo>,
|
pub account: RwLock<AccountInfo>,
|
||||||
pub docker: Docker,
|
|
||||||
pub net_controller: Arc<NetController>,
|
pub net_controller: Arc<NetController>,
|
||||||
pub managers: ManagerMap,
|
pub managers: ManagerMap,
|
||||||
pub metrics_cache: RwLock<Option<crate::system::Metrics>>,
|
pub metrics_cache: RwLock<Option<crate::system::Metrics>>,
|
||||||
@@ -189,9 +186,6 @@ impl RpcContext {
|
|||||||
let account = AccountInfo::load(&secret_store).await?;
|
let account = AccountInfo::load(&secret_store).await?;
|
||||||
let db = base.db(&account).await?;
|
let db = base.db(&account).await?;
|
||||||
tracing::info!("Opened PatchDB");
|
tracing::info!("Opened PatchDB");
|
||||||
let mut docker = Docker::connect_with_unix_defaults()?;
|
|
||||||
docker.set_timeout(Duration::from_secs(600));
|
|
||||||
tracing::info!("Connected to Docker");
|
|
||||||
let net_controller = Arc::new(
|
let net_controller = Arc::new(
|
||||||
NetController::init(
|
NetController::init(
|
||||||
base.tor_control
|
base.tor_control
|
||||||
@@ -225,7 +219,6 @@ impl RpcContext {
|
|||||||
db,
|
db,
|
||||||
secret_store,
|
secret_store,
|
||||||
account: RwLock::new(account),
|
account: RwLock::new(account),
|
||||||
docker,
|
|
||||||
net_controller,
|
net_controller,
|
||||||
managers,
|
managers,
|
||||||
metrics_cache,
|
metrics_cache,
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ use crate::install::PKG_ARCHIVE_DIR;
|
|||||||
use crate::middleware::auth::LOCAL_AUTH_COOKIE_PATH;
|
use crate::middleware::auth::LOCAL_AUTH_COOKIE_PATH;
|
||||||
use crate::sound::BEP;
|
use crate::sound::BEP;
|
||||||
use crate::system::time;
|
use crate::system::time;
|
||||||
|
use crate::util::docker::{create_bridge_network, CONTAINER_DATADIR, CONTAINER_TOOL};
|
||||||
use crate::util::Invoke;
|
use crate::util::Invoke;
|
||||||
use crate::{Error, ARCH};
|
use crate::{Error, ARCH};
|
||||||
|
|
||||||
@@ -289,51 +290,56 @@ pub async fn init(cfg: &RpcContextConfig) -> Result<InitResult, Error> {
|
|||||||
if tokio::fs::metadata(&tmp_dir).await.is_err() {
|
if tokio::fs::metadata(&tmp_dir).await.is_err() {
|
||||||
tokio::fs::create_dir_all(&tmp_dir).await?;
|
tokio::fs::create_dir_all(&tmp_dir).await?;
|
||||||
}
|
}
|
||||||
let tmp_docker = cfg.datadir().join("package-data/tmp/docker");
|
let tmp_docker = cfg
|
||||||
|
.datadir()
|
||||||
|
.join(format!("package-data/tmp/{CONTAINER_TOOL}"));
|
||||||
let tmp_docker_exists = tokio::fs::metadata(&tmp_docker).await.is_ok();
|
let tmp_docker_exists = tokio::fs::metadata(&tmp_docker).await.is_ok();
|
||||||
if should_rebuild && tmp_docker_exists {
|
if should_rebuild && tmp_docker_exists {
|
||||||
tokio::fs::remove_dir_all(&tmp_docker).await?;
|
tokio::fs::remove_dir_all(&tmp_docker).await?;
|
||||||
}
|
}
|
||||||
Command::new("systemctl")
|
if CONTAINER_TOOL == "docker" {
|
||||||
.arg("stop")
|
Command::new("systemctl")
|
||||||
.arg("docker")
|
.arg("stop")
|
||||||
.invoke(crate::ErrorKind::Docker)
|
.arg("docker")
|
||||||
.await?;
|
.invoke(crate::ErrorKind::Docker)
|
||||||
crate::disk::mount::util::bind(&tmp_docker, "/var/lib/docker", false).await?;
|
.await?;
|
||||||
Command::new("systemctl")
|
}
|
||||||
.arg("reset-failed")
|
crate::disk::mount::util::bind(&tmp_docker, CONTAINER_DATADIR, false).await?;
|
||||||
.arg("docker")
|
|
||||||
.invoke(crate::ErrorKind::Docker)
|
if CONTAINER_TOOL == "docker" {
|
||||||
.await?;
|
Command::new("systemctl")
|
||||||
Command::new("systemctl")
|
.arg("reset-failed")
|
||||||
.arg("start")
|
.arg("docker")
|
||||||
.arg("docker")
|
.invoke(crate::ErrorKind::Docker)
|
||||||
.invoke(crate::ErrorKind::Docker)
|
.await?;
|
||||||
.await?;
|
Command::new("systemctl")
|
||||||
|
.arg("start")
|
||||||
|
.arg("docker")
|
||||||
|
.invoke(crate::ErrorKind::Docker)
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
tracing::info!("Mounted Docker Data");
|
tracing::info!("Mounted Docker Data");
|
||||||
|
|
||||||
if should_rebuild || !tmp_docker_exists {
|
if CONTAINER_TOOL == "podman" {
|
||||||
tracing::info!("Creating Docker Network");
|
Command::new("podman")
|
||||||
bollard::Docker::connect_with_unix_defaults()?
|
.arg("run")
|
||||||
.create_network(bollard::network::CreateNetworkOptions {
|
.arg("-d")
|
||||||
name: "start9",
|
.arg("--rm")
|
||||||
driver: "bridge",
|
.arg("--network=start9")
|
||||||
ipam: bollard::models::Ipam {
|
.arg("--name=netdummy")
|
||||||
config: Some(vec![bollard::models::IpamConfig {
|
.arg("start9/x_system/utils:latest")
|
||||||
subnet: Some("172.18.0.1/24".into()),
|
.arg("sleep")
|
||||||
..Default::default()
|
.arg("infinity")
|
||||||
}]),
|
.invoke(crate::ErrorKind::Docker)
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
options: {
|
|
||||||
let mut m = HashMap::new();
|
|
||||||
m.insert("com.docker.network.bridge.name", "br-start9");
|
|
||||||
m
|
|
||||||
},
|
|
||||||
..Default::default()
|
|
||||||
})
|
|
||||||
.await?;
|
.await?;
|
||||||
tracing::info!("Created Docker Network");
|
}
|
||||||
|
|
||||||
|
if should_rebuild || !tmp_docker_exists {
|
||||||
|
if CONTAINER_TOOL == "docker" {
|
||||||
|
tracing::info!("Creating Docker Network");
|
||||||
|
create_bridge_network("start9", "172.18.0.1/24", "br-start9").await?;
|
||||||
|
tracing::info!("Created Docker Network");
|
||||||
|
}
|
||||||
|
|
||||||
tracing::info!("Loading System Docker Images");
|
tracing::info!("Loading System Docker Images");
|
||||||
crate::install::load_images("/usr/lib/embassy/system-images").await?;
|
crate::install::load_images("/usr/lib/embassy/system-images").await?;
|
||||||
@@ -345,7 +351,7 @@ pub async fn init(cfg: &RpcContextConfig) -> Result<InitResult, Error> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tracing::info!("Enabling Docker QEMU Emulation");
|
tracing::info!("Enabling Docker QEMU Emulation");
|
||||||
Command::new("docker")
|
Command::new(CONTAINER_TOOL)
|
||||||
.arg("run")
|
.arg("run")
|
||||||
.arg("--privileged")
|
.arg("--privileged")
|
||||||
.arg("--rm")
|
.arg("--rm")
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
use std::collections::HashMap;
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use bollard::image::{ListImagesOptions, RemoveImageOptions};
|
|
||||||
use patch_db::{DbHandle, LockReceipt, LockTargetId, LockType, PatchDbHandle, Verifier};
|
use patch_db::{DbHandle, LockReceipt, LockTargetId, LockType, PatchDbHandle, Verifier};
|
||||||
use sqlx::{Executor, Postgres};
|
use sqlx::{Executor, Postgres};
|
||||||
use tracing::instrument;
|
use tracing::instrument;
|
||||||
@@ -104,46 +102,12 @@ pub async fn cleanup(ctx: &RpcContext, id: &PackageId, version: &Version) -> Res
|
|||||||
let mut errors = ErrorCollection::new();
|
let mut errors = ErrorCollection::new();
|
||||||
ctx.managers.remove(&(id.clone(), version.clone())).await;
|
ctx.managers.remove(&(id.clone(), version.clone())).await;
|
||||||
// docker images start9/$APP_ID/*:$VERSION -q | xargs docker rmi
|
// docker images start9/$APP_ID/*:$VERSION -q | xargs docker rmi
|
||||||
let images = ctx
|
let images = crate::util::docker::images_for(id, version).await?;
|
||||||
.docker
|
|
||||||
.list_images(Some(ListImagesOptions {
|
|
||||||
all: false,
|
|
||||||
filters: {
|
|
||||||
let mut f = HashMap::new();
|
|
||||||
f.insert(
|
|
||||||
"reference".to_owned(),
|
|
||||||
vec![format!("start9/{}/*:{}", id, version)],
|
|
||||||
);
|
|
||||||
f
|
|
||||||
},
|
|
||||||
digests: false,
|
|
||||||
}))
|
|
||||||
.await
|
|
||||||
.apply(|res| errors.handle(res));
|
|
||||||
errors.extend(
|
errors.extend(
|
||||||
futures::future::join_all(
|
futures::future::join_all(images.into_iter().map(|sha| async {
|
||||||
images
|
let sha = sha; // move into future
|
||||||
.into_iter()
|
crate::util::docker::remove_image(&sha).await
|
||||||
.flatten()
|
}))
|
||||||
.flat_map(|image| image.repo_tags)
|
|
||||||
.filter(|tag| {
|
|
||||||
tag.starts_with(&format!("start9/{}/", id))
|
|
||||||
&& tag.ends_with(&format!(":{}", version))
|
|
||||||
})
|
|
||||||
.map(|tag| async {
|
|
||||||
let tag = tag; // move into future
|
|
||||||
ctx.docker
|
|
||||||
.remove_image(
|
|
||||||
&tag,
|
|
||||||
Some(RemoveImageOptions {
|
|
||||||
force: true,
|
|
||||||
noprune: false,
|
|
||||||
}),
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.await,
|
.await,
|
||||||
);
|
);
|
||||||
let pkg_archive_dir = ctx
|
let pkg_archive_dir = ctx
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ use crate::notifications::NotificationLevel;
|
|||||||
use crate::s9pk::manifest::{Manifest, PackageId};
|
use crate::s9pk::manifest::{Manifest, PackageId};
|
||||||
use crate::s9pk::reader::S9pkReader;
|
use crate::s9pk::reader::S9pkReader;
|
||||||
use crate::status::{MainStatus, Status};
|
use crate::status::{MainStatus, Status};
|
||||||
|
use crate::util::docker::CONTAINER_TOOL;
|
||||||
use crate::util::io::{copy_and_shutdown, response_to_reader};
|
use crate::util::io::{copy_and_shutdown, response_to_reader};
|
||||||
use crate::util::serde::{display_serializable, Port};
|
use crate::util::serde::{display_serializable, Port};
|
||||||
use crate::util::{display_none, AsyncFileExt, Version};
|
use crate::util::{display_none, AsyncFileExt, Version};
|
||||||
@@ -1087,7 +1088,7 @@ pub async fn install_s9pk<R: AsyncRead + AsyncSeek + Unpin + Send + Sync>(
|
|||||||
tracing::info!("Install {}@{}: Unpacking Docker Images", pkg_id, version);
|
tracing::info!("Install {}@{}: Unpacking Docker Images", pkg_id, version);
|
||||||
progress
|
progress
|
||||||
.track_read_during(progress_model.clone(), &ctx.db, || async {
|
.track_read_during(progress_model.clone(), &ctx.db, || async {
|
||||||
let mut load = Command::new("docker")
|
let mut load = Command::new(CONTAINER_TOOL)
|
||||||
.arg("load")
|
.arg("load")
|
||||||
.stdin(Stdio::piped())
|
.stdin(Stdio::piped())
|
||||||
.stderr(Stdio::piped())
|
.stderr(Stdio::piped())
|
||||||
@@ -1467,7 +1468,7 @@ pub fn load_images<'a, P: AsRef<Path> + 'a + Send + Sync>(
|
|||||||
let path = entry.path();
|
let path = entry.path();
|
||||||
let ext = path.extension().and_then(|ext| ext.to_str());
|
let ext = path.extension().and_then(|ext| ext.to_str());
|
||||||
if ext == Some("tar") || ext == Some("s9pk") {
|
if ext == Some("tar") || ext == Some("s9pk") {
|
||||||
let mut load = Command::new("docker")
|
let mut load = Command::new(CONTAINER_TOOL)
|
||||||
.arg("load")
|
.arg("load")
|
||||||
.stdin(Stdio::piped())
|
.stdin(Stdio::piped())
|
||||||
.stderr(Stdio::piped())
|
.stderr(Stdio::piped())
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
use bollard::container::{StopContainerOptions, WaitContainerOptions};
|
use models::ErrorKind;
|
||||||
use tokio_stream::StreamExt;
|
use tokio_stream::StreamExt;
|
||||||
|
|
||||||
use crate::context::RpcContext;
|
use crate::context::RpcContext;
|
||||||
use crate::s9pk::manifest::Manifest;
|
use crate::s9pk::manifest::Manifest;
|
||||||
|
use crate::util::docker::stop_container;
|
||||||
use crate::Error;
|
use crate::Error;
|
||||||
|
|
||||||
/// This is helper structure for a service, the seed of the data that is needed for the manager_container
|
/// This is helper structure for a service, the seed of the data that is needed for the manager_container
|
||||||
@@ -14,50 +15,20 @@ pub struct ManagerSeed {
|
|||||||
|
|
||||||
impl ManagerSeed {
|
impl ManagerSeed {
|
||||||
pub async fn stop_container(&self) -> Result<(), Error> {
|
pub async fn stop_container(&self) -> Result<(), Error> {
|
||||||
match self
|
match stop_container(
|
||||||
.ctx
|
&self.container_name,
|
||||||
.docker
|
self.manifest
|
||||||
.stop_container(
|
.containers
|
||||||
&self.container_name,
|
.as_ref()
|
||||||
Some(StopContainerOptions {
|
.and_then(|c| c.main.sigterm_timeout)
|
||||||
t: self
|
.map(|d| *d),
|
||||||
.manifest
|
None,
|
||||||
.containers
|
)
|
||||||
.as_ref()
|
.await
|
||||||
.and_then(|c| c.main.sigterm_timeout)
|
|
||||||
.map(|d| d.as_secs())
|
|
||||||
.unwrap_or(30) as i64,
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
{
|
{
|
||||||
Err(bollard::errors::Error::DockerResponseServerError {
|
Err(e) if e.kind == ErrorKind::NotFound => (), // Already stopped
|
||||||
status_code: 404, // NOT FOUND
|
|
||||||
..
|
|
||||||
})
|
|
||||||
| Err(bollard::errors::Error::DockerResponseServerError {
|
|
||||||
status_code: 409, // CONFLICT
|
|
||||||
..
|
|
||||||
})
|
|
||||||
| Err(bollard::errors::Error::DockerResponseServerError {
|
|
||||||
status_code: 304, // NOT MODIFIED
|
|
||||||
..
|
|
||||||
}) => (), // Already stopped
|
|
||||||
a => a?,
|
a => a?,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait for the container to stop
|
|
||||||
{
|
|
||||||
let mut waiting = self.ctx.docker.wait_container(
|
|
||||||
&self.container_name,
|
|
||||||
Some(WaitContainerOptions {
|
|
||||||
condition: "not-running",
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
while let Some(_) = waiting.next().await {
|
|
||||||
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ use crate::procedure::docker::{DockerContainer, DockerProcedure, LongRunning};
|
|||||||
use crate::procedure::{NoOutput, ProcedureName};
|
use crate::procedure::{NoOutput, ProcedureName};
|
||||||
use crate::s9pk::manifest::Manifest;
|
use crate::s9pk::manifest::Manifest;
|
||||||
use crate::status::MainStatus;
|
use crate::status::MainStatus;
|
||||||
|
use crate::util::docker::{get_container_ip, kill_container};
|
||||||
use crate::util::NonDetachingJoinHandle;
|
use crate::util::NonDetachingJoinHandle;
|
||||||
use crate::volume::Volume;
|
use crate::volume::Volume;
|
||||||
use crate::Error;
|
use crate::Error;
|
||||||
@@ -228,7 +229,7 @@ impl Manager {
|
|||||||
.send_replace(Default::default())
|
.send_replace(Default::default())
|
||||||
.join_handle()
|
.join_handle()
|
||||||
{
|
{
|
||||||
transition.abort();
|
(&**transition).abort();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn _transition_replace(&self, transition_state: TransitionState) {
|
fn _transition_replace(&self, transition_state: TransitionState) {
|
||||||
@@ -741,26 +742,10 @@ enum GetRunningIp {
|
|||||||
|
|
||||||
async fn get_long_running_ip(seed: &ManagerSeed, runtime: &mut LongRunning) -> GetRunningIp {
|
async fn get_long_running_ip(seed: &ManagerSeed, runtime: &mut LongRunning) -> GetRunningIp {
|
||||||
loop {
|
loop {
|
||||||
match container_inspect(seed).await {
|
match get_container_ip(&seed.container_name).await {
|
||||||
Ok(res) => {
|
Ok(Some(ip_addr)) => return GetRunningIp::Ip(ip_addr),
|
||||||
match res
|
Ok(None) => (),
|
||||||
.network_settings
|
Err(e) if e.kind == ErrorKind::NotFound => (),
|
||||||
.and_then(|ns| ns.networks)
|
|
||||||
.and_then(|mut n| n.remove("start9"))
|
|
||||||
.and_then(|es| es.ip_address)
|
|
||||||
.filter(|ip| !ip.is_empty())
|
|
||||||
.map(|ip| ip.parse())
|
|
||||||
.transpose()
|
|
||||||
{
|
|
||||||
Ok(Some(ip_addr)) => return GetRunningIp::Ip(ip_addr),
|
|
||||||
Ok(None) => (),
|
|
||||||
Err(e) => return GetRunningIp::Error(e.into()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(bollard::errors::Error::DockerResponseServerError {
|
|
||||||
status_code: 404, // NOT FOUND
|
|
||||||
..
|
|
||||||
}) => (),
|
|
||||||
Err(e) => return GetRunningIp::Error(e.into()),
|
Err(e) => return GetRunningIp::Error(e.into()),
|
||||||
}
|
}
|
||||||
if let Poll::Ready(res) = futures::poll!(&mut runtime.running_output) {
|
if let Poll::Ready(res) = futures::poll!(&mut runtime.running_output) {
|
||||||
@@ -777,16 +762,6 @@ async fn get_long_running_ip(seed: &ManagerSeed, runtime: &mut LongRunning) -> G
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(skip(seed))]
|
|
||||||
async fn container_inspect(
|
|
||||||
seed: &ManagerSeed,
|
|
||||||
) -> Result<bollard::models::ContainerInspectResponse, bollard::errors::Error> {
|
|
||||||
seed.ctx
|
|
||||||
.docker
|
|
||||||
.inspect_container(&seed.container_name, None)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
#[instrument(skip(seed))]
|
#[instrument(skip(seed))]
|
||||||
async fn add_network_for_main(
|
async fn add_network_for_main(
|
||||||
seed: &ManagerSeed,
|
seed: &ManagerSeed,
|
||||||
@@ -851,26 +826,10 @@ type RuntimeOfCommand = NonDetachingJoinHandle<Result<Result<NoOutput, (i32, Str
|
|||||||
#[instrument(skip(seed, runtime))]
|
#[instrument(skip(seed, runtime))]
|
||||||
async fn get_running_ip(seed: &ManagerSeed, mut runtime: &mut RuntimeOfCommand) -> GetRunningIp {
|
async fn get_running_ip(seed: &ManagerSeed, mut runtime: &mut RuntimeOfCommand) -> GetRunningIp {
|
||||||
loop {
|
loop {
|
||||||
match container_inspect(seed).await {
|
match get_container_ip(&seed.container_name).await {
|
||||||
Ok(res) => {
|
Ok(Some(ip_addr)) => return GetRunningIp::Ip(ip_addr),
|
||||||
match res
|
Ok(None) => (),
|
||||||
.network_settings
|
Err(e) if e.kind == ErrorKind::NotFound => (),
|
||||||
.and_then(|ns| ns.networks)
|
|
||||||
.and_then(|mut n| n.remove("start9"))
|
|
||||||
.and_then(|es| es.ip_address)
|
|
||||||
.filter(|ip| !ip.is_empty())
|
|
||||||
.map(|ip| ip.parse())
|
|
||||||
.transpose()
|
|
||||||
{
|
|
||||||
Ok(Some(ip_addr)) => return GetRunningIp::Ip(ip_addr),
|
|
||||||
Ok(None) => (),
|
|
||||||
Err(e) => return GetRunningIp::Error(e.into()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(bollard::errors::Error::DockerResponseServerError {
|
|
||||||
status_code: 404, // NOT FOUND
|
|
||||||
..
|
|
||||||
}) => (),
|
|
||||||
Err(e) => return GetRunningIp::Error(e.into()),
|
Err(e) => return GetRunningIp::Error(e.into()),
|
||||||
}
|
}
|
||||||
if let Poll::Ready(res) = futures::poll!(&mut runtime) {
|
if let Poll::Ready(res) = futures::poll!(&mut runtime) {
|
||||||
@@ -933,28 +892,10 @@ async fn send_signal(manager: &Manager, gid: Arc<Gid>, signal: Signal) -> Result
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// send signal to container
|
// send signal to container
|
||||||
manager
|
kill_container(&manager.seed.container_name, Some(signal))
|
||||||
.seed
|
|
||||||
.ctx
|
|
||||||
.docker
|
|
||||||
.kill_container(
|
|
||||||
&manager.seed.container_name,
|
|
||||||
Some(bollard::container::KillContainerOptions {
|
|
||||||
signal: signal.to_string(),
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.await
|
.await
|
||||||
.or_else(|e| {
|
.or_else(|e| {
|
||||||
if matches!(
|
if e.kind == ErrorKind::NotFound {
|
||||||
e,
|
|
||||||
bollard::errors::Error::DockerResponseServerError {
|
|
||||||
status_code: 409, // CONFLICT
|
|
||||||
..
|
|
||||||
} | bollard::errors::Error::DockerResponseServerError {
|
|
||||||
status_code: 404, // NOT FOUND
|
|
||||||
..
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(e)
|
Err(e)
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ use std::path::{Path, PathBuf};
|
|||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use async_stream::stream;
|
use async_stream::stream;
|
||||||
use bollard::container::RemoveContainerOptions;
|
|
||||||
use color_eyre::eyre::eyre;
|
use color_eyre::eyre::eyre;
|
||||||
use color_eyre::Report;
|
use color_eyre::Report;
|
||||||
use futures::future::{BoxFuture, Either as EitherFuture};
|
use futures::future::{BoxFuture, Either as EitherFuture};
|
||||||
@@ -26,6 +25,7 @@ use tracing::instrument;
|
|||||||
use super::ProcedureName;
|
use super::ProcedureName;
|
||||||
use crate::context::RpcContext;
|
use crate::context::RpcContext;
|
||||||
use crate::s9pk::manifest::{PackageId, SYSTEM_PACKAGE_ID};
|
use crate::s9pk::manifest::{PackageId, SYSTEM_PACKAGE_ID};
|
||||||
|
use crate::util::docker::{remove_container, CONTAINER_TOOL};
|
||||||
use crate::util::serde::{Duration as SerdeDuration, IoFormat};
|
use crate::util::serde::{Duration as SerdeDuration, IoFormat};
|
||||||
use crate::util::Version;
|
use crate::util::Version;
|
||||||
use crate::volume::{VolumeId, Volumes};
|
use crate::volume::{VolumeId, Volumes};
|
||||||
@@ -228,8 +228,8 @@ impl DockerProcedure {
|
|||||||
timeout: Option<Duration>,
|
timeout: Option<Duration>,
|
||||||
) -> Result<Result<O, (i32, String)>, Error> {
|
) -> Result<Result<O, (i32, String)>, Error> {
|
||||||
let name = name.docker_name();
|
let name = name.docker_name();
|
||||||
let name: Option<&str> = name.as_deref();
|
let name: Option<&str> = name.as_ref().map(|x| &**x);
|
||||||
let mut cmd = tokio::process::Command::new("docker");
|
let mut cmd = tokio::process::Command::new(CONTAINER_TOOL);
|
||||||
let container_name = Self::container_name(pkg_id, name);
|
let container_name = Self::container_name(pkg_id, name);
|
||||||
cmd.arg("run")
|
cmd.arg("run")
|
||||||
.arg("--rm")
|
.arg("--rm")
|
||||||
@@ -240,25 +240,7 @@ impl DockerProcedure {
|
|||||||
.arg(format!("--hostname={}", &container_name))
|
.arg(format!("--hostname={}", &container_name))
|
||||||
.arg("--no-healthcheck")
|
.arg("--no-healthcheck")
|
||||||
.kill_on_drop(true);
|
.kill_on_drop(true);
|
||||||
match ctx
|
remove_container(&container_name, true).await?;
|
||||||
.docker
|
|
||||||
.remove_container(
|
|
||||||
&container_name,
|
|
||||||
Some(RemoveContainerOptions {
|
|
||||||
v: false,
|
|
||||||
force: true,
|
|
||||||
link: false,
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
Ok(())
|
|
||||||
| Err(bollard::errors::Error::DockerResponseServerError {
|
|
||||||
status_code: 404, // NOT FOUND
|
|
||||||
..
|
|
||||||
}) => Ok(()),
|
|
||||||
Err(e) => Err(e),
|
|
||||||
}?;
|
|
||||||
cmd.args(self.docker_args(ctx, pkg_id, pkg_version, volumes).await?);
|
cmd.args(self.docker_args(ctx, pkg_id, pkg_version, volumes).await?);
|
||||||
let input_buf = if let (Some(input), Some(format)) = (&input, &self.io_format) {
|
let input_buf = if let (Some(input), Some(format)) = (&input, &self.io_format) {
|
||||||
cmd.stdin(std::process::Stdio::piped());
|
cmd.stdin(std::process::Stdio::piped());
|
||||||
@@ -407,7 +389,9 @@ impl DockerProcedure {
|
|||||||
input: Option<I>,
|
input: Option<I>,
|
||||||
timeout: Option<Duration>,
|
timeout: Option<Duration>,
|
||||||
) -> Result<Result<O, (i32, String)>, Error> {
|
) -> Result<Result<O, (i32, String)>, Error> {
|
||||||
let mut cmd = tokio::process::Command::new("docker");
|
let name = name.docker_name();
|
||||||
|
let name: Option<&str> = name.as_deref();
|
||||||
|
let mut cmd = tokio::process::Command::new(CONTAINER_TOOL);
|
||||||
|
|
||||||
cmd.arg("exec");
|
cmd.arg("exec");
|
||||||
|
|
||||||
@@ -556,9 +540,9 @@ impl DockerProcedure {
|
|||||||
pkg_version: &Version,
|
pkg_version: &Version,
|
||||||
volumes: &Volumes,
|
volumes: &Volumes,
|
||||||
input: Option<I>,
|
input: Option<I>,
|
||||||
_timeout: Option<Duration>,
|
timeout: Option<Duration>,
|
||||||
) -> Result<Result<O, (i32, String)>, Error> {
|
) -> Result<Result<O, (i32, String)>, Error> {
|
||||||
let mut cmd = tokio::process::Command::new("docker");
|
let mut cmd = tokio::process::Command::new(CONTAINER_TOOL);
|
||||||
cmd.arg("run").arg("--rm").arg("--network=none");
|
cmd.arg("run").arg("--rm").arg("--network=none");
|
||||||
cmd.args(
|
cmd.args(
|
||||||
self.docker_args(ctx, pkg_id, pkg_version, &volumes.to_readonly())
|
self.docker_args(ctx, pkg_id, pkg_version, &volumes.to_readonly())
|
||||||
@@ -639,7 +623,18 @@ impl DockerProcedure {
|
|||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
let exit_status = handle.wait().await.with_kind(crate::ErrorKind::Docker)?;
|
let handle = if let Some(dur) = timeout {
|
||||||
|
async move {
|
||||||
|
tokio::time::timeout(dur, handle.wait())
|
||||||
|
.await
|
||||||
|
.with_kind(crate::ErrorKind::Docker)?
|
||||||
|
.with_kind(crate::ErrorKind::Docker)
|
||||||
|
}
|
||||||
|
.boxed()
|
||||||
|
} else {
|
||||||
|
async { handle.wait().await.with_kind(crate::ErrorKind::Docker) }.boxed()
|
||||||
|
};
|
||||||
|
let exit_status = handle.await?;
|
||||||
Ok(
|
Ok(
|
||||||
if exit_status.success() || exit_status.code() == Some(143) {
|
if exit_status.success() || exit_status.code() == Some(143) {
|
||||||
Ok(serde_json::from_value(
|
Ok(serde_json::from_value(
|
||||||
@@ -820,10 +815,10 @@ impl LongRunning {
|
|||||||
const BIND_LOCATION: &str = "/usr/lib/embassy/container/";
|
const BIND_LOCATION: &str = "/usr/lib/embassy/container/";
|
||||||
tracing::trace!("setup_long_running_docker_cmd");
|
tracing::trace!("setup_long_running_docker_cmd");
|
||||||
|
|
||||||
LongRunning::cleanup_previous_container(ctx, container_name).await?;
|
remove_container(container_name, true).await?;
|
||||||
|
|
||||||
let image_architecture = {
|
let image_architecture = {
|
||||||
let mut cmd = tokio::process::Command::new("docker");
|
let mut cmd = tokio::process::Command::new(CONTAINER_TOOL);
|
||||||
cmd.arg("image")
|
cmd.arg("image")
|
||||||
.arg("inspect")
|
.arg("inspect")
|
||||||
.arg("--format")
|
.arg("--format")
|
||||||
@@ -838,7 +833,7 @@ impl LongRunning {
|
|||||||
arch.replace('\'', "").trim().to_string()
|
arch.replace('\'', "").trim().to_string()
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut cmd = tokio::process::Command::new("docker");
|
let mut cmd = tokio::process::Command::new(CONTAINER_TOOL);
|
||||||
cmd.arg("run")
|
cmd.arg("run")
|
||||||
.arg("--network=start9")
|
.arg("--network=start9")
|
||||||
.arg(format!("--add-host=embassy:{}", Ipv4Addr::from(HOST_IP)))
|
.arg(format!("--add-host=embassy:{}", Ipv4Addr::from(HOST_IP)))
|
||||||
@@ -891,31 +886,6 @@ impl LongRunning {
|
|||||||
cmd.stdin(std::process::Stdio::piped());
|
cmd.stdin(std::process::Stdio::piped());
|
||||||
Ok(cmd)
|
Ok(cmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn cleanup_previous_container(
|
|
||||||
ctx: &RpcContext,
|
|
||||||
container_name: &str,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
match ctx
|
|
||||||
.docker
|
|
||||||
.remove_container(
|
|
||||||
container_name,
|
|
||||||
Some(RemoveContainerOptions {
|
|
||||||
v: false,
|
|
||||||
force: true,
|
|
||||||
link: false,
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
Ok(())
|
|
||||||
| Err(bollard::errors::Error::DockerResponseServerError {
|
|
||||||
status_code: 404, // NOT FOUND
|
|
||||||
..
|
|
||||||
}) => Ok(()),
|
|
||||||
Err(e) => Err(e)?,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
async fn buf_reader_to_lines(
|
async fn buf_reader_to_lines(
|
||||||
reader: impl AsyncBufRead + Unpin,
|
reader: impl AsyncBufRead + Unpin,
|
||||||
|
|||||||
28
backend/src/s9pk/specv2.md
Normal file
28
backend/src/s9pk/specv2.md
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
## Header
|
||||||
|
|
||||||
|
### Magic
|
||||||
|
|
||||||
|
2B: `0x3b3b`
|
||||||
|
|
||||||
|
### Version
|
||||||
|
|
||||||
|
varint: `0x02`
|
||||||
|
|
||||||
|
### Pubkey
|
||||||
|
|
||||||
|
32B: ed25519 pubkey
|
||||||
|
|
||||||
|
### TOC
|
||||||
|
|
||||||
|
- number of sections (varint)
|
||||||
|
- FOREACH section
|
||||||
|
- sig (32B: ed25519 signature of BLAKE-3 of rest of section)
|
||||||
|
- name (varstring)
|
||||||
|
- TYPE (varint)
|
||||||
|
- TYPE=FILE (`0x01`)
|
||||||
|
- mime (varstring)
|
||||||
|
- pos (32B: u64 BE)
|
||||||
|
- len (32B: u64 BE)
|
||||||
|
- hash (32B: BLAKE-3 of file contents)
|
||||||
|
- TYPE=TOC (`0x02`)
|
||||||
|
- recursively defined
|
||||||
@@ -7,6 +7,7 @@ use crate::context::RpcContext;
|
|||||||
use crate::disk::main::export;
|
use crate::disk::main::export;
|
||||||
use crate::init::{STANDBY_MODE_PATH, SYSTEM_REBUILD_PATH};
|
use crate::init::{STANDBY_MODE_PATH, SYSTEM_REBUILD_PATH};
|
||||||
use crate::sound::SHUTDOWN;
|
use crate::sound::SHUTDOWN;
|
||||||
|
use crate::util::docker::CONTAINER_TOOL;
|
||||||
use crate::util::{display_none, Invoke};
|
use crate::util::{display_none, Invoke};
|
||||||
use crate::{Error, OS_ARCH};
|
use crate::{Error, OS_ARCH};
|
||||||
|
|
||||||
@@ -43,14 +44,16 @@ impl Shutdown {
|
|||||||
tracing::error!("Error Stopping Journald: {}", e);
|
tracing::error!("Error Stopping Journald: {}", e);
|
||||||
tracing::debug!("{:?}", e);
|
tracing::debug!("{:?}", e);
|
||||||
}
|
}
|
||||||
if let Err(e) = Command::new("systemctl")
|
if CONTAINER_TOOL == "docker" {
|
||||||
.arg("stop")
|
if let Err(e) = Command::new("systemctl")
|
||||||
.arg("docker")
|
.arg("stop")
|
||||||
.invoke(crate::ErrorKind::Docker)
|
.arg("docker")
|
||||||
.await
|
.invoke(crate::ErrorKind::Docker)
|
||||||
{
|
.await
|
||||||
tracing::error!("Error Stopping Docker: {}", e);
|
{
|
||||||
tracing::debug!("{:?}", e);
|
tracing::error!("Error Stopping Docker: {}", e);
|
||||||
|
tracing::debug!("{:?}", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if let Some(guid) = &self.disk_guid {
|
if let Some(guid) = &self.disk_guid {
|
||||||
if let Err(e) = export(guid, &self.datadir).await {
|
if let Err(e) = export(guid, &self.datadir).await {
|
||||||
|
|||||||
239
backend/src/util/docker.rs
Normal file
239
backend/src/util/docker.rs
Normal file
@@ -0,0 +1,239 @@
|
|||||||
|
use std::net::Ipv4Addr;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use models::{Error, ErrorKind, PackageId, ResultExt, Version};
|
||||||
|
use nix::sys::signal::Signal;
|
||||||
|
use tokio::process::Command;
|
||||||
|
|
||||||
|
use crate::util::Invoke;
|
||||||
|
|
||||||
|
#[cfg(not(feature = "podman"))]
|
||||||
|
pub const CONTAINER_TOOL: &str = "docker";
|
||||||
|
#[cfg(feature = "podman")]
|
||||||
|
pub const CONTAINER_TOOL: &str = "podman";
|
||||||
|
|
||||||
|
#[cfg(not(feature = "podman"))]
|
||||||
|
pub const CONTAINER_DATADIR: &str = "/var/lib/docker";
|
||||||
|
#[cfg(feature = "podman")]
|
||||||
|
pub const CONTAINER_DATADIR: &str = "/var/lib/containers";
|
||||||
|
|
||||||
|
pub struct DockerImageSha(String);
|
||||||
|
|
||||||
|
// docker images start9/${package}/*:${version} -q --no-trunc
|
||||||
|
pub async fn images_for(
|
||||||
|
package: &PackageId,
|
||||||
|
version: &Version,
|
||||||
|
) -> Result<Vec<DockerImageSha>, Error> {
|
||||||
|
Ok(String::from_utf8(
|
||||||
|
Command::new(CONTAINER_TOOL)
|
||||||
|
.arg("images")
|
||||||
|
.arg(format!("start9/{package}/*:{version}"))
|
||||||
|
.arg("--no-trunc")
|
||||||
|
.arg("-q")
|
||||||
|
.invoke(ErrorKind::Docker)
|
||||||
|
.await?,
|
||||||
|
)?
|
||||||
|
.lines()
|
||||||
|
.map(|l| DockerImageSha(l.trim().to_owned()))
|
||||||
|
.collect())
|
||||||
|
}
|
||||||
|
|
||||||
|
// docker rmi -f ${sha}
|
||||||
|
pub async fn remove_image(sha: &DockerImageSha) -> Result<(), Error> {
|
||||||
|
match Command::new(CONTAINER_TOOL)
|
||||||
|
.arg("rmi")
|
||||||
|
.arg("-f")
|
||||||
|
.arg(&sha.0)
|
||||||
|
.invoke(ErrorKind::Docker)
|
||||||
|
.await
|
||||||
|
.map(|_| ())
|
||||||
|
{
|
||||||
|
Err(e)
|
||||||
|
if e.source
|
||||||
|
.to_string()
|
||||||
|
.to_ascii_lowercase()
|
||||||
|
.contains("no such image") =>
|
||||||
|
{
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
a => a,
|
||||||
|
}?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// docker image prune -f
|
||||||
|
pub async fn prune_images() -> Result<(), Error> {
|
||||||
|
Command::new(CONTAINER_TOOL)
|
||||||
|
.arg("image")
|
||||||
|
.arg("prune")
|
||||||
|
.arg("-f")
|
||||||
|
.invoke(ErrorKind::Docker)
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// docker container inspect ${name} --format '{{.NetworkSettings.Networks.start9.IPAddress}}'
|
||||||
|
pub async fn get_container_ip(name: &str) -> Result<Option<Ipv4Addr>, Error> {
|
||||||
|
match Command::new(CONTAINER_TOOL)
|
||||||
|
.arg("container")
|
||||||
|
.arg("inspect")
|
||||||
|
.arg(name)
|
||||||
|
.arg("--format")
|
||||||
|
.arg("{{.NetworkSettings.Networks.start9.IPAddress}}")
|
||||||
|
.invoke(ErrorKind::Docker)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Err(e)
|
||||||
|
if e.source
|
||||||
|
.to_string()
|
||||||
|
.to_ascii_lowercase()
|
||||||
|
.contains("no such container") =>
|
||||||
|
{
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
Err(e) => Err(e),
|
||||||
|
Ok(a) => {
|
||||||
|
let out = std::str::from_utf8(&a)?.trim();
|
||||||
|
if out.is_empty() {
|
||||||
|
Ok(None)
|
||||||
|
} else {
|
||||||
|
Ok(Some({
|
||||||
|
out.parse()
|
||||||
|
.with_ctx(|_| (ErrorKind::ParseNetAddress, out.to_string()))?
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// docker stop -t ${timeout} -s ${signal} ${name}
|
||||||
|
pub async fn stop_container(
|
||||||
|
name: &str,
|
||||||
|
timeout: Option<Duration>,
|
||||||
|
signal: Option<Signal>,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let mut cmd = Command::new(CONTAINER_TOOL);
|
||||||
|
cmd.arg("stop");
|
||||||
|
if let Some(dur) = timeout {
|
||||||
|
cmd.arg("-t").arg(dur.as_secs().to_string());
|
||||||
|
}
|
||||||
|
if let Some(sig) = signal {
|
||||||
|
cmd.arg("-s").arg(sig.to_string());
|
||||||
|
}
|
||||||
|
cmd.arg(name);
|
||||||
|
match cmd.invoke(ErrorKind::Docker).await {
|
||||||
|
Ok(_) => Ok(()),
|
||||||
|
Err(mut e)
|
||||||
|
if e.source
|
||||||
|
.to_string()
|
||||||
|
.to_ascii_lowercase()
|
||||||
|
.contains("no such container") =>
|
||||||
|
{
|
||||||
|
e.kind = ErrorKind::NotFound;
|
||||||
|
Err(e)
|
||||||
|
}
|
||||||
|
Err(e) => Err(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// docker kill -s ${signal} ${name}
|
||||||
|
pub async fn kill_container(name: &str, signal: Option<Signal>) -> Result<(), Error> {
|
||||||
|
let mut cmd = Command::new(CONTAINER_TOOL);
|
||||||
|
cmd.arg("kill");
|
||||||
|
if let Some(sig) = signal {
|
||||||
|
cmd.arg("-s").arg(sig.to_string());
|
||||||
|
}
|
||||||
|
cmd.arg(name);
|
||||||
|
match cmd.invoke(ErrorKind::Docker).await {
|
||||||
|
Ok(_) => Ok(()),
|
||||||
|
Err(mut e)
|
||||||
|
if e.source
|
||||||
|
.to_string()
|
||||||
|
.to_ascii_lowercase()
|
||||||
|
.contains("no such container") =>
|
||||||
|
{
|
||||||
|
e.kind = ErrorKind::NotFound;
|
||||||
|
Err(e)
|
||||||
|
}
|
||||||
|
Err(e) => Err(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// docker pause ${name}
|
||||||
|
pub async fn pause_container(name: &str) -> Result<(), Error> {
|
||||||
|
let mut cmd = Command::new(CONTAINER_TOOL);
|
||||||
|
cmd.arg("pause");
|
||||||
|
cmd.arg(name);
|
||||||
|
match cmd.invoke(ErrorKind::Docker).await {
|
||||||
|
Ok(_) => Ok(()),
|
||||||
|
Err(mut e)
|
||||||
|
if e.source
|
||||||
|
.to_string()
|
||||||
|
.to_ascii_lowercase()
|
||||||
|
.contains("no such container") =>
|
||||||
|
{
|
||||||
|
e.kind = ErrorKind::NotFound;
|
||||||
|
Err(e)
|
||||||
|
}
|
||||||
|
Err(e) => Err(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// docker unpause ${name}
|
||||||
|
pub async fn unpause_container(name: &str) -> Result<(), Error> {
|
||||||
|
let mut cmd = Command::new(CONTAINER_TOOL);
|
||||||
|
cmd.arg("unpause");
|
||||||
|
cmd.arg(name);
|
||||||
|
match cmd.invoke(ErrorKind::Docker).await {
|
||||||
|
Ok(_) => Ok(()),
|
||||||
|
Err(mut e)
|
||||||
|
if e.source
|
||||||
|
.to_string()
|
||||||
|
.to_ascii_lowercase()
|
||||||
|
.contains("no such container") =>
|
||||||
|
{
|
||||||
|
e.kind = ErrorKind::NotFound;
|
||||||
|
Err(e)
|
||||||
|
}
|
||||||
|
Err(e) => Err(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// docker rm -f ${name}
|
||||||
|
pub async fn remove_container(name: &str, force: bool) -> Result<(), Error> {
|
||||||
|
let mut cmd = Command::new(CONTAINER_TOOL);
|
||||||
|
cmd.arg("rm");
|
||||||
|
if force {
|
||||||
|
cmd.arg("-f");
|
||||||
|
}
|
||||||
|
cmd.arg(name);
|
||||||
|
match cmd.invoke(ErrorKind::Docker).await {
|
||||||
|
Ok(_) => Ok(()),
|
||||||
|
Err(e)
|
||||||
|
if e.source
|
||||||
|
.to_string()
|
||||||
|
.to_ascii_lowercase()
|
||||||
|
.contains("no such container") =>
|
||||||
|
{
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Err(e) => Err(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// docker network create -d bridge --subnet ${subnet} --opt com.podman.network.bridge.name=${bridge_name}
|
||||||
|
pub async fn create_bridge_network(
|
||||||
|
name: &str,
|
||||||
|
subnet: &str,
|
||||||
|
bridge_name: &str,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let mut cmd = Command::new(CONTAINER_TOOL);
|
||||||
|
cmd.arg("network").arg("create");
|
||||||
|
cmd.arg("-d").arg("bridge");
|
||||||
|
cmd.arg("--subnet").arg(subnet);
|
||||||
|
cmd.arg("--opt")
|
||||||
|
.arg(format!("com.docker.network.bridge.name={bridge_name}"));
|
||||||
|
cmd.arg(name);
|
||||||
|
cmd.invoke(ErrorKind::Docker).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -24,6 +24,7 @@ use tracing::instrument;
|
|||||||
use crate::shutdown::Shutdown;
|
use crate::shutdown::Shutdown;
|
||||||
use crate::{Error, ErrorKind, ResultExt as _};
|
use crate::{Error, ErrorKind, ResultExt as _};
|
||||||
pub mod config;
|
pub mod config;
|
||||||
|
pub mod docker;
|
||||||
pub mod http_reader;
|
pub mod http_reader;
|
||||||
pub mod io;
|
pub mod io;
|
||||||
pub mod logger;
|
pub mod logger;
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ btrfs-progs
|
|||||||
ca-certificates
|
ca-certificates
|
||||||
cifs-utils
|
cifs-utils
|
||||||
containerd.io
|
containerd.io
|
||||||
curl
|
|
||||||
cryptsetup
|
cryptsetup
|
||||||
|
curl
|
||||||
docker-ce
|
docker-ce
|
||||||
docker-ce-cli
|
docker-ce-cli
|
||||||
docker-compose-plugin
|
docker-compose-plugin
|
||||||
@@ -23,6 +23,7 @@ iotop
|
|||||||
iw
|
iw
|
||||||
jq
|
jq
|
||||||
libavahi-client3
|
libavahi-client3
|
||||||
|
libyajl2
|
||||||
lm-sensors
|
lm-sensors
|
||||||
lshw
|
lshw
|
||||||
lvm2
|
lvm2
|
||||||
@@ -34,6 +35,7 @@ network-manager
|
|||||||
nvme-cli
|
nvme-cli
|
||||||
nyx
|
nyx
|
||||||
openssh-server
|
openssh-server
|
||||||
|
podman
|
||||||
postgresql
|
postgresql
|
||||||
psmisc
|
psmisc
|
||||||
qemu-guest-agent
|
qemu-guest-agent
|
||||||
|
|||||||
@@ -82,6 +82,7 @@ cat > /etc/docker/daemon.json << EOF
|
|||||||
"storage-driver": "overlay2"
|
"storage-driver": "overlay2"
|
||||||
}
|
}
|
||||||
EOF
|
EOF
|
||||||
|
podman network create -d bridge --subnet 172.18.0.1/24 --opt com.docker.network.bridge.name=br-start9 start9
|
||||||
mkdir -p /etc/nginx/ssl
|
mkdir -p /etc/nginx/ssl
|
||||||
|
|
||||||
# fix to suppress docker warning, fixed in 21.xx release of docker cli: https://github.com/docker/cli/pull/2934
|
# fix to suppress docker warning, fixed in 21.xx release of docker cli: https://github.com/docker/cli/pull/2934
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ ENVIRONMENT=$(cat ENVIRONMENT.txt)
|
|||||||
GIT_HASH=$(cat GIT_HASH.txt | head -c 7)
|
GIT_HASH=$(cat GIT_HASH.txt | head -c 7)
|
||||||
DATE=$(date +%Y%m%d)
|
DATE=$(date +%Y%m%d)
|
||||||
|
|
||||||
|
ROOT_PART_END=7217792
|
||||||
|
|
||||||
VERSION_FULL="$VERSION-$GIT_HASH"
|
VERSION_FULL="$VERSION-$GIT_HASH"
|
||||||
|
|
||||||
if [ -n "$ENVIRONMENT" ]; then
|
if [ -n "$ENVIRONMENT" ]; then
|
||||||
@@ -22,7 +24,7 @@ if [ -n "$ENVIRONMENT" ]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
TARGET_NAME=startos-${VERSION_FULL}-${DATE}_raspberrypi.img
|
TARGET_NAME=startos-${VERSION_FULL}-${DATE}_raspberrypi.img
|
||||||
TARGET_SIZE=$[(6817791+1)*512]
|
TARGET_SIZE=$[($ROOT_PART_END+1)*512]
|
||||||
|
|
||||||
rm -f $TARGET_NAME
|
rm -f $TARGET_NAME
|
||||||
truncate -s $TARGET_SIZE $TARGET_NAME
|
truncate -s $TARGET_SIZE $TARGET_NAME
|
||||||
@@ -43,7 +45,7 @@ truncate -s $TARGET_SIZE $TARGET_NAME
|
|||||||
echo p
|
echo p
|
||||||
echo 2
|
echo 2
|
||||||
echo 526336
|
echo 526336
|
||||||
echo 6817791
|
echo $ROOT_PART_END
|
||||||
echo a
|
echo a
|
||||||
echo 1
|
echo 1
|
||||||
echo w
|
echo w
|
||||||
|
|||||||
1
system-images/compat/Cargo.lock
generated
1
system-images/compat/Cargo.lock
generated
@@ -4202,7 +4202,6 @@ dependencies = [
|
|||||||
"base64 0.13.1",
|
"base64 0.13.1",
|
||||||
"base64ct",
|
"base64ct",
|
||||||
"basic-cookies",
|
"basic-cookies",
|
||||||
"bollard",
|
|
||||||
"bytes",
|
"bytes",
|
||||||
"chrono",
|
"chrono",
|
||||||
"ciborium",
|
"ciborium",
|
||||||
|
|||||||
Reference in New Issue
Block a user