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,51 +22,93 @@ 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+ g .SetProcessArgs ([]string {"sleep" , "50" })
52+
53+ if err := runtimeOutsideValidate (runtime , g ); err != nil {
54+ t .Errorf ("%s failed validation: %v" , runtime , err )
55+ }
56+ }
57+
58+ func runtimeInsideValidate (runtime string , g * generate.Generator ) error {
2559 // Find the runtime binary in the PATH
2660 runtimePath , err := exec .LookPath (runtime )
2761 if err != nil {
2862 return err
2963 }
3064
31- // Setup a temporary test directory
32- tmpDir , err := ioutil .TempDir ("" , "ocitest" )
65+ bundleDir , rootfsDir , err := prepareBundle (g )
3366 if err != nil {
3467 return err
3568 }
36- defer os .RemoveAll (tmpDir )
69+ defer os .RemoveAll (bundleDir )
3770
38- // Create bundle directory for the test container
39- bundleDir := tmpDir + "/busybox"
40- if err := os . MkdirAll ( bundleDir , 0755 ); err != nil {
71+ // Copy the runtimetest binary to the rootfs
72+ err = fileutils . CopyFile ( "../runtimetest" , filepath . Join ( rootfsDir , "runtimetest" ))
73+ if err != nil {
4174 return err
4275 }
4376
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 ))
77+ // TODO: Use a library to split run into create/start
78+ // Launch the OCI runtime
79+ containerID := uuid .NewV4 ()
80+ runtimeCmd := exec .Command (runtimePath , "run" , containerID .String ())
81+ runtimeCmd .Dir = bundleDir
82+ runtimeCmd .Stdin = os .Stdin
83+ runtimeCmd .Stdout = os .Stdout
84+ runtimeCmd .Stderr = os .Stderr
85+ if err = runtimeCmd .Run (); err != nil {
4986 return err
5087 }
5188
52- // Copy the runtimetest binary to the rootfs
53- err = fileutils .CopyFile ("../runtimetest" , filepath .Join (bundleDir , "runtimetest" ))
89+ if err = cleanup (runtimePath , containerID .String ()); err != nil {
90+ return err
91+ }
5492
55- // Generate test configuration
56- err = g .SaveToFile (filepath .Join (bundleDir , "config.json" ), generate.ExportOptions {})
93+ return nil
94+ }
95+
96+ func runtimeOutsideValidate (runtime string , g * generate.Generator ) error {
97+ // Find the runtime binary in the PATH
98+ runtimePath , err := exec .LookPath (runtime )
5799 if err != nil {
58100 return err
59101 }
60102
61- // TODO: Use a library to split run into create/start
103+ bundleDir , _ , err := prepareBundle (g )
104+ if err != nil {
105+ return err
106+ }
107+ // defer os.RemoveAll(bundleDir)
108+
62109 // Launch the OCI runtime
63110 containerID := uuid .NewV4 ()
64- runtimeCmd := exec .Command (runtimePath , "run " , containerID .String ())
111+ runtimeCmd := exec .Command (runtimePath , "create " , containerID .String ())
65112 runtimeCmd .Dir = bundleDir
66113 runtimeCmd .Stdin = os .Stdin
67114 runtimeCmd .Stdout = os .Stdout
@@ -70,29 +117,126 @@ func runtimeValidate(runtime string, g *generate.Generator) error {
70117 return err
71118 }
72119
120+ outsideValidations := []validation {
121+ {
122+ test : validateLabels ,
123+ description : "labels" ,
124+ },
125+ // Add more container outside validation
126+ }
127+
128+ t := tap .New ()
129+ t .Header (0 )
130+
131+ var validationErrors error
132+ for _ , v := range outsideValidations {
133+ err := v .test (runtimePath , containerID .String (), g .Spec ())
134+ t .Ok (err == nil , v .description )
135+ if err != nil {
136+ validationErrors = multierror .Append (validationErrors , err )
137+ }
138+ }
139+ t .AutoPlan ()
140+
141+ if err = cleanup (runtimePath , containerID .String ()); err != nil {
142+ validationErrors = multierror .Append (validationErrors , err )
143+ }
144+
145+ return validationErrors
146+ }
147+
148+ func validateLabels (runtimePath , id string , spec * rspec.Spec ) error {
149+ runtimeCmd := exec .Command (runtimePath , "state" , id )
150+ output , err := runtimeCmd .Output ()
151+ if err != nil {
152+ return err
153+ }
154+
155+ var state rspec.State
156+ if err := json .NewDecoder (strings .NewReader (string (output ))).Decode (& state ); err != nil {
157+ return err
158+ }
159+ for key , value := range spec .Annotations {
160+ if state .Annotations [key ] == value {
161+ continue
162+ }
163+ return fmt .Errorf ("Expected annotation %s:%s not set" , key , value )
164+ }
73165 return nil
74166}
75167
76- func getDefaultGenerator () * generate.Generator {
77- g := generate .New ()
78- g .SetRootPath ("." )
79- g .SetProcessArgs ([]string {"/runtimetest" })
80- return & g
168+ func cleanup (runtimePath , id string ) error {
169+ runtimeCmd := exec .Command (runtimePath , "kill" , id , "KILL" )
170+ if err := runtimeCmd .Run (); err != nil {
171+ return fmt .Errorf ("Failed to kill container %s: %v" , id , err )
172+ }
173+
174+ runtimeCmd = exec .Command (runtimePath , "delete" , id )
175+ if err := runtimeCmd .Run (); err != nil {
176+ return fmt .Errorf ("Failed to kill container %s: %v" , id , err )
177+ }
178+
179+ return nil
81180}
82181
83- func TestValidateBasic (t * testing.T ) {
84- g := getDefaultGenerator ()
182+ func prepareBundle (g * generate.Generator ) (string , string , error ) {
183+ // Setup a temporary test directory
184+ tmpDir , err := ioutil .TempDir ("" , "ocitest" )
185+ if err != nil {
186+ return "" , "" , err
187+ }
85188
86- if err := runtimeValidate (runtime , g ); err != nil {
87- t .Errorf ("%s failed validation: %v" , runtime , err )
189+ // Create bundle directory for the test container
190+ bundleDir := tmpDir
191+ if err := os .MkdirAll (bundleDir , 0755 ); err != nil {
192+ return "" , "" , err
193+ }
194+
195+ // Create rootfs directory for the test container
196+ rootfsDir := bundleDir + "/rootfs"
197+ if err := os .MkdirAll (rootfsDir , 0755 ); err != nil {
198+ return "" , "" , err
199+ }
200+
201+ // Untar the root fs
202+ untarCmd := exec .Command ("tar" , "-xf" , "../rootfs.tar.gz" , "-C" , rootfsDir )
203+ output , err := untarCmd .CombinedOutput ()
204+ if err != nil {
205+ fmt .Println (string (output ))
206+ return "" , "" , err
207+ }
208+
209+ // Generate test configuration
210+ err = g .SaveToFile (filepath .Join (bundleDir , "config.json" ), generate.ExportOptions {})
211+ if err != nil {
212+ return "" , "" , err
88213 }
214+
215+ // Copy the configuration file to the rootfs
216+ err = fileutils .CopyFile (filepath .Join (bundleDir , "config.json" ), filepath .Join (rootfsDir , "config.json" ))
217+ if err != nil {
218+ return "" , "" , err
219+ }
220+
221+ return bundleDir , rootfsDir , nil
89222}
90223
91- func TestValidateSysctls (t * testing.T ) {
92- g := getDefaultGenerator ()
93- g .AddLinuxSysctl ("net.ipv4.ip_forward" , "1" )
224+ func getDefaultGenerator () (* generate.Generator , error ) {
225+ // Generate testcase template
226+ generateCmd := exec .Command ("oci-runtime-tool" , "generate" , "--bind=/tmp:/volume/testing:rw" , "--cgroups-path=/tmp/testcgroup" , "--device-add=c:80:500:/dev/test:fileMode=438" , "--disable-oom-kill=true" , "--env=testvar=vartest" , "--hostname=localvalidation" , "--label=testlabel=nonevar" , "--linux-cpu-shares=1024" , "--output" , "/tmp/config.json" )
227+ output , err := generateCmd .CombinedOutput ()
228+ if err != nil {
229+ fmt .Println (string (output ))
230+ return nil , err
231+ }
94232
95- if err := runtimeValidate (runtime , g ); err != nil {
96- t .Errorf ("%s failed validation: %v" , runtime , err )
233+ // Get testcase configuration
234+ g , err := generate .NewFromFile ("/tmp/config.json" )
235+ if err != nil {
236+ return nil , err
97237 }
238+
239+ g .SetRootPath ("rootfs" )
240+
241+ return & g , nil
98242}
0 commit comments