Skip to content

Commit b18f644

Browse files
authored
Merge pull request #502 from docker/version/endpoint
feat(api): add daemon version endpoint client and version comparison
2 parents 88eb7a4 + 5c3ad2a commit b18f644

File tree

8 files changed

+519
-5
lines changed

8 files changed

+519
-5
lines changed

client/client.go

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,19 @@ import (
2525
"connectrpc.com/connect"
2626

2727
"github.com/docker/secrets-engine/x/api"
28+
healthv1 "github.com/docker/secrets-engine/x/api/health/v1"
29+
"github.com/docker/secrets-engine/x/api/health/v1/healthv1connect"
2830
"github.com/docker/secrets-engine/x/api/resolver"
2931
v1 "github.com/docker/secrets-engine/x/api/resolver/v1"
3032
"github.com/docker/secrets-engine/x/api/resolver/v1/resolverv1connect"
3133
"github.com/docker/secrets-engine/x/secrets"
3234
)
3335

3436
type (
35-
Envelope = secrets.Envelope
36-
ID = secrets.ID
37-
Pattern = secrets.Pattern
37+
Envelope = secrets.Envelope
38+
ID = secrets.ID
39+
Pattern = secrets.Pattern
40+
DaemonVersion = api.DaemonVersion
3841
)
3942

