github.com/tompreston/snapd@v0.0.0-20210817193607-954edfcb9611/snap/info_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2014-2021 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 snap_test 21 22 import ( 23 "fmt" 24 "io/ioutil" 25 "os" 26 "path/filepath" 27 "regexp" 28 "sort" 29 "strings" 30 "testing" 31 32 . "gopkg.in/check.v1" 33 "gopkg.in/yaml.v2" 34 35 "github.com/snapcore/snapd/dirs" 36 "github.com/snapcore/snapd/osutil" 37 "github.com/snapcore/snapd/snap" 38 "github.com/snapcore/snapd/snap/snapfile" 39 "github.com/snapcore/snapd/snap/snaptest" 40 "github.com/snapcore/snapd/snap/squashfs" 41 "github.com/snapcore/snapd/testutil" 42 ) 43 44 type infoSuite struct { 45 testutil.BaseTest 46 } 47 48 type infoSimpleSuite struct{} 49 50 var _ = Suite(&infoSuite{}) 51 var _ = Suite(&infoSimpleSuite{}) 52 53 func (s *infoSimpleSuite) SetUpTest(c *C) { 54 dirs.SetRootDir(c.MkDir()) 55 } 56 57 func (s *infoSimpleSuite) TearDownTest(c *C) { 58 dirs.SetRootDir("") 59 } 60 61 func (s *infoSimpleSuite) TestReadInfoPanicsIfSanitizeUnset(c *C) { 62 si := &snap.SideInfo{Revision: snap.R(1)} 63 snaptest.MockSnap(c, sampleYaml, si) 64 c.Assert(func() { snap.ReadInfo("sample", si) }, Panics, `SanitizePlugsSlots function not set`) 65 } 66 67 func (s *infoSuite) SetUpTest(c *C) { 68 s.BaseTest.SetUpTest(c) 69 dirs.SetRootDir(c.MkDir()) 70 hookType := snap.NewHookType(regexp.MustCompile(".*")) 71 s.BaseTest.AddCleanup(snap.MockSanitizePlugsSlots(func(snapInfo *snap.Info) {})) 72 s.BaseTest.AddCleanup(snap.MockSupportedHookTypes([]*snap.HookType{hookType})) 73 } 74 75 func (s *infoSuite) TearDownTest(c *C) { 76 s.BaseTest.TearDownTest(c) 77 dirs.SetRootDir("") 78 } 79 80 func (s *infoSuite) TestSideInfoOverrides(c *C) { 81 info := &snap.Info{ 82 SuggestedName: "name", 83 OriginalSummary: "summary", 84 OriginalDescription: "desc", 85 } 86 87 info.SideInfo = snap.SideInfo{ 88 RealName: "newname", 89 EditedSummary: "fixed summary", 90 EditedDescription: "fixed desc", 91 Revision: snap.R(1), 92 SnapID: "snapidsnapidsnapidsnapidsnapidsn", 93 } 94 95 c.Check(info.InstanceName(), Equals, "newname") 96 c.Check(info.Summary(), Equals, "fixed summary") 97 c.Check(info.Description(), Equals, "fixed desc") 98 c.Check(info.Revision, Equals, snap.R(1)) 99 c.Check(info.SnapID, Equals, "snapidsnapidsnapidsnapidsnapidsn") 100 c.Check(info.ID(), Equals, "snapidsnapidsnapidsnapidsnapidsn") 101 } 102 103 func (s *infoSuite) TestContactFromEdited(c *C) { 104 info := &snap.Info{ 105 OriginalLinks: nil, 106 } 107 108 info.SideInfo = snap.SideInfo{ 109 EditedContact: "mailto:econtact", 110 } 111 112 c.Check(info.Contact(), Equals, "mailto:econtact") 113 } 114 115 func (s *infoSuite) TestNoContact(c *C) { 116 info := &snap.Info{} 117 118 c.Check(info.Contact(), Equals, "") 119 } 120 121 func (s *infoSuite) TestContactFromLinks(c *C) { 122 info := &snap.Info{ 123 OriginalLinks: map[string][]string{ 124 "contact": {"ocontact1", "ocontact2"}, 125 }, 126 } 127 128 c.Check(info.Contact(), Equals, "mailto:ocontact1") 129 } 130 131 func (s *infoSuite) TestContactFromLinksMailtoAlready(c *C) { 132 info := &snap.Info{ 133 OriginalLinks: map[string][]string{ 134 "contact": {"mailto:ocontact1", "ocontact2"}, 135 }, 136 } 137 138 c.Check(info.Contact(), Equals, "mailto:ocontact1") 139 } 140 141 func (s *infoSuite) TestContactFromLinksNotEmail(c *C) { 142 info := &snap.Info{ 143 OriginalLinks: map[string][]string{ 144 "contact": {"https://ocontact1", "ocontact2"}, 145 }, 146 } 147 148 c.Check(info.Contact(), Equals, "https://ocontact1") 149 } 150 151 func (s *infoSuite) TestLinks(c *C) { 152 info := &snap.Info{ 153 OriginalLinks: map[string][]string{ 154 "contact": {"ocontact"}, 155 "website": {"owebsite"}, 156 }, 157 } 158 159 info.SideInfo = snap.SideInfo{ 160 EditedLinks: map[string][]string{ 161 "contact": {"econtact"}, 162 "website": {"ewebsite"}, 163 }, 164 } 165 166 c.Check(info.Links(), DeepEquals, map[string][]string{ 167 "contact": {"econtact"}, 168 "website": {"ewebsite"}, 169 }) 170 171 info.EditedLinks = nil 172 c.Check(info.Links(), DeepEquals, map[string][]string{ 173 "contact": {"ocontact"}, 174 "website": {"owebsite"}, 175 }) 176 } 177 178 func (s *infoSuite) TestAppInfoSecurityTag(c *C) { 179 appInfo := &snap.AppInfo{Snap: &snap.Info{SuggestedName: "http"}, Name: "GET"} 180 c.Check(appInfo.SecurityTag(), Equals, "snap.http.GET") 181 } 182 183 func (s *infoSuite) TestPlugSlotSecurityTags(c *C) { 184 info, err := snap.InfoFromSnapYaml([]byte(`name: name 185 apps: 186 app1: 187 app2: 188 hooks: 189 hook1: 190 plugs: 191 plug: 192 slots: 193 slot: 194 `)) 195 c.Assert(err, IsNil) 196 c.Assert(info.Plugs["plug"].SecurityTags(), DeepEquals, []string{ 197 "snap.name.app1", "snap.name.app2", "snap.name.hook.hook1"}) 198 c.Assert(info.Slots["slot"].SecurityTags(), DeepEquals, []string{ 199 "snap.name.app1", "snap.name.app2", "snap.name.hook.hook1"}) 200 } 201 202 func (s *infoSuite) TestAppInfoWrapperPath(c *C) { 203 info, err := snap.InfoFromSnapYaml([]byte(`name: foo 204 apps: 205 foo: 206 bar: 207 `)) 208 c.Assert(err, IsNil) 209 210 c.Check(info.Apps["bar"].WrapperPath(), Equals, filepath.Join(dirs.SnapBinariesDir, "foo.bar")) 211 c.Check(info.Apps["foo"].WrapperPath(), Equals, filepath.Join(dirs.SnapBinariesDir, "foo")) 212 } 213 214 func (s *infoSuite) TestAppInfoCompleterPath(c *C) { 215 info, err := snap.InfoFromSnapYaml([]byte(`name: foo 216 apps: 217 foo: 218 bar: 219 `)) 220 c.Assert(err, IsNil) 221 222 c.Check(info.Apps["bar"].CompleterPath(), Equals, filepath.Join(dirs.CompletersDir, "foo.bar")) 223 c.Check(info.Apps["foo"].CompleterPath(), Equals, filepath.Join(dirs.CompletersDir, "foo")) 224 } 225 226 func (s *infoSuite) TestAppInfoLauncherCommand(c *C) { 227 dirs.SetRootDir("") 228 229 info, err := snap.InfoFromSnapYaml([]byte(`name: foo 230 apps: 231 foo: 232 command: foo-bin 233 bar: 234 command: bar-bin -x 235 baz: 236 command: bar-bin -x 237 timer: 10:00-12:00,,mon,12:00~14:00 238 `)) 239 c.Assert(err, IsNil) 240 info.Revision = snap.R(42) 241 c.Check(info.Apps["bar"].LauncherCommand(), Equals, "/usr/bin/snap run foo.bar") 242 c.Check(info.Apps["bar"].LauncherStopCommand(), Equals, "/usr/bin/snap run --command=stop foo.bar") 243 c.Check(info.Apps["bar"].LauncherReloadCommand(), Equals, "/usr/bin/snap run --command=reload foo.bar") 244 c.Check(info.Apps["bar"].LauncherPostStopCommand(), Equals, "/usr/bin/snap run --command=post-stop foo.bar") 245 c.Check(info.Apps["foo"].LauncherCommand(), Equals, "/usr/bin/snap run foo") 246 c.Check(info.Apps["baz"].LauncherCommand(), Equals, `/usr/bin/snap run --timer="10:00-12:00,,mon,12:00~14:00" foo.baz`) 247 248 // snap with instance key 249 info.InstanceKey = "instance" 250 c.Check(info.Apps["bar"].LauncherCommand(), Equals, "/usr/bin/snap run foo_instance.bar") 251 c.Check(info.Apps["bar"].LauncherStopCommand(), Equals, "/usr/bin/snap run --command=stop foo_instance.bar") 252 c.Check(info.Apps["bar"].LauncherReloadCommand(), Equals, "/usr/bin/snap run --command=reload foo_instance.bar") 253 c.Check(info.Apps["bar"].LauncherPostStopCommand(), Equals, "/usr/bin/snap run --command=post-stop foo_instance.bar") 254 c.Check(info.Apps["foo"].LauncherCommand(), Equals, "/usr/bin/snap run foo_instance") 255 c.Check(info.Apps["baz"].LauncherCommand(), Equals, `/usr/bin/snap run --timer="10:00-12:00,,mon,12:00~14:00" foo_instance.baz`) 256 } 257 258 const sampleYaml = ` 259 name: sample 260 version: 1 261 apps: 262 app: 263 command: foo 264 app2: 265 command: bar 266 sample: 267 command: foobar 268 command-chain: [chain] 269 hooks: 270 configure: 271 command-chain: [hookchain] 272 ` 273 274 func (s *infoSuite) TestReadInfo(c *C) { 275 si := &snap.SideInfo{Revision: snap.R(42), EditedSummary: "esummary"} 276 277 snapInfo1 := snaptest.MockSnap(c, sampleYaml, si) 278 279 snapInfo2, err := snap.ReadInfo("sample", si) 280 c.Assert(err, IsNil) 281 282 c.Check(snapInfo2.InstanceName(), Equals, "sample") 283 c.Check(snapInfo2.Revision, Equals, snap.R(42)) 284 c.Check(snapInfo2.Summary(), Equals, "esummary") 285 286 c.Check(snapInfo2.Apps["app"].Command, Equals, "foo") 287 c.Check(snapInfo2.Apps["sample"].CommandChain, DeepEquals, []string{"chain"}) 288 c.Check(snapInfo2.Hooks["configure"].CommandChain, DeepEquals, []string{"hookchain"}) 289 290 c.Check(snapInfo2, DeepEquals, snapInfo1) 291 } 292 293 func (s *infoSuite) TestReadInfoWithInstance(c *C) { 294 si := &snap.SideInfo{Revision: snap.R(42), EditedSummary: "instance summary"} 295 296 snapInfo1 := snaptest.MockSnapInstance(c, "sample_instance", sampleYaml, si) 297 298 snapInfo2, err := snap.ReadInfo("sample_instance", si) 299 c.Assert(err, IsNil) 300 301 c.Check(snapInfo2.InstanceName(), Equals, "sample_instance") 302 c.Check(snapInfo2.SnapName(), Equals, "sample") 303 c.Check(snapInfo2.Revision, Equals, snap.R(42)) 304 c.Check(snapInfo2.Summary(), Equals, "instance summary") 305 306 c.Check(snapInfo2.Apps["app"].Command, Equals, "foo") 307 c.Check(snapInfo2.Apps["sample"].CommandChain, DeepEquals, []string{"chain"}) 308 c.Check(snapInfo2.Hooks["configure"].CommandChain, DeepEquals, []string{"hookchain"}) 309 310 c.Check(snapInfo2, DeepEquals, snapInfo1) 311 } 312 313 func (s *infoSuite) TestReadCurrentInfo(c *C) { 314 si := &snap.SideInfo{Revision: snap.R(42)} 315 316 snapInfo1 := snaptest.MockSnapCurrent(c, sampleYaml, si) 317 318 snapInfo2, err := snap.ReadCurrentInfo("sample") 319 c.Assert(err, IsNil) 320 321 c.Check(snapInfo2.InstanceName(), Equals, "sample") 322 c.Check(snapInfo2.Revision, Equals, snap.R(42)) 323 c.Check(snapInfo2, DeepEquals, snapInfo1) 324 325 snapInfo3, err := snap.ReadCurrentInfo("not-sample") 326 c.Check(snapInfo3, IsNil) 327 c.Assert(err, ErrorMatches, `cannot find current revision for snap not-sample:.*`) 328 } 329 330 func (s *infoSuite) TestReadCurrentInfoWithInstance(c *C) { 331 si := &snap.SideInfo{Revision: snap.R(42)} 332 333 snapInfo1 := snaptest.MockSnapInstanceCurrent(c, "sample_instance", sampleYaml, si) 334 335 snapInfo2, err := snap.ReadCurrentInfo("sample_instance") 336 c.Assert(err, IsNil) 337 338 c.Check(snapInfo2.InstanceName(), Equals, "sample_instance") 339 c.Check(snapInfo2.SnapName(), Equals, "sample") 340 c.Check(snapInfo2.Revision, Equals, snap.R(42)) 341 c.Check(snapInfo2, DeepEquals, snapInfo1) 342 343 snapInfo3, err := snap.ReadCurrentInfo("sample_other") 344 c.Check(snapInfo3, IsNil) 345 c.Assert(err, ErrorMatches, `cannot find current revision for snap sample_other:.*`) 346 } 347 348 func (s *infoSuite) TestInstallDate(c *C) { 349 si := &snap.SideInfo{Revision: snap.R(1)} 350 info := snaptest.MockSnap(c, sampleYaml, si) 351 // not current -> Zero 352 c.Check(info.InstallDate().IsZero(), Equals, true) 353 c.Check(snap.InstallDate(info.InstanceName()).IsZero(), Equals, true) 354 355 mountdir := info.MountDir() 356 dir, rev := filepath.Split(mountdir) 357 c.Assert(os.MkdirAll(dir, 0755), IsNil) 358 cur := filepath.Join(dir, "current") 359 c.Assert(os.Symlink(rev, cur), IsNil) 360 st, err := os.Lstat(cur) 361 c.Assert(err, IsNil) 362 instTime := st.ModTime() 363 // sanity 364 c.Check(instTime.IsZero(), Equals, false) 365 366 c.Check(info.InstallDate().Equal(instTime), Equals, true) 367 c.Check(snap.InstallDate(info.InstanceName()).Equal(instTime), Equals, true) 368 } 369 370 func (s *infoSuite) TestReadInfoNotFound(c *C) { 371 si := &snap.SideInfo{Revision: snap.R(42), EditedSummary: "esummary"} 372 info, err := snap.ReadInfo("sample", si) 373 c.Check(info, IsNil) 374 c.Check(err, ErrorMatches, `cannot find installed snap "sample" at revision 42: missing file .*sample/42/meta/snap.yaml`) 375 bse, ok := err.(snap.BrokenSnapError) 376 c.Assert(ok, Equals, true) 377 c.Check(bse.Broken(), Equals, bse.Error()) 378 } 379 380 func (s *infoSuite) TestReadInfoUnreadable(c *C) { 381 si := &snap.SideInfo{Revision: snap.R(42), EditedSummary: "esummary"} 382 c.Assert(os.MkdirAll(filepath.Join(snap.MinimalPlaceInfo("sample", si.Revision).MountDir(), "meta", "snap.yaml"), 0755), IsNil) 383 384 info, err := snap.ReadInfo("sample", si) 385 c.Check(info, IsNil) 386 // TODO: maybe improve this error message 387 c.Check(err, ErrorMatches, ".* is a directory") 388 } 389 390 func (s *infoSuite) TestReadInfoUnparsable(c *C) { 391 si := &snap.SideInfo{Revision: snap.R(42), EditedSummary: "esummary"} 392 p := filepath.Join(snap.MinimalPlaceInfo("sample", si.Revision).MountDir(), "meta", "snap.yaml") 393 c.Assert(os.MkdirAll(filepath.Dir(p), 0755), IsNil) 394 c.Assert(ioutil.WriteFile(p, []byte(`- :`), 0644), IsNil) 395 396 info, err := snap.ReadInfo("sample", si) 397 c.Check(info, IsNil) 398 // TODO: maybe improve this error message 399 c.Check(err, ErrorMatches, `cannot use installed snap "sample" at revision 42: cannot parse snap.yaml: yaml: .*`) 400 bse, ok := err.(snap.BrokenSnapError) 401 c.Assert(ok, Equals, true) 402 c.Check(bse.Broken(), Equals, bse.Error()) 403 } 404 405 func (s *infoSuite) TestReadInfoUnfindable(c *C) { 406 si := &snap.SideInfo{Revision: snap.R(42), EditedSummary: "esummary"} 407 p := filepath.Join(snap.MinimalPlaceInfo("sample", si.Revision).MountDir(), "meta", "snap.yaml") 408 c.Assert(os.MkdirAll(filepath.Dir(p), 0755), IsNil) 409 c.Assert(ioutil.WriteFile(p, []byte(``), 0644), IsNil) 410 411 info, err := snap.ReadInfo("sample", si) 412 c.Check(err, ErrorMatches, `cannot find installed snap "sample" at revision 42: missing file .*var/lib/snapd/snaps/sample_42.snap`) 413 c.Check(info, IsNil) 414 } 415 416 func (s *infoSuite) TestReadInfoDanglingSymlink(c *C) { 417 si := &snap.SideInfo{Revision: snap.R(42), EditedSummary: "esummary"} 418 mpi := snap.MinimalPlaceInfo("sample", si.Revision) 419 p := filepath.Join(mpi.MountDir(), "meta", "snap.yaml") 420 c.Assert(os.MkdirAll(filepath.Dir(p), 0755), IsNil) 421 c.Assert(ioutil.WriteFile(p, []byte(`name: test`), 0644), IsNil) 422 c.Assert(os.MkdirAll(filepath.Dir(mpi.MountFile()), 0755), IsNil) 423 c.Assert(os.Symlink("/dangling", mpi.MountFile()), IsNil) 424 425 info, err := snap.ReadInfo("sample", si) 426 c.Check(err, IsNil) 427 c.Check(info.SnapName(), Equals, "test") 428 c.Check(info.Revision, Equals, snap.R(42)) 429 c.Check(info.Summary(), Equals, "esummary") 430 c.Check(info.Size, Equals, int64(0)) 431 } 432 433 // makeTestSnap here can also be used to produce broken snaps (differently from snaptest.MakeTestSnapWithFiles)! 434 func makeTestSnap(c *C, snapYaml string) string { 435 var m struct { 436 Type string `yaml:"type"` 437 } 438 yaml.Unmarshal([]byte(snapYaml), &m) // yes, ignore the error 439 440 tmp := c.MkDir() 441 snapSource := filepath.Join(tmp, "snapsrc") 442 443 err := os.MkdirAll(filepath.Join(snapSource, "meta"), 0755) 444 c.Assert(err, IsNil) 445 446 // our regular snap.yaml 447 err = ioutil.WriteFile(filepath.Join(snapSource, "meta", "snap.yaml"), []byte(snapYaml), 0644) 448 c.Assert(err, IsNil) 449 450 dest := filepath.Join(tmp, "foo.snap") 451 snap := squashfs.New(dest) 452 err = snap.Build(snapSource, &squashfs.BuildOpts{SnapType: m.Type}) 453 c.Assert(err, IsNil) 454 455 return dest 456 } 457 458 // produce descrs for empty hooks suitable for snaptest.PopulateDir 459 func emptyHooks(hookNames ...string) (emptyHooks [][]string) { 460 for _, hookName := range hookNames { 461 emptyHooks = append(emptyHooks, []string{filepath.Join("meta", "hooks", hookName), ""}) 462 } 463 return 464 } 465 466 func (s *infoSuite) TestReadInfoFromSnapFile(c *C) { 467 yaml := `name: foo 468 version: 1.0 469 type: app 470 epoch: 1* 471 confinement: devmode` 472 snapPath := snaptest.MakeTestSnapWithFiles(c, yaml, nil) 473 474 snapf, err := snapfile.Open(snapPath) 475 c.Assert(err, IsNil) 476 477 info, err := snap.ReadInfoFromSnapFile(snapf, nil) 478 c.Assert(err, IsNil) 479 c.Check(info.InstanceName(), Equals, "foo") 480 c.Check(info.Version, Equals, "1.0") 481 c.Check(info.Type(), Equals, snap.TypeApp) 482 c.Check(info.Revision, Equals, snap.R(0)) 483 c.Check(info.Epoch.String(), Equals, "1*") 484 c.Check(info.Confinement, Equals, snap.DevModeConfinement) 485 c.Check(info.NeedsDevMode(), Equals, true) 486 c.Check(info.NeedsClassic(), Equals, false) 487 } 488 489 func (s *infoSuite) TestReadInfoFromClassicSnapFile(c *C) { 490 yaml := `name: foo 491 version: 1.0 492 type: app 493 confinement: classic` 494 snapPath := snaptest.MakeTestSnapWithFiles(c, yaml, nil) 495 496 snapf, err := snapfile.Open(snapPath) 497 c.Assert(err, IsNil) 498 499 info, err := snap.ReadInfoFromSnapFile(snapf, nil) 500 c.Assert(err, IsNil) 501 c.Check(info.InstanceName(), Equals, "foo") 502 c.Check(info.Version, Equals, "1.0") 503 c.Check(info.Type(), Equals, snap.TypeApp) 504 c.Check(info.Revision, Equals, snap.R(0)) 505 c.Check(info.Confinement, Equals, snap.ClassicConfinement) 506 c.Check(info.NeedsDevMode(), Equals, false) 507 c.Check(info.NeedsClassic(), Equals, true) 508 } 509 510 func (s *infoSuite) TestReadInfoFromSnapFileMissingEpoch(c *C) { 511 yaml := `name: foo 512 version: 1.0 513 type: app` 514 snapPath := snaptest.MakeTestSnapWithFiles(c, yaml, nil) 515 516 snapf, err := snapfile.Open(snapPath) 517 c.Assert(err, IsNil) 518 519 info, err := snap.ReadInfoFromSnapFile(snapf, nil) 520 c.Assert(err, IsNil) 521 c.Check(info.InstanceName(), Equals, "foo") 522 c.Check(info.Version, Equals, "1.0") 523 c.Check(info.Type(), Equals, snap.TypeApp) 524 c.Check(info.Revision, Equals, snap.R(0)) 525 c.Check(info.Epoch.String(), Equals, "0") // Defaults to 0 526 c.Check(info.Confinement, Equals, snap.StrictConfinement) 527 c.Check(info.NeedsDevMode(), Equals, false) 528 } 529 530 func (s *infoSuite) TestReadInfoFromSnapFileWithSideInfo(c *C) { 531 yaml := `name: foo 532 version: 1.0 533 type: app` 534 snapPath := snaptest.MakeTestSnapWithFiles(c, yaml, nil) 535 536 snapf, err := snapfile.Open(snapPath) 537 c.Assert(err, IsNil) 538 539 info, err := snap.ReadInfoFromSnapFile(snapf, &snap.SideInfo{ 540 RealName: "baz", 541 Revision: snap.R(42), 542 }) 543 c.Assert(err, IsNil) 544 c.Check(info.InstanceName(), Equals, "baz") 545 c.Check(info.Version, Equals, "1.0") 546 c.Check(info.Type(), Equals, snap.TypeApp) 547 c.Check(info.Revision, Equals, snap.R(42)) 548 } 549 550 func (s *infoSuite) TestReadInfoFromSnapFileValidates(c *C) { 551 yaml := `name: foo.bar 552 version: 1.0 553 type: app` 554 snapPath := makeTestSnap(c, yaml) 555 556 snapf, err := snapfile.Open(snapPath) 557 c.Assert(err, IsNil) 558 559 _, err = snap.ReadInfoFromSnapFile(snapf, nil) 560 c.Assert(err, ErrorMatches, `invalid snap name.*`) 561 } 562 563 func (s *infoSuite) TestReadInfoFromSnapFileCatchesInvalidType(c *C) { 564 yaml := `name: foo 565 version: 1.0 566 type: foo` 567 snapPath := makeTestSnap(c, yaml) 568 569 snapf, err := snapfile.Open(snapPath) 570 c.Assert(err, IsNil) 571 572 _, err = snap.ReadInfoFromSnapFile(snapf, nil) 573 c.Assert(err, ErrorMatches, ".*invalid snap type.*") 574 } 575 576 func (s *infoSuite) TestReadInfoFromSnapFileCatchesInvalidConfinement(c *C) { 577 yaml := `name: foo 578 version: 1.0 579 confinement: foo` 580 snapPath := makeTestSnap(c, yaml) 581 582 snapf, err := snapfile.Open(snapPath) 583 c.Assert(err, IsNil) 584 585 _, err = snap.ReadInfoFromSnapFile(snapf, nil) 586 c.Assert(err, ErrorMatches, ".*invalid confinement type.*") 587 } 588 589 func (s *infoSuite) TestAppEnvSimple(c *C) { 590 yaml := `name: foo 591 version: 1.0 592 type: app 593 environment: 594 global-k: global-v 595 apps: 596 foo: 597 environment: 598 app-k: app-v 599 ` 600 info, err := snap.InfoFromSnapYaml([]byte(yaml)) 601 c.Assert(err, IsNil) 602 603 c.Check(info.Apps["foo"].EnvChain(), DeepEquals, []osutil.ExpandableEnv{ 604 osutil.NewExpandableEnv("global-k", "global-v"), 605 osutil.NewExpandableEnv("app-k", "app-v"), 606 }) 607 } 608 609 func (s *infoSuite) TestAppEnvOverrideGlobal(c *C) { 610 yaml := `name: foo 611 version: 1.0 612 type: app 613 environment: 614 global-k: global-v 615 global-and-local: global-v 616 apps: 617 foo: 618 environment: 619 app-k: app-v 620 global-and-local: local-v 621 ` 622 info, err := snap.InfoFromSnapYaml([]byte(yaml)) 623 c.Assert(err, IsNil) 624 625 c.Check(info.Apps["foo"].EnvChain(), DeepEquals, []osutil.ExpandableEnv{ 626 osutil.NewExpandableEnv("global-k", "global-v", "global-and-local", "global-v"), 627 osutil.NewExpandableEnv("app-k", "app-v", "global-and-local", "local-v"), 628 }) 629 } 630 631 func (s *infoSuite) TestHookEnvSimple(c *C) { 632 yaml := `name: foo 633 version: 1.0 634 type: app 635 environment: 636 global-k: global-v 637 hooks: 638 foo: 639 environment: 640 app-k: app-v 641 ` 642 info, err := snap.InfoFromSnapYaml([]byte(yaml)) 643 c.Assert(err, IsNil) 644 645 c.Check(info.Hooks["foo"].EnvChain(), DeepEquals, []osutil.ExpandableEnv{ 646 osutil.NewExpandableEnv("global-k", "global-v"), 647 osutil.NewExpandableEnv("app-k", "app-v"), 648 }) 649 } 650 651 func (s *infoSuite) TestHookEnvOverrideGlobal(c *C) { 652 yaml := `name: foo 653 version: 1.0 654 type: app 655 environment: 656 global-k: global-v 657 global-and-local: global-v 658 hooks: 659 foo: 660 environment: 661 app-k: app-v 662 global-and-local: local-v 663 ` 664 info, err := snap.InfoFromSnapYaml([]byte(yaml)) 665 c.Assert(err, IsNil) 666 667 c.Check(info.Hooks["foo"].EnvChain(), DeepEquals, []osutil.ExpandableEnv{ 668 osutil.NewExpandableEnv("global-k", "global-v", "global-and-local", "global-v"), 669 osutil.NewExpandableEnv("app-k", "app-v", "global-and-local", "local-v"), 670 }) 671 } 672 673 func (s *infoSuite) TestSplitSnapApp(c *C) { 674 for _, t := range []struct { 675 in string 676 out []string 677 }{ 678 // normal cases 679 {"foo.bar", []string{"foo", "bar"}}, 680 {"foo.bar.baz", []string{"foo", "bar.baz"}}, 681 // special case, snapName == appName 682 {"foo", []string{"foo", "foo"}}, 683 // snap instance names 684 {"foo_instance.bar", []string{"foo_instance", "bar"}}, 685 {"foo_instance.bar.baz", []string{"foo_instance", "bar.baz"}}, 686 {"foo_instance", []string{"foo_instance", "foo"}}, 687 } { 688 snap, app := snap.SplitSnapApp(t.in) 689 c.Check([]string{snap, app}, DeepEquals, t.out) 690 } 691 } 692 693 func (s *infoSuite) TestJoinSnapApp(c *C) { 694 for _, t := range []struct { 695 in []string 696 out string 697 }{ 698 // normal cases 699 {[]string{"foo", "bar"}, "foo.bar"}, 700 {[]string{"foo", "bar-baz"}, "foo.bar-baz"}, 701 // special case, snapName == appName 702 {[]string{"foo", "foo"}, "foo"}, 703 // snap instance names 704 {[]string{"foo_instance", "bar"}, "foo_instance.bar"}, 705 {[]string{"foo_instance", "bar-baz"}, "foo_instance.bar-baz"}, 706 {[]string{"foo_instance", "foo"}, "foo_instance"}, 707 } { 708 snapApp := snap.JoinSnapApp(t.in[0], t.in[1]) 709 c.Check(snapApp, Equals, t.out) 710 } 711 } 712 713 func ExampleSplitSnapApp() { 714 fmt.Println(snap.SplitSnapApp("hello-world.env")) 715 // Output: hello-world env 716 } 717 718 func ExampleSplitSnapApp_short() { 719 fmt.Println(snap.SplitSnapApp("hello-world")) 720 // Output: hello-world hello-world 721 } 722 723 func (s *infoSuite) TestReadInfoFromSnapFileCatchesInvalidHook(c *C) { 724 yaml := `name: foo 725 version: 1.0 726 hooks: 727 123abc:` 728 snapPath := makeTestSnap(c, yaml) 729 730 snapf, err := snapfile.Open(snapPath) 731 c.Assert(err, IsNil) 732 733 _, err = snap.ReadInfoFromSnapFile(snapf, nil) 734 c.Assert(err, ErrorMatches, ".*invalid hook name.*") 735 } 736 737 func (s *infoSuite) TestReadInfoFromSnapFileCatchesInvalidImplicitHook(c *C) { 738 yaml := `name: foo 739 version: 1.0` 740 snapPath := snaptest.MakeTestSnapWithFiles(c, yaml, emptyHooks("123abc")) 741 742 snapf, err := snapfile.Open(snapPath) 743 c.Assert(err, IsNil) 744 745 _, err = snap.ReadInfoFromSnapFile(snapf, nil) 746 c.Assert(err, ErrorMatches, ".*invalid hook name.*") 747 } 748 749 func (s *infoSuite) checkInstalledSnapAndSnapFile(c *C, instanceName, yaml string, contents string, hooks []string, checker func(c *C, info *snap.Info)) { 750 // First check installed snap 751 sideInfo := &snap.SideInfo{Revision: snap.R(42)} 752 info0 := snaptest.MockSnapInstance(c, instanceName, yaml, sideInfo) 753 snaptest.PopulateDir(info0.MountDir(), emptyHooks(hooks...)) 754 info, err := snap.ReadInfo(info0.InstanceName(), sideInfo) 755 c.Check(err, IsNil) 756 checker(c, info) 757 758 // Now check snap file 759 snapPath := snaptest.MakeTestSnapWithFiles(c, yaml, emptyHooks(hooks...)) 760 snapf, err := snapfile.Open(snapPath) 761 c.Assert(err, IsNil) 762 info, err = snap.ReadInfoFromSnapFile(snapf, nil) 763 c.Check(err, IsNil) 764 checker(c, info) 765 } 766 767 func (s *infoSuite) TestReadInfoNoHooks(c *C) { 768 yaml := `name: foo 769 version: 1.0` 770 s.checkInstalledSnapAndSnapFile(c, "foo", yaml, "SNAP", nil, func(c *C, info *snap.Info) { 771 // Verify that no hooks were loaded for this snap 772 c.Check(info.Hooks, HasLen, 0) 773 }) 774 } 775 776 func (s *infoSuite) TestReadInfoSingleImplicitHook(c *C) { 777 yaml := `name: foo 778 version: 1.0` 779 s.checkInstalledSnapAndSnapFile(c, "foo", yaml, "SNAP", []string{"test-hook"}, func(c *C, info *snap.Info) { 780 // Verify that the `test-hook` hook has now been loaded, and that it has 781 // no associated plugs. 782 c.Check(info.Hooks, HasLen, 1) 783 verifyImplicitHook(c, info, "test-hook", nil) 784 }) 785 } 786 787 func (s *infoSuite) TestReadInfoMultipleImplicitHooks(c *C) { 788 yaml := `name: foo 789 version: 1.0` 790 s.checkInstalledSnapAndSnapFile(c, "foo", yaml, "SNAP", []string{"foo", "bar"}, func(c *C, info *snap.Info) { 791 // Verify that both hooks have now been loaded, and that neither have any 792 // associated plugs. 793 c.Check(info.Hooks, HasLen, 2) 794 verifyImplicitHook(c, info, "foo", nil) 795 verifyImplicitHook(c, info, "bar", nil) 796 }) 797 } 798 799 func (s *infoSuite) TestReadInfoInvalidImplicitHook(c *C) { 800 hookType := snap.NewHookType(regexp.MustCompile("foo")) 801 s.BaseTest.AddCleanup(snap.MockSupportedHookTypes([]*snap.HookType{hookType})) 802 803 yaml := `name: foo 804 version: 1.0` 805 s.checkInstalledSnapAndSnapFile(c, "foo", yaml, "SNAP", []string{"foo", "bar"}, func(c *C, info *snap.Info) { 806 // Verify that only foo has been loaded, not bar 807 c.Check(info.Hooks, HasLen, 1) 808 verifyImplicitHook(c, info, "foo", nil) 809 }) 810 } 811 812 func (s *infoSuite) TestReadInfoImplicitAndExplicitHooks(c *C) { 813 yaml := `name: foo 814 version: 1.0 815 hooks: 816 explicit: 817 plugs: [test-plug] 818 slots: [test-slot]` 819 s.checkInstalledSnapAndSnapFile(c, "foo", yaml, "SNAP", []string{"explicit", "implicit"}, func(c *C, info *snap.Info) { 820 // Verify that the `implicit` hook has now been loaded, and that it has 821 // no associated plugs. Also verify that the `explicit` hook is still 822 // valid. 823 c.Check(info.Hooks, HasLen, 2) 824 verifyImplicitHook(c, info, "implicit", nil) 825 verifyExplicitHook(c, info, "explicit", []string{"test-plug"}, []string{"test-slot"}) 826 }) 827 } 828 829 func (s *infoSuite) TestReadInfoExplicitHooks(c *C) { 830 yaml := `name: foo 831 version: 1.0 832 plugs: 833 test-plug: 834 slots: 835 test-slot: 836 hooks: 837 explicit: 838 ` 839 s.checkInstalledSnapAndSnapFile(c, "foo", yaml, "SNAP", []string{"explicit"}, func(c *C, info *snap.Info) { 840 c.Check(info.Hooks, HasLen, 1) 841 verifyExplicitHook(c, info, "explicit", []string{"test-plug"}, []string{"test-slot"}) 842 }) 843 } 844 845 func (s *infoSuite) TestReadInfoImplicitHookPlugWhenImplicitlyBoundToApp(c *C) { 846 yaml := `name: foo 847 version: 1.0 848 plugs: 849 test-plug: 850 apps: 851 app: 852 ` 853 s.checkInstalledSnapAndSnapFile(c, "foo", yaml, "SNAP", []string{"implicit"}, func(c *C, info *snap.Info) { 854 c.Check(info.Hooks, HasLen, 1) 855 verifyImplicitHook(c, info, "implicit", []string{"test-plug"}) 856 }) 857 } 858 859 func (s *infoSuite) TestReadInfoImplicitHookPlugWhenExplicitlyBoundToApp(c *C) { 860 yaml := `name: foo 861 version: 1.0 862 plugs: 863 test-plug: 864 apps: 865 app: 866 plugs: [test-plug] 867 ` 868 s.checkInstalledSnapAndSnapFile(c, "foo", yaml, "SNAP", []string{"implicit"}, func(c *C, info *snap.Info) { 869 c.Check(info.Hooks, HasLen, 1) 870 verifyImplicitHook(c, info, "implicit", nil) 871 }) 872 } 873 874 func (s *infoSuite) TestParallelInstanceReadInfoImplicitAndExplicitHooks(c *C) { 875 yaml := `name: foo 876 version: 1.0 877 hooks: 878 explicit: 879 plugs: [test-plug] 880 slots: [test-slot]` 881 s.checkInstalledSnapAndSnapFile(c, "foo_instance", yaml, "SNAP", []string{"explicit", "implicit"}, func(c *C, info *snap.Info) { 882 c.Check(info.Hooks, HasLen, 2) 883 verifyImplicitHook(c, info, "implicit", nil) 884 verifyExplicitHook(c, info, "explicit", []string{"test-plug"}, []string{"test-slot"}) 885 }) 886 } 887 888 func (s *infoSuite) TestReadInfoImplicitHookWithTopLevelPlugSlots(c *C) { 889 yaml1 := `name: snap-1 890 version: 1.0 891 plugs: 892 test-plug: 893 slots: 894 test-slot: 895 hooks: 896 explicit: 897 plugs: [test-plug,other-plug] 898 slots: [test-slot,other-slot] 899 ` 900 s.checkInstalledSnapAndSnapFile(c, "snap-1", yaml1, "SNAP", []string{"implicit"}, func(c *C, info *snap.Info) { 901 c.Check(info.Hooks, HasLen, 2) 902 implicitHook := info.Hooks["implicit"] 903 c.Assert(implicitHook, NotNil) 904 c.Assert(implicitHook.Explicit, Equals, false) 905 c.Assert(implicitHook.Plugs, HasLen, 0) 906 c.Assert(implicitHook.Slots, HasLen, 0) 907 908 c.Check(info.Plugs, HasLen, 2) 909 c.Check(info.Slots, HasLen, 2) 910 911 plug := info.Plugs["test-plug"] 912 c.Assert(plug, NotNil) 913 // implicit hook has not gained test-plug because it was already 914 // associated with an app or a hook (here with the hook called 915 // "explicit"). This is consistent with the hook called "implicit" 916 // having been defined in the YAML but devoid of any interface 917 // assignments. 918 c.Assert(implicitHook.Plugs["test-plug"], IsNil) 919 920 slot := info.Slots["test-slot"] 921 c.Assert(slot, NotNil) 922 c.Assert(implicitHook.Slots["test-slot"], IsNil) 923 924 explicitHook := info.Hooks["explicit"] 925 c.Assert(explicitHook, NotNil) 926 c.Assert(explicitHook.Explicit, Equals, true) 927 c.Assert(explicitHook.Plugs, HasLen, 2) 928 c.Assert(explicitHook.Slots, HasLen, 2) 929 930 plug = info.Plugs["test-plug"] 931 c.Assert(plug, NotNil) 932 c.Assert(explicitHook.Plugs["test-plug"], DeepEquals, plug) 933 934 slot = info.Slots["test-slot"] 935 c.Assert(slot, NotNil) 936 c.Assert(explicitHook.Slots["test-slot"], DeepEquals, slot) 937 }) 938 939 yaml2 := `name: snap-2 940 version: 1.0 941 plugs: 942 test-plug: 943 slots: 944 test-slot: 945 ` 946 s.checkInstalledSnapAndSnapFile(c, "snap-2", yaml2, "SNAP", []string{"implicit"}, func(c *C, info *snap.Info) { 947 c.Check(info.Hooks, HasLen, 1) 948 implicitHook := info.Hooks["implicit"] 949 c.Assert(implicitHook, NotNil) 950 c.Assert(implicitHook.Explicit, Equals, false) 951 c.Assert(implicitHook.Plugs, HasLen, 1) 952 c.Assert(implicitHook.Slots, HasLen, 1) 953 954 c.Check(info.Plugs, HasLen, 1) 955 c.Check(info.Slots, HasLen, 1) 956 957 plug := info.Plugs["test-plug"] 958 c.Assert(plug, NotNil) 959 c.Assert(implicitHook.Plugs["test-plug"], DeepEquals, plug) 960 961 slot := info.Slots["test-slot"] 962 c.Assert(slot, NotNil) 963 c.Assert(implicitHook.Slots["test-slot"], DeepEquals, slot) 964 }) 965 966 yaml3 := `name: snap-3 967 version: 1.0 968 plugs: 969 test-plug: 970 slots: 971 test-slot: 972 ` 973 s.checkInstalledSnapAndSnapFile(c, "snap-3", yaml3, "SNAP", []string{"implicit-1", "implicit-2"}, func(c *C, info *snap.Info) { 974 c.Check(info.Hooks, HasLen, 2) 975 implicit1Hook := info.Hooks["implicit-1"] 976 c.Assert(implicit1Hook, NotNil) 977 c.Assert(implicit1Hook.Explicit, Equals, false) 978 c.Assert(implicit1Hook.Plugs, HasLen, 1) 979 c.Assert(implicit1Hook.Slots, HasLen, 1) 980 981 implicit2Hook := info.Hooks["implicit-2"] 982 c.Assert(implicit2Hook, NotNil) 983 c.Assert(implicit2Hook.Explicit, Equals, false) 984 c.Assert(implicit2Hook.Plugs, HasLen, 1) 985 c.Assert(implicit2Hook.Slots, HasLen, 1) 986 987 c.Check(info.Plugs, HasLen, 1) 988 c.Check(info.Slots, HasLen, 1) 989 990 plug := info.Plugs["test-plug"] 991 c.Assert(plug, NotNil) 992 c.Assert(implicit1Hook.Plugs["test-plug"], DeepEquals, plug) 993 c.Assert(implicit2Hook.Plugs["test-plug"], DeepEquals, plug) 994 995 slot := info.Slots["test-slot"] 996 c.Assert(slot, NotNil) 997 c.Assert(implicit1Hook.Slots["test-slot"], DeepEquals, slot) 998 c.Assert(implicit2Hook.Slots["test-slot"], DeepEquals, slot) 999 }) 1000 1001 } 1002 1003 func verifyImplicitHook(c *C, info *snap.Info, hookName string, plugNames []string) { 1004 hook := info.Hooks[hookName] 1005 c.Assert(hook, NotNil, Commentf("Expected hooks to contain %q", hookName)) 1006 c.Check(hook.Name, Equals, hookName) 1007 1008 if len(plugNames) == 0 { 1009 c.Check(hook.Plugs, IsNil) 1010 } 1011 1012 for _, plugName := range plugNames { 1013 // Verify that the HookInfo and PlugInfo point to each other 1014 plug := hook.Plugs[plugName] 1015 c.Assert(plug, NotNil, Commentf("Expected hook plugs to contain %q", plugName)) 1016 c.Check(plug.Name, Equals, plugName) 1017 c.Check(plug.Hooks, HasLen, 1) 1018 hook = plug.Hooks[hookName] 1019 c.Assert(hook, NotNil, Commentf("Expected plug to be associated with hook %q", hookName)) 1020 c.Check(hook.Name, Equals, hookName) 1021 1022 // Verify also that the hook plug made it into info.Plugs 1023 c.Check(info.Plugs[plugName], DeepEquals, plug) 1024 } 1025 } 1026 1027 func verifyExplicitHook(c *C, info *snap.Info, hookName string, plugNames []string, slotNames []string) { 1028 hook := info.Hooks[hookName] 1029 c.Assert(hook, NotNil, Commentf("Expected hooks to contain %q", hookName)) 1030 c.Check(hook.Name, Equals, hookName) 1031 c.Check(hook.Plugs, HasLen, len(plugNames)) 1032 c.Check(hook.Slots, HasLen, len(slotNames)) 1033 1034 for _, plugName := range plugNames { 1035 // Verify that the HookInfo and PlugInfo point to each other 1036 plug := hook.Plugs[plugName] 1037 c.Assert(plug, NotNil, Commentf("Expected hook plugs to contain %q", plugName)) 1038 c.Check(plug.Name, Equals, plugName) 1039 c.Check(plug.Hooks, HasLen, 1) 1040 hook = plug.Hooks[hookName] 1041 c.Assert(hook, NotNil, Commentf("Expected plug to be associated with hook %q", hookName)) 1042 c.Check(hook.Name, Equals, hookName) 1043 1044 // Verify also that the hook plug made it into info.Plugs 1045 c.Check(info.Plugs[plugName], DeepEquals, plug) 1046 } 1047 1048 for _, slotName := range slotNames { 1049 // Verify that the HookInfo and SlotInfo point to each other 1050 slot := hook.Slots[slotName] 1051 c.Assert(slot, NotNil, Commentf("Expected hook slots to contain %q", slotName)) 1052 c.Check(slot.Name, Equals, slotName) 1053 c.Check(slot.Hooks, HasLen, 1) 1054 hook = slot.Hooks[hookName] 1055 c.Assert(hook, NotNil, Commentf("Expected slot to be associated with hook %q", hookName)) 1056 c.Check(hook.Name, Equals, hookName) 1057 1058 // Verify also that the hook plug made it into info.Slots 1059 c.Check(info.Slots[slotName], DeepEquals, slot) 1060 } 1061 1062 } 1063 1064 func (s *infoSuite) TestPlaceInfoRevision(c *C) { 1065 info := snap.MinimalPlaceInfo("name", snap.R("1")) 1066 c.Check(info.SnapRevision(), Equals, snap.R("1")) 1067 } 1068 1069 func (s *infoSuite) TestMinimalInfoDirAndFileMethods(c *C) { 1070 dirs.SetRootDir("") 1071 info := snap.MinimalPlaceInfo("name", snap.R("1")) 1072 s.testDirAndFileMethods(c, info) 1073 } 1074 1075 func (s *infoSuite) TestDirAndFileMethods(c *C) { 1076 dirs.SetRootDir("") 1077 info := &snap.Info{SuggestedName: "name"} 1078 info.SideInfo = snap.SideInfo{Revision: snap.R(1)} 1079 s.testDirAndFileMethods(c, info) 1080 } 1081 1082 func (s *infoSuite) testDirAndFileMethods(c *C, info snap.PlaceInfo) { 1083 c.Check(info.MountDir(), Equals, fmt.Sprintf("%s/name/1", dirs.SnapMountDir)) 1084 c.Check(info.MountFile(), Equals, "/var/lib/snapd/snaps/name_1.snap") 1085 c.Check(info.HooksDir(), Equals, fmt.Sprintf("%s/name/1/meta/hooks", dirs.SnapMountDir)) 1086 c.Check(info.DataDir(), Equals, "/var/snap/name/1") 1087 c.Check(info.UserDataDir("/home/bob"), Equals, "/home/bob/snap/name/1") 1088 c.Check(info.UserCommonDataDir("/home/bob"), Equals, "/home/bob/snap/name/common") 1089 c.Check(info.CommonDataDir(), Equals, "/var/snap/name/common") 1090 c.Check(info.UserXdgRuntimeDir(12345), Equals, "/run/user/12345/snap.name") 1091 // XXX: Those are actually a globs, not directories 1092 c.Check(info.DataHomeDir(), Equals, "/home/*/snap/name/1") 1093 c.Check(info.CommonDataHomeDir(), Equals, "/home/*/snap/name/common") 1094 c.Check(info.XdgRuntimeDirs(), Equals, "/run/user/*/snap.name") 1095 } 1096 1097 func (s *infoSuite) TestMinimalInfoDirAndFileMethodsParallelInstall(c *C) { 1098 dirs.SetRootDir("") 1099 info := snap.MinimalPlaceInfo("name_instance", snap.R("1")) 1100 s.testInstanceDirAndFileMethods(c, info) 1101 } 1102 1103 func (s *infoSuite) TestDirAndFileMethodsParallelInstall(c *C) { 1104 dirs.SetRootDir("") 1105 info := &snap.Info{SuggestedName: "name", InstanceKey: "instance"} 1106 info.SideInfo = snap.SideInfo{Revision: snap.R(1)} 1107 s.testInstanceDirAndFileMethods(c, info) 1108 } 1109 1110 func (s *infoSuite) testInstanceDirAndFileMethods(c *C, info snap.PlaceInfo) { 1111 c.Check(info.MountDir(), Equals, fmt.Sprintf("%s/name_instance/1", dirs.SnapMountDir)) 1112 c.Check(info.MountFile(), Equals, "/var/lib/snapd/snaps/name_instance_1.snap") 1113 c.Check(info.HooksDir(), Equals, fmt.Sprintf("%s/name_instance/1/meta/hooks", dirs.SnapMountDir)) 1114 c.Check(info.DataDir(), Equals, "/var/snap/name_instance/1") 1115 c.Check(info.UserDataDir("/home/bob"), Equals, "/home/bob/snap/name_instance/1") 1116 c.Check(info.UserCommonDataDir("/home/bob"), Equals, "/home/bob/snap/name_instance/common") 1117 c.Check(info.CommonDataDir(), Equals, "/var/snap/name_instance/common") 1118 c.Check(info.UserXdgRuntimeDir(12345), Equals, "/run/user/12345/snap.name_instance") 1119 // XXX: Those are actually a globs, not directories 1120 c.Check(info.DataHomeDir(), Equals, "/home/*/snap/name_instance/1") 1121 c.Check(info.CommonDataHomeDir(), Equals, "/home/*/snap/name_instance/common") 1122 c.Check(info.XdgRuntimeDirs(), Equals, "/run/user/*/snap.name_instance") 1123 } 1124 1125 func BenchmarkTestParsePlaceInfoFromSnapFileName(b *testing.B) { 1126 for n := 0; n < b.N; n++ { 1127 for _, sn := range []string{ 1128 "core_21.snap", 1129 "kernel_41.snap", 1130 "some-long-kernel-name-kernel_82.snap", 1131 "what-is-this-core_111.snap", 1132 } { 1133 snap.ParsePlaceInfoFromSnapFileName(sn) 1134 } 1135 } 1136 } 1137 1138 func (s *infoSuite) TestParsePlaceInfoFromSnapFileName(c *C) { 1139 tt := []struct { 1140 sn string 1141 name string 1142 rev string 1143 expectErr string 1144 }{ 1145 {sn: "", expectErr: "empty snap file name"}, 1146 {sn: "name", expectErr: `snap file name "name" has invalid format \(missing '_'\)`}, 1147 {sn: "name_", expectErr: `cannot parse revision in snap file name "name_": invalid snap revision: ""`}, 1148 {sn: "name__", expectErr: "too many '_' in snap file name"}, 1149 {sn: "_name.snap", expectErr: `snap file name \"_name.snap\" has invalid format \(no snap name before '_'\)`}, 1150 {sn: "name_key.snap", expectErr: `cannot parse revision in snap file name "name_key.snap": invalid snap revision: "key"`}, 1151 {sn: "name.snap", expectErr: `snap file name "name.snap" has invalid format \(missing '_'\)`}, 1152 {sn: "name_12.snap", name: "name", rev: "12"}, 1153 {sn: "name_key_12.snap", expectErr: "too many '_' in snap file name"}, 1154 } 1155 for _, t := range tt { 1156 p, err := snap.ParsePlaceInfoFromSnapFileName(t.sn) 1157 if t.expectErr != "" { 1158 c.Check(err, ErrorMatches, t.expectErr) 1159 } else { 1160 c.Check(p.SnapName(), Equals, t.name) 1161 c.Check(p.SnapRevision(), Equals, snap.R(t.rev)) 1162 } 1163 } 1164 } 1165 1166 func makeFakeDesktopFile(c *C, name, content string) string { 1167 df := filepath.Join(dirs.SnapDesktopFilesDir, name) 1168 err := os.MkdirAll(filepath.Dir(df), 0755) 1169 c.Assert(err, IsNil) 1170 err = ioutil.WriteFile(df, []byte(content), 0644) 1171 c.Assert(err, IsNil) 1172 return df 1173 } 1174 1175 func (s *infoSuite) TestAppDesktopFile(c *C) { 1176 snaptest.MockSnap(c, sampleYaml, &snap.SideInfo{}) 1177 snapInfo, err := snap.ReadInfo("sample", &snap.SideInfo{}) 1178 c.Assert(err, IsNil) 1179 1180 c.Check(snapInfo.InstanceName(), Equals, "sample") 1181 c.Check(snapInfo.Apps["app"].DesktopFile(), Matches, `.*/var/lib/snapd/desktop/applications/sample_app.desktop`) 1182 c.Check(snapInfo.Apps["sample"].DesktopFile(), Matches, `.*/var/lib/snapd/desktop/applications/sample_sample.desktop`) 1183 c.Check(snapInfo.DesktopPrefix(), Equals, "sample") 1184 1185 // snap with instance key 1186 snapInfo.InstanceKey = "instance" 1187 c.Check(snapInfo.InstanceName(), Equals, "sample_instance") 1188 c.Check(snapInfo.Apps["app"].DesktopFile(), Matches, `.*/var/lib/snapd/desktop/applications/sample\+instance_app.desktop`) 1189 c.Check(snapInfo.Apps["sample"].DesktopFile(), Matches, `.*/var/lib/snapd/desktop/applications/sample\+instance_sample.desktop`) 1190 c.Check(snapInfo.DesktopPrefix(), Equals, "sample+instance") 1191 } 1192 1193 const coreSnapYaml = `name: core 1194 version: 0 1195 type: os 1196 plugs: 1197 network-bind: 1198 core-support: 1199 ` 1200 1201 // reading snap via ReadInfoFromSnapFile renames clashing core plugs 1202 func (s *infoSuite) TestReadInfoFromSnapFileRenamesCorePlus(c *C) { 1203 snapPath := snaptest.MakeTestSnapWithFiles(c, coreSnapYaml, nil) 1204 1205 snapf, err := snapfile.Open(snapPath) 1206 c.Assert(err, IsNil) 1207 1208 info, err := snap.ReadInfoFromSnapFile(snapf, nil) 1209 c.Assert(err, IsNil) 1210 c.Check(info.Plugs["network-bind"], IsNil) 1211 c.Check(info.Plugs["core-support"], IsNil) 1212 c.Check(info.Plugs["network-bind-plug"], NotNil) 1213 c.Check(info.Plugs["core-support-plug"], NotNil) 1214 } 1215 1216 // reading snap via ReadInfo renames clashing core plugs 1217 func (s *infoSuite) TestReadInfoRenamesCorePlugs(c *C) { 1218 si := &snap.SideInfo{Revision: snap.R(42), RealName: "core"} 1219 snaptest.MockSnap(c, coreSnapYaml, si) 1220 info, err := snap.ReadInfo("core", si) 1221 c.Assert(err, IsNil) 1222 c.Check(info.Plugs["network-bind"], IsNil) 1223 c.Check(info.Plugs["core-support"], IsNil) 1224 c.Check(info.Plugs["network-bind-plug"], NotNil) 1225 c.Check(info.Plugs["core-support-plug"], NotNil) 1226 } 1227 1228 // reading snap via InfoFromSnapYaml renames clashing core plugs 1229 func (s *infoSuite) TestInfoFromSnapYamlRenamesCorePlugs(c *C) { 1230 info, err := snap.InfoFromSnapYaml([]byte(coreSnapYaml)) 1231 c.Assert(err, IsNil) 1232 c.Check(info.Plugs["network-bind"], IsNil) 1233 c.Check(info.Plugs["core-support"], IsNil) 1234 c.Check(info.Plugs["network-bind-plug"], NotNil) 1235 c.Check(info.Plugs["core-support-plug"], NotNil) 1236 } 1237 1238 func (s *infoSuite) TestInfoServices(c *C) { 1239 info, err := snap.InfoFromSnapYaml([]byte(`name: pans 1240 apps: 1241 svc1: 1242 daemon: potato 1243 svc2: 1244 daemon: no 1245 app1: 1246 app2: 1247 `)) 1248 c.Assert(err, IsNil) 1249 svcNames := []string{} 1250 svcs := info.Services() 1251 for i := range svcs { 1252 svcNames = append(svcNames, svcs[i].ServiceName()) 1253 } 1254 sort.Strings(svcNames) 1255 c.Check(svcNames, DeepEquals, []string{ 1256 "snap.pans.svc1.service", 1257 "snap.pans.svc2.service", 1258 }) 1259 1260 // snap with instance 1261 info.InstanceKey = "instance" 1262 svcNames = []string{} 1263 for i := range info.Services() { 1264 svcNames = append(svcNames, svcs[i].ServiceName()) 1265 } 1266 sort.Strings(svcNames) 1267 c.Check(svcNames, DeepEquals, []string{ 1268 "snap.pans_instance.svc1.service", 1269 "snap.pans_instance.svc2.service", 1270 }) 1271 } 1272 1273 func (s *infoSuite) TestAppInfoIsService(c *C) { 1274 info, err := snap.InfoFromSnapYaml([]byte(`name: pans 1275 apps: 1276 svc1: 1277 daemon: potato 1278 svc2: 1279 daemon: no 1280 svc3: 1281 daemon: simple 1282 daemon-scope: user 1283 app1: 1284 app2: 1285 `)) 1286 c.Assert(err, IsNil) 1287 1288 svc := info.Apps["svc1"] 1289 c.Check(svc.IsService(), Equals, true) 1290 c.Check(svc.DaemonScope, Equals, snap.SystemDaemon) 1291 c.Check(svc.ServiceName(), Equals, "snap.pans.svc1.service") 1292 c.Check(svc.ServiceFile(), Equals, dirs.GlobalRootDir+"/etc/systemd/system/snap.pans.svc1.service") 1293 1294 c.Check(info.Apps["svc2"].IsService(), Equals, true) 1295 userSvc := info.Apps["svc3"] 1296 c.Check(userSvc.IsService(), Equals, true) 1297 c.Check(userSvc.DaemonScope, Equals, snap.UserDaemon) 1298 c.Check(userSvc.ServiceName(), Equals, "snap.pans.svc3.service") 1299 c.Check(userSvc.ServiceFile(), Equals, dirs.GlobalRootDir+"/etc/systemd/user/snap.pans.svc3.service") 1300 c.Check(info.Apps["app1"].IsService(), Equals, false) 1301 c.Check(info.Apps["app1"].IsService(), Equals, false) 1302 1303 // snap with instance key 1304 info.InstanceKey = "instance" 1305 c.Check(svc.ServiceName(), Equals, "snap.pans_instance.svc1.service") 1306 c.Check(svc.ServiceFile(), Equals, dirs.GlobalRootDir+"/etc/systemd/system/snap.pans_instance.svc1.service") 1307 c.Check(userSvc.ServiceName(), Equals, "snap.pans_instance.svc3.service") 1308 c.Check(userSvc.ServiceFile(), Equals, dirs.GlobalRootDir+"/etc/systemd/user/snap.pans_instance.svc3.service") 1309 } 1310 1311 func (s *infoSuite) TestAppInfoStringer(c *C) { 1312 info, err := snap.InfoFromSnapYaml([]byte(`name: asnap 1313 apps: 1314 one: 1315 daemon: simple 1316 `)) 1317 c.Assert(err, IsNil) 1318 c.Check(fmt.Sprintf("%q", info.Apps["one"].String()), Equals, `"asnap.one"`) 1319 } 1320 1321 func (s *infoSuite) TestSocketFile(c *C) { 1322 info, err := snap.InfoFromSnapYaml([]byte(`name: pans 1323 apps: 1324 app1: 1325 daemon: true 1326 sockets: 1327 sock1: 1328 listen-stream: /tmp/sock1.socket 1329 `)) 1330 1331 c.Assert(err, IsNil) 1332 1333 app := info.Apps["app1"] 1334 socket := app.Sockets["sock1"] 1335 c.Check(socket.File(), Equals, dirs.GlobalRootDir+"/etc/systemd/system/snap.pans.app1.sock1.socket") 1336 1337 // snap with instance key 1338 info.InstanceKey = "instance" 1339 c.Check(socket.File(), Equals, dirs.GlobalRootDir+"/etc/systemd/system/snap.pans_instance.app1.sock1.socket") 1340 } 1341 1342 func (s *infoSuite) TestTimerFile(c *C) { 1343 info, err := snap.InfoFromSnapYaml([]byte(`name: pans 1344 apps: 1345 app1: 1346 daemon: true 1347 timer: mon,10:00-12:00 1348 `)) 1349 1350 c.Assert(err, IsNil) 1351 1352 app := info.Apps["app1"] 1353 timerFile := app.Timer.File() 1354 c.Check(timerFile, Equals, dirs.GlobalRootDir+"/etc/systemd/system/snap.pans.app1.timer") 1355 c.Check(strings.TrimSuffix(app.ServiceFile(), ".service")+".timer", Equals, timerFile) 1356 1357 // snap with instance key 1358 info.InstanceKey = "instance" 1359 c.Check(app.Timer.File(), Equals, dirs.GlobalRootDir+"/etc/systemd/system/snap.pans_instance.app1.timer") 1360 } 1361 1362 func (s *infoSuite) TestLayoutParsing(c *C) { 1363 info, err := snap.InfoFromSnapYaml([]byte(`name: layout-demo 1364 layout: 1365 /usr: 1366 bind: $SNAP/usr 1367 /mytmp: 1368 type: tmpfs 1369 mode: 1777 1370 /mylink: 1371 symlink: /link/target 1372 `)) 1373 c.Assert(err, IsNil) 1374 1375 layout := info.Layout 1376 c.Assert(layout, NotNil) 1377 c.Check(layout["/usr"], DeepEquals, &snap.Layout{ 1378 Snap: info, 1379 Path: "/usr", 1380 User: "root", 1381 Group: "root", 1382 Mode: 0755, 1383 Bind: "$SNAP/usr", 1384 }) 1385 c.Check(layout["/mytmp"], DeepEquals, &snap.Layout{ 1386 Snap: info, 1387 Path: "/mytmp", 1388 Type: "tmpfs", 1389 User: "root", 1390 Group: "root", 1391 Mode: 01777, 1392 }) 1393 c.Check(layout["/mylink"], DeepEquals, &snap.Layout{ 1394 Snap: info, 1395 Path: "/mylink", 1396 User: "root", 1397 Group: "root", 1398 Mode: 0755, 1399 Symlink: "/link/target", 1400 }) 1401 } 1402 1403 func (s *infoSuite) TestPlugInfoString(c *C) { 1404 plug := &snap.PlugInfo{Snap: &snap.Info{SuggestedName: "snap"}, Name: "plug"} 1405 c.Assert(plug.String(), Equals, "snap:plug") 1406 } 1407 1408 func (s *infoSuite) TestSlotInfoString(c *C) { 1409 slot := &snap.SlotInfo{Snap: &snap.Info{SuggestedName: "snap"}, Name: "slot"} 1410 c.Assert(slot.String(), Equals, "snap:slot") 1411 } 1412 1413 func (s *infoSuite) TestPlugInfoAttr(c *C) { 1414 var val string 1415 var intVal int 1416 1417 plug := &snap.PlugInfo{Snap: &snap.Info{SuggestedName: "snap"}, Name: "plug", Interface: "interface", Attrs: map[string]interface{}{"key": "value", "number": int(123)}} 1418 c.Assert(plug.Attr("key", &val), IsNil) 1419 c.Check(val, Equals, "value") 1420 1421 c.Assert(plug.Attr("number", &intVal), IsNil) 1422 c.Check(intVal, Equals, 123) 1423 1424 c.Check(plug.Attr("key", &intVal), ErrorMatches, `snap "snap" has interface "interface" with invalid value type for "key" attribute`) 1425 c.Check(plug.Attr("unknown", &val), ErrorMatches, `snap "snap" does not have attribute "unknown" for interface "interface"`) 1426 c.Check(plug.Attr("key", intVal), ErrorMatches, `internal error: cannot get "key" attribute of interface "interface" with non-pointer value`) 1427 } 1428 1429 func (s *infoSuite) TestSlotInfoAttr(c *C) { 1430 var val string 1431 var intVal int 1432 1433 slot := &snap.SlotInfo{Snap: &snap.Info{SuggestedName: "snap"}, Name: "plug", Interface: "interface", Attrs: map[string]interface{}{"key": "value", "number": int(123)}} 1434 1435 c.Assert(slot.Attr("key", &val), IsNil) 1436 c.Check(val, Equals, "value") 1437 1438 c.Assert(slot.Attr("number", &intVal), IsNil) 1439 c.Check(intVal, Equals, 123) 1440 1441 c.Check(slot.Attr("key", &intVal), ErrorMatches, `snap "snap" has interface "interface" with invalid value type for "key" attribute`) 1442 c.Check(slot.Attr("unknown", &val), ErrorMatches, `snap "snap" does not have attribute "unknown" for interface "interface"`) 1443 c.Check(slot.Attr("key", intVal), ErrorMatches, `internal error: cannot get "key" attribute of interface "interface" with non-pointer value`) 1444 } 1445 1446 func (s *infoSuite) TestDottedPathSlot(c *C) { 1447 attrs := map[string]interface{}{ 1448 "nested": map[string]interface{}{ 1449 "foo": "bar", 1450 }, 1451 } 1452 1453 slot := &snap.SlotInfo{Attrs: attrs} 1454 c.Assert(slot, NotNil) 1455 1456 v, ok := slot.Lookup("nested.foo") 1457 c.Assert(ok, Equals, true) 1458 c.Assert(v, Equals, "bar") 1459 1460 v, ok = slot.Lookup("nested") 1461 c.Assert(ok, Equals, true) 1462 c.Assert(v, DeepEquals, map[string]interface{}{ 1463 "foo": "bar", 1464 }) 1465 1466 _, ok = slot.Lookup("x") 1467 c.Assert(ok, Equals, false) 1468 1469 _, ok = slot.Lookup("..") 1470 c.Assert(ok, Equals, false) 1471 1472 _, ok = slot.Lookup("nested.foo.x") 1473 c.Assert(ok, Equals, false) 1474 1475 _, ok = slot.Lookup("nested.x") 1476 c.Assert(ok, Equals, false) 1477 } 1478 1479 func (s *infoSuite) TestDottedPathPlug(c *C) { 1480 attrs := map[string]interface{}{ 1481 "nested": map[string]interface{}{ 1482 "foo": "bar", 1483 }, 1484 } 1485 1486 plug := &snap.PlugInfo{Attrs: attrs} 1487 c.Assert(plug, NotNil) 1488 1489 v, ok := plug.Lookup("nested") 1490 c.Assert(ok, Equals, true) 1491 c.Assert(v, DeepEquals, map[string]interface{}{ 1492 "foo": "bar", 1493 }) 1494 1495 v, ok = plug.Lookup("nested.foo") 1496 c.Assert(ok, Equals, true) 1497 c.Assert(v, Equals, "bar") 1498 1499 _, ok = plug.Lookup("x") 1500 c.Assert(ok, Equals, false) 1501 1502 _, ok = plug.Lookup("..") 1503 c.Assert(ok, Equals, false) 1504 1505 _, ok = plug.Lookup("nested.foo.x") 1506 c.Assert(ok, Equals, false) 1507 } 1508 1509 func (s *infoSuite) TestDefaultContentProviders(c *C) { 1510 info, err := snap.InfoFromSnapYaml([]byte(yamlNeedDf)) 1511 c.Assert(err, IsNil) 1512 1513 plugs := make([]*snap.PlugInfo, 0, len(info.Plugs)) 1514 for _, plug := range info.Plugs { 1515 plugs = append(plugs, plug) 1516 } 1517 1518 dps := snap.DefaultContentProviders(plugs) 1519 c.Check(dps, DeepEquals, map[string][]string{"gtk-common-themes": {"gtk-3-themes", "icon-themes"}}) 1520 } 1521 1522 func (s *infoSuite) TestExpandSnapVariables(c *C) { 1523 dirs.SetRootDir("") 1524 info, err := snap.InfoFromSnapYaml([]byte(`name: foo`)) 1525 c.Assert(err, IsNil) 1526 info.Revision = snap.R(42) 1527 c.Assert(info.ExpandSnapVariables("$SNAP/stuff"), Equals, "/snap/foo/42/stuff") 1528 c.Assert(info.ExpandSnapVariables("$SNAP_DATA/stuff"), Equals, "/var/snap/foo/42/stuff") 1529 c.Assert(info.ExpandSnapVariables("$SNAP_COMMON/stuff"), Equals, "/var/snap/foo/common/stuff") 1530 c.Assert(info.ExpandSnapVariables("$GARBAGE/rocks"), Equals, "/rocks") 1531 1532 info.InstanceKey = "instance" 1533 // Despite setting the instance key the variables expand to the same 1534 // value as before. This is because they are used from inside the mount 1535 // namespace of the instantiated snap where the mount backend will 1536 // ensure that the regular (non-instance) paths contain 1537 // instance-specific code and data. 1538 c.Assert(info.ExpandSnapVariables("$SNAP/stuff"), Equals, "/snap/foo/42/stuff") 1539 c.Assert(info.ExpandSnapVariables("$SNAP_DATA/stuff"), Equals, "/var/snap/foo/42/stuff") 1540 c.Assert(info.ExpandSnapVariables("$SNAP_COMMON/stuff"), Equals, "/var/snap/foo/common/stuff") 1541 c.Assert(info.ExpandSnapVariables("$GARBAGE/rocks"), Equals, "/rocks") 1542 } 1543 1544 func (s *infoSuite) TestStopModeTypeKillMode(c *C) { 1545 for _, t := range []struct { 1546 stopMode string 1547 killall bool 1548 }{ 1549 {"", true}, 1550 {"sigterm", false}, 1551 {"sigterm-all", true}, 1552 {"sighup", false}, 1553 {"sighup-all", true}, 1554 {"sigusr1", false}, 1555 {"sigusr1-all", true}, 1556 {"sigusr2", false}, 1557 {"sigusr2-all", true}, 1558 } { 1559 c.Check(snap.StopModeType(t.stopMode).KillAll(), Equals, t.killall, Commentf("wrong KillAll for %v", t.stopMode)) 1560 } 1561 } 1562 1563 func (s *infoSuite) TestStopModeTypeKillSignal(c *C) { 1564 for _, t := range []struct { 1565 stopMode string 1566 killSig string 1567 }{ 1568 {"", ""}, 1569 {"sigterm", "SIGTERM"}, 1570 {"sigterm-all", "SIGTERM"}, 1571 {"sighup", "SIGHUP"}, 1572 {"sighup-all", "SIGHUP"}, 1573 {"sigusr1", "SIGUSR1"}, 1574 {"sigusr1-all", "SIGUSR1"}, 1575 {"sigusr2", "SIGUSR2"}, 1576 {"sigusr2-all", "SIGUSR2"}, 1577 } { 1578 c.Check(snap.StopModeType(t.stopMode).KillSignal(), Equals, t.killSig) 1579 } 1580 } 1581 1582 func (s *infoSuite) TestSplitInstanceName(c *C) { 1583 snapName, instanceKey := snap.SplitInstanceName("foo_bar") 1584 c.Check(snapName, Equals, "foo") 1585 c.Check(instanceKey, Equals, "bar") 1586 1587 snapName, instanceKey = snap.SplitInstanceName("foo") 1588 c.Check(snapName, Equals, "foo") 1589 c.Check(instanceKey, Equals, "") 1590 1591 // all following instance names are invalid 1592 1593 snapName, instanceKey = snap.SplitInstanceName("_bar") 1594 c.Check(snapName, Equals, "") 1595 c.Check(instanceKey, Equals, "bar") 1596 1597 snapName, instanceKey = snap.SplitInstanceName("foo___bar_bar") 1598 c.Check(snapName, Equals, "foo") 1599 c.Check(instanceKey, Equals, "__bar_bar") 1600 1601 snapName, instanceKey = snap.SplitInstanceName("") 1602 c.Check(snapName, Equals, "") 1603 c.Check(instanceKey, Equals, "") 1604 } 1605 1606 func (s *infoSuite) TestInstanceSnapName(c *C) { 1607 c.Check(snap.InstanceSnap("foo_bar"), Equals, "foo") 1608 c.Check(snap.InstanceSnap("foo"), Equals, "foo") 1609 1610 c.Check(snap.InstanceName("foo", "bar"), Equals, "foo_bar") 1611 c.Check(snap.InstanceName("foo", ""), Equals, "foo") 1612 } 1613 1614 func (s *infoSuite) TestInstanceNameInSnapInfo(c *C) { 1615 info := &snap.Info{ 1616 SuggestedName: "snap-name", 1617 InstanceKey: "foo", 1618 } 1619 1620 c.Check(info.InstanceName(), Equals, "snap-name_foo") 1621 c.Check(info.SnapName(), Equals, "snap-name") 1622 1623 info.InstanceKey = "" 1624 c.Check(info.InstanceName(), Equals, "snap-name") 1625 c.Check(info.SnapName(), Equals, "snap-name") 1626 } 1627 1628 func (s *infoSuite) TestIsActive(c *C) { 1629 info1 := snaptest.MockSnap(c, sampleYaml, &snap.SideInfo{Revision: snap.R(1)}) 1630 info2 := snaptest.MockSnap(c, sampleYaml, &snap.SideInfo{Revision: snap.R(2)}) 1631 // no current -> not active 1632 c.Check(info1.IsActive(), Equals, false) 1633 c.Check(info2.IsActive(), Equals, false) 1634 1635 mountdir := info1.MountDir() 1636 dir, rev := filepath.Split(mountdir) 1637 c.Assert(os.MkdirAll(dir, 0755), IsNil) 1638 cur := filepath.Join(dir, "current") 1639 c.Assert(os.Symlink(rev, cur), IsNil) 1640 1641 // is current -> is active 1642 c.Check(info1.IsActive(), Equals, true) 1643 c.Check(info2.IsActive(), Equals, false) 1644 } 1645 1646 func (s *infoSuite) TestInfoTypeSnapdBackwardCompatibility(c *C) { 1647 const snapdYaml = ` 1648 name: snapd 1649 type: app 1650 version: 1 1651 ` 1652 snapInfo := snaptest.MockSnap(c, sampleYaml, &snap.SideInfo{Revision: snap.R(1), SnapID: "PMrrV4ml8uWuEUDBT8dSGnKUYbevVhc4"}) 1653 c.Check(snapInfo.Type(), Equals, snap.TypeSnapd) 1654 } 1655 1656 func (s *infoSuite) TestDirAndFileHelpers(c *C) { 1657 dirs.SetRootDir("") 1658 1659 c.Check(snap.MountDir("name", snap.R(1)), Equals, fmt.Sprintf("%s/name/1", dirs.SnapMountDir)) 1660 c.Check(snap.MountFile("name", snap.R(1)), Equals, "/var/lib/snapd/snaps/name_1.snap") 1661 c.Check(snap.HooksDir("name", snap.R(1)), Equals, fmt.Sprintf("%s/name/1/meta/hooks", dirs.SnapMountDir)) 1662 c.Check(snap.DataDir("name", snap.R(1)), Equals, "/var/snap/name/1") 1663 c.Check(snap.CommonDataDir("name"), Equals, "/var/snap/name/common") 1664 c.Check(snap.UserDataDir("/home/bob", "name", snap.R(1)), Equals, "/home/bob/snap/name/1") 1665 c.Check(snap.UserCommonDataDir("/home/bob", "name"), Equals, "/home/bob/snap/name/common") 1666 c.Check(snap.UserXdgRuntimeDir(12345, "name"), Equals, "/run/user/12345/snap.name") 1667 c.Check(snap.UserSnapDir("/home/bob", "name"), Equals, "/home/bob/snap/name") 1668 1669 c.Check(snap.MountDir("name_instance", snap.R(1)), Equals, fmt.Sprintf("%s/name_instance/1", dirs.SnapMountDir)) 1670 c.Check(snap.MountFile("name_instance", snap.R(1)), Equals, "/var/lib/snapd/snaps/name_instance_1.snap") 1671 c.Check(snap.HooksDir("name_instance", snap.R(1)), Equals, fmt.Sprintf("%s/name_instance/1/meta/hooks", dirs.SnapMountDir)) 1672 c.Check(snap.DataDir("name_instance", snap.R(1)), Equals, "/var/snap/name_instance/1") 1673 c.Check(snap.CommonDataDir("name_instance"), Equals, "/var/snap/name_instance/common") 1674 c.Check(snap.UserDataDir("/home/bob", "name_instance", snap.R(1)), Equals, "/home/bob/snap/name_instance/1") 1675 c.Check(snap.UserCommonDataDir("/home/bob", "name_instance"), Equals, "/home/bob/snap/name_instance/common") 1676 c.Check(snap.UserXdgRuntimeDir(12345, "name_instance"), Equals, "/run/user/12345/snap.name_instance") 1677 c.Check(snap.UserSnapDir("/home/bob", "name_instance"), Equals, "/home/bob/snap/name_instance") 1678 } 1679 1680 func (s *infoSuite) TestSortByType(c *C) { 1681 infos := []*snap.Info{ 1682 {SuggestedName: "app1", SnapType: "app"}, 1683 {SuggestedName: "os1", SnapType: "os"}, 1684 {SuggestedName: "base1", SnapType: "base"}, 1685 {SuggestedName: "gadget1", SnapType: "gadget"}, 1686 {SuggestedName: "kernel1", SnapType: "kernel"}, 1687 {SuggestedName: "app2", SnapType: "app"}, 1688 {SuggestedName: "os2", SnapType: "os"}, 1689 {SuggestedName: "snapd", SnapType: "snapd"}, 1690 {SuggestedName: "base2", SnapType: "base"}, 1691 {SuggestedName: "gadget2", SnapType: "gadget"}, 1692 {SuggestedName: "kernel2", SnapType: "kernel"}, 1693 } 1694 sort.Stable(snap.ByType(infos)) 1695 1696 c.Check(infos, DeepEquals, []*snap.Info{ 1697 {SuggestedName: "snapd", SnapType: "snapd"}, 1698 {SuggestedName: "os1", SnapType: "os"}, 1699 {SuggestedName: "os2", SnapType: "os"}, 1700 {SuggestedName: "kernel1", SnapType: "kernel"}, 1701 {SuggestedName: "kernel2", SnapType: "kernel"}, 1702 {SuggestedName: "base1", SnapType: "base"}, 1703 {SuggestedName: "base2", SnapType: "base"}, 1704 {SuggestedName: "gadget1", SnapType: "gadget"}, 1705 {SuggestedName: "gadget2", SnapType: "gadget"}, 1706 {SuggestedName: "app1", SnapType: "app"}, 1707 {SuggestedName: "app2", SnapType: "app"}, 1708 }) 1709 } 1710 1711 func (s *infoSuite) TestSortByTypeAgain(c *C) { 1712 core := &snap.Info{SnapType: snap.TypeOS} 1713 base := &snap.Info{SnapType: snap.TypeBase} 1714 app := &snap.Info{SnapType: snap.TypeApp} 1715 snapd := &snap.Info{} 1716 snapd.SideInfo = snap.SideInfo{RealName: "snapd"} 1717 1718 byType := func(snaps ...*snap.Info) []*snap.Info { 1719 sort.Stable(snap.ByType(snaps)) 1720 return snaps 1721 } 1722 1723 c.Check(byType(base, core), DeepEquals, []*snap.Info{core, base}) 1724 c.Check(byType(app, core), DeepEquals, []*snap.Info{core, app}) 1725 c.Check(byType(app, base), DeepEquals, []*snap.Info{base, app}) 1726 c.Check(byType(app, base, core), DeepEquals, []*snap.Info{core, base, app}) 1727 c.Check(byType(app, core, base), DeepEquals, []*snap.Info{core, base, app}) 1728 1729 c.Check(byType(app, core, base, snapd), DeepEquals, []*snap.Info{snapd, core, base, app}) 1730 c.Check(byType(app, snapd, core, base), DeepEquals, []*snap.Info{snapd, core, base, app}) 1731 } 1732 1733 func (s *infoSuite) TestMedia(c *C) { 1734 c.Check(snap.MediaInfos{}.IconURL(), Equals, "") 1735 1736 media := snap.MediaInfos{ 1737 { 1738 Type: "screenshot", 1739 URL: "https://example.com/shot1.svg", 1740 }, { 1741 Type: "icon", 1742 URL: "https://example.com/icon.png", 1743 }, { 1744 Type: "screenshot", 1745 URL: "https://example.com/shot2.svg", 1746 Width: 42, 1747 Height: 17, 1748 }, 1749 } 1750 1751 c.Check(media.IconURL(), Equals, "https://example.com/icon.png") 1752 } 1753 1754 func (s *infoSuite) TestSortApps(c *C) { 1755 tcs := []struct { 1756 err string 1757 apps []*snap.AppInfo 1758 sorted []string 1759 }{{ 1760 apps: []*snap.AppInfo{ 1761 {Name: "bar", Before: []string{"baz"}}, 1762 {Name: "foo"}, 1763 }, 1764 sorted: []string{"bar", "foo"}, 1765 }, { 1766 apps: []*snap.AppInfo{ 1767 {Name: "bar", Before: []string{"foo"}}, 1768 {Name: "foo", Before: []string{"baz"}}, 1769 }, 1770 sorted: []string{"bar", "foo"}, 1771 }, { 1772 apps: []*snap.AppInfo{ 1773 {Name: "bar", Before: []string{"foo"}}, 1774 }, 1775 sorted: []string{"bar"}, 1776 }, { 1777 apps: []*snap.AppInfo{ 1778 {Name: "bar", After: []string{"foo"}}, 1779 }, 1780 sorted: []string{"bar"}, 1781 }, { 1782 apps: []*snap.AppInfo{ 1783 {Name: "bar", Before: []string{"baz"}}, 1784 {Name: "baz", After: []string{"bar", "foo"}}, 1785 {Name: "foo"}, 1786 }, 1787 sorted: []string{"bar", "foo", "baz"}, 1788 }, { 1789 apps: []*snap.AppInfo{ 1790 {Name: "foo", After: []string{"bar", "zed"}}, 1791 {Name: "bar", Before: []string{"foo"}}, 1792 {Name: "baz", After: []string{"foo"}}, 1793 {Name: "zed"}, 1794 }, 1795 sorted: []string{"bar", "zed", "foo", "baz"}, 1796 }, { 1797 apps: []*snap.AppInfo{ 1798 {Name: "foo", After: []string{"baz"}}, 1799 {Name: "bar", Before: []string{"baz"}}, 1800 {Name: "baz"}, 1801 {Name: "zed", After: []string{"foo", "bar", "baz"}}, 1802 }, 1803 sorted: []string{"bar", "baz", "foo", "zed"}, 1804 }, { 1805 apps: []*snap.AppInfo{ 1806 {Name: "foo", Before: []string{"bar"}, After: []string{"zed"}}, 1807 {Name: "bar", Before: []string{"baz"}}, 1808 {Name: "baz", Before: []string{"zed"}}, 1809 {Name: "zed"}, 1810 }, 1811 err: `applications are part of a before/after cycle: ((foo|bar|baz|zed)(, )?){4}`, 1812 }, { 1813 apps: []*snap.AppInfo{ 1814 {Name: "foo", Before: []string{"bar"}}, 1815 {Name: "bar", Before: []string{"foo"}}, 1816 {Name: "baz", Before: []string{"foo"}, After: []string{"bar"}}, 1817 }, 1818 err: `applications are part of a before/after cycle: ((foo|bar|baz)(, )?){3}`, 1819 }, { 1820 apps: []*snap.AppInfo{ 1821 {Name: "baz", After: []string{"bar"}}, 1822 {Name: "foo"}, 1823 {Name: "bar", After: []string{"foo"}}, 1824 }, 1825 sorted: []string{"foo", "bar", "baz"}, 1826 }} 1827 for _, tc := range tcs { 1828 sorted, err := snap.SortServices(tc.apps) 1829 if tc.err != "" { 1830 c.Assert(err, ErrorMatches, tc.err) 1831 } else { 1832 c.Assert(err, IsNil) 1833 c.Assert(sorted, HasLen, len(tc.sorted)) 1834 sortedNames := make([]string, len(sorted)) 1835 for i, app := range sorted { 1836 sortedNames[i] = app.Name 1837 } 1838 c.Assert(sortedNames, DeepEquals, tc.sorted) 1839 } 1840 } 1841 } 1842 1843 func (s *infoSuite) TestSortAppInfoBySnapApp(c *C) { 1844 snap1 := &snap.Info{SuggestedName: "snapa"} 1845 snap2 := &snap.Info{SuggestedName: "snapb"} 1846 infos := []*snap.AppInfo{ 1847 {Snap: snap1, Name: "b"}, 1848 {Snap: snap2, Name: "b"}, 1849 {Snap: snap1, Name: "a"}, 1850 {Snap: snap2, Name: "a"}, 1851 } 1852 sort.Stable(snap.AppInfoBySnapApp(infos)) 1853 1854 c.Check(infos, DeepEquals, []*snap.AppInfo{ 1855 {Snap: snap1, Name: "a"}, 1856 {Snap: snap1, Name: "b"}, 1857 {Snap: snap2, Name: "a"}, 1858 {Snap: snap2, Name: "b"}, 1859 }) 1860 }