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