forked from metal3-io/baremetal-operator
/
secret_manager.go
174 lines (150 loc) · 6.01 KB
/
secret_manager.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
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
package secretutils
import (
"context"
"fmt"
"github.com/go-logr/logr"
"github.com/pkg/errors"
corev1 "k8s.io/api/core/v1"
k8serrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
metal3v1alpha1 "github.com/metal3-io/baremetal-operator/apis/metal3.io/v1alpha1"
"github.com/metal3-io/baremetal-operator/pkg/utils"
)
const (
SecretsFinalizer = metal3v1alpha1.BareMetalHostFinalizer + "/secret"
)
// SecretManager is a type for fetching Secrets whether or not they are in the
// client cache, labelling so that they will be included in the client cache,
// and optionally setting an owner reference.
type SecretManager struct {
log logr.Logger
client client.Client
apiReader client.Reader
}
// NewSecretManager returns a new SecretManager
func NewSecretManager(log logr.Logger, cacheClient client.Client, apiReader client.Reader) SecretManager {
return SecretManager{
log: log.WithName("secret_manager"),
client: cacheClient,
apiReader: apiReader,
}
}
// findSecret retrieves a Secret from the cache if it is available, and from the
// k8s API if not.
func (sm *SecretManager) findSecret(key types.NamespacedName) (secret *corev1.Secret, err error) {
secret = &corev1.Secret{}
// Look for secret in the filtered cache
err = sm.client.Get(context.TODO(), key, secret)
if err == nil {
return secret, nil
}
if !k8serrors.IsNotFound(err) {
return nil, err
}
// Secret not in cache; check API directly for unlabelled Secret
err = sm.apiReader.Get(context.TODO(), key, secret)
if err != nil {
return nil, err
}
return secret, nil
}
// claimSecret ensures that the Secret has a label that will ensure it is
// present in the cache (and that we can watch for changes), and optionally
// that it has a particular owner reference.
func (sm *SecretManager) claimSecret(secret *corev1.Secret, owner client.Object, ownerIsController, addFinalizer bool) error {
log := sm.log.WithValues("secret", secret.Name, "secretNamespace", secret.Namespace)
needsUpdate := false
if !metav1.HasLabel(secret.ObjectMeta, LabelEnvironmentName) {
log.Info("settting secret environment label")
metav1.SetMetaDataLabel(&secret.ObjectMeta, LabelEnvironmentName, LabelEnvironmentValue)
needsUpdate = true
}
if owner != nil {
ownerLog := log.WithValues(
"ownerKind", owner.GetObjectKind().GroupVersionKind().Kind,
"owner", owner.GetNamespace()+"/"+owner.GetName(),
"ownerUID", owner.GetUID())
if ownerIsController {
if !metav1.IsControlledBy(secret, owner) {
ownerLog.Info("setting secret controller reference")
if err := controllerutil.SetControllerReference(owner, secret, sm.client.Scheme()); err != nil {
return errors.Wrap(err, "failed to set secret controller reference")
}
needsUpdate = true
}
} else {
alreadyOwned := false
ownerUID := owner.GetUID()
for _, ref := range secret.GetOwnerReferences() {
if ref.UID == ownerUID {
alreadyOwned = true
break
}
}
if !alreadyOwned {
ownerLog.Info("setting secret owner reference")
if err := controllerutil.SetOwnerReference(owner, secret, sm.client.Scheme()); err != nil {
return errors.Wrap(err, "failed to set secret owner reference")
}
needsUpdate = true
}
}
}
if addFinalizer && !utils.StringInList(secret.Finalizers, SecretsFinalizer) {
log.Info("setting secret finalizer")
secret.Finalizers = append(secret.Finalizers, SecretsFinalizer)
needsUpdate = true
}
if needsUpdate {
if err := sm.client.Update(context.TODO(), secret); err != nil {
return errors.Wrap(err, fmt.Sprintf("failed to update secret %s in namespace %s", secret.ObjectMeta.Name, secret.ObjectMeta.Namespace))
}
}
return nil
}
// obtainSecretForOwner retrieves a Secret and ensures that it has a label that
// will ensure it is present in the cache (and that we can watch for changes),
// and optionally that it has a particular owner reference. The owner reference
// may optionally be a controller reference.
func (sm *SecretManager) obtainSecretForOwner(key types.NamespacedName, owner client.Object, ownerIsController, addFinalizer bool) (*corev1.Secret, error) {
secret, err := sm.findSecret(key)
if err != nil {
return nil, errors.Wrap(err, fmt.Sprintf("failed to fetch secret %s in namespace %s", key.Name, key.Namespace))
}
err = sm.claimSecret(secret, owner, ownerIsController, addFinalizer)
return secret, err
}
// AcquireSecret retrieves a Secret and ensures that it has a label that will
// ensure it is present in the cache (and that we can watch for changes), and
// that it has a particular owner reference. The owner reference may optionally
// be a controller reference.
func (sm *SecretManager) AcquireSecret(key types.NamespacedName, owner client.Object, ownerIsController, addFinalizer bool) (*corev1.Secret, error) {
if owner == nil {
panic("AcquireSecret called with no owner")
}
return sm.obtainSecretForOwner(key, owner, ownerIsController, addFinalizer)
}
// ObtainSecret retrieves a Secret and ensures that it has a label that will
// ensure it is present in the cache (and that we can watch for changes).
func (sm *SecretManager) ObtainSecret(key types.NamespacedName) (*corev1.Secret, error) {
return sm.obtainSecretForOwner(key, nil, false, false)
}
// ReleaseSecret removes secrets manager finalizer from specified secret when needed.
func (sm *SecretManager) ReleaseSecret(secret *corev1.Secret) error {
if !utils.StringInList(secret.Finalizers, SecretsFinalizer) {
return nil
}
// Remove finalizer from secret to allow deletion
secret.Finalizers = utils.FilterStringFromList(
secret.Finalizers, SecretsFinalizer)
if err := sm.client.Update(context.Background(), secret); err != nil {
return errors.Wrap(err, fmt.Sprintf("failed to remove finalizer from secret %s in namespace %s",
secret.ObjectMeta.Name, secret.ObjectMeta.Namespace))
}
sm.log.Info("removed secret finalizer",
"remaining", secret.Finalizers)
return nil
}