gopkg.in/ubuntu-core/snappy.v0@v0.0.0-20210902073436-25a8614f10a6/overlord/snapstate/handlers_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2019 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 snapstate_test 21 22 import ( 23 "fmt" 24 25 . "gopkg.in/check.v1" 26 27 "github.com/snapcore/snapd/overlord/snapstate" 28 "github.com/snapcore/snapd/overlord/snapstate/snapstatetest" 29 "github.com/snapcore/snapd/overlord/state" 30 "github.com/snapcore/snapd/snap" 31 "github.com/snapcore/snapd/testutil" 32 ) 33 34 type handlersSuite struct { 35 baseHandlerSuite 36 37 stateBackend *witnessRestartReqStateBackend 38 } 39 40 var _ = Suite(&handlersSuite{}) 41 42 func (s *handlersSuite) SetUpTest(c *C) { 43 s.setup(c, s.stateBackend) 44 45 s.AddCleanup(snapstatetest.MockDeviceModel(DefaultModel())) 46 } 47 48 func (s *handlersSuite) TestSetTaskSnapSetupFirstTask(c *C) { 49 s.state.Lock() 50 defer s.state.Unlock() 51 52 // make a new task which will be the snap-setup-task for other tasks and 53 // write a SnapSetup to it 54 t := s.state.NewTask("link-snap", "test") 55 snapsup := &snapstate.SnapSetup{ 56 SideInfo: &snap.SideInfo{ 57 RealName: "foo", 58 Revision: snap.R(33), 59 SnapID: "foo-id", 60 }, 61 Channel: "beta", 62 UserID: 2, 63 } 64 t.Set("snap-setup", snapsup) 65 s.state.NewChange("dummy", "...").AddTask(t) 66 67 // mutate it and rewrite it with the helper 68 snapsup.Channel = "edge" 69 err := snapstate.SetTaskSnapSetup(t, snapsup) 70 c.Assert(err, IsNil) 71 72 // get a fresh version of this task from state to check that the task's 73 /// snap-setup has the changes in it now 74 newT := s.state.Task(t.ID()) 75 c.Assert(newT, Not(IsNil)) 76 var newsnapsup snapstate.SnapSetup 77 err = newT.Get("snap-setup", &newsnapsup) 78 c.Assert(err, IsNil) 79 c.Assert(newsnapsup.Channel, Equals, snapsup.Channel) 80 } 81 82 func (s *handlersSuite) TestSetTaskSnapSetupLaterTask(c *C) { 83 s.state.Lock() 84 defer s.state.Unlock() 85 t := s.state.NewTask("link-snap", "test") 86 87 snapsup := &snapstate.SnapSetup{ 88 SideInfo: &snap.SideInfo{ 89 RealName: "foo", 90 Revision: snap.R(33), 91 SnapID: "foo-id", 92 }, 93 Channel: "beta", 94 UserID: 2, 95 } 96 // setup snap-setup for the first task 97 t.Set("snap-setup", snapsup) 98 99 // make a new task and reference the first one in snap-setup-task 100 t2 := s.state.NewTask("next-task-snap", "test2") 101 t2.Set("snap-setup-task", t.ID()) 102 103 chg := s.state.NewChange("dummy", "...") 104 chg.AddTask(t) 105 chg.AddTask(t2) 106 107 // mutate it and rewrite it with the helper 108 snapsup.Channel = "edge" 109 err := snapstate.SetTaskSnapSetup(t2, snapsup) 110 c.Assert(err, IsNil) 111 112 // check that the original task's snap-setup is different now 113 newT := s.state.Task(t.ID()) 114 c.Assert(newT, Not(IsNil)) 115 var newsnapsup snapstate.SnapSetup 116 err = newT.Get("snap-setup", &newsnapsup) 117 c.Assert(err, IsNil) 118 c.Assert(newsnapsup.Channel, Equals, snapsup.Channel) 119 } 120 121 func (s *handlersSuite) TestComputeMissingDisabledServices(c *C) { 122 for _, tt := range []struct { 123 // inputs 124 stDisabledSvcsList []string 125 apps map[string]*snap.AppInfo 126 // outputs 127 missing []string 128 found []string 129 err error 130 comment string 131 }{ 132 // no apps 133 { 134 []string{}, 135 nil, 136 []string{}, 137 []string{}, 138 nil, 139 "no apps", 140 }, 141 // only apps, no services 142 { 143 []string{}, 144 map[string]*snap.AppInfo{ 145 "app": { 146 Daemon: "", 147 }, 148 }, 149 []string{}, 150 []string{}, 151 nil, 152 "no services", 153 }, 154 // services in snap, but not disabled 155 { 156 []string{}, 157 map[string]*snap.AppInfo{ 158 "svc1": { 159 Daemon: "simple", 160 }, 161 }, 162 []string{}, 163 []string{}, 164 nil, 165 "no disabled services", 166 }, 167 // all disabled services, but not present in snap 168 { 169 []string{"svc1"}, 170 nil, 171 []string{"svc1"}, 172 []string{}, 173 nil, 174 "all missing disabled services", 175 }, 176 // all disabled services, and found in snap 177 { 178 []string{"svc1"}, 179 map[string]*snap.AppInfo{ 180 "svc1": { 181 Daemon: "simple", 182 }, 183 }, 184 []string{}, 185 []string{"svc1"}, 186 nil, 187 "all found disabled services", 188 }, 189 // some disabled services, some missing, some present in snap 190 { 191 []string{"svc1", "svc2"}, 192 map[string]*snap.AppInfo{ 193 "svc1": { 194 Daemon: "simple", 195 }, 196 }, 197 []string{"svc2"}, 198 []string{"svc1"}, 199 nil, 200 "some missing, some found disabled services", 201 }, 202 // some disabled services, but app is not service 203 { 204 []string{"svc1"}, 205 map[string]*snap.AppInfo{ 206 "svc1": { 207 Daemon: "", 208 }, 209 }, 210 []string{"svc1"}, 211 []string{}, 212 nil, 213 "some disabled services that are now apps", 214 }, 215 } { 216 info := &snap.Info{Apps: tt.apps} 217 218 found, missing, err := snapstate.MissingDisabledServices(tt.stDisabledSvcsList, info) 219 c.Assert(missing, DeepEquals, tt.missing, Commentf(tt.comment)) 220 c.Assert(found, DeepEquals, tt.found, Commentf(tt.comment)) 221 c.Assert(err, Equals, tt.err, Commentf(tt.comment)) 222 } 223 } 224 225 type testLinkParticipant struct { 226 callCount int 227 instanceNames []string 228 linkageChanged func(st *state.State, instanceName string) error 229 } 230 231 func (lp *testLinkParticipant) SnapLinkageChanged(st *state.State, instanceName string) error { 232 lp.callCount++ 233 lp.instanceNames = append(lp.instanceNames, instanceName) 234 if lp.linkageChanged != nil { 235 return lp.linkageChanged(st, instanceName) 236 } 237 return nil 238 } 239 240 func (s *handlersSuite) TestAddLinkParticipant(c *C) { 241 s.state.Lock() 242 defer s.state.Unlock() 243 244 // Mock link snap participants. This ensures we can add a participant 245 // without affecting the other tests, as the original list will be 246 // restored. 247 restore := snapstate.MockLinkSnapParticipants(nil) 248 defer restore() 249 250 lp := &testLinkParticipant{ 251 linkageChanged: func(st *state.State, instanceName string) error { 252 c.Assert(st, NotNil) 253 c.Check(instanceName, Equals, "snap-name") 254 return nil 255 }, 256 } 257 snapstate.AddLinkSnapParticipant(lp) 258 259 t := s.state.NewTask("link-snap", "test") 260 snapstate.NotifyLinkParticipants(t, "snap-name") 261 c.Assert(lp.callCount, Equals, 1) 262 } 263 264 func (s *handlersSuite) TestNotifyLinkParticipantsErrorHandling(c *C) { 265 s.state.Lock() 266 defer s.state.Unlock() 267 268 // See comment in TestAddLinkParticipant for details. 269 restore := snapstate.MockLinkSnapParticipants(nil) 270 defer restore() 271 272 lp := &testLinkParticipant{ 273 linkageChanged: func(st *state.State, instanceName string) error { 274 return fmt.Errorf("something failed") 275 }, 276 } 277 snapstate.AddLinkSnapParticipant(lp) 278 279 t := s.state.NewTask("link-snap", "test") 280 snapstate.NotifyLinkParticipants(t, "snap-name") 281 c.Assert(lp.callCount, Equals, 1) 282 logs := t.Log() 283 c.Assert(logs, HasLen, 1) 284 c.Check(logs[0], testutil.Contains, "ERROR something failed") 285 }