diff --git a/src/api/container.rs b/src/api/container.rs index d60c541..4319116 100644 --- a/src/api/container.rs +++ b/src/api/container.rs @@ -47,6 +47,13 @@ pub fn inject(script: &str, name: &str) -> Vec { ), ] } +pub fn inject_sh(script: &str) -> Vec { + vec![ + "sh".to_string(), + "-c".to_string(), + script.trim().to_string(), + ] +} impl<'a> ContainerApi<'a> { pub async fn get_all(&self, labels: &Labels) -> Result, AnyError> { @@ -398,7 +405,7 @@ impl<'a> ContainerApi<'a> { pub async fn create(&self, spec: RunSpec<'a>) -> Result { log::debug!( - "[{}: {:?}]: CREATE CONTAINER - name: {}, uid: {}, user: {}, image: {}, entrypoint: {}", + "[{}: {:?}]: CREATE CONTAINER - name: {}, uid: {}, user: {}, image: {}, entrypoint: {:?}", &spec.reason, spec.run_mode, spec.container_name, @@ -512,23 +519,24 @@ EXEC_EXIT_CODE=$(cat /tmp/exec_exit) echo "Exec session ended: $EXEC_EXIT_CODE" exit $EXEC_EXIT_CODE"#; - let epv = inject(&wait_for_exec, "entrypoint.sh"); + let epv = inject_sh(&wait_for_exec); let entrypoint = epv.iter().map(String::as_str).collect(); - let work_dir = "/tmp/one-shot"; + let work_dir = "/tmp"; let id = self .create(RunSpec { reason: name, image: image.unwrap_or(constants::DEFAULT_IMAGE), container_name: &id::random_suffix("one-shot"), command: Some(entrypoint), - mounts: mounts.map(|mut m| { - m.extend_from_slice(&[Mount { + mounts: { + let mut m = mounts.unwrap_or_default(); + m.push(Mount { target: Some(work_dir.into()), typ: Some(MountTypeEnum::TMPFS), ..Default::default() - }]); - m - }), + }); + Some(m) + }, uid: uid.unwrap_or(constants::ROOT_UID), work_dir: Some(work_dir), ..Default::default() @@ -583,7 +591,7 @@ echo start > /tmp/exec_start "#, command ); - inject(&cmd, "exec.sh") + inject_sh(&cmd) } pub async fn one_shot_output( diff --git a/src/api/exec.rs b/src/api/exec.rs index 9747d3c..06a6a69 100644 --- a/src/api/exec.rs +++ b/src/api/exec.rs @@ -7,7 +7,7 @@ use bollard::{ use bollard_stubs::models::ExecInspectResponse; use futures::{Stream, StreamExt}; -use crate::api::container::inject; +use crate::api::container::inject_sh; use crossterm::terminal::{disable_raw_mode, enable_raw_mode}; use std::{io::Read, time::Duration}; use tokio::{ @@ -157,7 +157,7 @@ impl<'a> ExecApi<'a> { ) -> Result { #[cfg(not(windows))] { - log::debug!( + log::trace!( "[{}] exec: {:?} in working dir: {:?}", reason, cmd, @@ -255,7 +255,7 @@ echo '[install] {}' {}"#, container_name, script ); - let install_cmd = inject(cmd.as_str(), "install.sh"); + let install_cmd = inject_sh(cmd.as_str()); let v = install_cmd.iter().map(|x| x.as_str()).collect::>(); self.tty( "install", @@ -301,16 +301,34 @@ echo '[install] {}' Ok(()) } + pub async fn chmod(&self, container_id: &str, dir: &str) -> Result<(), AnyError> { + log::debug!("Changing permissions... ({})", &dir); + + let chmod_response = self + .output( + "chmod", + container_id, + Some(constants::ROOT_USER), + Some(vec![ + "sh", + "-c", + &format!("chmod -R 1777 {}", &dir.replace("~", "${ROOZ_META_HOME}")), + ]), + ) + .await?; + + log::debug!("{}", chmod_response); + Ok(()) + } + pub async fn ensure_user(&self, container_id: &str) -> Result<(), AnyError> { - let ensure_user_cmd = inject( + let ensure_user_cmd = inject_sh( format!( r#"grep -q "^$ROOZ_META_USER:x:$ROOZ_META_UID" /etc/passwd && exit 0 sed -i "/:x:${{ROOZ_META_UID}}/d" /etc/passwd && \ echo "$ROOZ_META_USER:x:$ROOZ_META_UID:$ROOZ_META_UID:$ROOZ_META_USER:$ROOZ_META_HOME:/bin/sh" >> /etc/passwd"#, ) - .as_ref(), - "make_user.sh", - ); + .as_ref()); let ensure_user_output = self .output( diff --git a/src/api/system_config.rs b/src/api/system_config.rs index adcbe3f..5e19dc5 100644 --- a/src/api/system_config.rs +++ b/src/api/system_config.rs @@ -12,10 +12,10 @@ impl<'a> Api<'a> { .container .one_shot_output( "read-sys-config", - "ls /tmp/sys/rooz.config > /dev/null 2>&1 && cat /tmp/sys/rooz.config || echo ''" + "ls /init/sys/rooz.config > /dev/null 2>&1 && cat /init/sys/rooz.config || echo ''" .into(), Some(vec![ - RoozVolume::system_config_read("/tmp/sys").to_mount(None), + RoozVolume::system_config_read("/init/sys").to_mount(None), ]), None, None, diff --git a/src/api/volume.rs b/src/api/volume.rs index 46baea2..b8b0247 100644 --- a/src/api/volume.rs +++ b/src/api/volume.rs @@ -25,7 +25,7 @@ use bollard::{ service::Mount, }; use bollard_stubs::models::MountTypeEnum::VOLUME; -use bollard_stubs::models::VolumeCreateRequest; +use bollard_stubs::models::{MountTmpfsOptions, MountTypeEnum, VolumeCreateRequest}; impl<'a> VolumeApi<'a> { pub async fn get_all(&self, labels: &Labels) -> Result, AnyError> { diff --git a/src/api/workspace/enter.rs b/src/api/workspace/enter.rs index 83464b7..bd1c208 100644 --- a/src/api/workspace/enter.rs +++ b/src/api/workspace/enter.rs @@ -107,18 +107,10 @@ impl<'a> WorkspaceApi<'a> { &config.sidecars[container_name].real_mounts }; - let chown_uid = if is_work_container { - &config.uid - } else { - &config.sidecars[container_name] - .uid - .unwrap_or_else(|| panic!("TODO: read default uid from the image")) - }; - for (target, _) in real_mounts { self.api .exec - .chown(&container_id, chown_uid, target.as_str()) + .chmod(&container_id, target.as_str()) .await?; } } diff --git a/src/cmd/init.rs b/src/cmd/init.rs index e15bd71..7451b29 100644 --- a/src/cmd/init.rs +++ b/src/cmd/init.rs @@ -17,11 +17,11 @@ impl<'a> InitApi<'a> { async fn init_ssh(&self, image_id: &str, uid: &str) -> Result<(), AnyError> { let hostname = self.client.info().await?.name.unwrap_or("unknown".into()); let init_ssh = format!( - r#"mkdir -p /tmp/.ssh - KEYFILE=/tmp/.ssh/id_ed25519 + r#"mkdir -p /init/.ssh + KEYFILE=/init/.ssh/id_ed25519 ls "$KEYFILE.pub" > /dev/null 2>&1 || ssh-keygen -t ed25519 -N '' -f $KEYFILE -C rooz@{} cat "$KEYFILE.pub" - chmod 400 $KEYFILE && chown -R {} /tmp/.ssh + chmod 400 $KEYFILE && chown -R {} /init/.ssh "#, &hostname, &uid, ); @@ -30,7 +30,7 @@ impl<'a> InitApi<'a> { .one_shot( "init", init_ssh, - Some(vec![ssh::mount("/tmp/.ssh")]), + Some(vec![ssh::mount("/init/.ssh")]), None, Some(&image_id), ) @@ -48,13 +48,13 @@ impl<'a> InitApi<'a> { self.volume .ensure_mounts( &vec![RoozVolume::system_config_init( - "/tmp/sys", + "/init/sys", SystemConfig { age_key: Some(age_key.to_string().expose_secret().to_string()), gitconfig: Some( r#" [core] - sshCommand = ssh -i /tmp/.ssh/id_ed25519 -o UserKnownHostsFile=/tmp/.ssh/known_hosts + sshCommand = ssh -i /init/.ssh/id_ed25519 -o UserKnownHostsFile=/init/.ssh/known_hosts "# .trim() .to_string(), diff --git a/src/model/volume.rs b/src/model/volume.rs index f537ca6..0bc886f 100644 --- a/src/model/volume.rs +++ b/src/model/volume.rs @@ -42,12 +42,20 @@ impl RoozVolumeRole { } } -#[derive(Debug, Clone)] +#[derive(Clone)] pub struct RoozVolumeFile { pub file_path: String, pub data: String, } +impl std::fmt::Debug for RoozVolumeFile { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("RoozVolumeFile") + .field("file_path", &self.file_path) + .field("data", &format!("<{} bytes>", self.data.len())) + .finish() + } +} #[derive(Debug, Clone)] pub struct RoozVolume { pub path: String, diff --git a/src/util/git.rs b/src/util/git.rs index 6e1911f..2dc8e1f 100644 --- a/src/util/git.rs +++ b/src/util/git.rs @@ -1,6 +1,6 @@ use gix_config::File; use std::collections::HashMap; - +use bollard_stubs::models::{Mount, MountTypeEnum}; use crate::{ api::{GitApi, config::ConfigBody, container}, config::config::FileFormat, @@ -134,7 +134,7 @@ impl<'a> GitApi<'a> { } } - let clone_cmd = container::inject(&clone_script, "clone.sh"); + let clone_cmd = container::inject_sh(&clone_script); let labels = Labels::from(&[Labels::workspace(&spec.workspace_key), Labels::role("git")]); let mut mounts = vec![ssh::mount("/tmp/.ssh")]; @@ -164,6 +164,12 @@ impl<'a> GitApi<'a> { mounts.push(vol.to_mount(None)); } + mounts.push(Mount { + target: Some("/tmp".to_string()), + typ: Some(MountTypeEnum::TMPFS), + ..Default::default() + }); + let run_spec = RunSpec { reason: "git-clone", image: &spec.image,