github.com/meulengracht/snapd@v0.0.0-20210719210640-8bde69bcc84e/cmd/snap/cmd_auto_import_test.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2016 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 main_test
    21  
    22  import (
    23  	"fmt"
    24  	"io/ioutil"
    25  	"net/http"
    26  	"os"
    27  	"path/filepath"
    28  
    29  	. "gopkg.in/check.v1"
    30  
    31  	snap "github.com/snapcore/snapd/cmd/snap"
    32  	"github.com/snapcore/snapd/dirs"
    33  	"github.com/snapcore/snapd/logger"
    34  	"github.com/snapcore/snapd/osutil"
    35  	"github.com/snapcore/snapd/release"
    36  	"github.com/snapcore/snapd/snap/snaptest"
    37  	"github.com/snapcore/snapd/testutil"
    38  )
    39  
    40  func makeMockMountInfo(c *C, content string) string {
    41  	fn := filepath.Join(c.MkDir(), "mountinfo")
    42  	err := ioutil.WriteFile(fn, []byte(content), 0644)
    43  	c.Assert(err, IsNil)
    44  	return fn
    45  }
    46  
    47  func (s *SnapSuite) TestAutoImportAssertsHappy(c *C) {
    48  	restore := release.MockOnClassic(false)
    49  	defer restore()
    50  
    51  	fakeAssertData := []byte("my-assertion")
    52  
    53  	n := 0
    54  	total := 2
    55  	s.RedirectClientToTestServer(func(w http.ResponseWriter, r *http.Request) {
    56  		switch n {
    57  		case 0:
    58  			c.Check(r.Method, Equals, "POST")
    59  			c.Check(r.URL.Path, Equals, "/v2/assertions")
    60  			postData, err := ioutil.ReadAll(r.Body)
    61  			c.Assert(err, IsNil)
    62  			c.Check(postData, DeepEquals, fakeAssertData)
    63  			fmt.Fprintln(w, `{"type": "sync", "result": {"ready": true, "status": "Done"}}`)
    64  			n++
    65  		case 1:
    66  			c.Check(r.Method, Equals, "POST")
    67  			c.Check(r.URL.Path, Equals, "/v2/users")
    68  			postData, err := ioutil.ReadAll(r.Body)
    69  			c.Assert(err, IsNil)
    70  			c.Check(string(postData), Equals, `{"action":"create","automatic":true}`)
    71  
    72  			fmt.Fprintln(w, `{"type": "sync", "result": [{"username": "foo"}]}`)
    73  			n++
    74  		default:
    75  			c.Fatalf("unexpected request: %v (expected %d got %d)", r, total, n)
    76  		}
    77  
    78  	})
    79  
    80  	fakeAssertsFn := filepath.Join(c.MkDir(), "auto-import.assert")
    81  	err := ioutil.WriteFile(fakeAssertsFn, fakeAssertData, 0644)
    82  	c.Assert(err, IsNil)
    83  
    84  	mockMountInfoFmt := `
    85  24 0 8:18 / %s rw,relatime shared:1 - ext4 /dev/sdb2 rw,errors=remount-ro,data=ordered`
    86  	content := fmt.Sprintf(mockMountInfoFmt, filepath.Dir(fakeAssertsFn))
    87  	restore = snap.MockMountInfoPath(makeMockMountInfo(c, content))
    88  	defer restore()
    89  
    90  	logbuf, restore := logger.MockLogger()
    91  	defer restore()
    92  
    93  	rest, err := snap.Parser(snap.Client()).ParseArgs([]string{"auto-import"})
    94  	c.Assert(err, IsNil)
    95  	c.Assert(rest, DeepEquals, []string{})
    96  	c.Check(s.Stdout(), Equals, `created user "foo"`+"\n")
    97  	// matches because we may get a:
    98  	//   "WARNING: cannot create syslog logger\n"
    99  	// in the output
   100  	c.Check(logbuf.String(), Matches, fmt.Sprintf("(?ms).*imported %s\n", fakeAssertsFn))
   101  	c.Check(n, Equals, total)
   102  }
   103  
   104  func (s *SnapSuite) TestAutoImportAssertsNotImportedFromLoop(c *C) {
   105  	restore := release.MockOnClassic(false)
   106  	defer restore()
   107  
   108  	fakeAssertData := []byte("bad-assertion")
   109  
   110  	s.RedirectClientToTestServer(func(w http.ResponseWriter, r *http.Request) {
   111  		// assertion is ignored, nothing is posted to this endpoint
   112  		panic("not reached")
   113  	})
   114  
   115  	fakeAssertsFn := filepath.Join(c.MkDir(), "auto-import.assert")
   116  	err := ioutil.WriteFile(fakeAssertsFn, fakeAssertData, 0644)
   117  	c.Assert(err, IsNil)
   118  
   119  	mockMountInfoFmtWithLoop := `
   120  24 0 8:18 / %s rw,relatime shared:1 - squashfs /dev/loop1 rw,errors=remount-ro,data=ordered`
   121  	content := fmt.Sprintf(mockMountInfoFmtWithLoop, filepath.Dir(fakeAssertsFn))
   122  	restore = snap.MockMountInfoPath(makeMockMountInfo(c, content))
   123  	defer restore()
   124  
   125  	rest, err := snap.Parser(snap.Client()).ParseArgs([]string{"auto-import"})
   126  	c.Assert(err, IsNil)
   127  	c.Assert(rest, DeepEquals, []string{})
   128  	c.Check(s.Stdout(), Equals, "")
   129  	c.Check(s.Stderr(), Equals, "")
   130  }
   131  
   132  func (s *SnapSuite) TestAutoImportCandidatesHappy(c *C) {
   133  	dirs := make([]string, 4)
   134  	args := make([]interface{}, len(dirs))
   135  	files := make([]string, len(dirs))
   136  	for i := range dirs {
   137  		dirs[i] = c.MkDir()
   138  		args[i] = dirs[i]
   139  		files[i] = filepath.Join(dirs[i], "auto-import.assert")
   140  		err := ioutil.WriteFile(files[i], nil, 0644)
   141  		c.Assert(err, IsNil)
   142  	}
   143  
   144  	mockMountInfoFmtWithLoop := `
   145  too short
   146  24 0 8:18 / %[1]s rw,relatime foo ext3 /dev/meep2 no,separator
   147  24 0 8:18 / %[2]s rw,relatime - ext3 /dev/meep2 rw,errors=remount-ro,data=ordered
   148  24 0 8:18 / %[3]s rw,relatime opt:1 - ext4 /dev/meep3 rw,errors=remount-ro,data=ordered
   149  24 0 8:18 / %[4]s rw,relatime opt:1 opt:2 - ext2 /dev/meep1 rw,errors=remount-ro,data=ordered
   150  `
   151  
   152  	content := fmt.Sprintf(mockMountInfoFmtWithLoop, args...)
   153  	restore := snap.MockMountInfoPath(makeMockMountInfo(c, content))
   154  	defer restore()
   155  
   156  	l, err := snap.AutoImportCandidates()
   157  	c.Check(err, IsNil)
   158  	c.Check(l, DeepEquals, files[1:])
   159  }
   160  
   161  func (s *SnapSuite) TestAutoImportAssertsHappyNotOnClassic(c *C) {
   162  	restore := release.MockOnClassic(true)
   163  	defer restore()
   164  
   165  	fakeAssertData := []byte("my-assertion")
   166  	s.RedirectClientToTestServer(func(w http.ResponseWriter, r *http.Request) {
   167  		c.Errorf("auto-import on classic is disabled, but something tried to do a %q with %s", r.Method, r.URL.Path)
   168  	})
   169  
   170  	fakeAssertsFn := filepath.Join(c.MkDir(), "auto-import.assert")
   171  	err := ioutil.WriteFile(fakeAssertsFn, fakeAssertData, 0644)
   172  	c.Assert(err, IsNil)
   173  
   174  	mockMountInfoFmt := `
   175  24 0 8:18 / %s rw,relatime shared:1 - ext4 /dev/sdb2 rw,errors=remount-ro,data=ordered`
   176  	content := fmt.Sprintf(mockMountInfoFmt, filepath.Dir(fakeAssertsFn))
   177  	restore = snap.MockMountInfoPath(makeMockMountInfo(c, content))
   178  	defer restore()
   179  
   180  	rest, err := snap.Parser(snap.Client()).ParseArgs([]string{"auto-import"})
   181  	c.Assert(err, IsNil)
   182  	c.Assert(rest, DeepEquals, []string{})
   183  	c.Check(s.Stdout(), Equals, "")
   184  	c.Check(s.Stderr(), Equals, "auto-import is disabled on classic\n")
   185  }
   186  
   187  func (s *SnapSuite) TestAutoImportIntoSpool(c *C) {
   188  	restore := release.MockOnClassic(false)
   189  	defer restore()
   190  
   191  	logbuf, restore := logger.MockLogger()
   192  	defer restore()
   193  
   194  	fakeAssertData := []byte("good-assertion")
   195  
   196  	// ensure we can not connect
   197  	snap.ClientConfig.BaseURL = "can-not-connect-to-this-url"
   198  
   199  	fakeAssertsFn := filepath.Join(c.MkDir(), "auto-import.assert")
   200  	err := ioutil.WriteFile(fakeAssertsFn, fakeAssertData, 0644)
   201  	c.Assert(err, IsNil)
   202  
   203  	mockMountInfoFmt := `
   204  24 0 8:18 / %s rw,relatime shared:1 - squashfs /dev/sc1 rw,errors=remount-ro,data=ordered`
   205  	content := fmt.Sprintf(mockMountInfoFmt, filepath.Dir(fakeAssertsFn))
   206  	restore = snap.MockMountInfoPath(makeMockMountInfo(c, content))
   207  	defer restore()
   208  
   209  	rest, err := snap.Parser(snap.Client()).ParseArgs([]string{"auto-import"})
   210  	c.Assert(err, IsNil)
   211  	c.Assert(rest, DeepEquals, []string{})
   212  	c.Check(s.Stdout(), Equals, "")
   213  	// matches because we may get a:
   214  	//   "WARNING: cannot create syslog logger\n"
   215  	// in the output
   216  	c.Check(logbuf.String(), Matches, "(?ms).*queuing for later.*\n")
   217  
   218  	files, err := ioutil.ReadDir(dirs.SnapAssertsSpoolDir)
   219  	c.Assert(err, IsNil)
   220  	c.Check(files, HasLen, 1)
   221  	c.Check(files[0].Name(), Equals, "iOkaeet50rajLvL-0Qsf2ELrTdn3XIXRIBlDewcK02zwRi3_TJlUOTl9AaiDXmDn.assert")
   222  }
   223  
   224  func (s *SnapSuite) TestAutoImportFromSpoolHappy(c *C) {
   225  	restore := release.MockOnClassic(false)
   226  	defer restore()
   227  
   228  	fakeAssertData := []byte("my-assertion")
   229  
   230  	n := 0
   231  	total := 2
   232  	s.RedirectClientToTestServer(func(w http.ResponseWriter, r *http.Request) {
   233  		switch n {
   234  		case 0:
   235  			c.Check(r.Method, Equals, "POST")
   236  			c.Check(r.URL.Path, Equals, "/v2/assertions")
   237  			postData, err := ioutil.ReadAll(r.Body)
   238  			c.Assert(err, IsNil)
   239  			c.Check(postData, DeepEquals, fakeAssertData)
   240  			fmt.Fprintln(w, `{"type": "sync", "result": {"ready": true, "status": "Done"}}`)
   241  			n++
   242  		case 1:
   243  			c.Check(r.Method, Equals, "POST")
   244  			c.Check(r.URL.Path, Equals, "/v2/users")
   245  			postData, err := ioutil.ReadAll(r.Body)
   246  			c.Assert(err, IsNil)
   247  			c.Check(string(postData), Equals, `{"action":"create","automatic":true}`)
   248  
   249  			fmt.Fprintln(w, `{"type": "sync", "result": [{"username": "foo"}]}`)
   250  			n++
   251  		default:
   252  			c.Fatalf("unexpected request: %v (expected %d got %d)", r, total, n)
   253  		}
   254  
   255  	})
   256  
   257  	fakeAssertsFn := filepath.Join(dirs.SnapAssertsSpoolDir, "1234343")
   258  	err := os.MkdirAll(filepath.Dir(fakeAssertsFn), 0755)
   259  	c.Assert(err, IsNil)
   260  	err = ioutil.WriteFile(fakeAssertsFn, fakeAssertData, 0644)
   261  	c.Assert(err, IsNil)
   262  
   263  	logbuf, restore := logger.MockLogger()
   264  	defer restore()
   265  
   266  	rest, err := snap.Parser(snap.Client()).ParseArgs([]string{"auto-import"})
   267  	c.Assert(err, IsNil)
   268  	c.Assert(rest, DeepEquals, []string{})
   269  	c.Check(s.Stdout(), Equals, `created user "foo"`+"\n")
   270  	// matches because we may get a:
   271  	//   "WARNING: cannot create syslog logger\n"
   272  	// in the output
   273  	c.Check(logbuf.String(), Matches, fmt.Sprintf("(?ms).*imported %s\n", fakeAssertsFn))
   274  	c.Check(n, Equals, total)
   275  
   276  	c.Check(osutil.FileExists(fakeAssertsFn), Equals, false)
   277  }
   278  
   279  func (s *SnapSuite) TestAutoImportIntoSpoolUnhappyTooBig(c *C) {
   280  	restore := release.MockOnClassic(false)
   281  	defer restore()
   282  
   283  	_, restoreLogger := logger.MockLogger()
   284  	defer restoreLogger()
   285  
   286  	// fake data is bigger than the default assertion limit
   287  	fakeAssertData := make([]byte, 641*1024)
   288  
   289  	// ensure we can not connect
   290  	snap.ClientConfig.BaseURL = "can-not-connect-to-this-url"
   291  
   292  	fakeAssertsFn := filepath.Join(c.MkDir(), "auto-import.assert")
   293  	err := ioutil.WriteFile(fakeAssertsFn, fakeAssertData, 0644)
   294  	c.Assert(err, IsNil)
   295  
   296  	mockMountInfoFmt := `
   297  24 0 8:18 / %s rw,relatime shared:1 - squashfs /dev/sc1 rw,errors=remount-ro,data=ordered`
   298  	content := fmt.Sprintf(mockMountInfoFmt, filepath.Dir(fakeAssertsFn))
   299  	restore = snap.MockMountInfoPath(makeMockMountInfo(c, content))
   300  	defer restore()
   301  
   302  	_, err = snap.Parser(snap.Client()).ParseArgs([]string{"auto-import"})
   303  	c.Assert(err, ErrorMatches, "cannot queue .*, file size too big: 656384")
   304  }
   305  
   306  func (s *SnapSuite) TestAutoImportUnhappyInInstallMode(c *C) {
   307  	restore := release.MockOnClassic(false)
   308  	defer restore()
   309  
   310  	_, restoreLogger := logger.MockLogger()
   311  	defer restoreLogger()
   312  
   313  	mockProcCmdlinePath := filepath.Join(c.MkDir(), "cmdline")
   314  	err := ioutil.WriteFile(mockProcCmdlinePath, []byte("foo=bar snapd_recovery_mode=install snapd_recovery_system=20191118"), 0644)
   315  	c.Assert(err, IsNil)
   316  
   317  	restore = osutil.MockProcCmdline(mockProcCmdlinePath)
   318  	defer restore()
   319  
   320  	_, err = snap.Parser(snap.Client()).ParseArgs([]string{"auto-import"})
   321  	c.Assert(err, IsNil)
   322  	c.Check(s.Stdout(), Equals, "")
   323  	c.Check(s.Stderr(), Equals, "auto-import is disabled in install-mode\n")
   324  }
   325  
   326  var mountStatic = []string{"mount", "-t", "ext4,vfat", "-o", "ro", "--make-private"}
   327  
   328  func (s *SnapSuite) TestAutoImportFromRemovable(c *C) {
   329  	restore := release.MockOnClassic(false)
   330  	defer restore()
   331  
   332  	_, restoreLogger := logger.MockLogger()
   333  	defer restoreLogger()
   334  
   335  	rootdir := c.MkDir()
   336  	dirs.SetRootDir(rootdir)
   337  
   338  	var umounts []string
   339  	restore = snap.MockSyscallUmount(func(p string, _ int) error {
   340  		umounts = append(umounts, p)
   341  		return nil
   342  	})
   343  	defer restore()
   344  
   345  	var tmpdirIdx int
   346  	restore = snap.MockIoutilTempDir(func(where string, p string) (string, error) {
   347  		c.Check(where, Equals, "")
   348  		tmpdirIdx++
   349  		return filepath.Join(rootdir, fmt.Sprintf("/tmp/%s%d", p, tmpdirIdx)), nil
   350  	})
   351  	defer restore()
   352  
   353  	mountCmd := testutil.MockCommand(c, "mount", "")
   354  	defer mountCmd.Restore()
   355  
   356  	snaptest.PopulateDir(rootdir, [][]string{
   357  		// removable without partitions
   358  		{"sys/block/sdremovable/removable", "1\n"},
   359  		// fixed disk
   360  		{"sys/block/sdfixed/removable", "0\n"},
   361  		// removable with partitions
   362  		{"sys/block/sdpart/removable", "1\n"},
   363  		{"sys/block/sdpart/sdpart1/partition", "1\n"},
   364  		{"sys/block/sdpart/sdpart2/partition", "0\n"},
   365  		{"sys/block/sdpart/sdpart3/partition", "1\n"},
   366  		// removable but subdevices are not partitions?
   367  		{"sys/block/sdother/removable", "1\n"},
   368  		{"sys/block/sdother/sdother1/partition", "0\n"},
   369  	})
   370  
   371  	// do not mock mountinfo contents, we just want to observe whether we
   372  	// try to mount and umount the right stuff
   373  
   374  	_, err := snap.Parser(snap.Client()).ParseArgs([]string{"auto-import"})
   375  	c.Assert(err, IsNil)
   376  	c.Check(s.Stdout(), Equals, "")
   377  	c.Check(s.Stderr(), Equals, "")
   378  	c.Check(mountCmd.Calls(), DeepEquals, [][]string{
   379  		append(mountStatic, "/dev/sdpart1", filepath.Join(rootdir, "/tmp/snapd-auto-import-mount-1")),
   380  		append(mountStatic, "/dev/sdpart3", filepath.Join(rootdir, "/tmp/snapd-auto-import-mount-2")),
   381  		append(mountStatic, "/dev/sdremovable", filepath.Join(rootdir, "/tmp/snapd-auto-import-mount-3")),
   382  	})
   383  	c.Check(umounts, DeepEquals, []string{
   384  		filepath.Join(rootdir, "/tmp/snapd-auto-import-mount-3"),
   385  		filepath.Join(rootdir, "/tmp/snapd-auto-import-mount-2"),
   386  		filepath.Join(rootdir, "/tmp/snapd-auto-import-mount-1"),
   387  	})
   388  }
   389  
   390  func (s *SnapSuite) TestAutoImportNoRemovable(c *C) {
   391  	restore := release.MockOnClassic(false)
   392  	defer restore()
   393  
   394  	rootdir := c.MkDir()
   395  	dirs.SetRootDir(rootdir)
   396  
   397  	var umounts []string
   398  	restore = snap.MockSyscallUmount(func(p string, _ int) error {
   399  		return fmt.Errorf("unexpected call")
   400  	})
   401  	defer restore()
   402  
   403  	mountCmd := testutil.MockCommand(c, "mount", "exit 1")
   404  	defer mountCmd.Restore()
   405  
   406  	snaptest.PopulateDir(rootdir, [][]string{
   407  		// fixed disk
   408  		{"sys/block/sdfixed/removable", "0\n"},
   409  		// removable but subdevices are not partitions?
   410  		{"sys/block/sdother/removable", "1\n"},
   411  		{"sys/block/sdother/sdother1/partition", "0\n"},
   412  	})
   413  
   414  	_, err := snap.Parser(snap.Client()).ParseArgs([]string{"auto-import"})
   415  	c.Assert(err, IsNil)
   416  	c.Check(s.Stdout(), Equals, "")
   417  	c.Check(s.Stderr(), Equals, "")
   418  	c.Check(mountCmd.Calls(), HasLen, 0)
   419  	c.Check(umounts, HasLen, 0)
   420  }
   421  
   422  func (s *SnapSuite) TestAutoImportFromMount(c *C) {
   423  	restore := release.MockOnClassic(false)
   424  	defer restore()
   425  
   426  	_, restoreLogger := logger.MockLogger()
   427  	defer restoreLogger()
   428  
   429  	mountCmd := testutil.MockCommand(c, "mount", "")
   430  
   431  	rootdir := c.MkDir()
   432  	dirs.SetRootDir(rootdir)
   433  
   434  	var umounts []string
   435  	restore = snap.MockSyscallUmount(func(p string, _ int) error {
   436  		c.Assert(umounts, HasLen, 0)
   437  		umounts = append(umounts, p)
   438  		return nil
   439  	})
   440  	defer restore()
   441  
   442  	var tmpdircalls int
   443  	restore = snap.MockIoutilTempDir(func(where string, p string) (string, error) {
   444  		c.Check(where, Equals, "")
   445  		c.Assert(tmpdircalls, Equals, 0)
   446  		tmpdircalls++
   447  		return filepath.Join(rootdir, fmt.Sprintf("/tmp/%s1", p)), nil
   448  	})
   449  	defer restore()
   450  
   451  	// do not mock mountinfo contents, we just want to observe whether we
   452  	// try to mount and umount the right stuff
   453  
   454  	_, err := snap.Parser(snap.Client()).ParseArgs([]string{"auto-import", "--mount", "/dev/foobar"})
   455  	c.Assert(err, IsNil)
   456  	c.Check(s.Stdout(), Equals, "")
   457  	c.Check(s.Stderr(), Equals, "")
   458  	c.Check(mountCmd.Calls(), DeepEquals, [][]string{
   459  		append(mountStatic, "/dev/foobar", filepath.Join(rootdir, "/tmp/snapd-auto-import-mount-1")),
   460  	})
   461  	c.Check(umounts, DeepEquals, []string{
   462  		filepath.Join(rootdir, "/tmp/snapd-auto-import-mount-1"),
   463  	})
   464  }
   465  
   466  func (s *SnapSuite) TestAutoImportUC20CandidatesIgnoresSystemPartitions(c *C) {
   467  
   468  	mountDirs := []string{
   469  		"/writable/system-data/var/lib/snapd/seed",
   470  		"/var/lib/snapd/seed",
   471  		"/run/mnt/ubuntu-boot",
   472  		"/run/mnt/ubuntu-seed",
   473  		"/run/mnt/ubuntu-data",
   474  		"/mnt/real-device",
   475  	}
   476  
   477  	rootDir := c.MkDir()
   478  	dirs.SetRootDir(rootDir)
   479  	defer func() { dirs.SetRootDir("") }()
   480  
   481  	args := make([]interface{}, 0, len(mountDirs)+1)
   482  	args = append(args, dirs.GlobalRootDir)
   483  	// pretend there are auto-import.asserts on all of them
   484  	for _, dir := range mountDirs {
   485  		args = append(args, dir)
   486  		file := filepath.Join(rootDir, dir, "auto-import.assert")
   487  		c.Assert(os.MkdirAll(filepath.Dir(file), 0755), IsNil)
   488  		c.Assert(ioutil.WriteFile(file, nil, 0644), IsNil)
   489  	}
   490  
   491  	mockMountInfoFmtWithLoop := `
   492  24 0 8:18 / %[1]s%[2]s rw,relatime foo ext3 /dev/meep2 no,separator
   493  24 0 8:18 / %[1]s%[3]s rw,relatime - ext3 /dev/meep2 rw,errors=remount-ro,data=ordered
   494  24 0 8:18 / %[1]s%[4]s rw,relatime opt:1 - ext4 /dev/meep3 rw,errors=remount-ro,data=ordered
   495  24 0 8:18 / %[1]s%[5]s rw,relatime opt:1 opt:2 - ext2 /dev/meep4 rw,errors=remount-ro,data=ordered
   496  24 0 8:18 / %[1]s%[6]s rw,relatime opt:1 opt:2 - ext2 /dev/meep5 rw,errors=remount-ro,data=ordered
   497  24 0 8:18 / %[1]s%[7]s rw,relatime opt:1 opt:2 - ext2 /dev/meep78 rw,errors=remount-ro,data=ordered
   498  `
   499  
   500  	content := fmt.Sprintf(mockMountInfoFmtWithLoop, args...)
   501  	restore := snap.MockMountInfoPath(makeMockMountInfo(c, content))
   502  	defer restore()
   503  
   504  	l, err := snap.AutoImportCandidates()
   505  	c.Check(err, IsNil)
   506  
   507  	// only device should be the /mnt/real-device one
   508  	c.Check(l, DeepEquals, []string{filepath.Join(rootDir, "/mnt/real-device", "auto-import.assert")})
   509  }
   510  
   511  func (s *SnapSuite) TestAutoImportAssertsManagedEmptyReply(c *C) {
   512  	restore := release.MockOnClassic(false)
   513  	defer restore()
   514  
   515  	_, restore = logger.MockLogger()
   516  	defer restore()
   517  
   518  	fakeAssertData := []byte("my-assertion")
   519  
   520  	n := 0
   521  	total := 2
   522  	s.RedirectClientToTestServer(func(w http.ResponseWriter, r *http.Request) {
   523  		switch n {
   524  		case 0:
   525  			c.Check(r.Method, Equals, "POST")
   526  			c.Check(r.URL.Path, Equals, "/v2/assertions")
   527  			postData, err := ioutil.ReadAll(r.Body)
   528  			c.Assert(err, IsNil)
   529  			c.Check(postData, DeepEquals, fakeAssertData)
   530  			fmt.Fprintln(w, `{"type": "sync", "result": {"ready": true, "status": "Done"}}`)
   531  			n++
   532  		case 1:
   533  			c.Check(r.Method, Equals, "POST")
   534  			c.Check(r.URL.Path, Equals, "/v2/users")
   535  			postData, err := ioutil.ReadAll(r.Body)
   536  			c.Assert(err, IsNil)
   537  			c.Check(string(postData), Equals, `{"action":"create","automatic":true}`)
   538  
   539  			fmt.Fprintln(w, `{"type": "sync", "result": []}`)
   540  			n++
   541  		default:
   542  			c.Fatalf("unexpected request: %v (expected %d got %d)", r, total, n)
   543  		}
   544  
   545  	})
   546  
   547  	fakeAssertsFn := filepath.Join(c.MkDir(), "auto-import.assert")
   548  	err := ioutil.WriteFile(fakeAssertsFn, fakeAssertData, 0644)
   549  	c.Assert(err, IsNil)
   550  
   551  	mockMountInfoFmt := `
   552  24 0 8:18 / %s rw,relatime shared:1 - ext4 /dev/sdb2 rw,errors=remount-ro,data=ordered`
   553  	content := fmt.Sprintf(mockMountInfoFmt, filepath.Dir(fakeAssertsFn))
   554  	restore = snap.MockMountInfoPath(makeMockMountInfo(c, content))
   555  	defer restore()
   556  
   557  	rest, err := snap.Parser(snap.Client()).ParseArgs([]string{"auto-import"})
   558  	c.Assert(err, IsNil)
   559  	c.Assert(rest, DeepEquals, []string{})
   560  	c.Check(s.Stdout(), Equals, ``)
   561  	c.Check(n, Equals, total)
   562  }