Skip to content

Commit 299f2c1

Browse files
fix: get secret from cluster if not present in cache (#1571) (#1581)
1 parent 5361cbb commit 299f2c1

File tree

11 files changed

+180
-105
lines changed

11 files changed

+180
-105
lines changed

pkg/k8scontext/context.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ func NewContext(kubeClient kubernetes.Interface, crdClient versioned.Interface,
107107
informers: &informerCollection,
108108
ingressSecretsMap: utils.NewThreadsafeMultimap(),
109109
Caches: &cacheCollection,
110-
CertificateSecretStore: NewSecretStore(),
110+
CertificateSecretStore: NewSecretStore(kubeClient),
111111
Work: make(chan events.Event, workBuffer),
112112
CacheSynced: make(chan interface{}),
113113

pkg/k8scontext/handlers_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import (
1212
v1 "k8s.io/api/core/v1"
1313
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1414

15-
"github.com/onsi/ginkgo"
15+
"github.com/onsi/ginkgo/v2"
1616
. "github.com/onsi/gomega"
1717
"k8s.io/client-go/kubernetes"
1818
testclient "k8s.io/client-go/kubernetes/fake"

pkg/k8scontext/ingress_handlers_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import (
99
"context"
1010
"time"
1111

12-
"github.com/onsi/ginkgo"
12+
"github.com/onsi/ginkgo/v2"
1313
. "github.com/onsi/gomega"
1414
v1 "k8s.io/api/core/v1"
1515
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

pkg/k8scontext/k8scontext_suite_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// Licensed under the MIT License. See License.txt in the project root for license information.
44
// --------------------------------------------------------------------------------------------
55

6+
//go:build unittest
67
// +build unittest
78

89
package k8scontext
@@ -11,7 +12,7 @@ import (
1112
"flag"
1213
"testing"
1314

14-
"github.com/onsi/ginkgo"
15+
"github.com/onsi/ginkgo/v2"
1516
"github.com/onsi/gomega"
1617
"k8s.io/klog/v2"
1718
)

pkg/k8scontext/k8scontext_test.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import (
1212

1313
"github.com/getlantern/deepcopy"
1414
"github.com/knative/pkg/apis/istio/v1alpha3"
15-
"github.com/onsi/ginkgo"
15+
"github.com/onsi/ginkgo/v2"
1616
. "github.com/onsi/gomega"
1717
v1 "k8s.io/api/core/v1"
1818
networking "k8s.io/api/networking/v1"
@@ -73,7 +73,6 @@ var _ = ginkgo.Describe("K8scontext", func() {
7373
}
7474
}
7575
case <-time.After(1 * time.Second):
76-
break
7776
}
7877

7978
if len(exists) == len(resourceList) {

pkg/k8scontext/secrets_handlers.go

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,12 @@ import (
1818

1919
// secret resource handlers
2020
func (h handlers) secretAdd(obj interface{}) {
21-
sec := obj.(*v1.Secret)
21+
sec, ok := obj.(*v1.Secret)
22+
if !ok {
23+
klog.Error("error decoding object, invalid type")
24+
return
25+
}
26+
2227
if _, exists := namespacesToIgnore[sec.Namespace]; exists {
2328
return
2429
}
@@ -42,7 +47,12 @@ func (h handlers) secretAdd(obj interface{}) {
4247
}
4348

4449
func (h handlers) secretUpdate(oldObj, newObj interface{}) {
45-
sec := newObj.(*v1.Secret)
50+
sec, ok := newObj.(*v1.Secret)
51+
if !ok {
52+
klog.Error("error decoding object, invalid type")
53+
return
54+
}
55+
4656
if _, exists := namespacesToIgnore[sec.Namespace]; exists {
4757
return
4858
}
@@ -70,6 +80,11 @@ func (h handlers) secretUpdate(oldObj, newObj interface{}) {
7080

7181
func (h handlers) secretDelete(obj interface{}) {
7282
sec, ok := obj.(*v1.Secret)
83+
if !ok {
84+
klog.Error("error decoding object, invalid type")
85+
return
86+
}
87+
7388
if _, exists := namespacesToIgnore[sec.Namespace]; exists {
7489
return
7590
}

pkg/k8scontext/secrets_handlers_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import (
1212
v1 "k8s.io/api/core/v1"
1313
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1414

15-
"github.com/onsi/ginkgo"
15+
"github.com/onsi/ginkgo/v2"
1616
. "github.com/onsi/gomega"
1717
"k8s.io/client-go/kubernetes"
1818
testclient "k8s.io/client-go/kubernetes/fake"

pkg/k8scontext/secretstore.go

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,19 @@ package k8scontext
77

88
import (
99
"bytes"
10+
"context"
1011
"io/ioutil"
1112
"os"
1213
"os/exec"
1314
"sync"
1415

1516
v1 "k8s.io/api/core/v1"
17+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
18+
"k8s.io/client-go/kubernetes"
1619
"k8s.io/client-go/tools/cache"
1720

1821
"github.com/Azure/application-gateway-kubernetes-ingress/pkg/controllererrors"
19-
)
20-
21-
const (
22-
recognizedSecretType = "kubernetes.io/tls"
23-
tlsKey = "tls.key"
24-
tlsCrt = "tls.crt"
22+
"github.com/Azure/application-gateway-kubernetes-ingress/pkg/utils"
2523
)
2624

2725
// SecretsKeeper is the interface definition for secret store
@@ -34,13 +32,15 @@ type SecretsKeeper interface {
3432
// SecretsStore maintains a cache of the deployment secrets.
3533
type SecretsStore struct {
3634
conversionSync sync.Mutex
35+
Client kubernetes.Interface
3736
Cache cache.ThreadSafeStore
3837
}
3938

4039
// NewSecretStore creates a new SecretsKeeper object
41-
func NewSecretStore() SecretsKeeper {
40+
func NewSecretStore(client kubernetes.Interface) SecretsKeeper {
4241
return &SecretsStore{
43-
Cache: cache.NewThreadSafeStore(cache.Indexers{}, cache.Indices{}),
42+
Cache: cache.NewThreadSafeStore(cache.Indexers{}, cache.Indices{}),
43+
Client: client,
4444
}
4545
}
4646

@@ -51,9 +51,33 @@ func (s *SecretsStore) GetPfxCertificate(secretKey string) []byte {
5151
return cert
5252
}
5353
}
54+
55+
if cert, err := s.GetFromCluster(secretKey); err == nil {
56+
return cert
57+
}
5458
return nil
5559
}
5660

61+
func (s *SecretsStore) GetFromCluster(secretKey string) ([]byte, error) {
62+
secretNamespace, secretName, err := utils.ParseNamespacedName(secretKey)
63+
if err != nil {
64+
return nil, err
65+
}
66+
67+
secret, err := s.Client.CoreV1().Secrets(secretNamespace).Get(context.Background(), secretName, metav1.GetOptions{})
68+
if err != nil {
69+
return nil, err
70+
}
71+
72+
if err := s.ConvertSecret(secretKey, secret); err != nil {
73+
return nil, err
74+
}
75+
76+
certInterface, _ := s.Cache.Get(secretKey)
77+
cert, _ := certInterface.([]byte)
78+
return cert, nil
79+
}
80+
5781
func (s *SecretsStore) delete(secretKey string) {
5882
s.conversionSync.Lock()
5983
defer s.conversionSync.Unlock()
@@ -67,14 +91,14 @@ func (s *SecretsStore) ConvertSecret(secretKey string, secret *v1.Secret) error
6791
defer s.conversionSync.Unlock()
6892

6993
// check if this is a secret with the correct type
70-
if secret.Type != recognizedSecretType {
94+
if secret.Type != v1.SecretTypeTLS {
7195
return controllererrors.NewErrorf(
7296
controllererrors.ErrorUnknownSecretType,
7397
"secret [%v] is not type kubernetes.io/tls", secretKey,
7498
)
7599
}
76100

77-
if len(secret.Data[tlsKey]) == 0 || len(secret.Data[tlsCrt]) == 0 {
101+
if len(secret.Data[v1.TLSCertKey]) == 0 || len(secret.Data[v1.TLSPrivateKeyKey]) == 0 {
78102
return controllererrors.NewErrorf(
79103
controllererrors.ErrorMalformedSecret,
80104
"secret [%v] is malformed, tls.key or tls.crt is not defined", secretKey,

pkg/k8scontext/secretstore_test.go

Lines changed: 40 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -6,42 +6,58 @@
66
package k8scontext
77

88
import (
9-
"github.com/onsi/ginkgo"
9+
"github.com/onsi/ginkgo/v2"
1010
. "github.com/onsi/gomega"
1111
v1 "k8s.io/api/core/v1"
12+
"k8s.io/client-go/kubernetes"
13+
testclient "k8s.io/client-go/kubernetes/fake"
1214

1315
"github.com/Azure/application-gateway-kubernetes-ingress/pkg/controllererrors"
1416
"github.com/Azure/application-gateway-kubernetes-ingress/pkg/tests"
1517
)
1618

1719
var _ = ginkgo.Describe("Testing K8sContext.SecretStore", func() {
18-
secretsStore := NewSecretStore()
19-
ginkgo.Context("Test ConvertSecret function", func() {
20-
secret := v1.Secret{}
21-
ginkgo.It("Should have returned an error - unrecognized type of secret", func() {
22-
err := secretsStore.ConvertSecret("someKey", &secret)
23-
Expect(err.(*controllererrors.Error).Code).To(Equal(controllererrors.ErrorUnknownSecretType))
24-
})
25-
ginkgo.It("", func() {
26-
malformed := secret
27-
malformed.Type = recognizedSecretType
28-
err := secretsStore.ConvertSecret("someKey", &malformed)
29-
Expect(err.(*controllererrors.Error).Code).To(Equal(controllererrors.ErrorMalformedSecret))
30-
})
31-
ginkgo.It("", func() {
32-
malformed := secret
33-
malformed.Type = recognizedSecretType
34-
malformed.Data = make(map[string][]byte)
35-
malformed.Data[tlsKey] = []byte("X")
36-
malformed.Data[tlsCrt] = []byte("Y")
37-
err := secretsStore.ConvertSecret("someKey", &malformed)
38-
Expect(err.(*controllererrors.Error).Code).To(Equal(controllererrors.ErrorExportingWithOpenSSL))
39-
})
40-
ginkgo.It("", func() {
20+
secretsStore := NewSecretStore(nil)
21+
22+
ginkgo.DescribeTable("when converting certificate to PFX",
23+
func(secret *v1.Secret, expectedError controllererrors.ErrorCode) {
24+
err := secretsStore.ConvertSecret("someKey", secret)
25+
Expect(err.(*controllererrors.Error).Code).To(Equal(expectedError))
26+
},
27+
ginkgo.Entry("no type in secret", &v1.Secret{}, controllererrors.ErrorUnknownSecretType),
28+
ginkgo.Entry("unrecognized type of secret", &v1.Secret{Type: v1.SecretTypeOpaque}, controllererrors.ErrorUnknownSecretType),
29+
ginkgo.Entry("malformed data", &v1.Secret{Type: v1.SecretTypeTLS, Data: map[string][]byte{}}, controllererrors.ErrorMalformedSecret),
30+
ginkgo.Entry("invalid data", &v1.Secret{Type: v1.SecretTypeTLS, Data: map[string][]byte{
31+
v1.TLSCertKey: []byte("X"),
32+
v1.TLSPrivateKeyKey: []byte("X"),
33+
}}, controllererrors.ErrorExportingWithOpenSSL),
34+
)
35+
36+
ginkgo.When("certificate gets stored", func() {
37+
ginkgo.It("should be retrivable with the secret key", func() {
4138
err := secretsStore.ConvertSecret("someKey", tests.NewSecretTestFixture())
4239
Expect(err).ToNot(HaveOccurred())
4340
actual := secretsStore.GetPfxCertificate("someKey")
4441
Expect(len(actual)).To(BeNumerically(">", 0))
4542
})
4643
})
44+
45+
ginkgo.When("certificate is no cached", func() {
46+
ginkgo.It("should get it from the api-server", func() {
47+
secret := tests.NewSecretTestFixture()
48+
var client kubernetes.Interface = testclient.NewSimpleClientset(secret)
49+
secretsStore := NewSecretStore(client)
50+
51+
actual := secretsStore.GetPfxCertificate(secret.Namespace + "/" + secret.Name)
52+
Expect(len(actual)).To(BeNumerically(">", 0))
53+
})
54+
55+
ginkgo.It("should return nil if secret does not exist", func() {
56+
var client kubernetes.Interface = testclient.NewSimpleClientset()
57+
secretsStore := NewSecretStore(client)
58+
59+
actual := secretsStore.GetPfxCertificate("someKey")
60+
Expect(actual).To(BeNil())
61+
})
62+
})
4763
})

pkg/utils/utils.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,3 +97,11 @@ func RemoveDuplicateStrings(list []string) []string {
9797

9898
return result
9999
}
100+
101+
func ParseNamespacedName(namespacedName string) (string, string, error) {
102+
split := strings.Split(namespacedName, "/")
103+
if len(split) != 2 {
104+
return "", "", fmt.Errorf("invalid namespaced name %s", namespacedName)
105+
}
106+
return split[0], split[1], nil
107+
}

0 commit comments

Comments
 (0)