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  }