Skip to content

Commit f6646b9

Browse files
authored
Merge pull request #61 from docker/keychain-ci
store/keychain: linux keychain tests inside docker
2 parents 45e4156 + 96835ef commit f6646b9

192 files changed

Lines changed: 423 additions & 38546 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/keychain.yml

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
name: Keychain
2+
on:
3+
push:
4+
branches:
5+
- main
6+
tags:
7+
- 'v*'
8+
pull_request:
9+
paths:
10+
- 'store/**'
11+
jobs:
12+
linux-keychain:
13+
permissions:
14+
id-token: write
15+
contents: read
16+
name: LinuxKeychainTests
17+
runs-on: ubuntu-24.04
18+
strategy:
19+
fail-fast: false
20+
matrix:
21+
subtest:
22+
- fedora-43-gnome-keyring
23+
- fedora-43-kdewallet
24+
- ubuntu-24-gnome-keyring
25+
- ubuntu-24-kdewallet
26+
steps:
27+
- name: Checkout
28+
uses: actions/checkout@v4
29+
- name: Hub login
30+
uses: docker/login-action@v3
31+
with:
32+
username: ${{ vars.DOCKERBUILDBOT_USERNAME }}
33+
password: ${{ secrets.DOCKERBUILDBOT_WRITE_PAT }}
34+
- name: Set up Docker Buildx
35+
id: buildx
36+
uses: docker/setup-buildx-action@v3
37+
with:
38+
driver: cloud
39+
endpoint: "docker/secrets-engine"
40+
install: true
41+
- name: Test
42+
run: DOCKER_TARGET=${{ matrix.subtest }} make keychain-linux-unit-tests
43+
# tests-windows:
44+
# permissions:
45+
# id-token: write
46+
# contents: read
47+
# name: WindowsKeychainTests
48+
# runs-on: ${{ matrix.os }}
49+
# strategy:
50+
# fail-fast: false
51+
# matrix:
52+
# os:
53+
# - windows-2022
54+
# - windows-2025
55+
# steps:
56+
# - name: Checkout
57+
# uses: actions/checkout@v4
58+
# - name: Setup Go
59+
# uses: actions/setup-go@v5
60+
# with:
61+
# go-version-file: ./store/go.mod
62+
# - name: Test keychain
63+
# run: make keychain-unit-tests
64+
tests-macos:
65+
permissions:
66+
id-token: write
67+
contents: read
68+
name: MacOSKeychainTests
69+
runs-on: ${{ matrix.os }}
70+
strategy:
71+
fail-fast: false
72+
matrix:
73+
os:
74+
- macOS-15
75+
- macOS-14
76+
- macOS-13
77+
steps:
78+
- name: Checkout
79+
uses: actions/checkout@v4
80+
- name: Setup Go
81+
uses: actions/setup-go@v5
82+
with:
83+
go-version-file: ./store/go.mod
84+
- name: Test keychain
85+
run: make keychain-unit-tests

Makefile

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,13 @@ clean: ## remove built binaries and packages
5252
@sh -c "rm -rf bin dist"
5353

5454
unit-tests:
55-
CGO_ENABLED=0 go test -v -tags="gen" ./...
55+
CGO_ENABLED=0 go test -v -tags="gen" $$(go list ./... | grep -v /store/)
56+
57+
keychain-linux-unit-tests:
58+
@docker buildx build $(DOCKER_BUILD_ARGS) --target=$(DOCKER_TARGET) --file store/Dockerfile .
59+
60+
keychain-unit-tests:
61+
CGO_ENABLED=1 go test -v $$(go list ./store/keychain/...)
5662

5763
nri-plugin:
5864
CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -trimpath -ldflags "-s -w ${GO_LDFLAGS}" -o ./dist/$(NRI_PLUGIN_BINARY)$(EXTENSION) ./cmd/nri-plugin
@@ -78,4 +84,4 @@ help: ## Show this help
7884
@echo Please specify a build target. The choices are:
7985
@grep -E '^[0-9a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "$(INFO_COLOR)%-30s$(NO_COLOR) %s\n", $$1, $$2}'
8086

81-
.PHONY: run bin format lint unit-tests cross x-package clean help generate docker-mcp
87+
.PHONY: run bin format lint unit-tests cross x-package clean help generate docker-mcp keychain-linux-unit-tests keychain-unit-tests

go.work

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@ go 1.24.3
33
use (
44
.
55
./plugin
6+
./store
67
)

