• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

k8snetworkplumbingwg / sriov-network-operator / 19573326688

21 Nov 2025 02:19PM UTC coverage: 62.238% (+0.2%) from 62.073%
19573326688

Pull #823

github

web-flow
Merge 4c7f92e5c into 811252cf3
Pull Request #823: Make ovs-vswitchd service 'other_config' option configurable

118 of 163 new or added lines in 10 files covered. (72.39%)

4 existing lines in 3 files now uncovered.

8859 of 14234 relevant lines covered (62.24%)

0.69 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

29.65
/pkg/host/internal/service/service.go
1
package service
2

3
import (
4
        "fmt"
5
        "io"
6
        "os"
7
        "path"
8
        "path/filepath"
9
        "strings"
10

11
        "github.com/coreos/go-systemd/v22/unit"
12
        "gopkg.in/yaml.v3"
13
        "sigs.k8s.io/controller-runtime/pkg/log"
14

15
        "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/consts"
16
        "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/host/types"
17
        "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/render"
18
        "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/utils"
19
)
20

21
// TODO: handle this to support unit-tests
22
const systemdDir = "/usr/lib/systemd/system/"
23

24
type service struct {
25
        utilsHelper utils.CmdInterface
26
}
27

28
func New(utilsHelper utils.CmdInterface) types.ServiceInterface {
1✔
29
        return &service{utilsHelper: utilsHelper}
1✔
30
}
1✔
31

32
// ServiceInjectionManifestFile service injection manifest file structure
33
type ServiceInjectionManifestFile struct {
34
        Name    string
35
        Dropins []struct {
36
                Contents string
37
        }
38
}
39

40
// IsServiceExist check if service unit exist
41
func (s *service) IsServiceExist(servicePath string) (bool, error) {
×
42
        _, err := os.Stat(path.Join(consts.Chroot, servicePath))
×
43
        if err != nil {
×
44
                if os.IsNotExist(err) {
×
45
                        return false, nil
×
46
                }
×
47
                return false, err
×
48
        }
49

50
        return true, nil
×
51
}
52

53
// IsServiceEnabled check if service exist and enabled
54
func (s *service) IsServiceEnabled(servicePath string) (bool, error) {
×
55
        exist, err := s.IsServiceExist(servicePath)
×
56
        if err != nil || !exist {
×
57
                return false, err
×
58
        }
×
59
        serviceName := filepath.Base(servicePath)
×
60
        // Change root dir
×
61
        exit, err := s.utilsHelper.Chroot(consts.Chroot)
×
62
        if err != nil {
×
63
                return false, err
×
64
        }
×
65
        defer exit()
×
66

×
67
        // TODO: add check for the output and logs
×
68
        _, _, err = s.utilsHelper.RunCommand("systemctl", "is-enabled", serviceName)
×
69
        return err == nil, nil
×
70
}
71

72
// ReadService read service from given path
73
func (s *service) ReadService(servicePath string) (*types.Service, error) {
×
74
        data, err := os.ReadFile(path.Join(consts.Chroot, servicePath))
×
75
        if err != nil {
×
76
                return nil, err
×
77
        }
×
78

79
        return &types.Service{
×
80
                Name:    filepath.Base(servicePath),
×
81
                Path:    servicePath,
×
82
                Content: string(data),
×
83
        }, nil
×
84
}
85

86
// EnableService creates service file and enables it with systemctl enable
87
func (s *service) EnableService(service *types.Service) error {
×
88
        // Write service file
×
89
        err := os.WriteFile(path.Join(consts.Chroot, service.Path), []byte(service.Content), 0644)
×
90
        if err != nil {
×
91
                return err
×
92
        }
×
93

94
        // Change root dir
95
        exit, err := s.utilsHelper.Chroot(consts.Chroot)
×
96
        if err != nil {
×
97
                return err
×
98
        }
×
99
        defer exit()
×
100

×
101
        // Enable the service
×
102
        // we use reenable command (the command is a combination of disable+enable) to reset
×
103
        // symlinks for the unit and make sure that only symlinks that are currently
×
104
        // configured in the [Install] section exist for the service.
×
105
        _, _, err = s.utilsHelper.RunCommand("systemctl", "reenable", service.Name)
×
106
        return err
×
107
}
108

109
// ReloadService reloads a systemd unit files on the host
110
func (s *service) ReloadService() error {
1✔
111
        // Change root dir
1✔
112
        exit, err := s.utilsHelper.Chroot(consts.Chroot)
1✔
113
        if err != nil {
1✔
NEW
114
                return err
×
NEW
115
        }
×
116
        defer exit()
1✔
117

1✔
118
        // Restart the service
1✔
119
        _, _, err = s.utilsHelper.RunCommand("systemctl", "daemon-reload")
1✔
120
        return err
1✔
121
}
122

123
// RestartService restarts systemd service with systemctl restart
124
func (s *service) RestartService(service *types.Service) error {
1✔
125
        // Change root dir
1✔
126
        exit, err := s.utilsHelper.Chroot(consts.Chroot)
1✔
127
        if err != nil {
1✔
NEW
128
                return err
×
NEW
129
        }
×
130
        defer exit()
1✔
131

1✔
132
        // Restart the service
1✔
133
        _, _, err = s.utilsHelper.RunCommand("systemctl", "restart", service.Name)
1✔
134
        return err
1✔
135
}
136

