Skip to content

Commit 687c61c

Browse files
committed
add CheckSemVer unit test
Signed-off-by: liang chenye <[email protected]>
1 parent 66950fa commit 687c61c

File tree

3 files changed

+119
-81
lines changed

3 files changed

+119
-81
lines changed

cmd/ocitools/validate.go

Lines changed: 2 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,9 @@
11
package main
22

33
import (
4-
"encoding/json"
54
"fmt"
6-
"io/ioutil"
7-
"os"
8-
"path"
95
"strings"
10-
"unicode/utf8"
116

12-
rspec "github.com/opencontainers/runtime-spec/specs-go"
137
"github.com/urfave/cli"
148

159
"github.com/opencontainers/ocitools/validate"
@@ -26,37 +20,12 @@ var bundleValidateCommand = cli.Command{
2620
Before: before,
2721
Action: func(context *cli.Context) error {
2822
inputPath := context.String("path")
29-
if inputPath == "" {
30-
return fmt.Errorf("Bundle path shouldn't be empty")
31-
}
32-
33-
if _, err := os.Stat(inputPath); err != nil {
34-
return err
35-
}
36-
37-
configPath := path.Join(inputPath, "config.json")
38-
content, err := ioutil.ReadFile(configPath)
23+
hostSpecific := context.GlobalBool("host-specific")
24+
v, err := validate.NewValidatorFromPath(inputPath, hostSpecific)
3925
if err != nil {
4026
return err
4127
}
42-
if !utf8.Valid(content) {
43-
return fmt.Errorf("%q is not encoded in UTF-8", configPath)
44-
}
45-
var spec rspec.Spec
46-
if err = json.Unmarshal(content, &spec); err != nil {
47-
return err
48-
}
49-
50-
rootfsPath := path.Join(inputPath, spec.Root.Path)
51-
if fi, err := os.Stat(rootfsPath); err != nil {
52-
return fmt.Errorf("Cannot find the root path %q", rootfsPath)
53-
} else if !fi.IsDir() {
54-
return fmt.Errorf("The root path %q is not a directory.", rootfsPath)
55-
}
56-
57-
hostSpecific := context.GlobalBool("host-specific")
5828

59-
v := validate.NewValidator(spec, rootfsPath, hostSpecific)
6029
errMsgs := v.CheckAll()
6130
if len(errMsgs) > 0 {
6231
return fmt.Errorf("%d Errors detected:\n%s", len(errMsgs), strings.Join(errMsgs, "\n"))

validate/validate.go

Lines changed: 84 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,16 @@ package validate
22

33
import (
44
"bufio"
5+
"encoding/json"
56
"fmt"
7+
"io/ioutil"
68
"os"
79
"path"
810
"path/filepath"
911
"reflect"
1012
"strings"
1113
"unicode"
14+
"unicode/utf8"
1215

1316
"github.com/Sirupsen/logrus"
1417
"github.com/blang/semver"
@@ -53,13 +56,45 @@ var (
5356
)
5457

5558
type Validator struct {
56-
spec rspec.Spec
57-
rootfs string
58-
hostSpecific bool
59+
Spec *rspec.Spec
60+
RootfsPath string
61+
HostSpecific bool
5962
}
6063

61-
func NewValidator(spec rspec.Spec, rootfs string, hostSpecific bool) Validator {
62-
return Validator{spec: spec, rootfs: rootfs, hostSpecific: hostSpecific}
64+
func NewValidator(spec *rspec.Spec, rootfsPath string, hostSpecific bool) Validator {
65+
return Validator{Spec: spec, RootfsPath: rootfsPath, HostSpecific: hostSpecific}
66+
}
67+
68+
func NewValidatorFromPath(bundlePath string, hostSpecific bool) (Validator, error) {
69+
if bundlePath == "" {
70+
return Validator{}, fmt.Errorf("Bundle path shouldn't be empty")
71+
}
72+
73+
if _, err := os.Stat(bundlePath); err != nil {
74+
return Validator{}, err
75+
}
76+
77+
configPath := path.Join(bundlePath, "config.json")
78+
content, err := ioutil.ReadFile(configPath)
79+
if err != nil {
80+
return Validator{}, err
81+
}
82+
if !utf8.Valid(content) {
83+
return Validator{}, fmt.Errorf("%q is not encoded in UTF-8", configPath)
84+
}
85+
var spec rspec.Spec
86+
if err = json.Unmarshal(content, &spec); err != nil {
87+
return Validator{}, err
88+
}
89+
90+
rootfsPath := path.Join(bundlePath, spec.Root.Path)
91+
if fi, err := os.Stat(rootfsPath); err != nil {
92+
return Validator{}, fmt.Errorf("Cannot find the root path %q", rootfsPath)
93+
} else if !fi.IsDir() {
94+
return Validator{}, fmt.Errorf("The root path %q is not a directory.", rootfsPath)
95+
}
96+
97+
return NewValidator(&spec, rootfsPath, hostSpecific), nil
6398
}
6499

65100
func (v *Validator) CheckAll() (msgs []string) {
@@ -77,7 +112,7 @@ func (v *Validator) CheckAll() (msgs []string) {
77112
func (v *Validator) CheckSemVer() (msgs []string) {
78113
logrus.Debugf("check semver")
79114

80-
version := v.spec.Version
115+
version := v.Spec.Version
81116
_, err := semver.Parse(version)
82117
if err != nil {
83118
msgs = append(msgs, fmt.Sprintf("%q is not valid SemVer: %s", version, err.Error()))
@@ -102,7 +137,7 @@ func (v *Validator) CheckPlatform() (msgs []string) {
102137
"plan9": {"386", "amd64"},
103138
"solaris": {"amd64"},
104139
"windows": {"386", "amd64"}}
105-
platform := v.spec.Platform
140+
platform := v.Spec.Platform
106141
for os, archs := range validCombins {
107142
if os == platform.OS {
108143
for _, arch := range archs {
@@ -118,6 +153,16 @@ func (v *Validator) CheckPlatform() (msgs []string) {
118153
return
119154
}
120155

156+
func (v *Validator) CheckHooks() (msgs []string) {
157+
logrus.Debugf("check hooks")
158+
159+
msgs = append(msgs, checkEventHooks("pre-start", v.Spec.Hooks.Prestart, v.HostSpecific)...)
160+
msgs = append(msgs, checkEventHooks("post-start", v.Spec.Hooks.Poststart, v.HostSpecific)...)
161+
msgs = append(msgs, checkEventHooks("post-stop", v.Spec.Hooks.Poststop, v.HostSpecific)...)
162+
163+
return
164+
}
165+
121166
func checkEventHooks(hookType string, hooks []rspec.Hook, hostSpecific bool) (msgs []string) {
122167
for _, hook := range hooks {
123168
if !filepath.IsAbs(hook.Path) {
@@ -143,20 +188,11 @@ func checkEventHooks(hookType string, hooks []rspec.Hook, hostSpecific bool) (ms
143188

144189
return
145190
}
146-
func (v *Validator) CheckHooks() (msgs []string) {
147-
logrus.Debugf("check hooks")
148-
149-
msgs = append(msgs, checkEventHooks("pre-start", v.spec.Hooks.Prestart, v.hostSpecific)...)
150-
msgs = append(msgs, checkEventHooks("post-start", v.spec.Hooks.Poststart, v.hostSpecific)...)
151-
msgs = append(msgs, checkEventHooks("post-stop", v.spec.Hooks.Poststop, v.hostSpecific)...)
152-
153-
return
154-
}
155191

156192
func (v *Validator) CheckProcess() (msgs []string) {
157193
logrus.Debugf("check process")
158194

159-
process := v.spec.Process
195+
process := v.Spec.Process
160196
if !path.IsAbs(process.Cwd) {
161197
msgs = append(msgs, fmt.Sprintf("cwd %q is not an absolute path", process.Cwd))
162198
}
@@ -181,7 +217,7 @@ func (v *Validator) CheckProcess() (msgs []string) {
181217
}
182218

183219
if len(process.ApparmorProfile) > 0 {
184-
profilePath := filepath.Join(v.rootfs, "/etc/apparmor.d", process.ApparmorProfile)
220+
profilePath := filepath.Join(v.RootfsPath, "/etc/apparmor.d", process.ApparmorProfile)
185221
_, err := os.Stat(profilePath)
186222
if err != nil {
187223
msgs = append(msgs, err.Error())
@@ -195,7 +231,7 @@ func supportedMountTypes(OS string, hostSpecific bool) (map[string]bool, error)
195231
supportedTypes := make(map[string]bool)
196232

197233
if OS != "linux" && OS != "windows" {
198-
logrus.Warnf("%v is not supported to (v *Validator) Check mount type", OS)
234+
logrus.Warnf("%v is not supported to check mount type", OS)
199235
return nil, nil
200236
} else if OS == "windows" {
201237
supportedTypes["ntfs"] = true
@@ -235,14 +271,14 @@ func supportedMountTypes(OS string, hostSpecific bool) (map[string]bool, error)
235271
func (v *Validator) CheckMounts() (msgs []string) {
236272
logrus.Debugf("check mounts")
237273

238-
supportedTypes, err := supportedMountTypes(v.spec.Platform.OS, v.hostSpecific)
274+
supportedTypes, err := supportedMountTypes(v.Spec.Platform.OS, v.HostSpecific)
239275
if err != nil {
240276
msgs = append(msgs, err.Error())
241277
return
242278
}
243279

244280
if supportedTypes != nil {
245-
for _, mount := range v.spec.Mounts {
281+
for _, mount := range v.Spec.Mounts {
246282
if !supportedTypes[mount.Type] {
247283
msgs = append(msgs, fmt.Sprintf("Unsupported mount type %q", mount.Type))
248284
}
@@ -262,33 +298,33 @@ func (v *Validator) CheckLinux() (msgs []string) {
262298
netExists := false
263299
userExists := false
264300

265-
for index := 0; index < len(v.spec.Linux.Namespaces); index++ {
266-
if !namespaceValid(v.spec.Linux.Namespaces[index]) {
267-
msgs = append(msgs, fmt.Sprintf("namespace %v is invalid.", v.spec.Linux.Namespaces[index]))
268-
} else if len(v.spec.Linux.Namespaces[index].Path) == 0 {
269-
if v.spec.Linux.Namespaces[index].Type == rspec.UTSNamespace {
301+
for index := 0; index < len(v.Spec.Linux.Namespaces); index++ {
302+
if !namespaceValid(v.Spec.Linux.Namespaces[index]) {
303+
msgs = append(msgs, fmt.Sprintf("namespace %v is invalid.", v.Spec.Linux.Namespaces[index]))
304+
} else if len(v.Spec.Linux.Namespaces[index].Path) == 0 {
305+
if v.Spec.Linux.Namespaces[index].Type == rspec.UTSNamespace {
270306
utsExists = true
271-
} else if v.spec.Linux.Namespaces[index].Type == rspec.IPCNamespace {
307+
} else if v.Spec.Linux.Namespaces[index].Type == rspec.IPCNamespace {
272308
ipcExists = true
273-
} else if v.spec.Linux.Namespaces[index].Type == rspec.NetworkNamespace {
309+
} else if v.Spec.Linux.Namespaces[index].Type == rspec.NetworkNamespace {
274310
netExists = true
275-
} else if v.spec.Linux.Namespaces[index].Type == rspec.MountNamespace {
311+
} else if v.Spec.Linux.Namespaces[index].Type == rspec.MountNamespace {
276312
mountExists = true
277-
} else if v.spec.Linux.Namespaces[index].Type == rspec.UserNamespace {
313+
} else if v.Spec.Linux.Namespaces[index].Type == rspec.UserNamespace {
278314
userExists = true
279315
}
280316
}
281317
}
282318

283-
if (len(v.spec.Linux.UIDMappings) > 0 || len(v.spec.Linux.GIDMappings) > 0) && !userExists {
319+
if (len(v.Spec.Linux.UIDMappings) > 0 || len(v.Spec.Linux.GIDMappings) > 0) && !userExists {
284320
msgs = append(msgs, "UID/GID mappings requires a new User namespace to be specified as well")
285-
} else if len(v.spec.Linux.UIDMappings) > 5 {
321+
} else if len(v.Spec.Linux.UIDMappings) > 5 {
286322
msgs = append(msgs, "Only 5 UID mappings are allowed (linux kernel restriction).")
287-
} else if len(v.spec.Linux.GIDMappings) > 5 {
323+
} else if len(v.Spec.Linux.GIDMappings) > 5 {
288324
msgs = append(msgs, "Only 5 GID mappings are allowed (linux kernel restriction).")
289325
}
290326

291-
for k := range v.spec.Linux.Sysctl {
327+
for k := range v.Spec.Linux.Sysctl {
292328
if strings.HasPrefix(k, "net.") && !netExists {
293329
msgs = append(msgs, fmt.Sprintf("Sysctl %v requires a new Network namespace to be specified as well", k))
294330
}
@@ -299,27 +335,27 @@ func (v *Validator) CheckLinux() (msgs []string) {
299335
}
300336
}
301337

302-
if v.spec.Platform.OS == "linux" && !utsExists && v.spec.Hostname != "" {
338+
if v.Spec.Platform.OS == "linux" && !utsExists && v.Spec.Hostname != "" {
303339
msgs = append(msgs, fmt.Sprintf("On Linux, hostname requires a new UTS namespace to be specified as well"))
304340
}
305341

306-
for index := 0; index < len(v.spec.Linux.Devices); index++ {
307-
if !deviceValid(v.spec.Linux.Devices[index]) {
308-
msgs = append(msgs, fmt.Sprintf("device %v is invalid.", v.spec.Linux.Devices[index]))
342+
for index := 0; index < len(v.Spec.Linux.Devices); index++ {
343+
if !deviceValid(v.Spec.Linux.Devices[index]) {
344+
msgs = append(msgs, fmt.Sprintf("device %v is invalid.", v.Spec.Linux.Devices[index]))
309345
}
310346
}
311347

312-
if v.spec.Linux.Resources != nil {
313-
ms := v.checkLinuxResources()
348+
if v.Spec.Linux.Resources != nil {
349+
ms := v.CheckLinuxResources()
314350
msgs = append(msgs, ms...)
315351
}
316352

317-
if v.spec.Linux.Seccomp != nil {
318-
ms := v.checkSeccomp()
353+
if v.Spec.Linux.Seccomp != nil {
354+
ms := v.CheckSeccomp()
319355
msgs = append(msgs, ms...)
320356
}
321357

322-
switch v.spec.Linux.RootfsPropagation {
358+
switch v.Spec.Linux.RootfsPropagation {
323359
case "":
324360
case "private":
325361
case "rprivate":
@@ -334,10 +370,10 @@ func (v *Validator) CheckLinux() (msgs []string) {
334370
return
335371
}
336372

337-
func (v *Validator) checkLinuxResources() (msgs []string) {
373+
func (v *Validator) CheckLinuxResources() (msgs []string) {
338374
logrus.Debugf("check linux resources")
339375

340-
r := v.spec.Linux.Resources
376+
r := v.Spec.Linux.Resources
341377
if r.Memory != nil {
342378
if r.Memory.Limit != nil && r.Memory.Swap != nil && uint64(*r.Memory.Limit) > uint64(*r.Memory.Swap) {
343379
msgs = append(msgs, fmt.Sprintf("Minimum memoryswap should be larger than memory limit"))
@@ -350,10 +386,10 @@ func (v *Validator) checkLinuxResources() (msgs []string) {
350386
return
351387
}
352388

353-
func (v *Validator) checkSeccomp() (msgs []string) {
389+
func (v *Validator) CheckSeccomp() (msgs []string) {
354390
logrus.Debugf("check linux seccomp")
355391

356-
s := v.spec.Linux.Seccomp
392+
s := v.Spec.Linux.Seccomp
357393
if !seccompActionValid(s.DefaultAction) {
358394
msgs = append(msgs, fmt.Sprintf("seccomp defaultAction %q is invalid.", s.DefaultAction))
359395
}
@@ -570,5 +606,5 @@ func checkMandatory(obj interface{}) (msgs []string) {
570606
func (v *Validator) CheckMandatoryFields() []string {
571607
logrus.Debugf("check mandatory fields")
572608

573-
return checkMandatory(v.spec)
609+
return checkMandatory(v.Spec)
574610
}

validate/validate_test.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package validate
2+
3+
import (
4+
"strings"
5+
"testing"
6+
7+
rspec "github.com/opencontainers/runtime-spec/specs-go"
8+
)
9+
10+
func checkErrors(t *testing.T, title string, msgs []string, valid bool) {
11+
if valid && len(msgs) > 0 {
12+
t.Fatalf("%s: expected not to get error, but get %d errors:\n%s", title, len(msgs), strings.Join(msgs, "\n"))
13+
} else if !valid && len(msgs) == 0 {
14+
t.Fatalf("%s: expected to get error, but actually not", title)
15+
}
16+
}
17+
18+
func TestCheckSemVer(t *testing.T) {
19+
cases := []struct {
20+
val string
21+
expected bool
22+
}{
23+
{rspec.Version, true},
24+
//FIXME: validate currently only handles rpsec.Version
25+
{"0.0.1", false},
26+
{"invalid", false},
27+
}
28+
29+
for _, c := range cases {
30+
v := NewValidator(&rspec.Spec{Version: c.val}, "", false)
31+
checkErrors(t, "checkSemVer "+c.val, v.CheckSemVer(), c.expected)
32+
}
33+
}

0 commit comments

Comments
 (0)