| package kernelmods | |
| import ( | |
| "context" | |
| "fmt" | |
| "os" | |
| "path/filepath" | |
| "reflect" | |
| "strings" | |
| "testing" | |
| "github.com/fsouza/fake-gcs-server/fakestorage" | |
| "cos-extensions/tools/gcs" | |
| ) | |
| func TestListInstalledModulesDirNonExistent(t *testing.T) { | |
| dirName := "nonexistent/" | |
| _, err := ListInstalledModules(dirName, false) | |
| wantErrMessage := "failed to retrieve module files from directory nonexistent/: failed to validate local directory" | |
| if err == nil || !strings.HasPrefix(err.Error(), wantErrMessage) { | |
| t.Errorf(" TestListNewModulesDirNonExistent: expected error: %s \t got error: %v", wantErrMessage, err) | |
| } | |
| } | |
| func TestListInstalledModulesSuccessful(t *testing.T) { | |
| retrieveInstalledModules = func(moduleFiles []string) (map[string]string, error) { | |
| modMap := make(map[string]string) | |
| for _, modFile := range moduleFiles { | |
| mod := strings.Split(modFile, "/") | |
| modName, _ := strings.CutSuffix(mod[len(mod)-1], ".ko") | |
| modMap[modFile] = modName | |
| } | |
| return modMap, nil | |
| } | |
| tmpDir, err := os.MkdirTemp("", "test-module") | |
| if err != nil { | |
| t.Fatalf("Failed to create temp dir: %v", err) | |
| } | |
| defer os.RemoveAll(tmpDir) | |
| filePath := filepath.Join(tmpDir, "unknown.txt") | |
| _, err = os.Create(filePath) | |
| if err != nil { | |
| t.Fatalf("Failed to create temp file: %v", err) | |
| } | |
| filePath = filepath.Join(tmpDir, "module.ko") | |
| _, err = os.Create(filePath) | |
| if err != nil { | |
| t.Fatalf("Failed to create temp file: %v", err) | |
| } | |
| got, err := ListInstalledModules(tmpDir, false) | |
| if err != nil { | |
| t.Errorf("TestListModulesSuccessful: error: %v", err) | |
| } | |
| want := map[string]string{filePath: "module"} | |
| if !reflect.DeepEqual(got, want) { | |
| t.Errorf("TestListModulesSuccessful: expected module paths: %v\t got module paths: %v", want, got) | |
| } | |
| } | |
| func TestListDownloadedModulesSuccessful(t *testing.T) { | |
| tmpDir, err := os.MkdirTemp("", "test-module") | |
| if err != nil { | |
| t.Fatalf("Failed to create temp dir: %v", err) | |
| } | |
| defer os.RemoveAll(tmpDir) | |
| filePath := filepath.Join(tmpDir, "unknown.txt") | |
| _, err = os.Create(filePath) | |
| if err != nil { | |
| t.Fatalf("Failed to create temp file: %v", err) | |
| } | |
| filePath = filepath.Join(tmpDir, "module.ko") | |
| _, err = os.Create(filePath) | |
| if err != nil { | |
| t.Fatalf("Failed to create temp file: %v", err) | |
| } | |
| retrieveLocalModulesInfo = func(moduleFiles []string) map[string]string { | |
| modMap := make(map[string]string) | |
| for _, modFile := range moduleFiles { | |
| mod := strings.Split(modFile, "/") | |
| modName, _ := strings.CutSuffix(mod[len(mod)-1], ".ko") | |
| modMap[modFile] = modName | |
| } | |
| return modMap | |
| } | |
| got, err := ListDownloadedModules(tmpDir, false) | |
| if err != nil { | |
| t.Errorf("TestListModulesSuccessful: error: %v", err) | |
| } | |
| want := map[string]string{ | |
| filePath: "module", | |
| } | |
| if !reflect.DeepEqual(got, want) { | |
| t.Errorf("TestListDownloadedModulesSuccessful: expected module paths: %v\t got module paths: %v", want, got) | |
| } | |
| } | |
| func TestInstallModules(t *testing.T) { | |
| retrieveInstalledModules = func(_ []string) (map[string]string, error) { | |
| return map[string]string{"/var/lib/module/skip1.ko": "skip1", "/var/lib/module/skip2.ko": "skip2"}, nil | |
| } | |
| installModule = func(modPath string, _ []string) error { | |
| if modPath == "/var/lib/module/error.ko" { | |
| return fmt.Errorf("An error occured") | |
| } | |
| return nil | |
| } | |
| tests := []struct { | |
| desc string | |
| modulePaths []string | |
| want []string | |
| }{ | |
| { | |
| desc: "Successfully installs modules", | |
| modulePaths: []string{"/var/lib/module/module1.ko", "/var/lib/module/module2.ko"}, | |
| want: []string{"/var/lib/module/module1.ko", "/var/lib/module/module2.ko"}, | |
| }, { | |
| desc: "Skips module already installed.", | |
| modulePaths: []string{"/var/lib/module/skip1.ko", "/var/lib/module/module2.ko"}, | |
| want: []string{"/var/lib/module/module2.ko"}, | |
| }, { | |
| desc: "Skips module that fails to install", | |
| modulePaths: []string{"/var/lib/module/error.ko", "/var/lib/module/module2.ko"}, | |
| want: []string{"/var/lib/module/module2.ko"}, | |
| }, | |
| } | |
| for _, test := range tests { | |
| t.Run(test.desc, func(t *testing.T) { | |
| got, err := InstallModules(test.modulePaths, []string{}) | |
| if err != nil { | |
| t.Errorf("TestInstallModules(%s): error: %v", test.desc, err) | |
| } | |
| if !reflect.DeepEqual(got, test.want) { | |
| t.Errorf("TestInstallModules(%s): expected module paths: %s\t got module paths: %s", test.desc, test.want, got) | |
| } | |
| }) | |
| } | |
| } | |
| func TestDownloadModulesSuccessful(t *testing.T) { | |
| ctx := context.Background() | |
| tmpDir, err := os.MkdirTemp("", "test-gcs") | |
| if err != nil { | |
| t.Fatalf("Failed to create temp dir: %v", err) | |
| } | |
| defer os.RemoveAll(tmpDir) | |
| server := fakestorage.NewServer( | |
| []fakestorage.Object{ | |
| {ObjectAttrs: fakestorage.ObjectAttrs{ | |
| BucketName: "my-bucket", Name: "path/to/module/module1.ko", | |
| }, | |
| Content: []byte("content"), | |
| }, | |
| }) | |
| var cfg gcs.GCSConfig | |
| err = cfg.Init(ctx, "gs://my-bucket/path/to/module", server.Client()) | |
| if err != nil { | |
| t.Fatalf("Failed to initialize gcs config: %v", err) | |
| } | |
| moduleFiles := []string{"module1.ko", "skip.ko"} | |
| want := []string{fmt.Sprintf("%s/module1.ko", tmpDir)} | |
| got, err := DownloadModules(ctx, &cfg, moduleFiles, tmpDir, false) | |
| if err != nil { | |
| t.Errorf("TestDownloadModulesSuccessful: error: %v", err) | |
| } | |
| if !reflect.DeepEqual(want, got) { | |
| t.Errorf("TestDownloadKernelModuleSuccessful: expected module paths: %s\t got module paths: %s", want, got) | |
| } | |
| } | |
| func TestDownloadModulesRecursivelySuccessful(t *testing.T) { | |
| ctx := context.Background() | |
| tmpDir, err := os.MkdirTemp("", "test-gcs") | |
| if err != nil { | |
| t.Fatalf("Failed to create temp dir: %v", err) | |
| } | |
| defer os.RemoveAll(tmpDir) | |
| server := fakestorage.NewServer( | |
| []fakestorage.Object{ | |
| {ObjectAttrs: fakestorage.ObjectAttrs{ | |
| BucketName: "my-bucket", Name: "path/to/module/a.ko", | |
| }, | |
| Content: []byte("content"), | |
| }, | |
| {ObjectAttrs: fakestorage.ObjectAttrs{ | |
| BucketName: "my-bucket", Name: "path/to/module/b/b.ko", | |
| }, | |
| Content: []byte("content"), | |
| }, | |
| {ObjectAttrs: fakestorage.ObjectAttrs{ | |
| BucketName: "my-bucket", Name: "path/to/module/c.ko", | |
| }, | |
| Content: []byte("content"), | |
| }, | |
| {ObjectAttrs: fakestorage.ObjectAttrs{ | |
| BucketName: "my-bucket", Name: "path/to/module/d", | |
| }, | |
| Content: []byte("not module"), | |
| }, | |
| }) | |
| defer server.Stop() | |
| var cfg gcs.GCSConfig | |
| err = cfg.Init(ctx, "gs://my-bucket/path/to/module", server.Client()) | |
| if err != nil { | |
| t.Fatalf("Failed to initialize gcs config: %v", err) | |
| } | |
| got, err := DownloadModules(ctx, &cfg, []string{}, tmpDir, true) | |
| if err != nil { | |
| t.Errorf("TestDownloadAllKernelModulesSuccessful: failed to download module: %v", err) | |
| } | |
| want := []string{filepath.Join(tmpDir, "a.ko"), filepath.Join(tmpDir, "b/b.ko"), filepath.Join(tmpDir, "c.ko")} | |
| if !reflect.DeepEqual(got, want) { | |
| t.Errorf("TestDownloadModulesRecursivelySuccessful: expected path: %s\t got path: %s", want, got) | |
| } | |
| } | |
| func TestRetrieveModulePaths(t *testing.T) { | |
| tmpDir, err := os.MkdirTemp("", "test-gcs") | |
| if err != nil { | |
| t.Fatalf("Failed to create temp dir (%s): %v", tmpDir, err) | |
| } | |
| defer os.RemoveAll(tmpDir) | |
| subdir, err := os.MkdirTemp(tmpDir, "sub-test-gcs") | |
| if err != nil { | |
| t.Fatalf("Failed to create temp dir (%s): %v", subdir, err) | |
| } | |
| dsts := []string{filepath.Join(tmpDir, "module1.ko"), filepath.Join(subdir, "module2.ko")} | |
| for _, dst := range dsts { | |
| f, err := os.Create(dst) | |
| f.Close() | |
| if err != nil { | |
| t.Fatalf("Failed to file (%s): %v", dst, err) | |
| } | |
| } | |
| tests := []struct { | |
| desc string | |
| recursive bool | |
| want []string | |
| }{ | |
| { | |
| desc: "Returns all module files in immediate directory.", | |
| want: []string{dsts[0]}, | |
| }, | |
| { | |
| desc: "Returns all module files in directory and subdirectory recursively.", | |
| recursive: true, | |
| want: dsts, | |
| }, | |
| } | |
| for _, test := range tests { | |
| t.Run(test.desc, func(t *testing.T) { | |
| var modPaths []string | |
| got, err := retrieveModulePaths(tmpDir, modPaths, test.recursive) | |
| if err != nil { | |
| t.Errorf("TestRetrieveModulePaths(%s): error: %v", test.desc, err) | |
| } | |
| if !reflect.DeepEqual(got, test.want) { | |
| t.Errorf("TestRetrieveModulePaths(%s): expected module paths: %s\t got module paths: %s", test.desc, test.want, got) | |
| } | |
| }) | |
| } | |
| } |