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,90 @@ 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+ return nil
90+ }
91+
92+ func runtimeOutsideValidate (runtime string , g * generate.Generator ) error {
93+ // Find the runtime binary in the PATH
94+ runtimePath , err := exec .LookPath (runtime )
5495 if err != nil {
5596 return err
5697 }
5798
58- // Generate test configuration
59- err = g .SaveToFile (filepath .Join (bundleDir , "config.json" ), generate.ExportOptions {})
99+ bundleDir , _ , err := prepareBundle (g )
60100 if err != nil {
61101 return err
62102 }
103+ // defer os.RemoveAll(bundleDir)
63104
64105 // TODO: Use a library to split run into create/start
65106 // Launch the OCI runtime
66107 containerID := uuid .NewV4 ()
67- runtimeCmd := exec .Command (runtimePath , "run" , containerID .String ())
108+ runtimeCmd := exec .Command (runtimePath , "run" , "-d" , containerID .String ())
68109 runtimeCmd .Dir = bundleDir
69110 runtimeCmd .Stdin = os .Stdin
70111 runtimeCmd .Stdout = os .Stdout
@@ -73,29 +114,108 @@ func runtimeValidate(runtime string, g *generate.Generator) error {
73114 return err
74115 }
75116
76- return nil
117+ outsideValidations := []validation {
118+ {
119+ test : validateLabels ,
120+ description : "labels" ,
121+ },
122+ // Add more container outside validation
123+ }
124+
125+ t := tap .New ()
126+ t .Header (0 )
127+
128+ var validationErrors error
129+ for _ , v := range outsideValidations {
130+ err := v .test (runtimePath , containerID .String (), g .Spec ())
131+ t .Ok (err == nil , v .description )
132+ if err != nil {
133+ validationErrors = multierror .Append (validationErrors , err )
134+ }
135+ }
136+ t .AutoPlan ()
137+
138+ return validationErrors
77139}
78140
79- func getDefaultGenerator () * generate.Generator {
80- g := generate .New ()
81- g .SetRootPath ("." )
82- g .SetProcessArgs ([]string {"/runtimetest" })
83- return & g
141+ func validateLabels (runtimePath , id string , spec * rspec.Spec ) error {
142+ runtimeCmd := exec .Command (runtimePath , "state" , id )
143+ output , err := runtimeCmd .Output ()
144+ if err != nil {
145+ return err
146+ }
147+
148+ var state rspec.State
149+ if err := json .NewDecoder (strings .NewReader (string (output ))).Decode (& state ); err != nil {
150+ return err
151+ }
152+ for key , value := range spec .Annotations {
153+ if state .Annotations [key ] == value {
154+ continue
155+ }
156+ return fmt .Errorf ("Expected annotation %s:%s not set" , key , value )
157+ }
158+ return nil
84159}
85160
86- func TestValidateBasic (t * testing.T ) {
87- g := getDefaultGenerator ()
161+ func prepareBundle (g * generate.Generator ) (string , string , error ) {
162+ // Setup a temporary test directory
163+ tmpDir , err := ioutil .TempDir ("" , "ocitest" )
164+ if err != nil {
165+ return "" , "" , err
166+ }
88167
89- if err := runtimeValidate (runtime , g ); err != nil {
90- t .Errorf ("%s failed validation: %v" , runtime , err )
168+ // Create bundle directory for the test container
169+ bundleDir := tmpDir
170+ if err := os .MkdirAll (bundleDir , 0755 ); err != nil {
171+ return "" , "" , err
172+ }
173+
174+ // Create rootfs directory for the test container
175+ rootfsDir := bundleDir + "/rootfs"
176+ if err := os .MkdirAll (rootfsDir , 0755 ); err != nil {
177+ return "" , "" , err
178+ }
179+
180+ // Untar the root fs
181+ untarCmd := exec .Command ("tar" , "-xf" , "../rootfs.tar.gz" , "-C" , rootfsDir )
182+ output , err := untarCmd .CombinedOutput ()
183+ if err != nil {
184+ fmt .Println (string (output ))
185+ return "" , "" , err
186+ }
187+
188+ // Generate test configuration
189+ err = g .SaveToFile (filepath .Join (bundleDir , "config.json" ), generate.ExportOptions {})
190+ if err != nil {
191+ return "" , "" , err
91192 }
193+
194+ // Copy the configuration file to the rootfs
195+ err = fileutils .CopyFile (filepath .Join (bundleDir , "config.json" ), filepath .Join (rootfsDir , "config.json" ))
196+ if err != nil {
197+ return "" , "" , err
198+ }
199+
200+ return bundleDir , rootfsDir , nil
92201}
93202
94- func TestValidateSysctls (t * testing.T ) {
95- g := getDefaultGenerator ()
96- g .AddLinuxSysctl ("net.ipv4.ip_forward" , "1" )
203+ func getDefaultGenerator () (* generate.Generator , error ) {
204+ // Generate testcase template
205+ 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" )
206+ output , err := generateCmd .CombinedOutput ()
207+ if err != nil {
208+ fmt .Println (string (output ))
209+ return nil , err
210+ }
97211
98- if err := runtimeValidate (runtime , g ); err != nil {
99- t .Errorf ("%s failed validation: %v" , runtime , err )
212+ // Get testcase configuration
213+ g , err := generate .NewFromFile ("/tmp/config.json" )
214+ if err != nil {
215+ return nil , err
100216 }
217+
218+ g .SetRootPath ("rootfs" )
219+
220+ return & g , nil
101221}
0 commit comments