mirror of
https://github.com/kairos-io/kairos-agent.git
synced 2025-07-18 18:21:07 +00:00
221 lines
4.6 KiB
Go
221 lines
4.6 KiB
Go
|
package machine
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"io/ioutil"
|
||
|
"os"
|
||
|
"strings"
|
||
|
|
||
|
"github.com/c3os-io/c3os/internal/utils"
|
||
|
"github.com/hashicorp/go-multierror"
|
||
|
)
|
||
|
|
||
|
type BundleConfig struct {
|
||
|
Target string
|
||
|
Repository string
|
||
|
DBPath string
|
||
|
RootPath string
|
||
|
}
|
||
|
|
||
|
// bundles needs to ship only /config/... (etc) and /bin/... (will go to /usr/local/bin)
|
||
|
type BundleOption func(bc *BundleConfig) error
|
||
|
|
||
|
func (bc *BundleConfig) Apply(opts ...BundleOption) error {
|
||
|
for _, o := range opts {
|
||
|
if err := o(bc); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func WithDBHPath(r string) BundleOption {
|
||
|
return func(bc *BundleConfig) error {
|
||
|
bc.DBPath = r
|
||
|
return nil
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func WithRootFS(r string) BundleOption {
|
||
|
return func(bc *BundleConfig) error {
|
||
|
bc.RootPath = r
|
||
|
return nil
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func WithRepository(r string) BundleOption {
|
||
|
return func(bc *BundleConfig) error {
|
||
|
bc.Repository = r
|
||
|
return nil
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func WithTarget(p string) BundleOption {
|
||
|
return func(bc *BundleConfig) error {
|
||
|
bc.Target = p
|
||
|
return nil
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (bc *BundleConfig) extractRepo() (string, string) {
|
||
|
s := strings.Split(bc.Repository, ":")
|
||
|
return s[0], s[1]
|
||
|
}
|
||
|
|
||
|
// XXX: directly to rootfs ? or maybe better to /usr/local/.c3os/rootfs and handle symlinks?
|
||
|
func defaultConfig() *BundleConfig {
|
||
|
return &BundleConfig{
|
||
|
DBPath: "/usr/local/.c3os/db",
|
||
|
RootPath: "/",
|
||
|
Repository: "quay.io/c3os/packages",
|
||
|
}
|
||
|
}
|
||
|
|
||
|
type BundleInstaller interface {
|
||
|
Install(*BundleConfig) error
|
||
|
}
|
||
|
|
||
|
// TODO:
|
||
|
// - Make provider consume bundles when bins are not detected in the rootfs
|
||
|
// - Default bundles preset in case of no binaries detected and version specified via config.
|
||
|
|
||
|
func RunBundles(bundles ...[]BundleOption) error {
|
||
|
var resErr error
|
||
|
for _, b := range bundles {
|
||
|
config := defaultConfig()
|
||
|
config.Apply(b...)
|
||
|
|
||
|
installer, err := NewBundleInstaller(*config)
|
||
|
if err != nil {
|
||
|
resErr = multierror.Append(err)
|
||
|
continue
|
||
|
}
|
||
|
dat := strings.Split(config.Target, ":")
|
||
|
if len(dat) != 2 {
|
||
|
resErr = multierror.Append(fmt.Errorf("invalid target"))
|
||
|
|
||
|
continue
|
||
|
}
|
||
|
config.Target = dat[1]
|
||
|
|
||
|
err = installer.Install(config)
|
||
|
if err != nil {
|
||
|
resErr = multierror.Append(err)
|
||
|
continue
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return resErr
|
||
|
}
|
||
|
|
||
|
func NewBundleInstaller(bc BundleConfig) (BundleInstaller, error) {
|
||
|
|
||
|
dat := strings.Split(bc.Target, ":")
|
||
|
if len(dat) != 2 {
|
||
|
return nil, fmt.Errorf("could not decode scheme")
|
||
|
}
|
||
|
switch strings.ToLower(dat[0]) {
|
||
|
case "container":
|
||
|
return &ContainerInstaller{}, nil
|
||
|
case "run":
|
||
|
return &ContainerRunner{}, nil
|
||
|
case "package":
|
||
|
return &LuetInstaller{}, nil
|
||
|
|
||
|
}
|
||
|
|
||
|
return &LuetInstaller{}, nil
|
||
|
}
|
||
|
|
||
|
// BundleInstall installs a bundle from a luet repo or a container image
|
||
|
type ContainerRunner struct{}
|
||
|
|
||
|
func (l *ContainerRunner) Install(config *BundleConfig) error {
|
||
|
|
||
|
tempDir, err := ioutil.TempDir("", "containerrunner")
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
defer os.RemoveAll(tempDir)
|
||
|
|
||
|
out, err := utils.SH(
|
||
|
fmt.Sprintf(
|
||
|
`luet util unpack %s %s`,
|
||
|
config.Target,
|
||
|
tempDir,
|
||
|
),
|
||
|
)
|
||
|
if err != nil {
|
||
|
return fmt.Errorf("could not unpack container: %w - %s", err, out)
|
||
|
}
|
||
|
|
||
|
out, err = utils.SH(fmt.Sprintf("CONTAINERDIR=%s %s/run.sh", tempDir, tempDir))
|
||
|
if err != nil {
|
||
|
return fmt.Errorf("could not execute container: %w - %s", err, out)
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
type ContainerInstaller struct{}
|
||
|
|
||
|
func (l *ContainerInstaller) Install(config *BundleConfig) error {
|
||
|
|
||
|
//mkdir -p test/etc/luet/repos.conf.d
|
||
|
_, err := utils.SH(
|
||
|
fmt.Sprintf(
|
||
|
`luet util unpack %s %s`,
|
||
|
config.Target,
|
||
|
config.RootPath,
|
||
|
),
|
||
|
)
|
||
|
if err != nil {
|
||
|
return fmt.Errorf("could not add repository: %w", err)
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
type LuetInstaller struct{}
|
||
|
|
||
|
func (l *LuetInstaller) Install(config *BundleConfig) error {
|
||
|
|
||
|
t, repo := config.extractRepo()
|
||
|
//mkdir -p test/etc/luet/repos.conf.d
|
||
|
_, err := utils.SH(
|
||
|
fmt.Sprintf(
|
||
|
`LUET_CONFIG_FROM_HOST=false luet repo add --system-dbpath %s --system-target %s c3os-system -y --description "Automatically generated c3os-system" --url "%s" --type "%s"`,
|
||
|
config.DBPath,
|
||
|
config.RootPath,
|
||
|
repo,
|
||
|
t,
|
||
|
),
|
||
|
)
|
||
|
if err != nil {
|
||
|
return fmt.Errorf("could not add repository: %w", err)
|
||
|
}
|
||
|
_, err = utils.SH(
|
||
|
fmt.Sprintf(
|
||
|
`LUET_CONFIG_FROM_HOST=false luet repo update -f --system-dbpath %s --system-target %s`,
|
||
|
config.DBPath,
|
||
|
config.RootPath,
|
||
|
),
|
||
|
)
|
||
|
if err != nil {
|
||
|
return fmt.Errorf("could not sync repository: %w", err)
|
||
|
}
|
||
|
_, err = utils.SH(
|
||
|
fmt.Sprintf(
|
||
|
`LUET_CONFIG_FROM_HOST=false luet install -y --system-dbpath %s --system-target %s %s`,
|
||
|
config.DBPath,
|
||
|
config.RootPath,
|
||
|
config.Target,
|
||
|
),
|
||
|
)
|
||
|
if err != nil {
|
||
|
return fmt.Errorf("could not sync repository: %w", err)
|
||
|
}
|
||
|
|
||
|
// copy bins to /usr/local/bin
|
||
|
return nil
|
||
|
}
|