diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 74ee92dda6..bbe0b65106 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -48,6 +48,7 @@ jobs: name: nydus-snapshotter_artifacts path: | bin/containerd-nydus-grpc + bin/nydus-overlayfs bin/optimizer-nri-plugin bin/optimizer-server upload: diff --git a/Makefile b/Makefile index 32313a4355..45e8e67861 100644 --- a/Makefile +++ b/Makefile @@ -54,9 +54,11 @@ STATIC_OPTIMIZER_SERVER_BIN = ${OPTIMIZER_SERVER}/target/x86_64-unknown-linux-gn .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: @@ -65,6 +67,7 @@ build-optimizer: 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 @@ -86,6 +89,8 @@ clean-optimizer: 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 @@ -129,6 +134,7 @@ smoke: .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 \ diff --git a/cmd/nydus-overlayfs/main.go b/cmd/nydus-overlayfs/main.go new file mode 100644 index 0000000000..1f3152e8ed --- /dev/null +++ b/cmd/nydus-overlayfs/main.go @@ -0,0 +1,146 @@ +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) +}