11package validation
22
33import (
4+ "encoding/json"
45 "fmt"
56 "io/ioutil"
67 "os"
78 "os/exec"
89 "path/filepath"
10+ "strings"
911 "testing"
1012
13+ "github.com/hashicorp/go-multierror"
14+ "github.com/mndrix/tap-go"
1115 "github.com/mrunalp/fileutils"
16+ rspec "github.com/opencontainers/runtime-spec/specs-go"
1217 "github.com/opencontainers/runtime-tools/generate"
1318 "github.com/satori/go.uuid"
1419)
@@ -17,54 +22,92 @@ var (
1722 runtime = "runc"
1823)
1924
25+ type validation struct {
26+ test func (string , string , * rspec.Spec ) error
27+ description string
28+ }
29+
2030func init () {
2131 runtime = os .Getenv ("RUNTIME" )
2232}
2333
24- func runtimeValidate (runtime string , g * generate.Generator ) error {
34+ func TestValidateRuntimeInside (t * testing.T ) {
35+ g , err := getDefaultGenerator ()
36+ if err != nil {
37+ t .Errorf ("%s failed validation: %v" , runtime , err )
38+ }
39+ g .SetProcessArgs ([]string {"/runtimetest" })
40+
41+ if err := runtimeInsideValidate (runtime , g ); err != nil {
42+ t .Errorf ("%s failed validation: %v" , runtime , err )
43+ }
44+ }
45+
46+ func TestValidateRuntimeOutside (t * testing.T ) {
47+ g , err := getDefaultGenerator ()
48+ if err != nil {
49+ t .Errorf ("%s failed validation: %v" , runtime , err )
50+ }
51+
52+ if err := runtimeOutsideValidate (runtime , g ); err != nil {
53+ t .Errorf ("%s failed validation: %v" , runtime , err )
54+ }
55+ }
56+
57+ func runtimeInsideValidate (runtime string , g * generate.Generator ) error {
2558 // Find the runtime binary in the PATH
2659 runtimePath , err := exec .LookPath (runtime )
2760 if err != nil {
2861 return err
2962 }
3063
31- // Setup a temporary test directory
32- tmpDir , err := ioutil .TempDir ("" , "ocitest" )
64+ bundleDir , rootfsDir , err := prepareBundle (g )
3365 if err != nil {
3466 return err
3567 }
36- defer os .RemoveAll (tmpDir )
68+ // defer os.RemoveAll(bundleDir )
3769
38- // Create bundle directory for the test container
39- bundleDir := tmpDir + "/busybox"
40- if err := os . MkdirAll ( bundleDir , 0755 ); err != nil {
70+ // Copy the runtimetest binary to the rootfs
71+ err = fileutils . CopyFile ( "../runtimetest" , filepath . Join ( rootfsDir , "runtimetest" ))
72+ if err != nil {
4173 return err
4274 }
4375
44- // Untar the root fs
45- untarCmd := exec .Command ("tar" , "-xf" , "../rootfs.tar.gz" , "-C" , bundleDir )
46- output , err := untarCmd .CombinedOutput ()
47- if err != nil {
48- fmt .Println (string (output ))
76+ // TODO: Use a library to split run into create/start
77+ // Launch the OCI runtime
78+ containerID := uuid .NewV4 ()
79+ runtimeCmd := exec .Command (runtimePath , "run" , containerID .String ())
80+ runtimeCmd .Dir = bundleDir
81+ runtimeCmd .Stdin = os .Stdin
82+ runtimeCmd .Stdout = os .Stdout
83+ runtimeCmd .Stderr = os .Stderr
84+ if err = runtimeCmd .Run (); err != nil {
4985 return err
5086 }
5187
52- // Copy the runtimetest binary to the rootfs
53- err = fileutils .CopyFile ("../runtimetest" , filepath .Join (bundleDir , "runtimetest" ))
88+ if err = cleanup (runtimePath , containerID .String ()); err != nil {
89+ return err
90+ }
91+
92+ return nil
93+ }
94+
95+ func runtimeOutsideValidate (runtime string , g * generate.Generator ) error {
96+ // Find the runtime binary in the PATH
97+ runtimePath , err := exec .LookPath (runtime )
5498 if err != nil {
5599 return err
56100 }
57101
58- // Generate test configuration
59- err = g .SaveToFile (filepath .Join (bundleDir , "config.json" ), generate.ExportOptions {})
102+ bundleDir , _ , err := prepareBundle (g )
60103 if err != nil {
61104 return err
62105 }
106+ defer os .RemoveAll (bundleDir )
63107
64- // TODO: Use a library to split run into create/start
65108 // Launch the OCI runtime
66109 containerID := uuid .NewV4 ()
67- runtimeCmd := exec .Command (runtimePath , "run " , containerID .String ())
110+ runtimeCmd := exec .Command (runtimePath , "create " , containerID .String ())
68111 runtimeCmd .Dir = bundleDir
69112 runtimeCmd .Stdin = os .Stdin
70113 runtimeCmd .Stdout = os .Stdout
@@ -73,29 +116,126 @@ func runtimeValidate(runtime string, g *generate.Generator) error {
73116 return err
74117 }
75118
119+ outsideValidations := []validation {
120+ {
121+ test : validateLabels ,
122+ description : "labels" ,
123+ },
124+ // Add more container outside validation
125+ }
126+
127+ t := tap .New ()
128+ t .Header (0 )
129+
130+ var validationErrors error
131+ for _ , v := range outsideValidations {
132+ err := v .test (runtimePath , containerID .String (), g .Spec ())
133+ t .Ok (err == nil , v .description )
134+ if err != nil {
135+ validationErrors = multierror .Append (validationErrors , err )
136+ }
137+ }
138+ t .AutoPlan ()
139+
140+ if err = cleanup (runtimePath , containerID .String ()); err != nil {
141+ validationErrors = multierror .Append (validationErrors , err )
142+ }
143+
144+ return validationErrors
145+ }
146+
147+ func validateLabels (runtimePath , id string , spec * rspec.Spec ) error {
148+ runtimeCmd := exec .Command (runtimePath , "state" , id )
149+ output , err := runtimeCmd .Output ()
150+ if err != nil {
151+ return err
152+ }
153+
154+ var state rspec.State
155+ if err := json .NewDecoder (strings .NewReader (string (output ))).Decode (& state ); err != nil {
156+ return err
157+ }
158+ for key , value := range spec .Annotations {
159+ if state .Annotations [key ] == value {
160+ continue
161+ }
162+ return fmt .Errorf ("Expected annotation %s:%s not set" , key , value )
163+ }
76164 return nil
77165}
78166
79- func getDefaultGenerator () * generate.Generator {
80- g := generate .New ()
81- g .SetRootPath ("." )
82- g .SetProcessArgs ([]string {"/runtimetest" })
83- return & g
167+ func cleanup (runtimePath , id string ) error {
168+ runtimeCmd := exec .Command (runtimePath , "kill" , id , "KILL" )
169+ if err := runtimeCmd .Run (); err != nil {
170+ return fmt .Errorf ("Failed to kill container %s: %v" , id , err )
171+ }
172+
173+ runtimeCmd = exec .Command (runtimePath , "delete" , id )
174+ if err := runtimeCmd .Run (); err != nil {
175+ return fmt .Errorf ("Failed to kill container %s: %v" , id , err )
176+ }
177+
178+ return nil
84179}
85180
86- func TestValidateBasic (t * testing.T ) {
87- g := getDefaultGenerator ()
181+ func prepareBundle (g * generate.Generator ) (string , string , error ) {
182+ // Setup a temporary test directory
183+ tmpDir , err := ioutil .TempDir ("" , "ocitest" )
184+ if err != nil {
185+ return "" , "" , err
186+ }
88187
89- if err := runtimeValidate (runtime , g ); err != nil {
90- t .Errorf ("%s failed validation: %v" , runtime , err )
188+ // Create bundle directory for the test container
189+ bundleDir := tmpDir
190+ if err := os .MkdirAll (bundleDir , 0755 ); err != nil {
191+ return "" , "" , err
192+ }
193+
194+ // Create rootfs directory for the test container
195+ rootfsDir := bundleDir + "/rootfs"
196+ if err := os .MkdirAll (rootfsDir , 0755 ); err != nil {
197+ return "" , "" , err
198+ }
199+
200+ // Untar the root fs
201+ untarCmd := exec .Command ("tar" , "-xf" , "../rootfs.tar.gz" , "-C" , rootfsDir )
202+ output , err := untarCmd .CombinedOutput ()
203+ if err != nil {
204+ fmt .Println (string (output ))
205+ return "" , "" , err
206+ }
207+
208+ // Generate test configuration
209+ err = g .SaveToFile (filepath .Join (bundleDir , "config.json" ), generate.ExportOptions {})
210+ if err != nil {
211+ return "" , "" , err
91212 }
213+
214+ // Copy the configuration file to the rootfs
215+ err = fileutils .CopyFile (filepath .Join (bundleDir , "config.json" ), filepath .Join (rootfsDir , "config.json" ))
216+ if err != nil {
217+ return "" , "" , err
218+ }
219+
220+ return bundleDir , rootfsDir , nil
92221}
93222
94- func TestValidateSysctls (t * testing.T ) {
95- g := getDefaultGenerator ()
96- g .AddLinuxSysctl ("net.ipv4.ip_forward" , "1" )
223+ func getDefaultGenerator () (* generate.Generator , error ) {
224+ // Generate testcase template
225+ generateCmd := exec .Command ("oci-runtime-tool" , "generate" , "--mount-bind=/tmp:/volume/testing:rw" , "--linux-cgroups-path=/tmp/testcgroup" , "--linux-device-add=c:80:500:/dev/test:fileMode=438" , "--linux-disable-oom-kill=true" , "--env=testvar=vartest" , "--hostname=localvalidation" , "--label=testlabel=nonevar" , "--linux-cpu-shares=1024" , "--output" , "/tmp/config.json" )
226+ output , err := generateCmd .CombinedOutput ()
227+ if err != nil {
228+ fmt .Println (string (output ))
229+ return nil , err
230+ }
97231
98- if err := runtimeValidate (runtime , g ); err != nil {
99- t .Errorf ("%s failed validation: %v" , runtime , err )
232+ // Get testcase configuration
233+ g , err := generate .NewFromFile ("/tmp/config.json" )
234+ if err != nil {
235+ return nil , err
100236 }
237+
238+ g .SetRootPath ("rootfs" )
239+
240+ return & g , nil
101241}
0 commit comments