Skip to content

Commit

Permalink
many: read component and related assertions when reading seeds (#14378)
Browse files Browse the repository at this point in the history
* seed/seedwriter: write the right component file name in options.yaml

The Unasserted field in options.yaml had the file name from the passed
option, not the one in the created seed.

* seed: read components from the seed

* overlord: adapt to seedtest changes

* image: check for components when reading seeds

* seed/internal: allow asserted components in options.yaml

Allow having asserted components for asserted snaps in options.yaml

* seed: download asserted extra components

* overlord,seed: pass nil instead of empty map in calls to MakeSeedWithModel

* seed: fix tests that depend on file size

It needs to be read as we have different default sizes on different
distros.

* seed: deal with extra asserted snaps in the seed

* seed,image: change seed.Component.CompSideInfo to a pointer

* seed: unexport ResourceKey

* seed: read component as late as possible in lookupVerifiedComponent

* seed: create separate method to avoid tricky error handling inside timings.Run

* seed: add tests for component provenance

* seed: no wrapper struct for internal error
  • Loading branch information
alfonsosanchezbeato authored Aug 20, 2024
1 parent 3a68adb commit 7e49f81
Show file tree
Hide file tree
Showing 12 changed files with 1,353 additions and 85 deletions.
48 changes: 46 additions & 2 deletions image/image_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ import (
"github.com/snapcore/snapd/seed/seedtest"
"github.com/snapcore/snapd/seed/seedwriter"
"github.com/snapcore/snapd/snap"
"github.com/snapcore/snapd/snap/naming"
"github.com/snapcore/snapd/snap/snaptest"
"github.com/snapcore/snapd/store"
"github.com/snapcore/snapd/store/tooling"
Expand Down Expand Up @@ -3343,11 +3344,20 @@ func (s *imageSuite) testSetupSeedCore20Grub(c *C, kernelContent [][]string, exp
Channel: channel,
})
}
// comp2 is optional in our model so it has not been included
// as it was not in the options either
cref1 := naming.NewComponentRef("required20", "comp1")
c.Check(runSnaps[0], DeepEquals, &seed.Snap{
Path: filepath.Join(seedsnapsdir, "required20_21.snap"),
SideInfo: &s.AssertedSnapInfo("required20").SideInfo,
Required: true,
Channel: stableChannel,
Components: []seed.Component{
{
Path: filepath.Join(seedsnapsdir, "required20+comp1_22.comp"),
CompSideInfo: *snap.NewComponentSideInfo(cref1, snap.R(22)),
},
},
})
c.Check(runSnaps[0].Path, testutil.FilePresent)

Expand Down Expand Up @@ -4072,11 +4082,23 @@ func (s *imageSuite) TestSetupSeedSnapRevisionsDownloadHappy(c *C) {
Channel: channel,
})
}
cref1 := naming.NewComponentRef("required20", "comp1")
cref2 := naming.NewComponentRef("required20", "comp2")
c.Check(runSnaps[0], DeepEquals, &seed.Snap{
Path: filepath.Join(seedsnapsdir, "required20_59.snap"),
SideInfo: &s.AssertedSnapInfo("required20").SideInfo,
Required: true,
Channel: stableChannel,
Components: []seed.Component{
{
Path: filepath.Join(seedsnapsdir, "required20+comp1_22.comp"),
CompSideInfo: *snap.NewComponentSideInfo(cref1, snap.R(22)),
},
{
Path: filepath.Join(seedsnapsdir, "required20+comp2_33.comp"),
CompSideInfo: *snap.NewComponentSideInfo(cref2, snap.R(33)),
},
},
})
c.Check(runSnaps[0].Path, testutil.FilePresent)

