mirror of
https://github.com/kairos-io/kairos-sdk.git
synced 2025-09-17 23:58:40 +00:00
art: Drop provider from c3os code
Part of: https://github.com/c3os-io/c3os/issues/68
This commit is contained in:
34
sdk/bundles/bundle_test.go
Normal file
34
sdk/bundles/bundle_test.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package bundles_test
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
. "github.com/c3os-io/c3os/sdk/bundles"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var _ = Describe("Bundle", func() {
|
||||
Context("install", func() {
|
||||
PIt("installs packages from luet repos", func() {
|
||||
dir, err := ioutil.TempDir("", "test")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
defer os.RemoveAll(dir)
|
||||
os.MkdirAll(filepath.Join(dir, "var", "tmp", "luet"), os.ModePerm)
|
||||
err = RunBundles([]BundleOption{WithDBPath(dir), WithRootFS(dir), WithTarget("package://utils/edgevpn")})
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(filepath.Join(dir, "usr", "bin", "edgevpn")).To(BeARegularFile())
|
||||
})
|
||||
|
||||
It("installs from container images", func() {
|
||||
dir, err := ioutil.TempDir("", "test")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
defer os.RemoveAll(dir)
|
||||
err = RunBundles([]BundleOption{WithDBPath(dir), WithRootFS(dir), WithTarget("container://quay.io/mocaccino/extra:edgevpn-utils-0.15.0")})
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(filepath.Join(dir, "usr", "bin", "edgevpn")).To(BeARegularFile())
|
||||
})
|
||||
})
|
||||
})
|
229
sdk/bundles/bundles.go
Normal file
229
sdk/bundles/bundles.go
Normal file
@@ -0,0 +1,229 @@
|
||||
package bundles
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/c3os-io/c3os/pkg/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()
|
||||
if err := config.Apply(b...); err != nil {
|
||||
resErr = multierror.Append(err)
|
||||
continue
|
||||
}
|
||||
|
||||
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
|
||||
}
|
@@ -18,6 +18,9 @@ const (
|
||||
|
||||
// EventBootstrap is issued to run any initial cluster configuration.
|
||||
EventBootstrap pluggable.EventType = "agent.bootstrap"
|
||||
|
||||
// EventInstallPrompt is issued to request which config are required to ask to the user
|
||||
EventInstallPrompt pluggable.EventType = "agent.installprompt"
|
||||
)
|
||||
|
||||
type InstallPayload struct {
|
||||
@@ -34,3 +37,35 @@ type BootstrapPayload struct {
|
||||
type EventPayload struct {
|
||||
Config string `json:"config"`
|
||||
}
|
||||
|
||||
// AllEvents is a convenience list of all the events streamed from the bus.
|
||||
var AllEvents = []pluggable.EventType{
|
||||
EventBootstrap,
|
||||
EventChallenge,
|
||||
EventBoot,
|
||||
EventInstall,
|
||||
}
|
||||
|
||||
// IsEventDefined checks wether an event is defined in the bus.
|
||||
// It accepts strings or EventType, returns a boolean indicating that
|
||||
// the event was defined among the events emitted by the bus.
|
||||
func IsEventDefined(i interface{}) bool {
|
||||
checkEvent := func(e pluggable.EventType) bool {
|
||||
for _, ee := range AllEvents {
|
||||
if ee == e {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
switch f := i.(type) {
|
||||
case string:
|
||||
return checkEvent(pluggable.EventType(f))
|
||||
case pluggable.EventType:
|
||||
return checkEvent(f)
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
18
sdk/bus/hooks.go
Normal file
18
sdk/bus/hooks.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package bus
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
func RunHookScript(s string) error {
|
||||
_, err := os.Stat(s)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
cmd := exec.Command(s)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
cmd.Stdin = os.Stdin
|
||||
return cmd.Run()
|
||||
}
|
Reference in New Issue
Block a user