-
Notifications
You must be signed in to change notification settings - Fork 456
/
extension_service.go
131 lines (99 loc) · 3.39 KB
/
extension_service.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package runtime
import (
"context"
"fmt"
"os"
"path/filepath"
"github.com/cosi-project/runtime/pkg/controller"
"go.uber.org/zap"
"gopkg.in/yaml.v3"
"github.com/siderolabs/talos/internal/app/machined/pkg/system"
"github.com/siderolabs/talos/internal/app/machined/pkg/system/services"
extservices "github.com/siderolabs/talos/pkg/machinery/extensions/services"
)
// ServiceManager is the interface to the v1alpha1 services subsystems.
type ServiceManager interface {
Load(services ...system.Service) []string
Start(serviceIDs ...string) error
}
// ExtensionServiceController creates extension services based on the extension service configuration found in the rootfs.
type ExtensionServiceController struct {
V1Alpha1Services ServiceManager
ConfigPath string
}
// Name implements controller.Controller interface.
func (ctrl *ExtensionServiceController) Name() string {
return "runtime.ExtensionServiceController"
}
// Inputs implements controller.Controller interface.
func (ctrl *ExtensionServiceController) Inputs() []controller.Input {
return nil
}
// Outputs implements controller.Controller interface.
func (ctrl *ExtensionServiceController) Outputs() []controller.Output {
return nil
}
// Run implements controller.Controller interface.
//
//nolint:gocyclo
func (ctrl *ExtensionServiceController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error {
select {
case <-ctx.Done():
return nil
case <-r.EventCh():
}
// controller runs only once, as services are static
serviceFiles, err := os.ReadDir(ctrl.ConfigPath)
if err != nil {
if os.IsNotExist(err) {
// directory not present, skip completely
logger.Debug("extension service directory is not found")
return nil
}
return err
}
extServices := map[string]struct{}{}
for _, serviceFile := range serviceFiles {
if filepath.Ext(serviceFile.Name()) != ".yaml" {
logger.Debug("skipping config file", zap.String("filename", serviceFile.Name()))
continue
}
spec, err := ctrl.loadSpec(filepath.Join(ctrl.ConfigPath, serviceFile.Name()))
if err != nil {
logger.Error("error loading extension service spec", zap.String("filename", serviceFile.Name()), zap.Error(err))
continue
}
if err = spec.Validate(); err != nil {
logger.Error("error validating extension service spec", zap.String("filename", serviceFile.Name()), zap.Error(err))
continue
}
if _, exists := extServices[spec.Name]; exists {
logger.Error("duplicate service spec", zap.String("filename", serviceFile.Name()), zap.String("name", spec.Name))
continue
}
extServices[spec.Name] = struct{}{}
svc := &services.Extension{
Spec: spec,
}
ctrl.V1Alpha1Services.Load(svc)
if err = ctrl.V1Alpha1Services.Start(svc.ID(nil)); err != nil {
return fmt.Errorf("error starting %q service: %w", spec.Name, err)
}
}
return nil
}
func (ctrl *ExtensionServiceController) loadSpec(path string) (extservices.Spec, error) {
var spec extservices.Spec
f, err := os.Open(path)
if err != nil {
return spec, err
}
defer f.Close() //nolint:errcheck
if err = yaml.NewDecoder(f).Decode(&spec); err != nil {
return spec, fmt.Errorf("error unmarshalling extension service config: %w", err)
}
return spec, nil
}