mirror of
https://github.com/token2/snapd.git
synced 2026-03-13 11:15:47 -07:00
Go 1.19 includes some changes to gofmt which intend to make lists and heading clearer when rendered (https://go.dev/doc/go1.19). This commit is the result of running the new gofmt and manually fixing some of it. This was necessary because the new gofmt assumed lines beginning w/ tabs to start lists or examples. While this is often true in our codebase, we occasionally also use tabs to indent the lines after a TODO or FIXME prefix or in yaml (e.g., excerpts of a snap.yaml). This meant that a lot of the reformatted comments were broken and had to be fixed manually. Signed-off-by: Miguel Pires <miguel.pires@canonical.com>
317 lines
8.6 KiB
Go
317 lines
8.6 KiB
Go
// -*- Mode: Go; indent-tabs-mode: t -*-
|
|
|
|
/*
|
|
* Copyright (C) 2016 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 patch
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
|
|
"github.com/snapcore/snapd/overlord/state"
|
|
"github.com/snapcore/snapd/snap"
|
|
)
|
|
|
|
func init() {
|
|
patches[4] = []PatchFunc{patch4}
|
|
}
|
|
|
|
type patch4Flags int
|
|
|
|
const (
|
|
patch4FlagDevMode = 1 << iota
|
|
patch4FlagTryMode
|
|
patch4FlagJailMode
|
|
)
|
|
|
|
const patch4FlagRevert = patch4Flags(0x40000000)
|
|
|
|
func (f patch4Flags) DevMode() bool {
|
|
return f&patch4FlagDevMode != 0
|
|
}
|
|
|
|
func (f patch4Flags) TryMode() bool {
|
|
return f&patch4FlagTryMode != 0
|
|
}
|
|
|
|
func (f patch4Flags) JailMode() bool {
|
|
return f&patch4FlagJailMode != 0
|
|
}
|
|
|
|
func (f patch4Flags) Revert() bool {
|
|
return f&patch4FlagRevert != 0
|
|
}
|
|
|
|
type patch4DownloadInfo struct {
|
|
AnonDownloadURL string `json:"anon-download-url,omitempty"`
|
|
DownloadURL string `json:"download-url,omitempty"`
|
|
|
|
Size int64 `json:"size,omitempty"`
|
|
Sha3_384 string `json:"sha3-384,omitempty"`
|
|
}
|
|
|
|
type patch4SideInfo struct {
|
|
RealName string `yaml:"name,omitempty" json:"name,omitempty"`
|
|
SnapID string `yaml:"snap-id" json:"snap-id"`
|
|
Revision snap.Revision `yaml:"revision" json:"revision"`
|
|
Channel string `yaml:"channel,omitempty" json:"channel,omitempty"`
|
|
DeveloperID string `yaml:"developer-id,omitempty" json:"developer-id,omitempty"`
|
|
Developer string `yaml:"developer,omitempty" json:"developer,omitempty"`
|
|
EditedSummary string `yaml:"summary,omitempty" json:"summary,omitempty"`
|
|
EditedDescription string `yaml:"description,omitempty" json:"description,omitempty"`
|
|
Private bool `yaml:"private,omitempty" json:"private,omitempty"`
|
|
}
|
|
|
|
type patch4SnapSetup struct {
|
|
Channel string `json:"channel,omitempty"`
|
|
UserID int `json:"user-id,omitempty"`
|
|
Flags patch4Flags `json:"flags,omitempty"`
|
|
SnapPath string `json:"snap-path,omitempty"`
|
|
DownloadInfo *patch4DownloadInfo `json:"download-info,omitempty"`
|
|
SideInfo *patch4SideInfo `json:"side-info,omitempty"`
|
|
}
|
|
|
|
func (snapsup *patch4SnapSetup) Name() string {
|
|
if snapsup.SideInfo.RealName == "" {
|
|
panic("SnapSetup.SideInfo.RealName not set")
|
|
}
|
|
return snapsup.SideInfo.RealName
|
|
}
|
|
|
|
func (snapsup *patch4SnapSetup) Revision() snap.Revision {
|
|
return snapsup.SideInfo.Revision
|
|
}
|
|
|
|
type patch4SnapState struct {
|
|
SnapType string `json:"type"` // Use Type and SetType
|
|
Sequence []*patch4SideInfo `json:"sequence"`
|
|
Active bool `json:"active,omitempty"`
|
|
Current snap.Revision `json:"current"`
|
|
Channel string `json:"channel,omitempty"`
|
|
Flags patch4Flags `json:"flags,omitempty"`
|
|
}
|
|
|
|
func (snapst *patch4SnapState) LastIndex(revision snap.Revision) int {
|
|
for i := len(snapst.Sequence) - 1; i >= 0; i-- {
|
|
if snapst.Sequence[i].Revision == revision {
|
|
return i
|
|
}
|
|
}
|
|
return -1
|
|
}
|
|
|
|
type patch4T struct{} // for namespacing of the helpers
|
|
|
|
func (p4 patch4T) taskSnapSetup(task *state.Task) (*patch4SnapSetup, error) {
|
|
var snapsup patch4SnapSetup
|
|
|
|
if err := p4.getMaybe(task, "snap-setup", &snapsup); err == nil {
|
|
return &snapsup, nil
|
|
} else if !errors.Is(err, state.ErrNoState) {
|
|
return nil, err
|
|
}
|
|
|
|
var id string
|
|
if err := p4.get(task, "snap-setup-task", &id); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err := p4.get(task.State().Task(id), "snap-setup", &snapsup); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &snapsup, nil
|
|
}
|
|
|
|
var errNoSnapState = errors.New("no snap state")
|
|
|
|
func (p4 patch4T) snapSetupAndState(task *state.Task) (*patch4SnapSetup, *patch4SnapState, error) {
|
|
var snapst patch4SnapState
|
|
|
|
snapsup, err := p4.taskSnapSetup(task)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
var snaps map[string]*json.RawMessage
|
|
err = task.State().Get("snaps", &snaps)
|
|
if err != nil {
|
|
return nil, nil, errNoSnapState
|
|
}
|
|
raw, ok := snaps[snapsup.Name()]
|
|
if !ok {
|
|
return nil, nil, errNoSnapState
|
|
}
|
|
err = json.Unmarshal([]byte(*raw), &snapst)
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("cannot get state for snap %q: %v", snapsup.Name(), err)
|
|
}
|
|
|
|
return snapsup, &snapst, err
|
|
}
|
|
|
|
// getMaybe calls task.Get and wraps any non-ErrNoState error in an informative message
|
|
func (p4 patch4T) getMaybe(task *state.Task, key string, value interface{}) error {
|
|
return p4.gget(task, key, true, value)
|
|
}
|
|
|
|
// get calls task.Get and wraps any error in an informative message
|
|
func (p4 patch4T) get(task *state.Task, key string, value interface{}) error {
|
|
return p4.gget(task, key, false, value)
|
|
}
|
|
|
|
// gget does the actual work of get and getMaybe
|
|
func (patch4T) gget(task *state.Task, key string, passThroughMissing bool, value interface{}) error {
|
|
err := task.Get(key, value)
|
|
if err == nil || (passThroughMissing && errors.Is(err, state.ErrNoState)) {
|
|
return err
|
|
}
|
|
change := task.Change()
|
|
|
|
return fmt.Errorf("cannot get %q from task %s (%s) of change %s (%s): %v",
|
|
key, task.ID(), task.Kind(), change.ID(), change.Kind(), err)
|
|
}
|
|
|
|
func (p4 patch4T) addCleanup(task *state.Task) error {
|
|
// NOTE we could check for the status of the change itself, but
|
|
// copy-snap-data is the one creating the trash, so if it's run there's
|
|
// no sense in fiddling with the change.
|
|
if task.Status().Ready() {
|
|
return nil
|
|
}
|
|
|
|
snapsup, err := p4.taskSnapSetup(task)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var tid string
|
|
if err := p4.get(task, "snap-setup-task", &tid); err != nil {
|
|
return err
|
|
}
|
|
|
|
change := task.Change()
|
|
revisionStr := ""
|
|
if snapsup.SideInfo != nil {
|
|
revisionStr = fmt.Sprintf(" (%s)", snapsup.Revision())
|
|
}
|
|
|
|
tasks := change.Tasks()
|
|
last := tasks[len(tasks)-1]
|
|
newTask := task.State().NewTask("cleanup", fmt.Sprintf("Clean up %q%s install", snapsup.Name(), revisionStr))
|
|
newTask.Set("snap-setup-task", tid)
|
|
newTask.WaitFor(last)
|
|
change.AddTask(newTask)
|
|
|
|
return nil
|
|
}
|
|
|
|
func (p4 patch4T) mangle(task *state.Task) error {
|
|
snapsup, snapst, err := p4.snapSetupAndState(task)
|
|
if err == errNoSnapState {
|
|
change := task.Change()
|
|
if change.Kind() != "install-snap" {
|
|
return fmt.Errorf("cannot get snap state for task %s (%s) of change %s (%s != install-snap)", task.ID(), task.Kind(), change.ID(), change.Kind())
|
|
}
|
|
// we expect pending/in-progress install changes
|
|
// possibly not to have reached link-sanp yet and so
|
|
// have no snap state yet, nothing to do
|
|
return nil
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var hadCandidate bool
|
|
if err := p4.getMaybe(task, "had-candidate", &hadCandidate); err != nil && !errors.Is(err, state.ErrNoState) {
|
|
return err
|
|
}
|
|
|
|
if hadCandidate {
|
|
change := task.Change()
|
|
if change.Kind() != "revert-snap" {
|
|
return fmt.Errorf("had-candidate true for task %s (%s) of non-revert change %s (%s)",
|
|
task.ID(), task.Kind(), change.ID(), change.Kind())
|
|
}
|
|
}
|
|
|
|
task.Clear("had-candidate")
|
|
|
|
task.Set("old-candidate-index", snapst.LastIndex(snapsup.SideInfo.Revision))
|
|
|
|
return nil
|
|
}
|
|
|
|
func (p4 patch4T) addRevertFlag(task *state.Task) error {
|
|
var snapsup patch4SnapSetup
|
|
if err := p4.getMaybe(task, "snap-setup", &snapsup); err == nil {
|
|
snapsup.Flags |= patch4FlagRevert
|
|
// save it back
|
|
task.Set("snap-setup", &snapsup)
|
|
return nil
|
|
} else if !errors.Is(err, state.ErrNoState) {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// patch4:
|
|
// - add Revert flag to in-progress revert-snap changes
|
|
// - move from had-candidate to old-candidate-index in link-snap tasks
|
|
// - add cleanup task to in-progress changes that have a copy-snap-data task
|
|
func patch4(s *state.State) error {
|
|
p4 := patch4T{}
|
|
for _, change := range s.Changes() {
|
|
// change is full done, take it easy
|
|
if change.Status().Ready() {
|
|
continue
|
|
}
|
|
|
|
if change.Kind() != "revert-snap" {
|
|
continue
|
|
}
|
|
for _, task := range change.Tasks() {
|
|
if err := p4.addRevertFlag(task); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
|
|
for _, task := range s.Tasks() {
|
|
// change is full done, take it easy
|
|
if task.Change().Status().Ready() {
|
|
continue
|
|
}
|
|
|
|
switch task.Kind() {
|
|
case "link-snap":
|
|
if err := p4.mangle(task); err != nil {
|
|
return err
|
|
}
|
|
case "copy-snap-data":
|
|
if err := p4.addCleanup(task); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|