github.com/stulluk/snapd@v0.0.0-20210611110309-f6d5d5bd24b0/image/helpers_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2014-2020 Canonical Ltd 5 * 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 3 as 8 * published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * 18 */ 19 20 package image_test 21 22 import ( 23 "os" 24 "os/exec" 25 "path/filepath" 26 "runtime" 27 "sort" 28 "strings" 29 30 "gopkg.in/check.v1" 31 32 "github.com/snapcore/snapd/gadget" 33 "github.com/snapcore/snapd/image" 34 "github.com/snapcore/snapd/logger" 35 "github.com/snapcore/snapd/snap" 36 "github.com/snapcore/snapd/snap/snaptest" 37 ) 38 39 func (s *imageSuite) TestDownloadpOptionsString(c *check.C) { 40 tests := []struct { 41 opts image.DownloadOptions 42 str string 43 }{ 44 {image.DownloadOptions{LeavePartialOnError: true}, ""}, 45 {image.DownloadOptions{}, ""}, 46 {image.DownloadOptions{TargetDir: "/foo"}, `in "/foo"`}, 47 {image.DownloadOptions{Basename: "foo"}, `to "foo.snap"`}, 48 {image.DownloadOptions{Channel: "foo"}, `from channel "foo"`}, 49 {image.DownloadOptions{Revision: snap.R(42)}, `(42)`}, 50 {image.DownloadOptions{ 51 CohortKey: "AbCdEfGhIjKlMnOpQrStUvWxYz", 52 }, `from cohort "…rStUvWxYz"`}, 53 {image.DownloadOptions{ 54 TargetDir: "/foo", 55 Basename: "bar", 56 Channel: "baz", 57 Revision: snap.R(13), 58 CohortKey: "MSBIc3dwOW9PemozYjRtdzhnY0MwMFh0eFduS0g5UWlDUSAxNTU1NDExNDE1IDBjYzJhNTc1ZjNjOTQ3ZDEwMWE1NTNjZWFkNmFmZDE3ZWJhYTYyNjM4ZWQ3ZGMzNjI5YmU4YjQ3NzAwMjdlMDk=", 59 }, `(13) from channel "baz" from cohort "…wMjdlMDk=" to "bar.snap" in "/foo"`}, // note this one is not 'valid' so it's ok if the string is a bit wonky 60 61 } 62 63 for _, t := range tests { 64 c.Check(t.opts.String(), check.Equals, t.str) 65 } 66 } 67 68 func (s *imageSuite) TestDownloadOptionsValid(c *check.C) { 69 tests := []struct { 70 opts image.DownloadOptions 71 err error 72 }{ 73 {image.DownloadOptions{}, nil}, // might want to error if no targetdir 74 {image.DownloadOptions{TargetDir: "foo"}, nil}, 75 {image.DownloadOptions{Channel: "foo"}, nil}, 76 {image.DownloadOptions{Revision: snap.R(42)}, nil}, 77 {image.DownloadOptions{ 78 CohortKey: "AbCdEfGhIjKlMnOpQrStUvWxYz", 79 }, nil}, 80 {image.DownloadOptions{ 81 Channel: "foo", 82 Revision: snap.R(42), 83 }, nil}, 84 {image.DownloadOptions{ 85 Channel: "foo", 86 CohortKey: "bar", 87 }, nil}, 88 {image.DownloadOptions{ 89 Revision: snap.R(1), 90 CohortKey: "bar", 91 }, image.ErrRevisionAndCohort}, 92 {image.DownloadOptions{ 93 Basename: "/foo", 94 }, image.ErrPathInBase}, 95 } 96 97 for _, t := range tests { 98 t.opts.LeavePartialOnError = true 99 c.Check(t.opts.Validate(), check.Equals, t.err) 100 t.opts.LeavePartialOnError = false 101 c.Check(t.opts.Validate(), check.Equals, t.err) 102 } 103 } 104 105 func (s *imageSuite) TestDownloadSnap(c *check.C) { 106 // TODO: maybe expand on this (test coverage of DownloadSnap is really bad) 107 108 // env shenanigans 109 runtime.LockOSThread() 110 defer runtime.UnlockOSThread() 111 112 debug, hadDebug := os.LookupEnv("SNAPD_DEBUG") 113 os.Setenv("SNAPD_DEBUG", "1") 114 if hadDebug { 115 defer os.Setenv("SNAPD_DEBUG", debug) 116 } else { 117 defer os.Unsetenv("SNAPD_DEBUG") 118 } 119 logbuf, restore := logger.MockLogger() 120 defer restore() 121 122 s.setupSnaps(c, map[string]string{ 123 "core": "canonical", 124 }, "") 125 126 dlDir := c.MkDir() 127 opts := image.DownloadOptions{ 128 TargetDir: dlDir, 129 } 130 fn, info, redirectChannel, err := s.tsto.DownloadSnap("core", opts) 131 c.Assert(err, check.IsNil) 132 c.Check(fn, check.Matches, filepath.Join(dlDir, `core_\d+.snap`)) 133 c.Check(info.SnapName(), check.Equals, "core") 134 c.Check(redirectChannel, check.Equals, "") 135 136 c.Check(logbuf.String(), check.Matches, `.* DEBUG: Going to download snap "core" `+opts.String()+".\n") 137 } 138 139 var validGadgetYaml = ` 140 volumes: 141 vol1: 142 bootloader: grub 143 structure: 144 - name: non-fs 145 type: bare 146 size: 512 147 offset: 0 148 content: 149 - image: non-fs.img 150 - name: ubuntu-seed 151 role: system-seed 152 type: 83,0FC63DAF-8483-4772-8E79-3D69D8477DE4 153 size: 100M 154 filesystem: ext4 155 content: 156 - source: system-seed.efi 157 target: EFI/boot/system-seed.efi 158 - name: structure-name 159 role: system-boot 160 type: 83,0FC63DAF-8483-4772-8E79-3D69D8477DE4 161 size: 100M 162 filesystem: ext4 163 content: 164 - source: grubx64.efi 165 target: EFI/boot/grubx64.efi 166 - name: ubuntu-data 167 role: system-data 168 type: 83,0FC63DAF-8483-4772-8E79-3D69D8477DE4 169 size: 100M 170 vol2: 171 structure: 172 - name: struct2 173 type: 83,0FC63DAF-8483-4772-8E79-3D69D8477DE4 174 size: 100M 175 filesystem: ext4 176 content: 177 - source: foo 178 target: subdir/foo 179 ` 180 181 func (s *imageSuite) TestWriteResolvedContent(c *check.C) { 182 prepareImageDir := c.MkDir() 183 184 s.testWriteResolvedContent(c, prepareImageDir) 185 } 186 187 func (s *imageSuite) TestWriteResolvedContentRelativePath(c *check.C) { 188 prepareImageDir := c.MkDir() 189 190 // chdir to prepareImage dir and run writeResolvedContent from 191 // this relative dir 192 cwd, err := os.Getwd() 193 c.Assert(err, check.IsNil) 194 err = os.Chdir(prepareImageDir) 195 c.Assert(err, check.IsNil) 196 defer func() { os.Chdir(cwd) }() 197 198 s.testWriteResolvedContent(c, ".") 199 } 200 201 // treeLines is used to sort the output from find 202 type treeLines []string 203 204 func (t treeLines) Len() int { 205 return len(t) 206 } 207 func (t treeLines) Less(i, j int) bool { 208 // strip off the first character of the two strings (assuming the strings 209 // are at least 1 character long) 210 s1 := t[i] 211 if len(s1) > 1 { 212 s1 = s1[1:] 213 } 214 s2 := t[j] 215 if len(s2) > 1 { 216 s2 = s2[1:] 217 } 218 return s1 < s2 219 } 220 func (t treeLines) Swap(i, j int) { 221 t[i], t[j] = t[j], t[i] 222 } 223 224 func (s *imageSuite) testWriteResolvedContent(c *check.C, prepareImageDir string) { 225 // on uc20 there is a "system-seed" under the <PrepareImageDir> 226 uc20systemSeed, err := filepath.Abs(filepath.Join(prepareImageDir, "system-seed")) 227 c.Assert(err, check.IsNil) 228 err = os.MkdirAll(uc20systemSeed, 0755) 229 c.Assert(err, check.IsNil) 230 231 // the resolved content is written here 232 gadgetRoot := c.MkDir() 233 snaptest.PopulateDir(gadgetRoot, [][]string{ 234 {"meta/snap.yaml", packageGadget}, 235 {"meta/gadget.yaml", validGadgetYaml}, 236 {"system-seed.efi", "content of system-seed.efi"}, 237 {"grubx64.efi", "content of grubx64.efi"}, 238 {"foo", "content of foo"}, 239 {"non-fs.img", "content of non-fs.img"}, 240 }) 241 kernelRoot := c.MkDir() 242 243 model := s.makeUC20Model(nil) 244 gadgetInfo, err := gadget.ReadInfoAndValidate(gadgetRoot, model, nil) 245 c.Assert(err, check.IsNil) 246 247 err = image.WriteResolvedContent(prepareImageDir, gadgetInfo, gadgetRoot, kernelRoot) 248 c.Assert(err, check.IsNil) 249 250 // XXX: add testutil.DirEquals([][]string) 251 cmd := exec.Command("find", ".", "-printf", "%y %P\n") 252 cmd.Dir = prepareImageDir 253 tree, err := cmd.CombinedOutput() 254 c.Assert(err, check.IsNil) 255 // sort the tree output 256 lines := strings.Split(string(tree), "\n") 257 sort.Sort(treeLines(lines)) 258 c.Check(strings.Join(lines, "\n"), check.Equals, ` 259 d 260 d resolved-content 261 d resolved-content/vol1 262 l resolved-content/vol1/part1 263 d resolved-content/vol1/part2 264 d resolved-content/vol1/part2/EFI 265 d resolved-content/vol1/part2/EFI/boot 266 f resolved-content/vol1/part2/EFI/boot/grubx64.efi 267 d resolved-content/vol2 268 d resolved-content/vol2/part0 269 d resolved-content/vol2/part0/subdir 270 f resolved-content/vol2/part0/subdir/foo 271 d system-seed 272 d system-seed/EFI 273 d system-seed/EFI/boot 274 f system-seed/EFI/boot/system-seed.efi`) 275 276 // check symlink target for "ubuntu-seed" -> <prepareImageDir>/system-seed 277 t, err := os.Readlink(filepath.Join(prepareImageDir, "resolved-content/vol1/part1")) 278 c.Assert(err, check.IsNil) 279 c.Check(t, check.Equals, uc20systemSeed) 280 }