gopkg.in/ubuntu-core/snappy.v0@v0.0.0-20210902073436-25a8614f10a6/overlord/snapstate/handlers_test.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2019 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  	"fmt"
    24  
    25  	. "gopkg.in/check.v1"
    26  
    27  	"github.com/snapcore/snapd/overlord/snapstate"
    28  	"github.com/snapcore/snapd/overlord/snapstate/snapstatetest"
    29  	"github.com/snapcore/snapd/overlord/state"
    30  	"github.com/snapcore/snapd/snap"
    31  	"github.com/snapcore/snapd/testutil"
    32  )
    33  
    34  type handlersSuite struct {
    35  	baseHandlerSuite
    36  
    37  	stateBackend *witnessRestartReqStateBackend
    38  }
    39  
    40  var _ = Suite(&handlersSuite{})
    41  
    42  func (s *handlersSuite) SetUpTest(c *C) {
    43  	s.setup(c, s.stateBackend)
    44  
    45  	s.AddCleanup(snapstatetest.MockDeviceModel(DefaultModel()))
    46  }
    47  
    48  func (s *handlersSuite) TestSetTaskSnapSetupFirstTask(c *C) {
    49  	s.state.Lock()
    50  	defer s.state.Unlock()
    51  
    52  	// make a new task which will be the snap-setup-task for other tasks and
    53  	// write a SnapSetup to it
    54  	t := s.state.NewTask("link-snap", "test")
    55  	snapsup := &snapstate.SnapSetup{
    56  		SideInfo: &snap.SideInfo{
    57  			RealName: "foo",
    58  			Revision: snap.R(33),
    59  			SnapID:   "foo-id",
    60  		},
    61  		Channel: "beta",
    62  		UserID:  2,
    63  	}
    64  	t.Set("snap-setup", snapsup)
    65  	s.state.NewChange("dummy", "...").AddTask(t)
    66  
    67  	// mutate it and rewrite it with the helper
    68  	snapsup.Channel = "edge"
    69  	err := snapstate.SetTaskSnapSetup(t, snapsup)
    70  	c.Assert(err, IsNil)
    71  
    72  	// get a fresh version of this task from state to check that the task's
    73  	/// snap-setup has the changes in it now
    74  	newT := s.state.Task(t.ID())
    75  	c.Assert(newT, Not(IsNil))
    76  	var newsnapsup snapstate.SnapSetup
    77  	err = newT.Get("snap-setup", &newsnapsup)
    78  	c.Assert(err, IsNil)
    79  	c.Assert(newsnapsup.Channel, Equals, snapsup.Channel)
    80  }
    81  
    82  func (s *handlersSuite) TestSetTaskSnapSetupLaterTask(c *C) {
    83  	s.state.Lock()
    84  	defer s.state.Unlock()
    85  	t := s.state.NewTask("link-snap", "test")
    86  
    87  	snapsup := &snapstate.SnapSetup{
    88  		SideInfo: &snap.SideInfo{
    89  			RealName: "foo",
    90  			Revision: snap.R(33),
    91  			SnapID:   "foo-id",
    92  		},
    93  		Channel: "beta",
    94  		UserID:  2,
    95  	}
    96  	// setup snap-setup for the first task
    97  	t.Set("snap-setup", snapsup)
    98  
    99  	// make a new task and reference the first one in snap-setup-task
   100  	t2 := s.state.NewTask("next-task-snap", "test2")
   101  	t2.Set("snap-setup-task", t.ID())
   102  
   103  	chg := s.state.NewChange("dummy", "...")
   104  	chg.AddTask(t)
   105  	chg.AddTask(t2)
   106  
   107  	// mutate it and rewrite it with the helper
   108  	snapsup.Channel = "edge"
   109  	err := snapstate.SetTaskSnapSetup(t2, snapsup)
   110  	c.Assert(err, IsNil)
   111  
   112  	// check that the original task's snap-setup is different now
   113  	newT := s.state.Task(t.ID())
   114  	c.Assert(newT, Not(IsNil))
   115  	var newsnapsup snapstate.SnapSetup
   116  	err = newT.Get("snap-setup", &newsnapsup)
   117  	c.Assert(err, IsNil)
   118  	c.Assert(newsnapsup.Channel, Equals, snapsup.Channel)
   119  }
   120  
   121  func (s *handlersSuite) TestComputeMissingDisabledServices(c *C) {
   122  	for _, tt := range []struct {
   123  		// inputs
   124  		stDisabledSvcsList []string
   125  		apps               map[string]*snap.AppInfo
   126  		// outputs
   127  		missing []string
   128  		found   []string
   129  		err     error
   130  		comment string
   131  	}{
   132  		// no apps
   133  		{
   134  			[]string{},
   135  			nil,
   136  			[]string{},
   137  			[]string{},
   138  			nil,
   139  			"no apps",
   140  		},
   141  		// only apps, no services
   142  		{
   143  			[]string{},
   144  			map[string]*snap.AppInfo{
   145  				"app": {
   146  					Daemon: "",
   147  				},
   148  			},
   149  			[]string{},
   150  			[]string{},
   151  			nil,
   152  			"no services",
   153  		},
   154  		// services in snap, but not disabled
   155  		{
   156  			[]string{},
   157  			map[string]*snap.AppInfo{
   158  				"svc1": {
   159  					Daemon: "simple",
   160  				},
   161  			},
   162  			[]string{},
   163  			[]string{},
   164  			nil,
   165  			"no disabled services",
   166  		},
   167  		// all disabled services, but not present in snap
   168  		{
   169  			[]string{"svc1"},
   170  			nil,
   171  			[]string{"svc1"},
   172  			[]string{},
   173  			nil,
   174  			"all missing disabled services",
   175  		},
   176  		// all disabled services, and found in snap
   177  		{
   178  			[]string{"svc1"},
   179  			map[string]*snap.AppInfo{
   180  				"svc1": {
   181  					Daemon: "simple",
   182  				},
   183  			},
   184  			[]string{},
   185  			[]string{"svc1"},
   186  			nil,
   187  			"all found disabled services",
   188  		},
   189  		// some disabled services, some missing, some present in snap
   190  		{
   191  			[]string{"svc1", "svc2"},
   192  			map[string]*snap.AppInfo{
   193  				"svc1": {
   194  					Daemon: "simple",
   195  				},
   196  			},
   197  			[]string{"svc2"},
   198  			[]string{"svc1"},
   199  			nil,
   200  			"some missing, some found disabled services",
   201  		},
   202  		// some disabled services, but app is not service
   203  		{
   204  			[]string{"svc1"},
   205  			map[string]*snap.AppInfo{
   206  				"svc1": {
   207  					Daemon: "",
   208  				},
   209  			},
   210  			[]string{"svc1"},
   211  			[]string{},
   212  			nil,
   213  			"some disabled services that are now apps",
   214  		},
   215  	} {
   216  		info := &snap.Info{Apps: tt.apps}
   217  
   218  		found, missing, err := snapstate.MissingDisabledServices(tt.stDisabledSvcsList, info)
   219  		c.Assert(missing, DeepEquals, tt.missing, Commentf(tt.comment))
   220  		c.Assert(found, DeepEquals, tt.found, Commentf(tt.comment))
   221  		c.Assert(err, Equals, tt.err, Commentf(tt.comment))
   222  	}
   223  }
   224  
   225  type testLinkParticipant struct {
   226  	callCount      int
   227  	instanceNames  []string
   228  	linkageChanged func(st *state.State, instanceName string) error
   229  }
   230  
   231  func (lp *testLinkParticipant) SnapLinkageChanged(st *state.State, instanceName string) error {
   232  	lp.callCount++
   233  	lp.instanceNames = append(lp.instanceNames, instanceName)
   234  	if lp.linkageChanged != nil {
   235  		return lp.linkageChanged(st, instanceName)
   236  	}
   237  	return nil
   238  }
   239  
   240  func (s *handlersSuite) TestAddLinkParticipant(c *C) {
   241  	s.state.Lock()
   242  	defer s.state.Unlock()
   243  
   244  	// Mock link snap participants. This ensures we can add a participant
   245  	// without affecting the other tests, as the original list will be
   246  	// restored.
   247  	restore := snapstate.MockLinkSnapParticipants(nil)
   248  	defer restore()
   249  
   250  	lp := &testLinkParticipant{
   251  		linkageChanged: func(st *state.State, instanceName string) error {
   252  			c.Assert(st, NotNil)
   253  			c.Check(instanceName, Equals, "snap-name")
   254  			return nil
   255  		},
   256  	}
   257  	snapstate.AddLinkSnapParticipant(lp)
   258  
   259  	t := s.state.NewTask("link-snap", "test")
   260  	snapstate.NotifyLinkParticipants(t, "snap-name")
   261  	c.Assert(lp.callCount, Equals, 1)
   262  }
   263  
   264  func (s *handlersSuite) TestNotifyLinkParticipantsErrorHandling(c *C) {
   265  	s.state.Lock()
   266  	defer s.state.Unlock()
   267  
   268  	// See comment in TestAddLinkParticipant for details.
   269  	restore := snapstate.MockLinkSnapParticipants(nil)
   270  	defer restore()
   271  
   272  	lp := &testLinkParticipant{
   273  		linkageChanged: func(st *state.State, instanceName string) error {
   274  			return fmt.Errorf("something failed")
   275  		},
   276  	}
   277  	snapstate.AddLinkSnapParticipant(lp)
   278  
   279  	t := s.state.NewTask("link-snap", "test")
   280  	snapstate.NotifyLinkParticipants(t, "snap-name")
   281  	c.Assert(lp.callCount, Equals, 1)
   282  	logs := t.Log()
   283  	c.Assert(logs, HasLen, 1)
   284  	c.Check(logs[0], testutil.Contains, "ERROR something failed")
   285  }