Skip to content

Commit

Permalink
elemental: bump elemental-cli to v0.1.0
Browse files Browse the repository at this point in the history
We cannot use Elemental-cli InstallSpec struct to generate config.yaml.
Because there are not all entry with "omitempty", it will cause
generated config.yaml will have several empty entry but didn't be
omitted.
Elemental-cli will treat those empty entry as user-specific value and
override the default value.

Use ElementalInstallSpec to ensure that all entry will be "omitempty".

Signed-off-by: Date Huang <date.huang@suse.com>
  • Loading branch information
Date Huang authored and bk201 committed Dec 28, 2022
1 parent cb666a0 commit cf08151
Show file tree
Hide file tree
Showing 5 changed files with 136 additions and 83 deletions.
2 changes: 1 addition & 1 deletion package/harvester-os/files/usr/sbin/cos-installer-shutdown
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/bin/bash
set -e

if [ "$ELEMENTAL_POWER_OFF" = true ] || grep -q 'cos.install.power_off=true' /proc/cmdline; then
if [ "$HARVESTER_POWER_OFF" = true ] || grep -q 'cos.install.power_off=true' /proc/cmdline; then
poweroff -f
else
echo " * Rebooting system in 5 seconds (CTRL+C to cancel)"
Expand Down
33 changes: 12 additions & 21 deletions package/harvester-os/files/usr/sbin/harv-install
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,8 @@ TARGET=/run/cos/target
DATA_DISK_FSLABEL="HARV_LH_DEFAULT"

# Update environment variables
export ELEMENTAL_TARGET=$ELEMENTAL_DEVICE
export ELEMENTAL_ISO=$ELEMENTAL_ISO_URL
export ELEMENTAL_CLOUD_INIT=$ELEMENTAL_CONFIG_URL
export QUIET=${ELEMENTAL_SILENT:+"--quiet"}
export DEBUG=${ELEMENTAL_DEBUG:+"--debug"}
export QUIET=${HARVESTER_SILENT:+"--quiet"}
export DEBUG=${HARVESTER_DEBUG:+"--debug"}

