Skip to content

Commit 25d51ff

Browse files
committed
feat: engine management API
Signed-off-by: Johannes Großmann <grossmann.johannes@t-online.de>
1 parent a56837f commit 25d51ff

8 files changed

Lines changed: 1220 additions & 518 deletions

File tree

client/client.go

Lines changed: 62 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,9 @@ import (
2727
"github.com/docker/secrets-engine/x/api"
2828
healthv1 "github.com/docker/secrets-engine/x/api/health/v1"
2929
"github.com/docker/secrets-engine/x/api/health/v1/healthv1connect"
30+
pluginsv1 "github.com/docker/secrets-engine/x/api/plugins/v1"
31+
"github.com/docker/secrets-engine/x/api/plugins/v1/pluginsv1connect"
3032
"github.com/docker/secrets-engine/x/api/resolver"
31-
v1 "github.com/docker/secrets-engine/x/api/resolver/v1"
32-
"github.com/docker/secrets-engine/x/api/resolver/v1/resolverv1connect"
3333
"github.com/docker/secrets-engine/x/secrets"
3434
)
3535

@@ -120,9 +120,14 @@ type config struct {
120120
responseTimeout time.Duration
121121
}
122122

123+
var (
124+
_ Client = &client{}
125+
_ PluginManagement = &client{}
126+
)
127+
123128
type client struct {
124129
resolverClient secrets.Resolver
125-
listClient resolverv1connect.ListServiceClient
130+
engineClient pluginsv1connect.PluginManagementServiceClient
126131
versionClient healthv1connect.VersionServiceClient
127132
}
128133

@@ -158,8 +163,20 @@ type Client interface {
158163

159164
// Version returns the name and version reported by the daemon.
160165
Version(ctx context.Context) (DaemonVersion, error)
166+
}
161167

168+
type PluginManagement interface {
162169
ListPlugins(ctx context.Context) ([]PluginInfo, error)
170+
EnablePlugin(ctx context.Context, name string) error
171+
DisablePlugin(ctx context.Context, name string) error
172+
}
173+
174+
func PluginManagementFromClient(c Client) (PluginManagement, error) {
175+
m, ok := c.(*client)
176+
if !ok {
177+
return nil, errors.New("invalid client")
178+
}
179+
return m, nil
163180
}
164181

165182
func isDialError(err error) bool {
@@ -209,14 +226,14 @@ func New(options ...Option) (Client, error) {
209226
}
210227
return &client{
211228
resolverClient: resolver.NewResolverClient(c),
212-
listClient: resolverv1connect.NewListServiceClient(c, "http://unix"),
229+
engineClient: pluginsv1connect.NewPluginManagementServiceClient(c, "http://unix"),
213230
versionClient: healthv1connect.NewVersionServiceClient(c, "http://unix"),
214231
}, nil
215232
}
216233

217234
func (c client) ListPlugins(ctx context.Context) ([]PluginInfo, error) {
218-
req := connect.NewRequest(v1.ListPluginsRequest_builder{}.Build())
219-
resp, err := c.listClient.ListPlugins(ctx, req)
235+
req := connect.NewRequest(pluginsv1.ListPluginsRequest_builder{}.Build())
236+
resp, err := c.engineClient.ListPlugins(ctx, req)
220237
if isDialError(err) {
221238
return nil, fmt.Errorf("%w: %w", ErrSecretsEngineNotAvailable, err)
222239
}
@@ -233,27 +250,54 @@ func (c client) ListPlugins(ctx context.Context) ([]PluginInfo, error) {
233250
if err != nil {
234251
continue
235252
}
236-
pattern, err := secrets.ParsePattern(item.GetPattern())
237-
if err != nil {
238-
continue
239-
}
240-
result = append(result, PluginInfo{
253+
info := PluginInfo{
241254
Name: name,
242255
Version: version,
243-
Pattern: pattern,
244256
External: item.GetExternal(),
245257
Configurable: item.GetConfigurable(),
246-
})
258+
}
259+
if sp := item.GetSecretsProvider(); sp != nil {
260+
pattern, err := secrets.ParsePattern(sp.GetPattern())
261+
if err != nil {
262+
continue
263+
}
264+
info.SecretsProvider = &SecretsProviderMetadata{Pattern: pattern}
265+
}
266+
result = append(result, info)
247267
}
248268
return result, nil
249269
}
250270

