1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320
|
// -*- Mode: Go; indent-tabs-mode: t -*-
/*
* Copyright (C) 2017 Canonical Ltd
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package interfaces
import (
"fmt"
"strings"
"github.com/snapcore/snapd/interfaces/utils"
"github.com/snapcore/snapd/metautil"
"github.com/snapcore/snapd/snap"
)
// Connection represents a connection between a particular plug and slot.
type Connection struct {
Plug *ConnectedPlug
Slot *ConnectedSlot
}
// ConnectedPlug represents a plug that is connected to a slot.
type ConnectedPlug struct {
plugInfo *snap.PlugInfo
appSet *SnapAppSet
staticAttrs map[string]any
dynamicAttrs map[string]any
}
// LabelExpression returns the label expression for the given plug. It is
// constructed from the apps and hooks that are associated with the plug.
func (plug *ConnectedPlug) LabelExpression() string {
return labelExpr(plug)
}
// ConnectedSlot represents a slot that is connected to a plug.
type ConnectedSlot struct {
slotInfo *snap.SlotInfo
appSet *SnapAppSet
staticAttrs map[string]any
dynamicAttrs map[string]any
}
// AppSet return the app set that this slot is associated with.
func (slot *ConnectedSlot) AppSet() *SnapAppSet {
return slot.appSet
}
// Runnables returns a list of all runnables that should be connected to the
// given slot.
func (slot *ConnectedSlot) Runnables() []snap.Runnable {
apps := slot.appSet.info.AppsForSlot(slot.slotInfo)
hooks := slot.appSet.info.HooksForSlot(slot.slotInfo)
// TODO: if components ever get slots, they will need to be considered here
return appAndHookRunnables(apps, hooks)
}
// LabelExpression returns the label expression for the given slot. It is
// constructed from the apps and hooks that are associated with the slot.
func (slot *ConnectedSlot) LabelExpression() string {
return labelExpr(slot)
}
// Attrer is an interface with Attr getter method common
// to ConnectedSlot, ConnectedPlug, PlugInfo and SlotInfo types.
type Attrer interface {
// Attr returns attribute value for given path, or an error. Dotted paths are supported.
Attr(path string, value any) error
// Lookup returns attribute value for given path, or false. Dotted paths are supported.
Lookup(path string) (value any, ok bool)
}
func lookupAttr(staticAttrs map[string]any, dynamicAttrs map[string]any, path string) (any, bool) {
var v any
comps := strings.FieldsFunc(path, func(r rune) bool { return r == '.' })
if len(comps) == 0 {
return nil, false
}
if _, ok := dynamicAttrs[comps[0]]; ok {
v = dynamicAttrs
} else {
v = staticAttrs
}
for _, comp := range comps {
m, ok := v.(map[string]any)
if !ok {
return nil, false
}
v, ok = m[comp]
if !ok {
return nil, false
}
}
return v, true
}
func getAttribute(snapName string, ifaceName string, staticAttrs map[string]any, dynamicAttrs map[string]any, path string, val any) error {
v, ok := lookupAttr(staticAttrs, dynamicAttrs, path)
if !ok {
err := fmt.Errorf("snap %q does not have attribute %q for interface %q", snapName, path, ifaceName)
return snap.AttributeNotFoundError{Err: err}
}
return metautil.SetValueFromAttribute(snapName, ifaceName, path, v, val)
}
// NewConnectedSlot creates an object representing a connected slot.
func NewConnectedSlot(slot *snap.SlotInfo, appSet *SnapAppSet, staticAttrs, dynamicAttrs map[string]any) *ConnectedSlot {
if slot.Snap.InstanceName() != appSet.Info().InstanceName() {
panic(fmt.Sprintf("internal error: slot must be from the same snap as the app set: %s != %s", slot.Snap.InstanceName(), appSet.Info().InstanceName()))
}
var static map[string]any
if staticAttrs != nil {
static = staticAttrs
} else {
static = slot.Attrs
}
return &ConnectedSlot{
slotInfo: slot,
appSet: appSet,
staticAttrs: utils.CopyAttributes(static),
dynamicAttrs: utils.NormalizeInterfaceAttributes(dynamicAttrs).(map[string]any),
}
}
// NewConnectedPlug creates an object representing a connected plug.
func NewConnectedPlug(plug *snap.PlugInfo, appSet *SnapAppSet, staticAttrs, dynamicAttrs map[string]any) *ConnectedPlug {
if plug.Snap.InstanceName() != appSet.Info().InstanceName() {
panic(fmt.Sprintf("internal error: plug must be from the same snap as the app set: %s != %s", plug.Snap.InstanceName(), appSet.Info().InstanceName()))
}
var static map[string]any
if staticAttrs != nil {
static = staticAttrs
} else {
static = plug.Attrs
}
return &ConnectedPlug{
plugInfo: plug,
appSet: appSet,
staticAttrs: utils.CopyAttributes(static),
dynamicAttrs: utils.NormalizeInterfaceAttributes(dynamicAttrs).(map[string]any),
}
}
// Interface returns the name of the interface for this plug.
func (plug *ConnectedPlug) Interface() string {
return plug.plugInfo.Interface
}
// Name returns the name of this plug.
func (plug *ConnectedPlug) Name() string {
return plug.plugInfo.Name
}
// Snap returns the snap Info of this plug.
func (plug *ConnectedPlug) Snap() *snap.Info {
return plug.plugInfo.Snap
}
// AppSet return the app set that this plug is associated with.
func (plug *ConnectedPlug) AppSet() *SnapAppSet {
return plug.appSet
}
// Runnables returns a list of all runnables that should be connected to the
// given plug.
func (plug *ConnectedPlug) Runnables() []snap.Runnable {
apps := plug.appSet.info.AppsForPlug(plug.plugInfo)
hooks := plug.appSet.info.HooksForPlug(plug.plugInfo)
for _, component := range plug.appSet.components {
hooks = append(hooks, component.HooksForPlug(plug.plugInfo)...)
}
return appAndHookRunnables(apps, hooks)
}
func appAndHookRunnables(apps []*snap.AppInfo, hooks []*snap.HookInfo) []snap.Runnable {
runnables := make([]snap.Runnable, 0, len(apps)+len(hooks))
for _, app := range apps {
runnables = append(runnables, app.Runnable())
}
for _, hook := range hooks {
runnables = append(runnables, hook.Runnable())
}
return runnables
}
// StaticAttr returns a static attribute with the given key, or error if attribute doesn't exist.
func (plug *ConnectedPlug) StaticAttr(key string, val any) error {
return getAttribute(plug.Snap().InstanceName(), plug.Interface(), plug.staticAttrs, nil, key, val)
}
// StaticAttrs returns all static attributes.
func (plug *ConnectedPlug) StaticAttrs() map[string]any {
return utils.CopyAttributes(plug.staticAttrs)
}
// DynamicAttrs returns all dynamic attributes.
func (plug *ConnectedPlug) DynamicAttrs() map[string]any {
return utils.CopyAttributes(plug.dynamicAttrs)
}
// Attr returns a dynamic attribute with the given name. It falls back to returning static
// attribute if dynamic one doesn't exist. Error is returned if neither dynamic nor static
// attribute exist.
func (plug *ConnectedPlug) Attr(key string, val any) error {
return getAttribute(plug.Snap().InstanceName(), plug.Interface(), plug.staticAttrs, plug.dynamicAttrs, key, val)
}
func (plug *ConnectedPlug) Lookup(path string) (any, bool) {
return lookupAttr(plug.staticAttrs, plug.dynamicAttrs, path)
}
// SetAttr sets the given dynamic attribute. Error is returned if the key is already used by a static attribute.
func (plug *ConnectedPlug) SetAttr(key string, value any) error {
if _, ok := plug.staticAttrs[key]; ok {
return fmt.Errorf("cannot change attribute %q as it was statically specified in the snap details", key)
}
if plug.dynamicAttrs == nil {
plug.dynamicAttrs = make(map[string]any)
}
plug.dynamicAttrs[key] = utils.NormalizeInterfaceAttributes(value)
return nil
}
// Ref returns the PlugRef for this plug.
func (plug *ConnectedPlug) Ref() *PlugRef {
return &PlugRef{Snap: plug.Snap().InstanceName(), Name: plug.Name()}
}
// Interface returns the name of the interface for this slot.
func (slot *ConnectedSlot) Interface() string {
return slot.slotInfo.Interface
}
// Name returns the name of this slot.
func (slot *ConnectedSlot) Name() string {
return slot.slotInfo.Name
}
// Snap returns the snap Info of this slot.
func (slot *ConnectedSlot) Snap() *snap.Info {
return slot.slotInfo.Snap
}
// Apps returns all the apps associated with this slot.
func (slot *ConnectedSlot) Apps() map[string]*snap.AppInfo {
return slot.slotInfo.Apps
}
// StaticAttr returns a static attribute with the given key, or error if attribute doesn't exist.
func (slot *ConnectedSlot) StaticAttr(key string, val any) error {
return getAttribute(slot.Snap().InstanceName(), slot.Interface(), slot.staticAttrs, nil, key, val)
}
// StaticAttrs returns all static attributes.
func (slot *ConnectedSlot) StaticAttrs() map[string]any {
return utils.CopyAttributes(slot.staticAttrs)
}
// DynamicAttrs returns all dynamic attributes.
func (slot *ConnectedSlot) DynamicAttrs() map[string]any {
return utils.CopyAttributes(slot.dynamicAttrs)
}
// Attr returns a dynamic attribute with the given name. It falls back to returning static
// attribute if dynamic one doesn't exist. Error is returned if neither dynamic nor static
// attribute exist.
func (slot *ConnectedSlot) Attr(key string, val any) error {
return getAttribute(slot.Snap().InstanceName(), slot.Interface(), slot.staticAttrs, slot.dynamicAttrs, key, val)
}
func (slot *ConnectedSlot) Lookup(path string) (any, bool) {
return lookupAttr(slot.staticAttrs, slot.dynamicAttrs, path)
}
// SetAttr sets the given dynamic attribute. Error is returned if the key is already used by a static attribute.
func (slot *ConnectedSlot) SetAttr(key string, value any) error {
if _, ok := slot.staticAttrs[key]; ok {
return fmt.Errorf("cannot change attribute %q as it was statically specified in the snap details", key)
}
if slot.dynamicAttrs == nil {
slot.dynamicAttrs = make(map[string]any)
}
slot.dynamicAttrs[key] = utils.NormalizeInterfaceAttributes(value)
return nil
}
// Ref returns the SlotRef for this slot.
func (slot *ConnectedSlot) Ref() *SlotRef {
return &SlotRef{Snap: slot.Snap().InstanceName(), Name: slot.Name()}
}
// Interface returns the name of the interface for this connection.
func (conn *Connection) Interface() string {
return conn.Plug.plugInfo.Interface
}
|