forked from canonical/snapd
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathconnection.go
284 lines (241 loc) · 9.23 KB
/
connection.go
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
// -*- 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"
"reflect"
"strings"
"github.com/snapcore/snapd/interfaces/utils"
"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
staticAttrs map[string]interface{}
dynamicAttrs map[string]interface{}
}
// ConnectedSlot represents a slot that is connected to a plug.
type ConnectedSlot struct {
slotInfo *snap.SlotInfo
staticAttrs map[string]interface{}
dynamicAttrs map[string]interface{}
}
// 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 interface{}) error
// Lookup returns attribute value for given path, or false. Dotted paths are supported.
Lookup(path string) (value interface{}, ok bool)
}
func lookupAttr(staticAttrs map[string]interface{}, dynamicAttrs map[string]interface{}, path string) (interface{}, bool) {
var v interface{}
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]interface{})
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]interface{}, dynamicAttrs map[string]interface{}, path string, val interface{}) error {
v, ok := lookupAttr(staticAttrs, dynamicAttrs, path)
if !ok {
return fmt.Errorf("snap %q does not have attribute %q for interface %q", snapName, path, ifaceName)
}
rt := reflect.TypeOf(val)
if rt.Kind() != reflect.Ptr || val == nil {
return fmt.Errorf("internal error: cannot get %q attribute of interface %q with non-pointer value", path, ifaceName)
}
if reflect.TypeOf(v) != rt.Elem() {
return fmt.Errorf("snap %q has interface %q with invalid value type %T for %q attribute: %T", snapName, ifaceName, v, path, val)
}
rv := reflect.ValueOf(val)
rv.Elem().Set(reflect.ValueOf(v))
return nil
}
// NewConnectedSlot creates an object representing a connected slot.
func NewConnectedSlot(slot *snap.SlotInfo, staticAttrs, dynamicAttrs map[string]interface{}) *ConnectedSlot {
var static map[string]interface{}
if staticAttrs != nil {
static = staticAttrs
} else {
static = slot.Attrs
}
return &ConnectedSlot{
slotInfo: slot,
staticAttrs: utils.CopyAttributes(static),
dynamicAttrs: utils.NormalizeInterfaceAttributes(dynamicAttrs).(map[string]interface{}),
}
}
// NewConnectedPlug creates an object representing a connected plug.
func NewConnectedPlug(plug *snap.PlugInfo, staticAttrs, dynamicAttrs map[string]interface{}) *ConnectedPlug {
var static map[string]interface{}
if staticAttrs != nil {
static = staticAttrs
} else {
static = plug.Attrs
}
return &ConnectedPlug{
plugInfo: plug,
staticAttrs: utils.CopyAttributes(static),
dynamicAttrs: utils.NormalizeInterfaceAttributes(dynamicAttrs).(map[string]interface{}),
}
}
// 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
}
// Apps returns all the apps associated with this plug.
func (plug *ConnectedPlug) Apps() map[string]*snap.AppInfo {
return plug.plugInfo.Apps
}
// Hooks returns all the hooks associated with this plug.
func (plug *ConnectedPlug) Hooks() map[string]*snap.HookInfo {
return plug.plugInfo.Hooks
}
// SecurityTags returns the security tags for this plug.
func (plug *ConnectedPlug) SecurityTags() []string {
return plug.plugInfo.SecurityTags()
}
// StaticAttr returns a static attribute with the given key, or error if attribute doesn't exist.
func (plug *ConnectedPlug) StaticAttr(key string, val interface{}) error {
return getAttribute(plug.Snap().InstanceName(), plug.Interface(), plug.staticAttrs, nil, key, val)
}
// StaticAttrs returns all static attributes.
func (plug *ConnectedPlug) StaticAttrs() map[string]interface{} {
return utils.CopyAttributes(plug.staticAttrs)
}
// DynamicAttrs returns all dynamic attributes.
func (plug *ConnectedPlug) DynamicAttrs() map[string]interface{} {
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 interface{}) error {
return getAttribute(plug.Snap().InstanceName(), plug.Interface(), plug.staticAttrs, plug.dynamicAttrs, key, val)
}
func (plug *ConnectedPlug) Lookup(path string) (interface{}, 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 interface{}) 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]interface{})
}
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
}
// Hooks returns all the hooks associated with this slot.
func (slot *ConnectedSlot) Hooks() map[string]*snap.HookInfo {
return slot.slotInfo.Hooks
}
// SecurityTags returns the security tags for this slot.
func (slot *ConnectedSlot) SecurityTags() []string {
return slot.slotInfo.SecurityTags()
}
// StaticAttr returns a static attribute with the given key, or error if attribute doesn't exist.
func (slot *ConnectedSlot) StaticAttr(key string, val interface{}) error {
return getAttribute(slot.Snap().InstanceName(), slot.Interface(), slot.staticAttrs, nil, key, val)
}
// StaticAttrs returns all static attributes.
func (slot *ConnectedSlot) StaticAttrs() map[string]interface{} {
return utils.CopyAttributes(slot.staticAttrs)
}
// DynamicAttrs returns all dynamic attributes.
func (slot *ConnectedSlot) DynamicAttrs() map[string]interface{} {
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 interface{}) error {
return getAttribute(slot.Snap().InstanceName(), slot.Interface(), slot.staticAttrs, slot.dynamicAttrs, key, val)
}
func (slot *ConnectedSlot) Lookup(path string) (interface{}, 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 interface{}) 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]interface{})
}
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
}