clear_disk_label()
{
Expand Down Expand Up @@ -41,7 +38,7 @@ umount_target() {
cleanup2()
{
sync
[ -n "$ELEMENTAL_ISO" ] && umount "$ISOMNT" || true
[ -n "$HARVESTER_ISO_URL" ] && umount "$ISOMNT" || true
[ -n "$ISOTEMP" ] && rm -f "$ISOTEMP"
umount_target || true
umount ${STATEDIR}
Expand Down Expand Up @@ -96,19 +93,19 @@ get_url()
}

check_iso(){
if [ -n "$ELEMENTAL_ISO" ]; then
if [ -n "$HARVESTER_ISO_URL" ]; then
echo "Checking ISO URL.."
check_url "$ELEMENTAL_ISO"
check_url "$HARVESTER_ISO_URL"
fi
}

get_iso()
{
if [ -n "$ELEMENTAL_ISO" ]; then
if [ -n "$HARVESTER_ISO_URL" ]; then
echo "Downloading ISO.."
ISOMNT=$(mktemp -d -p /tmp cos.XXXXXXXX.isomnt)
ISOTEMP=$(mktemp -p ${TARGET}/usr/local cos.XXXXXXXX.iso)
get_url ${ELEMENTAL_ISO} ${ISOTEMP}
get_url ${HARVESTER_ISO_URL} ${ISOTEMP}
ISO_DEVICE=$(losetup --show -f $ISOTEMP)
mount -o ro ${ISO_DEVICE} ${ISOMNT}
fi
Expand Down Expand Up @@ -285,10 +282,10 @@ EOF

update_grub_settings()
{
if [ -z "${ELEMENTAL_TTY}" ]; then
if [ -z "${HARVESTER_TTY}" ]; then
TTY=$(tty | sed 's!/dev/!!')
else
TTY=$ELEMENTAL_TTY
TTY=$HARVESTER_TTY
fi

if [ -e "/dev/${TTY%,*}" ] && [ "$TTY" != tty1 ] && [ "$TTY" != console ] && [ -n "$TTY" ]; then
Expand Down Expand Up @@ -333,8 +330,8 @@ save_configs()
cp $HARVESTER_CONFIG $save_dir/harvester.config
fi

if [ -e "$ELEMENTAL_PARTITION_LAYOUT" ]; then
cp $ELEMENTAL_PARTITION_LAYOUT $save_dir/part-layout.config
if [ -e "$ELEMENTAL_CONFIG" ]; then
cp $ELEMENTAL_CONFIG $save_dir/elemental.config
fi
}

Expand Down Expand Up @@ -378,20 +375,14 @@ trap cleanup exit

check_iso

# Follow the symbolic link to the real device
install_device="$ELEMENTAL_TARGET"
if [ -L "$ELEMENTAL_TARGET" ]; then
install_device=$(readlink -f "$ELEMENTAL_TARGET")
fi

# Tear down LVM and MD devices on the system, if the installing device is occuipied, the
# partitioning operation could fail later. Be forgiven here.
blkdeactivate --lvmoptions wholevg,retry --dmoptions force,retry --errors || true

clear_disk_label

# Run elemental installer but do not let it fetch ISO and do not shutdown
ELEMENTAL_ISO="" ELEMENTAL_TARGET="$install_device" ELEMENTAL_POWEROFF="" elemental ${QUIET} ${DEBUG} install
elemental install --config-dir ${ELEMENTAL_CONFIG_DIR} ${QUIET} ${DEBUG}

# Format the data disk if needed
do_data_disk_format
Expand Down
135 changes: 84 additions & 51 deletions pkg/config/cos.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,61 @@ var (
saveOriginalNetworkConfigOnce sync.Once
)

// refer: https://github.com/rancher/elemental-cli/blob/v0.1.0/config.yaml.example
type ElementalConfig struct {
Install ElementalInstallSpec `yaml:"install,omitempty"`
}

type ElementalInstallSpec struct {
Target string `yaml:"target,omitempty"`
Firmware string `yaml:"firmware,omitempty"`
PartTable string `yaml:"part-table,omitempty"`
Partitions *ElementalDefaultPartition `yaml:"partitions,omitempty"`
ExtraPartitions []ElementalPartition `yaml:"extra-partitions,omitempty"`
CloudInit string `yaml:"cloud-init,omitempty"`
Tty string `yaml:"tty,omitempty"`
}

type ElementalDefaultPartition struct {
OEM *ElementalPartition `yaml:"oem,omitempty"`
State *ElementalPartition `yaml:"state,omitempty"`
Recovery *ElementalPartition `yaml:"recovery,omitempty"`
Persistent *ElementalPartition `yaml:"persistent,omitempty"`
}

type ElementalPartition struct {
FilesystemLabel string `yaml:"label,omitempty"`
Size uint `yaml:"size,omitempty"`
FS string `yaml:"fs,omitempty"`
}

func NewElementalConfig() *ElementalConfig {
return &ElementalConfig{}
}

func ConvertToElementalConfig(config *HarvesterConfig) (*ElementalConfig, error) {
elementalConfig := NewElementalConfig()

if config.Install.ForceEFI {
elementalConfig.Install.Firmware = "efi"
}

elementalConfig.Install.PartTable = "gpt"
if !config.Install.ForceGPT {
elementalConfig.Install.PartTable = "mbr"
}

resolvedDevPath, err := filepath.EvalSymlinks(config.Install.Device)
if err != nil {
return nil, err
}
elementalConfig.Install.Target = resolvedDevPath
elementalConfig.Install.CloudInit = config.Install.ConfigURL
elementalConfig.Install.Tty = config.Install.TTY

return elementalConfig, nil
}

// ConvertToCOS converts HarvesterConfig to cOS configuration.
func ConvertToCOS(config *HarvesterConfig) (*yipSchema.YipConfig, error) {
cfg, err := config.DeepCopy()
Expand Down Expand Up @@ -569,7 +624,7 @@ func getAddStaticDNSServersCmd(servers []string) string {
}

func (c *HarvesterConfig) ToCosInstallEnv() ([]string, error) {
return ToEnv("ELEMENTAL_", c.Install)
return ToEnv("HARVESTER_", c.Install)
}

// Returns Rancherd bootstrap resources
Expand Down Expand Up @@ -625,7 +680,7 @@ func calcCosPersistentPartSize(diskSizeGiB uint64) (uint64, error) {
}
}

func CreateRootPartitioningLayout(devPath string) (*yipSchema.YipConfig, error) {
func CreateRootPartitioningLayout(elementalConfig *ElementalConfig, devPath string) (*ElementalConfig, error) {
diskSizeBytes, err := util.GetDiskSizeBytes(devPath)
if err != nil {
return nil, err
Expand All @@ -636,58 +691,36 @@ func CreateRootPartitioningLayout(devPath string) (*yipSchema.YipConfig, error)
return nil, err
}

resolvedDevPath, err := filepath.EvalSymlinks(devPath)
if err != nil {
return nil, err
elementalConfig.Install.Partitions = &ElementalDefaultPartition{
OEM: &ElementalPartition{
FilesystemLabel: "COS_OEM",
Size: 50,
FS: "ext4",
},
State: &ElementalPartition{
FilesystemLabel: "COS_STATE",
Size: 15360,
FS: "ext4",
},
Recovery: &ElementalPartition{
FilesystemLabel: "COS_RECOVERY",
Size: 8192,
FS: "ext4",
},
Persistent: &ElementalPartition{
FilesystemLabel: "COS_PERSISTENT",
Size: uint(cosPersistentSizeGiB << 10),
FS: "ext4",
},
}

yipConfig := yipSchema.YipConfig{
Name: "Root partitioning layout",
Stages: map[string][]yipSchema.Stage{
"partitioning": {
yipSchema.Stage{
Name: "Root partitioning layout",
Layout: yipSchema.Layout{
Device: &yipSchema.Device{
Path: resolvedDevPath,
},
Parts: []yipSchema.Partition{
{
FSLabel: "COS_OEM",
PLabel: "oem",
Size: 50,
FileSystem: "ext4",
},
{
FSLabel: "COS_STATE",
PLabel: "state",
Size: 15360,
FileSystem: "ext4",
},
{
FSLabel: "COS_RECOVERY",
PLabel: "recovery",
Size: 8192,
FileSystem: "ext4",
},
{
FSLabel: "COS_PERSISTENT",
PLabel: "persistent",
Size: uint(cosPersistentSizeGiB << 10),
FileSystem: "ext4",
},
{
// Do not specify PLabel because it's hard to remove!
FSLabel: "HARV_LH_DEFAULT",
Size: 0,
FileSystem: "ext4",
},
},
},
},
},
elementalConfig.Install.ExtraPartitions = []ElementalPartition{
ElementalPartition{
FilesystemLabel: "HARV_LH_DEFAULT",
Size: 0,
FS: "ext4",
},
}

return &yipConfig, nil
return elementalConfig, nil
}
4 changes: 2 additions & 2 deletions pkg/config/schemas_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ func TestToHarvesterConfig(t *testing.T) {
input: util.LoadFixture(t, "harvester-config.yaml"),
expected: &HarvesterConfig{
SchemeVersion: SchemeVersion,
ServerURL: "https://someserver:6443",
Token: "TOKEN_VALUE",
ServerURL: "https://someserver:6443",
Token: "TOKEN_VALUE",
OS: OS{
SSHAuthorizedKeys: []string{
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQAB...",
Expand Down
45 changes: 37 additions & 8 deletions pkg/console/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"net/url"
"os"
"os/exec"
"path/filepath"
"strconv"
"strings"
"sync"
Expand Down Expand Up @@ -44,6 +45,9 @@ You can see the full installation log by:
- Run the command: less %s.
`
https = "https://"

ElementalConfigDir = "/tmp/elemental"
ElementalConfigFile = "config.yaml"
)

func newProxyClient() http.Client {
Expand Down Expand Up @@ -399,6 +403,26 @@ func printToPanelAndLog(g *gocui.Gui, panel string, logPrefix string, reader io.
}
}

func saveElementalConfig(obj interface{}) (string, string, error) {
err := os.MkdirAll(ElementalConfigDir, os.ModePerm)
if err != nil {
return "", "", err
}

bytes, err := yaml.Marshal(obj)
if err != nil {
return "", "", err
}

elementalConfigFile := filepath.Join(ElementalConfigDir, ElementalConfigFile)
err = ioutil.WriteFile(elementalConfigFile, bytes, os.ModePerm)
if err != nil {
return "", "", err
}

return ElementalConfigDir, elementalConfigFile, nil
}

func saveTemp(obj interface{}, prefix string) (string, error) {
tempFile, err := ioutil.TempFile("/tmp", fmt.Sprintf("%s.", prefix))
if err != nil {
Expand Down Expand Up @@ -443,34 +467,39 @@ func doInstall(g *gocui.Gui, hvstConfig *config.HarvesterConfig, webhooks Render
defer os.Remove(hvstConfigFile)

hvstConfig.Install.ConfigURL = cosConfigFile
elementalConfig, err := config.ConvertToElementalConfig(hvstConfig)
if err != nil {
return err
}

// provide HARVESTER_ISO_URL, DEBUG, SILENT
ev, err := hvstConfig.ToCosInstallEnv()
if err != nil {
return nil
}

env := append(os.Environ(), ev...)
env = append(env, fmt.Sprintf("HARVESTER_CONFIG=%s", hvstConfigFile))
env = append(env, fmt.Sprintf("HARVESTER_INSTALLATION_LOG=%s", defaultLogFilePath))

if hvstConfig.ShouldCreateDataPartitionOnOsDisk() {
// Use custom layout (which also creates Longhorn partition) when needed
cosPartLayout, err := config.CreateRootPartitioningLayout(hvstConfig.Install.Device)
if err != nil {
return err
}
cosPartLayoutFile, err := saveTemp(cosPartLayout, "part-layout")
elementalConfig, err = config.CreateRootPartitioningLayout(elementalConfig, hvstConfig.Install.Device)
if err != nil {
return err
}
defer os.Remove(cosPartLayoutFile)
env = append(env, fmt.Sprintf("ELEMENTAL_PARTITION_LAYOUT=%s", cosPartLayoutFile))
}

if hvstConfig.DataDisk != "" {
env = append(env, fmt.Sprintf("HARVESTER_DATA_DISK=%s", hvstConfig.DataDisk))
}

elementalConfigDir, elementalConfigFile, err := saveElementalConfig(elementalConfig)
if err != nil {
return nil
}
env = append(env, fmt.Sprintf("ELEMENTAL_CONFIG=%s", elementalConfigFile))
env = append(env, fmt.Sprintf("ELEMENTAL_CONFIG_DIR=%s", elementalConfigDir))

if err := execute(ctx, g, env, "/usr/sbin/harv-install"); err != nil {
webhooks.Handle(EventInstallFailed)
printToPanel(g, fmt.Sprintf(installFailureMessage, defaultLogFilePath), installPanel)
Expand Down

0 comments on commit cf08151

Please sign in to comment.