store/Dockerfile

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
ARG GO_VERSION=latest
2+
3+
FROM --platform=${BUILDPLATFORM} golang:${GO_VERSION}-alpine AS go-base
4+
5+
FROM --platform=${BUILDPLATFORM} fedora:43 AS fedora43
6+
RUN dnf install -y gnome-keyring kf6-kwallet dbus-daemon
7+
COPY --from=go-base /usr/local/go /usr/local/go
8+
ENV PATH="/usr/local/go/bin:${PATH}"
9+
RUN useradd -ms /bin/bash user
10+
USER user
11+
WORKDIR /app
12+
RUN --mount=type=bind,target=.
13+
14+
FROM --platform=${BUILDPLATFORM} ubuntu:24.04 AS ubuntu24
15+
RUN apt update && apt install -y --no-install-recommends libglib2.0-bin dbus gnome-keyring kwalletmanager
16+
COPY --from=go-base /usr/local/go /usr/local/go
17+
ENV PATH="/usr/local/go/bin:${PATH}"
18+
RUN useradd -ms /bin/bash user
19+
USER user
20+
WORKDIR /app
21+
RUN --mount=type=bind,target=.
22+
23+
FROM fedora43 AS fedora-43-gnome-keyring
24+
ENV CGO_ENABLED=0
25+
USER user
26+
WORKDIR /app
27+
RUN --mount=type=bind,target=. \
28+
--mount=type=cache,target=/go/pkg/mod \
29+
--mount=type=cache,target=/root/.cache/go-build \
30+
/app/store/scripts/gnome-keyring \
31+
go test -v ./store/keychain/...
32+
33+
FROM fedora43 AS fedora-43-kdewallet
34+
ENV CGO_ENABLED=0
35+
USER user
36+
WORKDIR /app
37+
RUN --mount=type=bind,target=. \
38+
--mount=type=cache,target=/go/pkg/mod \
39+
--mount=type=cache,target=/root/.cache/go-build \
40+
/app/store/scripts/kdewallet \
41+
go test -v ./store/keychain/...
42+
43+
FROM ubuntu24 AS ubuntu-24-gnome-keyring
44+
ENV CGO_ENABLED=0
45+
USER user
46+
WORKDIR /app
47+
RUN --mount=type=bind,target=. \
48+
--mount=type=cache,target=/go/pkg/mod \
49+
--mount=type=cache,target=/root/.cache/go-build \
50+
/app/store/scripts/gnome-keyring \
51+
go test -v ./store/keychain/...
52+
53+
FROM ubuntu24 AS ubuntu-24-kdewallet
54+
ENV CGO_ENABLED=0
55+
USER user
56+
WORKDIR /app
57+
RUN --mount=type=bind,target=. \
58+
--mount=type=cache,target=/go/pkg/mod \
59+
--mount=type=cache,target=/root/.cache/go-build \
60+
/app/store/scripts/kdewallet \
61+
go test -v ./store/keychain/...

