Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 43 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,35 @@ It allows using rootless containers, running GUIs, quickly mounting your current

Even though **dogi** was originally inspired by [rocker](https://github.com/osrf/rocker) and solves a similar problem (or the same), it aims to do so with minimum user effort. Additionally, it provides the ability to interact with the `docker` client directly ([transparent](#design-principles)).

## !!! WARNING !!!

* Dogi's processes assume that Docker is able to build an image in the /tmp folder. This is typically verified if Docker is installed via the procedure described on the following pages: https://docs.docker.com/engine/install/ubuntu/ ; https://docs.docker.com/engine/install/linux-postinstall/ . This is typically not checked if Docker is installed via snap.

* For now, Dogi only works for images based on these distros: Ubuntu, Debian, Fedora. Dogi also won't work for images that are too simplistic (typically, the “hello-world” Docker image).

* In order to use Docker (and thus, Dogi) with GPU support, you need to follow the installation prerequisites instructions (https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html#prerequisites), and the subsequents configuration instructions (https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html#configuration). Otherwise, the following bug will appears :

```
Bug : Error response from daemon: unknown or invalid runtime name: nvidia
```

## Quickstart

First, do :

```
## To install this fork
export DOGI_INSTALL_GITHUB="XV25"
## To install the original repo
export DOGI_INSTALL_GITHUB="ntorresalberto"
```

Then :

```bash
# install binary
mkdir -pv ~/go/bin
wget -qO- https://github.com/ntorresalberto/dogi/releases/download/rolling/dogi-rolling-linux-amd64.tar.gz | tar xvz -C ~/go/bin
wget -qO- https://github.com/$DOGI_INSTALL_GITHUB/dogi/releases/download/rolling/dogi-rolling-linux-amd64.tar.gz | tar xvz -C ~/go/bin

# add bash autocompletion
echo 'source <(dogi completion bash)' >> ~/.bashrc
Expand Down Expand Up @@ -92,6 +115,13 @@ Some [optional setup steps](#optional-setup-steps) might be required.
dogi run ubuntu --no-user -- bash -c "apt install -y mesa-utils && glxgears" # as root
```

- Add access to a webcam (ex : /dev/video0) (after having given access, for example, through ``` sudo usermod -aG video $USER```) :

```bash
dogi run ubuntu --device-access "/dev/video0"
```


- Delete unused and/or dangling containers, images and volumes

```bash
Expand Down Expand Up @@ -133,8 +163,19 @@ You should find **dogi** useful if you:

### Installing from source

First, do :

```
## To install this fork
export DOGI_INSTALL_GITHUB="XV25"
## To install the original repo
export DOGI_INSTALL_GITHUB="ntorresalberto"
```

Then :

```bash
git clone https://github.com/ntorresalberto/dogi.git
git clone https://github.com/$DOGI_INSTALL_GITHUB/dogi.git
cd dogi
make install
dogi -v # test it!
Expand Down
11 changes: 10 additions & 1 deletion assets/apt-cacher/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,18 @@
# add this to container:
# echo 'Acquire::http { Proxy "http://localhost:3142"; };' >> /etc/apt/apt.conf.d/01proxy
# echo 'Acquire::http { Proxy "http://172.17.0.2:3142"; };' >> /etc/apt/apt.conf.d/01proxy
FROM ubuntu:22.04
FROM ubuntu:22.04

VOLUME ["/var/cache/apt-cacher-ng"]

## Added : Try to clean everything (for bug : apt-get BADSIG GPG)
RUN apt-get clean && \
cd /var/lib/apt &&\
mv lists lists.old &&\
mkdir -p lists/partial && \
apt-get clean &&\
apt-get update -qq -y

RUN apt-get update && apt-get install -y apt-cacher-ng \
&& sed -i 's/\# PassThroughPattern: \.\*/PassThroughPattern: \.\*/g' /etc/apt-cacher-ng/acng.conf

Expand Down
39 changes: 23 additions & 16 deletions assets/createUser.sh.in
Original file line number Diff line number Diff line change
Expand Up @@ -74,17 +74,21 @@ distro_unknown=""
sudo_ok=""
installing_packages_msg="installing sudo, tzdata, vim, bash-completion..."
if [ -n "${distro_ubuntu}" ] || [ -n "${distro_debian}" ]; then
if [ -n "${distro_ubuntu}" ]; then
echo -n "- Ubuntu distro, "
if [ "{{.setupSudo}}" == true ]; then
if [ -n "${distro_ubuntu}" ]; then
echo -n "- Ubuntu distro, "
else
echo -n "- Debian distro, "
fi
echo "running apt update..."
env DEBIAN_FRONTEND=noninteractive apt-get -qq update > /dev/null
echo "- installing apt-utils"
env DEBIAN_FRONTEND=noninteractive apt-get -qq install apt-utils > /dev/null 2>&1
echo "- ${installing_packages_msg}"
env DEBIAN_FRONTEND=noninteractive apt-get -qq install sudo tzdata vim bash-completion > /dev/null
else
echo -n "- Debian distro, "
sudo_ok=false
fi
echo "running apt update..."
env DEBIAN_FRONTEND=noninteractive apt-get -qq update > /dev/null
echo "- installing apt-utils"
env DEBIAN_FRONTEND=noninteractive apt-get -qq install apt-utils > /dev/null 2>&1
echo "- ${installing_packages_msg}"
env DEBIAN_FRONTEND=noninteractive apt-get -qq install sudo tzdata vim bash-completion > /dev/null
elif [ -n "${distro_fedora}" ]; then
echo "- Fedora distro, ${installing_packages_msg}"
dnf install -y sudo tzdata vim bash-completion > /dev/null
Expand All @@ -95,31 +99,34 @@ fi
if [ -n "${distro_unknown}" ]; then
echo "- UNKNOWN distro."
echo "failed to install packages sudo tzdata."
else
sudo_ok=false
fi

if [ "${sudo_ok}" != false ]; then
echo "{{.username}} ALL=NOPASSWD: ALL" >> /etc/sudoers.d/dogi
sed -i '/secure_path/ s/^/#/' /etc/sudoers
sudo_ok="True"
sudo_ok=true
fi
###############################################################

echo "- done, happy 🐳!"
echo "- you now are INSIDE the container"

echo "${sudo_ok}"
if [ $# -eq 0 ]; then
if [ -n "${sudo_ok}" ]; then
if [ "${sudo_ok}" = true ]; then
echo "- switch to user {{.username}}"
sudo -EHu {{.username}} bash
else
echo "- sudo not setup, will run as root"
echo "- sudo not setup for switch, will run as root"
bash
fi
fi

if [ -n "${sudo_ok}" ]; then
if [ "${sudo_ok}" = true ]; then
echo "- run as user: $@"
sudo -EHu {{.username}} "$@"
else
echo "- sudo not setup, will run as root"
echo "- sudo not setup in run, will run as root"
$@
fi

Expand Down
6 changes: 6 additions & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,14 @@ var (
noUSBPtr bool
noNethostPtr bool
noCacherPtr bool
noSetupSudoPtr bool
pidIPCHostPtr bool
workDirPtr string
contNamePtr string
othPtr string
devAccPtr string
devRMWPtr string
tempDirPtr string
logger = log.New(os.Stdout, appname+": ", log.Lmsgprefix)
dockerRunArgs = []string{
"--interactive",
Expand Down
80 changes: 74 additions & 6 deletions cmd/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"os/exec"
"os/user"
"path/filepath"
"strconv"
"strings"
"syscall"
"text/template"
Expand Down Expand Up @@ -222,7 +223,14 @@ func imageDistro(imageName string) string {
out, err := exec.Command("docker", "run", "--rm", "--tty",
"--entrypoint=cat",
imageName, "/etc/os-release").Output()
check(err)
// Debug for barebone images, where image distro could not
// even be checked
if err != nil {
logger.Printf("Error : could not check image distro. Recorded error : ")
logger.Println(err.Error())
logger.Fatalf("We'll assume the image is too basic for dogi. Exiting...")
}
//check(err)

for _, val := range supportedDistros() {
if strings.Contains(string(out), val) {
Expand All @@ -248,7 +256,8 @@ func setAptCacher() string {
{
logger.Printf("build apt cacher image: %s\n", imgName)
// build apt-cache-ng image
dir, err := os.MkdirTemp("", "dogi_apt-cache")
//dir, err := os.MkdirTemp("", "dogi_apt-cache")
dir, err := os.MkdirTemp(tempDirPtr, "dogi_apt-cache")
check(err)
defer os.RemoveAll(dir) // clean up

Expand Down Expand Up @@ -337,7 +346,7 @@ func setAptCacher() string {

aptCacherConf := fmt.Sprintf("Acquire::http { Proxy \"http://%s:3142\"; };", ip)

aptCacherFile, err := os.CreateTemp("", fmt.Sprintf(".%s_%s_*", appname, baseName))
aptCacherFile, err := os.CreateTemp(tempDirPtr, fmt.Sprintf(".%s_%s_*", appname, baseName))
check(err)
logger.Printf("apt-cacher file: %s", aptCacherFile.Name())
check(os.WriteFile(aptCacherFile.Name(), []byte(aptCacherConf), 0666))
Expand Down Expand Up @@ -478,6 +487,11 @@ const runExamples = `
- Launch an 3D accelerated GUI (opengl)

{{.appname}} run ubuntu -- bash -c "sudo apt install -y mesa-utils && glxgears"

- Add access to a webcam (ex : /dev/video0) :

{{.appname}} run ubuntu --device-access "/dev/video0"

`

var (
Expand Down Expand Up @@ -530,7 +544,9 @@ Examples:
check(err)

// create xauth magic cookie file
xauthfile, err := os.CreateTemp("", fmt.Sprintf(".%s*.xauth", appname))
//xauthfile, err := os.CreateTemp("", fmt.Sprintf(".%s*.xauth", appname))
xauthfile, err := os.CreateTemp(tempDirPtr, fmt.Sprintf(".%s*.xauth", appname))

check(err)
logger.Println("temp xauth file:", xauthfile.Name())
addCopyToContainerFile(xauthfile.Name(), "/.xauth")
Expand Down Expand Up @@ -559,7 +575,11 @@ Examples:
logger.Printf("workdir: %s\n", workDirPtr)
mountStrs := []string{fmt.Sprintf("--volume=%s:%s", workDirPtr, workDirPtr)}

cidFile := fmt.Sprintf("%s/.%s%v.cid", os.TempDir(), appname, rand.Int63())
//cidFile := fmt.Sprintf("%s/.%s%v.cid", os.TempDir(), appname, rand.Int63())
if tempDirPtr == "" {
tempDirPtr = os.TempDir()
}
cidFile := fmt.Sprintf("%s/.%s%v.cid", tempDirPtr, appname, rand.Int63())
mountStrs = append(mountStrs, fmt.Sprintf("--cidfile=%s", cidFile))
mountStrs = append(mountStrs, fmt.Sprintf("--volume=%s:%s", cidFile, cidFileContainer))

Expand Down Expand Up @@ -617,6 +637,12 @@ Examples:
if !noNethostPtr {
logger.Println("adding --network=host")
dockerRunArgs = append(dockerRunArgs, "--network=host")
if pidIPCHostPtr {
// https://github.com/eProsima/Fast-DDS/issues/2956
logger.Println("adding --pid=host and --ipc=host")
dockerRunArgs = append(dockerRunArgs, "--pid=host")
dockerRunArgs = append(dockerRunArgs, "--ipc=host")
}
}

if privilegedPtr {
Expand Down Expand Up @@ -708,6 +734,24 @@ Examples:
"--volume=/dev/bus/usb:/dev/bus/usb")
dockerRunArgs = append(dockerRunArgs,
"--device-cgroup-rule=c 189:* rmw")

// add commands to add rules to specific usb devices (as stated by https://stackoverflow.com/a/62758958)
if devRMWPtr != "" {
var indexes = strings.Split(devRMWPtr, ";")
for i := 0; i < len(indexes); i++ {
var s = "--device-cgroup-rule=c " + indexes[i] + ":* rmw"
dockerRunArgs = append(dockerRunArgs, s)
}
}
// add rules to mount specific usb devices
if devAccPtr != "" {
var indexes = strings.Split(devAccPtr, ";")
for i := 0; i < len(indexes); i++ {
var s = "--device=" + indexes[i]
dockerRunArgs = append(dockerRunArgs, s)
}
}

}

if !noUserPtr && userObj.Uid == "0" {
Expand Down Expand Up @@ -735,11 +779,19 @@ Examples:

// TODO: createUser file won't be removed because
// process is replaced at Exec, is there a way?
createUserFile, err := os.CreateTemp("",
// createUserFile, err := os.CreateTemp("",
// fmt.Sprintf(".%s*.sh", appname))
createUserFile, err := os.CreateTemp(tempDirPtr,
fmt.Sprintf(".%s*.sh", appname))
check(err)
logger.Println("create user script:", createUserFile.Name())
{
//logger.Println(strconv.FormatBool(setupSudoPtr))
var setupSudo bool = true
if noSetupSudoPtr {
setupSudo = false
}

groupsCmd := userSingleton().createGroupsCmd()
err := template.Must(template.New("").Option("missingkey=error").Parse(assets.CreateUserTemplate)).Execute(createUserFile,
map[string]string{"username": userObj.Username,
Expand All @@ -749,6 +801,7 @@ Examples:
"gnames": groupsCmd.gnames,
"Name": userObj.Name,
"createGroups": groupsCmd.cmd,
"setupSudo": strconv.FormatBool(setupSudo),
})
check(err)
}
Expand All @@ -759,6 +812,14 @@ Examples:
entrypoint = merge([]string{"bash", createUserScriptPath}, execCommand)
}

if othPtr != "" {
// add final custom commands.
outStr := strings.Split(othPtr, " ")
for _, elmt := range outStr {
dockerRunArgs = append(dockerRunArgs, elmt)
}
}

dockerRunArgs = append(dockerRunArgs, imageName)
// run command end
// ********************************************************
Expand Down Expand Up @@ -813,4 +874,11 @@ func init() {
runCmd.Flags().BoolVar(&noRMPtr, "no-rm", false, "don't launch with --rm (container will exist after exiting)")
runCmd.Flags().BoolVar(&noUSBPtr, "no-usb", false, "don't mount usb devices")
runCmd.Flags().BoolVar(&noNethostPtr, "no-nethost", false, "don't launch with --network=host")
runCmd.Flags().StringVar(&othPtr, "other", "", "add the following string to 'run' command.")
runCmd.Flags().StringVar(&devRMWPtr, "device-rmw", "", "add rmw rules to the following devices (as stated in https://stackoverflow.com/a/62758958). Format : <id_dev_a>;<id_dev_b>")
runCmd.Flags().StringVar(&devAccPtr, "device-access", "", "mount the following devices to container (through --device option). Format : <dev_name_a>;<dev_name_b>")
runCmd.Flags().BoolVar(&noSetupSudoPtr, "no-setup-sudo", false, "install inside containers various basic packages, such as apt-utils, sudo, tzdata, vim, or bash-completion.")
runCmd.Flags().StringVar(&tempDirPtr, "temp-dir", "", "temporary directory to use for dogi (default: $TMPDIR or /tmp, through empty command). Can be modified if there are access issues with this particular folder.")
runCmd.Flags().BoolVar(&pidIPCHostPtr, "pidipc-host", true, "add --pid=host (PID of the container) and --ipc=host (Memory Access) to docker run command. Automatically activated with --network=host. Although it removes a security layer, it is notably necessary to let ROS containers communicates between them in --network=host mode.")

}