Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move tools & configs from /home/user/ to /home/tooling/ #115

Merged
merged 16 commits into from
Oct 31, 2023

Conversation

AObuchow
Copy link
Contributor

@AObuchow AObuchow commented Aug 29, 2023

This PR aims to resolve eclipse-che/che#22412, where enabling devEnvironments.persistUserHome in the Che Cluster CR will cause the workspace's PVC to mount to /home/user/, overwriting any tools, configurations, etc. that are present in the UDI's /home/user/ directory.

In order to resolve this issue, we are now installing tools (and most config files) to /home/tooling/ and using GNU stow to create symbolic links to /home/user/ for all files in /home/tooling/ (with the exception of .viminfo, which cannot be a symbolic link by vim's design).

Rather than trying to keep $PATH to be exactly the same as prior to this change (i.e. tooling binaries are found in /home/user/) , $PATH has been modified to contain the appropriate directories in /home/tooling/. The reasoning for this change is due to the fact that the call to stow in the entrypoint is not guaranteed to run before any postStart commands. In order to ensure postStart commands can invoke all UDI binaries, they must be placed under /home/tooling/, which guarantees they will not be overwritten when a PVC is mounted to /home/user/.

Below are some other details regarding the changes made in this PR:

  1. Stow is invoked with the --no-folding option to recreate the directory tree structure from /home/tooling/ in /home/user/, to ensure any changes within a configuration folder will persist on the PVC (as directories in /home/tooling/ are essentially ephemeral).
  2. Stow is invoked up to 3 times:
    • Once in the UBI8 base image, where the --no-folding option is not used due to the lack of directories in the /home/tooling/ of the base image.
    • Once in the UDI, with --no-folding enabled.
    • Once in the UDI's entrypoint, if we detect that /home/user/ has been mounted by a PVC (erasing the contents of /home/user/)
  3. Stow does not create a symbolic link for .viminfo, as it must be a regular file by vim's design. Consequently, the .viminfo must be manually copied from /home/tooling/ -> /home/user/.
  4. Stow adds a bit of extra startup time to the container when it is invoked in the UDI's entrypoint . Unfortunately, we cannot run it as a background process or else it might not complete successfully when the container is started.
  5. The changes from chore: define user directory for binaries #117 were adopted for this PR. I kept /home/user/.local/bin/ still on $PATH so that users could mount their own binaries to /home/user/.local/bin/ and invoke them as VSCode tasks or postStart commands.
  6. $SDKMAN_DIR and $NVM_DIR are hard-coded to point to /home/tooling/.sdkman (rather than $HOME/.sdkman). sdkman will not function if $SDKMAN_DIR is set to /home/user/.sdkman/, as its init script won't detect symlinks.
  7. We are no longer modifying the ~/.bashrc to extend the bash environment. Instead, we now have 2 bash scripts located in /etc/profile.d/: one which modifies the bash environment, another to set the terminal prompt. These scripts are automatically loaded by bash. Now, users are free to modify the UDI's .bashrc (e.g. overwritting it with an automount configmap in Che) without breaking any modifications to $PATH that we provide in the UDI (such as sdkman).
  8. I removed code from the UBI8's entrypoint that involved manually creating a ~/.bashrc, which was obsolete.
  9. The entrypoint now looks for the kubeconfig in $KUBECONFIG rather than being hard-coded to /home/user/.kube/config.
  10. The kubedock entrypoint was modified to no longer fail if the kube config could not be found.
  11. The duration of the timeout for finding the kube config in the entrypoint can now be changed with $KUBEDOCK_TIMEOUT

This PR is also based off of #110, so reading the changes introduced in that PR is also worthwhile.

Testing

I've pushed quay.io/aobuchow/universal-developer-image:stow to test the UDI as a docker container. I've also created a sample devfile that uses the updated UDI, to test in Che with devEnvironments.persistUserHome.enabled: true.

Sample devfile notes

My sample devfile invokes some commands to ensure they are available on $PATH to run as preStart commands. Note: some of these commands generate stderr output, which can be viewed in /tmp/poststart-stderr.txt.

Additionally, I attempted to log the binaries available as postStart commands with compgen -c | xargs which --all --skip-functions | sort | uniq > /projects/udi-tooling/binaries.txt ; Here's the output of that command.

One final note about my devfile: since postStart commands in DWO are run as /bin/sh -C { <commands> } any modifications to $PATH present in the bash environment are normally not usable (e.g. nvm cannot be invoked). To workaround this, I added a postStart command that runs source /home/user/.bashrc in the parent devfile. This workaround could potentially be used in CheCode when running VSCode tasks (relevant comment).

Other testing notes

Properly ensuring this PR does not break any existing functionality or workflows may be tricky due to the nature of moving everything out of /home/user/ into /home/tooling/ (and then "back" into /home/user/ with stow).

Since the podman wrapper was affected by this change, it's also worth ensuring the test cases (wrapped podman commands) from #107 work as well.

Below are some comparisons of the /home/user/ directory, the /home/user/.bashrc and $PATH, before and after this change.

ls -la in /home/user/ directory

Before:

~ $ ls -la
total 108
drwxrwx---. 1 user root   244 Aug 29 02:12 .
drwxrwxr-x. 1 root root     8 Jul 17 19:51 ..
-rw-rw-r--. 1 user root    18 Jun 20  2022 .bash_logout
-rw-rw-r--. 1 user root   141 Jun 20  2022 .bash_profile
-rw-rw-r--. 1 user root   826 Aug  3 17:34 .bashrc
drwxrwxr-x. 1 root root    22 Aug  3 17:30 .cache
drwxrwxr-x. 1 root root    12 Aug  3 17:31 .cargo
drwxrwxr-x. 1 root root    36 Aug  3 17:33 .config
-rw-rw-r--. 1 root root   126 Jul 17 19:51 .gitconfig
drwxrwxr-x. 1 root root    42 Aug  3 17:33 .krew
-rw-rw-r--. 1 root root 88043 Aug  3 17:33 .kubectl_aliases
drwxr-xr-x. 1 user user     6 Aug 29 02:12 .local
drwxrwxr-x. 1 user root    84 Aug  3 17:27 .npm
drwxrwxr-x. 1 user root   610 Aug  3 17:27 .nvm
drwxrwxr-x. 1 root root    96 Aug  3 17:31 .rustup
drwxrwxr-x. 1 user root    84 Aug  3 04:47 .sdkman
-rw-rw-r--. 1 root root   172 Aug  3 17:28 .wget-hsts
drwxrwxr-x. 1 root root    12 Aug  3 17:30 go

After:

~ $ ls -la
total 28
drwxrwx---. 1 user root 260 Sep 20 04:28 .
drwxrwxr-x. 1 root root  22 Sep 20 04:07 ..
-rw-rw-r--. 1 user root  18 Jun 20  2022 .bash_logout
-rw-rw-r--. 1 user root 141 Jun 20  2022 .bash_profile
-rw-rw-r--. 1 user root 376 Jun 20  2022 .bashrc
drwxrwxr-x. 1 root root  22 Sep 20 04:28 .cache
drwxrwxr-x. 1 root root  12 Sep 20 04:28 .cargo
drwxrwxr-x. 1 root root  36 Sep 20 04:28 .config
lrwxrwxrwx. 1 root root  21 Sep 20 04:07 .gitconfig -> ../tooling/.gitconfig
drwxrwxr-x. 1 root root  42 Sep 20 04:28 .krew
lrwxrwxrwx. 1 root root  27 Sep 20 04:28 .kubectl_aliases -> ../tooling/.kubectl_aliases
drwxrwxr-x. 1 root root   6 Sep 20 04:28 .local
drwxrwxr-x. 1 root root  84 Sep 20 04:28 .npm
drwxrwxr-x. 1 root root 610 Sep 20 04:28 .nvm
drwxrwxr-x. 1 root root  96 Sep 20 04:28 .rustup
drwxrwxr-x. 1 root root  84 Sep 20 04:28 .sdkman
-rw-rw----. 1 root root 532 Sep 20 04:07 .viminfo
lrwxrwxrwx. 1 root root  21 Sep 20 04:28 .wget-hsts -> ../tooling/.wget-hsts
drwxrwxr-x. 1 root root  12 Sep 20 04:28 go

/home/user/ directory tree

Directory tree created using https://github.com/a8m/tree/tree/master: installed by running go install github.com/a8m/tree/cmd/tree@latest then go clean -cache

Before:

.
├── .bash_logout
├── .bash_profile
├── .bashrc
├── .cache
│   ├── go-build
│   │   ├── README
│   │   └── trim.txt
│   └── pip
│       ├── http
│       └── selfcheck
├── .cargo
│   ├── bin
│   │   ├── cargo
│   │   ├── cargo-clippy
│   │   ├── cargo-fmt
│   │   ├── cargo-miri
│   │   ├── clippy-driver
│   │   ├── rls
│   │   ├── rust-analyzer
│   │   ├── rust-gdb
│   │   ├── rust-gdbgui
│   │   ├── rust-lldb
│   │   ├── rustc
│   │   ├── rustdoc
│   │   ├── rustfmt
│   │   └── rustup
│   └── env
├── .config
│   ├── composer
│   │   ├── keys.dev.pub
│   │   └── keys.tags.pub
│   └── containers
│       └── storage.conf
├── .gitconfig
├── .krew
│   ├── bin
│   │   ├── kubectl-ctx -> /home/user/.krew/store/ctx/v0.9.5/kubectx
│   │   ├── kubectl-krew -> /home/user/.krew/store/krew/v0.4.4/krew
│   │   └── kubectl-ns -> /home/user/.krew/store/ns/v0.9.5/kubens
│   ├── index
│   │   └── default
│   ├── receipts
│   │   ├── ctx.yaml
│   │   ├── krew.yaml
│   │   └── ns.yaml
│   └── store
│       ├── ctx
│       ├── krew
│       └── ns
├── .kubectl_aliases
├── .npm
│   ├── _cacache
│   │   ├── content-v2
│   │   ├── index-v5
│   │   └── tmp
│   ├── _logs
│   │   ├── 2023-07-17T19_53_43_861Z-debug-0.log
│   │   ├── 2023-07-17T19_53_48_751Z-debug-0.log
│   │   ├── 2023-07-17T19_53_49_051Z-debug-0.log
│   │   └── 2023-07-17T19_53_49_684Z-debug-0.log
│   └── _update-notifier-last-checked
├── .nvm
│   ├── .cache
│   │   └── bin
│   ├── .dockerignore
│   ├── .editorconfig
│   ├── .git
│   │   ├── FETCH_HEAD
│   │   ├── HEAD
│   │   ├── branches
│   │   ├── config
│   │   ├── description
│   │   ├── hooks
│   │   ├── index
│   │   ├── info
│   │   ├── logs
│   │   ├── objects
│   │   ├── packed-refs
│   │   ├── refs
│   │   └── shallow
│   ├── .gitattributes
│   ├── .github
│   │   ├── FUNDING.yml
│   │   ├── ISSUE_TEMPLATE.md
│   │   ├── SECURITY.md
│   │   └── workflows
│   ├── .gitignore
│   ├── .mailmap
│   ├── .npmrc
│   ├── .travis.yml
│   ├── CODE_OF_CONDUCT.md
│   ├── CONTRIBUTING.md
│   ├── Dockerfile
│   ├── GOVERNANCE.md
│   ├── LICENSE.md
│   ├── Makefile
│   ├── PROJECT_CHARTER.md
│   ├── README.md
│   ├── ROADMAP.md
│   ├── alias
│   │   ├── default
│   │   └── lts
│   ├── bash_completion
│   ├── install.sh
│   ├── nvm-exec
│   ├── nvm.sh
│   ├── package.json
│   ├── rename_test.sh
│   ├── test
│   │   ├── common.sh
│   │   ├── fast
│   │   ├── install_script
│   │   ├── installation_iojs
│   │   ├── installation_node
│   │   ├── mocks
│   │   ├── slow
│   │   └── sourcing
│   ├── update_test_mocks.sh
│   └── versions
│       └── node
├── .rustup
│   ├── downloads
│   ├── settings.toml
│   ├── tmp
│   ├── toolchains
│   │   └── stable-x86_64-unknown-linux-gnu
│   └── update-hashes
│       └── stable-x86_64-unknown-linux-gnu
├── .sdkman
│   ├── bin
│   │   └── sdkman-init.sh
│   ├── candidates
│   │   ├── gradle
│   │   ├── java
│   │   ├── jbang
│   │   └── maven
│   ├── contrib
│   │   └── completion
│   ├── etc
│   │   └── config
│   ├── ext
│   ├── libexec
│   │   ├── default
│   │   ├── help
│   │   ├── home
│   │   ├── uninstall
│   │   └── version
│   ├── src
│   │   ├── sdkman-availability.sh
│   │   ├── sdkman-cache.sh
│   │   ├── sdkman-config.sh
│   │   ├── sdkman-current.sh
│   │   ├── sdkman-default.sh
│   │   ├── sdkman-env-helpers.sh
│   │   ├── sdkman-env.sh
│   │   ├── sdkman-flush.sh
│   │   ├── sdkman-help.sh
│   │   ├── sdkman-home.sh
│   │   ├── sdkman-install.sh
│   │   ├── sdkman-list.sh
│   │   ├── sdkman-main.sh
│   │   ├── sdkman-offline.sh
│   │   ├── sdkman-path-helpers.sh
│   │   ├── sdkman-selfupdate.sh
│   │   ├── sdkman-uninstall.sh
│   │   ├── sdkman-update.sh
│   │   ├── sdkman-upgrade.sh
│   │   ├── sdkman-use.sh
│   │   ├── sdkman-utils.sh
│   │   └── sdkman-version.sh
│   ├── tmp
│   └── var
│       ├── candidates
│       ├── delay_upgrade
│       ├── metadata
│       ├── platform
│       ├── version
│       └── version_native
├── .wget-hsts
├── .zshrc
└── go
    ├── bin
    │   ├── gopls
    │   └── tree
    └── pkg
        ├── mod
        └── sumdb

76 directories, 111 files

After:

~ $ tree -l -a
.
├── .bash_logout
├── .bash_profile
├── .bashrc
├── .cache
│   ├── go-build
│   │   ├── README -> ../../../tooling/.cache/go-build/README
│   │   └── trim.txt -> ../../../tooling/.cache/go-build/trim.txt
│   └── pip
│       ├── http
│       └── selfcheck
├── .cargo
│   ├── bin
│   │   ├── cargo -> ../../../tooling/.cargo/bin/cargo
│   │   ├── cargo-clippy -> ../../../tooling/.cargo/bin/cargo-clippy
│   │   ├── cargo-fmt -> ../../../tooling/.cargo/bin/cargo-fmt
│   │   ├── cargo-miri -> ../../../tooling/.cargo/bin/cargo-miri
│   │   ├── clippy-driver -> ../../../tooling/.cargo/bin/clippy-driver
│   │   ├── rls -> ../../../tooling/.cargo/bin/rls
│   │   ├── rust-analyzer -> ../../../tooling/.cargo/bin/rust-analyzer
│   │   ├── rust-gdb -> ../../../tooling/.cargo/bin/rust-gdb
│   │   ├── rust-gdbgui -> ../../../tooling/.cargo/bin/rust-gdbgui
│   │   ├── rust-lldb -> ../../../tooling/.cargo/bin/rust-lldb
│   │   ├── rustc -> ../../../tooling/.cargo/bin/rustc
│   │   ├── rustdoc -> ../../../tooling/.cargo/bin/rustdoc
│   │   ├── rustfmt -> ../../../tooling/.cargo/bin/rustfmt
│   │   └── rustup -> ../../../tooling/.cargo/bin/rustup
│   └── env -> ../../tooling/.cargo/env
├── .config
│   ├── composer
│   │   ├── keys.dev.pub -> ../../../tooling/.config/composer/keys.dev.pub
│   │   └── keys.tags.pub -> ../../../tooling/.config/composer/keys.tags.pub
│   └── containers
│       └── storage.conf -> ../../../tooling/.config/containers/storage.conf
├── .gitconfig -> ../tooling/.gitconfig
├── .krew
│   ├── bin
│   │   ├── kubectl-ctx -> ../../../tooling/.krew/bin/kubectl-ctx
│   │   ├── kubectl-krew -> ../../../tooling/.krew/bin/kubectl-krew
│   │   └── kubectl-ns -> ../../../tooling/.krew/bin/kubectl-ns
│   ├── index
│   │   └── default
│   ├── receipts
│   │   ├── ctx.yaml -> ../../../tooling/.krew/receipts/ctx.yaml
│   │   ├── krew.yaml -> ../../../tooling/.krew/receipts/krew.yaml
│   │   └── ns.yaml -> ../../../tooling/.krew/receipts/ns.yaml
│   └── store
│       ├── ctx
│       ├── krew
│       └── ns
├── .kubectl_aliases -> ../tooling/.kubectl_aliases
├── .local
│   └── bin
├── .npm
│   ├── _cacache
│   │   ├── content-v2
│   │   ├── index-v5
│   │   └── tmp
│   ├── _logs
│   │   ├── 2023-09-20T04_14_47_221Z-debug-0.log -> ../../../tooling/.npm/_logs/2023-09-20T04_14_47_221Z-debug-0.log
│   │   ├── 2023-09-20T04_15_31_306Z-debug-0.log -> ../../../tooling/.npm/_logs/2023-09-20T04_15_31_306Z-debug-0.log
│   │   ├── 2023-09-20T04_15_31_572Z-debug-0.log -> ../../../tooling/.npm/_logs/2023-09-20T04_15_31_572Z-debug-0.log
│   │   └── 2023-09-20T04_15_38_586Z-debug-0.log -> ../../../tooling/.npm/_logs/2023-09-20T04_15_38_586Z-debug-0.log
│   └── _update-notifier-last-checked -> ../../tooling/.npm/_update-notifier-last-checked
├── .nvm
│   ├── .cache
│   │   └── bin
│   ├── .dockerignore -> ../../tooling/.nvm/.dockerignore
│   ├── .editorconfig -> ../../tooling/.nvm/.editorconfig
│   ├── .git
│   │   ├── FETCH_HEAD -> ../../../tooling/.nvm/.git/FETCH_HEAD
│   │   ├── HEAD -> ../../../tooling/.nvm/.git/HEAD
│   │   ├── branches
│   │   ├── config -> ../../../tooling/.nvm/.git/config
│   │   ├── description -> ../../../tooling/.nvm/.git/description
│   │   ├── hooks
│   │   ├── index -> ../../../tooling/.nvm/.git/index
│   │   ├── info
│   │   ├── logs
│   │   ├── objects
│   │   ├── packed-refs -> ../../../tooling/.nvm/.git/packed-refs
│   │   ├── refs
│   │   └── shallow -> ../../../tooling/.nvm/.git/shallow
│   ├── .gitattributes -> ../../tooling/.nvm/.gitattributes
│   ├── .github
│   │   ├── FUNDING.yml -> ../../../tooling/.nvm/.github/FUNDING.yml
│   │   ├── ISSUE_TEMPLATE.md -> ../../../tooling/.nvm/.github/ISSUE_TEMPLATE.md
│   │   ├── SECURITY.md -> ../../../tooling/.nvm/.github/SECURITY.md
│   │   └── workflows
│   ├── .gitignore -> ../../tooling/.nvm/.gitignore
│   ├── .mailmap -> ../../tooling/.nvm/.mailmap
│   ├── .npmrc -> ../../tooling/.nvm/.npmrc
│   ├── .travis.yml -> ../../tooling/.nvm/.travis.yml
│   ├── CODE_OF_CONDUCT.md -> ../../tooling/.nvm/CODE_OF_CONDUCT.md
│   ├── CONTRIBUTING.md -> ../../tooling/.nvm/CONTRIBUTING.md
│   ├── Dockerfile -> ../../tooling/.nvm/Dockerfile
│   ├── GOVERNANCE.md -> ../../tooling/.nvm/GOVERNANCE.md
│   ├── LICENSE.md -> ../../tooling/.nvm/LICENSE.md
│   ├── Makefile -> ../../tooling/.nvm/Makefile
│   ├── PROJECT_CHARTER.md -> ../../tooling/.nvm/PROJECT_CHARTER.md
│   ├── README.md -> ../../tooling/.nvm/README.md
│   ├── ROADMAP.md -> ../../tooling/.nvm/ROADMAP.md
│   ├── alias
│   │   ├── default -> ../../../tooling/.nvm/alias/default
│   │   └── lts
│   ├── bash_completion -> ../../tooling/.nvm/bash_completion
│   ├── install.sh -> ../../tooling/.nvm/install.sh
│   ├── nvm-exec -> ../../tooling/.nvm/nvm-exec
│   ├── nvm.sh -> ../../tooling/.nvm/nvm.sh
│   ├── package.json -> ../../tooling/.nvm/package.json
│   ├── rename_test.sh -> ../../tooling/.nvm/rename_test.sh
│   ├── test
│   │   ├── common.sh -> ../../../tooling/.nvm/test/common.sh
│   │   ├── fast
│   │   ├── install_script
│   │   ├── installation_iojs
│   │   ├── installation_node
│   │   ├── mocks
│   │   ├── slow
│   │   └── sourcing
│   ├── update_test_mocks.sh -> ../../tooling/.nvm/update_test_mocks.sh
│   └── versions
│       └── node
├── .rustup
│   ├── downloads
│   ├── settings.toml -> ../../tooling/.rustup/settings.toml
│   ├── tmp
│   ├── toolchains
│   │   └── stable-x86_64-unknown-linux-gnu
│   └── update-hashes
│       └── stable-x86_64-unknown-linux-gnu -> ../../../tooling/.rustup/update-hashes/stable-x86_64-unknown-linux-gnu
├── .sdkman
│   ├── bin
│   │   └── sdkman-init.sh -> ../../../tooling/.sdkman/bin/sdkman-init.sh
│   ├── candidates
│   │   ├── gradle
│   │   ├── java
│   │   ├── jbang
│   │   └── maven
│   ├── contrib
│   │   └── completion
│   ├── etc
│   │   └── config -> ../../../tooling/.sdkman/etc/config
│   ├── ext
│   ├── libexec
│   │   ├── default -> ../../../tooling/.sdkman/libexec/default
│   │   ├── help -> ../../../tooling/.sdkman/libexec/help
│   │   ├── home -> ../../../tooling/.sdkman/libexec/home
│   │   ├── uninstall -> ../../../tooling/.sdkman/libexec/uninstall
│   │   └── version -> ../../../tooling/.sdkman/libexec/version
│   ├── src
│   │   ├── sdkman-availability.sh -> ../../../tooling/.sdkman/src/sdkman-availability.sh
│   │   ├── sdkman-cache.sh -> ../../../tooling/.sdkman/src/sdkman-cache.sh
│   │   ├── sdkman-config.sh -> ../../../tooling/.sdkman/src/sdkman-config.sh
│   │   ├── sdkman-current.sh -> ../../../tooling/.sdkman/src/sdkman-current.sh
│   │   ├── sdkman-default.sh -> ../../../tooling/.sdkman/src/sdkman-default.sh
│   │   ├── sdkman-env-helpers.sh -> ../../../tooling/.sdkman/src/sdkman-env-helpers.sh
│   │   ├── sdkman-env.sh -> ../../../tooling/.sdkman/src/sdkman-env.sh
│   │   ├── sdkman-flush.sh -> ../../../tooling/.sdkman/src/sdkman-flush.sh
│   │   ├── sdkman-help.sh -> ../../../tooling/.sdkman/src/sdkman-help.sh
│   │   ├── sdkman-home.sh -> ../../../tooling/.sdkman/src/sdkman-home.sh
│   │   ├── sdkman-install.sh -> ../../../tooling/.sdkman/src/sdkman-install.sh
│   │   ├── sdkman-list.sh -> ../../../tooling/.sdkman/src/sdkman-list.sh
│   │   ├── sdkman-main.sh -> ../../../tooling/.sdkman/src/sdkman-main.sh
│   │   ├── sdkman-offline.sh -> ../../../tooling/.sdkman/src/sdkman-offline.sh
│   │   ├── sdkman-path-helpers.sh -> ../../../tooling/.sdkman/src/sdkman-path-helpers.sh
│   │   ├── sdkman-selfupdate.sh -> ../../../tooling/.sdkman/src/sdkman-selfupdate.sh
│   │   ├── sdkman-uninstall.sh -> ../../../tooling/.sdkman/src/sdkman-uninstall.sh
│   │   ├── sdkman-update.sh -> ../../../tooling/.sdkman/src/sdkman-update.sh
│   │   ├── sdkman-upgrade.sh -> ../../../tooling/.sdkman/src/sdkman-upgrade.sh
│   │   ├── sdkman-use.sh -> ../../../tooling/.sdkman/src/sdkman-use.sh
│   │   ├── sdkman-utils.sh -> ../../../tooling/.sdkman/src/sdkman-utils.sh
│   │   └── sdkman-version.sh -> ../../../tooling/.sdkman/src/sdkman-version.sh
│   ├── tmp
│   └── var
│       ├── candidates -> ../../../tooling/.sdkman/var/candidates
│       ├── delay_upgrade -> ../../../tooling/.sdkman/var/delay_upgrade
│       ├── metadata
│       ├── platform -> ../../../tooling/.sdkman/var/platform
│       ├── version -> ../../../tooling/.sdkman/var/version
│       └── version_native -> ../../../tooling/.sdkman/var/version_native
├── .viminfo
├── .wget-hsts -> ../tooling/.wget-hsts
└── go
    ├── bin
    │   └── gopls -> ../../../tooling/go/bin/gopls
    └── pkg
        ├── mod
        └── sumdb

78 directories, 110 files

.bashrc

Before:

~ $ cat /home/user/.bashrc
# .bashrc

# Source global definitions
if [ -f /etc/bashrc ]; then
        . /etc/bashrc
fi

# User specific environment
if ! [[ "$PATH" =~ "$HOME/.local/bin:$HOME/bin:" ]]
then
    PATH="$HOME/.local/bin:$HOME/bin:$PATH"
fi
export PATH

# Uncomment the following line if you don't like systemctl's auto-paging feature:
# export SYSTEMD_PAGER=

# User specific aliases and functions
export PS1='\W `git branch --show-current 2>/dev/null | sed -r -e "s@^(.+)@\(\1\) @"`$ '

#THIS MUST BE AT THE END OF THE FILE FOR SDKMAN TO WORK!!!
export SDKMAN_DIR="$HOME/.sdkman"
[[ -s "$HOME/.sdkman/bin/sdkman-init.sh" ]] && source "$HOME/.sdkman/bin/sdkman-init.sh"

export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"  # This loads nvm
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"  # This loads nvm bash_completion
source /usr/share/bash-completion/completions/git
source /usr/share/bash-completion/completions/oc
[ -f ~/.kubectl_aliases ] && source ~/.kubectl_aliases
export PATH="${KREW_ROOT:-$HOME/.krew}/bin:$PATH"

After:

~ $ cat /home/user/.bashrc
# .bashrc

# Source global definitions
if [ -f /etc/bashrc ]; then
        . /etc/bashrc
fi

# User specific environment
if ! [[ "$PATH" =~ "$HOME/.local/bin:$HOME/bin:" ]]
then
    PATH="$HOME/.local/bin:$HOME/bin:$PATH"
fi
export PATH

# Uncomment the following line if you don't like systemctl's auto-paging feature:
# export SYSTEMD_PAGER=

# User specific aliases and functions

Also, here are the 2 new files in /etc/profile.d/:

profile.d $ cat udi_environment.sh 
export NVM_DIR="/home/tooling/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
[ -f ~/.kubectl_aliases ] && source ~/.kubectl_aliases
export PATH="${KREW_ROOT:-$HOME/.krew}/bin:$PATH"
export SDKMAN_DIR="/home/tooling/.sdkman"
[[ -s "$SDKMAN_DIR/bin/sdkman-init.sh" ]] && source "$SDKMAN_DIR/bin/sdkman-init.sh"
profile.d $ cat udi_prompt.sh 
export PS1='\W `git branch --show-current 2>/dev/null | sed -r -e "s@^(.+)@\(\1\) @"`$ '

$PATH

Before:

~ $ echo $PATH | tr : \\n | sort
/bin
/home/user/.cargo/bin
/home/user/.dotnet/tools
/home/user/.krew/bin
/home/user/.krew/bin
/home/user/.local/bin
/home/user/.local/share/coursier/bin
/home/user/.nvm/versions/node/v16.14.0/bin
/home/user/.sdkman/candidates/gradle/current/bin
/home/user/.sdkman/candidates/java/current/bin
/home/user/.sdkman/candidates/jbang/current/bin
/home/user/.sdkman/candidates/maven/current/bin
/home/user/bin
/home/user/go/bin/
/sbin
/usr/bin
/usr/local/bin
/usr/local/sbin
/usr/sbin
/usr/share/Modules/bin

After:

~ $ echo $PATH | tr : \\n | sort
/bin
/home/tooling/.cargo/bin
/home/tooling/.krew/bin
/home/tooling/.local/bin
/home/tooling/.local/share/coursier/bin
/home/tooling/.nvm/versions/node/v16.14.0/bin
/home/tooling/.sdkman/candidates/gradle/current/bin
/home/tooling/.sdkman/candidates/java/current/bin
/home/tooling/.sdkman/candidates/jbang/current/bin
/home/tooling/.sdkman/candidates/maven/current/bin
/home/tooling/go/bin/
/home/user/.dotnet/tools
/home/user/.krew/bin
/home/user/.local/bin
/home/user/.local/bin
/home/user/bin
/sbin
/usr/bin
/usr/local/bin
/usr/local/sbin
/usr/sbin
/usr/share/Modules/bin

@AObuchow
Copy link
Contributor Author

Something to note about using stow: the stow command in the entrypoint adds a bit of delay to the startup of the UDI, which is unfortunate. I haven't yet measured the exact amount of time it takes to startup on my local machine.

@l0rd
Copy link
Contributor

l0rd commented Aug 30, 2023

We should apply the same mechanism to the base image too so that also the tools installed in that Dockerfile assume that /home/tooling is $HOME (and there .bashrc should be created in /home/tooling).

It's surprising that /home/user/.dotnet/tools /home/user/.krew/bin /home/user/.local/bin /home/user/.nvm/version /node/v16.14.0/bin /home/user/bin are in PATH (I was expecting the /home/tooling counterpart)

Something to note about using stow: the stow command in the entrypoint adds a bit of delay to the startup of the UDI, which is unfortunate. I haven't yet measured the exact amount of time it takes to startup on my local machine.

What if we run it in background so that it doesn't block the execution? The side effect may be that commands that follow will not find conf files in /home/user but we can fix the entrypoint to work fine. That makes me think that we can have problems with post run commands....

@AObuchow
Copy link
Contributor Author

We should apply the same mechanism to the base image too so that also the tools installed in that Dockerfile assume that /home/tooling is $HOME (and there .bashrc should be created in /home/tooling).

We could either:

  • set $HOME to /home/tooling/ during the installation of tools in the base image
  • set $HOME to /home/tooling/ and change the useradd step to point to /home/tooling/ instead of /home/user/.
    • The useradd command is creating the default .bashrc in /home/tooling/ which we are copying over to /home/user/.
    • From what I understand, the useradd -d option is only supposed to create the home directory (if it doesn't exist) and set the user's login directory (which we override in our bashrc to point to the projects directory)

Something to note about changing the base image to install to /home/tooling/: the /home/user/.viminfo and /home/user/.gitconfig will be symbolic links located in `/home/tooling/. This means they won't persist if the user modifies them. This should be okay, as users could just overwrite those files with automount configmaps in their workspaces.

It's surprising that /home/user/.dotnet/tools /home/user/.krew/bin /home/user/.local/bin /home/user/.nvm/version /node/v16.14.0/bin /home/user/bin are in PATH (I was expecting the /home/tooling counterpart)

/home/user/.krew/bin /home/user/.local/bin /home/user/.nvm/version /node/v16.14.0/bin /home/user/bin are on PATH because they're coming from the .bashrc and use the $HOME environment variable: when the .bashrc is sourced, $HOME is set to /home/user/.

# User specific environment
if ! [[ "$PATH" =~ "$HOME/.local/bin:$HOME/bin:" ]]
then
    PATH="$HOME/.local/bin:$HOME/bin:$PATH"
fi
export PATH
...
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
...
export PATH="${KREW_ROOT:-$HOME/.krew}/bin:$PATH"

I'm not exactly sure yet why /home/user/.dotnet/tools is on PATH as $HOME is set to /home/tooling/ during the .NET installation step. It's also worth noting that /home/user/.dotnet/ does not exist (even with this PR not applied).

Something to note about using stow: the stow command in the entrypoint adds a bit of delay to the startup of the UDI, which is unfortunate. I haven't yet measured the exact amount of time it takes to startup on my local machine.

What if we run it in background so that it doesn't block the execution? The side effect may be that commands that follow will not find conf files in /home/user but we can fix the entrypoint to work fine. That makes me think that we can have problems with post run commands....

This is a good point - there is no guarantee that the entrypoint is run before postStart commands (see postStart documentation here). This is very problematic IMO.

We could change the stow step to be a default devworkspace postStart command, but that also comes with its own complexity such as verifying we only add the stow postStart command when the UDI is used (and the UDI would not be fully usable outside of Che).

@AObuchow
Copy link
Contributor Author

My latest round of changes involve running the stow command in the actual Dockerfile itself (to prevent unnecessary extra startup time when devEnvironments.persistUserHome is not enabled) and modifying the UDI entrypoint to execute stow as a background process if /home/user/ has been mounted to by the PVC.

@AObuchow
Copy link
Contributor Author

It's surprising that /home/user/.dotnet/tools /home/user/.krew/bin /home/user/.local/bin /home/user/.nvm/version /node/v16.14.0/bin /home/user/bin are in PATH (I was expecting the /home/tooling counterpart)

/home/user/.krew/bin /home/user/.local/bin /home/user/.nvm/version /node/v16.14.0/bin /home/user/bin are on PATH because they're coming from the .bashrc and use the $HOME environment variable: when the .bashrc is sourced, $HOME is set to /home/user/.

@l0rd after some consideration, I believe it's best I modify any additions to the .bashrc so that /home/tooling/ is hard-coded into PATH additions (rather than using $HOME). This way, if a postStart event invokes a command, they will be found on PATH regardless of whether the entrypoint has run or not.

ea0cb13 (#115) should probably be removed, as it causes more problems than it solves.

@spuranam
Copy link

spuranam commented Sep 8, 2023

@AObuchow @l0rd I tested this PR on DevSpaces v3.8, while it works as advertised, i also ran an issue as mentioned above wherein not everything from /home/tooling would be end up /home/user running the following by manually after the devspace instance startup copies the missing items

cd ${DEVWORKSPACE_NAME}
stow . -t /home/user/ -d /home/tooling/ --no-folding --adopt

@AObuchow
Copy link
Contributor Author

@spuranam Thank you for the testing & feedback Satish.

Would you be able to provide a specific example of a file(s) that isn't being linked from /home/tooling to /home/user? This would help me figure out edge-cases that I am currently missing.

Also, are you trying to reference/use these missing files in a postStart command (and potentially running into a race-condition with the entrypoint)?

Lastly, is there a specific reason why you're using the --adopt option when manually invoking stow?

@spuranam
Copy link

spuranam commented Sep 11, 2023

@AObuchow The issue i am running into is that some times all the file /home/tooling would get linked sometime only few files would get linked to /home/user, for example i have seen many time /home/tooling/.sdkman and /home/tooling/.nvm will not get linked.

My current workaround is to manually invoke stow, the only reason to --adopt is to force stow to run again since the run from the container entrypoint failed to link all the files.

I emailed all details needed for you to reproduce my results earlier today @l0rd , i am hoping it would be forward to you.

@AObuchow
Copy link
Contributor Author

AObuchow commented Sep 13, 2023

@spuranam Thank you for the information, @l0rd forwarded me the necessary details, and I was able to reproduce (what I suspect) was the issue. Below are 3 observations I made:

  1. $NVM_DIR and $SDKMAN_DIR should both point to /home/tooling/, i.e.:
$ cat /home/user/.bashrc
...
export NVM_DIR="/home/tooling/.nvm"
[ -s "${NVM_DIR}/nvm.sh" ] && source "${NVM_DIR}/nvm.sh"
export SDKMAN_DIR="/home/tooling/.sdkman"
[[ -s "${SDKMAN_DIR}/bin/sdkman-init.sh" ]] && source "${SDKMAN_DIR}/bin/sdkman-init.sh"

sdkman's init script is not able to find the symbolic links in /home/user/.sdkman/src/ as it only checks for files.

  1. Since postStart commands are run in their own shell using the format /bin/sh/ -c { <commands> }, they don't have access to the shell environment defined in the .bashrc. In the case of nvm and sdkman, both are added onto $PATH through their init scripts in the .bashrc, and thus won't normally be able to run as postStart commands.

A workaround is to add an additional postStart command that sources the .bashrc before the other postStart commands are run.

events:
  postStart:
    - load-environment
    - my-sdkman-command
    - my-nvm-command
commands:
  - id: load-environment
    exec:
      component: tools
      commandLine: source /home/tooling/.bashrc
  - id: my-sdkman-command
    exec:
      component: tools
      commandLine: sdkman --help
  - id: my-nvm-command
    exec:
      component: tools
      commandLine: nvm --help

In a devfile, the order of postStart commands in the devfile is preserved when executing the commands, so all commands that require PATH additions from the .bashrc must come after the load-environment command

Additionally, the load-environment command could be defined in a parent devfile like so, ensuring it is always run before other postStart commands that are defined in the child devfile.

  1. We might not be able to run the stow command as a background process in the entrypoint.sh. I believe this is why the stow command does not consistently complete in linking all files from /home/tooling/ -> /home/user/.

In other words, the entrypoint should be:

# /home/user/ will be mounted to by a PVC if persistUserHome is enabled
if mountpoint -q /home/user/; then
    # Create symbolic links from /home/tooling/ -> /home/user/
-    stow . -t /home/user/ -d /home/tooling/ --no-folding &
+    stow . -t /home/user/ -d /home/tooling/ --no-folding 
fi

I would be happy to submit the required changes as a PR to your reproducer repository if desired, so that you can easily check if my suggestions work for your needs.

@AObuchow
Copy link
Contributor Author

I realized it's actually problematic to have the .viminfo as a symbolic link, as it is supposed to be an actual file by design. I'll have to modify the stow command in the base container slightly to prevent this .viminfo from being stowed.

I'll have to update this PR as soon as I can with changes that address all the issues I've found so far.

@AObuchow
Copy link
Contributor Author

I have updated the PR with my latest changes and rebased it onto the main branch:

  • The .viminfo is now being ignored by stow and being manually copied as an extra step.
  • I've ensured sdkman (and nvm) both load their init scripts from /home/tooling/ to ensure they work properly.
  • The call to stow in the entrypoint is no longer being run as a background process to ensure it finishes successfully before the entrypoint exits.

There still are a few things that I need to address before this PR will no longer be in a draft state:

  1. We should probably modify the /etc/bashrc instead of the ~/.bashrc so that users are free to customize their bashrc without breaking important functionality. This also lets us guarantee that /etc/bashrc will remain the same whenever the UDI is used (i.e. users won't be modifying or overwriting the /etc/bashrc). This is important to ensure sdkman and nvm can be run as postStart commands.
  2. I need to adopt the changes from chore: define user directory for binaries #117 to work with this PR so that the podman wrapper always works as expected.
  3. I added a chown -R 10001 ${HOME}/ in the base image so that later calls to chgrp -R 0 /home/tooling && chmod -R g=u /home/tooling (like here) would work. Doing a recursive chown might be an antipattern, so we could instead chown the individual files.
  4. Since stow is now being installed in the base image (and not the UDI), we probably will want to clean up the dnf cache as was previously being done.

I've pushed to quay.io/aobuchow/universal-developer-image:stow for testing, and this devfile can also be used.

@spuranam
Copy link

@AObuchow Thanks to you i can now confirm the issues i reported earlier have been resolved, with an exception of podman wrapper that you noted above

@AObuchow AObuchow force-pushed the tooling_dir_new branch 2 times, most recently from 4ce203b to b615cc0 Compare September 18, 2023 04:27
@AObuchow
Copy link
Contributor Author

AObuchow commented Sep 18, 2023

@spuranam Glad to hear my changes resolved those issues. I am currently working on fixing the issue with the podman wrapper.

@l0rd I've been trying to test my latest changes (pushed to quay.io/aobuchow/universal-developer-image:stow-podman) by replicating the testing instructions in #107. However, even with the factory link from that PR, I am getting a service account permissions issue on the DevSandbox & Dog Fooding cluster:

Error: pods is forbidden: User "system:serviceaccount:aobuchow-che-1cac83:workspacee2cbcbd7b10147e8-sa" cannot create resource "pods" in API group "" in the namespace "aobuchow-che-1cac83

I've made the following devfile to test out podman run with my latest changes. I also mounted a /home/user/.kube/config to my workspace (with garbage testing data) so that the entrypoint won't abort.

@AObuchow
Copy link
Contributor Author

AObuchow commented Sep 18, 2023

I fixed the issue I was having with the podman wrapper: KUBECONFIG was accidentally pointing to /home/tooling/... instead of /home/user/... (where the user dashboard sets up the kube config).

I was able to do a podman run registry.fedoraproject.org/fedora:latest echo hello in a workspace created from this devfile.

@l0rd if you get a chance, could you please verify the podman wrapper is working as expected? My UDI image with the latest changes is at quay.io/aobuchow/universal-developer-image:stow-podman

@l0rd
Copy link
Contributor

l0rd commented Sep 19, 2023

@AObuchow I have tested your image and the wrapper is working as expected. 👍

@AObuchow
Copy link
Contributor Author

@l0rd I added two commits to address points 1. and 3. from my earlier comment. Point 4. wasn't necessary since we're already cleaning up the dnf cache after installing stow in the base image.

The newest changes have been pushed to quay.io/aobuchow/universal-developer-image:stow, which is used in this devfile.

Something that came up while ensuring the ~/.bashrc is no longer being modifed:

I noticed in the base image's entrypoint, we're creating a ~/.bashrc file manually (i.e. without useradd) in the event the ~/.bashrc doesn't exist when the entrypoint is run. I'm under the impression that this is legacy code from 5efac57 that was made obsolete when the useradd step was added.

However, this is also present in the upstream UDI, so maybe this code is still necessary?

If it's not necessary, I could remove it as part of this PR or in a future PR.

@l0rd
Copy link
Contributor

l0rd commented Sep 20, 2023

However, this is also present in the upstream UDI, so maybe this code is still necessary?

You probably mean downstream right? In any case you are right. In the entrypoints (both upstream and downstream) we should assume ~/.bashrc is there. So creation is not necessary (I fixed a bug about that downstream).

@AObuchow
Copy link
Contributor Author

However, this is also present in the upstream UDI, so maybe this code is still necessary?

You probably mean downstream right? In any case you are right. In the entrypoints (both upstream and downstream) we should assume ~/.bashrc is there. So creation is not necessary (I fixed a bug about that downstream).

Yes I meant the downstream UDI, my mistake.

Sounds good, I added an extra commit to remove the ~/.bashrc creation from the entrypoint.

@AObuchow
Copy link
Contributor Author

AObuchow commented Oct 13, 2023

@l0rd I've squashed all the fixup commits from this PR for when we are ready to merge it

Edit: I forgot to rebase this PR now that #132 has been merged. Should be done before this PR is merged.

All bash-completions located in /usr/share/bash-completion/completions/ are now
enabled by default.

oc and git completions were already being stored in /usr/share/bash-completion/completions/
which is why they are no longer being explicitly sourced in the .bashrc.

Signed-off-by: Andrew Obuchowicz <[email protected]>
Signed-off-by: Andrew Obuchowicz <[email protected]>
Fix eclipse-che/che#22412

When persistUserHome is enabled in the Che Cluster CR, the PVC will be mounted
to /home/user/, overwriting all tools and configuration present in the UDI /home/user/ directory.

To prevent this overwriting, all tools and configurations should be located in /home/tooling/.

To ensure existing workflows still function correctly, symbolic links should be created
to point from /home/tooling/ -> /home/user/. GNU stow is used to manage these symbolic links.
with the --no-folding option enabled, to recreate the directory tree, ensuring all configuration
directories exist in /home/user/ and can be written to.

Signed-off-by: Andrew Obuchowicz <[email protected]>
The .viminfo cannot be a symbolic link for security reasons.
Thus it is ignored by stow and manually copied from /home/tooling/ to /home/user/ instead.

Signed-off-by: Andrew Obuchowicz <[email protected]>
Signed-off-by: Andrew Obuchowicz <[email protected]>
@AObuchow
Copy link
Contributor Author

I rebased my changes and made the appropriate modification for the podman docker alias. This PR should be good to merge finally.

Copy link
Contributor

@l0rd l0rd left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@openshift-ci openshift-ci bot added the lgtm label Oct 16, 2023
@openshift-ci
Copy link

openshift-ci bot commented Oct 16, 2023

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: AObuchow, l0rd

The full list of commands accepted by this bot can be found here.

Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Enabling persistence of home directory breaks UDI home directory, PATH and environment variables
4 participants