From 3c6484de14d2630109bfd794cde0945f0069aada Mon Sep 17 00:00:00 2001 From: mudler Date: Wed, 10 Aug 2022 18:56:07 +0200 Subject: [PATCH] art: Drop provider from c3os code Part of: https://github.com/c3os-io/c3os/issues/68 --- internal/agent/agent.go | 5 +- internal/agent/install.go | 16 +- internal/agent/interactive_install.go | 23 +-- internal/agent/recovery.go | 4 +- internal/agent/reset.go | 4 +- internal/agent/rotate.go | 40 ---- internal/agent/upgrade.go | 2 +- internal/bus/bus.go | 20 +- internal/cmd/utils.go | 2 +- internal/machine/bootcmdline.go | 88 --------- internal/machine/bootcmdline_test.go | 29 --- internal/machine/bundle_test.go | 34 ---- internal/machine/bundles.go | 229 ----------------------- internal/machine/machine.go | 157 ---------------- internal/machine/machine_suite_test.go | 13 -- internal/machine/openrc/edgevpn.go | 19 -- internal/machine/openrc/unit.go | 98 ---------- internal/machine/systemd/edgevpn.go | 12 -- internal/machine/systemd/unit.go | 115 ------------ internal/provider/bootstrap.go | 221 ---------------------- internal/provider/bootstrap_test.go | 54 ------ internal/provider/challenge.go | 38 ---- internal/provider/challenge_test.go | 72 ------- internal/provider/common.go | 11 -- internal/provider/config/config.go | 24 --- internal/provider/install.go | 36 ---- internal/provider/provider_suite_test.go | 13 -- internal/provider/vpn.go | 92 --------- internal/role/auto.go | 62 ------ internal/role/common.go | 21 --- internal/role/master.go | 155 --------------- internal/role/schedule.go | 80 -------- internal/role/worker.go | 113 ----------- internal/utils/console.go | 37 ---- internal/utils/sh.go | 43 ----- internal/utils/signal.go | 15 -- internal/utils/strings.go | 20 -- internal/utils/system.go | 94 ---------- internal/utils/token.go | 13 -- 39 files changed, 26 insertions(+), 2098 deletions(-) delete mode 100644 internal/agent/rotate.go delete mode 100644 internal/machine/bootcmdline.go delete mode 100644 internal/machine/bootcmdline_test.go delete mode 100644 internal/machine/bundle_test.go delete mode 100644 internal/machine/bundles.go delete mode 100644 internal/machine/machine.go delete mode 100644 internal/machine/machine_suite_test.go delete mode 100644 internal/machine/openrc/edgevpn.go delete mode 100644 internal/machine/openrc/unit.go delete mode 100644 internal/machine/systemd/edgevpn.go delete mode 100644 internal/machine/systemd/unit.go delete mode 100644 internal/provider/bootstrap.go delete mode 100644 internal/provider/bootstrap_test.go delete mode 100644 internal/provider/challenge.go delete mode 100644 internal/provider/challenge_test.go delete mode 100644 internal/provider/common.go delete mode 100644 internal/provider/config/config.go delete mode 100644 internal/provider/install.go delete mode 100644 internal/provider/provider_suite_test.go delete mode 100644 internal/provider/vpn.go delete mode 100644 internal/role/auto.go delete mode 100644 internal/role/common.go delete mode 100644 internal/role/master.go delete mode 100644 internal/role/schedule.go delete mode 100644 internal/role/worker.go delete mode 100644 internal/utils/console.go delete mode 100644 internal/utils/sh.go delete mode 100644 internal/utils/signal.go delete mode 100644 internal/utils/strings.go delete mode 100644 internal/utils/system.go delete mode 100644 internal/utils/token.go diff --git a/internal/agent/agent.go b/internal/agent/agent.go index 47eafde..e2e749d 100644 --- a/internal/agent/agent.go +++ b/internal/agent/agent.go @@ -9,8 +9,9 @@ import ( events "github.com/c3os-io/c3os/sdk/bus" "github.com/c3os-io/c3os/internal/bus" - machine "github.com/c3os-io/c3os/internal/machine" config "github.com/c3os-io/c3os/pkg/config" + machine "github.com/c3os-io/c3os/pkg/machine" + bundles "github.com/c3os-io/c3os/sdk/bundles" "github.com/nxadm/tail" ) @@ -61,7 +62,7 @@ func Run(opts ...Option) error { if !machine.SentinelExist("bundles") { opts := c.Bundles.Options() - err := machine.RunBundles(opts...) + err := bundles.RunBundles(opts...) if !c.IgnoreBundleErrors && err != nil { return err } diff --git a/internal/agent/install.go b/internal/agent/install.go index 3c1b9b1..21179e1 100644 --- a/internal/agent/install.go +++ b/internal/agent/install.go @@ -15,10 +15,12 @@ import ( config "github.com/c3os-io/c3os/pkg/config" "github.com/c3os-io/c3os/internal/bus" - "github.com/c3os-io/c3os/internal/cmd" - "github.com/c3os-io/c3os/internal/utils" + sdkBus "github.com/c3os-io/c3os/sdk/bus" - machine "github.com/c3os-io/c3os/internal/machine" + "github.com/c3os-io/c3os/internal/cmd" + "github.com/c3os-io/c3os/pkg/utils" + + machine "github.com/c3os-io/c3os/pkg/machine" qr "github.com/mudler/go-nodepair/qrcode" "github.com/mudler/go-pluggable" "github.com/pterm/pterm" @@ -154,8 +156,8 @@ func Install(dir ...string) error { r["cc"] = config.AddHeader(header, string(out)) pterm.Info.Println("Starting installation") - utils.SH("elemental run-stage c3os-install.pre") //nolint:errcheck - bus.RunHookScript("/usr/bin/c3os-agent.install.pre.hook") //nolint:errcheck + utils.SH("elemental run-stage c3os-install.pre") //nolint:errcheck + sdkBus.RunHookScript("/usr/bin/c3os-agent.install.pre.hook") //nolint:errcheck if err := RunInstall(r); err != nil { return err @@ -214,8 +216,8 @@ func RunInstall(options map[string]string) error { fmt.Println(err) os.Exit(1) } - utils.SH("elemental run-stage c3os-install.after") //nolint:errcheck - bus.RunHookScript("/usr/bin/c3os-agent.install.after.hook") //nolint:errcheck + utils.SH("elemental run-stage c3os-install.after") //nolint:errcheck + sdkBus.RunHookScript("/usr/bin/c3os-agent.install.after.hook") //nolint:errcheck if reboot || c.Install != nil && c.Install.Reboot { utils.Reboot() diff --git a/internal/agent/interactive_install.go b/internal/agent/interactive_install.go index 829514b..ed6e2a0 100644 --- a/internal/agent/interactive_install.go +++ b/internal/agent/interactive_install.go @@ -6,8 +6,8 @@ import ( "github.com/c3os-io/c3os/internal/cmd" providerConfig "github.com/c3os-io/c3os/internal/provider/config" - "github.com/c3os-io/c3os/internal/utils" config "github.com/c3os-io/c3os/pkg/config" + "github.com/c3os-io/c3os/pkg/utils" "github.com/erikgeiser/promptkit/textinput" "github.com/jaypipes/ghw" "github.com/mudler/edgevpn/pkg/node" @@ -94,15 +94,12 @@ func InteractiveInstall(spawnShell bool) error { userPassword = "!" } - sshUsername, err := prompt("Username to grant SSH access to (github/gitlab supported)", "github:someuser", canBeEmpty, true, false) + users, err := prompt("SSH access (rsakey, github/gitlab supported, comma-separated)", "github:someuser,github:someuser2", canBeEmpty, true, false) if err != nil { return err } - sshPubkey, err := prompt("SSH pubkey", "github:username", canBeEmpty, true, false) - if err != nil { - return err - } + sshUsers := strings.Split(users, ",") k3sAuto, err := prompt("Do you want to enable k3s automated setup? (requires multiple nodes)", "n", yesNo, true, false) if err != nil { @@ -158,16 +155,10 @@ func InteractiveInstall(spawnShell bool) error { if userName != "" { user := schema.User{ - Name: userName, - PasswordHash: userPassword, - Groups: []string{"admin"}, - } - if sshUsername != "" { - user.SSHAuthorizedKeys = append(user.SSHAuthorizedKeys, sshUsername) - } - - if sshPubkey != "" { - user.SSHAuthorizedKeys = append(user.SSHAuthorizedKeys, sshPubkey) + Name: userName, + PasswordHash: userPassword, + Groups: []string{"admin"}, + SSHAuthorizedKeys: sshUsers, } usersToSet = map[string]schema.User{ diff --git a/internal/agent/recovery.go b/internal/agent/recovery.go index ec3b51e..cd20de5 100644 --- a/internal/agent/recovery.go +++ b/internal/agent/recovery.go @@ -8,11 +8,11 @@ import ( "time" "github.com/c3os-io/c3os/internal/cmd" - "github.com/c3os-io/c3os/internal/utils" config "github.com/c3os-io/c3os/pkg/config" + "github.com/c3os-io/c3os/pkg/utils" "github.com/ipfs/go-log" - machine "github.com/c3os-io/c3os/internal/machine" + machine "github.com/c3os-io/c3os/pkg/machine" "github.com/creack/pty" "github.com/gliderlabs/ssh" "github.com/mudler/edgevpn/pkg/logger" diff --git a/internal/agent/reset.go b/internal/agent/reset.go index 51b1f67..b4949dc 100644 --- a/internal/agent/reset.go +++ b/internal/agent/reset.go @@ -8,8 +8,8 @@ import ( "time" "github.com/c3os-io/c3os/internal/cmd" - "github.com/c3os-io/c3os/internal/machine" - "github.com/c3os-io/c3os/internal/utils" + "github.com/c3os-io/c3os/pkg/machine" + "github.com/c3os-io/c3os/pkg/utils" "github.com/pterm/pterm" ) diff --git a/internal/agent/rotate.go b/internal/agent/rotate.go deleted file mode 100644 index a14a506..0000000 --- a/internal/agent/rotate.go +++ /dev/null @@ -1,40 +0,0 @@ -package agent - -import ( - machine "github.com/c3os-io/c3os/internal/machine" - "github.com/c3os-io/c3os/internal/provider" - providerConfig "github.com/c3os-io/c3os/internal/provider/config" - config "github.com/c3os-io/c3os/pkg/config" -) - -func RotateToken(configDir []string, newToken, apiAddress, rootDir string, restart bool) error { - if err := config.ReplaceToken(configDir, newToken); err != nil { - return err - } - - c, err := config.Scan(config.Directories(configDir...)) - if err != nil { - return err - } - - providerCfg := &providerConfig.Config{} - err = c.Unmarshal(providerCfg) - if err != nil { - return err - } - - err = provider.SetupVPN(machine.EdgeVPNDefaultInstance, apiAddress, rootDir, false, providerCfg) - if err != nil { - return err - } - - if restart { - svc, err := machine.EdgeVPN(machine.EdgeVPNDefaultInstance, rootDir) - if err != nil { - return err - } - - return svc.Restart() - } - return nil -} diff --git a/internal/agent/upgrade.go b/internal/agent/upgrade.go index 1e75f71..055b478 100644 --- a/internal/agent/upgrade.go +++ b/internal/agent/upgrade.go @@ -8,7 +8,7 @@ import ( "os/exec" "github.com/c3os-io/c3os/internal/github" - "github.com/c3os-io/c3os/internal/utils" + "github.com/c3os-io/c3os/pkg/utils" ) func Upgrade(version, image string, force bool) error { diff --git a/internal/bus/bus.go b/internal/bus/bus.go index 3f2fbf3..20fe6ed 100644 --- a/internal/bus/bus.go +++ b/internal/bus/bus.go @@ -3,7 +3,6 @@ package bus import ( "fmt" "os" - "os/exec" "github.com/c3os-io/c3os/sdk/bus" @@ -13,12 +12,7 @@ import ( // Manager is the bus instance manager, which subscribes plugins to events emitted. var Manager = &Bus{ Manager: pluggable.NewManager( - []pluggable.EventType{ - bus.EventBootstrap, - bus.EventChallenge, - bus.EventBoot, - bus.EventInstall, - }, + bus.AllEvents, ), } @@ -57,15 +51,3 @@ func (b *Bus) Initialize() { }) } } - -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() -} diff --git a/internal/cmd/utils.go b/internal/cmd/utils.go index 51f7b90..ffc7663 100644 --- a/internal/cmd/utils.go +++ b/internal/cmd/utils.go @@ -6,7 +6,7 @@ import ( "os" "github.com/c3os-io/c3os/internal/c3os" - "github.com/c3os-io/c3os/internal/utils" + "github.com/c3os-io/c3os/pkg/utils" "github.com/pterm/pterm" ) diff --git a/internal/machine/bootcmdline.go b/internal/machine/bootcmdline.go deleted file mode 100644 index ede22c1..0000000 --- a/internal/machine/bootcmdline.go +++ /dev/null @@ -1,88 +0,0 @@ -package machine - -import ( - "errors" - "fmt" - "io/ioutil" - "strings" - - "github.com/google/shlex" - "github.com/hashicorp/go-multierror" - "github.com/itchyny/gojq" - "gopkg.in/yaml.v2" -) - -func DotToYAML(file string) ([]byte, error) { - if file == "" { - file = "/proc/cmdline" - } - dat, err := ioutil.ReadFile(file) - if err != nil { - return []byte{}, err - } - - v := stringToMap(string(dat)) - - return dotToYAML(v) -} - -func stringToMap(s string) map[string]interface{} { - v := map[string]interface{}{} - - splitted, _ := shlex.Split(s) - for _, item := range splitted { - parts := strings.SplitN(item, "=", 2) - value := "true" - if len(parts) > 1 { - value = strings.Trim(parts[1], `"`) - } - key := strings.Trim(parts[0], `"`) - v[key] = value - } - - return v -} -func jq(command string, data map[string]interface{}) (map[string]interface{}, error) { - query, err := gojq.Parse(command) - if err != nil { - return nil, err - } - code, err := gojq.Compile(query) - if err != nil { - return nil, err - } - iter := code.Run(data) - - v, ok := iter.Next() - if !ok { - return nil, errors.New("failed getting rsult from gojq") - } - if err, ok := v.(error); ok { - return nil, err - } - if t, ok := v.(map[string]interface{}); ok { - return t, nil - } - - return make(map[string]interface{}), nil -} - -func dotToYAML(v map[string]interface{}) ([]byte, error) { - data := map[string]interface{}{} - var errs error - - for k, value := range v { - newData, err := jq(fmt.Sprintf(".%s=\"%s\"", k, value), data) - if err != nil { - errs = multierror.Append(errs, err) - continue - } - data = newData - } - - out, err := yaml.Marshal(&data) - if err != nil { - errs = multierror.Append(errs, err) - } - return out, errs -} diff --git a/internal/machine/bootcmdline_test.go b/internal/machine/bootcmdline_test.go deleted file mode 100644 index b689ade..0000000 --- a/internal/machine/bootcmdline_test.go +++ /dev/null @@ -1,29 +0,0 @@ -package machine_test - -import ( - "io/ioutil" - "os" - - . "github.com/c3os-io/c3os/internal/machine" - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" -) - -var _ = Describe("BootCMDLine", func() { - Context("parses data", func() { - - It("returns cmdline if provided", func() { - f, err := ioutil.TempFile("", "test") - Expect(err).ToNot(HaveOccurred()) - defer os.RemoveAll(f.Name()) - - err = ioutil.WriteFile(f.Name(), []byte(`config_url="foo bar" baz.bar=""`), os.ModePerm) - Expect(err).ToNot(HaveOccurred()) - - b, err := DotToYAML(f.Name()) - Expect(err).ToNot(HaveOccurred()) - - Expect(string(b)).To(Equal("baz:\n bar: \"\"\nconfig_url: foo bar\n")) - }) - }) -}) diff --git a/internal/machine/bundle_test.go b/internal/machine/bundle_test.go deleted file mode 100644 index 0e518df..0000000 --- a/internal/machine/bundle_test.go +++ /dev/null @@ -1,34 +0,0 @@ -package machine_test - -import ( - "io/ioutil" - "os" - "path/filepath" - - . "github.com/c3os-io/c3os/internal/machine" - . "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()) - }) - }) -}) diff --git a/internal/machine/bundles.go b/internal/machine/bundles.go deleted file mode 100644 index 4369c8f..0000000 --- a/internal/machine/bundles.go +++ /dev/null @@ -1,229 +0,0 @@ -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() - 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 -} diff --git a/internal/machine/machine.go b/internal/machine/machine.go deleted file mode 100644 index fe4f68d..0000000 --- a/internal/machine/machine.go +++ /dev/null @@ -1,157 +0,0 @@ -package machine - -import ( - "fmt" - "io/ioutil" - "os" - "strings" - - "github.com/c3os-io/c3os/internal/machine/openrc" - "github.com/c3os-io/c3os/internal/machine/systemd" - "github.com/denisbrodbeck/machineid" - - "github.com/c3os-io/c3os/internal/utils" -) - -type Service interface { - WriteUnit() error - Start() error - OverrideCmd(string) error - Enable() error - Restart() error -} - -const ( - PassiveBoot = "passive" - ActiveBoot = "active" - RecoveryBoot = "recovery" - LiveCDBoot = "liveCD" - NetBoot = "netboot" - UnknownBoot = "unknown" -) - -// BootFrom returns the booting partition of the SUT. -func BootFrom() string { - out, err := utils.SH("cat /proc/cmdline") - if err != nil { - return UnknownBoot - } - switch { - case strings.Contains(out, "COS_ACTIVE"): - return ActiveBoot - case strings.Contains(out, "COS_PASSIVE"): - return PassiveBoot - case strings.Contains(out, "COS_RECOVERY"), strings.Contains(out, "COS_SYSTEM"): - return RecoveryBoot - case strings.Contains(out, "live:CDLABEL"): - return LiveCDBoot - case strings.Contains(out, "netboot"): - return NetBoot - default: - return UnknownBoot - } -} - -func EdgeVPN(instance, rootDir string) (Service, error) { - if utils.IsOpenRCBased() { - return openrc.NewService( - openrc.WithName("edgevpn"), - openrc.WithUnitContent(openrc.EdgevpnUnit), - openrc.WithRoot(rootDir), - ) - } - - return systemd.NewService( - systemd.WithName("edgevpn"), - systemd.WithInstance(instance), - systemd.WithUnitContent(systemd.EdgevpnUnit), - systemd.WithRoot(rootDir), - ) -} - -const EdgeVPNDefaultInstance string = "c3os" - -type fakegetty struct{} - -func (fakegetty) Restart() error { return nil } -func (fakegetty) Enable() error { return nil } -func (fakegetty) OverrideCmd(string) error { return nil } -func (fakegetty) SetEnvFile(string) error { return nil } -func (fakegetty) WriteUnit() error { return nil } -func (fakegetty) Start() error { - utils.SH("chvt 2") //nolint:errcheck - return nil -} - -func Getty(i int) (Service, error) { - if utils.IsOpenRCBased() { - return &fakegetty{}, nil - } - - return systemd.NewService( - systemd.WithName("getty"), - systemd.WithInstance(fmt.Sprintf("tty%d", i)), - ) -} - -func K3s() (Service, error) { - if utils.IsOpenRCBased() { - return openrc.NewService( - openrc.WithName("k3s"), - ) - } - - return systemd.NewService( - systemd.WithName("k3s"), - ) -} - -func K3sAgent() (Service, error) { - if utils.IsOpenRCBased() { - return openrc.NewService( - openrc.WithName("k3s-agent"), - ) - } - - return systemd.NewService( - systemd.WithName("k3s-agent"), - ) -} - -func K3sEnvUnit(unit string) string { - if utils.IsOpenRCBased() { - return fmt.Sprintf("/etc/rancher/k3s/%s.env", unit) - } - - return fmt.Sprintf("/etc/sysconfig/%s", unit) -} - -func UUID() string { - if os.Getenv("UUID") != "" { - return os.Getenv("UUID") - } - id, _ := machineid.ID() - hostname, _ := os.Hostname() - return fmt.Sprintf("%s-%s", id, hostname) -} - -func CreateSentinel(f string) error { - return ioutil.WriteFile(fmt.Sprintf("/usr/local/.c3os/sentinel_%s", f), []byte{}, os.ModePerm) -} - -func SentinelExist(f string) bool { - if _, err := os.Stat(fmt.Sprintf("/usr/local/.c3os/sentinel_%s", f)); err == nil { - return true - } - return false -} - -func ExecuteInlineCloudConfig(cloudConfig, stage string) error { - _, err := utils.ShellSTDIN(cloudConfig, fmt.Sprintf("elemental run-stage -s %s -", stage)) - return err -} - -func ExecuteCloudConfig(file, stage string) error { - _, err := utils.SH(fmt.Sprintf("elemental run-stage -s %s %s", stage, file)) - return err -} diff --git a/internal/machine/machine_suite_test.go b/internal/machine/machine_suite_test.go deleted file mode 100644 index 51bc130..0000000 --- a/internal/machine/machine_suite_test.go +++ /dev/null @@ -1,13 +0,0 @@ -package machine_test - -import ( - "testing" - - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" -) - -func TestInstaller(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "Machine Suite") -} diff --git a/internal/machine/openrc/edgevpn.go b/internal/machine/openrc/edgevpn.go deleted file mode 100644 index 246b525..0000000 --- a/internal/machine/openrc/edgevpn.go +++ /dev/null @@ -1,19 +0,0 @@ -package openrc - -const EdgevpnUnit string = `#!/sbin/openrc-run - -depend() { - after net - provide edgevpn -} - -supervisor=supervise-daemon -name="edgevpn" -command="edgevpn" -supervise_daemon_args="--stdout /var/log/edgevpn.log --stderr /var/log/edgevpn.log" -pidfile="/run/edgevpn.pid" -respawn_delay=5 -set -o allexport -if [ -f /etc/environment ]; then source /etc/environment; fi -if [ -f /etc/systemd/system.conf.d/edgevpn-c3os.env ]; then source /etc/systemd/system.conf.d/edgevpn-c3os.env; fi -set +o allexport` diff --git a/internal/machine/openrc/unit.go b/internal/machine/openrc/unit.go deleted file mode 100644 index f39f6b8..0000000 --- a/internal/machine/openrc/unit.go +++ /dev/null @@ -1,98 +0,0 @@ -package openrc - -import ( - "fmt" - "io/ioutil" - "path/filepath" - "strings" - - "github.com/c3os-io/c3os/internal/utils" -) - -type ServiceUnit struct { - content string - name string - rootdir string -} - -type ServiceOpts func(*ServiceUnit) error - -func WithRoot(n string) ServiceOpts { - return func(su *ServiceUnit) error { - su.rootdir = n - return nil - } -} - -func WithName(n string) ServiceOpts { - return func(su *ServiceUnit) error { - su.name = n - return nil - } -} - -func WithUnitContent(n string) ServiceOpts { - return func(su *ServiceUnit) error { - su.content = n - return nil - } -} - -func NewService(opts ...ServiceOpts) (ServiceUnit, error) { - s := &ServiceUnit{} - for _, o := range opts { - if err := o(s); err != nil { - return *s, err - } - } - return *s, nil -} - -func (s ServiceUnit) WriteUnit() error { - uname := s.name - - if err := ioutil.WriteFile(filepath.Join(s.rootdir, fmt.Sprintf("/etc/init.d/%s", uname)), []byte(s.content), 0755); err != nil { - return err - } - - return nil -} - -// TODO: This is too much k3s specific. -func (s ServiceUnit) OverrideCmd(cmd string) error { - k3sbin := utils.K3sBin() - if k3sbin == "" { - return fmt.Errorf("no k3s binary found (?)") - } - cmd = strings.ReplaceAll(cmd, k3sbin+" ", "") - envFile := filepath.Join(s.rootdir, fmt.Sprintf("/etc/rancher/k3s/%s.env", s.name)) - env := make(map[string]string) - env["command_args"] = fmt.Sprintf("%s >>/var/log/%s.log 2>&1", cmd, s.name) - - return utils.WriteEnv(envFile, env) -} - -func (s ServiceUnit) Start() error { - out, err := utils.SH(fmt.Sprintf("/etc/init.d/%s start", s.name)) - if err != nil { - return fmt.Errorf("failed starting service: %s. %s (%w)", s.name, out, err) - } - return nil -} - -func (s ServiceUnit) Restart() error { - out, err := utils.SH(fmt.Sprintf("/etc/init.d/%s restart", s.name)) - if err != nil { - return fmt.Errorf("failed restarting service: %s. %s (%w)", s.name, out, err) - } - return nil -} - -func (s ServiceUnit) Enable() error { - _, err := utils.SH(fmt.Sprintf("ln -sf /etc/init.d/%s /etc/runlevels/default/%s", s.name, s.name)) - return err -} - -func (s ServiceUnit) StartBlocking() error { - return s.Start() -} diff --git a/internal/machine/systemd/edgevpn.go b/internal/machine/systemd/edgevpn.go deleted file mode 100644 index 068ffdf..0000000 --- a/internal/machine/systemd/edgevpn.go +++ /dev/null @@ -1,12 +0,0 @@ -package systemd - -const EdgevpnUnit string = `[Unit] -Description=EdgeVPN Daemon -After=network.target -[Service] -EnvironmentFile=/etc/systemd/system.conf.d/edgevpn-%i.env -LimitNOFILE=49152 -ExecStart=edgevpn -Restart=always -[Install] -WantedBy=multi-user.target` diff --git a/internal/machine/systemd/unit.go b/internal/machine/systemd/unit.go deleted file mode 100644 index 796f386..0000000 --- a/internal/machine/systemd/unit.go +++ /dev/null @@ -1,115 +0,0 @@ -package systemd - -import ( - "fmt" - "io/ioutil" - "os" - "path/filepath" - "strings" - - "github.com/c3os-io/c3os/internal/utils" -) - -type ServiceUnit struct { - content string - name, instance string - rootdir string -} - -const overrideCmdTemplate string = ` -[Service] -ExecStart= -ExecStart=%s -` - -type ServiceOpts func(*ServiceUnit) error - -func WithRoot(n string) ServiceOpts { - return func(su *ServiceUnit) error { - su.rootdir = n - return nil - } -} - -func WithName(n string) ServiceOpts { - return func(su *ServiceUnit) error { - su.name = n - return nil - } -} - -func WithInstance(n string) ServiceOpts { - return func(su *ServiceUnit) error { - su.instance = n - return nil - } -} - -func WithUnitContent(n string) ServiceOpts { - return func(su *ServiceUnit) error { - su.content = n - return nil - } -} - -func NewService(opts ...ServiceOpts) (ServiceUnit, error) { - s := &ServiceUnit{} - for _, o := range opts { - if err := o(s); err != nil { - return *s, err - } - } - return *s, nil -} - -func (s ServiceUnit) WriteUnit() error { - uname := s.name - if s.instance != "" { - uname = fmt.Sprintf("%s@", s.name) - } - - if err := ioutil.WriteFile(filepath.Join(s.rootdir, fmt.Sprintf("/etc/systemd/system/%s.service", uname)), []byte(s.content), 0600); err != nil { - return err - } - - _, err := utils.SH("systemctl daemon-reload") - return err -} - -func (s ServiceUnit) OverrideCmd(cmd string) error { - svcDir := filepath.Join(s.rootdir, fmt.Sprintf("/etc/systemd/system/%s.service.d/", s.name)) - os.MkdirAll(svcDir, 0600) //nolint:errcheck - - return ioutil.WriteFile(filepath.Join(svcDir, "override.conf"), []byte(fmt.Sprintf(overrideCmdTemplate, cmd)), 0600) -} - -func (s ServiceUnit) Start() error { - return s.systemctl("start", false) -} - -func (s ServiceUnit) Restart() error { - return s.systemctl("restart", false) -} - -func (s ServiceUnit) Enable() error { - return s.systemctl("enable", false) -} - -func (s ServiceUnit) StartBlocking() error { - return s.systemctl("start", true) -} - -func (s ServiceUnit) systemctl(action string, blocking bool) error { - uname := s.name - if s.instance != "" { - uname = fmt.Sprintf("%s@%s", s.name, s.instance) - } - args := []string{action} - if !blocking { - args = append(args, "--no-block") - } - args = append(args, uname) - - _, err := utils.SH(fmt.Sprintf("systemctl %s", strings.Join(args, " "))) - return err -} diff --git a/internal/provider/bootstrap.go b/internal/provider/bootstrap.go deleted file mode 100644 index 987a4b0..0000000 --- a/internal/provider/bootstrap.go +++ /dev/null @@ -1,221 +0,0 @@ -package provider - -import ( - "context" - "encoding/json" - "fmt" - "strings" - - "github.com/c3os-io/c3os/sdk/bus" - - logging "github.com/ipfs/go-log" - edgeVPNClient "github.com/mudler/edgevpn/api/client" - "go.uber.org/zap" - - eventBus "github.com/c3os-io/c3os/internal/bus" - "github.com/c3os-io/c3os/internal/machine" - "github.com/c3os-io/c3os/internal/machine/openrc" - "github.com/c3os-io/c3os/internal/machine/systemd" - providerConfig "github.com/c3os-io/c3os/internal/provider/config" - "github.com/c3os-io/c3os/internal/role" - "github.com/c3os-io/c3os/internal/utils" - - "github.com/c3os-io/c3os/pkg/config" - "github.com/mudler/edgevpn/api/client/service" - "github.com/mudler/go-pluggable" -) - -func Bootstrap(e *pluggable.Event) pluggable.EventResponse { - cfg := &bus.BootstrapPayload{} - err := json.Unmarshal([]byte(e.Data), cfg) - if err != nil { - return ErrorEvent("Failed reading JSON input: %s input '%s'", err.Error(), e.Data) - } - - c := &config.Config{} - providerConfig := &providerConfig.Config{} - err = config.FromString(cfg.Config, c) - if err != nil { - return ErrorEvent("Failed reading JSON input: %s input '%s'", err.Error(), cfg.Config) - } - - err = config.FromString(cfg.Config, providerConfig) - if err != nil { - return ErrorEvent("Failed reading JSON input: %s input '%s'", err.Error(), cfg.Config) - } - // TODO: this belong to a systemd service that is started instead - - tokenNotDefined := (providerConfig.C3OS != nil && providerConfig.C3OS.NetworkToken == "") - - if providerConfig.C3OS == nil && !providerConfig.K3s.Enabled && !providerConfig.K3sAgent.Enabled { - return pluggable.EventResponse{State: "no c3os or k3s configuration. nothing to do"} - } - - utils.SH("elemental run-stage c3os-agent.bootstrap") //nolint:errcheck - eventBus.RunHookScript("/usr/bin/c3os-agent.bootstrap.hook") //nolint:errcheck - - logLevel := "debug" - - if providerConfig.C3OS != nil && providerConfig.C3OS.LogLevel != "" { - logLevel = providerConfig.C3OS.LogLevel - } - - lvl, err := logging.LevelFromString(logLevel) - if err != nil { - return ErrorEvent("Failed setup logger: %s", err.Error()) - } - - // TODO: Fixup Logging to file - loggerCfg := zap.NewProductionConfig() - loggerCfg.OutputPaths = []string{ - cfg.Logfile, - } - logger, err := loggerCfg.Build() - if err != nil { - return ErrorEvent("Failed setup logger: %s", err.Error()) - } - - logging.SetAllLoggers(lvl) - - log := &logging.ZapEventLogger{SugaredLogger: *logger.Sugar()} - - // Do onetimebootstrap if K3s or K3s-agent are enabled. - // Those blocks are not required to be enabled in case of a c3os - // full automated setup. Otherwise, they must be explicitly enabled. - if providerConfig.K3s.Enabled || providerConfig.K3sAgent.Enabled { - err := oneTimeBootstrap(log, providerConfig, func() error { - return SetupVPN(machine.EdgeVPNDefaultInstance, cfg.APIAddress, "/", true, providerConfig) - }) - if err != nil { - return ErrorEvent("Failed setup: %s", err.Error()) - } - return pluggable.EventResponse{} - } else if tokenNotDefined { - return ErrorEvent("No network token provided, exiting") - } - - logger.Info("Configuring VPN") - if err := SetupVPN(machine.EdgeVPNDefaultInstance, cfg.APIAddress, "/", true, providerConfig); err != nil { - return ErrorEvent("Failed setup VPN: %s", err.Error()) - } - - networkID := "c3os" - - if providerConfig.C3OS != nil && providerConfig.C3OS.NetworkID != "" { - networkID = providerConfig.C3OS.NetworkID - } - - cc := service.NewClient( - networkID, - edgeVPNClient.NewClient(edgeVPNClient.WithHost(cfg.APIAddress))) - - nodeOpts := []service.Option{ - service.WithLogger(log), - service.WithClient(cc), - service.WithUUID(machine.UUID()), - service.WithStateDir("/usr/local/.c3os/state"), - service.WithNetworkToken(providerConfig.C3OS.NetworkToken), - service.WithPersistentRoles("auto"), - service.WithRoles( - service.RoleKey{ - Role: "master", - RoleHandler: role.Master(c, providerConfig), - }, - service.RoleKey{ - Role: "worker", - RoleHandler: role.Worker(c, providerConfig), - }, - service.RoleKey{ - Role: "auto", - RoleHandler: role.Auto(c, providerConfig), - }, - ), - } - - // Optionally set up a specific node role if the user has defined so - if providerConfig.C3OS.Role != "" { - nodeOpts = append(nodeOpts, service.WithDefaultRoles(providerConfig.C3OS.Role)) - } - - k, err := service.NewNode(nodeOpts...) - if err != nil { - return ErrorEvent("Failed creating node: %s", err.Error()) - } - err = k.Start(context.Background()) - if err != nil { - return ErrorEvent("Failed start: %s", err.Error()) - } - - return pluggable.EventResponse{ - State: "", - Data: "", - Error: "shouldn't return here", - } -} - -func oneTimeBootstrap(l logging.StandardLogger, c *providerConfig.Config, vpnSetupFN func() error) error { - if role.SentinelExist() { - l.Info("Sentinel exists, nothing to do. exiting.") - return nil - } - l.Info("One time bootstrap starting") - - var svc machine.Service - k3sConfig := providerConfig.K3s{} - svcName := "k3s" - svcRole := "server" - - if c.K3s.Enabled { - k3sConfig = c.K3s - } else if c.K3sAgent.Enabled { - k3sConfig = c.K3sAgent - svcName = "k3s-agent" - svcRole = "agent" - } - - if utils.IsOpenRCBased() { - svc, _ = openrc.NewService( - openrc.WithName(svcName), - ) - } else { - svc, _ = systemd.NewService( - systemd.WithName(svcName), - ) - } - - envFile := machine.K3sEnvUnit(svcName) - if svc == nil { - return fmt.Errorf("could not detect OS") - } - - // Setup systemd unit and starts it - if err := utils.WriteEnv(envFile, - k3sConfig.Env, - ); err != nil { - return err - } - - k3sbin := utils.K3sBin() - if k3sbin == "" { - return fmt.Errorf("no k3s binary found (?)") - } - if err := svc.OverrideCmd(fmt.Sprintf("%s %s %s", k3sbin, svcRole, strings.Join(k3sConfig.Args, " "))); err != nil { - return err - } - - if err := svc.Start(); err != nil { - return err - } - - if err := svc.Enable(); err != nil { - return err - } - - if len(c.VPN) > 0 { - if err := vpnSetupFN(); err != nil { - return err - } - } - - return role.CreateSentinel() -} diff --git a/internal/provider/bootstrap_test.go b/internal/provider/bootstrap_test.go deleted file mode 100644 index 0f23337..0000000 --- a/internal/provider/bootstrap_test.go +++ /dev/null @@ -1,54 +0,0 @@ -package provider_test - -import ( - "encoding/json" - "io/ioutil" - "os" - - "github.com/c3os-io/c3os/sdk/bus" - - . "github.com/c3os-io/c3os/internal/provider" - providerConfig "github.com/c3os-io/c3os/internal/provider/config" - "github.com/mudler/go-pluggable" - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - "gopkg.in/yaml.v2" -) - -var _ = Describe("Bootstrap provider", func() { - Context("logging", func() { - e := &pluggable.Event{} - - BeforeEach(func() { - e = &pluggable.Event{} - }) - - It("logs to file", func() { - f, err := ioutil.TempFile(os.TempDir(), "tests") - Expect(err).ToNot(HaveOccurred()) - defer os.RemoveAll(f.Name()) - - cfg := &providerConfig.Config{ - C3OS: &providerConfig.C3OS{ - NetworkToken: "foo", - }, - } - dat, err := yaml.Marshal(cfg) - Expect(err).ToNot(HaveOccurred()) - payload := &bus.BootstrapPayload{Logfile: f.Name(), Config: string(dat)} - - dat, err = json.Marshal(payload) - Expect(err).ToNot(HaveOccurred()) - - e.Data = string(dat) - resp := Bootstrap(e) - dat, _ = json.Marshal(resp) - Expect(resp.Errored()).To(BeTrue(), string(dat)) - - data, err := ioutil.ReadFile(f.Name()) - Expect(err).ToNot(HaveOccurred()) - - Expect(string(data)).Should(ContainSubstring("Configuring VPN")) - }) - }) -}) diff --git a/internal/provider/challenge.go b/internal/provider/challenge.go deleted file mode 100644 index 5f21c23..0000000 --- a/internal/provider/challenge.go +++ /dev/null @@ -1,38 +0,0 @@ -package provider - -import ( - "encoding/json" - - "github.com/c3os-io/c3os/sdk/bus" - - providerConfig "github.com/c3os-io/c3os/internal/provider/config" - "github.com/c3os-io/c3os/pkg/config" - - "github.com/mudler/go-nodepair" - "github.com/mudler/go-pluggable" -) - -func Challenge(e *pluggable.Event) pluggable.EventResponse { - p := &bus.EventPayload{} - err := json.Unmarshal([]byte(e.Data), p) - if err != nil { - return ErrorEvent("Failed reading JSON input: %s input '%s'", err.Error(), e.Data) - } - - cfg := &providerConfig.Config{} - err = config.FromString(p.Config, cfg) - if err != nil { - return ErrorEvent("Failed reading JSON input: %s input '%s'", err.Error(), p.Config) - } - - tk := "" - if cfg.C3OS != nil && cfg.C3OS.NetworkToken != "" { - tk = cfg.C3OS.NetworkToken - } - if tk == "" { - tk = nodepair.GenerateToken() - } - return pluggable.EventResponse{ - Data: tk, - } -} diff --git a/internal/provider/challenge_test.go b/internal/provider/challenge_test.go deleted file mode 100644 index 06f8c02..0000000 --- a/internal/provider/challenge_test.go +++ /dev/null @@ -1,72 +0,0 @@ -package provider_test - -import ( - "encoding/json" - "io/ioutil" - "os" - - "github.com/c3os-io/c3os/sdk/bus" - - providerConfig "github.com/c3os-io/c3os/internal/provider/config" - - . "github.com/c3os-io/c3os/internal/provider" - "github.com/mudler/go-pluggable" - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - "gopkg.in/yaml.v2" -) - -var _ = Describe("Challenge provider", func() { - Context("network token", func() { - e := &pluggable.Event{} - - BeforeEach(func() { - e = &pluggable.Event{} - }) - - It("returns it if provided", func() { - f, err := ioutil.TempFile(os.TempDir(), "tests") - Expect(err).ToNot(HaveOccurred()) - defer os.RemoveAll(f.Name()) - - cfg := &providerConfig.Config{ - C3OS: &providerConfig.C3OS{ - NetworkToken: "foo", - }, - } - d, err := yaml.Marshal(cfg) - Expect(err).ToNot(HaveOccurred()) - - c := &bus.EventPayload{Config: string(d)} - dat, err := json.Marshal(c) - Expect(err).ToNot(HaveOccurred()) - - e.Data = string(dat) - resp := Challenge(e) - - Expect(string(resp.Data)).Should(ContainSubstring("foo")) - }) - - It("generates it if not provided", func() { - f, err := ioutil.TempFile(os.TempDir(), "tests") - Expect(err).ToNot(HaveOccurred()) - defer os.RemoveAll(f.Name()) - - cfg := &providerConfig.Config{ - C3OS: &providerConfig.C3OS{ - NetworkToken: "", - }, - } - d, err := yaml.Marshal(cfg) - Expect(err).ToNot(HaveOccurred()) - c := &bus.EventPayload{Config: string(d)} - dat, err := json.Marshal(c) - Expect(err).ToNot(HaveOccurred()) - - e.Data = string(dat) - resp := Challenge(e) - - Expect(len(string(resp.Data))).Should(BeNumerically(">", 12)) - }) - }) -}) diff --git a/internal/provider/common.go b/internal/provider/common.go deleted file mode 100644 index 0969c84..0000000 --- a/internal/provider/common.go +++ /dev/null @@ -1,11 +0,0 @@ -package provider - -import ( - "fmt" - - "github.com/mudler/go-pluggable" -) - -func ErrorEvent(format string, a ...interface{}) pluggable.EventResponse { - return pluggable.EventResponse{Error: fmt.Sprintf(format, a...)} -} diff --git a/internal/provider/config/config.go b/internal/provider/config/config.go deleted file mode 100644 index 2ce67b2..0000000 --- a/internal/provider/config/config.go +++ /dev/null @@ -1,24 +0,0 @@ -package config - -type C3OS struct { - NetworkToken string `yaml:"network_token,omitempty"` - NetworkID string `yaml:"network_id,omitempty"` - Role string `yaml:"role,omitempty"` - DNS bool `yaml:"dns,omitempty"` - LogLevel string `yaml:"loglevel,omitempty"` -} - -type Config struct { - C3OS *C3OS `yaml:"c3os,omitempty"` - K3sAgent K3s `yaml:"k3s-agent,omitempty"` - K3s K3s `yaml:"k3s,omitempty"` - VPN map[string]string `yaml:"vpn,omitempty"` -} - -type K3s struct { - Env map[string]string `yaml:"env,omitempty"` - ReplaceEnv bool `yaml:"replace_env,omitempty"` - ReplaceArgs bool `yaml:"replace_args,omitempty"` - Args []string `yaml:"args,omitempty"` - Enabled bool `yaml:"enabled,omitempty"` -} diff --git a/internal/provider/install.go b/internal/provider/install.go deleted file mode 100644 index e25721c..0000000 --- a/internal/provider/install.go +++ /dev/null @@ -1,36 +0,0 @@ -package provider - -import ( - "context" - "encoding/json" - - "github.com/c3os-io/c3os/sdk/bus" - - "github.com/mudler/go-nodepair" - "github.com/mudler/go-pluggable" -) - -func Install(e *pluggable.Event) pluggable.EventResponse { - cfg := &bus.InstallPayload{} - err := json.Unmarshal([]byte(e.Data), cfg) - if err != nil { - return ErrorEvent("Failed reading JSON input: %s", err.Error()) - } - - r := map[string]string{} - ctx := context.Background() - if err := nodepair.Receive(ctx, &r, nodepair.WithToken(cfg.Token)); err != nil { - return ErrorEvent("Failed reading JSON input: %s", err.Error()) - } - - payload, err := json.Marshal(r) - if err != nil { - return ErrorEvent("Failed marshalling JSON input: %s", err.Error()) - } - - return pluggable.EventResponse{ - State: "", - Data: string(payload), - Error: "", - } -} diff --git a/internal/provider/provider_suite_test.go b/internal/provider/provider_suite_test.go deleted file mode 100644 index c9a2885..0000000 --- a/internal/provider/provider_suite_test.go +++ /dev/null @@ -1,13 +0,0 @@ -package provider_test - -import ( - "testing" - - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" -) - -func TestInstaller(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "Provider Suite") -} diff --git a/internal/provider/vpn.go b/internal/provider/vpn.go deleted file mode 100644 index f71d97a..0000000 --- a/internal/provider/vpn.go +++ /dev/null @@ -1,92 +0,0 @@ -package provider - -import ( - "fmt" - "os" - "path/filepath" - "strings" - - "github.com/c3os-io/c3os/internal/machine" - "github.com/c3os-io/c3os/internal/machine/systemd" - providerConfig "github.com/c3os-io/c3os/internal/provider/config" - "github.com/c3os-io/c3os/internal/utils" - "github.com/c3os-io/c3os/pkg/config" - - yip "github.com/mudler/yip/pkg/schema" -) - -func SetupVPN(instance, apiAddress, rootDir string, start bool, c *providerConfig.Config) error { - - if c.C3OS == nil || c.C3OS.NetworkToken == "" { - return fmt.Errorf("no network token defined") - } - - svc, err := machine.EdgeVPN(instance, rootDir) - if err != nil { - return fmt.Errorf("could not create svc: %w", err) - } - - apiAddress = strings.ReplaceAll(apiAddress, "https://", "") - apiAddress = strings.ReplaceAll(apiAddress, "http://", "") - - vpnOpts := map[string]string{ - "EDGEVPNTOKEN": c.C3OS.NetworkToken, - "API": "true", - "APILISTEN": apiAddress, - "EDGEVPNLOWPROFILEVPN": "true", - "DHCP": "true", - "DHCPLEASEDIR": "/usr/local/.c3os/lease", - } - // Override opts with user-supplied - for k, v := range c.VPN { - vpnOpts[k] = v - } - - if c.C3OS.DNS { - vpnOpts["DNSADDRESS"] = "127.0.0.1:53" - vpnOpts["DNSFORWARD"] = "true" - if !utils.IsOpenRCBased() { - if _, err := os.Stat("/etc/sysconfig/network/config"); err == nil { - utils.WriteEnv("/etc/sysconfig/network/config", map[string]string{ //nolint:errcheck - "NETCONFIG_DNS_STATIC_SERVERS": "127.0.0.1", - }) - if utils.Flavor() == "opensuse" { - // TODO: This is dependant on wickedd, move this out in its own network detection block - svc, err := systemd.NewService(systemd.WithName("wickedd")) - if err == nil { - svc.Restart() //nolint:errcheck - } - } - } - } - if err := config.SaveCloudConfig("dns", yip.YipConfig{ - Name: "DNS Configuration", - Stages: map[string][]yip.Stage{ - config.NetworkStage.String(): {{Dns: yip.DNS{Nameservers: []string{"127.0.0.1"}}}}}, - }); err != nil { - return fmt.Errorf("could not create dns config: %w", err) - } - } - - os.MkdirAll("/etc/systemd/system.conf.d/", 0600) //nolint:errcheck - // Setup edgevpn instance - err = utils.WriteEnv(filepath.Join(rootDir, "/etc/systemd/system.conf.d/edgevpn-c3os.env"), vpnOpts) - if err != nil { - return fmt.Errorf("could not create write env file: %w", err) - } - - err = svc.WriteUnit() - if err != nil { - return fmt.Errorf("could not create write unit file: %w", err) - } - - if start { - err = svc.Start() - if err != nil { - return fmt.Errorf("could not start svc: %w", err) - } - - return svc.Enable() - } - return nil -} diff --git a/internal/role/auto.go b/internal/role/auto.go deleted file mode 100644 index 7db278c..0000000 --- a/internal/role/auto.go +++ /dev/null @@ -1,62 +0,0 @@ -package role - -import ( - "github.com/c3os-io/c3os/pkg/config" - - providerConfig "github.com/c3os-io/c3os/internal/provider/config" - utils "github.com/mudler/edgevpn/pkg/utils" - - service "github.com/mudler/edgevpn/api/client/service" -) - -func contains(slice []string, elem string) bool { - for _, s := range slice { - if elem == s { - return true - } - } - return false -} -func Auto(cc *config.Config, pconfig *providerConfig.Config) Role { - return func(c *service.RoleConfig) error { - advertizing, _ := c.Client.AdvertizingNodes() - actives, _ := c.Client.ActiveNodes() - - c.Logger.Info("Active nodes:", actives) - c.Logger.Info("Advertizing nodes:", advertizing) - - if len(advertizing) < 2 { - c.Logger.Info("Not enough nodes") - return nil - } - - // first get available nodes - nodes := advertizing - shouldBeLeader := utils.Leader(advertizing) - - lead, _ := c.Client.Get("auto", "leader") - - // From now on, only the leader keeps processing - // TODO: Make this more reliable with consensus - if shouldBeLeader != c.UUID && lead != c.UUID { - c.Logger.Infof("<%s> not a leader, leader is '%s', sleeping", c.UUID, shouldBeLeader) - return nil - } - - if shouldBeLeader == c.UUID && (lead == "" || !contains(nodes, lead)) { - if err := c.Client.Set("auto", "leader", c.UUID); err != nil { - c.Logger.Error(err) - return err - } - c.Logger.Info("Announcing ourselves as leader, backing off") - return nil - } - - if lead != c.UUID { - c.Logger.Info("Backing off, as we are not currently flagged as leader") - return nil - } - - return scheduleRoles(nodes, c, cc, pconfig) - } -} diff --git a/internal/role/common.go b/internal/role/common.go deleted file mode 100644 index 1efa51f..0000000 --- a/internal/role/common.go +++ /dev/null @@ -1,21 +0,0 @@ -package role - -import ( - "io/ioutil" - "os" - - service "github.com/mudler/edgevpn/api/client/service" -) - -type Role func(*service.RoleConfig) error - -func SentinelExist() bool { - if _, err := os.Stat("/usr/local/.c3os/deployed"); err == nil { - return true - } - return false -} - -func CreateSentinel() error { - return ioutil.WriteFile("/usr/local/.c3os/deployed", []byte{}, os.ModePerm) -} diff --git a/internal/role/master.go b/internal/role/master.go deleted file mode 100644 index ee9b447..0000000 --- a/internal/role/master.go +++ /dev/null @@ -1,155 +0,0 @@ -package role - -import ( - "encoding/base64" - "errors" - "fmt" - "io/ioutil" - "strings" - "time" - - "github.com/c3os-io/c3os/internal/machine" - "github.com/c3os-io/c3os/pkg/config" - - providerConfig "github.com/c3os-io/c3os/internal/provider/config" - "github.com/c3os-io/c3os/internal/utils" - - service "github.com/mudler/edgevpn/api/client/service" -) - -func propagateMasterData(ip string, c *service.RoleConfig) error { - defer func() { - // Avoid polluting the API. - // The ledger already retries in the background to update the blockchain, but it has - // a default timeout where it would stop trying afterwards. - // Each request here would have it's own background announce, so that can become expensive - // when network is having lot of changes on its way. - time.Sleep(30 * time.Second) - }() - - // If we are configured as master, always signal our role - if err := c.Client.Set("role", c.UUID, "master"); err != nil { - c.Logger.Error(err) - return err - } - - tokenB, err := ioutil.ReadFile("/var/lib/rancher/k3s/server/node-token") - if err != nil { - c.Logger.Error(err) - return err - } - - nodeToken := string(tokenB) - nodeToken = strings.TrimRight(nodeToken, "\n") - if nodeToken != "" { - err := c.Client.Set("nodetoken", "token", nodeToken) - if err != nil { - c.Logger.Error(err) - } - } - - kubeB, err := ioutil.ReadFile("/etc/rancher/k3s/k3s.yaml") - if err != nil { - c.Logger.Error(err) - return err - } - kubeconfig := string(kubeB) - if kubeconfig != "" { - err := c.Client.Set("kubeconfig", "master", base64.RawURLEncoding.EncodeToString(kubeB)) - if err != nil { - c.Logger.Error(err) - } - } - err = c.Client.Set("master", "ip", ip) - if err != nil { - c.Logger.Error(err) - } - return nil -} - -func Master(cc *config.Config, pconfig *providerConfig.Config) Role { - return func(c *service.RoleConfig) error { - - ip := utils.GetInterfaceIP("edgevpn0") - if ip == "" { - return errors.New("node doesn't have an ip yet") - } - - if pconfig.C3OS.Role != "" { - // propagate role if we were forced by configuration - // This unblocks eventual auto instances to try to assign roles - if err := c.Client.Set("role", c.UUID, pconfig.C3OS.Role); err != nil { - c.Logger.Error(err) - } - } - - if SentinelExist() { - c.Logger.Info("Node already configured, backing off") - return propagateMasterData(ip, c) - } - - // Configure k3s service to start on edgevpn0 - c.Logger.Info("Configuring k3s") - - svc, err := machine.K3s() - if err != nil { - return err - } - - k3sConfig := providerConfig.K3s{} - if pconfig.K3s.Enabled { - k3sConfig = pconfig.K3s - } - - env := map[string]string{} - if !k3sConfig.ReplaceEnv { - // Override opts with user-supplied - for k, v := range k3sConfig.Env { - env[k] = v - } - } else { - env = k3sConfig.Env - } - - if err := utils.WriteEnv(machine.K3sEnvUnit("k3s"), - env, - ); err != nil { - return err - } - - args := []string{"--flannel-iface=edgevpn0"} - if k3sConfig.ReplaceArgs { - args = k3sConfig.Args - } else { - args = append(args, k3sConfig.Args...) - } - - k3sbin := utils.K3sBin() - if k3sbin == "" { - return fmt.Errorf("no k3s binary found (?)") - } - - if err := svc.OverrideCmd(fmt.Sprintf("%s server %s", k3sbin, strings.Join(args, " "))); err != nil { - return err - } - - if err := svc.Start(); err != nil { - return err - } - - if err := svc.Enable(); err != nil { - return err - } - - if err := propagateMasterData(ip, c); err != nil { - return err - } - - return CreateSentinel() - } -} - -// TODO: https://rancher.com/docs/k3s/latest/en/installation/ha-embedded/ -func HAMaster(c *service.RoleConfig) { - c.Logger.Info("HA Role not implemented yet") -} diff --git a/internal/role/schedule.go b/internal/role/schedule.go deleted file mode 100644 index a15b4fd..0000000 --- a/internal/role/schedule.go +++ /dev/null @@ -1,80 +0,0 @@ -package role - -import ( - "math/rand" - "time" - - "github.com/c3os-io/c3os/pkg/config" - - providerConfig "github.com/c3os-io/c3os/internal/provider/config" - service "github.com/mudler/edgevpn/api/client/service" -) - -// scheduleRoles assigns roles to nodes. Meant to be called only by leaders -// TODO: HA-Auto. -func scheduleRoles(nodes []string, c *service.RoleConfig, cc *config.Config, pconfig *providerConfig.Config) error { - rand.Seed(time.Now().Unix()) - - // Assign roles to nodes - currentRoles := map[string]string{} - - existsMaster := false - unassignedNodes := []string{} - for _, a := range nodes { - role, _ := c.Client.Get("role", a) - currentRoles[a] = role - if role == "master" { - existsMaster = true - } else if role == "" { - unassignedNodes = append(unassignedNodes, a) - } - } - - c.Logger.Infof("I'm the leader. My UUID is: %s.\n Current assigned roles: %+v", c.UUID, currentRoles) - c.Logger.Infof("Master already present: %t", existsMaster) - c.Logger.Infof("Unassigned nodes: %+v", unassignedNodes) - - if !existsMaster && len(unassignedNodes) > 0 { - var selected string - toSelect := unassignedNodes - - // Avoid to schedule to ourselves if we have a static role - if pconfig.C3OS.Role != "" { - toSelect = []string{} - for _, u := range unassignedNodes { - if u != c.UUID { - toSelect = append(toSelect, u) - } - } - } - - // select one node without roles to become master - if len(toSelect) == 1 { - selected = toSelect[0] - } else { - selected = toSelect[rand.Intn(len(toSelect)-1)] - } - - if err := c.Client.Set("role", selected, "master"); err != nil { - return err - } - c.Logger.Info("-> Set master to", selected) - currentRoles[selected] = "master" - // Return here, so next time we get called - // makes sure master is set. - return nil - } - - // cycle all empty roles and assign worker roles - for _, uuid := range unassignedNodes { - if err := c.Client.Set("role", uuid, "worker"); err != nil { - c.Logger.Error(err) - return err - } - c.Logger.Info("-> Set worker to", uuid) - } - - c.Logger.Info("Done scheduling") - - return nil -} diff --git a/internal/role/worker.go b/internal/role/worker.go deleted file mode 100644 index 7e95d5e..0000000 --- a/internal/role/worker.go +++ /dev/null @@ -1,113 +0,0 @@ -package role - -import ( - "errors" - "fmt" - "strings" - - "github.com/c3os-io/c3os/internal/machine" - "github.com/c3os-io/c3os/internal/utils" - "github.com/c3os-io/c3os/pkg/config" - - providerConfig "github.com/c3os-io/c3os/internal/provider/config" - service "github.com/mudler/edgevpn/api/client/service" -) - -func Worker(cc *config.Config, pconfig *providerConfig.Config) Role { - return func(c *service.RoleConfig) error { - - if pconfig.C3OS.Role != "" { - // propagate role if we were forced by configuration - // This unblocks eventual auto instances to try to assign roles - if err := c.Client.Set("role", c.UUID, pconfig.C3OS.Role); err != nil { - return err - } - } - - if SentinelExist() { - c.Logger.Info("Node already configured, backing off") - return nil - } - - masterIP, _ := c.Client.Get("master", "ip") - if masterIP == "" { - c.Logger.Info("MasterIP not there still..") - return nil - } - - nodeToken, _ := c.Client.Get("nodetoken", "token") - if masterIP == "" { - c.Logger.Info("nodetoken not there still..") - return nil - } - - nodeToken = strings.TrimRight(nodeToken, "\n") - - ip := utils.GetInterfaceIP("edgevpn0") - if ip == "" { - return errors.New("node doesn't have an ip yet") - } - - c.Logger.Info("Configuring k3s-agent", ip, masterIP, nodeToken) - - svc, err := machine.K3sAgent() - if err != nil { - return err - } - - k3sConfig := providerConfig.K3s{} - if pconfig.K3sAgent.Enabled { - k3sConfig = pconfig.K3sAgent - } - - env := map[string]string{ - "K3S_URL": fmt.Sprintf("https://%s:6443", masterIP), - "K3S_TOKEN": nodeToken, - } - - if !k3sConfig.ReplaceEnv { - // Override opts with user-supplied - for k, v := range k3sConfig.Env { - env[k] = v - } - } else { - env = k3sConfig.Env - } - - // Setup systemd unit and starts it - if err := utils.WriteEnv(machine.K3sEnvUnit("k3s-agent"), - env, - ); err != nil { - return err - } - - args := []string{ - "--with-node-id", - fmt.Sprintf("--node-ip %s", ip), - "--flannel-iface=edgevpn0", - } - if k3sConfig.ReplaceArgs { - args = k3sConfig.Args - } else { - args = append(args, k3sConfig.Args...) - } - - k3sbin := utils.K3sBin() - if k3sbin == "" { - return fmt.Errorf("no k3s binary found (?)") - } - if err := svc.OverrideCmd(fmt.Sprintf("%s agent %s", k3sbin, strings.Join(args, " "))); err != nil { - return err - } - - if err := svc.Start(); err != nil { - return err - } - - if err := svc.Enable(); err != nil { - return err - } - - return CreateSentinel() - } -} diff --git a/internal/utils/console.go b/internal/utils/console.go deleted file mode 100644 index 4b9c12d..0000000 --- a/internal/utils/console.go +++ /dev/null @@ -1,37 +0,0 @@ -package utils - -import ( - "bufio" - "bytes" - "fmt" - "image" - "os" - "strings" - - "github.com/pterm/pterm" - "github.com/qeesung/image2ascii/convert" -) - -func Prompt(t string) (string, error) { - if t != "" { - pterm.Info.Println(t) - } - answer, err := bufio.NewReader(os.Stdin).ReadString('\n') - if err != nil { - return "", err - } - - return strings.TrimSpace(answer), nil -} - -func PrintBanner(d []byte) { - img, _, _ := image.Decode(bytes.NewReader(d)) - - convertOptions := convert.DefaultOptions - convertOptions.FixedWidth = 100 - convertOptions.FixedHeight = 40 - - // Create the image converter - converter := convert.NewImageConverter() - fmt.Print(converter.Image2ASCIIString(img, &convertOptions)) -} diff --git a/internal/utils/sh.go b/internal/utils/sh.go deleted file mode 100644 index 25758ec..0000000 --- a/internal/utils/sh.go +++ /dev/null @@ -1,43 +0,0 @@ -package utils - -import ( - "bytes" - "io/ioutil" - "os" - "os/exec" - - "github.com/joho/godotenv" -) - -func SH(c string) (string, error) { - o, err := exec.Command("/bin/sh", "-c", c).CombinedOutput() - return string(o), err -} - -func WriteEnv(envFile string, config map[string]string) error { - content, _ := ioutil.ReadFile(envFile) - env, _ := godotenv.Unmarshal(string(content)) - - for key, val := range config { - env[key] = val - } - - return godotenv.Write(env, envFile) -} - -func Shell() *exec.Cmd { - cmd := exec.Command("/bin/sh") - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - cmd.Stdin = os.Stdin - return cmd -} - -func ShellSTDIN(s, c string) (string, error) { - cmd := exec.Command("/bin/sh", "-c", c) - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - cmd.Stdin = bytes.NewBuffer([]byte(s)) - o, err := cmd.CombinedOutput() - return string(o), err -} diff --git a/internal/utils/signal.go b/internal/utils/signal.go deleted file mode 100644 index 62af370..0000000 --- a/internal/utils/signal.go +++ /dev/null @@ -1,15 +0,0 @@ -package utils - -import ( - "os" - "os/signal" -) - -func OnSignal(fn func(), sig ...os.Signal) { - sigs := make(chan os.Signal, 1) - signal.Notify(sigs, sig...) - go func() { - <-sigs - fn() - }() -} diff --git a/internal/utils/strings.go b/internal/utils/strings.go deleted file mode 100644 index 39c5109..0000000 --- a/internal/utils/strings.go +++ /dev/null @@ -1,20 +0,0 @@ -package utils - -import ( - "math/rand" - "time" -) - -func init() { - rand.Seed(time.Now().UnixNano()) -} - -var letterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") - -func RandStringRunes(n int) string { - b := make([]rune, n) - for i := range b { - b[i] = letterRunes[rand.Intn(len(letterRunes))] - } - return string(b) -} diff --git a/internal/utils/system.go b/internal/utils/system.go deleted file mode 100644 index 56dfa80..0000000 --- a/internal/utils/system.go +++ /dev/null @@ -1,94 +0,0 @@ -package utils - -import ( - "fmt" - "net" - "os" - "strings" - - "github.com/joho/godotenv" - "github.com/pterm/pterm" -) - -func Reboot() { - pterm.Info.Println("Rebooting node") - SH("reboot") //nolint:errcheck -} - -func PowerOFF() { - pterm.Info.Println("Shutdown node") - if IsOpenRCBased() { - SH("poweroff") //nolint:errcheck - } else { - SH("shutdown") //nolint:errcheck - } -} - -func Version() string { - release, _ := godotenv.Read("/etc/os-release") - v := release["VERSION"] - v = strings.ReplaceAll(v, "+k3s1-c3OS", "-") - v = strings.ReplaceAll(v, "+k3s-c3OS", "-") - return strings.ReplaceAll(v, "c3OS", "") -} - -func OSRelease(key string) (string, error) { - release, err := godotenv.Read("/etc/os-release") - if err != nil { - return "", err - } - v, exists := release[key] - if !exists { - return "", fmt.Errorf("key not found") - } - return v, nil -} - -func Flavor() string { - release, _ := godotenv.Read("/etc/os-release") - v := release["NAME"] - return strings.ReplaceAll(v, "c3os-", "") -} - -func IsOpenRCBased() bool { - f := Flavor() - return f == "alpine" || f == "alpine-arm-rpi" -} - -func GetInterfaceIP(in string) string { - ifaces, err := net.Interfaces() - if err != nil { - fmt.Println("failed getting system interfaces") - return "" - } - for _, i := range ifaces { - if i.Name == in { - addrs, _ := i.Addrs() - // handle err - for _, addr := range addrs { - var ip net.IP - switch v := addr.(type) { - case *net.IPNet: - ip = v.IP - case *net.IPAddr: - ip = v.IP - } - if ip != nil { - return ip.String() - - } - } - } - } - return "" -} - -func K3sBin() string { - for _, p := range []string{"/usr/bin/k3s", "/usr/local/bin/k3s"} { - if _, err := os.Stat(p); err == nil { - return p - } - } - - return "" -} diff --git a/internal/utils/token.go b/internal/utils/token.go deleted file mode 100644 index 884ed53..0000000 --- a/internal/utils/token.go +++ /dev/null @@ -1,13 +0,0 @@ -package utils - -import "strings" - -const sep = "_CREDENTIALS_" - -func EncodeRecoveryToken(data ...string) string { - return strings.Join(data, sep) -} - -func DecodeRecoveryToken(recoverytoken string) []string { - return strings.Split(recoverytoken, sep) -}