1
0
mirror of https://github.com/coreos/ignition.git synced 2026-02-07 03:47:55 +01:00
Files
ignition/tests/blackbox_test.go
yasminvalim 1ae3bc35d5 tests/blackbox_test: Log temp dir removal error
Fixes lint:
```
tests/blackbox_test.go:136:20: Error return value of `os.RemoveAll` is not checked (errcheck)
tests/blackbox_test.go:151:20: Error return value of `os.RemoveAll` is not checked (errcheck)
```
2025-08-07 12:37:43 +02:00

383 lines
10 KiB
Go

// 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")
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
}
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 := test.Env
appendEnv = append(appendEnv, "IGNITION_SYSTEM_CONFIG_DIR="+systemConfigDir)
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
}