271+
func (c client) EnablePlugin(ctx context.Context, name string) error {
272+
r := pluginsv1.EnablePluginRequest_builder{}.Build()
273+
r.SetName(name)
274+
_, err := c.engineClient.EnablePlugin(ctx, connect.NewRequest(r))
275+
if isDialError(err) {
276+
return fmt.Errorf("%w: %w", ErrSecretsEngineNotAvailable, err)
277+
}
278+
return err
279+
}
280+
281+
func (c client) DisablePlugin(ctx context.Context, name string) error {
282+
r := pluginsv1.DisablePluginRequest_builder{}.Build()
283+
r.SetName(name)
284+
_, err := c.engineClient.DisablePlugin(ctx, connect.NewRequest(r))
285+
if isDialError(err) {
286+
return fmt.Errorf("%w: %w", ErrSecretsEngineNotAvailable, err)
287+
}
288+
return err
289+
}
290+
251291
type PluginInfo struct {
252-
Name api.Name
253-
Version api.Version
254-
Pattern secrets.Pattern
255-
External bool
256-
Configurable bool
292+
Name api.Name
293+
Version api.Version
294+
External bool
295+
Configurable bool
296+
SecretsProvider *SecretsProviderMetadata
297+
}
298+
299+
type SecretsProviderMetadata struct {
300+
Pattern secrets.Pattern
257301
}
258302

