pax_global_header 0000666 0000000 0000000 00000000064 14533742372 0014524 g ustar 00root root 0000000 0000000 52 comment=28cd41e73158295e0698dea8f07b5860768a8ec1
nydus-snapshotter-0.13.4/ 0000775 0000000 0000000 00000000000 14533742372 0015323 5 ustar 00root root 0000000 0000000 nydus-snapshotter-0.13.4/.github/ 0000775 0000000 0000000 00000000000 14533742372 0016663 5 ustar 00root root 0000000 0000000 nydus-snapshotter-0.13.4/.github/codecov.yml 0000664 0000000 0000000 00000001216 14533742372 0021030 0 ustar 00root root 0000000 0000000 coverage:
status:
patch: off
project:
default:
enabled: yes
target: auto # auto compares coverage to the previous base commit
# adjust accordingly based on how flaky your tests are
# this allows a 0.3% drop from the previous base commit coverage
threshold: 0.3%
comment:
layout: "reach, diff, flags, files"
behavior: default
require_changes: true # if true: only post the comment if coverage changes
codecov:
require_ci_to_pass: false
notify:
wait_for_ci: true
# When modifying this file, please validate using
# curl -X POST --data-binary @codecov.yml https://codecov.io/validate
nydus-snapshotter-0.13.4/.github/workflows/ 0000775 0000000 0000000 00000000000 14533742372 0020720 5 ustar 00root root 0000000 0000000 nydus-snapshotter-0.13.4/.github/workflows/ci.yml 0000664 0000000 0000000 00000010254 14533742372 0022040 0 ustar 00root root 0000000 0000000 name: CI
on:
push:
branches: ["**", "stable/**"]
pull_request:
branches: ["**", "stable/**"]
env:
CARGO_TERM_COLOR: always
jobs:
build:
name: Build and Lint
timeout-minutes: 10
runs-on: ubuntu-latest
steps:
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: "1.20.1"
- name: Check out code
uses: actions/checkout@v3
- name: cache go mod
uses: actions/cache@v3
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('go.sum') }}
restore-keys: |
${{ runner.os }}-go
- name: Build
run: |
go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.51.2
export PATH=$PATH:$(go env GOPATH)/bin
make
make test
make check
build-optimizer:
name: Build optimizer
timeout-minutes: 10
runs-on: ubuntu-latest
steps:
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: "1.20.1"
- name: Check out code
uses: actions/checkout@v3
- name: cache go mod
uses: actions/cache@v3
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('go.sum') }}
restore-keys: |
${{ runner.os }}-go
- name: Build
run: |
go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.51.2
export PATH=$PATH:$(go env GOPATH)/bin
rustup component add rustfmt clippy
make build-optimizer
smoke:
name: Smoke
timeout-minutes: 10
runs-on: ubuntu-latest
steps:
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: "1.19.6"
- name: Check out code
uses: actions/checkout@v3
- name: cache go mod
uses: actions/cache@v3
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('go.sum') }}
restore-keys: |
${{ runner.os }}-go
- name: Set up containerd
uses: crazy-max/ghaction-setup-containerd@v2
- name: Build
run: |
# Download nydus components
NYDUS_VER=v$(curl --header 'authorization: Bearer ${{ secrets.GITHUB_TOKEN }}' -s "https://api.github.com/repos/dragonflyoss/nydus/releases/latest" | jq -r .tag_name | sed 's/^v//')
wget https://github.com/dragonflyoss/nydus/releases/download/$NYDUS_VER/nydus-static-$NYDUS_VER-linux-amd64.tgz
tar xzvf nydus-static-$NYDUS_VER-linux-amd64.tgz
mkdir -p /usr/bin
sudo mv nydus-static/nydus-image nydus-static/nydusd nydus-static/nydusify /usr/bin/
export PATH=$PATH:$(go env GOPATH)/bin
make smoke
cross-build-test:
name: Cross Build Test
timeout-minutes: 10
runs-on: ubuntu-latest
strategy:
matrix:
GOOS: ["linux", "windows", "darwin"]
GOARCH: ["amd64", "arm64"]
steps:
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: "1.19.6"
- name: Check out code
uses: actions/checkout@v3
- name: cache go mod
uses: actions/cache@v3
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('go.sum') }}
restore-keys: |
${{ runner.os }}-go
- name: Build
run: |
make -e GOOS=${{ matrix.GOOS }} GOARCH=${{ matrix.GOARCH }} converter
coverage:
name: Code coverage
timeout-minutes: 10
runs-on: ubuntu-latest
needs: [build]
steps:
- name: Install Go
uses: actions/setup-go@v3
with:
go-version: "1.19.6"
- name: Checkout code
uses: actions/checkout@v3
- name: cache go mod
uses: actions/cache@v3
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('go.sum') }}
restore-keys: |
${{ runner.os }}-go
- name: Run unit tests.
run: make cover
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
with:
token: ${{ secrets.CODECOV_TOKEN }} # not required for public repos
files: ./coverage.txt
nydus-snapshotter-0.13.4/.github/workflows/e2e.yml 0000664 0000000 0000000 00000004066 14533742372 0022124 0 ustar 00root root 0000000 0000000 name: integration test
on:
push:
branches:
- "main"
tags:
- v[0-9]+.[0-9]+.[0-9]+
pull_request:
branches:
- "main"
schedule:
# Trigger test every day at 00:03 clock UTC
- cron: "3 0 * * *"
workflow_dispatch:
env:
CARGO_TERM_COLOR: always
REGISTRY: ghcr.io
jobs:
run-e2e-for-cgroups-v1:
runs-on: ubuntu-20.04
steps:
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: "1.19.6"
- name: Checkout repository
uses: actions/checkout@v3
- name: cache go mod
uses: actions/cache@v3
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('go.sum') }}
restore-keys: |
${{ runner.os }}-go
- name: Log in to container registry
uses: docker/login-action@v2
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Run e2e test
run: |
TAG=$GITHUB_REF_NAME
[ "$TAG" == "main" ] && TAG="latest"
[ "$GITHUB_EVENT_NAME" == "pull_request" ] && TAG="local"
make integration
run-e2e-for-cgroups-v2:
runs-on: ubuntu-22.04
steps:
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: "1.19.6"
- name: Checkout repository
uses: actions/checkout@v3
- name: cache go mod
uses: actions/cache@v3
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('go.sum') }}
restore-keys: |
${{ runner.os }}-go
- name: Log in to container registry
uses: docker/login-action@v2
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Run e2e test
run: |
TAG=$GITHUB_REF_NAME
[ "$TAG" == "main" ] && TAG="latest"
[ "$GITHUB_EVENT_NAME" == "pull_request" ] && TAG="local"
make integration
nydus-snapshotter-0.13.4/.github/workflows/k8s-e2e-run.yml 0000664 0000000 0000000 00000000522 14533742372 0023422 0 ustar 00root root 0000000 0000000 name: E2E Test With Kubernetes
on:
push:
branches:
- "main"
tags:
- v[0-9]+.[0-9]+.[0-9]+
pull_request:
branches: [main]
jobs:
cri_auth:
uses: ./.github/workflows/k8s-e2e.yml
with:
auth-type: cri
kubeconf_auth:
uses: ./.github/workflows/k8s-e2e.yml
with:
auth-type: kubeconf
nydus-snapshotter-0.13.4/.github/workflows/k8s-e2e.yml 0000664 0000000 0000000 00000015756 14533742372 0022637 0 ustar 00root root 0000000 0000000 name: E2E Test With Kubernetes Template
on:
workflow_call:
inputs:
auth-type:
required: true
type: string
env:
DOCKER_USER: testuser
DOCKER_PASSWORD: testpassword
jobs:
e2e_tests_k8s:
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout code
uses: actions/checkout@v3
with:
submodules: recursive
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: "1.19.6"
- name: Setup Kind
uses: engineerd/setup-kind@v0.5.0
with:
version: v0.16.0
config: tests/e2e/k8s/kind.yaml
- name: Build nydus snapshotter dev image
run: |
make
cp bin/containerd-nydus-grpc ./
cp misc/snapshotter/* ./
ls -tl ./
NYDUS_VER=v$(curl --header 'authorization: Bearer ${{ secrets.GITHUB_TOKEN }}' -s "https://api.github.com/repos/dragonflyoss/nydus/releases/latest" | jq -r .tag_name | sed 's/^v//')
docker build --build-arg NYDUS_VER=${NYDUS_VER} -t local-dev:e2e .
## load local test image into kind node
kind load docker-image local-dev:e2e
- name: Setup registry
run: |
mkdir auth
docker run \
--entrypoint htpasswd \
httpd:2 -Bbn ${{ env.DOCKER_USER }} ${{ env.DOCKER_PASSWORD }} > auth/htpasswd
docker run -d \
-p 5000:5000 \
--restart=always \
--name registry \
-v "$(pwd)"/auth:/auth \
-e "REGISTRY_AUTH=htpasswd" \
-e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" \
-e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd \
registry:2
- name: Login to GitHub Container Registry
run: |
registry_ip=$(ip addr show eth0 | grep 'inet ' | awk '{print $2}' | cut -d/ -f1)
## sudo cat can't modify daemon.json
sudo chmod a+w /etc/docker/daemon.json
cat << EOF > /etc/docker/daemon.json
{
"exec-opts": ["native.cgroupdriver=cgroupfs"],
"cgroup-parent": "/actions_job",
"insecure-registries" : [ "${registry_ip}:5000" ]
}
EOF
sudo systemctl restart docker
docker login --username=${{ env.DOCKER_USER }} --password=${{ env.DOCKER_PASSWORD }} $registry_ip:5000
- name: Setup nydus snapshotter
run: |
kubectl create -f tests/e2e/k8s/snapshotter-${{ inputs.auth-type }}.yaml
export ns=nydus-system
p=`kubectl -n $ns get pods --no-headers -o custom-columns=NAME:metadata.name`
echo "snapshotter pod name ${p}"
kubectl -n $ns wait po $p --for=condition=ready --timeout=2m
# change snapshotter to nydus after nydus snapshotter started
registry_ip=$(ip addr show eth0 | grep 'inet ' | awk '{print $2}' | cut -d/ -f1)
docker cp kind-control-plane:/etc/containerd/config.toml containerd.config.toml.bak
sed -i -e 's|snapshotter = "overlayfs"|snapshotter = "nydus"|' containerd.config.toml.bak
cat << EOF >> containerd.config.toml.bak
[proxy_plugins]
[proxy_plugins.nydus]
type = "snapshot"
address = "/run/containerd-nydus/containerd-nydus-grpc.sock"
[plugins."io.containerd.grpc.v1.cri".registry.mirrors."${registry_ip}:5000"]
endpoint = ["http://${registry_ip}:5000"]
EOF
docker cp containerd.config.toml.bak kind-control-plane:/etc/containerd/config.toml.bak
docker exec kind-control-plane sh -c "cat /etc/containerd/config.toml.bak > /etc/containerd/config.toml"
docker exec kind-control-plane systemctl restart containerd
- name: Install Nydus binaries and convert nydus image
run: |
NYDUS_VER=v$(curl --header 'authorization: Bearer ${{ secrets.GITHUB_TOKEN }}' -s "https://api.github.com/repos/dragonflyoss/nydus/releases/latest" | jq -r .tag_name | sed 's/^v//')
wget -q https://github.com/dragonflyoss/nydus/releases/download/$NYDUS_VER/nydus-static-$NYDUS_VER-linux-amd64.tgz
tar xzf nydus-static-$NYDUS_VER-linux-amd64.tgz
sudo cp nydus-static/nydusify nydus-static/nydus-image /usr/local/bin/
registry_ip=$(ip addr show eth0 | grep 'inet ' | awk '{print $2}' | cut -d/ -f1)
sudo DOCKER_CONFIG=$HOME/.docker nydusify convert \
--source busybox:latest \
--target ${registry_ip}:5000/busybox:nydus-v6-latest \
--fs-version 6
- name: Run E2E test
run: |
registry_ip=$(ip addr show eth0 | grep 'inet ' | awk '{print $2}' | cut -d/ -f1)
kubectl create --namespace nydus-system secret generic regcred \
--from-file=.dockerconfigjson=$HOME/.docker/config.json \
--type=kubernetes.io/dockerconfigjson
sed -e "s|REGISTRY_IP|${registry_ip}|" tests/e2e/k8s/test-pod.yaml.tpl > tests/e2e/k8s/test-pod.yaml
if [[ "${{ inputs.auth-type }}" == "cri" ]]; then
docker exec kind-control-plane sh -c 'echo " --image-service-endpoint=unix:///run/containerd-nydus/containerd-nydus-grpc.sock" >> /etc/default/kubelet'
docker exec kind-control-plane sh -c 'systemctl daemon-reload && systemctl restart kubelet'
fi
kubectl apply -f tests/e2e/k8s/test-pod.yaml
kubectl wait po test-pod -n nydus-system --for=condition=ready --timeout=1m
kubectl delete -f tests/e2e/k8s/test-pod.yaml
- name: Dump logs
if: failure()
continue-on-error: true
run: |
log_dir="/tmp/nydus-log"
mkdir -p $log_dir
export ns=nydus-system
for p in `kubectl -n $ns get pods --no-headers -o custom-columns=NAME:metadata.name`; do
kubectl -n $ns get pod $p -o yaml >> $log_dir/nydus-pods.conf
kubectl -n $ns describe pod $p >> $log_dir/nydus-pods.conf
kubectl -n $ns logs $p -c nydus-snapshotter >> $log_dir/nydus-snapshotter.log || echo "failed to get snapshotter log"
done
kubectl -n $ns get secrets -o yaml >> $log_dir/nydus-secrets.log
docker exec kind-control-plane cat /etc/containerd/config.toml >> $log_dir/containerd-config.toml
docker exec kind-control-plane containerd config dump >> $log_dir/containerd-config-dump.toml
docker exec kind-control-plane journalctl --no-pager -u containerd >> $log_dir/containerd.log
docker exec kind-control-plane journalctl --no-pager -u kubelet >> $log_dir/kubelet.log
docker exec kind-control-plane ps -ef >> $log_dir/psef.log
kubectl get pod test-pod -o yaml >> $log_dir/test-pod.log || echo "test-pod may be deleted or not created"
cat ~/.docker/config.json > $log_dir/docker.config.json || echo "~/.docker/config.json not found"
- name: Upload Logs
uses: actions/upload-artifact@v3
if: failure()
with:
name: k8s-e2e-tests-logs
path: |
/tmp/nydus-log
nydus-snapshotter-0.13.4/.github/workflows/optimizer.yml 0000664 0000000 0000000 00000010421 14533742372 0023463 0 ustar 00root root 0000000 0000000 name: optimizer test
on:
push:
branches:
- "main"
tags:
- v[0-9]+.[0-9]+.[0-9]+
pull_request:
branches:
- "main"
schedule:
# Trigger test every day at 00:03 clock UTC
- cron: "3 0 * * *"
workflow_dispatch:
env:
CARGO_TERM_COLOR: always
jobs:
run_optimizer:
runs-on: ubuntu-latest
steps:
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: "1.19.6"
- name: Checkout repository
uses: actions/checkout@v3
- name: cache go mod
uses: actions/cache@v3
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('go.sum') }}
restore-keys: |
${{ runner.os }}-go
- name: cache cargo
uses: actions/cache@v3
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
tools/optimizer-server/target/
key: ${{ runner.os }}-cargo-${{ hashFiles('tools/optimizer-server/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo
- name: containerd runc and crictl
run: |
sudo wget https://github.com/kubernetes-sigs/cri-tools/releases/download/v1.26.0/crictl-v1.26.0-linux-amd64.tar.gz
sudo tar zxvf ./crictl-v1.26.0-linux-amd64.tar.gz -C /usr/local/bin
sudo install -D -m 755 misc/optimizer/crictl.yaml /etc/crictl.yaml
sudo wget https://github.com/containerd/containerd/releases/download/v1.7.0/containerd-static-1.7.0-linux-amd64.tar.gz
sudo systemctl stop containerd
sudo tar -zxf ./containerd-static-1.7.0-linux-amd64.tar.gz -C /usr/
sudo install -D -m 755 misc/optimizer/containerd-config.toml /etc/containerd/config.toml
sudo systemctl restart containerd
sudo wget https://github.com/opencontainers/runc/releases/download/v1.1.5/runc.amd64 -O /usr/bin/runc
sudo chmod +x /usr/bin/runc
- name: Setup CNI
run: |
wget https://github.com/containernetworking/plugins/releases/download/v1.2.0/cni-plugins-linux-amd64-v1.2.0.tgz
sudo mkdir -p /opt/cni/bin
sudo tar xzf cni-plugins-linux-amd64-v1.2.0.tgz -C /opt/cni/bin/
sudo install -D -m 755 misc/example/10-containerd-net.conflist /etc/cni/net.d/10-containerd-net.conflist
- name: Build and install optimizer
run: |
rustup component add rustfmt clippy
make optimizer
sudo chown -R $(id -un):$(id -gn) . ~/.cargo/
pwd
ls -lh bin/*optimizer*
sudo make install-optimizer
sudo install -D -m 755 misc/example/optimizer-nri-plugin.conf /etc/nri/conf.d/02-optimizer-nri-plugin.conf
sudo systemctl restart containerd
systemctl status containerd --no-pager -l
- name: Wait containerd ready
run: |
unset READY
for i in $(seq 30); do
if eval "timeout 180 ls /run/containerd/containerd.sock"; then
READY=true
break
fi
echo "Fail(${i}). Retrying..."
sleep 1
done
if [ "$READY" != "true" ];then
echo "containerd is not ready"
exit 1
fi
- name: Generate accessed files list
run: |
sed -i "s|host_path: script|host_path: $(pwd)/misc/optimizer/script|g" misc/optimizer/nginx.yaml
sudo crictl run misc/optimizer/nginx.yaml misc/optimizer/sandbox.yaml
sleep 20
sudo crictl rmp -f --all
tree /opt/nri/optimizer/results/
count=$(cat /opt/nri/optimizer/results/library/nginx:1.23.3 | wc -l)
expected=$(cat misc/optimizer/script/file_list.txt | wc -l)
echo "count: $count expected minimum value: $expected"
if [ $count -lt $expected ]; then
echo "failed to generate accessed files list for nginx:1.23.3"
cat misc/optimizer/script/file_list.txt
exit 1
fi
cat /opt/nri/optimizer/results/library/nginx:1.23.3.csv
- name: Dump logs
if: failure()
continue-on-error: true
run: |
systemctl status containerd --no-pager -l
journalctl -xeu containerd --no-pager
nydus-snapshotter-0.13.4/.github/workflows/release.yml 0000664 0000000 0000000 00000007525 14533742372 0023074 0 ustar 00root root 0000000 0000000 name: release
on:
push:
tags:
- v[0-9]+.[0-9]+.[0-9]+*
env:
CARGO_TERM_COLOR: always
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-go@v3
with:
go-version: "1.19.6"
- name: cache go mod
uses: actions/cache@v3
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('go.sum') }}
restore-keys: |
${{ runner.os }}-go
- name: cache cargo
uses: actions/cache@v3
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
tools/optimizer-server/target/
key: ${{ runner.os }}-cargo-${{ hashFiles('tools/optimizer-server/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo
- name: build nydus-snapshotter and optimizer
run: |
go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.51.2
export PATH=$PATH:$(go env GOPATH)/bin
make static-release
- name: upload artifacts
uses: actions/upload-artifact@v3
with:
name: nydus-snapshotter_artifacts
path: |
bin/containerd-nydus-grpc
bin/nydus-overlayfs
bin/optimizer-nri-plugin
bin/optimizer-server
upload:
runs-on: ubuntu-latest
needs: [build]
steps:
- uses: actions/checkout@v3
- name: Download Artifacts
uses: actions/download-artifact@v3
with:
name: nydus-snapshotter_artifacts
path: nydus-snapshotter
- name: Upload Artifacts
run: |
tag=$(echo $GITHUB_REF | cut -d/ -f3-)
tarball="nydus-snapshotter-$tag-x86_64.tgz"
chmod +x nydus-snapshotter/*
tar cf - nydus-snapshotter | gzip > ${tarball}
echo "tag=$tag" >> $GITHUB_ENV
echo "tarball=$tarball" >> $GITHUB_ENV
tarball_shasum="$tarball.sha256sum"
sha256sum $tarball > $tarball_shasum
echo "tarball_shasum=$tarball_shasum" >> $GITHUB_ENV
- name: Release
uses: softprops/action-gh-release@v1
with:
name: "Nydus Snapshotter ${{ env.tag }} Release"
generate_release_notes: true
files: |
${{ env.tarball }}
${{ env.tarball_shasum }}
publish-image:
runs-on: ubuntu-latest
needs: [build]
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Log in to the container registry
uses: docker/login-action@v2
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: download artifacts
uses: actions/download-artifact@v3
with:
name: nydus-snapshotter_artifacts
path: misc/snapshotter
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v4
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
- name: Get the nydusd version
id: step_one
run: |
export NYDUS_STABLE_VER=$(curl https://api.github.com/repos/dragonflyoss/nydus/releases/latest | jq -r .tag_name)
echo "NYDUS_STABLE_VER=$NYDUS_STABLE_VER" >> "$GITHUB_ENV"
printf 'nydus version is: %s\n' "$NYDUS_STABLE_VER"
- name: build and push nydus-snapshotter image
uses: docker/build-push-action@v3
with:
context: misc/snapshotter
file: misc/snapshotter/Dockerfile
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
build-args: NYDUS_VER=${{ env.NYDUS_STABLE_VER }}
nydus-snapshotter-0.13.4/.gitignore 0000664 0000000 0000000 00000000166 14533742372 0017316 0 ustar 00root root 0000000 0000000 bin/
pkg/filesystem/stargz/testdata/db/
coverage.txt
.vscode/
tests/output/
smoke.tests
tools/optimizer-server/target
nydus-snapshotter-0.13.4/.golangci.yml 0000664 0000000 0000000 00000002130 14533742372 0017703 0 ustar 00root root 0000000 0000000 # https://golangci-lint.run/usage/configuration#config-file
run:
concurrency: 4
deadline: 5m
issues-exit-code: 1
tests: true
skip-dirs:
- misc
# The package is ported from containerd project, let's skip it.
- pkg/remote/remotes
linters-settings:
# govet:
# check-shadowing: true
# enable:
# - fieldalignment
funlen:
# Checks the number of lines in a function.
# If lower than 0, disable the check.
# Default: 60
lines: 100
# Checks the number of statements in a function.
# If lower than 0, disable the check.
# Default: 40
statements: 80
nilnil:
checked-types:
- ptr
- func
- iface
- map
- chan
linters:
enable:
- staticcheck
- unconvert
- gofmt
- goimports
- revive
- ineffassign
- vet
- unused
- misspell
- bodyclose
# - cyclop
- dogsled
- nilnil
- unparam
- nilerr
# - goerr113
- exportloopref
# - gosec
- gocritic
- prealloc
- tenv
# - funlen
- exhaustive
- errcheck
disable:
- gosec
nydus-snapshotter-0.13.4/LICENSE 0000664 0000000 0000000 00000026136 14533742372 0016340 0 ustar 00root root 0000000 0000000
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"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.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"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.
"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).
"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.
"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."
"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.
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.
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.
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:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(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
(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.
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.
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.
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.
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.
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.
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.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
nydus-snapshotter-0.13.4/MAINTAINERS 0000664 0000000 0000000 00000001045 14533742372 0017020 0 ustar 00root root 0000000 0000000 # nydus-snapshotter maintainers
#
# As a containerd sub-project, containerd maintainers are also included from https://github.com/containerd/project/blob/main/MAINTAINERS.
# See https://github.com/containerd/project/blob/main/GOVERNANCE.md for description of maintainer role
#
# COMMITTERS
# GitHub ID, Name, Email address
changweige, Changwei Ge, changweige@gmail.com
eryugey, Eryu Guan, eguan@linux.alibaba.com
imeoer, Yan Song, yansong.ys@antgroup.com
#
# REVIEWERS
# GitHub ID, Name, Email address
sctb512, Bin Tang, tangbin.bin@bytedance.com
nydus-snapshotter-0.13.4/Makefile 0000664 0000000 0000000 00000014764 14533742372 0016777 0 ustar 00root root 0000000 0000000 all: clean build
optimizer: clean-optimizer build-optimizer
PKG = github.com/containerd/nydus-snapshotter
PACKAGES ?= $(shell go list ./... | grep -v /tests)
SUDO = $(shell which sudo)
GO_EXECUTABLE_PATH ?= $(shell which go)
NYDUS_BUILDER ?= /usr/bin/nydus-image
NYDUS_NYDUSD ?= /usr/bin/nydusd
GOOS ?= linux
GOARCH ?= $(shell go env GOARCH)
KERNEL_VER = $(shell uname -r)
# Used to populate variables in version package.
BUILD_TIMESTAMP=$(shell date '+%Y-%m-%dT%H:%M:%S')
VERSION=$(shell git describe --match 'v[0-9]*' --dirty='.m' --always --tags)
REVISION=$(shell git rev-parse HEAD)$(shell if ! git diff --no-ext-diff --quiet --exit-code; then echo .m; fi)
# Relpace test target images for e2e tests.
ifdef E2E_TEST_TARGET_IMAGES_FILE
ENV_TARGET_IMAGES_FILE = --env-file ${E2E_TEST_TARGET_IMAGES_FILE}
endif
ifdef E2E_DOWNLOADS_MIRROR
BUILD_ARG_E2E_DOWNLOADS_MIRROR = --build-arg DOWNLOADS_MIRROR=${E2E_DOWNLOADS_MIRROR}
endif
ifdef GOPROXY
PROXY := GOPROXY="${GOPROXY}"
endif
ifdef FS_CACHE
FS_DRIVER = fscache
else
FS_DRIVER = fusedev
endif
SNAPSHOTTER_CONFIG=/etc/nydus/config.toml
SOURCE_SNAPSHOTTER_CONFIG=misc/snapshotter/config.toml
NYDUSD_CONFIG=/etc/nydus/nydusd-config.${FS_DRIVER}.json
SOURCE_NYDUSD_CONFIG=misc/snapshotter/nydusd-config.${FS_DRIVER}.json
SNAPSHOTTER_SYSTEMD_UNIT_SERVICE=misc/snapshotter/nydus-snapshotter.${FS_DRIVER}.service
LDFLAGS = -s -w -X ${PKG}/version.Version=${VERSION} -X ${PKG}/version.Revision=$(REVISION) -X ${PKG}/version.BuildTimestamp=$(BUILD_TIMESTAMP)
DEBUG_LDFLAGS = -X ${PKG}/version.Version=${VERSION} -X ${PKG}/version.Revision=$(REVISION) -X ${PKG}/version.BuildTimestamp=$(BUILD_TIMESTAMP)
CARGO ?= $(shell which cargo)
OPTIMIZER_SERVER = tools/optimizer-server
OPTIMIZER_SERVER_TOML = ${OPTIMIZER_SERVER}/Cargo.toml
OPTIMIZER_SERVER_BIN = ${OPTIMIZER_SERVER}/target/release/optimizer-server
STATIC_OPTIMIZER_SERVER_BIN = ${OPTIMIZER_SERVER}/target/x86_64-unknown-linux-gnu/release/optimizer-server
.PHONY: build
build:
GOOS=${GOOS} GOARCH=${GOARCH} ${PROXY} go build -ldflags "$(LDFLAGS)" -v -o bin/containerd-nydus-grpc ./cmd/containerd-nydus-grpc
GOOS=${GOOS} GOARCH=${GOARCH} ${PROXY} go build -ldflags "$(LDFLAGS)" -v -o bin/nydus-overlayfs ./cmd/nydus-overlayfs
debug:
GOOS=${GOOS} GOARCH=${GOARCH} ${PROXY} go build -ldflags "$(DEBUG_LDFLAGS)" -gcflags "-N -l" -v -o bin/containerd-nydus-grpc ./cmd/containerd-nydus-grpc
GOOS=${GOOS} GOARCH=${GOARCH} ${PROXY} go build -ldflags "$(DEBUG_LDFLAGS)" -gcflags "-N -l" -v -o bin/nydus-overlayfs ./cmd/nydus-overlayfs
.PHONY: build-optimizer
build-optimizer:
GOOS=${GOOS} GOARCH=${GOARCH} ${PROXY} go build -ldflags "$(LDFLAGS)" -v -o bin/optimizer-nri-plugin ./cmd/optimizer-nri-plugin
make -C tools/optimizer-server release && cp ${OPTIMIZER_SERVER_BIN} ./bin
static-release:
CGO_ENABLED=0 ${PROXY} GOOS=${GOOS} GOARCH=${GOARCH} go build -ldflags "$(LDFLAGS) -extldflags -static" -v -o bin/containerd-nydus-grpc ./cmd/containerd-nydus-grpc
CGO_ENABLED=0 ${PROXY} GOOS=${GOOS} GOARCH=${GOARCH} go build -ldflags "$(LDFLAGS) -extldflags -static" -v -o bin/nydus-overlayfs ./cmd/nydus-overlayfs
CGO_ENABLED=0 ${PROXY} GOOS=${GOOS} GOARCH=${GOARCH} go build -ldflags "$(LDFLAGS) -extldflags -static" -v -o bin/optimizer-nri-plugin ./cmd/optimizer-nri-plugin
make -C tools/optimizer-server static-release && cp ${STATIC_OPTIMIZER_SERVER_BIN} ./bin
# Majorly for cross build for converter package since it is imported by other projects
converter:
GOOS=${GOOS} GOARCH=${GOARCH} ${PROXY} go build -ldflags "$(LDFLAGS)" -v -o bin/converter ./cmd/converter
.PHONY: clean
clean:
rm -f bin/*
rm -rf _out
.PHONY: clean-optimizer
clean-optimizer:
rm -rf bin/optimizer-nri-plugin bin/optimizer-server
make -C tools/optimizer-server clean
.PHONY: install
install:
@echo "+ $@ bin/containerd-nydus-grpc"
@sudo install -D -m 755 bin/containerd-nydus-grpc /usr/local/bin/containerd-nydus-grpc
@echo "+ $@ bin/nydus-overlayfs"
@sudo install -D -m 755 bin/nydus-overlayfs /usr/local/bin/nydus-overlayfs
@if [ ! -e ${NYDUSD_CONFIG} ]; then echo "+ $@ SOURCE_NYDUSD_CONFIG"; sudo install -D -m 664 ${SOURCE_NYDUSD_CONFIG} ${NYDUSD_CONFIG}; fi
@if [ ! -e ${SNAPSHOTTER_CONFIG} ]; then echo "+ $@ ${SOURCE_SNAPSHOTTER_CONFIG}"; sudo install -D -m 664 ${SOURCE_SNAPSHOTTER_CONFIG} ${SNAPSHOTTER_CONFIG}; fi
@sudo ln -f -s /etc/nydus/nydusd-config.${FS_DRIVER}.json /etc/nydus/nydusd-config.json
@echo "+ $@ ${SNAPSHOTTER_SYSTEMD_UNIT_SERVICE}"
@sudo install -D -m 644 ${SNAPSHOTTER_SYSTEMD_UNIT_SERVICE} /etc/systemd/system/nydus-snapshotter.service
@sudo mkdir -p /etc/nydus/certs.d
@if which systemctl >/dev/null; then sudo systemctl enable /etc/systemd/system/nydus-snapshotter.service; sudo systemctl restart nydus-snapshotter; fi
install-optimizer:
sudo install -D -m 755 bin/optimizer-nri-plugin /opt/nri/plugins/02-optimizer-nri-plugin
sudo install -D -m 755 bin/optimizer-server /usr/local/bin/optimizer-server
sudo install -D -m 755 misc/example/optimizer-nri-plugin.conf /etc/nri/conf.d/02-optimizer-nri-plugin.conf
@sudo mkdir -p /opt/nri/optimizer/results
.PHONY: vet
vet:
go vet $(PACKAGES) ./tests
.PHONY: check
check: vet
golangci-lint run
.PHONY: test
test:
go test -race -v -mod=mod -cover ${PACKAGES}
.PHONY: cover
cover:
go test -v -covermode=atomic -coverprofile=coverage.txt $(PACKAGES)
go tool cover -func=coverage.txt
# make smoke TESTS=TestPack
smoke:
${GO_EXECUTABLE_PATH} test -o smoke.tests -c -race -v -cover ./tests
$(SUDO) -E NYDUS_BUILDER=${NYDUS_BUILDER} NYDUS_NYDUSD=${NYDUS_NYDUSD} ./smoke.tests -test.v -test.timeout 10m -test.parallel=8 -test.run=$(TESTS)
.PHONY: integration
integration:
CGO_ENABLED=1 ${PROXY} GOOS=${GOOS} GOARCH=${GOARCH} go build -ldflags '-X "${PKG}/version.Version=${VERSION}" -extldflags "-static"' -race -v -o bin/containerd-nydus-grpc ./cmd/containerd-nydus-grpc
CGO_ENABLED=1 ${PROXY} GOOS=${GOOS} GOARCH=${GOARCH} go build -ldflags '-X "${PKG}/version.Version=${VERSION}" -extldflags "-static"' -race -v -o bin/nydus-overlayfs ./cmd/nydus-overlayfs
$(SUDO) DOCKER_BUILDKIT=1 docker build ${BUILD_ARG_E2E_DOWNLOADS_MIRROR} -t nydus-snapshotter-e2e:0.1 -f integration/Dockerfile .
$(SUDO) docker run --cap-add SYS_ADMIN --security-opt seccomp=unconfined --cgroup-parent=system.slice --cgroupns private --name nydus-snapshotter_e2e --rm --privileged -v /root/.docker:/root/.docker -v `go env GOMODCACHE`:/go/pkg/mod \
-v `go env GOCACHE`:/root/.cache/go-build -v `pwd`:/nydus-snapshotter \
-v /usr/src/linux-headers-${KERNEL_VER}:/usr/src/linux-headers-${KERNEL_VER} \
${ENV_TARGET_IMAGES_FILE} \
nydus-snapshotter-e2e:0.1
nydus-snapshotter-0.13.4/README.md 0000664 0000000 0000000 00000024565 14533742372 0016616 0 ustar 00root root 0000000 0000000 [**[⬇️ Download]**](https://github.com/containerd/nydus-snapshotter/releases)
[**[📖 Website]**](https://nydus.dev/)
[**[☸ Quick Start (Kubernetes)**]](https://github.com/containerd/nydus-snapshotter/blob/main/docs/run_nydus_in_kubernetes.md)
[**[🤓 Quick Start (nerdctl)**]](https://github.com/containerd/nerdctl/blob/master/docs/nydus.md)
[**[❓ FAQs & Troubleshooting]**](https://github.com/dragonflyoss/nydus/wiki/FAQ)
# Nydus Snapshotter
[](https://github.com/containerd/nydus-snapshotter/releases)
[](https://github.com/containerd/nydus-snapshotter/blob/main/LICENSE)

[](https://goreportcard.com/report/github.com/containerd/nydus-snapshotter)
[](https://twitter.com/dragonfly_oss)
[](https://github.com/dragonflyoss/nydus)
Nydus-snapshotter is a **non-core** sub-project of containerd.
Nydus snapshotter is an external plugin of containerd for [Nydus image service](https://nydus.dev) which implements a chunk-based content-addressable filesystem on top of a called `RAFS (Registry Acceleration File System)` format that improves the current OCI image specification, in terms of container launching speed, image space, and network bandwidth efficiency, as well as data integrity with several runtime backends: FUSE, virtiofs and in-kernel [EROFS](https://www.kernel.org/doc/html/latest/filesystems/erofs.html).
Nydus supports lazy pulling feature since pulling image is one of the time-consuming steps in the container lifecycle. Lazy pulling here means a container can run even the image is partially available and necessary chunks of the image are fetched on-demand. Apart from that, Nydus also supports [(e)Stargz](https://github.com/containerd/stargz-snapshotter) and OCI (by using [zran](https://github.com/dragonflyoss/nydus/blob/master/docs/nydus-zran.md)) lazy pulling directly **WITHOUT** any explicit conversion.
For more details about how to build Nydus container image, please refer to [nydusify](https://github.com/dragonflyoss/nydus/blob/master/docs/nydusify.md) conversion tool and [acceld](https://github.com/goharbor/acceleration-service).
## Architecture
### Architecture Based on FUSE

### Architecture Based on Fscache/Erofs

## Building
Just invoke `make` and check out the output executable binary `./bin/containerd-nydus-grpc`
```bash
make
```
## Integrate Nydus-snapshotter into Containerd
The following document will describe how to manually configure containerd + Nydus snapshotter. If you want to run Nydus snapshotter in Kubernetes cluster, you can try to use helm or run nydus snapshotter as a container. You can refer to [this documentation](./docs/run_nydus_in_kubernetes.md).
Containerd provides a general mechanism to exploit different types of snapshotters. Please ensure your containerd's version is 1.4.0 or above.
Add Nydus as a proxy plugin into containerd's configuration file which may be located at `/etc/containerd/config.toml`.
```toml
# The `address` field specifies through which socket snapshotter and containerd communicate.
[proxy_plugins]
[proxy_plugins.nydus]
type = "snapshot"
address = "/run/containerd-nydus/containerd-nydus-grpc.sock"
```
Restart your containerd service making the change take effect. Assume that your node is systemd based, restart the service as below:
```bash
systemctl restart containerd
```
### Get Nydus Binaries
Get `nydusd` `nydus-image` and `nydusctl` binaries from [nydus releases page](https://github.com/dragonflyoss/nydus/releases).
It's suggested to install the binaries to your system path. `nydusd` is FUSE userspace daemon and a vhost-user-fs backend. Nydus-snapshotter
will fork a nydusd process when necessary.
### Configure Nydus
Please follow instructions to [configure nydus](./docs/configure_nydus.md) in order to make it work properly in your environment.
### Start Nydus Snapshotter
Nydus-snapshotter is implemented as a [proxy plugin](https://github.com/containerd/containerd/blob/04985039cede6aafbb7dfb3206c9c4d04e2f924d/PLUGINS.md#proxy-plugins) (`containerd-nydus-grpc`) for containerd.
Assume your server is systemd based, install nydus-snapshotter:
Note: `nydusd` and `nydus-image` should be found from $PATH.
```bash
make install
systemctl restart containerd
```
Or you can start nydus-snapshotter manually.
```bash
# `--nydusd` specifies the path to nydusd binary. If `nydusd` and `nydus-image` are installed, `--nydusd` and `--nydus-image`can be omitted.
# Otherwise, provide them in below command line.
# `address` is the domain socket that you configured in containerd configuration file
# `--nydusd-config` is the path to `nydusd` configuration file
# The default nydus-snapshotter work directory is located at `/var/lib/containerd-nydus`
$ sudo ./containerd-nydus-grpc --config /etc/nydus/config.toml --nydusd-config /etc/nydus/nydusd-config.json --log-to-stdout
```
### Validate Nydus-snapshotter Setup
Utilize containerd's `ctr` CLI command to validate if nydus-snapshotter is set up successfully.
```bash
$ ctr -a /run/containerd/containerd.sock plugin ls
TYPE ID PLATFORMS STATUS
io.containerd.snapshotter.v1 nydus - ok
```
### Optimize Nydus Image as per Workload
Nydus usually prefetch image data to local filesystem before a real user on-demand read. It helps to improve the performance and availability. A containerd NRI plugin [container image optimizer](docs/optimize_nydus_image.md) can be used to generate nydus image building suggestions to optimize your nydus image making the nydusd runtime match your workload IO pattern. The optimized nydus image has
a better performance.
## Quickstart Container with Lazy Pulling
### Start Container on single Node
Start container using `nerdctl` (>=v0.22) which has native nydus support with `nydus-snapshotter`.
```bash
# Start container by `nerdctl`
nerdctl --snapshotter nydus run ghcr.io/dragonflyoss/image-service/nginx:nydus-latest
```
### Start Container in Kubernetes Cluster
Change containerd's CRI configuration:
```toml
[plugins."io.containerd.grpc.v1.cri".containerd]
snapshotter = "nydus"
disable_snapshot_annotations = false
```
Use `crictl` to debug starting container via Kubernetes CRI. Dry run [steps](./docs/crictl_dry_run.md) of using `crictl` can be found in [documents](./docs).
### Setup with nydus-snapshotter image
We can also use the `nydus-snapshotter` container image when we want to put Nydus stuffs inside a container. See the [nydus-snapshotter example](./misc/example/README.md) for how to setup and use it.
## Integrate with Dragonfly to Distribute Images by P2P
Nydus is a sub-project of [Dragonfly](https://github.com/dragonflyoss/Dragonfly2). So it closely works with Dragonfly to distribute container images in a fast and efficient P2P fashion to reduce network latency and lower the pressure on a single-point of the registry.
### Quickstart Dragonfly & Nydus in Kubernetes
We recommend using the Dragonfly P2P data distribution system to further improve the runtime performance of Nydus images.
If you want to deploy Dragonfly and Nydus at the same time, please refer to this **[Quick Start](https://github.com/dragonflyoss/helm-charts/blob/main/INSTALL.md)**.
### Config Dragonfly mode
Dragonfly supports both **mirror** mode and HTTP **proxy** mode to boost the containers startup. It is suggested to use Dragonfly mirror mode. To integrate with Dragonfly in the mirror mode, please provide registry mirror in nydusd's json configuration file in section `device.backend.mirrors`
```json
{
"mirrors": [
{
"host": "http://127.0.0.1:65001",
"headers": "https://index.docker.io/v1/"
}
]
}
```
### Hot updating mirror configurations
In addition to setting the registry mirror in nydusd's json configuration file, `nydus-snapshotter` also supports hot updating mirror configurations. You can set the configuration directory in nudus-snapshotter's toml configuration file with `remote.mirrors_config.dir`. The empty `remote.mirrors_config.dir` means disabling it.
```toml
[remote.mirrors_config]
dir = "/etc/nydus/certs.d"
```
Configuration file is compatible with containerd's configuration file in toml format.
```toml
[host]
[host."http://127.0.0.1:65001"]
[host."http://127.0.0.1:65001".header]
# NOTE: For Dragonfly, the HTTP scheme must be explicitly specified.
X-Dragonfly-Registry = ["https://p2p-nydus.com"]
```
Mirror configurations loaded from nydusd's json file will be overwritten before pulling image if the valid mirror configuration items loaded from `remote.mirrors_config.dir` are greater than 0.
## Community
Nydus aims to form a **vendor-neutral opensource** image distribution solution to all communities.
Questions, bug reports, technical discussion, feature requests and contribution are always welcomed!
We're very pleased to hear your use cases any time.
Feel free to reach/join us via Slack and/or Dingtalk.
- **Slack:** [Nydus Workspace](https://join.slack.com/t/nydusimageservice/shared_invite/zt-pz4qvl4y-WIh4itPNILGhPS8JqdFm_w)
- **Twitter:** [@dragonfly_oss](https://twitter.com/dragonfly_oss)
- **Dingtalk:** [34971767](https://qr.dingtalk.com/action/joingroup?code=v1,k1,ioWGzuDZEIO10Bf+/ohz4RcQqAkW0MtOwoG1nbbMxQg=&_dt_no_comment=1&origin=11)
- **Technical Meeting:** Every Wednesday at 06:00 UTC (Beijing, Shanghai 14:00), please see our [HackMD](https://hackmd.io/@Nydus/Bk8u2X0p9) page for more information.
## License
[](https://app.fossa.com/projects/git%2Bgithub.com%2Fcontainerd%2Fnydus-snapshotter?ref=badge_large)
nydus-snapshotter-0.13.4/cmd/ 0000775 0000000 0000000 00000000000 14533742372 0016066 5 ustar 00root root 0000000 0000000 nydus-snapshotter-0.13.4/cmd/containerd-nydus-grpc/ 0000775 0000000 0000000 00000000000 14533742372 0022305 5 ustar 00root root 0000000 0000000 nydus-snapshotter-0.13.4/cmd/containerd-nydus-grpc/main.go 0000664 0000000 0000000 00000007367 14533742372 0023575 0 ustar 00root root 0000000 0000000 /*
* Copyright (c) 2020. Ant Group. All rights reserved.
* Copyright (c) 2022. Nydus Developers. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*/
package main
import (
"fmt"
"os"
"github.com/containerd/containerd/log"
"github.com/pkg/errors"
"github.com/urfave/cli/v2"
"github.com/containerd/nydus-snapshotter/config"
"github.com/containerd/nydus-snapshotter/internal/flags"
"github.com/containerd/nydus-snapshotter/internal/logging"
"github.com/containerd/nydus-snapshotter/pkg/errdefs"
"github.com/containerd/nydus-snapshotter/version"
)
func main() {
flags := flags.NewFlags()
app := &cli.App{
Name: "containerd-nydus-grpc",
Usage: "Nydus remote snapshotter for containerd",
Version: version.Version,
Flags: flags.F,
HideVersion: true,
Action: func(c *cli.Context) error {
if flags.Args.PrintVersion {
fmt.Println("Version: ", version.Version)
fmt.Println("Revision: ", version.Revision)
fmt.Println("Go version: ", version.GoVersion)
fmt.Println("Build time: ", version.BuildTimestamp)
return nil
}
snapshotterConfigPath := flags.Args.SnapshotterConfigPath
var defaultSnapshotterConfig config.SnapshotterConfig
var snapshotterConfig config.SnapshotterConfig
if err := defaultSnapshotterConfig.FillUpWithDefaults(); err != nil {
return errors.New("failed to generate nydus default configuration")
}
// Once snapshotter's configuration file is provided, parse it and let command line parameters override it.
if snapshotterConfigPath != "" {
if c, err := config.LoadSnapshotterConfig(snapshotterConfigPath); err == nil {
// Command line parameters override the snapshotter's configurations for backwards compatibility
if err := config.ParseParameters(flags.Args, c); err != nil {
return errors.Wrap(err, "failed to parse commandline options")
}
snapshotterConfig = *c
} else {
return errors.Wrapf(err, "failed to load snapshotter configuration from %q", snapshotterConfigPath)
}
} else {
if err := config.ParseParameters(flags.Args, &snapshotterConfig); err != nil {
return errors.Wrap(err, "failed to parse commandline options")
}
}
if err := config.MergeConfig(&snapshotterConfig, &defaultSnapshotterConfig); err != nil {
return errors.Wrap(err, "failed to merge configurations")
}
if err := config.ValidateConfig(&snapshotterConfig); err != nil {
return errors.Wrapf(err, "failed to validate configurations")
}
if err := config.ProcessConfigurations(&snapshotterConfig); err != nil {
return errors.Wrap(err, "failed to process configurations")
}
if err := config.SetUpEnvironment(&snapshotterConfig); err != nil {
return errors.Wrap(err, "failed to setup environment")
}
ctx := logging.WithContext()
logConfig := &snapshotterConfig.LoggingConfig
logRotateArgs := &logging.RotateLogArgs{
RotateLogMaxSize: logConfig.RotateLogMaxSize,
RotateLogMaxBackups: logConfig.RotateLogMaxBackups,
RotateLogMaxAge: logConfig.RotateLogMaxAge,
RotateLogLocalTime: logConfig.RotateLogLocalTime,
RotateLogCompress: logConfig.RotateLogCompress,
}
if err := logging.SetUp(logConfig.LogLevel, logConfig.LogToStdout, logConfig.LogDir, logRotateArgs); err != nil {
return errors.Wrap(err, "failed to setup logger")
}
log.L.Infof("Start nydus-snapshotter. Version: %s, PID: %d, FsDriver: %s, DaemonMode: %s",
version.Version, os.Getpid(), config.GetFsDriver(), snapshotterConfig.DaemonMode)
return Start(ctx, &snapshotterConfig)
},
}
if err := app.Run(os.Args); err != nil {
if errdefs.IsConnectionClosed(err) {
log.L.Info("nydus-snapshotter exited")
} else {
log.L.WithError(err).Fatal("failed to start nydus-snapshotter")
}
}
}
nydus-snapshotter-0.13.4/cmd/containerd-nydus-grpc/snapshotter.go 0000664 0000000 0000000 00000006041 14533742372 0025207 0 ustar 00root root 0000000 0000000 /*
* Copyright (c) 2020. Ant Group. All rights reserved.
* Copyright (c) 2022. Nydus Developers. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*/
package main
import (
"context"
"net"
"os"
"path/filepath"
"github.com/pkg/errors"
"github.com/containerd/nydus-snapshotter/config"
"github.com/containerd/nydus-snapshotter/pkg/auth"
"github.com/containerd/nydus-snapshotter/pkg/utils/signals"
"github.com/containerd/nydus-snapshotter/snapshot"
api "github.com/containerd/containerd/api/services/snapshots/v1"
"github.com/containerd/containerd/contrib/snapshotservice"
"github.com/containerd/containerd/log"
"github.com/containerd/containerd/snapshots"
"google.golang.org/grpc"
)
func Start(ctx context.Context, cfg *config.SnapshotterConfig) error {
ctx, cancel := context.WithCancel(ctx)
defer cancel()
rs, err := snapshot.NewSnapshotter(ctx, cfg)
if err != nil {
return errors.Wrap(err, "failed to initialize snapshotter")
}
stopSignal := signals.SetupSignalHandler()
opt := ServeOptions{
ListeningSocketPath: cfg.Address,
EnableCRIKeychain: cfg.RemoteConfig.AuthConfig.EnableCRIKeychain,
ImageServiceAddress: cfg.RemoteConfig.AuthConfig.ImageServiceAddress,
}
if cfg.RemoteConfig.AuthConfig.EnableKubeconfigKeychain {
if err := auth.InitKubeSecretListener(ctx, cfg.RemoteConfig.AuthConfig.KubeconfigPath); err != nil {
return err
}
}
return Serve(ctx, rs, opt, stopSignal)
}
type ServeOptions struct {
ListeningSocketPath string
EnableCRIKeychain bool
ImageServiceAddress string
}
func Serve(ctx context.Context, sn snapshots.Snapshotter, options ServeOptions, stop <-chan struct{}) error {
err := ensureSocketNotExists(options.ListeningSocketPath)
if err != nil {
return err
}
rpc := grpc.NewServer()
if rpc == nil {
return errors.New("start gRPC server")
}
api.RegisterSnapshotsServer(rpc, snapshotservice.FromSnapshotter(sn))
listener, err := net.Listen("unix", options.ListeningSocketPath)
if err != nil {
return errors.Wrapf(err, "listen socket %q", options.ListeningSocketPath)
}
if options.EnableCRIKeychain {
auth.AddImageProxy(ctx, rpc, options.ImageServiceAddress)
}
go func() {
<-stop
log.L.Infof("Shutting down nydus-snapshotter!")
if err := sn.Close(); err != nil {
log.L.WithError(err).Errorf("Closing snapshotter error")
}
if err := listener.Close(); err != nil {
log.L.Errorf("Failed to close listener %s, err: %v", options.ListeningSocketPath, err)
}
}()
return rpc.Serve(listener)
}
func ensureSocketNotExists(listeningSocketPath string) error {
if err := os.MkdirAll(filepath.Dir(listeningSocketPath), 0700); err != nil {
return errors.Wrapf(err, "failed to create directory %q", filepath.Dir(listeningSocketPath))
}
finfo, err := os.Stat(listeningSocketPath)
// err is nil means listening socket path exists, remove before serve
if err == nil {
if finfo.Mode()&os.ModeSocket == 0 {
return errors.Errorf("file %s is not a socket", listeningSocketPath)
}
err := os.Remove(listeningSocketPath)
if err != nil {
return err
}
}
return nil
}
nydus-snapshotter-0.13.4/cmd/converter/ 0000775 0000000 0000000 00000000000 14533742372 0020075 5 ustar 00root root 0000000 0000000 nydus-snapshotter-0.13.4/cmd/converter/main.go 0000664 0000000 0000000 00000000340 14533742372 0021345 0 ustar 00root root 0000000 0000000 package main
// Import the converter package so that it can be compiled during
// `go build` to ensure cross-compilation compatibility.
import (
_ "github.com/containerd/nydus-snapshotter/pkg/converter"
)
func main() {
}
nydus-snapshotter-0.13.4/cmd/nydus-overlayfs/ 0000775 0000000 0000000 00000000000 14533742372 0021240 5 ustar 00root root 0000000 0000000 nydus-snapshotter-0.13.4/cmd/nydus-overlayfs/main.go 0000664 0000000 0000000 00000007271 14533742372 0022522 0 ustar 00root root 0000000 0000000 package main
import (
"fmt"
"log"
"os"
"strings"
"syscall"
"github.com/pkg/errors"
"github.com/urfave/cli/v2"
"golang.org/x/sys/unix"
)
const (
// Extra mount option to pass Nydus specific information from snapshotter to runtime through containerd.
extraOptionKey = "extraoption="
// Kata virtual volume infmation passed from snapshotter to runtime through containerd, superset of `extraOptionKey`.
// Please refer to `KataVirtualVolume` in https://github.com/kata-containers/kata-containers/blob/main/src/libs/kata-types/src/mount.rs
kataVolumeOptionKey = "io.katacontainers.volume="
)
var (
Version = "v0.1"
BuildTime = "unknown"
)
/*
containerd run fuse.mount format: nydus-overlayfs overlay /tmp/ctd-volume107067851
-o lowerdir=/foo/lower2:/foo/lower1,upperdir=/foo/upper,workdir=/foo/work,extraoption={...},dev,suid]
*/
type mountArgs struct {
fsType string
target string
options []string
}
func parseArgs(args []string) (*mountArgs, error) {
margs := &mountArgs{
fsType: args[0],
target: args[1],
}
if margs.fsType != "overlay" {
return nil, errors.Errorf("invalid filesystem type %s for overlayfs", margs.fsType)
}
if len(margs.target) == 0 {
return nil, errors.New("empty overlayfs mount target")
}
if args[2] == "-o" && len(args[3]) != 0 {
for _, opt := range strings.Split(args[3], ",") {
// filter Nydus specific options
if strings.HasPrefix(opt, extraOptionKey) || strings.HasPrefix(opt, kataVolumeOptionKey) {
continue
}
margs.options = append(margs.options, opt)
}
}
if len(margs.options) == 0 {
return nil, errors.New("empty overlayfs mount options")
}
return margs, nil
}
func parseOptions(options []string) (int, string) {
flagsTable := map[string]int{
"async": unix.MS_SYNCHRONOUS,
"atime": unix.MS_NOATIME,
"bind": unix.MS_BIND,
"defaults": 0,
"dev": unix.MS_NODEV,
"diratime": unix.MS_NODIRATIME,
"dirsync": unix.MS_DIRSYNC,
"exec": unix.MS_NOEXEC,
"mand": unix.MS_MANDLOCK,
"noatime": unix.MS_NOATIME,
"nodev": unix.MS_NODEV,
"nodiratime": unix.MS_NODIRATIME,
"noexec": unix.MS_NOEXEC,
"nomand": unix.MS_MANDLOCK,
"norelatime": unix.MS_RELATIME,
"nostrictatime": unix.MS_STRICTATIME,
"nosuid": unix.MS_NOSUID,
"rbind": unix.MS_BIND | unix.MS_REC,
"relatime": unix.MS_RELATIME,
"remount": unix.MS_REMOUNT,
"ro": unix.MS_RDONLY,
"rw": unix.MS_RDONLY,
"strictatime": unix.MS_STRICTATIME,
"suid": unix.MS_NOSUID,
"sync": unix.MS_SYNCHRONOUS,
}
var (
flags int
data []string
)
for _, o := range options {
if f, exist := flagsTable[o]; exist {
flags |= f
} else {
data = append(data, o)
}
}
return flags, strings.Join(data, ",")
}
func run(args cli.Args) error {
margs, err := parseArgs(args.Slice())
if err != nil {
return errors.Wrap(err, "parse mount options")
}
flags, data := parseOptions(margs.options)
err = syscall.Mount(margs.fsType, margs.target, margs.fsType, uintptr(flags), data)
if err != nil {
return errors.Wrapf(err, "mount overlayfs by syscall")
}
return nil
}
func main() {
app := &cli.App{
Name: "NydusOverlayfs",
Usage: "FUSE mount helper for containerd to filter out Nydus specific options",
Version: fmt.Sprintf("%s.%s", Version, BuildTime),
UsageText: "[Usage]: nydus-overlayfs overlay -o ",
Action: func(c *cli.Context) error {
return run(c.Args())
},
Before: func(c *cli.Context) error {
if c.NArg() != 4 {
cli.ShowAppHelpAndExit(c, 1)
}
return nil
},
}
err := app.Run(os.Args)
if err != nil {
log.Fatal(err)
}
os.Exit(0)
}
nydus-snapshotter-0.13.4/cmd/optimizer-nri-plugin/ 0000775 0000000 0000000 00000000000 14533742372 0022172 5 ustar 00root root 0000000 0000000 nydus-snapshotter-0.13.4/cmd/optimizer-nri-plugin/main.go 0000664 0000000 0000000 00000015613 14533742372 0023453 0 ustar 00root root 0000000 0000000 /*
* Copyright (c) 2023. Nydus Developers. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*/
package main
import (
"context"
"fmt"
"io"
"log/syslog"
"os"
"path/filepath"
"strings"
"time"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/urfave/cli/v2"
"github.com/containerd/containerd/reference/docker"
"github.com/containerd/nri/pkg/api"
"github.com/containerd/nri/pkg/stub"
"github.com/containerd/nydus-snapshotter/pkg/errdefs"
"github.com/containerd/nydus-snapshotter/pkg/fanotify"
"github.com/containerd/nydus-snapshotter/version"
"github.com/pelletier/go-toml"
)
const (
defaultEvents = "StartContainer,StopContainer"
defaultServerPath = "/usr/local/bin/optimizer-server"
defaultPersistDir = "/opt/nri/optimizer/results"
)
type PluginConfig struct {
Events []string `toml:"events"`
ServerPath string `toml:"server_path"`
PersistDir string `toml:"persist_dir"`
Readable bool `toml:"readable"`
Timeout int `toml:"timeout"`
Overwrite bool `toml:"overwrite"`
}
type PluginArgs struct {
PluginName string
PluginIdx string
PluginEvents string
Config PluginConfig
}
type Flags struct {
Args *PluginArgs
F []cli.Flag
}
func buildFlags(args *PluginArgs) []cli.Flag {
return []cli.Flag{
&cli.StringFlag{
Name: "name",
Usage: "plugin name to register to NRI",
Destination: &args.PluginName,
},
&cli.StringFlag{
Name: "idx",
Usage: "plugin index to register to NRI",
Destination: &args.PluginIdx,
},
&cli.StringFlag{
Name: "events",
Value: defaultEvents,
Usage: "the events that containerd subscribes to. DO NOT CHANGE THIS.",
Destination: &args.PluginEvents,
},
&cli.StringFlag{
Name: "server-path",
Value: defaultServerPath,
Usage: "the path of optimizer server binary",
Destination: &args.Config.ServerPath,
},
&cli.StringFlag{
Name: "persist-dir",
Value: defaultPersistDir,
Usage: "the directory to persist accessed files list for container",
Destination: &args.Config.PersistDir,
},
&cli.BoolFlag{
Name: "readable",
Value: false,
Usage: "whether to make the csv file human readable",
Destination: &args.Config.Readable,
},
&cli.IntFlag{
Name: "timeout",
Value: 0,
Usage: "the timeout to kill optimizer server, 0 to disable it",
Destination: &args.Config.Timeout,
},
&cli.BoolFlag{
Name: "overwrite",
Usage: "whether to overwrite the existed persistent files",
Destination: &args.Config.Overwrite,
},
}
}
func NewPluginFlags() *Flags {
var args PluginArgs
return &Flags{
Args: &args,
F: buildFlags(&args),
}
}
type plugin struct {
stub stub.Stub
mask stub.EventMask
}
var (
cfg PluginConfig
log *logrus.Logger
logWriter *syslog.Writer
_ = stub.ConfigureInterface(&plugin{})
globalFanotifyServer = make(map[string]*fanotify.Server)
)
const (
imageNameLabel = "io.kubernetes.cri.image-name"
)
func (p *plugin) Configure(config, runtime, version string) (stub.EventMask, error) {
log.Infof("got configuration data: %q from runtime %s %s", config, runtime, version)
if config == "" {
return p.mask, nil
}
tree, err := toml.Load(config)
if err != nil {
return 0, errors.Wrap(err, "parse TOML")
}
if err := tree.Unmarshal(&cfg); err != nil {
return 0, err
}
p.mask, err = api.ParseEventMask(cfg.Events...)
if err != nil {
return 0, errors.Wrap(err, "parse events in configuration")
}
log.Infof("configuration: %#v", cfg)
return p.mask, nil
}
func (p *plugin) StartContainer(_ *api.PodSandbox, container *api.Container) error {
dir, imageName, err := GetImageName(container.Annotations)
if err != nil {
return err
}
persistDir := filepath.Join(cfg.PersistDir, dir)
if err := os.MkdirAll(persistDir, os.ModePerm); err != nil {
return err
}
persistFile := filepath.Join(persistDir, imageName)
if cfg.Timeout > 0 {
persistFile = fmt.Sprintf("%s.timeout%ds", persistFile, cfg.Timeout)
}
fanotifyServer := fanotify.NewServer(cfg.ServerPath, container.Pid, imageName, persistFile, cfg.Readable, cfg.Overwrite, time.Duration(cfg.Timeout)*time.Second, logWriter)
if err := fanotifyServer.RunServer(); err != nil {
return err
}
globalFanotifyServer[imageName] = fanotifyServer
return nil
}
func (p *plugin) StopContainer(_ *api.PodSandbox, container *api.Container) ([]*api.ContainerUpdate, error) {
var update = []*api.ContainerUpdate{}
_, imageName, err := GetImageName(container.Annotations)
if err != nil {
return update, err
}
if fanotifyServer, ok := globalFanotifyServer[imageName]; ok {
fanotifyServer.StopServer()
} else {
return nil, errors.New("can not find fanotify server for container image " + imageName)
}
return update, nil
}
func GetImageName(annotations map[string]string) (string, string, error) {
named, err := docker.ParseDockerRef(annotations[imageNameLabel])
if err != nil {
return "", "", err
}
nameTagged := named.(docker.NamedTagged)
repo := docker.Path(nameTagged)
dir := filepath.Dir(repo)
image := filepath.Base(repo)
imageName := image + ":" + nameTagged.Tag()
return dir, imageName, nil
}
func (p *plugin) onClose() {
for _, fanotifyServer := range globalFanotifyServer {
fanotifyServer.StopServer()
}
os.Exit(0)
}
func main() {
flags := NewPluginFlags()
app := &cli.App{
Name: "optimizer-nri-plugin",
Usage: "Optimizer client for NRI plugin to manage optimizer server",
Version: version.Version,
Flags: flags.F,
HideVersion: true,
Action: func(c *cli.Context) error {
var (
opts []stub.Option
err error
)
cfg = flags.Args.Config
log = logrus.StandardLogger()
log.SetFormatter(&logrus.TextFormatter{
PadLevelText: true,
})
logWriter, err = syslog.New(syslog.LOG_INFO, "optimizer-nri-plugin")
if err == nil {
log.SetOutput(io.MultiWriter(os.Stdout, logWriter))
}
if flags.Args.PluginName != "" {
opts = append(opts, stub.WithPluginName(flags.Args.PluginName))
}
if flags.Args.PluginIdx != "" {
opts = append(opts, stub.WithPluginIdx(flags.Args.PluginIdx))
}
p := &plugin{}
if p.mask, err = api.ParseEventMask(flags.Args.PluginEvents); err != nil {
log.Fatalf("failed to parse events: %v", err)
}
cfg.Events = strings.Split(flags.Args.PluginEvents, ",")
if p.stub, err = stub.New(p, append(opts, stub.WithOnClose(p.onClose))...); err != nil {
log.Fatalf("failed to create plugin stub: %v", err)
}
err = p.stub.Run(context.Background())
if err != nil {
log.Errorf("plugin exited with error %v", err)
os.Exit(1)
}
return nil
},
}
if err := app.Run(os.Args); err != nil {
if errdefs.IsConnectionClosed(err) {
log.Info("optimizer NRI plugin exited")
} else {
log.WithError(err).Fatal("failed to start optimizer NRI plugin")
}
}
}
nydus-snapshotter-0.13.4/cmd/prefetchfiles-nri-plugin/ 0000775 0000000 0000000 00000000000 14533742372 0022773 5 ustar 00root root 0000000 0000000 nydus-snapshotter-0.13.4/cmd/prefetchfiles-nri-plugin/main.go 0000664 0000000 0000000 00000011505 14533742372 0024250 0 ustar 00root root 0000000 0000000 /*
* Copyright (c) 2023. Nydus Developers. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*/
package main
import (
"context"
"fmt"
"io"
"log/syslog"
"net"
"net/http"
"os"
"path/filepath"
"strings"
"github.com/containerd/nri/pkg/api"
"github.com/containerd/nri/pkg/stub"
"github.com/pelletier/go-toml"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/urfave/cli/v2"
"github.com/containerd/nydus-snapshotter/pkg/errdefs"
"github.com/containerd/nydus-snapshotter/version"
)
const (
endpointPrefetch = "/api/v1/prefetch"
defaultEvents = "RunPodSandbox"
defaultSystemControllerAddress = "/run/containerd-nydus/system.sock"
defaultPrefetchConfigDir = "/etc/nydus"
nydusPrefetchAnnotation = "containerd.io/nydus-prefetch"
)
type PluginArgs struct {
PluginName string
PluginIdx string
SocketAddress string
}
type Flags struct {
Args *PluginArgs
Flag []cli.Flag
}
func buildFlags(args *PluginArgs) []cli.Flag {
return []cli.Flag{
&cli.StringFlag{
Name: "name",
Usage: "plugin name to register to NRI",
Destination: &args.PluginName,
},
&cli.StringFlag{
Name: "idx",
Usage: "plugin index to register to NRI",
Destination: &args.PluginIdx,
},
&cli.StringFlag{
Name: "socket-addr",
Value: defaultSystemControllerAddress,
Usage: "unix domain socket address. If defined in the configuration file, there is no need to add ",
Destination: &args.SocketAddress,
},
}
}
func NewPluginFlags() *Flags {
var args PluginArgs
return &Flags{
Args: &args,
Flag: buildFlags(&args),
}
}
type plugin struct {
stub stub.Stub
mask stub.EventMask
}
var (
globalSocket string
log *logrus.Logger
logWriter *syslog.Writer
)
// sendDataOverHTTP sends the prefetch data to the specified endpoint over HTTP using a Unix socket.
func sendDataOverHTTP(data string, endpoint, sock string) error {
url := fmt.Sprintf("http://unix%s", endpoint)
req, err := http.NewRequest(http.MethodPut, url, strings.NewReader(data))
if err != nil {
return err
}
client := &http.Client{
Transport: &http.Transport{
DialContext: func(_ context.Context, _, _ string) (net.Conn, error) {
return net.Dial("unix", sock)
},
},
}
resp, err := client.Do(req)
if err != nil {
return err
}
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("failed to send data, status code: %d", resp.StatusCode)
}
resp.Body.Close()
return nil
}
func (p *plugin) RunPodSandbox(pod *api.PodSandbox) error {
prefetchList, ok := pod.Annotations[nydusPrefetchAnnotation]
if !ok {
return nil
}
err := sendDataOverHTTP(prefetchList, endpointPrefetch, globalSocket)
if err != nil {
log.Errorf("failed to send data: %v", err)
return err
}
return nil
}
func main() {
flags := NewPluginFlags()
app := &cli.App{
Name: "prefetch-nri-plugin",
Usage: "NRI plugin for obtaining and transmitting prefetch files path",
Version: version.Version,
Flags: flags.Flag,
HideVersion: true,
Action: func(c *cli.Context) error {
var (
opts []stub.Option
err error
)
log = logrus.StandardLogger()
configFileName := "prefetchConfig.toml"
configDir := defaultPrefetchConfigDir
configFilePath := filepath.Join(configDir, configFileName)
config, err := toml.LoadFile(configFilePath)
if err != nil {
log.Warnf("failed to read config file: %v", err)
}
configSocketAddrRaw := config.Get("file_prefetch.socket_address")
if configSocketAddrRaw != nil {
if configSocketAddr, ok := configSocketAddrRaw.(string); ok {
globalSocket = configSocketAddr
} else {
log.Warnf("failed to read config: 'file_prefetch.socket_address' is not a string")
}
} else {
globalSocket = flags.Args.SocketAddress
}
log.SetFormatter(&logrus.TextFormatter{
PadLevelText: true,
})
logWriter, err = syslog.New(syslog.LOG_INFO, "prefetch-nri-plugin")
if err == nil {
log.SetOutput(io.MultiWriter(os.Stdout, logWriter))
}
if flags.Args.PluginName != "" {
opts = append(opts, stub.WithPluginName(flags.Args.PluginName))
}
if flags.Args.PluginIdx != "" {
opts = append(opts, stub.WithPluginIdx(flags.Args.PluginIdx))
}
p := &plugin{}
if p.mask, err = api.ParseEventMask(defaultEvents); err != nil {
log.Fatalf("failed to parse events: %v", err)
}
if p.stub, err = stub.New(p, opts...); err != nil {
log.Fatalf("failed to create plugin stub: %v", err)
}
err = p.stub.Run(context.Background())
if err != nil {
return errors.Wrap(err, "plugin exited")
}
return nil
},
}
if err := app.Run(os.Args); err != nil {
if errdefs.IsConnectionClosed(err) {
log.Info("prefetch NRI plugin exited")
} else {
log.WithError(err).Fatal("failed to start prefetch NRI plugin")
}
}
}
nydus-snapshotter-0.13.4/config/ 0000775 0000000 0000000 00000000000 14533742372 0016570 5 ustar 00root root 0000000 0000000 nydus-snapshotter-0.13.4/config/config.go 0000664 0000000 0000000 00000026746 14533742372 0020403 0 ustar 00root root 0000000 0000000 /*
* Copyright (c) 2020. Ant Group. All rights reserved.
* Copyright (c) 2022. Nydus Developers. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*/
package config
import (
"os"
"github.com/imdario/mergo"
"github.com/pelletier/go-toml"
"github.com/pkg/errors"
"github.com/containerd/nydus-snapshotter/internal/constant"
"github.com/containerd/nydus-snapshotter/internal/flags"
"github.com/containerd/nydus-snapshotter/pkg/cgroup"
"github.com/containerd/nydus-snapshotter/pkg/errdefs"
"github.com/containerd/nydus-snapshotter/pkg/utils/file"
"github.com/containerd/nydus-snapshotter/pkg/utils/parser"
"github.com/containerd/nydus-snapshotter/pkg/utils/sysinfo"
)
func init() {
recoverPolicyParser = map[string]DaemonRecoverPolicy{
RecoverPolicyNone.String(): RecoverPolicyNone,
RecoverPolicyRestart.String(): RecoverPolicyRestart,
RecoverPolicyFailover.String(): RecoverPolicyFailover}
}
// Define a policy how to fork nydusd daemon and attach file system instances to serve.
type DaemonMode string
const (
// Spawn a dedicated nydusd for each RAFS instance.
DaemonModeMultiple DaemonMode = DaemonMode(constant.DaemonModeMultiple)
// Spawn a dedicated nydusd for each RAFS instance.
DaemonModeDedicated DaemonMode = DaemonMode(constant.DaemonModeDedicated)
// Share a global nydusd to serve all RAFS instances.
DaemonModeShared DaemonMode = DaemonMode(constant.DaemonModeShared)
// Do not spawn nydusd for RAFS instances.
//
// For tarfs and rund, there's no need to create nydusd to serve RAFS instances,
// the snapshotter just returns mount slices with additional information for runC/runD
// to manage those snapshots.
DaemonModeNone DaemonMode = DaemonMode(constant.DaemonModeNone)
DaemonModeInvalid DaemonMode = DaemonMode(constant.DaemonModeInvalid)
)
func parseDaemonMode(m string) (DaemonMode, error) {
switch m {
case string(DaemonModeMultiple):
return DaemonModeDedicated, nil
case string(DaemonModeDedicated):
return DaemonModeDedicated, nil
case string(DaemonModeShared):
return DaemonModeShared, nil
case string(DaemonModeNone):
return DaemonModeNone, nil
default:
return DaemonModeInvalid, errors.Errorf("invalid daemon mode %q", m)
}
}
type DaemonRecoverPolicy int
const (
RecoverPolicyInvalid DaemonRecoverPolicy = iota
RecoverPolicyNone
RecoverPolicyRestart
RecoverPolicyFailover
)
func (p DaemonRecoverPolicy) String() string {
switch p {
case RecoverPolicyNone:
return "none"
case RecoverPolicyRestart:
return "restart"
case RecoverPolicyFailover:
return "failover"
case RecoverPolicyInvalid:
fallthrough
default:
return ""
}
}
var recoverPolicyParser map[string]DaemonRecoverPolicy
func ParseRecoverPolicy(p string) (DaemonRecoverPolicy, error) {
policy, ok := recoverPolicyParser[p]
if !ok {
return RecoverPolicyInvalid, errors.Errorf("invalid recover policy %q", p)
}
return policy, nil
}
const (
FsDriverBlockdev string = constant.FsDriverBlockdev
FsDriverFusedev string = constant.FsDriverFusedev
FsDriverFscache string = constant.FsDriverFscache
FsDriverNodev string = constant.FsDriverNodev
FsDriverProxy string = constant.FsDriverProxy
)
type Experimental struct {
EnableStargz bool `toml:"enable_stargz"`
EnableReferrerDetect bool `toml:"enable_referrer_detect"`
TarfsConfig TarfsConfig `toml:"tarfs"`
}
type TarfsConfig struct {
EnableTarfs bool `toml:"enable_tarfs"`
MountTarfsOnHost bool `toml:"mount_tarfs_on_host"`
TarfsHint bool `toml:"tarfs_hint"`
MaxConcurrentProc int `toml:"max_concurrent_proc"`
ExportMode string `toml:"export_mode"`
}
type CgroupConfig struct {
Enable bool `toml:"enable"`
MemoryLimit string `toml:"memory_limit"`
}
// Configure how to start and recover nydusd daemons
type DaemonConfig struct {
NydusdPath string `toml:"nydusd_path"`
NydusdConfigPath string `toml:"nydusd_config"`
NydusImagePath string `toml:"nydusimage_path"`
RecoverPolicy string `toml:"recover_policy"`
FsDriver string `toml:"fs_driver"`
ThreadsNumber int `toml:"threads_number"`
LogRotationSize int `toml:"log_rotation_size"`
}
type LoggingConfig struct {
LogToStdout bool `toml:"log_to_stdout"`
LogLevel string `toml:"level"`
LogDir string `toml:"dir"`
RotateLogMaxSize int `toml:"log_rotation_max_size"`
RotateLogMaxBackups int `toml:"log_rotation_max_backups"`
RotateLogMaxAge int `toml:"log_rotation_max_age"`
RotateLogLocalTime bool `toml:"log_rotation_local_time"`
RotateLogCompress bool `toml:"log_rotation_compress"`
}
// Nydus image layers additional process
type ImageConfig struct {
PublicKeyFile string `toml:"public_key_file"`
ValidateSignature bool `toml:"validate_signature"`
}
// Configure containerd snapshots interfaces and how to process the snapshots
// requests from containerd
type SnapshotConfig struct {
EnableNydusOverlayFS bool `toml:"enable_nydus_overlayfs"`
EnableKataVolume bool `toml:"enable_kata_volume"`
SyncRemove bool `toml:"sync_remove"`
}
// Configure cache manager that manages the cache files lifecycle
type CacheManagerConfig struct {
Disable bool `toml:"disable"`
// Trigger GC gc_period after the specified period.
// Example format: 24h, 120min
GCPeriod string `toml:"gc_period"`
CacheDir string `toml:"cache_dir"`
}
// Configure how nydus-snapshotter receive auth information
type AuthConfig struct {
// based on kubeconfig or ServiceAccount
EnableKubeconfigKeychain bool `toml:"enable_kubeconfig_keychain"`
KubeconfigPath string `toml:"kubeconfig_path"`
// CRI proxy mode
EnableCRIKeychain bool `toml:"enable_cri_keychain"`
ImageServiceAddress string `toml:"image_service_address"`
}
// Configure remote storage like container registry
type RemoteConfig struct {
AuthConfig AuthConfig `toml:"auth"`
ConvertVpcRegistry bool `toml:"convert_vpc_registry"`
SkipSSLVerify bool `toml:"skip_ssl_verify"`
MirrorsConfig MirrorsConfig `toml:"mirrors_config"`
}
type MirrorsConfig struct {
Dir string `toml:"dir"`
}
type MetricsConfig struct {
Address string `toml:"address"`
}
type DebugConfig struct {
ProfileDuration int64 `toml:"daemon_cpu_profile_duration_secs"`
PprofAddress string `toml:"pprof_address"`
}
type SystemControllerConfig struct {
Enable bool `toml:"enable"`
Address string `toml:"address"`
DebugConfig DebugConfig `toml:"debug"`
}
type SnapshotterConfig struct {
// Configuration format version
Version int `toml:"version"`
// Snapshotter's root work directory
Root string `toml:"root"`
Address string `toml:"address"`
DaemonMode string `toml:"daemon_mode"`
// Clean up all the resources when snapshotter is closed
CleanupOnClose bool `toml:"cleanup_on_close"`
SystemControllerConfig SystemControllerConfig `toml:"system"`
MetricsConfig MetricsConfig `toml:"metrics"`
DaemonConfig DaemonConfig `toml:"daemon"`
SnapshotsConfig SnapshotConfig `toml:"snapshot"`
RemoteConfig RemoteConfig `toml:"remote"`
ImageConfig ImageConfig `toml:"image"`
CacheManagerConfig CacheManagerConfig `toml:"cache_manager"`
LoggingConfig LoggingConfig `toml:"log"`
CgroupConfig CgroupConfig `toml:"cgroup"`
Experimental Experimental `toml:"experimental"`
}
func LoadSnapshotterConfig(path string) (*SnapshotterConfig, error) {
var config SnapshotterConfig
// get nydus-snapshotter configuration from specified path of toml file
if path == "" {
return nil, errors.New("snapshotter configuration path cannot be empty")
}
tree, err := toml.LoadFile(path)
if err != nil {
return nil, errors.Wrapf(err, "load toml configuration from file %q", path)
}
if err = tree.Unmarshal(&config); err != nil {
return nil, errors.Wrap(err, "unmarshal snapshotter configuration")
}
if config.Version != 1 {
return nil, errors.Errorf("unsupported configuration version %d", config.Version)
}
return &config, nil
}
func MergeConfig(to, from *SnapshotterConfig) error {
err := mergo.Merge(to, from)
if err != nil {
return err
}
return nil
}
func ValidateConfig(c *SnapshotterConfig) error {
if c == nil {
return errors.Wrapf(errdefs.ErrInvalidArgument, "configuration is none")
}
if c.ImageConfig.ValidateSignature {
if c.ImageConfig.PublicKeyFile == "" {
return errors.New("public key file for signature validation is not provided")
} else if _, err := os.Stat(c.ImageConfig.PublicKeyFile); err != nil {
return errors.Wrapf(err, "check publicKey file %q", c.ImageConfig.PublicKeyFile)
}
}
if len(c.Root) == 0 {
return errors.New("empty root directory")
}
if c.DaemonConfig.FsDriver != FsDriverFscache && c.DaemonConfig.FsDriver != FsDriverFusedev &&
c.DaemonConfig.FsDriver != FsDriverBlockdev && c.DaemonConfig.FsDriver != FsDriverNodev &&
c.DaemonConfig.FsDriver != FsDriverProxy {
return errors.Errorf("invalid filesystem driver %q", c.DaemonConfig.FsDriver)
}
if _, err := ParseRecoverPolicy(c.DaemonConfig.RecoverPolicy); err != nil {
return err
}
if c.DaemonConfig.ThreadsNumber > 1024 {
return errors.Errorf("nydusd worker thread number %d is too big, max 1024", c.DaemonConfig.ThreadsNumber)
}
if c.RemoteConfig.AuthConfig.EnableCRIKeychain && c.RemoteConfig.AuthConfig.EnableKubeconfigKeychain {
return errors.Wrapf(errdefs.ErrInvalidArgument,
"\"enable_cri_keychain\" and \"enable_kubeconfig_keychain\" can't be set at the same time")
}
if c.RemoteConfig.MirrorsConfig.Dir != "" {
dirExisted, err := file.IsDirExisted(c.RemoteConfig.MirrorsConfig.Dir)
if err != nil {
return err
}
if !dirExisted {
return errors.Errorf("mirrors config directory %s does not exist", c.RemoteConfig.MirrorsConfig.Dir)
}
}
return nil
}
// Parse command line arguments and fill the nydus-snapshotter configuration
// Always let options from CLI override those from configuration file.
func ParseParameters(args *flags.Args, cfg *SnapshotterConfig) error {
// --- essential configuration
if args.Address != "" {
cfg.Address = args.Address
}
if args.RootDir != "" {
cfg.Root = args.RootDir
}
// Give --shared-daemon higher priority
if args.DaemonMode != "" {
cfg.DaemonMode = args.DaemonMode
}
// --- image processor configuration
// empty
// --- daemon configuration
daemonConfig := &cfg.DaemonConfig
if args.NydusdConfigPath != "" {
daemonConfig.NydusdConfigPath = args.NydusdConfigPath
}
if args.NydusdPath != "" {
daemonConfig.NydusdPath = args.NydusdPath
}
if args.NydusImagePath != "" {
daemonConfig.NydusImagePath = args.NydusImagePath
}
if args.FsDriver != "" {
daemonConfig.FsDriver = args.FsDriver
}
// --- cache manager configuration
// empty
// --- logging configuration
logConfig := &cfg.LoggingConfig
if args.LogLevel != "" {
logConfig.LogLevel = args.LogLevel
}
if args.LogToStdoutCount > 0 {
logConfig.LogToStdout = args.LogToStdout
}
// --- remote storage configuration
// empty
// --- snapshot configuration
// empty
// --- metrics configuration
// empty
return nil
}
func ParseCgroupConfig(config CgroupConfig) (cgroup.Config, error) {
totalMemory, err := sysinfo.GetTotalMemoryBytes()
if err != nil {
return cgroup.Config{}, errors.Wrap(err, "Failed to get total memory bytes")
}
memoryLimitInBytes, err := parser.MemoryConfigToBytes(config.MemoryLimit, totalMemory)
if err != nil {
return cgroup.Config{}, err
}
return cgroup.Config{
MemoryLimitInBytes: memoryLimitInBytes,
}, nil
}
nydus-snapshotter-0.13.4/config/config_test.go 0000664 0000000 0000000 00000017335 14533742372 0021434 0 ustar 00root root 0000000 0000000 /*
* Copyright (c) 2023. Nydus Developers. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*/
package config
import (
"path/filepath"
"testing"
"time"
"github.com/containerd/nydus-snapshotter/internal/constant"
"github.com/containerd/nydus-snapshotter/internal/flags"
"github.com/stretchr/testify/assert"
)
func TestLoadSnapshotterTOMLConfig(t *testing.T) {
A := assert.New(t)
cfg, err := LoadSnapshotterConfig("../misc/snapshotter/config.toml")
A.NoError(err)
exampleConfig := SnapshotterConfig{
Version: 1,
Root: "/var/lib/containerd-nydus",
Address: "/run/containerd-nydus/containerd-nydus-grpc.sock",
DaemonMode: "dedicated",
Experimental: Experimental{
EnableStargz: false,
EnableReferrerDetect: false,
},
CleanupOnClose: false,
SystemControllerConfig: SystemControllerConfig{
Enable: true,
Address: "/run/containerd-nydus/system.sock",
DebugConfig: DebugConfig{
ProfileDuration: 5,
PprofAddress: "",
},
},
DaemonConfig: DaemonConfig{
NydusdPath: "/usr/local/bin/nydusd",
NydusImagePath: "/usr/local/bin/nydus-image",
FsDriver: "fusedev",
RecoverPolicy: "restart",
NydusdConfigPath: "/etc/nydus/nydusd-config.fusedev.json",
ThreadsNumber: 4,
LogRotationSize: 100,
},
SnapshotsConfig: SnapshotConfig{
EnableNydusOverlayFS: false,
SyncRemove: false,
},
RemoteConfig: RemoteConfig{
ConvertVpcRegistry: false,
AuthConfig: AuthConfig{
EnableKubeconfigKeychain: false,
KubeconfigPath: "",
},
MirrorsConfig: MirrorsConfig{
Dir: "",
},
},
ImageConfig: ImageConfig{
PublicKeyFile: "",
ValidateSignature: false,
},
CacheManagerConfig: CacheManagerConfig{
Disable: false,
GCPeriod: "24h",
CacheDir: "",
},
LoggingConfig: LoggingConfig{
LogLevel: "info",
RotateLogCompress: true,
RotateLogLocalTime: true,
RotateLogMaxAge: 7,
RotateLogMaxBackups: 5,
RotateLogMaxSize: 100,
LogToStdout: false,
},
MetricsConfig: MetricsConfig{
Address: ":9110",
},
CgroupConfig: CgroupConfig{
Enable: true,
MemoryLimit: "",
},
}
A.EqualValues(cfg, &exampleConfig)
var args = flags.Args{}
args.RootDir = "/var/lib/containerd/nydus"
exampleConfig.Root = "/var/lib/containerd/nydus"
err = ParseParameters(&args, cfg)
A.NoError(err)
A.EqualValues(cfg, &exampleConfig)
A.EqualValues(cfg.LoggingConfig.LogToStdout, false)
args.LogToStdout = true
args.LogToStdoutCount = 1
err = ParseParameters(&args, cfg)
A.NoError(err)
A.EqualValues(cfg.LoggingConfig.LogToStdout, true)
err = ProcessConfigurations(cfg)
A.NoError(err)
A.Equal(GetCacheGCPeriod(), time.Hour*24)
}
func TestSnapshotterConfig(t *testing.T) {
A := assert.New(t)
var cfg SnapshotterConfig
var args flags.Args
// The log_to_stdout is false in toml file without --log-to-stdout flag.
// Expected false.
cfg.LoggingConfig.LogToStdout = false
args.LogToStdoutCount = 0
err := ParseParameters(&args, &cfg)
A.NoError(err)
A.EqualValues(cfg.LoggingConfig.LogToStdout, false)
// The log_to_stdout is true in toml file without --log-to-stdout flag.
// Expected true.
// This case is failed.
cfg.LoggingConfig.LogToStdout = true
args.LogToStdoutCount = 0
err = ParseParameters(&args, &cfg)
A.NoError(err)
A.EqualValues(cfg.LoggingConfig.LogToStdout, true)
// The log_to_stdout is false in toml file with --log-to-stdout=true.
// Expected true (command flag has higher priority).
args.LogToStdout = true
args.LogToStdoutCount = 1
cfg.LoggingConfig.LogToStdout = false
err = ParseParameters(&args, &cfg)
A.NoError(err)
A.EqualValues(cfg.LoggingConfig.LogToStdout, true)
// The log_to_stdout is true in toml file with --log-to-stdout=true.
// Expected true (command flag has higher priority).
args.LogToStdout = true
args.LogToStdoutCount = 1
cfg.LoggingConfig.LogToStdout = true
err = ParseParameters(&args, &cfg)
A.NoError(err)
A.EqualValues(cfg.LoggingConfig.LogToStdout, true)
// The log_to_stdout is false in toml file with --log-to-stdout=false.
// Expected false (command flag has higher priority).
args.LogToStdout = false
args.LogToStdoutCount = 1
cfg.LoggingConfig.LogToStdout = false
err = ParseParameters(&args, &cfg)
A.NoError(err)
A.EqualValues(cfg.LoggingConfig.LogToStdout, false)
// The log_to_stdout is true in toml file with --log-to-stdout=false.
// Expected false (command flag has higher priority).
args.LogToStdout = false
args.LogToStdoutCount = 1
cfg.LoggingConfig.LogToStdout = true
err = ParseParameters(&args, &cfg)
A.NoError(err)
A.EqualValues(cfg.LoggingConfig.LogToStdout, false)
}
func TestMergeConfig(t *testing.T) {
A := assert.New(t)
var defaultSnapshotterConfig SnapshotterConfig
var snapshotterConfig1 SnapshotterConfig
err := defaultSnapshotterConfig.FillUpWithDefaults()
A.NoError(err)
err = MergeConfig(&snapshotterConfig1, &defaultSnapshotterConfig)
A.NoError(err)
A.Equal(snapshotterConfig1.Root, constant.DefaultRootDir)
A.Equal(snapshotterConfig1.LoggingConfig.LogDir, "")
A.Equal(snapshotterConfig1.CacheManagerConfig.CacheDir, "")
A.Equal(snapshotterConfig1.DaemonMode, constant.DefaultDaemonMode)
A.Equal(snapshotterConfig1.SystemControllerConfig.Address, constant.DefaultSystemControllerAddress)
A.Equal(snapshotterConfig1.LoggingConfig.LogLevel, constant.DefaultLogLevel)
A.Equal(snapshotterConfig1.LoggingConfig.RotateLogMaxSize, constant.DefaultRotateLogMaxSize)
A.Equal(snapshotterConfig1.LoggingConfig.RotateLogMaxBackups, constant.DefaultRotateLogMaxBackups)
A.Equal(snapshotterConfig1.LoggingConfig.RotateLogMaxAge, constant.DefaultRotateLogMaxAge)
A.Equal(snapshotterConfig1.LoggingConfig.RotateLogCompress, constant.DefaultRotateLogCompress)
A.Equal(snapshotterConfig1.DaemonConfig.NydusdConfigPath, constant.DefaultNydusDaemonConfigPath)
A.Equal(snapshotterConfig1.DaemonConfig.RecoverPolicy, RecoverPolicyRestart.String())
A.Equal(snapshotterConfig1.CacheManagerConfig.GCPeriod, constant.DefaultGCPeriod)
var snapshotterConfig2 SnapshotterConfig
snapshotterConfig2.Root = "/snapshotter/root"
err = MergeConfig(&snapshotterConfig2, &defaultSnapshotterConfig)
A.NoError(err)
A.Equal(snapshotterConfig2.Root, "/snapshotter/root")
A.Equal(snapshotterConfig2.LoggingConfig.LogDir, "")
A.Equal(snapshotterConfig2.CacheManagerConfig.CacheDir, "")
}
func TestProcessConfigurations(t *testing.T) {
A := assert.New(t)
var defaultSnapshotterConfig SnapshotterConfig
var snapshotterConfig1 SnapshotterConfig
err := defaultSnapshotterConfig.FillUpWithDefaults()
A.NoError(err)
err = MergeConfig(&snapshotterConfig1, &defaultSnapshotterConfig)
A.NoError(err)
err = ValidateConfig(&snapshotterConfig1)
A.NoError(err)
err = ProcessConfigurations(&snapshotterConfig1)
A.NoError(err)
A.Equal(snapshotterConfig1.LoggingConfig.LogDir, filepath.Join(snapshotterConfig1.Root, "logs"))
A.Equal(snapshotterConfig1.CacheManagerConfig.CacheDir, filepath.Join(snapshotterConfig1.Root, "cache"))
var snapshotterConfig2 SnapshotterConfig
snapshotterConfig2.Root = "/snapshotter/root"
err = MergeConfig(&snapshotterConfig2, &defaultSnapshotterConfig)
A.NoError(err)
err = ValidateConfig(&snapshotterConfig2)
A.NoError(err)
err = ProcessConfigurations(&snapshotterConfig2)
A.NoError(err)
A.Equal(snapshotterConfig2.LoggingConfig.LogDir, filepath.Join(snapshotterConfig2.Root, "logs"))
A.Equal(snapshotterConfig2.CacheManagerConfig.CacheDir, filepath.Join(snapshotterConfig2.Root, "cache"))
var snapshotterConfig3 SnapshotterConfig
snapshotterConfig3.Root = "./snapshotter/root"
err = MergeConfig(&snapshotterConfig3, &defaultSnapshotterConfig)
A.NoError(err)
err = ValidateConfig(&snapshotterConfig3)
A.NoError(err)
err = ProcessConfigurations(&snapshotterConfig3)
A.NoError(err)
}
nydus-snapshotter-0.13.4/config/daemonconfig/ 0000775 0000000 0000000 00000000000 14533742372 0021221 5 ustar 00root root 0000000 0000000 nydus-snapshotter-0.13.4/config/daemonconfig/daemonconfig.go 0000664 0000000 0000000 00000013333 14533742372 0024204 0 ustar 00root root 0000000 0000000 /*
* Copyright (c) 2020. Ant Group. All rights reserved.
* Copyright (c) 2022. Nydus Developers. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*/
package daemonconfig
import (
"encoding/json"
"os"
"github.com/pkg/errors"
"github.com/containerd/nydus-snapshotter/config"
"github.com/containerd/nydus-snapshotter/pkg/auth"
"github.com/containerd/nydus-snapshotter/pkg/utils/registry"
)
type StorageBackendType = string
const (
backendTypeLocalfs StorageBackendType = "localfs"
backendTypeOss StorageBackendType = "oss"
backendTypeRegistry StorageBackendType = "registry"
)
type DaemonConfig interface {
// Provide stuffs relevant to accessing registry apart from auth
Supplement(host, repo, snapshotID string, params map[string]string)
// Provide auth
FillAuth(kc *auth.PassKeyChain)
StorageBackend() (StorageBackendType, *BackendConfig)
UpdateMirrors(mirrorsConfigDir, registryHost string) error
DumpString() (string, error)
DumpFile(path string) error
}
// Daemon configurations factory
func NewDaemonConfig(fsDriver, path string) (DaemonConfig, error) {
switch fsDriver {
case config.FsDriverFscache:
cfg, err := LoadFscacheConfig(path)
if err != nil {
return nil, err
}
return cfg, nil
case config.FsDriverFusedev:
cfg, err := LoadFuseConfig(path)
if err != nil {
return nil, err
}
return cfg, nil
default:
return nil, errors.Errorf("unsupported, fs driver %q", fsDriver)
}
}
type MirrorConfig struct {
Host string `json:"host,omitempty"`
Headers map[string]string `json:"headers,omitempty"`
HealthCheckInterval int `json:"health_check_interval,omitempty"`
FailureLimit uint8 `json:"failure_limit,omitempty"`
PingURL string `json:"ping_url,omitempty"`
}
type BackendConfig struct {
// Localfs backend configs
BlobFile string `json:"blob_file,omitempty"`
Dir string `json:"dir,omitempty"`
ReadAhead bool `json:"readahead"`
ReadAheadSec int `json:"readahead_sec,omitempty"`
// Registry backend configs
Host string `json:"host,omitempty"`
Repo string `json:"repo,omitempty"`
Auth string `json:"auth,omitempty"`
RegistryToken string `json:"registry_token,omitempty"`
BlobURLScheme string `json:"blob_url_scheme,omitempty"`
BlobRedirectedHost string `json:"blob_redirected_host,omitempty"`
Mirrors []MirrorConfig `json:"mirrors,omitempty"`
// OSS backend configs
EndPoint string `json:"endpoint,omitempty"`
AccessKeyID string `json:"access_key_id,omitempty"`
AccessKeySecret string `json:"access_key_secret,omitempty"`
BucketName string `json:"bucket_name,omitempty"`
ObjectPrefix string `json:"object_prefix,omitempty"`
// Shared by registry and oss backend
Scheme string `json:"scheme,omitempty"`
SkipVerify bool `json:"skip_verify,omitempty"`
// Below configs are common configs shared by all backends
Proxy struct {
URL string `json:"url,omitempty"`
Fallback bool `json:"fallback"`
PingURL string `json:"ping_url,omitempty"`
CheckInterval int `json:"check_interval,omitempty"`
UseHTTP bool `json:"use_http,omitempty"`
} `json:"proxy,omitempty"`
Timeout int `json:"timeout,omitempty"`
ConnectTimeout int `json:"connect_timeout,omitempty"`
RetryLimit int `json:"retry_limit,omitempty"`
}
type DeviceConfig struct {
Backend struct {
BackendType string `json:"type"`
Config BackendConfig `json:"config"`
} `json:"backend"`
Cache struct {
CacheType string `json:"type"`
Compressed bool `json:"compressed,omitempty"`
Config struct {
WorkDir string `json:"work_dir"`
DisableIndexedMap bool `json:"disable_indexed_map"`
} `json:"config"`
} `json:"cache"`
}
// For nydusd as FUSE daemon. Serialize Daemon info and persist to a json file
// We don't have to persist configuration file for fscache since its configuration
// is passed through HTTP API.
func DumpConfigFile(c interface{}, path string) error {
b, err := json.Marshal(c)
if err != nil {
return errors.Wrapf(err, "marshal config")
}
return os.WriteFile(path, b, 0600)
}
func DumpConfigString(c interface{}) (string, error) {
b, err := json.Marshal(c)
return string(b), err
}
// Achieve a daemon configuration from template or snapshotter's configuration
func SupplementDaemonConfig(c DaemonConfig, imageID, snapshotID string,
vpcRegistry bool, labels map[string]string, params map[string]string) error {
image, err := registry.ParseImage(imageID)
if err != nil {
return errors.Wrapf(err, "parse image %s", imageID)
}
backendType, _ := c.StorageBackend()
switch backendType {
case backendTypeRegistry:
registryHost := image.Host
if vpcRegistry {
registryHost = registry.ConvertToVPCHost(registryHost)
} else if registryHost == "docker.io" {
// For docker.io images, we should use index.docker.io
registryHost = "index.docker.io"
}
if err := c.UpdateMirrors(config.GetMirrorsConfigDir(), registryHost); err != nil {
return errors.Wrap(err, "update mirrors config")
}
// If no auth is provided, don't touch auth from provided nydusd configuration file.
// We don't validate the original nydusd auth from configuration file since it can be empty
// when repository is public.
keyChain := auth.GetRegistryKeyChain(registryHost, imageID, labels)
c.Supplement(registryHost, image.Repo, snapshotID, params)
c.FillAuth(keyChain)
// Localfs and OSS backends don't need any update,
// just use the provided config in template
case backendTypeLocalfs:
case backendTypeOss:
default:
return errors.Errorf("unknown backend type %s", backendType)
}
return nil
}
nydus-snapshotter-0.13.4/config/daemonconfig/daemonconfig_test.go 0000664 0000000 0000000 00000003005 14533742372 0025236 0 ustar 00root root 0000000 0000000 /*
* Copyright (c) 2020. Ant Group. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*/
package daemonconfig
import (
"encoding/json"
"testing"
"github.com/stretchr/testify/require"
)
func TestLoadConfig(t *testing.T) {
buf := []byte(`{
"device": {
"backend": {
"type": "registry",
"config": {
"skip_verify": true,
"host": "acr-nydus-registry-vpc.cn-hangzhou.cr.aliyuncs.com",
"repo": "test/myserver",
"auth": "",
"blob_url_scheme": "http",
"proxy": {
"url": "http://p2p-proxy:65001",
"fallback": true,
"ping_url": "http://p2p-proxy:40901/server/ping",
"check_interval": 5
},
"timeout": 5,
"connect_timeout": 5,
"retry_limit": 0
}
},
"cache": {
"type": "blobcache",
"config": {
"work_dir": "/cache"
}
}
},
"mode": "direct",
"digest_validate": true,
"iostats_files": true,
"enable_xattr": true,
"fs_prefetch": {
"enable": true,
"threads_count": 10,
"merging_size": 131072
}
}`)
var cfg FuseDaemonConfig
err := json.Unmarshal(buf, &cfg)
require.Nil(t, err)
require.Equal(t, cfg.FSPrefetch.Enable, true)
require.Equal(t, cfg.FSPrefetch.MergingSize, 131072)
require.Equal(t, cfg.FSPrefetch.ThreadsCount, 10)
require.Equal(t, cfg.Device.Backend.Config.BlobURLScheme, "http")
require.Equal(t, cfg.Device.Backend.Config.SkipVerify, true)
require.Equal(t, cfg.Device.Backend.Config.Proxy.CheckInterval, 5)
}
nydus-snapshotter-0.13.4/config/daemonconfig/fscache.go 0000664 0000000 0000000 00000006376 14533742372 0023160 0 ustar 00root root 0000000 0000000 /*
* Copyright (c) 2022. Nydus Developers. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*/
package daemonconfig
import (
"encoding/json"
"os"
"path"
"github.com/containerd/containerd/log"
"github.com/containerd/nydus-snapshotter/pkg/auth"
"github.com/containerd/nydus-snapshotter/pkg/utils/erofs"
"github.com/pkg/errors"
)
const (
WorkDir string = "workdir"
Bootstrap string = "bootstrap"
)
type BlobPrefetchConfig struct {
Enable bool `json:"enable"`
ThreadsCount int `json:"threads_count"`
MergingSize int `json:"merging_size"`
BandwidthRate int `json:"bandwidth_rate"`
}
type FscacheDaemonConfig struct {
// These fields is only for fscache daemon.
Type string `json:"type"`
// Snapshotter fills
ID string `json:"id"`
DomainID string `json:"domain_id"`
Config *struct {
ID string `json:"id"`
BackendType string `json:"backend_type"`
BackendConfig BackendConfig `json:"backend_config"`
CacheType string `json:"cache_type"`
// Snapshotter fills
CacheConfig struct {
WorkDir string `json:"work_dir"`
} `json:"cache_config"`
BlobPrefetchConfig BlobPrefetchConfig `json:"prefetch_config"`
MetadataPath string `json:"metadata_path"`
} `json:"config"`
}
// Load Fscache configuration template file
func LoadFscacheConfig(p string) (*FscacheDaemonConfig, error) {
var cfg FscacheDaemonConfig
b, err := os.ReadFile(p)
if err != nil {
return nil, errors.Wrapf(err, "read fscache configuration file %s", p)
}
if err = json.Unmarshal(b, &cfg); err != nil {
return nil, errors.Wrapf(err, "unmarshal")
}
if cfg.Config == nil {
return nil, errors.New("invalid fscache configuration")
}
return &cfg, nil
}
func (c *FscacheDaemonConfig) UpdateMirrors(mirrorsConfigDir, registryHost string) error {
mirrors, err := LoadMirrorsConfig(mirrorsConfigDir, registryHost)
if err != nil {
return err
}
if len(mirrors) > 0 {
c.Config.BackendConfig.Mirrors = mirrors
}
return nil
}
func (c *FscacheDaemonConfig) StorageBackend() (string, *BackendConfig) {
return c.Config.BackendType, &c.Config.BackendConfig
}
// Each fscache/erofs has a configuration with different fscache ID built from snapshot ID.
func (c *FscacheDaemonConfig) Supplement(host, repo, snapshotID string, params map[string]string) {
c.Config.BackendConfig.Host = host
c.Config.BackendConfig.Repo = repo
fscacheID := erofs.FscacheID(snapshotID)
c.ID = fscacheID
if c.DomainID != "" {
log.L.Warnf("Linux Kernel Shared Domain feature in use. make sure your kernel version >= 6.1")
} else {
c.DomainID = fscacheID
}
c.Config.ID = fscacheID
if WorkDir, ok := params[WorkDir]; ok {
c.Config.CacheConfig.WorkDir = WorkDir
}
if bootstrap, ok := params[Bootstrap]; ok {
c.Config.MetadataPath = bootstrap
}
}
func (c *FscacheDaemonConfig) FillAuth(kc *auth.PassKeyChain) {
if kc != nil {
if kc.TokenBase() {
c.Config.BackendConfig.RegistryToken = kc.Password
} else {
c.Config.BackendConfig.Auth = kc.ToBase64()
}
}
}
func (c *FscacheDaemonConfig) DumpString() (string, error) {
return DumpConfigString(c)
}
func (c *FscacheDaemonConfig) DumpFile(f string) error {
if err := os.MkdirAll(path.Dir(f), 0755); err != nil {
return err
}
return DumpConfigFile(c, f)
}
nydus-snapshotter-0.13.4/config/daemonconfig/fuse.go 0000664 0000000 0000000 00000005465 14533742372 0022524 0 ustar 00root root 0000000 0000000 /*
* Copyright (c) 2022. Nydus Developers. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*/
package daemonconfig
import (
"encoding/json"
"os"
"path"
"github.com/pkg/errors"
"github.com/containerd/nydus-snapshotter/pkg/auth"
)
const CacheDir string = "cachedir"
// Used when nydusd works as a FUSE daemon or vhost-user-fs backend
type FuseDaemonConfig struct {
Device *DeviceConfig `json:"device"`
Mode string `json:"mode"`
DigestValidate bool `json:"digest_validate"`
IOStatsFiles bool `json:"iostats_files,omitempty"`
EnableXattr bool `json:"enable_xattr,omitempty"`
AccessPattern bool `json:"access_pattern,omitempty"`
LatestReadFiles bool `json:"latest_read_files,omitempty"`
FSPrefetch `json:"fs_prefetch,omitempty"`
// (experimental) The nydus daemon could cache more data to increase hit ratio when enabled the warmup feature.
Warmup uint64 `json:"warmup,omitempty"`
}
// Control how to perform prefetch from file system layer
type FSPrefetch struct {
Enable bool `json:"enable"`
PrefetchAll bool `json:"prefetch_all"`
ThreadsCount int `json:"threads_count"`
MergingSize int `json:"merging_size"`
BandwidthRate int `json:"bandwidth_rate"`
}
// Load fuse daemon configuration from template file
func LoadFuseConfig(p string) (*FuseDaemonConfig, error) {
b, err := os.ReadFile(p)
if err != nil {
return nil, errors.Wrapf(err, "read FUSE configuration file %s", p)
}
var cfg FuseDaemonConfig
if err := json.Unmarshal(b, &cfg); err != nil {
return nil, errors.Wrapf(err, "unmarshal %s", p)
}
if cfg.Device == nil {
return nil, errors.New("invalid fuse daemon configuration")
}
return &cfg, nil
}
func (c *FuseDaemonConfig) Supplement(host, repo, snapshotID string, params map[string]string) {
c.Device.Backend.Config.Host = host
c.Device.Backend.Config.Repo = repo
c.Device.Cache.Config.WorkDir = params[CacheDir]
}
func (c *FuseDaemonConfig) FillAuth(kc *auth.PassKeyChain) {
if kc != nil {
if kc.TokenBase() {
c.Device.Backend.Config.RegistryToken = kc.Password
} else {
c.Device.Backend.Config.Auth = kc.ToBase64()
}
}
}
func (c *FuseDaemonConfig) UpdateMirrors(mirrorsConfigDir, registryHost string) error {
mirrors, err := LoadMirrorsConfig(mirrorsConfigDir, registryHost)
if err != nil {
return err
}
if len(mirrors) > 0 {
c.Device.Backend.Config.Mirrors = mirrors
}
return nil
}
func (c *FuseDaemonConfig) StorageBackend() (string, *BackendConfig) {
return c.Device.Backend.BackendType, &c.Device.Backend.Config
}
func (c *FuseDaemonConfig) DumpString() (string, error) {
return DumpConfigString(c)
}
func (c *FuseDaemonConfig) DumpFile(f string) error {
if err := os.MkdirAll(path.Dir(f), 0755); err != nil {
return err
}
return DumpConfigFile(c, f)
}
nydus-snapshotter-0.13.4/config/daemonconfig/mirrors.go 0000664 0000000 0000000 00000014131 14533742372 0023245 0 ustar 00root root 0000000 0000000 /*
* Copyright (c) 2023. Nydus Developers. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*/
package daemonconfig
import (
"fmt"
"net/http"
"net/url"
"os"
"path/filepath"
"sort"
"strings"
"github.com/containerd/containerd/log"
"github.com/pelletier/go-toml"
"github.com/pkg/errors"
)
// Copied from containerd, for compatibility with containerd's toml configuration file.
type HostFileConfig struct {
Capabilities []string `toml:"capabilities"`
CACert interface{} `toml:"ca"`
Client interface{} `toml:"client"`
SkipVerify *bool `toml:"skip_verify"`
Header map[string]interface{} `toml:"header"`
OverridePath bool `toml:"override_path"`
// The following configuration items are specific to nydus.
HealthCheckInterval int `toml:"health_check_interval,omitempty"`
FailureLimit uint8 `toml:"failure_limit,omitempty"`
PingURL string `toml:"ping_url,omitempty"`
}
type hostConfig struct {
Scheme string
Host string
Header http.Header
HealthCheckInterval int
FailureLimit uint8
PingURL string
}
func makeStringSlice(slice []interface{}, cb func(string) string) ([]string, error) {
out := make([]string, len(slice))
for i, value := range slice {
str, ok := value.(string)
if !ok {
return nil, fmt.Errorf("unable to cast %v to string", value)
}
if cb != nil {
out[i] = cb(str)
} else {
out[i] = str
}
}
return out, nil
}
func parseMirrorsConfig(hosts []hostConfig) []MirrorConfig {
var parsedMirrors = make([]MirrorConfig, len(hosts))
for i, host := range hosts {
parsedMirrors[i].Host = fmt.Sprintf("%s://%s", host.Scheme, host.Host)
parsedMirrors[i].HealthCheckInterval = host.HealthCheckInterval
parsedMirrors[i].FailureLimit = host.FailureLimit
parsedMirrors[i].PingURL = host.PingURL
if len(host.Header) > 0 {
mirrorHeader := make(map[string]string, len(host.Header))
for key, value := range host.Header {
if len(value) > 1 {
log.L.Warnf("some values of the header[%q] are omitted: %#v", key, value[1:])
}
mirrorHeader[key] = host.Header.Get(key)
}
parsedMirrors[i].Headers = mirrorHeader
}
}
return parsedMirrors
}
// hostDirectory converts ":port" to "_port_" in directory names
func hostDirectory(host string) string {
idx := strings.LastIndex(host, ":")
if idx > 0 {
return host[:idx] + "_" + host[idx+1:] + "_"
}
return host
}
func hostPaths(root, host string) []string {
var hosts []string
ch := hostDirectory(host)
if ch != host {
hosts = append(hosts, filepath.Join(root, ch))
}
return append(hosts,
filepath.Join(root, host),
filepath.Join(root, "_default"),
)
}
func hostDirFromRoot(root, host string) (string, error) {
for _, p := range hostPaths(root, host) {
if _, err := os.Stat(p); err == nil {
return p, nil
} else if !os.IsNotExist(err) {
return "", err
}
}
return "", nil
}
// getSortedHosts returns the list of hosts as they defined in the file.
func getSortedHosts(root *toml.Tree) ([]string, error) {
iter, ok := root.Get("host").(*toml.Tree)
if !ok {
return nil, errors.New("invalid `host` tree")
}
list := append([]string{}, iter.Keys()...)
// go-toml stores TOML sections in the map object, so no order guaranteed.
// We retrieve line number for each key and sort the keys by position.
sort.Slice(list, func(i, j int) bool {
h1 := iter.GetPath([]string{list[i]}).(*toml.Tree)
h2 := iter.GetPath([]string{list[j]}).(*toml.Tree)
return h1.Position().Line < h2.Position().Line
})
return list, nil
}
// parseHostConfig returns the parsed host configuration, make sure the server is not null.
func parseHostConfig(server string, config HostFileConfig) (hostConfig, error) {
var (
result = hostConfig{}
err error
)
if !strings.HasPrefix(server, "http") {
server = "https://" + server
}
u, err := url.Parse(server)
if err != nil {
return hostConfig{}, fmt.Errorf("unable to parse server %v: %w", server, err)
}
result.Scheme = u.Scheme
result.Host = u.Host
if config.Header != nil {
header := http.Header{}
for key, ty := range config.Header {
switch value := ty.(type) {
case string:
header[key] = []string{value}
case []interface{}:
header[key], err = makeStringSlice(value, nil)
if err != nil {
return hostConfig{}, err
}
default:
return hostConfig{}, fmt.Errorf("invalid type %v for header %q", ty, key)
}
}
result.Header = header
}
result.HealthCheckInterval = config.HealthCheckInterval
result.FailureLimit = config.FailureLimit
result.PingURL = config.PingURL
return result, nil
}
func parseHostsFile(b []byte) ([]hostConfig, error) {
tree, err := toml.LoadBytes(b)
if err != nil {
return nil, fmt.Errorf("failed to parse TOML: %w", err)
}
c := struct {
// HostConfigs store the per-host configuration
HostConfigs map[string]HostFileConfig `toml:"host"`
}{}
orderedHosts, err := getSortedHosts(tree)
if err != nil {
return nil, err
}
var (
hosts []hostConfig
)
if err := tree.Unmarshal(&c); err != nil {
return nil, err
}
// Parse hosts array
for _, host := range orderedHosts {
if host != "" {
config := c.HostConfigs[host]
parsed, err := parseHostConfig(host, config)
if err != nil {
return nil, err
}
hosts = append(hosts, parsed)
}
}
return hosts, nil
}
func loadHostDir(hostsDir string) ([]hostConfig, error) {
b, err := os.ReadFile(filepath.Join(hostsDir, "hosts.toml"))
if err != nil {
if !os.IsNotExist(err) {
return nil, err
}
return []hostConfig{}, nil
}
hosts, err := parseHostsFile(b)
if err != nil {
return nil, err
}
return hosts, nil
}
func LoadMirrorsConfig(mirrorsConfigDir, registryHost string) ([]MirrorConfig, error) {
var mirrors []MirrorConfig
if mirrorsConfigDir == "" {
return mirrors, nil
}
hostDir, err := hostDirFromRoot(mirrorsConfigDir, registryHost)
if err != nil {
return nil, err
}
if hostDir == "" {
return mirrors, nil
}
hostConfig, err := loadHostDir(hostDir)
if err != nil {
return nil, err
}
mirrors = append(mirrors, parseMirrorsConfig(hostConfig)...)
return mirrors, nil
}
nydus-snapshotter-0.13.4/config/daemonconfig/mirrors_test.go 0000664 0000000 0000000 00000005426 14533742372 0024313 0 ustar 00root root 0000000 0000000 /*
* Copyright (c) 2023. Nydus Developers. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*/
package daemonconfig
import (
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestLoadMirrorConfig(t *testing.T) {
tmpDir := t.TempDir()
defer os.RemoveAll(tmpDir)
registryHost := "registry.docker.io"
mirrorsConfigDir := filepath.Join(tmpDir, "certs.d")
registryHostConfigDir := filepath.Join(mirrorsConfigDir, registryHost)
defaultHostConfigDir := filepath.Join(mirrorsConfigDir, "_default")
mirrors, err := LoadMirrorsConfig("", registryHost)
require.NoError(t, err)
require.Nil(t, mirrors)
mirrors, err = LoadMirrorsConfig(mirrorsConfigDir, registryHost)
require.NoError(t, err)
require.Nil(t, mirrors)
err = os.MkdirAll(defaultHostConfigDir, os.ModePerm)
assert.NoError(t, err)
mirrors, err = LoadMirrorsConfig(mirrorsConfigDir, registryHost)
require.NoError(t, err)
require.Nil(t, mirrors)
buf1 := []byte(`server = "https://default-docker.hub.com"
[host]
[host."http://default-p2p-mirror1:65001"]
[host."http://default-p2p-mirror1:65001".header]
X-Dragonfly-Registry = ["https://default-docker.hub.com"]
`)
err = os.WriteFile(filepath.Join(defaultHostConfigDir, "hosts.toml"), buf1, 0600)
assert.NoError(t, err)
mirrors, err = LoadMirrorsConfig(mirrorsConfigDir, registryHost)
require.NoError(t, err)
require.Equal(t, len(mirrors), 1)
require.Equal(t, mirrors[0].Host, "http://default-p2p-mirror1:65001")
require.Equal(t, mirrors[0].Headers["X-Dragonfly-Registry"], "https://default-docker.hub.com")
err = os.MkdirAll(registryHostConfigDir, os.ModePerm)
assert.NoError(t, err)
buf2 := []byte(`server = "https://docker.hub.com"
[host]
[host."http://p2p-mirror1:65001"]
[host."http://p2p-mirror1:65001".header]
X-Dragonfly-Registry = ["https://docker.hub.com"]
`)
err = os.WriteFile(filepath.Join(registryHostConfigDir, "hosts.toml"), buf2, 0600)
assert.NoError(t, err)
mirrors, err = LoadMirrorsConfig(mirrorsConfigDir, registryHost)
require.NoError(t, err)
require.Equal(t, len(mirrors), 1)
require.Equal(t, mirrors[0].Host, "http://p2p-mirror1:65001")
require.Equal(t, mirrors[0].Headers["X-Dragonfly-Registry"], "https://docker.hub.com")
buf3 := []byte(`
[host."http://p2p-mirror2:65001"]
[host."http://p2p-mirror2:65001".header]
X-Dragonfly-Registry = ["https://docker.hub.com"]
`)
err = os.WriteFile(filepath.Join(registryHostConfigDir, "hosts.toml"), buf3, 0600)
assert.NoError(t, err)
mirrors, err = LoadMirrorsConfig(mirrorsConfigDir, registryHost)
require.NoError(t, err)
require.Equal(t, len(mirrors), 1)
require.Equal(t, mirrors[0].Host, "http://p2p-mirror2:65001")
require.Equal(t, mirrors[0].Headers["X-Dragonfly-Registry"], "https://docker.hub.com")
}
nydus-snapshotter-0.13.4/config/default.go 0000664 0000000 0000000 00000003620 14533742372 0020544 0 ustar 00root root 0000000 0000000 /*
* Copyright (c) 2023. Nydus Developers. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*/
package config
import (
"os/exec"
"github.com/containerd/nydus-snapshotter/internal/constant"
)
func (c *SnapshotterConfig) FillUpWithDefaults() error {
c.Version = 1
c.Root = constant.DefaultRootDir
c.Address = constant.DefaultAddress
// essential configuration
if c.DaemonMode == "" {
c.DaemonMode = constant.DefaultDaemonMode
}
// system controller configuration
c.SystemControllerConfig.Address = constant.DefaultSystemControllerAddress
// logging configuration
logConfig := &c.LoggingConfig
if logConfig.LogLevel == "" {
logConfig.LogLevel = constant.DefaultLogLevel
}
logConfig.RotateLogMaxSize = constant.DefaultRotateLogMaxSize
logConfig.RotateLogMaxBackups = constant.DefaultRotateLogMaxBackups
logConfig.RotateLogMaxAge = constant.DefaultRotateLogMaxAge
logConfig.RotateLogLocalTime = constant.DefaultRotateLogLocalTime
logConfig.RotateLogCompress = constant.DefaultRotateLogCompress
// daemon configuration
daemonConfig := &c.DaemonConfig
if daemonConfig.NydusdConfigPath == "" {
daemonConfig.NydusdConfigPath = constant.DefaultNydusDaemonConfigPath
}
daemonConfig.RecoverPolicy = RecoverPolicyRestart.String()
daemonConfig.FsDriver = constant.DefaultFsDriver
daemonConfig.LogRotationSize = constant.DefaultDaemonRotateLogMaxSize
// cache configuration
cacheConfig := &c.CacheManagerConfig
if cacheConfig.GCPeriod == "" {
cacheConfig.GCPeriod = constant.DefaultGCPeriod
}
return c.SetupNydusBinaryPaths()
}
func (c *SnapshotterConfig) SetupNydusBinaryPaths() error {
// resolve nydusd path
if path, err := exec.LookPath(constant.NydusdBinaryName); err == nil {
c.DaemonConfig.NydusdPath = path
}
// resolve nydus-image path
if path, err := exec.LookPath(constant.NydusImageBinaryName); err == nil {
c.DaemonConfig.NydusImagePath = path
}
return nil
}
nydus-snapshotter-0.13.4/config/global.go 0000664 0000000 0000000 00000013105 14533742372 0020357 0 ustar 00root root 0000000 0000000 /*
* Copyright (c) 2023. Nydus Developers. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*/
// Expose configurations across nydus-snapshotter, the configurations is parsed
// and extracted from nydus-snapshotter toml based configuration file or command line
package config
import (
"os"
"path/filepath"
"time"
"github.com/containerd/containerd/log"
"github.com/pkg/errors"
"github.com/containerd/nydus-snapshotter/internal/logging"
"github.com/containerd/nydus-snapshotter/pkg/utils/mount"
)
var (
globalConfig GlobalConfig
)
// Global cached configuration information to help:
// - access configuration information without passing a configuration object
// - avoid frequent generation of information from configuration information
type GlobalConfig struct {
origin *SnapshotterConfig
SnapshotsDir string
DaemonMode DaemonMode
SocketRoot string
ConfigRoot string
RootMountpoint string
DaemonThreadsNum int
CacheGCPeriod time.Duration
MirrorsConfig MirrorsConfig
}
func IsFusedevSharedModeEnabled() bool {
return globalConfig.DaemonMode == DaemonModeShared
}
func GetDaemonMode() DaemonMode {
return globalConfig.DaemonMode
}
func GetSnapshotsRootDir() string {
return globalConfig.SnapshotsDir
}
func GetRootMountpoint() string {
return globalConfig.RootMountpoint
}
func GetSocketRoot() string {
return globalConfig.SocketRoot
}
func GetConfigRoot() string {
return globalConfig.ConfigRoot
}
func GetMirrorsConfigDir() string {
return globalConfig.MirrorsConfig.Dir
}
func GetFsDriver() string {
return globalConfig.origin.DaemonConfig.FsDriver
}
func GetCacheGCPeriod() time.Duration {
return globalConfig.CacheGCPeriod
}
func GetLogDir() string {
return globalConfig.origin.LoggingConfig.LogDir
}
func GetLogLevel() string {
return globalConfig.origin.LoggingConfig.LogLevel
}
func GetDaemonLogRotationSize() int {
return globalConfig.origin.DaemonConfig.LogRotationSize
}
func GetDaemonThreadsNumber() int {
return globalConfig.origin.DaemonConfig.ThreadsNumber
}
func GetLogToStdout() bool {
return globalConfig.origin.LoggingConfig.LogToStdout
}
func IsSystemControllerEnabled() bool {
return globalConfig.origin.SystemControllerConfig.Enable
}
func SystemControllerAddress() string {
return globalConfig.origin.SystemControllerConfig.Address
}
func SystemControllerPprofAddress() string {
return globalConfig.origin.SystemControllerConfig.DebugConfig.PprofAddress
}
func GetDaemonProfileCPUDuration() int64 {
return globalConfig.origin.SystemControllerConfig.DebugConfig.ProfileDuration
}
func GetSkipSSLVerify() bool {
return globalConfig.origin.RemoteConfig.SkipSSLVerify
}
const (
TarfsLayerVerityOnly string = "layer_verity_only"
TarfsImageVerityOnly string = "image_verity_only"
TarfsLayerBlockDevice string = "layer_block"
TarfsImageBlockDevice string = "image_block"
TarfsLayerBlockWithVerity string = "layer_block_with_verity"
TarfsImageBlockWithVerity string = "image_block_with_verity"
)
func GetTarfsMountOnHost() bool {
return globalConfig.origin.Experimental.TarfsConfig.MountTarfsOnHost
}
func GetTarfsExportEnabled() bool {
switch globalConfig.origin.Experimental.TarfsConfig.ExportMode {
case TarfsLayerVerityOnly, TarfsLayerBlockDevice, TarfsLayerBlockWithVerity:
return true
case TarfsImageVerityOnly, TarfsImageBlockDevice, TarfsImageBlockWithVerity:
return true
default:
return false
}
}
// Returns (wholeImage, generateBlockImage, withVerityInfo)
// wholeImage: generate tarfs for the whole image instead of of a specific layer.
// generateBlockImage: generate a block image file.
// withVerityInfo: generate disk verity information.
func GetTarfsExportFlags() (bool, bool, bool) {
switch globalConfig.origin.Experimental.TarfsConfig.ExportMode {
case "layer_verity_only":
return false, false, true
case "image_verity_only":
return true, false, true
case "layer_block":
return false, true, false
case "image_block":
return true, true, false
case "layer_block_with_verity":
return false, true, true
case "image_block_with_verity":
return true, true, true
default:
return false, false, false
}
}
func ProcessConfigurations(c *SnapshotterConfig) error {
if c.LoggingConfig.LogDir == "" {
c.LoggingConfig.LogDir = filepath.Join(c.Root, logging.DefaultLogDirName)
}
if c.CacheManagerConfig.CacheDir == "" {
c.CacheManagerConfig.CacheDir = filepath.Join(c.Root, "cache")
}
globalConfig.origin = c
globalConfig.SnapshotsDir = filepath.Join(c.Root, "snapshots")
globalConfig.ConfigRoot = filepath.Join(c.Root, "config")
globalConfig.SocketRoot = filepath.Join(c.Root, "socket")
globalConfig.RootMountpoint = filepath.Join(c.Root, "mnt")
globalConfig.MirrorsConfig = c.RemoteConfig.MirrorsConfig
if c.CacheManagerConfig.GCPeriod != "" {
d, err := time.ParseDuration(c.CacheManagerConfig.GCPeriod)
if err != nil {
return errors.Errorf("invalid GC period '%s'", c.CacheManagerConfig.GCPeriod)
}
globalConfig.CacheGCPeriod = d
}
m, err := parseDaemonMode(c.DaemonMode)
if err != nil {
return err
}
if c.DaemonConfig.FsDriver == FsDriverFscache && m != DaemonModeShared {
log.L.Infof("fscache driver only supports 'shared' mode, override daemon mode from '%s' to 'shared'", m)
m = DaemonModeShared
}
globalConfig.DaemonMode = m
return nil
}
func SetUpEnvironment(c *SnapshotterConfig) error {
if err := os.MkdirAll(c.Root, 0700); err != nil {
return errors.Wrapf(err, "create root dir %s", c.Root)
}
realPath, err := mount.NormalizePath(c.Root)
if err != nil {
return errors.Wrapf(err, "invalid root path")
}
c.Root = realPath
return nil
}
nydus-snapshotter-0.13.4/docs/ 0000775 0000000 0000000 00000000000 14533742372 0016253 5 ustar 00root root 0000000 0000000 nydus-snapshotter-0.13.4/docs/configure_nydus.md 0000664 0000000 0000000 00000014166 14533742372 0022010 0 ustar 00root root 0000000 0000000 # Configure Nydus-snapshotter
Nydus-snapshotter can receive a toml file as its configurations to start providing image service through CLI parameter `--config`. An example configuration file can be found [here](../misc/snapshotter/config.toml). Besides nydus-snapshotter's configuration, `nydusd`'s configuration has to be provided to nydus-snapshotter too. Nydusd is started by nydus-snapshotter and it is configured by the provided json configuration file. A minimal configuration file can be found [here](../misc/snapshotter/nydusd-config.fusedev.json)
## Authentication
As [containerd#3731](https://github.com/containerd/containerd/issues/3731) discussed, containerd doesn't share credentials with third snapshotters now. Like [stargz snapshotter](https://github.com/containerd/stargz-snapshotter/blob/main/docs/overview.md#authentication), nydus-snapshotter supports 3 main ways to access registries with custom configurations. You can use configuration file to enable them.
The snapshotter will try to get image pull keychain in the following order if such way is enabled:
1. intercept CRI request and extract private registry auth
2. docker config (default enabled)
3. k8s docker config secret
### dockerconfig-based authentication
By default, the snapshotter tries to get credentials from `$DOCKER_CONFIG` or `~/.docker/config.json`.
Following example enables nydus-snapshotter to access to private registries using `docker login` command.
```console
# docker login
(Enter username and password)
# crictl pull --creds USERNAME[:PASSWORD] docker.io//ubuntu:22.04
(Here the credential is only used by containerd)
```
### CRI-based authentication
Following configuration enables nydus-snapshotter to pull private images via CRI requests.
```toml
[remote.auth]
# Fetch the private registry auth as CRI image service proxy
enable_cri_keychain = true
image_service_address = "/run/containerd/containerd.sock"
```
The snapshotter works as a proxy of CRI Image Service and exposes CRI Image Service API on the snapshotter's unix socket (i.e. `/run/containerd/containerd-nydus-grpc.sock`). The snapshotter acquires registry creds by scanning requests.
The `image_service_address` is the original image service socket. It can be omitted and the default value will be the containerd's default socket path `/run/containerd/containerd.sock`.
You **must** specify `--image-service-endpoint=unix:///run/containerd-nydus/containerd-nydus-grpc.sock` option to kubelet when using Kubernetes. Or specify `image-endpoint: "unix:////run/containerd-nydus/containerd-nydus-grpc.sock"` in `crictl.yaml` when using `crictl`.
### kubeconfig-based authentication
This is another way to enable lazy pulling of private images on Kubernetes, Nydus snapshotter will start a goroutine to listen on secrets (type = `kubernetes.io/dockerconfigjson`) for private registries.
#### use kubeconfig file
Following configuration enables nydus-snapshotter to access to private registries using kubernetes secrets in the cluster using kubeconfig files.
```toml
[remote.auth]
enable_kubeconfig_keychain = true
kubeconfig_path = "/path/to/.kubeconfig"
```
If no `kubeconfig_path` is specified, snapshotter searches kubeconfig files from `$KUBECONFIG` or `~/.kube/config`.
Please note that kubeconfig-based authentication requires additional privilege (i.e. kubeconfig to list/watch secrets) to the node.
And this doesn't work if `kubelet` retrieve credentials from somewhere not API server (e.g. [credential provider](https://kubernetes.io/docs/tasks/kubelet-credential-provider/kubelet-credential-provider/)).
#### use ServiceAccount
If your Nydus snapshotter runs in a Kubernetes cluster and you don't want to use kubeconfig, you can also choose to use ServiceAccount to configure the corresponding permissions for Nydus snapshotter.
```yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: nydus-snapshotter-sa
namespace: nydus-system
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: nydus-snapshotter-role
rules:
- apiGroups:
- ""
resources:
- secrets
verbs:
- get
- list
- watch
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: nydus-snapshotter-role-binding
roleRef:
kind: ClusterRole
name: nydus-snapshotter-role
apiGroup: rbac.authorization.k8s.io
subjects:
- kind: ServiceAccount
name: nydus-snapshotter-sa
namespace: nydus-system
```
Then you can set the desired ServiceAccount with the required permissions fro your Nydus snapshotter Pod:
```yaml
apiVersion: v1
kind: Pod
metadata:
name: nydus-snapshotter
namespace: nydus-system
spec:
containers:
name: nydus-snapshotter
serviceAccountName: nydus-snapshotter-sa
...
```
#### create secrets
If you have logged into a private registry, you can create a secret from the config file:
```bash
$ kubectl create --namespace nydus-system secret generic regcred \
--from-file=.dockerconfigjson=$HOME/.docker/config.json \
--type=kubernetes.io/dockerconfigjson
```
The Nydus snapshotter will get the new secret and parse the authorization. If your new Pod uses a private registry, then this authentication information will be used to pull the image from the private registry.
## Metrics
Nydusd records metrics in its own format. The metrics are exported via a HTTP server on top of unix domain socket. Nydus-snapshotter fetches the metrics and convert them in to Prometheus format which is exported via a network address. Nydus-snapshotter by default does not fetch metrics from nydusd. You can enable the nydusd metrics download by assigning a network address to `metrics.address` in nydus-snapshotter's toml [configuration file](../misc/snapshotter/config.toml).
Once this entry is enabled, not only nydusd metrics, but also some information about the nydus-snapshotter
runtime and snapshot related events are exported in Prometheus format as well.
## Diagnose
A system controller can be ran insides nydus-snapshotter.
By setting `system.enable` to `true`, nydus-snapshotter will start a simple HTTP server on unix domain socket `system.address` path and exports some internal working status to users. The address defaults to `/var/run/containerd-nydus/system.sock`
nydus-snapshotter-0.13.4/docs/crictl_dry_run.md 0000664 0000000 0000000 00000002147 14533742372 0021623 0 ustar 00root root 0000000 0000000 # Start Container by crictl
## Create crictl Config
The runtime endpoint can be set in the config file. Please refer to [crictl](https://github.com/kubernetes-sigs/cri-tools/blob/master/docs/crictl.md) document for more details.
Compose your `crictl` configuration file named as `crictl.yaml`:
```yml
runtime-endpoint: unix:///run/containerd/containerd.sock
image-endpoint: unix:///run/containerd/containerd.sock
timeout: 10
debug: true
```
Compose a pod configuration which can be named as `pod.yaml`
```yml
metadata:
attempt: 1
name: nydus-sandbox
namespace: default
uid: hdishd83djaidwnduwk28bcsb
log_directory: /tmp
linux:
security_context:
namespace_options:
network: 2
```
Compose a container configuration which can be named as `container.yaml`
```yml
metadata:
name: nydus-container
image:
image:
command:
- /bin/sleep
args:
- 600
log_path: container.1.log
```
Start a container based on nydus image.
```bash
# auth is base64 encoded string from "username:password"
$ crictl --config ./crictl.yaml run --auth ./container.yaml ./pod.yaml
```
nydus-snapshotter-0.13.4/docs/diagram/ 0000775 0000000 0000000 00000000000 14533742372 0017657 5 ustar 00root root 0000000 0000000 nydus-snapshotter-0.13.4/docs/diagram/nydus_fscache_erofs_arch.svg 0000664 0000000 0000000 00000056626 14533742372 0025430 0 ustar 00root root 0000000 0000000