mirror of
https://github.com/clearlinux/clr-installer.git
synced 2026-08-21 06:57:33 +00:00
721 lines
21 KiB
Go
721 lines
21 KiB
Go
// Copyright © 2020 Intel Corporation
|
|
//
|
|
// SPDX-License-Identifier: GPL-3.0-only
|
|
|
|
package model
|
|
|
|
import (
|
|
"fmt"
|
|
"io/ioutil"
|
|
"os"
|
|
"path/filepath"
|
|
"sort"
|
|
"strings"
|
|
"time"
|
|
|
|
"gopkg.in/yaml.v2"
|
|
|
|
"github.com/clearlinux/clr-installer/args"
|
|
"github.com/clearlinux/clr-installer/boolset"
|
|
"github.com/clearlinux/clr-installer/errors"
|
|
"github.com/clearlinux/clr-installer/kernel"
|
|
"github.com/clearlinux/clr-installer/keyboard"
|
|
"github.com/clearlinux/clr-installer/language"
|
|
"github.com/clearlinux/clr-installer/network"
|
|
"github.com/clearlinux/clr-installer/storage"
|
|
"github.com/clearlinux/clr-installer/telemetry"
|
|
"github.com/clearlinux/clr-installer/timezone"
|
|
"github.com/clearlinux/clr-installer/user"
|
|
"github.com/clearlinux/clr-installer/utils"
|
|
)
|
|
|
|
const (
|
|
// DemoVersion is hard coded string we display in log files
|
|
// when running in demo (aka documentation mode). We will
|
|
// now use this as a flag to not include the version in UI.
|
|
DemoVersion = "X.Y.Z"
|
|
)
|
|
|
|
// Version of Clear Installer.
|
|
// Also used by the Makefile for releases.
|
|
// Default to the version of the program
|
|
// but may be overridden for demo/documentation mode.
|
|
// Set by Go linker in the Makefile
|
|
var Version = "undefined"
|
|
|
|
// BuildDate is set by the Go linker with the build datetime
|
|
var BuildDate = "undefined"
|
|
var testAlias = []string{}
|
|
|
|
// SystemInstall represents the system install "configuration", the target
|
|
// medias, bundles to install and whatever state a install may require
|
|
type SystemInstall struct {
|
|
InstallSelected map[string]storage.InstallTarget `yaml:"-"`
|
|
TargetMedias []*storage.BlockDevice `yaml:"targetMedia"`
|
|
NetworkInterfaces []*network.Interface `yaml:"networkInterfaces,omitempty,flow"`
|
|
Keyboard *keyboard.Keymap `yaml:"keyboard,omitempty,flow"`
|
|
Language *language.Language `yaml:"language,omitempty,flow"`
|
|
Bundles []string `yaml:"bundles,omitempty,flow"`
|
|
TargetBundles []string `yaml:"targetBundles,omitempty,flow"`
|
|
UserBundles []string `yaml:"userBundles,omitempty,flow"`
|
|
Offline bool `yaml:"offline,omitempty,flow"`
|
|
HTTPSProxy string `yaml:"httpsProxy,omitempty,flow"`
|
|
Telemetry *telemetry.Telemetry `yaml:"telemetry,omitempty,flow"`
|
|
Timezone *timezone.TimeZone `yaml:"timezone,omitempty,flow"`
|
|
Users []*user.User `yaml:"users,omitempty,flow"`
|
|
KernelArguments *kernel.Arguments `yaml:"kernel-arguments,omitempty,flow"`
|
|
Kernel *kernel.Kernel `yaml:"kernel,omitempty,flow"`
|
|
PostReboot bool `yaml:"postReboot,omitempty,flow"`
|
|
SwupdMirror string `yaml:"swupdMirror,omitempty,flow"`
|
|
AllowInsecureHTTP bool `yaml:"allowInsecureHTTP,omitempty,flow"`
|
|
SwupdSkipOptional bool `yaml:"swupdSkipOptional,omitempty,flow"`
|
|
PostArchive *boolset.BoolSet `yaml:"postArchive,omitempty,flow"`
|
|
Hostname string `yaml:"hostname,omitempty,flow"`
|
|
AutoUpdate *boolset.BoolSet `yaml:"autoUpdate,flow"`
|
|
TelemetryURL string `yaml:"telemetryURL,omitempty,flow"`
|
|
TelemetryTID string `yaml:"telemetryTID,omitempty,flow"`
|
|
TelemetryPolicy string `yaml:"telemetryPolicy,omitempty,flow"`
|
|
PreInstall []*InstallHook `yaml:"pre-install,omitempty,flow"`
|
|
PostInstall []*InstallHook `yaml:"post-install,omitempty,flow"`
|
|
PostImage []*InstallHook `yaml:"post-image,omitempty,flow"`
|
|
SwupdFormat string `yaml:"swupdFormat,omitempty,flow"`
|
|
Version uint `yaml:"version,omitempty,flow"`
|
|
StorageAlias []*StorageAlias `yaml:"block-devices,omitempty,flow"`
|
|
CopyNetwork bool `yaml:"copyNetwork,omitempty,flow"`
|
|
CopySwupd bool `yaml:"copySwupd,omitempty,flow"`
|
|
Environment map[string]string `yaml:"env,omitempty,flow"`
|
|
CryptPass string `yaml:"-"`
|
|
MakeISO bool `yaml:"iso,omitempty,flow"`
|
|
ISOPublisher string `yaml:"isoPublisher,omitempty,flow"`
|
|
ISOApplicationID string `yaml:"isoApplicationId,omitempty,flow"`
|
|
KeepImage bool `yaml:"keepImage,omitempty,flow"`
|
|
LockFile string `yaml:"-"`
|
|
ClearCfFile string `yaml:"-"`
|
|
PreCheckDone bool `yaml:"preCheckDone,omitempty,flow"`
|
|
MediaOpts storage.MediaOpts `yaml:",inline"`
|
|
}
|
|
|
|
// SystemUsage is used to include additional information into the telemetry payload
|
|
type SystemUsage struct {
|
|
InstallModel SystemInstall `yaml:",inline"`
|
|
Hypervisor string `yaml:"hypervisor,omitempty,flow"`
|
|
}
|
|
|
|
// InstallHook is a commands to be executed in a given point of the install process
|
|
type InstallHook struct {
|
|
Chroot bool `yaml:"chroot,omitempty,flow"`
|
|
Cmd string `yaml:"cmd,omitempty,flow"`
|
|
}
|
|
|
|
// StorageAlias is used to expand variables in the targetMedia definitions
|
|
// a partition's block device name attribute could be declared in the form of:
|
|
//
|
|
// Name: ${alias}p1
|
|
//
|
|
// where ${alias} was previously declared pointing to a block device file such as:
|
|
// block-devices : [
|
|
//
|
|
// {name: "alias", file: "/dev/nvme0n1"}
|
|
//
|
|
// ]
|
|
type StorageAlias struct {
|
|
Name string `yaml:"name,omitempty,flow"`
|
|
File string `yaml:"file,omitempty,flow"`
|
|
DeviceFile bool `yaml:"devicefile,omitempty,flow"`
|
|
}
|
|
|
|
// InitializeDefaults ensure defaults are set such
|
|
// as initializing pointer members
|
|
func (si *SystemInstall) InitializeDefaults() {
|
|
// Default to archiving by default
|
|
if si.PostArchive == nil {
|
|
si.PostArchive = boolset.NewTrue()
|
|
} else {
|
|
si.PostArchive.SetDefault(true)
|
|
}
|
|
|
|
// Default to Auto Updating enabled by default
|
|
if si.AutoUpdate == nil {
|
|
si.AutoUpdate = boolset.NewTrue()
|
|
} else {
|
|
si.AutoUpdate.SetDefault(true)
|
|
}
|
|
}
|
|
|
|
// ClearInstallSelected clears the map of Installation Selected targets
|
|
func (si *SystemInstall) ClearInstallSelected() {
|
|
si.InstallSelected = map[string]storage.InstallTarget{}
|
|
}
|
|
|
|
// ClearExtraKernelArguments clears all of the of custom extra kernel arguments
|
|
func (si *SystemInstall) ClearExtraKernelArguments() {
|
|
if si.KernelArguments == nil {
|
|
si.KernelArguments = &kernel.Arguments{}
|
|
}
|
|
|
|
si.KernelArguments.Add = []string{}
|
|
}
|
|
|
|
// AddExtraKernelArguments adds a set of custom extra kernel arguments to be added to the
|
|
// clr-boot-manager configuration
|
|
func (si *SystemInstall) AddExtraKernelArguments(args []string) {
|
|
if si.KernelArguments == nil {
|
|
si.KernelArguments = &kernel.Arguments{}
|
|
}
|
|
|
|
for _, curr := range args {
|
|
if utils.StringSliceContains(si.KernelArguments.Add, curr) {
|
|
continue
|
|
}
|
|
|
|
si.KernelArguments.Add = append(si.KernelArguments.Add, curr)
|
|
}
|
|
}
|
|
|
|
// ClearRemoveKernelArguments clears all of the of custom remove kernel arguments
|
|
func (si *SystemInstall) ClearRemoveKernelArguments() {
|
|
if si.KernelArguments == nil {
|
|
si.KernelArguments = &kernel.Arguments{}
|
|
}
|
|
|
|
si.KernelArguments.Remove = []string{}
|
|
}
|
|
|
|
// RemoveKernelArguments adds a set of kernel arguments to be "black listed" on
|
|
// clear-boot-manager, meaning these arguments will never end up in the boot manager
|
|
// entry configuration
|
|
func (si *SystemInstall) RemoveKernelArguments(args []string) {
|
|
if si.KernelArguments == nil {
|
|
si.KernelArguments = &kernel.Arguments{}
|
|
}
|
|
|
|
for _, curr := range args {
|
|
if utils.StringSliceContains(si.KernelArguments.Remove, curr) {
|
|
continue
|
|
}
|
|
|
|
si.KernelArguments.Remove = append(si.KernelArguments.Remove, curr)
|
|
}
|
|
}
|
|
|
|
// ContainsBundle returns true if the data model has a bundle and false otherwise
|
|
func (si *SystemInstall) ContainsBundle(bundle string) bool {
|
|
for _, curr := range si.Bundles {
|
|
if curr == bundle {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// RemoveBundle removes a bundle from the data model
|
|
func (si *SystemInstall) RemoveBundle(bundle string) {
|
|
bundles := []string{}
|
|
|
|
for _, curr := range si.Bundles {
|
|
if curr != bundle {
|
|
bundles = append(bundles, curr)
|
|
}
|
|
}
|
|
|
|
si.Bundles = bundles
|
|
}
|
|
|
|
// AddBundle adds a new bundle to the data model, we make sure to not duplicate entries
|
|
func (si *SystemInstall) AddBundle(bundle string) {
|
|
for _, curr := range si.Bundles {
|
|
if curr == bundle {
|
|
return
|
|
}
|
|
}
|
|
|
|
si.Bundles = append(si.Bundles, bundle)
|
|
}
|
|
|
|
// ContainsUserBundle returns true if the data model has a user bundle and false otherwise
|
|
func (si *SystemInstall) ContainsUserBundle(bundle string) bool {
|
|
for _, curr := range si.UserBundles {
|
|
if curr == bundle {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// RemoveUserBundle removes a user bundle from the data model
|
|
func (si *SystemInstall) RemoveUserBundle(bundle string) {
|
|
bundles := []string{}
|
|
|
|
for _, curr := range si.UserBundles {
|
|
if curr != bundle {
|
|
bundles = append(bundles, curr)
|
|
}
|
|
}
|
|
|
|
si.UserBundles = bundles
|
|
}
|
|
|
|
// AddUserBundle adds a new user bundle to the data model, we make sure to not duplicate entries
|
|
func (si *SystemInstall) AddUserBundle(bundle string) {
|
|
for _, curr := range si.UserBundles {
|
|
if curr == bundle {
|
|
return
|
|
}
|
|
}
|
|
|
|
si.UserBundles = append([]string{bundle}, si.UserBundles...)
|
|
}
|
|
|
|
// OverrideBundles replaces the current bundles with the override list
|
|
// Sets the kernel to one of the kernel bundles, the rest remain in the list
|
|
func (si *SystemInstall) OverrideBundles(overrideBundles []string) {
|
|
var kernelBundles []string
|
|
si.Bundles = []string{} // Clear any existing bundles
|
|
|
|
for _, bundle := range overrideBundles {
|
|
if strings.HasPrefix(bundle, "kernel-") {
|
|
kernelBundles = append(kernelBundles, bundle)
|
|
continue
|
|
}
|
|
si.Bundles = append(si.Bundles, bundle) // Set Bundles
|
|
}
|
|
|
|
// Sort the kernels as we want one that had the CDROM loadable
|
|
// kernel modules by default: native or LTS
|
|
sort.Sort(sort.Reverse(sort.StringSlice(kernelBundles)))
|
|
|
|
var foundKernel bool
|
|
for _, bundle := range kernelBundles {
|
|
// Only use the first kernel bundle
|
|
if !foundKernel {
|
|
foundKernel = true
|
|
si.Kernel = &kernel.Kernel{Bundle: bundle}
|
|
} else {
|
|
si.Bundles = append(si.Bundles, bundle) // Add extra kernel bundles
|
|
fmt.Printf("WARNING: Extra kernel bundle '%s' detected; '%s' already in use.\n",
|
|
bundle, si.Kernel.Bundle)
|
|
}
|
|
}
|
|
}
|
|
|
|
// IsTargetDesktopInstall determines if this installation is a Desktop
|
|
// installation by check all bundle lists for any desktop bundles.
|
|
func (si *SystemInstall) IsTargetDesktopInstall() bool {
|
|
isDesktop := false
|
|
|
|
// Check the default bundle list
|
|
for _, curr := range si.Bundles {
|
|
if strings.Contains(strings.ToLower(curr), "desktop") {
|
|
isDesktop = true
|
|
break
|
|
}
|
|
}
|
|
|
|
if !isDesktop {
|
|
// Check the user bundle list
|
|
for _, curr := range si.UserBundles {
|
|
if strings.Contains(strings.ToLower(curr), "desktop") {
|
|
isDesktop = true
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
return isDesktop
|
|
}
|
|
|
|
// RemoveAllUsers remove from the data model all previously added user
|
|
func (si *SystemInstall) RemoveAllUsers() {
|
|
si.Users = []*user.User{}
|
|
}
|
|
|
|
// AddUser adds a new user to the data model, this function also prevents duplicate entries
|
|
func (si *SystemInstall) AddUser(usr *user.User) {
|
|
for _, curr := range si.Users {
|
|
if curr.Equals(usr) {
|
|
return
|
|
}
|
|
}
|
|
|
|
si.Users = append(si.Users, usr)
|
|
}
|
|
|
|
// EncryptionRequiresPassphrase checks all partition to see if encryption was enabled
|
|
func (si *SystemInstall) EncryptionRequiresPassphrase(isAdvanced bool) bool {
|
|
enabled := false
|
|
|
|
for _, curr := range si.TargetMedias {
|
|
enabled = enabled || curr.EncryptionRequiresPassphrase(isAdvanced)
|
|
}
|
|
|
|
return enabled
|
|
}
|
|
|
|
// Validate checks the model for possible inconsistencies or "minimum required"
|
|
// information
|
|
func (si *SystemInstall) Validate() error {
|
|
// si will be nil if we fail to unmarshall (coverage tests has a case for that)
|
|
if si == nil {
|
|
return errors.ValidationErrorf("model is nil")
|
|
}
|
|
|
|
if si.TargetMedias == nil || len(si.TargetMedias) == 0 {
|
|
return errors.ValidationErrorf("System Installation must provide a target media")
|
|
}
|
|
|
|
var results []string
|
|
if si.IsTargetDesktopInstall() {
|
|
results = storage.DesktopValidatePartitions(si.TargetMedias, si.MediaOpts)
|
|
} else {
|
|
results = storage.ServerValidatePartitions(si.TargetMedias, si.MediaOpts)
|
|
}
|
|
if len(results) > 0 && !si.MediaOpts.SkipValidationAll {
|
|
return errors.ValidationErrorf(strings.Join(results, ", "))
|
|
}
|
|
|
|
if si.Timezone == nil {
|
|
return errors.ValidationErrorf("Timezone not set")
|
|
}
|
|
|
|
if si.Keyboard == nil {
|
|
return errors.ValidationErrorf("Keyboard not set")
|
|
}
|
|
|
|
if si.Language == nil {
|
|
return errors.ValidationErrorf("System Language not set")
|
|
}
|
|
|
|
if si.Telemetry == nil {
|
|
return errors.ValidationErrorf("Telemetry not acknowledged")
|
|
}
|
|
|
|
if si.Kernel == nil {
|
|
return errors.ValidationErrorf("A kernel must be provided")
|
|
}
|
|
|
|
if len(si.ISOPublisher) > 128 {
|
|
return errors.ValidationErrorf("isoPublisher must be shorter than 128 characters")
|
|
}
|
|
|
|
if len(si.ISOApplicationID) > 128 {
|
|
return errors.ValidationErrorf("isoApplicationId must be shorter than 128 characters")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// AddTargetMedia adds a BlockDevice instance to the list of TargetMedias
|
|
// if bd was previously added to as a target media its pointer is updated
|
|
func (si *SystemInstall) AddTargetMedia(bd *storage.BlockDevice) {
|
|
if si.TargetMedias == nil {
|
|
si.TargetMedias = []*storage.BlockDevice{}
|
|
}
|
|
|
|
nList := []*storage.BlockDevice{bd}
|
|
|
|
for _, curr := range si.TargetMedias {
|
|
if !bd.Equals(curr) {
|
|
nList = append(nList, curr)
|
|
}
|
|
}
|
|
|
|
si.TargetMedias = nList
|
|
}
|
|
|
|
// AddNetworkInterface adds an Interface instance to the list of NetworkInterfaces
|
|
func (si *SystemInstall) AddNetworkInterface(iface *network.Interface) {
|
|
if si.NetworkInterfaces == nil {
|
|
si.NetworkInterfaces = []*network.Interface{}
|
|
}
|
|
|
|
si.NetworkInterfaces = append(si.NetworkInterfaces, iface)
|
|
}
|
|
|
|
// LoadFile loads a model from a yaml file pointed by path
|
|
func LoadFile(path string, options args.Args) (*SystemInstall, error) {
|
|
var result SystemInstall
|
|
|
|
if _, err := os.Stat(path); err == nil {
|
|
configStr, err := ioutil.ReadFile(path)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err)
|
|
}
|
|
|
|
err = yaml.UnmarshalStrict(configStr, &result)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err)
|
|
}
|
|
}
|
|
|
|
result.InitializeDefaults()
|
|
|
|
// Set default Timezone if not defined
|
|
if result.Timezone == nil {
|
|
result.Timezone = &timezone.TimeZone{Code: timezone.DefaultTimezone}
|
|
}
|
|
|
|
// Set default Keyboard if not defined
|
|
if result.Keyboard == nil {
|
|
result.Keyboard = &keyboard.Keymap{Code: keyboard.DefaultKeyboard}
|
|
}
|
|
|
|
// Set default Language if not defined
|
|
if result.Language == nil {
|
|
result.Language = &language.Language{Code: language.DefaultLanguage}
|
|
}
|
|
|
|
// Running in VirtualBox force the default to 'kernel-lts' if
|
|
// we are using the system default configuration file
|
|
// See https://github.com/clearlinux/clr-installer/issues/203
|
|
if options.ConfigFile == "" && utils.IsVirtualBox() {
|
|
result.Kernel = &kernel.Kernel{Bundle: "kernel-lts"}
|
|
}
|
|
|
|
tmp := map[string]*StorageAlias{}
|
|
|
|
for _, bds := range result.StorageAlias {
|
|
tmp[bds.Name] = bds
|
|
}
|
|
|
|
for _, bds := range options.BlockDevices {
|
|
var tks []string
|
|
|
|
if tks = strings.Split(bds, ":"); len(tks) < 2 {
|
|
continue
|
|
}
|
|
|
|
tmp[tks[0]] = &StorageAlias{Name: tks[0], File: tks[1]}
|
|
}
|
|
|
|
result.StorageAlias = []*StorageAlias{}
|
|
|
|
for _, bds := range tmp {
|
|
result.StorageAlias = append(result.StorageAlias, bds)
|
|
}
|
|
|
|
if len(result.StorageAlias) > 0 {
|
|
alias := map[string]string{}
|
|
keepMe := []*StorageAlias{}
|
|
|
|
for _, curr := range result.StorageAlias {
|
|
if !isAliasInUse(result.TargetMedias, curr) {
|
|
continue
|
|
}
|
|
|
|
fi, err := os.Lstat(curr.File)
|
|
inTestAlias := isTestAlias(curr.File)
|
|
|
|
// could be an image file to be created so we fail only if the error doesn't
|
|
// indicate the image file doesn't exist
|
|
if err != nil && !inTestAlias && !os.IsNotExist(err) {
|
|
return nil, errors.Wrap(err)
|
|
}
|
|
|
|
keepMe = append(keepMe, curr)
|
|
|
|
if !inTestAlias && os.IsNotExist(err) {
|
|
continue
|
|
}
|
|
|
|
if (fi != nil && fi.Mode()&os.ModeDevice == 0) && !inTestAlias {
|
|
continue
|
|
}
|
|
|
|
curr.DeviceFile = true
|
|
alias[curr.Name] = filepath.Base(curr.File)
|
|
}
|
|
|
|
// keep only the aliases we're using
|
|
result.StorageAlias = keepMe
|
|
|
|
for _, bd := range result.TargetMedias {
|
|
bd.ExpandName(alias)
|
|
}
|
|
}
|
|
|
|
if result.MediaOpts.SwapFileSize != "" {
|
|
result.MediaOpts.SwapFileSet = true
|
|
}
|
|
|
|
return &result, nil
|
|
}
|
|
|
|
func isAliasInUse(bds []*storage.BlockDevice, alias *StorageAlias) bool {
|
|
for _, curr := range bds {
|
|
rep := fmt.Sprintf("${%s}", alias.Name)
|
|
|
|
if strings.Contains(curr.Name, rep) {
|
|
return true
|
|
}
|
|
|
|
if isAliasInUse(curr.Children, alias) {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
func isTestAlias(file string) bool {
|
|
if len(testAlias) == 0 {
|
|
return false
|
|
}
|
|
|
|
return utils.StringSliceContains(testAlias, file)
|
|
}
|
|
|
|
// EnableTelemetry operates on the telemetry flag and enables or disables the target
|
|
// systems telemetry support based in enable argument
|
|
func (si *SystemInstall) EnableTelemetry(enable bool) {
|
|
if si.Telemetry == nil {
|
|
si.Telemetry = &telemetry.Telemetry{}
|
|
}
|
|
|
|
si.Telemetry.SetEnable(enable)
|
|
}
|
|
|
|
// IsTelemetryEnabled returns true if telemetry is enabled, false otherwise
|
|
func (si *SystemInstall) IsTelemetryEnabled() bool {
|
|
if si.Telemetry == nil {
|
|
return false
|
|
}
|
|
|
|
return si.Telemetry.Enabled
|
|
}
|
|
|
|
// IsTelemetryInstalled return true if telemetry tooling is present, false otherwise
|
|
func (si *SystemInstall) IsTelemetryInstalled() bool {
|
|
return si.Telemetry.Installed("")
|
|
}
|
|
|
|
// WriteFile writes a yaml formatted representation of si into the provided file path
|
|
func (si *SystemInstall) WriteFile(path string) error {
|
|
// Sanitized the model to item which should never be written
|
|
var copyModel SystemInstall
|
|
|
|
// Marshal current into bytes
|
|
confBytes, bytesErr := yaml.Marshal(si)
|
|
if bytesErr != nil {
|
|
return errors.Wrap(bytesErr)
|
|
}
|
|
|
|
// Unmarshal into a copy
|
|
if yamlErr := yaml.UnmarshalStrict(confBytes, ©Model); yamlErr != nil {
|
|
return errors.Wrap(bytesErr)
|
|
}
|
|
|
|
f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
defer func() {
|
|
_ = f.Close()
|
|
}()
|
|
|
|
// Screen item we never want stored in a YAML
|
|
// SkipValidation flags are okay for ready, but we
|
|
// never want to store them -- force use to set them
|
|
// Setting to default means they are omitted
|
|
copyModel.MediaOpts.SkipValidationAll = false
|
|
copyModel.MediaOpts.SkipValidationSize = false
|
|
|
|
b, err := yaml.Marshal(copyModel)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Write our header
|
|
_, err = f.WriteString("#clear-linux-config\n")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// Write our version
|
|
_, err = f.WriteString("#generated by clr-installer:" + Version + "\n")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// Write datetime stamp
|
|
t := time.Now().UTC()
|
|
_, err = f.WriteString("#generated on: " + fmt.Sprintf("%d-%02d-%02d_%02d:%02d:%02d_UTC\n",
|
|
t.Year(), t.Month(), t.Day(),
|
|
t.Hour(), t.Minute(), t.Second()))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
_, err = f.Write(b)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// WriteScrubModelTargetMedias writes out a copy the model with the
|
|
// TargetMedias removed to a temporary file
|
|
func (si *SystemInstall) WriteScrubModelTargetMedias() (string, error) {
|
|
// Sanitized the model to remove media
|
|
var cleanModel SystemInstall
|
|
|
|
// Marshal current into bytes
|
|
confBytes, bytesErr := yaml.Marshal(si)
|
|
if bytesErr != nil {
|
|
return "", errors.Wrap(bytesErr)
|
|
}
|
|
|
|
// Unmarshal into a copy
|
|
if yamlErr := yaml.UnmarshalStrict(confBytes, &cleanModel); yamlErr != nil {
|
|
return "", errors.Wrap(bytesErr)
|
|
}
|
|
|
|
// Sanitize the config data to remove any potential
|
|
// Remove the target media
|
|
cleanModel.TargetMedias = nil
|
|
|
|
// Remove the Media swapfile if set as we might return with a swap partition
|
|
cleanModel.MediaOpts.SwapFileSize = ""
|
|
|
|
tmpYaml, err := ioutil.TempFile("", "clr-installer-noMedia-*.yaml")
|
|
if err != nil {
|
|
return "", errors.Errorf("Could not make YAML tempfile: %v", err)
|
|
}
|
|
|
|
if saveErr := cleanModel.WriteFile(tmpYaml.Name()); saveErr != nil {
|
|
return "", errors.Errorf("Could not save config to %s", tmpYaml.Name())
|
|
}
|
|
|
|
return tmpYaml.Name(), nil
|
|
}
|
|
|
|
// InteractiveOptionsValid ensures that options which are not appropriate
|
|
// for interactive runs are screened
|
|
func (si *SystemInstall) InteractiveOptionsValid() error {
|
|
if si.Offline {
|
|
return fmt.Errorf("Incompatible flag '--offline' for the interactive installer")
|
|
}
|
|
if si.MakeISO {
|
|
return fmt.Errorf("Incompatible flag '--iso' for the interactive installer")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// SetDefaultSwapFileSize defines the swapfile sized based on
|
|
// the storage default swapfile size
|
|
func (si *SystemInstall) SetDefaultSwapFileSize() {
|
|
if si.MediaOpts.SwapFileSize == "" {
|
|
si.MediaOpts.SwapFileSize, _ = storage.HumanReadableSizeXiBWithPrecision(storage.SwapFileSizeDefault, 1)
|
|
}
|
|
}
|
|
|
|
// ResetDefaultSwapFileSize clears the swapfile size unless it
|
|
// has been explicitly set by the YAML or command line
|
|
func (si *SystemInstall) ResetDefaultSwapFileSize() {
|
|
if !si.MediaOpts.SwapFileSet {
|
|
si.MediaOpts.SwapFileSize = ""
|
|
}
|
|
}
|