mirror of
https://github.com/kairos-io/kairos-sdk.git
synced 2025-08-01 23:39:08 +00:00
art: Drop provider from c3os code
Part of: https://github.com/c3os-io/c3os/issues/68
This commit is contained in:
parent
7a148fe5bb
commit
52cb8cbc29
88
pkg/machine/bootcmdline.go
Normal file
88
pkg/machine/bootcmdline.go
Normal 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
|
||||
}
|
29
pkg/machine/bootcmdline_test.go
Normal file
29
pkg/machine/bootcmdline_test.go
Normal 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
157
pkg/machine/machine.go
Normal 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
|
||||
}
|
13
pkg/machine/machine_suite_test.go
Normal file
13
pkg/machine/machine_suite_test.go
Normal 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")
|
||||
}
|
19
pkg/machine/openrc/edgevpn.go
Normal file
19
pkg/machine/openrc/edgevpn.go
Normal 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`
|
98
pkg/machine/openrc/unit.go
Normal file
98
pkg/machine/openrc/unit.go
Normal 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()
|
||||
}
|
12
pkg/machine/systemd/edgevpn.go
Normal file
12
pkg/machine/systemd/edgevpn.go
Normal 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
115
pkg/machine/systemd/unit.go
Normal 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
|
||||
}
|
Loading…
Reference in New Issue
Block a user