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