Files
clr-installer/model/model_test.go
T
2019-04-23 18:52:18 -07:00

554 lines
14 KiB
Go

// Copyright © 2018 Intel Corporation
//
// SPDX-License-Identifier: GPL-3.0-only
package model
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
"testing"
"github.com/clearlinux/clr-installer/args"
"github.com/clearlinux/clr-installer/user"
"github.com/clearlinux/clr-installer/utils"
)
func init() {
utils.SetLocale("en_US.UTF-8")
}
var (
testsDir string
)
func init() {
testsDir = os.Getenv("TESTS_DIR")
testAlias = append(testAlias, "/dev/sda", "/dev/sdb")
}
func TestLoadFile(t *testing.T) {
tests := []struct {
file string
valid bool
}{
{"basic-invalid-descriptor.yaml", false},
{"basic-valid-descriptor.yaml", true},
{"invalid-no-keyboard.yaml", false},
{"invalid-no-language.yaml", false},
{"malformed-descriptor.yaml", false},
{"no-bootable-descriptor.yaml", false},
{"no-root-partition-descriptor.yaml", false},
{"no-telemetry.yaml", false},
{"invalid-no-kernel.yaml", false},
{"block-device-image.yaml", true},
{"block-devices-alias.yaml", true},
{"mixed-block-device.yaml", true},
{"real-example.yaml", true},
{"user-sshkeys.yaml", true},
{"valid-minimal.yaml", true},
{"valid-network.yaml", true},
{"valid-with-pre-post-hooks.yaml", true},
{"valid-with-version.yaml", true},
{"azure-config.json", true},
{"azure-docker-config.json", true},
{"azure-machine-learning-config.json", true},
{"ciao-networking-config.json", true},
{"cloud-config.json", true},
{"cloud-docker-config.json", true},
{"gce-config.json", true},
{"hyperv-config.json", true},
{"hyperv-mini-config.json", true},
{"hyperv-test-config.json", true},
{"kvm-config.json", true},
{"legacy-kvm-config.json", true},
{"live-config.json", true},
{"live-docker-config.json", true},
{"provision-config.json", true},
{"vmware-config.json", true},
{"mbr.json", true},
{"min-good.json", false},
{"release-image-config.json", true},
{"full-good.json", true},
{"installer-config.json", true},
{"installer-config-vm.json", true},
{"ister.json", true},
{"valid-ister-full-virtual.json", true},
{"valid-ister-full-physical.json", true},
{"invalid-ister-basic-descriptor.json", false},
{"invalid-ister-no-kernel.json", false},
{"invalid-ister-malformed-descriptor.json", false},
{"invalid-ister-dt.json", false},
{"invalid-ister-missing-pl.json", false},
{"invalid-ister-duplicate-pl.json", false},
{"invalid-ister-disk-ft.json", false},
{"invalid-ister-partition-ft.json", false},
{"invalid-ister-disk-pmp.json", false},
{"invalid-ister-partition-pmp.json", false},
}
for _, curr := range tests {
path := filepath.Join(testsDir, curr.file)
var err error
if filepath.Ext(curr.file) == ".json" {
path, err = JSONtoYAMLConfig(path)
if curr.valid && err != nil {
t.Fatalf("%s is a valid test and shouldn't return an error: %v", curr.file, err)
}
}
model, err := LoadFile(path, args.Args{})
if curr.valid && err != nil {
t.Fatalf("%s is a valid tests and shouldn't return an error: %v", curr.file, err)
}
err = model.Validate()
if curr.valid && err != nil {
t.Fatalf("%s is a valid tests and shouldn't return an error: %v", curr.file, err)
}
}
}
func TestIsTestAlias(t *testing.T) {
testAlias = []string{}
if isTestAlias("/dev/sda") {
t.Fatalf("Should have returned false for invalid alias")
}
testAlias = append(testAlias, "/dev/sda")
if !isTestAlias("/dev/sda") {
t.Fatalf("Should have returned true for valid alias")
}
testAlias = append(testAlias, "/dev/sdb")
if !isTestAlias("/dev/sdb") {
t.Fatalf("Should have returned true for valid alias")
}
}
func TestBlockDevicesAlias(t *testing.T) {
path := filepath.Join(testsDir, "block-devices-alias.yaml")
model, err := LoadFile(path, args.Args{})
if err != nil {
t.Fatalf("Failed to load yaml file: %s", err)
}
tm := model.TargetMedias[0]
if tm.Name != "sda" {
t.Fatalf("Failed to expand Name variable, value: %s, expected: sda", tm.Name)
}
if tm.GetDeviceFile() != "/dev/sda" {
t.Fatalf("Invalid device name value: %s, expected: /dev/sda", tm.GetDeviceFile())
}
for i, bd := range tm.Children {
expected := fmt.Sprintf("sda%d", i+1)
expectedFile := filepath.Join("/dev/", expected)
if bd.Name != expected {
t.Fatalf("Failed to expand Name variable, value: %s, expected: %s", bd.Name, expected)
}
if bd.GetDeviceFile() != expectedFile {
t.Fatalf("Invalid device name value: %s, expected: %s", bd.GetDeviceFile(), expectedFile)
}
}
}
func TestBlockDevicesAliasOverwrite(t *testing.T) {
path := filepath.Join(testsDir, "block-devices-alias.yaml")
options := args.Args{BlockDevices: []string{"target:/dev/sdb"}}
model, err := LoadFile(path, options)
if err != nil {
t.Fatalf("Failed to load yaml file: %s", err)
}
tm := model.TargetMedias[0]
if tm.Name != "sdb" {
t.Fatalf("Failed to expand Name variable, value: %s, expected: sdb", tm.Name)
}
if tm.GetDeviceFile() != "/dev/sdb" {
t.Fatalf("Invalid device name value: %s, expected: /dev/sdb", tm.GetDeviceFile())
}
for i, bd := range tm.Children {
expected := fmt.Sprintf("sdb%d", i+1)
expectedFile := filepath.Join("/dev/", expected)
if bd.Name != expected {
t.Fatalf("Failed to expand Name variable, value: %s, expected: %s", bd.Name, expected)
}
if bd.GetDeviceFile() != expectedFile {
t.Fatalf("Invalid device name value: %s, expected: %s", bd.GetDeviceFile(), expectedFile)
}
}
}
func TestInvalidBlockDeviceArgument(t *testing.T) {
path := filepath.Join(testsDir, "block-devices-alias.yaml")
options := args.Args{BlockDevices: []string{"invalid"}}
model, err := LoadFile(path, options)
if err != nil {
t.Fatalf("Failed to load yaml file: %s", err)
}
if len(model.StorageAlias) != 1 {
t.Fatalf("The model should contain only 2 storage aliases")
}
for _, curr := range model.StorageAlias {
if curr.Name == "invalid" {
t.Fatalf("The \"invalid\" block-device argument shouldn't be added to the model")
}
}
}
func TestEnableTelemetry(t *testing.T) {
si := &SystemInstall{}
if si.IsTelemetryEnabled() == true {
t.Fatal("Default value for telemetry should be false")
}
// should always succeed
si.EnableTelemetry(true)
if si.Telemetry == nil {
t.Fatal("SystemInstall.EnableTelemetry() should allocate Telemetry object")
}
if si.IsTelemetryEnabled() == false {
t.Fatal("Wrong Telemetry value set or returned")
}
}
func TestUnreadable(t *testing.T) {
file, err := ioutil.TempFile("", "test-")
if err != nil {
t.Fatal("Could not create a temp file")
}
defer func() {
if err = file.Close(); err != nil {
t.Fatal(err)
}
_ = os.Remove(file.Name())
}()
if file.Chmod(0111) != nil {
t.Fatal("Failed to change tmp file mod")
}
if utils.IsRoot() {
t.Log("Not running as 'root', not checking read permission")
} else {
_, err = LoadFile(file.Name(), args.Args{})
if err == nil {
t.Fatal("Should have failed to read")
}
}
if os.Remove(file.Name()) != nil {
t.Fatal("Failed to cleanup test file")
}
}
func TestBundle(t *testing.T) {
si := &SystemInstall{}
if si.ContainsBundle("test-bundle") {
t.Fatal("Should return false since test-bundle wasn't added to si")
}
si.AddBundle("test-bundle")
si.AddBundle("test-bundle-2")
if !si.ContainsBundle("test-bundle") {
t.Fatal("Should return true since test-bundle was added to si")
}
si.RemoveBundle("test-bundle")
if si.ContainsBundle("test-bundle") {
t.Fatal("Should return false since test-bundle was removed from si")
}
si.RemoveBundle("test-bundle-2")
// duplicated
si.AddBundle("test-bundle")
si.AddBundle("test-bundle")
if len(si.Bundles) > 1 {
t.Fatal("We should have handled the duplication")
}
}
func TestUserBundle(t *testing.T) {
si := &SystemInstall{}
if si.ContainsUserBundle("test-ubundle") {
t.Fatal("Should return false since test-ubundle wasn't added to si")
}
si.AddUserBundle("test-ubundle")
si.AddUserBundle("test-ubundle-2")
if !si.ContainsUserBundle("test-ubundle") {
t.Fatal("Should return true since test-ubundle was added to si")
}
si.RemoveUserBundle("test-ubundle")
if si.ContainsUserBundle("test-ubundle") {
t.Fatal("Should return false since test-ubundle was removed from si")
}
si.RemoveUserBundle("test-ubundle-2")
// duplicated
si.AddUserBundle("test-ubundle")
si.AddUserBundle("test-ubundle")
if len(si.UserBundles) > 1 {
t.Fatal("We should have handled the duplication")
}
}
func TestAddTargetMedia(t *testing.T) {
path := filepath.Join(testsDir, "basic-valid-descriptor.yaml")
loaded, err := LoadFile(path, args.Args{})
if err != nil {
t.Fatal("Failed to load a valid descriptor")
}
nm := &SystemInstall{}
nm.AddTargetMedia(loaded.TargetMedias[0])
if len(nm.TargetMedias) != 1 {
t.Fatal("Failed to add target media to model")
}
// the AddTargetMedia() interface must prevent duplication
cl := len(nm.TargetMedias)
nm.AddTargetMedia(loaded.TargetMedias[0])
if len(nm.TargetMedias) != cl {
t.Fatal("AddTargetMedia() must prevent duplication")
}
// AddTargetMedia() should always add non equal medias
clone := loaded.TargetMedias[0].Clone()
clone.Name = clone.Name + "-cloned"
// Check for encryption passphrase needed; should not
if nm.EncryptionRequiresPassphrase() {
t.Fatal("nm.EncryptionRequiresPassphrase() should NOT be true")
}
nm.AddTargetMedia(clone)
if len(nm.TargetMedias) == cl {
t.Fatal("AddTargetMedia() failed to add a cloned and modified target media")
}
}
func TestAddNetworkInterface(t *testing.T) {
path := filepath.Join(testsDir, "valid-network.yaml")
loaded, err := LoadFile(path, args.Args{})
if err != nil {
t.Fatal("Failed to load a valid descriptor")
}
nm := &SystemInstall{}
nm.AddNetworkInterface(loaded.NetworkInterfaces[0])
if len(nm.NetworkInterfaces) != 1 {
t.Fatal("Failed to add network interface to model")
}
}
func TestUser(t *testing.T) {
users := []*user.User{
{Login: "login1", Password: "pwd1", Admin: false},
{Login: "login2", Password: "pwd2", Admin: false},
{Login: "login3", Password: "pwd3", Admin: false},
{Login: "login4", Password: "pwd4", Admin: false},
}
si := &SystemInstall{}
for i, curr := range users {
si.AddUser(curr)
if len(si.Users) != i+1 {
t.Fatal("User wasn't added")
}
}
cl := len(si.Users)
// don't add same user twice
si.AddUser(users[0])
if len(si.Users) != cl {
t.Fatal("The AddUser() interface should prevent user duplication")
}
si.RemoveAllUsers()
if len(si.Users) != 0 {
t.Fatal("User list should be empty")
}
}
func TestWriteFile(t *testing.T) {
path := filepath.Join(testsDir, "basic-valid-descriptor.yaml")
loaded, err := LoadFile(path, args.Args{})
if err != nil {
t.Fatal("Failed to load a valid descriptor")
}
tmpFile, err := ioutil.TempFile("", "test-")
if err != nil {
t.Fatal("Could not create a temp file")
}
defer func() { _ = os.Remove(tmpFile.Name()) }()
path = tmpFile.Name()
if err = tmpFile.Close(); err != nil {
t.Fatal(err)
}
if err := loaded.WriteFile(path); err != nil {
t.Fatal("Failed to write descriptor, should be valid")
}
// test writing to an invalid file
if err := loaded.WriteFile("/invalid-dir/invalid.yaml"); err == nil {
t.Fatal("Should have failed writing to an invalid file")
}
}
func TestAddExtraKernelArguments(t *testing.T) {
args := []string{"arg1", "arg2", "arg3"}
si := &SystemInstall{}
si.AddExtraKernelArguments(args)
if si.KernelArguments == nil {
t.Fatal("AddExtraKernelArguments() should had created a KernelArguments object")
}
if len(si.KernelArguments.Add) != len(args) {
t.Fatal("AddExtraKernelArguments() didn't add all requested arguments")
}
for _, curr := range args {
if !utils.StringSliceContains(si.KernelArguments.Add, curr) {
t.Fatal("AddExtraKernelArguments() didn't add all the requested arguments")
}
}
l := len(si.KernelArguments.Add)
// testing duplication checks
si.AddExtraKernelArguments(args)
if l < len(si.KernelArguments.Add) {
t.Fatal("The duplication check has failed")
}
}
func TestRemoveKernelArguments(t *testing.T) {
args := []string{"arg1", "arg2", "arg3"}
si := &SystemInstall{}
si.RemoveKernelArguments(args)
if si.KernelArguments == nil {
t.Fatal("RemoveKernelArguments() should had created a KernelArguments object")
}
if len(si.KernelArguments.Remove) != len(args) {
t.Fatal("RemoveKernelArguments() didn't add all requested arguments")
}
for _, curr := range args {
if !utils.StringSliceContains(si.KernelArguments.Remove, curr) {
t.Fatal("RemoveKernelArguments() didn't add all the requested arguments")
}
}
l := len(si.KernelArguments.Remove)
// testing duplication check
si.RemoveKernelArguments(args)
if l < len(si.KernelArguments.Remove) {
t.Fatal("The duplication check has failed")
}
}
func TestAddEncryptedTargetMedia(t *testing.T) {
path := filepath.Join(testsDir, "encrypt-valid-descriptor.yaml")
loaded, err := LoadFile(path, args.Args{})
if err != nil {
t.Fatal("Failed to load a valid descriptor")
}
nm := &SystemInstall{}
nm.AddTargetMedia(loaded.TargetMedias[0])
if len(nm.TargetMedias) != 1 {
t.Fatal("Failed to add target media to model")
}
cl := len(nm.TargetMedias)
// AddTargetMedia() should always add non equal medias
clone := loaded.TargetMedias[0].Clone()
clone.Name = clone.Name + "-cloned"
// Check for encryption passphrase needed; should not
if !nm.EncryptionRequiresPassphrase() {
t.Fatal("nm.EncryptionRequiresPassphrase() must always be true")
}
nm.AddTargetMedia(clone)
if len(nm.TargetMedias) == cl {
t.Fatal("AddTargetMedia() failed to add a cloned and modified target media")
}
}
func TestBackupFile(t *testing.T) {
var err error
path := filepath.Join(testsDir, "valid-ister-full-physical.json")
cf := strings.TrimSuffix(path, filepath.Ext(path)) + ".yaml"
info, err := os.Stat(cf)
if os.IsNotExist(err) {
t.Fatalf("%s should already exist and shouldn't return an error: %v", cf, err)
}
mt := info.ModTime()
suffix := fmt.Sprintf("-%d-%02d-%02d-%02d%02d%02d",
mt.Year(), mt.Month(), mt.Day(),
mt.Hour(), mt.Minute(), mt.Second())
bf := strings.TrimSuffix(cf, filepath.Ext(cf)) + suffix + ".yaml"
path, err = JSONtoYAMLConfig(path)
if err != nil {
t.Fatalf("%s is a valid test and shouldn't return an error: %v", path, err)
}
info, err = os.Stat(cf)
if os.IsNotExist(err) {
t.Fatalf("%s should still exist and shouldn't return an error: %v", cf, err)
}
info, err = os.Stat(bf)
if os.IsNotExist(err) {
t.Fatalf("%s should exist and shouldn't return an error: %v", cf, err)
}
}