From cd82032ad505400b1182ec8f832dbf84212beb10 Mon Sep 17 00:00:00 2001 From: MdSahil-oss Date: Thu, 11 Apr 2024 00:02:01 +0530 Subject: [PATCH 1/2] feat: init commit Signed-off-by: MdSahil-oss --- go.mod | 1 + go.sum | 3 + machine/platform/systemd/manage.go | 18 +++++ machine/platform/systemd/options.go | 45 ++++++++++++ machine/platform/systemd/systemd.go | 110 ++++++++++++++++++++++++++++ 5 files changed, 177 insertions(+) create mode 100644 machine/platform/systemd/manage.go create mode 100644 machine/platform/systemd/options.go create mode 100644 machine/platform/systemd/systemd.go diff --git a/go.mod b/go.mod index f86505c3c..b9249d916 100644 --- a/go.mod +++ b/go.mod @@ -167,6 +167,7 @@ require ( github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect + github.com/kardianos/service v1.2.2 // indirect github.com/kevinburke/ssh_config v1.2.0 // indirect github.com/klauspost/compress v1.17.4 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect diff --git a/go.sum b/go.sum index 10b7044aa..774903f74 100644 --- a/go.sum +++ b/go.sum @@ -732,6 +732,8 @@ github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1 github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/kardianos/service v1.2.2 h1:ZvePhAHfvo0A7Mftk/tEzqEZ7Q4lgnR8sGz4xu1YX60= +github.com/kardianos/service v1.2.2/go.mod h1:CIMRFEJVL+0DS1a3Nx06NaMn4Dz63Ng6O7dl0qH0zVM= github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4= github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= @@ -1429,6 +1431,7 @@ golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200916030750-2334cc1a136f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200922070232-aee5d888a860/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201117170446-d9b008d0a637/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/machine/platform/systemd/manage.go b/machine/platform/systemd/manage.go new file mode 100644 index 000000000..660afa86d --- /dev/null +++ b/machine/platform/systemd/manage.go @@ -0,0 +1,18 @@ +package systemd + +import "github.com/kardianos/service" + +type startStop struct{} + +func (p *startStop) Start(s service.Service) error { + // Start should not block. Do the actual work async. + go p.run() + return nil +} +func (p *startStop) run() { + // Do work here +} +func (p *startStop) Stop(s service.Service) error { + // Stop should not block. Return with a few seconds. + return nil +} diff --git a/machine/platform/systemd/options.go b/machine/platform/systemd/options.go new file mode 100644 index 000000000..d4902c6f4 --- /dev/null +++ b/machine/platform/systemd/options.go @@ -0,0 +1,45 @@ +package systemd + +import "github.com/kardianos/service" + +type ServiceConfigOption func(*ServiceConfig) error + +// WithName sets the name of the systemd process. +func WithName(name string) ServiceConfigOption { + return func(config *ServiceConfig) error { + config.name = name + return nil + } +} + +// WithDisplayName sets the display-name of the systemd process. +func WithDisplayName(dName string) ServiceConfigOption { + return func(config *ServiceConfig) error { + config.displayName = dName + return nil + } +} + +// WithDescription sets the description of the systemd process. +func WithDescription(desc string) ServiceConfigOption { + return func(config *ServiceConfig) error { + config.description = desc + return nil + } +} + +// WithDependencies sets the dependencies of the systemd process. +func WithDependencies(deps []string) ServiceConfigOption { + return func(config *ServiceConfig) error { + config.dependencies = deps + return nil + } +} + +// WithOptions sets the options of the systemd process. +func WithOptions(opts service.KeyValue) ServiceConfigOption { + return func(config *ServiceConfig) error { + config.option = opts + return nil + } +} diff --git a/machine/platform/systemd/systemd.go b/machine/platform/systemd/systemd.go new file mode 100644 index 000000000..cb81bad54 --- /dev/null +++ b/machine/platform/systemd/systemd.go @@ -0,0 +1,110 @@ +package systemd + +import ( + "context" + "fmt" + "os" + + "github.com/kardianos/service" + machinev1alpha1 "kraftkit.sh/api/machine/v1alpha1" +) + +type ServiceConfig struct { + name string + displayName string + description string + dependencies []string + option service.KeyValue + + service service.Service + logger service.Logger +} + +func NewMachineV1alpha1ServiceSystemdWrapper(ctx context.Context, opts ...ServiceConfigOption) (ServiceConfig, error) { + config := ServiceConfig{} + + for _, opt := range opts { + if err := opt(&config); err != nil { + return config, err + } + } + + return config, nil +} + +func (sc ServiceConfig) Create(ctx context.Context, machine *machinev1alpha1.Machine) (*machinev1alpha1.Machine, error) { + var err error + uid := os.Getuid() + if uid != 0 { + return machine, fmt.Errorf("requires root permission") + } + + svcConfig := &service.Config{ + Name: sc.name, + DisplayName: sc.displayName, + Description: sc.description, + Dependencies: sc.dependencies, + Option: sc.option, + } + sys := service.ChosenSystem() + sc.service, err = sys.New(&startStop{}, svcConfig) + if err != nil { + return machine, err + } + + errs := make(chan error, 5) + sc.logger, err = sc.service.Logger(errs) + if err != nil { + return machine, err + } + + err = sc.service.Install() + if err != nil { + return machine, err + } + + return machine, nil +} + +func (sc ServiceConfig) Start(ctx context.Context, machine *machinev1alpha1.Machine) (*machinev1alpha1.Machine, error) { + // Implement `Start()` -> to start running systemd process, It also checks for the user permission same as above first. + return &machinev1alpha1.Machine{}, nil +} + +func (sc ServiceConfig) Pause(ctx context.Context, machine *machinev1alpha1.Machine) (*machinev1alpha1.Machine, error) { + return &machinev1alpha1.Machine{}, nil +} + +func (sc ServiceConfig) Stop(ctx context.Context, machine *machinev1alpha1.Machine) (*machinev1alpha1.Machine, error) { + // Implement `Stop()` -> to stop running systemd process, It also checks for the user permission same as above first. + return &machinev1alpha1.Machine{}, nil +} + +func (sc ServiceConfig) Update(ctx context.Context, machine *machinev1alpha1.Machine) (*machinev1alpha1.Machine, error) { + return &machinev1alpha1.Machine{}, nil +} + +func (sc ServiceConfig) Delete(ctx context.Context, machine *machinev1alpha1.Machine) (*machinev1alpha1.Machine, error) { + // Implement `Delete()` -> to uninstall systemd process, + // It also checks for the user permission same as above first & stop the process if it's in running state. + return &machinev1alpha1.Machine{}, nil +} + +func (sc ServiceConfig) Get(ctx context.Context, machine *machinev1alpha1.Machine) (*machinev1alpha1.Machine, error) { + // Implement `Get()` -> to return the systemd process with following info: `name`, `status` & etc. + return &machinev1alpha1.Machine{}, nil +} + +func (sc ServiceConfig) List(ctx context.Context, machineList *machinev1alpha1.MachineList) (*machinev1alpha1.MachineList, error) { + return &machinev1alpha1.MachineList{}, nil +} + +func (sc ServiceConfig) Watch(ctx context.Context, machine *machinev1alpha1.Machine) (chan *machinev1alpha1.Machine, chan error, error) { + events := make(chan *machinev1alpha1.Machine) + return events, nil, nil +} + +func (sc ServiceConfig) Logs(ctx context.Context, machine *machinev1alpha1.Machine) (chan string, chan error, error) { + logs := make(chan string) + return logs, nil, nil +} From 7e6a246ac7e7b471cf43df97c33d6c39e410d807 Mon Sep 17 00:00:00 2001 From: MdSahil-oss Date: Fri, 26 Apr 2024 18:01:51 +0530 Subject: [PATCH 2/2] Adds basic implementation for Systemd and adds systemd support for kraft run cmd Signed-off-by: MdSahil-oss --- go.mod | 2 +- internal/cli/kraft/run/run.go | 70 ++++++++++++- internal/cli/kraft/run/utils.go | 63 ++++++++++++ machine/platform/systemd/manage.go | 5 +- machine/platform/systemd/options.go | 36 +++++-- machine/platform/systemd/systemd.go | 153 ++++++++++++++++++++++------ 6 files changed, 282 insertions(+), 47 deletions(-) diff --git a/go.mod b/go.mod index b9249d916..7be7cb2db 100644 --- a/go.mod +++ b/go.mod @@ -39,6 +39,7 @@ require ( github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 github.com/google/uuid v1.6.0 github.com/henvic/httpretty v0.1.3 + github.com/kardianos/service v1.2.2 github.com/kubescape/go-git-url v0.0.30 github.com/mattn/go-colorable v0.1.13 github.com/mattn/go-isatty v0.0.20 @@ -167,7 +168,6 @@ require ( github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/kardianos/service v1.2.2 // indirect github.com/kevinburke/ssh_config v1.2.0 // indirect github.com/klauspost/compress v1.17.4 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect diff --git a/internal/cli/kraft/run/run.go b/internal/cli/kraft/run/run.go index 96d868eac..9353bf448 100644 --- a/internal/cli/kraft/run/run.go +++ b/internal/cli/kraft/run/run.go @@ -8,8 +8,12 @@ import ( "context" "errors" "fmt" + "os" + "path/filepath" + "strings" "github.com/MakeNowJust/heredoc" + "github.com/kardianos/service" "github.com/sirupsen/logrus" "github.com/spf13/cobra" corev1 "k8s.io/api/core/v1" @@ -19,11 +23,14 @@ import ( machineapi "kraftkit.sh/api/machine/v1alpha1" "kraftkit.sh/cmdfactory" "kraftkit.sh/config" + "kraftkit.sh/internal/cli/kraft/remove" "kraftkit.sh/internal/cli/kraft/start" + "kraftkit.sh/internal/cli/kraft/stop" "kraftkit.sh/internal/set" "kraftkit.sh/iostreams" "kraftkit.sh/log" mplatform "kraftkit.sh/machine/platform" + "kraftkit.sh/machine/platform/systemd" "kraftkit.sh/packmanager" "kraftkit.sh/tui/selection" ukarch "kraftkit.sh/unikraft/arch" @@ -51,6 +58,7 @@ type RunOptions struct { Rootfs string `long:"rootfs" usage:"Specify a path to use as root file system (can be volume or initramfs)"` RunAs string `long:"as" usage:"Force a specific runner"` Runtime string `long:"runtime" short:"r" usage:"Set an alternative unikernel runtime"` + Systemd bool `long:"systemd" usage:"runs unikernel as systemd process"` Target string `long:"target" short:"t" usage:"Explicitly use the defined project target"` Volumes []string `long:"volume" short:"v" usage:"Bind a volume to the instance"` WithKernelDbg bool `long:"symbolic" usage:"Use the debuggable (symbolic) unikernel"` @@ -376,15 +384,71 @@ func (opts *RunOptions) Run(ctx context.Context, args []string) error { return err } - if opts.NoStart { + if opts.NoStart && !opts.Systemd { // Output the name of the instance such that it can be piped fmt.Fprintf(iostreams.G(ctx).Out, "%s\n", machine.Name) return nil } - return start.Start(ctx, &start.StartOptions{ - Detach: opts.Detach, + err = start.Start(ctx, &start.StartOptions{ + Detach: opts.Systemd || opts.Detach, Platform: opts.platform.String(), Remove: opts.Remove, }, machine.Name) + + // Installs systemd serivce that runs a new unikernel instance on each start. + if err == nil && opts.Systemd { + // Stops & removes the created testing instance if that is still runnning & present. + if err = stop.Stop(ctx, &stop.StopOptions{Platform: opts.platform.String()}, machine.Name); err != nil { + log.G(ctx).Debugf("instance %s was already stopped", machine.Name) + } + if err = remove.Remove(ctx, &remove.RemoveOptions{Platform: opts.platform.String()}, machine.Name); err != nil { + log.G(ctx).Errorf("could not remove %s", machine.Name) + } + + opts.Name = machine.Name + opts.Remove = true + sysdArgs := []string{"run"} + sysdArgs = append(sysdArgs, opts.GetArgs()...) + + if len(args) > 0 { + if strings.HasPrefix(args[0], ".") { + pwd, err := os.Getwd() + if err != nil { + return err + } + args[0] = filepath.Join(pwd, args[0]) + } + sysdArgs = append(sysdArgs, args[0]) + } else { + sysdArgs = append(sysdArgs, opts.workdir) + } + + svcConfig, err := systemd.NewMachineV1alpha1ServiceSystemdWrapper( + ctx, + systemd.WithName(machine.Name), + systemd.WithDescription("created by Kraftkit"), + systemd.WithArguments(sysdArgs), + systemd.WithOptions(service.KeyValue{ + "Restart": "never", + }), + ) + if err != nil { + return err + } + + machine, err = svcConfig.Create(ctx, machine) + if err != nil { + return err + } + log.G(ctx).Infof("created a systemd process named %s ", svcConfig.Name) + + _, err = svcConfig.Start(ctx, machine) + if err != nil { + return err + } + log.G(ctx).Infof("started running %s as systemd process", svcConfig.Name) + } + + return err } diff --git a/internal/cli/kraft/run/utils.go b/internal/cli/kraft/run/utils.go index 5a9a04c5d..66af13502 100644 --- a/internal/cli/kraft/run/utils.go +++ b/internal/cli/kraft/run/utils.go @@ -459,3 +459,66 @@ func (opts *RunOptions) parseEnvs(_ context.Context, machine *machineapi.Machine return nil } + +// GetArgs returns all the arguments of run command as an array of strings. +func (opts *RunOptions) GetArgs() []string { + args := []string{} + if opts.Detach { + args = append(args, "--detach") + } + if opts.DisableAccel { + args = append(args, "--disable-acceleration") + } + if opts.PrefixName { + args = append(args, "--prefix-name") + } + if opts.WithKernelDbg { + args = append(args, "--symbolic") + } + if opts.Remove { + args = append(args, "--rm") + } + if len(opts.Name) > 0 { + args = append(args, "--name", opts.Name) + } + if len(opts.Architecture) > 0 { + args = append(args, "--arch", opts.Architecture) + } + if len(opts.RunAs) > 0 { + args = append(args, "--as", opts.RunAs) + } + if len(opts.KernelArgs) > 0 { + args = append(args, "--kernel-arg", strings.Join(opts.KernelArgs, " ")) + } + if len(opts.Kraftfile) > 0 { + args = append(args, "--kraftfile", opts.Kraftfile) + } + if len(opts.MacAddress) > 0 { + args = append(args, "--mac", opts.MacAddress) + } + if len(opts.Memory) > 0 { + args = append(args, "--memory", opts.Memory) + } + if len(opts.Networks) > 0 { + args = append(args, "--network", strings.Join(opts.Networks, " ")) + } + if len(opts.Platform) > 0 { + args = append(args, "--plat", opts.Platform) + } + if len(opts.Ports) > 0 { + args = append(args, "--port", strings.Join(opts.Ports, " ")) + } + if len(opts.Prefix) > 0 { + args = append(args, "--prefix", opts.Prefix) + } + if len(opts.Rootfs) > 0 { + args = append(args, "--rootfs", opts.Rootfs) + } + if len(opts.Target) > 0 { + args = append(args, "--target ", opts.Target) + } + if len(opts.Volumes) > 0 { + args = append(args, "--volume", strings.Join(opts.Volumes, " ")) + } + return args +} diff --git a/machine/platform/systemd/manage.go b/machine/platform/systemd/manage.go index 660afa86d..6ba07ee50 100644 --- a/machine/platform/systemd/manage.go +++ b/machine/platform/systemd/manage.go @@ -9,9 +9,12 @@ func (p *startStop) Start(s service.Service) error { go p.run() return nil } -func (p *startStop) run() { + +func (p *startStop) run() error { // Do work here + return nil } + func (p *startStop) Stop(s service.Service) error { // Stop should not block. Return with a few seconds. return nil diff --git a/machine/platform/systemd/options.go b/machine/platform/systemd/options.go index d4902c6f4..c64bed246 100644 --- a/machine/platform/systemd/options.go +++ b/machine/platform/systemd/options.go @@ -4,42 +4,58 @@ import "github.com/kardianos/service" type ServiceConfigOption func(*ServiceConfig) error -// WithName sets the name of the systemd process. +// WithName sets the name of systemd service. func WithName(name string) ServiceConfigOption { return func(config *ServiceConfig) error { - config.name = name + config.Name = name return nil } } -// WithDisplayName sets the display-name of the systemd process. +// WithDisplayName sets the display-name of systemd service. func WithDisplayName(dName string) ServiceConfigOption { return func(config *ServiceConfig) error { - config.displayName = dName + config.DisplayName = dName return nil } } -// WithDescription sets the description of the systemd process. +// WithDescription sets the description/heading of systemd service. func WithDescription(desc string) ServiceConfigOption { return func(config *ServiceConfig) error { - config.description = desc + config.Description = desc return nil } } -// WithDependencies sets the dependencies of the systemd process. +// WithDependencies sets the dependencies of systemd service. func WithDependencies(deps []string) ServiceConfigOption { return func(config *ServiceConfig) error { - config.dependencies = deps + config.Dependencies = deps return nil } } -// WithOptions sets the options of the systemd process. +// WithEnvVars sets the environment variables for systemd service. +func WithEnvVars(envVars map[string]string) ServiceConfigOption { + return func(config *ServiceConfig) error { + config.EnvVars = envVars + return nil + } +} + +// WithArguments sets the arguments to the command executed by systemd service. +func WithArguments(args []string) ServiceConfigOption { + return func(config *ServiceConfig) error { + config.Arguments = args + return nil + } +} + +// WithOptions sets the options of systemd service. func WithOptions(opts service.KeyValue) ServiceConfigOption { return func(config *ServiceConfig) error { - config.option = opts + config.Option = opts return nil } } diff --git a/machine/platform/systemd/systemd.go b/machine/platform/systemd/systemd.go index cb81bad54..b07327fc4 100644 --- a/machine/platform/systemd/systemd.go +++ b/machine/platform/systemd/systemd.go @@ -2,63 +2,122 @@ package systemd import ( "context" + "encoding/json" "fmt" "os" + "path/filepath" "github.com/kardianos/service" machinev1alpha1 "kraftkit.sh/api/machine/v1alpha1" + "kraftkit.sh/config" ) type ServiceConfig struct { - name string - displayName string - description string - dependencies []string - option service.KeyValue + Name string `json:"Name"` + DisplayName string `json:"DisplayName,omitempty"` + Description string `json:"Description"` + Dependencies []string `json:"Dependencies,omitempty"` + Arguments []string `json:"Arguments"` + Option service.KeyValue `json:"Option,omitempty"` + EnvVars map[string]string `json:"EnvVars,omitempty"` service service.Service logger service.Logger } +// NewMachineV1alpha1ServiceSystemdWrapper creates a new systemd service. func NewMachineV1alpha1ServiceSystemdWrapper(ctx context.Context, opts ...ServiceConfigOption) (ServiceConfig, error) { - config := ServiceConfig{} + svcConfig := ServiceConfig{} + + if uid := os.Getuid(); uid != 0 { + return svcConfig, fmt.Errorf("requires root permission") + } for _, opt := range opts { - if err := opt(&config); err != nil { - return config, err + if err := opt(&svcConfig); err != nil { + return svcConfig, err } } - return config, nil + // Creates service config file at `$HOME/.local/share/kraftkit/runtime/systemd/`. + byteJson, err := json.Marshal(svcConfig) + if err != nil { + return svcConfig, err + } + + systemdDir := filepath.Join(config.G[config.KraftKit](ctx).RuntimeDir, "systemd") + if err = os.MkdirAll(systemdDir, 0700); err != nil { + return svcConfig, err + } + + if err = os.WriteFile( + filepath.Join(systemdDir, svcConfig.Name+".json"), + byteJson, + 0644, + ); err != nil { + return svcConfig, err + } + + err = svcConfig.initService() + return svcConfig, err } -func (sc ServiceConfig) Create(ctx context.Context, machine *machinev1alpha1.Machine) (*machinev1alpha1.Machine, error) { - var err error - uid := os.Getuid() - if uid != 0 { - return machine, fmt.Errorf("requires root permission") +// GetMachineV1alpha1ServiceSystemdWrapper returns existing systemd service. +func GetMachineV1alpha1ServiceSystemdWrapper(ctx context.Context, name string) (ServiceConfig, error) { + svcConfig := ServiceConfig{} + + if uid := os.Getuid(); uid != 0 { + return svcConfig, fmt.Errorf("requires root permission") + } + + byteJson, err := os.ReadFile(filepath.Join(config.G[config.KraftKit](ctx).RuntimeDir, "systemd", name+".json")) + if err != nil { + // Return `Service is not pre-configured` + return svcConfig, err } - svcConfig := &service.Config{ - Name: sc.name, - DisplayName: sc.displayName, - Description: sc.description, - Dependencies: sc.dependencies, - Option: sc.option, + if err = json.Unmarshal(byteJson, &svcConfig); err != nil { + return svcConfig, err } + + err = svcConfig.initService() + return svcConfig, err +} + +// initService create systemd service. +func (sc *ServiceConfig) initService() error { + var err error sys := service.ChosenSystem() - sc.service, err = sys.New(&startStop{}, svcConfig) + sc.service, err = sys.New(&startStop{}, &service.Config{ + Name: sc.Name, + DisplayName: sc.DisplayName, + Description: sc.Description, + Dependencies: sc.Dependencies, + Arguments: sc.Arguments, + EnvVars: sc.EnvVars, + Option: sc.Option, + }) if err != nil { - return machine, err + return err } errs := make(chan error, 5) - sc.logger, err = sc.service.Logger(errs) + sc.logger, err = sc.service.SystemLogger(errs) if err != nil { - return machine, err + return err + } + + return nil +} + +// Create Install setups up the given service in the OS service manager. +// This may require greater rights. Will return an error if it is already installed. +func (sc ServiceConfig) Create(ctx context.Context, machine *machinev1alpha1.Machine) (*machinev1alpha1.Machine, error) { + if uid := os.Getuid(); uid != 0 { + return machine, fmt.Errorf("requires root permission") } - err = sc.service.Install() + err := sc.service.Install() if err != nil { return machine, err } @@ -66,37 +125,67 @@ func (sc ServiceConfig) Create(ctx context.Context, machine *machinev1alpha1.Mac return machine, nil } +// Start signals to the OS service manager the given service should start. func (sc ServiceConfig) Start(ctx context.Context, machine *machinev1alpha1.Machine) (*machinev1alpha1.Machine, error) { // Implement `Start()` -> to start running systemd process, It also checks for the user permission same as above first. - return &machinev1alpha1.Machine{}, nil + if uid := os.Getuid(); uid != 0 { + return machine, fmt.Errorf("requires root permission") + } + + if err := sc.service.Start(); err != nil { + return machine, nil + } + + return machine, nil } func (sc ServiceConfig) Pause(ctx context.Context, machine *machinev1alpha1.Machine) (*machinev1alpha1.Machine, error) { - return &machinev1alpha1.Machine{}, nil + // This fuction can be upgraded in future as per need + // But right now it does same as Stop(). + return sc.Stop(ctx, machine) } +// Stop signals to the OS service manager the given service should stop. func (sc ServiceConfig) Stop(ctx context.Context, machine *machinev1alpha1.Machine) (*machinev1alpha1.Machine, error) { // Implement `Stop()` -> to stop running systemd process, It also checks for the user permission same as above first. - return &machinev1alpha1.Machine{}, nil + if uid := os.Getuid(); uid != 0 { + return machine, fmt.Errorf("requires root permission") + } + + if err := sc.service.Stop(); err != nil { + return machine, nil + } + return machine, nil } func (sc ServiceConfig) Update(ctx context.Context, machine *machinev1alpha1.Machine) (*machinev1alpha1.Machine, error) { - return &machinev1alpha1.Machine{}, nil + // Stop & Delete the existing service if possible and Create & Start (in case service was running) + // using Stop(), Delete(), Create() & Start() respectively. + return machine, nil } +// Delete removes the given service from the OS service manager. +// This may require greater rights. Will return an error if the service is not present. func (sc ServiceConfig) Delete(ctx context.Context, machine *machinev1alpha1.Machine) (*machinev1alpha1.Machine, error) { // Implement `Delete()` -> to uninstall systemd process, // It also checks for the user permission same as above first & stop the process if it's in running state. - return &machinev1alpha1.Machine{}, nil + if uid := os.Getuid(); uid != 0 { + return machine, fmt.Errorf("requires root permission") + } + + if err := sc.service.Uninstall(); err != nil { + return machine, nil + } + return machine, nil } func (sc ServiceConfig) Get(ctx context.Context, machine *machinev1alpha1.Machine) (*machinev1alpha1.Machine, error) { // Implement `Get()` -> to return the systemd process with following info: `name`, `status` & etc. - return &machinev1alpha1.Machine{}, nil + return machine, nil } func (sc ServiceConfig) List(ctx context.Context, machineList *machinev1alpha1.MachineList) (*machinev1alpha1.MachineList, error) { - return &machinev1alpha1.MachineList{}, nil + return machineList, nil } func (sc ServiceConfig) Watch(ctx context.Context, machine *machinev1alpha1.Machine) (chan *machinev1alpha1.Machine, chan error, error) {