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 }