4043
var (
@@ -120,6 +123,7 @@ type config struct {
120123
type client struct {
121124
resolverClient secrets.Resolver
122125
listClient resolverv1connect.ListServiceClient
126+
versionClient healthv1connect.VersionServiceClient
123127
}
124128

125129
func (c client) GetSecrets(ctx context.Context, pattern secrets.Pattern) ([]secrets.Envelope, error) {
@@ -133,9 +137,28 @@ func (c client) GetSecrets(ctx context.Context, pattern secrets.Pattern) ([]secr
133137
return envelopes, nil
134138
}
135139

140+
func (c client) Version(ctx context.Context) (DaemonVersion, error) {
141+
resp, err := c.versionClient.GetVersion(ctx, connect.NewRequest(healthv1.GetVersionRequest_builder{}.Build()))
142+
if isDialError(err) {
143+
return DaemonVersion{}, fmt.Errorf("%w: %w", ErrSecretsEngineNotAvailable, err)
144+
}
145+
if err != nil {
146+
return DaemonVersion{}, err
147+
}
148+
ver, err := api.NewVersion(resp.Msg.GetVersion())
149+
if err != nil {
150+
return DaemonVersion{}, fmt.Errorf("parsing daemon version %q: %w", resp.Msg.GetVersion(), err)
151+
}
152+
return DaemonVersion{Version: ver, Date: resp.Msg.GetDate(), CommitHash: resp.Msg.GetCommitHash()}, nil
153+
}
154+
155+
// Client is the interface for interacting with the secrets engine daemon.
136156
type Client interface {
137157
secrets.Resolver
138158

159+
// Version returns the name and version reported by the daemon.
160+
Version(ctx context.Context) (DaemonVersion, error)
161+
139162
ListPlugins(ctx context.Context) ([]PluginInfo, error)
140163
}
141164

@@ -187,6 +210,7 @@ func New(options ...Option) (Client, error) {
187210
return &client{
188211
resolverClient: resolver.NewResolverClient(c),
189212
listClient: resolverv1connect.NewListServiceClient(c, "http://unix"),
213+
versionClient: healthv1connect.NewVersionServiceClient(c, "http://unix"),
190214
}, nil
191215
}
192216

client/client_test.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,37 @@ import (
2929
"google.golang.org/protobuf/proto"
3030

3131
"github.com/docker/secrets-engine/x/api"
32+
healthv1 "github.com/docker/secrets-engine/x/api/health/v1"
33+
"github.com/docker/secrets-engine/x/api/health/v1/healthv1connect"
3234
resolverv1 "github.com/docker/secrets-engine/x/api/resolver/v1"
3335
"github.com/docker/secrets-engine/x/api/resolver/v1/resolverv1connect"
3436
"github.com/docker/secrets-engine/x/secrets"
3537
"github.com/docker/secrets-engine/x/testhelper"
3638
)
3739

40+
var _ healthv1connect.VersionServiceHandler = &mockVersionService{}
41+
42+
type mockVersionService struct {
43+
version string
44+
date string
45+
commitHash string
46+
}
47+
48+
func (m mockVersionService) GetVersion(_ context.Context, _ *connect.Request[healthv1.GetVersionRequest]) (*connect.Response[healthv1.GetVersionResponse], error) {
49+
return connect.NewResponse(healthv1.GetVersionResponse_builder{
50+
Version: proto.String(m.version),
51+
Date: proto.String(m.date),
52+
CommitHash: proto.String(m.commitHash),
53+
}.Build()), nil
54+
}
55+
56+
func mockVersionEngine(t *testing.T, version, date, commitHash string) string {
57+
t.Helper()
58+
socketPath := testhelper.RandomShortSocketName()
59+
muxServer(t, socketPath, []handler{wrapHandler(healthv1connect.NewVersionServiceHandler(&mockVersionService{version: version, date: date, commitHash: commitHash}))})
60+
return socketPath
61+
}
62+
3863
var _ resolverv1connect.ListServiceHandler = &mockPluginsList{}
3964

4065
type mockPluginsList struct {
@@ -169,6 +194,27 @@ func Test_ListPlugins(t *testing.T) {
169194
})
170195
}
171196

197+
func Test_Version(t *testing.T) {
198+
t.Parallel()
199+
t.Run("returns version info", func(t *testing.T) {
200+
socket := mockVersionEngine(t, "v1.2.3", "2026-03-26", "abc1234")
201+
c, err := New(WithSocketPath(socket))
202+
require.NoError(t, err)
203+
dv, err := c.Version(t.Context())
204+
require.NoError(t, err)
205+
assert.Equal(t, "v1.2.3", dv.Version.String())
206+
assert.Equal(t, "2026-03-26", dv.Date)
207+
assert.Equal(t, "abc1234", dv.CommitHash)
208+
})
209+
t.Run("unavailable daemon", func(t *testing.T) {
210+
socketPath := testhelper.RandomShortSocketName()
211+
c, err := New(WithSocketPath(socketPath))
212+
require.NoError(t, err)
213+
_, err = c.Version(t.Context())
214+
require.ErrorIs(t, err, ErrSecretsEngineNotAvailable)
215+
})
216+
}
217+
172218
func TestSecretsEngineUnavailable(t *testing.T) {
173219
socketPath := testhelper.RandomShortSocketName()
174220
client, err := New(WithSocketPath(socketPath))

scripts/copy-only-diff

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,17 @@ if [ ! -d .git ]; then
2121
exit 1
2222
fi
2323

24-
if ! git diff --quiet; then
24+
modified=$(git diff --name-only --diff-filter=d)
25+
untracked=$(git ls-files --others --exclude-standard)
26+
27+
if [ -n "$modified" ] || [ -n "$untracked" ]; then
2528
echo "Detected modified files:"
26-
git diff --name-only | xargs -I{} cp --parents {} "$1"
29+
if [ -n "$modified" ]; then
30+
echo "$modified" | xargs -I{} cp --parents {} "$1"
31+
fi
32+
if [ -n "$untracked" ]; then
33+
echo "$untracked" | xargs -I{} cp --parents {} "$1"
34+
fi
2735
else
2836
echo "No changes detected"
2937
fi

x/api/daemon.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Copyright 2025-2026 Docker, Inc.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package api
16+
17+
// DaemonVersion holds version information reported by the daemon.
18+
type DaemonVersion struct {
19+
Version Version
20+
Date string
21+
CommitHash string
22+
}

0 commit comments

Comments
 (0)