github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/runsc/boot/fs_test.go (about) 1 // Copyright 2019 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package boot 16 17 import ( 18 "reflect" 19 "strings" 20 "testing" 21 22 specs "github.com/opencontainers/runtime-spec/specs-go" 23 "github.com/SagerNet/gvisor/runsc/config" 24 ) 25 26 func TestPodMountHintsHappy(t *testing.T) { 27 spec := &specs.Spec{ 28 Annotations: map[string]string{ 29 MountPrefix + "mount1.source": "foo", 30 MountPrefix + "mount1.type": "tmpfs", 31 MountPrefix + "mount1.share": "pod", 32 33 MountPrefix + "mount2.source": "bar", 34 MountPrefix + "mount2.type": "bind", 35 MountPrefix + "mount2.share": "container", 36 MountPrefix + "mount2.options": "rw,private", 37 }, 38 } 39 podHints, err := newPodMountHints(spec) 40 if err != nil { 41 t.Fatalf("newPodMountHints failed: %v", err) 42 } 43 44 // Check that fields were set correctly. 45 mount1 := podHints.mounts["mount1"] 46 if want := "mount1"; want != mount1.name { 47 t.Errorf("mount1 name, want: %q, got: %q", want, mount1.name) 48 } 49 if want := "foo"; want != mount1.mount.Source { 50 t.Errorf("mount1 source, want: %q, got: %q", want, mount1.mount.Source) 51 } 52 if want := "tmpfs"; want != mount1.mount.Type { 53 t.Errorf("mount1 type, want: %q, got: %q", want, mount1.mount.Type) 54 } 55 if want := pod; want != mount1.share { 56 t.Errorf("mount1 type, want: %q, got: %q", want, mount1.share) 57 } 58 if want := []string(nil); !reflect.DeepEqual(want, mount1.mount.Options) { 59 t.Errorf("mount1 type, want: %q, got: %q", want, mount1.mount.Options) 60 } 61 62 mount2 := podHints.mounts["mount2"] 63 if want := "mount2"; want != mount2.name { 64 t.Errorf("mount2 name, want: %q, got: %q", want, mount2.name) 65 } 66 if want := "bar"; want != mount2.mount.Source { 67 t.Errorf("mount2 source, want: %q, got: %q", want, mount2.mount.Source) 68 } 69 if want := "bind"; want != mount2.mount.Type { 70 t.Errorf("mount2 type, want: %q, got: %q", want, mount2.mount.Type) 71 } 72 if want := container; want != mount2.share { 73 t.Errorf("mount2 type, want: %q, got: %q", want, mount2.share) 74 } 75 if want := []string{"private", "rw"}; !reflect.DeepEqual(want, mount2.mount.Options) { 76 t.Errorf("mount2 type, want: %q, got: %q", want, mount2.mount.Options) 77 } 78 } 79 80 func TestPodMountHintsErrors(t *testing.T) { 81 for _, tst := range []struct { 82 name string 83 annotations map[string]string 84 error string 85 }{ 86 { 87 name: "too short", 88 annotations: map[string]string{ 89 MountPrefix + "mount1": "foo", 90 }, 91 error: "invalid mount annotation", 92 }, 93 { 94 name: "no name", 95 annotations: map[string]string{ 96 MountPrefix + ".source": "foo", 97 }, 98 error: "invalid mount name", 99 }, 100 { 101 name: "missing source", 102 annotations: map[string]string{ 103 MountPrefix + "mount1.type": "tmpfs", 104 MountPrefix + "mount1.share": "pod", 105 }, 106 error: "source field", 107 }, 108 { 109 name: "missing type", 110 annotations: map[string]string{ 111 MountPrefix + "mount1.source": "foo", 112 MountPrefix + "mount1.share": "pod", 113 }, 114 error: "type field", 115 }, 116 { 117 name: "missing share", 118 annotations: map[string]string{ 119 MountPrefix + "mount1.source": "foo", 120 MountPrefix + "mount1.type": "tmpfs", 121 }, 122 error: "share field", 123 }, 124 { 125 name: "invalid field name", 126 annotations: map[string]string{ 127 MountPrefix + "mount1.invalid": "foo", 128 }, 129 error: "invalid mount annotation", 130 }, 131 { 132 name: "invalid source", 133 annotations: map[string]string{ 134 MountPrefix + "mount1.source": "", 135 MountPrefix + "mount1.type": "tmpfs", 136 MountPrefix + "mount1.share": "pod", 137 }, 138 error: "source cannot be empty", 139 }, 140 { 141 name: "invalid type", 142 annotations: map[string]string{ 143 MountPrefix + "mount1.source": "foo", 144 MountPrefix + "mount1.type": "invalid-type", 145 MountPrefix + "mount1.share": "pod", 146 }, 147 error: "invalid type", 148 }, 149 { 150 name: "invalid share", 151 annotations: map[string]string{ 152 MountPrefix + "mount1.source": "foo", 153 MountPrefix + "mount1.type": "tmpfs", 154 MountPrefix + "mount1.share": "invalid-share", 155 }, 156 error: "invalid share", 157 }, 158 { 159 name: "invalid options", 160 annotations: map[string]string{ 161 MountPrefix + "mount1.source": "foo", 162 MountPrefix + "mount1.type": "tmpfs", 163 MountPrefix + "mount1.share": "pod", 164 MountPrefix + "mount1.options": "invalid-option", 165 }, 166 error: "unknown mount option", 167 }, 168 { 169 name: "duplicate source", 170 annotations: map[string]string{ 171 MountPrefix + "mount1.source": "foo", 172 MountPrefix + "mount1.type": "tmpfs", 173 MountPrefix + "mount1.share": "pod", 174 175 MountPrefix + "mount2.source": "foo", 176 MountPrefix + "mount2.type": "bind", 177 MountPrefix + "mount2.share": "container", 178 }, 179 error: "have the same mount source", 180 }, 181 } { 182 t.Run(tst.name, func(t *testing.T) { 183 spec := &specs.Spec{Annotations: tst.annotations} 184 podHints, err := newPodMountHints(spec) 185 if err == nil || !strings.Contains(err.Error(), tst.error) { 186 t.Errorf("newPodMountHints invalid error, want: .*%s.*, got: %v", tst.error, err) 187 } 188 if podHints != nil { 189 t.Errorf("newPodMountHints must return nil on failure: %+v", podHints) 190 } 191 }) 192 } 193 } 194 195 func TestGetMountAccessType(t *testing.T) { 196 const source = "foo" 197 for _, tst := range []struct { 198 name string 199 annotations map[string]string 200 want config.FileAccessType 201 }{ 202 { 203 name: "container=exclusive", 204 annotations: map[string]string{ 205 MountPrefix + "mount1.source": source, 206 MountPrefix + "mount1.type": "bind", 207 MountPrefix + "mount1.share": "container", 208 }, 209 want: config.FileAccessExclusive, 210 }, 211 { 212 name: "pod=shared", 213 annotations: map[string]string{ 214 MountPrefix + "mount1.source": source, 215 MountPrefix + "mount1.type": "bind", 216 MountPrefix + "mount1.share": "pod", 217 }, 218 want: config.FileAccessShared, 219 }, 220 { 221 name: "shared=shared", 222 annotations: map[string]string{ 223 MountPrefix + "mount1.source": source, 224 MountPrefix + "mount1.type": "bind", 225 MountPrefix + "mount1.share": "shared", 226 }, 227 want: config.FileAccessShared, 228 }, 229 { 230 name: "default=shared", 231 annotations: map[string]string{ 232 MountPrefix + "mount1.source": source + "mismatch", 233 MountPrefix + "mount1.type": "bind", 234 MountPrefix + "mount1.share": "container", 235 }, 236 want: config.FileAccessShared, 237 }, 238 } { 239 t.Run(tst.name, func(t *testing.T) { 240 spec := &specs.Spec{Annotations: tst.annotations} 241 podHints, err := newPodMountHints(spec) 242 if err != nil { 243 t.Fatalf("newPodMountHints failed: %v", err) 244 } 245 mounter := containerMounter{hints: podHints} 246 conf := &config.Config{FileAccessMounts: config.FileAccessShared} 247 if got := mounter.getMountAccessType(conf, &specs.Mount{Source: source}); got != tst.want { 248 t.Errorf("getMountAccessType(), want: %v, got: %v", tst.want, got) 249 } 250 }) 251 } 252 }