137
// CompareServices returns true if serviceA needs update(doesn't contain all fields from service B)
138
func (s *service) CompareServices(serviceA, serviceB *types.Service) (bool, error) {
×
139
        optsA, err := unit.Deserialize(strings.NewReader(serviceA.Content))
×
140
        if err != nil {
×
141
                return false, err
×
142
        }
×
143
        optsB, err := unit.Deserialize(strings.NewReader(serviceB.Content))
×
144
        if err != nil {
×
145
                return false, err
×
146
        }
×
147

148
OUTER:
×
149
        for _, optB := range optsB {
×
150
                for _, optA := range optsA {
×
151
                        if optA.Match(optB) {
×
152
                                continue OUTER
×
153
                        }
154
                }
155
                log.Log.V(2).Info("CompareServices", "ServiceA", optsA, "ServiceB", *optB)
×
156
                return true, nil
×
157
        }
158

159
        return false, nil
×
160
}
161

162
func (s *service) renderOtherOvsConfigOption(ovsConfig map[string]string) (string, string) {
1✔
163
        return utils.RenderOtherOvsConfigOption(ovsConfig)
1✔
164
}
1✔
165

166
// ReadServiceInjectionManifestFile reads service injection file
167
func (s *service) ReadServiceInjectionManifestFile(path string, ovsConfig map[string]string) (*types.Service, error) {
1✔
168
        data, err := os.ReadFile(path)
1✔
169
        if err != nil {
1✔
170
                return nil, err
×
171
        }
×
172

173
        var serviceContent ServiceInjectionManifestFile
1✔
174
        if err := yaml.Unmarshal(data, &serviceContent); err != nil {
1✔
175
                return nil, err
×
176
        }
×
177

178
        d := render.MakeRenderData()
1✔
179
        externalIds, otherOvsConfig := s.renderOtherOvsConfigOption(ovsConfig)
1✔
180
        d.Data["ExternalIds"] = externalIds
1✔
181
        d.Data["OtherOvsConfig"] = otherOvsConfig
1✔
182

1✔
183
        srv, err := render.RenderTemplate(serviceContent.Dropins[0].Contents, &d)
1✔
184
        if err != nil {
1✔
NEW
185
                return nil, err
×
NEW
186
        }
×
187

188
        return &types.Service{
1✔
189
                Name:    serviceContent.Name,
1✔
190
                Path:    systemdDir + serviceContent.Name,
1✔
191
                Content: srv.String(),
1✔
192
        }, nil
1✔
193
}
194

195
// ReadServiceManifestFile reads service file
196
func (s *service) ReadServiceManifestFile(path string) (*types.Service, error) {
1✔
197
        data, err := os.ReadFile(path)
1✔
198
        if err != nil {
1✔
199
                return nil, err
×
200
        }
×
201

202
        var serviceFile *types.ServiceManifestFile
1✔
203
        if err := yaml.Unmarshal(data, &serviceFile); err != nil {
1✔
204
                return nil, err
×
205
        }
×
206

207
        return &types.Service{
1✔
208
                Name:    serviceFile.Name,
1✔
209
                Path:    "/etc/systemd/system/" + serviceFile.Name,
1✔
210
                Content: serviceFile.Contents,
1✔
211
        }, nil
1✔
212
}
213

214
func (s *service) UpdateSystemService(serviceObj *types.Service) error {
×
215
        systemService, err := s.ReadService(serviceObj.Path)
×
216
        if err != nil {
×
217
                return err
×
218
        }
×
219
        if systemService == nil {
×
220
                // Invalid case to reach here
×
221
                return fmt.Errorf("can't update non-existing service %q", serviceObj.Name)
×
222
        }
×
223
        serviceOptions, err := unit.Deserialize(strings.NewReader(serviceObj.Content))
×
224
        if err != nil {
×
225
                return err
×
226
        }
×
227
        updatedService, err := appendToService(systemService, serviceOptions...)
×
228
        if err != nil {
×
229
                return err
×
230
        }
×
231

232
        return s.EnableService(updatedService)
×
233
}
234

235
// appendToService appends given fields to service
236
func appendToService(service *types.Service, options ...*unit.UnitOption) (*types.Service, error) {
×
237
        serviceOptions, err := unit.Deserialize(strings.NewReader(service.Content))
×
238
        if err != nil {
×
239
                return nil, err
×
240
        }
×
241

242
OUTER:
×
243
        for _, appendOpt := range options {
×
244
                for _, opt := range serviceOptions {
×
245
                        if opt.Match(appendOpt) {
×
246
                                continue OUTER
×
247
                        }
248
                }
249
                serviceOptions = append(serviceOptions, appendOpt)
×
250
        }
251

252
        data, err := io.ReadAll(unit.Serialize(serviceOptions))
×
253
        if err != nil {
×
254
                return nil, err
×
255
        }
×
256

257
        return &types.Service{
×
258
                Name:    service.Name,
×
259
                Path:    service.Path,
×
260
                Content: string(data),
×
261
        }, nil
×
262
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc