/
githubmirror
/
ignition
Обзор
Документация
Войти
/
githubmirror
/
ignition
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
tests/blackbox_test.go
407 строк
11 KB
Andrew Dodds
providers/cmdline: add device-based config loading
07 июл 2026, 11:38
07 июл 2026, 11:38
feb4f08
Код
Авторство
О чём код?
// Copyright 2017 CoreOS, Inc. // // 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 blackbox import ( "context" "flag" "fmt" "os" "os/exec" "os/signal" "path/filepath" "strings" "testing" "time" "github.com/coreos/ignition/v2/config" "github.com/coreos/ignition/v2/internal/exec/util" "github.com/coreos/ignition/v2/tests/register" "github.com/coreos/ignition/v2/tests/servers" "github.com/coreos/ignition/v2/tests/types" // Register the tests _ "github.com/coreos/ignition/v2/tests/registry" // UUID generation tool "github.com/google/uuid" "golang.org/x/sys/unix" ) var ( // testTimeout controls how long a given test is allowed to run before being // cancelled. testTimeout = time.Second * 60 // somewhat of an abuse of contexts but go's got our hands tied killContext = context.TODO() // flag for listing all subtests that would be run without running them listSubtests = false ) func TestMain(m *testing.M) { interruptChan := make(chan os.Signal, 3) signal.Notify(interruptChan, os.Interrupt, unix.SIGTERM) tmp, killCancel := context.WithCancel(context.Background()) killContext = tmp go func() { for { sig := <-interruptChan switch sig { case os.Interrupt, unix.SIGTERM: killCancel() } } }() flag.BoolVar(&listSubtests, "list", false, "list tests that would be run without running them") flag.Parse() if !listSubtests { httpServer := &servers.HTTPServer{} httpServer.Start() tftpServer := &servers.TFTPServer{} tftpServer.Start() } os.Exit(m.Run()) } func TestIgnitionBlackBox(t *testing.T) { for _, test := range register.Tests[register.PositiveTest] { test := test t.Run(test.Name, func(t *testing.T) { if killContext.Err() != nil || (testing.Short() && test.ConfigMinVersion != test.ConfigVersion) { t.SkipNow() } if listSubtests { fmt.Println(t.Name()) return } t.Parallel() err := outer(t, test, false) if err != nil { t.Error(err) } }) } } func TestIgnitionBlackBoxNegative(t *testing.T) { for _, test := range register.Tests[register.NegativeTest] { test := test t.Run(test.Name, func(t *testing.T) { if killContext.Err() != nil || (testing.Short() && test.ConfigMinVersion != test.ConfigVersion) { t.SkipNow() } if listSubtests { fmt.Println(t.Name()) return } t.Parallel() err := outer(t, test, true) if err != nil { t.Error(err) } }) } } func outer(t *testing.T, test types.Test, negativeTests bool) error { t.Log(test.Name) err := test.ReplaceAllUUIDVars() if err != nil { return err } ctx, cancelFunc := context.WithDeadline(killContext, time.Now().Add(testTimeout)) defer cancelFunc() tmpDirectory, err := os.MkdirTemp("/var/tmp", "ignition-blackbox-") if err != nil { return fmt.Errorf("failed to create a temp dir: %v", err) } defer func() { if err := os.RemoveAll(tmpDirectory); err != nil { t.Logf("failed to remove temp directory: %v", err) } }() // the tmpDirectory must be 0755 or the tests will fail as the tool will // not have permissions to perform some actions in the mounted folders err = os.Chmod(tmpDirectory, 0755) if err != nil { return fmt.Errorf("failed to change mode of temp dir: %v", err) } systemConfigDir := filepath.Join(tmpDirectory, "system") systemRuntimeConfigDir := filepath.Join(tmpDirectory, "system-runtime") systemLocalConfigDir := filepath.Join(tmpDirectory, "system-local") var rootPartition *types.Partition // Setup err = createFilesFromSlice(systemConfigDir, test.SystemDirFiles) // Defer before the error handling because the createFilesFromSlice function // can fail after partially-creating things defer func() { if err := os.RemoveAll(systemConfigDir); err != nil { t.Logf("failed to remove system config directory: %v", err) } }() if err != nil { return err } err = createFilesFromSlice(systemRuntimeConfigDir, test.SystemRuntimeDirFiles) defer func() { if err := os.RemoveAll(systemRuntimeConfigDir); err != nil { t.Logf("failed to remove system runtime config directory: %v", err) } }() if err != nil { return err } err = createFilesFromSlice(systemLocalConfigDir, test.SystemLocalDirFiles) defer func() { if err := os.RemoveAll(systemLocalConfigDir); err != nil { t.Logf("failed to remove system local config directory: %v", err) } }() if err != nil { return err } for i, disk := range test.In { // Set image file path disk.ImageFile = filepath.Join(tmpDirectory, fmt.Sprintf("hd%d", i)) test.Out[i].ImageFile = disk.ImageFile // There may be more partitions created by Ignition, so look at the // expected output instead of the input to determine image size imageSize := test.Out[i].CalculateImageSize() if inSize := disk.CalculateImageSize(); inSize > imageSize { imageSize = inSize } // Finish data setup for _, part := range disk.Partitions { if part.GUID == "" { part.GUID = uuid.New().String() if err != nil { return err } } err := updateTypeGUID(part) if err != nil { return err } } disk.SetOffsets() for _, part := range test.Out[i].Partitions { err := updateTypeGUID(part) if err != nil { return err } } test.Out[i].SetOffsets() if err = setupDisk(ctx, &disk, i, imageSize, tmpDirectory); err != nil { return err } // Creation // Move value into the local scope, because disk.ImageFile and device // will change by the time this runs imageFile := disk.ImageFile device := disk.Device defer func() { if err := os.Remove(imageFile); err != nil { t.Errorf("couldn't remove %s: %v", imageFile, err) } }() defer func() { if err := destroyDevice(device); err != nil { t.Errorf("couldn't destroy device: %v", err) } }() test.Out[i].Device = disk.Device err = createFilesForPartitions(ctx, disk.Partitions) if err != nil { return err } // Mount device name substitution for _, d := range test.MntDevices { device := pickPartition(disk.Device, disk.Partitions, d.Label) // The device may not be on this disk, if it's not found here let's // assume we'll find it on another one and keep going if device != "" { test.Config = strings.ReplaceAll(test.Config, d.Substitution, device) } } // Replace any instance of $disk<num> with the actual loop device // that got assigned to it test.Config = strings.ReplaceAll(test.Config, fmt.Sprintf("$disk%d", i), disk.Device) if rootPartition == nil { rootPartition = getRootPartition(disk.Partitions) } } if rootPartition == nil { return fmt.Errorf("ROOT filesystem not found! A partition labeled ROOT is requred") } if strings.Contains(test.Config, "passwd") { if err := prepareRootPartitionForPasswd(ctx, rootPartition); err != nil { return err } } // Validation and cleanup deferral for i, disk := range test.Out { // Update out structure with mount points & devices setExpectedPartitionsDrive(test.In[i].Partitions, disk.Partitions) } // Let's make sure that all of the devices we needed to substitute names in // for were found for _, d := range test.MntDevices { if strings.Contains(test.Config, d.Substitution) { return fmt.Errorf("Didn't find a drive with label: %s", d.Substitution) } } t.Logf("Rendered Ignition Config:\n%s", test.Config) // If we're not expecting the config to be bad, make sure it passes // validation. if !test.ConfigShouldBeBad { renderedConfig, rpt, err := config.Parse([]byte(test.Config)) if rpt.IsFatal() { return fmt.Errorf("test has bad config: %s", rpt.String()) } if err != nil { return fmt.Errorf("error parsing config: %v", err) } defer func() { for _, luks := range renderedConfig.Storage.Luks { if err := removeLuksDevice(luks.Name); err != nil { t.Error(fmt.Errorf("failed to remove existing LUKS device %s: %v", luks.Name, err)) } } }() } // Ignition config if err := os.WriteFile(filepath.Join(tmpDirectory, "config.ign"), []byte(test.Config), 0666); err != nil { return fmt.Errorf("error writing config: %v", err) } // Ignition appendEnv := make([]string, len(test.Env)) for i, env := range test.Env { appendEnv[i] = strings.ReplaceAll(env, "$SYSTEM_CONFIG_DIR", systemConfigDir) } appendEnv = append(appendEnv, "IGNITION_SYSTEM_CONFIG_DIR="+systemConfigDir) appendEnv = append(appendEnv, "IGNITION_SYSTEM_RUNTIME_CONFIG_DIR="+systemRuntimeConfigDir) appendEnv = append(appendEnv, "IGNITION_SYSTEM_LOCAL_CONFIG_DIR="+systemLocalConfigDir) if !negativeTests { if err := runIgnition(t, ctx, "fetch", "", tmpDirectory, appendEnv, test.SkipCriticalCheck); err != nil { return err } if err := runIgnition(t, ctx, "disks", "", tmpDirectory, appendEnv, test.SkipCriticalCheck); err != nil { return err } if err := mountPartition(ctx, rootPartition); err != nil { return err } if err := runIgnition(t, ctx, "mount", rootPartition.MountPath, tmpDirectory, appendEnv, test.SkipCriticalCheck); err != nil { return err } filesErr := runIgnition(t, ctx, "files", rootPartition.MountPath, tmpDirectory, appendEnv, test.SkipCriticalCheck) if err := runIgnition(t, ctx, "umount", rootPartition.MountPath, tmpDirectory, appendEnv, test.SkipCriticalCheck); err != nil { return err } if err := umountPartition(rootPartition); err != nil { return err } if filesErr != nil { return filesErr } for _, disk := range test.Out { err = validateDisk(t, disk) if err != nil { return err } err = validateFilesystems(t, disk.Partitions) if err != nil { return err } validateFilesDirectoriesAndLinks(t, ctx, disk.Partitions) } return nil } else { if err := runIgnition(t, ctx, "fetch", "", tmpDirectory, appendEnv, test.SkipCriticalCheck); err != nil { return nil // error is expected } if err := runIgnition(t, ctx, "disks", "", tmpDirectory, appendEnv, test.SkipCriticalCheck); err != nil { return nil // error is expected } if err := mountPartition(ctx, rootPartition); err != nil { return err } if err := runIgnition(t, ctx, "mount", rootPartition.MountPath, tmpDirectory, appendEnv, test.SkipCriticalCheck); err != nil { return nil // error is expected } filesErr := runIgnition(t, ctx, "files", rootPartition.MountPath, tmpDirectory, appendEnv, test.SkipCriticalCheck) if err := runIgnition(t, ctx, "umount", rootPartition.MountPath, tmpDirectory, appendEnv, test.SkipCriticalCheck); err != nil { return nil } if err := umountPartition(rootPartition); err != nil { return err } if filesErr != nil { return nil // error is expected } return fmt.Errorf("Expected failure and ignition succeeded") } } // Remove a LUKS device func removeLuksDevice(deviceName string) error { deviceExists, err := util.PathExists(fmt.Sprintf("/dev/mapper/%s", deviceName)) if err != nil { return fmt.Errorf("failed to check if device exists at %s: %v", deviceName, err) } if deviceExists { cmd := exec.Command("sudo", "cryptsetup", "luksClose", deviceName) if err := cmd.Run(); err != nil { return fmt.Errorf("failed to remove LUKS device %s: %v", deviceName, err) } } return nil }