Forms the GitHub repository to test; defaults to the current repository.
branch
The branch to test; defaults to the current branch.
tests
The list of tests, as a whitespace-separated glob expression relative to the
tests directory. The .bats
suffix may be omitted on test files.
platforms
A space-separated list of platforms to test on; defaults to everything,
and items may be removed to reduce coverage.
engines
A space-separated list of container engines to test on; defaults to
everything, and items may be removed to reduce coverage.
package-id
A specific GitHub run ID for the
package action
to test. This allows to test code from runs where it failed to build on
platforms that don't need to be tested, or in-process runs as long as the
relevant platforms have already completed.
### Debugging BATS in CI
Sometimes we may need to drill down why a test is failing in CI (for example,
when the same test doesn't fail locally). Some things might be helpful:
- Logs for failing runs can be downloaded by clicking on the :file_folder: icon
in the summary table at the bottom of the run.
- If changes to the application or BATS tests are required, a new [package
action] run will need to be manually triggered. In that case, setting `sign`
to `false` in that run will speed it up by a few minutes, by skipping the
check for properly signed installers — that can be dealt with when the actual
PR is made.
- When focusing on a particular failing platform, it may be possible to shave
off a few minutes by setting the `package-id` field (see above) when starting
the BATS run; this lets you start the run once the platform you're interested
in has completed packaging, without waiting for other platforms. This should
be set to the number after `…/actions/runs/` in the URL.
- When testing, it is a good idea to [fork the repository] and run the tests
there; this lets you have your own set of GitHub runner quota (which means not
waiting for PRs other people create). It is not necessary to set `owner` and
`repo` fields when running the BATS action (because it defaults to the
repository the action is running on). You will, however, need to run the
[package action] at least once in your fork.
- It is much faster to specify `tests`, `platforms`, and `engines` to limit
runs to only the tests you care about; the full run takes somewhere over two
hours total, even spread out over multiple parallel jobs.
[package action]: https://github.com/rancher-sandbox/rancher-desktop/actions/workflows/package.yaml
[fork the repository]: https://github.com/rancher-sandbox/rancher-desktop/fork
================================================
FILE: bats/scripts/bats-lint.pl
================================================
#!/usr/bin/env perl
# This script checks a BATS script to make sure every `run` or `try` call is
# followed by a call to `assert` or `refute`, or a reference to `$output` or
# `$status`. `assert` may be a variable reference like `${assert}`.
#
# The `run` or `try` call may be followed by blank lines or `if ...` statements
# before the assert/refute becomes required.
use strict;
use warnings;
my $problems = 0;
my $run;
my $continue;
while (<>) {
if ($ARGV =~ /\.bats$/) {
# bats files should not override the global setup and teardown functions.
# They should define local_* variants instead, which will be called from
# the global versions.
if (/^((setup|teardown)\w*)\(/) {
print "$ARGV:$.: Don't define $1(); define local_$1() instead\n";
$problems++;
}
if (/\b run \b .* \b load_var \b/x) {
print "$ARGV:$.: Running load_var in a subshell (via run) does not work\n";
$problems++;
}
}
# The semver comparison functions take arguments that are valid semver;
# catch uses of it with invalid versions, like '1.2' instead of '1.2.3'.
if (/
(semver_(?:n?eq|[lg]te?)) # Semver comparison function
[^#\n]* # Eat any number of characters before new line or comment
(?/dev/null; then
echo "This script requires the 'skopeo' utility to be installed"
exit 1
fi
source "$(dirname "${BASH_SOURCE[0]}")/../tests/helpers/images.bash"
# IMAGES is setup by ../tests/helpers/images.bash
# shellcheck disable=SC2153
for IMAGE in "${IMAGES[@]}"; do
echo "===== Copying $IMAGE ====="
skopeo copy --all "docker://$IMAGE" "docker://$GHCR_REPO/$IMAGE"
done
================================================
FILE: bats/tests/compose/compose.bats
================================================
# bats file_tags=opensuse
load '../helpers/load'
local_setup() {
TESTDATA_DIR="${PATH_BATS_ROOT}/tests/compose/testdata/"
TESTDATA_DIR_HOST=$(host_path "$TESTDATA_DIR")
}
@test 'factory reset' {
factory_reset
}
@test 'start container engine' {
start_container_engine
wait_for_container_engine
}
@test 'compose up' {
ctrctl compose --project-directory "$TESTDATA_DIR_HOST" build \
--build-arg IMAGE_NGINX="$IMAGE_NGINX" \
--build-arg IMAGE_PYTHON="$IMAGE_PYTHON_3_9_SLIM"
ctrctl compose --project-directory "$TESTDATA_DIR_HOST" up -d --no-build
}
verify_running_container() {
try --max 9 --delay 10 curl --silent --show-error "$1"
assert_success
assert_output --partial "$2"
}
@test 'verify app bound to localhost' {
verify_running_container "http://localhost:8080" "Welcome to nginx!"
skip_unless_host_ip
run curl --verbose --head "http://${HOST_IP}:8080"
assert_output --partial "curl: (7) Failed to connect"
}
@test 'verify app bound to wildcard IP' {
local expected_output="Hello World!"
verify_running_container "http://localhost:8000" "$expected_output"
skip_unless_host_ip
verify_running_container "http://${HOST_IP}:8000" "$expected_output"
}
@test 'verify connectivity via host.docker.internal' {
local expected_output="Hello World!"
verify_running_container "http://localhost:8080/app" "$expected_output"
}
@test 'compose down' {
run ctrctl compose --project-directory "$TESTDATA_DIR_HOST" down
assert_success
}
================================================
FILE: bats/tests/compose/testdata/Dockerfile.nginx
================================================
ARG IMAGE_NGINX
FROM ${IMAGE_NGINX}
================================================
FILE: bats/tests/compose/testdata/app/Dockerfile
================================================
ARG IMAGE_PYTHON=python:3.9-slim
FROM ${IMAGE_PYTHON}
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["python", "app.py"]
================================================
FILE: bats/tests/compose/testdata/app/app.py
================================================
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello():
return "Hello World!"
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8000)
================================================
FILE: bats/tests/compose/testdata/app/requirements.txt
================================================
flask
================================================
FILE: bats/tests/compose/testdata/compose.yaml
================================================
services:
nginx:
container_name: nginx
build:
args:
- IMAGE_NGINX
dockerfile: Dockerfile.nginx
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
ports:
- '127.0.0.1:8080:80'
web:
build:
args:
- IMAGE_PYTHON
context: app
# flask requires SIGINT to stop gracefully
# (default stop signal from Compose is SIGTERM)
stop_signal: SIGINT
ports:
- '8000:8000'
================================================
FILE: bats/tests/compose/testdata/nginx.conf
================================================
worker_processes 1;
error_log stderr info;
events {
worker_connections 1024;
}
http {
include mime.types;
sendfile on;
proxy_read_timeout 5s;
server {
listen 80;
server_name localhost;
# Serve the default nginx welcome page
location / {
root /usr/share/nginx/html;
index index.html;
}
# Proxy requests to /app to the backend service
location /app {
proxy_pass http://host.docker.internal:8000/;
}
}
}
================================================
FILE: bats/tests/containers/allowed-images.bats
================================================
load '../helpers/load'
RD_USE_IMAGE_ALLOW_LIST=true
@test 'start' {
factory_reset
start_kubernetes
wait_for_container_engine
wait_for_kubelet
}
@test 'update the list of patterns first time' {
update_allowed_patterns true "$IMAGE_NGINX" "$IMAGE_BUSYBOX" "$IMAGE_PYTHON"
}
@test 'verify pull nginx succeeds' {
ctrctl pull --quiet "$IMAGE_NGINX"
}
@test 'verify pull busybox succeeds' {
ctrctl pull --quiet "$IMAGE_BUSYBOX"
}
@test 'verify pull python succeeds' {
ctrctl pull --quiet "$IMAGE_PYTHON"
}
assert_pull_fails() {
run ctrctl pull "$1"
assert_failure
assert_output --regexp "(UNAUTHORIZED|Forbidden)"
}
@test 'verify pull ruby fails' {
try --max 9 --delay 10 assert_pull_fails "$IMAGE_RUBY"
}
@test 'drop python from the allowed-image list, add ruby' {
update_allowed_patterns true "$IMAGE_NGINX" "$IMAGE_BUSYBOX" "$IMAGE_RUBY"
}
@test 'clear images' {
for image in IMAGE_NGINX IMAGE_BUSYBOX IMAGE_PYTHON; do
ctrctl rmi "${!image}"
done
}
@test 'verify pull python fails' {
try --max 9 --delay 10 assert_pull_fails "$IMAGE_PYTHON"
}
@test 'verify pull ruby succeeds' {
# when using VZ and when traefik is enabled, then pulling the image does not always succeed on the first attempt
try --max 9 --delay 10 ctrctl pull --quiet "$IMAGE_RUBY"
}
@test 'clear all patterns' {
update_allowed_patterns true
}
@test 'can run kubectl' {
kubectl run nginx --image="${IMAGE_NGINX}" --port=8080
}
verify_no_nginx() {
run kubectl get pods
assert_success
assert_output --partial "ImagePullBackOff"
}
@test 'but fails to stand up a pod for forbidden image' {
try --max 18 --delay 10 verify_no_nginx
}
@test 'set patterns with the allowed list disabled' {
update_allowed_patterns false "$IMAGE_NGINX" "$IMAGE_BUSYBOX" "$IMAGE_RUBY"
}
@test 'verify pull python succeeds because allowedImages filter is disabled' {
# when using VZ and when traefik is enabled, then pulling the image does not always succeed on the first attempt
try --max 9 --delay 10 ctrctl pull --quiet "$IMAGE_PYTHON"
}
================================================
FILE: bats/tests/containers/auto-start.bats
================================================
load '../helpers/load'
@test 'factory reset' {
factory_reset
}
@test 'Start up Rancher Desktop' {
start_application
}
@test 'Verify that initial Behavior is all set to false' {
run get_setting '.application.autoStart'
assert_success
assert_output false
run get_setting '.application.startInBackground'
assert_success
assert_output false
run get_setting '.application.window.quitOnClose'
assert_success
assert_output false
run get_setting '.application.hideNotificationIcon'
assert_success
assert_output false
}
@test 'Enable auto start' {
rdctl set --application.auto-start=true
run get_setting '.application.autoStart'
assert_success
assert_output true
}
@test 'Verify that the auto-start config is created' {
if using_dev_mode; then
skip "Autostart prefs don't work in dev mode"
fi
if is_linux; then
assert_file_exists "${XDG_CONFIG_HOME:-$HOME/.config}/autostart/rancher-desktop.desktop"
fi
if is_macos; then
assert_file_exists "$HOME/Library/LaunchAgents/io.rancherdesktop.autostart.plist"
fi
if is_windows; then
run powershell.exe -c "reg query HKCU\Software\Microsoft\Windows\CurrentVersion\Run /v RancherDesktop"
assert_success
assert_line --index 2 --partial "\Rancher Desktop.exe"
fi
}
@test 'Disable auto start' {
rdctl set --application.auto-start=false
run get_setting '.application.autoStart'
assert_success
assert_output false
}
@test 'Verify that the auto-start config is removed' {
if using_dev_mode; then
skip "Autostart prefs don't work in dev mode"
fi
if is_linux; then
assert_file_not_exists "${XDG_CONFIG_HOME:-$HOME/.config}/autostart/rancher-desktop.desktop"
fi
if is_macos; then
assert_file_not_exists "$HOME/Library/LaunchAgents/io.rancherdesktop.autostart.plist"
fi
if is_windows; then
run powershell.exe -c "reg query HKCU\Software\Microsoft\Windows\CurrentVersion\Run /v RancherDesktop"
assert_failure
assert_output --partial "The system was unable to find the specified registry"
fi
}
@test 'Enable quit-on-close' {
rdctl set --application.window.quit-on-close=true
run get_setting '.application.window.quitOnClose'
assert_success
assert_output true
}
@test 'Disable quit-on-close' {
rdctl set --application.window.quit-on-close=false
run get_setting '.application.window.quitOnClose'
assert_success
assert_output false
}
@test 'Enable start-in-background' {
rdctl set --application.start-in-background=true
run get_setting '.application.startInBackground'
assert_success
assert_output true
}
@test 'Disable start-in-background' {
rdctl set --application.start-in-background=false
run get_setting '.application.startInBackground'
assert_success
assert_output false
}
@test 'Enable hide-notification-icon' {
rdctl set --application.hide-notification-icon=true
run get_setting '.application.hideNotificationIcon'
assert_success
assert_output true
}
@test 'Disable hide-notification-icon' {
rdctl set --application.hide-notification-icon=false
run get_setting '.application.hideNotificationIcon'
assert_success
assert_output false
}
================================================
FILE: bats/tests/containers/catch-duplicate-api-patterns.bats
================================================
load '../helpers/load'
RD_USE_IMAGE_ALLOW_LIST=true
@test 'catch attempts to add duplicate patterns via the API with enabled on' {
factory_reset
start_kubernetes
wait_for_kubelet
wait_for_container_engine
run update_allowed_patterns true "$IMAGE_NGINX" "$IMAGE_BUSYBOX" "$IMAGE_RUBY" "$IMAGE_BUSYBOX"
assert_failure
assert_output --partial $"field \"containerEngine.allowedImages.patterns\" has duplicate entries: \"$IMAGE_BUSYBOX\""
}
@test 'catch attempts to add duplicate patterns via the API with enabled off' {
run update_allowed_patterns false "$IMAGE_NGINX" "$IMAGE_BUSYBOX" "$IMAGE_RUBY" "$IMAGE_BUSYBOX"
assert_failure
assert_output --partial $"field \"containerEngine.allowedImages.patterns\" has duplicate entries: \"$IMAGE_BUSYBOX\""
}
================================================
FILE: bats/tests/containers/docker-buildx-python3-uname.bats
================================================
load '../helpers/load'
local_setup() {
if ! using_docker; then
skip "This test only applied to the moby container engine"
fi
TEMP=/tmp
if is_windows; then
# We need to use a directory that exists on the Win32 filesystem
# so the docker clients can correctly map the bind mounts.
# We can use host_path() on these paths because they will exist
# both here and in the rancher-desktop distro.
TEMP="$(wslpath_from_win32_env TEMP)"
fi
BUILDX_BUILDER=rd_bats_builder
WORK_DIR="$TEMP/$BUILDX_BUILDER"
BUILDX_INSTANCE=amd64builder
}
@test 'start' {
factory_reset
start_container_engine
wait_for_container_engine
# Do any cleanup from previous runs
run docker buildx rm "$BUILDX_INSTANCE"
assert_nothing
rm -fr "$WORK_DIR"
}
@test 'create the source directory to work in' {
mkdir -p "$WORK_DIR"
cat >"${WORK_DIR}/Dockerfile" <<'EOF'
FROM registry.access.redhat.com/ubi8/python-39:1-57
RUN python3 -m pip install tornado
CMD echo "Running on $(uname -m)"
EOF
}
@test 'build the container' {
docker buildx create --name "$BUILDX_INSTANCE"
docker buildx use "$BUILDX_INSTANCE"
cd "$WORK_DIR"
docker buildx build -t testbuild:00 --platform linux/amd64 --load .
run docker run --platform linux/amd64 testbuild:00
assert_success
assert_output "Running on x86_64"
}
================================================
FILE: bats/tests/containers/factory-reset-containerd-shims.bats
================================================
load '../helpers/load'
BOGUS_SHIM="${PATH_CONTAINERD_SHIMS}/containerd-shim-bogus-v1"
local_setup_file() {
RD_USE_RAMDISK=false # interferes with deleting $PATH_APP_HOME
delete_all_snapshots
rm -rf "$PATH_CONTAINERD_SHIMS"
}
local_teardown_file() {
rm -rf "$PATH_CONTAINERD_SHIMS"
}
@test 'factory reset' {
# On Windows the cache directory is under PATH_APP_HOME.
factory_reset --cache
assert_not_exists "$PATH_APP_HOME"
}
@test 'factory reset will not remove any shims' {
assert_not_exists "$PATH_CONTAINERD_SHIMS"
create_file "$BOGUS_SHIM" <<<''
factory_reset
assert_exists "$BOGUS_SHIM"
assert_exists "$PATH_APP_HOME"
}
@test 'factory reset will remove empty shim directory' {
rm "$BOGUS_SHIM"
factory_reset
assert_not_exists "$PATH_CONTAINERD_SHIMS"
assert_not_exists "$PATH_APP_HOME"
}
================================================
FILE: bats/tests/containers/factory-reset-snapshots.bats
================================================
load '../helpers/load'
local_setup_file() {
RD_USE_RAMDISK=false # interferes with deleting $PATH_APP_HOME
}
@test 'factory reset' {
delete_all_snapshots
rm -rf "$PATH_CONTAINERD_SHIMS"
# On Windows the cache directory is under PATH_APP_HOME.
factory_reset --cache
}
@test 'Start up Rancher Desktop with a snapshots subdirectory' {
start_container_engine
wait_for_container_engine
wait_for_backend
}
@test "Verify the snapshot dir isn't deleted on factory-reset" {
rdctl shutdown
rdctl snapshot create shortlived-snapshot
factory_reset --cache
assert_not_exists "$PATH_APP_HOME/rd-engine.json"
assert_exists "$PATH_SNAPSHOTS"
run ls -A "$PATH_SNAPSHOTS"
assert_output
}
@test 'Verify factory-reset deletes an empty snapshots directory' {
rdctl snapshot delete shortlived-snapshot
factory_reset --cache
assert_not_exists "$PATH_APP_HOME"
}
================================================
FILE: bats/tests/containers/factory-reset.bats
================================================
load '../helpers/load'
local_setup_file() {
RD_USE_RAMDISK=false # interferes with deleting $PATH_APP_HOME
}
@test 'factory reset' {
factory_reset
}
@test 'Start up Rancher Desktop' {
start_application
}
@test 'Verify that the expected directories were created' {
before check_directories
}
@test 'Verify that docker symlinks were created' {
before check_docker_symlinks
}
@test 'Verify that path management was set' {
before check_path
}
@test 'Verify that rancher desktop context was created' {
before check_rd_context
}
@test 'Verify that lima VM was created' {
before check_lima
}
@test 'Verify that WSL distributions were created' {
before check_WSL
}
@test 'Shutdown Rancher Desktop' {
rdctl shutdown
}
@test 'factory-reset when Rancher Desktop is not running' {
touch_updater_longhorn
rdctl_factory_reset --verbose
}
@test 'Verify that the expected directories were deleted' {
check_directories
}
@test 'Verify that docker symlinks were deleted' {
check_docker_symlinks
}
@test 'Verify that path management was unset' {
check_path
}
@test 'Verify that rancher desktop context was deleted' {
check_rd_context
}
@test 'Verify that lima VM was deleted' {
check_lima
}
@test 'Verify that WSL distributions were deleted' {
check_WSL
}
@test 'Verify updater-longhorn.json was deleted' {
check_updater_longhorn_gone
}
@test 'Start Rancher Desktop 2' {
start_application
}
@test 'factory reset - keep cached k8s images' {
rdctl_factory_reset --remove-kubernetes-cache=false --verbose
}
@test 'Verify that the expected directories were deleted 2' {
check_directories
}
@test 'Verify that docker symlinks were deleted 2' {
check_docker_symlinks
}
@test 'Verify that path management was unset 2' {
check_path
}
@test 'Verify that rancher desktop context was deleted 2' {
check_rd_context
}
@test 'Verify that lima VM was deleted 2' {
check_lima
}
@test 'Verify that WSL distributions were deleted 2' {
check_WSL
}
@test 'Verify updater-longhorn.json was deleted 2' {
check_updater_longhorn_gone
}
@test 'Start Rancher Desktop 3' {
start_application
}
@test 'factory reset - delete cached k8s images' {
rdctl_factory_reset --remove-kubernetes-cache=true --verbose
}
@test 'Verify that the expected directories were deleted 3' {
check_directories
}
@test 'Verify that docker symlinks were deleted 3' {
check_docker_symlinks
}
@test 'Verify that path management was unset 3' {
check_path
}
@test 'Verify that rancher desktop context was deleted 3' {
check_rd_context
}
@test 'Verify that lima VM was deleted 3' {
check_lima
}
@test 'Verify that WSL distributions were deleted 3' {
check_WSL
}
@test 'Verify updater-longhorn.json was deleted when cache was retained' {
check_updater_longhorn_gone
}
rdctl_factory_reset() {
capture_logs
rdctl factory-reset "$@"
if [[ $1 == "--remove-kubernetes-cache=true" ]]; then
assert_not_exist "$PATH_CACHE"
else
assert_exists "$PATH_CACHE"
fi
}
check_directories() {
# Check if all expected directories are created after starting application/ are deleted after a factory reset
delete_dir=("$PATH_LOGS" "$PATH_APP_HOME/credential-server.json" "$PATH_APP_HOME/rd-engine.json")
if is_unix; then
# On Windows "$PATH_CONFIG" == "$PATH_APP_HOME"
delete_dir+=("$HOME/.rd" "$LIMA_HOME" "$PATH_CONFIG")
# We can't make any general assertion on AppHome/snapshots - we don't know if it was created or not
# So just assert on the other members of AppHome
# TODO on macOS (not implemented by `rdctl factory-reset`)
# ~/Library/Saved Application State/io.rancherdesktop.app.savedState
# this one only exists after an update has been downloaded
# ~/Library/Application Support/Caches/rancher-desktop-updater
fi
if is_windows; then
# On Windows $PATH_CONFIG is the same as $PATH_APP_HOME
delete_dir+=("$PATH_CONFIG_FILE" "$PATH_DISTRO" "$PATH_DISTRO_DATA")
# TODO: What about $PATH_APP_HOME/vtunnel-config.yaml ?
fi
for dir in "${delete_dir[@]}"; do
echo "# $assert that $dir does not exist" 1>&3
"${assert}_not_exists" "$dir"
done
}
check_docker_symlinks() {
skip_on_windows
# Check if docker-X symlinks were deleted
for dfile in docker-buildx docker-compose; do
run readlink "$HOME/.docker/cli-plugins/$dfile"
"${refute}_output" "$HOME/.rd/bin/$dfile"
done
}
check_path() {
skip_on_windows
# Check if ./rd/bin was removed from the path
# TODO add check for config.fish
env_profiles=(
"$HOME/.bashrc"
"$HOME/.zshrc"
"$HOME/.cshrc"
"$HOME/.tcshrc"
)
for candidate in .bash_profile .bash_login .profile; do
if [ -e "$HOME/$candidate" ]; then
env_profiles+=("$HOME/$candidate")
# Only the first candidate that exists will be modified
if [ "${assert}" = "refute" ]; then
break
fi
fi
done
for profile in "${env_profiles[@]}"; do
echo "$assert that $profile does not add ~/.rd/bin to the PATH"
# cshrc: setenv PATH "/Users/jan/.rd/bin"\:"$PATH"
# posix: export PATH="/Users/jan/.rd/bin:$PATH"
run grep "PATH.\"$HOME/.rd/bin" "$profile"
"${assert}_failure"
done
}
check_rd_context() {
skip_on_windows
# Check if the rancher-desktop docker context has been removed
if using_docker; then
echo "$assert that the docker context rancher-desktop does not exist"
run grep -r rancher-desktop "$HOME/.docker/contexts/meta"
"${assert}_failure"
fi
}
check_lima() {
skip_on_windows
# Check that the VM has been removed and no longer exists.
run limactl ls
"${assert}_output" --regexp "No instance found|no such file or directory"
}
check_WSL() {
skip_on_unix
# Check if rancher-desktop WSL distros are deleted on Windows
run powershell.exe -c "wsl.exe --list"
"${refute}_output" --partial "rancher-desktop-data"
"${refute}_output" --partial "rancher-desktop"
}
check_updater_longhorn_gone() {
assert_not_exists "$PATH_CACHE/updater-longhorn.json"
}
touch_updater_longhorn() {
touch "$PATH_CACHE/updater-longhorn.json"
}
================================================
FILE: bats/tests/containers/host-connectivity.bats
================================================
# bats file_tags=opensuse
load '../helpers/load'
@test 'factory reset' {
factory_reset
}
@test 'start container engine' {
start_container_engine
wait_for_container_engine
}
verify_host_connectivity() {
run ctrctl run --rm "$IMAGE_BUSYBOX" timeout -s INT 10 ping -c 5 "$1"
assert_success
assert_output --partial "5 packets transmitted, 5 packets received, 0% packet loss"
}
@test 'ping host.docker.internal from a container' {
verify_host_connectivity "host.docker.internal"
}
@test 'ping host.rancher-desktop.internal from a container' {
verify_host_connectivity "host.rancher-desktop.internal"
}
================================================
FILE: bats/tests/containers/host-network-ports.bats
================================================
# bats file_tags=opensuse
load '../helpers/load'
LOCALHOST="127.0.0.1"
local_setup() {
if ! is_windows; then
skip "The test doesn't work on non-Windows platforms"
fi
}
@test 'factory reset' {
factory_reset
}
@test 'start container engine' {
start_container_engine
wait_for_container_engine
}
run_container_with_host_network_driver() {
local image="python:slim"
ctrctl pull --quiet "$image"
ctrctl run -d --network=host --restart=no "$image" "$@"
}
verify_container_port() {
run try --max 9 --delay 10 curl --insecure --verbose --show-error "$@"
assert_success
assert_output --partial 'Directory listing for'
}
@test 'process is bound to 0.0.0.0 using host network driver' {
local container_port="8010"
run_container_with_host_network_driver python -m http.server "$container_port"
verify_container_port "http://$LOCALHOST:$container_port"
skip_unless_host_ip
verify_container_port "http://${HOST_IP}:$container_port"
}
@test 'process is bound to 127.0.0.1 using host network driver' {
local container_port="8016"
run_container_with_host_network_driver python -m http.server $container_port --bind "$LOCALHOST"
verify_container_port "http://$LOCALHOST:$container_port"
skip_unless_host_ip
run curl --verbose --head "http://${HOST_IP}:$container_port"
assert_output --partial "curl: (7) Failed to connect"
}
================================================
FILE: bats/tests/containers/init.bats
================================================
# verify that running a container with --init is working
# bats file_tags=opensuse
load '../helpers/load'
@test 'factory reset' {
factory_reset
}
@test 'start container engine' {
start_container_engine
wait_for_container_engine
}
@test 'run container with init process' {
# BUG BUG BUG
# The following `ctrctl run` command includes the `-i` option to work around a docker
# bug on Windows: https://github.com/rancher-sandbox/rancher-desktop/issues/3239
# It is harmless in other configurations, but should not be required here.
# BUG BUG BUG
run ctrctl run -i --rm --init "$IMAGE_BUSYBOX" ps -ef
assert_success
# PID USER TIME COMMAND
# 1 root 0:00 /sbin/docker-init -- ps -ef
# 1 root 0:00 /sbin/tini -- ps -ef
assert_line --regexp '^ +1 .+ /sbin/(docker-init|tini) -- ps -ef$'
}
================================================
FILE: bats/tests/containers/platform.bats
================================================
load '../helpers/load'
@test 'factory reset' {
factory_reset
}
@test 'start container engine' {
start_container_engine
wait_for_container_engine
}
check_uname() {
local platform="linux/$1"
local cpu="$2"
# Pull container separately because `ctrctl run` doesn't have a --quiet option
ctrctl pull --quiet --platform "$platform" "$IMAGE_BUSYBOX"
# BUG BUG BUG
# Adding -i option to work around a bug with the Linux docker CLI in WSL
# https://github.com/rancher-sandbox/rancher-desktop/issues/3239
# BUG BUG BUG
run ctrctl run -i --platform "$platform" "$IMAGE_BUSYBOX" uname -m
if is_true "${assert_success:-true}"; then
assert_success
assert_output "$cpu"
fi
}
@test 'deploy amd64 container' {
check_uname amd64 x86_64
}
@test 'deploy arm64 container' {
if is_windows; then
# TODO why don't we do this?
skip "aarch64 emulation is not included in the Windows version"
fi
check_uname arm64 aarch64
}
@test 'uninstall s390x emulator' {
if is_windows; then
# On WSL the emulator might still be installed from a previous run
ctrctl run --privileged --rm "$IMAGE_TONISTIIGI_BINFMT" --uninstall qemu-s390x
else
skip "only required on Windows"
fi
}
@test 'deploy s390x container does not work' {
assert_success=false check_uname s390x s390x
assert_failure
assert_output --partial "exec /bin/uname: exec format error"
}
@test 'install s390x emulator' {
ctrctl run --privileged --rm "$IMAGE_TONISTIIGI_BINFMT" --install s390x
}
@test 'deploy s390x container' {
check_uname s390x s390x
}
================================================
FILE: bats/tests/containers/published-ports.bats
================================================
# bats file_tags=opensuse
load '../helpers/load'
@test 'factory reset' {
factory_reset
}
@test 'start container engine' {
start_container_engine
wait_for_container_engine
}
run_container_with_published_port() {
ctrctl pull --quiet "$IMAGE_NGINX"
ctrctl run -d -p "$@" --restart=no "$IMAGE_NGINX"
}
verify_container_published_port() {
run try --max 9 --delay 10 curl --insecure --verbose --show-error "$@"
assert_success
assert_output --partial 'Welcome to nginx!'
}
@test 'container published port binding to localhost' {
run_container_with_published_port "127.0.0.1:8080:80"
verify_container_published_port "http://127.0.0.1:8080"
}
@test 'container published port binding to localhost should not be accessible via 0.0.0.0' {
skip_unless_host_ip
run curl --verbose --head "http://${HOST_IP}:8080"
assert_output --partial "curl: (7) Failed to connect"
}
@test 'container published port binding to 0.0.0.0' {
skip_unless_host_ip
run_container_with_published_port "8081:80"
verify_container_published_port "http://${HOST_IP}:8081"
}
================================================
FILE: bats/tests/containers/published-udp-ports.bats
================================================
load '../helpers/load'
local_setup() {
skip_on_unix
}
@test 'factory reset' {
factory_reset
}
build_alpine_socat_image() {
cat <&3
"${assert}_not_exists" "$dir"
done
if is_false "${CACHE:-1}"; then
echo "# assert that cache does not exist" >&3
assert_not_exists "$PATH_CACHE"
else
echo "# assert that cache does exists" >&3
assert_exists "$PATH_CACHE"
fi
}
check_docker_symlinks() {
skip_on_windows
# Check if docker-X symlinks were deleted
for dfile in docker-buildx docker-compose; do
run readlink "$HOME/.docker/cli-plugins/$dfile"
"${refute:?}_output" "$HOME/.rd/bin/$dfile"
done
}
check_path() {
skip_on_windows
# Check if ./rd/bin was removed from the path
# TODO add check for config.fish
env_profiles=(
"$HOME/.bashrc"
"$HOME/.zshrc"
"$HOME/.cshrc"
"$HOME/.tcshrc"
)
for candidate in .bash_profile .bash_login .profile; do
if [ -e "$HOME/$candidate" ]; then
env_profiles+=("$HOME/$candidate")
# Only the first candidate that exists will be modified
if [ "${assert}" = "refute" ]; then
break
fi
fi
done
for profile in "${env_profiles[@]}"; do
echo "$assert that $profile does not add ~/.rd/bin to the PATH"
# cshrc: setenv PATH "/Users/jan/.rd/bin"\:"$PATH"
# posix: export PATH="/Users/jan/.rd/bin:$PATH"
run grep "PATH.\"$HOME/.rd/bin" "$profile"
"${assert}_failure"
done
}
check_rd_context() {
skip_on_windows
# Check if the rancher-desktop docker context has been removed
if using_docker; then
echo "$assert that the docker context rancher-desktop does not exist"
run grep -r rancher-desktop "$HOME/.docker/contexts/meta"
"${assert}_failure"
fi
}
check_lima() {
skip_on_windows
# Check that the VM has been removed and no longer exists.
run limactl ls
"${assert}_output" --regexp "No instance found|no such file or directory"
}
check_WSL() {
skip_on_unix
# Check if rancher-desktop WSL distros are deleted on Windows
run powershell.exe -c "wsl.exe --list"
"${refute}_output" --partial "rancher-desktop-data"
"${refute}_output" --partial "rancher-desktop"
}
check_updater_longhorn_gone() {
assert_not_exists "$PATH_CACHE/updater-longhorn.json"
}
touch_updater_longhorn() {
touch "$PATH_CACHE/updater-longhorn.json"
}
================================================
FILE: bats/tests/containers/run-rancher.bats
================================================
# bats file_tags=opensuse
load '../helpers/load'
RD_FILE_RAMDISK_SIZE=12 # We need more disk to run the Rancher image.
@test 'factory reset' {
factory_reset
}
@test 'start container engine' {
start_container_engine
wait_for_container_engine
}
@test 'run rancher' {
local rancher_image
rancher_image="rancher/rancher:$(rancher_image_tag)"
ctrctl pull --quiet "$rancher_image"
ctrctl run --privileged -d --restart=no -p 8080:80 -p 8443:443 --name rancher "$rancher_image"
}
@test 'verify rancher' {
local max_tries=9
if [[ -n ${CI:-} ]]; then
max_tries=30
fi
run try --max $max_tries --delay 10 curl --insecure --silent --show-error "https://localhost:8443/dashboard/auth/login"
assert_success
assert_output --partial "Rancher Dashboard"
run ctrctl logs rancher
assert_success
assert_output --partial "Bootstrap Password:"
}
================================================
FILE: bats/tests/containers/split-dns-vpn.bats
================================================
# bats file_tags=opensuse
load '../helpers/load'
REGISTRY_URL=$(echo "$RD_VPN_TEST_IMAGE" | cut -d'/' -f1)
local_setup() {
if ! using_vpn_test_image; then
skip "This test requires a connection to the designated VPN."
fi
}
@test 'factory reset' {
factory_reset
}
@test 'start container engine' {
start_container_engine
wait_for_container_engine
}
@test 'Can access private registry over VPN from host' {
run curl -I -k "https://$REGISTRY_URL/v2"
assert_success
# We avoid assert_line here due to the trailing carriage return (\r) issues.
assert_output --partial "docker-distribution-api-version: registry/2.0"
}
@test 'Can pull image from private registry over VPN' {
run ctrctl pull --quiet "$RD_VPN_TEST_IMAGE"
assert_success
}
@test 'Can verify container access to the registry' {
run ctrctl run --rm "$IMAGE_NGINX" curl -I -k "https://$REGISTRY_URL/v2"
assert_success
assert_output --partial "docker-distribution-api-version: registry/2.0"
}
@test 'Verify that a container can ping host.rancher-desktop.internal when the VPN is enabled' {
run ctrctl run --rm "$IMAGE_BUSYBOX" timeout -s INT 10 ping -c 5 host.rancher-desktop.internal
assert_success
assert_output --partial "5 packets transmitted, 5 packets received, 0% packet loss"
}
================================================
FILE: bats/tests/containers/switch-engines.bats
================================================
# Test case 20
load '../helpers/load'
RD_CONTAINER_ENGINE=moby
switch_container_engine() {
local name=$1
RD_CONTAINER_ENGINE="${name}"
# Make sure the backend is idle, to prevent wait_for_container_engine from
# erroring because the wrong engine is up.
wait_for_backend
rdctl set --container-engine.name="${name}"
wait_for_container_engine
}
pull_containers() {
ctrctl pull --quiet "$IMAGE_NGINX"
ctrctl pull --quiet "$IMAGE_BUSYBOX"
ctrctl run -d -p 8085:80 --restart=no "$IMAGE_NGINX"
ctrctl run -d --restart=always "$IMAGE_BUSYBOX" /bin/sh -c "sleep inf"
run ctrctl ps --format '{{json .Image}}'
assert_output --partial "$IMAGE_NGINX"
assert_output --partial "$IMAGE_BUSYBOX"
}
@test 'factory reset' {
factory_reset
}
@test 'start moby and pull nginx' {
start_container_engine
wait_for_container_engine
pull_containers
}
@test "switch to containerd" {
switch_container_engine containerd
pull_containers
}
verify_post_switch_containers() {
run ctrctl ps --format '{{json .Image}}'
assert_output --partial "$IMAGE_BUSYBOX"
refute_output --partial "$IMAGE_NGINX"
}
switch_back_verify_post_switch_containers() {
local name=$1
switch_container_engine "${name}"
try --max 12 --delay 5 verify_post_switch_containers
}
@test 'switch back to moby and verify containers' {
switch_back_verify_post_switch_containers moby
}
@test 'switch back to containerd and verify containers' {
switch_back_verify_post_switch_containers containerd
}
================================================
FILE: bats/tests/containers/volumes.bats
================================================
load '../helpers/load'
get_tempdir() {
if ! is_windows || ! using_windows_exe; then
echo "$BATS_TEST_TMPDIR"
return
fi
# On Windows, create a temporary directory that is in the Windows temporary
# directory so that it mounts correctly. Note that in CI we end up running
# with PSModulePath set to pwsh (7.x) paths, and that breaks the code for
# PowerShell 5.1. So we need to have alternative code in that case.
# See also https://github.com/PowerShell/PowerShell/issues/14100
if command -v pwsh.exe &>/dev/null; then
# shellcheck disable=SC2016 # Don't expand PowerShell expansion
local command='
$([System.IO.Directory]::CreateTempSubdirectory()).FullName
'
run pwsh.exe -Command "$command"
assert_success
else
# PowerShell 5.1 is built against .net Framework 4.x and doesn't have
# [System.IO.Directory]::CreateTempSubdirectory(); create a temporary
# file and use its name instead.
# shellcheck disable=SC2016 # Don't expand PowerShell expansion
local command='
$name = New-TemporaryFile
Remove-Item -Path $name
# In case anti-virus etc. holds files open, wait for a second to let
# things settle before we create a new directory with the same name.
Start-Sleep -Seconds 1
New-Item -Type Directory -Path $name | Out-Null
$name.FullName
'
run powershell.exe -Command "$command"
assert_success
fi
run wslpath -u "$output"
assert_success
echo "$output" | tr -d "\r"
}
local_setup() {
run get_tempdir
assert_success
export WORK_PATH=$output
run host_path "$WORK_PATH"
assert_success
export HOST_WORK_PATH=$output
export EXPECT_FAILURE=false
}
local_teardown() {
# Only do manual deletion on Windows; elsewhere we use BATS_TEST_TMPDIR so
# BATS is expected to do the cleanup.
if is_windows && [[ -n $HOST_WORK_PATH ]]; then
powershell.exe -Command "Remove-Item -Recurse -LiteralPath '$HOST_WORK_PATH'"
fi
}
known_failure_on_mount_type() {
local mount_type=$1
local actual_type=$RD_MOUNT_TYPE
if is_windows; then
if using_windows_exe; then
actual_type=win32
else
actual_type=wsl
fi
fi
if [ "$actual_type" = "$mount_type" ]; then
comment "Test is known to fail on $RD_MOUNT_TYPE mounts"
assert=refute
refute=assert
EXPECT_FAILURE=true
fi
}
@test 'factory reset' {
factory_reset
}
@test 'start container engine' {
if is_linux; then
# On linux, mount BATS_RUN_TMPDIR into the VM so that we can use
# BATS_TEST_TMPDIR as a volume.
local override_dir="${HOME}/.local/share/rancher-desktop/lima/_config"
mkdir -p "$override_dir"
{
echo "mounts:"
echo "- location: ${BATS_RUN_TMPDIR}"
echo " writable: true"
} >"$override_dir/override.yaml"
fi
start_container_engine
wait_for_container_engine
}
@test 'read-only volume mount' {
# Read a file that was created outside the container.
file_name=foo
file_path=$WORK_PATH/$file_name
file_content=hello
assert_not_exists "$file_path"
create_file "$file_path" <<<$file_content
# Use `--separate-stderr` to avoid image pull messages.
run --separate-stderr \
ctrctl run --volume "$HOST_WORK_PATH:/mount:ro" \
"$IMAGE_BUSYBOX" cat /mount/$file_name
assert_success
assert_output $file_content
}
@test 'read-write volume mount' {
file_name=foo
file_path=$WORK_PATH/$file_name
file_content=hello
# Create a file from the container.
assert_not_exists "$file_path"
ctrctl run --volume "$HOST_WORK_PATH:/mount:rw" \
"$IMAGE_BUSYBOX" sh -c "echo $file_content > /mount/$file_name"
# Check that the file was written to.
assert_file_contains "$file_path" $file_content
}
@test 'read-write single file using --mount' {
file_name=foo
file_content=hello
create_file "$WORK_PATH/$file_name" <<<$file_content
run --separate-stderr \
ctrctl run --mount "source=$HOST_WORK_PATH/$file_name,target=/mount,type=bind" \
"$IMAGE_BUSYBOX" cat /mount
assert_success
assert_output $file_content
}
@test 'read-write volume mount as user' {
known_failure_on_mount_type 9p
file_name=foo
file_contents=hello
host_file_path=$HOST_WORK_PATH/$file_name
# Create a file from within the container.
run ctrctl run --volume "$HOST_WORK_PATH:/mount:rw" \
--user 1000:1000 "$IMAGE_BUSYBOX" sh -c "echo $file_contents > /mount/$file_name"
"${assert}_success"
run cat "$WORK_PATH/$file_name"
"${assert}_success"
if is_true "$EXPECT_FAILURE"; then
skip "Test expected to fail"
fi
assert_output $file_contents
# Try to append to the file.
ctrctl run --volume "$HOST_WORK_PATH:/mount:rw" \
--user 1000:1000 "$IMAGE_BUSYBOX" sh -c "echo $file_contents | tee -a /mount/$file_name"
# Check that the file was modified.
run cat "$WORK_PATH/$file_name"
assert_success
assert_output $file_contents$'\n'$file_contents
if is_windows && using_windows_exe; then
# On Windows, the directory may be owned by a group that the user is in;
# additionally, there isn't an easy API to get effective access (!?).
if command -v pwsh.exe &>/dev/null; then
# shellcheck disable=SC2016 # Don't expand PowerShell expansion
local command='
$typeName = "System.Security.Principal.SecurityIdentifier, System.Security.Principal.Windows"
$type = [System.Type]::GetType($typeName)
$owner = $(Get-Acl '"'$host_file_path'"').GetOwner($type)
$owner.Value
'
run pwsh.exe -Command "$command"
assert_success
else
# shellcheck disable=SC2016 # Don't expand PowerShell expansion
local command='
$type = [System.Type]::GetType("System.Security.Principal.SecurityIdentifier")
$owner = $(Get-Acl '"'$host_file_path'"').GetOwner($type)
$owner.Value
'
run powershell.exe -Command "$command"
assert_success
fi
local undo
undo=$(shopt -p extglob || true)
shopt -s extglob
local owner=${output%%*([[:space:]])}
eval "$undo"
# shellcheck disable=SC2016 # Don't expand PowerShell expansion
command='
$identity = [System.Security.Principal.WindowsIdentity]::GetCurrent()
$groups = $identity.Groups
$groups.Add($identity.User)
$groups | ForEach-Object { $_.Value }
'
run powershell.exe -Command "$command"
assert_success
run cat <<<"${output//$'\r'/}" # Remove carriage returns
assert_success
assert_line "$owner"
else
# Check that the file is owned by the current user.
stat_arg=-f # Assume BSD stat
if { stat --version || true; } | grep 'GNU coreutils'; then
stat_arg=-c
fi
run stat "$stat_arg" '%u:%g' "$WORK_PATH/foo"
assert_success
assert_output "$(id -u):$(id -g)"
fi
}
@test 'host directory does not exist' {
if using_docker; then
known_failure_on_mount_type reverse-sshfs
known_failure_on_mount_type 9p
fi
file_name=foo
dir_name=baz
file_contents=hello
# Create a file from the container.
assert_not_exists "$WORK_PATH/$dir_name"
run ctrctl run --volume "$HOST_WORK_PATH/$dir_name:/mount:rw" \
"$IMAGE_BUSYBOX" sh -c "echo $file_contents > /mount/$file_name"
"${assert}_success"
# Check that the file was written to.
if is_true "$EXPECT_FAILURE"; then
assert_file_not_exists "$WORK_PATH/$dir_name"
else
assert_file_exists "$WORK_PATH/$dir_name/$file_name"
assert_file_contains "$WORK_PATH/$dir_name/$file_name" $file_contents
fi
}
@test 'directory contains space' {
dir_name="hello world"
file_name=foo
file_contents=hello
assert_not_exists "$WORK_PATH/$dir_name"
mkdir "$WORK_PATH/$dir_name"
ctrctl run --volume "$HOST_WORK_PATH/$dir_name:/mount:rw" \
"$IMAGE_BUSYBOX" sh -c "echo $file_contents > /mount/$file_name"
assert_file_exists "$WORK_PATH/$dir_name/$file_name"
assert_file_contains "$WORK_PATH/$dir_name/$file_name" $file_contents
}
@test 'directory contains non-ascii' {
dir_name=snow☃︎man
file_name=foo
file_contents=hello
assert_not_exists "$WORK_PATH/$dir_name"
mkdir "$WORK_PATH/$dir_name"
ctrctl run --volume "$HOST_WORK_PATH/$dir_name:/mount:rw" \
"$IMAGE_BUSYBOX" sh -c "echo $file_contents > /mount/$file_name"
assert_file_exists "$WORK_PATH/$dir_name/$file_name"
assert_file_contains "$WORK_PATH/$dir_name/$file_name" "$file_contents"
}
@test 'directory should be owned by current user' {
known_failure_on_mount_type virtiofs
known_failure_on_mount_type 9p
known_failure_on_mount_type reverse-sshfs
known_failure_on_mount_type win32
user_id=3678:2974
run --separate-stderr \
ctrctl run --volume "$HOST_WORK_PATH:/mount:ro" \
--user $user_id "$IMAGE_BUSYBOX" stat -c '%u:%g' /mount
assert_success
"${assert}_output" $user_id
}
@test 'change ownership of mounted file' {
known_failure_on_mount_type reverse-sshfs
known_failure_on_mount_type 9p
file_name=foo
file_contents=hello
run ctrctl run --volume "$HOST_WORK_PATH:/mount:rw" \
--user 0 "$IMAGE_BUSYBOX" \
sh -c "echo $file_contents > /mount/$file_name; chown 1234:5678 /mount/$file_name"
"${assert}_success"
assert_file_exists "$WORK_PATH/$file_name"
assert_file_contains "$WORK_PATH/$file_name" "$file_contents"
}
@test 'change file permissions' {
file_name=foo
assert_not_exists "$WORK_PATH/$file_name"
local command="
touch /mount/$file_name
chmod 0755 /mount/$file_name
stat -c %A /mount/$file_name
"
run --separate-stderr \
ctrctl run --volume "$HOST_WORK_PATH:/mount:rw" \
"$IMAGE_BUSYBOX" sh -c "$command"
assert_success
"${assert}_output" -rwxr-xr-x # spellcheck-ignore-line
}
================================================
FILE: bats/tests/containers/wasm.bats
================================================
# shellcheck disable=SC2030,SC2031
# See https://github.com/koalaman/shellcheck/issues/2431
# https://www.shellcheck.net/wiki/SC2030 -- Modification of output is local (to subshell caused by @bats test)
# https://www.shellcheck.net/wiki/SC2031 -- output was modified in a subshell. That change might be lost
load '../helpers/load'
# Bundled shims are this version or newer.
BUNDLED_VERSION=0.11.1
# Manually managed versions intentionally use an older version
# so we can verify that they still override the bundled version.
MANUAL_VERSION=0.10.0
local_setup() {
if using_containerd; then
skip "this test only works on moby right now"
fi
skip "spin shim is broken with docker 28+; see #9476"
}
local_teardown_file() {
rm -rf "$PATH_CONTAINERD_SHIMS"
}
@test 'factory reset' {
factory_reset
rm -rf "$PATH_CONTAINERD_SHIMS"
}
@test 'start engine without wasm support' {
start_container_engine --experimental.container-engine.web-assembly.enabled=false
wait_for_container_engine
}
shim_version() {
local shim=$1
local version=$2
run rdctl shell "containerd-shim-${shim}-${version}" -v
assert_success
semver "$output"
}
@test 'verify spin shim is not installed on PATH' {
run shim_version spin v2
assert_failure
assert_output --regexp 'containerd-shim-spin-v2.*(not found|No such file)'
}
hello() {
local shim=$1
local version=$2
local lang=$3
local port=$4
local internal_port=$5
# The '/' at the very end of the command is required by the container entrypoint.
ctrctl run \
--detach \
--name "${shim}-demo-${port}" \
--runtime "io.containerd.${shim}.${version}" \
--platform wasi/wasm \
--publish "${port}:${internal_port}" \
"ghcr.io/deislabs/containerd-wasm-shims/examples/${shim}-${lang}-hello:v${MANUAL_VERSION}" /
}
@test 'verify shim is not configured in container engine' {
run hello spin v2 rust 8080 80
assert_nothing # We assert after removing the container.
ctrctl rm --force spin-demo-8080 || true # Force delete the container if it got created.
assert_failure
assert_output --regexp 'operating system is not supported|binary not installed'
}
@test 'enable wasm support' {
pid=$(get_service_pid "$CONTAINER_ENGINE_SERVICE")
rdctl set --experimental.container-engine.web-assembly.enabled
try --max 15 --delay 5 refute_service_pid "$CONTAINER_ENGINE_SERVICE" "$pid"
wait_for_container_engine
}
@test "check spin shim version >= ${BUNDLED_VERSION}" {
run shim_version spin v2
assert_success
semver_gte "$output" "$BUNDLED_VERSION"
}
@test 'deploy sample spin app' {
hello spin v2 rust 8080 80
}
check_container_logs() {
run ctrctl logs spin-demo-8080
assert_success
assert_output --partial "Available Routes"
}
@test 'check wasm container logs' {
try --max 5 --delay 2 check_container_logs
}
@test 'verify wasm container is running' {
run curl --silent --fail http://localhost:8080/hello
assert_success
assert_output --partial "Hello world from Spin!"
run curl --silent --fail http://localhost:8080/go-hello
assert_success
assert_output --partial "Hello Spin Shim!"
}
download_shim() {
local shim=$1
local version=$2
local base_url="https://github.com/deislabs/containerd-wasm-shims/releases/download/v${MANUAL_VERSION}"
local filename="containerd-wasm-shims-${version}-${shim}-linux-${ARCH}.tar.gz"
local host_archive
# Since we end up using curl.exe on Windows, pass the host path to curl.
host_archive=$(host_path "${PATH_CONTAINERD_SHIMS}/${filename}")
mkdir -p "$PATH_CONTAINERD_SHIMS"
curl --location --output "$host_archive" "${base_url}/${filename}"
tar xfz "${PATH_CONTAINERD_SHIMS}/${filename}" --directory "$PATH_CONTAINERD_SHIMS"
rm "${PATH_CONTAINERD_SHIMS}/${filename}"
}
@test 'install user-managed shims' {
download_shim spin v2
download_shim wws v1
rdctl shutdown
launch_the_application
wait_for_container_engine
}
verify_shim() {
local shim=$1
local version=$2
local lang=$3
local port=$4
local external_port=$5
run shim_version "${shim}" "${version}"
assert_success
semver_eq "$output" "$MANUAL_VERSION"
hello "$shim" "$version" "$lang" "$port" "$external_port"
try --max 10 --delay 3 curl --silent --fail "http://localhost:${port}/hello"
}
@test 'verify spin shim' {
verify_shim spin v2 rust 8181 80
assert_output --partial "Hello world from Spin!"
}
@test 'verify wws shim' {
verify_shim wws v1 js 8282 3000
assert_output --partial "Hello from Wasm Workers Server"
}
================================================
FILE: bats/tests/extensions/allow-list.bats
================================================
load '../helpers/load'
local_setup() {
CONTAINERD_NAMESPACE=rancher-desktop-extensions
TESTDATA_DIR_HOST=$(host_path "${PATH_BATS_ROOT}/tests/extensions/testdata/")
}
write_allow_list() { # list
local list=${1:-}
local allowed=true
if [ -z "$list" ]; then
allowed=false
fi
# Note that the list preference is not writable using `rdctl set`, and we
# need to do a direct API call instead.
rdctl api /v1/settings --input - <<<'{
"version": 8,
"application": {
"extensions": {
"allowed": {
"enabled": '"${allowed}"',
"list": '"${list:-[]}"'
}
}
}
}'
}
check_extension_installed() { # refute, name
run rdctl extension ls
assert_success
"${1:-assert}_output" --partial "${2:-rd/extension/basic}"
}
@test 'factory reset' {
factory_reset
}
@test 'start container engine' {
start_container_engine
wait_for_container_engine
}
@test 'build extension testing image' {
ctrctl build \
--tag "rd/extension/basic" \
--build-arg "variant=basic" \
"$TESTDATA_DIR_HOST"
run ctrctl image list --format '{{ .Repository }}'
assert_success
assert_line "rd/extension/basic"
}
@test 'when no extension allow list is set up, all extensions can install' {
wait_for_extension_manager
write_allow_list ''
rdctl extension install rd/extension/basic
check_extension_installed
rdctl extension uninstall rd/extension/basic
}
@test 'empty allow list disables extension installs' {
write_allow_list '[]'
run rdctl extension install rd/extension/basic
assert_failure
check_extension_installed refute
}
@test 'when an extension is explicitly allowed, it can be installed' {
write_allow_list '["irrelevant/image","rd/extension/basic:latest"]'
rdctl extension install rd/extension/basic:latest
check_extension_installed
rdctl extension uninstall rd/extension/basic
check_extension_installed refute
}
@test 'when an extension is not in the allowed list, it cannot be installed' {
write_allow_list '["rd/extension/other","registry.test/image"]'
run rdctl extension install rd/extension/basic
assert_failure
check_extension_installed refute
}
@test 'when no tags given, any tag is allowed' {
write_allow_list '["rd/extension/basic"]'
ctrctl tag rd/extension/basic rd/extension/basic:0.0.3
rdctl extension install rd/extension/basic:0.0.3
check_extension_installed
rdctl extension uninstall rd/extension/basic
check_extension_installed refute
}
@test 'when tags are given, only the specified tag is allowed' {
sleep 20
write_allow_list '["rd/extension/basic:0.0.2"]'
ctrctl tag rd/extension/basic rd/extension/basic:0.0.3
run rdctl extension install rd/extension/basic:0.0.3
assert_failure
check_extension_installed refute
}
@test 'extensions can be allowed by organization' {
write_allow_list '["rd/extension/"]'
rdctl extension install rd/extension/basic
check_extension_installed
rdctl extension uninstall rd/extension/basic
check_extension_installed refute
}
@test 'extensions can be allowed by repository host' {
write_allow_list '["registry.test/"]'
ctrctl tag rd/extension/basic registry.test/basic:0.0.3
rdctl extension install registry.test/basic:0.0.3
check_extension_installed '' registry.test/basic
rdctl extension uninstall registry.test/basic
check_extension_installed refute registry.test/basic
}
================================================
FILE: bats/tests/extensions/containers.bats
================================================
load '../helpers/load'
local_setup() {
CONTAINERD_NAMESPACE=rancher-desktop-extensions
TESTDATA_DIR_HOST=$(host_path "${PATH_BATS_ROOT}/tests/extensions/testdata/")
}
local_teardown_file() {
if using_docker; then
docker context use default
docker context rm bats-invalid-context
fi
}
id() { # variant
echo "rd/extension/$1"
}
encoded_id() { # variant
id "$1" | tr -d '\r\n' | base64 | tr '+/' '-_' | tr -d '='
}
@test 'factory reset' {
factory_reset
}
@test 'start container engine' {
start_container_engine
wait_for_container_engine
}
@test 'default to custom docker context' {
if ! using_docker; then
skip 'docker context only applies when using docker backend'
fi
# Remove the context if it previously existed.
run docker context rm --force bats-invalid-context
assert_nothing
docker context create bats-invalid-context --docker 'host=tcp://invalid.test:99999'
docker context use bats-invalid-context
}
@test 'no extensions installed' {
wait_for_extension_manager
run rdctl api /v1/extensions
assert_success
assert_output $'\x7b'$'\x7d' # empty JSON dict, {}
assert_dir_not_exist "$PATH_EXTENSIONS"
}
@test 'build extension testing images' {
local extension
for extension in vm-image vm-compose; do
ctrctl build \
--tag rd/extension/$extension \
--build-arg variant=$extension "$TESTDATA_DIR_HOST"
done
}
@test 'image - install' {
rdctl api --method=POST "/v1/extensions/install?id=$(id vm-image)"
run rdctl api /v1/extensions
assert_success
run jq_output ".[\"$(id vm-image)\"].version"
assert_output latest
}
@test 'image - check for running container' {
run ctrctl container ls
assert_success
assert_line --regexp "$(id vm-image).*[[:space:]]Up[[:space:]]"
}
@test 'image - uninstall' {
rdctl api --method=POST "/v1/extensions/uninstall?id=$(id vm-image)"
run ctrctl container ls --all
assert_success
refute_line --partial "$(id vm-image)"
}
@test 'compose - install' {
rdctl api --method=POST "/v1/extensions/install?id=$(id vm-compose)"
run rdctl api /v1/extensions
assert_success
run jq_output ".[\"$(id vm-compose)\"].version"
assert_output latest
}
@test 'compose - check for running container' {
run ctrctl container ls
assert_success
assert_line --regexp "$(id vm-compose).*[[:space:]]Up[[:space:]]"
}
@test 'compose - check for dangling symlinks' {
if ! using_containerd; then
skip 'This test only applies to containerd'
fi
assert_exists "$PATH_EXTENSIONS/$(encoded_id vm-compose)/compose/link"
assert_not_exists "$PATH_EXTENSIONS/$(encoded_id vm-compose)/compose/dangling-link"
}
@test 'compose - uninstall' {
rdctl api --method=POST "/v1/extensions/uninstall?id=$(id vm-compose)"
run ctrctl container ls --all
assert_success
refute_line --partial "$(id vm-compose)"
}
@test 'compose - with a long name' {
local name
name="$(id vm-compose)-with-an-unusually-long-name-yes-it-is-very-long"
ctrctl tag "$(id vm-compose)" "$name"
rdctl extension install "$name"
run ctrctl container ls --all
assert_success
assert_line --partial "$(id vm-compose)"
rdctl extension uninstall "$name"
}
================================================
FILE: bats/tests/extensions/install.bats
================================================
load '../helpers/load'
local_setup() {
CONTAINERD_NAMESPACE=rancher-desktop-extensions
TESTDATA_DIR="${PATH_BATS_ROOT}/tests/extensions/testdata/"
TESTDATA_DIR_HOST=$(host_path "$TESTDATA_DIR")
}
assert_file_contents_equal() { # $have $want
local have="$1" want="$2"
assert_file_exist "$have"
assert_file_exist "$want"
local have_hash want_hash
# md5sum is not available on macOS unless you install GNU coreutils
have_hash="$(openssl md5 -r "$have" | cut -d ' ' -f 1)"
want_hash="$(openssl md5 -r "$want" | cut -d ' ' -f 1)"
if [ "$have_hash" != "$want_hash" ]; then
printf "expected : %s (%s)\nactual : %s (%s)" \
"$want" "$want_hash" "$have" "$have_hash" |
batslib_decorate "files are different" |
fail
fi
}
id() { # variant
echo "rd/extension/$1"
}
encoded_id() { # variant
id "$1" | tr -d '\r\n' | base64 | tr '+/' '-_' | tr -d '='
}
@test 'factory reset' {
factory_reset
}
@test 'start container engine' {
start_container_engine
wait_for_container_engine
wait_for_extension_manager
}
@test 'no extensions installed' {
run rdctl extension ls
assert_success
assert_output "No extensions are installed."
assert_dir_not_exist "$PATH_EXTENSIONS"
}
@test 'build various extension testing images' {
local extension
local variants=(
basic host-binaries missing-icon missing-icon-file ui
)
for extension in "${variants[@]}"; do
ctrctl build \
--tag "rd/extension/$extension" \
--build-arg "variant=$extension" \
"$TESTDATA_DIR_HOST"
done
run ctrctl image list --format '{{ .Repository }}'
assert_success
for extension in "${variants[@]}"; do
assert_line "rd/extension/$extension"
done
}
@test 'extension API - require auth' {
local port
run cat "${PATH_APP_HOME}/rd-engine.json"
assert_success
port="$(jq_output .port)"
assert [ -n "$port" ]
run curl --fail "http://127.0.0.1:${port}/v1/settings"
assert_failure
assert_output --partial "The requested URL returned error: 401"
}
@test 'basic extension - install' {
assert_dir_not_exist "$PATH_EXTENSIONS/$(encoded_id basic)"
rdctl extension install "$(id basic)"
}
@test 'basic extension - check extension is installed' {
run rdctl extension ls
assert_success
assert_line --partial "rd/extension/basic"
}
@test 'basic extension - check extension contents' {
assert_dir_exist "$PATH_EXTENSIONS/$(encoded_id basic)"
assert_file_contents_equal "$PATH_EXTENSIONS/$(encoded_id basic)/icon.svg" "$TESTDATA_DIR/extension-icon.svg"
}
@test 'basic extension - upgrades' {
local tag
ctrctl image tag "$(id basic)" "$(id basic):0.0.1"
ctrctl image tag "$(id basic)" "$(id basic):v0.0.2"
run rdctl extension ls
assert_success
assert_line --partial "$(id basic):latest"
rdctl extension install "$(id basic)"
run rdctl extension ls
assert_success
# The highest semver tag should be installed, replacing the existing one.
assert_line --partial "$(id basic):v0.0.2"
}
@test 'basic extension - uninstalling not installed version' {
rdctl extension uninstall "$(id basic):0.0.1"
run rdctl extension ls
assert_success
# Trying to uninstall a version that isn't installed should be a no-op
assert_line --partial "$(id basic):v0.0.2"
}
@test 'basic extension - uninstall' {
ctrctl image tag "$(id basic)" "$(id basic):0.0.3"
# Uninstall should remove whatever version is installed, not the newest.
rdctl extension uninstall "$(id basic)"
run rdctl extension ls
assert_success
assert_output "No extensions are installed."
assert_dir_not_exist "$PATH_EXTENSIONS/$(encoded_id basic)"
}
@test 'missing-icon - attempt to install' {
assert_dir_not_exist "$PATH_EXTENSIONS/$(encoded_id missing-icon)"
run rdctl extension install "$(id missing-icon)"
assert_failure
assert_output --partial "has invalid extension metadata"
assert_dir_not_exist "$PATH_EXTENSIONS/$(encoded_id missing-icon)"
}
@test 'missing-icon-file - attempt to install' {
assert_dir_not_exist "$PATH_EXTENSIONS/$(encoded_id missing-icon-file)"
run rdctl extension install "$(id missing-icon-file)"
assert_failure
assert_output --partial "Could not copy icon file does-not-exist.svg"
assert_dir_not_exist "$PATH_EXTENSIONS/$(encoded_id missing-icon-file)"
}
@test 'host-binaries - install' {
assert_dir_not_exist "$PATH_EXTENSIONS/$(encoded_id host-binaries)"
run rdctl extension install "$(id host-binaries)"
assert_success
}
@test 'host-binaries - check extension is installed' {
run rdctl extension ls
assert_success
assert_output --partial "rd/extension/host-binaries:latest"
}
@test 'host-binaries - check files' {
assert_dir_exist "$PATH_EXTENSIONS/$(encoded_id host-binaries)"
if is_windows; then
assert_file_exist "$PATH_EXTENSIONS/$(encoded_id host-binaries)/bin/dummy.exe"
assert_file_not_exist "$PATH_EXTENSIONS/$(encoded_id host-binaries)/bin/dummy.sh"
else
assert_file_exist "$PATH_EXTENSIONS/$(encoded_id host-binaries)/bin/dummy.sh"
assert_file_not_exist "$PATH_EXTENSIONS/$(encoded_id host-binaries)/bin/dummy.exe"
fi
}
@test 'host-binaries - upgrade' {
# We test upgrades with host-binaries as there was a bug about reinstalling
# an extension with host binaries.
ctrctl image tag "$(id host-binaries)" "$(id host-binaries):0.0.1"
ctrctl image tag "$(id host-binaries)" "$(id host-binaries):v0.0.2"
run rdctl extension ls
assert_success
assert_line --partial "$(id host-binaries):latest"
rdctl extension install "$(id host-binaries)"
run rdctl extension ls
assert_success
# The highest semver tag should be installed, replacing the existing one.
assert_line --partial "$(id host-binaries):v0.0.2"
}
@test 'host-binaries - uninstalling not installed version' {
rdctl extension uninstall "$(id host-binaries):0.0.1"
run rdctl extension ls
assert_success
# Trying to uninstall a version that isn't installed should be a no-op
assert_line --partial "$(id host-binaries):v0.0.2"
}
@test 'host-binaries - uninstall' {
ctrctl image tag "$(id host-binaries)" "$(id host-binaries):0.0.3"
# Uninstall should remove whatever version is installed, not the newest.
rdctl extension uninstall "$(id host-binaries)"
run rdctl extension ls
assert_success
assert_output "No extensions are installed."
assert_dir_not_exist "$PATH_EXTENSIONS/$(encoded_id host-binaries)"
}
@test 'ui - install' {
assert_dir_not_exist "$PATH_EXTENSIONS/$(encoded_id ui)"
run rdctl extension install "$(id ui)"
assert_success
}
@test 'ui - check files' {
assert_file_exist "$PATH_EXTENSIONS/$(encoded_id ui)/ui/dashboard-tab/ui/index.html"
}
@test 'ui - uninstall' {
rdctl extension uninstall "$(id ui)"
run rdctl extension ls
assert_success
assert_output "No extensions are installed."
assert_dir_not_exist "$PATH_EXTENSIONS/$(encoded_id ui)"
}
================================================
FILE: bats/tests/extensions/testdata/Dockerfile
================================================
FROM registry.suse.com/bci/golang:latest AS builder
WORKDIR /usr/src/app
COPY bin/dummy.go .
ENV GOOS=windows
RUN go build -o /dummy.exe -ldflags '-s -w' dummy.go
FROM registry.suse.com/bci/golang:latest AS server-builder
WORKDIR /usr/src/app
COPY bin/server.go .
ENV GOOS=linux
RUN go build -o /server -ldflags '-s -w' server.go
FROM registry.suse.com/bci/bci-minimal:16.0
ARG variant=basic
ADD ${variant}.json /metadata.json
ADD extension-icon.svg /extension-icon.svg
ADD ui /ui/
ADD bin /bin/
COPY --from=builder /dummy.exe /bin/
COPY --from=server-builder /server /bin/
ADD compose.yaml /compose/
RUN ln -s does/not/exist /compose/dangling-link
RUN ln -s compose.yaml /compose/link
ENTRYPOINT ["/bin/server"]
================================================
FILE: bats/tests/extensions/testdata/Makefile
================================================
all: \
image-basic image-missing-icon image-ui \
image-vm-image image-vm-compose image-host-binaries image-host-apis
TOOL ?= docker
NAMESPACE := $(if $(filter %nerdctl,${TOOL}),--namespace=rancher-desktop-extensions)
NAMESPACE = $(if $(filter %nerdctl,${TOOL}),--namespace=rancher-desktop-extensions)
image-%:
${TOOL} ${NAMESPACE} build -t rd/extension/$(@:image-%=%) --build-arg variant=$(@:image-%=%) .
================================================
FILE: bats/tests/extensions/testdata/README.md
================================================
This directory contains sample docker extensions.
### basic
A basic extension, containing the bare minimum (just an icon).
### missing-icon
As above, but even the icon is missing.
### missing-icon-file
Like the basic extension, but the icon file specified does not exist.
### ui
Presents basic UI. We do not yet test interaction with UI.
### vm-image
Contains a docker image to run.
### vm-compose
Contains a docker compose file to run. (Not supported.)
### host-binaries
Contains binaries to be copied to the host.
================================================
FILE: bats/tests/extensions/testdata/basic.json
================================================
{
"icon": "extension-icon.svg"
}
================================================
FILE: bats/tests/extensions/testdata/bin/dummy.go
================================================
package main
import (
"context"
"log"
"os"
"os/exec"
)
func main() {
ctx := context.Background()
cmd := exec.CommandContext(ctx, os.Args[1], os.Args[2:]...)
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Run()
if exitError, ok := err.(*exec.ExitError); ok {
if exitError.ExitCode() > -1 {
os.Exit(exitError.ExitCode())
}
}
if err != nil {
log.Fatal(err)
}
}
================================================
FILE: bats/tests/extensions/testdata/bin/dummy.sh
================================================
#!/bin/sh
exec "$@"
================================================
FILE: bats/tests/extensions/testdata/bin/server.go
================================================
// command server listens on the Unix socket `/run/guest-services/hello.sock`
// (see `everything.json`) to exercise the ability for the front end to talk to
// the back end.
package main
import (
"context"
"encoding/json"
"errors"
"fmt"
"io"
"log/slog"
"net"
"net/http"
"os"
"os/signal"
"strconv"
"syscall"
)
const (
addr = "/run/guest-services/hello.sock"
)
// Listen on a port and return the listener
func listen() (net.Listener, error) {
err := os.Remove(addr)
if err != nil && !errors.Is(err, os.ErrNotExist) {
slog.Error("failed to remove old socket", "socket", addr, "error", err)
}
listener, err := net.Listen("unix", addr)
if err == nil {
return listener, nil
}
listener, err = net.Listen("tcp", "")
if err != nil {
return nil, fmt.Errorf("failed to listen on fallback TCP: %w", err)
}
return listener, nil
}
// Handle HTTP POST requests
func handlePost(w http.ResponseWriter, req *http.Request) {
data := map[string]any{"headers": req.Header}
body, err := io.ReadAll(req.Body)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
_, _ = io.WriteString(w, fmt.Sprintf("failed to read body: %s", err))
return
}
data["body"] = string(body)
encoder := json.NewEncoder(w)
encoder.SetIndent("", " ")
if err := encoder.Encode(data); err != nil {
// This ends up after partially written JSON, but that's the best we can do
// and should still show up in the result.
_, _ = io.WriteString(w, fmt.Sprintf("failed to encode response: %w", err))
}
}
// Handle POST returning given status
func handleWithStatus(w http.ResponseWriter, req *http.Request) {
statusText := req.PathValue("status")
statusCode, err := strconv.Atoi(statusText)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
_, _ = fmt.Fprintf(w, "failed to parse status %s", statusText)
return
}
w.WriteHeader(statusCode)
_, _ = fmt.Fprintf(w, "returning status code %d", statusCode)
}
func main() {
listener, err := listen()
if err != nil {
slog.Error("failed to listen", "error", err)
os.Exit(1)
}
http.DefaultServeMux.Handle("GET /get/", http.StripPrefix("/get/", http.FileServer(http.Dir("/"))))
http.DefaultServeMux.HandleFunc("POST /post", handlePost)
http.DefaultServeMux.HandleFunc("/status/{status}", handleWithStatus)
server := &http.Server{}
ch := make(chan os.Signal)
errCh := make(chan error)
signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM)
go func() {
<-ch
errCh <- server.Shutdown(context.Background())
}()
slog.Info("Serving HTTP", "address", listener.Addr().String())
err = server.Serve(listener)
if err != nil && !errors.Is(err, http.ErrServerClosed) {
slog.Error("server closed", "error", err)
os.Exit(1)
}
if err = <-errCh; err != nil {
slog.Error("failed to shutdown server", "error", err)
os.Exit(1)
}
}
================================================
FILE: bats/tests/extensions/testdata/compose.yaml
================================================
name: sample-compose
services:
backend-service:
image: "${DESKTOP_PLUGIN_IMAGE}"
================================================
FILE: bats/tests/extensions/testdata/everything.json
================================================
{
"icon": "extension-icon.svg",
"host": {
"binaries": [
{
"darwin": [
{
"path": "/bin/dummy.sh"
}
],
"windows": [
{
"path": "/bin/dummy.exe"
}
],
"linux": [
{
"path": "/bin/dummy.sh"
}
]
}
]
},
"ui": {
"dashboard-tab": {
"title": "Sample Extension With Everything",
"root": "/ui",
"src": "index.html",
"backend": {
"socket": "hello.sock"
}
}
},
"vm": {
"composefile": "/compose/compose.yaml",
"exposes": {
"socket": "hello.sock"
}
}
}
================================================
FILE: bats/tests/extensions/testdata/host-apis.json
================================================
{
"info": "This is used to do manual testing of various host APIs",
"icon": "extension-icon.svg",
"ui": {
"dashboard-tab": {
"title": "RDX Host-APIs Test",
"root": "/ui",
"src": "host-apis.html"
}
}
}
================================================
FILE: bats/tests/extensions/testdata/host-binaries.json
================================================
{
"icon": "extension-icon.svg",
"host": {
"binaries": [
{
"darwin": [
{
"path": "/bin/dummy.sh"
}
],
"windows": [
{
"path": "/bin/dummy.exe"
}
],
"linux": [
{
"path": "/bin/dummy.sh"
}
]
}
]
}
}
================================================
FILE: bats/tests/extensions/testdata/missing-icon-file.json
================================================
{
"icon": "does-not-exist.svg",
"info": "This extension uses an icon that does not exist."
}
================================================
FILE: bats/tests/extensions/testdata/missing-icon.json
================================================
{
"info": "This extension definition is missing an icon."
}
================================================
FILE: bats/tests/extensions/testdata/ui/host-apis.html
================================================
Rancher Desktop Extensions Host API Testing
Open external
Clicking the button should open the Rancher Desktop home page in a browser.
Select file
Clicking on the button should show a file open dialog.
Dialog Output
cancelled
file paths
Toast
Clicking on each of the buttons should pop up a notification where the
notification title matches the type clicked on.
================================================
FILE: bats/tests/extensions/testdata/ui/index.html
================================================
Test Extension
Test Extension
This is a test extension.
================================================
FILE: bats/tests/extensions/testdata/ui.json
================================================
{
"icon": "extension-icon.svg",
"ui": {
"dashboard-tab": {
"title": "Sample Extension",
"root": "/ui",
"src": "index.html"
}
}
}
================================================
FILE: bats/tests/extensions/testdata/vm-compose.json
================================================
{
"icon": "extension-icon.svg",
"info": "This image uses vm.composefile",
"vm": {
"composefile": "/compose/compose.yaml"
}
}
================================================
FILE: bats/tests/extensions/testdata/vm-image.json
================================================
{
"icon": "extension-icon.svg",
"info": "This extension contains a reference to an image.",
"vm": {
"image": "rd/extension/vm-image"
}
}
================================================
FILE: bats/tests/helpers/commands.bash
================================================
EXE=""
PLATFORM=$OS
if is_windows; then
PLATFORM=linux
if using_windows_exe; then
EXE=".exe"
PLATFORM=win32
fi
fi
if using_containerd; then
CONTAINER_ENGINE_SERVICE=containerd
else
CONTAINER_ENGINE_SERVICE=docker
fi
if is_macos; then
CRED_HELPER="$PATH_RESOURCES/$PLATFORM/bin/docker-credential-osxkeychain"
elif is_linux; then
if command -v pass; then
CRED_HELPER="$PATH_RESOURCES/$PLATFORM/bin/docker-credential-pass"
else
CRED_HELPER="$PATH_RESOURCES/$PLATFORM/bin/docker-credential-secretservice"
fi
elif is_windows; then
# Our docker-cli for WSL defaults to "wincred.exe" as well
CRED_HELPER="$PATH_RESOURCES/win32/bin/docker-credential-wincred.exe"
fi
if is_windows; then
RD_DOCKER_CONTEXT=default
else
RD_DOCKER_CONTEXT=rancher-desktop
fi
CONTAINERD_NAMESPACE=default
WSL_DISTRO=rancher-desktop
no_cr() {
tr -d '\r'
}
ctrctl() {
if using_docker; then
docker "$@"
else
nerdctl "$@"
fi
}
curl() {
command "curl$EXE" "$@"
}
docker() {
docker_exe --context $RD_DOCKER_CONTEXT "$@"
}
docker_exe() {
# Add path to bundled credential helpers to the front of the PATH; also
# ensure that on Windows, it gets exported.
PATH="$PATH_RESOURCES/$PLATFORM/bin:$PATH" WSLENV="PATH/l:${WSLENV:-}" \
"$PATH_RESOURCES/$PLATFORM/bin/docker$EXE" "$@" | no_cr
}
helm() {
# Add path to bundled credential helpers to the front of the PATH; also
# ensure that on Windows, it gets exported.
PATH="$PATH_RESOURCES/$PLATFORM/bin:$PATH" WSLENV="PATH/l:${WSLENV:-}" \
"$PATH_RESOURCES/$PLATFORM/bin/helm$EXE" --kube-context rancher-desktop "$@" | no_cr
}
kubectl() {
kubectl_exe --context rancher-desktop "$@"
}
kubectl_exe() {
"$PATH_RESOURCES/$PLATFORM/bin/kubectl$EXE" "$@" | no_cr
}
limactl() {
# LIMA_HOME is set by paths.bash but not exported
LIMA_HOME="$LIMA_HOME" "$PATH_RESOURCES/$PLATFORM/lima/bin/limactl" "$@"
}
nerdctl() {
# Add path to bundled credential helpers to the front of the PATH; also
# ensure that on Windows, it gets exported.
PATH="$PATH_RESOURCES/$PLATFORM/bin:$PATH" WSLENV="PATH/l:${WSLENV:-}" \
"$PATH_RESOURCES/$PLATFORM/bin/nerdctl$EXE" --namespace "$CONTAINERD_NAMESPACE" "$@" | no_cr
}
# Run `rdctl`; if $RD_TIMEOUT is set, the value is used as the first argument to
# the `timeout` command.
rdctl() {
if is_windows; then
timeout "${RD_TIMEOUT:-0}" "$PATH_RESOURCES/win32/bin/rdctl.exe" "$@" | no_cr
else
timeout "${RD_TIMEOUT:-0}" "$PATH_RESOURCES/$PLATFORM/bin/rdctl$EXE" "$@"
fi
}
rdshell() {
rdctl shell "$@"
}
rdsudo() {
rdshell sudo "$@"
}
spin() {
# spin may call itself recursively, so make sure it calls the correct binary
PATH="$PATH_RESOURCES/$PLATFORM/bin:$PATH" "$PATH_RESOURCES/$PLATFORM/bin/spin$EXE" "$@" | no_cr
}
wsl() {
wsl.exe -d "$WSL_DISTRO" "$@"
}
================================================
FILE: bats/tests/helpers/defaults.bash
================================================
########################################################################
: "${RD_CONTAINER_ENGINE:=containerd}"
validate_enum RD_CONTAINER_ENGINE containerd moby
using_containerd() {
test "$RD_CONTAINER_ENGINE" = "containerd"
}
using_docker() {
! using_containerd
}
########################################################################
: "${RD_RANCHER_IMAGE_TAG:=}"
rancher_image_tag() {
echo "${RANCHER_IMAGE_TAG:-v2.7.0}"
}
########################################################################
# Defaults to true, except in the helper unit tests, which default to false
: "${RD_INFO:=}"
########################################################################
: "${RD_CAPTURE_LOGS:=false}"
capturing_logs() {
is_true "$RD_CAPTURE_LOGS"
}
########################################################################
: "${RD_NO_MODAL_DIALOGS:=true}"
suppressing_modal_dialogs() {
is_true "$RD_NO_MODAL_DIALOGS"
}
########################################################################
: "${RD_TAKE_SCREENSHOTS:=false}"
taking_screenshots() {
is_true "$RD_TAKE_SCREENSHOTS"
}
########################################################################
: "${RD_TRACE:=false}"
########################################################################
# When RD_USE_GHCR_IMAGES is true, then all images will be pulled from
# ghcr.io instead of docker.io, to avoid hitting the docker hub pull
# rate limit.
: "${RD_USE_GHCR_IMAGES:=false}"
using_ghcr_images() {
is_true "$RD_USE_GHCR_IMAGES"
}
########################################################################
: "${RD_DELETE_PROFILES:=true}"
deleting_profiles() {
is_true "$RD_DELETE_PROFILES"
}
########################################################################
: "${RD_USE_IMAGE_ALLOW_LIST:=false}"
using_image_allow_list() {
is_true "$RD_USE_IMAGE_ALLOW_LIST"
}
########################################################################
# RD_USE_PROFILE is for internal use. It uses a profile instead of
# settings.json to set initial values for WSL integrations and allowed
# images list because when settings.json exists the default profile is
# ignored.
: "${RD_USE_PROFILE:=false}"
########################################################################
# RD_TIMEOUT is for internal use. It is used to configure timeouts for
# the `rdctl` command, and should not be set outside of specific
# commands.
: "${RD_TIMEOUT:=}"
if [[ -n $RD_TIMEOUT ]]; then
fatal "RD_TIMEOUT should not be set"
fi
########################################################################
: "${RD_USE_VZ_EMULATION:=$(bool is_macos)}"
using_vz_emulation() {
is_true "$RD_USE_VZ_EMULATION"
}
if using_vz_emulation && ! supports_vz_emulation; then
fatal "RD_USE_VZ_EMULATION is not supported on this OS or OS version"
fi
########################################################################
: "${RD_USE_WINDOWS_EXE:=$(bool is_windows)}"
using_windows_exe() {
is_true "$RD_USE_WINDOWS_EXE"
}
if using_windows_exe && ! is_windows; then
fatal "RD_USE_WINDOWS_EXE only works on Windows"
fi
########################################################################
if is_unix; then
: "${RD_MOUNT_TYPE:=reverse-sshfs}"
validate_enum RD_MOUNT_TYPE reverse-sshfs 9p virtiofs
if [ "$RD_MOUNT_TYPE" = "virtiofs" ] && ! using_vz_emulation; then
fatal "RD_MOUNT_TYPE=virtiofs only works with VZ emulation"
fi
if [ "$RD_MOUNT_TYPE" = "9p" ] && using_vz_emulation; then
fatal "RD_MOUNT_TYPE=9p only works with qemu emulation"
fi
else
: "${RD_MOUNT_TYPE:=}"
if [ -n "${RD_MOUNT_TYPE:-}" ]; then
fatal "RD_MOUNT_TYPE only works on Linux and macOS"
fi
fi
########################################################################
: "${RD_9P_CACHE_MODE:=mmap}"
validate_enum RD_9P_CACHE_MODE none loose fscache mmap
########################################################################
: "${RD_9P_MSIZE:=128}"
########################################################################
: "${RD_9P_PROTOCOL_VERSION:=9p2000.L}"
validate_enum RD_9P_PROTOCOL_VERSION 9p2000 9p2000.u 9p2000.L
########################################################################
: "${RD_9P_SECURITY_MODEL:=none}"
validate_enum RD_9P_SECURITY_MODEL passthrough mapped-xattr mapped-file none
########################################################################
# When RD_USE_RAMDISK is true, we will try to set up a temporary ramdisk
# for the application profile to make things run faster. This is not
# supported on all platforms, but is a no-op on unsupported platforms.
# Some test files may override this due to interactions with factory reset.
: "${RD_USE_RAMDISK:=false}"
# Size of the ramdisk, in gigabytes. If a test requires more space than given,
# then ramdisk will be disabled for that test.
: "${RD_RAMDISK_SIZE:=12}"
using_ramdisk() {
is_true "${RD_USE_RAMDISK}"
}
########################################################################
# Use RD_PROTECTED_DOT in profile settings for WSL distro names.
: "${RD_PROTECTED_DOT:=·}"
########################################################################
# RD_KUBELET_TIMEOUT specifies the number of minutes wait_for_kubelet()
# waits before it times out.
: "${RD_KUBELET_TIMEOUT:=10}"
########################################################################
# RD_LOCATION specifies the location where Rancher Desktop is installed
# system: default system-wide install location shared for all users
# user: per-user install location
# dist: use the result of `yarn package` in ../dist
# dev: dev mode; start app with `cd ..; yarn dev`
# "": use first location from the list above that contains the app
: "${RD_LOCATION:=}"
validate_enum RD_LOCATION system user dist dev ""
using_dev_mode() {
[ "$RD_LOCATION" = "dev" ]
}
########################################################################
# Kubernetes versions
# The main Kubernetes version to test.
: "${RD_KUBERNETES_VERSION:=1.32.7}"
# A secondary Kubernetes version; this is used for testing upgrades.
: "${RD_KUBERNETES_ALT_VERSION:=1.31.3}"
# RD_K3S_VERSIONS specifies a list of k3s versions. foreach_k3s_version()
# can dynamically register a test to run once for each version in the
# list. Only versions between RD_K3S_MIN and RD_K3S_MAX (inclusively)
# will be used.
#
# Special values:
# "all" will fetch the list of all k3s releases from GitHub
# "latest" will fetch the list of latest versions from the release channel
: "${RD_K3S_MIN:=1.25.3}"
: "${RD_K3S_MAX:=1.99.0}"
: "${RD_K3S_VERSIONS:=$RD_KUBERNETES_VERSION}"
validate_semver RD_K3S_MIN
validate_semver RD_K3S_MAX
# Cache expansion of RD_K3S_VERSIONS special versions because they are slow to compute
if ! load_var RD_K3S_VERSIONS; then
# Fetch "all" or "latest" versions
get_k3s_versions
for k3s_version in ${RD_K3S_VERSIONS}; do
validate_semver k3s_version
done
save_var RD_K3S_VERSIONS
fi
########################################################################
# RD_VPN_TEST_IMAGE specifies the URL used by the split DNS test to access
# the private registry. Defaults to empty. Can be set via environment
# variable when running tests.
: "${RD_VPN_TEST_IMAGE:=}"
using_vpn_test_image() {
[[ -n $RD_VPN_TEST_IMAGE ]]
}
================================================
FILE: bats/tests/helpers/images.bash
================================================
# These images have been mirrored to ghcr.io (using bats/scripts/ghcr-mirror.sh)
# to avoid hitting Docker Hub pull limits during testing.
# TODO TODO TODO
# The python image is huge (10GB across all platforms). We should either pin the
# tag, or replace it with a different image for testing, so we don't have to mirror
# the images to ghcr.io every time we run the mirror script.
# TODO TODO TODO
# Any time you add an image here you need to re-run the mirror script!
IMAGES=(alpine busybox nginx python python:3.9-slim ruby tonistiigi/binfmt registry:2.8.1)
GHCR_REPO=ghcr.io/rancher-sandbox/bats
# Create IMAGE_FOO_BAR_TAG=foo/bar:tag variables
for IMAGE in "${IMAGES[@]}"; do
VAR="IMAGE_$(echo "$IMAGE" | tr '[:lower:]' '[:upper:]' | tr -C '[:alnum:][:space:]' _)"
# file may be loaded outside BATS environment
if [ "$(type -t using_ghcr_images)" = "function" ] && using_ghcr_images; then
eval "$VAR=$GHCR_REPO/$IMAGE"
else
eval "$VAR=$IMAGE"
fi
done
# shellcheck disable=2034 # The registry image doesn't really need the tag
IMAGE_REGISTRY=$IMAGE_REGISTRY_2_8_1
================================================
FILE: bats/tests/helpers/info.bash
================================================
# shellcheck disable=SC2059
# https://www.shellcheck.net/wiki/SC2059 -- Don't use variables in the printf format string. Use printf '..%s..' "$foo".
# This file exists to print information about the configuration.
show_info() { # @test
# In case the file is loaded as a test: bats tests/helpers/info.bash
if [ -z "$RD_HELPERS_LOADED" ]; then
load load.bash
fi
if capturing_logs || taking_screenshots; then
rm -rf "$PATH_BATS_LOGS"
fi
if is_false "${RD_INFO:-true}"; then
return
fi
(
local format="# %s | %s\n"
printf "$format" "Install location:" "$RD_LOCATION"
printf "$format" "Resources path:" "$PATH_RESOURCES"
echo "#"
printf "$format" "Container engine:" "$RD_CONTAINER_ENGINE"
printf "$format" "Kubernetes version:" "$RD_KUBERNETES_VERSION ($RD_K3S_VERSIONS)"
printf "$format" "Mount type:" "$RD_MOUNT_TYPE"
if [ "$RD_MOUNT_TYPE" = "9p" ]; then
printf "$format" " 9p cache mode:" "$RD_9P_CACHE_MODE"
printf "$format" " 9p msize:" "$RD_9P_MSIZE"
printf "$format" " 9p protocol version:" "$RD_9P_PROTOCOL_VERSION"
printf "$format" " 9p security model:" "$RD_9P_SECURITY_MODEL"
fi
printf "$format" "Using image allow list:" "$(bool using_image_allow_list)"
if is_macos; then
printf "$format" "Using VZ emulation:" "$(bool using_vz_emulation)"
printf "$format" "Using ramdisk:" "$(bool using_ramdisk)"
fi
if is_windows; then
printf "$format" "Using Windows executables:" "$(bool using_windows_exe)"
fi
echo "#"
printf "$format" "Capturing logs:" "$(bool capturing_logs)"
printf "$format" "Tracing execution:" "$(bool is_true "$RD_TRACE")"
printf "$format" "Taking screenshots:" "$(bool taking_screenshots)"
printf "$format" "Using ghcr.io images:" "$(bool using_ghcr_images)"
) | column -t -s '|' >&3
}
================================================
FILE: bats/tests/helpers/kubernetes.bash
================================================
wait_for_kubelet() {
local desired_version=${1:-$RD_KUBERNETES_VERSION}
local timeout=$(($(date +%s) + RD_KUBELET_TIMEOUT * 60))
trace "waiting for Kubernetes ${desired_version} to be available"
while true; do
sleep 1
assert [ "$(date +%s)" -lt "$timeout" ]
if ! kubectl get --raw /readyz &>/dev/null; then
continue
fi
# Check that kubelet is Ready
run kubectl get node -o jsonpath="{.items[0].status.conditions[?(@.type=='Ready')].status}"
if ((status != 0)) || [[ $output != "True" ]]; then
continue
fi
# Make sure the "default" serviceaccount exists
if ! kubectl get --namespace default serviceaccount default &>/dev/null; then
continue
fi
# Get kubelet version
run kubectl get node -o jsonpath="{.items[0].status.nodeInfo.kubeletVersion}"
if ((status != 0)); then
continue
fi
# Turn "v1.23.4+k3s1" into "1.23.4"
local version=${output#v}
version=${version%+*}
if [ "$version" == "$desired_version" ]; then
return 0
fi
done
}
# unwrap_kube_list removes the "List" wrapper from the JSON in $output if .kind is "List".
# Returns an error if the number of .items in the List isn't exactly 1.
unwrap_kube_list() {
local json=$output
run jq_output '.kind'
assert_success
if [[ $output == "List" ]]; then
run jq --raw-output '.items | length' <<<"$json"
assert_success
assert_output "1"
run jq --raw-output '.items[0]' <<<"$json"
assert_success
json=$output
fi
echo "$json"
}
assert_kube_deployment_available() {
local jsonpath="jsonpath={.status.conditions[?(@.type=='Available')].status}"
run --separate-stderr kubectl get deployment "$@" --output "$jsonpath"
assert_success
assert_output "True"
}
wait_for_kube_deployment_available() {
trace "waiting for deployment $*"
try assert_kube_deployment_available "$@"
}
assert_pod_containers_are_running() {
run kubectl get pod "$@" --output json
assert_success
# Make sure the query returned just a single pod
run unwrap_kube_list
assert_success
# Confirm that **all** containers of the pod are in "running" state
run jq_output '[.status.containerStatuses[].state | keys] | add | unique | .[]'
assert_success
assert_output "running"
}
traefik_ip() {
local jsonpath='jsonpath={.status.loadBalancer.ingress[0].ip}'
run --separate-stderr kubectl get service traefik --namespace kube-system --output "$jsonpath"
assert_success
assert_output
echo "$output"
}
traefik_hostname() {
if is_windows; then
# BUG BUG BUG
# Currently the service ip address is not routable from the host
# https://github.com/rancher-sandbox/rancher-desktop/issues/6934
# BUG BUG BUG
# local ip
# ip=$(traefik_ip)
# echo "${ip}.sslip.io"
# caller must have called `skip_unless_host_ip`
output=$HOST_IP assert_output
echo "${HOST_IP}.sslip.io"
else
echo "localhost"
fi
}
wait_for_traefik() {
try traefik_ip
}
get_k3s_versions() {
if [[ $RD_K3S_VERSIONS == "all" ]]; then
# filter out duplicates; RD only supports the latest of +k3s1, +k3s2, etc.
RD_K3S_VERSIONS=$(
gh api /repos/k3s-io/k3s/releases --paginate --jq '.[].tag_name' |
grep -E '^v1\.[0-9]+\.[0-9]+\+k3s[0-9]+$' |
sed -E 's/v([^+]+)\+.*/\1/' |
sort --unique --version-sort
)
fi
if [[ $RD_K3S_VERSIONS == "latest" ]]; then
RD_K3S_VERSIONS=$(
curl --silent --fail https://update.k3s.io/v1-release/channels |
jq --raw-output '.data[] | select(.name | test("^v[0-9]+\\.[0-9]+$")).latest' |
sed -E 's/v([^+]+)\+.*/\1/'
)
fi
}
================================================
FILE: bats/tests/helpers/kubernetes.bats
================================================
load '../helpers/load'
: "${RD_INFO:=false}"
@test 'unwrap_kube_list: no list' {
run echo '{"kind": "Pod"}'
assert_success
run unwrap_kube_list
assert_success
run jq_output .kind
assert_success
assert_output Pod
}
@test 'unwrap_kube_list: no items' {
run echo '{"kind": "List"}'
assert_success
run unwrap_kube_list
assert_failure
}
@test 'unwrap_kube_list: one item' {
run echo '{"kind": "List", "items": [{"kind": "Pod"}]}'
assert_success
run unwrap_kube_list
assert_success
run jq_output .kind
assert_success
assert_output Pod
}
@test 'unwrap_kube_list: two items' {
run echo '{"kind": "List", "items": [{"kind": "Pod"},{"kind": "Pod"}]}'
assert_success
run unwrap_kube_list
assert_failure
}
@test 'unwrap_kube_list: not JSON' {
run echo 'Some random error message'
assert_success
run unwrap_kube_list
assert_failure
}
================================================
FILE: bats/tests/helpers/load.bash
================================================
set -o errexit -o nounset -o pipefail
# Make sure run() will execute all functions with errexit enabled
export BATS_RUN_ERREXIT=1
# RD_HELPERS_LOADED is set when bats/helpers/load.bash has been loaded
RD_HELPERS_LOADED=1
absolute_path() {
(
cd "$1"
pwd
)
}
PATH_BATS_HELPERS=$(absolute_path "$(dirname "${BASH_SOURCE[0]}")")
PATH_BATS_ROOT=$(absolute_path "$PATH_BATS_HELPERS/../..")
PATH_BATS_LOGS=$PATH_BATS_ROOT/logs
# RD_TEST_FILENAME is relative to tests/ and strips the .bats extension,
# e.g. "registry/creds" for ".../bats/tests/registry/creds.bats"
RD_TEST_FILENAME=${BATS_TEST_FILENAME#"$PATH_BATS_ROOT/tests/"}
RD_TEST_FILENAME=${RD_TEST_FILENAME%.bats}
# Use fatal() to abort loading helpers; don't run any tests
fatal() {
local fd=2
# fd 3 might not be open if we're not fully under bats yet; detect that.
[[ -e /dev/fd/3 ]] && fd=3
echo " $1" >&$fd
# Print (ugly) stack trace if we are outside any @test function
if [ -z "${BATS_SUITE_TEST_NUMBER:-}" ]; then
echo >&$fd
local frame=0
while caller $frame >&$fd; do
((frame++))
done
fi
exit 1
}
source "$PATH_BATS_ROOT/bats-support/load.bash"
source "$PATH_BATS_ROOT/bats-assert/load.bash"
source "$PATH_BATS_ROOT/bats-file/load.bash"
source "$PATH_BATS_HELPERS/os.bash"
source "$PATH_BATS_HELPERS/utils.bash"
source "$PATH_BATS_HELPERS/snapshots.bash"
# kubernetes.bash has no load-time dependencies
source "$PATH_BATS_HELPERS/kubernetes.bash"
# defaults.bash uses is_windows() from os.bash and
# validate_enum() and is_true() from utils.bash.
# get_k3s_versions from kubernetes.bash.
source "$PATH_BATS_HELPERS/defaults.bash"
# images.bash uses using_ghcr_images() from defaults.bash
source "$PATH_BATS_HELPERS/images.bash"
# paths.bash uses RD_LOCATION from defaults.bash
source "$PATH_BATS_HELPERS/paths.bash"
# commands.bash uses is_containerd() from defaults.bash,
# is_windows() etc from os.bash,
# and PATH_* variables from paths.bash
source "$PATH_BATS_HELPERS/commands.bash"
# profile.bash uses is_xxx() from os.bash
source "$PATH_BATS_HELPERS/profile.bash"
# vm.bash uses various PATH_* variables from paths.bash,
# rdctl from commands.bash, and jq_output from utils.bash
source "$PATH_BATS_HELPERS/vm.bash"
# Add BATS helper executables to $PATH. On Windows, we use the Linux version
# from WSL.
export PATH="$PATH_BATS_ROOT/bin/${OS/windows/linux}:$PATH"
# If called from foo() this function will call local_foo() if it exist.
call_local_function() {
local func
func="local_$(calling_function)"
if [ "$(type -t "$func")" = "function" ]; then
"$func"
fi
}
setup_file() {
# We require bash 4; bash 3.2 (as shipped by macOS) seems to have
# compatibility issues.
if semver_gt 4.0.0 "$(semver "$BASH_VERSION")"; then
fail "Bash 4.0.0 is required; you have $BASH_VERSION"
fi
# We currently use a submodule that provides BATS 1.10; we do not test
# against any other copy of BATS (and therefore only support the version in
# that submodule).
bats_require_minimum_version 1.10.0
# Ideally this should be printed only when using the tap formatter,
# but I don't see a way to check for this.
echo "# ===== $RD_TEST_FILENAME =====" >&3
# local_setup_file may override RD_USE_RAMDISK
call_local_function
setup_ramdisk
}
teardown_file() {
capture_logs
local shutdown=false
if is_linux || is_windows; then
# On Linux & Windows if we don't shutdown Rancher Desktop bats tests don't terminate.
shutdown=true
elif using_dev_mode; then
# In dev mode, we also need to shut down.
shutdown=true
elif using_ramdisk; then
# When using a ramdisk, we need to shut down to clean up.
shutdown=true
fi
if is_true $shutdown; then
rdctl shutdown || :
fi
teardown_ramdisk
call_local_function
}
setup() {
if [ "${BATS_SUITE_TEST_NUMBER}" -eq 1 ] && [ "$RD_TEST_FILENAME" != "helpers/info.bash" ]; then
source "$PATH_BATS_HELPERS/info.bash"
show_info
echo "#"
fi
call_local_function
}
teardown() {
if [ -z "$BATS_TEST_SKIPPED" ] && [ -z "$BATS_TEST_COMPLETED" ]; then
capture_logs
take_screenshot
fi
call_local_function
}
================================================
FILE: bats/tests/helpers/os.bash
================================================
# https://www.shellcheck.net/wiki/SC2120 -- disabled due to complaining about not referencing arguments that are optional on functions is_platformName
# shellcheck disable=SC2120
UNAME=$(uname)
ARCH=$(uname -m)
ARCH=${ARCH/arm64/aarch64}
case $UNAME in
Darwin)
# OS matches the directory name of the PATH_RESOURCES directory,
# so uses "darwin" and not "macos".
OS=darwin
;;
Linux)
if [[ $(uname -a) =~ microsoft ]]; then
OS=windows
else
OS=linux
fi
;;
*)
echo "Unexpected uname: $UNAME" >&2
exit 1
;;
esac
is_linux() {
if [ -z "${1:-}" ]; then
test "$OS" = linux
else
test "$OS" = linux -a "$ARCH" = "$1"
fi
}
is_macos() {
if [ -z "${1:-}" ]; then
test "$OS" = darwin
else
test "$OS" = darwin -a "$ARCH" = "$1"
fi
}
is_windows() {
if [ -z "${1:-}" ]; then
test "$OS" = windows
else
test "$OS" = windows -a "$ARCH" = "$1"
fi
}
is_unix() {
! is_windows "$@"
}
skip_on_windows() {
if is_windows; then
skip "${1:-This test is not applicable on Windows.}"
fi
}
skip_on_unix() {
if is_unix; then
skip "${1:-This test is not applicable on macOS/Linux.}"
fi
}
needs_port() {
local port=$1
if is_linux; then
if [ "$(cat /proc/sys/net/ipv4/ip_unprivileged_port_start)" -gt "$port" ]; then
# Run sudo non-interactive, so don't prompt for password
run sudo -n sh -c "echo $port > /proc/sys/net/ipv4/ip_unprivileged_port_start"
if ((status > 0)); then
skip "net.ipv4.ip_unprivileged_port_start must be $port or less"
fi
fi
fi
}
sudo_needs_password() {
# Check if we can run /usr/bin/true (or /bin/true) without requiring a password
run sudo --non-interactive --reset-timestamp true
((status != 0))
}
supports_vz_emulation() {
if ! is_macos; then
return 1
fi
[[ -n ${_RD_SUPPORTS_VZ_EMULATION:-} ]] || load_var _RD_SUPPORTS_VZ_EMULATION || true
if [[ -z ${_RD_SUPPORTS_VZ_EMULATION:-} ]]; then
local version
version=$(semver "$(/usr/bin/sw_vers -productVersion)")
trace "macOS version is $version"
if semver_gte "$version" 13.3.0; then
_RD_SUPPORTS_VZ_EMULATION=true
elif [[ $ARCH == x86_64 ]] && semver_gte "$version" 13.0.0; then
# Versions 13.0.x .. 13.2.x work only on x86_64, not aarch64
_RD_SUPPORTS_VZ_EMULATION=true
else
_RD_SUPPORTS_VZ_EMULATION=false
fi
save_var _RD_SUPPORTS_VZ_EMULATION
fi
is_true "${_RD_SUPPORTS_VZ_EMULATION}"
}
================================================
FILE: bats/tests/helpers/paths.bash
================================================
# PATH_BATS_ROOT, PATH_BATS_LOGS, and PATH_BATS_HELPERS are already set by load.bash
PATH_REPO_ROOT=$(absolute_path "$PATH_BATS_ROOT/..")
inside_repo_clone() {
[ -d "$PATH_REPO_ROOT/pkg/rancher-desktop" ]
}
set_path_resources() {
local system=$1
local user=$2
local dist=$3
local subdir=$4
local fd=3
if [[ ! -e /dev/fd/3 ]]; then
fd=2
fi
if [ -z "${RD_LOCATION:-}" ]; then
if [ -d "$system" ]; then
RD_LOCATION=system
elif [ -d "$user" ]; then
RD_LOCATION=user
elif [ -d "$dist" ]; then
RD_LOCATION=dist
elif inside_repo_clone; then
RD_LOCATION=dev
else
(
echo "Couldn't locate Rancher Desktop in"
echo "- \"$system\""
echo "- \"$user\""
echo "- \"$dist\""
echo "and 'yarn dev' is unavailable outside repo clone"
) >&$fd
exit 1
fi
fi
if using_dev_mode; then
if is_windows; then
fatal "yarn operation not yet implemented for Windows"
fi
PATH_RESOURCES="$PATH_REPO_ROOT/resources"
else
PATH_RESOURCES="${!RD_LOCATION}/${subdir}"
fi
if [ ! -d "$PATH_RESOURCES" ]; then
fatal "App resource directory '$PATH_RESOURCES' does not exist"
fi
}
if is_macos; then
PATH_APP_HOME="$HOME/Library/Application Support/rancher-desktop"
PATH_CONFIG="$HOME/Library/Preferences/rancher-desktop"
PATH_CACHE="$HOME/Library/Caches/rancher-desktop"
PATH_LOGS="$HOME/Library/Logs/rancher-desktop"
PATH_EXTENSIONS="$PATH_APP_HOME/extensions"
LIMA_HOME="$PATH_APP_HOME/lima"
PATH_SNAPSHOTS="$PATH_APP_HOME/snapshots"
PATH_CONTAINERD_SHIMS="$PATH_APP_HOME/containerd-shims"
ELECTRON_DIST_ARCH="mac"
if is_macos aarch64; then
ELECTRON_DIST_ARCH="mac-arm64"
fi
set_path_resources \
"/Applications/Rancher Desktop.app" \
"$HOME/Applications/Rancher Desktop.app" \
"$PATH_REPO_ROOT/dist/$ELECTRON_DIST_ARCH/Rancher Desktop.app" \
"Contents/Resources/resources"
fi
if is_linux; then
PATH_APP_HOME="$HOME/.local/share/rancher-desktop"
PATH_CONFIG="$HOME/.config/rancher-desktop"
PATH_CACHE="$HOME/.cache/rancher-desktop"
PATH_LOGS="$PATH_APP_HOME/logs"
PATH_EXTENSIONS="$PATH_APP_HOME/extensions"
LIMA_HOME="$PATH_APP_HOME/lima"
PATH_SNAPSHOTS="$PATH_APP_HOME/snapshots"
PATH_CONTAINERD_SHIMS="$PATH_APP_HOME/containerd-shims"
set_path_resources \
"/opt/rancher-desktop" \
"$HOME/opt/rancher-desktop" \
"$PATH_REPO_ROOT/dist/linux-unpacked" \
"resources/resources"
fi
wslpath_from_win32_env() {
# The cmd.exe _sometimes_ returns an empty string when invoked in a subshell
# wslpath "$(cmd.exe /c "echo %$1%" 2>/dev/null)" | tr -d "\r"
# Let's see if powershell.exe avoids this issue
wslpath "$(powershell.exe -Command "Write-Output \${Env:$1}")" | tr -d "\r"
}
if is_windows; then
LOCALAPPDATA="$(wslpath_from_win32_env LOCALAPPDATA)"
PROGRAMFILES="$(wslpath_from_win32_env ProgramFiles)"
SYSTEMROOT="$(wslpath_from_win32_env SystemRoot)"
PATH_APP_HOME="$LOCALAPPDATA/rancher-desktop"
PATH_CONFIG="$LOCALAPPDATA/rancher-desktop"
PATH_CACHE="$PATH_APP_HOME/cache"
PATH_LOGS="$PATH_APP_HOME/logs"
PATH_DISTRO="$PATH_APP_HOME/distro"
PATH_DISTRO_DATA="$PATH_APP_HOME/distro-data"
PATH_EXTENSIONS="$PATH_APP_HOME/extensions"
PATH_SNAPSHOTS="$PATH_APP_HOME/snapshots"
PATH_CONTAINERD_SHIMS="$PATH_APP_HOME/containerd-shims"
set_path_resources \
"$PROGRAMFILES/Rancher Desktop" \
"$LOCALAPPDATA/Programs/Rancher Desktop" \
"$PATH_REPO_ROOT/dist/win-unpacked" \
"resources/resources"
fi
PATH_CONFIG_FILE="$PATH_CONFIG/settings.json"
USERPROFILE=$HOME
if using_windows_exe; then
USERPROFILE="$(wslpath_from_win32_env USERPROFILE)"
fi
host_path() {
local path=$1
if using_windows_exe; then
path=$(wslpath -w "$path")
fi
echo "$path"
}
================================================
FILE: bats/tests/helpers/profile.bash
================================================
case $OS in
darwin)
PROFILE_SYSTEM_DEFAULTS=/Library/Preferences/io.rancherdesktop.profile.defaults.plist
PROFILE_SYSTEM_LOCKED=/Library/Preferences/io.rancherdesktop.profile.locked.plist
PROFILE_USER_DEFAULTS="${HOME}${PROFILE_SYSTEM_DEFAULTS}"
PROFILE_USER_LOCKED="${HOME}${PROFILE_SYSTEM_LOCKED}"
;;
linux)
PROFILE_SYSTEM_DEFAULTS=/etc/rancher-desktop/defaults.json
PROFILE_SYSTEM_LOCKED=/etc/rancher-desktop/locked.json
PROFILE_USER_DEFAULTS="${HOME}/.config/rancher-desktop.defaults.json"
PROFILE_USER_LOCKED="${HOME}/.config/rancher-desktop.locked.json"
;;
windows)
PROFILE='Software\Policies\Rancher Desktop'
PROFILE_SYSTEM_DEFAULTS="HKLM\\${PROFILE}\\Defaults"
PROFILE_SYSTEM_LOCKED="HKLM\\${PROFILE}\\Locked"
PROFILE_USER_DEFAULTS="HKCU\\${PROFILE}\\Defaults"
PROFILE_USER_LOCKED="HKCU\\${PROFILE}\\Locked"
# The legacy profiles (for both system and user) are supported for backward
# compatibility with Rancher Desktop 1.8.x. For BATS purposes the legacy
# user profiles have the advantage of being writable without admin rights.
PROFILE='Software\Rancher Desktop\Profile'
PROFILE_SYSTEM_LEGACY_DEFAULTS="HKLM\\${PROFILE}\\Defaults"
PROFILE_SYSTEM_LEGACY_LOCKED="HKLM\\${PROFILE}\\Locked"
PROFILE_USER_LEGACY_DEFAULTS="HKCU\\${PROFILE}\\Defaults"
PROFILE_USER_LEGACY_LOCKED="HKCU\\${PROFILE}\\Locked"
;;
esac
PROFILE_SYSTEM=system
PROFILE_SYSTEM_LEGACY=system-legacy
PROFILE_USER=user
PROFILE_USER_LEGACY=user-legacy
PROFILE_DEFAULTS=defaults
PROFILE_LOCKED=locked
# Default location is a writable user location
if is_windows; then
PROFILE_LOCATION=$PROFILE_USER_LEGACY
else
PROFILE_LOCATION=$PROFILE_USER
fi
PROFILE_TYPE=$PROFILE_DEFAULTS
# profile_location is a registry key on Windows, or a filename on macOS and Linux.
profile_location() {
local profile
profile=$(to_upper "profile_${PROFILE_LOCATION}_${PROFILE_TYPE}" | tr - _)
echo "${!profile}"
}
# Execute command for each profile
foreach_profile() {
local locations=("$PROFILE_SYSTEM" "$PROFILE_USER")
if is_windows; then
locations+=("$PROFILE_SYSTEM_LEGACY" "$PROFILE_USER_LEGACY")
fi
local PROFILE_LOCATION PROFILE_TYPE
for PROFILE_LOCATION in "${locations[@]}"; do
for PROFILE_TYPE in "$PROFILE_DEFAULTS" "$PROFILE_LOCKED"; do
"$@"
done
done
}
# Check if profile exists
profile_exists() {
case $OS in
darwin | linux)
[[ -f $(profile_location) ]]
;;
windows)
profile_reg query &>/dev/null
;;
esac
}
# Create empty profile
create_profile() {
case $OS in
darwin)
profile_plutil -create xml1
;;
linux)
local filename
filename=$(profile_location)
profile_sudo mkdir -p "$(dirname "$filename")"
echo "{}" | profile_cat "$filename"
;;
windows)
# Make sure any old profile data at this location is removed
run profile_reg delete "."
assert_nothing
# Create subkey so that profile_exists returns true now
profile_reg add "."
;;
esac
}
# Completely remove the profile. Ignores error if profile doesn't exist
delete_profile() {
if deleting_profiles; then
case $OS in
darwin | linux)
run profile_sudo rm -f "$(profile_location)"
assert_nothing
;;
windows)
run profile_reg delete "."
assert_nothing
;;
esac
fi
}
# Export/copy profile to a directory
export_profile() {
local dir=$1
if profile_exists; then
local export="${dir}/profile.${PROFILE_LOCATION}.${PROFILE_TYPE}"
case $OS in
darwin | linux)
local filename
filename=$(profile_location)
# Keep .plist or .json file extension
cp "$filename" "${export}.${filename##*.}"
;;
windows)
export="$(wslpath -w "${export}.reg")"
profile_reg export "${export}" /y
;;
esac
fi
}
# Set a profile setting to a boolean; value must be "true" or "false"
# The profile must exist before calling this function.
add_profile_bool() {
local setting=$1
local value=$2
assert profile_exists
case $OS in
darwin)
profile_plutil -replace "$setting" -bool "$value"
;;
linux)
profile_jq ".${setting} = ${value}"
;;
windows)
if [[ $value == true ]]; then
profile_reg add "$setting" /t REG_DWORD /d 1
else
profile_reg add "$setting" /t REG_DWORD /d 0
fi
;;
esac
}
# Set a profile setting to an integer.
# The profile must exist before calling this function.
add_profile_int() {
local setting=$1
local value=$2
assert profile_exists
case $OS in
darwin)
profile_plutil -replace "$setting" -integer "$value"
;;
linux)
profile_jq ".${setting} = ${value}"
;;
windows)
profile_reg add "$setting" /t REG_DWORD /d "$value"
;;
esac
}
# Set a profile setting to a string.
# The profile must exist before calling this function.
add_profile_string() {
local setting=$1
local value=$2
assert profile_exists
case $OS in
darwin)
profile_plutil -replace "$setting" -string "$value"
;;
linux)
profile_jq ".${setting} = $(json_string "$value")"
;;
windows)
profile_reg add "$setting" /t REG_SZ /d "$value"
;;
esac
}
# Set a profile setting to a list of strings, replacing any existing elements.
# The profile must exist before calling this function.
add_profile_list() {
local elem
local setting=$1
shift
assert profile_exists
case $OS in
darwin)
profile_plutil -replace "$setting" -array
for elem in "$@"; do
profile_plutil -insert "$setting" -string "$elem" -append
done
;;
linux)
profile_jq ".${setting} = []"
for elem in "$@"; do
profile_jq ".${setting} += [$(json_string "$elem")]"
done
;;
windows)
# TODO: what happens when the values contain whitespace or quote characters?
profile_reg add "$setting" /t REG_MULTI_SZ /d "$(join_map '\0' echo "$@")"
;;
esac
}
# Remove a key or named value from the profile.
# Use a trailing dot to specify that the setting points to a key, e.g. "foo.bar.".
# It only makes a difference on Windows but will work on all platforms.
remove_profile_entry() {
local setting=$1
assert profile_exists
case $OS in
darwin)
profile_plutil -remove "${setting%.}"
;;
linux)
# This relies on `null` not being a valid setting value.
profile_jq "
if (try .${setting%.}) | type == \"null\" then
error(\"setting ${setting%.} not found\")
else
del(.${setting%.})
end
"
;;
windows)
profile_reg delete "$setting"
;;
esac
}
################################################################################
# functions defined below this line are implementation detail and should not
# be called directly from any tests.
################################################################################
# Returns number of setting segments (separated by dots), e.g. foo.bar.baz returns 3
count_setting_segments() {
echo "${1//./$'\n'}" | wc -l
}
# Usage: profile_jq $expr
#
# Applies $expr against the profile and update it in-place.
profile_jq() {
local expr=$1
local filename
filename=$(profile_location)
assert_file_exists "$filename"
# Need to use a temp file to avoid truncating the file before it has been read.
jq "$expr" "$filename" | profile_cat "${filename}.tmp"
profile_sudo mv "${filename}.tmp" "$filename"
}
# Usage: profile_plutil $action $options
#
# For -insert|-replace|-remove actions it will make sure all higher level
# dictionaries are created first because plutil doesn't do it by itself.
profile_plutil() {
local action=$1
# Make sure all the dictionaries for the setting path exist
if [[ $action =~ ^-insert|-replace|-remove$ ]]; then
local setting=$2
local count
count=$(count_setting_segments "$setting")
if ((count > 1)); then
local index
for index in $(seq $((count - 1))); do
local keypath
keypath=$(echo "$setting" | cut -d . -f 1-"$index")
# Ignore error if dictionary already exists
profile_sudo plutil -insert "$keypath" -dictionary "$(profile_location)" || :
done
fi
fi
profile_sudo plutil "$@" "$(profile_location)"
}
# Usage: profile_reg $action $options
# or: profile_reg add|delete $setting $options
#
# Determines the $reg_key from both the profile_location() and the $setting.
# Setting `foo.bar.baz` means `foo\bar` is the reg_subkey, and `baz` is the value name.
#
# Special case `foo.bar.` is used only for "delete" action and specifies `foo\bar`
# as the subkey to be deleted (including all values under the key).
profile_reg() {
local action=$1
shift
local reg_key
reg_key=$(profile_location)
if [[ $action =~ ^add|delete$ ]]; then
local setting=$1
shift
local count
count=$(count_setting_segments "$setting")
if ((count > 1)); then
local reg_subkey
reg_subkey=$(echo "$setting" | cut -d . -f 1-"$((count - 1))")
# reg_key uses backslashes instead of dot separators
reg_key="${reg_key}\\${reg_subkey//./\\}"
fi
local reg_value_name
reg_value_name=$(echo "$setting" | cut -d . -f "$count")
# reg_value_name may be empty when deleting a registry key instead of a named value
if [[ -n $reg_value_name ]]; then
# turn protected dots back into regular dots again
set - /v "${reg_value_name//$RD_PROTECTED_DOT/.}" "$@"
fi
# Delete entries (and overwrite existing ones) without prompt
set - "$@" /f
fi
reg.exe "$action" "$reg_key" "$@"
}
profile_sudo() {
# TODO How can we make this work on Windows?
if [[ $PROFILE_LOCATION == system ]]; then
sudo -n "$@"
else
"$@"
fi
}
profile_cat() {
profile_sudo tee "$1" >/dev/null
}
ensure_profile_is_deleted() {
delete_profile
if profile_exists; then
fatal "Cannot delete $(profile_location)"
fi
}
# Only run this once per test file. It cannot be part of setup_file() because
# we want to be able to call fatal() and skip the rest of the tests.
if [[ -z ${BATS_SUITE_TEST_NUMBER:-} ]] && deleting_profiles; then
foreach_profile ensure_profile_is_deleted
fi
================================================
FILE: bats/tests/helpers/snapshots.bash
================================================
delete_all_snapshots() {
run rdctl snapshot list --json
assert_success
# On Windows, executing native Windows executables consumes stdin.
# https://github.com/microsoft/WSL/issues/10429
# Work around the issue by using `run` to populate `${lines[@]}` ahead of
# time, so that we don't need the buffer during the loop.
run jq_output .name
assert_success
local name
for name in "${lines[@]}"; do
rdctl snapshot delete "$name"
done
run rdctl snapshot list
assert_success
assert_output --partial 'No snapshots'
}
================================================
FILE: bats/tests/helpers/utils.bash
================================================
to_lower() {
echo "$@" | tr '[:upper:]' '[:lower:]'
}
to_upper() {
echo "$@" | tr '[:lower:]' '[:upper:]'
}
is_true() {
# case-insensitive check; false values: '', '0', 'no', and 'false'
local value
value=$(to_lower "$1")
[[ ! $value =~ ^(0|no|false)?$ ]]
}
is_false() {
! is_true "$1"
}
bool() {
if "$@"; then
echo "true"
else
echo "false"
fi
}
# Ensure that the variable contains a valid value, e.g.
# `validate_enum VAR value1 value2`
validate_enum() {
local var=$1
shift
for value in "$@"; do
if [[ ${!var} == "$value" ]]; then
return
fi
done
fatal "$var=${!var} is not a valid setting; select from [$*]"
}
# Ensure that the variable contains a valid semver (major.minor.path) version, e.g.
# `validate_semver RD_K3S_MAX`
validate_semver() {
local var=$1
if ! semver_is_valid "${!var}"; then
fatal "$var=${!var} is not a valid semver value (major.minor.patch)"
fi
}
assert_nothing() {
# This is a no-op, used to show that run() has been used to continue the
# test even when the command failed, but the failure itself is ignored.
true
}
########################################################################
assert=assert
refute=refute
before() {
local assert=refute
local refute=assert
"$@"
}
refute_success() {
assert_failure
}
refute_failure() {
assert_success
}
refute_not_exists() {
assert_exists "$@"
}
refute_file_exists() {
assert_file_not_exists "$@"
}
refute_file_contains() {
assert_file_not_contains "$@"
}
########################################################################
# Convert raw string into properly quoted JSON string
json_string() {
echo -n "$1" | jq --raw-input --raw-output @json
}
# Join list elements by separator after converting them via the mapping function
# Examples:
# join_map "/" echo usr local bin => usr/local/bin
# join_map ", " json_string a b\ c\"d\\e f => "a", "b c\"d\\e", "f"
join_map() {
local sep=$1
local map=$2
shift 2
local elem
local result=""
for elem in "$@"; do
elem=$(eval "$map" '"$elem"')
if [[ -z $result ]]; then
result=$elem
else
result="${result}${sep}${elem}"
fi
done
echo "$result"
}
# Run jq on the current $output
# Note that when capturing $output, you may need to use `run --separate-stderr`
# to avoid also capturing stderr and ending up with invalid JSON.
jq_output() {
local json=$output
run jq --raw-output "$@" <<<"${json}"
if [[ -n $output ]]; then
echo "$output"
if [[ $output == null ]]; then
status=1
fi
elif ((status == 0)); then
# The command succeeded, so we should be able to run it again without error
# If the jq command emitted a newline, then we want to emit a newline too.
if [ "$(jq --raw-output "$@" <<<"${json}" | wc -c)" -gt 0 ]; then
echo ""
fi
fi
return "$status"
}
# semver returns the first semver version from its first argument (which may be multiple lines).
# It does not include pre-release markers or build ids.
# It will match major.minor, or even just major if it can't find major.minor.patch.
# The returned version will always be a major.minor.patch string.
# Each part will have leading zeros removed.
# semver will fail when the input contains no number.
semver() {
local input=$1
local semver
semver=$(awk 'match($0, /([0-9]+\.[0-9]+\.[0-9]+)/) {print substr($0, RSTART, RLENGTH); exit}' <<<"$input")
if [[ -z $semver ]]; then
semver=$(awk 'match($0, /([0-9]+\.[0-9]+)/) {print substr($0, RSTART, RLENGTH); exit}' <<<"$input")
fi
if [[ -z $semver ]]; then
semver=$(awk 'match($0, /([0-9]+)/) {print substr($0, RSTART, RLENGTH); exit}' <<<"$input")
fi
if [[ -z $semver ]]; then
return 1
fi
until [[ $semver =~ \..+\. ]]; do
semver="${semver}.0"
done
sed -E 's/^0*([0-9])/\1/; s/\.0*([0-9])/.\1/g' <<<"$semver"
}
# Check if the argument is a valid 3-tuple version number with no leading 0s and no newlines
semver_is_valid() {
[[ ! $1 =~ $'\n' ]] && grep -q -E '^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$' <<<"$1"
}
# All semver comparison functions will return false when called without any argument
# and return true when called with just a single argument.
# semver_eq checks that all specified arguments are equal to each other.
# (semver_eq and semver_neq don't really depend on the arguments being versions).
# `A = B = C`
semver_eq() {
[[ $# -gt 0 ]] && [[ $(printf "%s\n" "$@" | sort --unique | wc -l) -eq 1 ]]
}
# semver_neq checks that all arguments are unique. `semver_neq A B C` is not the same as
# `A ≠ B ≠ C` because semver_neq will also return a failure if `A = C`.
# `(A ≠ B) & (A ≠ C) & (B ≠ C)`
semver_neq() {
[[ $# -gt 0 ]] && printf "%s\n" "$@" | sort | sort --check=silent --unique
}
# `A ≤ B ≤ C`
semver_lte() {
[[ $# -gt 0 ]] && printf "%s\n" "$@" | sort --check=silent --version-sort
}
# `A < B < C`
semver_lt() {
[[ $# -gt 0 ]] && semver_lte "$@" && semver_neq "$@"
}
# `A ≥ B ≥ C`
semver_gte() {
[[ $# -gt 0 ]] && printf "%s\n" "$@" | sort --check=silent --reverse --version-sort
}
# `A > B > C`
semver_gt() {
[[ $# -gt 0 ]] && semver_gte "$@" && semver_neq "$@"
}
########################################################################
get_setting() {
run rdctl api /settings
assert_success
jq_output "$@"
}
this_function() {
echo "${FUNCNAME[1]}"
}
calling_function() {
echo "${FUNCNAME[2]}"
}
# Write a comment to the TAP stream.
# Set CALLER to print a calling function higher up in the call stack.
comment() {
local prefix=""
if is_true "$RD_TRACE"; then
local caller="${CALLER:-$(calling_function)}"
prefix="($(date -u +"%FT%TZ"): ${caller}): "
fi
local line
while IFS= read -r line; do
if [[ -e /dev/fd/3 ]]; then
printf "# %s%s\n" "$prefix" "$line" >&3
else
printf "# %s%s\n" "$prefix" "$line" >&2
fi
done <<<"$*"
}
# Write a comment to the TAP stream if RD_TRACE is set.
# Set CALLER to print a calling function higher up in the call stack.
trace() {
if is_true "$RD_TRACE"; then
CALLER=${CALLER:-$(calling_function)} comment "$@"
fi
}
# try runs the specified command until it either succeeds, or --max attempts
# have been made (with a --delay seconds sleep in between).
#
# Right now the command is **always** run with --separate-stderr, and stderr
# is output after all of stdout. This is subject to change, if we can figure
# out a way to detect if the caller used `run --separate-stderr try …` or not.
try() {
local max=24
local delay=5
while [[ $# -gt 0 ]] && [[ $1 == -* ]]; do
case "$1" in
--max)
max=$2
shift
;;
--delay)
delay=$2
shift
;;
--)
shift
break
;;
*)
printf "Usage error: unknown flag '%s'" "$1" >&2
return 1
;;
esac
shift
done
local count=0
while true; do
run --separate-stderr "$@"
if ((status == 0 || ++count >= max)); then
trace "$count/$max tries: $*"
break
fi
sleep "$delay"
done
echo "$output"
if [ -n "${stderr:-}" ]; then
echo "$stderr" >&2
fi
return "$status"
}
image_without_tag_as_json_string() {
local image=$1
# If the tag looks like a port number and follows something that looks
# like a domain name, then don't strip the tag (e.g. foo.io:5000).
if [[ ${image##*:} =~ ^[0-9]+(/|$) && ${image%:*} =~ \.[a-z]+$ ]]; then
json_string "$image"
else
json_string "${image%:*}"
fi
}
update_allowed_patterns() {
local enabled=$1
shift
local patterns
patterns=$(join_map ", " image_without_tag_as_json_string "$@")
# If the enabled state changes, then the container engine will be restarted.
# Record PID of the current daemon process so we can wait for it to be ready again.
local pid
if [ "$enabled" != "$(get_setting .containerEngine.allowedImages.enabled)" ]; then
pid=$(get_service_pid "$CONTAINER_ENGINE_SERVICE")
fi
rdctl api settings -X PUT --input - <$1`. Will create any parent
# directories.
create_file() {
local dest=$1
# On Windows, avoid creating files from within WSL; this leads to issues
# where the WSL view of the filesystem is desynchronized from the Windows
# view, so we end up having ghost files that can't be deleted from Windows.
if ! is_windows; then
mkdir -p "$(dirname "$dest")"
cat >"$dest"
return
fi
local contents # Base64 encoded file contents
contents="$(base64)"
local winParent
local winDest
winParent="$(wslpath -w "$(dirname "$dest")")"
winDest="$(wslpath -w "$dest")"
PowerShell.exe -NoProfile -NoLogo -NonInteractive -Command "New-Item -ItemType Directory -ErrorAction SilentlyContinue '$winParent'" || true
local command="[IO.File]::WriteAllBytes('$winDest', \$([System.Convert]::FromBase64String('$contents')))"
PowerShell.exe -NoProfile -NoLogo -NonInteractive -Command "$command"
}
# unique_filename /tmp/image .png
# will return /tmp/image.png, or /tmp/image_2.png, etc.
unique_filename() {
local basename=$1
local extension=${2:-}
local index=1
local suffix=""
while true; do
local filename="${basename}${suffix}${extension}"
if [[ ! -e $filename ]]; then
echo "$filename"
return
fi
suffix="_$((++index))"
done
}
capture_logs() {
if capturing_logs && [[ -d $PATH_LOGS ]]; then
local logdir
logdir=$(unique_filename "${PATH_BATS_LOGS}/${RD_TEST_FILENAME}")
mkdir -p "$logdir"
# On Linux/macOS, the symlinks to the lima logs might be dangling.
# Remove any dangling ones before doing the copy.
find -L "${PATH_LOGS}/" -type l \
-exec rm -f -- '{}' ';' \
-exec touch -- '{}' ';' \
-exec echo 'Replaced dangling symlink with empty file:' '{}' ';'
cp -LR "${PATH_LOGS}/" "$logdir"
echo "${BATS_TEST_DESCRIPTION:-teardown}" >"${logdir}/test_description"
# Capture settings.json
cp "$PATH_CONFIG_FILE" "$logdir"
foreach_profile export_profile "$logdir"
fi
}
take_screenshot() {
if taking_screenshots; then
local image_path
image_path="$(unique_filename "${PATH_BATS_LOGS}/${BATS_SUITE_TEST_NUMBER}-${BATS_TEST_DESCRIPTION}" .png)"
mkdir -p "$PATH_BATS_LOGS"
if is_macos; then
# The terminal app must have "Screen Recording" permission;
# otherwise only the desktop background is captured.
# -x option means "do not play sound"
screencapture -x "$image_path"
elif is_linux; then
if import -help &1 | grep --quiet -E 'Version:.*Magick'; then
# `import` from ImageMagick is available.
import -window root "$image_path"
elif gm import -help &1 | grep --quiet -E 'Version:.*Magick'; then
# GraphicsMagick is installed (its command is `gm`).
gm import -window root "$image_path"
fi
fi
fi
}
skip_unless_host_ip() {
if using_windows_exe; then
# Make sure the exit code is 0 even when netsh.exe or grep fails, in case errexit is in effect
HOST_IP=$(netsh.exe interface ip show addresses 'vEthernet (WSL)' | grep -Po 'IP Address:\s+\K[\d.]+' || :)
# The veth interface name changed at some time on Windows 11, so try the new name if the old one doesn't exist
if [[ -z $HOST_IP ]]; then
HOST_IP=$(netsh.exe interface ip show addresses 'vEthernet (WSL (Hyper-V firewall))' | grep -Po 'IP Address:\s+\K[\d.]+' || :)
fi
else
# TODO determine if the Lima VM has its own IP address
HOST_IP=""
fi
if [[ -z $HOST_IP ]]; then
skip "Test requires a routable host ip address"
fi
}
########################################################################
# Register one or more test commands for each k3s version in RD_K3S_VERSIONS.
# Versions can be filtered by RD_K3S_MIN and RD_K3S_MAX.
foreach_k3s_version() {
local k3s_version
for k3s_version in $RD_K3S_VERSIONS; do
if semver_lte "$RD_K3S_MIN" "$k3s_version" "$RD_K3S_MAX"; then
local cmd
for cmd in "$@"; do
bats_test_function --description "$cmd $k3s_version" -- _foreach_k3s_version "$k3s_version" "$cmd"
done
fi
done
}
_foreach_k3s_version() {
local RD_KUBERNETES_VERSION=$1
local skip_kubernetes_version
skip_kubernetes_version=$(cat "${BATS_FILE_TMPDIR}/skip-kubernetes-version" 2>/dev/null || echo none)
if [[ $skip_kubernetes_version == "$RD_KUBERNETES_VERSION" ]]; then
skip "All remaining tests for Kubernetes $RD_KUBERNETES_VERSION are skipped"
fi
"$2"
}
# Tests can call mark_k3s_version_skipped to skip the rest of the tests within
# this iteration of foreach_k3s_version.
mark_k3s_version_skipped() {
echo "$RD_KUBERNETES_VERSION" >"${BATS_FILE_TMPDIR}/skip-kubernetes-version"
}
########################################################################
_var_filename() {
# Can't use BATS_SUITE_TMPDIR because it is unset outside of @test functions
echo "${BATS_RUN_TMPDIR}/var_$1"
}
# Save env variables on disk, so they can be reloaded in different tests.
# This is mostly useful if calculating the setting takes a long time.
# Returns false if any variable was unbound, but will continue saving remaining variables.
# `save_var VAR1 VAR2`
save_var() {
local res=0
local var
for var in "$@"; do
# Using [[ -v $var ]] requires bash 4.2 but macOS only ships with 3.2
if [ -n "${!var+exists}" ]; then
printf "%s=%q\n" "$var" "${!var}" >"$(_var_filename "$var")"
else
res=1
fi
done
return $res
}
# Load env variables saved by `save_var`. Returns an error if any of the variables
# had not been saved, but will continue to try to load the remaining variables.
# `load_var VAR1 VAR2`
load_var() {
local res=0
local var
for var in "$@"; do
local file
file=$(_var_filename "$var")
if [[ -r $file ]]; then
# shellcheck disable=SC1090 # Can't follow non-constant source
source "$file"
else
res=1
fi
done
return $res
}
================================================
FILE: bats/tests/helpers/utils.bats
================================================
# bats file_tags=opensuse
load '../helpers/load'
: "${RD_INFO:=false}"
########################################################################
local_setup() {
COUNTER="${BATS_FILE_TMPDIR}/counter"
reset_counter
}
reset_counter() {
echo 0 >"$COUNTER"
SECONDS=0
}
# Increment counter file. Return success when counter >= max.
inc_counter() {
local max=${1-9999}
local counter=$(($(cat "$COUNTER") + 1))
echo $counter >"$COUNTER"
((counter >= max))
}
assert_counter_is() {
run cat "${COUNTER}"
assert_output "$1"
}
is() {
local expect=$1
# shellcheck disable=SC2086 # we want to split on whitespace
run ${BATS_TEST_DESCRIPTION}
assert_success
assert_output "$expect"
}
is_quoted() {
is "\"$1\""
}
succeeds() {
# shellcheck disable=SC2086 # we want to split on whitespace
run ${BATS_TEST_DESCRIPTION}
assert_success
}
fails() {
# shellcheck disable=SC2086 # we want to split on whitespace
run ${BATS_TEST_DESCRIPTION}
assert_failure
}
########################################################################
errexit() {
false
true
}
@test 'run() calls functions with errexit enabled' {
run errexit
assert_failure
}
########################################################################
@test 'to_lower Upper and Lower' {
is "upper and lower"
}
@test 'to_lower' {
is ""
}
@test 'to_upper 123+abc' {
is "123+ABC"
}
@test 'to_upper' {
is ""
}
########################################################################
check_truthiness() {
local predicate=$1
local value
# test true values
for value in 1 true True TRUE yes Yes YES any; do
run "$predicate" "$value"
"${assert}_success"
done
# test false values
for value in 0 false False FALSE no No NO ''; do
run "$predicate" "$value"
"${assert}_failure"
done
}
@test 'is_true' {
check_truthiness is_true
}
@test 'is_false' {
assert=refute
check_truthiness is_false
}
@test 'bool [ 0 -eq 0 ]' {
is true
}
@test 'bool [ 0 -eq 1 ]' {
is false
}
########################################################################
@test 'validate_enum OS should pass' {
run validate_enum OS darwin linux windows
assert_success
}
@test 'validate_enum FRUIT should fail' {
FRUIT=apple
run validate_enum FRUIT banana cherry pear
assert_failure
# Can't check output; it is written using "fatal":
# FRUIT=apple is not a valid setting; select from [banana cherry pear]
}
########################################################################
@test 'is_xxx' {
# Exactly one of the is_xxx functions should return true
count=0
for os in linux macos windows; do
if "is_$os"; then
((++count))
fi
done
((count == 1))
}
########################################################################
get_json_test_data() {
# The run/assert silliness is because shellcheck gets confused by direct assignment to $output
run echo '{"String":"string", "False":false, "Null":null}'
assert_success
}
@test 'jq_output extracts string value' {
get_json_test_data
run jq_output .String
assert_success
assert_output string
}
@test 'jq_output extracts "false" value' {
get_json_test_data
run jq_output .False
assert_success
assert_output false
}
@test 'jq_output cannot extract "null" value' {
get_json_test_data
run jq_output .Null
assert_failure
assert_output null
}
@test 'jq_output fails when key is not found' {
get_json_test_data
run jq_output .DoesNotExist
assert_failure
assert_output null
}
@test 'jq_output fails on null' {
output=null
run jq_output .Anything
assert_failure
assert_output null
}
@test 'jq_output fails on undefined' {
output=undefined
run jq_output .Anything
assert_failure
assert_output --partial "parse error"
}
@test 'jq_output fails on non-JSON data' {
output="This is not JSON"
run jq_output .Anything
assert_failure
assert_output --partial "parse error"
}
@test 'jq_output does not return a newline when the output is "nothing"' {
output=""
output=$(
jq_output .Anything
echo "."
)
assert_output "."
}
@test 'jq_output does return a newline when the output is the empty string' {
output='{"Empty": ""}'
output=$(
jq_output .Empty
echo "."
)
assert_output $'\n.'
}
@test 'jq must be version 1.7.1 or newer' {
run semver "$(jq --version)"
assert_success
semver_gte "$output" 1.7.1
}
########################################################################
@test 'semver a1b2.3c4.5.6d7.8.9.0' {
is 4.5.6
}
@test 'semver a1b2.3c4.5' {
is 2.3.0
}
@test 'semver a1b2c3' {
is 1.0.0
}
@test 'semver 1.2.3.4' {
is 1.2.3
}
@test 'semver 00.00.00' {
is 0.0.0
}
@test 'semver 000000' {
is 0.0.0
}
@test 'semver 0.001' {
is 0.1.0
}
@test 'semver 00100.00200.00300' {
is 100.200.300
}
@test 'semver ignores dates/times' {
run semver "1/1/70 12:00:00 version 7.8"
assert_success
assert_output 7.8.0
}
@test 'semver looks at all lines of the input' {
run semver $'Version1: 1.2\nVersion2: 3.4.5'
assert_success
assert_output 3.4.5
}
@test 'semver looks only at the first argument' {
run semver 'Version1: 1.2' 'Version2: 3.4.5'
assert_success
assert_output 1.2.0
}
@test 'semver fails when input has no number' {
run semver "Hello world"
assert_failure
}
########################################################################
@test 'semver_is_valid 1.2.3' {
succeeds
}
@test 'semver_is_valid 1.2.3-pre' {
fails
}
@test 'semver_is_valid v1.2.3' {
fails
}
@test 'semver_is_valid 1.2.' {
fails
}
@test 'semver_is_valid 1' {
fails
}
@test 'semver_is_valid 0.0.0' {
succeeds
}
@test 'semver_is_valid 01.2.3' {
fails
}
@test 'semver_is_valid 1.02.3' {
fails
}
@test 'semver_is_valid fails on trailing newline' {
run semver_is_valid $'1.2.3\n'
assert_failure
}
########################################################################
@test 'semver_eq' {
fails
}
@test 'semver_eq 1.2.3' {
succeeds
}
@test 'semver_eq 1.2.3 1.2.3' {
succeeds
}
@test 'semver_eq 1.2.3 4.5.6' {
fails
}
@test 'semver_eq 1.2.3 1.2.3 1.2.3' {
succeeds
}
@test 'semver_eq 1.2.3 1.2.3 4.5.6' {
fails
}
########################################################################
@test 'semver_neq' {
fails
}
@test 'semver_neq 1.2.3' {
succeeds
}
@test 'semver_neq 1.2.3 1.2.3' {
fails
}
@test 'semver_neq 1.2.3 4.5.6' {
succeeds
}
@test 'semver_neq 4.5.6 1.2.3' {
succeeds
}
@test 'semver_neq 1.2.3 4.5.6 1.2.3' {
fails
}
@test 'semver_neq 1.2.3 4.5.6 7.8.9' {
succeeds
}
@test 'semver_neq 4.5.6 7.8.9 1.2.3' {
succeeds
}
########################################################################
@test 'semver_lt' {
fails
}
@test 'semver_lt 1.2.3' {
succeeds
}
@test 'semver_lt 1.2.3 1.2.3' {
fails
}
@test 'semver_lt 1.2.3 4.5.6' {
succeeds
}
@test 'semver_lt 4.5.6 1.2.3' {
fails
}
@test 'semver_lt 1.2.3 4.5.6 7.8.9' {
succeeds
}
@test 'semver_lt 1.2.3 4.5.6 4.5.6' {
fails
}
########################################################################
@test 'semver_lte' {
fails
}
@test 'semver_lte 1.2.3' {
succeeds
}
@test 'semver_lte 1.2.3 1.2.3' {
succeeds
}
@test 'semver_lte 1.2.3 4.5.6' {
succeeds
}
@test 'semver_lte 4.5.6 1.2.3' {
fails
}
@test 'semver_lte 1.2.3 4.5.6 4.5.6' {
succeeds
}
@test 'semver_lte 1.2.3 4.5.6 1.2.3' {
fails
}
########################################################################
@test 'semver_gt' {
fails
}
@test 'semver_gt 1.2.3' {
succeeds
}
@test 'semver_gt 1.2.3 1.2.3' {
fails
}
@test 'semver_gt 1.2.3 4.5.6' {
fails
}
@test 'semver_gt 4.5.6 1.2.3' {
succeeds
}
@test 'semver_gt 7.8.9 4.5.6 1.2.3' {
succeeds
}
@test 'semver_gt 7.8.9 4.5.6 4.5.6' {
fails
}
########################################################################
@test 'semver_gte' {
fails
}
@test 'semver_gte 1.2.3' {
succeeds
}
@test 'semver_gte 1.2.3 1.2.3' {
succeeds
}
@test 'semver_gte 1.2.3 4.5.6' {
fails
}
@test 'semver_gte 4.5.6 1.2.3' {
succeeds
}
@test 'semver_gte 7.8.9 4.5.6 4.5.6' {
succeeds
}
@test 'semver_gte 7.8.9 4.5.6 7.8.9' {
fails
}
########################################################################
@test 'this_function' {
foo() {
this_function
}
run foo
assert_success
assert_output foo
}
@test 'calling_function' {
bar() {
baz
}
baz() {
calling_function
}
run bar
assert_success
assert_output bar
}
########################################################################
@test 'call_local_function' {
local_func() {
echo local_func
}
func() {
call_local_function
}
run func
assert_success
assert_output local_func
}
########################################################################
@test 'try returns stdout and stderr together' {
run try --max 1 sh -c 'echo foo; echo bar >&2; echo baz'
trace "output=$output"
trace "stderr=${stderr:-}"
assert_success
# output is currently re-ordered that all stderr follows all stdout
# this is subject to change
assert_line -n 0 foo
assert_line -n 2 bar
assert_line -n 1 baz
output=${stderr:-} assert_output ''
}
@test 'try supports --separate-stderr' {
run --separate-stderr try --max 1 sh -c 'echo foo; echo bar >&2; echo baz'
trace "output=$output"
trace "stderr=${stderr:-}"
assert_success
assert_output $'foo\nbaz'
output=$stderr assert_output bar
}
@test 'try will run command at least once' {
run try --max 0 --delay 15 inc_counter
assert_failure
assert_counter_is 1
# "try" should not have called "sleep 15" at all
((SECONDS < 15))
}
@test 'try will stop as soon as the command succeeds' {
run try --max 3 --delay 3 inc_counter 2
assert_success
assert_counter_is 2
# "try" should have called "sleep 3" exactly once
((SECONDS >= 3))
if ((SECONDS >= 6)); then
# maybe slow machine; try again with longer sleep
reset_counter
run try --max 3 --delay 15 inc_counter 2
assert_success
assert_counter_is 2
# "try" should have called "sleep 15" exactly once
((SECONDS >= 15))
((SECONDS < 30))
fi
}
@test 'try will return after max retries' {
run try --max 3 --delay 3 inc_counter
assert_failure
assert_counter_is 3
# "try" should have called "sleep 3" exactly twice
((SECONDS >= 6))
if ((SECONDS >= 9)); then
# maybe slow machine; try again with longer sleep
reset_counter
run try --max 3 --delay 15 inc_counter
assert_failure
assert_counter_is 3
# "try" should have called "sleep 15" exactly twice
((SECONDS >= 30))
((SECONDS < 45))
fi
}
########################################################################
@test 'json_string' {
run json_string foo\ bar\"baz\'
assert_success
assert_output "\"foo bar\\\"baz'\""
}
########################################################################
@test 'join_map echo' {
run join_map / echo usr local bin
assert_success
assert_output usr/local/bin
}
@test 'join_map false' {
run join_map / false usr local bin
assert_failure
}
@test 'join_map json_string' {
run join_map ", " json_string true "foo bar" baz:80
assert_success
assert_output '"true", "foo bar", "baz:80"'
}
@test 'join_map empty list' {
run join_map / echo
assert_success
assert_output ''
}
########################################################################
@test 'image_without_tag_as_json_string busybox' {
is_quoted busybox
}
@test 'image_without_tag_as_json_string busybox:latest' {
is_quoted busybox
}
@test 'image_without_tag_as_json_string busybox:5000' {
is_quoted busybox
}
@test 'image_without_tag_as_json_string registry.io:5000' {
is_quoted registry.io:5000
}
@test 'image_without_tag_as_json_string registry.io:5000/busybox' {
is_quoted registry.io:5000/busybox
}
@test 'image_without_tag_as_json_string registry.io:5000/busybox:8080' {
is_quoted registry.io:5000/busybox
}
########################################################################
@test 'unique_filename without extension' {
run unique_filename "$COUNTER"
assert_success
assert_output "${COUNTER}_2"
touch "$output"
run unique_filename "$COUNTER"
assert_success
assert_output "${COUNTER}_3"
}
@test 'unique_filename with extension' {
run unique_filename "$COUNTER" .png
assert_success
assert_output "${COUNTER}.png"
touch "$output"
run unique_filename "$COUNTER" .png
assert_success
assert_output "${COUNTER}_2.png"
touch "$output"
run unique_filename "$COUNTER" .png
assert_success
assert_output "${COUNTER}_3.png"
}
########################################################################
@test 'save_var existing variables' {
FOO=baz BAR=foo
save_var FOO BAR
}
@test 'load_var existing variables' {
# shellcheck disable=SC2030
FOO=bar BAR=bar
load_var FOO BAR
[[ $FOO == baz ]]
[[ $BAR == foo ]]
}
@test 'save_var mix of existing and non-existing variables' {
ONE=one TWO=two
FAILED=false
# Don't use run because it may mask errexit failures
save_var ONE DOES_NOT_EXIST TWO || FAILED=true
[[ $FAILED == true ]]
[[ $ONE == one ]]
[[ $TWO == two ]]
}
@test 'load_var mix of existing and non-existing variables' {
DOES_NOT_EXIST=false
# Can't use `run` because variable would be sourced in a subshell
load_var FOO DOES_NOT_EXIST BAR || DOES_NOT_EXIST=true
[[ $DOES_NOT_EXIST == true ]]
# shellcheck disable=SC2031
[[ $FOO == baz ]]
# shellcheck disable=SC2031
[[ $BAR == foo ]]
}
================================================
FILE: bats/tests/helpers/vm.bash
================================================
wait_for_shell() {
if is_windows; then
try --max 48 --delay 5 rdctl shell grep ID= /etc/os-release
if using_systemd; then
try --max 24 --delay 5 rdctl shell test -f /var/run/lima-boot-done
try --max 24 --delay 5 rdctl shell systemctl is-active rancher-desktop.target
try --max 48 --delay 5 rdctl shell sudo systemctl is-system-running --wait
fi
else
# Be at the root directory to avoid issues with limactl automatic
# changing to the current directory, which might not exist.
pushd /
try --max 24 --delay 5 rdctl shell test -f /var/run/lima-boot-done
# wait until sshfs mounts are done
try --max 12 --delay 5 rdctl shell test -d "$HOME/.rd"
popd || :
fi
}
pkill_by_path() {
local arg
arg=$(readlink -f "$1")
if [[ -n $arg ]]; then
pkill -f "$arg"
fi
}
clear_iptables_chain() {
local chain=$1
local rule
wsl sudo iptables -L | awk "/^Chain ${chain}/ {print \$2}" | while IFS= read -r rule; do
wsl sudo iptables -X "$rule"
done
}
flush_iptables() {
# reset default policies
wsl sudo iptables -P INPUT ACCEPT
wsl sudo iptables -P FORWARD ACCEPT
wsl sudo iptables -P OUTPUT ACCEPT
wsl sudo iptables -t nat -F
wsl sudo iptables -t mangle -F
wsl sudo iptables -F
wsl sudo iptables -X
}
# Helper to eject all existing ramdisk instances on macOS
macos_eject_ramdisk() {
local mount="$1"
run hdiutil info -plist
assert_success
# shellcheck disable=2154 # $output set by `run`
run plutil -convert json -o - - <<<"$output"
assert_success
# shellcheck disable=2016 # $mount is interpreted by jq, not shell.
local expr='.images[]."system-entities"[] | select(."mount-point" == $mount) | ."dev-entry"'
run jq_output --arg mount "$mount" "$expr"
assert_success
if [[ -z $output ]]; then
return
fi
# We don't need to worry about splitting here, it's all /dev/disk*
# However, we do need to ensure $output isn't clobbered.
# shellcheck disable=2206
local disks=($output)
local disk
for disk in "${disks[@]}"; do
CALLER="$(calling_function):umount" trace "$(umount "$disk" 2>&1 || :)"
done
for disk in "${disks[@]}"; do
CALLER="$(calling_function):hdiutil" trace "$(hdiutil eject "$disk" 2>&1 || :)"
done
}
# Set up the use of a ramdisk for application data, to make things faster.
setup_ramdisk() {
if ! using_ramdisk; then
return
fi
# Force eject any existing disks.
if is_macos; then
# Try to eject the disk, if it already exists.
macos_eject_ramdisk "$LIMA_HOME"
fi
local ramdisk_size="${RD_RAMDISK_SIZE}"
if ((ramdisk_size < ${RD_FILE_RAMDISK_SIZE:-0})); then
local fmt='%s requires %dGB of ramdisk; disabling ramdisk for this file'
# shellcheck disable=SC2059 # The string is set the line above.
printf -v fmt "$fmt" "$BATS_TEST_FILENAME" "$RD_FILE_RAMDISK_SIZE"
printf "RD: %s\n" "$fmt" >>"$BATS_WARNING_FILE"
printf "# WARN: %s\n" "$fmt" >&3
return
fi
if is_macos; then
local sectors=$((ramdisk_size * 1024 * 1024 * 1024 / 512)) # Size, in sectors.
# hdiutil space-pads the output; strip it.
disk="$(hdiutil attach -nomount "ram://$sectors" | xargs echo)"
newfs_hfs -v 'Rancher Desktop BATS' "$disk"
mkdir -p "$LIMA_HOME"
mount -t hfs "$disk" "$LIMA_HOME"
CALLER="$(this_function):hdiutil" trace "$(hdiutil info)"
CALLER="$(this_function):df" trace "$(df -h)"
fi
}
# Remove any ramdisks
teardown_ramdisk() {
# We run this even if ramdisk is not in use, in case a previous run had
# used ramdisk.
if is_macos; then
CALLER="$(this_function):hdiutil" trace "$(hdiutil info)"
CALLER="$(this_function):df" trace "$(df -h)"
macos_eject_ramdisk "$LIMA_HOME"
fi
}
factory_reset() {
if [ "$BATS_TEST_NUMBER" -gt 1 ]; then
capture_logs
fi
if using_dev_mode; then
if is_unix; then
rdctl shutdown || :
pkill_by_path "$PATH_REPO_ROOT/node_modules" || :
pkill_by_path "$PATH_RESOURCES" || :
pkill_by_path "$LIMA_HOME" || :
else
# TODO: kill `yarn dev` instance on Windows
true
fi
fi
if is_windows && wsl true >/dev/null; then
wsl sudo ip link delete docker0 || :
wsl sudo ip link delete nerdctl0 || :
# reset iptables to original state
flush_iptables
clear_iptables_chain "CNI"
clear_iptables_chain "KUBE"
fi
rdctl reset --factory "$@"
setup_ramdisk
}
# Turn `rdctl start` arguments into `yarn dev` arguments
apify_arg() {
# TODO this should be done via autogenerated code from command-api.yaml
perl -w - "$1" <<'EOF'
# don't modify the value part after the first '=' sign
($_, my $value) = split /=/, shift, 2;
if (/^--/) {
# turn "--virtual-machine.memory-in-gb" into "--virtualMachine.memoryInGb"
s/(\w)-(\w)/$1\U$2/g;
# fixup acronyms
s/memoryInGb/memoryInGB/;
s/numberCpus/numberCPUs/;
s/--wsl/--WSL/;
}
print;
print "=$value" if $value;
EOF
}
start_container_engine() {
local args=(
--application.debug
--application.updater.enabled=false
--kubernetes.enabled=false
)
local admin_access=false
if [ -n "$RD_CONTAINER_ENGINE" ]; then
args+=(--container-engine.name="$RD_CONTAINER_ENGINE")
fi
if is_unix; then
args+=(
--application.admin-access="$admin_access"
--application.path-management-strategy rcfiles
--virtual-machine.memory-in-gb 6
--virtual-machine.mount.type="$RD_MOUNT_TYPE"
)
fi
if [ "$RD_MOUNT_TYPE" = "9p" ]; then
args+=(
--experimental.virtual-machine.mount.9p.cache-mode="$RD_9P_CACHE_MODE"
--experimental.virtual-machine.mount.9p.msize-in-kib="$RD_9P_MSIZE"
--experimental.virtual-machine.mount.9p.protocol-version="$RD_9P_PROTOCOL_VERSION"
--experimental.virtual-machine.mount.9p.security-model="$RD_9P_SECURITY_MODEL"
)
fi
if is_macos; then
if using_vz_emulation; then
args+=(--virtual-machine.type vz)
if is_macos aarch64; then
args+=(--virtual-machine.use-rosetta)
fi
else
args+=(--virtual-machine.type qemu)
fi
fi
# TODO containerEngine.allowedImages.patterns and WSL.integrations
# TODO cannot be set from the commandline yet
image_allow_list="$(bool using_image_allow_list)"
registry="docker.io"
if using_ghcr_images; then
registry="ghcr.io"
fi
if is_true "${RD_USE_PROFILE:-}"; then
if ! profile_exists; then
create_profile
fi
add_profile_int "version" 7
if is_windows; then
# Translate any dots in the distro name into $RD_PROTECTED_DOT (e.g. "Ubuntu-22.04")
# so that they are not treated as setting separator characters.
add_profile_bool "WSL.integrations.${WSL_DISTRO_NAME//./$RD_PROTECTED_DOT}" true
fi
# TODO Figure out the interaction between RD_USE_PROFILE and RD_USE_IMAGE_ALLOW_LIST!
# TODO For now we need to avoid overwriting settings that may already exist in the profile.
# add_profile_bool containerEngine.allowedImages.enabled "$image_allow_list"
# add_profile_list containerEngine.allowedImages.patterns "$registry"
else
local wsl_integrations="{}"
if is_windows; then
wsl_integrations="{\"$WSL_DISTRO_NAME\":true}"
fi
create_file "$PATH_CONFIG_FILE" <"$PATH_APP_HOME/provisioning/bats.start"
else
mkdir -p "$LIMA_HOME/_config"
cat <"$LIMA_HOME/_config/override.yaml"
provision:
- mode: system
script: |
$(sed 's/^/ /')
EOF
fi
}
get_container_engine_info() {
run ctrctl info
echo "$output"
assert_success
assert_output --partial "Server Version:"
}
docker_context_exists() {
# We don't use docker contexts on Windows
if is_windows; then
return
fi
run docker_exe context ls -q
assert_success
assert_line "$RD_DOCKER_CONTEXT"
# Ensure that the context actually exists by reading from the file.
run docker_exe context inspect "$RD_DOCKER_CONTEXT" --format '{{ .Name }}'
assert_success
assert_output "$RD_DOCKER_CONTEXT"
}
# Check if the VM is using systemd (instead of OpenRC).
using_systemd() {
[[ -n ${_RD_USING_SYSTEMD:-} ]] || load_var _RD_USING_SYSTEMD || true
if [[ -z ${_RD_USING_SYSTEMD:-} ]]; then
# `systemctl whoami` contacts the systemd init to check things, so if
# it succeeds we're using systemd. On alpine-based systems, the
# `systemctl` command would be missing so this still applies.
if rdctl shell /usr/bin/systemctl whoami &>/dev/null; then
_RD_USING_SYSTEMD=true
else
_RD_USING_SYSTEMD=false
fi
save_var _RD_USING_SYSTEMD
fi
is_true "${_RD_USING_SYSTEMD}"
}
# Manage a service in the vm.
# service_control [--ifstarted] $SERVICE start|stop|restart
service_control() {
local if_started
if [[ ${1:-} == "--ifstarted" ]]; then
if_started=$1
shift
fi
local service=$1 action=$2
if using_systemd; then
if [[ -n $ifstarted && $action == restart ]]; then
rdsudo systemctl try-restart "$service"
else
rdsudo systemctl "$action" "$service"
fi
else
# shellcheck disable=2086 # the argument may expand to nothing.
rdsudo rc-service ${if_started:-} "$service" "$action"
fi
}
get_service_pid() {
local service_name=$1
if using_systemd; then
RD_TIMEOUT=10s run rdshell systemctl show --property MainPID --value "$service_name.service"
assert_success
echo "$output"
else
RD_TIMEOUT=10s run rdshell sh -c "RC_SVCNAME=$service_name /usr/libexec/rc/bin/service_get_value pidfile"
assert_success
RD_TIMEOUT=10s rdshell cat "$output"
fi
}
assert_service_pid() {
local service_name=$1
local expected_pid=$2
run get_service_pid "$service_name"
assert_success
assert_output "$expected_pid"
}
# Check that the given service does not have the given PID. It is acceptable
# for the service to not be running.
refute_service_pid() {
local service_name=$1
local unexpected_pid=$2
run get_service_pid "$service_name"
if [ "$status" -eq 0 ]; then
refute_output "$unexpected_pid"
fi
}
assert_service_status() {
local service_name=$1
local expect=$2
if using_systemd; then
local mapped_status
case $expect in
started) mapped_status=active ;;
stopped) mapped_status=inactive ;;
*) fail "Status $expect is unsupported" ;;
esac
RD_TIMEOUT=10s run rdsudo systemctl is-active "$service_name"
# `systemctl is-active` returns 0 on active, and non-0 on non-active.
if [[ $expect == started ]]; then
assert_success
fi
assert_line "$mapped_status"
else
RD_TIMEOUT=10s run rdsudo rc-service "$service_name" status
# rc-service report non-zero status (3) when the service is stopped
if [[ $expect == started ]]; then
assert_success
fi
assert_output --partial "status: ${expect}"
fi
}
wait_for_service_status() {
local service_name=$1
local expect=$2
trace "waiting for VM to be available"
wait_for_shell
trace "waiting for ${service_name} to be ${expect}"
try --max 30 --delay 5 assert_service_status "$service_name" "$expect"
}
wait_for_container_engine() {
local CALLER
CALLER=$(this_function)
trace "waiting for api /settings to be callable"
RD_TIMEOUT=10s try --max 30 --delay 5 rdctl api /settings
if using_docker; then
wait_for_service_status docker started
trace "waiting for docker context to exist"
try --max 30 --delay 10 docker_context_exists
else
wait_for_service_status buildkitd started
fi
trace "waiting for container engine info to be available"
try --max 12 --delay 10 get_container_engine_info
}
# Wait fot the extension manager to be initialized.
wait_for_extension_manager() {
trace "waiting for extension manager to be ready"
# We want to match specific error strings, so we can't use try() directly.
local count=0 max=30 message
while true; do
run --separate-stderr rdctl api /extensions
if ((status == 0 || ++count >= max)); then
break
fi
message=$(jq_output .message)
output="$message" assert_output "503 Service Unavailable"
sleep 10
done
trace "$count/$max tries: wait_for_extension_manager"
}
# See definition of `State` in
# pkg/rancher-desktop/backend/backend.ts for an explanation of each state.
assert_backend_available() {
RD_TIMEOUT=10s run rdctl api /v1/backend_state
if ((status == 0)); then
run jq_output .vmState
case "$output" in
ERROR) return 0 ;;
STARTED) return 0 ;;
DISABLED) return 0 ;;
esac
fi
return 1
}
wait_for_backend() {
trace "waiting for backend to be available"
try --max 60 --delay 10 assert_backend_available
}
================================================
FILE: bats/tests/k8s/enable-disable-k8s.bats
================================================
# Test case 8, 13, 22
load '../helpers/load'
@test 'factory reset' {
factory_reset
}
verify_k8s_is_running() {
wait_for_container_engine
wait_for_service_status k3s started
}
@test 'start rancher desktop with kubernetes enabled' {
start_kubernetes
wait_for_kubelet
verify_k8s_is_running
}
@test 'disable kubernetes' {
rdctl set --kubernetes.enabled=false
wait_for_container_engine
wait_for_service_status k3s stopped
}
@test 're-enable kubernetes' {
rdctl set --kubernetes.enabled=true
wait_for_kubelet
verify_k8s_is_running
}
================================================
FILE: bats/tests/k8s/foreach-k3s-version.bats
================================================
load '../helpers/load'
wait_for_dns() {
try assert_pod_containers_are_running \
--namespace kube-system \
--selector k8s-app=kube-dns
}
foreach_k3s_version \
factory_reset \
start_kubernetes \
wait_for_kubelet \
wait_for_dns
================================================
FILE: bats/tests/k8s/helm-install-rancher.bats
================================================
# Test case 11 & 12
# bats file_tags=opensuse
load '../helpers/load'
RD_FILE_RAMDISK_SIZE=12 # We need more disk to run the Rancher image.
local_setup() {
needs_port 443
}
# Check that the rancher-latest/rancher helm chart at the given version is
# supported on the current Kubernetes version (as determined by
# $RD_KUBERNETES_VERSION)
is_rancher_chart_compatible() {
local chart_version=$1
run helm show chart rancher-latest/rancher --version "$chart_version"
assert_success
run awk '/^kubeVersion:/ { $1 = ""; print }' <<<"$output"
assert_success
# We only support kubeVersion of form "< x.y.z"
assert_output --regexp '^[[:space:]]*<[[:space:]]*[^[:space:]]+$'
run awk '{ print $2 }' <<<"$output"
assert_success
local unsupported_version=$output
semver_gt "$unsupported_version" "$RD_KUBERNETES_VERSION"
}
# Set (and save) $rancher_chart_version to $RD_RANCHER_IMAGE_TAG if it is set
# (and compatible), or otherwise the oldest chart version that supports
# $RD_KUBERNETES_VERSION.
# If no compatible chart version could be found, calls mark_k3s_version_skipped
# and fails the test.
determine_chart_version() {
local rancher_chart_version
if [[ -n $RD_RANCHER_IMAGE_TAG ]]; then
# If a version is given, check that it's compatible.
rancher_chart_version=${RD_RANCHER_IMAGE_TAG#v}
if ! is_rancher_chart_compatible "$rancher_chart_version"; then
mark_k3s_version_skipped
printf "Rancher %s is not compatible with Kubernetes %s" \
"$rancher_chart_version" "$RD_KUBERNETES_VERSION" |
fail
return
fi
save_var rancher_chart_version
return
fi
local default_version
default_version=$(rancher_image_tag)
default_version=${default_version#v}
run --separate-stderr helm search repo --versions rancher-latest/rancher --output json
assert_success
run jq_output 'map(.version).[]'
assert_success
run sort --version-sort <<<"$output"
assert_success
local versions=$output
for rancher_chart_version in $versions; do
if ! semver_is_valid "$rancher_chart_version"; then
continue # Skip invalid / RC versions.
fi
if semver_lt "$rancher_chart_version" "$default_version"; then
continue # Skip any versions older than the default version
fi
if is_rancher_chart_compatible "$rancher_chart_version"; then
# Once we find a compatible version, use it (and don't look at the
# rest of the chart versions).
trace "$(printf "Selected rancher chart version %s for Kubernetes %s" \
"$rancher_chart_version" "$RD_KUBERNETES_VERSION")"
save_var rancher_chart_version
return
fi
done
mark_k3s_version_skipped
printf "Could not find a version of rancher-latest/rancher compatible with Kubernetes %s\n" \
"$RD_KUBERNETES_VERSION" |
fail
}
deploy_rancher() {
# TODO remove `skip_unless_host_ip` once `traefik_hostname` no longer needs it
if is_windows; then
skip_unless_host_ip
fi
local rancher_chart_version
if ! load_var rancher_chart_version; then
fail "Could not restore Rancher chart version"
fi
helm upgrade \
--install cert-manager oci://quay.io/jetstack/charts/cert-manager \
--namespace cert-manager \
--set installCRDs=true \
--set "extraArgs[0]=--enable-certificate-owner-ref=true" \
--create-namespace
local host
host=$(traefik_hostname)
comment "Installing rancher $rancher_chart_version"
helm upgrade \
--install rancher rancher-latest/rancher \
--version "$rancher_chart_version" \
--namespace cattle-system \
--set hostname="$host" \
--wait \
--timeout=10m \
--create-namespace
}
verify_rancher() {
# TODO remove `skip_unless_host_ip` once `traefik_hostname` no longer needs it
if is_windows; then
skip_unless_host_ip
fi
local host
host=$(traefik_hostname)
run try --max 9 --delay 10 curl --insecure --silent --show-error "https://${host}/dashboard/auth/login"
assert_success
assert_output --partial 'src="/dashboard/'
run kubectl get secret --namespace cattle-system bootstrap-secret -o json
assert_success
assert_output --partial "bootstrapPassword"
}
uninstall_rancher() {
run helm uninstall rancher --namespace cattle-system --wait
assert_nothing
run helm uninstall cert-manager --namespace cert-manager --wait
assert_nothing
}
@test 'add helm repo' {
helm repo add jetstack https://charts.jetstack.io
helm repo add rancher-latest https://releases.rancher.com/server-charts/latest
helm repo update
}
foreach_k3s_version \
determine_chart_version \
factory_reset \
start_kubernetes \
wait_for_kubelet \
wait_for_traefik \
deploy_rancher \
verify_rancher \
uninstall_rancher
================================================
FILE: bats/tests/k8s/port-forwarding.bats
================================================
load '../helpers/load'
@test 'start k8s' {
factory_reset
start_kubernetes
wait_for_kubelet
}
@test 'deploy sample app' {
kubectl apply --filename - </dev/null; do
assert [ "$(date +%s)" -lt "$timeout" ]
sleep 1
done
# No way there's a race-condition here.
# The version was checked and written to the log file before starting k8s,
# and we have to wait a few minutes before k8s is ready and we're at the next line.
assert_file_contains "$PATH_LOGS/kube.log" "Requested kubernetes version 'moose' is not a supported version. Falling back to"
}
# on macOS it still hangs without this
@test 'shutdown' {
if is_macos; then
rdctl shutdown
fi
}
================================================
FILE: bats/tests/k8s/spinkube-npm.bats
================================================
load '../helpers/load'
local_setup_file() {
echo "$RANDOM" >"${BATS_FILE_TMPDIR}/random"
}
local_setup() {
if using_docker; then
skip "this test only works on containerd right now"
fi
if ! command -v "npm${EXE}" >/dev/null; then
skip "this test requires npm${EXE} to be installed and on the PATH"
fi
needs_port 80
MY_APP=my-app
MY_APP_NAME="${MY_APP}-$(cat "${BATS_FILE_TMPDIR}/random")"
MY_APP_IMAGE="ttl.sh/${MY_APP_NAME}:15m"
}
@test 'start k8s with spinkube' {
factory_reset
start_kubernetes \
--experimental.container-engine.web-assembly.enabled \
--experimental.kubernetes.options.spinkube
wait_for_kubelet
wait_for_traefik
}
@test 'create sample application' {
cd "$BATS_FILE_TMPDIR"
spin new --accept-defaults --template http-js "$MY_APP"
cd "$MY_APP"
"npm${EXE}" install
spin build
spin registry push "$MY_APP_IMAGE"
}
@test 'wait for spinkube operator' {
wait_for_kube_deployment_available --namespace spin-operator spin-operator-controller-manager
}
@test 'deploy app to kubernetes' {
spin kube deploy --context rancher-desktop --from "$MY_APP_IMAGE"
}
# TODO replace ingress with port-forwarding
@test 'deploy ingress' {
# TODO remove `skip_unless_host_ip` once `traefik_hostname` no longer needs it
if is_windows; then
skip_unless_host_ip
fi
local host
host=$(traefik_hostname)
kubectl apply --filename - < 0))
echo "$rtc"
}
create_bats_runtimeclass() {
provisioning_script </var/lib/rancher/k3s/server/manifests/zzzz-bats.yaml
apiVersion: node.k8s.io/v1
kind: RuntimeClass
metadata:
name: bats
handler: bats
YAML
EOF
}
@test 'start k8s without wasm support' {
factory_reset
create_bats_runtimeclass
start_kubernetes
wait_for_kubelet
}
@test 'verify no runtimeclasses have been defined' {
run try get_runtime_classes
assert_success
run jq_output --raw-output '.items[0].metadata.name'
assert_success
assert_output 'bats'
}
@test 'start k8s with wasm support' {
# TODO We should enable the wasm feature on a running app to make sure the
# TODO runtime class is defined even after k3s is initially installed.
factory_reset
create_bats_runtimeclass
start_kubernetes --experimental.container-engine.web-assembly.enabled
wait_for_kubelet
wait_for_traefik
}
@test 'verify spin runtime class has been defined (and no others)' {
run try get_runtime_classes
assert_success
rtc=$output
run jq '.items | length' <<<"$rtc"
assert_success
assert_output 2
run jq --raw-output '.items[].metadata.name' <<<"$rtc"
assert_success
assert_line 'bats'
assert_line 'spin'
}
@test 'deploy sample app' {
kubectl apply --filename - < local appdata migration is windows-only'
ROAMING_HOME="$(wslpath_from_win32_env APPDATA)/rancher-desktop"
}
@test 'factory reset' {
factory_reset
# WSL sometimes ends up not seeing deletes from Windows; force it here.
rm -rf "$PATH_CONFIG" "$ROAMING_HOME"
}
@test 'start app, create a setting, and move settings to roaming' {
start_container_engine
wait_for_container_engine
rdctl api -X PUT /settings --body '{ "version": 9, "WSL": {"integrations": { "beaker" : true }}}'
rdctl shutdown
create_file "$ROAMING_HOME/settings.json" <"$PATH_CONFIG_FILE"
rm -f "$PATH_CONFIG_FILE"
}
@test 'restart app, verify settings has been migrated' {
launch_the_application
wait_for_container_engine
run rdctl api /settings
assert_success
run jq_output .WSL.integrations.beaker
assert_success
assert_output true
}
@test 'verify the settings file exists in both Local/ and Roaming/' {
# Migration doesn't delete it from Roaming/ in case the user decides to roll back to an earlier version.
test -f "$PATH_CONFIG_FILE"
test -f "$ROAMING_HOME/settings.json"
}
@test 'verify factory-reset deletes all of Roaming/rancher-desktop' {
rdctl factory-reset
assert_not_exists "$ROAMING_HOME"
}
================================================
FILE: bats/tests/preferences/surface-invalid-args.bats
================================================
load '../helpers/load'
local_setup() {
skip_on_windows
}
@test 'initial factory reset' {
factory_reset
}
@test 'mac-specific failure for unacceptable start setting' {
if ! is_macos; then
skip 'need a mac for the --virtual-machine.type setting'
elif supports_vz_emulation; then
skip 'no error setting virtualMachine.type to "vz" on this platform'
fi
RD_NO_MODAL_DIALOGS=1 launch_the_application --virtual-machine.type vz
try --max 36 --delay 5 assert_file_contains \
"$PATH_LOGS/background.log" \
'Setting virtualMachine.type to "vz" on Intel requires macOS 13.0 (Ventura) or later.'
rdctl shutdown
}
@test 'report unrecognized options in the log file' {
if ! using_dev_mode; then
skip 'hard to get unrecognized options past rdctl-start; run this test in dev-mode'
fi
yarn dev --his-face-rings-a-bell --no-modal-dialogs &
try --max 36 --delay 5 assert_file_contains "$PATH_LOGS/settings.log" "Unrecognized command-line argument --his-face-rings-a-bell"
rdctl shutdown
}
================================================
FILE: bats/tests/preferences/verify-paths.bats
================================================
# Test case 30
load '../helpers/load'
# Ensure subshells don't inherit a path that includes ~/.rd/bin
export PATH
PATH=$(echo "$PATH" | tr ':' '\n' | grep -v /.rd/bin | tr '\n' ':')
local_setup() {
if is_windows; then
skip "test not applicable on Windows"
fi
}
@test 'factory reset' {
factory_reset
}
@test 'start app' {
start_container_engine
wait_for_container_engine
}
# Running `bash -l -c` can cause bats to hang, so close the output file descriptor with '3>&-'
@test 'bash managed' {
if command -v bash >/dev/null; then
run bash -l -c "which rdctl" 3>&-
assert_success
assert_output --partial "$HOME/.rd/bin/rdctl"
else
skip 'bash not found'
fi
}
@test 'zsh managed' {
if command -v zsh >/dev/null; then
run zsh -i -c "which rdctl"
assert_success
assert_output --partial "$HOME/.rd/bin/rdctl"
else
skip 'zsh not found'
fi
}
@test 'fish managed' {
if command -v fish >/dev/null; then
run fish -c "which rdctl"
assert_success
assert_output --partial "$HOME/.rd/bin/rdctl"
else
skip 'fish not found'
fi
}
# This bashrc test assumes that this test will succeed, but it frees us
# from sleeping after changing application.path-management-strategy
no_bashrc_path_manager() {
! grep --silent 'MANAGED BY RANCHER DESKTOP START' "$HOME/.bashrc"
}
@test 'move to manual path-management' {
rdctl set --application.path-management-strategy=manual
try --max 5 --delay 2 no_bashrc_path_manager
}
@test 'bash unmanaged' {
if command -v bash >/dev/null; then
run bash -l -c "which rdctl" 3>&-
# Can't assert success or failure because rdctl might be in a directory other than ~/.rd/bin
refute_output --partial "$HOME/.rd/bin/rdctl"
else
skip 'bash not found'
fi
}
@test 'zsh unmanaged' {
if command -v zsh >/dev/null; then
run zsh -i -c "which rdctl"
refute_output --partial "$HOME/.rd/bin/rdctl"
else
skip 'zsh not found'
fi
}
@test 'fish unmanaged' {
if command -v fish >/dev/null; then
run fish -c "which rdctl"
refute_output --partial "$HOME/.rd/bin/rdctl"
else
skip 'fish not found'
fi
}
================================================
FILE: bats/tests/preferences/verify-settings.bats
================================================
load '../helpers/load'
local_setup() {
if is_windows; then
skip "test not applicable on Windows"
fi
}
@test 'initial factory reset' {
factory_reset
}
@test 'start the app' {
start_container_engine
wait_for_container_engine
}
proxy_set() {
local field=$1
local value=$2
printf -v payload '{ "version": 10, "experimental": { "virtualMachine": { "proxy": { "%s": %s }}}}' "$field" "$value"
run rdctl api settings -X PUT --body "$payload"
assert_failure
assert_output --partial "Changing field \"experimental.virtualMachine.proxy.${field}\" via the API isn't supported"
}
@test 'complain about windows-specific vm settings' {
run rdctl api /settings
assert_success
run jq_output .experimental.virtualMachine.proxy.enabled
assert_success
assert_output false
proxy_set enabled "true"
for field in address password username; do
# Need to include the quotes for a string-value
proxy_set $field '"smorgasbord"'
done
proxy_set port -1
proxy_set noproxy '["buffalo"]'
}
@test 'ignores echoing current vm settings' {
run rdctl api /settings
assert_success
run jq_output .experimental.virtualMachine.proxy
assert_success
printf -v payload '{ "version": 10, "experimental": { "virtualMachine": { "proxy": %s }}}' "$output"
run rdctl api settings -X PUT --body "$payload"
assert_success
}
================================================
FILE: bats/tests/profile/create-profile-output.bats
================================================
load '../helpers/load'
@test 'factory reset' {
factory_reset
}
BOGUS_CONTAINERD_NAMESPACE=change-to-k8s.io
@test 'start app' {
start_container_engine
wait_for_container_engine
rdctl set --images.namespace="$BOGUS_CONTAINERD_NAMESPACE"
}
@test 'complains when no output type is specified' {
run rdctl create-profile --from-settings
assert_failure
assert_output --partial 'an "--output FORMAT" option of either "plist" or "reg" must be specified'
}
@test 'complains when an invalid output type is specified' {
run rdctl create-profile --from-settings --output=cabbage
assert_failure
assert_output --partial 'received unrecognized "--output FORMAT" option of "cabbage"; "plist" or "reg" must be specified'
}
@test 'complains when no input source is specified' {
for type in reg plist; do
run rdctl create-profile --output $type
assert_failure
assert_output --partial 'no input format specified: must specify exactly one input format of "--input FILE|-", "--body|-b STRING", or "--from-settings"'
done
}
@test 'complains when no --input or --body arg is specified' {
for type in reg plist; do
for input in input body; do
run rdctl create-profile --output "$type" --"$input"
assert_failure
assert_output --partial $"Error: flag needs an argument: --$input"
done
done
}
too_many_input_formats() {
run rdctl create-profile "$@"
assert_failure
assert_output --partial 'too many input formats specified: must specify exactly one input format of "--input FILE|-", "--body|-b STRING", or "--from-settings"'
}
@test 'complains when multiple input sources are specified' {
for type in reg plist; do
too_many_input_formats --output $type --input some-file.txt -b moose
too_many_input_formats --output $type --input some-file.txt --from-settings
too_many_input_formats --output $type --input some-file.txt -b moose --from-settings
too_many_input_formats --output $type -b moose --from-settings
done
}
@test "complains when input file doesn't exist" {
run rdctl create-profile --output reg --input /no/such/file/here
assert_failure
assert_output --partial 'Error: open /no/such/file/here:'
}
@test 'report invalid parameters for plist' {
run rdctl create-profile --output=plist --from-settings --hive=fish
assert_failure
assert_output --partial $"registry hive and type can't be specified with \"plist\""
run rdctl create-profile --output plist --from-settings --type=writer
assert_failure
assert_output --partial $"registry hive and type can't be specified with \"plist\""
}
@test 'report unrecognized output-options' {
run rdctl create-profile --output=pickle
assert_failure
assert_output --partial 'received unrecognized "--output FORMAT" option of "pickle"; "plist" or "reg" must be specified'
}
@test 'report unrecognized registry type sub-option' {
run rdctl create-profile --output=reg --hive=hklm --type=ruff --from-settings
assert_failure
assert_output --partial 'invalid registry type of "ruff" specified'
}
@test 'report unrecognized registry hive sub-option' {
run rdctl create-profile --output=reg --hive=stuff --type=locked --from-settings
assert_failure
assert_output --partial 'invalid registry hive of "stuff" specified'
}
@test 'report unrecognized registry hive and type sub-options reports only the bad hive' {
run rdctl create-profile --output=reg --hive=shelves --type=cows --from-settings
assert_failure
assert_output --partial 'invalid registry hive of "shelves" specified'
}
# Happy tests follow
# Sample input-generating functions
json_maps_and_lists() {
cat <<'EOF'
{
"kubernetes": {
"enabled": false
},
"containerEngine": {
"allowedImages": {"patterns": ["abc", "ghi", "def"] }
},
"WSL": {
"integrations": { "second": false, "first": true }
},
"application": {
"extensions": {
"allowed": {
"enabled": true,
"list": []
},
"installed": { }
}
}
}
EOF
}
export -f json_maps_and_lists
simple_json_data() {
echo '{ "kubernetes": {"version": "moose-head" }}'
}
export -f simple_json_data
# Verify that fields in this structure appear alphabetically in reg output,
# and in the same order as the settings struct in plutil output.
json_with_special_chars() {
cat <<'EOF'
{
"containerEngine": {
"name": "small-less-<-than"
},
"application": {
"extensions": {
"allowed": {
"enabled": false,
"list": ["less-than:<", "greater:>", "and:&", "d-quote:\"", "emoji:😀"]
},
"installed": {
"key-with-less-than: <": true,
"key-with-ampersand: &": true,
"key-with-greater-than: >": true,
"key-with-emoji: 🐤": false
}
}
}
}
EOF
}
export -f json_with_special_chars
assert_full_settings_registry_output() {
local hive=$1
local type=$2
assert_success
assert_output --partial "Windows Registry Editor Version 5.00"
assert_output --partial "[$hive\\SOFTWARE\\Policies\\Rancher Desktop\\$type\\application]"
assert_output --partial '"debug"=dword:1'
assert_output --partial "[$hive\\SOFTWARE\\Policies\\Rancher Desktop\\$type\\images]"
assert_output --partial '"namespace"="'${BOGUS_CONTAINERD_NAMESPACE}'"'
}
assert_full_settings_plist_output() {
assert_success
assert_output --partial ''
assert_output --partial ''
# this next line makes sense only after namespace
assert_output --partial "${BOGUS_CONTAINERD_NAMESPACE}"
}
@test 'generates registry output for hklm/defaults' {
run rdctl create-profile --output reg --from-settings
assert_full_settings_registry_output HKEY_LOCAL_MACHINE defaults
run rdctl create-profile --output reg --hive=hklm --from-settings
assert_full_settings_registry_output HKEY_LOCAL_MACHINE defaults
run rdctl create-profile --output reg --hive=HKLM --type=Defaults --from-settings
assert_full_settings_registry_output HKEY_LOCAL_MACHINE defaults
run rdctl create-profile --output reg --type=DEFAULTS --from-settings
assert_full_settings_registry_output HKEY_LOCAL_MACHINE defaults
}
@test 'generates registry output for hklm/locked' {
run rdctl create-profile --output reg --hive=Hklm --type=Locked --from-settings
assert_full_settings_registry_output HKEY_LOCAL_MACHINE locked
run rdctl create-profile --output reg --type=LOCKED --from-settings
assert_full_settings_registry_output HKEY_LOCAL_MACHINE locked
}
@test 'generates registry output for hkcu/defaults' {
run rdctl create-profile --output reg --hive=Hkcu --from-settings
assert_full_settings_registry_output HKEY_CURRENT_USER defaults
run rdctl create-profile --output reg --hive=hkcu --type=Defaults --from-settings
assert_full_settings_registry_output HKEY_CURRENT_USER defaults
}
# This next directive makes no sense.
# shellcheck disable=SC2030
@test 'generates registry output for hkcu/locked' {
run rdctl create-profile --output reg --hive=HKCU --type=locked --from-settings
assert_full_settings_registry_output HKEY_CURRENT_USER locked
}
assert_check_registry_output() {
local bashSideTemp
local winSideTemp
local testFile
local salt
local safePolicyName
assert_success
# We need to formulate /tmp as a directory both sides can see.
winSideTemp=$(wslpath -a -m /tmp) # //wsl$/distro/tmp
bashSideTemp=$(wslpath -a -u "$winSideTemp") # /tmp
testFile=test.reg
salt=$$
# Can't write into ...\Policies\ as non-administrator, so populate a different directory.
safePolicyName="fakeProfile${salt}"
# shellcheck disable=SC2001,SC2031
sed "s/Policies/${safePolicyName}/" <<<"$output" >"${bashSideTemp}/${testFile}"
reg.exe import "${winSideTemp}\\${testFile}"
reg.exe delete "HKCU\\Software\\${safePolicyName}\\Rancher Desktop" /f /va
rm "${bashSideTemp}/${testFile}"
}
@test 'validate full-setting registry output on Windows' {
if ! is_windows; then
skip "Test requires the reg utility and only works on Windows"
fi
run rdctl create-profile --output reg --hive=HKCU --type=defaults --from-settings
assert_check_registry_output
}
@test 'validate special-characters' {
if ! is_windows; then
skip "Test requires the reg utility and only works on Windows"
fi
run rdctl create-profile --output reg --hive=HKCU --type=defaults --input - <<<"$(json_with_special_chars)"
assert_check_registry_output
}
@test 'generates registry output from inline json' {
run rdctl create-profile --output reg --body '{"application": { "window": { "quitOnClose": true }}}'
assert_success
SETTINGS_VERSION=$(get_setting .version)
printf -v HEX_SETTINGS_VERSION "%x" "$SETTINGS_VERSION"
assert_output - <version$SETTINGS_VERSIONkubernetesversionmoose-head
EOF
}
@test 'generates plist output from a command-line argument' {
run rdctl create-profile --output plist --body "$(simple_json_data)"
assert_moose_head_plist_output
}
@test 'generates plist output from a file' {
run rdctl create-profile --output plist --body "$(simple_json_data)"
assert_moose_head_plist_output
}
@test 'verify plutil is ok with the generated plist output from input file' {
if ! is_macos; then
skip "Test requires the plist utility and only works on macOS"
fi
# This input form is ok here because it won't run in WSL/Windows
run rdctl create-profile --output plist --input <(simple_json_data)
assert_success
plutil -s - <<<"$output"
}
assert_complex_plist_output() {
assert_success
SETTINGS_VERSION=$(get_setting .version)
assert_output - <version$SETTINGS_VERSIONapplicationextensionsallowedenabledlistinstalledcontainerEngineallowedImagespatternsabcghidefkubernetesenabledWSLintegrationsfirstsecond
EOF
}
@test 'plist-encodes multi-string values and maps from a file' {
run rdctl create-profile --output plist --body "$(json_maps_and_lists)"
assert_complex_plist_output
}
@test 'plist-encodes multi-string values and maps from a json string' {
run rdctl create-profile --output plist --body "$(json_maps_and_lists)"
assert_complex_plist_output
}
# Actual output-testing of this input is done in `plist_test.go` -- the purpose of this test is to just
# make sure that we're generating compliant data.
@test 'verify converted special-char input is escaped and satisfies plutil' {
if ! is_macos; then
skip "Test requires the plist utility and only works on macOS"
fi
# This input form is ok here because it won't run in WSL/Windows
run rdctl create-profile --output plist --input <(json_with_special_chars)
assert_success
plutil -s - <<<"$output"
}
@test 'verify converted special-char output' {
SETTINGS_VERSION=$(get_setting .version)
run rdctl create-profile --output plist --body "$(json_with_special_chars)"
assert_success
assert_output - <version$SETTINGS_VERSIONapplicationextensionsallowedenabledlistless-than:<greater:>and:&d-quote:"emoji:😀installedkey-with-ampersand: &key-with-emoji: 🐤key-with-greater-than: >key-with-less-than: <containerEnginenamesmall-less-<-than
END
}
@test "and shutdown" {
if is_macos; then
rdctl shutdown
fi
}
================================================
FILE: bats/tests/profile/deployment.bats
================================================
load '../helpers/load'
local_setup() {
# Tell start_container_engine to store additional settings in the current
# profile and not in settings.json.
RD_USE_PROFILE=true
RD_USE_IMAGE_ALLOW_LIST=true
ALLOWED_EXTENSION_NAME="joycelin79/newman-extension"
ALLOWED_EXTENSION_TAG="0.0.7"
FORBIDDEN_EXTENSION_TAG="0.0.5"
FORBIDDEN_EXTENSION="ignatandrei/blockly-automation" # spellcheck-ignore-line
KUBERNETES_RANDOM_VERSION="1.29.5"
# profile settings should be the opposite of the default config
if using_docker; then
DEFAULTS_CONTAINER_ENGINE_NAME=containerd
else
DEFAULTS_CONTAINER_ENGINE_NAME=moby
fi
DEFAULTS_START_IN_BACKGROUND=true
DEFAULTS_KUBERNETES_VERSION="$RD_KUBERNETES_VERSION"
LOCKED_KUBERNETES_VERSION="1.27.3"
LOCKED_ALLOWED_IMAGES_ENABLED=true
LOCKED_ALLOWED_IMAGES_PATTERNS=("$ALLOWED_EXTENSION_NAME" "$IMAGE_NGINX")
LOCKED_EXTENSIONS_ALLOWED_ENABLED=true
LOCKED_EXTENSIONS_ALLOWED_LIST=("$ALLOWED_EXTENSION_NAME:$ALLOWED_EXTENSION_TAG")
}
local_teardown_file() {
foreach_profile delete_profile
}
start_app() {
# Store WSL integration and allowed images list in locked profile instead of settings.json
PROFILE_TYPE=$PROFILE_LOCKED
start_container_engine
try --max 40 --delay 5 rdctl api /settings
RD_CONTAINER_ENGINE=$(jq_output .containerEngine.name)
wait_for_container_engine
}
verify_profiles() {
local PROFILE_TYPE
for PROFILE_TYPE in "$PROFILE_LOCKED" "$PROFILE_DEFAULTS"; do
run profile_exists
"${assert}_success"
done
}
verify_settings() {
# settings from defaults profile
run get_setting .containerEngine.name
"${assert}_output" "$DEFAULTS_CONTAINER_ENGINE_NAME"
run get_setting .application.startInBackground
"${assert}_output" "$DEFAULTS_START_IN_BACKGROUND"
# settings from locked profile
run get_setting .containerEngine.allowedImages.enabled
"${assert}_output" "$LOCKED_ALLOWED_IMAGES_ENABLED"
run get_setting .containerEngine.allowedImages.patterns
"${assert}_output" --partial "${LOCKED_ALLOWED_IMAGES_PATTERNS[@]}"
run get_setting .application.extensions.allowed.enabled
"${assert}_output" "$LOCKED_EXTENSIONS_ALLOWED_ENABLED"
run get_setting .application.extensions.allowed.list
"${assert}_output" --partial "${LOCKED_EXTENSIONS_ALLOWED_LIST[@]}"
run get_setting .kubernetes.version
"${assert}_output" "$LOCKED_KUBERNETES_VERSION"
"${refute}_output" "$DEFAULTS_KUBERNETES_VERSION"
}
install_extensions() {
# Extension install doesn't work until startup is fully complete.
wait_for_backend
RD_TIMEOUT=120s run rdctl extension install "$FORBIDDEN_EXTENSION"
"${refute}_success"
RD_TIMEOUT=120s run rdctl extension install "$ALLOWED_EXTENSION_NAME:$FORBIDDEN_EXTENSION_TAG"
"${refute}_success"
RD_TIMEOUT=120s run rdctl extension install "${LOCKED_EXTENSIONS_ALLOWED_LIST[0]}"
assert_success
}
@test 'initial factory reset' {
factory_reset
}
@test 'start up with NO profiles' {
assert_not_equal "$DEFAULTS_KUBERNETES_VERSION" "$LOCKED_KUBERNETES_VERSION"
assert_not_equal "$KUBERNETES_RANDOM_VERSION" "$LOCKED_KUBERNETES_VERSION"
RD_USE_PROFILE=false
RD_USE_IMAGE_ALLOW_LIST=false
start_application
}
@test 'verify there were NO profiles created' {
before verify_profiles
}
@test 'verify default settings were applied' {
before verify_settings
}
@test 'verify all extensions can be installed' {
wait_for_kubelet
before install_extensions
}
@test 'factory reset before creating profiles' {
factory_reset
}
@test 'create profiles' {
PROFILE_TYPE=$PROFILE_LOCKED
create_profile
add_profile_int version 10
PROFILE_TYPE=$PROFILE_DEFAULTS
create_profile
add_profile_int version 10
add_profile_bool application.startInBackground "$DEFAULTS_START_IN_BACKGROUND"
verify_profiles
}
@test 'create defaults profile' {
PROFILE_TYPE=$PROFILE_DEFAULTS
add_profile_bool application.startInBackground "$DEFAULTS_START_IN_BACKGROUND"
add_profile_string containerEngine.name "$DEFAULTS_CONTAINER_ENGINE_NAME"
add_profile_string kubernetes.version "$DEFAULTS_KUBERNETES_VERSION"
}
@test 'create locked profile' {
PROFILE_TYPE="$PROFILE_LOCKED"
add_profile_bool containerEngine.allowedImages.enabled "$LOCKED_ALLOWED_IMAGES_ENABLED"
add_profile_list containerEngine.allowedImages.patterns "${LOCKED_ALLOWED_IMAGES_PATTERNS[@]}"
add_profile_bool application.extensions.allowed.enabled "$LOCKED_EXTENSIONS_ALLOWED_ENABLED"
add_profile_list application.extensions.allowed.list "${LOCKED_EXTENSIONS_ALLOWED_LIST[@]}"
add_profile_string kubernetes.version "$LOCKED_KUBERNETES_VERSION"
}
@test 'start app with new profiles' {
RD_CONTAINER_ENGINE=""
start_app
}
@test 'verify profile settings were applied' {
verify_settings
}
@test 'install only allowed extensions' {
install_extensions
}
@test 'try to change locked fields via rdctl set' {
run rdctl set --container-engine.allowed-images.enabled=false
assert_failure
assert_output --partial 'field "containerEngine.allowedImages.enabled" is locked'
run rdctl set --kubernetes.version="$KUBERNETES_RANDOM_VERSION"
assert_failure
assert_output --partial 'field "kubernetes.version" is locked'
}
api_set() {
local body version
body=$(jq ".version=10" <<<"{$1}")
rdctl api /v1/settings -X PUT --body "$body"
}
@test 'try to change locked fields via API' {
run api_set '"containerEngine": {"allowedImages": {"patterns": ["pattern1"]}}'
assert_failure
assert_output --partial 'field "containerEngine.allowedImages.patterns" is locked'
run api_set '"containerEngine": {"allowedImages": {"enabled": false}}'
assert_failure
assert_output --partial 'field "containerEngine.allowedImages.enabled" is locked'
run api_set '"application": {"extensions": {"allowed": {"enabled": false}}}'
assert_failure
assert_output --partial 'field "application.extensions.allowed.enabled" is locked'
run api_set '"application": {"extensions": {"allowed": {"list": ["pattern1"]}}}'
assert_failure
assert_output --partial 'field "application.extensions.allowed.list" is locked'
run api_set '"kubernetes": {"version": "'"$KUBERNETES_RANDOM_VERSION"'"}'
assert_failure
assert_output --partial 'field "kubernetes.version" is locked'
}
@test 'ensure locked settings are preserved' {
verify_settings
}
@test 'change defaults profile setting' {
run rdctl set --application.start-in-background=false
assert_success
run rdctl set --application.auto-start=true
assert_success
run rdctl set --kubernetes.version="$KUBERNETES_RANDOM_VERSION"
assert_failure
}
@test 'verify that the new defaults settings are applied' {
DEFAULTS_START_IN_BACKGROUND=false
verify_settings
run get_setting .application.autoStart
assert_output true
}
@test 'shutdown app' {
rdctl shutdown
}
@test 'restart app' {
RD_CONTAINER_ENGINE=""
start_app
}
@test 'verify that default profile is not applied again' {
DEFAULTS_START_IN_BACKGROUND=false
verify_settings
run get_setting .application.autoStart
assert_output true
}
@test 'shutdown Rancher Desktop' {
rdctl shutdown
}
@test 'try to change locked fields via rdctl start and watch it fail' {
launch_the_application --container-engine.allowed-images.enabled=false
if using_dev_mode; then
numTries=36
else
numTries=12
fi
try --max $numTries --delay 5 assert_file_contains "$PATH_LOGS/background.log" 'field "containerEngine.allowedImages.enabled" is locked'
# The app-launch commands are expected to fail. We wait until we see the failure
# message in the log file, but at that time the process may still be running.
# Make sure that Rancher Desktop has really stopped; otherwise `rdctl start/yarn dev` may not launch a new instance
rdctl shutdown
launch_the_application --kubernetes.version="1.16.15"
try --max $numTries --delay 5 assert_file_contains "$PATH_LOGS/background.log" 'field "kubernetes.version" is locked'
# And again verify that the app is no longer running
rdctl shutdown
}
@test 'restart application' {
RD_CONTAINER_ENGINE=""
start_app
}
@test 'ensure profile settings are preserved' {
DEFAULTS_START_IN_BACKGROUND=false
verify_settings
}
================================================
FILE: bats/tests/profile/invalid-locked-k8s-version.bats
================================================
load '../helpers/load'
local_setup() {
RD_USE_PROFILE=true
PROFILE_TYPE=$PROFILE_LOCKED
}
local_teardown_file() {
foreach_profile delete_profile
}
@test 'initial factory reset' {
factory_reset
}
@test 'create profile' {
create_profile
add_profile_int version 8
add_profile_string kubernetes.version NattyBo
}
@test 'fails to start app with an invalid locked k8s version' {
# Have to set the version field or RD will think we're trying to change a locked field.
RD_KUBERNETES_VERSION=NattyBo start_kubernetes
# Don't do wait_for_container_engine because RD will shut down in the middle
# and the function will take a long time to time out making futile queries.
# The app should exit gracefully; after that we can check for contents.
try --max 60 --delay 5 assert_file_contains "$PATH_LOGS/background.log" "Child exited"
assert_file_contains "$PATH_LOGS/background.log" "Error Starting Rancher Desktop"
assert_file_contains "$PATH_LOGS/background.log" "Locked kubernetes version 'NattyBo' isn't a valid version"
}
@test 'recreate profile with a valid k8s version' {
add_profile_string kubernetes.version v1.27.1
}
@test 'fails to start app with a specified k8s version != locked k8s version' {
factory_reset
# Have to set the version field or RD will think we're trying to change a locked field.
RD_KUBERNETES_VERSION=v1.27.2 start_kubernetes
# The app should exit gracefully; after that we can check for contents.
try --max 60 --delay 5 assert_file_contains "$PATH_LOGS/background.log" "Child exited"
assert_file_contains "$PATH_LOGS/background.log" "Error Starting Rancher Desktop"
assert_file_contains "$PATH_LOGS/background.log" 'field "kubernetes.version" is locked'
}
================================================
FILE: bats/tests/profile/wasm.bats
================================================
load '../helpers/load'
local_teardown_file() {
foreach_profile delete_profile
}
@test 'create version 10 locked profile' {
PROFILE_TYPE=$PROFILE_LOCKED
create_profile
add_profile_int version 10
}
@test 'start application' {
factory_reset
start_container_engine
wait_for_container_engine
}
@test 'WASM mode should be locked down' {
run rdctl set --experimental.container-engine.web-assembly.enabled
assert_failure
assert_output --partial 'field "experimental.containerEngine.webAssembly.enabled" is locked'
}
@test 'update locked profile to version 11' {
PROFILE_TYPE=$PROFILE_LOCKED
add_profile_int version 11
}
@test 'restart application with version 11 locked profile' {
factory_reset
start_container_engine
wait_for_backend
}
@test 'WASM mode is now unlocked' {
run rdctl set --experimental.container-engine.web-assembly.enabled
assert_success
assert_output --partial 'reconfiguring Rancher Desktop to apply changes'
}
================================================
FILE: bats/tests/registry/creds.bats
================================================
load '../helpers/load'
local_setup() {
REGISTRY_PORT="5050"
if is_windows && ! using_windows_exe; then
# TODO TODO TODO
# RD will only modify the Windows version of .docker/config.json;
# there is no WSL integration support for it. Therefore this test
# always needs to modify the Windows version and not touch the
# Linux one. This may change depending on:
# https://github.com/rancher-sandbox/rancher-desktop/issues/5523
# TODO TODO TODO
USERPROFILE="$(wslpath_from_win32_env USERPROFILE)"
fi
DOCKER_CONFIG_FILE="$USERPROFILE/.docker/config.json"
TEMP=/tmp
if is_windows; then
# We need to use a directory that exists on the Win32 filesystem
# so the ctrctl clients can correctly map the bind mounts.
# We can use host_path() on these paths because they will exist
# both here and in the rancher-desktop distro.
TEMP="$(wslpath_from_win32_env TEMP)"
fi
AUTH_DIR="$TEMP/auth"
CAROOT="$TEMP/caroot"
CERTS_DIR="$TEMP/certs"
if is_windows && using_docker; then
# BUG BUG BUG
# docker service on Windows cannot be restarted, so we can't register
# a new CA. `localhost` is an insecure registry, not requiring certs.
# https://github.com/rancher-sandbox/rancher-desktop/issues/3878
# BUG BUG BUG
REGISTRY_HOST="localhost"
else
# Determine IP address of the VM that is routable inside the VM itself.
# Essentially localhost, but needs to be a routable IP that also works
# from inside a container. Will be turned into a DNS name using sslip.io.
if is_windows; then
ipaddr="192.168.143.1"
else
# Lima uses a fixed hard-coded IP address
ipaddr="192.168.5.15"
fi
REGISTRY_HOST="registry.$ipaddr.sslip.io"
fi
REGISTRY="$REGISTRY_HOST:$REGISTRY_PORT"
}
create_registry() {
run ctrctl rm -f registry
assert_nothing
rdshell mkdir -p "$CERTS_DIR"
ctrctl run \
--detach \
--name registry \
--restart always \
-p "$REGISTRY_PORT:$REGISTRY_PORT" \
-e "REGISTRY_HTTP_ADDR=0.0.0.0:$REGISTRY_PORT" \
-v "$(host_path "$CERTS_DIR"):/certs" \
-e "REGISTRY_HTTP_TLS_CERTIFICATE=/certs/$REGISTRY_HOST.pem" \
-e "REGISTRY_HTTP_TLS_KEY=/certs/$REGISTRY_HOST-key.pem" \
"$@" \
"$IMAGE_REGISTRY"
wait_for_registry
}
wait_for_registry() {
trace "$(ctrctl ps -a)"
# registry port is forwarded to host
try --max 20 --delay 5 curl -k --silent --show-error "https://localhost:$REGISTRY_PORT/v2/_catalog"
}
using_insecure_registry() {
[ "$REGISTRY_HOST" = "localhost" ]
}
skip_for_insecure_registry() {
if using_insecure_registry; then
skip "BUG: docker on Windows can only use insecure registry"
fi
}
@test 'factory reset' {
factory_reset
rm -f "$DOCKER_CONFIG_FILE"
}
@test 'start container engine' {
start_container_engine
wait_for_shell
for dir in "$AUTH_DIR" "$CAROOT" "$CERTS_DIR"; do
rdshell rm -rf "$dir"
done
if using_image_allow_list; then
update_allowed_patterns true "$IMAGE_REGISTRY" "$REGISTRY"
fi
}
@test 'wait for container engine' {
wait_for_container_engine
}
@test 'verify credential is set correctly' {
verify_default_credStore
}
verify_default_credStore() {
local CREDHELPER_NAME
CREDHELPER_NAME="$(basename "$CRED_HELPER" .exe | sed s/^docker-credential-//)"
run jq --raw-output .credsStore "$DOCKER_CONFIG_FILE"
assert_success
assert_output "$CREDHELPER_NAME"
}
@test 'verify allowed-images config' {
run ctrctl pull --quiet "$IMAGE_BUSYBOX"
if using_image_allow_list; then
assert_failure
assert_output --regexp "(UNAUTHORIZED|Forbidden)"
else
assert_success
fi
}
@test 'create server certs for registry' {
rdsudo apk add mkcert --force-broken-world --repository https://dl-cdn.alpinelinux.org/alpine/edge/testing
rdshell mkdir -p "$CAROOT" "$CERTS_DIR"
rdshell sh -c "CAROOT=\"$CAROOT\" TRUST_STORES=none mkcert -install"
rdshell sh -c "cd \"$CERTS_DIR\"; CAROOT=\"$CAROOT\" mkcert \"$REGISTRY_HOST\""
}
@test 'pull registry image' {
ctrctl pull --quiet "$IMAGE_REGISTRY"
}
@test 'create plain registry' {
create_registry
}
@test 'tag image with registry' {
ctrctl tag "$IMAGE_REGISTRY" "$REGISTRY/registry"
}
@test 'expect push image to registry to fail because CA cert has not been installed' {
skip_for_insecure_registry
run ctrctl push "$REGISTRY/registry"
assert_failure
# we don't get cert errors when going through the proxy; they turn into 502's
assert_output --regexp "(certificate signed by unknown authority|502 Bad Gateway)"
}
@test 'install CA cert' {
skip_for_insecure_registry
rdsudo cp "$CAROOT/rootCA.pem" /usr/local/share/ca-certificates/
rdsudo update-ca-certificates
}
restart_container_engine() {
# BUG BUG BUG
# When using containerd, sometimes the container would get wedged on a
# restart; however, restarting containerd again seems to fix this.
# So we need to keep trying until the registry container is not `created`.
# BUG BUG BUG
service_control "$CONTAINER_ENGINE_SERVICE" restart
service_control --ifstarted rd-openresty restart
wait_for_container_engine
trace "$(ctrctl ps -a)"
if using_containerd; then
run ctrctl ps --filter status=created,name=registry --format '{{.Names}}'
assert_success
refute_output registry
fi
}
@test 'restart container engine to refresh certs' {
skip_for_insecure_registry
try restart_container_engine
wait_for_registry
}
@test 'expect push image to registry to succeed now' {
ctrctl push "$REGISTRY/registry"
}
@test 'create registry with basic auth' {
# note: docker htpasswd **must** use bcrypt algorithm, i.e. `htpasswd -nbB user password`
# We intentionally use single-quotes; the '$' characters are literals
# shellcheck disable=SC2016
HTPASSWD='user:$2y$05$pd/kWjYSW9x48yaPQgrl.eLn02DdMPyoYPUy/yac601k6w.okKgmG'
rdshell mkdir -p "$AUTH_DIR"
echo "$HTPASSWD" | rdshell tee "$AUTH_DIR/htpasswd" >/dev/null
create_registry \
-v "$(host_path "$AUTH_DIR"):/auth" \
-e REGISTRY_AUTH=htpasswd \
-e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd \
-e REGISTRY_AUTH_HTPASSWD_REALM="Registry Realm"
}
@test 'verify that registry requires basic auth' {
local curl_options=(--silent --show-error)
if using_insecure_registry; then
curl_options+=(--insecure)
fi
local registry_url="https://$REGISTRY/v2/_catalog"
run rdshell curl "${curl_options[@]}" "$registry_url"
assert_success
assert_output --partial '"message":"authentication required"'
run rdshell curl "${curl_options[@]}" --user user:password "$registry_url"
assert_success
assert_output '{"repositories":[]}'
}
@test 'verify that pushing fails when not logged in' {
run bash -c "echo \"$REGISTRY\" | \"$CRED_HELPER\" erase"
assert_nothing
run ctrctl push "$REGISTRY/registry"
assert_failure
assert_output --regexp "(401 Unauthorized|no basic auth credentials)"
}
@test 'verify that pushing succeeds after logging in' {
run ctrctl login -u user -p password "$REGISTRY"
assert_success
assert_output --partial "Login Succeeded"
ctrctl push "$REGISTRY/registry"
}
@test 'verify credentials in host cred store' {
run bash -c "echo \"$REGISTRY\" | \"$CRED_HELPER\" get"
assert_success
assert_output --partial '"Secret":"password"'
ctrctl logout "$REGISTRY"
run bash -c "echo \"$REGISTRY\" | \"$CRED_HELPER\" get"
refute_output --partial '"Secret":"password"'
}
@test 'verify the docker-desktop credential helper is replaced with the rancher-desktop default' {
factory_reset
create_file "$DOCKER_CONFIG_FILE" <<<'{ "credsStore": "desktop" }'
start_container_engine
wait_for_container_engine
verify_default_credStore
}
================================================
FILE: bats/tests/snapshots/create-use-snapshot.bats
================================================
load '../helpers/load'
local_setup() {
SNAPSHOT=the-ubiquitous-flounder
}
@test 'factory reset and delete all the snapshots' {
delete_all_snapshots
factory_reset
}
@test 'start up in moby' {
RD_CONTAINER_ENGINE=moby
start_kubernetes
wait_for_container_engine
wait_for_kubelet
wait_for_backend
}
@test 'push an nginx pod and verify' {
kubectl run nginx --image="$IMAGE_NGINX" --port=8080
try --max 48 --delay 5 running_nginx
}
@test 'shutdown, make a snapshot, and run factory-reset' {
rdctl shutdown
snapshot_description="first snapshot"
rdctl snapshot create "$SNAPSHOT" --description "$snapshot_description"
run rdctl snapshot list
assert_success
assert_output --partial "$SNAPSHOT"
assert_output --partial "$snapshot_description"
rdctl factory-reset
}
@test 'startup, verify using new settings' {
RD_CONTAINER_ENGINE=containerd
start_kubernetes
wait_for_container_engine
wait_for_kubelet
run rdctl api /settings
assert_success
run jq_output .containerEngine.name
assert_success
assert_output --partial containerd
run kubectl get pods -A
assert_success
refute_output --regexp 'default.*nginx.*Running'
}
# This should be one long test because if `snapshot restore` fails there's no point starting up
@test 'shutdown, restore, restart and verify snapshot state' {
rdctl shutdown
run rdctl snapshot restore "$SNAPSHOT"
assert_success
refute_output --partial fail
launch_the_application
# Keep this variable in sync with the current setting so the wait_for commands work
RD_CONTAINER_ENGINE=moby
wait_for_container_engine
wait_for_kubelet
run rdctl api /settings
assert_success
run jq_output .containerEngine.name
assert_success
assert_output moby
try --max 48 --delay 5 running_nginx
}
@test 'verify identification errors' {
for action in restore delete; do
run rdctl snapshot "$action" 'the-nomadic-pond'
assert_failure
assert_output --partial "Error: failed to $action snapshot \"the-nomadic-pond\": can't find snapshot \"the-nomadic-pond\""
run rdctl snapshot "$action" 'the-nomadic-pond' --json
assert_failure
run jq_output '.error'
assert_success
assert_output "failed to $action snapshot \"the-nomadic-pond\": can't find snapshot \"the-nomadic-pond\""
done
}
@test "attempt to create a snapshot with an existing name is flagged and doesn't do factory-reset" {
# Shutdown RD for faster snapshot creation
# Also verify that the failed creation doesn't trigger a factory-reset and remove settings.json
rdctl shutdown
assert_exists "$PATH_CONFIG_FILE"
run rdctl snapshot create "$SNAPSHOT" --json
assert_failure
run jq_output '.error'
assert_success
assert_output "name \"$SNAPSHOT\" already exists"
assert_exists "$PATH_CONFIG_FILE"
}
@test 'rejects attempts to create a snapshot with different description sources' {
run rdctl snapshot create --description abc --description-from my-sad-file my-happy-snapshot-2
assert_failure
assert_output --partial "Error: can't specify more than one option from \"--description\" and \"--description-from\""
}
@test 'can create a snapshot where proposed name is a current ID' {
run ls -1 "$PATH_SNAPSHOTS"
assert_success
refute_output ""
snapshot_id="${lines[0]}"
test -n "$snapshot_id"
snapshot_description="second snapshot made with the --description option with \\ and \" and '."
rdctl snapshot create "$snapshot_id" --description "$snapshot_description"
run rdctl snapshot list --json
assert_success
run jq_output "select(.name == \"$snapshot_id\").description"
assert_success
assert_output "$snapshot_description"
# And we can delete that snapshot
run rdctl snapshot delete "$snapshot_id" --json
assert_success
assert_output ""
}
@test 'very long descriptions are truncated in the table view' {
snapshot_name=armadillo_farm
description_part="very long description names are truncated in the table view"
long_description="$description_part, repeat: $description_part"
rdctl snapshot create "$snapshot_name" --description "$long_description"
run rdctl snapshot list --json
assert_success
run jq_output "select(.name == \"$snapshot_name\").description"
assert_success
assert_output "$long_description"
run rdctl snapshot list
assert_success
run grep "$snapshot_name" <<<"$output"
assert_success
# Shouldn't have the whole description, but part of it
refute_output --partial "$long_description"
assert_output --partial "$description_part"
}
@test 'table view truncates descriptions at an internal newline' {
snapshot_name=retinal_asparagus
newline=$'\n'
part1="there's a new"
description="${part1}${newline}line somewhere in this description"
rdctl snapshot create "$snapshot_name" --description "$description"
run rdctl snapshot list --json
assert_success
run jq_output "select(.name == \"$snapshot_name\").description"
assert_success
assert_output "$description"
run rdctl snapshot list
assert_success
run grep "$snapshot_name" <<<"$output"
assert_success
# Shouldn't have the whole description, but part of it
refute_output --partial "$description"
assert_output --partial "${part1}…"
}
@test "factory-reset doesn't delete a non-empty snapshots directory" {
rdctl factory-reset
assert_exists "$PATH_SNAPSHOTS"
}
@test 'factory-reset does delete an empty snapshots directory' {
delete_all_snapshots
rdctl factory-reset
assert_not_exists "$PATH_SNAPSHOTS"
}
running_nginx() {
run kubectl get pods -A
assert_success
assert_output --regexp 'default.*nginx.*Running'
}
================================================
FILE: bats/tests/snapshots/restore-snapshot-after-factory-reset.bats
================================================
load '../helpers/load'
local_setup() {
SNAPSHOT=some-test-snapshot-name
}
@test 'factory reset and delete all the snapshots' {
delete_all_snapshots
factory_reset
}
@test 'start up using containerd' {
# TODO TODO TODO
# Using the container engine name to check if a snapshot has been
# restored is one of the worst possible choices, as the engine choice
# is built into so much logic in the helper functions.
# Maybe pull an image and verify the image is still there after the
# restore.
# TODO TODO TODO
RD_CONTAINER_ENGINE=containerd
start_kubernetes
wait_for_container_engine
wait_for_kubelet
wait_for_backend
}
@test 'shut down and make a snapshot' {
rdctl shutdown
rdctl snapshot create "$SNAPSHOT"
run rdctl snapshot list
assert_success
assert_output --partial "$SNAPSHOT"
}
@test 'do a factory reset' {
rdctl factory-reset
}
@test 'restore the snapshot without starting up first' {
run rdctl snapshot restore "$SNAPSHOT"
assert_success
}
@test 'start back up' {
# don't provide a --container-engine.name argument to `rdctl start`
RD_CONTAINER_ENGINE=""
# don't create settings.json file
RD_USE_PROFILE=true
start_kubernetes
unset RD_USE_PROFILE
# make sure we are not waiting for the docker context to be created
RD_CONTAINER_ENGINE="containerd"
wait_for_container_engine
wait_for_kubelet
wait_for_backend
}
@test 'verify that we are running containerd' {
run rdctl api /settings
assert_success
run jq_output .containerEngine.name
assert_success
assert_output --partial containerd
}
@test 'delete the snapshot and verify there are no others' {
rdctl snapshot delete "$SNAPSHOT"
run rdctl snapshot list --json
assert_success
assert_output ''
}
================================================
FILE: bats/tests/snapshots/test-snapshot-list.bats
================================================
load '../helpers/load'
local_setup() {
skip_on_windows "snapshots test not applicable on Windows"
NON_ALNUM_SNAPSHOT_NAME='@#$%'
MULTI_WORD_SNAPSHOT_NAME='=with '\''single'\'' and "double" quotes, /slashes/, and \backslashes\.'
EMOJI_SNAPSHOT_NAME="emoji's 😍 are cool"
NON_ALNUM_DESCRIPTION='description for non-alnum-snapshot-name'
MULTI_WORD_DESCRIPTION='description for multi-word-snapshot-name'
EMOJI_DESCRIPTION='description for emoji-snapshot-name'
TEMP=$BATS_FILE_TMPDIR
if is_windows; then
TEMP="$(wslpath_from_win32_env TEMP)"
fi
}
@test 'factory reset and delete all the snapshots' {
delete_all_snapshots
factory_reset
}
# This test ensures that we have something to take a snapshot of, because appHome might not exist.
@test 'start up' {
start_kubernetes
wait_for_container_engine
wait_for_kubelet
}
@test 'verify empty snapshot-list output' {
run rdctl snapshot list --json
assert_success
assert_output ''
run rdctl snapshot list
assert_success
assert_output 'No snapshots present.'
}
@test 'create three snapshots with RD turned off, spaced every 5 seconds' {
# It's much faster to create snapshots when RD isn't running.
rdctl shutdown
# Sleep 5 seconds after creating each snapshot so later we can verify
# that the differences in each snapshot's creation time makes sense.
rdctl snapshot create --description-from - "$NON_ALNUM_SNAPSHOT_NAME" <<<"$NON_ALNUM_DESCRIPTION"
sleep 5
rdctl snapshot create --description-from - "$MULTI_WORD_SNAPSHOT_NAME" <<<"$MULTI_WORD_DESCRIPTION"
sleep 5
DESC_FILE="$TEMP/emoji-snapshot-description.txt"
echo "$EMOJI_DESCRIPTION" >"$DESC_FILE"
rdctl snapshot create --description-from "$DESC_FILE" "$EMOJI_SNAPSHOT_NAME"
}
created() {
local name
name=$(json_string "$1")
jq_output "select(.name == $name).created"
}
@test 'verify snapshot-list output with snapshots' {
run rdctl snapshot list --json
assert_success
DATE1=$(created "$MULTI_WORD_SNAPSHOT_NAME")
DATE2=$(created "$EMOJI_SNAPSHOT_NAME")
if is_macos; then
TIME1=$(/bin/date -jf "%Y-%m-%dT%H:%M:%S" "$DATE1" +%s 2>/dev/null)
TIME2=$(/bin/date -jf "%Y-%m-%dT%H:%M:%S" "$DATE2" +%s 2>/dev/null)
elif is_linux; then
TIME1=$(date --date="$DATE1" +%s)
TIME2=$(date --date="$DATE2" +%s)
fi
# This is all we can assert, because we don't have an upper bound for the time
# between the two `snapshot create's`, and we don't have info on fractions of a second,
# so a difference of 4.9999 could show up as 4
((TIME2 - TIME1 > 4))
run rdctl snapshot list
assert_success
assert_output --partial "$NON_ALNUM_SNAPSHOT_NAME"
assert_output --partial "$MULTI_WORD_SNAPSHOT_NAME"
assert_output --partial "$EMOJI_SNAPSHOT_NAME"
assert_output --partial "$NON_ALNUM_DESCRIPTION"
assert_output --partial "$MULTI_WORD_DESCRIPTION"
assert_output --partial "$EMOJI_DESCRIPTION"
}
@test 'verify k8s is off' {
start_container_engine
wait_for_container_engine
wait_for_backend
run rdctl api /v1/settings
assert_success
run jq_output .kubernetes.enabled
assert_success
assert_output "false"
}
@test 'create a snapshot with k8s off' {
# This tests that wait_for_backend accepts the DISABLED state as a final state.
rdctl snapshot create anime-walnut-festival
wait_for_container_engine
wait_for_backend
}
@test 'and verify the new snapshot is listed' {
run rdctl snapshot list
assert_success
assert_output --partial anime-walnut-festival
}
@test 'and clean up' {
delete_all_snapshots
run rdctl snapshot list
assert_success
assert_output 'No snapshots present.'
}
================================================
FILE: bats/tests/snapshots/test_rdctl_snapshot.bats
================================================
load '../helpers/load'
# TODO: Uncomment this test when snapshots go unhidden.
#@test 'snapshot shows up in general help' {
# run rdctl --help
# assert_success
# assert_output -partial snapshot
#}
@test 'complain about missing argument' {
# These test the rdctl cmd layer, can't be easily unit-tested
for arg in create restore delete; do
run rdctl snapshot "$arg"
assert_failure
done
}
================================================
FILE: bats/tests/utils/rdctl.bats
================================================
load '../helpers/load'
# Verify various operations of `rdctl`
@test 'factory reset' {
factory_reset
}
@test 'start Rancher Desktop' {
start_container_engine
wait_for_container_engine
}
@test 'rdctl info' {
run --separate-stderr rdctl info
assert_success
assert_output --partial 'Version:'
}
@test 'rdctl info --output=json' {
run --separate-stderr rdctl info --output=json
assert_success
json=$output
run jq_output .version
assert_success
assert_output --regexp '^v1\.'
output=$json
run jq_output '.["ip-address"]'
assert_success
assert_output --regexp '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$'
}
@test 'rdctl info --field version' {
run rdctl info --field version
assert_success
assert_output --regexp '^v1\.'
}
@test 'rdctl info --field ip-address' {
run rdctl info --field ip-address
assert_success
if is_windows; then
# On Windows, the IP address should be constant.
assert_output 192.168.127.2
elif is_linux; then
assert_output 192.168.5.15 # qemu SLIRP
elif is_macos; then
address=$output
if is_true "$(get_setting '.application.adminAccess')"; then
# This is provided by the user's DHCP server
output=$address assert_output --regexp '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$'
elif [[ $(get_setting .virtualMachine.type) == vz ]]; then
# macOS Virtualization.Framework NAT; should be a local address
output=$address assert_output --regexp '^192\.168\.'
# but the address should not be from the regular SLIRP range
output=$address refute_output --regexp '^192\.168\.5\.'
else
output=$address assert_output 192.168.5.15 # qemu SLIRP
fi
else
fail 'Unknown OS'
fi
}
================================================
FILE: bats/tests/utils/spin.bats
================================================
load '../helpers/load'
# Verify that enabling Wasm support will install spin plugins and templates
local_setup() {
SPIN_DATA_DIR="${PATH_APP_HOME}/spin"
}
cmd_exe() {
"${SYSTEMROOT}/system32/cmd.exe" /c "$@"
}
dir_exists() {
if using_windows_exe; then
run --separate-stderr cmd_exe if exist "$(host_path "$1")" echo True
# Output may have trailing \r
[[ $output =~ ^True ]]
else
[[ -d $1 ]]
fi
}
@test 'delete spin plugins and templates' {
if using_windows_exe; then
run cmd_exe rmdir /s /q "$(host_path "${SPIN_DATA_DIR:?}")"
assert_nothing
else
rm -rf "${SPIN_DATA_DIR:?}"
fi
}
@test 'confirm the spin directory is gone' {
run dir_exists "$SPIN_DATA_DIR"
assert_failure
}
@test 'start container engine with wasm support enabled' {
factory_reset
start_container_engine --experimental.container-engine.web-assembly.enabled
wait_for_container_engine
}
@test 'plugins are installed' {
run dir_exists "${SPIN_DATA_DIR}/plugins/kube"
assert_success
}
@test 'templates are installed' {
if using_windows_exe; then
run --separate-stderr cmd_exe dir /b "$(host_path "${SPIN_DATA_DIR}/templates")"
assert_success
else
run ls -1 "${SPIN_DATA_DIR}/templates"
assert_success
fi
assert_line --regexp "^http-go_" # from spin
assert_line --regexp "^http-js_" # from spin-js-sdk
assert_line --regexp "^http-py_" # from spin-python-sdk
}
================================================
FILE: build/electron-publisher-custom.js
================================================
const childProcess = require('child_process');
const fs = require('fs');
const path = require('path');
const url = require('url');
const util = require('util');
const electronPublish = require('electron-publish');
class LonghornPublisher extends electronPublish.Publisher {
providerName = 'longhorn';
toString() {
return '';
}
upload() {
// We're not doing any uploading here.
return Promise.resolve();
}
/**
* checkAndResolveOptions is used to resolve publisher configs, which is then
* stored in the `app-update.yml` config file shipped with the application.
*/
static async checkAndResolveOptions(options) {
// Try to auto-fill the GitHub repository info.
if (!options.owner || !options.repo) {
// Try to get the repository info from package.json
let repository;
const packagePath = path.join(path.dirname(module.path), 'package.json');
const packageData = JSON.parse(await fs.promises.readFile(packagePath, { encoding: 'utf8' }));
if (packageData.repository.url) {
repository = new url.URL(packageData.repository.url);
} else {
// Try to get the repository info from git config
const execFile = util.promisify(childProcess.execFile);
const { stdout } = await execFile('git', ['config', 'remote.origin.url']);
repository = new url.URL(stdout.trim());
}
if (repository.hostname === 'github.com') {
const [, owner, repo] = repository.pathname.replace(/\.git$/, '').split('/');
options.owner = options.owner || owner;
options.repo = options.repo || repo;
}
}
}
}
module.exports = LonghornPublisher;
================================================
FILE: build/license.rtf
================================================
{\rtf1
{\fonttbl{\f0\fmodern\fcharset0}}
\f0\fs18
{\qc
Apache License\line
Version 2.0, January 2004\line
http://www.apache.org/licenses/
\par
}
\par\ql
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\par\par
1. Definitions.\par\par
{\li200
"License" shall mean the terms and conditions for use, reproduction, and
distribution as defined by Sections 1 through 9 of this document.\par\par
"Licensor" shall mean the copyright owner or entity authorized by the copyright
owner that is granting the License.\par\par
"Legal Entity" shall mean the union of the acting entity and all other entities
that control, are controlled by, or are under common control with that entity.
For the purposes of this definition, "control" means (i) the power, direct or
indirect, to cause the direction or management of such entity, whether by
contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.\par\par
"You" (or "Your") shall mean an individual or Legal Entity exercising
permissions granted by this License.\par\par
"Source" form shall mean the preferred form for making modifications, including
but not limited to software source code, documentation source, and
configuration files.\par\par
"Object" form shall mean any form resulting from mechanical transformation or
translation of a Source form, including but not limited to compiled object
code, generated documentation, and conversions to other media types.\par\par
"Work" shall mean the work of authorship, whether in Source or Object form, made
available under the License, as indicated by a copyright notice that is
included in or attached to the work (an example is provided in the Appendix
below).\par\par
"Derivative Works" shall mean any work, whether in Source or Object form, that
is based on (or derived from) the Work and for which the editorial revisions,
annotations, elaborations, or other modifications represent, as a whole, an
original work of authorship. For the purposes of this License, Derivative Works
shall not include works that remain separable from, or merely link (or bind by
name) to the interfaces of, the Work and Derivative Works thereof.\par\par
"Contribution" shall mean any work of authorship, including the original version
of the Work and any modifications or additions to that Work or Derivative Works
thereof, that is intentionally submitted to Licensor for inclusion in the Work
by the copyright owner or by an individual or Legal Entity authorized to submit
on behalf of the copyright owner. For the purposes of this definition,
"submitted" means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems, and
issue tracking systems that are managed by, or on behalf of, the Licensor for
the purpose of discussing and improving the Work, but excluding communication
that is conspicuously marked or otherwise designated in writing by the
copyright owner as "Not a Contribution."\par\par
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf
of whom a Contribution has been received by Licensor and subsequently
incorporated within the Work.\par\par
}
2. Grant of Copyright License. Subject to the terms and conditions of this
License, each Contributor hereby grants to You a perpetual, worldwide,
non-exclusive, no-charge, royalty-free, irrevocable copyright license to
reproduce, prepare Derivative Works of, publicly display, publicly perform,
sublicense, and distribute the Work and such Derivative Works in Source or
Object form.\par\par
3. Grant of Patent License. Subject to the terms and conditions of this
License, each Contributor hereby grants to You a perpetual, worldwide,
non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this
section) patent license to make, have made, use, offer to sell, sell, import,
and otherwise transfer the Work, where such license applies only to those
patent claims licensable by such Contributor that are necessarily infringed by
their Contribution(s) alone or by combination of their Contribution(s) with the
Work to which such Contribution(s) was submitted. If You institute patent
litigation against any entity (including a cross-claim or counterclaim in a
lawsuit) alleging that the Work or a Contribution incorporated within the Work
constitutes direct or contributory patent infringement, then any patent
licenses granted to You under this License for that Work shall terminate as of
the date such litigation is filed.\par\par
4. Redistribution. You may reproduce and distribute copies of the Work
or Derivative Works thereof in any medium, with or without modifications, and
in Source or Object form, provided that You meet the following conditions:
\par\par
{\fi-100\li200
a. You must give any other recipients of the Work or Derivative Works a
copy of this License; and\par\par
b. You must cause any modified files to carry prominent notices stating
that You changed the files; and\par\par
c. You must retain, in the Source form of any Derivative Works that You
distribute, all copyright, patent, trademark, and attribution notices from the
Source form of the Work, excluding those notices that do not pertain to any
part of the Derivative Works; and\par\par
d. If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must include a
readable copy of the attribution notices contained within such NOTICE file,
excluding those notices that do not pertain to any part of the Derivative
Works, in at least one of the following places: within a NOTICE text file
distributed as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or, within a
display generated by the Derivative Works, if and wherever such third-party
notices normally appear. The contents of the NOTICE file are for informational
purposes only and do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside or as an
addendum to the NOTICE text from the Work, provided that such additional
attribution notices cannot be construed as modifying the License.\par\par
You may add Your own copyright statement to Your modifications and may provide
additional or different license terms and conditions for use, reproduction, or
distribution of Your modifications, or for any such Derivative Works as a
whole, provided Your use, reproduction, and distribution of the Work otherwise
complies with the conditions stated in this License.\par\par
}
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work by You to
the Licensor shall be under the terms and conditions of this License, without
any additional terms or conditions. Notwithstanding the above, nothing herein
shall supersede or modify the terms of any separate license agreement you may
have executed with Licensor regarding such Contributions.\par\par
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor, except as
required for reasonable and customary use in describing the origin of the Work
and reproducing the content of the NOTICE file.\par\par
7. Disclaimer of Warranty. Unless required by applicable law or agreed
to in writing, Licensor provides the Work (and each Contributor provides its
Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied, including, without limitation, any warranties
or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any risks
associated with Your exercise of permissions under this License.\par\par
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise, unless required
by applicable law (such as deliberate and grossly negligent acts) or agreed to
in writing, shall any Contributor be liable to You for damages, including any
direct, indirect, special, incidental, or consequential damages of any
character arising as a result of this License or out of the use or inability to
use the Work (including but not limited to damages for loss of goodwill, work
stoppage, computer failure or malfunction, or any and all other commercial
damages or losses), even if such Contributor has been advised of the
possibility of such damages.\par\par
9. Accepting Warranty or Additional Liability. While redistributing the
Work or Derivative Works thereof, You may choose to offer, and charge a fee
for, acceptance of support, warranty, indemnity, or other liability obligations
and/or rights consistent with this License. However, in accepting such
obligations, You may act only on Your own behalf and on Your sole
responsibility, not on behalf of any other Contributor, and only if You agree
to indemnify, defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason of your
accepting any such warranty or additional liability.\par\par
\par END OF TERMS AND CONDITIONS\par
\par
How to apply the Apache License to your work\par\par
Include a copy of the Apache License, typically in a file called LICENSE, in
your work, and consider also including a NOTICE file that references the
License.
\par
To apply the Apache License to specific files in your work, attach the following
boilerplate declaration, replacing the fields enclosed by brackets "[]" with
your own identifying information. (Don't include the brackets!) Enclose the
text in the appropriate comment syntax for the file format. We also recommend
that you include a file or class name and description of purpose on the same
"printed page" as the copyright notice for easier identification within
third-party archives.
\par
\pard
\par Copyright [yyyy] [name of copyright owner]
\par
\par Licensed under the Apache License, Version 2.0 (the "License");
\par you may not use this file except in compliance with the License.
\par You may obtain a copy of the License at
\par
\par http://www.apache.org/licenses/LICENSE-2.0
\par
\par Unless required by applicable law or agreed to in writing, software
\par distributed under the License is distributed on an "AS IS" BASIS,
\par WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
\par See the License for the specific language governing permissions and
\par limitations under the License.}
================================================
FILE: build/signing-config-mac.yaml
================================================
# This file describes the code signing configuration for macOS.
# List of entitlements.
entitlements:
# This contains the default entitlements, for files not otherwise listed.
default:
- com.apple.security.inherit
# Entitlement overrides. This is a list of overrides, each with a "paths"
# key describing which paths to override, and an "entitlements" key for the
# overriding entitlements.
overrides:
- paths:
- '' # This is the main application
entitlements:
- com.apple.security.cs.allow-jit
- paths:
- Contents/Resources/resources/darwin/lima/bin/limactl
entitlements:
- com.apple.security.virtualization
- paths:
- Contents/Resources/resources/darwin/lima/bin/qemu-system-aarch64
- Contents/Resources/resources/darwin/lima/bin/qemu-system-x86_64
entitlements:
- com.apple.security.cs.allow-jit
- com.apple.security.hypervisor
- paths:
- Contents/Resources/resources/darwin/internal/spin
entitlements:
- com.apple.security.cs.allow-unsigned-executable-memory
- paths:
- Contents/Frameworks/Rancher Desktop Helper (GPU).app
- Contents/Frameworks/Rancher Desktop Helper (Renderer).app
entitlements:
- com.apple.security.cs.allow-jit
- paths:
- Contents/Frameworks/Rancher Desktop Helper (Plugin).app
entitlements:
- com.apple.security.cs.allow-unsigned-executable-memory
- com.apple.security.cs.disable-library-validation
# List of launch constraints.
# This is similar to entitlements, but has no default: it's just a list of
# paths, plus matching "self", "parent", and "responsible" constraints.
constraints:
- paths:
- Contents/Resources/resources/darwin/lima/bin/limactl
self:
team-identifier: '${AC_TEAMID}'
# A list of files/directories to remove before signing.
remove:
- Contents/build
- Contents/electron-builder.yml
================================================
FILE: build/signing-config-win.yaml
================================================
# This file describes the code signing configuration for Windows.
# The key is a directory name, relative to the unpacked zip file.
# The value is an array of files in that directory to sign, or an explicit
# negation (prefixed with "!"). Any files not listed is an error.
.:
- Rancher Desktop.exe
- wix-custom-action.dll
- '!dxcompiler.dll' # spellcheck-ignore-line
- '!ffmpeg.dll'
- '!libEGL.dll' # spellcheck-ignore-line
- '!libGLESv2.dll' # spellcheck-ignore-line
- '!vk_swiftshader.dll' # spellcheck-ignore-line
- '!vulkan-1.dll' # spellcheck-ignore-line
resources/resources/win32/bin:
- docker.exe
- docker-credential-none.exe
- nerdctl.exe
- rdctl.exe
- spin.exe
- '!docker-credential-ecr-login.exe'
- '!docker-credential-wincred.exe'
- '!helm.exe'
- '!kubectl.exe'
- '!kuberlr.exe'
resources/resources/win32/docker-cli-plugins:
- '!docker-buildx.exe'
- '!docker-compose.exe'
resources/resources/win32/internal:
- host-switch.exe
- steve.exe
- wsl-helper.exe
- '!spin.exe'
================================================
FILE: build/wix/dialogs.wxs
================================================
!(loc.Error_WSLNotInstalled)WIXUI_EXITDIALOGOPTIONALCHECKBOX = 1 and NOT Installed111111Installed AND NOT RESUME AND NOT Preselected AND NOT PATCH
================================================
FILE: build/wix/main.wxs
================================================
= 603 ]]>
NOT WSLINSTALLED AND NOT Installed
(NOT WSLINSTALLED OR NOT MSIINSTALLPERUSER) AND NOT Installed
NOT WSLINSTALLED
NOT WSLINSTALLED AND NOT Installed
NOT WSLINSTALLED
NOT WSLINSTALLED AND NOT Installed
WSLINSTALLED AND WSLKERNELOUTDATED
NSISUNINSTALLCOMMAND AND NOT Installed
MSIINSTALLPERUSER = 1
1]]>
1]]>
1]]>
RDRUNAFTERINSTALL
{{ &fileList }}
================================================
FILE: build/wix/scope.wxs
================================================
================================================
FILE: build/wix/string-overrides.wxl
================================================
[ProductName] will be installed in a per-machine folder and be available for all users. This enables listening on non-loopback ports and other features that require additional privileges. You must have local Administrator privileges.[ProductName] will be installed in a per-user folder and be available just for your user account. You do not need local Administrator privileges. This will disable support for listening on non-loopback ports, and requires WSL2 to already be installed on your machine.[ProductName] cannot be installed per-user, as Windows Subsystem for Linux 2 was not found. Continuing to install [ProductName] will also install WSL2.[ProductName] requires Windows Subsystem for Linux 2 (WSL2) to be installed as a prerequisite. Please follow the instructions at https://aka.ms/wslinstall
================================================
FILE: build/wix/verify.wxs
================================================
================================================
FILE: build/wix/welcome.wxs
================================================
NOT Installed AND NOT WIX_UPGRADE_DETECTED AND NOT REMOVE
================================================
FILE: dev-app-update.yml
================================================
owner: rancher-sandbox
repo: rancher-desktop
updaterCacheDirName: rancher-desktop
provider: custom
upgradeServer: https://desktop.version.rancher.io/v1/checkupgrade
vPrefixedTagName: true
================================================
FILE: docs/development/README.md
================================================
# Developer Documentation
Note that the below table of contents may be out of date
(we may forget to update it). If you don't find what you need,
ask around!
[Information About Factory Reset](factory-reset.md)
[Feature Tracker](features.md)
[Tips for Working with OBS](obs.md)
[Linux Release Process](linux-release-process.md)
[Release Checklist](release-checklist.md)
[Signing Rancher Desktop Releases](signing.md)
[Generating Screenshots for User Documentation](../../screenshots/README.md)
[Information on how to setup and run BATS tests](../../bats/README.md)
================================================
FILE: docs/development/env.md
================================================
# Internal Rancher Desktop environment variables
These variables are used for build and development purposes; they are not meant to be set by users.
They do not form an API and may be changed or removed at any time without prior notice.
## RD_DEBUG_ENABLED=anything
Forces debug logging to always be enabled. Useful to debug first-run issues when there is no `settings.yaml` yet to set debug mode.
## RD_FORCE_UPDATES_ENABLED=anything
When set, it will force auto-update to be enabled even in `yarn dev` mode. Updates will be checked and downloaded, but **not** installed.
## RD_MOCK_MACOS_VERSION=semver
Used for testing compatibility of the app with the OS version, for upgrade responder tests, and for enabling/disabling certain parts of the preferences (related to VZ emulation mode).
## RD_UPGRADE_RESPONDER_URL=http://localhost:8314/v1/checkupgrade
Set an alternate upgrade responder endpoint for testing.
================================================
FILE: docs/development/factory-reset.md
================================================
When `rdctl reset --factory` is launched from the UI, it writes its stdout into
`TMP/rdctl-stdout.txt`
where on linux `TMP` is usually `/tmp`,
on macOS it's given by `$TMPDIR`
and on Windows by `%TEMP%`(command shell) or `$env:TEMP`(powershell).
This is most useful during development. When the UI runs in debug mode, it spawns `rdctl reset --factory` with the `--verbose` option.
We can't write the output into the `logs` directory as `reset --factory` deletes it.
================================================
FILE: docs/development/features.md
================================================
# Rancher Desktop Features
This document lists the high-level Rancher Desktop features and their current status.
| Symbol | Description |
| ------------- | ---------------- |
| :heavy_check_mark: | released |
| :calendar: | targeted for the [next] or the [later] milestone release |
| :sun_with_face:| not planned yet, but considering for a future release |
Note:
- Items under the [next] milestone are targeted for the upcoming monthly release, which usually happens on the 4th Wednesday of the month.
- Items under the [later] milestone and any spillover items from the [next] milestone are targeted for the release after.
- Items under the [next] and [later] milestones might change based on user feedback, technical challenges, etc.
[next]: https://github.com/rancher-sandbox/rancher-desktop/projects/1?card_filter_query=milestone%3Anext
[later]: https://github.com/rancher-sandbox/rancher-desktop/projects/1?card_filter_query=milestone%3Alater
### OS & Platform Support
:heavy_check_mark: Win 10/11
:heavy_check_mark: Mac (Intel)
:heavy_check_mark: Mac M1 (apple silicon)
:heavy_check_mark: Linux
:sun_with_face: Linux AArch64
:sun_with_face: Windows on AArch64
:sun_with_face: Windows Containers
### Container Engines
:heavy_check_mark: Multiple CR support (containerd, dockerd)
### Docker
:heavy_check_mark: CLI
:heavy_check_mark: Swarm
:heavy_check_mark: Compose
:heavy_check_mark: Docker-only
### Kubernetes
:heavy_check_mark: K3s bundled
:heavy_check_mark: Multiple versions support
### Bundled Tooling
:heavy_check_mark: Helm
:sun_with_face: Kubectx
:sun_with_face: [kwctl]
[kwctl]: https://github.com/kubewarden/kwctl
### Image Management
:heavy_check_mark: Build, Push, Pull & Scan images
:calendar: Registry Configuration
:sun_with_face: Registry Access Control
### Networking
:heavy_check_mark: Simple VPN
:calendar: Restricted VPN (Ex: Cisco AnyConnect)
### Host Access
:sun_with_face: GPU
:sun_with_face: USB
### Performance & System Resources
:heavy_check_mark: System resource allocation
:sun_with_face: Pause app to save power
### Security
:heavy_check_mark: Signed builds
:sun_with_face: SBOM generation for images
:sun_with_face: Image Signing
:sun_with_face: Attain SLSA Level
### Troubleshooting
:heavy_check_mark: View logs
:heavy_check_mark: Partial Reset
:heavy_check_mark: Factory Reset
### GUI/Installation
:heavy_check_mark: View Containers
:heavy_check_mark: View Images
:heavy_check_mark: Port forwarding
:heavy_check_mark: Auto updates
:heavy_check_mark: Cluster exploration - Rancher Dashboard (Preview)
:sun_with_face: Container Exploration
:sun_with_face: Configuration settings
:sun_with_face: Start/Stop/Pause Containers
:sun_with_face: Silent (No-GUI) Install
:sun_with_face: CLI/Headless mode
:calendar: Offline (air gap) mode
:heavy_check_mark: Rancher Desktop CLI aka rdctl (Preview)
### IDE Compatibility
:heavy_check_mark: VS Code extension (With dockerd(moby))
:sun_with_face: Visual Studio IDE (Needs Validation)
:sun_with_face: Eclipse (Needs Validation)
### Integration with Other Rancher Projects
:heavy_check_mark: k3s
:calendar: Rancher Dashboard
:sun_with_face: Epinio
:sun_with_face: NeuVector
:sun_with_face: Marketplace
:sun_with_face: Kubewarden
### Development
:heavy_check_mark: Open source
:heavy_check_mark: Public roadmap
================================================
FILE: docs/development/linux-release-process.md
================================================
# Linux Release Process
**Note**: please read the [OBS Tips Documentation](obs.md)
before this document. It includes information that is important to be familiar
with when working with OBS.
## When do I need to modify OBS?
OBS is set up so that you only need to act when you are releasing
a new major or minor version of Rancher Desktop. For example, when
we released 1.11.0 we had to make changes. When we released 1.11.1
nothing had to be done other than the usual checks.
## How do I modify OBS when releasing a new major or minor version?
Before you begin, you must have `osc` set up. Once you have that done,
you can create a new package for the new major-minor version. Luckily,
we don't have to create a new package from scratch: we can use the
`osc copypac` command to copy an existing package. This command has
the following signature:
```
osc copypac
```
For example, if we wanted to copy the `rancher-desktop-release-1.11`
package from the `isv:Rancher:dev` project to
`rancher-desktop-release-1.12`, also in the `isv:Rancher:dev` project,
we would run `osc copypac` as follows:
```
osc copypac isv:Rancher:dev rancher-desktop-release-1.11 isv:Rancher:dev rancher-desktop-release-1.12
```
Once this is done, you must update the `_service` file and the `Meta`
tab in the package to refer to the new major-minor version. The
easiest way to do this is via the OBS web interface, which you will
need to be logged into. Generally speaking, you can simply replace
all instances of `1.11` with `1.12` (assuming we're using the above
example). Of course, it is best to understand what you are changing -
the next section will help you with that. Once you have made these
changes, the services will run and the builds should start and
complete successfully.
Finally, you should check the results. This is important - sometimes
the build process falls over, sometimes VMs aren't available to build
your package, and so on. If you run into issues, they are usually
resolved by triggering a rebuild in the web interface. This can
be done by clicking "Trigger Services" in the left navigation bar.
Alternatively, you can trigger a rebuild for a specific package format
by clicking on that package format (i.e. AppImage) from the main page
of the package and then clicking "Trigger rebuild". You will need to
be logged into the web interface to take these actions.
You should also check that the link used to download the "latest"
AppImage *actually* downloads the latest AppImage - the link is
sometimes not updated, at least, not updated promptly.
## How do Linux releases actually *work*?
### The `dev` Channel
The `dev` channel is intended to be used by developers and perhaps
intrepid users. It corresponds to the
[isv:Rancher:dev OBS project](https://build.opensuse.org/project/show/isv:Rancher:dev).
1. A new commit is pushed to a branch of the form `main` or `release-X.Y`
(for example `release-1.2` or `release-1.11`), which triggers the
`package.yml` github actions workflow. It builds Rancher Desktop and
uploads the resultant .zip file to an S3 bucket under a name of the form
`rancher-desktop-linux-.zip`.
3. As its last step, the `package.yml` workflow triggers a service run in
the OBS package that corresponds to the branch that triggered the workflow
run.it. This causes OBS to download and unpack the .zip file that was uploaded
to S3 in step 2. It also causes OBS to pull some files related to the
package formats will build from the rancher-desktop repository.
4. The new files trigger a build in OBS.
5. Once the build is complete in OBS, the new versions of the packages are
available to users to download via `zypper install`, `apt install`, etc.
### The `stable` Channel
The `stable` channel is where actual releases are hosted. It is
intended for use by actual users. The `stable` channel corresponds
to the
[isv:Rancher:stable OBS project](https://build.opensuse.org/project/show/isv:Rancher:stable).
The `stable` build process is similar to the `dev` channel, but works
slightly differently: OBS builds are triggered by published github
releases rather than new commits on branches of a particular format.
1. A new release is published, causing the `linux-release.yml` github
actions workflow to run. This workflow fetches the linux .zip file
from the release, and uploads it to AWS S3 with a name in the format
`rancher-desktop-linux-X.Y.zip` (for example, `rancher-desktop-linux-1.12.zip`).
2. The `linux-release.yml` workflow triggers a service run in the OBS
package that corresponds to the major and minor version of the tag
of the published release. This causes OBS to download and unpack the
.zip file uploaded to S3 in step 1. It also causes OBS to pull some files
related to the package formats will build from the rancher-desktop
repository.
4. The new files trigger a build in OBS.
5. Once the build is complete in OBS, the new versions of the packages are
available to users to download via `zypper install`, `apt install`, etc.
================================================
FILE: docs/development/obs.md
================================================
# Tips for Working with OBS
This document contains information on how to use OBS effectively.
If you have not used OBS before, you should read
[Getting Started](#getting-started) and
[Important Concepts](#important-concepts) first. Then, come back to
the other sections as you begin to work with the relevant parts of OBS.
## Getting Started
The first thing you need to work with OBS is an installation of
openSUSE Leap. Tumbleweed may work, but given its bleeding-edge
nature, Leap is probably a better bet.
The reason you need an installation of openSUSE is because any
real work you do with OBS should be done using the `osc` command
line tool, which is only available on openSUSE. There *is* a web
interface, but it lacks much of the functionality that you will need.
Use it for checking on the status of your package, and possibly small
changes, but for everything else use `osc`.
## Important Concepts
There are a few concepts that one should understand in order to use OBS.
The way they work and interact can be unintuitive at first, so a brief
overview is provided here.
A **project** is the object in which you do everything in OBS.
Everything falls under projects: repositories, packages, services;
all of these things must belong to a project. Projects may have
subprojects, which are themselves full projects. You have to be an
OBS admin to create a root-level project, so our project (`Rancher`)
was created as a subproject of the `isv` root project. Projects are
referred to as each of their parent projects plus their name, all
separated by colons. So to refer to our top-level project, you use
the name `isv:Rancher`.
A **repository** is configured on a project. The best way to think of
repositories is in the context of package managers: they are a remote
endpoint from which you can download packages. There are several types of
repositories - some are true repositories in the sense that tools like
`apt` and `dnf` can be configured to use them, and others are just endpoints
you can download assets from. Also, you can configure multiple repositories
on each project. This is useful for building and serving packages of multiple
formats from the same binary or source code.
A **package** is also configured on a project. Conceptually, OBS packages
are different from packages in other contexts. In OBS, a package represents
a set of files that go into a build, such as source files and any package
metadata files (such as rpm `.spec` files). Also, from the perspective of the
user's package manager, an OBS package represents exactly one version of the
package. So if you want to provide multiple versions of the package in each
repository, you must have one OBS package for each version.
A **service** is basically a script that can be triggered in a few different
ways. A common use for services is to get the latest version of code from
version control before building and packaging that code. For more information
on services see below; also, you may find the
[documentation for services][service_documentation] helpful.
[service_documentation]: https://openbuildservice.org/help/manuals/obs-user-guide/cha.obs.source_service.html#sec.obs.sserv.about
## Service Tips
### Update your services to the latest versions
Before doing anything with services, you should ensure that you have installed
the latest versions of any services you want to work with. This is important
because the remote version of OBS (build.opensuse.org) always uses the latest
version of services - if you are working with a different version on your local
machine, you may run into issues. Also note that services do not always (ever?)
use semantic versioning despite having versions of the form `X.Y.Z`.
The repositories that openSUSE comes configured with do not contain the latest
versions of the OBS services. In order to get the latest versions you need to
add a repository:
```
zypper addrepo https://download.opensuse.org/repositories/openSUSE:/Tools/openSUSE_15.3/openSUSE:Tools.repo
zypper refresh
```
After you do this you can install/update the services you need. If you aren't
on Leap 15.3, you may have to find a different version of this repo, but this
is what works at the time of writing.
### How to find out what services are available
Services come in the form of rpm packages that can be installed via `zypper`.
In order to search your installed repos for services, simply run:
```
zypper search obs-service
```
### How to find out what configuration each service takes
Once services are installed you can look at their interface schema in order
to understand how to use them. The interface schema (as well as the source code)
are stored in the directory `/usr/lib/obs/service/`.
## Local Build Tips
### How to get around slow mirrors
When you do a local build, the first thing `osc` does is cache any dependencies
of the build. `osc` will download these dependencies from mirrors of their
repositories. Unfortunately these mirrors can be very slow. If the dependency
caching step is too slow, you can tell `osc build` to only fetch packages from
the build.opensuse.org api with the `--download-api-only` flag.
### How to skip running services before build
Use the `--no-service` flag on `osc build` for this.
### How to find the output of a local build
When you build locally, it is not always obvious where the output of the build
has been saved. To find the location of your build output, look at the text that
the build has printed at the screen. At the end of it there should be a path;
this is where you can find your built package.
## Additional Resources
- The `help-obs` and `discuss-zypp` slack channels are always friendly and helpful.
- The [OBS documentation][obs_docs] might help resolve any problems you run into.
- The output of `osc --help` and `osc --help` may be helpful.
- The [Using the Open Build Service][using_obs] may be helpful for understanding
how we build AppImages using OBS.
[obs_docs]: https://openbuildservice.org/help/manuals/obs-user-guide/
[using_obs]: https://docs.appimage.org/packaging-guide/hosted-services/opensuse-build-service.html
================================================
FILE: docs/development/release-checklist.md
================================================
## Release Checklist
- [ ] Update version number in package.json if not done after last release.
- [ ] Tag release branch. Wait for the CI to build artifacts.
- [ ] Sign windows installer.
### Sign mac installer (As there's an issue with the zip produced by the build script, we need to manually build and zip, rename the file to replace space with dot etc )
- [ ] Make sure the required env variables are set for the notarize, signing process.
- [ ] git clean, reset to make sure a clean (CI equivalent) build.
- [ ] Manually zip the installer.
- [ ] Rename installer filename to replace space with dot.
### Release Documentation
- [ ] Release notes. Update on the GitHub draft Release page.
- [ ] docs update (Help, Readme..)
- [ ] Slack Announcements
- [ ] Newsletter summary
- [ ] Update metrics, roadmap on Confluence page
### Release
- [ ] Perform smoke test on release artifacts.
- [ ] Upload mac, win release artifacts on the GitHub draft Release page.
- [ ] Update the release version for upgrade responder.
- [ ] Move from draft release to Release.
- [ ] Check the auto update functionality.
================================================
FILE: docs/development/signing.md
================================================
# Signing Releases
Normally, we build artifacts via GitHub Actions as zip files, which can then be
signed offline. This is necessary as the relevant certificates are not
available online during CI.
In general, the process involves:
1. Download the zip archive from GitHub.
**Note** The archive will be a zip within a zip. Extract one level to get
the file as generated by electron-builder.
2. Set up the signing environment; see the platform-specific sections below.
3. Run the signing tool:
```sh
yarn sign path/to/archive.zip
```
4. Look in `dist/` for the signed files (`Rancher.Desktop.Setup.msi`, etc.).
## Windows
On Windows, it is necessary to obtain a code signing certificate that can be
used with the Windows infrastructure. It is then necessary to determine the
fingerprint of the certificate, and set it as the `CSC_FINGERPRINT` environment
variable before running `yarn sign`.
### Generate a Test Certificate
For testing purposes, we can generate a certificate locally by running the
following command in PowerShell:
```powershell
New-SelfSignedCertificate `
-Type Custom `
-Subject "CN=Rancher-Sandbox, C=CA" `
-KeyUsage DigitalSignature `
-CertStoreLocation Cert:\CurrentUser\My `
-FriendlyName "Rancher-Sandbox Code Signing" `
-TextExtension @("2.5.29.37={text}1.3.6.1.5.5.7.3.3", "2.5.29.19={text}")
```
This will display the new certificate, with a `Thumbprint` column; this is the
certificate fingerprint that we will need to set as `CSC_FINGERPRINT`. If you
need to refer to the certificate later, it can be obtained by running
`ls Cert:\CurrentUser\My` in a PowerShell prompt.
### Using a Certificate Stored on a YubiKey
If you have a certificate (with private key) that is stored on a YubiKey device,
it is first necessary to install the [YubiKey Minidriver]. After plugging in
your device, you should then be able to run `certutil -scinfo -silent` to locate
the fingerprint for your desired certificate. Note that this will list all the
certificates on the device, including the chain to your certificate, and you
must search the output for your signing certificate and locate the
`Cert Hash(sha1):` entry. That hash is then used for `CSC_FINGERPRINT` above.
[YubiKey Minidriver]: https://www.yubico.com/support/download/smart-card-drivers-tools/
### Verifying the Signed Product
In explorer, right-click on the final `.exe` file, choose `Properties`, `Digital Signatures`,
and verify that `Suse LLC` is listed in the Signature List.
## macOS
On macOS, a signing certificate from Apple is required (via their developer
program). Please refer to [Apple Documentation] for details. Note that a
_Mac Development_ certificate is insufficient for notarization; it must be a
_Developer ID Application_ certificate. This will be reflected in the Common
Name of the certificate.
Launch constraints require macOS Ventura (macOS 13) or newer. This is therefore
needed for production signing.
[Apple Documentation]: https://developer.apple.com/help/account/create-certificates/create-developer-id-certificates
### Generate a test certificate
If a real certificate from Apple is unavailable, it is possible to generate a
self-signed test certificate; however, note that this wouldn't properly exercise
all of the signing code.
```sh
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem \
-keyform pem -sha256 -days 3650 -nodes -subj \
"/C=XX/ST=NA/L=Some Town/O=No Org/OU=No Unit/CN=RD Test Signing Key" \
-addext keyUsage=critical,digitalSignature \
-addext extendedKeyUsage=critical,codeSigning
security import key.pem -t priv -A
security import cert.pem -t cert -A
security set-key-partition-list -S apple-tool:,apple:,codesign: -s
security add-trusted-cert -p codeSign cert.pem
```
### Configuring Access
- Import your signing certificate into your macOS Keychain.
- Run `security find-identity -v` to locate the fingerprint of the key to use.
Export the long hex string as the `CSC_FINGERPRINT` environment variable.
- For a test certificate, use `security find-identity` without `-v`; the
certificate to use isn't valid.
For notarization, the following environment variables are also needed:
- `APPLEID`
- This is your Apple ID login; for example, `john.doe@example.com`
- `AC_PASSWORD`
- This is an application-specific password for your Apple ID; to create it:
1. Navigate to https://appleid.apple.com/account/manage
2. Click on _App-Specific Passwords_ at the bottom.
3. Create one (with a label of your choice) and copy the resulting password.
- `AC_TEAMID`
- This is the Apple Team ID. This is the _Organizational Unit (OU)_ field of
the subject of your signing certificate; for Rancher Desktop / SUSE, this is
`2Q6FHJR3H3`.
(This value can be extracted from the published application.)
### Performing signing
When signing for M1/aarch64, please set the `M1` environment variable ahead of
time as usual.
If notarization is not required, append `--skip-notarize` to the command:
```sh
yarn sign --skip-notarize path/to/archive.zip
```
This is necessary to test the signing flow (since there's no way to notarize
without the production certificate). This is also necessary to use a test
certificate (since Apple will reject it).
When using an older version of macOS (12/Monterey or older),
`--skip-constraints` is also needed to skip assigning launch constraints, as
that requires Ventura or later. This is inappropriate for the actual release.
================================================
FILE: docs/networking/windows/README.md
================================================
# Rancher Desktop Network Documentation
The table of contents below provides references to all the projects that comprise the Rancher Desktop network stack on windows platform.
- [Rancher Desktop Guest Agent](rancher-desktop-guest-agent.md)
- [Rancher Desktop Networking](rancher-desktop-networking.md)
## Feature Parity
Below is table to demonstrate the feature parity between both classic networking and tunneled networking.
Tame your developer workflow to go from Code
to URL in one step.
Epinio installs into any Kubernetes cluster to
bring your application from source code to deployment and allow for
Developers and Operators to work better together!
com.docker.extension.publisher-url: https://epinio.io
com.docker.extension.screenshots: '[{"alt": "Epinio after Installation", "url":
"https://epinio.io/images/epinio-docker-desktop-screenshot.png"}]'
org.opencontainers.image.description: Push from source to Kubernetes in one step
org.opencontainers.image.title: Epinio
org.opencontainers.image.vendor: Epinio by Krumware and SUSE
title: Epinio
logo: https://epinio.io/images/icon-epinio.svg
publisher: Epinio by Krumware and SUSE
short_description: Push from source to Kubernetes in one step
- slug: julianb90/tachometer
version: 0.1.1
containerd_compatible: true
labels:
com.docker.desktop.extension.api.version: 0.3.4
com.docker.desktop.extension.icon: https://raw.githubusercontent.com/julian-b90/tachometer/main/speedometer.png
com.docker.extension.additional-urls: '[{"title":"Issues","url":"https://github.com/julian-b90/tachometer/issues"}]'
com.docker.extension.categories: development,utility-tools
com.docker.extension.changelog: "
V 0.1.1 ### Changed
update
dependencies
update node to latest LTS 20.17.0
"
com.docker.extension.detailed-description: Extension shows real-time cpu and memory usage of containers
com.docker.extension.publisher-url: https://github.com/julian-b90/tachometer
com.docker.extension.screenshots: '[{"alt":"tachometer",
"url":"https://raw.githubusercontent.com/julian-b90/tachometer/main/screenshot.png"},
{"alt":"details view",
"url":"https://raw.githubusercontent.com/julian-b90/tachometer/main/screenshot_2.png"}]'
org.opencontainers.image.description: Extension shows real-time cpu and memory usage of containers
org.opencontainers.image.title: Tachometer
org.opencontainers.image.vendor: julian-b90
title: Tachometer
logo: https://raw.githubusercontent.com/julian-b90/tachometer/main/speedometer.png
publisher: julian-b90
short_description: Extension shows real-time cpu and memory usage of containers
- slug: docker/logs-explorer-extension
version: 0.2.5
containerd_compatible: true
labels:
com.docker.desktop.extension.api.version: ">= 0.2.3"
com.docker.desktop.extension.icon: https://docker-extension-screenshots.s3.amazonaws.com/logs-explorer-extension/icon.svg
com.docker.extension.additional-urls: "[]"
com.docker.extension.categories: utility-tools
com.docker.extension.changelog: "
Fix missing logs on Windows.
"
com.docker.extension.detailed-description: "
Logs Explorer provides deeper
insight into your logs.
✨ Key
features
Multiple filters: You can
browse logs by status or log type, or you can select individual
containers.
Advanced search
functionality:
You can search for logs that have
occurred after a certain amount of time or since a given
date.
You can use regular expressions or exact matches when
you search.
You can save each search query you enter into the
search bar to help narrow your search with “sticky” search filters. You
can save more than one query at a
time.
Improved scrolling
experience: When containers are running, scrolling is locked to
the bottom by default so you see the latest logs.
"
com.docker.extension.publisher-url: https://www.docker.com/
com.docker.extension.screenshots: '[ {"url":
"https://docker-extension-screenshots.s3.amazonaws.com/logs-explorer-extension/0.2.1/1-all-containers.png",
"alt": "View logs from all the containers"}, { "url":
"https://docker-extension-screenshots.s3.amazonaws.com/logs-explorer-extension/0.2.1/2-filters.png", "alt":
"View logs matching a filter" }, { "url":
"https://docker-extension-screenshots.s3.amazonaws.com/logs-explorer-extension/0.2.1/2-collapsed-filters.png", "alt":
"View logs with filter panel collapsed" }, { "url":
"https://docker-extension-screenshots.s3.amazonaws.com/logs-explorer-extension/0.2.1/3-expanded-rows.png", "alt":
"Expand rows" }, { "url":
"https://docker-extension-screenshots.s3.amazonaws.com/logs-explorer-extension/0.2.1/3-log-type-status-filter.png", "alt":
"View logs depending on the log type or the container
status" }, { "url":
"https://docker-extension-screenshots.s3.amazonaws.com/logs-explorer-extension/0.2.1/4-since-search-filter.png", "alt":
"View logs since a duration" }, { "url":
"https://docker-extension-screenshots.s3.amazonaws.com/logs-explorer-extension/0.2.1/5-from-search-filter.png", "alt":
"View logs from a time" }, { "url":
"https://docker-extension-screenshots.s3.amazonaws.com/logs-explorer-extension/0.2.1/6-tips.png", "alt":
"Learn tips to search logs" }, { "url":
"https://docker-extension-screenshots.s3.amazonaws.com/logs-explorer-extension/0.2.1/7-all-containers-dark-mode.png", "alt":
"Dark mode" } ]'
org.opencontainers.image.description: View all your container logs in one place
so you can debug and troubleshoot faster.
org.opencontainers.image.revision: 8351516879c55dae0d7324ce49214568307306da
org.opencontainers.image.source: https://github.com/docker/logs-explorer-extension
org.opencontainers.image.title: Logs Explorer
org.opencontainers.image.vendor: Docker Inc.
title: Logs Explorer
logo: https://docker-extension-screenshots.s3.amazonaws.com/logs-explorer-extension/icon.svg
publisher: Docker Inc.
short_description: View all your container logs in one place so you can debug
and troubleshoot faster.
- slug: prakhar1989/dive-in
version: 0.0.8
containerd_compatible: false
labels:
com.docker.desktop.extension.api.version: 0.3.0
com.docker.desktop.extension.icon: https://raw.githubusercontent.com/prakhar1989/dive-in/main/scuba.svg
com.docker.extension.additional-urls: '[{"title":"Documentation","url":"https://github.com/prakhar1989/dive-in"}]'
com.docker.extension.categories: utility-tools
com.docker.extension.changelog: First version
com.docker.extension.detailed-description:
Dive In
Explore docker
images, layer contents, and discover ways to shrink the size of your
Docker/OCI image.
com.docker.extension.publisher-url: https://prakhar.me
com.docker.extension.screenshots: '[{"alt":"main page",
"url":"https://github.com/prakhar1989/dive-in/blob/main/screenshots/1.png?raw=true"},
{"alt":"start containers",
"url":"https://github.com/prakhar1989/dive-in/blob/main/screenshots/2.png?raw=true"}]'
org.opencontainers.image.description: Explore docker images, layer contents, and
discover ways to shrink the size of your Docker/OCI image.
org.opencontainers.image.title: Dive In
org.opencontainers.image.vendor: Prakhar Srivastav
title: Dive In
logo: https://raw.githubusercontent.com/prakhar1989/dive-in/main/scuba.svg
publisher: Prakhar Srivastav
short_description: Explore docker images, layer contents, and discover ways to
shrink the size of your Docker/OCI image.
- slug: joycelin79/newman-extension
version: 0.0.7
containerd_compatible: true
labels:
com.docker.desktop.extension.api.version: ">= 0.0.1"
com.docker.desktop.extension.icon: https://voyager.postman.com/icon/icon-newman-docker-orange-postman.svg
com.docker.extension.additional-urls: '[{"title":"GitHub
Repository","url":"https://github.com/loopDelicious/docker-extension"},
{"title":"Feedback and
issues","url":"https://github.com/loopDelicious/docker-extension/issues"},
{"title":"Privacy
Policy","url":"https://www.postman.com/legal/privacy-policy"},{"title":"Terms
of Service","url":"https://www.postman.com/legal/terms"}]'
com.docker.extension.changelog:
Added metadata to provide more
information about the extension.
com.docker.extension.detailed-description:
Newman
The Postman
extension uses Newman to run collections on Docker Desktop. View results
of your API tests in a staging or production environment.
Known
issues
In some cases, depending on test collections and test
results, some buttons (expand/collapse folder, copy/paste) might be
inoperative.
com.docker.extension.publisher-url: https://www.postman.com
com.docker.extension.screenshots: '[{"alt":"View collection run results",
"url":"https://user-images.githubusercontent.com/17693714/200926587-cc817844-419d-4a39-abfa-e3b9b43881ea.png"},
{"alt":"Add Postman API key",
"url":"https://user-images.githubusercontent.com/17693714/200926910-a0fdba8d-02b0-4025-b7d6-95eb556eefa7.png"},{"alt":"Select
Postman collection to run",
"url":"https://user-images.githubusercontent.com/17693714/200926775-35e8dc5a-6c44-45de-80a8-f80430c81066.png"}]'
org.opencontainers.image.description: Run your Postman collections from Docker Desktop.
org.opencontainers.image.title: Newman
org.opencontainers.image.vendor: Postman
title: Newman
logo: https://voyager.postman.com/icon/icon-newman-docker-orange-postman.svg
publisher: Postman
short_description: Run your Postman collections from Docker Desktop.
- slug: docker/resource-usage-extension
version: 1.0.3
containerd_compatible: true
labels:
com.docker.desktop.extension.api.version: 0.3.0
com.docker.desktop.extension.icon: https://docker-extension-screenshots.s3.amazonaws.com/resource-usage-extension/icon.svg
com.docker.extension.additional-urls: ""
com.docker.extension.categories: utility-tools
com.docker.extension.changelog: Fix style on grid buttons
com.docker.extension.detailed-description: "
With Resource Usage you
can:
Find out which containers or Docker Compose
projects consume the most resources.
Monitor the evolution of
resource usage by containers over time.
See how much CPU,
Memory, Network and Disk space your containers are
using.
"
com.docker.extension.publisher-url: https://www.docker.com/
com.docker.extension.screenshots: '[ {"url":
"https://docker-extension-screenshots.s3.amazonaws.com/resource-usage-extension/1-processes.png", "alt":
"View all docker containers resources in a table"}, {"url":
"https://docker-extension-screenshots.s3.amazonaws.com/resource-usage-extension/2-graphs.png", "alt":
"View containers resource usage as graphs"} ]'
org.opencontainers.image.description: Monitor and manage live data stream for running containers.
org.opencontainers.image.revision: 1.0.3
org.opencontainers.image.source: https://github.com/docker/resource-usage-extension
org.opencontainers.image.title: Resource usage
org.opencontainers.image.vendor: Docker Inc.
title: Resource usage
logo: https://docker-extension-screenshots.s3.amazonaws.com/resource-usage-extension/icon.svg
publisher: Docker Inc.
short_description: Monitor and manage live data stream for running containers.
- slug: anchore/docker-desktop-extension
version: 0.5.1
containerd_compatible: false
labels:
com.docker.desktop.extension.api.version: ">= 0.2.3"
com.docker.desktop.extension.icon: https://user-images.githubusercontent.com/590471/164125752-b83d973c-f161-4d54-889f-352dee0ec795.svg
com.docker.extension.additional-urls: '[{"title":"Support","url":"https://github.com/anchore/docker-desktop-extension-support"}]'
com.docker.extension.screenshots: '[{"alt": "image listing", "url":
"https://user-images.githubusercontent.com/590471/164122365-efb150a9-3c97-42d2-bb46-7ba434fc21d2.png"},{"alt":
"package listing", "url":
"https://user-images.githubusercontent.com/590471/164122366-a7b89526-29c0-498c-b23b-d96667368637.png"},{"alt":
"vulnerability listing", "url":
"https://user-images.githubusercontent.com/590471/164122368-601d1ee2-a77d-4c0f-a98a-1aa68ea79d2a.png"}]'
org.opencontainers.image.description: Content and security analysis for container images
org.opencontainers.image.title: anchore
org.opencontainers.image.vendor: Anchore Inc.
title: anchore
logo: https://user-images.githubusercontent.com/590471/164125752-b83d973c-f161-4d54-889f-352dee0ec795.svg
publisher: Anchore Inc.
short_description: Content and security analysis for container images
- slug: ignatandrei/blockly-automation
version: 0.0.7
containerd_compatible: true
labels:
com.docker.desktop.extension.api.version: ">= 0.2.0"
com.docker.desktop.extension.icon: https://github.com/ignatandrei/BlocklyAutomation/wiki/imgs/logoBADocker.png
com.docker.extension.additional-urls: '[{"title":"Main
Page","url":"https://github.com/ignatandrei/BlocklyAutomation/wiki/DockerExtension"}]'
com.docker.extension.categories: development,testing-tools
com.docker.extension.changelog:
first version
com.docker.extension.detailed-description:
Description
You can
automate pretty much every docker command. Press Execute to see in action
and LoadBlocks for more examples.
com.docker.extension.publisher-url: https://github.com/ignatandrei/blocklyautomation
com.docker.extension.screenshots: '[{"alt":"Load Demos",
"url":"https://raw.githubusercontent.com/wiki/ignatandrei/BlocklyAutomation/imgs/DockerExtension/LoadBlocks.png"},
{"alt":"start containers",
"url":"https://raw.githubusercontent.com/wiki/ignatandrei/BlocklyAutomation/imgs/DockerExtension/StartContainers.png"}]'
org.opencontainers.image.description: A extension that displays lowCode with Blockly for any Docker command
org.opencontainers.image.title: Blockly Automation
org.opencontainers.image.vendor: Andrei Ignat
title: Blockly Automation
logo: https://github.com/ignatandrei/BlocklyAutomation/wiki/imgs/logoBADocker.png
publisher: Andrei Ignat
short_description: A extension that displays lowCode with Blockly for any Docker command
- slug: docker/disk-usage-extension
version: 0.2.8
containerd_compatible: false
labels:
com.docker.desktop.extension.api.version: ">= 0.2.3"
com.docker.desktop.extension.icon: https://docker-extension-screenshots.s3.us-east-1.amazonaws.com/disk-usage-extension/hard-drive.svg
com.docker.extension.additional-urls: "[]"
com.docker.extension.changelog:
Adding 'Select all' button for
reclaimable space options.
com.docker.extension.detailed-description: "
Disk Usage displays and
categorizes the disk space used by Docker. It also shows you how much of
the disk space is reclaimable and provides an easy one-click experience to
reclaim space.
Who might find it
useful?
If you need more visibility about how
much space Docker resources (e.g. images, volumes, etc) are using and how
much of it is reclaimable.
If you are looking for
a quick way to clean up disk space used by
Docker.
How it
works
You can reclaim the disk space used by Docker by
removing:
Stopped containers
Unused
images
Dangling images
Build
cache
Unused volumes
"
com.docker.extension.publisher-url: https://www.docker.com/
com.docker.extension.screenshots: '[ {"url":
"https://docker-extension-screenshots.s3.amazonaws.com/disk-usage-extension/1-disk-usage.png",
"alt": "Disk usage stats"}, {"url":
"https://docker-extension-screenshots.s3.amazonaws.com/disk-usage-extension/2-reclaim-popup.png",
"alt": "Reclaim space popup"}, {"url":
"https://docker-extension-screenshots.s3.amazonaws.com/disk-usage-extension/3-space-reclaimed.png",
"alt": "Space reclaimed successfully"} ]'
org.opencontainers.image.description: Optimize your disk space by removing unused objects from Docker Desktop.
org.opencontainers.image.revision: 929ff4d90be404fdf4325537286603d6c7c515ae
org.opencontainers.image.source: https://github.com/docker/disk-usage-extension
org.opencontainers.image.title: Disk Usage
org.opencontainers.image.vendor: Docker Inc.
title: Disk Usage
logo: https://docker-extension-screenshots.s3.us-east-1.amazonaws.com/disk-usage-extension/hard-drive.svg
publisher: Docker Inc.
short_description: Optimize your disk space by removing unused objects from Docker Desktop.
- slug: harpooncorp/harpoon-ext
version: 0.0.6
containerd_compatible: true
labels:
com.docker.desktop.extension.api.version: ">=0.2.3"
com.docker.desktop.extension.icon: https://docker-desktop-screenshots.s3.us-west-2.amazonaws.com/harpoon_logo_white_whale_transparent.png
com.docker.extension.additional-urls: '[{"title":"Documentation","url":"https://docs.harpoon.io/en/latest/introduction.html"},
{"title":"Features","url":"https://docs.harpoon.io/en/latest/features.html"},
{"title":"Support","url":"https://www.harpoon.io/contact"}, {"title":"Book
Demo","url":"https://www.harpoon.io/demo"}]'
com.docker.extension.categories: kubernetes
com.docker.extension.changelog: ""
com.docker.extension.detailed-description: harpoon is a drag and drop Kubernetes
tool for deploying any software in seconds. Our visual Kubernetes
interface enables anyone to deploy production-grade software with no code.
Whether you're new to Kubernetes and are looking for the best way to learn
or a seasoned pro, harpoon has all the features you need to be successful
in deploying and configuring your software using the industry-leading
container orchestrator, all with no code.
com.docker.extension.publisher-url: https://harpoon.io
com.docker.extension.screenshots: '[{"alt":"harpoon project page dark mode",
"url":"https://docker-desktop-screenshots.s3.us-west-2.amazonaws.com/project-dark.png"},
{"alt":"harpoon project page light mode",
"url":"https://docker-desktop-screenshots.s3.us-west-2.amazonaws.com/project-light.png"},
{"alt":"harpoon home page dark mode",
"url":"https://docker-desktop-screenshots.s3.us-west-2.amazonaws.com/home-dark.png"},
{"alt":"harpoon home page light mode",
"url":"https://docker-desktop-screenshots.s3.us-west-2.amazonaws.com/home-light.png"}]'
org.opencontainers.image.description: Docker Extension for the No Code Kubernetes platform
org.opencontainers.image.title: harpoon
org.opencontainers.image.vendor: harpoon Corp
title: harpoon
logo: https://docker-desktop-screenshots.s3.us-west-2.amazonaws.com/harpoon_logo_white_whale_transparent.png
publisher: harpoon Corp
short_description: Docker Extension for the No Code Kubernetes platform
- slug: vklokun/docker-desktop-extension
version: 0.1.1
containerd_compatible: true
labels:
com.docker.desktop.extension.api.version: ">= 0.2.3"
com.docker.desktop.extension.icon: https://raw.githubusercontent.com/cncf/artwork/ec3936fa0256c768b538247d20f130d293a9faed/projects/kubescape/stacked/color/kubescape-stacked-color.svg
com.docker.extension.account-info: required
com.docker.extension.additional-urls: ""
com.docker.extension.categories: kubernetes,security
com.docker.extension.changelog:
Extension changelog
Support access
key required by ARMO platform
Update Kubescape helm chart
name
com.docker.extension.detailed-description:
Kubescape Extension for Docker
Desktop
Kubescape helps harden your Kubernetes cluster by
providing insight into your cluster's security posture. Some of the
features that help you achieve this are - regular configuration and image
scans, visualizing your RBAC rules and suggesting automatic fixes where
applicable.
The Kubescape Extension for Docker Desktop works by
installing the Kubescape in-cluster components, connecting them to ARMO
Platform and providing insights into the Kubernetes cluster deployed by
Docker Desktop via the dashboard on ARMO Platform.
com.docker.extension.publisher-url: https://cloud.armosec.io/
com.docker.extension.screenshots: '[ { "alt": "Kubescape Extension for Docker
Desktop, Select Provider screen", "url":
"https://raw.githubusercontent.com/kubescape/docker-desktop-extension/main/docs/screenshots/dark-01.png"
}, { "alt": "Kubescape Extension for Docker Desktop, Sign Up screen",
"url":
"https://raw.githubusercontent.com/kubescape/docker-desktop-extension/main/docs/screenshots/dark-02.png"
}, { "alt": "Kubescape Extension for Docker Desktop, Secure Your Cluster
screen", "url":
"https://raw.githubusercontent.com/kubescape/docker-desktop-extension/main/docs/screenshots/dark-03.png"
}, { "alt": "Kubescape Extension for Docker Desktop, Cluster Secured
screen", "url":
"https://raw.githubusercontent.com/kubescape/docker-desktop-extension/main/docs/screenshots/dark-04.png"
}, { "alt": "Kubescape Extension for Docker Desktop, Monitor screen",
"url":
"https://raw.githubusercontent.com/kubescape/docker-desktop-extension/main/docs/screenshots/dark-05.png"
} ]'
org.opencontainers.image.description: Secure your Kubernetes cluster and gain
insight into your cluster’s security posture via an easy-to-use online
dashboard.
org.opencontainers.image.licenses: Apache-2.0
org.opencontainers.image.title: Kubescape
org.opencontainers.image.vendor: ARMO
title: Kubescape
logo: https://raw.githubusercontent.com/cncf/artwork/ec3936fa0256c768b538247d20f130d293a9faed/projects/kubescape/stacked/color/kubescape-stacked-color.svg
publisher: ARMO
short_description: Secure your Kubernetes cluster and gain insight into your
cluster’s security posture via an easy-to-use online dashboard.
- slug: caretdev/intersystems-extension
version: 0.1.7
containerd_compatible: true
labels:
com.docker.desktop.extension.api.version: ">= 0.2.3"
com.docker.desktop.extension.icon: https://raw.githubusercontent.com/caretdev/docker-intersystems-extension/main/intersystems.svg
com.docker.extension.additional-urls: '[{"title":"InterSystems","url":"https://intersystems.com/"},{"title":"Support","url":"https://github.com/caretdev/docker-intersystems-extension/issues"},{"title":"Discord","url":"https://discord.gg/Bt5DUwJhdt"}]'
com.docker.extension.categories: image-registry
com.docker.extension.changelog: ""
com.docker.extension.detailed-description: '
This provides a new distribution channel for customers to access
container-based releases and previews. All Community Edition images are
available in a public repository with no login required. All full released
images (IRIS, IRIS for Health, Health Connect, System Alerting and
Monitoring, InterSystems Cloud Manager) and utility images (such
as arbiter, Web Gateway, and PasswordHash) require a login token,
generated from your WRC (Worldwide Response Center) account credentials.
Why do I need this Extension
This extension provides
integrated UI for InterSystems Container Registry, so, you can easily
follow any updates, and quickly find and pull any images available on
Container Registry there.
Features available
Observe
the list of available public images
Observe the list of
available private images for users with access to WRC
Easy
pulling images
Delete local images
Copy image name
with tag
OS Support
Windows
x86-64
Linux x86-64 and ARM64
macOS x86-64
and ARM64
Filter images by name and
tag
Filter for ARM64 images
How to
use
It is already usable right after installation. And all
public images are already available. The list of images is cached, and it
is possible to refresh the list manually.
To get access to
private images with your WRC account, you have to go to https://containers.intersystems.com
login there, and using provided token login in docker.
InterSystems Developer Community is a global network of highly
experienced technology experts, influencers, and thought leaders who
have expertise in InterSystems technologies. It’s a multilingual platform
both for InterSystems employees, customers and partners.
'
com.docker.extension.publisher-url: https://github.com/caretdev/docker-intersystems-extension
com.docker.extension.screenshots: '[{"url":"https://raw.githubusercontent.com/caretdev/docker-intersystems-extension/main/img/screenshot1.png","alt":"Community
images"},{"url":"https://raw.githubusercontent.com/caretdev/docker-intersystems-extension/main/img/screenshot2.png","alt":"Community
ARM64 images"}]'
org.opencontainers.image.description: Convenient way to access InterSystems
Container Registry, public and private images of such products as IRIS and
IRIS for Health and many others in one place.
org.opencontainers.image.title: InterSystems
org.opencontainers.image.vendor: CaretDev Corp.
title: InterSystems
logo: https://raw.githubusercontent.com/caretdev/docker-intersystems-extension/main/intersystems.svg
publisher: CaretDev Corp.
short_description: Convenient way to access InterSystems Container Registry,
public and private images of such products as IRIS and IRIS for Health and
many others in one place.
================================================
FILE: pkg/rancher-desktop/assets/lima-config.yaml
================================================
# Default Lima configuration; parts will be overridden in code.
# Rancher Desktop ships with a patched QEMU that supports the Apple M4 CPU
# So override Lima 1.0.3 falling back to cortex-a72.
cpuType:
aarch64: host
ssh:
loadDotSSHPubKeys: false
firmware:
legacyBIOS: false
containerd:
system: false
user: false
# Provisioning scripts run on every boot, not just initial VM provisioning.
provision:
- # When the ISO image is updated, only preserve selected data from /etc but otherwise use the new files.
# Update files in /usr/local on the data volume from the new versions on the ISO.
mode: system
script: |
#!/bin/sh
set -o errexit -o nounset -o xtrace
mkdir -p /bootfs
mount --bind / /bootfs
# /bootfs/etc is empty on first boot because it has been moved to /mnt/data/etc by lima
if [ -f /bootfs/etc/os-release ]; then
# Alpine turned /etc/os-release into a symlink; we dereference it again. If we still have a symlink
# here, then this is an upgrade from an older version and needs to go through the migration.
if [ -L /etc/os-release ] || ! diff -q /etc/os-release /bootfs/etc/os-release; then
# When we are upgrading from an ISO we will install new packages during boot.
# But Lima will restore the old /etc/apk/world file and run `apk fix --no-network`
# to restore packages from cache that were installed manually. This will however
# uninstall all packages again that were not previously installed because they are
# not listed in the old world file. We need to install them once more from the
# boot media to update the world file.
# We are not bothering with uninstalling packages that are not part of the new release.
apk add --no-network --keys-dir /bootfs/etc/apk/keys --repositories-file /bootfs/etc/apk/repositories \
$(cat /bootfs/etc/apk/world)
# Using a temp file just in case dereferencing a symlink onto itself has a race condition.
cp -L /etc/os-release /etc/os-release.tmp
mv /etc/os-release.tmp /etc/os-release
cp /etc/machine-id /bootfs/etc
cp /etc/ssh/ssh_host* /bootfs/etc/ssh/
mkdir -p /etc/docker /etc/rancher
cp -pr /etc/docker /bootfs/etc
cp -pr /etc/rancher /bootfs/etc
rm -rf /mnt/data/etc.prev
mkdir /mnt/data/etc.prev
mv /etc/* /mnt/data/etc.prev
cp -L /bootfs/etc/os-release /tmp/os-release
mv /bootfs/etc/* /etc
mv /tmp/os-release /etc
# install updated files from /usr/local, e.g. nerdctl, buildkit, cni plugins
cp -pr /bootfs/usr/local /usr
# Keep the lima-init.log around for debugging
cp /var/log/lima-init.log /mnt/data/lima-init-upgrade.log
# lima has applied changes while the "old" /etc was in place; restart to apply them to the updated one.
reboot
fi
fi
umount /bootfs
rmdir /bootfs
- # make sure we booted with the right cgroup mode; k3s versions before 1.20.4 only support cgroup v1
mode: system
script: |
#!/bin/sh
set -o errexit -o nounset -o xtrace
RC_CGROUP_MODE=unified
if ! grep -q -E "^#?rc_cgroup_mode=\"$RC_CGROUP_MODE\"" /etc/rc.conf; then
sed -i -E "s/^#?rc_cgroup_mode=\".*\"/rc_cgroup_mode=\"$RC_CGROUP_MODE\"/" /etc/rc.conf
# avoid reboot loop if sed failed for any reason
if grep -q -E "^rc_cgroup_mode=\"$RC_CGROUP_MODE\"" /etc/rc.conf; then
reboot
fi
fi
- # return unused space from the data volume back to the host
mode: system
script: |
#!/bin/sh
set -o errexit -o nounset -o xtrace
fstrim /mnt/data
- # allow more than 10 sessions over the master control path
mode: system
script: |
#!/bin/sh
set -o errexit -o nounset -o xtrace
sed -i -E 's/^#?MaxSessions +[0-9]+/MaxSessions 25/g' /etc/ssh/sshd_config
rc-service --ifstarted sshd reload
- # Persist /root directory on data volume
mode: system
script: |
#!/bin/sh
set -o errexit -o nounset -o xtrace
if ! [ -d /mnt/data/root ]; then
mkdir -p /root
mv /root /mnt/data/root
fi
mkdir -p /root
mount --bind /mnt/data/root /root
- # Create /etc/docker/certs.d symlink
mode: system
script: |
#!/bin/sh
set -o errexit -o nounset -o xtrace
mkdir -p /etc/docker
# Delete certs.d if it is a symlink (from previous boot).
[ -L /etc/docker/certs.d ] && rm /etc/docker/certs.d
# Create symlink if certs.d doesn't exist (user may have created a regular directory).
if [ ! -e /etc/docker/certs.d ]; then
# We don't know if the host is Linux or macOS, so we take a guess based on which mountpoint exists.
if [ -d "/Users/{{.User}}" ]; then
ln -s "/Users/{{.User}}/.docker/certs.d" /etc/docker
elif [ -d "/home/{{.User}}" ]; then
ln -s "/home/{{.User}}/.docker/certs.d" /etc/docker
fi
fi
- # Make sure hostname doesn't change during upgrade from earlier versions
mode: system
script: |
#!/bin/sh
hostname lima-rancher-desktop
- # Clean up filesystems
mode: system
script: |
#!/bin/sh
set -o errexit -o nounset -o xtrace
# During boot is the only safe time to delete old k3s versions.
rm -rf /var/lib/rancher/k3s/data
# Delete all tmp files older than 3 days.
find /tmp -depth -mtime +3 -delete
- # Make mount-points shared.
mode: system
script: |
#!/bin/sh
set -o errexit -o nounset -o xtrace
for dir in / /etc /tmp /var/lib; do
mount --make-shared "${dir}"
done
- # This sets up cron (used for logrotate)
mode: system
script: |
#!/bin/sh
# Move logrotate to hourly, because busybox crond only handles time jumps up
# to one hour; this ensures that if the machine is suspended over long
# periods, things will still happen often enough. This is idempotent.
mv -n /etc/periodic/daily/logrotate /etc/periodic/hourly/
rc-update add crond default
rc-service crond start
- # Ensure the user is in the docker group to access the docker socket
mode: system
script: |
set -o errexit -o nounset -o xtrace
usermod --append --groups docker "{{.User}}"
- # Install mkcert and prepare default/fallback cert for localhost
mode: system
script: |
export CAROOT=/run/mkcert
mkdir -p $CAROOT
cd $CAROOT
# Remove old mkcert certificates
rm -f /usr/local/share/ca-certificates/mkcert_development_CA_*.crt
mkcert -install
mkcert localhost
chown -R nobody:nobody $CAROOT
- # Configure HTTPS_PROXY to OpenResty
mode: system
script: |
set -o errexit -o nounset -o xtrace
# openresty is backgrounding itself (and writes its own pid file)
sed -i 's/^command_background/#command_background/' /etc/init.d/rd-openresty
# configure proxy only when allowed-images exists
allowed_images_conf=/usr/local/openresty/nginx/conf/allowed-images.conf
# Remove the reference to an obsolete image conf filename
obsolete_image_allow_list_conf=/usr/local/openresty/nginx/conf/image-allow-list.conf
setproxy="[ -f $allowed_images_conf ] && supervise_daemon_args=\"-e HTTPS_PROXY=http://127.0.0.1:3128 \${supervise_daemon_args:-}\" || true"
for svc in containerd docker; do
sed -i "\#-f $allowed_images_conf#d" /etc/init.d/$svc
sed -i "\#-f $obsolete_image_allow_list_conf#d" /etc/init.d/$svc
echo "$setproxy" >> /etc/init.d/$svc
done
# Make sure openresty log directory exists
install -d -m755 /var/log/openresty
- # mount bpffs to allow containers to leverage bpf, and make both bpffs and
# cgroupfs shared mounts so the pods can mount them correctly
mode: system
script: |
#!/bin/sh
set -o errexit
mount bpffs -t bpf /sys/fs/bpf
mount --make-shared /sys/fs/bpf
mount --make-shared /sys/fs/cgroup
- # we run trivy as root now; remove any cached databases installed into the user directory by previous version
# trivy.db is 600M and trivy-java.db is 1.1G
mode: user
script: |
rm -rf "${HOME}/.cache/trivy"
portForwards:
- guestPortRange: [1, 65535]
guestIPMustBeZero: true
hostIP: "0.0.0.0"
proto: any
================================================
FILE: pkg/rancher-desktop/assets/networks-config.yaml
================================================
# Path to socket_vmnet executable. Because socket_vmnet is invoked via sudo, it
# must be installed where only root can modify/replace it. This means also none
# of the parent directories should be writable by the user.
#
# The varRun directory also must not be writable by the user because it will
# include the socket_vmnet pid file. socket_vmnet will be terminated via
# sudo, so replacing the pid file would allow killing of arbitrary privileged
# processes. varRun however MUST be writable by the daemon user.
#
# None of the paths segments may be symlinks, which is why it has to be /private/var
# instead of /var etc.
paths:
socketVMNet: /opt/rancher-desktop/bin/socket_vmnet
varRun: /private/var/run
sudoers: /private/etc/sudoers.d/zzzzz-rancher-desktop-lima
group: everyone
networks:
rancher-desktop-shared:
mode: shared
gateway: 192.168.205.1
dhcpEnd: 192.168.205.254
netmask: 255.255.255.0
host:
mode: host
gateway: 192.168.206.1
dhcpEnd: 192.168.206.254
netmask: 255.255.255.0
# We will add bridged-en0 etc. networks, one for each host interface.
================================================
FILE: pkg/rancher-desktop/assets/scripts/10-flannel.conflist
================================================
{
"name":"cbr0",
"cniVersion":"0.3.1",
"plugins":[
{
"type":"flannel",
"delegate":{
"hairpinMode":true,
"forceAddress":true,
"isDefaultGateway":true
}
},
{
"type":"portmap",
"capabilities":{
"portMappings":true
}
}
]
}
================================================
FILE: pkg/rancher-desktop/assets/scripts/buildkit.confd
================================================
# config file for /etc/init.d/buildkit
# overrides the main command executed by the supervise daemon
buildkitd_command="/usr/local/bin/buildkitd"
# any other options you want to pass to buildkitd_command
buildkitd_opts="--addr=unix:///run/buildkit/buildkitd.sock --containerd-worker=true --containerd-worker-addr=/run/k3s/containerd/containerd.sock --containerd-worker-gc --oci-worker=false"
# Settings for process limits (ulimit)
#ulimit_opts="-c unlimited -n 1048576 -u unlimited"
# seconds to wait for sending SIGTERM and SIGKILL signals when stopping buildkitd
#signal_retry="TERM/60/KILL/10"
# where buildkit stdout (and perhaps stderr) goes.
#log_file="/var/log/buildkit.log"
# where buildkit stderr optionally goes.
# if this is not set, the value in 'logfile' is used
#err_file="/var/log/buildkit-err.log"
# mode of the log files
#log_mode=0644
# user that owns the log files (no group root on WSL)
log_owner=root
# to override the default supervise_daemon_args
#supervise_daemon_opts=""
================================================
FILE: pkg/rancher-desktop/assets/scripts/buildkit.initd
================================================
#!/sbin/openrc-run
supervisor=supervise-daemon
name="BuildKit Daemon"
description="Standalone buildkitd"
command="${buildkitd_command:-/usr/bin/buildkitd}"
command_args="${buildkitd_opts:---oci-worker=false --containerd-worker=true}"
rc_ulimit="${ulimit_opts:--c unlimited -n 1048576 -u unlimited}"
retry="${signal_retry:-TERM/60/KILL/10}"
log_file="${log_file:-/var/log/${RC_SVCNAME}.log}"
err_file="${err_file:-${log_file}}"
log_mode="${log_mode:-0644}"
log_owner="${log_owner:-root}"
supervise_daemon_args="${supervise_daemon_opts:---stderr \"${err_file}\" --stdout \"${log_file}\"}"
start_pre() {
checkpath -f -m "$log_mode" -o "$log_owner" "$log_file" "$err_file"
}
================================================
FILE: pkg/rancher-desktop/assets/scripts/cert-manager.yaml
================================================
---
apiVersion: v1
kind: Namespace
metadata:
name: cert-manager
---
apiVersion: helm.cattle.io/v1
kind: HelmChart
metadata:
name: cert-manager
namespace: kube-system
spec:
chart: "https://%{KUBERNETES_API}%/static/rancher-desktop/cert-manager.tgz"
targetNamespace: cert-manager
# Old versions of the helm-controller don't support createNamespace, so we
# created the namespace ourselves.
createNamespace: false
================================================
FILE: pkg/rancher-desktop/assets/scripts/configure-allowed-images
================================================
#!/bin/sh
# This script configures the VM for the allowed-images feature.
# shellcheck shell=ash
set -o errexit -o nounset
# Create nobody user and group for nginx.
addgroup -S -g 65534 nobody 2>/dev/null || true
adduser -S -D -H -h /dev/null -s /sbin/nologin -u 65534 -G nobody -g nobody nobody 2>/dev/null || true
# Install mkcert and create default certs for localhost.
export CAROOT=/run/mkcert
mkdir -p $CAROOT
cd $CAROOT
# Remove old mkcert certificates
rm -f /usr/local/share/ca-certificates/mkcert_development_CA_*.crt
mkcert -install
mkcert localhost
chown -R nobody:nobody $CAROOT
# configure proxy only when allowed-images exists
allowed_images_conf=/usr/local/openresty/nginx/conf/allowed-images.conf
setproxy="[ -f $allowed_images_conf ] && supervise_daemon_args=\"-e HTTPS_PROXY=http://127.0.0.1:3128 \${supervise_daemon_args:-}\" || true"
sed -i "\#-f $allowed_images_conf#d" /etc/init.d/containerd
echo "$setproxy" >> /etc/init.d/containerd
# openresty is backgrounding itself (and writes its own pid file)
sed -i 's/^command_background/#command_background/' /etc/init.d/rd-openresty
# Make sure openresty log directory exists
install -d -m755 /var/log/openresty
================================================
FILE: pkg/rancher-desktop/assets/scripts/docker-credential-rancher-desktop
================================================
#!/bin/sh
set -eu
source /etc/rancher/desktop/credfwd
DATA="@-"
# The "list" command doesn't have a payload on STDIN
[ "$1" = "list" ] && DATA=""
# $CREDFWD_CURL_OPTS is intentionally *not* quoted
exec curl --silent --user "$CREDFWD_AUTH" --data "$DATA" --noproxy '*' --fail-with-body ${CREDFWD_CURL_OPTS:-} "$CREDFWD_URL/$1"
================================================
FILE: pkg/rancher-desktop/assets/scripts/install-containerd-shims
================================================
#!/bin/sh
set -o errexit -o nounset -o pipefail
dest=/usr/local/containerd-shims
# Copy all shims into the data volume so they become part of snapshots.
# TODO Maybe use rsync to avoid copying files repeatedly?
mkdir -p "$dest"
for dir in "$@"; do
if [[ "$(uname -a)" =~ microsoft ]]; then
dir=$(wslpath -a -u "$dir")
fi
cp "${dir}/containerd-shim-"* "$dest" || :
done
# Make sure all shims are executable.
for file in "${dest}/"*; do
if [ -e "$file" ]; then
chmod 755 "$file"
fi
done
# Create symlinks to each shim into /usr/local/bin.
# In the future this will enable us putting only shims from an allow list on the PATH.
find /usr/local/bin -type l -name 'containerd-shim-*' -delete
find "$dest" -type f -exec ln -sf {} /usr/local/bin \;
================================================
FILE: pkg/rancher-desktop/assets/scripts/install-k3s
================================================
#!/bin/sh
set -o errexit -o nounset -o pipefail
if [ -n "${XTRACE:-}" ]; then
set -o xtrace
fi
VERSION="${1}"
CACHE_DIR="${CACHE_DIR:-${2}}"
# Update symlinks for k3s and images to new version
K3S_DIR="${CACHE_DIR}/${VERSION}"
if [ ! -d "${K3S_DIR}" ]; then
echo "Directory ${K3S_DIR} does not exist"
exit 1
fi
# Make sure any outdated kubeconfig file is gone
mkdir -p /etc/rancher/k3s
rm -f /etc/rancher/k3s/k3s.yaml
K3S=k3s
ARCH=amd64
if [ "$(uname -m)" = "aarch64" ]; then
K3S=k3s-arm64
ARCH=arm64
fi
# Add images
IMAGES="/var/lib/rancher/k3s/agent/images"
mkdir -p "${IMAGES}"
IMAGEPATH="${K3S_DIR}/k3s-airgap-images-${ARCH}"
if [ -f "${IMAGEPATH}.tar.zst" ]; then
ln -s -f "${IMAGEPATH}.tar.zst" "${IMAGES}"
fi
if [ -f "${IMAGEPATH}.tar" ]; then
ln -s -f "${IMAGEPATH}.tar" "${IMAGES}"
fi
# Add k3s binary
ln -s -f "${K3S_DIR}/${K3S}" /usr/local/bin/k3s
# The file system may be readonly (on macOS)
chmod a+x "${K3S_DIR}/${K3S}" || true
# Make sure any old manifests are removed before configuring k3s again.
# All Rancher Desktop manifest have a name like z123-foo-bar, so only delete
# those. That way provisioning scripts can still create other manifests.
# We need to create the directory before we run `k3s server ...` because
# we install additional manifests that k3s will install during startup.
MANIFESTS=/var/lib/rancher/k3s/server/manifests
rm -rf "${MANIFESTS}/z"[0-9]*
mkdir -p "$MANIFESTS"
STATIC=/var/lib/rancher/k3s/server/static/rancher-desktop
rm -rf "$STATIC"
mkdir -p "$STATIC"
================================================
FILE: pkg/rancher-desktop/assets/scripts/install-wsl-helpers
================================================
#!/bin/sh
# This script installs WSL helpers into the shared WSL mount at `/mnt/wsl`.
# Usage: $0
# shellcheck shell=ash
set -o errexit -o nounset
# The nerdctl shim must be setuid root to be able to create bind mounts within
# /mnt/wsl so that nerdctl can see it.
mkdir -p "/mnt/wsl/rancher-desktop/bin/"
cp "${1}" "/mnt/wsl/rancher-desktop/bin/nerdctl"
chmod u+s "/mnt/wsl/rancher-desktop/bin/nerdctl"
================================================
FILE: pkg/rancher-desktop/assets/scripts/k3s-containerd-config.toml
================================================
version = 2
root = "/var/lib/rancher/k3s/agent/containerd"
state = "/run/k3s/containerd"
[grpc]
address = "/run/k3s/containerd/containerd.sock"
[plugins."io.containerd.internal.v1.opt"]
path = "/var/lib/rancher/k3s/agent/containerd"
[plugins."io.containerd.grpc.v1.cri"]
stream_server_address = "127.0.0.1"
stream_server_port = "10010"
enable_selinux = false
enable_unprivileged_ports = true
enable_unprivileged_icmp = true
sandbox_image = "rancher/mirrored-pause:3.6"
[plugins."io.containerd.grpc.v1.cri".containerd]
snapshotter = "overlayfs"
disable_snapshot_annotations = true
[plugins."io.containerd.grpc.v1.cri".cni]
bin_dir = "/usr/libexec/cni"
conf_dir = "/etc/cni/net.d"
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc]
runtime_type = "io.containerd.runc.v2"
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
SystemdCgroup = false
[plugins."io.containerd.grpc.v1.cri".registry]
config_path = "/var/lib/rancher/k3s/agent/etc/containerd/certs.d"
================================================
FILE: pkg/rancher-desktop/assets/scripts/logrotate-k3s
================================================
/var/log/k3s.log {
missingok
notifempty
copytruncate
}
================================================
FILE: pkg/rancher-desktop/assets/scripts/logrotate-lima-guestagent
================================================
/var/log/lima-guestagent.log {
missingok
notifempty
copytruncate
}
================================================
FILE: pkg/rancher-desktop/assets/scripts/logrotate-openresty
================================================
/var/log/openresty/*.log {
missingok
sharedscripts
postrotate
/etc/init.d/rd-openresty --quiet --ifstarted reopen
endscript
}
================================================
FILE: pkg/rancher-desktop/assets/scripts/moproxy.initd
================================================
#!/sbin/openrc-run
name=moproxy
description="A transparent TCP to SOCKSv5/HTTP proxy."
extra_started_commands="enable disable reload"
description_enable="Start redirecting the network traffic to the HTTP proxy."
description_disable="Stop redirecting the network traffic to the HTTP proxy."
description_reload="Reload the proxy list."
# TCP Listen address
: ${host:=${MOPROXY_HOST:-"::"}}
# TCP Listen port
: ${port:=${MOPROXY_PORT:-"2080"}}
# List of backend proxy servers
: ${proxy_list:=${MOPROXY_PROXYLIST:-"/etc/moproxy/proxy.ini"}}
# Additional arguments to pass to moproxy
: ${moproxy_args:=${MOPROXY_ARGS:-""}}
# Override this argument to disable the use of TLS SNI
: ${moproxy_remotedns:=${MOPROXY_REMOTE_DNS:-"--remote-dns"}}
# Comma-separated list of port traffic to redirect to moproxy
: ${ports_redirected:=${MOPROXY_REDIRECTED_PORT:-"80,443"}}
# Comma-separated list of hostname to not redirect to the proxy
: ${noproxy_rules:=${MOPROXY_NOPROXY:-"0.0.0.0/8,10.0.0.0/8,127.0.0.0/8,169.254.0.0/16,172.16.0.0/12,192.168.0.0/16,224.0.0.0/4,240.0.0.0/4"}}
command="'${MOPROXY_BINARY:-/usr/sbin/moproxy}'"
command_args="--host ${host} --port ${port} ${moproxy_remotedns} --list ${proxy_list} ${moproxy_args}"
command_background="yes"
pidfile="/run/${name}.pid"
MOPROXY_LOGFILE="${MOPROXY_LOGFILE:-${LOG_DIR:-/var/log}/${RC_SVCNAME}.log}"
output_log="'${MOPROXY_LOGFILE}'"
error_log="'${MOPROXY_LOGFILE}'"
iptables_redirect_to_moproxy_chain() {
iptables --table nat --$1 $2 --protocol tcp --match multiport --dports "${ports_redirected}" --jump MOPROXY
}
iptables_redirect() {
iptables --table nat --append MOPROXY --protocol tcp --jump REDIRECT --to-port "${port}"
}
iptables_accept() {
iptables --table nat --append MOPROXY --protocol tcp --destination "$1" --jump ACCEPT
}
add_noproxy_rules() {
for i in ${noproxy_rules//,/ }
do
iptables_accept "$i"
done
}
enable_redirection_to_moproxy_chain() {
if ! iptables_redirect_to_moproxy_chain check $1 &> /dev/null
then
iptables_redirect_to_moproxy_chain append $1
else
einfo "Rule already in table"
fi
}
disable_redirection_to_moproxy_chain() {
while iptables_redirect_to_moproxy_chain check $1 &> /dev/null
do
iptables_redirect_to_moproxy_chain delete $1
done
}
create_moproxy_chain() {
iptables --table nat --new MOPROXY
}
delete_moproxy_chain() {
iptables --table nat --flush MOPROXY
iptables --table nat --delete-chain MOPROXY
}
depend() {
after iptables ip6tables
}
enable() {
einfo "Starting the iptables rules to start redirection of ports ${ports_redirected} to ${name}"
create_moproxy_chain
add_noproxy_rules
iptables_redirect
enable_redirection_to_moproxy_chain OUTPUT
enable_redirection_to_moproxy_chain PREROUTING
}
disable() {
einfo "Removing all the iptables rules to stop redirection to ${name}"
disable_redirection_to_moproxy_chain PREROUTING
disable_redirection_to_moproxy_chain OUTPUT
delete_moproxy_chain
}
start_post() {
enable
}
stop_pre() {
disable
}
reload() {
ebegin "Reloading ${name}"
start-stop-daemon --signal HUP --pidfile "$pidfile"
iptables --table nat --flush MOPROXY
add_noproxy_rules
iptables_redirect
}
================================================
FILE: pkg/rancher-desktop/assets/scripts/nerdctl
================================================
#!/bin/sh
export CONTAINERD_ADDRESS=/run/k3s/containerd/containerd.sock
if [ -f /usr/local/openresty/nginx/conf/allowed-images.conf ]; then
export HTTPS_PROXY=http://127.0.0.1:3128
fi
# On WSL, we need to enter the correct pid &c. namespace for nerdctl to work
# correctly.
if [ -r /run/wsl-init.pid ]; then
parent="$(cat /run/wsl-init.pid)"
pid="$(ps -o pid,ppid,comm | awk '$2 == "'"${parent}"'" && $3 == "init" { print $1 }')"
if [ -n "${pid}" ]; then
exec /usr/bin/nsenter -p -m -n -t "${pid}" /usr/local/libexec/nerdctl/nerdctl "$@"
fi
fi
exec /usr/local/libexec/nerdctl/nerdctl "$@"
================================================
FILE: pkg/rancher-desktop/assets/scripts/nginx.conf
================================================
worker_processes auto;
error_log /var/log/openresty/error.log warn;
events {
worker_connections 1024;
}
http {
map_hash_bucket_size 128;
include mime.types;
default_type application/octet-stream;
log_format proxy escape=json
'{'
'"access_time":"$time_local",'
'"request":"$request",'
'"status":"$status",'
'"bytes_sent":"$body_bytes_sent",'
'"host":"$host",'
'"ssl_protocol":"$ssl_protocol",'
'"connect_host":"$connect_host",'
'"connect_port":"$connect_port",'
'}';
log_format mitm escape=json
'{'
'"access_time":"$time_local",'
'"method":"$request_method",'
'"uri":"$uri",'
'"status":"$status",'
'"bytes_sent":"$body_bytes_sent",'
'"upstream_response_time":"$upstream_response_time",'
'"host":"$host",'
'"http_host":"$http_host",'
'"upstream":"$upstream_addr"'
'}';
server {
listen 3128;
listen [::]:3128;
server_name proxy;
access_log /var/log/openresty/proxy.log proxy;
proxy_connect;
proxy_connect_allow all;
proxy_connect_address 127.0.0.1:3129;
proxy_max_temp_file_size 0;
# response non-CONNECT requests
location / {
add_header "Content-type" "text/plain" always;
return 404 "The Rancher Desktop allowed-images proxy only allows CONNECT requests\n";
}
}
map "$http_host$uri" $forbidden {
default 1;
include allowed-images.conf;
}
# don't limit maximum request size to allow for pushing large image layers
client_max_body_size 0;
server {
listen 3129 ssl default_server;
server_name mitm;
access_log /var/log/openresty/access.log mitm;
# nginx complains if these are not set; we'll clear them again right after
ssl_certificate /run/mkcert/localhost.pem;
ssl_certificate_key /run/mkcert/localhost-key.pem;
ssl_certificate_by_lua_block {
local ssl = require "ngx.ssl"
local name = ssl.server_name()
local ok, err = ssl.clear_certs()
if not ok then
ngx.log(ngx.ERR, "failed to clear existing (fallback) certificates")
return ngx.exit(ngx.ERROR)
end
local certs_dir = "/run/mkcert/"
local cert_file = certs_dir .. name .. ".pem"
local key_file = certs_dir .. name .. "-key.pem"
local my_load_certificate_chain = function()
local f = io.open(cert_file, "rb")
if f == nil then
local ngx_pipe = require "ngx.pipe"
local cmd = { "/usr/bin/mkcert", "-cert-file", cert_file, "-key-file", key_file, name }
local opts = { environ = {"CAROOT="..certs_dir}, merge_stderr = true }
local proc, err = ngx_pipe.spawn(cmd, opts)
if proc == nil then
ngx.log(ngx.ERR, "failed to spawn mkcert command: ", err)
return ngx.exit(ngx.ERROR)
end
local data, err, partial = proc:stdout_read_all()
local ok, reason, status = proc:wait()
if not ok then
ngx.log(ngx.ERR, "failed to create cert for ", name, " reason: ", reason, " status: ", status)
ngx.log(ngx.ERR, " output: ", data, " err: ", err, " partial: ", partial)
return ngx.exit(ngx.ERROR)
end
f = io.open(cert_file, "rb")
end
local a = f:read("a*")
f:close()
return a
end
local pem_cert_chain = assert(my_load_certificate_chain())
local der_cert_chain, err = ssl.cert_pem_to_der(pem_cert_chain)
if not der_cert_chain then
ngx.log(ngx.ERR, "failed to convert certificate chain from PEM to DER: ", err)
return ngx.exit(ngx.ERROR)
end
local ok, err = ssl.set_der_cert(der_cert_chain)
if not ok then
ngx.log(ngx.ERR, "failed to set DER cert: ", err)
return ngx.exit(ngx.ERROR)
end
local my_load_private_key = function()
local f = assert(io.open(key_file, "rb"))
local a = f:read("a*")
f:close()
return a
end
local pem_pkey = assert(my_load_private_key())
local der_pkey, err = ssl.priv_key_pem_to_der(pem_pkey, nil)
if not der_pkey then
ngx.log(ngx.ERR, "failed to convert private key from PEM to DER: ", err)
return ngx.exit(ngx.ERROR)
end
local ok, err = ssl.set_der_priv_key(der_pkey)
if not ok then
ngx.log(ngx.ERR, "failed to set DER private key: ", err)
return ngx.exit(ngx.ERROR)
end
}
# We need to resolve the real names of our proxied servers.
include resolver.conf;
# Docker needs this. Don't ask.
chunked_transfer_encoding on;
proxy_read_timeout 900;
# Use SNI during the TLS handshake with the upstream.
proxy_ssl_server_name on;
proxy_ssl_verify on;
proxy_ssl_trusted_certificate /etc/ssl/certs/ca-certificates.crt;
proxy_ssl_verify_depth 2;
location ~ ^/v[12]/(.+)/manifests/([^/]+)$ {
if ($forbidden) {
add_header "Content-type" "application/json" always;
# `code` from https://github.com/distribution/distribution/blob/main/registry/api/errcode/register.go
return 403 "{\"errors\":[{\"code\":\"UNAUTHORIZED\",\"message\":\"image $http_host/$1:$2 is not covered by the Rancher Desktop allowed-images list\"}]}\n";
}
proxy_pass https://$http_host;
}
location / {
proxy_pass https://$http_host;
}
}
}
================================================
FILE: pkg/rancher-desktop/assets/scripts/rancher-desktop-guestagent.initd
================================================
#!/sbin/openrc-run
# shellcheck shell=ksh
depend() {
after network-online
}
GUESTAGENT_LOGFILE="${GUESTAGENT_LOGFILE:-${LOG_DIR:-/var/log}/${RC_SVCNAME}.log}"
supervisor=supervise-daemon
name="Rancher Desktop Guest Agent"
command=/usr/local/bin/rancher-desktop-guestagent
command_args="
${GUESTAGENT_ADMIN_INSTALL:+-adminInstall=${GUESTAGENT_ADMIN_INSTALL}}
${GUESTAGENT_KUBERNETES:+-kubernetes=${GUESTAGENT_KUBERNETES}}
${GUESTAGENT_DOCKER:+-docker=${GUESTAGENT_DOCKER}}
${GUESTAGENT_CONTAINERD:+-containerd=${GUESTAGENT_CONTAINERD}}
${GUESTAGENT_K8S_SVC_ADDR:+-k8sServiceListenerAddr=${GUESTAGENT_K8S_SVC_ADDR}}
${GUESTAGENT_DEBUG:+-debug}
"
command_args="${command_args//$'\n'/ }"
output_log="'${GUESTAGENT_LOGFILE}'"
error_log="'${GUESTAGENT_LOGFILE}'"
respawn_delay=5
respawn_max=0
start_pre() {
cat > /etc/logrotate.d/guestagent <&2
exit 1
fi
# If the pid is _not_ /sbin/init, find the child that is.
command="$(ps -o pid,args | awk "\$1 == $pid { print \$2 }")"
if [ "$command" != "/sbin/init" ]; then
newpid="$(ps -o pid,ppid,args | awk "\$2 == $pid && \$3 == \"/sbin/init\" { print \$1 }")"
if [ -n "${newpid}" ]; then
pid="${newpid}"
fi
fi
if [ $# -eq 0 ]; then
set -- /bin/sh
fi
# If -w$PWD is specified on the first nsenter, then `wsl-exec pwd`
# fails with "pwd: getcwd: No such file or directory"
exec /usr/bin/nsenter -n -p -m -t "${pid}" /usr/bin/nsenter "-w${PWD}" "$@"
================================================
FILE: pkg/rancher-desktop/assets/scripts/wsl-init
================================================
#!/bin/sh
# This script is used to launch (busybox) init on WSL2 through network-setup process.
# The network-setup process starts the vm-switch and unshare as its sub processes. this
# is necessary since we need to do some mount namespace, since we store the data on the
# WSL shared mount (/mnt/wsl/rancher/desktop/) and that can have issues with
# lingering tmpfs mounts after we exit. This means we need to run this script
# under unshare (to get a private mount namespace), and then we can mark various
# mount points as shared (for buildkit). Kubelet will internally do some
# tmpfs mounts for volumes (secrets, etc.), which will stay private and go away
# once k3s exits, so that we can delete the data as necessary.
set -o errexit -o nounset -o xtrace
NETWORK_SETUP_LOG="${LOG_DIR}/network-setup.log"
VM_SWITCH_LOG="${LOG_DIR}/vm-switch.log"
if [ $$ -ne "1" ]; then
# This is not running as PID 1; this means that this is a normal invocation
# from WSL.
exec /usr/local/bin/network-setup --logfile "$NETWORK_SETUP_LOG" \
--vm-switch-path /usr/local/bin/vm-switch --vm-switch-logfile \
"$VM_SWITCH_LOG" ${RD_DEBUG:+-debug} --unshare-arg "${0}"
fi
# Mark directories that we will need to bind mount as shared mounts.
(
IFS=:
for dir in / ${DISTRO_DATA_DIRS}; do
mount --make-shared "${dir}"
done
)
# Mount bpffs to allow containers to leverage bpf, and make both bpffs and
# cgroupfs shared mounts so the pods can mount them correctly.
mount bpffs -t bpf /sys/fs/bpf
mount --make-shared /sys/fs/bpf
mount --make-shared /sys/fs/cgroup
# Mount binfmt_misc to allow nerdctl to see which qemu-* handlers have been loaded.
# It will display a warning for foreign platforms if their handler seems missing.
mount -t binfmt_misc binfmt_misc /proc/sys/fs/binfmt_misc
mount --make-shared /proc/sys/fs/binfmt_misc
if [ -f /var/lib/resolv.conf ]; then
ln -s -f /var/lib/resolv.conf /etc/resolv.conf
fi
# Run init (which never exits).
exec /sbin/init
================================================
FILE: pkg/rancher-desktop/assets/specs/README.md
================================================
## Generators
### To generate go code:
`oapi-codegen pkg/rancher-desktop/assets/specs/command-api.yaml > api/commands.go`
#### Dependencies:
* opai-codegen: To install:
```bash
go get github.com/deepmap/oapi-codegen/cmd/oapi-codegen
```
### To generate documentation:
```
mkdir tmp
docker run --rm -v ${PWD}:/local openapitools/openapi-generator-cli:v5.4.2 generate -i /local/src/assets/specs/command-api.yaml -g html -o /local/tmp/
open tmp/index.html # (macOS)
start tmp/index.html # (Powershell)
xdg-open tmp/index.html # (linux, replace with path to a specific browser if you prefer).
```
Recommended tag: openapitools/openapi-generator-cli:v5.4.2
So run:
```
docker run ... openapitools/openapi-generator-cli@sha256:3d7c84e4b8f25a2074d6ab44d936cd69d08a223021197269e75d29992204e15e
```
## References:
* OpenAPI spec: https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.0.md#mediaTypeObject
* Tools: https://openapi.tools/
================================================
FILE: pkg/rancher-desktop/assets/specs/command-api.yaml
================================================
info:
title: Rancher Desktop API
version: 0.0.1
paths:
/:
get:
operationId: listEndpoints
summary: List all endpoints.
responses:
'200':
description: A list of endpoints
content:
application/json:
schema:
type: array
items: { type: string }
/v0:
get:
operationId: listV0Endpoints
summary: List all version zero endpoints.
responses:
'200':
description: A list of version 0 endpoints, of which there are none.
content:
application/json:
schema:
type: array
items: { type: string }
/v1:
get:
operationId: listV1Endpoints
summary: List all version one endpoints.
responses:
'200':
description: A list of endpoints
content:
application/json:
schema:
type: array
items: { type: string }
/v1/about:
get:
operationId: getAbout
summary: Returns a description of the endpoints
responses:
'200':
description: A note about endpoints not being forwards-compatible.
content:
text/plain:
schema:
type: string
/v1/diagnostic_categories:
get:
operationId: diagnosticCategories
summary: Return a list of the category names for the Diagnostics component. Takes no parameters.
responses:
'200':
description: A list of the category names.
content:
application/json:
schema:
type: array
items:
type: string
/v1/diagnostic_checks:
get:
operationId: diagnosticChecks
summary: Return all the checks, optionally filtered by specified category and/or checkID.
parameters:
- in: query
name: category
- in: query
name: checkID
responses:
'200':
description: A list of check objects. An invalid or unrecognized query parameter returns (200, empty array)
content:
application/json:
schema:
"$ref" : "#/components/schemas/diagnostics"
post:
operationId: diagnosticRunChecks
summary: Run all diagnostic checks, and return any results.
responses:
'200':
description: A list of check results.
content:
application/json:
schema:
"$ref": "#/components/schemas/diagnostics"
/v1/diagnostic_ids:
get:
operationId: diagnosticIDsForCategory
summary: >-
Return a list of the check IDs for the Diagnostics category,
or 404 if there is no such `category`.
Specifying an existing category with no checks
will return status code 200 and an empty array.
parameters:
- in: query
name: category
responses:
'200':
description: A list of the check IDs for the specified category.
content:
application/json:
schema:
type: array
items:
type: string
'404':
description: The category is not recognized.
/v1/extensions:
get:
operationId: listExtensions
summary: List currently-installed RDX extensions.
responses:
'200':
description: A list of installed RDX extensions.
content:
application/json:
schema:
type: object
additionalProperties:
type: object
properties:
version:
type: string
metadata:
type: object
labels:
type: object
additionalProperties:
type: string
'503':
description: >-
The extension manager has not been loaded yet. The client should
retry the request at some future point in time.
/v1/extensions/install:
post:
operationId: installExtension
summary: Install an RDX extension
parameters:
- in: query
name: id
responses:
'201':
description: The extension was installed.
'204':
description: The extension was already installed.
'400':
description: There was an issue with the parameters.
'422':
description: The extension could not be installed.
content:
text/plain:
schema:
type: string
'503':
description: An internal error occurred.
/v1/extensions/uninstall:
post:
operationId: uninstallExtension
summary: Uninstall an RDX extension
parameters:
- in: query
name: id
responses:
'201':
description: The extension was uninstalled.
'204':
description: The extension was already uninstalled.
'400':
description: There was an issue with the parameters.
'422':
description: The extension could not be installed.
content:
text/plain:
schema:
type: string
'503':
description: An internal error occurred.
/v1/factory_reset:
put:
operationId: factoryReset
summary: Factory reset Rancher Desktop, losing user data
requestBody:
description: JSON block giving factory reset options.
content:
application/json:
schema:
type: object
properties:
keepSystemImages:
type: boolean
required: true
responses:
'202':
description: The application is performing a factory reset.
'400':
description: An error occurred
/v1/k8s_reset:
put:
operationId: k8sReset
summary: Reset Kubernetes
requestBody:
description: JSON block giving k8s reset options.
content:
application/json:
schema:
type: object
properties:
mode:
type: string
enum: [ fast, wipe ]
required: true
responses:
'202':
description: The application is performing a Kubernetes reset.
'400':
description: An error occurred
/v1/port_forwarding:
post:
operationId: createPortForward
summary: Create a new port forwarding
requestBody:
description: JSON block consisting of the port forwarding details
content:
application/json:
schema:
type: object
properties:
namespace:
type: string
service:
type: string
k8sPort:
type:
- string
- integer
hostPort:
type: integer
required: true
responses:
'200':
description: The port forwarding was created or already exists; the response contains the listening host port.
content:
text/plain:
schema:
type: integer
'400':
description: The port forwarding could not be created.
delete:
operationId: deletePortForward
summary: Delete a port forwarding
parameters:
- in: query
name: namespace
- in: query
name: service
- in: query
name: k8sPort
responses:
'200':
description: The port forwarding was deleted or doesn't exist.
'400':
description: The port forwarding could not be deleted.
/v1/propose_settings:
put:
operationId: proposeSettings
summary: >-
Propose some settings and determine if the backend needs to be restarted
or reset (losing user data).
requestBody:
description: >-
JSON block consisting of some or all of the current preferences,
with changes applied to any number of settings the backend supports changing this way.
content:
application/json:
schema:
"$ref" : "#/components/schemas/preferences"
required: true
responses:
'202':
description: A description of the effects of the proposed settings on the backend.
content:
application/json:
schema:
type: object
additionalProperties:
type: object
properties:
current: {}
desired: {}
severity:
type: string
enum: [ restart, reset ]
'400':
description: The proposed settings were not valid.
content:
text/plain:
schema:
type: string
/v1/settings:
get:
operationId: listSettings
summary: List the current preference settings
responses:
'200':
description: The current preferences in JSON format
content:
application/json:
schema:
"$ref" : "#/components/schemas/preferences"
put:
operationId: updateSettings
summary: Updates the specified preference settings
requestBody:
description: >-
JSON block consisting of some or all of the current preferences,
with changes applied to any number of settings the backend supports changing this way.
content:
application/json:
schema:
"$ref" : "#/components/schemas/preferences"
required: true
responses:
'202':
description: The settings were accepted.
content:
text/plain:
schema:
type: string
'400':
description: The proposed settings were not valid.
content:
text/plain:
schema:
type: string
/v1/settings/locked:
get:
operationId: listLockedSettings
summary: List the current locked settings
responses:
'200':
description: The locked preferences in JSON format
content:
application/json:
schema:
"$ref" : "#/components/schemas/preferences"
/v1/shutdown:
put:
operationId: shutdownApp
summary: Shuts down Rancher Desktop
responses:
'202':
description: The application is in the process of shutting down.
content:
text/plain:
schema:
type: string
/v1/snapshots:
get:
operationId: listSnapshots
summary: List the snapshots
responses:
'200':
description: The snapshots list in JSON format
content:
application/json:
schema:
type: array
items:
"$ref" : "#/components/schemas/snapshot"
post:
operationId: createSnapshot
summary: Creates a new snapshot
responses:
'200':
description: The snapshot was created.
'400':
description: The snapshot could not be created.
content:
text/plain:
schema:
type: string
delete:
operationId: deleteSnapshot
summary: Deletes a snapshot
parameters:
- in: query
name: name
responses:
'200':
description: The snapshot was deleted.
'404':
description: The snapshot could not be deleted.
content:
text/plain:
schema:
type: string
/v1/snapshots/cancel:
post:
operationId: cancelSnapshot
summary: Cancels active snapshot operation
responses:
'200':
description: The snapshot operation was canceled.
'400':
description: The snapshot could not be canceled.
content:
text/plain:
schema:
type: string
/v1/snapshot/restore:
post:
operationId: restoreSnapshot
summary: Restore a snapshot
parameters:
- in: query
name: name
responses:
'202':
description: The snapshot was restored.
'404':
description: The snapshot could not be restored.
content:
text/plain:
schema:
type: string
/v1/transient_settings:
get:
operationId: listTransientSettings
summary: List the current transient settings
responses:
'200':
description: The current transient settings in JSON format
content:
application/json:
schema:
"$ref" : "#/components/schemas/transientSettings"
put:
operationId: updateTransientSettings
summary: Updates application transient settings
requestBody:
description: JSON block consisting of transient settings
content:
application/json:
schema:
"$ref" : "#/components/schemas/transientSettings"
required: true
responses:
'202':
description: The settings were accepted.
content:
text/plain:
schema:
type: string
'400':
description: The proposed transient settings were not valid.
content:
text/plain:
schema:
type: string
/v1/backend_state:
get:
operationId: getBackendState
summary: Get the current backend state
responses:
'200':
description: The current backend state
content:
application/json:
schema:
type: object
required:
- vmState
- locked
properties:
vmState:
type: string
locked:
type: boolean
put:
operationId: setBackendState
summary: Set the desired backend state
requestBody:
description: >-
JSON block consisting of some or all of desired backend state, with changes applied
to the parts of backend state that are specified.
content:
application/json:
schema:
type: object
properties:
vmState:
type: string
locked:
type: boolean
required: true
responses:
'202':
description: The desired backend state was accepted.
content:
text/plain:
schema:
type: string
components:
schemas:
preferences:
type: object
properties:
application:
type: object
properties:
adminAccess:
type: boolean
x-rd-platforms: [darwin, linux] # Only in the specified platforms
x-rd-usage: enable privileged operations
debug:
type: boolean
x-rd-usage: generate more verbose logging
extensions:
type: object
properties:
allowed:
type: object
properties:
enabled:
type: boolean
x-rd-hidden: true
list:
type: array
items:
type: string
x-rd-hidden: true
installed:
type: object
x-rd-usage: installed extensions and their tag
additionalProperties:
type: string
pathManagementStrategy:
type: string
enum: [manual, rcfiles]
x-rd-platforms: [darwin, linux]
x-rd-usage: update PATH to include ~/.rd/bin
telemetry:
type: object
properties:
enabled:
type: boolean
x-rd-usage: allow collection of anonymous statistics
updater:
type: object
properties:
enabled:
type: boolean
x-rd-usage: automatically update to the latest release
autoStart:
type: boolean
x-rd-usage: start app when logging in
startInBackground:
type: boolean
x-rd-usage: start app without window
hideNotificationIcon:
type: boolean
x-rd-usage: don't show notification icon
window:
type: object
properties:
quitOnClose:
type: boolean
x-rd-usage: terminate app when the main window is closed
theme:
type: string
enum: [system, light, dark]
x-rd-usage: set the color theme (system follows OS setting)
containerEngine:
type: object
properties:
name:
type: string
# TODO "docker" setting should be a hidden alias of "moby".
# Why have two values for exactly the same thing?
enum: [containerd, docker, moby]
x-rd-aliases: [container-engine]
x-rd-usage: set engine
allowedImages:
type: object
properties:
enabled:
type: boolean
x-rd-usage: only allow images to be pulled that match the allowed patterns
patterns:
type: array
# TODO It is not yet possible to specify array/list values with `rdctl set`
x-rd-usage: allowed image names
items:
type: string
mobyStorageDriver:
type: string
enum: [classic, snapshotter, auto]
x-rd-usage: override Moby storage driver selection
virtualMachine:
type: object
properties:
memoryInGB:
type: integer
minimum: 1
x-rd-platforms: [darwin, linux]
x-rd-usage: reserved RAM size
numberCPUs:
type: integer
minimum: 1
x-rd-platforms: [darwin, linux]
x-rd-usage: reserved number of CPUs
type:
type: string
enum: [qemu, vz]
x-rd-platforms: [darwin]
useRosetta:
type: boolean
x-rd-platforms: [darwin]
mount:
type: object
x-rd-platforms: [darwin, linux]
properties:
type:
type: string
enum: [reverse-sshfs, 9p, virtiofs]
x-rd-usage: how directories are shared; 9p is experimental
kubernetes:
type: object
properties:
version:
type: string
x-rd-aliases: [kubernetes-version]
x-rd-usage: choose which version of Kubernetes to run
port:
type: integer
x-rd-usage: apiserver port
enabled:
type: boolean
x-rd-aliases: [kubernetes-enabled]
x-rd-usage: run Kubernetes
options:
type: object
properties:
traefik:
type: boolean
x-rd-usage: install and run traefik
flannel:
type: boolean
x-rd-aliases: [flannel-enabled]
x-rd-usage: use flannel networking; disable to install your own CNI
ingress:
type: object
properties:
localhostOnly:
type: boolean
x-rd-platforms: [win32]
x-rd-usage: bind services to 127.0.0.1 instead of 0.0.0.0
experimental:
type: object
properties:
containerEngine:
type: object
properties:
webAssembly:
type: object
properties:
enabled:
type: boolean
x-rd-usage: enable support for containerd-wasm shims
kubernetes:
type: object
properties:
options:
type: object
properties:
spinkube:
type: boolean
x-rd-usage: install spin operator
virtualMachine:
type: object
properties:
diskSize:
type: string
x-rd-platforms: [darwin, linux]
x-rd-usage: >-
desired size of the disk; changing this setting will not
shrink existing disks (example: 10GiB)
mount:
type: object
x-rd-platforms: [darwin, linux]
properties:
9p:
type: object
properties:
securityModel:
type: string
enum: [passthrough, mapped-xattr, mapped-file, none]
protocolVersion:
type: string
enum: [9p2000, 9p2000.u, 9p2000.L]
msizeInKib:
type: integer
minimum: 4
x-rd-usage: maximum packet size
cacheMode:
type: string
enum: [none, loose, fscache, mmap]
proxy:
type: object
x-rd-platforms: [win32]
x-rd-usage: configure proxy address
properties:
enabled:
type: boolean
x-rd-usage: redirect the traffic to the configured proxy address
address:
type: string
x-rd-usage: proxy address
password:
type: string
x-rd-usage: if needed the password to connect to the proxy
port:
type: integer
x-rd-usage: proxy port
username:
type: string
x-rd-usage: if needed the username to connect to the proxy
noproxy:
type: array
x-rd-usage: list of hostname to exclude from using the proxy
items: { type: string }
sshPortForwarder:
type: boolean
x-rd-platforms: [darwin, linux]
x-rd-usage: use SSH for port forwarding instead of gRPC
WSL:
type: object
x-rd-platforms: [win32]
# TODO It is not yet possible to configure this via `rdctl set`.
x-rd-usage: make container engine and Kubernetes available in these WSL2 distros
properties:
integrations:
type: object
additionalProperties: true
portForwarding:
type: object
properties:
includeKubernetesServices:
type: boolean
x-rd-usage: show Kubernetes system services on Port Forwarding page
images:
type: object
properties:
showAll:
type: boolean
x-rd-usage: show system images on Images page
namespace:
type: string
x-rd-usage: select only images from this namespace (containerd only)
containers:
type: object
properties:
showAll:
type: boolean
x-rd-usage: show system containers on Containers page
namespace:
type: string
x-rd-usage: select only namespaces from this namespace (containerd only)
diagnostics:
type: object
properties:
showMuted:
type: boolean
x-rd-usage: unhide muted diagnostics
mutedChecks:
type: object
# TODO It is not possible to modify this setting via `rdctl set`.
x-rd-usage: diagnostic ids that have been muted
additionalProperties: true
connectivity:
type: object
properties:
interval:
type: integer
x-rd-usage: >-
Number of milliseconds before polling for network access;
set this to zero to disable background connectivity checking
timeout:
type: integer
x-rd-usage: Number of milliseconds to wait before timing out
diagnostics:
type: object
properties:
last_update:
type: string
format: date-time
example: "1970-01-01T00:00:00.000Z"
checks:
type: array
items:
type: object
properties:
id:
type: string
category:
type: string
documentation:
type: string
description:
type: string
passed:
type: boolean
mute:
type: boolean
fixes:
type: array
items:
type: object
properties:
description:
type: string
transientSettings:
type: object
properties:
noModalDialogs:
type: boolean
preferences:
type: object
properties:
navItem:
type: object
properties:
current:
type: string
currentTabs:
type: object
additionalProperties: true
================================================
FILE: pkg/rancher-desktop/assets/styles/app.scss
================================================
@import "./vendor/normalize";
@import "./base/variables";
@import "./base/functions";
@import "./base/mixins";
@import "./base/helpers";
@import "./base/color";
@import "./base/basic";
@import "./base/typography";
@import "./fonts/fontstack";
@import "./fonts/dots";
@import "./fonts/zerowidthspace";
@import "./fonts/icons";
@import "./themes/light";
@media screen and (prefers-color-scheme: dark) {
@import "./themes/dark";
}
@import './themes/_suse.scss';
@import "./global/columns";
@import "./global/cards";
@import "./global/button";
@import "./global/form";
@import "./global/gauges";
@import "./global/labeled-input";
@import "./global/tooltip";
@import "./global/table";
@import "./global/select";
@import "./global/resource";
@import "./vendor/vue-select";
@import "./rancher-desktop.scss";
================================================
FILE: pkg/rancher-desktop/assets/styles/base/_basic.scss
================================================
// -----------------------------------------------------------------------------
// This file contains very basic styles.
// -----------------------------------------------------------------------------
//
HTML {
box-sizing: border-box;
height: 100%;
}
BODY {
color: var(--body-text);
direction: ltr;
position: relative;
margin: 0;
scrollbar-width: thin;
scrollbar-color: var(--scrollbar-thumb) var(--scrollbar-track);
&.overflow-hidden {
overflow: hidden;
}
}
.dashboard-body {
// this was moved to its own class because the background color prop conflicts with storybookjs addon "backgrounds".
// decoupling this style allows us to preview components in both light and dark themes
background: var(--body-bg);
}
::-webkit-scrollbar {
width: 8px !important;
height: 8px !important;
}
::-webkit-scrollbar {
width: 8px !important;
height: 8px !important;
}
::-webkit-scrollbar-thumb {
background-color: var(--scrollbar-thumb) !important;
border-radius: var(--border-radius);
}
::-webkit-scrollbar-track {
background-color: var(--scrollbar-track) !important;
}
/*
* Make all elements from the DOM inherit from the parent box-sizing
* Since `*` has a specificity of 0, it does not override the `html` value
* making all elements inheriting from the root box-sizing value
* See: https://css-tricks.com/inheriting-box-sizing-probably-slightly-better-best-practice/
*/
*, *::before, *::after {
box-sizing: inherit;
}
:focus, .focused {
outline-color: var(--outline);
outline-style: solid;
outline-width: var(--outline-width);
}
INPUT,
SELECT,
TEXTAREA,
BUTTON,
.btn,
.labeled-input,
.labeled-select,
.unlabeled-select,
.checkbox-custom,
.radio-custom {
&:focus, &.focused {
@include form-focus }
}
button,
input,
optgroup,
select,
textarea {
margin: var(--outline-width);
}
A {
@include link-color(var(--link), var(--body-text));
text-decoration: none;
&:hover,
&:active {
text-decoration: underline;
color: var(--body-text);
}
}
HR {
height: 0;
border: 0;
border-top: 1px solid var(--border);
width: 100%;
&.dark {
border-color: var(--nav-bg);
}
}
HR.vertical {
border-top: 0;
border-left: 1px solid var(--border);
height: 100%;
position: absolute;
left: 50%;
margin-left: -1px;
top: 0;
}
================================================
FILE: pkg/rancher-desktop/assets/styles/base/_color.scss
================================================
.bg-transparent {
background-color: transparent;
}
.bg-disabled {
background-color: var(--disabled-bg) !important;
}
.text-disabled {
color: var(--disabled-text) !important;
}
================================================
FILE: pkg/rancher-desktop/assets/styles/base/_functions.scss
================================================
///Computes the "brightness" of a color
@use 'sass:math';
@function brightness($color) {
@if type-of($color) == color {
@return math.div(red($color) * 0.299 + green($color) * 0.587 + blue($color) * 0.114, 255) * 100%;
}
@else {
@return unquote("brightness(#{$color})");
}
}
///Select the more readable foreground color for a given background color.
@function contrast-color($color, $dark: $contrasted-dark, $light: $contrasted-light) {
@if $color == null {
@return null;
}
@else {
$color-brightness: brightness($color);
$dark-text-brightness: brightness($dark);
$light-text-brightness: brightness($light);
@return if(math.abs($color-brightness - $light-text-brightness) > math.abs($color-brightness - $dark-text-brightness), $light, $dark);
}
}
@function add-z-index($key, $value) {
@return map-merge($z-indexes, ($key: $value));
}
@function z-index($key) {
@if map-has-key($z-indexes, $key) {
@return map-get($z-indexes, $key);
}
@warn "Unknown key `#{$key}` in $z-indexes";
@return null;
}
// _decimal.scss | MIT License | gist.github.com/terkel/4373420
// Round a number to specified digits.
//
// @param {Number} $number A number to round
// @param {Number} [$digits:0] Digits to output
// @param {String} [$mode:round] (round|ceil|floor) How to round a number
// @return {Number} A rounded number
// @example
// decimal-round(0.333) => 0
// decimal-round(0.333, 1) => 0.3
// decimal-round(0.333, 2) => 0.33
// decimal-round(0.666) => 1
// decimal-round(0.666, 1) => 0.7
// decimal-round(0.666, 2) => 0.67
//
@function decimal-round ($number, $digits: 0, $mode: round) {
$n: 1;
// $number must be a number
@if type-of($number) != number {
@warn '#{ $number } is not a number.';
@return $number;
}
// $digits must be a unitless number
@if type-of($digits) != number {
@warn '#{ $digits } is not a number.';
@return $number;
} @else if not unitless($digits) {
@warn '#{ $digits } has a unit.';
@return $number;
}
@for $i from 1 through $digits {
$n: $n * 10;
}
@if $mode == round {
@return math.div(round($number * $n), $n);
} @else if $mode == ceil {
@return math.div(ceil($number * $n), $n);
} @else if $mode == floor {
@return math.div(floor($number * $n), $n);
} @else {
@warn '#{ $mode } is undefined keyword.';
@return $number;
}
}
// Ceil a number to specified digits.
//
// @param {Number} $number A number to round
// @param {Number} [$digits:0] Digits to output
// @return {Number} A ceiled number
// @example
// decimal-ceil(0.333) => 1
// decimal-ceil(0.333, 1) => 0.4
// decimal-ceil(0.333, 2) => 0.34
// decimal-ceil(0.666) => 1
// decimal-ceil(0.666, 1) => 0.7
// decimal-ceil(0.666, 2) => 0.67
//
@function decimal-ceil ($number, $digits: 0) {
@return decimal-round($number, $digits, ceil);
}
// Floor a number to specified digits.
//
// @param {Number} $number A number to round
// @param {Number} [$digits:0] Digits to output
// @return {Number} A floored number
// @example
// decimal-floor(0.333) => 0
// decimal-floor(0.333, 1) => 0.3
// decimal-floor(0.333, 2) => 0.33
// decimal-floor(0.666) => 0
// decimal-floor(0.666, 1) => 0.6
// decimal-floor(0.666, 2) => 0.66
//
@function decimal-floor ($number, $digits: 0) {
@return decimal-round($number, $digits, floor);
}
@function sizzle-gradient($color) {
$angle: 135deg;
$startPos: 0%;
$start: 0.3;
$middlePos: 110px;
$middle: 0.1;
$endPos: 100%;
$end: 0;
@return transparent linear-gradient(#{$angle},
#{rgba($color, $start)} #{$startPos},
#{rgba($color, $middle)} #{$middlePos},
#{rgba($color, $end)} #{$endPos}
) 0% 0% no-repeat padding-box;
}
================================================
FILE: pkg/rancher-desktop/assets/styles/base/_helpers.scss
================================================
// -----------------------------------------------------------------------------
// This file contains CSS helper classes.
// -----------------------------------------------------------------------------
// Text indent, margins, and paddings from 5-50px in 5px increments
// e.g. in-10 - {text-indent: 10px}
// e.g. p-10 - {padding: 10px}
// e.g. mt-20 - {margin-top: 20px}
$spacing-property-map: (
m: margin,
mt: margin-top,
mr: margin-right,
ml: margin-left,
mb: margin-bottom,
p: padding,
pt: padding-top,
pb: padding-bottom,
pl: padding-left,
pr: padding-right,
in: text-indent,
);
@each $keyword, $property in $spacing-property-map {
.#{$keyword}-0 { #{$property}: 0 !important; }
@for $size from 1 through 10 {
$val: $size * 5;
.#{$keyword}-#{$val} { #{$property}: $val * 1px !important; }
}
}
.spacer {
padding: 40px 0 0 0;
}
.spacer-small {
padding: 20px 0 0 0;
}
.pull-right {
float: right !important;
}
.pull-left {
float: left !important;
}
/**
* Main content containers
* 1. Make the container full-width with a maximum width
* 2. Center it in the viewport
* 3. Leave some space on the edges, especially valuable on small screens
*/
.container {
max-width: $max-width; /* 1 */
min-width: $min-width;
margin-left: auto; /* 2 */
margin-right: auto; /* 2 */
padding-left: 20px; /* 3 */
padding-right: 20px; /* 3 */
width: 100%; /* 1 */
}
/**
* Hide text while making it readable for screen readers
* 1. Needed in WebKit-based browsers because of an implementation bug;
* See: https://code.google.com/p/chromium/issues/detail?id=457146
*/
.hide-text {
overflow: hidden;
padding: 0; /* 1 */
text-indent: 101%;
white-space: nowrap;
}
/**
* Hide element while making it readable for screen readers
* https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css#L119-L133
*/
.visually-hidden {
border: 0;
clip: rect(0 0 0 0);
height: 1px;
margin: -1px;
overflow: hidden;
padding: 0;
position: absolute;
width: 1px;
}
.text-left {
text-align: left !important;
}
.text-center {
text-align: center !important;
}
.text-right {
text-align: right !important;
}
.text-small {
font-size: .8em;
}
.text-normal {
font-size: initial;
}
.text-italic {
font-style: italic;
}
.text-bold {
font-weight: bold;
}
.text-uppercase {
text-transform: uppercase;
}
.text-lowercase {
text-transform: lowercase;
}
.text-capitalize {
text-transform: capitalize;
}
.text-label {
color: var(--input-label);
}
.hide {
display: none !important;
}
.block {
display: block !important;
}
.inline {
display: inline !important;
}
.inline-block {
display: inline-block !important;
}
.table-cell {
display: table-cell !important;
}
.vertical-middle {
display: inline-block;
vertical-align: middle;
}
.invisible {
visibility: hidden;
}
.helper-text {
font-size: 12px;
color: var(--secondary);
}
// Only display content to screen readers
//
// See: http://a11yproject.com/posts/how-to-hide-content
.sr-only {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
clip: rect(0,0,0,0);
border: 0;
}
// Use in conjunction with .sr-only to only display content when it's focused.
// Useful for "Skip to main content" links; see http://www.w3.org/TR/2013/NOTE-WCAG20-TECHS-20130905/G1
// Credit: HTML5 Boilerplate
.sr-only-focusable {
&:active,
&:focus {
position: static;
width: auto;
height: auto;
margin: 0;
overflow: visible;
clip: auto;
}
}
[role="button"] {
cursor: pointer;
}
.eased {
-webkit-transition: all 0.5s ease;
-moz-transition: all 0.5s ease;
-o-transition: all 0.5s ease;
transition: all 0.5s ease;
}
.no-ease {
-webkit-transition: none !important;
-moz-transition: none !important;
-o-transition: none !important;
transition: none !important;
}
.full-height {
height: 100%;
}
.full-width {
width: 100%;
}
.align-top {
vertical-align: top !important;
}
.vertical-scroll {
max-height: 150px;
overflow: scroll;
}
.comma-list {
span:after {
content: ','
}
span:last-of-type:after {
content: ''
}
}
.link[disabled] {
pointer-events: none;
}
.subtle-box {
border: .5px solid var(--subtle-border);
box-shadow: 0 0 10px var(--shadow);
padding: 20px;
margin-bottom: 20px;
border-radius: var(--border-radius);
}
.plus-more {
color: var( --input-placeholder );
font-size: 0.8em;
}
================================================
FILE: pkg/rancher-desktop/assets/styles/base/_mixins.scss
================================================
// -----------------------------------------------------------------------------
// This file contains all application-wide Sass mixins.
// -----------------------------------------------------------------------------
/// Clear inner floats
@mixin clearfix() {
&:before,
&:after {
content: " "; // 1
display: table; // 2
}
&:after {
clear: both;
}
}
@mixin list-unstyled {
margin: 0;
padding: 0;
list-style-type: none;
}
@mixin no-select {
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
@mixin no-resize {
resize : none;
}
@mixin hand {
cursor : pointer;
cursor : hand;
}
@mixin fixed {
table-layout : fixed;
}
@mixin clip {
text-overflow : ellipsis;
overflow : hidden;
white-space : nowrap;
word-wrap : break-word;
}
@mixin force-wrap {
word-wrap : break-word;
white-space: normal;
}
@mixin bordered-section {
border-bottom: 1px solid var(--border);
margin-bottom: 20px;
padding-bottom: 20px;
}
@mixin section-divider {
margin-bottom: 20px;
margin-top: 20px;
}
.clearfix { @include clearfix; }
.list-unstyled { @include list-unstyled }
.no-select { @include no-select }
.no-resize { @include no-resize }
.hand { @include hand }
.fixed { @include fixed }
.clip { @include clip }
.force-wrap { @include force-wrap }
.bordered-section { @include bordered-section }
.section-divider { @include section-divider }
/// Sets the specified background color and calculates a dark or light contrasted text color.
@mixin contrasted($background-color, $dark: $contrasted-dark, $light: $contrasted-light) {
color: contrast-color($background-color, $dark, $light);
&:hover {
text-decoration: underline;
color: var(--body-text);
}
}
/// Sets base color and darkens bg on hover
@mixin bg-lighten($bg) {
background: $bg;
* {
background:lighten($bg,20%);
}
}
@mixin link-color($color, $hover) {
@if not($hover) {
$hover: $color;
}
color: $color;
&:hover
{
text-decoration: underline;
color: $hover;
}
}
@mixin icon-rotate($degrees, $rotation) {
filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=#{$rotation});
-webkit-transform: rotate($degrees);
-ms-transform: rotate($degrees);
transform: rotate($degrees);
}
@mixin icon-flip($horiz, $vert, $rotation) {
filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=#{$rotation});
-webkit-transform: scale($horiz, $vert);
-ms-transform: scale($horiz, $vert);
transform: scale($horiz, $vert);
}
@mixin input-status-color {
&:not(.focused) {
&.success {
border: solid 1px var(--success);
input, .selected {
color: var(--success);
}
.vs__actions:after {
color: var(--success);
}
}
&.warning {
border: solid 1px var(--warning);
input, .selected {
color: var(--warning);
}
.vs__actions:after {
color: var(--warning);
}
}
&.error {
border: solid 1px var(--error);
input, .selected {
color: var(--error);
}
.vs__actions:after {
color: var(--error);
}
}
}
}
@mixin form-focus {
// Focus for form like elements (not to be confused with basic :focus style)
outline: none;
box-shadow: 0 0 0 var(--outline-width) var(--outline);
background: var(--input-focus-bg)
}
================================================
FILE: pkg/rancher-desktop/assets/styles/base/_typography.scss
================================================
HTML, BODY {
font-family: $body-font;
font-size: 14px;
}
H1, H2, H3, H4, H5, H6 {
color: var(--body-text);
font-style: normal;
font-weight: 400;
margin: 0 0 10px 0;
}
H1 {
font-size: 24px;
}
H2 {
font-size: 21px;
}
H3 {
font-size: 18px;
}
H4 {
font-size: 16px;
}
H5 {
font-size: 14px;
}
H6 {
font-size: 12px;
text-transform: uppercase;
}
P {
font-weight: 400;
font-style: normal;
margin: 0;
}
//code
code, samp, kbd, .monospace {
font-family: $mono-font;
text-align: left;
}
pre {
padding: 10px;
border-radius: var(--border-radius);
background: var(--box-bg);
margin: 5px;
overflow: auto;
}
code {
background-color: var(--box-bg);
display: inline-block;
padding: 5px;
border: 1px solid var(--border);
border-radius: var(--border-radius);
}
.v-popper__popper code,
pre code {
background: transparent;
padding: 0;
}
================================================
FILE: pkg/rancher-desktop/assets/styles/base/_variables.scss
================================================
$header-font: 'Poppins', sans-serif;
$body-font: 'Lato', arial, helvetica, sans-serif;
$mono-font: 'Roboto Mono', monospace;
$max-width: 1440px !default;
$min-width: 75% !default;
$input-height: 61px;
$unlabeled-input-height: 40px;
$input-padding-lg: 18px;
$input-padding-sm: 10px;
$input-line-height: 18px;
$column-gutter: 1.75%;
$sideways-tabs-width: 200px;
$array-list-remove-margin: 75px;
$z-indexes: (
zero: 0,
default: 1,
overContent: 2,
hoverOverContent: 3,
tableGroup: 10,
fixedTableHeader: 11,
modalOverlay: 20,
modalContent: 21,
tooltip: 30,
dropdownOverlay: 40,
dropdownContent: 41,
loadingOverlay: 50,
loadingContent: 51
);
// Usage Example:
// @media only screen and (min-width: map-get($breakpoints, '--viewport-*')) {
// }
$breakpoints: (
'--viewport-4': 480px,
'--viewport-7': 768px,
'--viewport-9': 992px,
'--viewport-12': 1281px,
);
================================================
FILE: pkg/rancher-desktop/assets/styles/fonts/_dots.scss
================================================
@font-face {
font-family: 'dotsfont';
font-weight: normal;
font-style: normal;
src: url(data:application/font-woff;charset=utf-8;base64,d09GRgABAAAAAAyEABEAAAAAV7gAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABGRlRNAAABgAAAABwAAAAcgM9Wv0dERUYAAAGcAAAAHQAAAB4AJwDeT1MvMgAAAbwAAABMAAAAYHOnumtjbWFwAAACCAAAAUwAAAGSId/p/mN2dCAAAANUAAAADAAAAAwF+BA6ZnBnbQAAA2AAAAGxAAACZVO0L6dnYXNwAAAFFAAAAAgAAAAIAAAAEGdseWYAAAUcAAABlQAARtxIEfQVaGVhZAAABrQAAAAyAAAANhPqDEtoaGVhAAAG6AAAAB4AAAAkD9kKlmhtdHgAAAcIAAAATAAAA2Cb0mq3bG9jYQAAB1QAAAGfAAABsumd1/5tYXhwAAAI9AAAACAAAAAgAfIAQm5hbWUAAAkUAAABRwAAApIWY2jUcG9zdAAAClwAAAHJAAACkyUTPVZwcmVwAAAMKAAAAFIAAABSWo1sY3dlYmYAAAx8AAAABgAAAAaskFpGAAAAAQAAAADV7pT1AAAAANR0ZLoAAAAA1mxdD3jaY2BkYGDgAWIxIGZiYATC60DMAuYxAAAM2wEGAAAAeNpjYOaUY5zAwMrAwmrMcpaBgWEWhGY6y5DGlAbkA6XggJkBCYR6h/sxODAoqP5hY/gH5LMxMGkoMDAwguQYvzC9A1IKDIwAF8ELN3jaY2BgYGaAYBkGRgYQ6AHyGMF8FoYCIC3BIAAU4WCoY9jC8J/pGNMdBS4FEQVJBX2FeNU///8DVSgwLGDYBpZhUBBQkIDJ/H/8/9D/g39//3324PCDfQ92P1j2oPzWbagtWAEjGwNcmpEJSDChKwA6lYWVjZ2Dk4ubh5ePX0BQSFhEVExcQlJKWkZWTl5BUUlZRVVNXUNTS1tHV0/fwNDI2MTUzNzC0sraxtbO3sHRydnF1c3dw9PL28fXzz8gMCg4JDQsPCIyKjomNi4+IZGhta2ja9L0uYsWLl66ZNmKVStXr1m3dv2GTVs2b92+bfeuPXsZilJSMxnKFxRkM5RlMbTPZChmYEiHuC6nmmH5zobkPBA7t4YhqbFlGgPDxUsMDJev7GA4APNDBRA3dzf1dPb1T+idMpVh8uw5sxgOHioEClcCMQAKvGiaAAAFdQW0BbQARAUReNpdUbtOW0EQ3Q0PA4HE2CA52hSzmZDGe6EFCcTVjWJkO4XlCGk3cpGLcQEfQIFEDdqvGaChpEibBiEXSHxCPiESM2uIojQ7O7NzzpkzS8qRqnfpa89T5ySQwt0GzTb9Tki1swD3pOvrjYy0gwdabGb0ynX7/gsGm9GUO2oA5T1vKQ8ZTTuBWrSn/tH8Cob7/B/zOxi0NNP01DoJ6SEE5ptxS4PvGc26yw/6gtXhYjAwpJim4i4/plL+tzTnasuwtZHRvIMzEfnJNEBTa20Emv7UIdXzcRRLkMumsTaYmLL+JBPBhcl0VVO1zPjawV2ys+hggyrNgQfYw1Z5DB4ODyYU0rckyiwNEfZiq8QIEZMcCjnl3Mn+pED5SBLGvElKO+OGtQbGkdfAoDZPs/88m01tbx3C+FkcwXe/GUs6+MiG2hgRYjtiKYAJREJGVfmGGs+9LAbkUvvPQJSA5fGPf50ItO7YRDyXtXUOMVYIen7b3PLLirtWuc6LQndvqmqo0inN+17OvscDnh4Lw0FjwZvP+/5Kgfo8LK40aA4EQ3o3ev+iteqIq7wXPrIn07+xWgAAAAABAAH//wAPeNrt2jFLI0EUwPH3dncSBeVcOWxssnE97ggYTYxVOlNa2PgNLPwOVgpiYXNe5XewMLOoYDiOuxPtQsDCWGhhuYiFrcrgrkW6a2yE888w8OYx7/emfyOetES8VbMivhRlxqpUm0kxCO5rtmCum4nvZaFYP0+bPJ0UC+a5mWier4dROB2FUcsruVj33JpZedxvBV0RUe3LSHHHbMgnmZR2ULUjmmp7rGpFK3bIS22oFZmd08/jQb0WjzfmvXiqHHja7+hSr6fLvzvusNtzB3+8X6fu/OhEF8/+avO4435iY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2Njv7MtbwH4W4aNjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY39oebC726Y/sCYqNcWcuPLVLmQGZvp7o+77c30++799r/rhwf1EkSlWBrzEkelQLSvRi+0nK0rHXVPbsbduhv31T38X7Py/PFmy2yJL98kyQ4Vq5oOAs3ateXS+lmXfJvXTnNhFOZFj+vZvReYPphnAAAAeNpjYGRgYADiTd8OXoznt/nKIM/BAAJXSlJ2gehrObH8IJrzOmsrkOJgYALxAFBACosAAHjaY2BkYGBj+HuDgYH7KQMQcF5nYGRABTcAYDIEhgAAeNpjesPgwgAETKsYGDhngjDj9VE8EHg07AcaMx1iYGBtBeYFKM14HYgTgZnjNRRvAPKlgLQfalyxP2W8zv0UwQepAekDmQEADJUueHjadcI7KIQBAADg//1+v98vkiQZJEmSDJIkXZcuGS5J0iXDdYMkSZcMMkiSDDJckiTJIIOkSwYZJF2XdBl0SZckA8mq7wMAoP5PAsgCR8ATKIEdYArcAPPgB1QDxaA5aB8qwAzcCo/Bq/A5/IYESB8yjeSQOxRDm9Akuoyeoi+YhXVjaWwbu8EBvAEfwhfxY7xEKEQnMUlsElfEJ1lLxsl58oAsUhzVRo1Ta9QFVaEjup+eoXfpe4ZgmpkRZoU5Y8qsw/awGXaHveUgrpEb5pa4E+6Z1/guforf4q/5L6FOGBQWhEPhURR+tIsT4rp4Kb5L1dKANCvtSQ8yJbfICTkrH8lPiqR0KCllQ8krH2qNGlPn1H21oDFaqzamrWrn2pse6H36tJ7T7wzMaDKSxrJxaryYltltps1t88YCrAZryFq0jq2Srdid9qS9aV/Zn06tE3fmnQOn6HJumzvurrkXbsWLvH5vxtv17n3Cb/ZH/BX/zC8HTtATZIKd4DaEwsZwOFwKT8LnSIu6/jEaZaNclI9eq7yq3l+pb5RddPEAAAEAAADYABAAAgAAAAAAAgABAAIAFgAAAQAALgAAAAB42oWRu0oDQRSGvzFRiEjURiTVFrbGRIjXSpQ0gkVE05rLGqMbL9kkoE/gkwj2VhapvTyBjc9h6b+zQxIWQZaZ8838Z/5zZhaY55UUJp0BLjViNixoFfMUWR4cp9jg0XEaj6HjaXJ8O57R/o/jDEtm2fEcKybveJGsqTh+U86Z43cKZuD4QzlPjj+ZNS8xf6XImSH73HDLPV3atLigp6rPGusUKKpLj7pUj0PNAb6oqjkgtPt5rfe0ChTHDqFd+Yq+4kBzU5lNVYq0c8VrUUX7Lfo6W1NWURkF++1yompVjkTJM6vWd3wqqXsJ11PbQajOIt2bqPKf89/3jW7X03uF7LCmryPtymXmacirk/CJTtcn+os7KNvX8jiQ2rCvvW21knxLbNm5NPoLm+rWl0fNuvbk1XU3Ko98j7mT2pYS1Q9+AfhBY1UAeNpt0UdMVHEUxeHfpczA0DvYFUFRwPfeMBT7DDhYUGyISlEUmBlFRHBUbGgssUSCMWEHEXWjiZpoosaEFQsVe0ACLlzbMCzUnYno+7vzbL7kLM7iXgL4m19e6vlfhkECJJBAggjGgpUQQrERRjgRRBJFNDHEEkc8CSSSRDIpTGAik5jMFKYyjenMIJWZpJHOLGaTwRzmkkkW2cxDQ8fATg4OcskjnwLms4CFLGIxS1iKExeFFLEMN8UsZwUrWUUJq1lDKWtZx3o2sJEyNlHOZrawlQoqqaKabWynRoK4zmnO0EsnHzlLOxfp4iY3JJgLvOcUV8QiVi5xjj4+SAjd3OIH3/nJNW7zjCfcYQc76aCW59TxlH5e84KXvOLT+O0GeMNb7uJhjMsMMcg7vHxhlPPswsdu9tBAI1fZyz6aaKYFP/s5wEE+c4jDtHKEYxzlET20cZwTnOQr33g8/oMRCRWbhEm4REikREm0xEisxEm8JEgi97jPAx5KkiRLisXT0Nrk1U0Mq7/Rp2lOTVlk6lK9y64s+KOhaZpSVxpKuzJH6VDmKvOU+cp/e05TXe3quq3e5/E319XWtHjNynCbOkwd7sLfiLiJTAAAALgB/4WwAY0AS7AIUFixAQGOWbFGBitYIbAQWUuwFFJYIbCAWR2wBitcWACwASBFsAMrRAGwAiBFsAMrRLADIEW6AAJ//wACK7EDRnYrRFmwFCsAAAABWkasjwAA) format('woff');
}
.conceal:not(:invalid):not(:focus) {
font-family: 'dotsfont' !important;
font-size: 6px;
}
.conceal PRE {
font-family: 'dotsfont' !important;
font-size: 10px;
}
================================================
FILE: pkg/rancher-desktop/assets/styles/fonts/_fontstack.scss
================================================
/* poppins-300 - latin */
@font-face {
font-family: 'Poppins';
font-style: normal;
font-weight: normal;
src: local(''),
url('@pkg/assets/fonts/poppins/poppins-v15-latin-300.woff2') format('woff2'), /* Super Modern Browsers */
url('@pkg/assets/fonts/poppins/poppins-v15-latin-300.woff') format('woff'), /* Modern Browsers */
}
/* poppins-500 - latin */
@font-face {
font-family: 'Poppins';
font-style: normal;
font-weight: bold;
src: local(''),
url('@pkg/assets/fonts/poppins/poppins-v15-latin-500.woff2') format('woff2'), /* Super Modern Browsers */
url('@pkg/assets/fonts/poppins/poppins-v15-latin-500.woff') format('woff'), /* Modern Browsers */
}
/* lato-regular - latin */
@font-face {
font-family: 'Lato';
font-style: normal;
font-weight: normal;
src: local(''),
url('@pkg/assets/fonts/lato/lato-v17-latin-regular.woff2') format('woff2'), /* Super Modern Browsers */
url('@pkg/assets/fonts/lato/lato-v17-latin-regular.woff') format('woff'), /* Modern Browsers */
}
/* lato-700 - latin */
@font-face {
font-family: 'Lato';
font-style: normal;
font-weight: bold;
src: local(''),
url('@pkg/assets/fonts/lato/lato-v17-latin-700.woff2') format('woff2'), /* Super Modern Browsers */
url('@pkg/assets/fonts/lato/lato-v17-latin-700.woff') format('woff'), /* Modern Browsers */
}
/* roboto-mono-regular - latin */
@font-face {
font-family: 'Roboto Mono';
font-style: normal;
font-weight: normal;
src: local(''),
url('@pkg/assets/fonts/roboto-mono/roboto-mono-v13-latin-regular.woff2') format('woff2'), /* Super Modern Browsers */
url('@pkg/assets/fonts/roboto-mono/roboto-mono-v13-latin-regular.woff') format('woff'), /* Modern Browsers */
}
================================================
FILE: pkg/rancher-desktop/assets/styles/fonts/_icons.scss
================================================
@use 'sass:math';
@import "~rancher-icons/style.scss";
// Animated Icons
// --------------------------
.icon-spin {
-webkit-animation: icon-spin 5000ms infinite linear;
animation: icon-spin 5000ms infinite linear;
}
@-webkit-keyframes icon-spin {
from {
transform:rotate(0deg);
}
to {
transform:rotate(360deg);
}
}
@keyframes icon-spin {
from {
transform:rotate(0deg);
}
to {
transform:rotate(360deg);
}
}
// FontAwesomeness
$icon-li-width: math.div(30em, 14) !default;
$icon-inverse: #fff !default;
.icon {
display: inline-block;
}
// Sizes
.icon-fw {
width: math.div(18em, 14);
text-align: center;
}
.icon-sm {
font-size: (1em*0.8);
}
.icon-lg {
font-size: math.div(4em, 3);
line-height: (3em * 0.25);
vertical-align: -15%;
}
.icon-2x { font-size: 2em; }
.icon-3x { font-size: 3em; }
.icon-4x { font-size: 4em; }
.icon-5x { font-size: 5em; }
// Stacked
.icon-stack {
position: relative;
display: inline-block;
// width: 2em;
height: 2em;
line-height: 2em;
vertical-align: middle;
}
.icon-stack-1x, .icon-stack-2x {
position: absolute;
left: 0;
width: 100%;
text-align: center;
}
.icon-stack-1x { line-height: inherit; }
.icon-stack-2x { font-size: 2em; }
.icon-inverse { color: $icon-inverse; }
// List
.icon-ul {
padding-left: 0;
margin-left: $icon-li-width;
list-style-type: none;
> li { position: relative; }
}
.icon-li {
position: absolute;
left: -$icon-li-width;
width: $icon-li-width;
top: math.div(2em, 14);
text-align: center;
&.icon-lg {
left: -$icon-li-width + math.div(4em, 14);
}
}
.icon-rotate-90 { @include icon-rotate(90deg, 1); }
.icon-rotate-180 { @include icon-rotate(180deg, 2); }
.icon-rotate-270 { @include icon-rotate(270deg, 3); }
.icon-flip-horizontal { @include icon-flip(-1, 1, 0); }
.icon-flip-vertical { @include icon-flip(1, -1, 2); }
================================================
FILE: pkg/rancher-desktop/assets/styles/fonts/_zerowidthspace.scss
================================================
// Zero-width space font, for killing space between inline-block elements
@font-face {
font-family: 'zerowidthspace';
font-weight: normal;
font-style: normal;
src: url(data:application/font-woff;charset=utf-8;base64,d09GRgABAAAAAAakABEAAAAACYwAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABGRlRNAAABgAAAABwAAAAceSKT/EdERUYAAAGcAAAAIgAAACYAJwAsT1MvMgAAAcAAAABKAAAAYGJnjp9jbWFwAAACDAAAAEoAAAFSBLks8GN2dCAAAAJYAAAAAgAAAAIAAAAAZnBnbQAAAlwAAAGxAAACZVO0L6dnYXNwAAAEEAAAAAgAAAAIAAAAEGdseWYAAAQYAAAANgAAADg03gskaGVhZAAABFAAAAA0AAAANgPvJ9JoaGVhAAAEhAAAAB4AAAAkBEYD32htdHgAAASkAAAAFQAAABgKqv8zbG9jYQAABLwAAAAOAAAADgBEADxtYXhwAAAEzAAAAB8AAAAgASAADG5hbWUAAATsAAABUgAAAq4ayl+KcG9zdAAABkAAAAAsAAAAPmMjcrlwcmVwAAAGbAAAAC4AAAAusPIrFHdlYmYAAAacAAAABgAAAAa9JVnfAAAAAQAAAADUUbVqAAAAAM7LcO4AAAAA1gVto3jaY2BkYGDgAWIxBjkGJgZGIGQFYhagCBMQM0IwAAipAFQAAHjaY2BhYWD8wsDKwMJqzDqDgYFRHkIzX2VIYRJgYGBiYGNmgAEECwgC0lxTGA4wKKj+YUv7l8bAwOLCoAEUZkRSosDACADyugnvAAB42mNgYGBmgGAZBkYGEPAB8hjBfBYGAyDNAYRMQFqBYYHqn///Eaz/j/+n3OKE6gIDRjYGOJcRpIeJARUwQqwaGoCFLF0AOm0M0gAAAAAAAHjaXVG7TltBEN0NDwOBxNggOdoUs5mQxnuhBQnE1Y1iZDuF5QhpN3KRi3EBH0CBRA3arxmgoaRImwYhF0h8Qj4hEjNriKI0Ozuzc86ZM0vKkap36WvPU+ckkMLdBs02/U5ItbMA96Tr642MtIMHWmxm9Mp1+/4LBpvRlDtqAOU9bykPGU07gVq0p/7R/AqG+/wf8zsYtDTT9NQ6CekhBOabcUuD7xnNussP+oLV4WIwMKSYpuIuP6ZS/rc052rLsLWR0byDMxH5yTRAU2ttBJr+1CHV83EUS5DLprE2mJiy/iQTwYXJdFVTtcz42sFdsrPoYIMqzYEH2MNWeQweDg8mFNK3JMosDRH2YqvECBGTHAo55dzJ/qRA+UgSxrxJSjvjhrUGxpHXwKA2T7P/PJtNbW8dwvhZHMF3vxlLOvjIhtoYEWI7YimACURCRlX5hhrPvSwG5FL7z0CUgOXxj3+dCLTu2EQ8l7V1DjFWCHp+29zyy4q7VrnOi0J3b6pqqNIpzftezr7HA54eC8NBY8Gbz/v+SoH6PCyuNGgOBEN6N3r/orXqiKu8Fz6yJ9O/sVoAAAAAAQAB//8AD3jaY2D6b8zAwHCWxYWBmYGdgUFdUFGQ1VhQ+SzjrH9nz5xJYy7/05kGlGRgZEAChgwAYkELOgAAeNpjYGRgYABincimc/H8Nl8ZuDkYQODc6YJ3IPoaa+7i/8ZAxlkWFyDJwcAEEgUAM5AKvXjaY2BkYGBx+X8DSDL8N2ZgYDjLABRBAWwAa5AEKwAAeNpjYfhvzAAETKsY4IAFiAEktwHnAAAAAAAAFAAUABQAFAAUABwAAHjaY2BkYGBgY+BgYGIAASYGRiAWY2BgZIAAAAMcAC4AeNqFUctKw0AUPWOqYBGXLlyUWSrYGCtW7LbQhSAWKgruUjuxkfpKUsQu/QjX4ke4dqnVH/AH/ALX4snMNShCZZg7594599yTCYA53MKDKs0CuOd2WKHCzOEpch4FezjBq+ASVlRd8DS06gqeQUXdCH7CgroT/IxAPQgeY169C35BWX04/OZhUX2iiQQGITLGHjS6uGbcZiXFOc6I2/AZm8yO0Cc7ZNWg/KfzCjFxn6hlOzN7JjjmvUaNKgHPJTIyrgs0sMoVCTcquD4nR4z5lAzLGPFMeJvr9+yElN0h3RiiHTs9xhCnE+c2uA9FqYqDQkuj80NNiw9NhuFbuL4tdqxRqcodEK3/40n/crVvuSkz956B1fDtmTubrOV8fL+Sls49sobMdsly1ZqNG/QboM7oKs7vJnUNNUL2DMjPv9C5aRW6HVzyNuZN/lcHX4j5amYAAHjaY2BiAIO/5xnSGLABNiBmZGBiYGZkYmRmL83LNDBwNADRRqZuzgCcZgavuAH/hbABjQBLsAhQWLEBAY5ZsUYGK1ghsBBZS7AUUlghsIBZHbAGK1xYWbAUKwAAAAFZ370kAAA=) format('woff');
}
================================================
FILE: pkg/rancher-desktop/assets/styles/global/_button.scss
================================================
$btn-padding: 0 21px 0 21px;
$btn-sm-padding: 0 7px 0 7px;
$btn-height: 40px;
$btn-sm-height: 30px;
// -----------------------------------------------------------------------------
// This file contains all styles related to the button component.
// -----------------------------------------------------------------------------
.btn,
button,
[class^='btn-'] {
display: inline-block;
text-align: center;
white-space: nowrap;
vertical-align: middle;
cursor: pointer;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
border: 0;
padding: $btn-padding;
border-radius: var(--border-radius);
color: var(--lightest);
line-height: $btn-height;
min-height: $btn-height;
&:hover {
text-decoration: none;
color: var(--lightest);
}
&.bg-transparent {
color: var(--body-text);
}
}
//icon button
.icon-btn {
padding: 0;
line-height: initial;
&.btn-sm {
padding: 0;
}
span {
padding: 0 10px 0 5px;
vertical-align: middle;
}
}
.btn-sm,
.btn-group-sm > .btn,
.btn-sm .btn-label {
padding: $btn-sm-padding;
min-height: $btn-sm-height;
line-height: 28px;
}
//btn roles
.role-primary {
background: var(--primary);
color: var(--primary-text);
&:hover {
background-color: var(--primary-hover-bg);
color: var(--primary-text);
}
&:focus {
background-color: var(--primary-hover-bg);
color: var(--primary-text);
}
}
.role-secondary {
background: transparent;
color: var(--primary) !important;
border: solid 1px var(--primary);
line-height: $btn-height - 2px;
}
.role-tertiary {
background: var(--accent-btn);
border: solid 1px var(--primary);
color: var(--primary);
}
.role-danger {
background: var(--error);
&:hover {
background-color: var(--error-hover-bg);
}
}
.role-link {
background: transparent;
color: var(--link) !important;
}
.role-multi-action {
background: var(--accent-btn);
border: solid thin var(--primary);
color: var(--primary);
border-radius: 2px;
}
.icon-group i {
font-size: 1.5em;
}
//disabled
.btn-disabled,
.btn.disabled,
.btn[disabled],
fieldset[disabled] .btn {
cursor: not-allowed;
color: var(--disabled-text) !important;
&:not(.role-link){
background-color: var(--disabled-bg) !important;
}
}
.btn-group {
position: relative;
text-align: initial;
vertical-align: middle;
padding: 0;
border-radius: var(--border-radius);
.btn {
position: relative;
display: inline-block;
border-radius: 0;
text-align: center;
&:focus {
// Move the focused one to the top so that the focus ring is all visible
z-index: 1;
}
&.active {
@extend .bg-primary;
}
&:first-child {
border-top-left-radius: var(--border-radius);
border-bottom-left-radius: var(--border-radius);
}
&:last-child {
border-top-right-radius: var(--border-radius);
border-bottom-right-radius: var(--border-radius);
}
}
.btn[disabled] {
// Ensure disabled button's border remains as-is; otherwise, button appears vertically shorter than others in group
border: inherit;
}
}
================================================
FILE: pkg/rancher-desktop/assets/styles/global/_cards.scss
================================================
.links {
display: flex;
flex-wrap: wrap;
width: 100%;
.link-container {
position: relative;
background-color: var(--input-bg);
border-radius: var(--border-radius);
border: solid 1px var(--input-border);
display: flex;
flex-basis: 40%;
margin: 0 10px 10px 0;
max-width: 325px;
min-height: 100px;
border-left: solid 10px var(--primary);
a[disabled], &.disabled {
cursor: not-allowed;
color: var(--disabled-text);
}
&.disabled{
background-color: var(--disabled-bg);
border-left: solid 10px var(--disabled-text);
> * {
opacity: .3;
}
.disabled-msg{
position:absolute;
color: var(--error);
z-index: z-index('hoverOverContent');
opacity: 1;
top: 0px;
bottom: 0px;
left: 0px;
right: 0px;
display: flex;
justify-content: center;
align-items: flex-end;
}
}
&:hover:not(.disabled) {
box-shadow: 0px 0px 1px var(--outline-width) var(--outline);
}
> * {
align-items: center;
display: flex;
flex: 1 0;
padding: 10px;
.link-logo,
.link-content {
display: inline-block;
height: 100%;
}
.link-logo {
text-align: center;
width: 60px;
height: 60px;
border-radius: calc(2 * var(--border-radius));
background-color: white;
img {
width: 56px;
height: 56px;
-o-object-fit: contain;
object-fit: contain;
position: relative;
top: 2px;
}
}
.link-content {
width: 100%;
margin-left: 10px;
}
.description {
margin-top: 10px;
display: -webkit-box;
-webkit-box-orient: vertical;
// -webkit-line-clamp: 3;
// line-clamp: 3;
text-overflow: ellipsis;
color: var(--secondary);
}
}
}
}
================================================
FILE: pkg/rancher-desktop/assets/styles/global/_columns.scss
================================================
@use 'sass:math';
@import "@pkg/assets/styles/base/_functions";
@import "@pkg/assets/styles/base/_variables";
$COLUMNS: 6 10 11 12 23 24;
$DEFAULT_COLUMNS: 12;
/* SECTIONS */
.section {
clear: both;
padding: 0px;
margin: 0px;
}
/* COLUMN SETUP */
.col {
flex: 0 0 auto;
margin: 0 $column-gutter 0 0;
&:last-child {
margin-right: 0;
}
&.equal-height {
display: flex;
flex: 1;
}
}
/* ROWS */
.row {
display: flex;
// margin-bottom: 20px;
}
.row:before,
.row:after {
content:"";
display:table;
}
.row:after {
clear:both;
}
/* COLUMNS */
@each $cols in $COLUMNS {
$span: $cols;
$suffix: null;
@if ( $cols != $DEFAULT_COLUMNS ) {
$suffix: -of-#{$cols}
}
@while $span > 0 {
// Normal column with a gutter
.span-#{$span}#{$suffix} { width: decimal-round( (math.div(100 - ($column-gutter * ($cols - 1)), $cols) * $span) + (($span - 1) * $column-gutter) , 3, 'floor'); }
// Gutterless column
.gutless .span-#{$span}#{$suffix} { margin-right: 0; width: decimal-round( math.div($span, $cols) * 100%, 3, 'floor'); }
// Offsets
.offset-#{$span}#{$suffix} {
margin-left: decimal-round( (math.div($span, $cols)*100%) + $span*math.div($column-gutter, $cols), 3, 'floor' );
}
// Gutterless offset
.gutless .offset-#{$span}#{$suffix} { margin-left: decimal-round( (math.div($span, $cols)*100%), 3, 'floor' ); }
$span: $span - 1;
}
}
//flex grid
.container-flex {
display: flex;
flex-wrap: wrap;
.flex-item-half {
flex-grow: 1;
display: flex;
width: 50%;
}
}
.flex-justify-center {
justify-content: center;
}
.flex-justify-right {
justify-content: right;
}
.flex-justify-left {
justify-content: left;
}
.container-flex-center {
@extend .container-flex;
align-items: center;
}
// Equal height columns
.row-full-height {
height: 100%;
}
.col-full-height {
height: 100%;
vertical-align: middle;
}
.row-same-height {
display: table;
width: 100%;
/* fix overflow */
table-layout: fixed;
}
//breakpoint stuff here
================================================
FILE: pkg/rancher-desktop/assets/styles/global/_form.scss
================================================
$spacing: 10px;
INPUT:not([type]),
INPUT[type='text'],
INPUT[type='password'],
INPUT[type='number'],
INPUT[type='date'],
INPUT[type='email'],
INPUT[type='search']:not(.vs__search),
INPUT[type='tel'],
INPUT[type='url'],
SELECT,
TEXTAREA,
.labeled-input,
.labeled-select,
.unlabeled-input,
.unlabeled-select {
position: relative;
display: block;
box-sizing: border-box;
width: 100%;
opacity: 1;
padding: $input-padding-sm;
background-color: var(--input-bg);
border-radius: var(--border-radius);
border: solid var(--border-width) var(--input-border);
color: var(--input-text);
@include input-status-color;
LABEL {
color: var(--input-label);
}
&:hover {
&, .vs__dropdown-menu {
background: var(--input-hover-bg);
}
}
&::placeholder {
color: var(--input-placeholder);
}
&.disabled, &.disabled .selected, &[disabled], &[disabled]:hover, &.view {
color: var(--input-disabled-text);
background-color: var(--input-disabled-bg);
outline-width: 0;
border-color: var(--input-disabled-border);
cursor: not-allowed;
label {
color: var(--input-disabled-label);
display: inline-block;
z-index: 1;
}
&::placeholder {
color: var(--input-disabled-placeholder);
}
}
LABEL {
margin: $spacing 0 0 0;
}
}
INPUT[type='search']:not(.vs__search) {
padding: calc(#{$input-padding-sm} + 2px);
}
TEXTAREA {
padding: $input-padding-lg 10px 10px 10px;
line-height: $input-line-height;
}
FORM {
LABEL {
color: var(--input-label);
display: inline-block;
margin: $spacing 0 $spacing 0;
font-size: 12px;
.radio-label,
.checkbox-label {
font-size: 14px;
}
&.radio, &.checkbox {
cursor: pointer;
margin: 5px 0;
> INPUT {
margin-right: 5px;
}
}
&.radio + LABEL.radio,
&.checkbox + LABEL.checkbox {
margin-left: 20px;
}
}
.actions {
padding-top: $spacing;
}
.detail {
margin-top: 2px;
@extend .text-small;
color: var(--muted);
}
.group {
border: 1px solid var(--input-border);
padding: 20px;
}
}
.field-required {
color: var(--error);
font-weight: bold;
}
INPUT.inline-input {
display: inline-block;
width: 75px;
margin: 0 10px;
}
.input-title {
clear: both;
margin-left: 24px;
font-size: 12px;
}
.fixed select, .fixed.v-select, .fixed input:not(.vs__search){
height: 50px;
}
================================================
FILE: pkg/rancher-desktop/assets/styles/global/_gauges.scss
================================================
@media only screen and (min-width: map-get($breakpoints, "--viewport-7")) {
.resource-gauges {
grid-template-columns: 1fr 1fr;
}
.hardware-resource-gauges {
&,
&.live {
grid-template-columns: 1fr;
}
}
}
@media only screen and (min-width: map-get($breakpoints, "--viewport-9")) {
.resource-gauges {
grid-template-columns: 1fr 1fr 1fr;
}
.hardware-resource-gauges {
grid-template-columns: 1fr 1fr 1fr;
&.live {
grid-template-columns: 1fr 1fr;
}
}
}
@media only screen and (min-width: map-get($breakpoints, "--viewport-12")) {
.resource-gauges {
grid-template-columns: 1fr 1fr 1fr;
}
}
.resource-gauges {
display: grid;
grid-column-gap: 10px;
grid-row-gap: 15px;
margin-top: 25px;
& > * {
width: 100%;
height: 100%;
}
}
.hardware-resource-gauges {
display: grid;
grid-column-gap: 15px;
grid-row-gap: 20px;
&:first-of-type {
margin-top: 35px;
}
& > * {
width: 100%;
}
}
================================================
FILE: pkg/rancher-desktop/assets/styles/global/_labeled-input.scss
================================================
.labeled-input {
position: relative;
display: table;
border-collapse: separate;
min-height: $input-height;
LABEL {
position: absolute;
transform: translate(0, -10px) scale(1);
transform-origin: top left;
transition-property: transform, font-size;
transition-duration: 0.1s;
transition-timing-function: ease-in-out;
color: var(--input-label);
pointer-events: none;
i {
pointer-events: initial;
}
}
.corner {
top: 5px;
right: 10px;
margin: 0;
padding: 0;
text-align: right;
z-index: 3;
transform: none !important;
}
.required {
color: var(--error);
}
INPUT, SELECT {
position: relative;
font-size: 14px;
display: block;
width: 100%;
}
SELECT.empty {
color: var(--input-placeholder);
}
SELECT {
-webkit-appearance: textfield;
user-select: none;
}
INPUT, INPUT:hover, INPUT:focus,
TEXTAREA, TEXTAREA:hover, TEXTAREA:focus,
SELECT, SELECT:hover, SELECT:focus {
border: none;
background-color: transparent;
outline: 0;
box-shadow: none;
padding: $input-padding-lg 0 0 0;
line-height: calc(#{$input-line-height} + 1px);
&.no-label {
padding: $input-padding-sm 0px $input-padding-sm 0px;
}
}
&.view > DIV:not(.addon) {
font-size: 14px;
padding: $input-padding-lg 0 0 0;
&.no-label {
padding-top:0px;
}
}
&.create,
&.edit,
&.view {
.addon,
.addon.btn {
display: table-cell;
vertical-align: middle;
width: 1%;
white-space: nowrap;
vertical-align: middle;
color: #{$secondary};
}
.addon {
padding: 6px 12px;
font-size: 14px;
font-weight: normal;
line-height: 1;
text-align: center;
border-left: solid thin #{$secondary};
}
}
&.suffix INPUT {
padding-right: 8px;
}
.cron-label{
position: absolute;
top: 100%;
padding-top: 5px;
left: 0;
color: var(--input-label);
}
}
================================================
FILE: pkg/rancher-desktop/assets/styles/global/_resource.scss
================================================
.create-resource-container {
.subtypes-container {
display: flex;
flex-wrap: wrap;
width: 100%;
}
.subtype-content {
width: 100%;
}
.subtype-banner {
border-left: 5px solid var(--primary);
border-radius: var(--border-radius);
display: flex;
flex-basis: 40%;
margin: 10px;
min-height: 80px;
padding: 10px;
box-shadow: 0 0 20px var(--shadow);
&.disabled {
cursor: not-allowed !important;
background-color: var(--disabled-bg);
}
&.selected {
background-color: var(--accent-btn);
}
&.top {
background-image: linear-gradient(
-90deg,
var(--body-bg),
var(--accent-btn)
);
h2 {
margin: 0px;
}
}
.title {
align-items: center;
display: flex;
width: 100%;
h5 {
margin: 0;
}
.flex-right {
margin-left: auto;
}
}
.description {
color: var(--input-label);
display: flex;
flex-direction: column;
justify-content: center;
}
&:not(.top) {
align-items: top;
flex-direction: row;
justify-content: start;
&:hover {
cursor: pointer;
box-shadow: 0px 0px 1px var(--outline-width) var(--outline);
}
}
.round-image {
border-radius: 50%;
height: 50px;
margin-right: 10px;
width: 50px;
overflow: hidden;
}
.banner-abbrv {
align-items: center;
background-color: var(--primary);
color: white;
display: flex;
font-size: 2.5em;
height: 100%;
justify-content: center;
width: 100%;
}
}
}
================================================
FILE: pkg/rancher-desktop/assets/styles/global/_select.scss
================================================
.labeled-select {
cursor: text;
padding: 0;
width: 100%;
.selected {
padding-top: $input-padding-lg;
}
}
.col {
> .labeled-select:not(.taggable),
> .unlabeled-select:not(.taggable) {
min-height: $input-height;
padding-bottom: calc(#{$input-padding-sm}/2);
}
}
.labeled-select,
.unlabeled-select {
min-width: 75px;
// line-height: $input-line-height;
.required {
color: var(--error);
}
.v-select {
&.inline {
.vs__search {
background-color: transparent;
}
.vs__dropdown-toggle,
.vs__dropdown-toggle > * {
background-color: transparent;
border: transparent;
}
.vs__dropdown-menu {
outline: none;
}
.selected {
position: relative;
top: 1.4em;
}
}
}
.v-select.inline.vs--single {
&.vs--searching .vs__selected {
display: none;
}
&:not(.vs--searching) {
.vs__selected-options {
overflow: hidden;
flex-wrap: nowrap;
.vs__selected {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
display: inline-block;
}
}
}
}
.v-select.inline:not(.vs--single) {
margin-bottom: -5px; // targets multi-select tag boxes to make the same size as rows next to it
min-height: 30px;
.vs__selected {
min-height: 25px;
padding: 0 7px;
&:not(:only-child) {
margin-bottom: 3px;
}
}
}
&.focused {
outline: none;
box-shadow: 0 0 0 var(--outline-width) var(--outline);
.v-select {
// Can toggle this to get full width dd - maybe make an option?
.vs__dropdown-menu {
min-width: max-content;
background: var(--dropdown-bg);
}
}
}
}
.unlabeled-select {
background-color: var(--input-bg);
border-radius: var(--border-radius);
color: var(--input-text);
padding: 3px 0;
&.disabled {
border: solid var(--border-width) var(--input-disabled-border);
.vs__dropdown-toggle, input {
cursor: not-allowed;
}
}
.vs--single .vs__selected-options {
flex-wrap: nowrap;
}
.v-select {
&.inline {
height: 100%;
.vs__dropdown-toggle {
height: 100%;
}
.vs__actions {
width: auto;
}
}
}
&:not(.view) {
background-color: var(--input-bg);
border: solid var(--border-width) var(--input-border);
&:hover {
&,
.vs__dropdown-menu {
background: var(--input-hover-bg);
}
}
&.disabled .v-select {
background-color: var(--input-disabled-bg);
border-color: var(--input-disabled-border);
cursor: not-allowed;
.vs__dropdown-toggle, input {
cursor: not-allowed;
}
.vs__selected {
color: var(--input-disabled-text);
}
}
}
.labeled-tooltip .status-icon {
top: $input-padding-sm;
}
}
================================================
FILE: pkg/rancher-desktop/assets/styles/global/_table.scss
================================================
//bordered table
.bordered-table {
width: 100%;
max-width: 100%;
margin-bottom: 1rem;
// background-color: var(--body-bg);
border-collapse: collapse;
th,
td {
padding: 12px;
border-top: 1px solid var(--border);
}
thead th {
vertical-align: bottom;
border-bottom: 2px solid var(--border);
}
tbody + tbody {
border-top: 2px solid var(--border);
}
table {
// background-color: var(--body-bg);
}
}
//zebra table
.zebra-table {
border-collapse: collapse;
margin: 25px 0;
min-width: 400px;
border-radius: 5px 5px 0 0;
overflow: hidden;
box-shadow: 0 0 20px var(--shadow);
thead {
tr {
background-color: var(--sortable-table-header-bg);
color: #ffffff;
text-align: left;
}
}
th {
padding: 12px 15px;
font-weight: normal;
border: 0;
}
td {
padding: 12px 15px;
border: 0;
}
tbody {
tr {
border-bottom: 1px solid var(--sortable-table-top-divider);
&:nth-of-type(even) {
background-color: var(--sortable-table-accent-bg);
}
&:last-of-type {
border-bottom: 2px solid var(--sortable-table-top-divider);
}
}
tr.active-row {
color: var(--sortable-table-header-bg);
}
}
}
.for-inputs{
& TABLE.sortable-table {
width: 100%;
border-collapse: collapse;
margin-bottom: $spacing;
>TBODY>TR>TD, >THEAD>TR>TH {
padding-right: $spacing;
padding-bottom: $spacing;
&:last-of-type {
padding-right: 0;
}
}
>TBODY>TR:first-of-type>TD {
padding-top: $spacing;
}
>TBODY>TR:last-of-type>TD {
padding-bottom: 0;
}
}
&.edit, &.create, &.clone {
TABLE.sortable-table>THEAD>TR>TH {
border-color: transparent;
}
}
}
================================================
FILE: pkg/rancher-desktop/assets/styles/global/_tooltip.scss
================================================
.v-popper__popper.v-popper--theme-tooltip {
$triangle-size: 8px;
$triangle-inner-size: $triangle-size - 1px;
$center: calc(50% - #{$triangle-size});
display: block !important;
z-index: z-index('tooltip');
max-width: 50vw;
.v-popper__inner {
background: var(--tooltip-bg);
color: var(--tooltip-text);
border-radius: var(--border-radius);
padding: 8px;
}
.v-popper__arrow-container {
border: 0 solid transparent;
z-index: 1;
position: absolute;
width: $triangle-size;
height: $triangle-size;
.v-popper__arrow-outer {
border-radius: 0;
border: $triangle-size solid transparent;
width: 0;
height: 0;
box-sizing: content-box;
margin-left: -2px;
}
.v-popper__arrow-inner {
border: $triangle-inner-size solid transparent;
border-radius: 0;
width: 0;
height: 0;
box-sizing: content-box;
margin-left: -1px;
}
}
:is(&[data-popper-placement^="top"], &[data-popper-placement^="bottom"]) {
.v-popper__arrow-inner {
margin-top: -$triangle-size;
}
}
:is(&[data-popper-placement^="left"], &[data-popper-placement^="right"]) {
.v-popper__arrow-inner {
margin-top: calc(-2 * $triangle-size);
}
}
&[data-popper-placement^="top"] {
.v-popper__wrapper {
margin-bottom: $triangle-size;
}
.v-popper__arrow-container > * {
border-top-color: var(--tooltip-bg);
border-bottom-width: 0;
}
}
&[data-popper-placement^="bottom"] {
.v-popper__inner {
margin-top: $triangle-size;
}
.v-popper__arrow-container {
top: 0;
& > * {
border-bottom-color: var(--tooltip-bg);
border-top-width: 0;
}
}
}
&[data-popper-placement^="right"] {
.v-popper__inner {
margin-left: calc(#{$triangle-size} - 2px);
}
.v-popper__arrow-container > * {
border-right-color: var(--tooltip-bg);
border-left-width: 0;
}
}
&[data-popper-placement^="left"] {
.v-popper__inner {
margin-right: $triangle-size;
}
.v-popper__arrow-container > * {
border-left-color: var(--tooltip-bg);
border-right-width: 0;
}
}
&.tooltip-warning {
.v-popper__inner {
background: var(--tooltip-bg-warning);
color: var(--tooltip-text-warning);
}
&[data-popper-placement^="top"] {
.v-popper__arrow-container {
.v-popper__arrow-outer {
border-top-color: var(--tooltip-bg-warning);
}
}
}
&[data-popper-placement^="bottom"] {
.v-popper__arrow-container {
.v-popper__arrow-outer {
border-bottom-color: var(--body-bg);
}
}
}
&[data-popper-placement^="right"] {
.v-popper__arrow-container {
.v-popper__arrow-outer {
border-right-color: var(--tooltip-bg-warning);
}
}
}
&[data-popper-placement^="left"] {
.v-popper__arrow-container {
.v-popper__arrow-outer {
border-left-color: var(--tooltip-bg-warning);
}
}
}
}
}
.v-popper__popper {
$color: var(--popover-bg);
top: 0;
left: 0;
border-radius: var(--border-radius-lg);
&:focus {
outline: none;
}
.v-popper__inner {
background: $color;
color: var(--popover-text);
padding: 0;
border-radius: var(--border-radius-lg);
overflow: hidden; /* for border-radius */
border: 1px solid var(--dropdown-border);
li {
padding: 10px;
&:not(.divider):hover {
background-color: var(--dropdown-hover-bg);
color: var(--dropdown-hover-text);
cursor: pointer;
}
}
a {
color: var(--popover-text);
}
.resize-observer, .resize-observer object {
position: absolute;
}
}
.v-popper__arrow-container {
border-color: transparent;
.v-popper__arrow-outer {
border-color: transparent;
}
}
}
.v-popper__popper.v-popper--theme-dropdown {
z-index: z-index('tooltip');
&.containerLogsDropdown, &.fleet-summary-tooltip{
.v-popper__arrow-container {
display: none;
}
}
}
.v-popper {
display: inline;
}
.v-popper__popper.v-popper--theme-tooltip,
.v-popper {
&[aria-hidden='true'] {
// This removes it from the layout of ButtonDropDown (so it doesn't render huge for SSR) but
// still allows it to maintain its dimensions for v-tooltip to calculate the appropriate position.
position: absolute;
visibility: hidden;
opacity: 0;
transition: opacity .15s, visibility .15s;
}
&[aria-hidden='false'] {
visibility: visible;
opacity: 1;
transition: opacity .15s;
}
}
//icon tooltip
.icon-info.v-popper--has-tooltip {
font-size: 14px;
}
.tooltip-footer {
top: 0;
font-size: 12px;
.v-popper__inner {
font-size: 12px;
}
}
================================================
FILE: pkg/rancher-desktop/assets/styles/rancher-desktop.scss
================================================
/** Rancher Desktop specific styles */
body {
background-color: var(--body-bg);
color: var(--body-text);
font-size: 14px;
}
.labeled-input {
margin-bottom: 1em;
}
label > button:first-child:not(.btn-sm) {
margin-right: 1em;
}
.locked-radio .radio-container span.radio-custom {
&[aria-checked="true"] {
opacity: 1;
}
&:not([aria-checked="true"]) {
opacity: 1;
background-color: var(--radio-locked-bg);
box-shadow: var(--radio-locked-shadow);
}
}
@media screen and (prefers-color-scheme: dark) {
option {
background-color: var(--input-bg);
}
}
:is(.btn, button, [class*='btn-']):not([class*='role-']):not([class*=bg-primary]) {
/* buttons should have one of the role- classes:
* .role-primary, .role-secondary, .role-tertiary, .role-multi-action, etc.
*/
outline: 10px dashed red !important;
}
.btn-icon-text {
display: flex;
align-items: center;
gap: 0.5rem;
}
:root {
--preferences-content-padding: 0.75rem;
}
================================================
FILE: pkg/rancher-desktop/assets/styles/themes/_dark.scss
================================================
:root {
// Local variables for reused colors
//dark sidebar
$darkest: #141419;
//dark body
$darker: #1b1c21;
//dark inputs
$dark: #27292e;
//dark borders and button
$medium: #4a4b52;
// dark disabled,
$light: #6c6c76;
//dark secondary
$lighter: #b6b6c2;
// dark main text
$lightest: #ffffff;
$secondary: $lighter;
$disabled: $light;
//Contrast colors
$contrasted-dark: $lightest !default;
$contrasted-light: $darkest !default;
--default : #{$dark};
--default-text : #{$light};
--default-hover-bg : #{darken($dark, 10%)};
--default-hover-text : #{saturate($lightest, 20%)};
--default-active-bg : #{darken($dark, 25%)};
--default-active-text : #{contrast-color(darken($dark, 25%))};
--default-border : #($dark);
--default-banner-bg : #{rgba($dark, 0.15)};
--default-light-bg : #{rgba($dark, 0.05)};
--muted : #{$disabled};
--body-bg : #{$darker};
--body-text : #{$lightest};
--scrollbar-thumb : #{$medium};
--scrollbar-thumb-dropdown : #{$medium};
--header-bg : #{$darker};
--header-border : #{$medium};
--header-btn-bg : #{$dark};
--header-btn-text : #{$lightest};
--footer-bg : #{$darker};
--footer-border : #{$medium};
--nav-bg : #{$darkest};
--nav-active : var(--primary-active-bg);
--nav-border : #{$medium};
--nav-hover : var(--primary);
--nav-expander-hover : var(--primary-banner-bg);
--disabled-bg : #{darken($disabled, 10%)};
--disabled-text : #{$secondary};
--box-bg : #{$darkest};
--subtle-border : #{$darkest};
--border : #{$medium};
--topmenu-bg : #{$darkest};
--topmenu-text : #{$lightest};
--topmost-border : #{$medium};
--topmost-shadow : #{lighten($darkest, 5%)};
--topmost-light-hover : #{$medium};
--accent-btn : var(--primary-banner-bg);
--accent-btn-hover : var(--primary);
--accent-btn-hover-text : #{$lightest};
--modal-bg : #{$dark};
--modal-border : #{$medium};
--overlay-bg : #{rgba($darkest, 0.75)};
--shadow : #{rgba($darkest, 0.9)};
--checkbox-tick : #{$lightest};
--checkbox-border : #{$medium};
--checkbox-tick-disabled : #{lighten($disabled, 50%)};
--checkbox-disabled-bg : #{$disabled};
--checkbox-tick-locked : #{$darkest};
--checkbox-locked-bg : #{lighten($disabled, 50%)};
--checkbox-ticked-bg : var(--primary);
--checkbox-locked-border : #{lighten($disabled, 50%)};
--checkbox-locked-shadow : #{lighten($disabled, 50%)};
--dropdown-bg : #{mix($medium, $dark, 10%)};
--dropdown-border : #{$light};
--dropdown-divider : #{$light};
--dropdown-text : #{$link};
--dropdown-active-text : #{$lightest};
--dropdown-active-bg : #{$selected};
--dropdown-hover-text : #{$lightest};
--dropdown-hover-bg : #{$link};
--dropdown-disabled-bg : #{$disabled};
--dropdown-disabled-text : #{$disabled};
--dropdown-locked-text : #{$lightest};
--input-text : #{$lightest};
--input-label : #{$lighter};
--input-placeholder : #{$disabled};
--input-border : var(--border);
--input-bg : var(--body-bg);
--input-bg-accent : #{darken($dark, 3%)};
--input-hover-bg : var(--box-bg);
--input-focus-bg : var(--box-bg);
--input-disabled-text : #{darken($lightest, 50%)};
--input-disabled-label : #{darken($lighter, 30%)};
--input-disabled-bg : #{darken($disabled, 30%)};
--input-disabled-border : #{darken($medium, 30%)};
--input-disabled-placeholder : #{darken($disabled, 10%)};
--input-addon-bg : #{$darker};
--input-locked-text : #{$lightest};
--radio-locked-bg : var(--body-bg);
--radio-locked-shadow : var(--body-bg);
--progress-bg : #{$medium};
--progress-divider : #{$lightest};
--sortable-table-bg : #{lighten($darkest, 10%)};
--sortable-table-row-bg : #{$darker};;
--sortable-table-header-bg : #{$darkest};
--sortable-table-accent-bg : #{$darker};
--sortable-table-accent-alt : #{$dark};
--sortable-table-top-divider : var(--border);
--sortable-table-hover-bg : #{$darkest};
--sortable-table-selected-bg : var(--primary-light-bg);
--sortable-table-group-label : #{$lighter};
--tag-primary : #{$lightest};
--tag-bg : #{$medium};
--popover-bg : var(--body-bg);
--popover-border : var(--border);
--popover-text : var(--body-text);
--tooltip-bg : #{$medium};
--tooltip-border : var(--tag-primary);
--tooltip-text : var(--body-text);
--tooltip-text-warning : var(--body-text);
--icon-circle : #{$medium};
--tabbed-border : #{$medium};
--tabbed-sidebar-bg : #{$darkest};
--tabbed-container-bg : #{mix($medium, $dark, 20%)};
--yaml-editor-bg : #{$darkest};
--diff-border : var(--border);
--diff-header-bg : var(--nav-bg);
--diff-header-border : var(--border);
--diff-header : #{rgba($darkest, 0.3)};
--diff-linenum-bg : var(--nav-bg);
--diff-linenum : var(--muted);
--diff-linenum-border : var(--border);
--diff-line-ins-bg : $success;
--diff-line-del-bg : #{rgba($error, 0.75)};
--diff-del-bg : #{rgba($error, 0.3)};
--diff-del-border : #{$error};
--diff-ins-bg : #{rgba($success, 0.3)};
--diff-ins-border : #{rgba($success, 0.5)};
--diff-chg-ins : #{rgba($success, 0.25)};
--diff-chg-del : #{rgba($warning, 0.5)};
--diff-empty-placeholder : #{$darker};
--wm-tabs-bg : #{mix($medium, $dark, 10%)};
--wm-tab-bg : #{$darkest};
--wm-closer-hover-bg : #{$medium};
--wm-tab-active-bg : #{$darker};
--wm-title-bg : #{$darkest};
--wm-title-border : #{$medium};
--wm-body-bg : #{$darkest};
--wm-border : black;
--glance-divider : #{$medium};
--resource-gauge-back-circle : 74, 75, 82, 0.5;
--simple-box-bg : #{$darker};
--simple-box-border : #{$darkest};
--simple-box-divider : #{$medium};
--simple-box-shadow : rgba(0, 0, 0, 0.15);
--terminal-bg : var(--wm-body-bg);
--terminal-cursor : var(--warning);
--terminal-selection : #{$selected};
--terminal-text : var(--body-text);
--logs-bg : var(--wm-body-bg);
--logs-highlight : var(--wm-body-bg);
--logs-highlight-bg : var(--warning);
--logs-text : var(--body-text);
--gauge-divider : rgba(255, 255, 255, 0.3);
--gauge-success-primary : 75, 95, 64;
--gauge-success-secondary : 150, 189, 127;
--gauge-warning-primary : 218, 195, 66;
--gauge-warning-secondary : 109, 98, 33;
--gauge-error-primary : 239, 90, 83;
--gauge-error-secondary : 120, 45, 42;
--product-icon : #{$lighter};
--product-icon-active : #{$lightest};
--button-icon : #{$medium};
--button-icon-bg : #{$lightest};
}
================================================
FILE: pkg/rancher-desktop/assets/styles/themes/_light.scss
================================================
// Local variables for reused colors
//light main text
$darkest : #141419;
//light secondary
$darker : #6C6C76;
//light disabled
$dark : #B6B6C2;
//light border and buttons
$medium : #DCDEE7;
//light inputs
$light : #EEEFF4;
//light sidebar and box
$lighter : #F4F5FA;
//light body bg
$lightest : #FFFFFF;
//color for items that are not enabled
$disabled : $medium;
$primary : #3D98D3;
$secondary : $darker;
$link : #3D98D3;
// Status colors
$success : #5D995D;
$warning : #DAC342;
$error : #F64747;
$info : #3D98D3;
$contrasted-dark: $darkest !default;
$contrasted-light: $lightest !default;
// Text selection color for terminal window (we don't want this to change with the primary color)
// The terminal alway uses a light background, so okay to use a fixed color
$selected: rgba(#3D98D3, .5);
:root {
--primary : #{$primary};
--primary-text : #{contrast-color($primary)};
--primary-hover-bg : #{darken($primary, 10%)};
--primary-hover-text : #{saturate($lightest, 20%)};
--primary-active-bg : #{darken($primary, 25%)};
--primary-active-text : #{contrast-color(darken($primary, 25%))};
--primary-border : #($primary);
--primary-banner-bg : #{rgba($primary, 0.15)};
--primary-light-bg : #{rgba($primary, 0.05)};
.text-primary {
color: var(--primary) !important;
}
.bg-primary {
background-color: var(--primary);
color: var(--primary-text);
&.btn:hover {
color: var(--primary-hover-text);
background: var(--primary-hover-bg);
transition: all 0.3s ease;
}
&.btn:active {
color: var(--primary-active-text);
background: var(--primary-active-bg);
}
}
--link : #{$link};
--link-text : #{contrast-color($link)};
--link-hover-bg : #{darken($link, 10%)};
--link-hover-text : #{saturate($lightest, 20%)};
--link-active-bg : #{darken($link, 25%)};
--link-active-text : #{contrast-color(darken($link, 25%))};
--link-border : #($link);
--link-banner-bg : #{rgba($link, 0.15)};
--link-light-bg : #{rgba($link, 0.05)};
.text-link {
color: var(--link) !important;
}
.bg-link {
background-color: var(--link);
color: var(--link-text);
&.btn:hover {
color: var(--link-hover-text);
background: var(--link-hover-bg);
transition: all 0.3s ease;
}
&.btn:active {
color: var(--link-active-text);
background: var(--link-active-bg);
}
}
--default : #{$light};
--default-text : #{contrast-color($light)};
--default-hover-bg : #{darken($light, 10%)};
--default-hover-text : #{saturate($lightest, 20%)};
--default-active-bg : #{darken($light, 25%)};
--default-active-text : #{contrast-color(darken($light, 25%))};
--default-border : #($light);
--default-banner-bg : #{rgba($light, 0.15)};
--default-light-bg : #{rgba($light, 0.05)};
.text-default {
color: var(--default) !important;
}
.bg-default {
background-color: var(--default);
color: var(--default-text);
&.btn:hover {
color: var(--default-hover-text);
background: var(--default-hover-bg);
transition: all 0.3s ease;
}
&.btn:active {
color: var(--default-active-text);
background: var(--default-active-bg);
}
}
--muted : #{$dark};
.text-muted {
color: var(--muted) !important;
}
--darker : #{$darker};
--darker-text : #{contrast-color($darker)};
--darker-hover-bg : #{darken($darker, 10%)};
--darker-hover-text : #{saturate($lightest, 20%)};
--darker-active-bg : #{darken($darker, 25%)};
--darker-active-text : #{contrast-color(darken($darker, 25%))};
--darker-border : #($darker);
--darker-banner-bg : #{rgba($darker, 0.15)};
--darker-light-bg : #{rgba($darker, 0.05)};
.text-darker {
color: var(--default) !important;
}
.bg-darker {
background-color: var(--darker);
color: var(--darker-text);
&.btn:hover {
color: var(--darker-hover-text);
background: var(--darker-hover-bg);
transition: all 0.3s ease;
}
&.btn:active {
color: var(--darker-active-text);
background: var(--darker-active-bg);
}
}
--success : #{$success};
--success-text : #{contrast-color($success)};
--success-hover-bg : #{darken($success, 10%)};
--success-hover-text : #{saturate($lightest, 20%)};
--success-active-bg : #{darken($success, 25%)};
--success-active-text : #{contrast-color(darken($success, 25%))};
--success-border : #($success);
--success-banner-bg : #{rgba($success, 0.15)};
--success-light-bg : #{rgba($success, 0.05)};
.text-success {
color: var(--success) !important;
}
.bg-success {
background-color: var(--success);
color: var(--success-text);
&.btn:hover {
color: var(--success-hover-text);
background: var(--success-hover-bg);
transition: all 0.3s ease;
}
&.btn:active {
color: var(--success-active-text);
background: var(--success-active-bg);
}
}
--info : #{$info};
--info-text : #{contrast-color($info)};
--info-hover-bg : #{darken($info, 10%)};
--info-hover-text : #{saturate($lightest, 20%)};
--info-active-bg : #{darken($info, 25%)};
--info-active-text : #{contrast-color(darken($info, 25%))};
--info-border : #($info);
--info-banner-bg : #{rgba($info, 0.15)};
--info-light-bg : #{rgba($info, 0.05)};
.text-info {
color: var(--info) !important;
}
.bg-info {
background-color: var(--info);
color: var(--info-text);
&.btn:hover {
color: var(--info-hover-text);
background: var(--info-hover-bg);
transition: all 0.3s ease;
}
&.btn:active {
color: var(--info-active-text);
background: var(--info-active-bg);
}
}
--warning : #{$warning};
--warning-text : #{contrast-color($warning)};
--warning-hover-bg : #{darken($warning, 10%)};
--warning-hover-text : #{saturate($lightest, 20%)};
--warning-active-bg : #{darken($warning, 25%)};
--warning-active-text : #{contrast-color(darken($warning, 25%))};
--warning-border : #($warning);
--warning-banner-bg : #{rgba($warning, 0.15)};
--warning-light-bg : #{rgba($warning, 0.05)};
.text-warning {
color: var(--error) !important;
}
.bg-warning {
background-color: var(--warning);
color: var(--warning-text);
&.btn:hover {
color: var(--warning-hover-text);
background: var(--warning-hover-bg);
transition: all 0.3s ease;
}
&.btn:active {
color: var(--warning-active-text);
background: var(--warning-active-bg);
}
}
--error : #{$error};
--error-text : #{contrast-color($error)};
--error-hover-bg : #{darken($error, 10%)};
--error-hover-text : #{saturate($lightest, 20%)};
--error-active-bg : #{darken($error, 25%)};
--error-active-text : #{contrast-color(darken($error, 25%))};
--error-border : #($error);
--error-banner-bg : #{rgba($error, 0.15)};
--error-light-bg : #{rgba($error, 0.05)};
.text-error {
color: var(--error) !important;
}
.bg-error {
background-color: var(--error);
color: var(--error-text);
&.btn:hover {
color: var(--error-hover-text);
background: var(--error-hover-bg);
transition: all 0.3s ease;
}
&.btn:active {
color: var(--error-active-text);
background: var(--error-active-bg);
}
}
--body-bg : #{$lightest};
--body-text : #{$darkest};
--scrollbar-thumb : #{$dark};
--scrollbar-thumb-dropdown : #{$lighter};
--scrollbar-track : transparent;
--header-bg : #{$lightest};
--header-btn-bg : #{$light};
--header-btn-text : #{$darker};
--header-input-text : #{$lightest};
--header-height : 55px;
--header-border : #{$medium};
--header-border-size : 1px;
--nav-width : 230px;
--nav-bg : #{$lightest};
--nav-active : #{$light};
--nav-hover : #{$medium};
--nav-expander-hover : #{darken($medium, 10%)};
--nav-border : #{$medium};
--nav-border-size : 1px;
--footer-bg : #{$lightest};
--footer-border : #{$medium};
--topmenu-bg : #{$lightest};
--topmenu-text : #{$darkest};
--topmost-border : #{$medium};
--topmost-shadow : #{lighten($lightest, 10%)};
--topmost-light-hover : #{$light};
--disabled-bg : #{$disabled};
--disabled-text : #{$secondary};
--box-bg : #{$lighter};
--subtle-border : #{$medium};
--border : #{$medium};
--border-width : 1px;
--border-radius : 4px;
--border-radius-md : 6px;
--border-radius-lg : 8px;
--outline : var(--primary);
--outline-width : 1px;
--accent-btn : var(--primary-banner-bg);
--accent-btn-hover : var(--primary);
--accent-btn-hover-text : #{$lightest};
--modal-bg : #{$lightest};
--modal-border : #{$dark};
--overlay-bg : #{rgba($lighter, 0.75)};
--shadow : #{rgba($medium, 0.85)};
--checkbox-tick : #{$lightest};
--checkbox-border : #{$medium};
--checkbox-tick-disabled : #{darken($disabled, 40%)};
--checkbox-disabled-bg : #{$disabled};
--checkbox-tick-locked : #{$darkest};
--checkbox-locked-bg : #{lighten($disabled, 5%)};
--checkbox-ticked-bg : #{$link};
--checkbox-locked-border : #{lighten($disabled, 5%)};
--checkbox-locked-shadow : #{lighten($disabled, 5%)};
--dropdown-bg : #{$lightest};
--dropdown-border : #{$medium};
--dropdown-divider : #{$medium};
--dropdown-text : #{$link};
--dropdown-active-text : #{$lightest};
--dropdown-active-bg : #{$dark};
--dropdown-hover-text : var(--body-text);
--dropdown-hover-bg : #{$light};
--dropdown-disabled-text : var(--muted);
--dropdown-disabled-bg : #{$disabled};
--dropdown-locked-text : #{$darkest};
// UNUSED?
--card-header : var(--primary-banner-bg);
--input-text : #{$darkest};
--input-label : #{$secondary};
--input-placeholder : #{darken($disabled, 10%)};
--input-border : var(--border);
--input-bg : var(--body-bg);
--input-bg-accent : #{darken($light, 2%)};
--input-hover-bg : var(--box-bg);
--input-focus-bg : var(--box-bg);
--input-disabled-text : #{darken($disabled, 60%)};
--input-disabled-label : #{darken($disabled, 40%)};
--input-disabled-bg : #{$disabled};
--input-disabled-border : #{darken($medium, 10%)};
--input-disabled-placeholder : #{darken($medium, 15%)};
--input-addon-bg : #{$darker};
--input-locked-text : #{$darkest};
--radio-locked-bg : var(--body-bg);
--radio-locked-shadow : var(--body-bg);
--progress-bg : #{$medium};
--progress-divider : #{$medium};
--sortable-table-bg : #{darken($lightest, 5%)};
--sortable-table-row-bg : #{$lightest};
--sortable-table-header-bg : #{$lighter};
--sortable-table-accent-bg : #{$lighter};
--sortable-table-accent-alt : #{$lightest};
--sortable-table-top-divider : var(--border);
--sortable-table-body-divider : #{$medium};
--sortable-table-hover-bg : #{$lighter};
//--sortable-table-selected-bg : #{rgba($primary, 0.02)};
--sortable-table-selected-bg : var(--primary-light-bg);
--sortable-table-group-label : #{$secondary};
--tag-primary : #{$darkest};
--tag-bg : #{$medium};
--popover-bg : var(--body-bg);
--popover-border : var(--border);
--popover-text : var(--body-text);
--popover-border-radius : var(--border-radius);
--tooltip-bg : #{$medium};
--tooltip-border : var(--tag-primary);
--tooltip-text : var(--tag-primary);
--tooltip-bg-warning : #{rgba($warning, 0.8)};
--tooltip-text-warning : var(--body-text);
--icon-circle : #{$medium};
--tabbed-border : #{$medium};
--tabbed-sidebar-bg : #{$lighter};
--tabbed-container-bg : #{mix($light, $lighter, 15%)};
--yaml-editor-bg : #{$lighter};
--diff-border : var(--border);
--diff-header-bg : var(--nav-bg);
--diff-header-border : var(--border);
--diff-header : #{rgba($darkest, 0.3)};
--diff-linenum-bg : var(--nav-bg);
--diff-linenum : var(--muted);
--diff-linenum-border : var(--border);
--diff-line-ins-bg : $success;
--diff-line-del-bg : #{rgba($error, 0.75)};
--diff-del-bg : #{rgba($error, 0.3)};
--diff-del-border : #{$error};
--diff-ins-bg : #{rgba($success, 0.3)};
--diff-ins-border : #{rgba($success, 0.5)};
--diff-chg-ins : #{rgba($success, 0.25)};
--diff-chg-del : #{rgba($warning, 0.5)};
--diff-empty-placeholder : #{$lightest};
--wm-tabs-bg : #{$medium};
--wm-tab-bg : #{$light};
--wm-closer-hover-bg : #{$lighter};
--wm-tab-active-bg : #{$lighter};
--wm-title-bg : #{$lightest};
--wm-title-border : #{$medium};
--wm-body-bg : #{$lighter};
--wm-border : var(--border);
--wm-tab-height : 29px;
--glance-bg-rgb : 61, 152, 211;
--glance-divider : #{$medium};
--resource-gauge-back-circle : 255, 255, 255, 0.15;
--simple-box-bg : #{$lightest};
--simple-box-border : #{$medium};
--simple-box-divider : #{$medium};
--simple-box-shadow : none;
--terminal-bg : var(--body-bg);
--terminal-cursor : var(--warning);
--terminal-selection : #{$selected};
--terminal-text : var(--body-text);
--logs-bg : var(--wm-body-bg);
--logs-highlight : var(--wm-body-bg);
--logs-highlight-bg : var(--warning);
--logs-text : var(--body-text);
--gauge-divider : #{$lightest};
--gauge-zero : #{$medium};
--gauge-success-primary : 150, 189, 127;
--gauge-success-secondary : 190, 211, 172;
--gauge-warning-primary : 238, 226, 176;
--gauge-warning-secondary : 218, 195, 66;
--gauge-error-primary : 249, 186, 171;
--gauge-error-secondary : 239, 90, 83;
--sizzle-0 : 180, 210, 30;
--sizzle-1 : 225, 45, 74;
--sizzle-2 : 212, 66, 148;
--sizzle-3 : 0, 169, 217;
--sizzle-4 : 244, 136, 68;
--sizzle-5 : 0, 147, 128;
--sizzle-6 : 136, 81, 165;
--sizzle-7 : 45, 47, 149;
--sizzle-8 : 255, 235, 0;
--sizzle-success : #{red($success)}, #{green($success)}, #{blue($success)};
--sizzle-info : #{red($info)}, #{green($info)}, #{blue($info)};
--sizzle-warning : #{red($warning)}, #{green($warning)}, #{blue($warning)};
--sizzle-error : #{red($error)}, #{green($error)}, #{blue($error)};
--sizzle-unknown : #{red($disabled)},#{green($disabled)},#{blue($disabled)};
$rancher : $primary;
$partner : #FEA424;
$other : #614EA2;
--app-rancher-accent : #{$rancher};
--app-rancher-accent-text : #{$darkest};
--app-partner-accent : #{$partner};
--app-partner-accent-text : black;
--app-color1-accent : rgba(var(--sizzle-1), 1);
--app-color1-accent-text : white;
--app-color2-accent : rgba(var(--sizzle-2), 1);
--app-color2-accent-text : white;
--app-color3-accent : rgba(var(--sizzle-3), 1);
--app-color3-accent-text : white;
--app-color4-accent : rgba(var(--sizzle-4), 1);
--app-color4-accent-text : white;
--app-color5-accent : rgba(var(--sizzle-5), 1);
--app-color5-accent-text : white;
--app-color6-accent : rgba(var(--sizzle-6), 1);
--app-color6-accent-text : white;
--app-color7-accent : rgba(var(--sizzle-7), 1);
--app-color7-accent-text : white;
--app-color8-accent : rgba(var(--sizzle-8), 1);
--app-color8-accent-text : white;
--product-icon : #{$darker};
--product-icon-active : #{$darkest};
--button-icon : #{$dark};
--button-icon-bg : #{$secondary};
}
================================================
FILE: pkg/rancher-desktop/assets/styles/themes/_suse.scss
================================================
.suse {
$primary: hsl(151, 59%, 46%);
$info: mix($primary, $secondary, 50%);
$selected: rgba($primary, .5);
--primary : #{$primary};
--primary-text : #{contrast-color($primary)};
--primary-hover-bg : #{darken($primary, 10%)};
--primary-hover-text : #{saturate($lightest, 20%)};
--primary-active-bg : #{darken($primary, 25%)};
--primary-active-text : #{contrast-color(darken($primary, 25%))};
--primary-border : #($primary);
--primary-banner-bg : #{rgba($primary, 0.15)};
--primary-light-bg : #{rgba($primary, 0.05)};
--info : #{$info};
--info-text : #{contrast-color($info)};
--info-hover-bg : #{darken($info, 10%)};
--info-hover-text : #{saturate($lightest, 20%)};
--info-active-bg : #{darken($info, 25%)};
--info-active-text : #{contrast-color(darken($info, 25%))};
--info-border : #($info);
--info-banner-bg : #{rgba($info, 0.15)};
--info-light-bg : #{rgba($info, 0.05)};
}
================================================
FILE: pkg/rancher-desktop/assets/styles/vendor/normalize.scss
================================================
/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */
/* Document
========================================================================== */
/**
* 1. Correct the line height in all browsers.
* 2. Prevent adjustments of font size after orientation changes in iOS.
*/
html {
line-height: 1.15; /* 1 */
-webkit-text-size-adjust: 100%; /* 2 */
}
/* Sections
========================================================================== */
/**
* Remove the margin in all browsers.
*/
body {
margin: 0;
}
/**
* Render the `main` element consistently in IE.
*/
main {
display: block;
}
/**
* Correct the font size and margin on `h1` elements within `section` and
* `article` contexts in Chrome, Firefox, and Safari.
*/
h1 {
font-size: 2em;
margin: 0.67em 0;
}
/* Grouping content
========================================================================== */
/**
* 1. Add the correct box sizing in Firefox.
* 2. Show the overflow in Edge and IE.
*/
hr {
box-sizing: content-box; /* 1 */
height: 0; /* 1 */
overflow: visible; /* 2 */
}
/**
* 1. Correct the inheritance and scaling of font size in all browsers.
* 2. Correct the odd `em` font sizing in all browsers.
*/
pre {
font-family: monospace, monospace; /* 1 */
font-size: 1em; /* 2 */
}
/* Text-level semantics
========================================================================== */
/**
* Remove the gray background on active links in IE 10.
*/
a {
background-color: transparent;
}
/**
* 1. Remove the bottom border in Chrome 57-
* 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
*/
abbr[title] {
border-bottom: none; /* 1 */
text-decoration: underline; /* 2 */
text-decoration: underline dotted; /* 2 */
}
/**
* Add the correct font weight in Chrome, Edge, and Safari.
*/
b,
strong {
font-weight: bolder;
}
/**
* 1. Correct the inheritance and scaling of font size in all browsers.
* 2. Correct the odd `em` font sizing in all browsers.
*/
code,
kbd,
samp {
font-family: monospace, monospace; /* 1 */
font-size: 1em; /* 2 */
}
/**
* Add the correct font size in all browsers.
*/
small {
font-size: 80%;
}
/**
* Prevent `sub` and `sup` elements from affecting the line height in
* all browsers.
*/
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sub {
bottom: -0.25em;
}
sup {
top: -0.5em;
}
/* Embedded content
========================================================================== */
/**
* Remove the border on images inside links in IE 10.
*/
img {
border-style: none;
}
/* Forms
========================================================================== */
/**
* 1. Change the font styles in all browsers.
* 2. Remove the margin in Firefox and Safari.
*/
button,
input,
optgroup,
select,
textarea {
font-family: inherit; /* 1 */
font-size: 100%; /* 1 */
line-height: 1.15; /* 1 */
margin: 0; /* 2 */
}
/**
* Show the overflow in IE.
* 1. Show the overflow in Edge.
*/
button,
input { /* 1 */
overflow: visible;
}
/**
* Remove the inheritance of text transform in Edge, Firefox, and IE.
* 1. Remove the inheritance of text transform in Firefox.
*/
button,
select { /* 1 */
text-transform: none;
}
/**
* Correct the inability to style clickable types in iOS and Safari.
*/
button,
[type="button"],
[type="reset"],
[type="submit"] {
-webkit-appearance: button;
}
/**
* Remove the inner border and padding in Firefox.
*/
button::-moz-focus-inner,
[type="button"]::-moz-focus-inner,
[type="reset"]::-moz-focus-inner,
[type="submit"]::-moz-focus-inner {
border-style: none;
padding: 0;
}
/**
* Restore the focus styles unset by the previous rule.
*/
button:-moz-focusring,
[type="button"]:-moz-focusring,
[type="reset"]:-moz-focusring,
[type="submit"]:-moz-focusring {
outline: 1px dotted ButtonText;
}
/**
* Correct the padding in Firefox.
*/
fieldset {
padding: 0.35em 0.75em 0.625em;
}
/**
* 1. Correct the text wrapping in Edge and IE.
* 2. Correct the color inheritance from `fieldset` elements in IE.
* 3. Remove the padding so developers are not caught out when they zero out
* `fieldset` elements in all browsers.
*/
legend {
box-sizing: border-box; /* 1 */
color: inherit; /* 2 */
display: table; /* 1 */
max-width: 100%; /* 1 */
padding: 0; /* 3 */
white-space: normal; /* 1 */
}
/**
* Add the correct vertical alignment in Chrome, Firefox, and Opera.
*/
progress {
vertical-align: baseline;
}
/**
* Remove the default vertical scrollbar in IE 10+.
*/
textarea {
overflow: auto;
}
/**
* 1. Add the correct box sizing in IE 10.
* 2. Remove the padding in IE 10.
*/
[type="checkbox"],
[type="radio"] {
box-sizing: border-box; /* 1 */
padding: 0; /* 2 */
}
/**
* Correct the cursor style of increment and decrement buttons in Chrome.
*/
[type="number"]::-webkit-inner-spin-button,
[type="number"]::-webkit-outer-spin-button {
height: auto;
}
/**
* 1. Correct the odd appearance in Chrome and Safari.
* 2. Correct the outline style in Safari.
*/
[type="search"] {
-webkit-appearance: textfield; /* 1 */
outline-offset: -2px; /* 2 */
}
/**
* Remove the inner padding in Chrome and Safari on macOS.
*/
[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
/**
* 1. Correct the inability to style clickable types in iOS and Safari.
* 2. Change font properties to `inherit` in Safari.
*/
::-webkit-file-upload-button {
-webkit-appearance: button; /* 1 */
font: inherit; /* 2 */
}
/* Interactive
========================================================================== */
/*
* Add the correct display in Edge, IE 10+, and Firefox.
*/
details {
display: block;
}
/*
* Add the correct display in all browsers.
*/
summary {
display: list-item;
}
/* Misc
========================================================================== */
/**
* Add the correct display in IE 10+.
*/
template {
display: none;
}
/**
* Add the correct display in IE 10.
*/
[hidden] {
display: none;
}
================================================
FILE: pkg/rancher-desktop/assets/styles/vendor/vue-select.scss
================================================
.v-select {
position: relative;
font-family: inherit;
&.auto-width {
display: inline-block;
width: auto;
min-width: 2em;
}
}
.v-select,
.v-select * {
box-sizing: border-box;
}
.vs__dropdown-toggle {
cursor: pointer;
}
.vs--disabled {
.vs__dropdown-toggle,
.vs__clear,
.vs__search,
.vs__open-indicator {
cursor: not-allowed;
color: var(--dropdown-disabled-text);
}
}
.vs__dropdown-menu {
display: block;
position: absolute;
left: -2px;
z-index: z-index('dropdownContent');
padding: $input-padding-sm 0;
margin: 0;
width: calc(100% + 4px);
max-height: 350px;
min-width: 160px;
overflow-y: auto;
border: 1px solid var(--dropdown-border);
border-radius: var(--border-radius);
text-align: left;
list-style: none;
background: var(--dropdown-bg);
&::-webkit-scrollbar-thumb {
background-color: var(--scrollbar-thumb-dropdown) !important;
border-radius: 4px;
}
&[data-popper-placement='top'] {
border-radius: 4px 4px 0 0;
border-top-style: solid;
box-shadow: 0px -8px 16px 0px var(--shadow);
}
}
.vs__dropdown-option {
line-height: 1.42857143; /* Normalize line height */
display: block;
padding: 0 calc(#{$input-padding-sm}/2);
clear: both;
color: var(--dropdown-text);
white-space: nowrap;
z-index: 1000;
&:hover {
cursor: pointer;
}
a {
display: block;
&:hover {
color: var(--body-text);
}
}
&.vs__dropdown-option--disabled {
color: var(--dropdown-disabled-text);
cursor: not-allowed;
hr {
cursor: default;
}
}
&.vs__dropdown-option--selected {
background-color: var(--dropdown-active-bg);
color: var(--dropdown-active-text);
text-decoration: none;
}
&.vs__dropdown-option--highlight {
color: var(--dropdown-hover-text);
background: var(--dropdown-hover-bg);
a {
color: var(--dropdown-hover-text);
text-decoration: none;
}
}
}
.vs__dropdown-toggle {
appearance: none;
display: flex;
background: var(--input-bg);
border: 1px solid var(--dropdown-border);
border-radius: var(--border-radius);
white-space: normal;
}
.vs__selected-options {
display: flex;
flex-basis: 100%;
flex-grow: 1;
flex-wrap: wrap;
padding: 0;
position: relative;
}
.lg .vs__selected-options {
margin: 8px;
}
.vs__actions {
display: flex;
align-items: center;
pointer-events: none;
position: relative;
width: 100%;
justify-content: flex-end;
flex-shrink: 8;
svg {
display: none;
}
&:after {
content: $icon-chevron-down;
font-family: 'icons';
font-size: 2rem;
color: var(--secondary);
}
}
.vs--searchable .vs__dropdown-toggle {
cursor: text;
}
.vs--unsearchable .vs__dropdown-toggle {
cursor: pointer;
}
$transition-timing-function: cubic-bezier(1, -0.115, 0.975, 0.855);
$transition-duration: 150ms;
.vs__action:after {
fill: var(--dropdown-disabled-text);
transform: scale(1);
transition: transform $transition-duration $transition-timing-function;
transition-timing-function: $transition-timing-function;
}
.vs--open .vs__actions:after {
transform: rotate(180deg) scale(1);
}
.vs--loading .vs__open-indicator {
opacity: 0;
}
/**
* Super weird bug... If this declaration is grouped
* below, the cancel button will still appear in chrome.
* If it's up here on it's own, it'll hide it.
*/
.vs__search::-webkit-search-cancel-button {
display: none;
}
.vs__search::-webkit-search-decoration,
.vs__search::-webkit-search-results-button,
.vs__search::-webkit-search-results-decoration,
.vs__search::-ms-clear {
display: none;
}
.vs__search,
.vs__search:focus {
appearance: none;
border-left: none;
outline: none;
margin: 0;
background: none;
box-shadow: none;
width: 0;
max-width: 100%;
flex-grow: 1;
margin-left: $input-padding-sm;
}
.vs__search::placeholder {
color: var(--input-placeholder);
}
.vs--unsearchable {
.vs__search {
opacity: 1;
&:hover {
cursor: pointer;
}
}
}
.vs--single.vs--searching:not(.vs--open):not(.vs--loading) {
.vs__search {
opacity: 0.2;
}
}
/* States */
.vs--single {
.vs__selected {
background-color: transparent;
border-color: transparent;
}
&.vs--searching .vs__selected {
display: none;
}
}
.vs__selected {
display: flex;
align-items: center;
background-color: var(--accent-btn);
border: 1px solid var(--primary);
border-radius: 3px;
color: var(--link);
margin-left: $input-padding-sm;
&:not(:last-of-type) {
margin-right: 2px;
}
}
.vs__deselect {
display: inline-flex;
appearance: none;
margin-left: 8px;
padding: 0;
border: 0;
cursor: pointer;
background: none;
fill: var(--primary);
svg {
display: none;
}
&:after {
content: $icon-close;
font-family: 'icons';
color: var(--link);
}
}
/*inline single-option select*/
.v-select.inline {
background-color: transparent;
&.vs--single {
min-height: 29px;
&.vs--open {
.vs__selected {
position: absolute;
opacity: 0.4;
}
.vs__search {
margin-left: $input-padding-sm;
}
}
.vs__selected {
color: var(--input-text);
}
}
.vs__dropdown-menu {
min-width: 0px;
margin-top: 2px;
}
.vs__dropdown-toggle {
background-color: var(--input-bg);
border: none;
padding: none;
border-radius: var(--border-radius);
border: 1px solid var(--dropdown-border);
}
&.vs--single .vs__selected-options {
align-items: center;
}
.vs__search {
background-color: rgba(0, 0, 0, 0);
&:hover {
background-color: rgba(0, 0, 0, 0);
}
}
.vs__open-indicator {
fill: var(--input-label);
}
.vs__clear {
display: none;
}
}
.v-select.mini {
position: relative;
top: 2px;
.vs__dropdown-toggle {
padding: 5px 0;
}
.vs__selected {
margin: 0;
}
input {
padding: 0;
}
}
.vs__selected-options input {
width: 0;
display: inline-block;
border: 0;
background-color: var(--input-bg);
color: var(--input-text);
}
header .vs__selected-options input {
color: var(--header-input-text);
}
header .vs-select .vs__dropdown-toggle {
background: var(--error) !important;
}
.vs__no-options {
color: var(--dropdown-text);
padding: 3px 20px;
}
header {
.unlabeled-select {
padding: 0;
&.focused {
border: 0;
}
}
}
================================================
FILE: pkg/rancher-desktop/assets/translations/en-us.yaml
================================================
##############################
# Special stuff
##############################
generic:
add: Add
back: Back
cancel: Cancel
close: Close
comingSoon: Coming Soon
copy: Copy
create: Create
created: Created
customize: Customize
default: Default
disabled: Disabled
done: Done
enabled: Enabled
ignored: Ignored
invalidCron: Invalid cron schedule
labelsAndAnnotations: Labels & Annotations
loading: Loading…
members: Members
na: n/a
name: Name
never: Never
none: None
notFound: Not Found
number: '{prefix}{value, number}{suffix}'
overview: Overview
plusMore: "+ {n} more"
readFromFile: Read from File
register: Register
remove: Remove
resource: |-
{count, plural,
one {resource}
other {resources}
}
resourceCount: |-
{count, plural,
one {1 resource}
other {# resources}
}
save: Save
showAdvanced: Show Advanced
hideAdvanced: Hide Advanced
type: Type
unknown: Unknown
key: Key
value: Value
yes: Yes
no: No
units:
time:
5s: 5s
10s: 10s
30s: 30s
1m: 1m
5m: 5m
15m: 15m
30m: 30m
1h: 1h
2h: 2h
6h: 6h
1d: 1d
7d: 7d
30d: 30d
locale:
en-us: English
zh-hans: 简体中文
none: (None)
nav:
backToRancher: Cluster Manager
clusterTools: Cluster Tools
kubeconfig: Download KubeConfig
import: Import YAML
home: Home
shell: Kubectl Shell
support: Get Support
group:
cluster: Cluster
inUse: More Resources
rbac: RBAC
serviceDiscovery: Service Discovery
starred: Starred
storage: Storage
workload: Workload
monitoring: Monitoring
ns:
all: All Namespaces
clusterLevel: Only Cluster Resources
namespace: "{name}"
namespaced: Only Namespaced Resources
orphan: Not in a Project
project: "Project: {name}"
system: Only System Namespaces
user: Only User Namespaces
apps: Apps
categories:
explore: Explore Cluster
multiCluster: Global Apps
legacy: Legacy Apps
configuration: Configuration
search:
placeholder: Type to search clusters
noResults: No matching clusters
resourceSearch:
label: Resource Search
toolTip: Resource Search {key}
placeholder: Type to search for a resource...
header:
setLoginPage: Set as login page
restoreCards: Restore hidden cards
userMenu:
clusterDashboard: Cluster Dashboard
preferences: Preferences
accountAndKeys: Account & API Keys
logOut: Log Out
product:
apps: Apps & Marketplace
auth: Users & Authentication
backup: Rancher Backups
cis: CIS Benchmark
ecm: Cluster Manager
explorer: Cluster Explorer
fleet: Continuous Delivery
longhorn: Longhorn
manager: Cluster Management
gatekeeper: OPA Gatekeeper
istio: Istio
logging: Logging
rio: Rio
settings: Global Settings
clusterManagement: Cluster Management
monitoring: Monitoring
mcapps: Multi-cluster Apps
version: Version
versionChecking: (checking...)
networkStatus: Network status
kubernetesVersion: Kubernetes
containerEngine:
fullName: Container Engine
abbreviation: CE
notFound: not found
deactivated: deactivated
suffix:
percent: "%"
milliCpus: milli CPUs
cpus: CPUs
ib: iB
mib: MiB
gb: GB
revisions: |-
{count, plural,
=1 { Revision }
other { Revisions }
}
seconds: |-
{count, plural,
=1 { Second }
other { Seconds }
}
sec: Sec
times: |-
{count, plural,
=1 { Time }
other { Times }
}
##############################
# Components & Pages
##############################
app:
name: Rancher Desktop
update: "There's one last step to finish updating Rancher Desktop"
firstRun:
kubernetesVersion:
legend: Kubernetes Version
cachedOnly: (cached versions only)
ok: OK
marketplace:
title: Extensions
noResults: No extension found for the search criteria
tabs:
catalog: Catalog
installed: Installed
banners:
install: Extension {name} has been installed.
uninstall: Extension {name} has been removed.
labels:
install: Install
uninstall: Remove
upgrade: Upgrade
loading:
install: Installing...
uninstall: Removing...
upgrade: Upgrading...
moreInfo: More information
containers:
title: Containers
sortableTables:
noRows: There are no containers to show
logs:
title: Container Logs
loading: Loading container logs...
noLogs: No logs available
refresh: Refresh
fetchError: Failed to fetch container logs
manage:
table:
header:
state: State
containerName: Name
image: Image
ports: Port(s)
started: Uptime
actions: Actions
volumes:
title: Volumes
sortableTables:
noRows: There are no volumes to show
manage:
table:
header:
volumeName: Volume Name
driver: Driver
mountpoint: Mount Point
created: Created
manager:
table:
action:
browse: Browse Files
delete: Delete
files:
title: Volume Files
loading: Loading volume files...
volumeNotFound: 'Volume "{name}" not found'
checkError: Error checking volume status
listError: 'Error listing files: {error}'
noFiles: This volume is empty
table:
header:
name: Name
size: Size
modified: Modified
permissions: Permissions
images:
title: Images
sortableTables:
noRows: There are no images to show
state:
k8sUnready: Waiting for Kubernetes to be ready
imagesUnready: Waiting for image manager to be ready
unknown: 'Error: Unknown state; please reload.'
close: Close Output to Continue
action:
add: Add Image
manager:
close: Close Output to Continue
title: Image Output
input:
pull:
label: 'Name of image to pull:'
placeholder: 'registry.example.com/namespace/image'
button: Pull
build:
label: 'Name of image to build:'
placeholder: 'registry.example.com/namespace/image:tag'
button: Build
table:
label: All images
header:
imageName: Image
tag: Tag
imageId: Image ID
size: Size
action:
push: Push
delete: Delete
scan: Scan...
add:
title: Add Image
action:
build: Build
pull: Pull
pastTense:
build: Built
pull: Pulled
loadingText: '{action}ing image...'
successText: '{action} image'
errorText: Error trying to { action } ''{ image }'' - see console output for more information
scan:
title: Scan details for ''{ image }''
loadingText: Scanning ''{ image }''
errorText: Error trying to scan ''{ image }'' - see console output for more information
results:
headers:
severity: Severity
package: Package
vulnerabilityId: Vulnerability ID
installed: Installed
fixed: Fixed
labels:
critical: CRITICAL
high: HIGH
medium: MEDIUM
low: LOW
issuesFound: Issues Found
details:
description: Description
primaryUrl: Primary URL
references: References
k8s:
title: Kubernetes Settings
dialog:
ok: OK
cancel: Cancel
portForwarding:
title: Port Forwarding
sortableTables:
noRows: There are no port forwarding entries to show
general:
title: Welcome to Rancher Desktop by SUSE
description: Rancher Desktop provides Kubernetes and image management through the use of a desktop application.
about:
title: About
versions:
title: Versions
component: Component
version: Version
cli: CLI
helm: Helm
machine: Machine
releaseNotes: 'View release notes'
os:
mac: macOS
windows: Windows
linux: Linux
downloadImageList:
title: Image Lists
downloadCLI:
title: CLI Downloads
application:
behavior:
autoStart:
legendText: Startup
label: Automatically start at login
background:
legendText: Background
legendTooltip: >
Hide the app window when Rancher Desktop is running in the background.
It can be opened via the Notification Icon (if visible) or by running Rancher Desktop from the Applications menu.
startInBackground:
label: Start in the background
windowQuitOnClose:
label: Quit when closing application window
notificationIcon:
legendText: Notification Icon
label: Hide Notification Icon
theme:
legendText: Appearance
options:
system:
label: System
description: Follow the operating system setting
light:
label: Light
description: Always use light mode
dark:
label: Dark
description: Always use dark mode
virtualMachine:
networkingTunnel:
legend: Networking Tunnel
label: Enable networking tunnel
mount:
type:
legend: Mount Type
options:
reverse-sshfs:
label: reverse-sshfs
description: Exposes the filesystem by running an SFTP server.
9p:
label: 9p
description: Exposes the filesystem by using QEMU's virtio-9p-pci devices.
options:
cacheMode:
legend: Cache Mode
tooltip: Caching policy to be used
options:
none: none
loose: loose
fscache: fscache
mmap: mmap
mSizeInKib:
legend: Memory Size In KiB
tooltip: Maximum package size in KiB
protocolVersion:
legend: Protocol Version
tooltip: 9P protocol version
options:
9p2000: 9p2000
9p2000u: '9p2000.u'
9p2000L: '9p2000.L'
securityModel:
legend: Security Model
tooltip: Security model used for the export path
options:
passthrough: passthrough
'mapped-xattr': mapped-xattr
'mapped-file': mapped-file
none: none
virtiofs:
label: virtiofs
description: Exposes the filesystem by using an Apple Virtualization framework shared directory device.
proxy:
legend: WSL Proxy
label: Enable the proxy used by rancher-desktop
addressTitle: Proxy address
address: Address
port: Port
authTitle: Authentication information
username: Username
password: Password
noproxy:
legend: No proxy hostname list
placeholder: Hostname to not redirect to the proxy
errors:
duplicate: Error, item is duplicate.
type:
legend: Virtual Machine Type
options:
qemu:
label: QEMU
description: Use the QEMU emulator.
vz:
label: VZ
description: Use the Apple Virtualization framework.
useRosetta:
legend: VZ Option
label: Enable Rosetta support
containerEngine:
label: Container Engine
options:
moby:
label: dockerd (moby)
description: Docker API; use with Docker CLI.
containerd:
label: containerd
description: Namespaces for container images; use with nerdctl.
webAssembly:
label: WebAssembly (Wasm)
enabled: Enabled
description: Please read the documentation before enabling the experimental WebAssembly feature!
allowedImages:
label: Allowed Image Patterns
patterns:
placeholder: Type the image pattern
errors:
duplicate: Error, item is duplicate.
enable: Enable
alert:
The image name needs to match one of the patterns defined in the Allowed Images preference tab.
pathManagement:
label: Configure PATH
tooltip: Rancher Desktop ships with tools, such as kubectl, nerdctl, helm and docker. In order to use these tools, $HOME/.rd/bin must be in your PATH.
options:
rcFiles:
label: Automatic
description: Rancher Desktop edits your shell profile for you. Restart any open shells for changes to take effect.
manual:
label: Manual
description: 'Rancher Desktop does not change your PATH configuration; add $HOME/.rd/bin to your path manually.'
accept: Accept
legacyIntegrations:
title: Legacy Integrations Found
messageFirstPart: Rancher Desktop detected legacy tool symlinks in
messageSecondPart: but did not have the permissions required to remove them.
messageThirdPart: >
Legacy symlinks have the potential to cause path conflicts.
Please remove them at your earliest convenience.
details: >
Rancher Desktop creates symlinks from bundled tools, such as kubectl and docker,
to a directory in order to make them usable. This directory changed in Rancher Desktop
1.3.0. If you are seeing this message, Rancher Desktop was unable to remove the symlinks
from the old directory automatically. You should remove them manually to prevent future
path conflicts.
ok: OK
sudoPrompt:
title: Administrative Access Required
message: 'Rancher Desktop requires administrative access ("sudo access") for the following reasons:'
messageSecondPart: The prompt will be displayed once this window is closed. Cancelling the prompt or disabling administrative access requires you to switch the docker context to rancher-desktop in order to continue using Rancher Desktop.
explanation: 'This will modify the following paths:'
buttonText: OK
unmetPrerequisites:
title: Rancher Desktop is unable to start
message: 'Rancher Desktop cannot start because requirements are missing or not configured:'
action: 'Please ensure all requirements are met and try again. Rancher Desktop will now close.'
buttonText: OK
accountAndKeys:
title: Account and API Keys
account:
title: Account
change: Change Password
apiKeys:
title: API Keys
notAllowed: You do not have permission to manage API Keys
add:
description:
label: Description
placeholder: Optionally enter a description to help you identify this API Key
label: Create API Key
expiry:
label: Automatically expire
options:
never: Never
day: A day from now
month: A month from now
year: A year from now
custom: Custom
maximum: "{value} - Maximum allowed"
customExpiry:
options:
minute: Minutes
hour: Hours
day: Days
month: Months
year: Years
scope: Scope
noScope: No Scope
info:
accessKey: Access Key
secretKey: Secret Key
bearerToken: Bearer Token
saveWarning: Save the info above! This is the only time you'll be able to see it. If you lose it, you'll need to create a new API key.
keyCreated: A new API Key has been created
bearerTokenTip: "Access Key and Secret Key can be sent as the username and password for HTTP Basic auth to authorize requests. You can also combine them to use as a Bearer token:"
ttlLimitedWarning: The Expiry time for this API Key was reduced due to system configuration
authConfig:
accessMode:
label: 'Configure who should be able to log in and use {vendor}'
required: Restrict access to only the authorized users & groups
restricted: 'Allow members of clusters and projects, plus authorized users & groups'
unrestricted: Allow any valid user
allowedPrincipalIds:
title: Authorized Users & Groups
associatedWarning: 'Note: The {provider} user you authenticate as will be associated as an alternate way to log in to the {vendor} user you are currently logged in as {username}; all the global permissions, project, and cluster role bindings of this {vendor} user will also apply to the {provider} user.'
github:
clientId:
label: Client ID
clientSecret:
label: Client Secret
form:
app:
label: Application name
value: 'Anything you like, e.g. My {vendor}'
calllback:
label: Authorization callback URL
description:
label: Application description
value: 'Optional, can be left blank'
homepage:
label: Homepage URL
instruction: 'Fill in the form with these values:'
prefix:
1:
Copy and paste the Client ID and Client Secret of your newly created OAuth app into the fields below
host:
label: GitHub Enterprise Host
placeholder: e.g. github.mycompany.example
target:
label: Which version of GitHub do you want to use?
private: A private installation of GitHub Enterprise
public: Public GitHub.com
table:
server: Server
clientId: Client ID
googleoauth:
adminEmail: Admin Email
domain: Domain
oauthCredentials:
label: OAuth Credentials
tip: The OAuth Credentials JSON can be found in the Google API developers console.
serviceAccountCredentials:
label: Service Account Credentials
tip: The Service Account Credentials JSON can be found in the service accounts section of the Google API developers console.
steps:
1:
title: 'Open applications settings in a new window'
body:
1: Login to your account. Navigate to "APIs & Services" and then select "OAuth consent screen".
2: 'Authorized domains:'
3: 'Application homepage link: '
4: 'Under Scopes for Google APIs, enable "email", "profile", and "openid".'
5: 'Click on "Save".'
topPrivateDomain: 'Top private domain of:'
2:
title: 'Navigate to the "Credentials" tab to create your OAuth client ID'
body:
1: 'Select the "Create Credentials" dropdown, and select "OAuth clientID", then select "Web application".'
2: 'Authorized JavaScript origins:'
3: 'Authorized redirect URIs:'
4: 'Click "Create", and then click on the "Download JSON" button.'
5: 'Upload the downloaded JSON file in the OAuth credentials box.'
3:
title: 'Create Service Account credentials'
introduction: 'Follow this guide to:'
body:
1: Create a service account.
2: Generate a key for the service account.
3: Add the service account as an OAuth client in your google domain.
ldap:
freeipa: Configure a FreeIPA server
activedirectory: Configure an Active Directory account
openldap: Configure an OpenLDAP server
defaultLoginDomain:
label: Default Login Domain
placeholder: eg mycompany
hint: This domain will be used if a user logs in without specifying one.
cert: Certificate
disabledStatusBitmask: Disabled Status Bitmask
groupDNAttribute: Group DN Attribute
groupMemberMappingAttribute: Group Member Mapping Attribute
groupMemberUserAttribute: Group Member User Attribute
groupSearchBase:
label: Group Search Base
placeholder: 'ou=groups,dc=mycompany,dc=com'
hostname: Hostname/IP
loginAttribute: Login Attribute
nameAttribute: Name Attribute
nestedGroupMembership:
label: Nested Group Membership
options:
direct: Search only direct group memberships
nested: Search direct and nested group memberships
objectClass: Object Class
password: Password
port: Port
customizeSchema: Customize Schema
users: Users
groups: Groups
searchAttribute: Search Attribute
searchFilter: Search Filter
serverConnectionTimeout: Server Connection Timeout
serviceAccountDN: Service Account Distinguished Name
serviceAccountPassword: Service Account Password
serviceAccountInfo: '{vendor} needs a service account that has read-only access to all of the domains that will be able to login, so that we can determine what groups a user is a member of when they make a request with an API key.'
starttls:
label: Start TLS
tip: Upgrades non-encrypted connections by wrapping with TLS during the connection process. Cannot be used in conjunction with TLS.
tls: TLS
userEnabledAttribute: User Enabled Attribute
userMemberAttribute: User Member Attribute
userSearchBase:
label: User Search Base
placeholder: 'e.g. ou=users,dc=mycompany,dc=com'
username: Username
usernameAttribute: Username Attribute
table:
server: Server
clientId: Client ID
saml:
entityID: Entity ID Field
UID: UID Field
adfs: Configure an AD FS account
api: '{vendor} API Host'
cert:
label: Certificate
placeholder: Paste in the certificate, starting with -----BEGIN CERTIFICATE-----
displayName: Display Name Field
groups: Groups Field
key:
label: Private Key
placeholder: Paste in the private key, typically starting with -----BEGIN RSA PRIVATE KEY-----
keycloak: Configure a Keycloak account
metadata:
label: Metadata XML
placeholder: Paste in the IDP Metadata XML
okta: Configure an Okta account
ping: Configure a Ping account
shibboleth: Configure a Shibboleth account
showLdap: Configure an OpenLDAP Server
userName: User Name Field
azuread:
tenantId: Tenant ID
applicationId: Application ID
endpoint: Endpoint
graphEndpoint: Graph Endpoint
tokenEndpoint: Token Endpoint
authEndpoint: Auth Endpoint
oidc:
oidc: Configure an OIDC account
keycloakoidc: Configure a Keycloak OIDC account
rancherUrl: Rancher URL
clientId: Client ID
clientSecret: Client Secret
customEndpoint:
label: Endpoints
custom: Specify
standard: Generate
keycloak:
url: Keycloak URL
realm: Keycloak Realm
issuer: Issuer
authEndpoint: Auth Endpoint
cert:
label: Certificate
placeholder: Paste in the certificate, starting with -----BEGIN CERTIFICATE-----
key:
label: Private Key
placeholder: Paste in the private key, typically starting with -----BEGIN RSA PRIVATE KEY-----
stateBanner:
disabled: 'The {provider} authentication provider is currently disabled.'
enabled: 'The {provider} authentication provider is currently enabled.'
testAndEnable: Test and Enable Authentication
noneEnabled: Local Authentication is always enabled, but you may select another additional authentication provider from those shown below.
localEnabled: '{vendor} is configured to allow access to accounts in its local database.'
manageLocal: Manage Accounts
authGroups:
actions:
refresh: Refresh Group Memberships
assignRoles: Assign Global Roles
assignEdit:
assignTitle: Assign Global Roles To Group
assignTo:
title: |-
{count, plural,
=1 { Assign Cluster To… }
other { Assign {count} Clusters To… }
}
labelsTitle: |-
{count, plural,
=1 { Assign Cluster To… }
other { Assign {count} Clusters To… }
}
workspace: Workspace
asyncButton:
apply:
action: Apply
success: Applied
waiting: Applying…
continue:
action: Continue
success: Saved
waiting: Saving…
copy:
action: Click to Copy
success: Copied!
create:
action: Create
success: Created
waiting: Creating…
default:
action: Action
error: Error
success: Success
waiting: Waiting
delete:
action: Delete
success: Deleted
waiting: Deleting…
disable:
action: Disable
success: Disabled
waiting: Disabling…
activate:
action: Activate
waiting: Activating…
success: Activated
deactivate:
action: Deactivate
waiting: Deactivating…
success: Deactivated
done:
action: Done
success: Saved
waiting: Saving…
download:
action: Download
success: Saving
waiting: Downloading…
edit:
action: Save
success: Saved
waiting: Saving…
enable:
action: Enable
success: Enabled
waiting: Enabling…
finish:
action: Finish
success: Finished
waiting: Finishing…
import:
action: Import
success: Imported
waiting: Importing…
install:
action: Install
success: Installing
waiting: Starting…
refresh:
action: ''
actionIcon: refresh
error: ''
errorIcon: error
success: ''
successIcon: checkmark
waiting: ''
waitingIcon: refresh
remove:
action: Remove
success: Removed
waiting: Removing…
restore:
action: Restore
waiting: Restoring…
success: Restored
snapshot:
action: Snapshot Now
waiting: Snapshotting…
success: Snapshot Creating
uninstall:
action: Uninstall
success: Uninstalled
waiting: Uninstalling…
update:
action: Update
success: Updated
waiting: Updating…
upgrade:
action: Upgrade
success: Upgrading
waiting: Starting…
backupRestoreOperator:
backupFilename: Backup Filename
deleteTimeout:
label: Delete Timeout
tip: Seconds to wait for a resource delete to succeed before removing finalizers to force deletion.
deployment:
rancherNamespace: Rancher ResourceSet Namespace
size: Size
storage:
label: Default Storage Location
options:
defaultStorageClass: 'Use the default storage class ({name})'
none: No default storage location
pickPV: Use an existing persistent volume
pickSC: Use an existing storage class
s3: Use an S3-compatible object store
persistentVolume:
label: Persistent Volume
storageClass:
label: Storage Class
tip: 'Configure a storage location where all backups are saved by default. You will have the option to override this with each backup, but will be limited to using an S3-compatible object store.'
warning: 'This {type} does not have its reclaim policy set to "Retain". Your backups may be lost if the volume is changed or becomes unbound.'
encryption: Encryption
encryptionConfigName:
backuptip: 'Any secret in the cattle-resource-system namespace that has an encryption-provider-config.yaml key. The contents of this file are necessary to perform a restore from this backup, and are not stored by Rancher Backup.'
label: Encryption Config Secret
options:
none: Store the contents of the backup unencrypted
secret: 'Encrypt backups using an Encryption Config Secret (Recommended)'
restoretip: 'If the backup was performed with encryption enabled, a secret containing the same encryption-provider-config should be used during restore.'
warning: 'The contents of this file are necessary to perform a restore from this backup, and are not stored by Rancher Backup.'
lastBackup: Last Backup
nextBackup: Next Backup
noResourceSet: You must define a ResourceSet in this namespace to create a backup CR.
prune:
label: Prune
tip: Delete the resources managed by Rancher that are not present in the backup. (Recommended)
resourceSetName: Resource Set
restoreFrom:
default: The default storage target
existing: An existing backup config
s3: An S3-compatible object store
retentionCount:
label: Retention Count
units: |-
{count, plural,
=1 { File }
other { Files }
}
s3:
bucketName: Bucket Name
credentialSecretName: Credential Secret
endpoint: Endpoint
endpointCA: Endpoint CA
folder: Folder
insecureTLSSkipVerify: Skip TLS Verifications
region: Region
storageLocation: Storage Location
titles:
backupLocation: Backup Source
location: Storage Location
s3: S3
schedule:
label: Schedule
options:
disabled: One-Time Backup
enabled: Recurring Backups
placeholder: e.g. @midnight or 0 0 * * *
storageSource:
configureS3: Use an S3-compatible object store
useBackup: Use the s3 location specified on the Backup CR
useDefault: Use the default storage location configured during installation
targetBackup: Target Backup
catalog:
app:
managed: Managed
section:
notes: Release Notes
readme: Chart README
resources: Resources
values: Values YAML
chart:
header:
charts: Charts
info:
appVersion: Application Version
chartVersions:
label: Chart Versions
showMore: Show More
showLess: Show Less
home: Home
maintainers: Maintainers
related: Related
chartUrls: Chart
keywords: Keywords
errors:
clusterToolExists: This chart has a fixed namespace and name. A matching application has been found and any changes will be made to it.
charts:
all: All
categories:
all: All Categories
certified:
other: Other
partner: Partner
rancher: '{vendor}'
header: Deploy Chart
noCharts: 'There are no charts available, have you added any repos?'
noWindows: Your catalogs do not contain any charts capable of being deployed on a Windows cluster.
search: Filter
install:
action:
goToUpgrade: Edit/Upgrade
appReadmeMissing: This chart doesn't have any additional chart information.
appReadmeTitle: Chart Information (Helm README)
chart: Chart
error:
requiresFound: '{name} must be installed before you can install this chart.'
requiresMissing: 'This chart requires another chart that provides {name}, but none was found.'
insufficientCpu: 'This chart requires {need, number} CPU cores, but the cluster only has {have, number} available.'
insufficientMemory: 'This chart requires {need} of memory, but the cluster only has {have} available.'
header:
install: 'Install {name}'
installGeneric: Install Chart
upgrade: 'Upgrade {name}'
helm:
atomic: Atomic
cleanupOnFail: Cleanup on Failure
crds: Apply custom resource definitions
dryRun: Dry Run
force: Force
historyMax:
label: Keep last
unit: |-
{value, plural,
=1 { revision }
other { revisions }
}
hooks: Execute chart hooks
openapi: Validate OpenAPI schema
resetValues: Reset Values
timeout:
label: Timeout
unit: |-
{value, plural,
=1 { second }
other { seconds }
}
wait: Wait
namespaceIsInProject: "This chart's target namespace, {namespace}, already exists and cannot be added to a different project."
project: Install into Project
section:
chartOptions: Edit Options
valuesYaml: Edit YAML
diff: Compare Changes
slideIn:
dock: Dock to bottom
steps:
basics:
label: Metadata
subtext: Set App metadata
description: This process will help {action, select,
install { create }
upgrade { upgrade }
update { update }
} the {existing, select,
true { app}
false { chart}
}. Start by setting some basic information used by {vendor} to manage the App.
nsCreationDescription: "To install the app into a new namespace enter it's name and select it in the Namespace field."
createNamespace: "Namespace {namespace} will be created."
helmValues:
label: Values
subtext: Change how the App works
description: Configure Values used by Helm that help define the App.
chartInfo:
button: View Chart Info
label: Chart Info
helmCli:
checkbox: Customize Helm options before install
label: Helm Options
subtext: Change how the app is deployed
description: Supply additional deployment options
version: Version
versions:
current: '{ver} (Current)'
linux: '{ver} (Linux-only)'
windows: '{ver} (Windows-only)'
operation:
tableHeaders:
action: Action
releaseName: Release Name
releaseNamespace: Release Namespace
repo:
action:
refresh: Refresh
all: All
gitBranch:
label: Git Branch
placeholder: e.g. master
gitRepo:
label: Git Repo URL
placeholder: 'e.g. https://github.com/your-company/charts.git'
name:
rancher-charts: '{vendor}'
rancher-partner-charts: Partners
rancher-rke2-charts: RKE2
target:
git: Git Repository containing Helm chart definitions
http: http(s) URL to an index generated by Helm
label: Target
url:
label: Index URL
placeholder: 'e.g. https://charts.rancher.io'
tools:
header: Cluster Tools
action:
install: Install
upgrade: Upgrade/Edit
edit: Edit
remove: Remove
manage: Manage
changePassword:
title: Change Password
cancel: Cancel
deleteKeys:
label: Delete all existing API keys
changeOnLogin:
label: Ask user to change their password on first login
generatePassword:
label: Generate a random password
currentPassword:
label: Current Password
userGen:
newPassword:
label: New Password
confirmPassword:
label: Confirm Password
randomGen:
generated:
label: Generated Password
newGeneratedPassword: Suggest a password
errors:
missmatchedPassword: Passwords do not match
failedToChange: Failed to change password
failedDeleteKey: Failed to delete key
failedDeleteKeys: Failed to delete keys
chartHeading:
overview: Overview
poweredBy: "Powered by:"
cis:
addTest: Add Test ID
alertNeeded: |-
Alerting must be enabled within the CIS chart values.yaml.
This requires that the {vendor} Monitoring and Alerting app is installed
and the Receivers and Routes are configured to send out alerts.
alertOnComplete: Alert on scan completion
alertOnFailure: Alert on scan failure
benchmarkVersion: Benchmark Version
clusterProvider: Cluster Provider
cronSchedule:
label: Schedule
placeholder: "e.g. 0 * * * *"
customConfigMap: Custom Benchmark ConfigMap
deleteBenchmarkWarning: |-
{count, plural,
=1 { Any profiles using this benchmark version will no longer work. }
other { Any profiles using these benchmark versions will no longer work }
}
deleteProfileWarning: |-
{count, plural,
=1 { Any scheduled scans using this profile will no longer work. }
other { Any scheduled scans using either of these profiles will no longer work. }
}
downloadAllReports: Download All Saved Reports
downloadLatestReport: Download Latest Report
downloadReport: Download Report
maxKubernetesVersion: Maximum allowed Kubernetes version
minKubernetesVersion: Minimum required Kubernetes version
noProfiles: There are no valid ClusterScanProfiles for this cluster type to select.
noReportFound: No scan report found
profile: Profile
reports: Reports
retention: Retention Count
scan:
description: Description
fail: Fail
lastScanTime: Last Scan Time
notApplicable: N/A
number: Number
pass: Pass
remediation: Remediation
scanDate: Scan Date
scanReport: Scan Report
skip: Skip
total: Total
warn: Warn
scheduling:
disable: Run scan once
enable: Run scan on a schedule
scoreWarning:
label: Scan state for "warn" results
protip: Scans with no failures will be marked "Pass" by default even if some of the tests generate "warn" output. This behavior can be changed by selecting the "fail" option from this section.
testID: Test ID
testsSkipped: Tests Skipped
testsToSkip: Tests to Skip
cluster:
agentEnvVars:
label: Agent Environment
detail: Add additional environment variables to the agent container. This is most commonly useful for configuring a HTTP proxy.
custom:
nodeRole:
label: Node Role
detail: Choose what roles the node will have in the cluster. The cluster needs to have at least one node with each role.
advanced:
label: Advanced
detail: Additional control over how the node will be registered. These values will often need to be different for each node registered.
registrationCommand:
label: Registration Command
detail: Run this command on each of the existing machines you want to register.
insecure: "Insecure: Select this to skip TLS verification if your server has a self-signed certificate."
credential:
aws:
accessKey:
label: Access Key
placeholder: Your AWS Access Key
defaultRegion:
help: The default region to use when creating clusters. Also contacted to verify that this credential works.
label: Default Region
secretKey:
label: SecretKey
placeholder: Your AWS Secret Key
digitalocean:
accessToken:
help: Paste in a Personal Access Token from the DigitalOcean Applications & API screen.
label: Access Token
placeholder: Your DigitalOcean API Access Token
label: Cloud Credential
name:
label: Credential Name
placeholder: Name for this credential (optional)
vmwarevsphere:
server:
label: vCenter or ESXi Server
placeholder: vcenter.domain.com
port:
label: Port
username:
label: Username
password:
label: Password
note: 'Note: The free ESXi license does not support API access. Only servers with a valid or evaluation license are supported.'
description:
label: Cluster Description
placeholder: Any text you want that better describes this cluster
import:
commandInstructions: 'Run the kubectl command below on an existing Kubernetes cluster running a supported Kubernetes version to import it into {vendor}:'
commandInstructionsInsecure: 'If you get a "certificate signed by unknown authority" error, your {vendor} installation has a self-signed or untrusted SSL certificate. Run the command below instead to bypass the certificate verification:'
clusterRoleBindingInstructions: 'If you get permission errors creating some of the resources, your user may not have the cluster-admin role. Use this command to apply it:'
clusterRoleBindingCommand: 'kubectl create clusterrolebinding cluster-admin-binding --clusterrole cluster-admin --user '
importAction: Import Existing
kubernetesVersion:
label: Kubernetes Version
toolsTip: Use the new Cluster Tools to manage and install Monitoring, Logging and other tools
name:
label: Cluster Name
placeholder: A unique name for the cluster
machineConfig:
aws:
sizeLabel: |-
{apiName}: {cpu}, {memory, number} GiB Memory, {storageSize, plural,
=0 {EBS-Only}
other {{storageSize, number} GiB {storageType}}
}
digitalocean:
sizeLabel: |-
{plan, select,
s {Basic: }
g {General: }
gd {General: }
c {CPU: }
m {Memory: }
so {Storage: }
standard {Standard: }
other {}
}{memoryGb} GB, {vcpus, plural,
=1 {# vCPU}
other {# vCPUs}
}, {disk} GB Disk ({value})
machinePool:
name:
label: Pool Name
placeholder: A random one will be generated by default
nodeTotals:
label:
controlPlane: '{count} Control Plane'
etcd: '{count} etcd'
worker: '{count} Worker'
tooltip:
controlPlane: |-
{count, plural,
=0 { A cluster needs at least one control plane node to be usable. }
=1 { A cluster with only one control plane node is not fault-tolerant. }
other {}
}
etcd: |-
{count, plural,
=0 { A cluster needs at least one etcd node to be usable. }
=1 { A cluster with only one etcd node is not fault-tolerant. }
=2 { Clusters should have an odd number of nodes. A cluster with 2 etcd nodes is not fault-tolerant. }
=3 {}
=4 { Clusters should have an odd number of nodes. }
=5 {}
=6 { Clusters should have an odd number of nodes. }
=7 {}
other { More than 7 etcd nods is not recommended. }
}
worker: |-
{count, plural,
=0 { A cluster needs at least one worker node to be usable. }
=1 { A cluster with only one worker node is not fault-tolerant. }
other {}
}
provider:
aliyunecs: Aliyun ECS
aliyunkubernetescontainerservice: Alibaba ACK
aliyun: Alibaba ACK
amazonec2: Amazon EC2
amazoneks: Amazon EKS
aws: Amazon AWS
azure: Azure
azureaks: Azure AKS
aks: Azure AKS
baiducloudcontainerengine: Baidu CCE
baidu: Baidu CCE
cloudca: Cloud.ca
custom: Custom
digitalocean: DigitalOcean
docker: Docker
eks: Amazon EKS
exoscale: Exoscale
google: Google GCE
googlegke: Google GKE
gke: Google GKE
huaweicce: Huawei CCE
import: Generic
imported: Imported
k3s: K3s
kubeAdmin: KubeADM
linode: Linode
local: Local
minikube: Minikube
oci: Oracle Cloud Infrastructure
openstack: OpenStack
opentelekomcloudcontainerengine: Open Telekom Cloud CCE
otccce: Open Telekom Cloud CCE
oracleoke: Oracle OKE
otc: Open Telekom Cloud
other: Other
packet: Packet
pinganyunecs: Pinganyun ECS
rackspace: RackSpace
rancherkubernetesengine: RKE
rke2: RKE2
rke: RKE1
rkeWindows: Windows
softlayer: SoftLayer
tencenttke: Tencent TKE
upcloud: UpCloud
vmwarevsphere: vSphere
zstack: ZStack
providerGroup:
create-custom1: Use existing nodes and create a cluster using RKE
create-custom2: Use existing nodes and create a cluster using RKE2
create-kontainer: Create a cluster in a hosted Kubernetes provider
register-kontainer: Register an existing cluster in a hosted Kubernetes provider
create-rke1: Provision new nodes and create a cluster using RKE
create-rke2: Provision new nodes and create a cluster using RKE2
create-template: Use a Catalog Template to create a cluster
register-custom: Import any Kubernetes cluster
rke2:
systemService:
rke2-coredns: 'CoreDNS'
rke2-ingress-nginx: 'NGINX Ingress Controller'
rke2-kube-proxy: 'Kube Proxy'
rke2-metrics-server: 'Metrics Server'
tabs:
ace: Authorized Endpoint
advanced: Advanced
agentEnv: Agent Environment Vars
basic: Basics
cluster: Cluster Configuration
etcd: etcd
networking: Networking
machinePools: Machine Pools
registry: Private Registry
upgrade: Upgrade Strategy
clusterIndexPage:
hardwareResourceGauge:
consumption: "{useful} of {total} {units} {suffix}"
cores: Cores
pods: Pods
ram: Memory
used: Used
reserved: Reserved
header: Cluster Dashboard
resourceGauge:
totalResources: Total Resources
sections:
capacity:
label: Capacity
events:
label: Events
resource:
label: Resource
date:
label: Date
alerts:
label: Alerts
clusterMetrics:
label: Cluster Metrics
etcdMetrics:
label: Etcd Metrics
k8sMetrics:
label: Kubernetes Components Metrics
gatekeeper:
buttonText: Configure Gatekeeper
disabled: OPA Gatekeeper is not configured.
label: OPA Gatekeeper Constraint Violations
noRows: There are no constraints with violations to show.
nodes:
label: Unhealthy Nodes
noRows: There are no unhealthy nodes to show.
configmap:
tabs:
data:
label: Data
protip: Use this area for anything that's UTF-8 text data
binaryData:
label: Binary Data
containerResourceLimit:
cpuPlaceholder: e.g. 1000
helpText: Configure how much resources the container can consume by default.
helpTextDetail: The amount of resources the container can consume by default.
label: Container Default Resource Limit
limitsCpu: CPU Limit
limitsMemory: Memory Limit
memPlaceholder: e.g. 128
requestsCpu: CPU Reservation
requestsMemory: Memory Reservation
cruResource:
backToForm: Back to Form
backBody: You will lose any changes made to the YAML.
cancelBody: You will lose any changes made to the YAML.
confirmBack: "Okay"
confirmCancel: "Okay"
reviewForm: "Keep editing YAML"
reviewYaml: "Keep editing YAML"
previewYaml: Edit as YAML
detailText:
collapse: Hide
binary: ''
empty: ''
unsupported: ''
plusMore: |-
{n, plural,
=1 {+ 1 more char}
other {+ {n, number} more chars}
}
etcdInfoBanner:
hasLeader: "Etcd has a leader:"
leaderChanges: "Number of leader changes:"
failedProposals: "Number of failed proposals:"
fleet:
cluster:
summary: Resource Summary
nonReady: Non-Ready Bundles
fleetSummary:
state:
success: 'Ready'
info: 'Transitioning'
warning: 'Warning'
error: 'Error'
unknown: 'Unknown'
gitRepo:
tabs:
resources: Resources
unready: Non-Ready
auth:
label: Authentication
git: Git Authentication
helm: Helm Authentication
caBundle:
label: Certificates
placeholder: "Paste in one or more certificates, starting with -----BEGIN CERTIFICATE----"
paths:
label: Paths
placeholder: e.g. /directory/in/your/repo
addLabel: Add Path
empty: The root of the repo is used by default. To use one or more different directories, add them here.
repo:
label: Repository URL
placeholder: 'e.g. https://github.com/rancher/fleet-examples.git'
ref:
label: Watch
branch: A Branch
revision: A Revision
branchLabel: Branch Name
branchPlaceholder: e.g. master
revisionLabel: Tag or Commit Hash
revisionPlaceholder: e.g. v1.0.0
serviceAccount:
label: Service Account Name
placeholder: "Optional: Use a service account in the target clusters"
targetNamespace:
label: Target Namespace
placeholder: "Optional: Require all resources to be in this namespace"
target:
selectLabel: Target
advanced: Advanced
cluster: Cluster
clusterGroup: Cluster Group
label: Deploy To
labelLocal: Deploy With
targetDisplay:
advanced: Advanced
cluster: "Cluster"
clusterGroup: "Group"
all: All
none: None
local: Local
tls:
label: TLS Certificate Verification
verify: Require a valid certificate
specify: Specify additional certificates to be accepted
skip: Accept any certificate (insecure)
workspace:
label: Workspace
clusterGroup:
selector:
label: Cluster Selectors
matchesAll: Matches all {total, number} existing clusters
matchesNone: Matches no existing clusters
matchesSome: |-
{matched, plural,
=1 {Matches 1 of {total, number} existing clusters: "{sample}"}
other {Matches {matched, number} of {total, number} existing clusters, including "{sample}"}
}
footer:
docs: Docs
download: Download CLI
forums: Forums
issue: File an Issue
slack: Slack
gatekeeperConstraint:
match:
title: Match
tab:
enforcementAction:
title: Enforcement Action
rules:
title: Rules
sub:
labelSelector:
addLabel: Add Label
title: Label Selector
namespaces:
sub:
excludedNamespaces: Excluded Namespaces
namespaces: Namespaces
namespaceSelector:
addNamespace: Add Namespace
title: Namespace Selector
scope:
title: Scope
title: Namespaces
parameters:
addParameter: Add Parameter
editAsForm: Edit as Form
editAsYaml: Edit as YAML
title: Parameters
template: Template
violations:
title: Violations
gatekeeperIndex:
poweredBy: OPA Gatekeeper
unavailable: OPA + Gatekeeper is not available in the system-charts catalog.
violations: Violations
glance:
created: Created
cpu: CPU Usage
memory: Memory
nodes:
total:
label: |-
{count, plural,
=1 { Node }
other { Total Nodes }
}
pods: Pods
provider: Provider
version: Kubernetes Version
monitoringDashboard: Monitoring Dashboard
installMonitoring: Install Monitoring
v1MonitoringInstalled: V1 Monitoring Installed
grafanaDashboard:
failedToLoad: Failed to load graph
reload: Reload
grafana: Grafana
graphOptions:
detail: Detail
summary: Summary
refresh: Refresh
range: Range
hpa:
detail:
currentMetrics:
header: Current Metrics
noMetrics: No Current Metrics
metricHeader: '{source} Metric'
metricIdentifier:
name:
label: Metric Name
placeholder: e.g. packets-per-second
selector:
label: Add Selector
metricTarget:
averageVal:
label: Average Value
quantity:
label: Quantity
type:
label: Type
utilization:
label: Average Utilization
value:
label: Value
metrics:
headers:
metricName: Name
objectKind: Object Kind
objectName: Object Name
quantity: Quantity
resource: Resource Name
targetName: Target Name
value: Value
source: Source
objectReferance:
api:
label: Referent API Version
placeholder: e.g. apps/v1beta1
kind:
label: Referent Kind
placeholder: e.g. Deployment
name:
label: Referent Name
placeholder: e.g. php-apache
tabs:
labels: Labels
metrics: Metrics
target: Target
workload: Workload
types:
cpu: CPU
memory: Memory
warnings:
custom: In order to use custom metrics with HPA, you need to deploy the custom metrics server such as prometheus adapter.
external: In order to use external metrics with HPA, you need to deploy the external metrics server such as prometheus adapter.
noMetric: In order to use resource metrics with HPA, you need to deploy the metrics server.
resource: The selected target reference does not have the correct resource requests on the spec. Without this the HPA metric will have no effect.
workloadTab:
current: Current Replicas
last: Last Scale Time
max: Maximum Replicas
min: Minimum Replicas
targetReference: Target Reference
import:
title: Import YAML
defaultNamespace:
label: Default Namespace
success: |-
Applied {count, plural,
=1 {1 Resource}
other {# Resources}
}
ingress:
certificates:
addCertificate: Add Certificate
addHost: Add Host
certificate:
label: Certificate - Secret Name
doesntExist: The selected certificate does not exist
defaultCertLabel: Default Ingress Controller Certificate
headers:
certificate: Certificate
hosts: Hosts
host:
label: Host
placeholder: e.g. example.com
label: Certificates
removeHost: Remove
defaultBackend:
label: Default Backend
noServiceSelected: No default backend is configured.
port:
label: Port
placeholder: e.g. 80 or http
targetService:
label: Target Service
doesntExist: The selected service does not exist
warning: "Warning: Default backend is used globally for the entire cluster."
rules:
addPath: Add Path
addRule: Add Rule
headers:
pathType: Path Type
path: Path
port: Port
target: Target Service
certificates: Certificates
hostname: Hostname
path:
label: Path
placeholder: e.g. /foo
port:
label: Port
placeholder: e.g. 80 or http
removePath: Remove
requestHost:
label: Request Host
placeholder: e.g. example.com
target:
label: Target Service
doesntExist: The selected service does not exist
title: Rules
rulesAndCertificates:
title: Rules and Certificates
defaultCertificate: default
target:
default: Default
internalExternalIP:
none: None
istio:
links:
kiali:
label: Kiali
description: 'Visualization of services within a service mesh and how they are connected. For Kiali to display data, you need Prometheus installed. If you need a monitoring solution, install {vendor} monitoring.'
jaeger:
label: Jaeger
description: Monitor and Troubleshoot microservices-based distributed systems.
disabled: '{app} is not installed'
cni: Enabled CNI
customOverlayFile:
label: Custom Overlay File
tip: 'The overlay file allows for additional configuration on top of the base {vendor} Istio installation. You can utilize the IstioOperator API to make changes and additions for all components and apply those changes via this overlay YAML file.'
description: '{vendor} Istio helm chart installs a minimal Istio configuration for you to get started integrating with your applications.
If you would like to get additional information about Istio, visit https://istio.io/latest/docs/concepts/what-is-istio/'
egressGateway: Enabled Egress Gateway
ingressGateway: Enabled Ingress Gateway
istiodRemote: Enabled istiodRemote
kiali: Enabled Kiali
pilot: Enabled Pilot
policy: Enabled Policy
poweredBy: Powered by Istio
telemetry: Enabled Telemetry
titles:
components: Components
customAnswers: Custom Answers
advanced: Advanced Settings
description: Description
tracing: Enabled Jaeger Tracing (limited)
v1Warning: Please uninstall the current Istio version in the istio-system namespace before attempting to install this version.
labels:
addLabel: Add Label
addSetLabel: Add/Set Label
addAnnotation: Add Annotation
labels:
title: Labels
annotations:
title: Annotations
landing:
clusters:
title: Clusters
provider: Provider
kubernetesVersion: Kubernetes Version
explorer: Explorer
explore: Explore
cores: |-
{count, plural,
=1 {core}
other {cores}}
seeWhatsNew: Learn more about the improvements and new capabilities in this version.
whatsNewLink: "What's new in 2.5"
learnMore: Learn More
gettingStarted:
title: Getting Started
body: Take a look at the quick getting started guide. For Cluster Manager users, learn more about where you can find you favorite features in the Dashboard UI.
community:
title: Community Support
docs: Docs
forums: Forums
commercial:
title: Commercial Support
body: Learn about commercial support
landingPrefs:
title: What do you want to see when you log in?
body: "You can change where you land when you login:"
options:
homePage: Take me to the home page
lastVisited: Take me to the area I last visited
custom: "Take me to cluster:"
welcomeToRancher: 'Welcome to {vendor}'
logging:
clusterFlow:
noOutputsBanner: There are no cluster outputs in the selected namespace.
flow:
clusterOutputs:
doesntExistTooltip: This cluster output doesn't exist
label: Cluster Outputs
matches:
label: Matches
addSelect: Add Include Rule
addExclude: Add Exclude Rule
filters:
label: Filters
outputs:
doesntExistTooltip: This output doesn't exist
label: Outputs
install:
k3sContainerEngine: K3S Container Engine
enableAdditionalLoggingSources: Enable enhanced cloud provider logging
dockerRootDirectory: Docker Root Directory
elasticsearch:
host: Host
scheme: Scheme
port: Port
indexName: Index Name
user: User
password: Password from Secret
caFile:
label: CA File from Secret
clientCert:
label: Client Cert from Secret
placeholder: Paste in the CA certificate
clientKey:
label: Client Key from Secret
placeholder: Paste in the client key
clientKeyPass: Client Key Pass from Secret
kafka:
brokers: Brokers
defaultTopic: Default Topic
saslOverSsl: SASL Over SSL
scramMechanism: Scram Mechanism
username: Username from Secret
password: Password from Secret
sslCaCert:
label: CA Cert from Secret
placeholder: Paste in the CA certificate
sslClientCert:
label: Cert from Secret
placeholder: Paste in the client cert
sslClientCertChain:
label: Cert Chain from Secret
placeholder: Paste in the client cert chain
sslClientCertKey: Cert Key from Secret
loki:
url: URL
tenant: Tenant
username: User from Secret
password: Password from Secret
configureKubernetesLabels: Configure Kubernetes metadata in a Prometheus like format
extractKubernetesLabels: Extract Kubernetes labels as Loki labels
dropSingleKey: If a record only has 1 key, then just set the log line to the value and discard the key
caCert: CA Cert from Secret
cert: Cert from Secret
key: Key from Secret
awsElasticsearch:
url: URL
keyId: Key ID from Secret
secretKey: Secret Key from Secret
azurestorage:
storageAccount: Account from Secret
accessKey: Access Key from Secret
container: Container
path: Path
storeAs: Store As
cloudwatch:
keyId: Key ID from Secret
secretKey: Secret Key from Secret
endpoint: Endpoint
region: Region
datadog:
apiKey: API Key from Secret
useSSL: Use SSL
useCompression: Use Compression
host: Host
file:
path: Path
gcs:
project: Project
credentialsJson: Credentials from Secret
bucket: Bucket
path: Path
overwriteExistingPath: Overwrite Existing Path
kinesisStream:
streamName: Stream Name
keyId: Key ID from Secret
secretKey: Secret Key from Secret
logdna:
apiKey: API Key
hostname: Hostname
app: App
logz:
url: URL
port: Port
token: Api Token from Secret
enableCompression: Enable Compression
newrelic:
apiKey: API Key from Secret
licenseKey: License Key from Secret
baseURI: Base URI
sumologic:
endpoint: Endpoint from Secret
sourceName: Source Name
syslog:
host: Host
port: Port
transport: Transport
insecure: insecure
trustedCaPath: CA Path from Secret
format:
title: Format
type: Type
addNewLine: Add New Line
messageKey: Message Key
buffer:
title: Buffer
tags: Tags
chunkLimitSize: Chunk Limit Size
chunkLimitRecords: Chunk Limit chunkLimitRecords
totalLimitSize: Total Limit Size
flushInterval: Flush Interval
timekey: Timekey
timekeyWait: Timekey Wait
timekeyUseUTC: Timekey Use UTC
s3:
keyId: Key ID from Secret
secretKey: Secret Key from Secret
endpoint: Endpoint
bucket: Bucket
path: Path
overwriteExistingPath: Overwrite Existing Path
output:
selectOutputs: Select Outputs
selectBanner: Select to configure an output
sections:
target: Target
access: Access
certificate: Connection
labels: Labels
outputProviders:
elasticsearch: Elasticsearch
splunkHec: Splunk
kafka: Kafka
forward: Fluentd
loki: Loki
awsElasticsearch: Amazon Elasticsearch
azurestorage: Azure Storage
cloudwatch: Cloudwatch
datadog: Datadog
file: File
gcs: GCS
kinesisStream: Kinesis Stream
logdna: LogDNA
logz: LogZ
newrelic: New Relic
sumologic: SumoLogic
syslog: Syslog
s3: S3
unknown: Unknown
overview:
poweredBy: Banzai Cloud
clusterLevel: Cluster-Level
namespaceLevel: Namespace-Level
provider: Provider
splunk:
host: Host
port: Port
protocol: Protocol
index: Index
token: Token from Secret
insecureSsl: Insecure SSL
indexName: Index Name
source: Source
caFile: CA File from Secret
caPath: CA Path from Secret
clientCert: Client Cert from Secret
clientKey: Client Key from Secret
forward:
host: Host
port: Port
sharedKey: Shared Key from Secret
username: Username from Secret
password: Password from Secret
clientCertPath: Client Cert Path from Secret
clientPrivateKeyPath: Client Private Key Path from Secret
clientPrivateKeyPassphrase: Client Private Key Passphrase from Secret
longhorn:
overview:
title: Overview
subtitle: "Powered By: Longhorn"
linkedList:
longhorn:
label: 'Longhorn'
description: 'Manage storage system via UI'
na: Resource Unavailable
login:
howdy: Howdy!
welcome: Welcome to {vendor}
loggedOut: You have been logged out.
loginAgain: Log in again to continue.
error: An error occurred logging in. Please try again.
useLocal: Use a local user
loginWithProvider: Log in with {provider}
username: Username
password: Password
loggingIn: Logging in...
loggedIn: Logged in
loginWithLocal: Log in with Local User
useProvider: Use a {provider} user
members:
clusterMembers: Cluster Members
createActionLabel: Add
clusterPermissions:
noDescription: User created - no description
label: Cluster Permissions
description: Controls what access users have to the Cluster
createProjects: Create Projects
manageClusterBackups: Manage Cluster Backups
manageClusterCatalogs: Manage Cluster Catalogs
manageClusterMembers: Manage Cluster Members
manageNodes: Manage Nodes
manageStorage: Manage Storage
viewAllProjects: View All Projects
viewClusterCatalogs: View Cluster Catalogs
viewClusterMembers: View Cluster Members
viewNodes: View Nodes
owner:
label: Owner
description: Owners have full control over the Cluster and all resources inside it.
member:
label: Member
description: Members can manage the resources inside the Cluster but not change the Cluster itself.
custom:
label: Custom
description: Choose individual roles for this user.
monitoring:
accessModes:
many: ReadWriteMany
once: ReadWriteOnce
readOnlyMany: ReadOnlyMany
aggregateDefaultRoles:
label: Aggregate to Default Kubernetes Roles
tip: 'Adds labels to the ClusterRoles deployed by the Monitoring chart to aggregate to the corresponding default k8s admin, edit, and view ClusterRoles.'
alerting:
config:
label: Alert Manager Config
enable:
label: Deploy Alertmanager
secrets:
additional:
info: Secrets should be mounted at
/etc/alertmanager/secrets/
label: Additional Secrets
existing: Choose an existing config secret
info: |
Create default config: A Secret containing your Alertmanager Config will be created in the
cattle-monitoring-system
namespace on deploying this chart under the name
alertmanager-rancher-monitoring-alertmanager
. By default, this Secret will never be modified on an uninstall or upgrade of this chart.
Once you have deployed this chart, you should edit the Secret via the UI in order to add your custom notification configurations that will be used by Alertmanager to send alerts.
Choose an existing config secret: You must specify a Secret that exists in the
cattle-monitoring-system
namespace. If the namespace does not exist, you will not be able to select an existing secret.
label: Alertmanager Secret
new: Create default config
radio:
label: Config Secret
templates:
keyLabel: File Name
label: Template Files
valueLabel: YAML Template
title: Configure Alertmanager
clusterType:
label: Cluster Type
placeholder: Select cluster type
createDefaultRoles:
label: Create Default Monitoring Cluster Roles
tip: 'Creates monitoring-admin, monitoring-edit, and monitoring-view ClusterRoles that can be assigned to users to provide permissions to CRDs installed by the Monitoring chart.'
etcdNodeDirectory:
label: etcd Node Certificate Directory
tooltip: 'For clusters that use RancherOS for the etcd nodes, this option should be set to
/opt/rke/etc/kubernetes/ssl
. Hybrid environments that require specifying multiple certificate directories (e.g. an etcd plane composed of both RancherOS and Ubuntu hosts) are not supported.'
grafana:
storage:
annotations: PVC Annotations
className: Storage Class Name
existingClaim: Use Existing Claim
finalizers: PVC Finalizers
label: Grafana Storage
mode: Access Mode
selector: Selector
size: Size
subpath: Use Subpath
type: Persistent Storage Types
types:
existing: Enable With Existing PVC
statefulset: Enable with StatefulSet Template
template: Enable with PVC Template
volumeMode: Volume Mode
volumeName: Volume Name
title: Configure Grafana
hostNetwork:
label: Use Host Network For Prometheus Operator
tip: If you are using a managed Kubernetes cluster with custom CNI (e.g. Calico), you must enable this option to allow a managed control plane to contact the admission webhook exposed by Prometheus Operator to mutate or validate incoming PrometheusRules.
overview:
alertsList:
ends:
label: Ends At
label: Active Alerts
message:
label: Message
severity:
label: Severity
start:
label: Starts At
linkedList:
alertManager:
description: Active Alerts
label: Alertmanager
grafana:
description: Metrics Dashboards
label: Grafana
na: Resource Unavailable
prometheusPromQl:
description: PromQL Graph
label: Prometheus Graph
prometheusRules:
description: Configured Rules
label: Prometheus Rules
prometheusTargets:
description: Configured Targets
label: Prometheus Targets
subtitle: 'Powered By: Prometheus'
title: Dashboard
prometheus:
config:
adminApi: Admin API
evaluation: Evaluation Interval
ignoreNamespaceSelectors:
help: 'Ignoring Namespace Selectors allows Cluster Admins to limit teams from monitoring resources outside of namespaces they have permissions to but can break the functionality of Apps that rely on setting up Monitors that scrape targets across multiple namespaces, such as Istio.'
label: Namespace Selectors
radio:
enforced: 'Use: Monitors can access resources based on namespaces that match the namespace selector field'
ignored: 'Ignore: Monitors can only access resources in the namespace they are deployed in'
limits:
cpu: CPU Limit
memory: Memory Limit
requests:
cpu: Requested CPU
memory: Requested Memory
resourceLimits: Resource Limits
retention: Retention
retentionSize: Retention Size
scrape: Scrape Interval
storage:
className: Storage Class Name
label: Persistent Storage for Prometheus
mode: Access Mode
selector: Selector
selectorWarning: 'If you are using a dynamic provisioner (e.g. Longhorn), no Selectors should be specified since a PVC with a non-empty selector can''t have a PV dynamically provisioned for it.'
size: Size
volumeMode: Volume Mode
volumeName: Volume Name
title: Configure Prometheus
warningInstalled: |
Warning: Prometheus Operators are currently deployed. Deploying multiple Prometheus Operators onto one cluster is not currently supported. Please remove all other Prometheus Operator deployments from this cluster before trying to install this chart.
If you are migrating from an older version of {vendor} with Monitoring enabled, please disable Monitoring on this cluster completely before attempting to install this chart.
receiver:
fields:
name: Name
tls:
label: SSL
caFilePath:
label: CA File Path
placeholder: e.g. ./ca-file.csr
certFilePath:
label: Cert File Path
placeholder: e.g. ./cert-file.crt
keyFilePath:
label: Key File Path
placeholder: e.g. ./key-file.pfx
secretsBanner: The file paths below must be referenced in
alertmanager.alertmanagerSpec.secrets
when deploying the Monitoring chart. For more information see our documentation.
route:
fields:
groupBy: Group By
groupInterval: Group Interval
groupWait: Group Wait
receiver: Receiver
repeatInterval: Repeat Interval
routesAndReceivers: Routes and Receivers
monitors: Monitors
installSteps:
uninstallV1:
stepTitle: Uninstall V1
stepSubtext: Uninstall Previous Monitoring
warning1: V1 Monitoring is currently deployed. This needs to be uninstalled before V2 monitoring can be installed.
warning2: Learn more about migrating to V2 Monitoring.
success1: V1 monitoring successfully uninstalled.
success2: Press Next to continue
tabs:
alerting: Alerting
general: General
grafana: Grafana
prometheus: Prometheus
v1Warning: 'Monitoring is currently deployed from Cluster Manager. If you are migrating from an older version of {vendor} with monitoring enabled, please disable monitoring in Cluster Manager before attempting to install the new {vendor} Monitoring chart in Cluster Explorer.'
volume:
modes:
block: Block
file: Filesystem
monitoringReceiver:
addButton: Add {type}
custom:
label: Custom
title: Custom Config
info: The YAML provided here will be directly appended to your receiver within the Alertmanager Config Secret.
email:
label: Email
title: Email Config
opsgenie:
label: Opsgenie
title: Opsgenie Config
pagerduty:
label: PagerDuty
title: PagerDuty Config
info: "See additional info on creating an Integration Key for PagerDuty."
slack:
label: Slack
title: Slack Config
info: "See additional info on creating Incoming Webhooks for Slack ."
webhook:
label: Webhook
title: Webhook Config
urlTooltip: For some webhooks this a url that points to the service DNS
modifyNamespace: If
rancher-alerting-drivers
default values were changed, please update the url below in the format http://<new_service_name>.<new_namespace>.svc.<port>/<path>
banner: To use MS Teams or SMS you will need to have
rancher-alerting-drivers
installed first.
add:
generic: Generic
msTeams: MS Teams
alibabaCloudSms: SMS
auth:
label: Auth
authType: Auth Type
username: Username
password: Password
none:
label: None
bearerToken:
label: Bearer Token
placeholder: e.g. secret-token
basicAuth:
label: Basic Auth
bearerTokenFile:
label: Bearer Token File
placeholder: e.g. ./user_token
shared:
proxyUrl:
label: Proxy URL
placeholder: e.g. http://my-proxy/
sendResolved:
label: Enable send resolved alerts
monitoringRoute:
groups:
label: Group By
info: This is the top-level Route used by Alertmanager as the default destination for any Alerts that do not match any other Routes. This Route must exist and cannot be deleted.
interval:
label: Group Interval
matching:
info: The root route has to match everything so matching cannot be configured.
label: Match
receiver:
label: Receiver
regex:
label: Match Regex
repeatInterval:
label: Repeat Interval
wait:
label: Group Wait
moveModal:
title: Move to a new project?
description: 'You are moving the following namespaces:'
moveButtonLabel: Move
nameNsDescription:
name:
label: Name
placeholder: 'A unique name'
namespace:
label: Namespace
placeholder:
workspace:
label: Workspace
placeholder:
description:
label: Description
placeholder: Any text you want that better describes this resource
namespace:
containerResourceLimit: Container Resource Limit
project:
label: Project
resources: Resources
enableAutoInjection: Enable Istio Auto Injection
disableAutoInjection: Disable Istio Auto Injection
move: Move
namespaceFilter:
selected:
label: "{total} items selected"
namespaceList:
selectLabel: Namespace
addLabel: Add Namespace
node:
detail:
detailTop:
containerRuntime: Container Runtime
internalIP: Internal IP
externalIP: External IP
os: OS
version: Version
glance:
consumptionGauge:
used: Used
amount: "{used} of {total} {unit}"
cpu: CPU
memory: MEMORY
pods: PODS
diskPressure: Disk Pressure
kubelet: kubelet
memoryPressure: Memory Pressure
pidPressure: PID Pressure
tab:
conditions: Conditions
images: Images
metrics: Metrics
info:
label: Info
key:
architecture: Architecture
bootID: Boot ID
containerRuntimeVersion: Container Runtime Version
kernelVersion: Kernel Version
kubeProxyVersion: Kube Proxy Version
kubeletVersion: Kubelet Version
machineID: Machine ID
operatingSystem: Operating System
osImage: Image
systemUUID: System UUID
pods: Pods
taints: Taints
persistentVolume:
pluginConfiguration:
label: Plugin configuration
customize:
label: Customize
affinity:
label: Node Selectors
addLabel: Add Node Selector
assignToStorageClass:
label: Assign to Storage Class
mountOptions:
label: Mount Options
addLabel: Add Option
accessModes:
label: Access Modes
readWriteOnce: Single Node Read-Write
readOnlyMany: Many Nodes Read-Only
readWriteMany: Many Nodes Read-Write
shared:
partition:
label: Partition
placeholder: e.g. 1; 0 for entire device
readOnly:
label: Read Only
filesystemType:
label: Filesystem Type
placeholder: e.g. ext4
secretName:
label: Secret Name
placeholder: e.g. secret
secretNamespace:
label: Secret Namespace
placeholder: e.g. default
monitors:
add: Add Monitor
vsphereVolume:
label: VMWare vSphere Volume
volumePath:
label: Volume Path
placeholder: e.g. /
storagePolicyName:
label: Storage Policy Name
placeholder: e.g. sp
storagePolicyId:
label: Storage Policy ID
placeholder: e.g. sp1
csi:
label: CSI (Unsupported)
driver:
label: Driver
placeholder: e.g. driver.longhorn.io
volumeHandle:
label: Volume Handle
placeholder: e.g. pvc-xxxx
volumeAttributes:
add: Add Volume Attribute
nodePublishSecretName:
label: Node Publish Secret Name
placeholder: e.g. secret
nodePublishSecretNamespace:
label: Node Publish Secret Namespace
placeholder: e.g. default
nodeStageSecretName:
label: Node Stage Secret Name
placeholder: e.g. secret
nodeStageSecretNamespace:
label: Node Stage Secret Namespace
placeholder: e.g. default
controllerExpandSecretName:
label: Controller Expand Secret Name
placeholder: e.g. secret
controllerExpandSecretNamespace:
label: Controller Expand Secret Namespace
placeholder: e.g. default
controllerPublishSecretName:
label: Controller Publish Secret Name
placeholder: e.g. secret
controllerPublishSecretNamespace:
label: Controller Publish Secret Namespace
placeholder: e.g. default
cephfs:
label: Ceph Filesystem (Unsupported)
path:
label: Path
placeholder: e.g. /var
user:
label: User
placeholder: e.g. root
secretFile:
label: Secret File
placeholder: e.g. secret
rbd:
label: Ceph RBD (Unsupported)
user:
label: User
placeholder: e.g. root
keyRing:
label: Key Ring
placeholder: e.g. /etc/ceph/keyring
pool:
label: Pool
placeholder: e.g. rbd
image:
label: Image
placeholder: e.g. image
fc:
label: Fibre Channel (Unsupported)
targetWWNS:
add: Add Target WWN
wwids:
add: Add WWID
lun:
label: Lun
placeholder: e.g. 2
flexVolume:
label: Flex Volume (Unsupported)
driver:
label: Driver
placeholder: e.g. driver
options:
add: Add Option
flocker:
label: Flocker (Unsupported)
datasetName:
label: Dataset Name
placeholder: e.g. dataset
datasetUUID:
label: Dataset UUID
placeholder: e.g. uuid
glusterfs:
label: Gluster Volume (Unsupported)
endpoints:
label: Endpoints
placeholder: e.g. glusterfs-cluster
path:
label: Path
placeholder: e.g. kube-vol
iscsi:
label: iSCSI Target (Unsupported)
initiatorName:
label: Initiator Name
placeholder: iqn.1994-05.com.redhat:1df7a24fcb92
iscsiInterface:
label: iSCSI Interface
placeholder: e.g. interface
chapAuthDiscovery:
label: Chap Auth Discovery
chapAuthSession:
label: Chap Auth Session
iqn:
label: IQN
placeholder: iqn.2001-04.com.example:storage.kube.sys1.xyz
lun:
label: Lun
placeholder: e.g. 2
targetPortal:
label: Target Portal
placeholder: e.g. portal
portals:
add: Add Portal
cinder:
label: Openstack Cinder Volume (Unsupported)
volumeId:
label: Volume ID
placeholder: e.g. vol
quobyte:
label: Quobyte Volume (Unsupported)
volume:
label: Volume
placeholder: e.g. vol
user:
label: User
placeholder: e.g. root
group:
label: Group
placeholder: e.g. abc
registry:
label: Registry
placeholder: e.g. abc
photonPersistentDisk:
label: Photon Volume (Unsupported)
pdId:
label: PD ID
placeholder: e.g. abc
portworxVolume:
label: Portworx Volume (Unsupported)
volumeId:
label: Volume ID
placeholder: e.g. abc
scaleIO:
label: ScaleIO Volume (Unsupported)
volumeName:
label: Volume Name
placeholder: e.g. vol-0
gateway:
label: Gateway
placeholder: e.g. https://localhost:443/api
protectionDomain:
label: Protection Domain
placeholder: e.g. pd01
storageMode:
label: Storage Mode
placeholder: e.g. ThinProvisioned
storagePool:
label: Storage Pool
placeholder: e.g. sp01
system:
label: System
placeholder: e.g. scaleio
sslEnabled:
label: SSL Enabled
storageos:
label: StorageOS (Unsupported)
volumeName:
label: Volume Name
placeholder: e.g. vol
volumeNamespace:
label: Volume Namespace
placeholder: e.g. default
nfs:
label: NFS Share
path:
label: Path
placeholder: e.g. /var
server:
label: Server
placeholder: e.g. 10.244.1.4
longhorn:
label: Longhorn
volumeHandle:
label: Volume Handle
placeholder: e.g. pvc-xxxx
options:
label: Options
addLabel: Add
local:
label: Local
path:
label: Path
placeholder: e.g. /mnt/disks/ssd1
hostPath:
label: HostPath
pathOnTheNode:
label: Path on the Node
placeholder: /mnt/disks/ssd1
mustBe:
label: The Path on the Node must be
anything: 'Anything: do not check the target path'
directory: A directory, or create if it does not exist
file: A file, or create if it does not exist
existingDirectory: An existing directory
existingFile: An existing file
existingSocket: An existing socket
existingCharacter: An existing character device
existingBlock: An existing block device
gcePersistentDisk:
label: Google Persistent Disk
persistentDiskName:
label: Persistent Disk Name
placeholder: e.g. abc
awsElasticBlockStore:
label: Amazon EBS Disk
volumeId:
label: Volume ID
placeholder: e.g. volume1
azureFile:
label: Azure Filesystem
shareName:
label: Share Name
placeholder: e.g. abc
azureDisk:
label: Azure Disk
diskName:
label: Disk Name
placeholder: e.g. kubernetes-pvc
diskURI:
label: Disk URI
placeholder: e.g. https://example.com/disk
kind:
label: Kind
dedicated: Dedicated
managed: Managed
shared: Shared
cachingMode:
label: Caching Mode
none: None
readOnly: Read Only
readWrite: Read Write
filesystemType:
label: Filesystem Type
placeholder: e.g. ext4
readOnly:
label: Read Only
persistentVolumeClaim:
accessModes: Access Modes
capacity: Capacity
storageClass: Storage Class
useDefault: Use the default class
volumes: Persistent Volumes
volumeName: Persistent Volume Name
source:
label: Source
options:
new: Use a Storage Class to provision a new Persistent Volume
existing: Use an existing Persistent Volume
volumeClaim:
label: Volume Claim
storageClass: Storage Class
requestStorage: Request Storage
persistentVolume: Persistent Volume
customize:
label: Customize
accessModes:
readWriteOnce: Single Node Read-Write
readOnlyMany: Many Nodes Read-Only
readWriteMany: Many Nodes Read-Write
status:
label: Status
prefs:
title: Preferences
theme:
label: Theme
light: Light
auto: Auto
dark: Dark
autoDetail: Auto uses OS preference if available, or dark from {pm} to {am}
landing:
label: Login Landing Page
vue: Cluster Explorer
ember: Cluster Manager
formatting: Formatting
clusterToShow:
label: Number of clusters to show in side menu
value: |-
{count, number}
dateFormat:
label: Date Format
timeFormat:
label: Time Format
perPage:
label: Table Rows per Page
value: |-
{count, number}
keymap:
label: YAML Editor Key Mapping
sublime: 'Normal human'
emacs: 'Emacs'
vim: 'Vim'
advanced: Advanced
dev:
label: Enable Developer Tools & Features
hideDesc:
label: Hide All Type Description Boxes
helm:
'true': Include Prerelease Versions
'false': Show Releases Only
label: Helm Charts
experimental: Experimental
onlyFromVentura_x64: This setting requires macOS 13.0 (Ventura) or later.
onlyFromVentura_arm64: This setting requires macOS 13.3 (Ventura) or later.
onlyWithVZ_x64: This setting requires using the VZ emulation mode. VZ is only available on macOS 13.0 (Ventura) or later.
onlyWithVZ_arm64: This setting requires using the VZ emulation mode. VZ is only available on macOS 13.3 (Ventura) or later.
principal:
loading: Loading…
error: Unable to fetch principal info
name: Name
loginName: Username
type: Type
probe:
checkInterval:
label: Check Interval
placeholder: 'Default: 10'
command:
label: Command to run
placeholder: e.g. cat /tmp/health
failureThreshold:
label: Failure Threshold
placeholder: 'Default: 3'
httpGet:
headers:
label: Request Headers
path:
label: Request Path
placeholder: e.g. /healthz
port:
label: Check Port
placeholder: e.g. 80
placeholderDuex: e.g. 25
initialDelay:
label: Initial Delay
placeholder: 'Default: 0'
successThreshold:
label: Success Threshold
placeholder: 'Default: 1'
timeout:
label: Timeout
placeholder: 'Default: 3'
type:
label: Type
placeholder: Select a check type
project:
containerDefaultResourceLimit: Container Default Resource Limit
resourceQuotas: Resource Quotas
projectNamespaces:
createNamespace: Create Namespace
createProject: Create Project
label: Projects/Namespaces
noNamespaces: There are no namespaces defined.
prometheusRule:
alertingRules:
addLabel: Add Alert
annotations:
description:
input: Description Annotation Value
label: Description
label: Annotations
message:
input: Message Annotation Value
label: Message
runbook:
input: Runbook URL Annotation Value
label: Runbook URL
summary:
input: Summary Annotation Value
label: Summary
bannerText: 'When firing alerts, the annotations and labels will be passed to the configured AlertManagers to allow them to construct the notification that will be sent to any configured Receivers.'
for:
label: Wait to fire for
placeholder: '60'
label: Alerting Rules
labels:
label: Labels
severity:
choices:
critical: critical
label: Severity Label Value
none: none
warning: warning
label: Severity
name: Alert Name
removeAlert: Remove Alert
groups:
add: Add Rule Group
groupRowLabel: Rule Group {index}
groupInterval:
label: Override Group Interval
placeholder: '60'
label: Rule Groups
name: Group Name
none: Please add at least one rule group that contains at least one alerting or one recording rule.
removeGroup: Remove Group
responseStrategy:
label: Partial Response Strategy
promQL:
label: PromQL Expression
recordingRules:
addLabel: Add Record
label: Recording Rules
labels: Labels
name: Time Series Name
removeRecord: Remove Record
promptRemove:
andOthers: |-
{count, plural,
=0 {.}
=1 { and one other.}
other { and {count} others.}
}
attemptingToRemove: "You are attempting to delete the {type}"
protip: "Tip: Hold the {alternateLabel} key while clicking delete to bypass this confirmation"
confirmName: "Enter {nameToMatch} below to confirm:"
promptRestore:
title: Restore Snapshot
promptSaveAsRKETemplate:
title: Create RKE Template from {cluster}
name: Cluster Template Name
description: Create a new RKE cluster template and initial revision from the current cluster configuration.
warning: This will modify the cluster, setting it up to use the newly created cluster template and revision.
rancherAlertingDrivers:
msTeams: Enable Microsoft Teams
sms: Enable SMS
selectOne: You must select at least one of the options below.
rbac:
roleBinding:
noData: There are no members associated with this resource.
user:
label: User
role:
label: Role
add: Add Member
displayRole:
fleetworkspace-admin: Admin
fleetworkspace-member: Member
fleetworkspace-readonly: Read-Only
members:
label: Members
roletemplate:
label: Roles
newUserDefault:
no: No
tooltip: This does not affect any bindings to the role that already exist.
locked:
label: Locked
yes: 'Yes: New bindings are not allowed to use this role'
no: No
tabs:
grantResources:
label: Grant Resources
tableHeaders:
verbs: Verbs
resources: Resource
nonResourceUrls: Non-Resource URLs
apiGroups: API Groups
subtypes:
GLOBAL:
createButton: Create Global Role
label: Global
yes: "Yes: Default role for new users"
defaultLabel: New User Default
CLUSTER:
createButton: Create Cluster Role
label: Cluster
yes: "Yes: Default role for new cluster creation"
defaultLabel: Cluster Creator Default
NAMESPACE:
createButton: Create Project/Namespaces Role
label: Project/Namespaces
yes: "Yes: Default role for new project creation"
defaultLabel: Project Creator Default
RBAC_ROLE:
label: Role
RBAC_CLUSTER_ROLE:
label: Cluster Role
noContext:
label: No Context
globalRoles:
types:
global:
label: Global Permissions
description: |-
Controls what access the {isUser, select,
true {user}
false {group}} has to administer the overall {appName} installation.
custom:
label: Custom
description: 'Roles not created by {vendor}.'
builtin:
label: Built-in
description: Additional roles to define more fine-grain permissions model.
unknownRole:
description: No description provided
assignOnlyRole: This role is already assigned
role:
admin:
label: Administrator
description: Administrators have full control over the entire installation and all resources in all clusters.
restricted-admin:
label: Restricted Administrator
description: Restricted Admins have full control over all resources in all downstream clusters but no access to the local cluster.
user:
label: Standard User
description: Standard Users can create new clusters and manage clusters and projects they have been granted access to.
user-base:
label: User-Base
description: User-Base users have login-access only.
clusters-create:
label: Create new Clusters
description: Allows the user to create new clusters and become the owner of them. Standard Users have this permission by default.
clustertemplates-create:
label: Create new RKE Cluster Templates
description: Allows the user to create new RKE cluster templates and become the owner of them.
authn-manage:
label: Configure Authentication
description: Allows the user to enable, configure, and disable all Authentication provider settings.
catalogs-manage:
label: Configure Catalogs
description: Allows the user to add, edit, and remove Catalogs.
clusters-manage:
label: Manage all Clusters
description: Allows the user to manage all clusters, including ones they are not a member of.
clusterscans-manage:
label: Manage CIS Cluster Scans
description: Allows the user to launch new and manage CIS cluster scans.
kontainerdrivers-manage:
label: Create new Cluster Drivers
description: Allows the user to create new cluster drivers and become the owner of them.
features-manage:
label: Configure Feature Flags
description: Allows the user to enable and disable custom features via feature flag settings.
nodedrivers-manage:
label: Configure Node Drivers
description: Allows the user to enable, configure, and remove all Node Driver settings.
nodetemplates-manage:
label: Manage Node Templates
description: Allows the user to define, edit, and remove Node Templates.
podsecuritypolicytemplates-manage:
label: Manage Pod Security Policies (PSPs)
description: Allows the user to define, edit, and remove PSPs.
roles-manage:
label: Manage Roles
description: Allows the user to define, edit, and remove Role definitions.
settings-manage:
label: Manage Settings
description: 'Allows the user to manage {vendor} Settings.'
users-manage:
label: Manage Users
description: Allows the user to create, remove, and set passwords for all Users.
catalogs-use:
label: Use Catalogs
description: Allows the user to see and deploy Templates from the Catalog. Standard Users have this permission by default.
nodetemplates-use:
label: Use Node Templates
description: Allows the user to deploy new Nodes using any existing Node Templates.
view-rancher-metrics:
label: 'View {vendor} Metrics'
description: Allows the user to view Metrics through the API.
base:
label: Login Access
resourceDetail:
detailTop:
annotations: Annotations
created: Created
deleted: Deleted
description: Description
labels: Labels
ownerReferences: |-
{count, plural,
=1 {Owner}
other {Owners}}
hideAnnotations: |-
{annotations, plural,
=1 {Hide 1 annotation}
other {Hide {annotations} annotations}}
showAnnotations: |-
{annotations, plural,
=1 {Show 1 annotation}
other {Show {annotations} annotations}}
name: Name
header:
clone: "Clone from {subtype} {name}"
create: Create {subtype}
import: Import {subtype}
edit: "{subtype} {name}"
stage: "Stage from {subtype} {name}"
view: "{subtype} {name}"
masthead:
age: Age
defaultBannerMessage:
error: This resource is currently in an error state, but there isn't a detailed message available.
transitioning: This resource is currently in a transitioning state, but there isn't a detailed message available.
sensitive:
hide: Hide Sensitive Values
show: Show Sensitive Values
namespace: Namespace
workspace: Workspace
project: Project
detail: Detail
config: Config
yaml: YAML
managedWarning: |-
This {type} is managed by {hasName, select,
no {a {managedBy} app}
yes {the {managedBy} app {appName}}}; changes made here will likely be overwritten the next time the app is changed.
resourceList:
head:
create: Create
createFromYaml: Create from YAML
createResource: "Create {resourceName}"
resourceTable:
groupBy:
none: Flat List
namespace: Group by Namespace
project: Group by Project
groupLabel:
cluster: "Cluster: {name}"
namespace: "Namespace: {name}"
machinePool: "Machine Pool: {name}"
notInANamespace: Not Namespaced
notInAProject: Not in a Project
project: "Project: {name}"
notInAWorkspace: Not in a Workspace
workspace: "Workspace: {name}"
resourceTabs:
conditions:
tab: Conditions
events:
tab: Recent Events
related:
tab: Related Resources
from: Referred To By
to: Refers To
resourceYaml:
errors:
namespaceRequired: This resource is namespaced, so a namespace must be provided.
buttons:
continue: Continue Editing
edit: Edit YAML
diff: Show Diff
unified: Unified
split: Split
rioConfig:
configure:
description: Description
helpText:
listItem1: The application deployment engine for Kubernetes.
listItem2: "Rio makes it faster and easier for DevOps to build, test, deploy, scale and version stateless applications"
requirements:
header: Requirements
helpText:
listItem1: 1 CPU Core
listItem2: 2 GiB of Memory
header: Rio
yaml:
buttonText: Customize
secret:
authentication: Authentication
certificate:
certificate: Certificate
cn: Domain Name
expires: Expires
issuer: Issuer
plusMore: "+ {n} more"
privateKey: Private Key
data: Data
registry:
address: Registry
domainName: Registry Domain Name
password: Password
username: Username
basic:
password: Password
username: Username
ssh:
keys: Keys
public: Public Key
private: Private Key
serviceAcct:
ca: CA Certificate
token: Token
type: Type
types:
'opaque': 'Opaque'
'kubernetes.io/service-account-token': 'Svc Acct Token'
'kubernetes.io/dockercfg': 'Registry'
'kubernetes.io/dockerconfigjson': 'Registry'
'kubernetes.io/basic-auth': 'HTTP Basic Auth'
'kubernetes.io/ssh-auth': 'SSH Key'
'kubernetes.io/tls': 'TLS Certificate'
'bootstrap.kubernetes.io/token': 'Bootstrap Token'
'istio.io/key-and-cert': 'Istio Certificate'
'helm.sh/release.v1': 'Helm Release'
'fleet.cattle.io/cluster-registration-values': 'Fleet Cluster'
'provisioning.cattle.io/cloud-credential': 'Cloud Credential'
initials:
'opaque': 'O'
'kubernetes.io/service-account-token': 'SAT'
'kubernetes.io/dockercfg': 'R'
'kubernetes.io/dockerconfigjson': 'R'
'kubernetes.io/basic-auth': 'HTTP'
'kubernetes.io/ssh-auth': 'SSH'
'kubernetes.io/tls': 'TLS'
'bootstrap.kubernetes.io/token': 'Boot'
'istio.io/key-and-cert': 'Ist'
'helm.sh/release.v1': 'Helm'
'fleet.cattle.io/cluster-registration-values': 'F'
'provisioning.cattle.io/cloud-credential': 'CC'
relatedWorkloads: Related Workloads
selectOrCreateAuthSecret:
label: Authentication
options:
none: None
basic: HTTP Basic Auth
ssh: SSH Key
aws: AWS/S3
custom: Secret Name
aws:
accessKey: Access Key
secretKey: Secret Key
ssh:
publicKey: Public Key
privateKey: Private Key
basic:
username: Username
password: Password
namespaceGroup: "Namespace: {name}"
chooseExisting: "Choose an existing secret:"
createSsh: Create a SSH Key Secret
createBasic: Create a HTTP Basic Auth Secret
createAws: Create an AWS/S3 Auth Secret
servicePorts:
header:
label: Port Rules
rules:
listening:
label: Listening Port
placeholder: e.g. 8080
name:
label: Port Name
placeholder: e.g. myport
node:
label: Node Port
placeholder: e.g. 30000
protocol:
label: Protocol
target:
label: Target Port
placeholder: e.g. 80 or http
serviceTypes:
clusterip: Cluster IP
externalname: External Name
headless: Headless
loadbalancer: Load Balancer
nodeport: Node Port
servicesPage:
anyNode: Any Node
labelsAnnotations:
label: Labels & Annotations
affinity:
actionLabels:
clientIp: ClientIP
none: There is no session affinity configured.
helpText: Map connections to a consistent target based on their source IP.
label: Session Affinity
timeout:
label: Session Sticky Time
placeholder: e.g. 10800
externalName:
define: External Name
helpText: "External Name is intended to specify a canonical DNS name. This is a required field. To hardcode an IP address, use a Headless service."
label: External Name
placeholder: e.g. my.database.example.com
input:
label: DNS Name
ips:
define: Service Ports
clusterIpHelpText: The Cluster IP address must be within the CIDR range configured for the API server.
external:
label: External IPs
placeholder: e.g. 1.1.1.1
protip: List of IP addresses for which nodes in the cluster will also accept traffic for this service.
input:
label: Cluster IP
placeholder: e.g. 10.43.xxx.xxx
label: IP Addresses
pods:
label: Pods
ports:
label: Ports
selectors:
helpText: ""
label: Selectors
matchingPods:
matchesSome: |-
{matched, plural,
=0 {Matches 0 of {total, number} pods. If no selector is created, manual endpoints must be made.}
=1 {Matches 1 of {total, number} pods: "{sample}"}
other {Matches {matched, number} of {total, number} existing pods, including "{sample}"}
}
serviceTypes:
clusterIp:
abbrv: IP
description: Exposes the service on a cluster-internal IP. Choosing this value makes the service only reachable from within the cluster. This is the default type.
label: Cluster IP
externalName:
abbrv: EN
description: "Maps the service to the contents of the `externalName` field (e.g. foo.bar.example.com), by returning a CNAME record with its value. No proxying of any kind is set up."
label: External Name
headless:
abbrv: H
description: Neither a cluster IP or load balancer is defined. These are used to interface with other service discovery mechanisms outside of Kubernetes implementation. A cluster IP is not allocated and kube-proxy does not handle these services.
label: Headless
loadBalancer:
abbrv: LB
description: Exposes the service externally using a cloud provider's load balancer.
label: Load Balancer
nodePort:
abbrv: NP
description: "Exposes the service on each node's IP at a static port (the `NodePort`). You'll be able to contact this type of service, from outside the cluster, by requesting `:`."
label: Node Port
typeOpts:
label: Service Type
setup:
welcome: Welcome to {vendor}!
setPassword: The first order of business is to set a strong password for the default admin user. We suggest using this random one generated just for you, but enter your own if you like.
newPassword: New Password
confirmPassword: Confirm New Password
useRandom: Use a randomly generated password
useManual: Set a specific password to use
defaultPasswordError: It looks like this is your first time visiting the Rancher UI, but the local admin account password is already set to something unique. Log in with that account below to continue the setup process.
telemetry:
label: Allow collection of anonymous statistics to help us improve Rancher
tip: 'Rancher Labs would like to collect a bit of anonymized information
about the configuration of your installation to help make Rio better.
Your data will not be shared with anyone else, and no information about
what specific resources or endpoints you are deploying is included.
Once enabled you can view exactly what data will be sent at /v1-telemetry.
More Info'
eula: I agree to the terms and conditions for using Rancher.
serverUrl:
label: Server URL
tip: What URL should be used for this Rancher installation? All the nodes in your clusters will need to be able to reach this. You can skip setting this for now, and update it later in General Settings>Advanced Settings.
skip: Skip
sortableTable:
bulkActions:
collapsed:
label: Actions
actionAvailability:
selected: "{actionable} selected"
some: "Available for {actionable} of the {total} selected"
noData: There are no rows which match your search query.
noRows: There are no rows to show.
noActions: No actions available
paging:
generic: |-
{pages, plural,
=0 {No Items}
=1 {{count} {count, plural, =1 {Item} other {Items}}}
other {{from} - {to} of {count} Items}}
resource: |-
{pages, plural,
=0 {No {pluralLabel}}
=1 {{count} {count, plural, =1 {{singularLabel}} other {{pluralLabel}}}}
other {{from} - {to} of {count} {pluralLabel}}}
search: Filter
in: in
addFilter: Add Filter
filterFor: Filter for...
selectCol: Select a column
resetFilters: Reset
add: Add
tableHeader:
noFilter: This column cannot be filtered by
groupBy: Group by
show: Show
storageClass:
actions:
setAsDefault: Set as Default
resetDefault: Reset Default
parameters:
label: Parameters
customize:
label: Customize
reclaimPolicy:
label: Reclaim Policy
delete: Delete volumes and underlying device when volume claim is deleted
retain: Retain the volume for manual cleanup
allowVolumeExpansion:
label: Allow Volume Expansion
enabled: Enabled
disabled: Disabled
volumeBindingMode:
label: Volume Binding Mode
now: Bind and provision a persistent volume once the PersistentVolumeClaim is created
later: Bind and provision a persistent volume once a Pod using the PersistentVolumeClaim is created
mountOptions:
label: Mount Options
addlabel: Add Option
aws-ebs:
title: Amazon EBS Disk
volumeType:
label: Volume Type
gp2: GP2 - General Purpose SSD
io1: IO1 - Provisioned IOPS SSD
st1: ST1 - Throughput-Optimized HDD
sc1: SC1 - Cold-Storage HDD
provisionedIops:
label: Provisioned IOPS
suffix: per second, per GB
filesystemType:
label: Filesystem Type
placeholder: e.g. ext4
availabilityZone:
label: Availability Zone
automatic: 'Automatic: Zones the cluster has a node in'
manual: 'Manual: Choose specific zones'
placeholder: us-east-1d, us-east-1c
encryption:
label: Encryption
enabled: Enabled
disabled: Disabled
keyId:
label: KMS Key ID for Encryption
automatic: 'Automatic: Generate a key'
manual: 'Manual: Use a specific key (full ARN)'
azure-disk:
title: Azure Disk
storageAccountType:
label: Storage Account Type
placeholder: e.g. Standard_LRS
kind:
label: Kind
shared: Shared (unmanaged disk)
dedicated: Dedicated (unmanaged disk)
managed: Managed
azure-file:
title: Azure File
skuName:
label: Sku Name
placeholder: e.g. Standard_LRS
location:
label: Location
placeholder: e.g. eastus
storageAccount:
label: Storage Account
placeholder: e.g. azure_storage_account_name
gce-pd:
title: Google Persistent Disk
volumeType:
label: Volume Type
standard: Standard
ssd: SSD
filesystemType:
label: Filesystem Type
placeholder: e.g. ext4
availabilityZone:
label: Availability Zone
automatic: 'Automatic: Zones the cluster has a node in'
manual: 'Manual: Choose specific zones'
placeholder: us-east-1d, us-east-1c
replicationType:
label: Replication Type
zonal: Zonal
regional: Regional
longhorn:
title: Longhorn
addLabel: Add Parameter
vsphere-volume:
title: VMWare vSphere Volume
diskFormat:
label: Disk Format
thin: Thin
zeroedthick: Zeroed Thick
eagerzeroedthick: Eager Zeroed Thick
storagePolicyName:
label: Storage Policy Name
placeholder: e.g. gold
datastore:
label: Datastore
placeholder: e.g. VSANDatastore
hostFailuresToTolerate:
label: Host Failures To Tolerate
placeholder: e.g. 2
cacheReservation:
label: Cache Reservation
placeholder: e.g. 20
filesystemType:
label: Filesystem Type
placeholder: e.g. ext3
custom:
addLabel: Add Parameter
glusterfs:
title: Gluster Volume (Unsupported)
restUrl:
label: REST URL
placeholder: e.g. http://127.0.0.1:8081
restUser:
label: REST User
placeholder: e.g. admin
restUserKey:
label: REST User Key
placeholder: e.g. password
secretNamespace:
label: Secret Namespace
placeholder: e.g. default
secretName:
label: Secret Name
placeholder: e.g. heketi-secret
clusterId:
label: Cluster ID
placeholder: e.g. 630372ccdc720a92c681fb928f27b53f
gidMin:
label: GID MIN
placeholder: e.g. 40000
gidMax:
label: GID MAX
placeholder: e.g. 50000
volumeType:
label: Volume Type
placeholder: "e.g. replicate:3"
cinder:
title: Openstack Cinder Volume (Unsupported)
volumeType:
label: Volume Type
placeholder: e.g. fast
availabilityZone:
label: Availability Zone
automatic: "Automatic: Zones the cluster has a node in"
manual:
label: "Manual: Choose specific zones"
placeholder: e.g. nova
rbd:
title: Ceph RBD (Unsupported)
monitors:
label: Monitors
placeholder: e.g. 10.16.153.105:6789
adminId:
label: Admin ID
placeholder: e.g. kube
adminSecretNamespace:
label: Admin Secret Namespace
placeholder: e.g. kube-system
adminSecret:
label: Admin Secret
placeholder: e.g. Secret
pool:
label: Pool
placeholder: e.g. kube
userId:
label: User ID
placeholder: e.g. kube
userSecretNamespace:
label: User Secret Namespace
placeholder: e.g. default
userSecretName:
label: User Secret Name
placeholder: e.g. ceph-secret-user
filesystemType:
label: Filesystem Type
placeholder: e.g. ext4
imageFormat:
label: Image Format
placeholder: e.g. 2
imageFeatures:
label: Image Features
placeholder: e.g. layering
quobyte:
title: Quobyte Volume (Unsupported)
quobyteApiServer:
label: Quobyte API Server
placeholder: "e.g. http://138.68.74.142:7860"
registry:
label: Registry
placeholder: e.g. 138.68.74.142:7861
adminSecretNamespace:
label: Admin Secret Namespace
placeholder: e.g. kube-system
adminSecretName:
label: Admin Secret Name
placeholder: e.g. quobyte-admin-secret
user:
label: User
placeholder: e.g. root
group:
label: Group
placeholder: e.g. root
quobyteConfig:
label: Quobyte Config
placeholder: e.g. BASE
quobyteTenant:
label: Quobyte Tenant
placeholder: e.g. DEFAULT
portworx-volume:
title: Portworx Volume (Unsupported)
filesystem:
label: Filesystem
placeholder: e.g. ext4
blockSize:
label: Block Size
placeholder: e.g. 32
repl:
label: Repl
placeholder: e.g.1; 0 for entire device
ioPriority:
label: I/O Priority
placeholder: e.g. low
snapshotsInterval:
label: Snapshots Interval
placeholder: e.g. 70
aggregationLevel:
label: Aggregation Level
placeholder: e.g. 0
ephemeral:
label: Ephemeral
placeholder: e.g. true
scaleio:
title: ScaleIO Volume (Unsupported)
gateway:
label: Gateway
placeholder: e.g. https://192.168.99.200:443/api
system:
label: System
placeholder: e.g. scaleio
protectionDomain:
label: Protection Domain
placeholder: e.g. pd0
storagePool:
label: Storage Pool
placeholder: e.g. sp1
storageMode:
label: StorageMode
thin: Thin Provisioned
thick: Thick Provisioned
secretRef:
label: Secret Ref
placeholder: e.g. sio-secret
readOnly:
label: Read Only
filesystemType:
label: Filesystem Type
placeholder: e.g. xfs
storageos:
title: StorageOS (Unsupported)
pool:
label: Pool
placeholder: e.g. default
description:
label: Description
placeholder: e.g. Kubernetes volume
filesystemType:
label: Filesystem Type
placeholder: e.g. ext4
adminSecretNamespace:
label: Admin Secret Namespace
placeholder: e.g. default
adminSecretName:
label: Admin Secret Name
placeholder: e.g. storageos-secret
no-provisioner:
title: Local Storage (Unsupported)
tableHeaders:
accessKey: Access Key
address: Address
age: Age
apiGroup: API Groups
authRoles:
globalDefault: New User Default
clusterDefault: Cluster Creator Default
projectDefault: Project Creator Default
branch: Branch
builtIn: Built In
bundlesReady: Bundles
bundleDeploymentsReady: Deployments
builtin: Built-In
chart: Chart
clusterCreatorDefault: Cluster Creator Default
clusterFlow: Cluster Flow
clusterOutput: Cluster Output
clusters: Clusters
clustersReady: Clusters Ready
clusterGroups: Cluster Groups
commit: Commit
condition: Condition
customVerbs: Custom Verbs
description: Description
expires: Expires
providers: Providers
cpu: CPU
date: Date
default: Default
destination: Target
download: Download
effect: Effect
endpoints: Endpoints
flow: Flow
gitRepos: Git Repos
host: |-
{count, plural,
one { Host }
other { Hosts }
}
image: Image
imageSize: Size
ingressDefaultBackend: Default
ingressTarget: Target
internalExternalIp: External/Internal IP
jobs: Jobs
key: Key
keys: Data
lastUpdated: Last Updated
lastSeen: Last Seen
loggingOutputProviders: Provider
machines: Machines
manual: Manual
matches: Matches
maxKubernetesVersion: Max Kubernetes Version
message: Message
minKubernetesVersion: Min Kubernetes Version
memory: Memory
name: Name
nameDisplay: Display Name
nameUnlinked: Name
namespace: Namespace
namespaceName: Name
namespaceNameUnlinked: Name
node: Node
nodeName: Name
nodesReady: Nodes Ready
nodePort: Node Port
object: Object
output: Output
p95: 95%tile
persistentVolumeSource: Source
podImages: Image
pods: Pods
port: Port
protocol: Protocol
provider: Provider
publicPorts: Public Ports
ram: RAM
rbac:
create: Create
delete: Delete
get: Get
list: List
patch: Patch
update: Update
watch: Watch
ready: Ready
reason: Reason
repo: Repo
reposReady: Repos Ready
replicas: Replicas
reqRate: Req Rate
resource: Resource
resources: Resources
restarts: Restarts
rioImage: Image
role: Role
roles: Roles
scale: Scale
scope: Scope
selector: Selector
simpleName: Name
simpleScale: Scale
simpleType: Type
size: Size
started: Started
state: State
status: Status
storage_class_provisioner: Provisioner
subject: Subject
subType: Kind
success: Success
summary: Summary
target: Target
targetKind: Target Type
targetPort: Target
type: Type
updated: Updated
upgrade: Upgradable
url: URL
userDisplayName: Display Name
userId: ID
userStatus: Status
username: Local Username
value: Value
version: Version
weight: Weight
target:
router:
label: Router
placeholder: Select a router
service:
label: Service
placeholder: Select a service
title: Target
version:
label: Version
placeholder: Select a version
user:
detail:
username: Username
globalPermissions:
label: Global Permissions
description: Access to manage resources that affect the entire installation
adminMessage: This user is an administrator and has all permissions
tableHeaders:
permission: Permission
clusterRoles:
label: Cluster Roles
description: Roles granted to this user for individual clusters
tableHeaders:
cluster: Cluster
projectRoles:
label: Project Roles
description: Roles granted to this user for individual projects
tableHeaders:
project: Project
generic:
tableHeaders:
role: Role
granted: Granted
edit:
credentials:
label: Credentials
username:
label: Username
placeholder: e.g. jsmith
exists: 'Username is already in use. Please choose a new username'
displayName:
label: Display Name
placeholder: e.g. John Smith
userDescription:
label: Description
placeholder: e.g. This account is for John Smith
list:
errorRefreshingGroupMemberships: Error refreshing group memberships
validation:
arrayLength:
between: '"{key}" should contain between {min} and {max} {max, plural, =1 {item} other {items}}'
exactly: '"{key}" should contain {count, plural, =1 {# item} other {# items}}'
max: '"{key}" should contain at most {count} {count, plural, =1 {item} other {items}}'
min: '"{key}" should contain at least {count} {count, plural, =1 {item} other {items}}'
boolean: '"{key}" must be a boolean value.'
chars: '"{key}" contains {count, plural, =1 {an invalid character} other {# invalid characters}}: {chars}'
custom:
missing: 'No validator exists for { validatorName }! Does the validator exist in custom-validators? Is the name spelled correctly?'
dns:
doubleHyphen: '"{key}" Cannot contain two or more consecutive hyphens'
hostname:
empty: '"{key}" must be at least one character'
emptyLabel: '"{key}" cannot contain two consecutive dots'
endDot: '"{key}" cannot end with a dot'
endHyphen: '"{key}" cannot end with a hyphen'
startDot: '"{key}" cannot start with a dot'
startHyphen: '"{key}" cannot start with a hyphen'
startNumber: '"{key}" cannot start with a number'
tooLong: '"{key}" cannot be longer than {max} characters'
tooLongLabel: '"{key}" cannot contain a section longer than {max} characters'
label:
emptyLabel: '"{key}" cannot be empty'
endHyphen: '"{key}" cannot end with a hyphen'
startHyphen: '"{key}" cannot start with a hyphen'
startNumber: '"{key}" cannot start with a number'
tooLongLabel: '"{key}" cannot be more than {max} characters'
flowOutput:
both: Requires "Output" or "Cluster Output" to be selected.
global: Requires "Cluster Output" to be selected.
output:
logdna:
apiKey: Required an "Api Key" to be set.
invalidCron: Invalid cron schedule
k8s:
identifier:
emptyLabel: '"{key}" cannot have an empty key'
emptyPrefix: '"{key}" cannot have an empty prefix'
endLetter: '"{key}" must end with a letter or number'
startLetter: '"{key}" must start with a letter or number'
tooLongKey: '"{key}" cannot have a key longer than {max} characters'
tooLongPrefix: '"{key}" cannot have a prefix longer than {max} characters'
noSchema: No schema found to validate
noType: No type to validate
number:
between: '"{key}" should be between {min} and {max}'
exactly: '"{key}" should be exactly {val}'
max: '"{key}" should be at most {val}'
min: '"{key}" should be at least {val}'
podAffinity:
affinityTitle: Pod Affinity
antiAffinityTitle: Pod Anti-Affinity
requiredDuringSchedulingIgnoredDuringExecution: required rules
preferredDuringSchedulingIgnoredDuringExecution: preferred rules
topologyKey: Rule [{index}] of {group} {rules} - Topology key is required.
matchExpressions:
operator: Rule [{index}] of {group} {rules} - operator must be one of 'In', 'NotIn', 'Exists', 'DoesNotExist'
valueMustBeEmpty: Rule [{index}] of {group} {rules} - value must be empty if operator is 'Exists' or 'DoesNotExist'
valuesMustBeDefined: Rule [{index}] of {group} {rules} - value must be defined if operator is 'In' or 'NotIn'
port: A port must be a number between 1 and 65535.
prometheusRule:
groups:
required: At least one rule group is required.
singleAlert: A rule may contain alert rules or recording rules but not both.
valid:
name: 'Name is required for rule group {index}.'
rule:
alertName: 'Rule group {groupIndex} rule {ruleIndex} requires a Alert Name.'
expr: 'Rule group {groupIndex} rule {ruleIndex} requires a PromQL Expression.'
labels: 'Rule group {groupIndex} rule {ruleIndex} requires at least one label. Severity is recommended.'
recordName: 'Rule group {groupIndex} rule {ruleIndex} requires a Time Series Name.'
singleEntry: 'At least one alert rule or one recording rule is required in rule group {index}.'
required: '"{key}" is required'
requiredOrOverride: '"{key}" is required or must allow override'
roleTemplate:
roleTemplateRules:
missingVerb: You must specify at least one verb for each resource grant
missingResource: You must specify a Resource for each resource grant
missingApiGroup: You must specify an API Group for each resource grant
missingOneResource: You must specify at least one Resource, Non-Resource URL or API Group for each resource grant
service:
externalName:
none: External Name is required on an ExternalName Service.
ports:
name:
required: 'Port Rule [{position}] - Name is required.'
nodePort:
requriedInt: 'Port Rule [{position}] - Node Port must be integer values if included.'
port:
required: 'Port Rule [{position}] - Port is required.'
requriedInt: 'Port Rule [{position}] - Port must be integer values if included.'
targetPort:
between: 'Port Rule [{position}] - Target Port must be between 1 and 65535'
iana: 'Port Rule [{position}] - Target Port must be an IANA Service Name or Integer'
ianaAt: 'Port Rule [{position}] - Target Port '
required: 'Port Rule [{position}] - Target Port is required'
stringLength:
between: '"{key}" should be between {min} and {max} {max, plural, =1 {character} other {characters}}'
exactly: '"{key}" should be {count, plural, =1 {# character} other {# characters}}'
max: '"{key}" should be at most {count} {count, plural, =1 {character} other {characters}}'
min: '"{key}" should be at least {count} {count, plural, =1 {character} other {characters}}'
targets:
missingProjectId: A target must have a project selected.
monitoring:
route:
match: At least one Match or Match Regex must be selected
interval: '"{key}" must be of a format with digits followed by a unit i.e. 1h, 2m, 30s'
wizard:
previous: Previous
finish: Finish
next: Next
step: "Step {number}"
wm:
connection:
connected: Connected
connecting: Connecting…
disconnected: Disconnected
error: Error
containerLogs:
clear: Clear
containerName: "Container: {label}"
download: Download
follow: Follow
noData: There are no log entries to show in the current range.
noMatch: No lines match the current filter.
previous: Use Previous Container
range:
all: Everything
hours: |-
{value, number}
{value, plural,
=1 {Hour}
other {Hours}
}
label: Show the last
lines: "{value, number} Lines"
minutes: |-
{value, number} {value, plural,
=1 {Minute}
other {Minutes}
}
search: Filter
timestamps: Show Timestamps
wrap: Wrap Lines
containerShell:
clear: Clear
containerName: "Container: {label}"
kubectlShell:
title: "Kubectl: {name}"
workload:
container:
command:
addEnvVar: Add Variable
args: Arguments
as: as
command: Command
env: Environment Variables
fromResource:
key:
label: Key
placeholder: "e.g. metadata.labels['']"
name:
label: Variable Name
placeholder: "e.g. FOO"
prefix: Prefix
source:
label: Source
placeholder: e.g. my-container
secret: Secret
configMap: ConfigMap
containerName: Container Name
type: Type
value:
label: Value
placeholder: e.g. bar
tty: TTY
workingDir: WorkingDir
stdin: Stdin
containerName: Container Name
healthCheck:
checkInterval: Check Interval
command:
command: Command to run
failureThreshold: Failure Threshold
httpGet:
headers: Request Headers
path: Request Path
port: Check Port
initialDelay: Initial Delay
livenessProbe: Liveness Check
livenessTip: Containers will be restarted when this check is failing. Not recommended for most uses.
noHealthCheck: "There is not a Readiness Check, Liveness Check or Startup Check configured."
readinessProbe: Readiness Check
readinessTip: Containers will be removed from service endpoints when this check is failing. Recommended.
startupProbe: Startup Check
startupTip: Containers will wait until this check succeeds before attempting other health checks.
successThreshold: Success Threshold
timeout: Timeout
kind:
none: None
HTTP: HTTP request returns a successful status (200-399)
HTTPS: HTTPS request returns a successful status
tcp: TCP connection opens successfully
exec: Command run inside the container exits with status 0
image: Container Image
imagePullPolicy: Pull Policy
imagePullSecrets: Pull Secrets
init: Init Container
name: Container Name
noResourceLimits: There are no resource requirements configured.
noPorts: There are no ports configured.
ports:
createService: Service Type
noCreateService: Do not create a service
containerPort: Private Container Port
hostIP: Host IP
hostPort: Public Host Port
name: Name
protocol: Protocol
listeningPort: Listening Port
removeContainer: Remove Container
security:
addCapabilities: Add Capabilities
addGroupIDs: Add Group IDs
allowPrivilegeEscalation:
label: Privilege Escalation
'false': No
'true': "Yes: container can gain more privileges than its parent process"
dropCapabilities: Drop Capabilities
fsGroup: Filesystem Group
hostIPC: Use Host IPC Namespace
hostPID: Use Host PID Namespace
privileged:
label: Privileged
'false': No
'true': "Yes: container has full access to the host"
readOnlyRootFilesystem:
label: Read-Only Root Filesystem
'false': No
'true': "Yes: container has a read-only root filesystem"
runAsGroup: Run as Group ID
runAsNonRoot:
label: Run as Non-Root
'false': No
'true': "Yes: container must run as a non-root user"
runAsNonRootOptions:
noOption: "No"
yesOption: "Yes: containers must run as non-root-user"
runAsUser: Run as User ID
shareProcessNamespace: Share single process namespace
supplementalGroups: Additional Group IDs
sysctls: Sysctls
sysctlsKey: Name
standard: Standard Container
titles:
container: Container
command: Command
containers: Containers
env: Environment Variables
events: Events
general: General
healthCheck: Health Check
image: Image
networking: Networking
networkSettings: Network Settings
podAnnotations: Pod Annotations
podLabels: Pod Labels
metrics: Metrics
podScheduling: Pod Scheduling
nodeScheduling: Node Scheduling
ports: Ports
resources: Resources
securityContext: Security Context
status: Status
volumeClaimTemplates: Volume Claim Templates
upgrading: Scaling and Upgrade Policy
cronSchedule: Schedule
detail:
pods:
title: Pods
detailTop:
node: Node
podIP: Pod IP
podRestarts: Pod Restarts
workload: Workload
pods: Pods by State
runs: Runs
gaugeStates:
active: Active
transitioning: Transitioning
warning: Warning
error: Error
succeeded: Successful
running: Running
failed: Failed
hideTabs: 'Hide Advanced Options'
job:
activeDeadlineSeconds:
label: Active Deadline
tip: The duration that the job may be active before the system tries to terminate it.
backoffLimit:
label: Back Off Limit
tip: The number of retries before marking this job failed.
completions:
label: Completions
tip: The number of successfully finished pods the job should be run with.
failedJobsHistoryLimit:
label: Failed Job History Limit
tip: The number of failed finished jobs to retain.
parallelism:
label: Parallelism
tip: The maximum number of pods the job should run at any given time.
startingDeadlineSeconds:
label: Starting Deadline Seconds
tip: The deadline in seconds for starting the job if it misses scheduled time
successfulJobsHistoryLimit:
label: Successful Job History Limit
tip: The number of successful finished jobs to retain.
suspend: Suspend
metrics:
pod: Pod Metrics
metricsView: Metrics View
networking:
dnsPolicy:
label: DNS Policy
options:
clusterFirst: Cluster First
clusterFirstWithHostNet: Cluster First With Host Network
default: Default
none: None
placeholder: Select a Policy...
hostAliases:
add: Add Alias
keyLabel: IP Address
keyPlaceholder: e.g. 1.1.1.1
label: Host Aliases
tip: Additional /etc/hosts entries to be injected in the container.
valueLabel: Hostname
valuePlaceholder: "e.g. foo.com, bar.com"
hostname:
label: Hostname
placeholder: e.g. web
nameservers:
add: Add Nameserver
label: Nameservers
placeholder: e.g. 1.1.1.1
networkMode:
label: Network Mode
options:
hostNetwork: Host Network
normal: Normal
placeholder: Select a Mode...
dns: DNS
resolver:
label: Resolver Options
add: Add Option
searches:
add: Add Search Domain
label: Search Domains
placeholder: e.g. mycompany.com
subdomain:
label: Subdomain
placeholder: e.g. web
validation:
containers: Containers
containerImage: Container {name} - "Container Image" is required.
replicas: Replicas
showTabs: 'Show Advanced Options'
scheduling:
activeDeadlineSeconds: Pod Active Deadline
activeDeadlineSecondsTip: The duration that the pod may be active before the system tries to mark it failed and kill associated containers.
affinity:
addNodeSelector: Add Node Selector
anyNode: Run pods on any available node
affinityTitle: Run pods on nodes with pods matching these selectors
antiAffinityTitle: Run pods on nodes without pods matching these selectors
affinityOption: Affinity
antiAffinityOption: Anti-Affinity
matchExpressions:
addRule: Add Rule
doesNotExist: Does Not Exist
exists: Exists
greaterThan: ">"
in: =
inNamespaces: "Pods in these namespaces:"
key: Key
lessThan: <
namespaces: Namespaces
notIn: ≠
operator: Operator
value: Value
weight: Weight
noPodRules: There are no pod scheduling rules configured.
nodeName: Node Name
priority: Priority
preferAny: "Prefer any of:"
preferred: Preferred
required: Required
requireAny: "Require any of:"
schedulingRules: Run pods on node(s) matching scheduling rules
specificNode: Run pods on specific node(s)
thisPodNamespace: This pod's namespace
topologyKey:
label: Topology Key
placeholder: e.g. failure-domain.beta.kubernetes.io/zone
type: Type
priority:
className: Priority Class Name
priority: Priority
terminationGracePeriodSeconds: Termination Grace Period
terminationGracePeriodSecondsTip: The duration that the pod needs to terminate gracefully.
titles:
advanced: Advanced
nodeScheduling: Node Scheduling
nodeSelector: Nodes with these labels
podScheduling: Pod Scheduling
priority: Priority
tab: Scheduling
tolerations: Tolerations
limits: Limits and Reservations
tolerations:
addToleration: Add Toleration
effect: Effect
effectOptions:
all: All
noExecute: NoExecute
noSchedule: "NoSchedule,"
preferNoSchedule: PreferNoSchedule
labelKey: Label Key
operator: Operator
operatorOptions:
equal: =
exists: Exists
tolerationSeconds: Toleration Seconds
value: Value
serviceName: Service Name
storage:
subtypes:
secret: Secret
configMap: ConfigMap
hostPath: Bind-Mount
persistentVolumeClaim: Persistent Volume Claim
createPVC: Create Persistent Volume Claim
csi: CSI
nfs: NFS
awsElasticBlockStore: Amazon EBS Disk
azureDisk: Azure Disk
azureFile: Azure File
gcePersistentDisk: Google Persistent Disk
driver.longhorn.io: Longhorn
vsphereVolume: VMWare vSphere Volume
addClaim: Add Claim
addMount: Add Mount
addVolume: Add Volume
certificate: Certificate
csi:
diskName: Disk Name
diskURI: Disk URI
cachingMode:
label: Caching Mode
options:
none: None
readOnly: Read Only
readWrite: Read Write
kind:
label: Kind
options:
dedicated: Dedicated
managed: Managed
shared: Shared
drivers:
driver.longhorn.io: Longhorn
fsType: Filesystem Type
shareName: Share Name
secretName: Secret Name
volumeID: Volume ID
partition: Partition
pdName: Persistent Disk Name
storagePolicyID: Storage Policy ID
storagePolicyName: Storage Policy Name
volumePath: Volume Path
defaultMode: Default Mode
driver: driver
hostPath:
label: The Path on the Node must be
options:
default: 'Anything: do not check the target path'
directoryOrCreate: A directory, or create if it doesn't exist
directory: An existing directory
fileOrCreate: A file, or create if it doesn't exist
file: An existing file
socket: An existing socket
charDevice: An existing character device
blockDevice: An existing block device
mountPoint: Mount Point
nodePath: Path on Node
optional:
label: Optional
'no': 'No'
'yes': 'Yes'
path: Path
readOnly: Read Only
server: Server
subPath: Sub Path in Volume
title: 'Storage'
volumeName: Volume Name
volumePath: Volume Path
typeDescriptions:
apps.daemonset: DaemonSets run exactly one pod on every eligible node. When new nodes are added to the cluster, DaemonSets automatically deploy to them. Recommended for system-wide or vertically-scalable workloads that never need more than one pod per node.
apps.deployment: Deployments run a scalable number of replicas of a pod distributed among the eligible nodes. Changes are rolled out incrementally and can be rolled back to the previous revision when needed. Recommended for stateless & horizontally-scalable workloads.
apps.statefulset: StatefulSets manage stateful applications and provide guarantees about the ordering and uniqueness of the pods created. Recommended for workloads with persistent storage or strict identity, quorum, or upgrade order requirements.
batch.cronjob: CronJobs create Jobs, which then run Pods, on a repeating schedule. The schedule is expressed in standard Unix cron format, and uses the timezone of the Kubernetes control plane (typically UTC).
batch.job: Jobs create one or more pods to reliably perform a one-time task by running a pod until it exits successfully. Failed pods are automatically replaced until the specified number of completed runs has been reached. Jobs can also run multiple pods in parallel or function as a batch work queue.
upgrading:
activeDeadlineSeconds:
label: Pod Active Deadline
tip: The duration the pod may be active before the system will try to mark it failed and kill associated containers.
concurrencyPolicy:
label: Concurrency
options:
allow: Allow CronJobs to run concurrently
forbid: Skip next run if current run hasn't finished
replace: Replace run if current run hasn't finished
maxSurge:
label: Max Surge
tip: The maximum number of pods allowed beyond the desired scale at any given time.
maxUnavailable:
label: Max Unavailable
tip: The maximum number of pods which can be unavailable at any given time.
minReadySeconds:
label: Minimum Ready
tip: The minimum duration a pod should be ready without containers crashing for it to be considered available.
podManagementPolicy:
label: Pod Management Policy
progressDeadlineSeconds:
label: Progress Deadline
tip: The minimum duration to wait for a deployment to progress before marking it failed.
revisionHistoryLimit:
label: Revision History Limit
tip: The number of old ReplicaSets to retain for rollback.
strategies:
labels:
delete: "On Delete: New pods are only created when old pods are manually deleted."
recreate: "Recreate: Kill ALL pods, then start new pods."
rollingUpdate: "Rolling Update: Create new pods, until max surge is reached, before deleting old pods. Don't stop more pods than max unavailable."
terminationGracePeriodSeconds:
label: Termination Grace Period
tip: The duration the pod needs to terminate successfully.
title: Upgrading
##############################
# Model Properties
##############################
model:
account:
kind:
admin: Admin
agent: Agent
project: Environment
registeredAgent: Registered Agent
service: Service
user: User
"catalog.cattle.io.app":
firstDeployed: First Deployed
lastDeployed: Last Deployed
authConfig:
description:
ldap: LDAP
saml: SAML
oauth: OAuth
oidc: OIDC
name:
keycloak: Keycloak (SAML)
keycloakoidc: Keycloak (OIDC)
provider:
system: System
local: Local
multiple: Multiple
activedirectory: ActiveDirectory
azuread: AzureAD
github: GitHub
keycloak: Keycloak
ldap: LDAP
openldap: OpenLDAP
shibboleth: Shibboleth
ping: Ping Identity
adfs: ADFS
okta: Okta
freeipa: FreeIPA
googleoauth: Google
oidc: OIDC
keycloakoidc: Keycloak
cluster:
name: Cluster Name
ingress:
displayKind: L7 Ingress
machine:
role:
controlPlane: Control Plane
etcd: etcd
worker: Worker
openldapconfig:
domain:
help: Only users below this base will be used.
label: User Search Base
placeholder: "e.g. ou=Users,dc=mycompany,dc=com"
server:
label: Hostname or IP Address
serviceAccountPassword:
label: Service Account Password
serviceAccountUsername:
label: Service Account Username
projectMember:
role:
member: Member
owner: Owner
readonly: Read-Only
restricted: Restricted
service:
displayKind:
generic: Service
loadBalancer: L4 Balancer
typeDescription:
# Map of
# type: Description to be shown on the top of list view describing the type.
# Should fit on one line.
# If you link to anything external, it MUST have
# target="_blank" rel="noopener noreferrer nofollow"
cis.cattle.io.clusterscanbenchmark: A benchmark version is the name of benchmark to run using kube-bench as well as the valid configuration parameters for that benchmark.
cis.cattle.io.clusterscanprofile: A profile is the configuration for the CIS scan, which is the benchmark versions to use and any specific tests to skip in that benchmark.
cis.cattle.io.clusterscan: A scan is created to trigger a CIS scan on the cluster based on the defined profile. A report is created after the scan is completed.
cis.cattle.io.clusterscanreport: A report is the result of a CIS scan of the cluster.
management.cattle.io.feature: Feature Flags allow certain {vendor} features to be toggled on and off. Features that are off by default should be considered experimental functionality.
resources.cattle.io.backup: A backup is created to perform one-time backups or schedule recurring backups based on a ResourceSet.
resources.cattle.io.restore: A restore is created to trigger a restore to the cluster based on a backup file.
resources.cattle.io.resourceset: A resource set defines which CRDs and resources to store in the backup.
monitoring.coreos.com.servicemonitor: A service monitor defines the group of services and the endpoints that Prometheus will scrape for metrics. This is the most common way to define metrics collection.
monitoring.coreos.com.podmonitor: A pod monitor defines the group of pods that Prometheus will scrape for metrics. The common way is to use service monitors, but pod monitors allow you to handle any situation where a service monitor wouldn't work.
monitoring.coreos.com.prometheusrule: A Prometheus Rule resource defines both recording and/or alert rules. A recording rule can pre-compute values and save the results. Alerting rules allow you to define conditions on when to send notifications to AlertManager.
monitoring.coreos.com.prometheus: A Prometheus server is a Prometheus deployment whose scrape configuration and rules are determined by selected ServiceMonitors, PodMonitors, and PrometheusRules and whose alerts will be sent to all selected Alertmanagers with the custom resource's configuration.
monitoring.coreos.com.alertmanager: An alert manager is deployment whose configuration will be specified by a secret in the same namespace, which determines which alerts should go to which receiver.
catalog.cattle.io.clusterrepo: 'A chart repository is a Helm repository or {vendor} git based application catalog. It provides the list of available charts in the cluster.'
catalog.cattle.io.operation: An operation is the list of recent Helm operations that have been applied to the cluster.
catalog.cattle.io.app: An installed application is a Helm 3 chart that was installed either via our charts or through the Helm CLI.
logging.banzaicloud.io.clusterflow: Logs from the cluster will be collected and logged to the selected Cluster Output.
logging.banzaicloud.io.clusteroutput: A cluster output defines which logging providers that logs can be sent to and is only effective when deployed in the namespace that the logging operator is in.
logging.banzaicloud.io.flow: A flow defines which logs to collect and filter as well as which output to send the logs. The flow is a namespaced resource, which means logs will only be collected from the namespace that the flow is deployed in.
logging.banzaicloud.io.output: An output defines which logging providers that logs can be sent to. The output needs to be in the same namespace as the flow that is using it.
group.principal: Assigning global roles to a group only works with external auth providers that support groups. Local authorization does not support groups.
typeLabel:
management.cattle.io.token: |-
{count, plural,
one { API Key }
other { API Keys }
}
cis.cattle.io.clusterscan: |-
{count, plural,
one { Scan }
other { Scans }
}
cis.cattle.io.clusterscanprofile: |-
{count, plural,
one { Profile }
other { Profiles }
}
cis.cattle.io.clusterscanbenchmark: |-
{count, plural,
one { Benchmark Version }
other { Benchmark Versions }
}
catalog.cattle.io.operation: |-
{count, plural,
one { Recent Operation }
other { Recent Operations }
}
catalog.cattle.io.app: |-
{count, plural,
one { Installed App }
other { Installed Apps }
}
catalog.cattle.io.clusterrepo: |-
{count, plural,
one { Chart Repository }
other { Chart Repositories }
}
catalog.cattle.io.repo: |-
{count, plural,
one { Namespaced Repo }
other { Namespaced Repos }
}
chartInstallAction: |-
{count, plural,
one { App }
other { Apps }
}
chartUpgradeAction: |-
{count, plural,
one { App }
other { Apps }
}
endpoints: |-
{count, plural,
one { Endpoint }
other { Endpoints }
}
fleet.cattle.io.cluster: |-
{count, plural,
=1 { Cluster }
other {Clusters }
}
fleet.cattle.io.clustergroup: |-
{count, plural,
one { Cluster Group }
other {Cluster Groups }
}
management.cattle.io.clusterroletemplatebinding: |-
{count, plural,
one { Cluster Member }
other { Cluster Members }
}
fleet.cattle.io.gitrepo: |-
{count, plural,
one { Git Repo }
other {Git Repos }
}
management.cattle.io.authconfig: |-
{count, plural,
one { Authentication Provider }
other { Authentication Providers }
}
management.cattle.io.feature: |-
{count, plural,
one { Feature Flag }
other { Feature Flags }
}
management.cattle.io.setting: |-
{count, plural,
one { Advanced Setting }
other { Advanced Settings }
}
management.cattle.io.fleetworkspace: |-
{count, plural,
one { Workspace }
other { Workspaces }
}
# pruh-mee-thee-eyes https://www.prometheus.io/docs/introduction/faq/#what-is-the-plural-of-prometheus
monitoring.coreos.com.prometheus: |-
{count, plural,
one { Prometheus }
other { Prometheis }
}
monitoring.coreos.com.servicemonitor: |-
{count, plural,
one { Service Monitor }
other { Service Monitors }
}
monitoring.coreos.com.alertmanager: |-
{count, plural,
one { Alert Manager }
other { Alert Managers }
}
monitoring.coreos.com.podmonitor: |-
{count, plural,
one { Pod Monitor }
other { Pod Monitors }
}
monitoring.coreos.com.prometheusrule: |-
{count, plural,
one { Prometheus Rule }
other { Prometheus Rules }
}
monitoring.coreos.com.thanosruler: |-
{count, plural,
one { Thanos Rule }
other { Thanos Rules }
}
monitoring.coreos.com.receiver: |-
{count, plural,
one { Receiver }
other { Receivers }
}
monitoring.coreos.com.route: |-
{count, plural,
one { Route }
other { Routes }
}
'management.cattle.io.cluster': |-
{count, plural,
one { Mgmt Cluster }
other { Mgmt Clusters }
}
'cluster.x-k8s.io.cluster': |-
{count, plural,
one { CAPI Cluster }
other { CAPI Clusters }
}
'provisioning.cattle.io.cluster': |-
{count, plural,
one { Cluster }
other { Clusters }
}
'management.cattle.io.user': |-
{count, plural,
one { User }
other { Users }
}
namespace: |-
{count, plural,
one { Namespace }
other { Namespaces }
}
group.principal: |-
{count, plural,
one { Group }
other { Groups }
}
token: |-
{count, plural,
one { API Key }
other { API Keys }
}
action:
clone: Clone
disable: Disable
download: Download YAML
edit: Edit Config
editYaml: Edit YAML
enable: Enable
openLogs: View Logs
refresh: Refresh
remove: Delete
view: View Config
viewInApi: View in API
viewYaml: View YAML
activate: Activate
deactivate: Deactivate
show: Show
hide: Hide
copy: Copy
unassign: 'Unassign'
uninstall: Uninstall
unit:
sec: secs
min: mins
hour: |-
{count, plural,
one { hour }
other { hours }
}
day: |-
{count, plural,
one { day }
other { days }
}
workloadPorts:
addPort: Add Port
remove: Remove
addHost: Add Host
podAffinity:
addLabel: Add Pod Selector
keyValue:
keyPlaceholder: e.g. foo
valuePlaceholder: e.g. bar
##############################
### Advanced Settings
##############################
advancedSettings:
label: Advanced Settings
subtext: Typical users will not need to change these. Proceed with caution, incorrect values can break your {appName} installation. Settings which have been customized from default settings are tagged 'Modified'.
show: Show
hide: Hide
none: None
edit:
label: Edit Setting
changeSetting: "Change Setting:"
trueOption: "True"
falseOption: "False"
value: Value
useDefault: Copy the default value
invalidJSON: Invalid JSON - please check and correct your input before saving
descriptions:
'cacerts': "CA Certificates needed to verify the server's certificate."
'cluster-defaults': 'Override RKE Defaults when creating new clusters.'
'engine-install-url': 'Default Docker engine installation URL (for most node drivers).'
'engine-iso-url': 'Default OS installation URL (for vSphere driver).'
'engine-newest-version': 'The newest supported version of Docker at the time of this release. A Docker version that does not satisfy supported docker range but is newer than this will be marked as untested.'
'engine-supported-range': 'Semver range for supported Docker engine versions. Versions which do not satisfy this range will be marked unsupported in the UI.'
'ingress-ip-domain': 'Wildcard DNS domain to use for automatically generated Ingress hostnames. .. will be added to the domain.'
'server-url': 'Default {appName} install url. Must be HTTPS. All nodes in your cluster must be able to reach this.'
'system-default-registry': 'Private registry to be used for all system Docker images.'
'ui-index': 'HTML index location for the Cluster Manager UI.'
'ui-dashboard-index': 'HTML index location for the {appName} UI.'
'ui-offline-preferred': 'Controls whether UI assets are served locally by the server container or from the remote URL defined in the ui-index and ui-dashboard-index settings. The `Dynamic` option will use local assets in production builds of {appName}.'
'ui-pl': 'Private-Label company name.'
'ui-issues': "Use a url address to send new 'File an Issue' reports instead of sending users to the GitHub issues page."
'telemetry-opt': 'Telemetry reporting opt-in.'
'auth-user-info-max-age-seconds': 'The maximum age of a users auth tokens before an auth provider group membership sync will be performed.'
'auth-user-info-resync-cron': 'Default cron schedule for resyncing auth provider group memberships.'
'cluster-template-enforcement': 'Non-admins will be restricted to launching clusters via preapproved RKE Templates only.'
'auth-user-session-ttl-minutes': 'Custom TTL (in minutes) on a user auth session.'
'auth-token-max-ttl-minutes': 'Custom max TTL (in minutes) on an auth token.'
'kubeconfig-generate-token': 'Automatically generate kubeconfig tokens for users.'
'kubeconfig-token-ttl-minutes': 'Custom max TTL (in minutes) on a kubeconfig token.'
'rke-metadata-config': 'Configure RKE metadata refresh parameters.'
'ui-banners': 'Classification banner is used to display a custom fixed banner in the header, footer, or both.'
'ui-default-landing': 'The default page users land on after login.'
'brand': Folder name for an alternative theme defined in '/assets/brand'
editHelp:
'ui-banners': This setting takes a JSON object containing 3 root parameters; banner, showHeader, showFooter. banner is an object containing; textColor, background, and text, where textColor and background are any valid CSS color value.
enum:
'ui-default-landing':
ember: Cluster Manager
vue: Cluster Explorer
'telemetry-opt':
prompt: Prompt
in: Opt-in to Telemetry
out: Opt-out of Telemetry
'ui-offline-preferred':
dynamic: Dynamic
true: Local
false: Remote
featureFlags:
label: Feature Flags
warning: |-
Feature flags allow {vendor} to gate certain features behind flags.
Features that are off by default should be considered experimental functionality.
Some features require a restart of the {vendor} server to change.
This will result in a short outage of the API and UI, but not affect running clusters or workloads.
restartRequired: "Note: Updating this feature flag requires a restart"
restart:
title: Waiting for Restart
wait: This may take a few moments
branding:
label: Branding
directoryName: Brand Asset Directory Name
logos:
label: Logo
tip: 'Upload a logo to replace the Rancher logo in the top-level navigation header. Image height should be 21 pixels with a max width of 200 pixels. Max file size is 20KB'
lightPreview: Light Theme Preview
darkPreview: Dark Theme Preview
uploadLight: Upload Light Logo
uploadDark: Upload Dark Logo
useCustom: Use a Custom Logo
options:
default: Default Rancher Theme
suse: SUSE Theme
custom: Define a Custom Theme
uiPL:
label: Private Label Company Name
uiIssues:
label: Issue Reporting URL
uiBanner:
label: Fixed Banners
text: Text
textColor: Text Color
background: Background Color
showHeader: Show Banner in Header
showFooter: Show Banner in Footer
color:
label: Primary Color
tip: You can override the primary color used throughout the UI with a custom color of your choice.
useCustom: Use a Custom Color
resourceQuota:
configMaps: Config Maps
limitsCpu: CPU Limit
limitsMemory: Memory Limit
persistentVolumeClaims: Persistent Volume Claims
pods: Pods
replicationControllers: Replication Controllers
requestsCpu: CPU Reservation
requestsMemory: Memory Reservation
requestsStorage: Storage Reservation
secrets: Secrets
services: Services
servicesLoadBalancers: Services Load Balancers
servicesNodePorts: Service Node Ports
projectLimit:
label: Project Limit
cpuPlaceholder: e.g. 2000
memoryPlaceholder: e.g. 2048
storagePlaceholder: e.g. 50
unitlessPlaceholder: e.g. 50
namespaceDefaultLimit:
label: Namespace Default Limit
cpuPlaceholder: e.g. 500
memoryPlaceholder: e.g. 1024
storagePlaceholder: e.g. 10
unitlessPlaceholder: e.g. 10
resourceType:
label: Resource Type
snapshots:
title: Snapshots
action:
create: Create Snapshot
card:
created: "Created on ''{date}'' at ''{time}''"
action:
restore: Restore
remove: Delete
create:
title: 'Create a new Snapshot'
name:
label: Snapshot Name
description:
label: Description
actions:
submit: Create
back: Cancel
info: 'Rancher Desktop will be temporarily unavailable while creating a new Snapshot'
empty:
icon: icon-search
heading: No snapshots found
body: Click on Create Snapshot to get started
dialog:
restore:
header: Restore snapshot?
info: 'Restoring this snapshot will replace your current installation, including preferences. If the Preferences window is open, it will be automatically closed and any unsaved changes will be discarded.'
actions:
ok: 'Restore'
cancel: 'Cancel'
error:
header: 'Error restoring snapshot'
description: "Failed to restore snapshot ''{snapshot}''.'
'Rancher Desktop has performed a Factory Reset and will now exit. Please try to address the issue that led to this error before restarting Rancher Desktop and attempting to restore the snapshot again."
buttonText: 'Quit Rancher Desktop'
delete:
header: Permanently delete snapshot?
info: 'text'
actions:
ok: 'Delete'
cancel: 'Cancel'
restoring:
header: 'Restoring {snapshot}'
message: "Please wait while snapshot ''{snapshot}'' is being restored. This may take some time depending on your machine's resources.
Rancher Desktop will be temporarily unavailable during this operation. Once completed, the VM will restart with the {snapshot} snapshot active."
actions:
cancel: 'Cancel'
creating:
header: 'Creating {snapshot}'
message: "Please wait while snapshot ''{snapshot}'' is being created.' 'This may take some time depending on your machine's resources.' 'Rancher Desktop will be temporarily unavailable during this operation.'
'Once the snapshot is complete, the VM will restart."
actions:
cancel: 'Cancel'
buttons:
error: Close
generic:
header: Snapshot operation in progress
message: "Please wait while the snapshot operation is active. This may take some time depending on your machine's resources. Rancher Desktop will be temporarily unavailable during this operation.
Once the snapshot process is complete, the VM will restart."
showLogs: Show logs
info:
when: " at {time}"
restore:
success: "Restored ''{snapshot}''"
cancel: 'Cancelled restoration of {snapshot}'
create:
success: "Created ''{snapshot}''"
cancel: 'Cancelled creation of {snapshot}'
delete:
success: "Deleted ''{snapshot}''"
error: 'Delete error: {error}'
lock:
info: "A snapshot lock file has been detected for an unusually long time. If you believe this is an error, you can try to remove the lock file by running rdctl snapshot unlock."
##############################
### Troubleshooting Page
##############################
troubleshooting:
title: Troubleshooting
description: Use these tools to help identify and resolve issues.
kubernetes:
title: Kubernetes
resetKubernetes:
title: Reset Kubernetes
description: Resetting Kubernetes will delete all workloads and configuration.
buttonText: Reset Kubernetes
messageBox:
title: Rancher Desktop - Reset Kubernetes
message: Reset Kubernetes?
checkboxLabel: Delete container images
ok: Reset
cancel: Cancel
resetContainer:
title: 'Reset Kubernetes & Container Images'
description: All images will be lost and Kubernetes will be reset.
buttonText: Reset Container Images
general:
title: General
logs:
title: Logs
description: Show Rancher Desktop logs
buttonText: Show Logs
factoryReset:
title: Factory Reset
description: Factory Reset will remove all Rancher Desktop Configurations.
buttonText: Factory Reset
messageBox:
title: Rancher Desktop - Factory Reset
message: Perform a factory reset?
detail:
Doing a factory reset will remove your cluster and all Rancher Desktop settings, and shut down Rancher Desktop. If you intend to continue using Rancher Desktop, you will need to manually start it and go through the initial set up again.
Are you sure you want to reset everything?
checkboxLabel: Keep cached Kubernetes images
ok: Factory Reset
cancel: Cancel
needHelp: 'Still having problems? Start a discussion in the #rancher-desktop channel on the Rancher Users Slack or Report an Issue.'
##############################
### Diagnostics Page
##############################
diagnostics:
results:
muted:
icon: icon-search
heading: No results found
body: Try showing muted diagnostics.
success:
icon: icon-checkmark
heading: No problems detected
body: Rancher Desktop appears to be functioning correctly.
##############################
### Extensions Page
##############################
extensions:
icon: icon-extension
installed:
list:
upgrade: Upgrade
uninstall: Remove
emptyState:
icon: icon-extension
heading: No extensions installed
body: It looks like you don't have any extensions installed yet. Browse the extensions catalog to get started.
button:
text: Browse Extensions
view:
emptyState:
heading: Extension removed
body: '{extensionId} was removed. Please visit the extensions catalog to reinstall the extension or browse for other extensions to enhance your Rancher Desktop experience.'
##############################
### Preferences Page
##############################
preferences:
actions:
banner:
reset: Kubernetes will reset after applying changes.
restart: Kubernetes will restart after applying changes.
error: "There's a problem with the preferences selection."
locked:
tooltip: Locked due to organization's policy
incompatibleTypeWarningPre: 'The option requires'
incompatibleTypeWarningPostSelected: 'to be selected.'
incompatibleTypeWarningPostDisabled: 'to be unselected.'
incompatiblePrefWarningOr: 'or'
##############################
### Integrations Page
##############################
integrations:
windows:
title: WSL Integrations
description: "Expose Rancher Desktop's Kubernetes configuration and Docker socket to Windows Subsystem for Linux (WSL) distros"
##############################
### Support Page
##############################
support:
community:
title: SUSE Rancher provides world-class support
linksTitle: Community Support
learnMore: Find out more about SUSE Rancher Support
pricing: Contact us for pricing
subscription:
haveSupport: Already have support?
addSubscription: Add a Subscription ID
removeSubscription: Remove your Subscription ID
addTitle: Add your SUSE Subscription ID
addLabel: "Please enter a valid Subscription ID:"
removeTitle: Remove your ID?
removeBody: "Note: This will not affect your subscription."
suse:
title: "Great News - You're covered"
editBrand: Customize UI Theme
access:
title: Get Support
text: Login to SUSE Customer Center to access support for your subscription
action: SUSE Customer Center
promos:
one:
title: 24x7 Support
text: We provide tightly defined SLAs, and offer round the clock support options.
two:
title: Issue Resolution
text: Run SUSE Rancher products with confidence, knowing that the developers who built them are available to quickly resolve issues.
three:
title: Troubleshooting
text: We focus on uncovering the root cause of any issue, whether it is related to Rancher Labs products, Kubernetes, Docker or your underlying infrastructure.
four:
title: Innovate with Freedom
text: Take advantage of our certified compatibility with a wide range of Kubernetes providers, operating systems, and open source software.
embedding:
retry: Retry
unavailable: Cluster Manager UI is not available
v1ClusterTools:
monitoring:
label: Monitoring (Legacy)
description: 'Legacy V1 monitoring. V1 Monitoring is deprecated since Rancher 2.5.0. Learn more about migrating to V2 Monitoring.'
logging:
label: Logging (Legacy)
description: 'Legacy V1 logging. V1 Logging is deprecated since Rancher 2.5.0. Learn more about migrating to V2 Logging.'
legacy:
alerts: Alerts
apps: Apps
catalogs: Catalogs
globalDnsEntries: Global DNS Entries
globalDnsProviders: Global DNS Providers
logging: Logging
notifiers: Notifiers
monitoring: Monitoring
psps: Pod Security Policies
project:
label: Project
select: "Use the Project/Namespace filter at the top of the page to select a Project in order to see legacy Project features."
harvester:
tableHeaders:
actions: Actions
================================================
FILE: pkg/rancher-desktop/assets/translations/zh-hans.yaml
================================================
##############################
# Special stuff
##############################
generic:
add: 添加
back: 返回
cancel: 取消
close: 关闭
comingSoon: 即将推出
copy: 复制
create: 创建
created: 创建时间
customize: 定制
default: 默认
disabled: 禁用
done: 完成
enabled: 启用
ignored: 忽略
invalidCron: 无效的 cron 调度
labelsAndAnnotations: 标签和注释
loading: 正在加载中...
members: 成员
#na: n/a
name: 名称
never: 从不
none: 无
#number: '{prefix}{value, number}{suffix}'
overview: 概述
readFromFile: 从文件读取
register: 注册
remove: 移除
resource: |-
{count, plural,
one {资源}
other {资源}
}
resourceCount: |-
{count, plural,
one {1 resource}
other {# resources}
}
save: 保存
type: 类型
unknown: 未知
key: 键
value: 值
yes: 是
no: 否
units:
time:
5s: 5秒
10s: 10秒
30s: 30秒
1m: 1分钟
5m: 5分钟
15m: 15分钟
30m: 30分钟
1h: 1小时
2h: 2小时
6h: 6小时
1d: 1小时
7d: 7天
30d: 30天
#locale:
#en-us: English
#zh-hans: 简体中文
#none: (None)
nav:
title: 仪表盘
#backToRancher: Cluster Manager
clusterTools: 集群工具
shell: 命令行
import: 导入 YAML 文件
home: 返回首页
support: 帮助
group:
cluster: 集群
inUse: 更多资源
rbac: RBAC
serviceDiscovery: 服务发现
starred: 已收藏
storage: 存储
workload: 工作负载
monitoring: 监控
ns:
all: 全部命名空间
clusterLevel: 集群资源
#namespace: "{name}"
namespaced: 命名空间资源
orphan: 不在项目中
project: "项目名称: {name}"
system: 系统命名空间
user: 用户命名空间
apps: 应用商店
categories:
explore: 浏览集群
multiCluster: 全局应用
configuration: 配置
search:
placeholder: 输入关键词,搜索集群
noResults: 没有与关键词匹配的集群
resourceSearch:
label: 资源搜索
placeholder: 输入关键词,搜索资源
product:
clusterGroup: 集群应用
globalGroup: 全局应用
apps: 应用市场
auth: 用户及认证方式
backup: 备份
cis: CIS 基线测试
ecm: 集群管理员
explorer: 集群浏览器
fleet: Fleet
#longhorn: Longhorn
manager: 管理集群
#gatekeeper: OPA Gatekeeper
#istio: Istio
logging: 日志
#rio: Rio
settings: 全局设置
monitoring: 监控
suffix:
#percent: "%"
#cpus: CPUs
#ib: iB
revisions: |-
{count, plural,
=1 { 版本 }
other { 版本 }
}
seconds: |-
{count, plural,
=1 { 秒 }
other { 秒 }
}
sec: Sec
times: |-
{count, plural,
=1 { 次 }
other { 次 }
}
##############################
# Components & Pages
##############################
accountAndKeys:
title: 账户和API密钥
account:
title: 账户
change: 修改密码
apiKeys:
title: API密钥
notAllowed: 对不起,您没有权限编辑API密钥
add:
description:
label: 描述
placeholder: 可选择输入一个描述,以帮助您识别该API密钥。
label: 创建API密钥
expiry:
label: 自动过期
options:
never: 从不过期
day: 一天后过期
month: 一个月后过期
year: 一年后过期
custom: 自定义过期之间
maximum: "{value} - 最大有效期"
customExpiry:
options:
minute: 分钟
hour: 小时
day: 日
month: 月
year: 年
scope: 适用范围
noScope: 没有适用范围
info:
#accessKey: Access Key
#secretKey: Secret Key
#bearerToken: Bearer Token
saveWarning: 请在云端或本地妥善保存以上的信息! 如果丢失这些信息,你需要创建一个新的API密钥。
keyCreated: 已创建一个新的API密钥。
bearerTokenTip: "Access Key 和 Secret Key 可以作为 HTTP Basic auth 的用户名和密码发送,以授权请求。您也可以将它们组合起来作为一个Bearer token使用。"
ttlLimitedWarning: 由于系统配置的原因,该API密钥的到期时间缩短了。
authConfig:
accessMode:
label: '配置够登录和使用{vendor}的人员名单'
required: '只有授权用户和用户组能够访问。'
restricted: '允许集群和项目的成员,以及授权用户和用户组访问'
unrestricted: '允许所有用户访问'
allowedPrincipalIds:
title: 授权用户和用户组
associatedWarning: '注意:您认证为的{provider} 用户将作为您当前登录的 {vendor} 用户的替代登录方式({username})。'
github:
clientId:
label: 账户名
clientSecret:
label: 密码
form:
app:
label: 应用名称
value: '输入一个应用名称,例如:我的{vendor}'
calllback:
label: 授权回调URL
description:
label: 应用描述
value: '选填项,可留空'
homepage:
label: 主页URL地址
instruction: '请在表格中输入以下值:'
prefix: |-