github.com/ubuntu-core/snappy@v0.0.0-20210827154228-9e584df982bb/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 (s *infoSuite) TestAppDesktopFile(c *C) { 1167 snaptest.MockSnap(c, sampleYaml, &snap.SideInfo{}) 1168 snapInfo, err := snap.ReadInfo("sample", &snap.SideInfo{}) 1169 c.Assert(err, IsNil) 1170 1171 c.Check(snapInfo.InstanceName(), Equals, "sample") 1172 c.Check(snapInfo.Apps["app"].DesktopFile(), Matches, `.*/var/lib/snapd/desktop/applications/sample_app.desktop`) 1173 c.Check(snapInfo.Apps["sample"].DesktopFile(), Matches, `.*/var/lib/snapd/desktop/applications/sample_sample.desktop`) 1174 c.Check(snapInfo.DesktopPrefix(), Equals, "sample") 1175 1176 // snap with instance key 1177 snapInfo.InstanceKey = "instance" 1178 c.Check(snapInfo.InstanceName(), Equals, "sample_instance") 1179 c.Check(snapInfo.Apps["app"].DesktopFile(), Matches, `.*/var/lib/snapd/desktop/applications/sample\+instance_app.desktop`) 1180 c.Check(snapInfo.Apps["sample"].DesktopFile(), Matches, `.*/var/lib/snapd/desktop/applications/sample\+instance_sample.desktop`) 1181 c.Check(snapInfo.DesktopPrefix(), Equals, "sample+instance") 1182 } 1183 1184 const coreSnapYaml = `name: core 1185 version: 0 1186 type: os 1187 plugs: 1188 network-bind: 1189 core-support: 1190 ` 1191 1192 // reading snap via ReadInfoFromSnapFile renames clashing core plugs 1193 func (s *infoSuite) TestReadInfoFromSnapFileRenamesCorePlus(c *C) { 1194 snapPath := snaptest.MakeTestSnapWithFiles(c, coreSnapYaml, nil) 1195 1196 snapf, err := snapfile.Open(snapPath) 1197 c.Assert(err, IsNil) 1198 1199 info, err := snap.ReadInfoFromSnapFile(snapf, nil) 1200 c.Assert(err, IsNil) 1201 c.Check(info.Plugs["network-bind"], IsNil) 1202 c.Check(info.Plugs["core-support"], IsNil) 1203 c.Check(info.Plugs["network-bind-plug"], NotNil) 1204 c.Check(info.Plugs["core-support-plug"], NotNil) 1205 } 1206 1207 // reading snap via ReadInfo renames clashing core plugs 1208 func (s *infoSuite) TestReadInfoRenamesCorePlugs(c *C) { 1209 si := &snap.SideInfo{Revision: snap.R(42), RealName: "core"} 1210 snaptest.MockSnap(c, coreSnapYaml, si) 1211 info, err := snap.ReadInfo("core", si) 1212 c.Assert(err, IsNil) 1213 c.Check(info.Plugs["network-bind"], IsNil) 1214 c.Check(info.Plugs["core-support"], IsNil) 1215 c.Check(info.Plugs["network-bind-plug"], NotNil) 1216 c.Check(info.Plugs["core-support-plug"], NotNil) 1217 } 1218 1219 // reading snap via InfoFromSnapYaml renames clashing core plugs 1220 func (s *infoSuite) TestInfoFromSnapYamlRenamesCorePlugs(c *C) { 1221 info, err := snap.InfoFromSnapYaml([]byte(coreSnapYaml)) 1222 c.Assert(err, IsNil) 1223 c.Check(info.Plugs["network-bind"], IsNil) 1224 c.Check(info.Plugs["core-support"], IsNil) 1225 c.Check(info.Plugs["network-bind-plug"], NotNil) 1226 c.Check(info.Plugs["core-support-plug"], NotNil) 1227 } 1228 1229 func (s *infoSuite) TestInfoServices(c *C) { 1230 info, err := snap.InfoFromSnapYaml([]byte(`name: pans 1231 apps: 1232 svc1: 1233 daemon: potato 1234 svc2: 1235 daemon: no 1236 app1: 1237 app2: 1238 `)) 1239 c.Assert(err, IsNil) 1240 svcNames := []string{} 1241 svcs := info.Services() 1242 for i := range svcs { 1243 svcNames = append(svcNames, svcs[i].ServiceName()) 1244 } 1245 sort.Strings(svcNames) 1246 c.Check(svcNames, DeepEquals, []string{ 1247 "snap.pans.svc1.service", 1248 "snap.pans.svc2.service", 1249 }) 1250 1251 // snap with instance 1252 info.InstanceKey = "instance" 1253 svcNames = []string{} 1254 for i := range info.Services() { 1255 svcNames = append(svcNames, svcs[i].ServiceName()) 1256 } 1257 sort.Strings(svcNames) 1258 c.Check(svcNames, DeepEquals, []string{ 1259 "snap.pans_instance.svc1.service", 1260 "snap.pans_instance.svc2.service", 1261 }) 1262 } 1263 1264 func (s *infoSuite) TestAppInfoIsService(c *C) { 1265 info, err := snap.InfoFromSnapYaml([]byte(`name: pans 1266 apps: 1267 svc1: 1268 daemon: potato 1269 svc2: 1270 daemon: no 1271 svc3: 1272 daemon: simple 1273 daemon-scope: user 1274 app1: 1275 app2: 1276 `)) 1277 c.Assert(err, IsNil) 1278 1279 svc := info.Apps["svc1"] 1280 c.Check(svc.IsService(), Equals, true) 1281 c.Check(svc.DaemonScope, Equals, snap.SystemDaemon) 1282 c.Check(svc.ServiceName(), Equals, "snap.pans.svc1.service") 1283 c.Check(svc.ServiceFile(), Equals, dirs.GlobalRootDir+"/etc/systemd/system/snap.pans.svc1.service") 1284 1285 c.Check(info.Apps["svc2"].IsService(), Equals, true) 1286 userSvc := info.Apps["svc3"] 1287 c.Check(userSvc.IsService(), Equals, true) 1288 c.Check(userSvc.DaemonScope, Equals, snap.UserDaemon) 1289 c.Check(userSvc.ServiceName(), Equals, "snap.pans.svc3.service") 1290 c.Check(userSvc.ServiceFile(), Equals, dirs.GlobalRootDir+"/etc/systemd/user/snap.pans.svc3.service") 1291 c.Check(info.Apps["app1"].IsService(), Equals, false) 1292 c.Check(info.Apps["app1"].IsService(), Equals, false) 1293 1294 // snap with instance key 1295 info.InstanceKey = "instance" 1296 c.Check(svc.ServiceName(), Equals, "snap.pans_instance.svc1.service") 1297 c.Check(svc.ServiceFile(), Equals, dirs.GlobalRootDir+"/etc/systemd/system/snap.pans_instance.svc1.service") 1298 c.Check(userSvc.ServiceName(), Equals, "snap.pans_instance.svc3.service") 1299 c.Check(userSvc.ServiceFile(), Equals, dirs.GlobalRootDir+"/etc/systemd/user/snap.pans_instance.svc3.service") 1300 } 1301 1302 func (s *infoSuite) TestAppInfoStringer(c *C) { 1303 info, err := snap.InfoFromSnapYaml([]byte(`name: asnap 1304 apps: 1305 one: 1306 daemon: simple 1307 `)) 1308 c.Assert(err, IsNil) 1309 c.Check(fmt.Sprintf("%q", info.Apps["one"].String()), Equals, `"asnap.one"`) 1310 } 1311 1312 func (s *infoSuite) TestSocketFile(c *C) { 1313 info, err := snap.InfoFromSnapYaml([]byte(`name: pans 1314 apps: 1315 app1: 1316 daemon: true 1317 sockets: 1318 sock1: 1319 listen-stream: /tmp/sock1.socket 1320 `)) 1321 1322 c.Assert(err, IsNil) 1323 1324 app := info.Apps["app1"] 1325 socket := app.Sockets["sock1"] 1326 c.Check(socket.File(), Equals, dirs.GlobalRootDir+"/etc/systemd/system/snap.pans.app1.sock1.socket") 1327 1328 // snap with instance key 1329 info.InstanceKey = "instance" 1330 c.Check(socket.File(), Equals, dirs.GlobalRootDir+"/etc/systemd/system/snap.pans_instance.app1.sock1.socket") 1331 } 1332 1333 func (s *infoSuite) TestTimerFile(c *C) { 1334 info, err := snap.InfoFromSnapYaml([]byte(`name: pans 1335 apps: 1336 app1: 1337 daemon: true 1338 timer: mon,10:00-12:00 1339 `)) 1340 1341 c.Assert(err, IsNil) 1342 1343 app := info.Apps["app1"] 1344 timerFile := app.Timer.File() 1345 c.Check(timerFile, Equals, dirs.GlobalRootDir+"/etc/systemd/system/snap.pans.app1.timer") 1346 c.Check(strings.TrimSuffix(app.ServiceFile(), ".service")+".timer", Equals, timerFile) 1347 1348 // snap with instance key 1349 info.InstanceKey = "instance" 1350 c.Check(app.Timer.File(), Equals, dirs.GlobalRootDir+"/etc/systemd/system/snap.pans_instance.app1.timer") 1351 } 1352 1353 func (s *infoSuite) TestLayoutParsing(c *C) { 1354 info, err := snap.InfoFromSnapYaml([]byte(`name: layout-demo 1355 layout: 1356 /usr: 1357 bind: $SNAP/usr 1358 /mytmp: 1359 type: tmpfs 1360 mode: 1777 1361 /mylink: 1362 symlink: /link/target 1363 `)) 1364 c.Assert(err, IsNil) 1365 1366 layout := info.Layout 1367 c.Assert(layout, NotNil) 1368 c.Check(layout["/usr"], DeepEquals, &snap.Layout{ 1369 Snap: info, 1370 Path: "/usr", 1371 User: "root", 1372 Group: "root", 1373 Mode: 0755, 1374 Bind: "$SNAP/usr", 1375 }) 1376 c.Check(layout["/mytmp"], DeepEquals, &snap.Layout{ 1377 Snap: info, 1378 Path: "/mytmp", 1379 Type: "tmpfs", 1380 User: "root", 1381 Group: "root", 1382 Mode: 01777, 1383 }) 1384 c.Check(layout["/mylink"], DeepEquals, &snap.Layout{ 1385 Snap: info, 1386 Path: "/mylink", 1387 User: "root", 1388 Group: "root", 1389 Mode: 0755, 1390 Symlink: "/link/target", 1391 }) 1392 } 1393 1394 func (s *infoSuite) TestPlugInfoString(c *C) { 1395 plug := &snap.PlugInfo{Snap: &snap.Info{SuggestedName: "snap"}, Name: "plug"} 1396 c.Assert(plug.String(), Equals, "snap:plug") 1397 } 1398 1399 func (s *infoSuite) TestSlotInfoString(c *C) { 1400 slot := &snap.SlotInfo{Snap: &snap.Info{SuggestedName: "snap"}, Name: "slot"} 1401 c.Assert(slot.String(), Equals, "snap:slot") 1402 } 1403 1404 func (s *infoSuite) TestPlugInfoAttr(c *C) { 1405 var val string 1406 var intVal int 1407 1408 plug := &snap.PlugInfo{Snap: &snap.Info{SuggestedName: "snap"}, Name: "plug", Interface: "interface", Attrs: map[string]interface{}{"key": "value", "number": int(123)}} 1409 c.Assert(plug.Attr("key", &val), IsNil) 1410 c.Check(val, Equals, "value") 1411 1412 c.Assert(plug.Attr("number", &intVal), IsNil) 1413 c.Check(intVal, Equals, 123) 1414 1415 c.Check(plug.Attr("key", &intVal), ErrorMatches, `snap "snap" has interface "interface" with invalid value type for "key" attribute`) 1416 c.Check(plug.Attr("unknown", &val), ErrorMatches, `snap "snap" does not have attribute "unknown" for interface "interface"`) 1417 c.Check(plug.Attr("key", intVal), ErrorMatches, `internal error: cannot get "key" attribute of interface "interface" with non-pointer value`) 1418 } 1419 1420 func (s *infoSuite) TestSlotInfoAttr(c *C) { 1421 var val string 1422 var intVal int 1423 1424 slot := &snap.SlotInfo{Snap: &snap.Info{SuggestedName: "snap"}, Name: "plug", Interface: "interface", Attrs: map[string]interface{}{"key": "value", "number": int(123)}} 1425 1426 c.Assert(slot.Attr("key", &val), IsNil) 1427 c.Check(val, Equals, "value") 1428 1429 c.Assert(slot.Attr("number", &intVal), IsNil) 1430 c.Check(intVal, Equals, 123) 1431 1432 c.Check(slot.Attr("key", &intVal), ErrorMatches, `snap "snap" has interface "interface" with invalid value type for "key" attribute`) 1433 c.Check(slot.Attr("unknown", &val), ErrorMatches, `snap "snap" does not have attribute "unknown" for interface "interface"`) 1434 c.Check(slot.Attr("key", intVal), ErrorMatches, `internal error: cannot get "key" attribute of interface "interface" with non-pointer value`) 1435 } 1436 1437 func (s *infoSuite) TestDottedPathSlot(c *C) { 1438 attrs := map[string]interface{}{ 1439 "nested": map[string]interface{}{ 1440 "foo": "bar", 1441 }, 1442 } 1443 1444 slot := &snap.SlotInfo{Attrs: attrs} 1445 c.Assert(slot, NotNil) 1446 1447 v, ok := slot.Lookup("nested.foo") 1448 c.Assert(ok, Equals, true) 1449 c.Assert(v, Equals, "bar") 1450 1451 v, ok = slot.Lookup("nested") 1452 c.Assert(ok, Equals, true) 1453 c.Assert(v, DeepEquals, map[string]interface{}{ 1454 "foo": "bar", 1455 }) 1456 1457 _, ok = slot.Lookup("x") 1458 c.Assert(ok, Equals, false) 1459 1460 _, ok = slot.Lookup("..") 1461 c.Assert(ok, Equals, false) 1462 1463 _, ok = slot.Lookup("nested.foo.x") 1464 c.Assert(ok, Equals, false) 1465 1466 _, ok = slot.Lookup("nested.x") 1467 c.Assert(ok, Equals, false) 1468 } 1469 1470 func (s *infoSuite) TestDottedPathPlug(c *C) { 1471 attrs := map[string]interface{}{ 1472 "nested": map[string]interface{}{ 1473 "foo": "bar", 1474 }, 1475 } 1476 1477 plug := &snap.PlugInfo{Attrs: attrs} 1478 c.Assert(plug, NotNil) 1479 1480 v, ok := plug.Lookup("nested") 1481 c.Assert(ok, Equals, true) 1482 c.Assert(v, DeepEquals, map[string]interface{}{ 1483 "foo": "bar", 1484 }) 1485 1486 v, ok = plug.Lookup("nested.foo") 1487 c.Assert(ok, Equals, true) 1488 c.Assert(v, Equals, "bar") 1489 1490 _, ok = plug.Lookup("x") 1491 c.Assert(ok, Equals, false) 1492 1493 _, ok = plug.Lookup("..") 1494 c.Assert(ok, Equals, false) 1495 1496 _, ok = plug.Lookup("nested.foo.x") 1497 c.Assert(ok, Equals, false) 1498 } 1499 1500 func (s *infoSuite) TestDefaultContentProviders(c *C) { 1501 info, err := snap.InfoFromSnapYaml([]byte(yamlNeedDf)) 1502 c.Assert(err, IsNil) 1503 1504 plugs := make([]*snap.PlugInfo, 0, len(info.Plugs)) 1505 for _, plug := range info.Plugs { 1506 plugs = append(plugs, plug) 1507 } 1508 1509 dps := snap.DefaultContentProviders(plugs) 1510 c.Check(dps, DeepEquals, map[string][]string{"gtk-common-themes": {"gtk-3-themes", "icon-themes"}}) 1511 } 1512 1513 func (s *infoSuite) TestExpandSnapVariables(c *C) { 1514 dirs.SetRootDir("") 1515 info, err := snap.InfoFromSnapYaml([]byte(`name: foo`)) 1516 c.Assert(err, IsNil) 1517 info.Revision = snap.R(42) 1518 c.Assert(info.ExpandSnapVariables("$SNAP/stuff"), Equals, "/snap/foo/42/stuff") 1519 c.Assert(info.ExpandSnapVariables("$SNAP_DATA/stuff"), Equals, "/var/snap/foo/42/stuff") 1520 c.Assert(info.ExpandSnapVariables("$SNAP_COMMON/stuff"), Equals, "/var/snap/foo/common/stuff") 1521 c.Assert(info.ExpandSnapVariables("$GARBAGE/rocks"), Equals, "/rocks") 1522 1523 info.InstanceKey = "instance" 1524 // Despite setting the instance key the variables expand to the same 1525 // value as before. This is because they are used from inside the mount 1526 // namespace of the instantiated snap where the mount backend will 1527 // ensure that the regular (non-instance) paths contain 1528 // instance-specific code and data. 1529 c.Assert(info.ExpandSnapVariables("$SNAP/stuff"), Equals, "/snap/foo/42/stuff") 1530 c.Assert(info.ExpandSnapVariables("$SNAP_DATA/stuff"), Equals, "/var/snap/foo/42/stuff") 1531 c.Assert(info.ExpandSnapVariables("$SNAP_COMMON/stuff"), Equals, "/var/snap/foo/common/stuff") 1532 c.Assert(info.ExpandSnapVariables("$GARBAGE/rocks"), Equals, "/rocks") 1533 } 1534 1535 func (s *infoSuite) TestStopModeTypeKillMode(c *C) { 1536 for _, t := range []struct { 1537 stopMode string 1538 killall bool 1539 }{ 1540 {"", true}, 1541 {"sigterm", false}, 1542 {"sigterm-all", true}, 1543 {"sighup", false}, 1544 {"sighup-all", true}, 1545 {"sigusr1", false}, 1546 {"sigusr1-all", true}, 1547 {"sigusr2", false}, 1548 {"sigusr2-all", true}, 1549 } { 1550 c.Check(snap.StopModeType(t.stopMode).KillAll(), Equals, t.killall, Commentf("wrong KillAll for %v", t.stopMode)) 1551 } 1552 } 1553 1554 func (s *infoSuite) TestStopModeTypeKillSignal(c *C) { 1555 for _, t := range []struct { 1556 stopMode string 1557 killSig string 1558 }{ 1559 {"", ""}, 1560 {"sigterm", "SIGTERM"}, 1561 {"sigterm-all", "SIGTERM"}, 1562 {"sighup", "SIGHUP"}, 1563 {"sighup-all", "SIGHUP"}, 1564 {"sigusr1", "SIGUSR1"}, 1565 {"sigusr1-all", "SIGUSR1"}, 1566 {"sigusr2", "SIGUSR2"}, 1567 {"sigusr2-all", "SIGUSR2"}, 1568 } { 1569 c.Check(snap.StopModeType(t.stopMode).KillSignal(), Equals, t.killSig) 1570 } 1571 } 1572 1573 func (s *infoSuite) TestSplitInstanceName(c *C) { 1574 snapName, instanceKey := snap.SplitInstanceName("foo_bar") 1575 c.Check(snapName, Equals, "foo") 1576 c.Check(instanceKey, Equals, "bar") 1577 1578 snapName, instanceKey = snap.SplitInstanceName("foo") 1579 c.Check(snapName, Equals, "foo") 1580 c.Check(instanceKey, Equals, "") 1581 1582 // all following instance names are invalid 1583 1584 snapName, instanceKey = snap.SplitInstanceName("_bar") 1585 c.Check(snapName, Equals, "") 1586 c.Check(instanceKey, Equals, "bar") 1587 1588 snapName, instanceKey = snap.SplitInstanceName("foo___bar_bar") 1589 c.Check(snapName, Equals, "foo") 1590 c.Check(instanceKey, Equals, "__bar_bar") 1591 1592 snapName, instanceKey = snap.SplitInstanceName("") 1593 c.Check(snapName, Equals, "") 1594 c.Check(instanceKey, Equals, "") 1595 } 1596 1597 func (s *infoSuite) TestInstanceSnapName(c *C) { 1598 c.Check(snap.InstanceSnap("foo_bar"), Equals, "foo") 1599 c.Check(snap.InstanceSnap("foo"), Equals, "foo") 1600 1601 c.Check(snap.InstanceName("foo", "bar"), Equals, "foo_bar") 1602 c.Check(snap.InstanceName("foo", ""), Equals, "foo") 1603 } 1604 1605 func (s *infoSuite) TestInstanceNameInSnapInfo(c *C) { 1606 info := &snap.Info{ 1607 SuggestedName: "snap-name", 1608 InstanceKey: "foo", 1609 } 1610 1611 c.Check(info.InstanceName(), Equals, "snap-name_foo") 1612 c.Check(info.SnapName(), Equals, "snap-name") 1613 1614 info.InstanceKey = "" 1615 c.Check(info.InstanceName(), Equals, "snap-name") 1616 c.Check(info.SnapName(), Equals, "snap-name") 1617 } 1618 1619 func (s *infoSuite) TestIsActive(c *C) { 1620 info1 := snaptest.MockSnap(c, sampleYaml, &snap.SideInfo{Revision: snap.R(1)}) 1621 info2 := snaptest.MockSnap(c, sampleYaml, &snap.SideInfo{Revision: snap.R(2)}) 1622 // no current -> not active 1623 c.Check(info1.IsActive(), Equals, false) 1624 c.Check(info2.IsActive(), Equals, false) 1625 1626 mountdir := info1.MountDir() 1627 dir, rev := filepath.Split(mountdir) 1628 c.Assert(os.MkdirAll(dir, 0755), IsNil) 1629 cur := filepath.Join(dir, "current") 1630 c.Assert(os.Symlink(rev, cur), IsNil) 1631 1632 // is current -> is active 1633 c.Check(info1.IsActive(), Equals, true) 1634 c.Check(info2.IsActive(), Equals, false) 1635 } 1636 1637 func (s *infoSuite) TestInfoTypeSnapdBackwardCompatibility(c *C) { 1638 const snapdYaml = ` 1639 name: snapd 1640 type: app 1641 version: 1 1642 ` 1643 snapInfo := snaptest.MockSnap(c, snapdYaml, &snap.SideInfo{Revision: snap.R(1), SnapID: "PMrrV4ml8uWuEUDBT8dSGnKUYbevVhc4"}) 1644 c.Check(snapInfo.Type(), Equals, snap.TypeSnapd) 1645 } 1646 1647 func (s *infoSuite) TestDirAndFileHelpers(c *C) { 1648 dirs.SetRootDir("") 1649 1650 c.Check(snap.MountDir("name", snap.R(1)), Equals, fmt.Sprintf("%s/name/1", dirs.SnapMountDir)) 1651 c.Check(snap.MountFile("name", snap.R(1)), Equals, "/var/lib/snapd/snaps/name_1.snap") 1652 c.Check(snap.HooksDir("name", snap.R(1)), Equals, fmt.Sprintf("%s/name/1/meta/hooks", dirs.SnapMountDir)) 1653 c.Check(snap.DataDir("name", snap.R(1)), Equals, "/var/snap/name/1") 1654 c.Check(snap.CommonDataDir("name"), Equals, "/var/snap/name/common") 1655 c.Check(snap.UserDataDir("/home/bob", "name", snap.R(1)), Equals, "/home/bob/snap/name/1") 1656 c.Check(snap.UserCommonDataDir("/home/bob", "name"), Equals, "/home/bob/snap/name/common") 1657 c.Check(snap.UserXdgRuntimeDir(12345, "name"), Equals, "/run/user/12345/snap.name") 1658 c.Check(snap.UserSnapDir("/home/bob", "name"), Equals, "/home/bob/snap/name") 1659 1660 c.Check(snap.MountDir("name_instance", snap.R(1)), Equals, fmt.Sprintf("%s/name_instance/1", dirs.SnapMountDir)) 1661 c.Check(snap.MountFile("name_instance", snap.R(1)), Equals, "/var/lib/snapd/snaps/name_instance_1.snap") 1662 c.Check(snap.HooksDir("name_instance", snap.R(1)), Equals, fmt.Sprintf("%s/name_instance/1/meta/hooks", dirs.SnapMountDir)) 1663 c.Check(snap.DataDir("name_instance", snap.R(1)), Equals, "/var/snap/name_instance/1") 1664 c.Check(snap.CommonDataDir("name_instance"), Equals, "/var/snap/name_instance/common") 1665 c.Check(snap.UserDataDir("/home/bob", "name_instance", snap.R(1)), Equals, "/home/bob/snap/name_instance/1") 1666 c.Check(snap.UserCommonDataDir("/home/bob", "name_instance"), Equals, "/home/bob/snap/name_instance/common") 1667 c.Check(snap.UserXdgRuntimeDir(12345, "name_instance"), Equals, "/run/user/12345/snap.name_instance") 1668 c.Check(snap.UserSnapDir("/home/bob", "name_instance"), Equals, "/home/bob/snap/name_instance") 1669 } 1670 1671 func (s *infoSuite) TestSortByType(c *C) { 1672 infos := []*snap.Info{ 1673 {SuggestedName: "app1", SnapType: "app"}, 1674 {SuggestedName: "os1", SnapType: "os"}, 1675 {SuggestedName: "base1", SnapType: "base"}, 1676 {SuggestedName: "gadget1", SnapType: "gadget"}, 1677 {SuggestedName: "kernel1", SnapType: "kernel"}, 1678 {SuggestedName: "app2", SnapType: "app"}, 1679 {SuggestedName: "os2", SnapType: "os"}, 1680 {SuggestedName: "snapd", SnapType: "snapd"}, 1681 {SuggestedName: "base2", SnapType: "base"}, 1682 {SuggestedName: "gadget2", SnapType: "gadget"}, 1683 {SuggestedName: "kernel2", SnapType: "kernel"}, 1684 } 1685 sort.Stable(snap.ByType(infos)) 1686 1687 c.Check(infos, DeepEquals, []*snap.Info{ 1688 {SuggestedName: "snapd", SnapType: "snapd"}, 1689 {SuggestedName: "os1", SnapType: "os"}, 1690 {SuggestedName: "os2", SnapType: "os"}, 1691 {SuggestedName: "kernel1", SnapType: "kernel"}, 1692 {SuggestedName: "kernel2", SnapType: "kernel"}, 1693 {SuggestedName: "base1", SnapType: "base"}, 1694 {SuggestedName: "base2", SnapType: "base"}, 1695 {SuggestedName: "gadget1", SnapType: "gadget"}, 1696 {SuggestedName: "gadget2", SnapType: "gadget"}, 1697 {SuggestedName: "app1", SnapType: "app"}, 1698 {SuggestedName: "app2", SnapType: "app"}, 1699 }) 1700 } 1701 1702 func (s *infoSuite) TestSortByTypeAgain(c *C) { 1703 core := &snap.Info{SnapType: snap.TypeOS} 1704 base := &snap.Info{SnapType: snap.TypeBase} 1705 app := &snap.Info{SnapType: snap.TypeApp} 1706 snapd := &snap.Info{} 1707 snapd.SideInfo = snap.SideInfo{RealName: "snapd"} 1708 1709 byType := func(snaps ...*snap.Info) []*snap.Info { 1710 sort.Stable(snap.ByType(snaps)) 1711 return snaps 1712 } 1713 1714 c.Check(byType(base, core), DeepEquals, []*snap.Info{core, base}) 1715 c.Check(byType(app, core), DeepEquals, []*snap.Info{core, app}) 1716 c.Check(byType(app, base), DeepEquals, []*snap.Info{base, app}) 1717 c.Check(byType(app, base, core), DeepEquals, []*snap.Info{core, base, app}) 1718 c.Check(byType(app, core, base), DeepEquals, []*snap.Info{core, base, app}) 1719 1720 c.Check(byType(app, core, base, snapd), DeepEquals, []*snap.Info{snapd, core, base, app}) 1721 c.Check(byType(app, snapd, core, base), DeepEquals, []*snap.Info{snapd, core, base, app}) 1722 } 1723 1724 func (s *infoSuite) TestMedia(c *C) { 1725 c.Check(snap.MediaInfos{}.IconURL(), Equals, "") 1726 1727 media := snap.MediaInfos{ 1728 { 1729 Type: "screenshot", 1730 URL: "https://example.com/shot1.svg", 1731 }, { 1732 Type: "icon", 1733 URL: "https://example.com/icon.png", 1734 }, { 1735 Type: "screenshot", 1736 URL: "https://example.com/shot2.svg", 1737 Width: 42, 1738 Height: 17, 1739 }, 1740 } 1741 1742 c.Check(media.IconURL(), Equals, "https://example.com/icon.png") 1743 } 1744 1745 func (s *infoSuite) TestSortApps(c *C) { 1746 tcs := []struct { 1747 err string 1748 apps []*snap.AppInfo 1749 sorted []string 1750 }{{ 1751 apps: []*snap.AppInfo{ 1752 {Name: "bar", Before: []string{"baz"}}, 1753 {Name: "foo"}, 1754 }, 1755 sorted: []string{"bar", "foo"}, 1756 }, { 1757 apps: []*snap.AppInfo{ 1758 {Name: "bar", Before: []string{"foo"}}, 1759 {Name: "foo", Before: []string{"baz"}}, 1760 }, 1761 sorted: []string{"bar", "foo"}, 1762 }, { 1763 apps: []*snap.AppInfo{ 1764 {Name: "bar", Before: []string{"foo"}}, 1765 }, 1766 sorted: []string{"bar"}, 1767 }, { 1768 apps: []*snap.AppInfo{ 1769 {Name: "bar", After: []string{"foo"}}, 1770 }, 1771 sorted: []string{"bar"}, 1772 }, { 1773 apps: []*snap.AppInfo{ 1774 {Name: "bar", Before: []string{"baz"}}, 1775 {Name: "baz", After: []string{"bar", "foo"}}, 1776 {Name: "foo"}, 1777 }, 1778 sorted: []string{"bar", "foo", "baz"}, 1779 }, { 1780 apps: []*snap.AppInfo{ 1781 {Name: "foo", After: []string{"bar", "zed"}}, 1782 {Name: "bar", Before: []string{"foo"}}, 1783 {Name: "baz", After: []string{"foo"}}, 1784 {Name: "zed"}, 1785 }, 1786 sorted: []string{"bar", "zed", "foo", "baz"}, 1787 }, { 1788 apps: []*snap.AppInfo{ 1789 {Name: "foo", After: []string{"baz"}}, 1790 {Name: "bar", Before: []string{"baz"}}, 1791 {Name: "baz"}, 1792 {Name: "zed", After: []string{"foo", "bar", "baz"}}, 1793 }, 1794 sorted: []string{"bar", "baz", "foo", "zed"}, 1795 }, { 1796 apps: []*snap.AppInfo{ 1797 {Name: "foo", Before: []string{"bar"}, After: []string{"zed"}}, 1798 {Name: "bar", Before: []string{"baz"}}, 1799 {Name: "baz", Before: []string{"zed"}}, 1800 {Name: "zed"}, 1801 }, 1802 err: `applications are part of a before/after cycle: ((foo|bar|baz|zed)(, )?){4}`, 1803 }, { 1804 apps: []*snap.AppInfo{ 1805 {Name: "foo", Before: []string{"bar"}}, 1806 {Name: "bar", Before: []string{"foo"}}, 1807 {Name: "baz", Before: []string{"foo"}, After: []string{"bar"}}, 1808 }, 1809 err: `applications are part of a before/after cycle: ((foo|bar|baz)(, )?){3}`, 1810 }, { 1811 apps: []*snap.AppInfo{ 1812 {Name: "baz", After: []string{"bar"}}, 1813 {Name: "foo"}, 1814 {Name: "bar", After: []string{"foo"}}, 1815 }, 1816 sorted: []string{"foo", "bar", "baz"}, 1817 }} 1818 for _, tc := range tcs { 1819 sorted, err := snap.SortServices(tc.apps) 1820 if tc.err != "" { 1821 c.Assert(err, ErrorMatches, tc.err) 1822 } else { 1823 c.Assert(err, IsNil) 1824 c.Assert(sorted, HasLen, len(tc.sorted)) 1825 sortedNames := make([]string, len(sorted)) 1826 for i, app := range sorted { 1827 sortedNames[i] = app.Name 1828 } 1829 c.Assert(sortedNames, DeepEquals, tc.sorted) 1830 } 1831 } 1832 } 1833 1834 func (s *infoSuite) TestSortAppInfoBySnapApp(c *C) { 1835 snap1 := &snap.Info{SuggestedName: "snapa"} 1836 snap2 := &snap.Info{SuggestedName: "snapb"} 1837 infos := []*snap.AppInfo{ 1838 {Snap: snap1, Name: "b"}, 1839 {Snap: snap2, Name: "b"}, 1840 {Snap: snap1, Name: "a"}, 1841 {Snap: snap2, Name: "a"}, 1842 } 1843 sort.Stable(snap.AppInfoBySnapApp(infos)) 1844 1845 c.Check(infos, DeepEquals, []*snap.AppInfo{ 1846 {Snap: snap1, Name: "a"}, 1847 {Snap: snap1, Name: "b"}, 1848 {Snap: snap2, Name: "a"}, 1849 {Snap: snap2, Name: "b"}, 1850 }) 1851 }