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 }