github.com/chipaca/snappy@v0.0.0-20210104084008-1f06296fe8ad/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  	svcName := "snap.test-snap.foo.service"
   102  	c.Check(s.systemctlArgs, DeepEquals, [][]string{
   103  		{"is-enabled", "snap.test-snap.foo.service"},
   104  		{"daemon-reload"},
   105  		{"enable", "snap.test-snap.foo.service"},
   106  		{"start", "snap.test-snap.foo.service"},
   107  	})
   108  	svcPath := filepath.Join(dirs.SnapServicesDir, svcName)
   109  	c.Check(svcPath, testutil.FileContains, "\nOOMScoreAdjust=-898\n")
   110  }
   111  
   112  func (s *vitalitySuite) TestConfigureVitalityHintTooMany(c *C) {
   113  	l := make([]string, 101)
   114  	for i := range l {
   115  		l[i] = strconv.Itoa(i)
   116  	}
   117  	manyStr := strings.Join(l, ",")
   118  	err := configcore.Run(&mockConf{
   119  		state: s.state,
   120  		changes: map[string]interface{}{
   121  			"resilience.vitality-hint": manyStr,
   122  		},
   123  	})
   124  	c.Assert(err, ErrorMatches, `cannot set more than 100 snaps in "resilience.vitality-hint": got 101`)
   125  }
   126  
   127  func (s *vitalitySuite) TestConfigureVitalityManySnaps(c *C) {
   128  	for _, snapName := range []string{"snap1", "snap2", "snap3"} {
   129  		si := &snap.SideInfo{RealName: snapName, Revision: snap.R(1)}
   130  		snaptest.MockSnap(c, mockSnapWithService, si)
   131  		s.state.Lock()
   132  		snapstate.Set(s.state, snapName, &snapstate.SnapState{
   133  			Sequence: []*snap.SideInfo{si},
   134  			Current:  snap.R(1),
   135  			Active:   true,
   136  			SnapType: "app",
   137  		})
   138  		s.state.Unlock()
   139  	}
   140  
   141  	// snap1,snap2,snap3
   142  	err := configcore.Run(&mockConf{
   143  		state: s.state,
   144  		changes: map[string]interface{}{
   145  			"resilience.vitality-hint": "snap1,snap2,snap3",
   146  		},
   147  	})
   148  	c.Assert(err, IsNil)
   149  	// test
   150  	svcPath := filepath.Join(dirs.SnapServicesDir, "snap.snap1.foo.service")
   151  	c.Check(svcPath, testutil.FileContains, "\nOOMScoreAdjust=-899\n")
   152  	svcPath = filepath.Join(dirs.SnapServicesDir, "snap.snap2.foo.service")
   153  	c.Check(svcPath, testutil.FileContains, "\nOOMScoreAdjust=-898\n")
   154  	svcPath = filepath.Join(dirs.SnapServicesDir, "snap.snap3.foo.service")
   155  	c.Check(svcPath, testutil.FileContains, "\nOOMScoreAdjust=-897\n")
   156  }
   157  
   158  func (s *vitalitySuite) TestConfigureVitalityManySnapsDelta(c *C) {
   159  	for _, snapName := range []string{"snap1", "snap2", "snap3"} {
   160  		si := &snap.SideInfo{RealName: snapName, Revision: snap.R(1)}
   161  		snaptest.MockSnap(c, mockSnapWithService, si)
   162  		s.state.Lock()
   163  		snapstate.Set(s.state, snapName, &snapstate.SnapState{
   164  			Sequence: []*snap.SideInfo{si},
   165  			Current:  snap.R(1),
   166  			Active:   true,
   167  			SnapType: "app",
   168  		})
   169  		s.state.Unlock()
   170  	}
   171  
   172  	// snap1,snap2,snap3 switch to snap3,snap1
   173  	err := configcore.Run(&mockConf{
   174  		state: s.state,
   175  		conf: map[string]interface{}{
   176  			"resilience.vitality-hint": "snap1,snap2,snap3",
   177  		},
   178  		changes: map[string]interface{}{
   179  			"resilience.vitality-hint": "snap3,snap1",
   180  		},
   181  	})
   182  	c.Assert(err, IsNil)
   183  	// test that snap1,snap3 got the new rank
   184  	svcPath := filepath.Join(dirs.SnapServicesDir, "snap.snap1.foo.service")
   185  	c.Check(svcPath, testutil.FileContains, "\nOOMScoreAdjust=-898")
   186  	// and that snap2 no longer has a OOMScoreAdjust setting
   187  	svcPath = filepath.Join(dirs.SnapServicesDir, "snap.snap2.foo.service")
   188  	c.Check(svcPath, Not(testutil.FileContains), "\nOOMScoreAdjust=")
   189  	svcPath = filepath.Join(dirs.SnapServicesDir, "snap.snap3.foo.service")
   190  	c.Check(svcPath, testutil.FileContains, "\nOOMScoreAdjust=-899\n")
   191  }
   192  
   193  func (s *vitalitySuite) TestConfigureVitalityManySnapsOneRemovedOneUnchanged(c *C) {
   194  	for _, snapName := range []string{"snap1", "snap2", "snap3"} {
   195  		si := &snap.SideInfo{RealName: snapName, Revision: snap.R(1)}
   196  		snaptest.MockSnap(c, mockSnapWithService, si)
   197  		s.state.Lock()
   198  		snapstate.Set(s.state, snapName, &snapstate.SnapState{
   199  			Sequence: []*snap.SideInfo{si},
   200  			Current:  snap.R(1),
   201  			Active:   true,
   202  			SnapType: "app",
   203  		})
   204  		s.state.Unlock()
   205  	}
   206  
   207  	// first run generates the snap1,snap2 configs
   208  	err := configcore.Run(&mockConf{
   209  		state: s.state,
   210  		changes: map[string]interface{}{
   211  			"resilience.vitality-hint": "snap1,snap2",
   212  		},
   213  	})
   214  	c.Assert(err, IsNil)
   215  	svcPath := filepath.Join(dirs.SnapServicesDir, "snap.snap1.foo.service")
   216  	c.Check(svcPath, testutil.FileContains, "\nOOMScoreAdjust=-899")
   217  	svcPath = filepath.Join(dirs.SnapServicesDir, "snap.snap2.foo.service")
   218  	c.Check(svcPath, testutil.FileContains, "\nOOMScoreAdjust=-898\n")
   219  	c.Check(s.systemctlArgs, testutil.DeepContains, []string{"start", "snap.snap1.foo.service"})
   220  	c.Check(s.systemctlArgs, testutil.DeepContains, []string{"start", "snap.snap2.foo.service"})
   221  	s.systemctlArgs = nil
   222  
   223  	// now we change the configuration and set snap1,snap3
   224  	err = configcore.Run(&mockConf{
   225  		state: s.state,
   226  		conf: map[string]interface{}{
   227  			"resilience.vitality-hint": "snap1,snap2",
   228  		},
   229  		changes: map[string]interface{}{
   230  			"resilience.vitality-hint": "snap1,snap3",
   231  		},
   232  	})
   233  	c.Assert(err, IsNil)
   234  	// test that snap1 is unchanged
   235  	svcPath = filepath.Join(dirs.SnapServicesDir, "snap.snap1.foo.service")
   236  	c.Check(svcPath, testutil.FileContains, "\nOOMScoreAdjust=-899")
   237  	// and that snap2 no longer has a OOMScoreAdjust setting
   238  	svcPath = filepath.Join(dirs.SnapServicesDir, "snap.snap2.foo.service")
   239  	c.Check(svcPath, Not(testutil.FileContains), "\nOOMScoreAdjust=")
   240  	// snap3 got added
   241  	svcPath = filepath.Join(dirs.SnapServicesDir, "snap.snap3.foo.service")
   242  	c.Check(svcPath, testutil.FileContains, "\nOOMScoreAdjust=-898\n")
   243  
   244  	// ensure that snap1 did not get started again (it is unchanged)
   245  	c.Check(s.systemctlArgs, Not(testutil.DeepContains), []string{"start", "snap.snap1.foo.service"})
   246  	// snap2 changed (no OOMScoreAdjust anymore) so needs restart
   247  	c.Check(s.systemctlArgs, testutil.DeepContains, []string{"start", "snap.snap2.foo.service"})
   248  	// snap3 changed so needs restart
   249  	c.Check(s.systemctlArgs, testutil.DeepContains, []string{"start", "snap.snap3.foo.service"})
   250  }
   251  
   252  func (s *vitalitySuite) TestConfigureVitalityNotActiveSnap(c *C) {
   253  	si := &snap.SideInfo{RealName: "test-snap", Revision: snap.R(1)}
   254  	snaptest.MockSnap(c, mockSnapWithService, si)
   255  	s.state.Lock()
   256  	snapstate.Set(s.state, "test-snap", &snapstate.SnapState{
   257  		Sequence: []*snap.SideInfo{si},
   258  		Current:  snap.R(1),
   259  		Active:   false,
   260  		SnapType: "app",
   261  	})
   262  	s.state.Unlock()
   263  
   264  	err := configcore.Run(&mockConf{
   265  		state: s.state,
   266  		changes: map[string]interface{}{
   267  			"resilience.vitality-hint": "unrelated,test-snap",
   268  		},
   269  	})
   270  	c.Assert(err, IsNil)
   271  	c.Check(s.systemctlArgs, HasLen, 0)
   272  }