Expand Down Expand Up @@ -4421,6 +4443,8 @@ func (s *imageSuite) TestLocalSnapWithCompsRevisionMatchingStoreRevision(c *C) {
Channel: essChannel[i],
})
}
cref1 := naming.NewComponentRef("required20", "comp1")
cref2 := naming.NewComponentRef("required20", "comp2")
c.Check(runSnaps[0], DeepEquals, &seed.Snap{
Path: filepath.Join(seedsnapsdir, "required20_21.snap"),
Required: true,
Expand All @@ -4430,6 +4454,16 @@ func (s *imageSuite) TestLocalSnapWithCompsRevisionMatchingStoreRevision(c *C) {
Revision: snap.R(21),
},
Channel: "latest/stable",
Components: []seed.Component{
{
Path: filepath.Join(seedsnapsdir, "required20+comp1_22.comp"),
CompSideInfo: *snap.NewComponentSideInfo(cref1, snap.R(22)),
},
{
Path: filepath.Join(seedsnapsdir, "required20+comp2_33.comp"),
CompSideInfo: *snap.NewComponentSideInfo(cref2, snap.R(33)),
},
},
})
c.Check(runSnaps[0].Path, testutil.FilePresent)
// Check components exist
Expand Down Expand Up @@ -5138,17 +5172,27 @@ func (s *imageSuite) TestSetupSeedLocalComponents(c *C) {
}
expectedLabel := image.MakeLabel(time.Now())
extraSnapsDir := filepath.Join(seeddir, "systems", expectedLabel, "snaps")
cref1 := naming.NewComponentRef("required20", "comp1")
cref2 := naming.NewComponentRef("required20", "comp2")
c.Check(runSnaps[0], DeepEquals, &seed.Snap{
Path: filepath.Join(extraSnapsDir, "required20_1.0.snap"),
SideInfo: &snap.SideInfo{
RealName: "required20",
},
Required: true,
Components: []seed.Component{
{
Path: filepath.Join(extraSnapsDir, "required20+comp1_1.0.comp"),
CompSideInfo: *snap.NewComponentSideInfo(cref1, snap.R(0)),
},
{
Path: filepath.Join(extraSnapsDir, "required20+comp2_2.0.comp"),
CompSideInfo: *snap.NewComponentSideInfo(cref2, snap.R(0)),
},
},
})
c.Check(runSnaps[0].Path, testutil.FilePresent)

// TODO these files will be loaded when opening the seed, but that is
// not implemented yet
c.Check(osutil.FileExists(filepath.Join(extraSnapsDir, "required20+comp1_1.0.comp")),
Equals, true)
c.Check(osutil.FileExists(filepath.Join(extraSnapsDir, "required20+comp2_2.0.comp")),
Expand Down
4 changes: 2 additions & 2 deletions overlord/managers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7483,7 +7483,7 @@ func (s *mgrsSuiteCore) testRemodelUC20WithRecoverySystem(c *C, encrypted bool)
{Path: coreInfo.MountFile()},
{Path: snapdInfo.MountFile()},
{Path: bazInfo.MountFile()},
})
}, nil)

// create a new model
newModel := s.brands.Model("can0nical", "my-model", uc20ModelDefaults, map[string]interface{}{
Expand Down Expand Up @@ -7876,7 +7876,7 @@ func (s *mgrsSuiteCore) testRemodelUC20WithRecoverySystemSimpleSetUp(c *C, model
{Path: pcKernelInfo.MountFile()},
{Path: coreInfo.MountFile()},
{Path: snapdInfo.MountFile()},
})
}, nil)