259303
func dialFromPath(path string) dial {

client/client_test.go

Lines changed: 50 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ import (
3131
"github.com/docker/secrets-engine/x/api"
3232
healthv1 "github.com/docker/secrets-engine/x/api/health/v1"
3333
"github.com/docker/secrets-engine/x/api/health/v1/healthv1connect"
34-
resolverv1 "github.com/docker/secrets-engine/x/api/resolver/v1"
35-
"github.com/docker/secrets-engine/x/api/resolver/v1/resolverv1connect"
34+
pluginsv1 "github.com/docker/secrets-engine/x/api/plugins/v1"
35+
"github.com/docker/secrets-engine/x/api/plugins/v1/pluginsv1connect"
3636
"github.com/docker/secrets-engine/x/secrets"
3737
"github.com/docker/secrets-engine/x/testhelper"
3838
)
@@ -60,14 +60,14 @@ func mockVersionEngine(t *testing.T, version, date, commitHash string) string {
6060
return socketPath
6161
}
6262

63-
var _ resolverv1connect.ListServiceHandler = &mockPluginsList{}
63+
var _ pluginsv1connect.PluginManagementServiceHandler = &mockPluginsList{}
6464

6565
type mockPluginsList struct {
6666
list []PluginInfo
6767
}
6868

69-
func (m mockPluginsList) ListPlugins(context.Context, *connect.Request[resolverv1.ListPluginsRequest]) (*connect.Response[resolverv1.ListPluginsResponse], error) {
70-
var plugins []*resolverv1.Plugin
69+
func (m mockPluginsList) ListPlugins(_ context.Context, _ *connect.Request[pluginsv1.ListPluginsRequest]) (*connect.Response[pluginsv1.ListPluginsResponse], error) {
70+
var plugins []*pluginsv1.Plugin
7171
for _, plugin := range m.list {
7272
var name string
7373
if plugin.Name != nil {
@@ -77,23 +77,32 @@ func (m mockPluginsList) ListPlugins(context.Context, *connect.Request[resolverv
7777
if plugin.Version != nil {
7878
version = plugin.Version.String()
7979
}
80-
var pattern string
81-
if plugin.Pattern != nil {
82-
pattern = plugin.Pattern.String()
83-
}
84-
plugins = append(plugins, resolverv1.Plugin_builder{
80+
b := pluginsv1.Plugin_builder{
8581
Name: proto.String(name),
8682
Version: proto.String(version),
87-
Pattern: proto.String(pattern),
8883
External: proto.Bool(plugin.External),
8984
Configurable: proto.Bool(plugin.Configurable),
90-
}.Build())
85+
}
86+
if plugin.SecretsProvider != nil {
87+
b.SecretsProvider = pluginsv1.SecretsProvider_builder{
88+
Pattern: proto.String(plugin.SecretsProvider.Pattern.String()),
89+
}.Build()
90+
}
91+
plugins = append(plugins, b.Build())
9192
}
92-
return connect.NewResponse(resolverv1.ListPluginsResponse_builder{
93+
return connect.NewResponse(pluginsv1.ListPluginsResponse_builder{
9394
Plugins: plugins,
9495
}.Build()), nil
9596
}
9697

98+
func (m mockPluginsList) EnablePlugin(_ context.Context, _ *connect.Request[pluginsv1.EnablePluginRequest]) (*connect.Response[pluginsv1.EnablePluginResponse], error) {
99+
return connect.NewResponse(pluginsv1.EnablePluginResponse_builder{}.Build()), nil
100+
}
101+
102+
func (m mockPluginsList) DisablePlugin(_ context.Context, _ *connect.Request[pluginsv1.DisablePluginRequest]) (*connect.Response[pluginsv1.DisablePluginResponse], error) {
103+
return connect.NewResponse(pluginsv1.DisablePluginResponse_builder{}.Build()), nil
104+
}
105+
97106
type handler struct {
98107
pattern string
99108
handler http.Handler
@@ -133,7 +142,7 @@ func wrapHandler(pattern string, h http.Handler) handler {
133142
func mockListPluginsEngine(t *testing.T, plugins []PluginInfo) string {
134143
t.Helper()
135144
socketPath := testhelper.RandomShortSocketName()
136-
muxServer(t, socketPath, []handler{wrapHandler(resolverv1connect.NewListServiceHandler(&mockPluginsList{list: plugins}))})
145+
muxServer(t, socketPath, []handler{wrapHandler(pluginsv1connect.NewPluginManagementServiceHandler(&mockPluginsList{list: plugins}))})
137146
return socketPath
138147
}
139148

@@ -142,22 +151,24 @@ func Test_ListPlugins(t *testing.T) {
142151
t.Run("external and internal configurable plugins", func(t *testing.T) {
143152
plugins := []PluginInfo{
144153
{
145-
Name: api.MustNewName("foo"),
146-
Version: api.MustNewVersion("v1"),
147-
Pattern: secrets.MustParsePattern("**"),
148-
Configurable: true,
154+
Name: api.MustNewName("foo"),
155+
Version: api.MustNewVersion("v1"),
156+
SecretsProvider: &SecretsProviderMetadata{Pattern: secrets.MustParsePattern("**")},
157+
Configurable: true,
149158
},
150159
{
151-
Name: api.MustNewName("bar"),
152-
Version: api.MustNewVersion("v1"),
153-
Pattern: secrets.MustParsePattern("**"),
154-
External: true,
160+
Name: api.MustNewName("bar"),
161+
Version: api.MustNewVersion("v1"),
162+
SecretsProvider: &SecretsProviderMetadata{Pattern: secrets.MustParsePattern("**")},
163+
External: true,
155164
},
156165
}
157166
socket := mockListPluginsEngine(t, plugins)
158167
client, err := New(WithSocketPath(socket))
159168
require.NoError(t, err)
160-
result, err := client.ListPlugins(t.Context())
169+
m, err := PluginManagementFromClient(client)
170+
require.NoError(t, err)
171+
result, err := m.ListPlugins(t.Context())
161172
require.NoError(t, err)
162173
assert.Equal(t, plugins, result)
163174
})
@@ -166,7 +177,9 @@ func Test_ListPlugins(t *testing.T) {
166177
socket := mockListPluginsEngine(t, plugins)
167178
client, err := New(WithSocketPath(socket))
168179
require.NoError(t, err)
169-
result, err := client.ListPlugins(t.Context())
180+
m, err := PluginManagementFromClient(client)
181+
require.NoError(t, err)
182+
result, err := m.ListPlugins(t.Context())
170183
require.NoError(t, err)
171184
assert.Empty(t, result)
172185
})
@@ -176,21 +189,24 @@ func Test_ListPlugins(t *testing.T) {
176189
Name: api.MustNewName("foo"),
177190
},
178191
{
179-
Name: api.MustNewName("bar"),
180-
Version: api.MustNewVersion("v1"),
181-
Pattern: secrets.MustParsePattern("**"),
192+
Name: api.MustNewName("bar"),
193+
Version: api.MustNewVersion("v1"),
194+
SecretsProvider: &SecretsProviderMetadata{Pattern: secrets.MustParsePattern("**")},
182195
},
183196
}
184197
socket := mockListPluginsEngine(t, plugins)
185198
client, err := New(WithSocketPath(socket))
186199
require.NoError(t, err)
187-
result, err := client.ListPlugins(t.Context())
200+
m, err := PluginManagementFromClient(client)
188201
require.NoError(t, err)
202+
result, err := m.ListPlugins(t.Context())
203+
require.NoError(t, err)
204+
189205
assert.Equal(t, []PluginInfo{
190206
{
191-
Name: api.MustNewName("bar"),
192-
Version: api.MustNewVersion("v1"),
193-
Pattern: secrets.MustParsePattern("**"),
207+
Name: api.MustNewName("bar"),
208+
Version: api.MustNewVersion("v1"),
209+
SecretsProvider: &SecretsProviderMetadata{Pattern: secrets.MustParsePattern("**")},
194210
},
195211
}, result)
196212
})
@@ -221,7 +237,9 @@ func TestSecretsEngineUnavailable(t *testing.T) {
221237
socketPath := testhelper.RandomShortSocketName()
222238
client, err := New(WithSocketPath(socketPath))
223239
require.NoError(t, err)
224-
_, err = client.ListPlugins(t.Context())
240+
m, err := PluginManagementFromClient(client)
241+
require.NoError(t, err)
242+
_, err = m.ListPlugins(t.Context())
225243
require.ErrorIs(t, err, ErrSecretsEngineNotAvailable)
226244
_, err = client.GetSecrets(t.Context(), secrets.MustParsePattern("**"))
227245
require.ErrorIs(t, err, ErrSecretsEngineNotAvailable)

0 commit comments

Comments
 (0)