store/go.mod

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,11 @@ require (
1515
require (
1616
github.com/davecgh/go-spew v1.1.1 // indirect
1717
github.com/inconshreveable/mousetrap v1.1.0 // indirect
18+
github.com/kr/text v0.2.0 // indirect
19+
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
1820
github.com/pmezard/go-difflib v1.0.0 // indirect
1921
github.com/spf13/pflag v1.0.6 // indirect
2022
golang.org/x/crypto v0.32.0 // indirect
23+
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
2124
gopkg.in/yaml.v3 v3.0.1 // indirect
2225
)

store/go.sum

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ github.com/keybase/dbus v0.0.0-20220506165403-5aa21ea2c23a h1:K0EAzgzEQHW4Y5lxrm
77
github.com/keybase/dbus v0.0.0-20220506165403-5aa21ea2c23a/go.mod h1:YPNKjjE7Ubp9dTbnWvsP3HT+hYnY6TfXzubYTBeUxc8=
88
github.com/keybase/go-keychain v0.0.1 h1:way+bWYa6lDppZoZcgMbYsvC7GxljxrskdNInRtuthU=
99
github.com/keybase/go-keychain v0.0.1/go.mod h1:PdEILRW3i9D8JcdM+FmY6RwkHGnhHxXwkPPMeUgOK1k=
10+
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
11+
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
1012
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
1113
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
1214
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
@@ -18,7 +20,7 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf
1820
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
1921
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
2022
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
21-
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
2223
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
24+
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
2325
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
2426
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

store/keychain/keychain_test.go

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
package keychain
2+
3+
import (
4+
"maps"
5+
"testing"
6+
7+
"github.com/docker/secrets-engine/store"
8+
"github.com/docker/secrets-engine/store/mocks"
9+
"github.com/stretchr/testify/require"
10+
)
11+
12+
func TestKeychain(t *testing.T) {
13+
ks, err := New("com.test.test", "test",
14+
func() *mocks.MockCredential {
15+
return &mocks.MockCredential{}
16+
},
17+
)
18+
require.NoError(t, err)
19+
20+
t.Run("save credentials", func(t *testing.T) {
21+
id := store.ID("com.test.test/test/bob")
22+
require.NoError(t, id.Valid())
23+
creds := &mocks.MockCredential{
24+
Username: "bob",
25+
Password: "bob-password",
26+
}
27+
require.NoError(t, ks.Save(t.Context(), id, creds))
28+
})
29+
30+
t.Run("get credential", func(t *testing.T) {
31+
id := store.ID("com.test.test/test/bob")
32+
require.NoError(t, id.Valid())
33+
secret, err := ks.Get(t.Context(), id)
34+
require.NoError(t, err)
35+
36+
actual, ok := secret.(*mocks.MockCredential)
37+
require.True(t, ok)
38+
39+
expected := &mocks.MockCredential{
40+
Username: "bob",
41+
Password: "bob-password",
42+
}
43+
require.EqualValues(t, expected, actual)
44+
})
45+
46+
t.Run("list all credentials", func(t *testing.T) {
47+
moreCreds := map[store.ID]*mocks.MockCredential{
48+
"com.test.test/test/jeff": {
49+
Username: "jeff",
50+
Password: "jeff-password",
51+
},
52+
"com.test.test/test/pete": {
53+
Username: "pete",
54+
Password: "pete-password",
55+
},
56+
}
57+
58+
for id, anotherCred := range moreCreds {
59+
require.NoError(t, ks.Save(t.Context(), id, anotherCred))
60+
}
61+
secrets, err := ks.GetAll(t.Context())
62+
require.NoError(t, err)
63+
64+
actual := make(map[store.ID]*mocks.MockCredential)
65+
for id, s := range secrets {
66+
actual[id] = s.(*mocks.MockCredential)
67+
}
68+
require.Len(t, actual, 3)
69+
70+
expected := map[store.ID]*mocks.MockCredential{
71+
"com.test.test/test/bob": {
72+
Username: "bob",
73+
Password: "bob-password",
74+
},
75+
}
76+
maps.Copy(expected, moreCreds)
77+
require.Len(t, expected, 3)
78+
require.Equal(t, expected, actual)
79+
})
80+
81+
t.Run("delete credential", func(t *testing.T) {
82+
id := store.ID("com.test.test/test/bob")
83+
require.NoError(t, id.Valid())
84+
require.NoError(t, ks.Delete(t.Context(), id))
85+
_, err := ks.Get(t.Context(), id)
86+
require.ErrorIs(t, err, store.ErrCredentialNotFound)
87+
})
88+
}

store/scripts/gnome-keyring

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
#!/bin/sh
2+
3+
set -eux pipefail
4+
5+
if test -z $(command -v gnome-keyring-daemon); then
6+
echo "gnome-keyring-daemon is not installed"
7+
exit 1
8+
fi
9+
10+
if test -z $(command -v dbus-daemon); then
11+
echo "dbus-daemon is not installed"
12+
exit 1
13+
fi
14+
15+
mkdir -p ~/.local/share/keyrings
16+
touch ~/.local/share/keyrings/login.keyring
17+
18+
# create fake passwordless 'login' keyring
19+
echo '[keyring]
20+
display-name=login
21+
ctime=1750965549
22+
mtime=0
23+
lock-on-idle=false
24+
lock-after=false' > ~/.local/share/keyrings/login.keyring
25+
26+
# Start D-Bus session (dbus must be installed)
27+
export DBUS_SESSION_BUS_ADDRESS=$(dbus-daemon --session --print-address --fork)
28+
29+
gnome-keyring-daemon --start --components=secrets
30+
31+
# Wait up to 5 seconds for org.freedesktop.secrets to appear on D-Bus
32+
timeout=5000
33+
interval=100
34+
elapsed=0
35+
36+
while ! gdbus call --session \
37+
--dest org.freedesktop.DBus \
38+
--object-path /org/freedesktop/DBus \
39+
--method org.freedesktop.DBus.GetNameOwner \
40+
org.freedesktop.secrets >/dev/null 2>&1; do
41+
42+
sleep 0.1
43+
elapsed=$((elapsed + interval))
44+
if (( elapsed >= timeout )); then
45+
echo "❌ Timeout waiting for gnome-keyring-daemon to register org.freedesktop.secrets"
46+
exit 1
47+
fi
48+
done
49+
50+
owner=$(gdbus call --session \
51+
--dest org.freedesktop.DBus \
52+
--object-path /org/freedesktop/DBus \
53+
--method org.freedesktop.DBus.GetNameOwner \
54+
org.freedesktop.secrets | awk -F"'" '{print $2}')
55+
56+
if test -v $owner; then
57+
echo "there is no owner of the org.freedesktop.secrets API"
58+
exit 1
59+
fi
60+
61+
pid=$(gdbus call --session \
62+
--dest org.freedesktop.DBus \
63+
--object-path /org/freedesktop/DBus \
64+
--method org.freedesktop.DBus.GetConnectionUnixProcessID \
65+
"$owner" | awk '{print $2}' | tr -d ',)')
66+
67+
if test -v $pid; then
68+
echo "there is no registered org.freedesktop.secrets daemon"
69+
exit 1
70+
fi
71+
72+
exe=$(readlink -f /proc/$pid/exe)
73+
74+
if [[ "$exe" != *gnome-keyring-daemon* ]]; then
75+
echo "dbus org.freedesktop.secrets is not using gnome-keyring-daemon"
76+
exit 1
77+
fi

0 commit comments

Comments
 (0)