github.com/david-imola/snapd@v0.0.0-20210611180407-2de8ddeece6d/overlord/snapstate/handlers_download_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 snapstate_test
    21  
    22  import (
    23  	"path/filepath"
    24  
    25  	. "gopkg.in/check.v1"
    26  
    27  	"github.com/snapcore/snapd/dirs"
    28  	"github.com/snapcore/snapd/overlord/configstate/config"
    29  	"github.com/snapcore/snapd/overlord/snapstate"
    30  	"github.com/snapcore/snapd/overlord/snapstate/snapstatetest"
    31  	"github.com/snapcore/snapd/overlord/state"
    32  	"github.com/snapcore/snapd/snap"
    33  	"github.com/snapcore/snapd/store"
    34  )
    35  
    36  type downloadSnapSuite struct {
    37  	baseHandlerSuite
    38  
    39  	fakeStore *fakeStore
    40  }
    41  
    42  var _ = Suite(&downloadSnapSuite{})
    43  
    44  func (s *downloadSnapSuite) SetUpTest(c *C) {
    45  	s.setup(c, nil)
    46  
    47  	s.fakeStore = &fakeStore{
    48  		state:       s.state,
    49  		fakeBackend: s.fakeBackend,
    50  	}
    51  	s.state.Lock()
    52  	defer s.state.Unlock()
    53  	snapstate.ReplaceStore(s.state, s.fakeStore)
    54  	s.state.Set("refresh-privacy-key", "privacy-key")
    55  
    56  	s.AddCleanup(snapstatetest.UseFallbackDeviceModel())
    57  }
    58  
    59  func (s *downloadSnapSuite) TestDoDownloadSnapCompatibility(c *C) {
    60  	s.state.Lock()
    61  	t := s.state.NewTask("download-snap", "test")
    62  	t.Set("snap-setup", &snapstate.SnapSetup{
    63  		SideInfo: &snap.SideInfo{
    64  			RealName: "foo",
    65  		},
    66  		Channel: "some-channel",
    67  		// explicitly set to "nil", this ensures the compatibility
    68  		// code path in the task is hit and the store is queried
    69  		// in the task (instead of using the new
    70  		// SnapSetup.{SideInfo,DownloadInfo} that gets set in
    71  		// snapstate.{Install,Update} directly.
    72  		DownloadInfo: nil,
    73  	})
    74  	s.state.NewChange("dummy", "...").AddTask(t)
    75  
    76  	s.state.Unlock()
    77  
    78  	s.se.Ensure()
    79  	s.se.Wait()
    80  
    81  	// the compat code called the store "Snap" endpoint
    82  	c.Assert(s.fakeBackend.ops, DeepEquals, fakeOps{
    83  		{
    84  			op: "storesvc-snap-action",
    85  		},
    86  		{
    87  			op: "storesvc-snap-action:action",
    88  			action: store.SnapAction{
    89  				Action:       "install",
    90  				InstanceName: "foo",
    91  				Channel:      "some-channel",
    92  			},
    93  			revno: snap.R(11),
    94  		},
    95  		{
    96  			op:   "storesvc-download",
    97  			name: "foo",
    98  		},
    99  	})
   100  
   101  	s.state.Lock()
   102  	defer s.state.Unlock()
   103  
   104  	var snapsup snapstate.SnapSetup
   105  	t.Get("snap-setup", &snapsup)
   106  	c.Check(snapsup.SideInfo, DeepEquals, &snap.SideInfo{
   107  		RealName: "foo",
   108  		SnapID:   "foo-id",
   109  		Revision: snap.R(11),
   110  		Channel:  "some-channel",
   111  	})
   112  	c.Check(t.Status(), Equals, state.DoneStatus)
   113  }
   114  
   115  func (s *downloadSnapSuite) TestDoDownloadSnapNormal(c *C) {
   116  	s.state.Lock()
   117  
   118  	si := &snap.SideInfo{
   119  		RealName: "foo",
   120  		SnapID:   "mySnapID",
   121  		Revision: snap.R(11),
   122  		Channel:  "my-channel",
   123  	}
   124  
   125  	// download, ensure the store does not query
   126  	t := s.state.NewTask("download-snap", "test")
   127  	t.Set("snap-setup", &snapstate.SnapSetup{
   128  		Channel:  "some-channel",
   129  		SideInfo: si,
   130  		DownloadInfo: &snap.DownloadInfo{
   131  			DownloadURL: "http://some-url.com/snap",
   132  		},
   133  	})
   134  	chg := s.state.NewChange("dummy", "...")
   135  	chg.AddTask(t)
   136  
   137  	s.state.Unlock()
   138  
   139  	s.se.Ensure()
   140  	s.se.Wait()
   141  
   142  	s.state.Lock()
   143  	defer s.state.Unlock()
   144  
   145  	c.Assert(chg.Err(), IsNil)
   146  
   147  	// only the download endpoint of the store was hit
   148  	c.Assert(s.fakeBackend.ops, DeepEquals, fakeOps{
   149  		{
   150  			op:   "storesvc-download",
   151  			name: "foo",
   152  		},
   153  	})
   154  
   155  	var snapsup snapstate.SnapSetup
   156  	t.Get("snap-setup", &snapsup)
   157  	c.Check(snapsup.SideInfo, DeepEquals, si)
   158  	c.Check(t.Status(), Equals, state.DoneStatus)
   159  
   160  	// check no IsAutoRefresh was passed in
   161  	c.Assert(s.fakeStore.downloads, DeepEquals, []fakeDownload{
   162  		{
   163  			name:   "foo",
   164  			target: filepath.Join(dirs.SnapBlobDir, "foo_11.snap"),
   165  			opts:   nil,
   166  		},
   167  	})
   168  }
   169  
   170  func (s *downloadSnapSuite) TestDoDownloadSnapWithDeviceContext(c *C) {
   171  	s.state.Lock()
   172  
   173  	// unset the global store, it will need to come via the device context
   174  	// CtxStore
   175  	snapstate.ReplaceStore(s.state, nil)
   176  
   177  	r := snapstatetest.MockDeviceContext(&snapstatetest.TrivialDeviceContext{
   178  		CtxStore: s.fakeStore,
   179  	})
   180  	defer r()
   181  
   182  	si := &snap.SideInfo{
   183  		RealName: "foo",
   184  		SnapID:   "mySnapID",
   185  		Revision: snap.R(11),
   186  		Channel:  "my-channel",
   187  	}
   188  
   189  	// download, ensure the store does not query
   190  	t := s.state.NewTask("download-snap", "test")
   191  	t.Set("snap-setup", &snapstate.SnapSetup{
   192  		Channel:  "some-channel",
   193  		SideInfo: si,
   194  		DownloadInfo: &snap.DownloadInfo{
   195  			DownloadURL: "http://some-url.com/snap",
   196  		},
   197  	})
   198  	s.state.NewChange("dummy", "...").AddTask(t)
   199  
   200  	s.state.Unlock()
   201  
   202  	s.se.Ensure()
   203  	s.se.Wait()
   204  
   205  	// only the download endpoint of the store was hit
   206  	c.Assert(s.fakeBackend.ops, DeepEquals, fakeOps{
   207  		{
   208  			op:   "storesvc-download",
   209  			name: "foo",
   210  		},
   211  	})
   212  }
   213  
   214  func (s *downloadSnapSuite) TestDoUndoDownloadSnap(c *C) {
   215  	s.state.Lock()
   216  	si := &snap.SideInfo{
   217  		RealName: "foo",
   218  		Revision: snap.R(33),
   219  	}
   220  	t := s.state.NewTask("download-snap", "test")
   221  	t.Set("snap-setup", &snapstate.SnapSetup{
   222  		SideInfo: si,
   223  		DownloadInfo: &snap.DownloadInfo{
   224  			DownloadURL: "http://something.com/snap",
   225  		},
   226  	})
   227  	chg := s.state.NewChange("dummy", "...")
   228  	chg.AddTask(t)
   229  
   230  	terr := s.state.NewTask("error-trigger", "provoking total undo")
   231  	terr.WaitFor(t)
   232  	chg.AddTask(terr)
   233  
   234  	s.state.Unlock()
   235  
   236  	for i := 0; i < 3; i++ {
   237  		s.se.Ensure()
   238  		s.se.Wait()
   239  	}
   240  
   241  	s.state.Lock()
   242  	defer s.state.Unlock()
   243  
   244  	// task was undone
   245  	c.Check(t.Status(), Equals, state.UndoneStatus)
   246  
   247  	// and nothing is in the state for "foo"
   248  	var snapst snapstate.SnapState
   249  	err := snapstate.Get(s.state, "foo", &snapst)
   250  	c.Assert(err, Equals, state.ErrNoState)
   251  
   252  }
   253  
   254  func (s *downloadSnapSuite) TestDoDownloadRateLimitedIntegration(c *C) {
   255  	s.state.Lock()
   256  
   257  	// set auto-refresh rate-limit
   258  	tr := config.NewTransaction(s.state)
   259  	tr.Set("core", "refresh.rate-limit", "1234B")
   260  	tr.Commit()
   261  
   262  	// setup fake auto-refresh download
   263  	si := &snap.SideInfo{
   264  		RealName: "foo",
   265  		SnapID:   "foo-id",
   266  		Revision: snap.R(11),
   267  	}
   268  	t := s.state.NewTask("download-snap", "test")
   269  	t.Set("snap-setup", &snapstate.SnapSetup{
   270  		SideInfo: si,
   271  		DownloadInfo: &snap.DownloadInfo{
   272  			DownloadURL: "http://some-url.com/snap",
   273  		},
   274  		Flags: snapstate.Flags{
   275  			IsAutoRefresh: true,
   276  		},
   277  	})
   278  	s.state.NewChange("dummy", "...").AddTask(t)
   279  
   280  	s.state.Unlock()
   281  
   282  	s.se.Ensure()
   283  	s.se.Wait()
   284  
   285  	// ensure that rate limit was honored
   286  	c.Assert(s.fakeStore.downloads, DeepEquals, []fakeDownload{
   287  		{
   288  			name:   "foo",
   289  			target: filepath.Join(dirs.SnapBlobDir, "foo_11.snap"),
   290  			opts: &store.DownloadOptions{
   291  				RateLimit:     1234,
   292  				IsAutoRefresh: true,
   293  			},
   294  		},
   295  	})
   296  
   297  }