1 Star 0 Fork 1

mysnapcore/mysnapd

forked from tupelo-shen/mysnapd 
Create your Gitee Account
Explore and code with more than 13.5 million developers,Free private repositories !:)
Sign up
文件
Clone or Download
aliasesv2.go 20.58 KB
Copy Edit Raw Blame History
tupelo-shen authored 2022-11-08 15:12 +08:00 . fix: overlord commit
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705
// -*- Mode: Go; indent-tabs-mode: t -*-
/*
* Copyright (C) 2016-2017 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 snapstate
import (
"encoding/json"
"errors"
"fmt"
"strings"
"gitee.com/mysnapcore/mysnapd/i18n"
"gitee.com/mysnapcore/mysnapd/logger"
"gitee.com/mysnapcore/mysnapd/overlord/snapstate/backend"
"gitee.com/mysnapcore/mysnapd/overlord/state"
"gitee.com/mysnapcore/mysnapd/snap"
"gitee.com/mysnapcore/mysnapd/strutil"
)
// AliasTarget carries the targets of an alias in the context of snap.
// If Manual is set it is the target of an enabled manual alias.
// Auto is set to the target for an automatic alias, enabled or
// disabled depending on the automatic aliases flag state.
type AliasTarget struct {
Manual string `json:"manual,omitempty"`
Auto string `json:"auto,omitempty"`
}
// Effective returns the target to use considering whether automatic
// aliases are disabled for the whole snap (autoDisabled), returns ""
// if the alias is disabled.
func (at *AliasTarget) Effective(autoDisabled bool) string {
if at == nil {
return ""
}
if at.Manual != "" {
return at.Manual
}
if !autoDisabled {
return at.Auto
}
return ""
}
/*
State for aliases for a snap is tracked in SnapState with:
type SnapState struct {
...
Aliases map[string]*AliasTarget
AutoAliasesDisabled bool
}
There are two kinds of aliases:
* automatic aliases listed with their target application in the
snap-declaration of the snap (using AliasTarget.Auto)
* manual aliases setup with "snap alias SNAP.APP ALIAS" (tracked
using AliasTarget.Manual)
Further
* all automatic aliases of a snap are either enabled
or disabled together (tracked with AutoAliasesDisabled)
* disabling a manual alias removes it from disk and state (for
simplicity there is no disabled state for manual aliases)
* an AliasTarget with both Auto and Manual set is a manual alias
that has the same name as an automatic one, the manual target
is what wins
*/
// autoDisabled options and doApply
const (
autoDis = true
autoEn = false
doApply = false
)
// applyAliasesChange applies the necessary changes to aliases on disk
// to go from prevAliases considering the automatic aliases flag
// (prevAutoDisabled) to newAliases considering newAutoDisabled for
// snapName. It assumes that conflicts have already been checked.
func applyAliasesChange(snapName string, prevAutoDisabled bool, prevAliases map[string]*AliasTarget, newAutoDisabled bool, newAliases map[string]*AliasTarget, be managerBackend, dryRun bool) (add, remove []*backend.Alias, err error) {
for alias, prevTargets := range prevAliases {
if _, ok := newAliases[alias]; ok {
continue
}
// gone
if effTgt := prevTargets.Effective(prevAutoDisabled); effTgt != "" {
remove = append(remove, &backend.Alias{
Name: alias,
Target: snap.JoinSnapApp(snapName, effTgt),
})
}
}
for alias, newTargets := range newAliases {
prevTgt := prevAliases[alias].Effective(prevAutoDisabled)
newTgt := newTargets.Effective(newAutoDisabled)
if prevTgt == newTgt {
// nothing to do
continue
}
if prevTgt != "" {
remove = append(remove, &backend.Alias{
Name: alias,
Target: snap.JoinSnapApp(snapName, prevTgt),
})
}
if newTgt != "" {
add = append(add, &backend.Alias{
Name: alias,
Target: snap.JoinSnapApp(snapName, newTgt),
})
}
}
if !dryRun {
if err := be.UpdateAliases(add, remove); err != nil {
return nil, nil, err
}
}
return add, remove, nil
}
// AutoAliases allows to hook support for retrieving the automatic aliases of a snap.
var AutoAliases func(st *state.State, info *snap.Info) (map[string]string, error)
// autoAliasesDelta compares the automatic aliases with the current snap
// declaration for the installed snaps with the given names (or all if
// names is empty) and returns changed and dropped auto-aliases by
// snap name.
func autoAliasesDelta(st *state.State, names []string) (changed map[string][]string, dropped map[string][]string, err error) {
var snapStates map[string]*SnapState
if len(names) == 0 {
var err error
snapStates, err = All(st)
if err != nil {
return nil, nil, err
}
} else {
snapStates = make(map[string]*SnapState, len(names))
for _, name := range names {
var snapst SnapState
err := Get(st, name, &snapst)
if err != nil {
return nil, nil, err
}
snapStates[name] = &snapst
}
}
var firstErr error
changed = make(map[string][]string)
dropped = make(map[string][]string)
for instanceName, snapst := range snapStates {
aliases := snapst.Aliases
info, err := snapst.CurrentInfo()
if err != nil {
if firstErr == nil {
firstErr = err
}
continue
}
autoAliases, err := AutoAliases(st, info)
if err != nil {
if firstErr == nil {
firstErr = err
}
continue
}
for alias, target := range autoAliases {
curTarget := aliases[alias]
if curTarget == nil || curTarget.Auto != target {
changed[instanceName] = append(changed[instanceName], alias)
}
}
for alias, target := range aliases {
if target.Auto != "" && autoAliases[alias] == "" {
dropped[instanceName] = append(dropped[instanceName], alias)
}
}
}
return changed, dropped, firstErr
}
// refreshAliases applies the current snap-declaration aliases
// considering which applications exist in info and produces new aliases
// for the snap.
func refreshAliases(st *state.State, info *snap.Info, curAliases map[string]*AliasTarget) (newAliases map[string]*AliasTarget, err error) {
autoAliases, err := AutoAliases(st, info)
if err != nil {
return nil, err
}
newAliases = make(map[string]*AliasTarget, len(autoAliases))
// apply the current auto-aliases
for alias, target := range autoAliases {
if app := info.Apps[target]; app == nil || app.IsService() {
// non-existing app or a daemon
continue
}
newAliases[alias] = &AliasTarget{Auto: target}
}
// carry over the current manual ones
for alias, curTarget := range curAliases {
if curTarget.Manual == "" {
continue
}
if app := info.Apps[curTarget.Manual]; app == nil || app.IsService() {
// non-existing app or daemon
continue
}
newTarget := newAliases[alias]
if newTarget == nil {
newAliases[alias] = &AliasTarget{Manual: curTarget.Manual}
} else {
// alias is both manually setup but has an underlying auto-alias
newAliases[alias].Manual = curTarget.Manual
}
}
return newAliases, nil
}
type AliasConflictError struct {
Snap string
Alias string
Reason string
Conflicts map[string][]string
}
func (e *AliasConflictError) Error() string {
if len(e.Conflicts) != 0 {
errParts := []string{"cannot enable"}
first := true
for instanceName, aliases := range e.Conflicts {
if !first {
errParts = append(errParts, "nor")
}
if len(aliases) == 1 {
errParts = append(errParts, fmt.Sprintf("alias %q", aliases[0]))
} else {
errParts = append(errParts, fmt.Sprintf("aliases %s", strutil.Quoted(aliases)))
}
if first {
errParts = append(errParts, fmt.Sprintf("for %q,", e.Snap))
first = false
}
errParts = append(errParts, fmt.Sprintf("already enabled for %q", instanceName))
}
// TODO: add recommendation about what to do next
return strings.Join(errParts, " ")
}
return fmt.Sprintf("cannot enable alias %q for %q, %s", e.Alias, e.Snap, e.Reason)
}
func addAliasConflicts(st *state.State, skipSnap string, testAliases map[string]bool, aliasConflicts map[string][]string, changing map[string]*SnapState) error {
snapStates, err := All(st)
if err != nil {
return err
}
for otherSnap, snapst := range snapStates {
if otherSnap == skipSnap {
// skip
continue
}
if nextSt, ok := changing[otherSnap]; ok {
snapst = nextSt
}
autoDisabled := snapst.AutoAliasesDisabled
var confls []string
if len(snapst.Aliases) < len(testAliases) {
for alias, target := range snapst.Aliases {
if testAliases[alias] && target.Effective(autoDisabled) != "" {
confls = append(confls, alias)
}
}
} else {
for alias := range testAliases {
target := snapst.Aliases[alias]
if target != nil && target.Effective(autoDisabled) != "" {
confls = append(confls, alias)
}
}
}
if len(confls) > 0 {
aliasConflicts[otherSnap] = confls
}
}
return nil
}
// checkAliasesStatConflicts checks candAliases considering
// candAutoDisabled for conflicts against other snap aliases returning
// conflicting snaps and aliases for alias conflicts.
// changing can specify about to be set states for some snaps that will
// then be considered.
func checkAliasesConflicts(st *state.State, snapName string, candAutoDisabled bool, candAliases map[string]*AliasTarget, changing map[string]*SnapState) (conflicts map[string][]string, err error) {
var snapNames map[string]*json.RawMessage
err = st.Get("snaps", &snapNames)
if err != nil && !errors.Is(err, state.ErrNoState) {
return nil, err
}
enabled := make(map[string]bool, len(candAliases))
for alias, candTarget := range candAliases {
if candTarget.Effective(candAutoDisabled) != "" {
enabled[alias] = true
} else {
continue
}
namespace := alias
if i := strings.IndexRune(alias, '.'); i != -1 {
namespace = alias[:i]
}
// check against snap namespaces
if snapNames[namespace] != nil {
return nil, &AliasConflictError{
Alias: alias,
Snap: snapName,
Reason: fmt.Sprintf("it conflicts with the command namespace of installed snap %q", namespace),
}
}
}
// check against enabled aliases
conflicts = make(map[string][]string)
if err := addAliasConflicts(st, snapName, enabled, conflicts, changing); err != nil {
return nil, err
}
if len(conflicts) != 0 {
return conflicts, &AliasConflictError{Snap: snapName, Conflicts: conflicts}
}
return nil, nil
}
// checkSnapAliasConflict checks whether instanceName and its command
// namepsace conflicts against installed snap aliases.
func checkSnapAliasConflict(st *state.State, instanceName string) error {
prefix := fmt.Sprintf("%s.", instanceName)
snapStates, err := All(st)
if err != nil {
return err
}
for otherSnap, snapst := range snapStates {
autoDisabled := snapst.AutoAliasesDisabled
for alias, target := range snapst.Aliases {
if alias == instanceName || strings.HasPrefix(alias, prefix) {
if target.Effective(autoDisabled) != "" {
return fmt.Errorf("snap %q command namespace conflicts with alias %q for %q snap", instanceName, alias, otherSnap)
}
}
}
}
return nil
}
// disableAliases returns newAliases corresponding to the disabling of
// curAliases, for manual aliases that means removed.
func disableAliases(curAliases map[string]*AliasTarget) (newAliases map[string]*AliasTarget, disabledManual map[string]string) {
newAliases = make(map[string]*AliasTarget, len(curAliases))
disabledManual = make(map[string]string, len(curAliases))
for alias, curTarget := range curAliases {
if curTarget.Manual != "" {
disabledManual[alias] = curTarget.Manual
}
if curTarget.Auto != "" {
newAliases[alias] = &AliasTarget{Auto: curTarget.Auto}
}
}
if len(disabledManual) == 0 {
disabledManual = nil
}
return newAliases, disabledManual
}
// reenableAliases returns newAliases corresponding to the re-enabling over
// curAliases of disabledManual manual aliases.
func reenableAliases(info *snap.Info, curAliases map[string]*AliasTarget, disabledManual map[string]string) (newAliases map[string]*AliasTarget) {
newAliases = make(map[string]*AliasTarget, len(curAliases))
for alias, aliasTarget := range curAliases {
newAliases[alias] = aliasTarget
}
for alias, manual := range disabledManual {
if app := info.Apps[manual]; app == nil || app.IsService() {
// not a non-daemon app presently
continue
}
newTarget := newAliases[alias]
if newTarget == nil {
newAliases[alias] = &AliasTarget{Manual: manual}
} else {
manualTarget := *newTarget
manualTarget.Manual = manual
newAliases[alias] = &manualTarget
}
}
return newAliases
}
// pruneAutoAliases returns newAliases by dropping the automatic
// aliases autoAliases from curAliases, used as the task
// prune-auto-aliases to handle transfers of automatic aliases in a
// refresh.
func pruneAutoAliases(curAliases map[string]*AliasTarget, autoAliases []string) (newAliases map[string]*AliasTarget) {
newAliases = make(map[string]*AliasTarget, len(curAliases))
for alias, aliasTarget := range curAliases {
newAliases[alias] = aliasTarget
}
for _, alias := range autoAliases {
curTarget := curAliases[alias]
if curTarget == nil {
// nothing to do
continue
}
if curTarget.Manual == "" {
delete(newAliases, alias)
} else {
newAliases[alias] = &AliasTarget{Manual: curTarget.Manual}
}
}
return newAliases
}
// transition to aliases v2
func (m *SnapManager) ensureAliasesV2() error {
m.state.Lock()
defer m.state.Unlock()
var aliasesV1 map[string]interface{}
err := m.state.Get("aliases", &aliasesV1)
if err != nil && !errors.Is(err, state.ErrNoState) {
return err
}
if len(aliasesV1) == 0 {
if err == nil { // something empty was there, delete it
m.state.Set("aliases", nil)
}
// nothing to do
return nil
}
snapStates, err := All(m.state)
if err != nil {
return err
}
// mark pending "alias" tasks as errored
// they were never parts of lanes but either standalone or at
// the start of wait chains
for _, t := range m.state.Tasks() {
if t.Kind() == "alias" && !t.Status().Ready() {
var param interface{}
err := t.Get("aliases", &param)
if errors.Is(err, state.ErrNoState) {
// not the old variant, leave alone
continue
}
t.Errorf("internal representation for aliases has changed, please retry")
t.SetStatus(state.ErrorStatus)
}
}
withAliases := make(map[string]*SnapState, len(snapStates))
for instanceName, snapst := range snapStates {
err := m.backend.RemoveSnapAliases(instanceName)
if err != nil {
logger.Noticef("cannot cleanup aliases for %q: %v", instanceName, err)
continue
}
info, err := snapst.CurrentInfo()
if err != nil {
logger.Noticef("cannot get info for %q: %v", instanceName, err)
continue
}
newAliases, err := refreshAliases(m.state, info, nil)
if err != nil {
logger.Noticef("cannot get automatic aliases for %q: %v", instanceName, err)
continue
}
// TODO: check for conflicts
if len(newAliases) != 0 {
snapst.Aliases = newAliases
withAliases[instanceName] = snapst
}
snapst.AutoAliasesDisabled = false
if !snapst.Active {
snapst.AliasesPending = true
}
}
for instanceName, snapst := range withAliases {
if !snapst.AliasesPending {
_, _, err := applyAliasesChange(instanceName, autoDis, nil, autoEn, snapst.Aliases, m.backend, doApply)
if err != nil {
// try to clean up and disable
logger.Noticef("cannot create automatic aliases for %q: %v", instanceName, err)
m.backend.RemoveSnapAliases(instanceName)
snapst.AutoAliasesDisabled = true
}
}
Set(m.state, instanceName, snapst)
}
m.state.Set("aliases", nil)
return nil
}
// Alias sets up a manual alias from alias to app in snapName.
func Alias(st *state.State, instanceName, app, alias string) (*state.TaskSet, error) {
if err := snap.ValidateAlias(alias); err != nil {
return nil, err
}
var snapst SnapState
err := Get(st, instanceName, &snapst)
if errors.Is(err, state.ErrNoState) {
return nil, &snap.NotInstalledError{Snap: instanceName}
}
if err != nil {
return nil, err
}
if err := CheckChangeConflict(st, instanceName, nil); err != nil {
return nil, err
}
snapName, instanceKey := snap.SplitInstanceName(instanceName)
snapsup := &SnapSetup{
SideInfo: &snap.SideInfo{RealName: snapName},
InstanceKey: instanceKey,
}
manualAlias := st.NewTask("alias", fmt.Sprintf(i18n.G("Setup manual alias %q => %q for snap %q"), alias, app, snapsup.InstanceName()))
manualAlias.Set("alias", alias)
manualAlias.Set("target", app)
manualAlias.Set("snap-setup", &snapsup)
return state.NewTaskSet(manualAlias), nil
}
// manualAliases returns newAliases with a manual alias to target setup over
// curAliases.
func manualAlias(info *snap.Info, curAliases map[string]*AliasTarget, target, alias string) (newAliases map[string]*AliasTarget, err error) {
if app := info.Apps[target]; app == nil || app.IsService() {
var reason string
if app == nil {
reason = fmt.Sprintf("target application %q does not exist", target)
} else {
reason = fmt.Sprintf("target application %q is a daemon", target)
}
return nil, fmt.Errorf("cannot enable alias %q for %q, %s", alias, info.InstanceName(), reason)
}
newAliases = make(map[string]*AliasTarget, len(curAliases))
for alias, aliasTarget := range curAliases {
newAliases[alias] = aliasTarget
}
newTarget := newAliases[alias]
if newTarget == nil {
newAliases[alias] = &AliasTarget{Manual: target}
} else {
manualTarget := *newTarget
manualTarget.Manual = target
newAliases[alias] = &manualTarget
}
return newAliases, nil
}
// DisableAllAliases disables all aliases of a snap, removing all manual ones.
func DisableAllAliases(st *state.State, instanceName string) (*state.TaskSet, error) {
var snapst SnapState
err := Get(st, instanceName, &snapst)
if errors.Is(err, state.ErrNoState) {
return nil, &snap.NotInstalledError{Snap: instanceName}
}
if err != nil {
return nil, err
}
if err := CheckChangeConflict(st, instanceName, nil); err != nil {
return nil, err
}
snapName, instanceKey := snap.SplitInstanceName(instanceName)
snapsup := &SnapSetup{
SideInfo: &snap.SideInfo{RealName: snapName},
InstanceKey: instanceKey,
}
disableAll := st.NewTask("disable-aliases", fmt.Sprintf(i18n.G("Disable aliases for snap %q"), instanceName))
disableAll.Set("snap-setup", &snapsup)
return state.NewTaskSet(disableAll), nil
}
// RemoveManualAlias removes a manual alias.
func RemoveManualAlias(st *state.State, alias string) (ts *state.TaskSet, instanceName string, err error) {
instanceName, err = findSnapOfManualAlias(st, alias)
if err != nil {
return nil, "", err
}
if err := CheckChangeConflict(st, instanceName, nil); err != nil {
return nil, "", err
}
snapName, instanceKey := snap.SplitInstanceName(instanceName)
snapsup := &SnapSetup{
SideInfo: &snap.SideInfo{RealName: snapName},
InstanceKey: instanceKey,
}
unalias := st.NewTask("unalias", fmt.Sprintf(i18n.G("Remove manual alias %q for snap %q"), alias, instanceName))
unalias.Set("alias", alias)
unalias.Set("snap-setup", &snapsup)
return state.NewTaskSet(unalias), instanceName, nil
}
func findSnapOfManualAlias(st *state.State, alias string) (snapName string, err error) {
snapStates, err := All(st)
if err != nil {
return "", err
}
for instanceName, snapst := range snapStates {
target := snapst.Aliases[alias]
if target != nil && target.Manual != "" {
return instanceName, nil
}
}
return "", fmt.Errorf("cannot find manual alias %q in any snap", alias)
}
// manualUnalias returns newAliases with the manual alias removed from
// curAliases.
func manualUnalias(curAliases map[string]*AliasTarget, alias string) (newAliases map[string]*AliasTarget, err error) {
newTarget := curAliases[alias]
if newTarget == nil {
return nil, fmt.Errorf("no alias %q", alias)
}
newAliases = make(map[string]*AliasTarget, len(curAliases))
for alias, aliasTarget := range curAliases {
newAliases[alias] = aliasTarget
}
if newTarget.Auto == "" {
delete(newAliases, alias)
} else {
newAliases[alias] = &AliasTarget{Auto: newTarget.Auto}
}
return newAliases, nil
}
// Prefer enables all aliases of a snap in preference to conflicting aliases
// of other snaps whose aliases will be disabled (removed for manual ones).
func Prefer(st *state.State, name string) (*state.TaskSet, error) {
var snapst SnapState
err := Get(st, name, &snapst)
if errors.Is(err, state.ErrNoState) {
return nil, &snap.NotInstalledError{Snap: name}
}
if err != nil {
return nil, err
}
if err := CheckChangeConflict(st, name, nil); err != nil {
return nil, err
}
snapName, instanceKey := snap.SplitInstanceName(name)
snapsup := &SnapSetup{
SideInfo: &snap.SideInfo{RealName: snapName},
InstanceKey: instanceKey,
}
prefer := st.NewTask("prefer-aliases", fmt.Sprintf(i18n.G("Prefer aliases for snap %q"), name))
prefer.Set("snap-setup", &snapsup)
return state.NewTaskSet(prefer), nil
}
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
Go
1
https://gitee.com/mysnapcore/mysnapd.git
git@gitee.com:mysnapcore/mysnapd.git
mysnapcore
mysnapd
mysnapd
v0.1.0

Search