kairos-agent/pkg/uki/common.go
Dimitris Karakasilis 046f6879ff
Skip dirs in copyArtifactSetRole and replace only basename
otherwise we get the error:

```
panic: open /efi/EFI/kairos/passive.efi.extra.d: is a directory
```

and we may replace parts of the path that we shouldn't.

Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2024-06-18 14:09:14 +03:00

169 lines
4.4 KiB
Go

package uki
import (
"errors"
"fmt"
"io"
"os"
"path/filepath"
"strings"
"github.com/kairos-io/kairos-agent/v2/pkg/constants"
v1 "github.com/kairos-io/kairos-agent/v2/pkg/types/v1"
fsutils "github.com/kairos-io/kairos-agent/v2/pkg/utils/fs"
sdkTypes "github.com/kairos-io/kairos-sdk/types"
sdkutils "github.com/kairos-io/kairos-sdk/utils"
"github.com/sanity-io/litter"
)
const UnassignedArtifactRole = "norole"
// overwriteArtifactSetRole first deletes all artifacts prefixed with newRole
// (because they are going to be replaced, e.g. old "passive") and then installs
// the artifacts prefixed with oldRole as newRole.
// E.g. removes "passive" and moved "active" to "passive"
// This is a step that should happen before a new passive is installed on upgrades.
func overwriteArtifactSetRole(fs v1.FS, dir, oldRole, newRole string, logger sdkTypes.KairosLogger) error {
if err := removeArtifactSetWithRole(fs, dir, newRole); err != nil {
return fmt.Errorf("deleting role %s: %w", newRole, err)
}
if err := copyArtifactSetRole(fs, dir, oldRole, newRole, logger); err != nil {
return fmt.Errorf("copying artifact set role from %s to %s: %w", oldRole, newRole, err)
}
return nil
}
// copy the source file but rename the base name to as
func copyArtifact(fs v1.FS, source, oldRole, newRole string) (string, error) {
dir := filepath.Dir(source)
base := filepath.Base(source)
// Replace the substring in the base name
newBase := strings.ReplaceAll(base, oldRole, newRole)
// Join the directory and the new base name
newName := filepath.Join(dir, newBase)
return newName, fsutils.Copy(fs, source, newName)
}
func removeArtifactSetWithRole(fs v1.FS, artifactDir, role string) error {
return fsutils.WalkDirFs(fs, artifactDir, func(path string, info os.DirEntry, err error) error {
if !info.IsDir() && strings.HasPrefix(info.Name(), role) {
return os.Remove(path)
}
return nil
})
}
func copyArtifactSetRole(fs v1.FS, artifactDir, oldRole, newRole string, logger sdkTypes.KairosLogger) error {
return fsutils.WalkDirFs(fs, artifactDir, func(path string, info os.DirEntry, err error) error {
if err != nil {
return err
}
if info.IsDir() {
return nil
}
if !strings.HasPrefix(info.Name(), oldRole) {
return nil
}
newPath, err := copyArtifact(fs, path, oldRole, newRole)
if err != nil {
return fmt.Errorf("copying artifact from %s to %s: %w", path, newPath, err)
}
if strings.HasSuffix(path, ".conf") {
if err := replaceRoleInKey(newPath, "efi", oldRole, newRole, logger); err != nil {
return err
}
if err := replaceConfTitle(newPath, newRole); err != nil {
return err
}
}
return nil
})
}
func replaceRoleInKey(path, key, oldRole, newRole string, logger sdkTypes.KairosLogger) (err error) {
// Extract the values
conf, err := sdkutils.SystemdBootConfReader(path)
if err != nil {
logger.Errorf("Error reading conf file %s: %s", path, err)
return err
}
logger.Debugf("Conf file %s has values %v", path, litter.Sdump(conf))
_, hasKey := conf[key]
if !hasKey {
return fmt.Errorf("no %s entry in .conf file", key)
}
conf[key] = strings.ReplaceAll(conf[key], oldRole, newRole)
newContents := ""
for k, v := range conf {
newContents = fmt.Sprintf("%s%s %s\n", newContents, k, v)
}
logger.Debugf("Conf file %s new values %v", path, litter.Sdump(conf))
return os.WriteFile(path, []byte(newContents), os.ModePerm)
}
func replaceConfTitle(path, role string) error {
conf, err := sdkutils.SystemdBootConfReader(path)
if err != nil {
return err
}
if len(conf["title"]) == 0 {
return errors.New("no title in .conf file")
}
newTitle, err := constants.BootTitleForRole(role, conf["title"])
if err != nil {
return err
}
conf["title"] = newTitle
newContents := ""
for k, v := range conf {
newContents = fmt.Sprintf("%s%s %s\n", newContents, k, v)
}
return os.WriteFile(path, []byte(newContents), os.ModePerm)
}
func copyFile(src, dst string) error {
sourceFile, err := os.Open(src)
if err != nil {
panic(err)
}
defer sourceFile.Close()
destinationFile, err := os.Create(dst)
if err != nil {
panic(err)
}
defer destinationFile.Close()
if _, err = io.Copy(destinationFile, sourceFile); err != nil {
return err
}
// Flushes any buffered data to the destination file
if err = destinationFile.Sync(); err != nil {
return err
}
if err = sourceFile.Close(); err != nil {
return err
}
return destinationFile.Close()
}