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