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 7a148fe5bb
commit 52cb8cbc29
8 changed files with 531 additions and 0 deletions

View File

@ -0,0 +1,88 @@
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
}

View File

@ -0,0 +1,29 @@
package machine_test
import (
"io/ioutil"
"os"
. "github.com/c3os-io/c3os/pkg/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"))
})
})
})

157
pkg/machine/machine.go Normal file
View File

@ -0,0 +1,157 @@
package machine
import (
"fmt"
"io/ioutil"
"os"
"strings"
"github.com/c3os-io/c3os/pkg/machine/openrc"
"github.com/c3os-io/c3os/pkg/machine/systemd"
"github.com/denisbrodbeck/machineid"
"github.com/c3os-io/c3os/pkg/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
}

View File

@ -0,0 +1,13 @@
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")
}

View File

@ -0,0 +1,19 @@
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`

View File

@ -0,0 +1,98 @@
package openrc
import (
"fmt"
"io/ioutil"
"path/filepath"
"strings"
"github.com/c3os-io/c3os/pkg/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()
}

View File

@ -0,0 +1,12 @@
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`

115
pkg/machine/systemd/unit.go Normal file
View File

@ -0,0 +1,115 @@
package systemd
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
"github.com/c3os-io/c3os/pkg/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
}