@@ -2,13 +2,16 @@ package validate
22
33import (
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
5558type 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
65100func (v * Validator ) CheckAll () (msgs []string ) {
@@ -77,7 +112,7 @@ func (v *Validator) CheckAll() (msgs []string) {
77112func (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+
121166func 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
156192func (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)
235271func (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) {
570606func (v * Validator ) CheckMandatoryFields () []string {
571607 logrus .Debugf ("check mandatory fields" )
572608
573- return checkMandatory (v .spec )
609+ return checkMandatory (v .Spec )
574610}
0 commit comments