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,88 @@ 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+ return nil
89+ }
90+
91+ func runtimeOutsideValidate (runtime string , g * generate.Generator ) error {
92+ // Find the runtime binary in the PATH
93+ runtimePath , err := exec .LookPath (runtime )
5494 if err != nil {
5595 return err
5696 }
5797
58- // Generate test configuration
59- err = g .SaveToFile (filepath .Join (bundleDir , "config.json" ), generate.ExportOptions {})
98+ bundleDir , _ , err := prepareBundle (g )
6099 if err != nil {
61100 return err
62101 }
102+ defer os .RemoveAll (bundleDir )
63103
64- // TODO: Use a library to split run into create/start
65104 // Launch the OCI runtime
66105 containerID := uuid .NewV4 ()
67- runtimeCmd := exec .Command (runtimePath , "run " , containerID .String ())
106+ runtimeCmd := exec .Command (runtimePath , "create " , containerID .String ())
68107 runtimeCmd .Dir = bundleDir
69108 runtimeCmd .Stdin = os .Stdin
70109 runtimeCmd .Stdout = os .Stdout
@@ -73,29 +112,126 @@ func runtimeValidate(runtime string, g *generate.Generator) error {
73112 return err
74113 }
75114
115+ outsideValidations := []validation {
116+ {
117+ test : validateLabels ,
118+ description : "labels" ,
119+ },
120+ // Add more container outside validation
121+ }
122+
123+ t := tap .New ()
124+ t .Header (0 )
125+
126+ var validationErrors error
127+ for _ , v := range outsideValidations {
128+ err := v .test (runtimePath , containerID .String (), g .Spec ())
129+ t .Ok (err == nil , v .description )
130+ if err != nil {
131+ validationErrors = multierror .Append (validationErrors , err )
132+ }
133+ }
134+ t .AutoPlan ()
135+
136+ if err = cleanup (runtimePath , containerID .String ()); err != nil {
137+ validationErrors = multierror .Append (validationErrors , err )
138+ }
139+
140+ return validationErrors
141+ }
142+
143+ func validateLabels (runtimePath , id string , spec * rspec.Spec ) error {
144+ runtimeCmd := exec .Command (runtimePath , "state" , id )
145+ output , err := runtimeCmd .Output ()
146+ if err != nil {
147+ return err
148+ }
149+
150+ var state rspec.State
151+ if err := json .NewDecoder (strings .NewReader (string (output ))).Decode (& state ); err != nil {
152+ return err
153+ }
154+ for key , value := range spec .Annotations {
155+ if state .Annotations [key ] == value {
156+ continue
157+ }
158+ return fmt .Errorf ("Expected annotation %s:%s not set" , key , value )
159+ }
76160 return nil
77161}
78162
79- func getDefaultGenerator () * generate.Generator {
80- g := generate .New ()
81- g .SetRootPath ("." )
82- g .SetProcessArgs ([]string {"/runtimetest" })
83- return & g
163+ func cleanup (runtimePath , id string ) error {
164+ runtimeCmd := exec .Command (runtimePath , "kill" , id , "KILL" )
165+ if err := runtimeCmd .Run (); err != nil {
166+ return fmt .Errorf ("Failed to kill container %s: %v" , id , err )
167+ }
168+
169+ runtimeCmd = exec .Command (runtimePath , "delete" , id )
170+ if err := runtimeCmd .Run (); err != nil {
171+ return fmt .Errorf ("Failed to kill container %s: %v" , id , err )
172+ }
173+
174+ return nil
84175}
85176
86- func TestValidateBasic (t * testing.T ) {
87- g := getDefaultGenerator ()
177+ func prepareBundle (g * generate.Generator ) (string , string , error ) {
178+ // Setup a temporary test directory
179+ tmpDir , err := ioutil .TempDir ("" , "ocitest" )
180+ if err != nil {
181+ return "" , "" , err
182+ }
88183
89- if err := runtimeValidate (runtime , g ); err != nil {
90- t .Errorf ("%s failed validation: %v" , runtime , err )
184+ // Create bundle directory for the test container
185+ bundleDir := tmpDir
186+ if err := os .MkdirAll (bundleDir , 0755 ); err != nil {
187+ return "" , "" , err
188+ }
189+
190+ // Create rootfs directory for the test container
191+ rootfsDir := bundleDir + "/rootfs"
192+ if err := os .MkdirAll (rootfsDir , 0755 ); err != nil {
193+ return "" , "" , err
91194 }
195+
196+ // Untar the root fs
197+ untarCmd := exec .Command ("tar" , "-xf" , "../rootfs.tar.gz" , "-C" , rootfsDir )
198+ output , err := untarCmd .CombinedOutput ()
199+ if err != nil {
200+ fmt .Println (string (output ))
201+ return "" , "" , err
202+ }
203+
204+ // Generate test configuration
205+ err = g .SaveToFile (filepath .Join (bundleDir , "config.json" ), generate.ExportOptions {})
206+ if err != nil {
207+ return "" , "" , err
208+ }
209+
210+ // Copy the configuration file to the rootfs
211+ err = fileutils .CopyFile (filepath .Join (bundleDir , "config.json" ), filepath .Join (rootfsDir , "config.json" ))
212+ if err != nil {
213+ return "" , "" , err
214+ }
215+
216+ return bundleDir , rootfsDir , nil
92217}
93218
94- func TestValidateSysctls (t * testing.T ) {
95- g := getDefaultGenerator ()
96- g .AddLinuxSysctl ("net.ipv4.ip_forward" , "1" )
219+ func getDefaultGenerator () (* generate.Generator , error ) {
220+ // Generate testcase template
221+ 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" )
222+ output , err := generateCmd .CombinedOutput ()
223+ if err != nil {
224+ fmt .Println (string (output ))
225+ return nil , err
226+ }
97227
98- if err := runtimeValidate (runtime , g ); err != nil {
99- t .Errorf ("%s failed validation: %v" , runtime , err )
228+ // Get testcase configuration
229+ g , err := generate .NewFromFile ("/tmp/config.json" )
230+ if err != nil {
231+ return nil , err
100232 }
233+
234+ g .SetRootPath ("rootfs" )
235+
236+ return & g , nil
101237}
0 commit comments