diff --git a/asserts/snapasserts/validation_sets.go b/asserts/snapasserts/validation_sets.go index c53ef70b69f..1b36c38a91e 100644 --- a/asserts/snapasserts/validation_sets.go +++ b/asserts/snapasserts/validation_sets.go @@ -751,7 +751,12 @@ func (v *ValidationSets) CheckInstalledSnaps(snaps []*InstalledSnap, ignoreValid return err } - vcerrs, err := v.checkInstalledComponents(installed, ignoreValidation) + missingSnapNames := make(map[string]bool, len(missing)) + for name := range missing { + missingSnapNames[name] = true + } + + vcerrs, err := v.checkInstalledComponents(installed, ignoreValidation, missingSnapNames) if err != nil { return err } @@ -768,7 +773,7 @@ func (v *ValidationSets) CheckInstalledSnaps(snaps []*InstalledSnap, ignoreValid return nil } -func (v *ValidationSets) checkInstalledComponents(installedSnaps installedSnapSet, ignore map[string]bool) (map[string]*ValidationSetsComponentValidationError, error) { +func (v *ValidationSets) checkInstalledComponents(installedSnaps installedSnapSet, ignore map[string]bool, missingSnaps map[string]bool) (map[string]*ValidationSetsComponentValidationError, error) { componentInstalled := func(cstrs constraints) (snap.Revision, error) { if cstrs.compRef == nil || cstrs.snapRef == nil { return snap.Revision{}, errors.New("internal error: component constraint should have component and snap refs") @@ -787,11 +792,13 @@ func (v *ValidationSets) checkInstalledComponents(installedSnaps installedSnapSe // if we're ignoring the snap, then we don't consider its components // // if the snap is not installed, then nothing can be wrong with the - // components, since none will be installed. + // components, since none will be installed. however, for error + // reporting reasons, we consider components for snaps that are required + // by the validation sets. // // note that we consider "required" components to only be required if // the snap itself is installed. - if ignore[sc.name] || !installedSnaps.Contains(sc.snapRef) { + if ignore[sc.name] || (!installedSnaps.Contains(sc.snapRef) && !missingSnaps[sc.name]) { continue } diff --git a/asserts/snapasserts/validation_sets_test.go b/asserts/snapasserts/validation_sets_test.go index b9bece2e81f..781929ec435 100644 --- a/asserts/snapasserts/validation_sets_test.go +++ b/asserts/snapasserts/validation_sets_test.go @@ -737,6 +737,25 @@ func (s *validationSetsSuite) TestCheckInstalledSnapsWithComponents(c *C) { }, }, }, + "four": { + map[string]interface{}{ + "name": "snap-a", + "id": snaptest.AssertedSnapID("snap-a"), + "presence": "required", + "components": map[string]interface{}{ + "comp-3": map[string]interface{}{ + "presence": "required", + }, + "comp-4": map[string]interface{}{ + "presence": "required", + "revision": "2", + }, + "comp-5": map[string]interface{}{ + "presence": "optional", + }, + }, + }, + }, } assertions := make(map[string]*asserts.ValidationSet) @@ -862,7 +881,7 @@ func (s *validationSetsSuite) TestCheckInstalledSnapsWithComponents(c *C) { "snap-a": { MissingComponents: map[string]map[snap.Revision][]string{ "comp-2": { - {}: {"acme/three"}, + snap.R(0): {"acme/three"}, snap.R(3): {"acme/one"}, }, }, @@ -870,6 +889,32 @@ func (s *validationSetsSuite) TestCheckInstalledSnapsWithComponents(c *C) { }, }, }, + { + summary: "missing required snap and required component", + assertions: []string{"four"}, + verr: &snapasserts.ValidationSetsValidationError{ + Sets: map[string]*asserts.ValidationSet{ + "acme/four": assertions["four"], + }, + ComponentErrors: map[string]*snapasserts.ValidationSetsComponentValidationError{ + "snap-a": { + MissingComponents: map[string]map[snap.Revision][]string{ + "comp-3": { + snap.R(0): {"acme/four"}, + }, + "comp-4": { + snap.R(2): {"acme/four"}, + }, + }, + }, + }, + MissingSnaps: map[string]map[snap.Revision][]string{ + "snap-a": { + snap.R(0): {"acme/four"}, + }, + }, + }, + }, } for i, tc := range cases {