github.com/bugraaydogar/snapd@v0.0.0-20210315170335-8c70bb858939/overlord/snapstate/aliasesv2.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2016-2017 Canonical Ltd
     5   *
     6   * This program is free software: you can redistribute it and/or modify
     7   * it under the terms of the GNU General Public License version 3 as
     8   * published by the Free Software Foundation.
     9   *
    10   * This program is distributed in the hope that it will be useful,
    11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13   * GNU General Public License for more details.
    14   *
    15   * You should have received a copy of the GNU General Public License
    16   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17   *
    18   */
    19  
    20  package snapstate
    21  
    22  import (
    23  	"encoding/json"
    24  	"fmt"
    25  	"strings"
    26  
    27  	"github.com/snapcore/snapd/i18n"
    28  	"github.com/snapcore/snapd/logger"
    29  	"github.com/snapcore/snapd/overlord/snapstate/backend"
    30  	"github.com/snapcore/snapd/overlord/state"
    31  	"github.com/snapcore/snapd/snap"
    32  	"github.com/snapcore/snapd/strutil"
    33  )
    34  
    35  // AliasTarget carries the targets of an alias in the context of snap.
    36  // If Manual is set it is the target of an enabled manual alias.
    37  // Auto is set to the target for an automatic alias, enabled or
    38  // disabled depending on the automatic aliases flag state.
    39  type AliasTarget struct {
    40  	Manual string `json:"manual,omitempty"`
    41  	Auto   string `json:"auto,omitempty"`
    42  }
    43  
    44  // Effective returns the target to use considering whether automatic
    45  // aliases are disabled for the whole snap (autoDisabled), returns ""
    46  // if the alias is disabled.
    47  func (at *AliasTarget) Effective(autoDisabled bool) string {
    48  	if at == nil {
    49  		return ""
    50  	}
    51  	if at.Manual != "" {
    52  		return at.Manual
    53  	}
    54  	if !autoDisabled {
    55  		return at.Auto
    56  	}
    57  	return ""
    58  }
    59  
    60  /*
    61     State for aliases for a snap is tracked in SnapState with:
    62  
    63  	type SnapState struct {
    64                  ...
    65  		Aliases              map[string]*AliasTarget
    66  		AutoAliasesDisabled  bool
    67  	}
    68  
    69     There are two kinds of aliases:
    70  
    71     * automatic aliases listed with their target application in the
    72       snap-declaration of the snap (using AliasTarget.Auto)
    73  
    74     * manual aliases setup with "snap alias SNAP.APP ALIAS" (tracked
    75       using AliasTarget.Manual)
    76  
    77     Further
    78  
    79     * all automatic aliases of a snap are either enabled
    80       or disabled together (tracked with AutoAliasesDisabled)
    81  
    82     * disabling a manual alias removes it from disk and state (for
    83       simplicity there is no disabled state for manual aliases)
    84  
    85     * an AliasTarget with both Auto and Manual set is a manual alias
    86       that has the same name as an automatic one, the manual target
    87       is what wins
    88  
    89  */
    90  
    91  // autoDisabled options and doApply
    92  const (
    93  	autoDis = true
    94  	autoEn  = false
    95  
    96  	doApply = false
    97  )
    98  
    99  // applyAliasesChange applies the necessary changes to aliases on disk
   100  // to go from prevAliases considering the automatic aliases flag
   101  // (prevAutoDisabled) to newAliases considering newAutoDisabled for
   102  // snapName. It assumes that conflicts have already been checked.
   103  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) {
   104  	for alias, prevTargets := range prevAliases {
   105  		if _, ok := newAliases[alias]; ok {
   106  			continue
   107  		}
   108  		// gone
   109  		if effTgt := prevTargets.Effective(prevAutoDisabled); effTgt != "" {
   110  			remove = append(remove, &backend.Alias{
   111  				Name:   alias,
   112  				Target: snap.JoinSnapApp(snapName, effTgt),
   113  			})
   114  		}
   115  	}
   116  	for alias, newTargets := range newAliases {
   117  		prevTgt := prevAliases[alias].Effective(prevAutoDisabled)
   118  		newTgt := newTargets.Effective(newAutoDisabled)
   119  		if prevTgt == newTgt {
   120  			// nothing to do
   121  			continue
   122  		}
   123  		if prevTgt != "" {
   124  			remove = append(remove, &backend.Alias{
   125  				Name:   alias,
   126  				Target: snap.JoinSnapApp(snapName, prevTgt),
   127  			})
   128  		}
   129  		if newTgt != "" {
   130  			add = append(add, &backend.Alias{
   131  				Name:   alias,
   132  				Target: snap.JoinSnapApp(snapName, newTgt),
   133  			})
   134  		}
   135  	}
   136  	if !dryRun {
   137  		if err := be.UpdateAliases(add, remove); err != nil {
   138  			return nil, nil, err
   139  		}
   140  	}
   141  	return add, remove, nil
   142  }
   143  
   144  // AutoAliases allows to hook support for retrieving the automatic aliases of a snap.
   145  var AutoAliases func(st *state.State, info *snap.Info) (map[string]string, error)
   146  
   147  // autoAliasesDelta compares the automatic aliases with the current snap
   148  // declaration for the installed snaps with the given names (or all if
   149  // names is empty) and returns changed and dropped auto-aliases by
   150  // snap name.
   151  func autoAliasesDelta(st *state.State, names []string) (changed map[string][]string, dropped map[string][]string, err error) {
   152  	var snapStates map[string]*SnapState
   153  	if len(names) == 0 {
   154  		var err error
   155  		snapStates, err = All(st)
   156  		if err != nil {
   157  			return nil, nil, err
   158  		}
   159  	} else {
   160  		snapStates = make(map[string]*SnapState, len(names))
   161  		for _, name := range names {
   162  			var snapst SnapState
   163  			err := Get(st, name, &snapst)
   164  			if err != nil {
   165  				return nil, nil, err
   166  			}
   167  			snapStates[name] = &snapst
   168  		}
   169  	}
   170  	var firstErr error
   171  	changed = make(map[string][]string)
   172  	dropped = make(map[string][]string)
   173  	for instanceName, snapst := range snapStates {
   174  		aliases := snapst.Aliases
   175  		info, err := snapst.CurrentInfo()
   176  		if err != nil {
   177  			if firstErr == nil {
   178  				firstErr = err
   179  			}
   180  			continue
   181  		}
   182  		autoAliases, err := AutoAliases(st, info)
   183  		if err != nil {
   184  			if firstErr == nil {
   185  				firstErr = err
   186  			}
   187  			continue
   188  		}
   189  		for alias, target := range autoAliases {
   190  			curTarget := aliases[alias]
   191  			if curTarget == nil || curTarget.Auto != target {
   192  				changed[instanceName] = append(changed[instanceName], alias)
   193  			}
   194  		}
   195  		for alias, target := range aliases {
   196  			if target.Auto != "" && autoAliases[alias] == "" {
   197  				dropped[instanceName] = append(dropped[instanceName], alias)
   198  			}
   199  		}
   200  	}
   201  	return changed, dropped, firstErr
   202  }
   203  
   204  // refreshAliases applies the current snap-declaration aliases
   205  // considering which applications exist in info and produces new aliases
   206  // for the snap.
   207  func refreshAliases(st *state.State, info *snap.Info, curAliases map[string]*AliasTarget) (newAliases map[string]*AliasTarget, err error) {
   208  	autoAliases, err := AutoAliases(st, info)
   209  	if err != nil {
   210  		return nil, err
   211  	}
   212  
   213  	newAliases = make(map[string]*AliasTarget, len(autoAliases))
   214  	// apply the current auto-aliases
   215  	for alias, target := range autoAliases {
   216  		if app := info.Apps[target]; app == nil || app.IsService() {
   217  			// non-existing app or a daemon
   218  			continue
   219  		}
   220  		newAliases[alias] = &AliasTarget{Auto: target}
   221  	}
   222  
   223  	// carry over the current manual ones
   224  	for alias, curTarget := range curAliases {
   225  		if curTarget.Manual == "" {
   226  			continue
   227  		}
   228  		if app := info.Apps[curTarget.Manual]; app == nil || app.IsService() {
   229  			// non-existing app or daemon
   230  			continue
   231  		}
   232  		newTarget := newAliases[alias]
   233  		if newTarget == nil {
   234  			newAliases[alias] = &AliasTarget{Manual: curTarget.Manual}
   235  		} else {
   236  			// alias is both manually setup but has an underlying auto-alias
   237  			newAliases[alias].Manual = curTarget.Manual
   238  		}
   239  	}
   240  	return newAliases, nil
   241  }
   242  
   243  type AliasConflictError struct {
   244  	Snap      string
   245  	Alias     string
   246  	Reason    string
   247  	Conflicts map[string][]string
   248  }
   249  
   250  func (e *AliasConflictError) Error() string {
   251  	if len(e.Conflicts) != 0 {
   252  		errParts := []string{"cannot enable"}
   253  		first := true
   254  		for instanceName, aliases := range e.Conflicts {
   255  			if !first {
   256  				errParts = append(errParts, "nor")
   257  			}
   258  			if len(aliases) == 1 {
   259  				errParts = append(errParts, fmt.Sprintf("alias %q", aliases[0]))
   260  			} else {
   261  				errParts = append(errParts, fmt.Sprintf("aliases %s", strutil.Quoted(aliases)))
   262  			}
   263  			if first {
   264  				errParts = append(errParts, fmt.Sprintf("for %q,", e.Snap))
   265  				first = false
   266  			}
   267  			errParts = append(errParts, fmt.Sprintf("already enabled for %q", instanceName))
   268  		}
   269  		// TODO: add recommendation about what to do next
   270  		return strings.Join(errParts, " ")
   271  	}
   272  	return fmt.Sprintf("cannot enable alias %q for %q, %s", e.Alias, e.Snap, e.Reason)
   273  }
   274  
   275  func addAliasConflicts(st *state.State, skipSnap string, testAliases map[string]bool, aliasConflicts map[string][]string, changing map[string]*SnapState) error {
   276  	snapStates, err := All(st)
   277  	if err != nil {
   278  		return err
   279  	}
   280  	for otherSnap, snapst := range snapStates {
   281  		if otherSnap == skipSnap {
   282  			// skip
   283  			continue
   284  		}
   285  		if nextSt, ok := changing[otherSnap]; ok {
   286  			snapst = nextSt
   287  		}
   288  		autoDisabled := snapst.AutoAliasesDisabled
   289  		var confls []string
   290  		if len(snapst.Aliases) < len(testAliases) {
   291  			for alias, target := range snapst.Aliases {
   292  				if testAliases[alias] && target.Effective(autoDisabled) != "" {
   293  					confls = append(confls, alias)
   294  				}
   295  			}
   296  		} else {
   297  			for alias := range testAliases {
   298  				target := snapst.Aliases[alias]
   299  				if target != nil && target.Effective(autoDisabled) != "" {
   300  					confls = append(confls, alias)
   301  				}
   302  			}
   303  		}
   304  		if len(confls) > 0 {
   305  			aliasConflicts[otherSnap] = confls
   306  		}
   307  	}
   308  	return nil
   309  }
   310  
   311  // checkAliasesStatConflicts checks candAliases considering
   312  // candAutoDisabled for conflicts against other snap aliases returning
   313  // conflicting snaps and aliases for alias conflicts.
   314  // changing can specify about to be set states for some snaps that will
   315  // then be considered.
   316  func checkAliasesConflicts(st *state.State, snapName string, candAutoDisabled bool, candAliases map[string]*AliasTarget, changing map[string]*SnapState) (conflicts map[string][]string, err error) {
   317  	var snapNames map[string]*json.RawMessage
   318  	err = st.Get("snaps", &snapNames)
   319  	if err != nil && err != state.ErrNoState {
   320  		return nil, err
   321  	}
   322  
   323  	enabled := make(map[string]bool, len(candAliases))
   324  	for alias, candTarget := range candAliases {
   325  		if candTarget.Effective(candAutoDisabled) != "" {
   326  			enabled[alias] = true
   327  		} else {
   328  			continue
   329  		}
   330  		namespace := alias
   331  		if i := strings.IndexRune(alias, '.'); i != -1 {
   332  			namespace = alias[:i]
   333  		}
   334  		// check against snap namespaces
   335  		if snapNames[namespace] != nil {
   336  			return nil, &AliasConflictError{
   337  				Alias:  alias,
   338  				Snap:   snapName,
   339  				Reason: fmt.Sprintf("it conflicts with the command namespace of installed snap %q", namespace),
   340  			}
   341  		}
   342  	}
   343  
   344  	// check against enabled aliases
   345  	conflicts = make(map[string][]string)
   346  	if err := addAliasConflicts(st, snapName, enabled, conflicts, changing); err != nil {
   347  		return nil, err
   348  	}
   349  	if len(conflicts) != 0 {
   350  		return conflicts, &AliasConflictError{Snap: snapName, Conflicts: conflicts}
   351  	}
   352  	return nil, nil
   353  }
   354  
   355  // checkSnapAliasConflict checks whether instanceName and its command
   356  // namepsace conflicts against installed snap aliases.
   357  func checkSnapAliasConflict(st *state.State, instanceName string) error {
   358  	prefix := fmt.Sprintf("%s.", instanceName)
   359  	snapStates, err := All(st)
   360  	if err != nil {
   361  		return err
   362  	}
   363  	for otherSnap, snapst := range snapStates {
   364  		autoDisabled := snapst.AutoAliasesDisabled
   365  		for alias, target := range snapst.Aliases {
   366  			if alias == instanceName || strings.HasPrefix(alias, prefix) {
   367  				if target.Effective(autoDisabled) != "" {
   368  					return fmt.Errorf("snap %q command namespace conflicts with alias %q for %q snap", instanceName, alias, otherSnap)
   369  				}
   370  			}
   371  		}
   372  	}
   373  	return nil
   374  }
   375  
   376  // disableAliases returns newAliases corresponding to the disabling of
   377  // curAliases, for manual aliases that means removed.
   378  func disableAliases(curAliases map[string]*AliasTarget) (newAliases map[string]*AliasTarget, disabledManual map[string]string) {
   379  	newAliases = make(map[string]*AliasTarget, len(curAliases))
   380  	disabledManual = make(map[string]string, len(curAliases))
   381  	for alias, curTarget := range curAliases {
   382  		if curTarget.Manual != "" {
   383  			disabledManual[alias] = curTarget.Manual
   384  		}
   385  		if curTarget.Auto != "" {
   386  			newAliases[alias] = &AliasTarget{Auto: curTarget.Auto}
   387  		}
   388  	}
   389  	if len(disabledManual) == 0 {
   390  		disabledManual = nil
   391  	}
   392  	return newAliases, disabledManual
   393  }
   394  
   395  // reenableAliases returns newAliases corresponding to the re-enabling over
   396  // curAliases of disabledManual manual aliases.
   397  func reenableAliases(info *snap.Info, curAliases map[string]*AliasTarget, disabledManual map[string]string) (newAliases map[string]*AliasTarget) {
   398  	newAliases = make(map[string]*AliasTarget, len(curAliases))
   399  	for alias, aliasTarget := range curAliases {
   400  		newAliases[alias] = aliasTarget
   401  	}
   402  
   403  	for alias, manual := range disabledManual {
   404  		if app := info.Apps[manual]; app == nil || app.IsService() {
   405  			// not a non-daemon app presently
   406  			continue
   407  		}
   408  
   409  		newTarget := newAliases[alias]
   410  		if newTarget == nil {
   411  			newAliases[alias] = &AliasTarget{Manual: manual}
   412  		} else {
   413  			manualTarget := *newTarget
   414  			manualTarget.Manual = manual
   415  			newAliases[alias] = &manualTarget
   416  		}
   417  	}
   418  
   419  	return newAliases
   420  }
   421  
   422  // pruneAutoAliases returns newAliases by dropping the automatic
   423  // aliases autoAliases from curAliases, used as the task
   424  // prune-auto-aliases to handle transfers of automatic aliases in a
   425  // refresh.
   426  func pruneAutoAliases(curAliases map[string]*AliasTarget, autoAliases []string) (newAliases map[string]*AliasTarget) {
   427  	newAliases = make(map[string]*AliasTarget, len(curAliases))
   428  	for alias, aliasTarget := range curAliases {
   429  		newAliases[alias] = aliasTarget
   430  	}
   431  	for _, alias := range autoAliases {
   432  		curTarget := curAliases[alias]
   433  		if curTarget == nil {
   434  			// nothing to do
   435  			continue
   436  		}
   437  		if curTarget.Manual == "" {
   438  			delete(newAliases, alias)
   439  		} else {
   440  			newAliases[alias] = &AliasTarget{Manual: curTarget.Manual}
   441  		}
   442  	}
   443  	return newAliases
   444  }
   445  
   446  // transition to aliases v2
   447  func (m *SnapManager) ensureAliasesV2() error {
   448  	m.state.Lock()
   449  	defer m.state.Unlock()
   450  
   451  	var aliasesV1 map[string]interface{}
   452  	err := m.state.Get("aliases", &aliasesV1)
   453  	if err != nil && err != state.ErrNoState {
   454  		return err
   455  	}
   456  	if len(aliasesV1) == 0 {
   457  		if err == nil { // something empty was there, delete it
   458  			m.state.Set("aliases", nil)
   459  		}
   460  		// nothing to do
   461  		return nil
   462  	}
   463  
   464  	snapStates, err := All(m.state)
   465  	if err != nil {
   466  		return err
   467  	}
   468  
   469  	// mark pending "alias" tasks as errored
   470  	// they were never parts of lanes but either standalone or at
   471  	// the start of wait chains
   472  	for _, t := range m.state.Tasks() {
   473  		if t.Kind() == "alias" && !t.Status().Ready() {
   474  			var param interface{}
   475  			err := t.Get("aliases", &param)
   476  			if err == state.ErrNoState {
   477  				// not the old variant, leave alone
   478  				continue
   479  			}
   480  			t.Errorf("internal representation for aliases has changed, please retry")
   481  			t.SetStatus(state.ErrorStatus)
   482  		}
   483  	}
   484  
   485  	withAliases := make(map[string]*SnapState, len(snapStates))
   486  	for instanceName, snapst := range snapStates {
   487  		err := m.backend.RemoveSnapAliases(instanceName)
   488  		if err != nil {
   489  			logger.Noticef("cannot cleanup aliases for %q: %v", instanceName, err)
   490  			continue
   491  		}
   492  
   493  		info, err := snapst.CurrentInfo()
   494  		if err != nil {
   495  			logger.Noticef("cannot get info for %q: %v", instanceName, err)
   496  			continue
   497  		}
   498  		newAliases, err := refreshAliases(m.state, info, nil)
   499  		if err != nil {
   500  			logger.Noticef("cannot get automatic aliases for %q: %v", instanceName, err)
   501  			continue
   502  		}
   503  		// TODO: check for conflicts
   504  		if len(newAliases) != 0 {
   505  			snapst.Aliases = newAliases
   506  			withAliases[instanceName] = snapst
   507  		}
   508  		snapst.AutoAliasesDisabled = false
   509  		if !snapst.Active {
   510  			snapst.AliasesPending = true
   511  		}
   512  	}
   513  
   514  	for instanceName, snapst := range withAliases {
   515  		if !snapst.AliasesPending {
   516  			_, _, err := applyAliasesChange(instanceName, autoDis, nil, autoEn, snapst.Aliases, m.backend, doApply)
   517  			if err != nil {
   518  				// try to clean up and disable
   519  				logger.Noticef("cannot create automatic aliases for %q: %v", instanceName, err)
   520  				m.backend.RemoveSnapAliases(instanceName)
   521  				snapst.AutoAliasesDisabled = true
   522  			}
   523  		}
   524  		Set(m.state, instanceName, snapst)
   525  	}
   526  
   527  	m.state.Set("aliases", nil)
   528  	return nil
   529  }
   530  
   531  // Alias sets up a manual alias from alias to app in snapName.
   532  func Alias(st *state.State, instanceName, app, alias string) (*state.TaskSet, error) {
   533  	if err := snap.ValidateAlias(alias); err != nil {
   534  		return nil, err
   535  	}
   536  
   537  	var snapst SnapState
   538  	err := Get(st, instanceName, &snapst)
   539  	if err == state.ErrNoState {
   540  		return nil, &snap.NotInstalledError{Snap: instanceName}
   541  	}
   542  	if err != nil {
   543  		return nil, err
   544  	}
   545  	if err := CheckChangeConflict(st, instanceName, nil); err != nil {
   546  		return nil, err
   547  	}
   548  
   549  	snapName, instanceKey := snap.SplitInstanceName(instanceName)
   550  	snapsup := &SnapSetup{
   551  		SideInfo:    &snap.SideInfo{RealName: snapName},
   552  		InstanceKey: instanceKey,
   553  	}
   554  
   555  	manualAlias := st.NewTask("alias", fmt.Sprintf(i18n.G("Setup manual alias %q => %q for snap %q"), alias, app, snapsup.InstanceName()))
   556  	manualAlias.Set("alias", alias)
   557  	manualAlias.Set("target", app)
   558  	manualAlias.Set("snap-setup", &snapsup)
   559  
   560  	return state.NewTaskSet(manualAlias), nil
   561  }
   562  
   563  // manualAliases returns newAliases with a manual alias to target setup over
   564  // curAliases.
   565  func manualAlias(info *snap.Info, curAliases map[string]*AliasTarget, target, alias string) (newAliases map[string]*AliasTarget, err error) {
   566  	if app := info.Apps[target]; app == nil || app.IsService() {
   567  		var reason string
   568  		if app == nil {
   569  			reason = fmt.Sprintf("target application %q does not exist", target)
   570  		} else {
   571  			reason = fmt.Sprintf("target application %q is a daemon", target)
   572  		}
   573  		return nil, fmt.Errorf("cannot enable alias %q for %q, %s", alias, info.InstanceName(), reason)
   574  	}
   575  	newAliases = make(map[string]*AliasTarget, len(curAliases))
   576  	for alias, aliasTarget := range curAliases {
   577  		newAliases[alias] = aliasTarget
   578  	}
   579  
   580  	newTarget := newAliases[alias]
   581  	if newTarget == nil {
   582  		newAliases[alias] = &AliasTarget{Manual: target}
   583  	} else {
   584  		manualTarget := *newTarget
   585  		manualTarget.Manual = target
   586  		newAliases[alias] = &manualTarget
   587  	}
   588  
   589  	return newAliases, nil
   590  }
   591  
   592  // DisableAllAliases disables all aliases of a snap, removing all manual ones.
   593  func DisableAllAliases(st *state.State, instanceName string) (*state.TaskSet, error) {
   594  	var snapst SnapState
   595  	err := Get(st, instanceName, &snapst)
   596  	if err == state.ErrNoState {
   597  		return nil, &snap.NotInstalledError{Snap: instanceName}
   598  	}
   599  	if err != nil {
   600  		return nil, err
   601  	}
   602  
   603  	if err := CheckChangeConflict(st, instanceName, nil); err != nil {
   604  		return nil, err
   605  	}
   606  
   607  	snapName, instanceKey := snap.SplitInstanceName(instanceName)
   608  	snapsup := &SnapSetup{
   609  		SideInfo:    &snap.SideInfo{RealName: snapName},
   610  		InstanceKey: instanceKey,
   611  	}
   612  
   613  	disableAll := st.NewTask("disable-aliases", fmt.Sprintf(i18n.G("Disable aliases for snap %q"), instanceName))
   614  	disableAll.Set("snap-setup", &snapsup)
   615  
   616  	return state.NewTaskSet(disableAll), nil
   617  }
   618  
   619  // RemoveManualAlias removes a manual alias.
   620  func RemoveManualAlias(st *state.State, alias string) (ts *state.TaskSet, instanceName string, err error) {
   621  	instanceName, err = findSnapOfManualAlias(st, alias)
   622  	if err != nil {
   623  		return nil, "", err
   624  	}
   625  
   626  	if err := CheckChangeConflict(st, instanceName, nil); err != nil {
   627  		return nil, "", err
   628  	}
   629  
   630  	snapName, instanceKey := snap.SplitInstanceName(instanceName)
   631  	snapsup := &SnapSetup{
   632  		SideInfo:    &snap.SideInfo{RealName: snapName},
   633  		InstanceKey: instanceKey,
   634  	}
   635  
   636  	unalias := st.NewTask("unalias", fmt.Sprintf(i18n.G("Remove manual alias %q for snap %q"), alias, instanceName))
   637  	unalias.Set("alias", alias)
   638  	unalias.Set("snap-setup", &snapsup)
   639  
   640  	return state.NewTaskSet(unalias), instanceName, nil
   641  }
   642  
   643  func findSnapOfManualAlias(st *state.State, alias string) (snapName string, err error) {
   644  	snapStates, err := All(st)
   645  	if err != nil {
   646  		return "", err
   647  	}
   648  	for instanceName, snapst := range snapStates {
   649  		target := snapst.Aliases[alias]
   650  		if target != nil && target.Manual != "" {
   651  			return instanceName, nil
   652  		}
   653  	}
   654  	return "", fmt.Errorf("cannot find manual alias %q in any snap", alias)
   655  }
   656  
   657  // manualUnalias returns newAliases with the manual alias removed from
   658  // curAliases.
   659  func manualUnalias(curAliases map[string]*AliasTarget, alias string) (newAliases map[string]*AliasTarget, err error) {
   660  	newTarget := curAliases[alias]
   661  	if newTarget == nil {
   662  		return nil, fmt.Errorf("no alias %q", alias)
   663  	}
   664  	newAliases = make(map[string]*AliasTarget, len(curAliases))
   665  	for alias, aliasTarget := range curAliases {
   666  		newAliases[alias] = aliasTarget
   667  	}
   668  
   669  	if newTarget.Auto == "" {
   670  		delete(newAliases, alias)
   671  	} else {
   672  		newAliases[alias] = &AliasTarget{Auto: newTarget.Auto}
   673  	}
   674  
   675  	return newAliases, nil
   676  }
   677  
   678  // Prefer enables all aliases of a snap in preference to conflicting aliases
   679  // of other snaps whose aliases will be disabled (removed for manual ones).
   680  func Prefer(st *state.State, name string) (*state.TaskSet, error) {
   681  	var snapst SnapState
   682  	err := Get(st, name, &snapst)
   683  	if err == state.ErrNoState {
   684  		return nil, &snap.NotInstalledError{Snap: name}
   685  	}
   686  	if err != nil {
   687  		return nil, err
   688  	}
   689  
   690  	if err := CheckChangeConflict(st, name, nil); err != nil {
   691  		return nil, err
   692  	}
   693  
   694  	snapName, instanceKey := snap.SplitInstanceName(name)
   695  	snapsup := &SnapSetup{
   696  		SideInfo:    &snap.SideInfo{RealName: snapName},
   697  		InstanceKey: instanceKey,
   698  	}
   699  
   700  	prefer := st.NewTask("prefer-aliases", fmt.Sprintf(i18n.G("Prefer aliases for snap %q"), name))
   701  	prefer.Set("snap-setup", &snapsup)
   702  
   703  	return state.NewTaskSet(prefer), nil
   704  }