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 }