-
Notifications
You must be signed in to change notification settings - Fork 206
/
loader.go
145 lines (116 loc) · 4.29 KB
/
loader.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
package pluggable
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"time"
"get.porter.sh/porter/pkg/config"
"get.porter.sh/porter/pkg/plugins"
"get.porter.sh/porter/pkg/tracing"
"go.opentelemetry.io/otel/attribute"
)
const (
// PluginStartTimeoutDefault is the default amount of time to wait for a plugin
// to start. Override with PluginStartTimeoutEnvVar.
PluginStartTimeoutDefault = 5 * time.Second
// PluginStopTimeoutDefault is the default amount of time to wait for a plugin
// to stop (kill). Override with PluginStopTimeoutEnvVar.
PluginStopTimeoutDefault = 5 * time.Second
// PluginStartTimeoutEnvVar is the environment variable used to override
// PluginStartTimeoutDefault.
PluginStartTimeoutEnvVar = "PORTER_PLUGIN_START_TIMEOUT"
// PluginStopTimeoutEnvVar is the environment variable used to override
// PluginStopTimeoutDefault.
PluginStopTimeoutEnvVar = "PORTER_PLUGIN_STOP_TIMEOUT"
)
// PluginLoader handles finding, configuring and loading porter plugins.
type PluginLoader struct {
// config is the Porter configuration
config *config.Config
// selectedPluginKey is the loaded plugin.
selectedPluginKey *plugins.PluginKey
// selectedPluginConfig is the relevant section of the Porter config file containing
// the plugin's configuration.
selectedPluginConfig interface{}
}
func NewPluginLoader(c *config.Config) *PluginLoader {
return &PluginLoader{
config: c,
}
}
// Load a plugin, returning the plugin's interface which the caller must then cast to
// the typed interface, a cleanup function to stop the plugin when finished communicating with it,
// and an error if the plugin could not be loaded.
func (l *PluginLoader) Load(ctx context.Context, pluginType PluginTypeConfig) (*PluginConnection, error) {
ctx, span := tracing.StartSpan(ctx,
attribute.String("plugin-interface", pluginType.Interface),
attribute.String("requested-protocol-version", fmt.Sprintf("%v", pluginType.ProtocolVersion)))
defer span.EndSpan()
err := l.selectPlugin(ctx, pluginType)
if err != nil {
return nil, err
}
// quick check to detect that we are running as porter, and not a plugin already
if l.config.IsInternalPlugin {
err := fmt.Errorf("the internal plugin %s tried to load the %s plugin. Report this error to https://github.com/getporter/porter", l.config.InternalPluginKey, l.selectedPluginKey)
return nil, span.Error(err)
}
span.SetAttributes(attribute.String("plugin-key", l.selectedPluginKey.String()))
configReader, err := l.readPluginConfig()
if err != nil {
return nil, span.Error(err)
}
conn := NewPluginConnection(l.config, pluginType, *l.selectedPluginKey)
if err = conn.Start(ctx, configReader); err != nil {
return nil, err
}
return conn, nil
}
// selectPlugin picks the plugin to use and loads its configuration.
func (l *PluginLoader) selectPlugin(ctx context.Context, cfg PluginTypeConfig) error {
//lint:ignore SA4006 ignore unused ctx for now.
ctx, span := tracing.StartSpan(ctx)
defer span.EndSpan()
l.selectedPluginKey = nil
l.selectedPluginConfig = nil
var pluginKey string
defaultStore := cfg.GetDefaultPluggable(l.config)
if defaultStore != "" {
span.SetAttributes(attribute.String("default-plugin", defaultStore))
is, err := cfg.GetPluggable(l.config, defaultStore)
if err != nil {
return span.Error(err)
}
pluginKey = is.GetPluginSubKey()
l.selectedPluginConfig = is.GetConfig()
if l.selectedPluginConfig == nil {
span.Debug("No plugin config defined")
}
}
// If there isn't a specific plugin configured for this plugin type, fall back to the default plugin for this type
if pluginKey == "" {
pluginKey = cfg.GetDefaultPlugin(l.config)
span.Debug("Selected default plugin", attribute.String("plugin-key", pluginKey))
} else {
span.Debug("Selected configured plugin", attribute.String("plugin-key", pluginKey))
}
key, err := plugins.ParsePluginKey(pluginKey)
if err != nil {
return span.Error(err)
}
l.selectedPluginKey = &key
l.selectedPluginKey.Interface = cfg.Interface
return nil
}
func (l *PluginLoader) readPluginConfig() (io.Reader, error) {
if l.selectedPluginConfig == nil {
return &bytes.Buffer{}, nil
}
b, err := json.Marshal(l.selectedPluginConfig)
if err != nil {
return nil, fmt.Errorf("could not marshal plugin config for %s: %w", l.selectedPluginKey, err)
}
return bytes.NewBuffer(b), nil
}