github.com/meulengracht/snapd@v0.0.0-20210719210640-8bde69bcc84e/interfaces/connection.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 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 interfaces 21 22 import ( 23 "fmt" 24 "reflect" 25 "strings" 26 27 "github.com/snapcore/snapd/interfaces/utils" 28 "github.com/snapcore/snapd/snap" 29 ) 30 31 // Connection represents a connection between a particular plug and slot. 32 type Connection struct { 33 Plug *ConnectedPlug 34 Slot *ConnectedSlot 35 } 36 37 // ConnectedPlug represents a plug that is connected to a slot. 38 type ConnectedPlug struct { 39 plugInfo *snap.PlugInfo 40 staticAttrs map[string]interface{} 41 dynamicAttrs map[string]interface{} 42 } 43 44 // ConnectedSlot represents a slot that is connected to a plug. 45 type ConnectedSlot struct { 46 slotInfo *snap.SlotInfo 47 staticAttrs map[string]interface{} 48 dynamicAttrs map[string]interface{} 49 } 50 51 // Attrer is an interface with Attr getter method common 52 // to ConnectedSlot, ConnectedPlug, PlugInfo and SlotInfo types. 53 type Attrer interface { 54 // Attr returns attribute value for given path, or an error. Dotted paths are supported. 55 Attr(path string, value interface{}) error 56 // Lookup returns attribute value for given path, or false. Dotted paths are supported. 57 Lookup(path string) (value interface{}, ok bool) 58 } 59 60 func lookupAttr(staticAttrs map[string]interface{}, dynamicAttrs map[string]interface{}, path string) (interface{}, bool) { 61 var v interface{} 62 comps := strings.FieldsFunc(path, func(r rune) bool { return r == '.' }) 63 if len(comps) == 0 { 64 return nil, false 65 } 66 if _, ok := dynamicAttrs[comps[0]]; ok { 67 v = dynamicAttrs 68 } else { 69 v = staticAttrs 70 } 71 72 for _, comp := range comps { 73 m, ok := v.(map[string]interface{}) 74 if !ok { 75 return nil, false 76 } 77 v, ok = m[comp] 78 if !ok { 79 return nil, false 80 } 81 } 82 83 return v, true 84 } 85 86 func getAttribute(snapName string, ifaceName string, staticAttrs map[string]interface{}, dynamicAttrs map[string]interface{}, path string, val interface{}) error { 87 v, ok := lookupAttr(staticAttrs, dynamicAttrs, path) 88 if !ok { 89 return fmt.Errorf("snap %q does not have attribute %q for interface %q", snapName, path, ifaceName) 90 } 91 92 rt := reflect.TypeOf(val) 93 if rt.Kind() != reflect.Ptr || val == nil { 94 return fmt.Errorf("internal error: cannot get %q attribute of interface %q with non-pointer value", path, ifaceName) 95 } 96 97 if reflect.TypeOf(v) != rt.Elem() { 98 return fmt.Errorf("snap %q has interface %q with invalid value type %T for %q attribute: %T", snapName, ifaceName, v, path, val) 99 } 100 rv := reflect.ValueOf(val) 101 rv.Elem().Set(reflect.ValueOf(v)) 102 return nil 103 } 104 105 // NewConnectedSlot creates an object representing a connected slot. 106 func NewConnectedSlot(slot *snap.SlotInfo, staticAttrs, dynamicAttrs map[string]interface{}) *ConnectedSlot { 107 var static map[string]interface{} 108 if staticAttrs != nil { 109 static = staticAttrs 110 } else { 111 static = slot.Attrs 112 } 113 return &ConnectedSlot{ 114 slotInfo: slot, 115 staticAttrs: utils.CopyAttributes(static), 116 dynamicAttrs: utils.NormalizeInterfaceAttributes(dynamicAttrs).(map[string]interface{}), 117 } 118 } 119 120 // NewConnectedPlug creates an object representing a connected plug. 121 func NewConnectedPlug(plug *snap.PlugInfo, staticAttrs, dynamicAttrs map[string]interface{}) *ConnectedPlug { 122 var static map[string]interface{} 123 if staticAttrs != nil { 124 static = staticAttrs 125 } else { 126 static = plug.Attrs 127 } 128 return &ConnectedPlug{ 129 plugInfo: plug, 130 staticAttrs: utils.CopyAttributes(static), 131 dynamicAttrs: utils.NormalizeInterfaceAttributes(dynamicAttrs).(map[string]interface{}), 132 } 133 } 134 135 // Interface returns the name of the interface for this plug. 136 func (plug *ConnectedPlug) Interface() string { 137 return plug.plugInfo.Interface 138 } 139 140 // Name returns the name of this plug. 141 func (plug *ConnectedPlug) Name() string { 142 return plug.plugInfo.Name 143 } 144 145 // Snap returns the snap Info of this plug. 146 func (plug *ConnectedPlug) Snap() *snap.Info { 147 return plug.plugInfo.Snap 148 } 149 150 // Apps returns all the apps associated with this plug. 151 func (plug *ConnectedPlug) Apps() map[string]*snap.AppInfo { 152 return plug.plugInfo.Apps 153 } 154 155 // Hooks returns all the hooks associated with this plug. 156 func (plug *ConnectedPlug) Hooks() map[string]*snap.HookInfo { 157 return plug.plugInfo.Hooks 158 } 159 160 // SecurityTags returns the security tags for this plug. 161 func (plug *ConnectedPlug) SecurityTags() []string { 162 return plug.plugInfo.SecurityTags() 163 } 164 165 // StaticAttr returns a static attribute with the given key, or error if attribute doesn't exist. 166 func (plug *ConnectedPlug) StaticAttr(key string, val interface{}) error { 167 return getAttribute(plug.Snap().InstanceName(), plug.Interface(), plug.staticAttrs, nil, key, val) 168 } 169 170 // StaticAttrs returns all static attributes. 171 func (plug *ConnectedPlug) StaticAttrs() map[string]interface{} { 172 return utils.CopyAttributes(plug.staticAttrs) 173 } 174 175 // DynamicAttrs returns all dynamic attributes. 176 func (plug *ConnectedPlug) DynamicAttrs() map[string]interface{} { 177 return utils.CopyAttributes(plug.dynamicAttrs) 178 } 179 180 // Attr returns a dynamic attribute with the given name. It falls back to returning static 181 // attribute if dynamic one doesn't exist. Error is returned if neither dynamic nor static 182 // attribute exist. 183 func (plug *ConnectedPlug) Attr(key string, val interface{}) error { 184 return getAttribute(plug.Snap().InstanceName(), plug.Interface(), plug.staticAttrs, plug.dynamicAttrs, key, val) 185 } 186 187 func (plug *ConnectedPlug) Lookup(path string) (interface{}, bool) { 188 return lookupAttr(plug.staticAttrs, plug.dynamicAttrs, path) 189 } 190 191 // SetAttr sets the given dynamic attribute. Error is returned if the key is already used by a static attribute. 192 func (plug *ConnectedPlug) SetAttr(key string, value interface{}) error { 193 if _, ok := plug.staticAttrs[key]; ok { 194 return fmt.Errorf("cannot change attribute %q as it was statically specified in the snap details", key) 195 } 196 if plug.dynamicAttrs == nil { 197 plug.dynamicAttrs = make(map[string]interface{}) 198 } 199 plug.dynamicAttrs[key] = utils.NormalizeInterfaceAttributes(value) 200 return nil 201 } 202 203 // Ref returns the PlugRef for this plug. 204 func (plug *ConnectedPlug) Ref() *PlugRef { 205 return &PlugRef{Snap: plug.Snap().InstanceName(), Name: plug.Name()} 206 } 207 208 // Interface returns the name of the interface for this slot. 209 func (slot *ConnectedSlot) Interface() string { 210 return slot.slotInfo.Interface 211 } 212 213 // Name returns the name of this slot. 214 func (slot *ConnectedSlot) Name() string { 215 return slot.slotInfo.Name 216 } 217 218 // Snap returns the snap Info of this slot. 219 func (slot *ConnectedSlot) Snap() *snap.Info { 220 return slot.slotInfo.Snap 221 } 222 223 // Apps returns all the apps associated with this slot. 224 func (slot *ConnectedSlot) Apps() map[string]*snap.AppInfo { 225 return slot.slotInfo.Apps 226 } 227 228 // Hooks returns all the hooks associated with this slot. 229 func (slot *ConnectedSlot) Hooks() map[string]*snap.HookInfo { 230 return slot.slotInfo.Hooks 231 } 232 233 // SecurityTags returns the security tags for this slot. 234 func (slot *ConnectedSlot) SecurityTags() []string { 235 return slot.slotInfo.SecurityTags() 236 } 237 238 // StaticAttr returns a static attribute with the given key, or error if attribute doesn't exist. 239 func (slot *ConnectedSlot) StaticAttr(key string, val interface{}) error { 240 return getAttribute(slot.Snap().InstanceName(), slot.Interface(), slot.staticAttrs, nil, key, val) 241 } 242 243 // StaticAttrs returns all static attributes. 244 func (slot *ConnectedSlot) StaticAttrs() map[string]interface{} { 245 return utils.CopyAttributes(slot.staticAttrs) 246 } 247 248 // DynamicAttrs returns all dynamic attributes. 249 func (slot *ConnectedSlot) DynamicAttrs() map[string]interface{} { 250 return utils.CopyAttributes(slot.dynamicAttrs) 251 } 252 253 // Attr returns a dynamic attribute with the given name. It falls back to returning static 254 // attribute if dynamic one doesn't exist. Error is returned if neither dynamic nor static 255 // attribute exist. 256 func (slot *ConnectedSlot) Attr(key string, val interface{}) error { 257 return getAttribute(slot.Snap().InstanceName(), slot.Interface(), slot.staticAttrs, slot.dynamicAttrs, key, val) 258 } 259 260 func (slot *ConnectedSlot) Lookup(path string) (interface{}, bool) { 261 return lookupAttr(slot.staticAttrs, slot.dynamicAttrs, path) 262 } 263 264 // SetAttr sets the given dynamic attribute. Error is returned if the key is already used by a static attribute. 265 func (slot *ConnectedSlot) SetAttr(key string, value interface{}) error { 266 if _, ok := slot.staticAttrs[key]; ok { 267 return fmt.Errorf("cannot change attribute %q as it was statically specified in the snap details", key) 268 } 269 if slot.dynamicAttrs == nil { 270 slot.dynamicAttrs = make(map[string]interface{}) 271 } 272 slot.dynamicAttrs[key] = utils.NormalizeInterfaceAttributes(value) 273 return nil 274 } 275 276 // Ref returns the SlotRef for this slot. 277 func (slot *ConnectedSlot) Ref() *SlotRef { 278 return &SlotRef{Snap: slot.Snap().InstanceName(), Name: slot.Name()} 279 } 280 281 // Interface returns the name of the interface for this connection. 282 func (conn *Connection) Interface() string { 283 return conn.Plug.plugInfo.Interface 284 }