art: Drop provider from c3os code

Part of: https://github.com/c3os-io/c3os/issues/68
This commit is contained in:
mudler
2022-08-10 18:56:07 +02:00
committed by Itxaka
parent 2844b7fc15
commit a8f5593beb
4 changed files with 316 additions and 0 deletions

View 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
View 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
}

View File

@@ -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
View 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()
}