github.com/hugh712/snapd@v0.0.0-20200910133618-1a99902bd583/overlord/configstate/configcore/vitality_test.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 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 configcore_test
    21  
    22  import (
    23  	"path/filepath"
    24  	"strconv"
    25  	"strings"
    26  
    27  	. "gopkg.in/check.v1"
    28  
    29  	"github.com/snapcore/snapd/dirs"
    30  	"github.com/snapcore/snapd/overlord/configstate/configcore"
    31  	"github.com/snapcore/snapd/overlord/snapstate"
    32  	"github.com/snapcore/snapd/snap"
    33  	"github.com/snapcore/snapd/snap/snaptest"
    34  	"github.com/snapcore/snapd/testutil"
    35  )
    36  
    37  type vitalitySuite struct {
    38  	configcoreSuite
    39  }
    40  
    41  var _ = Suite(&vitalitySuite{})
    42  
    43  func (s *vitalitySuite) TestConfigureVitalityUnhappyName(c *C) {
    44  	err := configcore.Run(&mockConf{
    45  		state: s.state,
    46  		changes: map[string]interface{}{
    47  			"resilience.vitality-hint": "-invalid-snap-name!yf",
    48  		},
    49  	})
    50  	c.Assert(err, ErrorMatches, `cannot set "resilience.vitality-hint": invalid snap name: ".*"`)
    51  }
    52  
    53  func (s *vitalitySuite) TestConfigureVitalityNoSnapd(c *C) {
    54  	err := configcore.Run(&mockConf{
    55  		state: s.state,
    56  		changes: map[string]interface{}{
    57  			"resilience.vitality-hint": "snapd",
    58  		},
    59  	})
    60  	c.Assert(err, ErrorMatches, `cannot set "resilience.vitality-hint": snapd snap vitality cannot be changed`)
    61  }
    62  
    63  func (s *vitalitySuite) TestConfigureVitalityhappyName(c *C) {
    64  	err := configcore.Run(&mockConf{
    65  		state: s.state,
    66  		changes: map[string]interface{}{
    67  			"resilience.vitality-hint": "valid-snapname",
    68  		},
    69  	})
    70  	c.Assert(err, IsNil)
    71  	// no snap named "valid-snapname" is installed, so no systemd action
    72  	c.Check(s.systemctlArgs, HasLen, 0)
    73  }
    74  
    75  var mockSnapWithService = `name: test-snap
    76  version: 1.0
    77  apps:
    78   foo:
    79    daemon: simple
    80  `
    81  
    82  func (s *vitalitySuite) TestConfigureVitalityWithValidSnap(c *C) {
    83  	si := &snap.SideInfo{RealName: "test-snap", Revision: snap.R(1)}
    84  	snaptest.MockSnap(c, mockSnapWithService, si)
    85  	s.state.Lock()
    86  	snapstate.Set(s.state, "test-snap", &snapstate.SnapState{
    87  		Sequence: []*snap.SideInfo{si},
    88  		Current:  snap.R(1),
    89  		Active:   true,
    90  		SnapType: "app",
    91  	})
    92  	s.state.Unlock()
    93  
    94  	err := configcore.Run(&mockConf{
    95  		state: s.state,
    96  		changes: map[string]interface{}{
    97  			"resilience.vitality-hint": "unrelated,test-snap",
    98  		},
    99  	})
   100  	c.Assert(err, IsNil)
   101  	rootdir := dirs.GlobalRootDir
   102  	svcName := "snap.test-snap.foo.service"
   103  	c.Check(s.systemctlArgs, DeepEquals, [][]string{
   104  		{"--root", rootdir, "is-enabled", "snap.test-snap.foo.service"},
   105  		{"--root", rootdir, "enable", "snap.test-snap.foo.service"},
   106  		{"daemon-reload"},
   107  		{"--root", rootdir, "is-enabled", "snap.test-snap.foo.service"},
   108  		{"start", "snap.test-snap.foo.service"},
   109  	})
   110  	svcPath := filepath.Join(dirs.SnapServicesDir, svcName)
   111  	c.Check(svcPath, testutil.FileContains, "\nOOMScoreAdjust=-898\n")
   112  }
   113  
   114  func (s *vitalitySuite) TestConfigureVitalityHintTooMany(c *C) {
   115  	l := make([]string, 101)
   116  	for i := range l {
   117  		l[i] = strconv.Itoa(i)
   118  	}
   119  	manyStr := strings.Join(l, ",")
   120  	err := configcore.Run(&mockConf{
   121  		state: s.state,
   122  		changes: map[string]interface{}{
   123  			"resilience.vitality-hint": manyStr,
   124  		},
   125  	})
   126  	c.Assert(err, ErrorMatches, `cannot set more than 100 snaps in "resilience.vitality-hint": got 101`)
   127  }
   128  
   129  func (s *vitalitySuite) TestConfigureVitalityManySnaps(c *C) {
   130  	for _, snapName := range []string{"snap1", "snap2", "snap3"} {
   131  		si := &snap.SideInfo{RealName: snapName, Revision: snap.R(1)}
   132  		snaptest.MockSnap(c, mockSnapWithService, si)
   133  		s.state.Lock()
   134  		snapstate.Set(s.state, snapName, &snapstate.SnapState{
   135  			Sequence: []*snap.SideInfo{si},
   136  			Current:  snap.R(1),
   137  			Active:   true,
   138  			SnapType: "app",
   139  		})
   140  		s.state.Unlock()
   141  	}
   142  
   143  	// snap1,snap2,snap3
   144  	err := configcore.Run(&mockConf{
   145  		state: s.state,
   146  		changes: map[string]interface{}{
   147  			"resilience.vitality-hint": "snap1,snap2,snap3",
   148  		},
   149  	})
   150  	c.Assert(err, IsNil)
   151  	// test
   152  	svcPath := filepath.Join(dirs.SnapServicesDir, "snap.snap1.foo.service")
   153  	c.Check(svcPath, testutil.FileContains, "\nOOMScoreAdjust=-899\n")
   154  	svcPath = filepath.Join(dirs.SnapServicesDir, "snap.snap2.foo.service")
   155  	c.Check(svcPath, testutil.FileContains, "\nOOMScoreAdjust=-898\n")
   156  	svcPath = filepath.Join(dirs.SnapServicesDir, "snap.snap3.foo.service")
   157  	c.Check(svcPath, testutil.FileContains, "\nOOMScoreAdjust=-897\n")
   158  }
   159  
   160  func (s *vitalitySuite) TestConfigureVitalityManySnapsDelta(c *C) {
   161  	for _, snapName := range []string{"snap1", "snap2", "snap3"} {
   162  		si := &snap.SideInfo{RealName: snapName, Revision: snap.R(1)}
   163  		snaptest.MockSnap(c, mockSnapWithService, si)
   164  		s.state.Lock()
   165  		snapstate.Set(s.state, snapName, &snapstate.SnapState{
   166  			Sequence: []*snap.SideInfo{si},
   167  			Current:  snap.R(1),
   168  			Active:   true,
   169  			SnapType: "app",
   170  		})
   171  		s.state.Unlock()
   172  	}
   173  
   174  	// snap1,snap2,snap3 switch to snap3,snap1
   175  	err := configcore.Run(&mockConf{
   176  		state: s.state,
   177  		conf: map[string]interface{}{
   178  			"resilience.vitality-hint": "snap1,snap2,snap3",
   179  		},
   180  		changes: map[string]interface{}{
   181  			"resilience.vitality-hint": "snap3,snap1",
   182  		},
   183  	})
   184  	c.Assert(err, IsNil)
   185  	// test that snap1,snap3 got the new rank
   186  	svcPath := filepath.Join(dirs.SnapServicesDir, "snap.snap1.foo.service")
   187  	c.Check(svcPath, testutil.FileContains, "\nOOMScoreAdjust=-898")
   188  	// and that snap2 no longer has a OOMScoreAdjust setting
   189  	svcPath = filepath.Join(dirs.SnapServicesDir, "snap.snap2.foo.service")
   190  	c.Check(svcPath, Not(testutil.FileContains), "\nOOMScoreAdjust=")
   191  	svcPath = filepath.Join(dirs.SnapServicesDir, "snap.snap3.foo.service")
   192  	c.Check(svcPath, testutil.FileContains, "\nOOMScoreAdjust=-899\n")
   193  }
   194  
   195  func (s *vitalitySuite) TestConfigureVitalityManySnapsOneRemovedOneUnchanged(c *C) {
   196  	for _, snapName := range []string{"snap1", "snap2", "snap3"} {
   197  		si := &snap.SideInfo{RealName: snapName, Revision: snap.R(1)}
   198  		snaptest.MockSnap(c, mockSnapWithService, si)
   199  		s.state.Lock()
   200  		snapstate.Set(s.state, snapName, &snapstate.SnapState{
   201  			Sequence: []*snap.SideInfo{si},
   202  			Current:  snap.R(1),
   203  			Active:   true,
   204  			SnapType: "app",
   205  		})
   206  		s.state.Unlock()
   207  	}
   208  
   209  	// first run generates the snap1,snap2 configs
   210  	err := configcore.Run(&mockConf{
   211  		state: s.state,
   212  		changes: map[string]interface{}{
   213  			"resilience.vitality-hint": "snap1,snap2",
   214  		},
   215  	})
   216  	c.Assert(err, IsNil)
   217  	svcPath := filepath.Join(dirs.SnapServicesDir, "snap.snap1.foo.service")
   218  	c.Check(svcPath, testutil.FileContains, "\nOOMScoreAdjust=-899")
   219  	svcPath = filepath.Join(dirs.SnapServicesDir, "snap.snap2.foo.service")
   220  	c.Check(svcPath, testutil.FileContains, "\nOOMScoreAdjust=-898\n")
   221  	c.Check(s.systemctlArgs, testutil.DeepContains, []string{"start", "snap.snap1.foo.service"})
   222  	c.Check(s.systemctlArgs, testutil.DeepContains, []string{"start", "snap.snap2.foo.service"})
   223  	s.systemctlArgs = nil
   224  
   225  	// now we change the configuration and set snap1,snap3
   226  	err = configcore.Run(&mockConf{
   227  		state: s.state,
   228  		conf: map[string]interface{}{
   229  			"resilience.vitality-hint": "snap1,snap2",
   230  		},
   231  		changes: map[string]interface{}{
   232  			"resilience.vitality-hint": "snap1,snap3",
   233  		},
   234  	})
   235  	c.Assert(err, IsNil)
   236  	// test that snap1 is unchanged
   237  	svcPath = filepath.Join(dirs.SnapServicesDir, "snap.snap1.foo.service")
   238  	c.Check(svcPath, testutil.FileContains, "\nOOMScoreAdjust=-899")
   239  	// and that snap2 no longer has a OOMScoreAdjust setting
   240  	svcPath = filepath.Join(dirs.SnapServicesDir, "snap.snap2.foo.service")
   241  	c.Check(svcPath, Not(testutil.FileContains), "\nOOMScoreAdjust=")
   242  	// snap3 got added
   243  	svcPath = filepath.Join(dirs.SnapServicesDir, "snap.snap3.foo.service")
   244  	c.Check(svcPath, testutil.FileContains, "\nOOMScoreAdjust=-898\n")
   245  
   246  	// ensure that snap1 did not get started again (it is unchanged)
   247  	c.Check(s.systemctlArgs, Not(testutil.DeepContains), []string{"start", "snap.snap1.foo.service"})
   248  	// snap2 changed (no OOMScoreAdjust anymore) so needs restart
   249  	c.Check(s.systemctlArgs, testutil.DeepContains, []string{"start", "snap.snap2.foo.service"})
   250  	// snap3 changed so needs restart
   251  	c.Check(s.systemctlArgs, testutil.DeepContains, []string{"start", "snap.snap3.foo.service"})
   252  }
   253  
   254  func (s *vitalitySuite) TestConfigureVitalityNotActiveSnap(c *C) {
   255  	si := &snap.SideInfo{RealName: "test-snap", Revision: snap.R(1)}
   256  	snaptest.MockSnap(c, mockSnapWithService, si)
   257  	s.state.Lock()
   258  	snapstate.Set(s.state, "test-snap", &snapstate.SnapState{
   259  		Sequence: []*snap.SideInfo{si},
   260  		Current:  snap.R(1),
   261  		Active:   false,
   262  		SnapType: "app",
   263  	})
   264  	s.state.Unlock()
   265  
   266  	err := configcore.Run(&mockConf{
   267  		state: s.state,
   268  		changes: map[string]interface{}{
   269  			"resilience.vitality-hint": "unrelated,test-snap",
   270  		},
   271  	})
   272  	c.Assert(err, IsNil)
   273  	c.Check(s.systemctlArgs, HasLen, 0)
   274  }