Skip to content

Commit

Permalink
Abstract kardianos/service behind pkg/service
Browse files Browse the repository at this point in the history
Signed-off-by: Kimmo Lehto <[email protected]>
  • Loading branch information
kke committed Sep 26, 2024
1 parent ae615ad commit 4e47b05
Show file tree
Hide file tree
Showing 6 changed files with 195 additions and 87 deletions.
20 changes: 13 additions & 7 deletions cmd/start/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,7 @@ import (
"fmt"
"os"

"github.com/k0sproject/k0s/pkg/install"

"github.com/kardianos/service"
"github.com/k0sproject/k0s/pkg/service"
"github.com/spf13/cobra"
)

Expand All @@ -34,16 +32,24 @@ func NewStartCmd() *cobra.Command {
if os.Geteuid() != 0 {
return fmt.Errorf("this command must be run as root")
}
svc, err := install.InstalledService()
svc, err := service.InstalledK0sService()
if err != nil {
return err
}

status, err := svc.Status()
if err != nil {
return err
}
status, _ := svc.Status()
if status == service.StatusRunning {
return fmt.Errorf("already running")
}
return svc.Start()

if err := svc.Start(); err != nil {
return fmt.Errorf("failed to start the service: %w", err)
}

return nil
},
}

}
15 changes: 9 additions & 6 deletions cmd/stop/stop.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,7 @@ import (
"fmt"
"os"

"github.com/k0sproject/k0s/pkg/install"

"github.com/kardianos/service"
"github.com/k0sproject/k0s/pkg/service"
"github.com/spf13/cobra"
)

Expand All @@ -34,19 +32,24 @@ func NewStopCmd() *cobra.Command {
if os.Geteuid() != 0 {
return fmt.Errorf("this command must be run as root")
}
svc, err := install.InstalledService()
svc, err := service.InstalledK0sService()
if err != nil {
return err
}

status, err := svc.Status()
if err != nil {
return err
}
if status == service.StatusStopped {
return fmt.Errorf("already stopped")
}
return svc.Stop()

if err := svc.Stop(); err != nil {
return fmt.Errorf("failed to stop the service: %w", err)
}

return nil
},
}

}
3 changes: 2 additions & 1 deletion pkg/install/linux_systemd.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ ConditionFileIsExecutable={{.Path|cmdEscape}}
StartLimitInterval=5
StartLimitBurst=10
ExecStart={{.Path|cmdEscape}}{{range .Arguments}} {{.|cmdEscape}}{{end}}
Environment="{{- range $key, $value := .EnvVars}}{{$key}}={{$value}} {{- end}}"
{{- if .Option.Environment}}{{range .Option.Environment}}
Environment="{{.}}"{{end}}{{- end}}
RestartSec=10
Delegate=yes
Expand Down
91 changes: 18 additions & 73 deletions pkg/install/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,71 +17,28 @@ limitations under the License.
package install

import (
"errors"
"fmt"
"strings"

"github.com/kardianos/service"
"github.com/k0sproject/k0s/pkg/service"
"github.com/sirupsen/logrus"
)

var (
k0sServiceName = "k0s"
k0sDescription = "k0s - Zero Friction Kubernetes"
)

type Program struct{}

func (p *Program) Start(service.Service) error {
// Start should not block. Do the actual work async.
return nil
}

func (p *Program) Stop(service.Service) error {
// Stop should not block. Return with a few seconds.
return nil
}

// InstalledService returns a k0s service if one has been installed on the host or an error otherwise.
func InstalledService() (service.Service, error) {
prg := &Program{}
for _, role := range []string{"controller", "worker"} {
c := GetServiceConfig(role)
s, err := service.New(prg, c)
if err != nil {
return nil, err
}
_, err = s.Status()

if err != nil && errors.Is(err, service.ErrNotInstalled) {
continue
}
if err != nil {
return nil, err
}
return s, nil
}

var s service.Service
return s, fmt.Errorf("k0s has not been installed as a service")
}

