Skip to content

Commit 346df66

Browse files
committed
fix: create secret with kube certs and cleanup talos secret
This PR will create a new secret called `$cluster_name-ca` that holds certs that were generated for kubernetes. Cluster API uses these certs to create a kubeconfig for the cluster. Additionally, in exploring this, I discovered that we should be adding owners to our secrets so that they get deleted automatically upon cluster deletion. Signed-off-by: Spencer Smith <[email protected]>
1 parent 7a96120 commit 346df66

File tree

2 files changed

+100
-32
lines changed

2 files changed

+100
-32
lines changed

controllers/secrets.go

Lines changed: 68 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,28 +20,30 @@ import (
2020

2121
bootstrapv1alpha2 "github.com/talos-systems/cluster-api-bootstrap-provider-talos/api/v1alpha2"
2222
"github.com/talos-systems/talos/pkg/config/types/v1alpha1/generate"
23+
"github.com/talos-systems/talos/pkg/crypto/x509"
2324
"gopkg.in/yaml.v2"
2425
corev1 "k8s.io/api/core/v1"
26+
k8serrors "k8s.io/apimachinery/pkg/api/errors"
2527
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
28+
clusterv1 "sigs.k8s.io/cluster-api/api/v1alpha2"
2629
"sigs.k8s.io/controller-runtime/pkg/client"
2730
)
2831

29-
func (r *TalosConfigReconciler) fetchInputSecret(ctx context.Context, config *bootstrapv1alpha2.TalosConfig, clusterName string) (*corev1.Secret, error) {
30-
31-
inputSecret := &corev1.Secret{}
32+
func (r *TalosConfigReconciler) fetchSecret(ctx context.Context, config *bootstrapv1alpha2.TalosConfig, secretName string) (*corev1.Secret, error) {
33+
retSecret := &corev1.Secret{}
3234
err := r.Client.Get(context.Background(), client.ObjectKey{
3335
Namespace: config.GetNamespace(),
34-
Name: clusterName,
35-
}, inputSecret)
36+
Name: secretName,
37+
}, retSecret)
3638

3739
if err != nil {
3840
return nil, err
3941
}
4042

41-
return inputSecret, nil
43+
return retSecret, nil
4244
}
4345

