-
Notifications
You must be signed in to change notification settings - Fork 89
/
load.go
159 lines (130 loc) · 3.55 KB
/
load.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
// Copyright (c) 2018-2020, Sylabs Inc. All rights reserved.
// This software is licensed under a 3-clause BSD license. Please consult the
// LICENSE.md file distributed with the sources of this project regarding your
// rights to use or distribute this software.
package plugin
import (
"errors"
"fmt"
"plugin"
"strings"
"sync"
callback "github.com/sylabs/singularity/v4/internal/pkg/plugin/callback"
pluginapi "github.com/sylabs/singularity/v4/pkg/plugin"
)
type loadedPlugins struct {
metas []*Meta
plugins map[string]struct{}
sync.Mutex
}
var lp loadedPlugins
// LoadCallbacks loads plugins registered for the hook instance passed in parameter.
func LoadCallbacks(cb pluginapi.Callback) ([]pluginapi.Callback, error) {
callbackName := callback.Name(cb)
if err := initMetaPlugin(); err != nil {
return nil, err
}
var errs []error
for _, meta := range lp.metas {
if !meta.Enabled {
continue
}
for _, name := range meta.Callbacks {
if name == callbackName {
if err := loadCallbacks(meta.binaryName()); err != nil {
// This might be destroying information by
// grabbing only the textual description of the
// error
wrappedErr := fmt.Errorf("while initializing plugin %q: %s", meta.Name, err)
errs = append(errs, wrappedErr)
}
}
}
}
if len(errs) > 0 {
// Collect all the errors into a single one that can be
// returned.
//
// Beware that we are destroying information that might
// be part of the type underlying the error interface we
// are getting here. UI-wise this might not be ideal,
// because the user might end up seeing a bunch of
// errors "slightly" separated by "; ".
//
// The alternative is to implement a type that collects
// the individual errors and implements the error
// interface by doing something similar to this. If
// there's some code that needs to handle errors in a
// more discrete way, it could type-assert an interface
// to check if it's possible to obtain the individual
// errors.
var b strings.Builder
for i, err := range errs {
if i > 0 {
b.WriteString("; ")
}
b.WriteString(err.Error())
}
return nil, errors.New(b.String())
}
return callback.Loaded(cb)
}
// initMetaPlugin reads plugin metadata files and stores data
// in the loaded plugin instance.
func initMetaPlugin() error {
var err error
lp.Lock()
defer lp.Unlock()
if lp.metas != nil {
return nil
}
if lp.plugins == nil {
lp.plugins = make(map[string]struct{})
}
lp.metas, err = List()
if err != nil {
return fmt.Errorf("while getting plugin's metadata: %s", err)
}
return nil
}
// loadCallbacks loads the plugin and the plugin callbacks.
func loadCallbacks(path string) error {
lp.Lock()
defer lp.Unlock()
if _, ok := lp.plugins[path]; ok {
return nil
}
pl, err := LoadObject(path)
if err != nil {
return err
}
lp.plugins[path] = struct{}{}
for _, c := range pl.Callbacks {
callback.Load(c)
}
return nil
}
// LoadObject loads a plugin object in memory and returns
// the Plugin object set within the plugin.
func LoadObject(path string) (*pluginapi.Plugin, error) {
pluginPointer, err := plugin.Open(path)
if err != nil {
return nil, err
}
pluginObject, err := getPluginObject(pluginPointer)
if err != nil {
return nil, err
}
return pluginObject, nil
}
func getPluginObject(pl *plugin.Plugin) (*pluginapi.Plugin, error) {
sym, err := pl.Lookup(pluginapi.PluginSymbol)
if err != nil {
return nil, err
}
p, ok := sym.(*pluginapi.Plugin)
if !ok {
return nil, fmt.Errorf("symbol \"Plugin\" not of type Plugin")
}
return p, nil
}