1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295
|
// -*- Mode: Go; indent-tabs-mode: t -*-
/*
* Copyright (C) 2016-2024 Canonical Ltd
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
// Package sequence contains types representing a sequence of snap
// revisions (with components) that describe current and past states
// of the snap in the system.
package sequence
import (
"encoding/json"
"errors"
"github.com/snapcore/snapd/snap"
"github.com/snapcore/snapd/snap/naming"
)
// ComponentState contains information about an installed component.
type ComponentState struct {
SideInfo *snap.ComponentSideInfo `json:"side-info"`
CompType snap.ComponentType `json:"type"`
}
// NewComponentState creates a ComponentState from components side information and type.
func NewComponentState(si *snap.ComponentSideInfo, tp snap.ComponentType) *ComponentState {
return &ComponentState{SideInfo: si, CompType: tp}
}
// RevisionSideState contains the side information for a snap and related components
// installed in the system.
type RevisionSideState struct {
Snap *snap.SideInfo
Components []*ComponentState
}
// revisionSideInfoMarshal is an ancillary structure used exclusively to
// help marshaling of RevisionSideInfo.
type revisionSideInfoMarshal struct {
// SideInfo is included for compatibility with older snapd state files
*snap.SideInfo
Components []*ComponentState `json:"components,omitempty"`
}
// MarshalJSON implements the json.Marshaler interface
func (bsi RevisionSideState) MarshalJSON() ([]byte, error) {
return json.Marshal(&revisionSideInfoMarshal{bsi.Snap, bsi.Components})
}
// UnmarshalJSON implements the json.Unmarshaler interface
func (bsi *RevisionSideState) UnmarshalJSON(in []byte) error {
var aux revisionSideInfoMarshal
if err := json.Unmarshal(in, &aux); err != nil {
return err
}
bsi.Snap = aux.SideInfo
bsi.Components = aux.Components
return nil
}
// FindComponent returns the ComponentState if cref is found in the sequence point.
func (rss *RevisionSideState) FindComponent(cref naming.ComponentRef) *ComponentState {
for _, csi := range rss.Components {
if csi.SideInfo.Component == cref {
return csi
}
}
return nil
}
// NewRevisionSideState creates a RevisionSideInfo from snap and
// related components side information.
func NewRevisionSideState(snapSideInfo *snap.SideInfo, compSideInfo []*ComponentState) *RevisionSideState {
return &RevisionSideState{Snap: snapSideInfo, Components: compSideInfo}
}
// SnapSequence is a container for a slice containing revisions of
// snaps plus related components.
// TODO add methods to access Revisions (length, copy, append) and
// use them in handlers.go and snapstate.go.
type SnapSequence struct {
// Revisions contains information for a snap revision and
// components SideInfo.
Revisions []*RevisionSideState
}
// MarshalJSON implements the json.Marshaler interface. We override the default
// so serialization of the SnapState.Sequence field is compatible to what was
// produced when it was defined as a []*snap.SideInfo. This is also the reason
// to have SnapSequence.UnmarshalJSON and MarshalJSON/UnmarshalJSON for
// RevisionSideState.
func (snapSeq SnapSequence) MarshalJSON() ([]byte, error) {
return json.Marshal(snapSeq.Revisions)
}
// UnmarshalJSON implements the json.Unmarshaler interface
func (snapSeq *SnapSequence) UnmarshalJSON(in []byte) error {
aux := []*RevisionSideState{}
if err := json.Unmarshal(in, &aux); err != nil {
return err
}
snapSeq.Revisions = aux
return nil
}
// SideInfos returns a slice with all the SideInfos for the snap sequence.
func (snapSeq SnapSequence) SideInfos() []*snap.SideInfo {
sis := make([]*snap.SideInfo, len(snapSeq.Revisions))
for i, rev := range snapSeq.Revisions {
sis[i] = rev.Snap
}
return sis
}
// LastIndex returns the last index of the given revision in snapSeq,
// or -1 if the revision was not found.
func (snapSeq *SnapSequence) LastIndex(revision snap.Revision) int {
for i := len(snapSeq.Revisions) - 1; i >= 0; i-- {
if snapSeq.Revisions[i].Snap.Revision == revision {
return i
}
}
return -1
}
var ErrSnapRevNotInSequence = errors.New("snap is not in the sequence")
// AddComponentForRevision adds a component to the last instance of snapRev in
// the sequence.
func (snapSeq *SnapSequence) AddComponentForRevision(snapRev snap.Revision, cs *ComponentState) error {
snapIdx := snapSeq.LastIndex(snapRev)
if snapIdx == -1 {
return ErrSnapRevNotInSequence
}
revSt := snapSeq.Revisions[snapIdx]
if currentCompSt := revSt.FindComponent(cs.SideInfo.Component); currentCompSt != nil {
// Component already present, replace revision
*currentCompSt = *cs
return nil
}
// Append new component to components of the current snap
revSt.Components = append(revSt.Components, cs)
return nil
}
// RemoveComponentForRevision removes the cref component for the last instance
// of snapRev in the sequence and returns a pointer to it, which might be nil
// if not found.
func (snapSeq *SnapSequence) RemoveComponentForRevision(snapRev snap.Revision, cref naming.ComponentRef) (unlinkedComp *ComponentState) {
snapIdx := snapSeq.LastIndex(snapRev)
if snapIdx == -1 {
return nil
}
revSt := snapSeq.Revisions[snapIdx]
var leftComp []*ComponentState
for _, csi := range revSt.Components {
if csi.SideInfo.Component == cref {
unlinkedComp = csi
continue
}
leftComp = append(leftComp, csi)
}
revSt.Components = leftComp
// might be nil
return unlinkedComp
}
// ComponentStateForRev returns cref's component side info for the revision
// (sequence point) indicated by revIdx if there is one.
func (snapSeq *SnapSequence) ComponentStateForRev(revIdx int, cref naming.ComponentRef) *ComponentState {
for _, comp := range snapSeq.Revisions[revIdx].Components {
if comp.SideInfo.Component == cref {
return comp
}
}
// component not found
return nil
}
// IsComponentRevPresent tells us if a given component revision is
// present in the system for this snap.
func (snapSeq *SnapSequence) IsComponentRevPresent(compSi *snap.ComponentSideInfo) bool {
for _, rev := range snapSeq.Revisions {
for _, cs := range rev.Components {
if cs.SideInfo.Equal(compSi) {
return true
}
}
}
return false
}
func (snapSeq *SnapSequence) ComponentsForRevision(rev snap.Revision) []*ComponentState {
for _, rss := range snapSeq.Revisions {
if rss.Snap.Revision == rev {
return rss.Components
}
}
return nil
}
func (snapSeq *SnapSequence) ComponentsWithTypeForRev(rev snap.Revision, compType snap.ComponentType) []*snap.ComponentSideInfo {
comps := snapSeq.ComponentsForRevision(rev)
kmodComps := make([]*snap.ComponentSideInfo, 0, len(comps))
for _, comp := range comps {
if comp.CompType != snap.KernelModulesComponent {
continue
}
kmodComps = append(kmodComps, comp.SideInfo)
}
return kmodComps
}
// IsComponentRevInRefSeqPtInAnyOtherSeqPt tells us if the component cref in
// the sequence point defined by refIdx is used in another sequence point too.
func (snapSeq *SnapSequence) IsComponentRevInRefSeqPtInAnyOtherSeqPt(cref naming.ComponentRef, refIdx int) bool {
// Find component in reference sequence point
refSeqPt := snapSeq.Revisions[refIdx]
refSeqPtComp := refSeqPt.FindComponent(cref)
if refSeqPtComp == nil {
return false
}
// Find if the reference component revision is used elsewhere
for idx, seqPt := range snapSeq.Revisions {
if idx == refIdx {
continue
}
compInSeqPt := seqPt.FindComponent(cref)
if compInSeqPt == nil {
continue
}
if compInSeqPt.SideInfo.Revision == refSeqPtComp.SideInfo.Revision {
return true
}
}
return false
}
// MinimumLocalRevision returns the the smallest local revision for the
// sequence. Local revisions start at -1 and are counted down. 0 will be
// returned if no local revision for the snap is found.
func (snapSeq *SnapSequence) MinimumLocalRevision() snap.Revision {
var local snap.Revision
for _, rev := range snapSeq.Revisions {
if rev.Snap.Revision.N < local.N {
local = rev.Snap.Revision
}
}
return local
}
// MinimumLocalComponentRevision returns the smallest local revision for the
// compName component in the sequence. Local revisions start at -1 and are
// counted down. 0 will be returned if no local revision for the component is
// found.
func (snapSeq *SnapSequence) MinimumLocalComponentRevision(compName string) snap.Revision {
var local snap.Revision
for _, revSt := range snapSeq.Revisions {
for _, compSt := range revSt.Components {
if compSt.SideInfo.Component.ComponentName != compName {
continue
}
if compSt.SideInfo.Revision.N >= local.N {
continue
}
local = compSt.SideInfo.Revision
}
}
return local
}
// HasComponents returns true if the revision at the given index has
// any components installed with it.
func (snapSeq *SnapSequence) HasComponents(revIdx int) bool {
return len(snapSeq.Revisions[revIdx].Components) > 0
}
|