KubeOS/0001-KubeOS-add-unit-tests-of-containerd-and-docker-modif.patch
Yuhang Wei 1d36b74685 KubeOS:sync code from source master branch
Signed-off-by: Yuhang Wei <weiyuhang3@huawei.com>
2024-02-26 09:54:27 +08:00

480 lines
15 KiB
Diff

From 790d53dc581874575aef1777122856a59bcdbf8b Mon Sep 17 00:00:00 2001
From: liyuanr <liyuanrong1@huawei.com>
Date: Thu, 3 Aug 2023 22:18:23 +0800
Subject: [PATCH 01/17] KubeOS:add unit tests of containerd and docker,modify
code for cleancode
Add unit tests of using containerd or docker to upgrade.
Modify code for cleancode and fix issue of ctr images pull
Signed-off-by: liyuanr <liyuanrong1@huawei.com>
---
cmd/agent/server/containerd_image.go | 8 +-
cmd/agent/server/containerd_image_test.go | 139 ++++++++++++++++++++++
cmd/agent/server/docker_image.go | 2 +-
cmd/agent/server/docker_image_test.go | 88 ++++++++++++--
cmd/agent/server/utils.go | 102 ++++++++--------
5 files changed, 272 insertions(+), 67 deletions(-)
create mode 100644 cmd/agent/server/containerd_image_test.go
diff --git a/cmd/agent/server/containerd_image.go b/cmd/agent/server/containerd_image.go
index f180fb5..fd61274 100644
--- a/cmd/agent/server/containerd_image.go
+++ b/cmd/agent/server/containerd_image.go
@@ -23,9 +23,7 @@ import (
pb "openeuler.org/KubeOS/cmd/agent/api"
)
-var (
- defaultNamespace = "k8s.io"
-)
+const defaultNamespace = "k8s.io"
type conImageHandler struct{}
@@ -56,7 +54,7 @@ func (c conImageHandler) getRootfsArchive(req *pb.UpdateRequest, neededPath prep
}
} else {
containerdCommand = "ctr"
- if err := runCommand("ctr", "-n", defaultNamespace, "images", "pull", "--host-dir",
+ if err := runCommand("ctr", "-n", defaultNamespace, "images", "pull", "--hosts-dir",
"/etc/containerd/certs.d", imageName); err != nil {
return "", err
}
@@ -76,7 +74,7 @@ func (c conImageHandler) getRootfsArchive(req *pb.UpdateRequest, neededPath prep
return "", err
}
defer checkAndCleanMount(mountPath)
- if err := copyFile(neededPath.tarPath, mountPath+"/"+rootfsArchive); err != nil {
+ if err := copyFile(neededPath.tarPath, mountPath+"/"+neededPath.rootfsFile); err != nil {
return "", err
}
return "", nil
diff --git a/cmd/agent/server/containerd_image_test.go b/cmd/agent/server/containerd_image_test.go
new file mode 100644
index 0000000..d7133c3
--- /dev/null
+++ b/cmd/agent/server/containerd_image_test.go
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) Huawei Technologies Co., Ltd. 2023. All rights reserved.
+ * KubeOS is licensed under the Mulan PSL v2.
+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
+ * You may obtain a copy of Mulan PSL v2 at:
+ * http://license.coscl.org.cn/MulanPSL2
+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
+ * PURPOSE.
+ * See the Mulan PSL v2 for more details.
+ */
+
+// Package server implements server of os-agent and listener of os-agent server. The server uses gRPC interface.
+package server
+
+import (
+ "os"
+ "testing"
+
+ "github.com/agiledragon/gomonkey/v2"
+ pb "openeuler.org/KubeOS/cmd/agent/api"
+)
+
+func Test_conImageHandler_downloadImage(t *testing.T) {
+ type args struct {
+ req *pb.UpdateRequest
+ }
+ tests := []struct {
+ name string
+ c conImageHandler
+ args args
+ want string
+ wantErr bool
+ }{
+
+ {
+ name: "pullImageError",
+ c: conImageHandler{},
+ args: args{
+ req: &pb.UpdateRequest{ContainerImage: "testError"},
+ },
+ want: "",
+ wantErr: true,
+ },
+ {
+ name: "checkSumError",
+ c: conImageHandler{},
+ args: args{
+ req: &pb.UpdateRequest{ContainerImage: "docker.io/library/hello-world:latest"},
+ },
+ want: "",
+ wantErr: true,
+ },
+ {
+ name: "normal",
+ c: conImageHandler{},
+ args: args{
+ req: &pb.UpdateRequest{
+ ContainerImage: "docker.io/library/hello-world:latest",
+ },
+ },
+ want: "update-test1/upadte.img",
+ wantErr: false,
+ },
+ }
+ patchPrepareEnv := gomonkey.ApplyFunc(prepareEnv, func() (preparePath, error) {
+ return preparePath{updatePath: "update-test1/",
+ mountPath: "update-test1/mountPath",
+ tarPath: "update-test1/mountPath/hello",
+ imagePath: "update-test1/upadte.img",
+ rootfsFile: "hello"}, nil
+ })
+ defer patchPrepareEnv.Reset()
+ patchCreateOSImage := gomonkey.ApplyFunc(createOSImage, func(neededPath preparePath) (string, error) {
+ return "update-test1/upadte.img", nil
+ })
+ defer patchCreateOSImage.Reset()
+
+ if err := os.MkdirAll("update-test1/mountPath", os.ModePerm); err != nil {
+ t.Errorf("create test dir error = %v", err)
+ return
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ c := conImageHandler{}
+ if tt.name == "normal" {
+ imageDigests, err := getOCIImageDigest("crictl", "docker.io/library/hello-world:latest")
+ if err != nil {
+ t.Errorf("conImageHandler.getRootfsArchive() get oci image digests error = %v", err)
+ }
+ tt.args.req.CheckSum = imageDigests
+ }
+ got, err := c.downloadImage(tt.args.req)
+ if (err != nil) != tt.wantErr {
+ t.Errorf("conImageHandler.downloadImage() error = %v, wantErr %v", err, tt.wantErr)
+ return
+ }
+ if got != tt.want {
+ t.Errorf("conImageHandler.downloadImage() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+ defer func() {
+ if err := runCommand("crictl", "rmi", "docker.io/library/hello-world:latest"); err != nil {
+ t.Errorf("remove kubeos-temp container error = %v", err)
+ }
+ if err := os.RemoveAll("update-test1"); err != nil {
+ t.Errorf("remove update-test error = %v", err)
+ }
+ }()
+}
+
+func Test_copyFile(t *testing.T) {
+ type args struct {
+ dstFileName string
+ srcFileName string
+ }
+ tests := []struct {
+ name string
+ args args
+ wantErr bool
+ }{
+ {
+ name: "srcFileNotExist",
+ args: args{
+ dstFileName: "bbb.txt",
+ srcFileName: "aaa.txt",
+ },
+ wantErr: true,
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ if err := copyFile(tt.args.dstFileName, tt.args.srcFileName); (err != nil) != tt.wantErr {
+ t.Errorf("copyFile() error = %v, wantErr %v", err, tt.wantErr)
+ }
+ })
+ }
+}
diff --git a/cmd/agent/server/docker_image.go b/cmd/agent/server/docker_image.go
index 23e596b..0b6ee35 100644
--- a/cmd/agent/server/docker_image.go
+++ b/cmd/agent/server/docker_image.go
@@ -61,7 +61,7 @@ func (d dockerImageHandler) getRootfsArchive(req *pb.UpdateRequest, neededPath p
if err != nil {
return "", err
}
- if err := runCommand("docker", "cp", containerId+":/"+rootfsArchive, neededPath.updatePath); err != nil {
+ if err := runCommand("docker", "cp", containerId+":/"+neededPath.rootfsFile, neededPath.updatePath); err != nil {
return "", err
}
defer func() {
diff --git a/cmd/agent/server/docker_image_test.go b/cmd/agent/server/docker_image_test.go
index 9987939..2dbf337 100644
--- a/cmd/agent/server/docker_image_test.go
+++ b/cmd/agent/server/docker_image_test.go
@@ -17,38 +17,102 @@ import (
"os"
"testing"
+ "github.com/agiledragon/gomonkey/v2"
pb "openeuler.org/KubeOS/cmd/agent/api"
)
-func TestpullOSImage(t *testing.T) {
+func Test_dockerImageHandler_downloadImage(t *testing.T) {
type args struct {
req *pb.UpdateRequest
}
- os.Mkdir("/persist", os.ModePerm)
tests := []struct {
name string
+ d dockerImageHandler
args args
want string
wantErr bool
}{
- {name: "pull image error", args: args{req: &pb.UpdateRequest{
- DockerImage: "test",
- }}, want: "", wantErr: true},
- {name: "normal", args: args{req: &pb.UpdateRequest{
- DockerImage: "centos",
- }}, want: "/persist/update.img", wantErr: false},
+ {
+ name: "pullImageError",
+ d: dockerImageHandler{},
+ args: args{
+ req: &pb.UpdateRequest{ContainerImage: "testError"},
+ },
+ want: "",
+ wantErr: true,
+ },
+
+ {
+ name: "checkSumError",
+ d: dockerImageHandler{},
+ args: args{
+ req: &pb.UpdateRequest{ContainerImage: "hello-world", CheckSum: "aaaaaa"},
+ },
+ want: "",
+ wantErr: true,
+ },
+
+ {
+ name: "normal",
+ d: dockerImageHandler{},
+ args: args{
+ req: &pb.UpdateRequest{ContainerImage: "hello-world"},
+ },
+ want: "update-test/upadte.img",
+ wantErr: false,
+ },
+ }
+ patchPrepareEnv := gomonkey.ApplyFunc(prepareEnv, func() (preparePath, error) {
+ return preparePath{updatePath: "update-test/",
+ mountPath: "update-test/mountPath",
+ tarPath: "update-test/mountPath/hello",
+ imagePath: "update-test/upadte.img",
+ rootfsFile: "hello"}, nil
+ })
+ defer patchPrepareEnv.Reset()
+
+ patchCreateOSImage := gomonkey.ApplyFunc(createOSImage, func(neededPath preparePath) (string, error) {
+ return "update-test/upadte.img", nil
+ })
+ defer patchCreateOSImage.Reset()
+
+ if err := os.MkdirAll("update-test/mountPath", os.ModePerm); err != nil {
+ t.Errorf("create test dir error = %v", err)
+ return
}
+
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
- got, err := pullOSImage(tt.args.req)
+ if tt.name == "normal" {
+ _, err := runCommandWithOut("docker", "create", "--name", "kubeos-temp", "hello-world")
+ if err != nil {
+ t.Errorf("Test_dockerImageHandler_getRootfsArchive create container error = %v", err)
+ return
+ }
+ imageDigests, err := getOCIImageDigest("docker", "hello-world")
+
+ if err != nil {
+ t.Errorf("Test_dockerImageHandler_getRootfsArchive get oci image digests error = %v", err)
+ }
+ tt.args.req.CheckSum = imageDigests
+ }
+ d := dockerImageHandler{}
+ got, err := d.downloadImage(tt.args.req)
if (err != nil) != tt.wantErr {
- t.Errorf("pullOSImage() error = %v, wantErr %v", err, tt.wantErr)
+ t.Errorf("dockerImageHandler.downloadImage() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.want {
- t.Errorf("pullOSImage() = %v, want %v", got, tt.want)
+ t.Errorf("dockerImageHandler.downloadImage() = %v, want %v", got, tt.want)
}
})
}
- defer os.RemoveAll("/persist")
+ defer func() {
+ if err := runCommand("docker", "rmi", "hello-world"); err != nil {
+ t.Errorf("remove kubeos-temp container error = %v", err)
+ }
+ if err := os.RemoveAll("update-test"); err != nil {
+ t.Errorf("remove update-test error = %v", err)
+ }
+ }()
}
diff --git a/cmd/agent/server/utils.go b/cmd/agent/server/utils.go
index c8a72c3..7134d74 100644
--- a/cmd/agent/server/utils.go
+++ b/cmd/agent/server/utils.go
@@ -31,10 +31,7 @@ import (
const (
needGBSize = 3 // the max size of update files needed
// KB is 1024 B
- KB = 1024
-)
-
-var (
+ KB = 1024
rootfsArchive = "os.tar"
updateDir = "KubeOS-Update"
mountDir = "kubeos-update"
@@ -51,6 +48,7 @@ type preparePath struct {
mountPath string
tarPath string
imagePath string
+ rootfsFile string
}
func runCommand(name string, args ...string) error {
@@ -192,9 +190,10 @@ func prepareEnv() (preparePath, error) {
if err := checkDiskSize(needGBSize, PersistDir); err != nil {
return preparePath{}, err
}
+ rootfsFile := rootfsArchive
updatePath := splicePath(PersistDir, updateDir)
mountPath := splicePath(updatePath, mountDir)
- tarPath := splicePath(updatePath, rootfsArchive)
+ tarPath := splicePath(updatePath, rootfsFile)
imagePath := splicePath(PersistDir, osImageName)
if err := cleanSpace(updatePath, mountPath, imagePath); err != nil {
@@ -208,6 +207,7 @@ func prepareEnv() (preparePath, error) {
mountPath: mountPath,
tarPath: tarPath,
imagePath: imagePath,
+ rootfsFile: rootfsFile,
}
return upgradePath, nil
}
@@ -284,50 +284,9 @@ func checkFileExist(path string) (bool, error) {
}
func checkOCIImageDigestMatch(containerRuntime string, imageName string, checkSum string) error {
- var cmdOutput string
- var err error
- switch containerRuntime {
- case "crictl":
- cmdOutput, err = runCommandWithOut("crictl", "inspecti", "--output", "go-template",
- "--template", "{{.status.repoDigests}}", imageName)
- if err != nil {
- return err
- }
- case "docker":
- cmdOutput, err = runCommandWithOut("docker", "inspect", "--format", "{{.RepoDigests}}", imageName)
- if err != nil {
- return err
- }
- case "ctr":
- cmdOutput, err = runCommandWithOut("ctr", "-n", "k8s.io", "images", "ls", "name=="+imageName)
- if err != nil {
- return err
- }
- // after Fields, we get slice like [REF TYPE DIGEST SIZE PLATFORMS LABELS x x x x x x]
- // the digest is the position 8 element
- imageDigest := strings.Split(strings.Fields(cmdOutput)[8], ":")[1]
- if imageDigest != checkSum {
- logrus.Errorln("checkSumFailed ", imageDigest, " mismatch to ", checkSum)
- return fmt.Errorf("checkSumFailed %s mismatch to %s", imageDigest, checkSum)
- }
- return nil
- default:
- logrus.Errorln("containerRuntime ", containerRuntime, " cannot be recognized")
- return fmt.Errorf("containerRuntime %s cannot be recognized", containerRuntime)
- }
- // cmdOutput format is as follows:
- // [imageRepository/imageName:imageTag@sha256:digests]
- // parse the output and get digest
- var imageDigests string
- outArray := strings.Split(cmdOutput, "@")
- if strings.HasPrefix(outArray[len(outArray)-1], "sha256") {
- pasredArray := strings.Split(strings.TrimSuffix(outArray[len(outArray)-1], "]"), ":")
- // 2 is the expected length of the array after dividing "imageName:imageTag@sha256:digests" based on ':'
- rightLen := 2
- if len(pasredArray) == rightLen {
- digestIndex := 1 // 1 is the index of digest data in pasredArray
- imageDigests = pasredArray[digestIndex]
- }
+ imageDigests, err := getOCIImageDigest(containerRuntime, imageName)
+ if err != nil {
+ return err
}
if imageDigests == "" {
logrus.Errorln("error when get ", imageName, " digests")
@@ -367,3 +326,48 @@ func isValidImageName(image string) error {
}
return nil
}
+
+func getOCIImageDigest(containerRuntime string, imageName string) (string, error) {
+ var cmdOutput string
+ var err error
+ var imageDigests string
+ switch containerRuntime {
+ case "crictl":
+ cmdOutput, err = runCommandWithOut("crictl", "inspecti", "--output", "go-template",
+ "--template", "{{.status.repoDigests}}", imageName)
+ if err != nil {
+ return "", err
+ }
+ case "docker":
+ cmdOutput, err = runCommandWithOut("docker", "inspect", "--format", "{{.RepoDigests}}", imageName)
+ if err != nil {
+ return "", err
+ }
+ case "ctr":
+ cmdOutput, err = runCommandWithOut("ctr", "-n", "k8s.io", "images", "ls", "name=="+imageName)
+ if err != nil {
+ return "", err
+ }
+ // after Fields, we get slice like [REF TYPE DIGEST SIZE PLATFORMS LABELS x x x x x x]
+ // the digest is the position 8 element
+ imageDigest := strings.Split(strings.Fields(cmdOutput)[8], ":")[1]
+ return imageDigest, nil
+ default:
+ logrus.Errorln("containerRuntime ", containerRuntime, " cannot be recognized")
+ return "", fmt.Errorf("containerRuntime %s cannot be recognized", containerRuntime)
+ }
+ // cmdOutput format is as follows:
+ // [imageRepository/imageName:imageTag@sha256:digests]
+ // parse the output and get digest
+ outArray := strings.Split(cmdOutput, "@")
+ if strings.HasPrefix(outArray[len(outArray)-1], "sha256") {
+ pasredArray := strings.Split(strings.TrimSuffix(outArray[len(outArray)-1], "]"), ":")
+ // 2 is the expected length of the array after dividing "imageName:imageTag@sha256:digests" based on ':'
+ rightLen := 2
+ if len(pasredArray) == rightLen {
+ digestIndex := 1 // 1 is the index of digest data in pasredArray
+ imageDigests = pasredArray[digestIndex]
+ }
+ }
+ return imageDigests, nil
+}
--
2.39.0