diff --git a/images/cdi-artifact/patches/024-cdi-controller-change-bash-utils-to-binary.patch b/images/cdi-artifact/patches/024-cdi-controller-change-bash-utils-to-binary.patch index 7545eb3530..b8007eb235 100644 --- a/images/cdi-artifact/patches/024-cdi-controller-change-bash-utils-to-binary.patch +++ b/images/cdi-artifact/patches/024-cdi-controller-change-bash-utils-to-binary.patch @@ -1,15 +1,14 @@ diff --git a/pkg/controller/clone/prep-claim.go b/pkg/controller/clone/prep-claim.go -index 68a249b77..5e96feee0 100644 +index 68a249b77..3c7e96959 100644 --- a/pkg/controller/clone/prep-claim.go +++ b/pkg/controller/clone/prep-claim.go -@@ -162,8 +162,8 @@ func (p *PrepClaimPhase) createPod(ctx context.Context, name string, pvc *corev1 +@@ -162,8 +162,7 @@ func (p *PrepClaimPhase) createPod(ctx context.Context, name string, pvc *corev1 Name: "dummy", Image: p.Image, ImagePullPolicy: p.PullPolicy, - Command: []string{"/bin/bash"}, - Args: []string{"-c", "echo", "'hello cdi'"}, + Command: []string{"/bin/hello"}, -+ // Args: []string{"-c", "echo", "'hello cdi'"}, }, }, ImagePullSecrets: imagePullSecrets, diff --git a/images/cdi-controller/hello.c b/images/cdi-artifact/static_binaries/hello.c similarity index 100% rename from images/cdi-controller/hello.c rename to images/cdi-artifact/static_binaries/hello.c diff --git a/images/cdi-controller/print_file_context.c b/images/cdi-artifact/static_binaries/print_file_context.c similarity index 100% rename from images/cdi-controller/print_file_context.c rename to images/cdi-artifact/static_binaries/print_file_context.c diff --git a/images/cdi-artifact/werf.inc.yaml b/images/cdi-artifact/werf.inc.yaml index ccc3db53f4..54dc518522 100644 --- a/images/cdi-artifact/werf.inc.yaml +++ b/images/cdi-artifact/werf.inc.yaml @@ -86,3 +86,26 @@ shell: - chmod +x /cdi-binaries/* - chown -R 64535:64535 /cdi-binaries/* - ls -la /cdi-binaries + +--- +image: {{ $.ImageName }}-cbuilder +final: false +from: {{ .Images.BASE_DEBIAN_BOOKWORM_SLIM }} +git: + - add: /images/{{ $.ImageName }}/static_binaries + to: / + stageDependencies: + install: + - '*.c' +shell: + install: + - | + apt-get update && apt-get install --yes gcc musl-dev musl-tools + apt-get clean + + echo "Building simple app that prints hello cdi" + mkdir -p /bins + musl-gcc -static -Os -o /bins/hello hello.c + musl-gcc -static -Os -o /bins/printFile print_file_context.c + strip /bins/hello + strip /bins/printFile diff --git a/images/cdi-cloner/cloner-startup/.golangci.yaml b/images/cdi-cloner/cloner-startup/.golangci.yaml new file mode 100644 index 0000000000..e1e5415199 --- /dev/null +++ b/images/cdi-cloner/cloner-startup/.golangci.yaml @@ -0,0 +1,60 @@ +run: + timeout: 10m +# skip-dirs: +# this code has been copied from kubectl cli. No need to lint external code. +# - go_lib/dependency/k8s/drain +# this code has been copied from kubernetes vertical-pod-autoscaler. No need to lint external code. +# - modules/302-vertical-pod-autoscaler/hooks/internal/vertical-pod-autoscaler/v1 +issues: + # Show all errors. + max-issues-per-linter: 0 + max-same-issues: 0 + exclude: + # - ST1005.* + - "don't use an underscore in package name" +# - "exported: .*" + +linters-settings: + gofumpt: + extra-rules: true + gci: + sections: + - standard + - default + - prefix(github.com/deckhouse/) + goimports: + local-prefixes: github.com/deckhouse/ + errcheck: + exclude-functions: fmt:.*,[rR]ead|[wW]rite|[cC]lose,io:Copy + revive: + rules: + - name: dot-imports + disabled: true + +linters: + disable-all: true + enable: + - asciicheck + - bidichk + - bodyclose + - dogsled + - errcheck + - errname + - errorlint + - gci + - gocritic + - gofmt + - gofumpt + - goimports + - gosimple + - govet + - ineffassign + - misspell + - nolintlint + - revive + - staticcheck + - typecheck + - unconvert + - unparam + - unused + - whitespace diff --git a/images/cdi-cloner/cloner-startup/cmd/cloner-startup/main.go b/images/cdi-cloner/cloner-startup/cmd/cloner-startup/main.go new file mode 100644 index 0000000000..67a1431e93 --- /dev/null +++ b/images/cdi-cloner/cloner-startup/cmd/cloner-startup/main.go @@ -0,0 +1,101 @@ +/* +Copyright 2025 Flant JSC + +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. +*/ + +package main + +import ( + "fmt" + "log/slog" + "os" + + "cloner-startup/internal/helpers" +) + +func main() { + var uploadBytes uint64 + + logger := slog.New(slog.NewJSONHandler(os.Stdout, nil)) + slog.SetDefault(logger) + + volumeMode, err := helpers.GetEnv("VOLUME_MODE") + if err != nil { + logger.Error("Failed to get env VOLUME_MODE", slog.String("error", err.Error())) + os.Exit(1) + } + + mountPoint, err := helpers.GetEnv("MOUNT_POINT") + if err != nil { + logger.Error("Failed to get env MOUNT_POINT", slog.String("error", err.Error())) + os.Exit(1) + } + + preallocation, err := helpers.GetBoolEnv("PREALLOCATION") + if err != nil { + logger.Error("Failed to get env PREALLOCATION", slog.String("error", err.Error())) + os.Exit(1) + } + + logger.Info(fmt.Sprintf( + "VOLUME_MODE=%s\n"+ + "MOUNT_POINT=%s\n"+ + "PREALLOCATION=%v", + volumeMode, + mountPoint, + preallocation)) + + if volumeMode == "block" { + uploadBytes, err = helpers.GetBlockDeviceSize(mountPoint) + if err != nil { + logger.Error("Block size calculation failed", slog.String("error", err.Error())) + os.Exit(1) + } + + logger.Info(fmt.Sprintf("Start clone block with %s", helpers.FormatBytes(float64(uploadBytes)))) + + if err = helpers.RunCloner("blockdevice-clone", uploadBytes, mountPoint); err != nil { + logger.Error("Error running cdi-cloner", slog.String("error", err.Error())) + os.Exit(1) + } + } else { + // Check if directory accesseble + if err := os.Chdir(mountPoint); err != nil { + logger.Error("Mount point access failed", slog.String("error", err.Error())) + os.Exit(1) + } + + totalBytes, totalUsedBytes, err := helpers.GetDirectorySize(".") + if err != nil { + logger.Error("Directory size calculation failed", slog.String("error", err.Error())) + os.Exit(1) + } + + if preallocation { + uploadBytes = totalBytes + logger.Info("Preallocating filesystem, uploading all bytes") + } else { + uploadBytes = totalUsedBytes + logger.Info("Not preallocating filesystem, get only used blocks in bytes") + } + + logger.Info(fmt.Sprintf("Start clone with %d bytes", uploadBytes)) + logger.Info(fmt.Sprintf("Start clone with %s", helpers.FormatBytes(float64(uploadBytes)))) + + if err = helpers.RunCloner("filesystem-clone", uploadBytes, mountPoint); err != nil { + logger.Error("Error running cdi-cloner", slog.String("error", err.Error())) + os.Exit(1) + } + } +} diff --git a/images/cdi-cloner/cloner-startup/go.mod b/images/cdi-cloner/cloner-startup/go.mod new file mode 100644 index 0000000000..cc62c0d415 --- /dev/null +++ b/images/cdi-cloner/cloner-startup/go.mod @@ -0,0 +1,5 @@ +module cloner-startup + +go 1.22.8 + +require golang.org/x/sys v0.30.0 diff --git a/images/cdi-cloner/cloner-startup/go.sum b/images/cdi-cloner/cloner-startup/go.sum new file mode 100644 index 0000000000..241f4caf9c --- /dev/null +++ b/images/cdi-cloner/cloner-startup/go.sum @@ -0,0 +1,2 @@ +golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= +golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= diff --git a/images/cdi-cloner/cloner-startup/internal/helpers/cloner.go b/images/cdi-cloner/cloner-startup/internal/helpers/cloner.go new file mode 100644 index 0000000000..eae15e6cae --- /dev/null +++ b/images/cdi-cloner/cloner-startup/internal/helpers/cloner.go @@ -0,0 +1,39 @@ +/* +Copyright 2025 Flant JSC + +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. +*/ + +package helpers + +import ( + "os" + "os/exec" + "strconv" +) + +func RunCloner(contentType string, uploadBytes uint64, mountPoint string) error { + cmd := exec.Command("/usr/bin/cdi-cloner", + "-v=3", + "-alsologtostderr", + "-content-type="+contentType, + "-upload-bytes="+strconv.FormatUint(uploadBytes, 10), + "-mount="+mountPoint, + ) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + if err := cmd.Run(); err != nil { + return err + } + return nil +} diff --git a/images/cdi-cloner/cloner-startup/internal/helpers/env_process.go b/images/cdi-cloner/cloner-startup/internal/helpers/env_process.go new file mode 100644 index 0000000000..efdc88101f --- /dev/null +++ b/images/cdi-cloner/cloner-startup/internal/helpers/env_process.go @@ -0,0 +1,39 @@ +/* +Copyright 2025 Flant JSC + +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. +*/ + +package helpers + +import ( + "fmt" + "os" + "strconv" +) + +func GetEnv(key string) (string, error) { + value, ok := os.LookupEnv(key) + if !ok { + return "", fmt.Errorf("missing required environment variable: %s", key) + } + return value, nil +} + +func GetBoolEnv(key string) (bool, error) { + value, err := GetEnv(key) + if err != nil { + return false, err + } + return strconv.ParseBool(value) +} diff --git a/images/cdi-cloner/cloner-startup/internal/helpers/size.go b/images/cdi-cloner/cloner-startup/internal/helpers/size.go new file mode 100644 index 0000000000..2eb05cba89 --- /dev/null +++ b/images/cdi-cloner/cloner-startup/internal/helpers/size.go @@ -0,0 +1,109 @@ +/* +Copyright 2025 Flant JSC + +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. +*/ + +package helpers + +import ( + "fmt" + "os" + "path/filepath" + "syscall" + "unsafe" + + "golang.org/x/sys/unix" +) + +const ( + /* + IOCTL request code that queries the size of a block device (e.g., disks, partitions) directly from the kernel + + Structure of ioctl Codes: + + Data Type: 0x8008 (upper 16 bits) indicates: + Direction: Read from the kernel (0x2) (upper 2 bits). + Size: 0x008 (remaining 14 bits) == 8 bytes, matching the uint64 return type + + Magic Number : 0x12 (the third byte) identifies this as a block device ioctl + + Command Number : 0x72 (the fourth byte) specifies the exact operation (size query) + */ + BLKGETSIZE64 = 0x80081272 +) + +// GetBlockDeviceSize returns block size in bytes +func GetBlockDeviceSize(device string) (uint64, error) { + fd, err := unix.Open(device, unix.O_RDONLY, 0) + if err != nil { + return 0, fmt.Errorf("open device %s: %w", device, err) + } + defer unix.Close(fd) + + var size uint64 + _, _, errno := unix.Syscall( + syscall.SYS_IOCTL, + uintptr(fd), + uintptr(BLKGETSIZE64), + uintptr(unsafe.Pointer(&size)), + ) + if errno != 0 { + return 0, fmt.Errorf("get size for block device %s: %w", device, errno) + } + return size, nil +} + +// Calculates directory size using Go's filepath.Walk +// +// return totalBytes and totalUsedBytes(Blocks * Blksize) +func GetDirectorySize(path string) (uint64, uint64, error) { + var ( + totalBytes uint64 + totalUsedBytes uint64 + ) + + err := filepath.Walk(path, func(p string, info os.FileInfo, err error) error { + if err != nil || info.IsDir() { + return err + } + + totalBytes += uint64(info.Size()) + + if stat, ok := info.Sys().(*syscall.Stat_t); ok { + totalUsedBytes += uint64(stat.Blocks * int64(stat.Blksize)) + } + return nil + }) + return totalBytes, totalUsedBytes, err +} + +// Convert byte size to human readable format +func FormatBytes(s float64) string { + var base float64 = 1024.0 + sizes := []string{"B", "kB", "MB", "GB", "TB", "PB", "EB"} + + unitsLimit := len(sizes) + i := 0 + for s >= base && i < unitsLimit { + s /= base + i++ + } + + f := "%.0f %s" + if i > 1 { + f = "%.2f %s" + } + + return fmt.Sprintf(f, s, sizes[i]) +} diff --git a/images/cdi-cloner/werf.inc.yaml b/images/cdi-cloner/werf.inc.yaml index ea0e24c4a4..ef32a1d8a4 100644 --- a/images/cdi-cloner/werf.inc.yaml +++ b/images/cdi-cloner/werf.inc.yaml @@ -6,20 +6,38 @@ import: add: /relocate to: / before: setup +- image: {{ $.ImageName }}-gobuild + add: /cdi-binaries + to: /usr/bin + includePaths: + - cloner-startup + before: setup +- image: cdi-artifact-cbuilder + add: /bins + to: /usr/bin + before: setup + includePaths: + - hello - image: cdi-artifact add: /cdi-binaries to: /usr/bin includePaths: - cdi-cloner - - cloner_startup.sh before: setup -# Source https://github.com/kubevirt/containerized-data-importer/blob/v1.58.0/cmd/cdi-cloner/BUILD.bazel +# Source https://github.com/kubevirt/containerized-data-importer/blob/v1.60.3/cmd/cdi-cloner/BUILD.bazel imageSpec: config: - entrypoint: ["/usr/bin/cloner_startup.sh"] + entrypoint: ["/usr/bin/cloner-startup"] user: 64535 --- -{{- $binaries := "/usr/bin/sh /usr/bin/bash /usr/sbin/blockdev /usr/bin/mount /usr/bin/umount /usr/sbin/fsck /usr/sbin/blkid /usr/sbin/mkfs /usr/sbin/mkfs.ext4 /usr/sbin/mkfs.xfs /usr/sbin/dumpe2fs /usr/sbin/xfs_io /usr/sbin/xfs_growfs /usr/sbin/resize2fs" }} +{{- $name := print $.ImageName "-dependencies" -}} +{{- define "$name" -}} +binaries: +- /usr/bin/mount +- /usr/bin/umount +{{- end -}} + +{{ $virtCDIClonerDependencies := include "$name" . | fromYaml }} image: {{ $.ImageName }}-bins final: false @@ -27,4 +45,20 @@ fromImage: base-alt-p11-binaries shell: beforeInstall: - | - /relocate_binaries.sh -i "{{ $binaries }}" -o /relocate \ No newline at end of file + /relocate_binaries.sh -i "{{ $virtCDIClonerDependencies.binaries | join " " }}" -o /relocate +--- +image: {{ $.ImageName }}-gobuild +final: false +from: {{ .Images.BASE_GOLANG_22_BOOKWORM }} +git: + - add: /images/{{ $.ImageName }}/cloner-startup + to: /app + stageDependencies: + install: + - '**/*' +shell: + install: + - | + mkdir -p /cdi-binaries + cd /app + go build -ldflags="-s -w" -o /cdi-binaries/cloner-startup ./cmd/cloner-startup \ No newline at end of file diff --git a/images/cdi-controller/werf.inc.yaml b/images/cdi-controller/werf.inc.yaml index a59e446e2c..8407e06d1b 100644 --- a/images/cdi-controller/werf.inc.yaml +++ b/images/cdi-controller/werf.inc.yaml @@ -25,10 +25,12 @@ image: {{ $.ImageName }}-bins final: false fromImage: base-alt-p11-binaries import: -- image: {{ $.ImageName }}-cbuilder +- image: cdi-artifact-cbuilder add: /bins to: /relocate/usr/bin after: setup + includePaths: + - printFile - image: cdi-artifact add: /cdi-binaries to: /usr/bin @@ -51,26 +53,3 @@ shell: - | mkdir -p /relocate/{tmp,var/run/certs/cdi-uploadserver-signer,var/run/certs/cdi-uploadserver-client-signer} chown -R 64535:64535 /relocate/ - ---- -image: {{ $.ImageName }}-cbuilder -final: false -from: {{ .Images.BASE_DEBIAN_BOOKWORM_SLIM }} -git: - - add: /images/{{ $.ImageName }} - to: / - stageDependencies: - install: - - '*.c' -shell: - install: - - | - apt-get update && apt-get install --yes gcc musl-dev musl-tools - apt-get clean - - echo "Building simple app that prints hello cdi" - mkdir -p /bins - musl-gcc -static -Os -o /bins/hello hello.c - musl-gcc -static -Os -o /bins/printFile print_file_context.c - strip /bins/hello - strip /bins/printFile