github.com/bugraaydogar/snapd@v0.0.0-20210315170335-8c70bb858939/daemon/api_aliases.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2015-2020 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 daemon
    21  
    22  import (
    23  	"encoding/json"
    24  	"fmt"
    25  	"net/http"
    26  
    27  	"github.com/snapcore/snapd/i18n"
    28  	"github.com/snapcore/snapd/overlord/auth"
    29  	"github.com/snapcore/snapd/overlord/snapstate"
    30  	"github.com/snapcore/snapd/overlord/state"
    31  	"github.com/snapcore/snapd/snap"
    32  )
    33  
    34  var (
    35  	aliasesCmd = &Command{
    36  		Path:   "/v2/aliases",
    37  		UserOK: true,
    38  		GET:    getAliases,
    39  		POST:   changeAliases,
    40  	}
    41  )
    42  
    43  // aliasAction is an action performed on aliases
    44  type aliasAction struct {
    45  	Action string `json:"action"`
    46  	Snap   string `json:"snap"`
    47  	App    string `json:"app"`
    48  	Alias  string `json:"alias"`
    49  	// old now unsupported api
    50  	Aliases []string `json:"aliases"`
    51  }
    52  
    53  func changeAliases(c *Command, r *http.Request, user *auth.UserState) Response {
    54  	var a aliasAction
    55  	decoder := json.NewDecoder(r.Body)
    56  	if err := decoder.Decode(&a); err != nil {
    57  		return BadRequest("cannot decode request body into an alias action: %v", err)
    58  	}
    59  	if len(a.Aliases) != 0 {
    60  		return BadRequest("cannot interpret request, snaps can no longer be expected to declare their aliases")
    61  	}
    62  
    63  	var taskset *state.TaskSet
    64  	var err error
    65  
    66  	st := c.d.overlord.State()
    67  	st.Lock()
    68  	defer st.Unlock()
    69  
    70  	switch a.Action {
    71  	default:
    72  		return BadRequest("unsupported alias action: %q", a.Action)
    73  	case "alias":
    74  		taskset, err = snapstate.Alias(st, a.Snap, a.App, a.Alias)
    75  	case "unalias":
    76  		if a.Alias == a.Snap {
    77  			// Do What I mean:
    78  			// check if a snap is referred/intended
    79  			// or just an alias
    80  			var snapst snapstate.SnapState
    81  			err := snapstate.Get(st, a.Snap, &snapst)
    82  			if err != nil && err != state.ErrNoState {
    83  				return InternalError("%v", err)
    84  			}
    85  			if err == state.ErrNoState { // not a snap
    86  				a.Snap = ""
    87  			}
    88  		}
    89  		if a.Snap != "" {
    90  			a.Alias = ""
    91  			taskset, err = snapstate.DisableAllAliases(st, a.Snap)
    92  		} else {
    93  			taskset, a.Snap, err = snapstate.RemoveManualAlias(st, a.Alias)
    94  		}
    95  	case "prefer":
    96  		taskset, err = snapstate.Prefer(st, a.Snap)
    97  	}
    98  	if err != nil {
    99  		return errToResponse(err, nil, BadRequest, "%v")
   100  	}
   101  
   102  	var summary string
   103  	switch a.Action {
   104  	case "alias":
   105  		summary = fmt.Sprintf(i18n.G("Setup alias %q => %q for snap %q"), a.Alias, a.App, a.Snap)
   106  	case "unalias":
   107  		if a.Alias != "" {
   108  			summary = fmt.Sprintf(i18n.G("Remove manual alias %q for snap %q"), a.Alias, a.Snap)
   109  		} else {
   110  			summary = fmt.Sprintf(i18n.G("Disable all aliases for snap %q"), a.Snap)
   111  		}
   112  	case "prefer":
   113  		summary = fmt.Sprintf(i18n.G("Prefer aliases of snap %q"), a.Snap)
   114  	}
   115  
   116  	change := newChange(st, a.Action, summary, []*state.TaskSet{taskset}, []string{a.Snap})
   117  	st.EnsureBefore(0)
   118  
   119  	return AsyncResponse(nil, &Meta{Change: change.ID()})
   120  }
   121  
   122  type aliasStatus struct {
   123  	Command string `json:"command"`
   124  	Status  string `json:"status"`
   125  	Manual  string `json:"manual,omitempty"`
   126  	Auto    string `json:"auto,omitempty"`
   127  }
   128  
   129  // getAliases produces a response with a map snap -> alias -> aliasStatus
   130  func getAliases(c *Command, r *http.Request, user *auth.UserState) Response {
   131  	state := c.d.overlord.State()
   132  	state.Lock()
   133  	defer state.Unlock()
   134  
   135  	res := make(map[string]map[string]aliasStatus)
   136  
   137  	allStates, err := snapstate.All(state)
   138  	if err != nil {
   139  		return InternalError("cannot list local snaps: %v", err)
   140  	}
   141  
   142  	for snapName, snapst := range allStates {
   143  		if err != nil {
   144  			return InternalError("cannot retrieve info for snap %q: %v", snapName, err)
   145  		}
   146  		if len(snapst.Aliases) != 0 {
   147  			snapAliases := make(map[string]aliasStatus)
   148  			res[snapName] = snapAliases
   149  			autoDisabled := snapst.AutoAliasesDisabled
   150  			for alias, aliasTarget := range snapst.Aliases {
   151  				aliasStatus := aliasStatus{
   152  					Manual: aliasTarget.Manual,
   153  					Auto:   aliasTarget.Auto,
   154  				}
   155  				status := "auto"
   156  				tgt := aliasTarget.Effective(autoDisabled)
   157  				if tgt == "" {
   158  					status = "disabled"
   159  					tgt = aliasTarget.Auto
   160  				} else if aliasTarget.Manual != "" {
   161  					status = "manual"
   162  				}
   163  				aliasStatus.Status = status
   164  				aliasStatus.Command = snap.JoinSnapApp(snapName, tgt)
   165  				snapAliases[alias] = aliasStatus
   166  			}
   167  		}
   168  	}
   169  
   170  	return SyncResponse(res, nil)
   171  }