// mock the modeenv file
m := &boot.Modeenv{
Expand Down
8 changes: 8 additions & 0 deletions seed/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,14 @@ func readInfo(snapPath string, si *snap.SideInfo) (*snap.Info, error) {
return snap.ReadInfoFromSnapFile(snapf, si)
}

func readComponentInfo(compPath string, info *snap.Info, csi *snap.ComponentSideInfo) (*snap.ComponentInfo, error) {
compf, err := snapfile.Open(compPath)
if err != nil {
return nil, err
}
return snap.ReadComponentInfoFromContainer(compf, info, csi)
}

func snapTypeFromModel(modSnap *asserts.ModelSnap) snap.Type {
switch modSnap.SnapType {
case "base":
Expand Down
15 changes: 7 additions & 8 deletions seed/internal/options20.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ import (
"github.com/snapcore/snapd/snap/naming"
)

// Component contains the options for components for grade: dangerous.
type Component struct {
// Component20 contains the options for components for grade: dangerous.
type Component20 struct {
// Name is the component name
Name string `yaml:"name"`
// Unasserted has the filename for an unasserted local component
Expand All @@ -56,7 +56,7 @@ type Snap20 struct {
// Components is a list of component options. It is only valid to add a
// list of unasserted local components when we are using an unasserted
// local snap.
Components []Component `yaml:"components,omitempty"`
Components []Component20 `yaml:"components,omitempty"`
}

// SnapName implements naming.SnapRef.
Expand Down Expand Up @@ -114,17 +114,16 @@ func ReadOptions20(optionsFn string) (*Options20, error) {
return nil, fmt.Errorf("%s: %q must be a filename, not a path", errPrefix, sn.Unasserted)
}
if len(sn.Components) > 0 {
if sn.Unasserted == "" {
return nil, fmt.Errorf("%s: local components specified for non-local snap %q", errPrefix, sn.Name)
}

for _, comp := range sn.Components {
if err := naming.ValidateSnap(comp.Name); err != nil {
return nil, fmt.Errorf("%s: %v", errPrefix, err)
}
if comp.Unasserted == "" {
if comp.Unasserted == "" && sn.Unasserted != "" {
return nil, fmt.Errorf("%s: no file specified for unasserted component %q", errPrefix, comp.Name)
}
if comp.Unasserted != "" && sn.Unasserted == "" {
return nil, fmt.Errorf("%s: unasserted component specified for asserted snap %q", errPrefix, sn.Name)
}
if strings.Contains(comp.Unasserted, "/") {
return nil, fmt.Errorf("%s: %q must be a filename, not a path", errPrefix, comp.Unasserted)
}
Expand Down
27 changes: 25 additions & 2 deletions seed/internal/options20_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ snaps:
c.Assert(options20.Snaps[0], DeepEquals, &internal.Snap20{
Name: "foo",
Unasserted: "bar.snap",
Components: []internal.Component{
Components: []internal.Component20{
{Name: "comp1", Unasserted: "comp1_1.comp"},
{Name: "comp2", Unasserted: "file.comp"},
},
Expand All @@ -217,7 +217,30 @@ snaps:
c.Assert(err, IsNil)
options20, err := internal.ReadOptions20(fn)
c.Assert(options20, IsNil)
c.Assert(err, ErrorMatches, `cannot read grade dangerous options yaml: local components specified for non-local snap "foo"`)
c.Assert(err, ErrorMatches, `cannot read grade dangerous options yaml: unasserted component specified for asserted snap "foo"`)
}

func (s *options20Suite) TestWithComponentsAssertedSnapAssertedComp(c *C) {
fn := filepath.Join(c.MkDir(), "options.yaml")
err := os.WriteFile(fn, []byte(`
snaps:
- name: foo
id: snapidsnapidsnapidsnapidsnapidsn
components:
- name: comp1
`), 0644)

c.Assert(err, IsNil)
options20, err := internal.ReadOptions20(fn)
c.Assert(err, IsNil)
c.Assert(options20.Snaps, HasLen, 1)
c.Assert(options20.Snaps[0], DeepEquals, &internal.Snap20{
Name: "foo",
SnapID: "snapidsnapidsnapidsnapidsnapidsn",
Components: []internal.Component20{
{Name: "comp1"},
},
})
}

func (s *options20Suite) TestWithComponentsBadCompName(c *C) {
Expand Down
9 changes: 9 additions & 0 deletions seed/seed.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@ var (
open = Open
)

// Component holds the details of a component in a seed.
type Component struct {
Path string
CompSideInfo snap.ComponentSideInfo
}

// Snap holds the details of a snap in a seed.
type Snap struct {
Path string
Expand All @@ -56,6 +62,9 @@ type Snap struct {
Channel string
DevMode bool
Classic bool

// Components for the snap
Components []Component
}

func (s *Snap) SnapName() string {
Expand Down
Loading

0 comments on commit 7e49f81

Please sign in to comment.