• 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

72.26
/pkg/render/render.go
1
package render
2

3
import (
4
        "bytes"
5
        "fmt"
6
        "io"
7
        "os"
8
        "path/filepath"
9
        "sort"
10
        "strings"
11
        "text/template"
12

13
        sprig "github.com/Masterminds/sprig/v3"
14
        "github.com/pkg/errors"
15

16
        "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
17
        "k8s.io/apimachinery/pkg/util/yaml"
18

19
        mcfgv1 "github.com/openshift/api/machineconfiguration/v1"
20
        "github.com/openshift/machine-config-operator/pkg/controller/common"
21
)
22

23
type RenderData struct {
24
        Funcs template.FuncMap
25
        Data  map[string]interface{}
26
}
27

28
type RenderConfig struct {
29
        *mcfgv1.ControllerConfigSpec
30
        PullSecret string
31
}
32

33
type DeviceInfo struct {
34
        PciAddress string
35
        NumVfs     int
36
}
37

38
const (
39
        ovsUnitsDir = "ovs-units"
40
)
41

42
func MakeRenderData() RenderData {
1✔
43
        return RenderData{
1✔
44
                Funcs: template.FuncMap{},
1✔
45
                Data:  map[string]interface{}{},
1✔
46
        }
1✔
47
}
1✔
48

49
// RenderDir will render all manifests in a directory, descending in to subdirectories
50
// It will perform template substitutions based on the data supplied by the RenderData
51
func RenderDir(manifestDir string, d *RenderData) ([]*unstructured.Unstructured, error) {
1✔
52
        out := []*unstructured.Unstructured{}
1✔
53

1✔
54
        if err := filepath.Walk(manifestDir, func(path string, info os.FileInfo, err error) error {
2✔
55
                if err != nil {
1✔
56
                        return err
×
57
                }
×
58
                if info.IsDir() {
2✔
59
                        return nil
1✔
60
                }
1✔
61

62
                // Skip non-manifest files
63
                if !(strings.HasSuffix(path, ".yml") || strings.HasSuffix(path, ".yaml") || strings.HasSuffix(path, ".json")) {
1✔
64
                        return nil
×
65
                }
×
66

67
                objs, err := RenderFileTemplate(path, d)
1✔
68
                if err != nil {
1✔
69
                        return err
×
70
                }
×
71
                out = append(out, objs...)
1✔
72
                return nil
1✔
73
        }); err != nil {
×
74
                return nil, errors.Wrap(err, "error rendering manifests")
×
75
        }
×
76

77
        return out, nil
1✔
78
}
79

80
// RenderTemplate renders provided template to string
81
func RenderTemplate(template string, d *RenderData) (*bytes.Buffer, error) {
1✔
82
        return renderTemplate(template, d)
1✔
83
}
1✔
84

85
// RenderFileTemplate reads, renders, and attempts to parse a yaml or
86
// json file representing one or more k8s api objects
87
func RenderFileTemplate(path string, d *RenderData) ([]*unstructured.Unstructured, error) {
1✔
88
        rendered, err := renderFileTemplate(path, d)
1✔
89
        if err != nil {
2✔
90
                return nil, err
1✔
91
        }
1✔
92

93
        out := []*unstructured.Unstructured{}
1✔
94

1✔
95
        // special case - if the entire file is whitespace, skip
1✔
96
        if len(strings.TrimSpace(rendered.String())) == 0 {
2✔
97
                return out, nil
1✔
98
        }
1✔
99

100
        decoder := yaml.NewYAMLOrJSONDecoder(rendered, 4096)
1✔
101
        for {
2✔
102
                u := unstructured.Unstructured{}
1✔
103
                if err := decoder.Decode(&u); err != nil {
2✔
104
                        if err == io.EOF {
2✔
105
                                break
1✔
106
                        }
107
                        return nil, errors.Wrapf(err, "failed to unmarshal manifest %s", path)
×
108
                }
109

110
                if u.Object == nil {
2✔
111
                        continue
1✔
112
                }
113

114
                out = append(out, &u)
1✔
115
        }
116

117
        return out, nil
1✔
118
}
119

120
func renderTemplate(rawTemplate string, d *RenderData) (*bytes.Buffer, error) {
1✔
121
        tmpl := template.New("template").Option("missingkey=error")
1✔
122
        if d.Funcs != nil {
2✔
123
                tmpl.Funcs(d.Funcs)
1✔
124
        }
1✔
125

126
        // Add universal functions
127
        tmpl.Funcs(template.FuncMap{"getOr": getOr, "isSet": isSet})
1✔
128
        tmpl.Funcs(sprig.TxtFuncMap())
1✔
129

1✔
130
        if _, err := tmpl.Parse(rawTemplate); err != nil {
2✔
131
                return nil, errors.Wrapf(err, "failed to parse manifest %s as template", rawTemplate)
1✔
132
        }
1✔
133

134
        rendered := bytes.Buffer{}
1✔
135
        if err := tmpl.Execute(&rendered, d.Data); err != nil {
2✔
136
                return nil, errors.Wrapf(err, "failed to render manifest %s", rawTemplate)
1✔
137
        }
1✔
138

139
        return &rendered, nil
1✔
140
}
141

