KubeOS/0019-KubeOS-add-unit-tests.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

1070 lines
34 KiB
Diff

From f35fee982d0afefef4643993dc0f64c7aa243a22 Mon Sep 17 00:00:00 2001
From: Yuhang Wei <weiyuhang3@huawei.com>
Date: Wed, 16 Aug 2023 14:27:36 +0800
Subject: [PATCH 2/5] KubeOS:add unit tests
modify proxy ut to avoid panic bug
add config, disk_image, operator, proxy, server and utils unit tests
Signed-off-by: Yuhang Wei <weiyuhang3@huawei.com>
---
cmd/agent/server/config_test.go | 41 ++-
cmd/agent/server/disk_image_test.go | 56 +++-
cmd/agent/server/server_test.go | 1 -
cmd/agent/server/utils_test.go | 112 +++++++
.../controllers/os_controller_test.go | 232 +++++++++++++-
cmd/proxy/controllers/os_controller_test.go | 295 +++++++++++++++---
6 files changed, 685 insertions(+), 52 deletions(-)
diff --git a/cmd/agent/server/config_test.go b/cmd/agent/server/config_test.go
index 08daf99..29bb926 100644
--- a/cmd/agent/server/config_test.go
+++ b/cmd/agent/server/config_test.go
@@ -75,6 +75,16 @@ func TestKernelSysctl_SetConfig(t *testing.T) {
},
}},
},
+ {
+ name: "nil key",
+ k: KernelSysctl{},
+ args: args{config: &agent.SysConfig{
+ Contents: map[string]*agent.KeyInfo{
+ "": {Value: "1"},
+ },
+ }},
+ wantErr: true,
+ },
}
tmpDir := t.TempDir()
patchGetProcPath := gomonkey.ApplyFuncReturn(getDefaultProcPath, tmpDir+"/")
@@ -320,8 +330,7 @@ menuentry 'B' --class KubeOS --class gnu-linux --class gnu --class os --unrestri
defer patchGetConfigPartition.Reset()
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
- g := GrubCmdline{}
- if err := g.SetConfig(tt.args.config); (err != nil) != tt.wantErr {
+ if err := tt.g.SetConfig(tt.args.config); (err != nil) != tt.wantErr {
t.Errorf("GrubCmdline.SetConfig() error = %v, wantErr %v", err, tt.wantErr)
}
contents, err := os.ReadFile(grubCfgPath)
@@ -513,3 +522,31 @@ func Test_ConfigFactoryTemplate(t *testing.T) {
})
}
}
+
+func Test_convertNewConfigsToString(t *testing.T) {
+ type args struct {
+ newConfigs []string
+ }
+ tests := []struct {
+ name string
+ args args
+ want string
+ wantErr bool
+ }{
+ {name: "error", args: args{newConfigs: []string{"a"}}, want: "", wantErr: true},
+ }
+ patchFprintf := gomonkey.ApplyFuncReturn(fmt.Fprintf, 0, fmt.Errorf("error"))
+ defer patchFprintf.Reset()
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ got, err := convertNewConfigsToString(tt.args.newConfigs)
+ if (err != nil) != tt.wantErr {
+ t.Errorf("convertNewConfigsToString() error = %v, wantErr %v", err, tt.wantErr)
+ return
+ }
+ if got != tt.want {
+ t.Errorf("convertNewConfigsToString() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
diff --git a/cmd/agent/server/disk_image_test.go b/cmd/agent/server/disk_image_test.go
index 265b323..f970bd7 100644
--- a/cmd/agent/server/disk_image_test.go
+++ b/cmd/agent/server/disk_image_test.go
@@ -23,6 +23,7 @@ import (
"encoding/pem"
"fmt"
"io"
+ "io/fs"
"math/big"
"net/http"
"os"
@@ -159,6 +160,7 @@ func Test_checkSumMatch(t *testing.T) {
wantErr: false,
},
{name: "error", args: args{filePath: tmpFileForCheckSum, checkSum: "aaa"}, wantErr: true},
+ {name: "unfound error", args: args{filePath: "", checkSum: "aaa"}, wantErr: true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
@@ -271,6 +273,7 @@ func Test_loadCaCerts(t *testing.T) {
},
wantErr: false,
},
+ {name: "no cert", args: args{caCert: ""}, wantErr: true},
}
patchGetCertPath := gomonkey.ApplyFuncReturn(getCertPath, "")
defer patchGetCertPath.Reset()
@@ -339,12 +342,22 @@ func Test_certExist(t *testing.T) {
}{
{name: "fileEmpty", args: args{certFile: ""}, wantErr: true},
{name: "fileNotExist", args: args{certFile: "bb.txt"}, wantErr: true},
+ {name: "unknow error", args: args{certFile: "cc.txt"}, wantErr: true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
+ var patchStat *gomonkey.Patches
+ if tt.name == "unknow error" {
+ patchStat = gomonkey.ApplyFunc(os.Stat, func(name string) (fs.FileInfo, error) {
+ return fs.FileInfo(nil), fmt.Errorf("error")
+ })
+ }
if err := certExist(tt.args.certFile); (err != nil) != tt.wantErr {
t.Errorf("certExist() error = %v, wantErr %v", err, tt.wantErr)
}
+ if tt.name == "unknow error" {
+ patchStat.Reset()
+ }
})
}
defer os.RemoveAll("/etc/KubeOS/")
@@ -396,8 +409,17 @@ func Test_diskHandler_getRootfsArchive(t *testing.T) {
want: "/persist/update.img",
wantErr: false,
},
+ {
+ name: "error", d: diskHandler{},
+ args: args{req: &pb.UpdateRequest{ImageUrl: "http://www.openeuler.org/zh/"}, neededPath: preparePath{}},
+ want: "",
+ wantErr: true,
+ },
}
- patchDownload := gomonkey.ApplyFuncReturn(download, "/persist/update.img", nil)
+ patchDownload := gomonkey.ApplyFuncSeq(download, []gomonkey.OutputCell{
+ {Values: gomonkey.Params{"/persist/update.img", nil}},
+ {Values: gomonkey.Params{"", fmt.Errorf("error")}},
+ })
defer patchDownload.Reset()
patchCheckSumMatch := gomonkey.ApplyFuncReturn(checkSumMatch, nil)
defer patchCheckSumMatch.Reset()
@@ -415,3 +437,35 @@ func Test_diskHandler_getRootfsArchive(t *testing.T) {
})
}
}
+
+func Test_diskHandler_downloadImage(t *testing.T) {
+ type args struct {
+ req *pb.UpdateRequest
+ }
+ tests := []struct {
+ name string
+ d diskHandler
+ args args
+ want string
+ wantErr bool
+ }{
+ {name: "normal", d: diskHandler{}, args: args{req: &pb.UpdateRequest{ImageUrl: "http://www.openeuler.org/zh/"}}, want: "/persist/update.img", wantErr: false},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ d := diskHandler{}
+ patchGetRootfsArchive := gomonkey.ApplyPrivateMethod(reflect.TypeOf(d), "getRootfsArchive", func(_ *diskHandler, _ *pb.UpdateRequest, _ preparePath) (string, error) {
+ return "/persist/update.img", nil
+ })
+ got, err := d.downloadImage(tt.args.req)
+ if (err != nil) != tt.wantErr {
+ t.Errorf("diskHandler.downloadImage() error = %v, wantErr %v", err, tt.wantErr)
+ return
+ }
+ if got != tt.want {
+ t.Errorf("diskHandler.downloadImage() = %v, want %v", got, tt.want)
+ }
+ patchGetRootfsArchive.Reset()
+ })
+ }
+}
diff --git a/cmd/agent/server/server_test.go b/cmd/agent/server/server_test.go
index 74e2ead..15b6f5e 100644
--- a/cmd/agent/server/server_test.go
+++ b/cmd/agent/server/server_test.go
@@ -311,7 +311,6 @@ func TestServer_Configure(t *testing.T) {
want *pb.ConfigureResponse
wantErr bool
}{
- // TODO: Add test cases.
{
name: "nil",
fields: fields{UnimplementedOSServer: pb.UnimplementedOSServer{}, disableReboot: true},
diff --git a/cmd/agent/server/utils_test.go b/cmd/agent/server/utils_test.go
index 0796bce..da53c0e 100644
--- a/cmd/agent/server/utils_test.go
+++ b/cmd/agent/server/utils_test.go
@@ -15,6 +15,7 @@ package server
import (
"archive/tar"
+ "fmt"
"os"
"os/exec"
"reflect"
@@ -58,12 +59,14 @@ func Test_install(t *testing.T) {
}{
{name: "normal uefi", args: args{imagePath: "aa.txt", side: "/dev/sda3", next: "A"}, wantErr: false},
{name: "normal legacy", args: args{imagePath: "aa.txt", side: "/dev/sda3", next: "A"}, wantErr: false},
+ {name: "get boot mode error", args: args{imagePath: "aa.txt", side: "/dev/sda3", next: "A"}, wantErr: true},
}
patchRunCommand := gomonkey.ApplyFuncReturn(runCommand, nil)
defer patchRunCommand.Reset()
patchGetBootMode := gomonkey.ApplyFuncSeq(getBootMode, []gomonkey.OutputCell{
{Values: gomonkey.Params{"uefi", nil}},
{Values: gomonkey.Params{"legacy", nil}},
+ {Values: gomonkey.Params{"", fmt.Errorf("error")}},
})
defer patchGetBootMode.Reset()
for _, tt := range tests {
@@ -89,10 +92,12 @@ func Test_getNextPart(t *testing.T) {
}{
{name: "switch to sda3", args: args{partA: "/dev/sda2", partB: "/dev/sda3"}, want: "/dev/sda3", want1: "B", wantErr: false},
{name: "switch to sda2", args: args{partA: "/dev/sda2", partB: "/dev/sda3"}, want: "/dev/sda2", want1: "A", wantErr: false},
+ {name: "error", args: args{partA: "/dev/sda2", partB: "/dev/sda3"}, want: "", want1: "", wantErr: true},
}
patchExecCommand := gomonkey.ApplyMethodSeq(&exec.Cmd{}, "CombinedOutput", []gomonkey.OutputCell{
{Values: gomonkey.Params{[]byte("/"), nil}},
{Values: gomonkey.Params{[]byte(""), nil}},
+ {Values: gomonkey.Params{[]byte(""), fmt.Errorf("error")}},
})
defer patchExecCommand.Reset()
for _, tt := range tests {
@@ -242,10 +247,16 @@ func Test_getBootMode(t *testing.T) {
want: "legacy",
wantErr: false,
},
+ {
+ name: "error",
+ want: "",
+ wantErr: true,
+ },
}
patchOSStat := gomonkey.ApplyFuncSeq(os.Stat, []gomonkey.OutputCell{
{Values: gomonkey.Params{nil, nil}},
{Values: gomonkey.Params{nil, os.ErrNotExist}},
+ {Values: gomonkey.Params{nil, fmt.Errorf("fake error")}},
})
defer patchOSStat.Reset()
for _, tt := range tests {
@@ -326,3 +337,104 @@ func Test_checkOCIImageDigestMatch(t *testing.T) {
})
}
}
+
+func Test_runCommandWithOut(t *testing.T) {
+ type args struct {
+ name string
+ args []string
+ }
+ tests := []struct {
+ name string
+ args args
+ want string
+ wantErr bool
+ }{
+ {name: "error", args: args{name: "/mmm", args: []string{"", ""}}, wantErr: true},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ got, err := runCommandWithOut(tt.args.name, tt.args.args...)
+ if (err != nil) != tt.wantErr {
+ t.Errorf("runCommandWithOut() error = %v, wantErr %v", err, tt.wantErr)
+ return
+ }
+ if got != tt.want {
+ t.Errorf("runCommandWithOut() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
+
+func Test_getRootfsDisks(t *testing.T) {
+ tests := []struct {
+ name string
+ want string
+ want1 string
+ wantErr bool
+ }{
+ {name: "error", want: "", want1: "", wantErr: true},
+ }
+ patchRunCommandWithOut := gomonkey.ApplyFuncSeq(runCommandWithOut, []gomonkey.OutputCell{
+ {Values: gomonkey.Params{"", fmt.Errorf("fake error")}},
+ })
+ defer patchRunCommandWithOut.Reset()
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ got, got1, err := getRootfsDisks()
+ if (err != nil) != tt.wantErr {
+ t.Errorf("getRootfsDisks() error = %v, wantErr %v", err, tt.wantErr)
+ return
+ }
+ if got != tt.want {
+ t.Errorf("getRootfsDisks() got = %v, want %v", got, tt.want)
+ }
+ if got1 != tt.want1 {
+ t.Errorf("getRootfsDisks() got1 = %v, want %v", got1, tt.want1)
+ }
+ })
+ }
+}
+
+func Test_checkDiskSize(t *testing.T) {
+ type args struct {
+ needGBSize int
+ path string
+ }
+ tests := []struct {
+ name string
+ args args
+ wantErr bool
+ }{
+ {name: "zero GB need", args: args{needGBSize: 0, path: "/dev/sda"}, wantErr: false},
+ {name: "disk not enough", args: args{needGBSize: 100000, path: "/dev/sda"}, wantErr: true},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ if err := checkDiskSize(tt.args.needGBSize, tt.args.path); (err != nil) != tt.wantErr {
+ t.Errorf("checkDiskSize() error = %v, wantErr %v", err, tt.wantErr)
+ }
+ })
+ }
+}
+
+func Test_deleteFile(t *testing.T) {
+ type args struct {
+ path string
+ }
+ tests := []struct {
+ name string
+ args args
+ wantErr bool
+ }{
+ {name: "error", args: args{path: "/mmm"}, wantErr: true},
+ }
+ patchStat := gomonkey.ApplyFuncReturn(os.Stat, nil, fmt.Errorf("fake error"))
+ defer patchStat.Reset()
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ if err := deleteFile(tt.args.path); (err != nil) != tt.wantErr {
+ t.Errorf("deleteFile() error = %v, wantErr %v", err, tt.wantErr)
+ }
+ })
+ }
+}
diff --git a/cmd/operator/controllers/os_controller_test.go b/cmd/operator/controllers/os_controller_test.go
index e59ce7e..6cc2760 100644
--- a/cmd/operator/controllers/os_controller_test.go
+++ b/cmd/operator/controllers/os_controller_test.go
@@ -14,17 +14,22 @@ package controllers
import (
"context"
+ "fmt"
+ "reflect"
"testing"
"time"
+ "github.com/agiledragon/gomonkey/v2"
"github.com/google/uuid"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
+ corev1 "k8s.io/api/core/v1"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/types"
-
upgradev1 "openeuler.org/KubeOS/api/v1alpha1"
+ "openeuler.org/KubeOS/pkg/common"
"openeuler.org/KubeOS/pkg/values"
)
@@ -775,3 +780,228 @@ func Test_deepCopySpecConfigs(t *testing.T) {
})
}
}
+
+func Test_getConfigOSInstances(t *testing.T) {
+ type args struct {
+ ctx context.Context
+ r common.ReadStatusWriter
+ }
+ tests := []struct {
+ name string
+ args args
+ want []upgradev1.OSInstance
+ wantErr bool
+ }{
+ {
+ name: "list error",
+ args: args{
+ ctx: context.Background(),
+ r: &OSReconciler{},
+ },
+ want: nil,
+ wantErr: true,
+ },
+ }
+ patchList := gomonkey.ApplyMethodSeq(&OSReconciler{}, "List", []gomonkey.OutputCell{
+ {Values: gomonkey.Params{fmt.Errorf("list error")}},
+ })
+ defer patchList.Reset()
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ got, err := getConfigOSInstances(tt.args.ctx, tt.args.r)
+ if (err != nil) != tt.wantErr {
+ t.Errorf("getConfigOSInstances() error = %v, wantErr %v", err, tt.wantErr)
+ return
+ }
+ if !reflect.DeepEqual(got, tt.want) {
+ t.Errorf("getConfigOSInstances() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
+
+func Test_checkUpgrading(t *testing.T) {
+ type args struct {
+ ctx context.Context
+ r common.ReadStatusWriter
+ maxUnavailable int
+ }
+ tests := []struct {
+ name string
+ args args
+ want int
+ wantErr bool
+ }{
+ {
+ name: "label error",
+ args: args{
+ ctx: context.Background(),
+ r: &OSReconciler{},
+ },
+ want: 0,
+ wantErr: true,
+ },
+ }
+ patchNewRequirement := gomonkey.ApplyFuncSeq(labels.NewRequirement, []gomonkey.OutputCell{
+ {Values: gomonkey.Params{nil, fmt.Errorf("label error")}},
+ {Values: gomonkey.Params{nil, nil}},
+ })
+ defer patchNewRequirement.Reset()
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ got, err := checkUpgrading(tt.args.ctx, tt.args.r, tt.args.maxUnavailable)
+ if (err != nil) != tt.wantErr {
+ t.Errorf("checkUpgrading() error = %v, wantErr %v", err, tt.wantErr)
+ return
+ }
+ if got != tt.want {
+ t.Errorf("checkUpgrading() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
+
+func Test_getIdleOSInstances(t *testing.T) {
+ type args struct {
+ ctx context.Context
+ r common.ReadStatusWriter
+ limit int
+ }
+ tests := []struct {
+ name string
+ args args
+ want []upgradev1.OSInstance
+ wantErr bool
+ }{
+ {
+ name: "list error",
+ args: args{
+ ctx: context.Background(),
+ r: &OSReconciler{},
+ limit: 1,
+ },
+ want: nil,
+ wantErr: true,
+ },
+ }
+ patchList := gomonkey.ApplyMethodSeq(&OSReconciler{}, "List", []gomonkey.OutputCell{
+ {Values: gomonkey.Params{fmt.Errorf("list error")}},
+ })
+ defer patchList.Reset()
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ got, err := getIdleOSInstances(tt.args.ctx, tt.args.r, tt.args.limit)
+ if (err != nil) != tt.wantErr {
+ t.Errorf("getIdleOSInstances() error = %v, wantErr %v", err, tt.wantErr)
+ return
+ }
+ if !reflect.DeepEqual(got, tt.want) {
+ t.Errorf("getIdleOSInstances() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
+
+func Test_getNodes(t *testing.T) {
+ type args struct {
+ ctx context.Context
+ r common.ReadStatusWriter
+ limit int
+ reqs []labels.Requirement
+ }
+ tests := []struct {
+ name string
+ args args
+ want []corev1.Node
+ wantErr bool
+ }{
+ {
+ name: "list error",
+ args: args{
+ ctx: context.Background(),
+ r: &OSReconciler{},
+ limit: 1,
+ },
+ want: nil,
+ wantErr: true,
+ },
+ }
+ patchList := gomonkey.ApplyMethodSeq(&OSReconciler{}, "List", []gomonkey.OutputCell{
+ {Values: gomonkey.Params{fmt.Errorf("list error")}},
+ })
+ defer patchList.Reset()
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ got, err := getNodes(tt.args.ctx, tt.args.r, tt.args.limit, tt.args.reqs...)
+ if (err != nil) != tt.wantErr {
+ t.Errorf("getNodes() error = %v, wantErr %v", err, tt.wantErr)
+ return
+ }
+ if !reflect.DeepEqual(got, tt.want) {
+ t.Errorf("getNodes() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
+
+func Test_getAndUpdateOS(t *testing.T) {
+ type args struct {
+ ctx context.Context
+ r common.ReadStatusWriter
+ name types.NamespacedName
+ }
+ tests := []struct {
+ name string
+ args args
+ wantOs upgradev1.OS
+ wantNodeNum int
+ wantErr bool
+ }{
+ {
+ name: "label error",
+ args: args{
+ ctx: context.Background(),
+ r: &OSReconciler{},
+ name: types.NamespacedName{Namespace: "test_ns", Name: "test"},
+ },
+ wantOs: upgradev1.OS{},
+ wantNodeNum: 0,
+ wantErr: true,
+ },
+ {
+ name: "get nodes error",
+ args: args{
+ ctx: context.Background(),
+ r: &OSReconciler{},
+ name: types.NamespacedName{Namespace: "test_ns", Name: "test"},
+ },
+ wantOs: upgradev1.OS{},
+ wantNodeNum: 0,
+ wantErr: true,
+ },
+ }
+ patchGet := gomonkey.ApplyMethodReturn(&OSReconciler{}, "Get", nil)
+ defer patchGet.Reset()
+ patchNewRequirement := gomonkey.ApplyFuncSeq(labels.NewRequirement, []gomonkey.OutputCell{
+ {Values: gomonkey.Params{nil, fmt.Errorf("label error")}},
+ {Values: gomonkey.Params{&labels.Requirement{}, nil}},
+ })
+ defer patchNewRequirement.Reset()
+ patchGetNodes := gomonkey.ApplyFuncReturn(getNodes, nil, fmt.Errorf("get nodes error"))
+ defer patchGetNodes.Reset()
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ gotOs, gotNodeNum, err := getAndUpdateOS(tt.args.ctx, tt.args.r, tt.args.name)
+ if (err != nil) != tt.wantErr {
+ t.Errorf("getAndUpdateOS() error = %v, wantErr %v", err, tt.wantErr)
+ return
+ }
+ if !reflect.DeepEqual(gotOs, tt.wantOs) {
+ t.Errorf("getAndUpdateOS() gotOs = %v, want %v", gotOs, tt.wantOs)
+ }
+ if gotNodeNum != tt.wantNodeNum {
+ t.Errorf("getAndUpdateOS() gotNodeNum = %v, want %v", gotNodeNum, tt.wantNodeNum)
+ }
+ })
+ }
+}
diff --git a/cmd/proxy/controllers/os_controller_test.go b/cmd/proxy/controllers/os_controller_test.go
index 27cb0cd..14b6b66 100644
--- a/cmd/proxy/controllers/os_controller_test.go
+++ b/cmd/proxy/controllers/os_controller_test.go
@@ -16,25 +16,27 @@ import (
"context"
"fmt"
"reflect"
+ "testing"
"time"
"github.com/agiledragon/gomonkey/v2"
"github.com/google/uuid"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
+ corev1 "k8s.io/api/core/v1"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
-
+ "k8s.io/kubectl/pkg/drain"
upgradev1 "openeuler.org/KubeOS/api/v1alpha1"
"openeuler.org/KubeOS/pkg/agentclient"
+ "openeuler.org/KubeOS/pkg/common"
"openeuler.org/KubeOS/pkg/values"
)
var _ = Describe("OsController", func() {
const (
- OSName = "test-os"
-
+ OSName = "test-os"
timeout = time.Second * 20
interval = time.Millisecond * 500
)
@@ -67,6 +69,24 @@ var _ = Describe("OsController", func() {
testNamespace = existingNamespace.Name
})
+ AfterEach(func() {
+ // delete all OS CRs
+ osList := &upgradev1.OSList{}
+ err := k8sClient.List(context.Background(), osList)
+ Expect(err).ToNot(HaveOccurred())
+ for _, os := range osList.Items {
+ k8sClient.Delete(context.Background(), &os)
+ }
+ osList = &upgradev1.OSList{}
+ Eventually(func() bool {
+ err = k8sClient.List(context.Background(), osList)
+ if err != nil || len(osList.Items) != 0 {
+ return false
+ }
+ return true
+ }, timeout, interval).Should(BeTrue())
+ })
+
Context("When we want to rollback", func() {
It("Should be able to rollback to previous version", func() {
ctx := context.Background()
@@ -182,6 +202,13 @@ var _ = Describe("OsController", func() {
Expect(k8sClient.Status().Update(ctx, existingNode)).Should(Succeed())
By("Changing the OS Spec config to trigger reconcile")
+ createdOSIns = &upgradev1.OSInstance{}
+ Eventually(func() bool {
+ err := k8sClient.Get(ctx, osInsCRLookupKey, createdOSIns)
+ return err == nil
+ }, timeout, interval).Should(BeTrue())
+ createdOSIns.Spec.SysConfigs = upgradev1.SysConfigs{Version: "v1", Configs: []upgradev1.SysConfig{}}
+ Expect(k8sClient.Update(ctx, createdOSIns)).Should(Succeed())
createdOS = &upgradev1.OS{}
Eventually(func() bool {
err := k8sClient.Get(ctx, osCRLookupKey, createdOS)
@@ -190,7 +217,7 @@ var _ = Describe("OsController", func() {
createdOS.Spec.SysConfigs = upgradev1.SysConfigs{Version: "v1", Configs: []upgradev1.SysConfig{}}
Expect(k8sClient.Update(ctx, createdOS)).Should(Succeed())
- time.Sleep(2 * time.Second) // sleep a while to make sure Reconcile finished
+ time.Sleep(1 * time.Second) // sleep a while to make sure Reconcile finished
createdOSIns = &upgradev1.OSInstance{}
Eventually(func() bool {
err := k8sClient.Get(ctx, osInsCRLookupKey, createdOSIns)
@@ -335,7 +362,7 @@ var _ = Describe("OsController", func() {
Expect(createdOS.Spec.OSVersion).Should(Equal("KubeOS v1"))
By("Checking the OSInstance status config version")
- time.Sleep(2 * time.Second) // sleep a while to make sure Reconcile finished
+ time.Sleep(1 * time.Second) // sleep a while to make sure Reconcile finished
osInsCRLookupKey = types.NamespacedName{Name: node1Name, Namespace: testNamespace}
createdOSIns = &upgradev1.OSInstance{}
Eventually(func() bool {
@@ -451,7 +478,7 @@ var _ = Describe("OsController", func() {
OSVersion: "KubeOS v2",
FlagSafe: true,
MTLS: false,
- EvictPodForce: true,
+ EvictPodForce: false,
SysConfigs: upgradev1.SysConfigs{Configs: []upgradev1.SysConfig{}},
UpgradeConfigs: upgradev1.SysConfigs{
Version: "v2",
@@ -478,7 +505,7 @@ var _ = Describe("OsController", func() {
Expect(createdOS.Spec.OSVersion).Should(Equal("KubeOS v2"))
By("Checking the OSInstance status config version")
- time.Sleep(2 * time.Second) // sleep a while to make sure Reconcile finished
+ time.Sleep(1 * time.Second) // sleep a while to make sure Reconcile finished
osInsCRLookupKey = types.NamespacedName{Name: node1Name, Namespace: testNamespace}
createdOSIns = &upgradev1.OSInstance{}
Eventually(func() bool {
@@ -566,7 +593,7 @@ var _ = Describe("OsController", func() {
Expect(createdOS.Spec.OSVersion).Should(Equal("KubeOS v1"))
By("Checking the existence of new OSInstance")
- time.Sleep(2 * time.Second) // sleep a while to make sure Reconcile finished
+ time.Sleep(1 * time.Second) // sleep a while to make sure Reconcile finished
osInsCRLookupKey := types.NamespacedName{Name: node1Name, Namespace: testNamespace}
createdOSIns := &upgradev1.OSInstance{}
Eventually(func() bool {
@@ -644,7 +671,7 @@ var _ = Describe("OsController", func() {
},
UpgradeConfigs: upgradev1.SysConfigs{Configs: []upgradev1.SysConfig{}},
},
- Status: upgradev1.OSInstanceStatus{},
+ Status: upgradev1.OSInstanceStatus{SysConfigs: upgradev1.SysConfigs{Version: "v1"}},
}
Expect(k8sClient.Create(ctx, OSIns)).Should(Succeed())
@@ -711,7 +738,7 @@ var _ = Describe("OsController", func() {
Expect(createdOS.Spec.OSVersion).Should(Equal("KubeOS v1"))
By("Checking the OSInstance status config version failed to be updated")
- time.Sleep(2 * time.Second) // sleep a while to make sure Reconcile finished
+ time.Sleep(1 * time.Second) // sleep a while to make sure Reconcile finished
osInsCRLookupKey = types.NamespacedName{Name: node1Name, Namespace: testNamespace}
createdOSIns = &upgradev1.OSInstance{}
Eventually(func() bool {
@@ -721,10 +748,18 @@ var _ = Describe("OsController", func() {
Expect(createdOSIns.Status.SysConfigs.Version).Should(Equal("v1"))
Expect(createdOSIns.Spec.SysConfigs.Version).Should(Equal("v2"))
- By("Changing the OS Spec config version to previous one")
+ By("Changing the OS and OSi Spec config version to previous one")
OS.Spec.SysConfigs = upgradev1.SysConfigs{Version: "v1", Configs: []upgradev1.SysConfig{}}
Expect(k8sClient.Update(ctx, OS)).Should(Succeed())
- time.Sleep(1 * time.Second) // sleep a while to make sure Reconcile finished
+ createdOSIns = &upgradev1.OSInstance{}
+ Eventually(func() bool {
+ err := k8sClient.Get(ctx, osInsCRLookupKey, createdOSIns)
+ return err == nil
+ }, timeout, interval).Should(BeTrue())
+ createdOSIns.Spec.SysConfigs = upgradev1.SysConfigs{Version: "v1", Configs: []upgradev1.SysConfig{}}
+ Expect(k8sClient.Update(ctx, createdOSIns)).Should(Succeed())
+
+ time.Sleep(2 * time.Second) // sleep a while to make sure Reconcile finished
createdOSIns = &upgradev1.OSInstance{}
Eventually(func() bool {
err := k8sClient.Get(ctx, osInsCRLookupKey, createdOSIns)
@@ -799,7 +834,6 @@ var _ = Describe("OsController", func() {
},
},
},
- Status: upgradev1.OSInstanceStatus{},
}
Expect(k8sClient.Create(ctx, OSIns)).Should(Succeed())
@@ -870,7 +904,7 @@ var _ = Describe("OsController", func() {
Expect(createdOS.Spec.OSVersion).Should(Equal("KubeOS v2"))
By("Checking the OSInstance status config version failed to be updated")
- time.Sleep(2 * time.Second) // sleep a while to make sure Reconcile finished
+ time.Sleep(1 * time.Second) // sleep a while to make sure Reconcile finished
osInsCRLookupKey = types.NamespacedName{Name: node1Name, Namespace: testNamespace}
createdOSIns = &upgradev1.OSInstance{}
Eventually(func() bool {
@@ -889,6 +923,16 @@ var _ = Describe("OsController", func() {
err := k8sClient.Get(ctx, osInsCRLookupKey, createdOSIns)
return err == nil
}, timeout, interval).Should(BeTrue())
+
+ osInsCRLookupKey = types.NamespacedName{Name: node1Name, Namespace: testNamespace}
+ createdOSIns = &upgradev1.OSInstance{}
+ Eventually(func() bool {
+ err := k8sClient.Get(ctx, osInsCRLookupKey, createdOSIns)
+ return err == nil
+ }, timeout, interval).Should(BeTrue())
+ createdOSIns.Spec.UpgradeConfigs = upgradev1.SysConfigs{Version: "v1", Configs: []upgradev1.SysConfig{}}
+ Expect(k8sClient.Update(ctx, createdOSIns)).Should(Succeed())
+
// NodeStatus changes to idle then operator can reassign configs to this node
Expect(createdOSIns.Spec.NodeStatus).Should(Equal(values.NodeStatusIdle.String()))
existingNode = &v1.Node{}
@@ -906,37 +950,7 @@ var _ = Describe("OsController", func() {
It("Should be able to rollback to previous config version to jump out of error state", func() {
ctx := context.Background()
- By("Creating a worker node")
node1Name = "test-node-" + uuid.New().String()
- node1 := &v1.Node{
- ObjectMeta: metav1.ObjectMeta{
- Name: node1Name,
- Namespace: testNamespace,
- Labels: map[string]string{
- "beta.kubernetes.io/os": "linux",
- values.LabelUpgrading: "",
- },
- },
- TypeMeta: metav1.TypeMeta{
- APIVersion: "v1",
- Kind: "Node",
- },
- Status: v1.NodeStatus{
- NodeInfo: v1.NodeSystemInfo{
- OSImage: "KubeOS v2",
- },
- },
- }
- err := k8sClient.Create(ctx, node1)
- Expect(err).ToNot(HaveOccurred())
- existingNode := &v1.Node{}
- Eventually(func() bool {
- err := k8sClient.Get(context.Background(),
- types.NamespacedName{Name: node1Name, Namespace: testNamespace}, existingNode)
- return err == nil
- }, timeout, interval).Should(BeTrue())
- reconciler.hostName = node1Name
-
By("Creating the corresponding OSInstance")
OSIns := &upgradev1.OSInstance{
TypeMeta: metav1.TypeMeta{
@@ -1004,6 +1018,37 @@ var _ = Describe("OsController", func() {
createdOSIns.Status.SysConfigs.Version = "v1"
Expect(k8sClient.Status().Update(ctx, createdOSIns)).Should(Succeed())
Expect(createdOSIns.Status.UpgradeConfigs.Version).Should(Equal("v2"))
+ Expect(createdOSIns.Status.SysConfigs.Version).Should(Equal("v1"))
+
+ By("Creating a worker node")
+ node1 := &v1.Node{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: node1Name,
+ Namespace: testNamespace,
+ Labels: map[string]string{
+ "beta.kubernetes.io/os": "linux",
+ values.LabelUpgrading: "",
+ },
+ },
+ TypeMeta: metav1.TypeMeta{
+ APIVersion: "v1",
+ Kind: "Node",
+ },
+ Status: v1.NodeStatus{
+ NodeInfo: v1.NodeSystemInfo{
+ OSImage: "KubeOS v2",
+ },
+ },
+ }
+ err := k8sClient.Create(ctx, node1)
+ Expect(err).ToNot(HaveOccurred())
+ existingNode := &v1.Node{}
+ Eventually(func() bool {
+ err := k8sClient.Get(context.Background(),
+ types.NamespacedName{Name: node1Name, Namespace: testNamespace}, existingNode)
+ return err == nil
+ }, timeout, interval).Should(BeTrue())
+ reconciler.hostName = node1Name
// stub r.Connection.ConfigureSpec()
patchConfigure := gomonkey.ApplyMethod(reflect.TypeOf(reconciler.Connection),
@@ -1067,7 +1112,6 @@ var _ = Describe("OsController", func() {
By("Checking the OSInstance status config version failed to be updated")
time.Sleep(1 * time.Second) // sleep a while to make sure Reconcile finished
- osInsCRLookupKey = types.NamespacedName{Name: node1Name, Namespace: testNamespace}
createdOSIns = &upgradev1.OSInstance{}
Eventually(func() bool {
err := k8sClient.Get(ctx, osInsCRLookupKey, createdOSIns)
@@ -1077,8 +1121,21 @@ var _ = Describe("OsController", func() {
Expect(createdOSIns.Spec.SysConfigs.Version).Should(Equal("v2"))
By("Changing the OS Spec config version to previous one")
- OS.Spec.SysConfigs = upgradev1.SysConfigs{Version: "v1", Configs: []upgradev1.SysConfig{}}
- Expect(k8sClient.Update(ctx, OS)).Should(Succeed())
+ createdOS = &upgradev1.OS{}
+ Eventually(func() bool {
+ err := k8sClient.Get(ctx, osCRLookupKey, createdOS)
+ return err == nil
+ }, timeout, interval).Should(BeTrue())
+ createdOS.Spec.SysConfigs = upgradev1.SysConfigs{Version: "v1", Configs: []upgradev1.SysConfig{}}
+ Expect(k8sClient.Update(ctx, createdOS)).Should(Succeed())
+ getOSIns := &upgradev1.OSInstance{}
+ Eventually(func() bool {
+ err := k8sClient.Get(ctx, osInsCRLookupKey, getOSIns)
+ return err == nil
+ }, timeout, interval).Should(BeTrue())
+ getOSIns.Spec.SysConfigs = upgradev1.SysConfigs{Version: "v1", Configs: []upgradev1.SysConfig{}}
+ Expect(k8sClient.Update(ctx, getOSIns)).Should(Succeed())
+
time.Sleep(2 * time.Second) // sleep a while to make sure Reconcile finished
createdOSIns = &upgradev1.OSInstance{}
Eventually(func() bool {
@@ -1225,3 +1282,147 @@ var _ = Describe("OsController", func() {
})
})
})
+
+func Test_evictNode(t *testing.T) {
+ type args struct {
+ drainer *drain.Helper
+ node *corev1.Node
+ }
+ tests := []struct {
+ name string
+ args args
+ wantErr bool
+ }{
+ {
+ name: "node unschedulable",
+ args: args{
+ drainer: &drain.Helper{},
+ node: &corev1.Node{Spec: v1.NodeSpec{Unschedulable: true}},
+ },
+ wantErr: false,
+ },
+ {
+ name: "runCordonError1",
+ args: args{
+ drainer: &drain.Helper{},
+ node: &corev1.Node{},
+ },
+ wantErr: true,
+ },
+ {
+ name: "runNodeDrainError",
+ args: args{
+ drainer: &drain.Helper{},
+ node: &corev1.Node{},
+ },
+ wantErr: true,
+ },
+ {
+ name: "runUncordonError2",
+ args: args{
+ drainer: &drain.Helper{},
+ node: &corev1.Node{},
+ },
+ wantErr: true,
+ },
+ }
+ patchRunCordon := gomonkey.ApplyFuncSeq(drain.RunCordonOrUncordon, []gomonkey.OutputCell{
+ {Values: gomonkey.Params{fmt.Errorf("cordon error")}},
+ {Values: gomonkey.Params{nil}},
+ {Values: gomonkey.Params{fmt.Errorf("cordon error")}},
+ {Values: gomonkey.Params{nil}},
+ {Values: gomonkey.Params{nil}},
+ })
+ defer patchRunCordon.Reset()
+ patchRunNodeDrain := gomonkey.ApplyFuncSeq(drain.RunNodeDrain, []gomonkey.OutputCell{
+ {Values: gomonkey.Params{fmt.Errorf("node drain error")}},
+ {Values: gomonkey.Params{fmt.Errorf("node drain error")}},
+ })
+ defer patchRunNodeDrain.Reset()
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ if err := evictNode(tt.args.drainer, tt.args.node); (err != nil) != tt.wantErr {
+ t.Errorf("evictNode() error = %v, wantErr %v", err, tt.wantErr)
+ }
+ })
+ }
+}
+
+func Test_updateConfigStatus(t *testing.T) {
+ type args struct {
+ ctx context.Context
+ r common.ReadStatusWriter
+ osInstance *upgradev1.OSInstance
+ configType string
+ }
+ tests := []struct {
+ name string
+ args args
+ wantErr bool
+ }{
+ {
+ name: "invalid config type",
+ args: args{
+ ctx: context.Background(),
+ r: &OSReconciler{},
+ osInstance: &upgradev1.OSInstance{},
+ configType: "invalid",
+ },
+ wantErr: true,
+ },
+ }
+ patchUpdate := gomonkey.ApplyMethodReturn(&OSReconciler{}, "Update", fmt.Errorf("update error"))
+ patchStatus := gomonkey.ApplyMethodReturn(&OSReconciler{}, "Status", &OSReconciler{})
+ defer patchUpdate.Reset()
+ defer patchStatus.Reset()
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ if err := updateConfigStatus(tt.args.ctx, tt.args.r, tt.args.osInstance, tt.args.configType); (err != nil) != tt.wantErr {
+ t.Errorf("updateConfigStatus() error = %v, wantErr %v", err, tt.wantErr)
+ }
+ })
+ }
+}
+
+func Test_getOSAndNodeStatus(t *testing.T) {
+ type args struct {
+ ctx context.Context
+ r common.ReadStatusWriter
+ name types.NamespacedName
+ hostName string
+ }
+ tests := []struct {
+ name string
+ args args
+ wantOS upgradev1.OS
+ wantNode corev1.Node
+ }{
+ {
+ name: "get node error",
+ args: args{
+ ctx: context.Background(),
+ r: &OSReconciler{},
+ name: types.NamespacedName{},
+ hostName: "test-node",
+ },
+ wantOS: upgradev1.OS{},
+ wantNode: corev1.Node{},
+ },
+ }
+ patchGet := gomonkey.ApplyMethodSeq(&OSReconciler{}, "Get", []gomonkey.OutputCell{
+ {Values: gomonkey.Params{nil}},
+ {Values: gomonkey.Params{fmt.Errorf("get node error")}},
+ })
+ defer patchGet.Reset()
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ gotOS, gotNode := getOSAndNodeStatus(tt.args.ctx, tt.args.r, tt.args.name, tt.args.hostName)
+ if !reflect.DeepEqual(gotOS, tt.wantOS) {
+ t.Errorf("getOSAndNodeStatus() gotOS = %v, want %v", gotOS, tt.wantOS)
+ }
+ if !reflect.DeepEqual(gotNode, tt.wantNode) {
+ t.Errorf("getOSAndNodeStatus() gotNode = %v, want %v", gotNode, tt.wantNode)
+ }
+ })
+ }
+}
--
2.39.0