/
reference.go
153 lines (135 loc) · 5.19 KB
/
reference.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
package reference
import (
"context"
corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
k8stypes "k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/kong/kubernetes-ingress-controller/v2/internal/controllers"
)
const (
VersionV1 = "v1"
KindSecret = "Secret"
CACertLabelKey = "konghq.com/ca-cert"
)
// UpdateReferencesToSecret updates the reference records between referrer and each secret
// in namespacedNames in record cache.
func UpdateReferencesToSecret(
ctx context.Context,
c client.Client, indexers CacheIndexers, dataplaneClient controllers.DataPlaneClient,
referrer client.Object, referencedSecretNameMap map[k8stypes.NamespacedName]struct{},
) error {
for nsName := range referencedSecretNameMap {
secret := &corev1.Secret{
TypeMeta: metav1.TypeMeta{
APIVersion: VersionV1,
Kind: KindSecret,
},
ObjectMeta: metav1.ObjectMeta{
Namespace: nsName.Namespace,
Name: nsName.Name,
},
}
// Here we update the reference relationship even when the referred secret does not exist yet
// If the referred secret is created, it could be reconciled in secret controller.
referrerCopy := referrer.DeepCopyObject().(client.Object)
if err := indexers.SetObjectReference(
referrerCopy, secret.DeepCopy()); err != nil {
return err
}
if err := c.Get(ctx, nsName, secret); err != nil {
return err
}
if err := dataplaneClient.UpdateObject(secret); err != nil {
return err
}
}
return removeOutdatedReferencesToSecret(ctx, indexers, c, dataplaneClient, referrer, referencedSecretNameMap)
}
// removeOutdatedReferenceToSecret removes outdated reference records to secrets in reference indexer.
// secrets that are referred by referrer are passed in referredSecretNames parameter.
// If a secret is not referenced by any other object after deleting outdated reference records,
// and it does not have label "konghq.com/ca-cert:true", it is not possible to be used in Kong gateway config
// and should be removed from the object cache inside KongClient.
func removeOutdatedReferencesToSecret(
ctx context.Context,
indexers CacheIndexers, c client.Client, dataplaneClient controllers.DataPlaneClient,
referrer client.Object, referredSecretNameMap map[k8stypes.NamespacedName]struct{},
) error {
referents, err := indexers.ListReferredObjects(referrer)
if err != nil {
return err
}
for _, obj := range referents {
gvk := obj.GetObjectKind().GroupVersionKind()
// delete the reference record if the secret is not referred by the service.
if gvk.Group == corev1.GroupName && gvk.Version == VersionV1 && gvk.Kind == KindSecret {
namespacedName := k8stypes.NamespacedName{
Namespace: obj.GetNamespace(),
Name: obj.GetName(),
}
// if the secret is still referenced, no operations are taken so continue here.
if _, ok := referredSecretNameMap[namespacedName]; ok {
continue
}
if err := indexers.DeleteObjectReference(referrer, obj); err != nil {
return err
}
// remove the secret in object cache if it is not referred and does not have label "konghq.com/ca-cert:true".
// Do this check and delete when the reference count may be reduced by 1.
// retrieve the secret in k8s and check it has the label.
secret := &corev1.Secret{}
getErr := c.Get(ctx, namespacedName, secret)
// if the secret exists in k8s and has the label, we should not delete it in object cache.
if getErr == nil {
if secret.Labels != nil && secret.Labels[CACertLabelKey] == "true" {
continue
}
} else {
// if the secret does not exist in k8s, we ignore the error and continue the check and delete operation.
// for other errors, we return the error and stop the operation.
if !apierrors.IsNotFound(getErr) {
return err
}
}
if err := indexers.DeleteObjectIfNotReferred(obj, dataplaneClient); err != nil {
return err
}
}
}
return nil
}
// DeleteReferencesByReferrer deletes all reference records with specified referrer
// in reference cache.
// If the affected secret is not referred by any other objects, it deletes the secret in object cache.
func DeleteReferencesByReferrer(indexers CacheIndexers, dataplaneClient controllers.DataPlaneClient, referrer client.Object) error {
referents, err := indexers.ListReferredObjects(referrer)
if err != nil {
indexers.logger.Error(err, "failed to list referred objects",
"referrer_kind", referrer.GetObjectKind().GroupVersionKind().String(),
"referrer_namespace", referrer.GetNamespace(),
"referrer_name", referrer.GetName(),
)
return err
}
// delete(gc) the reference record between referrer and referent.
for _, referent := range referents {
err := indexers.DeleteObjectReference(referrer, referent)
if err != nil {
return err
}
}
// delete the referent in object cache if it is a secret and it is not referenced anymore.
for _, referent := range referents {
gvk := referent.GetObjectKind().GroupVersionKind()
if !(gvk.Group == corev1.GroupName && gvk.Version == VersionV1 && gvk.Kind == KindSecret) {
continue
}
err := indexers.DeleteObjectIfNotReferred(referent, dataplaneClient)
if err != nil {
return err
}
}
return nil
}