// EnsureService installs the k0s service, per the given arguments, and the detected platform
func EnsureService(args []string, envVars []string, force bool) error {
var deps []string
var svcConfig *service.Config

prg := &Program{}
for _, v := range args {
if v == "controller" || v == "worker" {
svcConfig = GetServiceConfig(v)
svcConfig = service.K0sConfig(v)
break
}
}

s, err := service.New(prg, svcConfig)
s, err := service.NewService(svcConfig)
if err != nil {
return err
return fmt.Errorf("failed to create service: %w", err)
}

// fetch service type
Expand Down Expand Up @@ -117,9 +74,14 @@ func EnsureService(args []string, envVars []string, force bool) error {
svcConfig.Arguments = args
if force {
logrus.Infof("Uninstalling %s service", svcConfig.Name)
err = s.Uninstall()
if err != nil && !errors.Is(err, service.ErrNotInstalled) {
logrus.Warnf("failed to uninstall service: %v", err)
status, err := s.Status()
if err != nil {
logrus.Warnf("failed to get service status: %v", err)
}
if status != service.StatusNotInstalled {
if err := s.Uninstall(); err != nil {
logrus.Warnf("failed to uninstall service: %v", err)
}
}
}
logrus.Infof("Installing %s service", svcConfig.Name)
Expand All @@ -131,33 +93,16 @@ func EnsureService(args []string, envVars []string, force bool) error {
}

func UninstallService(role string) error {
prg := &Program{}

if role == "controller+worker" {
role = "controller"
}

svcConfig := GetServiceConfig(role)
s, err := service.New(prg, svcConfig)
s, err := service.InstalledK0sService()
if err != nil {
return err
return fmt.Errorf("uninstall service: %w", err)
}

return s.Uninstall()
}

func GetServiceConfig(role string) *service.Config {
var k0sDisplayName string

if role == "controller" || role == "worker" {
k0sDisplayName = "k0s " + role
k0sServiceName = "k0s" + role
}
return &service.Config{
Name: k0sServiceName,
DisplayName: k0sDisplayName,
Description: k0sDescription,
if err := s.Uninstall(); err != nil {
return fmt.Errorf("uninstall service: %w", err)
}

return nil
}

func prepareEnvVars(envVars []string) map[string]string {

Check failure on line 108 in pkg/install/service.go

View workflow job for this annotation

GitHub Actions / Lint

func `prepareEnvVars` is unused (unused)
Expand Down
11 changes: 11 additions & 0 deletions pkg/service/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package service

Check failure on line 1 in pkg/service/config.go

View workflow job for this annotation

GitHub Actions / Lint

Missed header for check (goheader)

// Config describes a configuration for a system service
type Config struct {
Name string
DisplayName string
Description string
Arguments []string
Option map[string]any
Dependencies []string
}
142 changes: 142 additions & 0 deletions pkg/service/service.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
package service

Check failure on line 1 in pkg/service/service.go

View workflow job for this annotation

GitHub Actions / Lint

Missed header for check (goheader)

import (
"errors"
"fmt"

"github.com/kardianos/service"
)

// Status represents the status of a system service
type Status string

const (
StatusRunning Status = "running"
StatusStopped Status = "stopped"
StatusUnknown Status = "unknown"
StatusNotInstalled Status = "not installed"

k0sServicePrefix = "k0s"
k0sDescription = "k0s - Zero Friction Kubernetes"
)

var ErrK0sNotInstalled = errors.New("k0s has not been installed as a system service")

type Service struct {
svc service.Service
}

// dummy implementation for kardianos.Interface
type program struct{}

func (p *program) Start(service.Service) error {
// Start should not block. Do the actual work async.
return nil
}

func (p *program) Stop(service.Service) error {
// Stop should not block. Return with a few seconds.
return nil
}

// NewService creates a new system service instance
func NewService(cfg *Config) (*Service, error) {
kardianosCfg := &service.Config{
Name: cfg.Name,
Description: cfg.Description,
Arguments: cfg.Arguments,
Option: cfg.Option,
}

kardSvc, err := service.New(&program{}, kardianosCfg)
if err != nil {
return nil, fmt.Errorf("get service: %w", err)
}
return &Service{svc: kardSvc}, nil
}

// K0sConfig returns a Config for a k0s system service
func K0sConfig(role string) *Config {
var k0sDisplayName, k0sServiceName string

if role == "controller" || role == "worker" {
k0sDisplayName = k0sServicePrefix + " " + role
k0sServiceName = k0sServicePrefix + role
}
return &Config{
Name: k0sServiceName,
DisplayName: k0sDisplayName,
Description: k0sDescription,
}
}

func InstalledK0sService() (*Service, error) {
for _, role := range []string{"controller", "worker"} {
c := K0sConfig(role)
s, err := NewService(c)
if err != nil {
return nil, err
}
status, err := s.Status()
if err != nil {
return nil, err
}

if status != StatusNotInstalled {
return s, nil
}
}
return nil, ErrK0sNotInstalled
}

// Start the system service
func (s *Service) Start() error {
if err := s.svc.Start(); err != nil {
return fmt.Errorf("start service: %w", err)
}
return nil
}

// Stop the system service
func (s *Service) Stop() error {
if err := s.svc.Stop(); err != nil {
return fmt.Errorf("stop service: %w", err)
}
return nil
}

// Status of the system service or an error if the status could not be determined
func (s *Service) Status() (Status, error) {
status, err := s.svc.Status()
if err != nil {
if errors.Is(err, service.ErrNotInstalled) {
return StatusNotInstalled, nil
}
return StatusUnknown, fmt.Errorf("get service status: %w", err)
}

// Map kardianos status codes to our defined constants
switch status {
case service.StatusRunning:
return StatusRunning, nil
case service.StatusStopped:
return StatusStopped, nil
default:
return StatusUnknown, nil
}
}

// Uninstall the system service
func (s *Service) Uninstall() error {
return s.svc.Uninstall()
}

// Install the system service
func (s *Service) Install() error {
return s.svc.Install()
}

// Platform returns the init system used by the host
func (s *Service) Platform() string {
return s.svc.Platform()
}

0 comments on commit 4e47b05

Please sign in to comment.