44-
func (r *TalosConfigReconciler) writeInputSecret(ctx context.Context, config *bootstrapv1alpha2.TalosConfig, clusterName string, input *generate.Input) (*corev1.Secret, error) {
46+
func (r *TalosConfigReconciler) writeInputSecret(ctx context.Context, scope *TalosConfigScope, input *generate.Input) (*corev1.Secret, error) {
4547

4648
certMarshal, err := yaml.Marshal(input.Certs)
4749
if err != nil {
@@ -60,8 +62,19 @@ func (r *TalosConfigReconciler) writeInputSecret(ctx context.Context, config *bo
6062

6163
certSecret := &corev1.Secret{
6264
ObjectMeta: metav1.ObjectMeta{
63-
Namespace: config.GetNamespace(),
64-
Name: clusterName,
65+
Namespace: scope.Config.Namespace,
66+
Name: scope.Cluster.Name + "-talos",
67+
Labels: map[string]string{
68+
clusterv1.MachineClusterLabelName: scope.Cluster.Name,
69+
},
70+
OwnerReferences: []metav1.OwnerReference{
71+
metav1.OwnerReference{
72+
APIVersion: clusterv1.GroupVersion.String(),
73+
Kind: "Cluster",
74+
Name: scope.Cluster.Name,
75+
UID: scope.Cluster.UID,
76+
},
77+
},
6578
},
6679
Data: map[string][]byte{
6780
"certs": certMarshal,
@@ -77,13 +90,56 @@ func (r *TalosConfigReconciler) writeInputSecret(ctx context.Context, config *bo
7790
return certSecret, nil
7891
}
7992

80-
func (r *TalosConfigReconciler) deleteInputSecret(ctx context.Context, config *bootstrapv1alpha2.TalosConfig, clusterName string) error {
81-
return r.Client.Delete(ctx,
93+
func (r *TalosConfigReconciler) writeK8sCASecret(ctx context.Context, scope *TalosConfigScope, certs *x509.PEMEncodedCertificateAndKey) error {
94+
// Create ca secret only if it doesn't already exist
95+
_, err := r.fetchSecret(ctx, scope.Config, scope.Cluster.Name+"-ca")
96+
if k8serrors.IsNotFound(err) {
97+
certSecret := &corev1.Secret{
98+
ObjectMeta: metav1.ObjectMeta{
99+
Namespace: scope.Config.Namespace,
100+
Name: scope.Cluster.Name + "-ca",
101+
Labels: map[string]string{
102+
clusterv1.MachineClusterLabelName: scope.Cluster.Name,
103+
},
104+
OwnerReferences: []metav1.OwnerReference{
105+
metav1.OwnerReference{
106+
APIVersion: clusterv1.GroupVersion.String(),
107+
Kind: "Cluster",
108+
Name: scope.Cluster.Name,
109+
UID: scope.Cluster.UID,
110+
},
111+
},
112+
},
113+
Data: map[string][]byte{
114+
"tls.crt": certs.Crt,
115+
"tls.key": certs.Key,
116+
},
117+
}
118+
119+
err = r.Client.Create(ctx, certSecret)
120+
if err != nil {
121+
return err
122+
}
123+
} else if err != nil {
124+
return err
125+
}
126+
127+
return nil
128+
}
129+
130+
func (r *TalosConfigReconciler) deleteSecret(ctx context.Context, config *bootstrapv1alpha2.TalosConfig, secretName string) error {
131+
err := r.Client.Delete(ctx,
82132
&corev1.Secret{
83133
ObjectMeta: metav1.ObjectMeta{
84134
Namespace: config.GetNamespace(),
85-
Name: clusterName,
135+
Name: secretName,
86136
},
87137
},
88138
)
139+
140+
if err != nil && !k8serrors.IsNotFound(err) {
141+
return err
142+
}
143+
144+
return nil
89145
}

controllers/talosconfig_controller.go

Lines changed: 32 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,13 @@ type talosConfigContext struct {
7373
Key string
7474
}
7575

76+
func (r *TalosConfigReconciler) SetupWithManager(mgr ctrl.Manager, options controller.Options) error {
77+
return ctrl.NewControllerManagedBy(mgr).
78+
WithOptions(options).
79+
For(&bootstrapv1alpha2.TalosConfig{}).
80+
Complete(r)
81+
}
82+
7683
// +kubebuilder:rbac:groups=bootstrap.cluster.x-k8s.io,resources=talosconfigs,verbs=get;list;watch;create;update;patch;delete
7784
// +kubebuilder:rbac:groups=bootstrap.cluster.x-k8s.io,resources=talosconfigs/status,verbs=get;update;patch
7885
// +kubebuilder:rbac:groups=cluster.x-k8s.io,resources=clusters;clusters/status;machines;machines/status,verbs=get;list;watch
@@ -135,7 +142,7 @@ func (r *TalosConfigReconciler) Reconcile(req ctrl.Request) (_ ctrl.Result, rerr
135142

136143
// Handle deleted machines
137144
if !config.ObjectMeta.DeletionTimestamp.IsZero() {
138-
return r.reconcileDelete(ctx, config, cluster.ObjectMeta.Name)
145+
return r.reconcileDelete(ctx, config)
139146
}
140147

141148
// bail super early if it's already ready
@@ -187,29 +194,14 @@ func (r *TalosConfigReconciler) Reconcile(req ctrl.Request) (_ ctrl.Result, rerr
187194
return ctrl.Result{}, nil
188195
}
189196

190-
func (r *TalosConfigReconciler) reconcileDelete(ctx context.Context, config *bootstrapv1alpha2.TalosConfig, clusterName string) (ctrl.Result, error) {
191-
192-
if config.Spec.GenerateType == "init" {
193-
err := r.deleteInputSecret(ctx, config, clusterName)
194-
if err != nil {
195-
return ctrl.Result{}, err
196-
}
197-
}
198-
197+
func (r *TalosConfigReconciler) reconcileDelete(ctx context.Context, config *bootstrapv1alpha2.TalosConfig) (ctrl.Result, error) {
199198
// Config is deleted so remove the finalizer.
200199
config.Finalizers = util.Filter(config.Finalizers, bootstrapv1alpha2.ConfigFinalizer)
201200

202201
return ctrl.Result{}, nil
203202

204203
}
205204

206-
func (r *TalosConfigReconciler) SetupWithManager(mgr ctrl.Manager, options controller.Options) error {
207-
return ctrl.NewControllerManagedBy(mgr).
208-
WithOptions(options).
209-
For(&bootstrapv1alpha2.TalosConfig{}).
210-
Complete(r)
211-
}
212-
213205
func genTalosConfigFile(clusterName string, certs *generate.Certs) (string, error) {
214206
talosConfig := &talosConfig{
215207
Context: clusterName,
@@ -241,6 +233,14 @@ func (r *TalosConfigReconciler) userConfigs(ctx context.Context, scope *TalosCon
241233
return retBundle, err
242234
}
243235

236+
// Create the secret with kubernetes certs so a kubeconfig can be generated
237+
if userConfig.Machine().Type() == configmachine.TypeInit {
238+
err = r.writeK8sCASecret(ctx, scope, userConfig.Cluster().CA())
239+
if err != nil {
240+
return retBundle, err
241+
}
242+
}
243+
244244
userConfigStr, err := userConfig.String()
245245
if err != nil {
246246
return retBundle, err
@@ -265,17 +265,29 @@ func (r *TalosConfigReconciler) genConfigs(ctx context.Context, scope *TalosConf
265265
}
266266

267267
APIEndpointPort := strconv.Itoa(scope.Cluster.Status.APIEndpoints[0].Port)
268-
input, err := generate.NewInput(scope.Cluster.ObjectMeta.Name,
268+
input, err := generate.NewInput(scope.Cluster.Name,
269269
"https://"+scope.Cluster.Status.APIEndpoints[0].Host+":"+APIEndpointPort,
270270
*scope.Machine.Spec.Version,
271271
)
272272
if err != nil {
273273
return retBundle, err
274274
}
275275

276-
inputSecret, err := r.fetchInputSecret(ctx, scope.Config, scope.Cluster.ObjectMeta.Name)
276+
// Stash our generated input secrets so that we can reuse them for other nodes
277+
inputSecret, err := r.fetchSecret(ctx, scope.Config, scope.Cluster.Name+"-talos")
278+
if machineType == configmachine.TypeInit && k8serrors.IsNotFound(err) {
279+
inputSecret, err = r.writeInputSecret(ctx, scope, input)
280+
if err != nil {
281+
return retBundle, err
282+
}
283+
} else if err != nil {
284+
return retBundle, err
285+
}
286+
287+
// Create the secret with kubernetes certs so a kubeconfig can be generated
288+
_, err = r.fetchSecret(ctx, scope.Config, scope.Cluster.Name+"-ca")
277289
if machineType == configmachine.TypeInit && k8serrors.IsNotFound(err) {
278-
inputSecret, err = r.writeInputSecret(ctx, scope.Config, scope.Cluster.ObjectMeta.Name, input)
290+
err = r.writeK8sCASecret(ctx, scope, input.Certs.K8s)
279291
if err != nil {
280292
return retBundle, err
281293
}

0 commit comments

Comments
 (0)