mirror of
https://github.com/kairos-io/kairos-agent.git
synced 2025-07-15 17:01:41 +00:00
228 lines
4.8 KiB
Go
228 lines
4.8 KiB
Go
package machine
|
|
|
|
import (
|
|
"fmt"
|
|
"io/ioutil"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/c3os-io/c3os/internal/utils"
|
|
"github.com/hashicorp/go-multierror"
|
|
)
|
|
|
|
type BundleConfig struct {
|
|
Target string
|
|
Repository string
|
|
DBPath string
|
|
RootPath string
|
|
}
|
|
|
|
// BundleOption defines a configuration option for a bundle
|
|
type BundleOption func(bc *BundleConfig) error
|
|
|
|
// Apply applies bundle options to the config
|
|
func (bc *BundleConfig) Apply(opts ...BundleOption) error {
|
|
for _, o := range opts {
|
|
if err := o(bc); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// WithDBPath sets the DB path for package installs.
|
|
// In case of luet packages will contain the db of the installed packages
|
|
func WithDBPath(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, error) {
|
|
s := strings.Split(bc.Repository, "://")
|
|
if len(s) != 2 {
|
|
return "", "", fmt.Errorf("invalid repo schema")
|
|
}
|
|
return s[0], s[1], nil
|
|
}
|
|
|
|
func defaultConfig() *BundleConfig {
|
|
return &BundleConfig{
|
|
DBPath: "/usr/local/.c3os/db",
|
|
RootPath: "/",
|
|
Repository: "docker://quay.io/c3os/packages",
|
|
}
|
|
}
|
|
|
|
type BundleInstaller interface {
|
|
Install(*BundleConfig) error
|
|
}
|
|
|
|
// RunBundles runs bundles in a system.
|
|
// Accept a list of bundles options, which gets applied based on the bundle configuration
|
|
func RunBundles(bundles ...[]BundleOption) 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.
|
|
|
|
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
|
|
out, err := utils.SH(
|
|
fmt.Sprintf(
|
|
`luet util unpack %s %s`,
|
|
config.Target,
|
|
config.RootPath,
|
|
),
|
|
)
|
|
if err != nil {
|
|
return fmt.Errorf("could not unpack bundle: %w - %s", err, out)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
type LuetInstaller struct{}
|
|
|
|
func (l *LuetInstaller) Install(config *BundleConfig) error {
|
|
|
|
t, repo, err := config.extractRepo()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = os.MkdirAll(filepath.Join(config.RootPath, "etc/luet/repos.conf.d/"), os.ModePerm)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
out, 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 - %s", err, out)
|
|
}
|
|
|
|
out, 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 install bundle: %w - %s", err, out)
|
|
}
|
|
|
|
// copy bins to /usr/local/bin
|
|
return nil
|
|
}
|