Skip to content

Commit e28a0e4

Browse files
author
CodingWizKid
committed
add more tests
1 parent 4b6bf69 commit e28a0e4

File tree

7 files changed

+296
-12
lines changed

7 files changed

+296
-12
lines changed

cmd/main.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"os"
66

77
"github.com/cert-manager/cert-manager/pkg/acme/webhook/cmd"
8+
"github.com/stackitcloud/stackit-cert-manager-webhook/internal/repository"
89
"github.com/stackitcloud/stackit-cert-manager-webhook/internal/resolver"
910
_ "go.uber.org/automaxprocs"
1011
)
@@ -22,7 +23,14 @@ func main() {
2223
// You can register multiple DNS provider implementations with a single
2324
// webhook, where the Name() method will be used to disambiguate between
2425
// the different implementations.
25-
cmd.RunWebhookServer(GroupName,
26-
resolver.NewResolver(&http.Client{}),
26+
cmd.RunWebhookServer(
27+
GroupName,
28+
resolver.NewResolver(
29+
&http.Client{},
30+
repository.NewZoneRepositoryFactory(),
31+
repository.NewRRSetRepositoryFactory(),
32+
resolver.NewSecretFetcher(),
33+
resolver.NewConfigProvider(),
34+
),
2735
)
2836
}

internal/resolver/config.go

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@ type ConfigProvider interface {
1414
LoadConfig(cfgJSON *extapi.JSON) (StackitDnsProviderConfig, error)
1515
}
1616

17-
type defaultConfigProvider struct{}
17+
type defaultConfigProvider struct {
18+
fileNamespaceName string
19+
}
1820

1921
type StackitDnsProviderConfig struct {
2022
ProjectId string `json:"projectId"`
@@ -41,7 +43,7 @@ func (d defaultConfigProvider) LoadConfig(cfgJSON *extapi.JSON) (StackitDnsProvi
4143

4244
setDefaultValues(&cfg)
4345

44-
namespace, err := determineNamespace(cfg.AuthTokenSecretNamespace)
46+
namespace, err := determineNamespace(cfg.AuthTokenSecretNamespace, d.fileNamespaceName)
4547
if err != nil {
4648
return cfg, err
4749
}
@@ -78,12 +80,12 @@ func setDefaultValues(cfg *StackitDnsProviderConfig) {
7880
}
7981
}
8082

81-
func determineNamespace(currentNamespace string) (string, error) {
83+
func determineNamespace(currentNamespace string, fileNamespaceName string) (string, error) {
8284
if currentNamespace != "" {
8385
return currentNamespace, nil
8486
}
8587

86-
data, err := os.ReadFile("/var/run/secrets/kubernetes.io/serviceaccount/namespace")
88+
data, err := os.ReadFile(fileNamespaceName)
8789
if err != nil {
8890
return "", fmt.Errorf("failed to find the webhook pod namespace: %w", err)
8991
}
@@ -95,3 +97,9 @@ func determineNamespace(currentNamespace string) (string, error) {
9597

9698
return namespace, nil
9799
}
100+
101+
func NewConfigProvider() ConfigProvider {
102+
return defaultConfigProvider{
103+
fileNamespaceName: "/var/run/secrets/kubernetes.io/serviceaccount/namespace",
104+
}
105+
}

internal/resolver/config_test.go

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
package resolver
2+
3+
import (
4+
"os"
5+
"testing"
6+
7+
"github.com/stretchr/testify/assert"
8+
"go.uber.org/mock/gomock"
9+
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
10+
)
11+
12+
func TestLoadConfig(t *testing.T) {
13+
t.Parallel()
14+
ctrl := gomock.NewController(t)
15+
t.Cleanup(ctrl.Finish)
16+
17+
d := defaultConfigProvider{}
18+
19+
t.Run("nil cfgJSON", func(t *testing.T) {
20+
t.Parallel()
21+
22+
cfg, err := d.LoadConfig(nil)
23+
assert.Error(t, err)
24+
assert.Equal(t, "no configProvider provided", err.Error())
25+
assert.Equal(t, StackitDnsProviderConfig{}, cfg)
26+
})
27+
28+
t.Run("valid cfgJSON", func(t *testing.T) {
29+
t.Parallel()
30+
31+
rawCfg := &v1.JSON{Raw: []byte(`{"projectId":"test", "authTokenSecretNamespace": "test"}`)}
32+
33+
cfg, err := d.LoadConfig(rawCfg)
34+
assert.NoError(t, err)
35+
assert.Equal(t, "test", cfg.ProjectId)
36+
})
37+
38+
t.Run("not parsable cfgJSON", func(t *testing.T) {
39+
t.Parallel()
40+
41+
rawCfg := &v1.JSON{Raw: []byte(`{"projectId":}`)}
42+
cfg, err := d.LoadConfig(rawCfg)
43+
assert.Error(t, err)
44+
assert.Contains(t, err.Error(), "error decoding solver configProvider")
45+
assert.Equal(t, StackitDnsProviderConfig{}, cfg)
46+
})
47+
48+
t.Run("invalid cfgJSON", func(t *testing.T) {
49+
t.Parallel()
50+
51+
rawCfg := &v1.JSON{Raw: []byte(`{"projectId": ""}`)}
52+
cfg, err := d.LoadConfig(rawCfg)
53+
assert.Error(t, err)
54+
assert.Contains(t, err.Error(), "projectId must be specified")
55+
assert.Equal(t, StackitDnsProviderConfig{}, cfg)
56+
})
57+
58+
t.Run("missing projectId", func(t *testing.T) {
59+
t.Parallel()
60+
61+
rawCfg := &v1.JSON{Raw: []byte(`{}`)}
62+
cfg, err := d.LoadConfig(rawCfg)
63+
assert.Error(t, err)
64+
assert.Equal(t, "projectId must be specified", err.Error())
65+
assert.Equal(t, StackitDnsProviderConfig{}, cfg)
66+
})
67+
68+
t.Run("default values set", func(t *testing.T) {
69+
t.Parallel()
70+
71+
rawCfg := &v1.JSON{Raw: []byte(`{"projectId":"test", "authTokenSecretNamespace": "test"}`)} // Only projectId provided
72+
cfg, err := d.LoadConfig(rawCfg)
73+
assert.NoError(t, err)
74+
assert.Equal(t, "test", cfg.ProjectId)
75+
assert.Equal(t, "https://dns.api.stackit.cloud", cfg.ApiBasePath)
76+
assert.Equal(t, "stackit-cert-manager-webhook", cfg.AuthTokenSecretRef)
77+
assert.Equal(t, "auth-token", cfg.AuthTokenSecretKey)
78+
})
79+
}
80+
81+
func TestDefaultConfigProvider_LoadConfigNamespaceFile(t *testing.T) {
82+
t.Parallel()
83+
84+
ctrl := gomock.NewController(t)
85+
t.Cleanup(ctrl.Finish)
86+
87+
d := defaultConfigProvider{}
88+
89+
t.Run("determine namespace from file", func(t *testing.T) {
90+
t.Parallel()
91+
92+
rawCfg := &v1.JSON{Raw: []byte(`{"projectId":"test"}`)}
93+
94+
f, err := os.CreateTemp("", "example")
95+
assert.NoError(t, err)
96+
defer os.Remove(f.Name())
97+
_, err = f.Write([]byte("test-namespace"))
98+
assert.NoError(t, err)
99+
err = f.Close()
100+
assert.NoError(t, err)
101+
102+
dcp := defaultConfigProvider{fileNamespaceName: f.Name()}
103+
cfg, err := dcp.LoadConfig(rawCfg)
104+
assert.NoError(t, err)
105+
assert.Equal(t, "test-namespace", cfg.AuthTokenSecretNamespace)
106+
})
107+
108+
t.Run("fail determine namespace from file, no content", func(t *testing.T) {
109+
t.Parallel()
110+
111+
rawCfg := &v1.JSON{Raw: []byte(`{"projectId":"test"}`)}
112+
113+
f, err := os.CreateTemp("", "example")
114+
assert.NoError(t, err)
115+
defer os.Remove(f.Name())
116+
_, err = f.Write([]byte(""))
117+
assert.NoError(t, err)
118+
err = f.Close()
119+
assert.NoError(t, err)
120+
121+
dcp := defaultConfigProvider{fileNamespaceName: f.Name()}
122+
_, err = dcp.LoadConfig(rawCfg)
123+
assert.Error(t, err)
124+
assert.Contains(t, err.Error(), "invalid webhook pod namespace provided")
125+
})
126+
127+
t.Run("fail to determine namespace from file", func(t *testing.T) {
128+
t.Parallel()
129+
130+
rawCfg := &v1.JSON{Raw: []byte(`{"projectId":"test"}`)}
131+
132+
_, err := d.LoadConfig(rawCfg)
133+
assert.Error(t, err)
134+
assert.Contains(t, err.Error(), "failed to find the webhook pod namespace")
135+
})
136+
}

internal/resolver/resolver.go

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,20 @@ import (
1616

1717
const typeTxtRecord = "TXT"
1818

19-
func NewResolver(httpClient *http.Client) webhook.Solver {
19+
func NewResolver(
20+
httpClient *http.Client,
21+
zoneRepositoryFactory repository.ZoneRepositoryFactory,
22+
rrSetRepositoryFactory repository.RRSetRepositoryFactory,
23+
secretFetcher SecretFetcher,
24+
configProvider ConfigProvider,
25+
) webhook.Solver {
2026
return &stackitDnsProviderResolver{
2127
ctx: context.Background(),
2228
httpClient: httpClient,
23-
configProvider: defaultConfigProvider{},
24-
secretFetcher: &kubeSecretFetcher{},
25-
zoneRepositoryFactory: repository.NewZoneRepositoryFactory(),
26-
rrSetRepositoryFactory: repository.NewRRSetRepositoryFactory(),
29+
configProvider: configProvider,
30+
secretFetcher: secretFetcher,
31+
zoneRepositoryFactory: zoneRepositoryFactory,
32+
rrSetRepositoryFactory: rrSetRepositoryFactory,
2733
}
2834
}
2935

internal/resolver/resolver_test.go

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
package resolver_test
2+
3+
import (
4+
"fmt"
5+
"net/http"
6+
"testing"
7+
8+
"github.com/cert-manager/cert-manager/pkg/acme/webhook/apis/acme/v1alpha1"
9+
repository_mock "github.com/stackitcloud/stackit-cert-manager-webhook/internal/repository/mock"
10+
"github.com/stackitcloud/stackit-cert-manager-webhook/internal/resolver"
11+
resolver_mock "github.com/stackitcloud/stackit-cert-manager-webhook/internal/resolver/mock"
12+
"github.com/stretchr/testify/assert"
13+
"go.uber.org/mock/gomock"
14+
v1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
15+
"k8s.io/client-go/rest"
16+
)
17+
18+
func TestName(t *testing.T) {
19+
t.Parallel()
20+
21+
r := resolver.NewResolver(nil, nil, nil, nil, nil)
22+
23+
assert.Equal(t, r.Name(), "stackit")
24+
}
25+
26+
func TestInitialize(t *testing.T) {
27+
t.Parallel()
28+
29+
r := resolver.NewResolver(nil, nil, nil, nil, nil)
30+
31+
t.Run("successful init", func(t *testing.T) {
32+
t.Parallel()
33+
34+
kubeConfig := &rest.Config{}
35+
err := r.Initialize(kubeConfig, nil)
36+
assert.NoError(t, err)
37+
})
38+
39+
t.Run("unsuccessful init", func(t *testing.T) {
40+
t.Parallel()
41+
42+
kubeConfig := &rest.Config{Burst: -1, RateLimiter: nil, QPS: 1}
43+
err := r.Initialize(kubeConfig, nil)
44+
assert.Error(t, err)
45+
})
46+
}
47+
48+
func TestStackitDnsProviderResolver_Present(t *testing.T) {
49+
t.Parallel()
50+
51+
ctrl := gomock.NewController(t)
52+
defer ctrl.Finish()
53+
54+
mockSecretFetcher := resolver_mock.NewMockSecretFetcher(ctrl)
55+
mockConfigProvider := resolver_mock.NewMockConfigProvider(ctrl)
56+
mockZoneRepositoryFactory := repository_mock.NewMockZoneRepositoryFactory(ctrl)
57+
mockRRSetRepositoryFactory := repository_mock.NewMockRRSetRepositoryFactory(ctrl)
58+
59+
configJson := &v1.JSON{Raw: []byte(`{"projectId":"test"}`)}
60+
mockConfigProvider.EXPECT().
61+
LoadConfig(configJson).
62+
Return(resolver.StackitDnsProviderConfig{}, fmt.Errorf("error decoding solver configProvider"))
63+
64+
r := resolver.NewResolver(
65+
&http.Client{},
66+
mockZoneRepositoryFactory,
67+
mockRRSetRepositoryFactory,
68+
mockSecretFetcher,
69+
mockConfigProvider,
70+
)
71+
72+
ch := &v1alpha1.ChallengeRequest{
73+
Config: configJson,
74+
}
75+
76+
err := r.Present(ch)
77+
assert.Error(t, err)
78+
}

internal/resolver/secrets.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ type SecretFetcher interface {
1414
}
1515

1616
type kubeSecretFetcher struct {
17-
client *kubernetes.Clientset
17+
client kubernetes.Interface
1818
ctx context.Context
1919
}
2020

@@ -32,3 +32,7 @@ func (k *kubeSecretFetcher) StringFromSecret(namespace, secretName, key string)
3232

3333
return string(binary), nil
3434
}
35+
36+
func NewSecretFetcher() SecretFetcher {
37+
return &kubeSecretFetcher{}
38+
}

internal/resolver/secrets_test.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package resolver
2+
3+
import (
4+
"context"
5+
"testing"
6+
7+
"github.com/stretchr/testify/assert"
8+
corev1 "k8s.io/api/core/v1"
9+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
10+
"k8s.io/client-go/kubernetes/fake"
11+
)
12+
13+
func TestStringFromSecret(t *testing.T) {
14+
t.Parallel()
15+
16+
client := fake.NewSimpleClientset(&corev1.Secret{
17+
ObjectMeta: metav1.ObjectMeta{
18+
Name: "test-secret",
19+
Namespace: "test-namespace",
20+
},
21+
Data: map[string][]byte{
22+
"test-key": []byte("test-value"),
23+
},
24+
})
25+
26+
fetcher := &kubeSecretFetcher{
27+
client: client,
28+
ctx: context.TODO(),
29+
}
30+
31+
// check for expected value
32+
value, err := fetcher.StringFromSecret("test-namespace", "test-secret", "test-key")
33+
assert.NoError(t, err)
34+
assert.Equal(t, "test-value", value)
35+
36+
// check for a non-existent key
37+
_, err = fetcher.StringFromSecret("test-namespace", "test-secret", "non-existent-key")
38+
assert.Error(t, err)
39+
assert.Contains(t, err.Error(), "key `\"non-existent-key\"` not found in secretFetcher `test-namespace/test-secret`")
40+
41+
// check for a non-existent secret
42+
_, err = fetcher.StringFromSecret("test-namespace", "non-existent-secret", "test-key")
43+
assert.Error(t, err)
44+
}

0 commit comments

Comments
 (0)