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
|
||||
- unstable
|
||||
- dev-unstable
|
||||
- podman
|
||||
- dev-podman
|
||||
- dev-unstable-podman
|
||||
runner:
|
||||
type: choice
|
||||
description: Runner
|
||||
@@ -39,7 +42,7 @@ on:
|
||||
|
||||
env:
|
||||
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:
|
||||
all:
|
||||
@@ -139,6 +142,7 @@ jobs:
|
||||
with:
|
||||
repository: Start9Labs/startos-image-recipes
|
||||
path: startos-image-recipes
|
||||
ref: feature/podman
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
@@ -166,7 +170,7 @@ jobs:
|
||||
|
||||
- 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
|
||||
working-directory: startos-image-recipes
|
||||
|
||||
1
backend/Cargo.lock
generated
1
backend/Cargo.lock
generated
@@ -4665,7 +4665,6 @@ dependencies = [
|
||||
"base64 0.13.1",
|
||||
"base64ct",
|
||||
"basic-cookies",
|
||||
"bollard",
|
||||
"bytes",
|
||||
"chrono",
|
||||
"ciborium",
|
||||
|
||||
@@ -33,6 +33,7 @@ avahi-alias = ["avahi"]
|
||||
cli = []
|
||||
sdk = []
|
||||
daemon = []
|
||||
podman = []
|
||||
|
||||
[dependencies]
|
||||
aes = { version = "0.7.5", features = ["ctr"] }
|
||||
@@ -50,7 +51,6 @@ base32 = "0.4.0"
|
||||
base64 = "0.13.0"
|
||||
base64ct = "1.5.1"
|
||||
basic-cookies = "0.1.4"
|
||||
bollard = "0.13.0"
|
||||
bytes = "1"
|
||||
chrono = { version = "0.4.19", features = ["serde"] }
|
||||
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 ..
|
||||
FLAGS=""
|
||||
if [[ "$ENVIRONMENT" =~ (^|-)podman($|-) ]]; then
|
||||
FLAGS="podman,$FLAGS"
|
||||
fi
|
||||
if [[ "$ENVIRONMENT" =~ (^|-)unstable($|-) ]]; then
|
||||
FLAGS="unstable,$FLAGS"
|
||||
fi
|
||||
@@ -56,7 +59,7 @@ else
|
||||
fi
|
||||
for ARCH in x86_64 aarch64
|
||||
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
|
||||
fail=true
|
||||
fi
|
||||
|
||||
@@ -4,9 +4,7 @@ use std::ops::Deref;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
use bollard::Docker;
|
||||
use helpers::to_tmp_path;
|
||||
use josekit::jwk::Jwk;
|
||||
use patch_db::json_ptr::JsonPointer;
|
||||
@@ -111,7 +109,6 @@ pub struct RpcContextSeed {
|
||||
pub db: PatchDb,
|
||||
pub secret_store: PgPool,
|
||||
pub account: RwLock<AccountInfo>,
|
||||
pub docker: Docker,
|
||||
pub net_controller: Arc<NetController>,
|
||||
pub managers: ManagerMap,
|
||||
pub metrics_cache: RwLock<Option<crate::system::Metrics>>,
|
||||
@@ -189,9 +186,6 @@ impl RpcContext {
|
||||
let account = AccountInfo::load(&secret_store).await?;
|
||||
let db = base.db(&account).await?;
|
||||
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(
|
||||
NetController::init(
|
||||
base.tor_control
|
||||
@@ -225,7 +219,6 @@ impl RpcContext {
|
||||
db,
|
||||
secret_store,
|
||||
account: RwLock::new(account),
|
||||
docker,
|
||||
net_controller,
|
||||
managers,
|
||||
metrics_cache,
|
||||
|
||||
@@ -20,6 +20,7 @@ use crate::install::PKG_ARCHIVE_DIR;
|
||||
use crate::middleware::auth::LOCAL_AUTH_COOKIE_PATH;
|
||||
use crate::sound::BEP;
|
||||
use crate::system::time;
|
||||
use crate::util::docker::{create_bridge_network, CONTAINER_DATADIR, CONTAINER_TOOL};
|
||||
use crate::util::Invoke;
|
||||
use crate::{Error, ARCH};
|
||||
|
||||
@@ -289,17 +290,23 @@ pub async fn init(cfg: &RpcContextConfig) -> Result<InitResult, Error> {
|
||||
if tokio::fs::metadata(&tmp_dir).await.is_err() {
|
||||
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();
|
||||
if should_rebuild && tmp_docker_exists {
|
||||
tokio::fs::remove_dir_all(&tmp_docker).await?;
|
||||
}
|
||||
if CONTAINER_TOOL == "docker" {
|
||||
Command::new("systemctl")
|
||||
.arg("stop")
|
||||
.arg("docker")
|
||||
.invoke(crate::ErrorKind::Docker)
|
||||
.await?;
|
||||
crate::disk::mount::util::bind(&tmp_docker, "/var/lib/docker", false).await?;
|
||||
}
|
||||
crate::disk::mount::util::bind(&tmp_docker, CONTAINER_DATADIR, false).await?;
|
||||
|
||||
if CONTAINER_TOOL == "docker" {
|
||||
Command::new("systemctl")
|
||||
.arg("reset-failed")
|
||||
.arg("docker")
|
||||
@@ -310,30 +317,29 @@ pub async fn init(cfg: &RpcContextConfig) -> Result<InitResult, Error> {
|
||||
.arg("docker")
|
||||
.invoke(crate::ErrorKind::Docker)
|
||||
.await?;
|
||||
}
|
||||
tracing::info!("Mounted Docker Data");
|
||||
|
||||
if should_rebuild || !tmp_docker_exists {
|
||||
tracing::info!("Creating Docker Network");
|
||||
bollard::Docker::connect_with_unix_defaults()?
|
||||
.create_network(bollard::network::CreateNetworkOptions {
|
||||
name: "start9",
|
||||
driver: "bridge",
|
||||
ipam: bollard::models::Ipam {
|
||||
config: Some(vec![bollard::models::IpamConfig {
|
||||
subnet: Some("172.18.0.1/24".into()),
|
||||
..Default::default()
|
||||
}]),
|
||||
..Default::default()
|
||||
},
|
||||
options: {
|
||||
let mut m = HashMap::new();
|
||||
m.insert("com.docker.network.bridge.name", "br-start9");
|
||||
m
|
||||
},
|
||||
..Default::default()
|
||||
})
|
||||
if CONTAINER_TOOL == "podman" {
|
||||
Command::new("podman")
|
||||
.arg("run")
|
||||
.arg("-d")
|
||||
.arg("--rm")
|
||||
.arg("--network=start9")
|
||||
.arg("--name=netdummy")
|
||||
.arg("start9/x_system/utils:latest")
|
||||
.arg("sleep")
|
||||
.arg("infinity")
|
||||
.invoke(crate::ErrorKind::Docker)
|
||||
.await?;
|
||||
}
|
||||
|
||||
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");
|
||||
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");
|
||||
Command::new("docker")
|
||||
Command::new(CONTAINER_TOOL)
|
||||
.arg("run")
|
||||
.arg("--privileged")
|
||||
.arg("--rm")
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
use std::collections::HashMap;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
||||
use bollard::image::{ListImagesOptions, RemoveImageOptions};
|
||||
use patch_db::{DbHandle, LockReceipt, LockTargetId, LockType, PatchDbHandle, Verifier};
|
||||
use sqlx::{Executor, Postgres};
|
||||
use tracing::instrument;
|
||||
@@ -104,46 +102,12 @@ pub async fn cleanup(ctx: &RpcContext, id: &PackageId, version: &Version) -> Res
|
||||
let mut errors = ErrorCollection::new();
|
||||
ctx.managers.remove(&(id.clone(), version.clone())).await;
|
||||
// docker images start9/$APP_ID/*:$VERSION -q | xargs docker rmi
|
||||
let images = ctx
|
||||
.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));
|
||||
let images = crate::util::docker::images_for(id, version).await?;
|
||||
errors.extend(
|
||||
futures::future::join_all(
|
||||
images
|
||||
.into_iter()
|
||||
.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
|
||||
}),
|
||||
)
|
||||
futures::future::join_all(images.into_iter().map(|sha| async {
|
||||
let sha = sha; // move into future
|
||||
crate::util::docker::remove_image(&sha).await
|
||||
}))
|
||||
.await,
|
||||
);
|
||||
let pkg_archive_dir = ctx
|
||||
|
||||
@@ -46,6 +46,7 @@ use crate::notifications::NotificationLevel;
|
||||
use crate::s9pk::manifest::{Manifest, PackageId};
|
||||
use crate::s9pk::reader::S9pkReader;
|
||||
use crate::status::{MainStatus, Status};
|
||||
use crate::util::docker::CONTAINER_TOOL;
|
||||
use crate::util::io::{copy_and_shutdown, response_to_reader};
|
||||
use crate::util::serde::{display_serializable, Port};
|
||||
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);
|
||||
progress
|
||||
.track_read_during(progress_model.clone(), &ctx.db, || async {
|
||||
let mut load = Command::new("docker")
|
||||
let mut load = Command::new(CONTAINER_TOOL)
|
||||
.arg("load")
|
||||
.stdin(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 ext = path.extension().and_then(|ext| ext.to_str());
|
||||
if ext == Some("tar") || ext == Some("s9pk") {
|
||||
let mut load = Command::new("docker")
|
||||
let mut load = Command::new(CONTAINER_TOOL)
|
||||
.arg("load")
|
||||
.stdin(Stdio::piped())
|
||||
.stderr(Stdio::piped())
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
use bollard::container::{StopContainerOptions, WaitContainerOptions};
|
||||
use models::ErrorKind;
|
||||
use tokio_stream::StreamExt;
|
||||
|
||||
use crate::context::RpcContext;
|
||||
use crate::s9pk::manifest::Manifest;
|
||||
use crate::util::docker::stop_container;
|
||||
use crate::Error;
|
||||
|
||||
/// 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 {
|
||||
pub async fn stop_container(&self) -> Result<(), Error> {
|
||||
match self
|
||||
.ctx
|
||||
.docker
|
||||
.stop_container(
|
||||
match stop_container(
|
||||
&self.container_name,
|
||||
Some(StopContainerOptions {
|
||||
t: self
|
||||
.manifest
|
||||
self.manifest
|
||||
.containers
|
||||
.as_ref()
|
||||
.and_then(|c| c.main.sigterm_timeout)
|
||||
.map(|d| d.as_secs())
|
||||
.unwrap_or(30) as i64,
|
||||
}),
|
||||
.map(|d| *d),
|
||||
None,
|
||||
)
|
||||
.await
|
||||
{
|
||||
Err(bollard::errors::Error::DockerResponseServerError {
|
||||
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
|
||||
Err(e) if e.kind == ErrorKind::NotFound => (), // Already stopped
|
||||
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(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@ use crate::procedure::docker::{DockerContainer, DockerProcedure, LongRunning};
|
||||
use crate::procedure::{NoOutput, ProcedureName};
|
||||
use crate::s9pk::manifest::Manifest;
|
||||
use crate::status::MainStatus;
|
||||
use crate::util::docker::{get_container_ip, kill_container};
|
||||
use crate::util::NonDetachingJoinHandle;
|
||||
use crate::volume::Volume;
|
||||
use crate::Error;
|
||||
@@ -228,7 +229,7 @@ impl Manager {
|
||||
.send_replace(Default::default())
|
||||
.join_handle()
|
||||
{
|
||||
transition.abort();
|
||||
(&**transition).abort();
|
||||
}
|
||||
}
|
||||
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 {
|
||||
loop {
|
||||
match container_inspect(seed).await {
|
||||
Ok(res) => {
|
||||
match res
|
||||
.network_settings
|
||||
.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()
|
||||
{
|
||||
match get_container_ip(&seed.container_name).await {
|
||||
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) if e.kind == ErrorKind::NotFound => (),
|
||||
Err(e) => return GetRunningIp::Error(e.into()),
|
||||
}
|
||||
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))]
|
||||
async fn add_network_for_main(
|
||||
seed: &ManagerSeed,
|
||||
@@ -851,26 +826,10 @@ type RuntimeOfCommand = NonDetachingJoinHandle<Result<Result<NoOutput, (i32, Str
|
||||
#[instrument(skip(seed, runtime))]
|
||||
async fn get_running_ip(seed: &ManagerSeed, mut runtime: &mut RuntimeOfCommand) -> GetRunningIp {
|
||||
loop {
|
||||
match container_inspect(seed).await {
|
||||
Ok(res) => {
|
||||
match res
|
||||
.network_settings
|
||||
.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()
|
||||
{
|
||||
match get_container_ip(&seed.container_name).await {
|
||||
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) if e.kind == ErrorKind::NotFound => (),
|
||||
Err(e) => return GetRunningIp::Error(e.into()),
|
||||
}
|
||||
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 {
|
||||
// send signal to container
|
||||
manager
|
||||
.seed
|
||||
.ctx
|
||||
.docker
|
||||
.kill_container(
|
||||
&manager.seed.container_name,
|
||||
Some(bollard::container::KillContainerOptions {
|
||||
signal: signal.to_string(),
|
||||
}),
|
||||
)
|
||||
kill_container(&manager.seed.container_name, Some(signal))
|
||||
.await
|
||||
.or_else(|e| {
|
||||
if matches!(
|
||||
e,
|
||||
bollard::errors::Error::DockerResponseServerError {
|
||||
status_code: 409, // CONFLICT
|
||||
..
|
||||
} | bollard::errors::Error::DockerResponseServerError {
|
||||
status_code: 404, // NOT FOUND
|
||||
..
|
||||
}
|
||||
) {
|
||||
if e.kind == ErrorKind::NotFound {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(e)
|
||||
|
||||
@@ -7,7 +7,6 @@ use std::path::{Path, PathBuf};
|
||||
use std::time::Duration;
|
||||
|
||||
use async_stream::stream;
|
||||
use bollard::container::RemoveContainerOptions;
|
||||
use color_eyre::eyre::eyre;
|
||||
use color_eyre::Report;
|
||||
use futures::future::{BoxFuture, Either as EitherFuture};
|
||||
@@ -26,6 +25,7 @@ use tracing::instrument;
|
||||
use super::ProcedureName;
|
||||
use crate::context::RpcContext;
|
||||
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::Version;
|
||||
use crate::volume::{VolumeId, Volumes};
|
||||
@@ -228,8 +228,8 @@ impl DockerProcedure {
|
||||
timeout: Option<Duration>,
|
||||
) -> Result<Result<O, (i32, String)>, Error> {
|
||||
let name = name.docker_name();
|
||||
let name: Option<&str> = name.as_deref();
|
||||
let mut cmd = tokio::process::Command::new("docker");
|
||||
let name: Option<&str> = name.as_ref().map(|x| &**x);
|
||||
let mut cmd = tokio::process::Command::new(CONTAINER_TOOL);
|
||||
let container_name = Self::container_name(pkg_id, name);
|
||||
cmd.arg("run")
|
||||
.arg("--rm")
|
||||
@@ -240,25 +240,7 @@ impl DockerProcedure {
|
||||
.arg(format!("--hostname={}", &container_name))
|
||||
.arg("--no-healthcheck")
|
||||
.kill_on_drop(true);
|
||||
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),
|
||||
}?;
|
||||
remove_container(&container_name, true).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) {
|
||||
cmd.stdin(std::process::Stdio::piped());
|
||||
@@ -407,7 +389,9 @@ impl DockerProcedure {
|
||||
input: Option<I>,
|
||||
timeout: Option<Duration>,
|
||||
) -> 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");
|
||||
|
||||
@@ -556,9 +540,9 @@ impl DockerProcedure {
|
||||
pkg_version: &Version,
|
||||
volumes: &Volumes,
|
||||
input: Option<I>,
|
||||
_timeout: Option<Duration>,
|
||||
timeout: Option<Duration>,
|
||||
) -> 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.args(
|
||||
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(
|
||||
if exit_status.success() || exit_status.code() == Some(143) {
|
||||
Ok(serde_json::from_value(
|
||||
@@ -820,10 +815,10 @@ impl LongRunning {
|
||||
const BIND_LOCATION: &str = "/usr/lib/embassy/container/";
|
||||
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 mut cmd = tokio::process::Command::new("docker");
|
||||
let mut cmd = tokio::process::Command::new(CONTAINER_TOOL);
|
||||
cmd.arg("image")
|
||||
.arg("inspect")
|
||||
.arg("--format")
|
||||
@@ -838,7 +833,7 @@ impl LongRunning {
|
||||
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")
|
||||
.arg("--network=start9")
|
||||
.arg(format!("--add-host=embassy:{}", Ipv4Addr::from(HOST_IP)))
|
||||
@@ -891,31 +886,6 @@ impl LongRunning {
|
||||
cmd.stdin(std::process::Stdio::piped());
|
||||
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(
|
||||
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::init::{STANDBY_MODE_PATH, SYSTEM_REBUILD_PATH};
|
||||
use crate::sound::SHUTDOWN;
|
||||
use crate::util::docker::CONTAINER_TOOL;
|
||||
use crate::util::{display_none, Invoke};
|
||||
use crate::{Error, OS_ARCH};
|
||||
|
||||
@@ -43,6 +44,7 @@ impl Shutdown {
|
||||
tracing::error!("Error Stopping Journald: {}", e);
|
||||
tracing::debug!("{:?}", e);
|
||||
}
|
||||
if CONTAINER_TOOL == "docker" {
|
||||
if let Err(e) = Command::new("systemctl")
|
||||
.arg("stop")
|
||||
.arg("docker")
|
||||
@@ -52,6 +54,7 @@ impl Shutdown {
|
||||
tracing::error!("Error Stopping Docker: {}", e);
|
||||
tracing::debug!("{:?}", e);
|
||||
}
|
||||
}
|
||||
if let Some(guid) = &self.disk_guid {
|
||||
if let Err(e) = export(guid, &self.datadir).await {
|
||||
tracing::error!("Error Exporting Volume Group: {}", e);
|
||||
|
||||
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::{Error, ErrorKind, ResultExt as _};
|
||||
pub mod config;
|
||||
pub mod docker;
|
||||
pub mod http_reader;
|
||||
pub mod io;
|
||||
pub mod logger;
|
||||
|
||||
@@ -7,8 +7,8 @@ btrfs-progs
|
||||
ca-certificates
|
||||
cifs-utils
|
||||
containerd.io
|
||||
curl
|
||||
cryptsetup
|
||||
curl
|
||||
docker-ce
|
||||
docker-ce-cli
|
||||
docker-compose-plugin
|
||||
@@ -23,6 +23,7 @@ iotop
|
||||
iw
|
||||
jq
|
||||
libavahi-client3
|
||||
libyajl2
|
||||
lm-sensors
|
||||
lshw
|
||||
lvm2
|
||||
@@ -34,6 +35,7 @@ network-manager
|
||||
nvme-cli
|
||||
nyx
|
||||
openssh-server
|
||||
podman
|
||||
postgresql
|
||||
psmisc
|
||||
qemu-guest-agent
|
||||
|
||||
@@ -82,6 +82,7 @@ cat > /etc/docker/daemon.json << EOF
|
||||
"storage-driver": "overlay2"
|
||||
}
|
||||
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
|
||||
|
||||
# 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)
|
||||
DATE=$(date +%Y%m%d)
|
||||
|
||||
ROOT_PART_END=7217792
|
||||
|
||||
VERSION_FULL="$VERSION-$GIT_HASH"
|
||||
|
||||
if [ -n "$ENVIRONMENT" ]; then
|
||||
@@ -22,7 +24,7 @@ if [ -n "$ENVIRONMENT" ]; then
|
||||
fi
|
||||
|
||||
TARGET_NAME=startos-${VERSION_FULL}-${DATE}_raspberrypi.img
|
||||
TARGET_SIZE=$[(6817791+1)*512]
|
||||
TARGET_SIZE=$[($ROOT_PART_END+1)*512]
|
||||
|
||||
rm -f $TARGET_NAME
|
||||
truncate -s $TARGET_SIZE $TARGET_NAME
|
||||
@@ -43,7 +45,7 @@ truncate -s $TARGET_SIZE $TARGET_NAME
|
||||
echo p
|
||||
echo 2
|
||||
echo 526336
|
||||
echo 6817791
|
||||
echo $ROOT_PART_END
|
||||
echo a
|
||||
echo 1
|
||||
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",
|
||||
"base64ct",
|
||||
"basic-cookies",
|
||||
"bollard",
|
||||
"bytes",
|
||||
"chrono",
|
||||
"ciborium",
|
||||
|
||||
Reference in New Issue
Block a user