142
func renderFileTemplate(path string, d *RenderData) (*bytes.Buffer, error) {
1✔
143
        source, err := os.ReadFile(path)
1✔
144
        if err != nil {
1✔
NEW
145
                return nil, errors.Wrapf(err, "failed to read manifest %s", path)
×
NEW
146
        }
×
147

148
        return renderTemplate(string(source[:]), d)
1✔
149
}
150

151
func formateDeviceList(devs []DeviceInfo) string {
×
152
        out := ""
×
153
        for _, dev := range devs {
×
154
                out = out + fmt.Sprintln(dev.PciAddress, dev.NumVfs)
×
155
        }
×
156
        return out
×
157
}
158

159
func GenerateMachineConfig(path, name, mcRole string, ovsOffload bool, d *RenderData) (*mcfgv1.MachineConfig, error) {
1✔
160
        d.Funcs["formateDeviceList"] = formateDeviceList
1✔
161

1✔
162
        exists, err := existsDir(path)
1✔
163
        if err != nil {
1✔
164
                return nil, err
×
165
        }
×
166
        if !exists {
1✔
167
                return nil, errors.Errorf("%s is not a directory", path)
×
168
        }
×
169
        units := map[string]string{}
1✔
170

1✔
171
        if ovsOffload {
2✔
172
                p := filepath.Join(path, ovsUnitsDir)
1✔
173
                exists, err = existsDir(p)
1✔
174
                if err != nil {
1✔
175
                        return nil, err
×
176
                }
×
177
                if exists {
2✔
178
                        if err := filterTemplates(units, p, d); err != nil {
1✔
179
                                return nil, err
×
180
                        }
×
181
                }
182
        }
183
        // keySortVals returns a list of values, sorted by key
184
        // we need the lists of files and units to have a stable ordering for the checksum
185
        keySortVals := func(m map[string]string) []string {
2✔
186
                ks := []string{}
1✔
187
                for k := range m {
2✔
188
                        ks = append(ks, k)
1✔
189
                }
1✔
190
                sort.Strings(ks)
1✔
191

1✔
192
                vs := []string{}
1✔
193
                for _, k := range ks {
2✔
194
                        vs = append(vs, m[k])
1✔
195
                }
1✔
196

197
                return vs
1✔
198
        }
199

200
        ignCfg, err := common.TranspileCoreOSConfigToIgn(nil, keySortVals(units))
1✔
201
        if err != nil {
1✔
202
                return nil, errors.Wrap(err, "error transpiling CoreOS config to Ignition config")
×
203
        }
×
204
        mcfg, err := common.MachineConfigFromIgnConfig(mcRole, name, ignCfg)
1✔
205
        if err != nil {
1✔
206
                return nil, errors.Wrap(err, "error creating MachineConfig from Ignition config")
×
207
        }
×
208

209
        return mcfg, nil
1✔
210
}
211

212
// existsDir returns true if path exists and is a directory, false if the path
213
// does not exist, and error if there is a runtime error or the path is not a directory
214
func existsDir(path string) (bool, error) {
1✔
215
        info, err := os.Stat(path)
1✔
216
        if err != nil {
1✔
217
                if os.IsNotExist(err) {
×
218
                        return false, nil
×
219
                }
×
220
                return false, errors.Wrapf(err, "failed to open dir %q", path)
×
221
        }
222
        if !info.IsDir() {
1✔
223
                return false, errors.Wrapf(err, "expected template directory, %q is not a directory", path)
×
224
        }
×
225
        return true, nil
1✔
226
}
227

228
func filterTemplates(toFilter map[string]string, path string, d *RenderData) error {
1✔
229
        walkFn := func(path string, info os.FileInfo, err error) error {
2✔
230
                if err != nil {
1✔
231
                        return err
×
232
                }
×
233
                if info.IsDir() {
2✔
234
                        return nil
1✔
235
                }
1✔
236

237
                // empty templates signify don't create
238
                if info.Size() == 0 {
1✔
239
                        delete(toFilter, info.Name())
×
240
                        return nil
×
241
                }
×
242

243
                // Render the template file
244
                renderedData, err := renderFileTemplate(path, d)
1✔
245
                if err != nil {
1✔
246
                        return err
×
247
                }
×
248
                toFilter[info.Name()] = renderedData.String()
1✔
249
                return nil
1✔
250
        }
251

252
        return filepath.Walk(path, walkFn)
1✔
253
}
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