diff --git a/cmd/agent/server/config.go b/cmd/agent/server/config.go index 653f9132ba2df48512cc3962bd47eec380fce7bd..20af2673689353be31f4a948b7dcc34e2b717ddd 100644 --- a/cmd/agent/server/config.go +++ b/cmd/agent/server/config.go @@ -57,7 +57,7 @@ func (k KernelSysctl) SetConfig(config *agent.SysConfig) error { } logrus.Infof("Configured kernel.sysctl %s=%s", key, keyInfo.Value) } else { - logrus.Errorf("Failed to parse kernel.sysctl config operation %s value %s", keyInfo.Operation, keyInfo.Value) + logrus.Warnf("Failed to parse kernel.sysctl key: %s value: %s operation: %s", key, keyInfo.Value, keyInfo.Operation) } } return nil @@ -259,7 +259,7 @@ func getAndSetConfigsFromFile(expectConfigs map[string]*agent.KeyInfo, path stri configsWrite = append(configsWrite, line) continue } - configKV := strings.Split(line, "=") + configKV := strings.SplitN(line, "=", kvPair) if len(configKV) != kvPair { logrus.Errorf("could not parse systctl config %s", line) return nil, fmt.Errorf("could not parse systctl config %s", line) @@ -317,7 +317,7 @@ func createConfigPath(configPath string) error { if err != nil { return err } - defer f.Close() + f.Close() return nil } @@ -335,11 +335,16 @@ func getGrubCfgPath() string { // handleDeleteKey deletes key if oldValue==newValue and returns "" string. Otherwier, it returns key=oldValue func handleDeleteKey(config []string, configInfo *agent.KeyInfo) string { - if len(config) == onlyKey { - logrus.Infoln("delete configuration ", config[0]) + key := config[0] + if len(config) == onlyKey && configInfo.Value == "" { + logrus.Infoln("delete configuration ", key) return "" + } else if len(config) == onlyKey && configInfo.Value != "" { + logrus.Warnf("Failed to delete key %s with inconsistent values "+ + "nil and %s", key, configInfo.Value) + return key } - key, oldValue := config[0], config[1] + oldValue := config[1] if oldValue != configInfo.Value { logrus.Warnf("Failed to delete key %s with inconsistent values "+ "%s and %s", key, oldValue, configInfo.Value) @@ -351,22 +356,30 @@ func handleDeleteKey(config []string, configInfo *agent.KeyInfo) string { // handleUpdateKey updates key if key is found, otherwise it returns old config. func handleUpdateKey(config []string, configInfo *agent.KeyInfo, isFound bool) string { - if len(config) == onlyKey { - return config[0] + key := config[0] + if !isFound && len(config) == onlyKey { + return key } - key, oldValue := config[0], config[1] - if !isFound { - return key + "=" + oldValue + if !isFound && len(config) == kvPair { + return key + "=" + config[1] } if configInfo.Operation != "" { logrus.Warnf("Unknown operation %s, updating key %s with value %s by default", configInfo.Operation, key, configInfo.Value) } + if len(config) == onlyKey && configInfo.Value == "" { + return key + } + newValue := strings.TrimSpace(configInfo.Value) + if len(config) == onlyKey && configInfo.Value != "" { + logrus.Infof("update configuration %s=%s", key, newValue) + return key + "=" + newValue + } + oldValue := config[1] if configInfo.Value == "" { logrus.Warnf("Failed to update key %s with null value", key) return key + "=" + oldValue } - newValue := strings.TrimSpace(configInfo.Value) logrus.Infof("update configuration %s=%s", key, newValue) return key + "=" + newValue } @@ -374,17 +387,21 @@ func handleUpdateKey(config []string, configInfo *agent.KeyInfo, isFound bool) s func handleAddKey(m map[string]*agent.KeyInfo, isOnlyKeyValid bool) []string { var configs []string for key, keyInfo := range m { - if key == "" { - logrus.Warnln("Failed to add nil key") + if key == "" || strings.Contains(key, "=") { + logrus.Warnf("Failed to add nil key or key containing =, key: %s", key) continue } if keyInfo.Operation == "delete" { logrus.Warnf("Failed to delete inexistent key %s", key) continue } + if keyInfo.Operation != "" { + logrus.Warnf("Unknown operation %s, adding key %s with value %s by default", + keyInfo.Operation, key, keyInfo.Value) + } k, v := strings.TrimSpace(key), strings.TrimSpace(keyInfo.Value) if keyInfo.Value == "" && isOnlyKeyValid { - logrus.Infoln("add configuration ", k) + logrus.Infoln("add configuration", k) configs = append(configs, k) } else if keyInfo.Value == "" { logrus.Warnf("Failed to add key %s with null value", k) diff --git a/cmd/agent/server/config_test.go b/cmd/agent/server/config_test.go index 2deb15f8527381fbfb264af5df37bb052f04dda9..08daf9955facd48922ef683f9af381c74e4bf433 100644 --- a/cmd/agent/server/config_test.go +++ b/cmd/agent/server/config_test.go @@ -23,7 +23,6 @@ import ( "testing" "github.com/agiledragon/gomonkey/v2" - agent "openeuler.org/KubeOS/cmd/agent/api" ) @@ -105,17 +104,27 @@ func TestKerSysctlPersist_SetConfig(t *testing.T) { wantErr bool }{ {name: "create file", args: args{config: &agent.SysConfig{ConfigPath: persistPath}}, want: []string{comment}, wantErr: false}, + { + name: "nil path", + args: args{ + config: &agent.SysConfig{}, + }, + want: []string{}, + wantErr: false, + }, { name: "add configs", args: args{ config: &agent.SysConfig{ ConfigPath: persistPath, Contents: map[string]*agent.KeyInfo{ - "a": {Value: "1"}, - "b": {Value: "2"}, - "c": {Value: ""}, - "": {Value: "4"}, - "e": {Value: "5"}, + "a": {Value: "1"}, + "b": {Value: "2"}, + "c": {Value: ""}, + "": {Value: "4"}, + "e": {Value: "5", Operation: "xxx"}, + "y=1": {Value: "26"}, + "z": {Value: "x=1"}, }, }, }, @@ -123,6 +132,7 @@ func TestKerSysctlPersist_SetConfig(t *testing.T) { "a=1", "b=2", "e=5", + "z=x=1", }, wantErr: false, }, @@ -134,6 +144,7 @@ func TestKerSysctlPersist_SetConfig(t *testing.T) { Contents: map[string]*agent.KeyInfo{ "a": {Value: "2"}, "b": {Value: ""}, + "z": {Value: "x=2", Operation: "zzz"}, }, }, }, @@ -141,6 +152,7 @@ func TestKerSysctlPersist_SetConfig(t *testing.T) { "a=2", "b=2", "e=5", + "z=x=2", }, wantErr: false, }, @@ -155,6 +167,7 @@ func TestKerSysctlPersist_SetConfig(t *testing.T) { "c": {Value: "3", Operation: "delete"}, "e": {Value: "5", Operation: "remove"}, "f": {Value: "6", Operation: "remove"}, + "z": {Value: "x=2", Operation: "delete"}, }, }, }, @@ -166,6 +179,8 @@ func TestKerSysctlPersist_SetConfig(t *testing.T) { wantErr: false, }, } + patchGetKernelConPath := gomonkey.ApplyFuncReturn(getKernelConPath, persistPath) + defer patchGetKernelConPath.Reset() for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { k := KerSysctlPersist{} @@ -247,10 +262,11 @@ menuentry 'B' --class KubeOS --class gnu-linux --class gnu --class os --unrestri "": {Value: "test"}, // warning, skip, failed to add kv with empty key "selinux": {Value: "1", Operation: "delete"}, // failed to delete inconsistent kv "acpi": {Value: "off", Operation: "delete"}, // failed to delete inexistent kv + "ro": {Value: "1"}, // update key to kv }, }, }, - pattern: `(?m)^\s+linux\s+\/boot\/vmlinuz\s+root=UUID=[0-1]\s+ro\s+rootfstype=ext4\s+nomodeset\s+oops=panic\s+softlockup_panic=0\s+nmi_watchdog=1\s+rd\.shell=0\s+selinux=0\s+crashkernel=256M\s+panic=5\s+(debug\spci=nomis|pci=nomis\sdebug)$`, + pattern: `(?m)^\s+linux\s+\/boot\/vmlinuz\s+root=UUID=[0-1]\s+ro=1\s+rootfstype=ext4\s+nomodeset\s+oops=panic\s+softlockup_panic=0\s+nmi_watchdog=1\s+rd\.shell=0\s+selinux=0\s+crashkernel=256M\s+panic=5\s+(debug\spci=nomis|pci=nomis\sdebug)$`, wantErr: false, }, { @@ -259,14 +275,15 @@ menuentry 'B' --class KubeOS --class gnu-linux --class gnu --class os --unrestri args: args{ config: &agent.SysConfig{ Contents: map[string]*agent.KeyInfo{ - "debug": {Operation: "delete"}, // delete key - "pci": {Value: "nomis", Operation: "delete"}, // delete kv - "debugpat": {Value: "", Operation: "add"}, // passed key, operation is invalid, default to add key - "audit": {Value: "1", Operation: "add"}, // passed kv, key is inexistent, operation is invalid, default to add kv + "debug": {Operation: "delete"}, // delete key + "pci": {Value: "nomis", Operation: "delete"}, // delete kv + "debugpat": {Value: "", Operation: "add"}, // passed key, operation is invalid, default to add key + "audit": {Value: "1", Operation: "add"}, // passed kv, key is inexistent, operation is invalid, default to add kv + "nomodeset": {Value: "1", Operation: "delete"}, // delete key with inconsistent value }, }, }, - pattern: `(?m)^\s+linux\s+\/boot\/vmlinuz\s+root=UUID=[0-1]\s+ro\s+rootfstype=ext4\s+nomodeset\s+oops=panic\s+softlockup_panic=0\s+nmi_watchdog=1\s+rd\.shell=0\s+selinux=0\s+crashkernel=256M\s+panic=5\s+(debugpat\saudit=1|audit=1\sdebugpat)$`, + pattern: `(?m)^\s+linux\s+\/boot\/vmlinuz\s+root=UUID=[0-1]\s+ro=1\s+rootfstype=ext4\s+nomodeset\s+oops=panic\s+softlockup_panic=0\s+nmi_watchdog=1\s+rd\.shell=0\s+selinux=0\s+crashkernel=256M\s+panic=5\s+(debugpat\saudit=1|audit=1\sdebugpat)$`, wantErr: false, }, { @@ -285,6 +302,7 @@ menuentry 'B' --class KubeOS --class gnu-linux --class gnu --class os --unrestri "": {Value: "test"}, // warning, skip, failed to add kv with empty key "selinux": {Value: "1", Operation: "delete"}, "acpi": {Value: "off", Operation: "delete"}, + "ro": {Value: ""}, }, }, }, @@ -467,3 +485,31 @@ func Test_getConfigPartition(t *testing.T) { }) } } + +func Test_ConfigFactoryTemplate(t *testing.T) { + type args struct { + configType string + config *agent.SysConfig + } + tests := []struct { + name string + args args + wantErr bool + }{ + { + name: "error", + args: args{ + configType: "test", + config: &agent.SysConfig{}, + }, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := ConfigFactoryTemplate(tt.args.configType, tt.args.config); (err != nil) != tt.wantErr { + t.Errorf("ConfigFactoryTemplate() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} diff --git a/cmd/agent/server/utils.go b/cmd/agent/server/utils.go index 7134d74f155e4f022f4da2fd056987839fdbdb3a..b42db18fb374234452a163f200e900cc80437a2b 100644 --- a/cmd/agent/server/utils.go +++ b/cmd/agent/server/utils.go @@ -316,7 +316,7 @@ func isCommandAvailable(name string) bool { } func isValidImageName(image string) error { - pattern := `^((?:[\w.-]+)(?::\d+)?\/)*(?:[\w.-]+)(?::[\w_.-]+)?(?:@sha256:[a-fA-F0-9]+)?$` + pattern := `^((?:[\w.-]+)(?::\d+)?\/)*(?:[\w.-]+)((?::[\w_.-]+)?|(?:@sha256:[a-fA-F0-9]+)?)$` regEx, err := regexp.Compile(pattern) if err != nil { return err diff --git a/cmd/agent/server/utils_test.go b/cmd/agent/server/utils_test.go index 89b2c3bf0a94e750f0b1ea35ff6f015f83547a14..0796bce4eee40bbfcff617869bcc083c3156154a 100644 --- a/cmd/agent/server/utils_test.go +++ b/cmd/agent/server/utils_test.go @@ -56,10 +56,16 @@ func Test_install(t *testing.T) { args args wantErr bool }{ - {name: "normal", args: args{imagePath: "aa.txt", side: "/dev/sda3", next: "A"}, wantErr: false}, + {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}, } patchRunCommand := gomonkey.ApplyFuncReturn(runCommand, nil) defer patchRunCommand.Reset() + patchGetBootMode := gomonkey.ApplyFuncSeq(getBootMode, []gomonkey.OutputCell{ + {Values: gomonkey.Params{"uefi", nil}}, + {Values: gomonkey.Params{"legacy", nil}}, + }) + defer patchGetBootMode.Reset() for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if err := install(tt.args.imagePath, tt.args.side, tt.args.next); (err != nil) != tt.wantErr { @@ -219,3 +225,104 @@ func createTmpTarFile(tarPath string) (string, error) { } return tempFile.Name(), nil } + +func Test_getBootMode(t *testing.T) { + tests := []struct { + name string + want string + wantErr bool + }{ + { + name: "uefi", + want: "uefi", + wantErr: false, + }, + { + name: "legacy", + want: "legacy", + wantErr: false, + }, + } + patchOSStat := gomonkey.ApplyFuncSeq(os.Stat, []gomonkey.OutputCell{ + {Values: gomonkey.Params{nil, nil}}, + {Values: gomonkey.Params{nil, os.ErrNotExist}}, + }) + defer patchOSStat.Reset() + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := getBootMode() + if (err != nil) != tt.wantErr { + t.Errorf("getBootMode() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("getBootMode() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_isValidImageName(t *testing.T) { + type args struct { + image string + } + tests := []struct { + name string + args args + wantErr bool + }{ + {name: "valid", args: args{image: "alpine"}, wantErr: false}, + {name: "valid", args: args{image: "alpine:latest"}, wantErr: false}, + {name: "valid", args: args{image: "localhost:1234/test"}, wantErr: false}, + {name: "valid", args: args{image: "alpine:3.7"}, wantErr: false}, + {name: "valid", args: args{image: "docker.example.edu/gmr/alpine:3.7"}, wantErr: false}, + {name: "valid", args: args{image: "docker.example.com:5000/gmr/alpine@sha256:11111111111111111111111111111111"}, wantErr: false}, + {name: "valid", args: args{image: "registry.dobby.org/dobby/dobby-servers/arthound:2019-08-08"}, wantErr: false}, + {name: "valid", args: args{image: "registry.dobby.org/dobby/dobby-servers/lerphound:latest"}, wantErr: false}, + {name: "valid", args: args{image: "registry.dobby.org/dobby/dobby-servers/loophole@sha256:5a156ff125e5a12ac7ff43ee5120fa249cf62248337b6d04abc574c8"}, wantErr: false}, + {name: "valid", args: args{image: "sosedoff/pgweb@sha256:5a156ff125e5a12ac7ff43ee5120fa249cf62248337b6d04574c8"}, wantErr: false}, + {name: "valid", args: args{image: "registry.dobby.org/dobby/antique-penguin:release-production"}, wantErr: false}, + {name: "valid", args: args{image: "dalprodictus/halcon:6.7.5"}, wantErr: false}, + {name: "valid", args: args{image: "antigua/antigua:v31"}, wantErr: false}, + {name: "invalid ;", args: args{image: "alpine;v1.0"}, wantErr: true}, + {name: "invalid tag and digest1", args: args{image: "alpine:latest@sha256:11111111111111111111111111111111"}, wantErr: true}, + {name: "invalid |", args: args{image: "alpine|v1.0"}, wantErr: true}, + {name: "invalid &", args: args{image: "alpine&v1.0"}, wantErr: true}, + {name: "invalid tag and digest2", args: args{image: "sosedoff/pgweb:latest@sha256:5a156ff125e5a12ac7ff43ee5120fa249cf62248337b6d04574c8"}, wantErr: true}, + {name: "invalid tag and digest3", args: args{image: "192.168.122.123:5000/kubeos_uefi-x86_64:euleros_v2_docker-2023-01@sha256:1a1a1a1a1a1a1a1a1a1a1a1a1a1a"}, wantErr: true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := isValidImageName(tt.args.image); (err != nil) != tt.wantErr { + t.Errorf("isValidImageName() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func Test_checkOCIImageDigestMatch(t *testing.T) { + type args struct { + containerRuntime string + imageName string + checkSum string + } + tests := []struct { + name string + args args + wantErr bool + }{ + {name: "invalid container runtion", args: args{containerRuntime: "dockctl", imageName: "docker.io/library/hello-world:latest", checkSum: "1abf18abf9bf9baa0a4a38d1afad4abf0d7da4544e163186e036c906c09c94fe"}, wantErr: true}, + {name: "nil image digets", args: args{containerRuntime: "crictl", imageName: "docker.io/library/hello-world:latest", checkSum: "1abf18abf9bf9baa0a4a38d1afad4abf0d7da4544e163186e036c906c09c94fe"}, wantErr: true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if tt.name == "nil image digets" { + patchGetOCIImageDigest := gomonkey.ApplyFuncReturn(getOCIImageDigest, "", nil) + defer patchGetOCIImageDigest.Reset() + } + if err := checkOCIImageDigestMatch(tt.args.containerRuntime, tt.args.imageName, tt.args.checkSum); (err != nil) != tt.wantErr { + t.Errorf("checkOCIImageDigestMatch() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +}