Skip to content
This repository was archived by the owner on Jul 30, 2021. It is now read-only.

Commit a467d8c

Browse files
committed
Add test for existing Etcd cert
Signed-off-by: Chuck Ha <[email protected]>
1 parent b612d5d commit a467d8c

File tree

3 files changed

+87
-73
lines changed

3 files changed

+87
-73
lines changed

cloudinit/cloudinit_test.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222

2323
infrav1 "sigs.k8s.io/cluster-api-bootstrap-provider-kubeadm/api/v1alpha2"
2424
"sigs.k8s.io/cluster-api-bootstrap-provider-kubeadm/internal"
25+
"sigs.k8s.io/cluster-api/util/certs"
2526
)
2627

2728
func TestNewInitControlPlaneAdditionalFileEncodings(t *testing.T) {
@@ -51,8 +52,10 @@ func TestNewInitControlPlaneAdditionalFileEncodings(t *testing.T) {
5152
}
5253

5354
for _, certificate := range cpinput.Certificates {
54-
certificate.KeyPair.Cert = []byte("some certificate")
55-
certificate.KeyPair.Key = []byte("some key")
55+
certificate.KeyPair = &certs.KeyPair{
56+
Cert: []byte("some certificate"),
57+
Key: []byte("some key"),
58+
}
5659
}
5760

5861
out, err := NewInitControlPlane(cpinput)

controllers/kubeadmconfig_controller_test.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -966,6 +966,38 @@ func TestKubeadmConfigReconciler_ClusterToKubeadmConfigs(t *testing.T) {
966966
}
967967
}
968968

969+
// Reconcile should not fail if the Etcd CA Secret already exists
970+
func TestKubeadmConfigReconciler_Reconcile_DoesNotFailIfCASecretsAlreadyExist(t *testing.T) {
971+
cluster := newCluster("my-cluster")
972+
cluster.Status.InfrastructureReady = true
973+
cluster.Status.ControlPlaneInitialized = false
974+
m := newControlPlaneMachine(cluster)
975+
configName := "my-config"
976+
c := newControlPlaneInitKubeadmConfig(m, configName)
977+
scrt := &corev1.Secret{
978+
ObjectMeta: metav1.ObjectMeta{
979+
Name: fmt.Sprintf("%s-%s", cluster.Name, internal.EtcdCAName),
980+
Namespace: "default",
981+
},
982+
Data: map[string][]byte{
983+
"tls.crt": []byte("hello world"),
984+
"tls.key": []byte("hello world"),
985+
},
986+
}
987+
fakec := fake.NewFakeClientWithScheme(setupScheme(), []runtime.Object{cluster, m, c, scrt}...)
988+
reconciler := &KubeadmConfigReconciler{
989+
Log: log.Log,
990+
Client: fakec,
991+
KubeadmInitLock: &myInitLocker{},
992+
}
993+
req := ctrl.Request{
994+
NamespacedName: types.NamespacedName{Namespace: "default", Name: configName},
995+
}
996+
if _, err := reconciler.Reconcile(req); err != nil {
997+
t.Fatal(err)
998+
}
999+
}
1000+
9691001
// test utils
9701002

9711003
// newCluster return a CAPI cluster object

internal/certificates.go

Lines changed: 50 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,19 @@
1+
/*
2+
Copyright 2019 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
117
package internal
218

319
import (
@@ -8,10 +24,8 @@ import (
824
"crypto/x509"
925
"crypto/x509/pkix"
1026
"encoding/hex"
11-
"encoding/pem"
1227
"fmt"
1328
"math/big"
14-
"net"
1529
"strings"
1630
"time"
1731

@@ -22,14 +36,17 @@ import (
2236
"k8s.io/client-go/util/cert"
2337
bootstrapv1 "sigs.k8s.io/cluster-api-bootstrap-provider-kubeadm/api/v1alpha2"
2438
clusterv1 "sigs.k8s.io/cluster-api/api/v1alpha2"
39+
"sigs.k8s.io/cluster-api/util/certs"
2540
"sigs.k8s.io/controller-runtime/pkg/client"
2641
)
2742

2843
const (
29-
rsaKeySize = 2048
3044
rootOwnerValue = "root:root"
3145

46+
// TLSKeyDataName is the name of the secret key within the certificate secret of the key
3247
TLSKeyDataName = "tls.key"
48+
49+
// TLSCrtDataName is the name of the secret key within the certificate secret of the certificate
3350
TLSCrtDataName = "tls.crt"
3451

3552
// ClusterCAName is the secret name suffix for apiserver CA
@@ -45,8 +62,10 @@ const (
4562
FrontProxyCAName = "proxy"
4663
)
4764

65+
// Certificates are the certificates necessary to bootstrap a cluster.
4866
type Certificates []*Certificate
4967

68+
// NewCertificates return an initialized but empty set of CA certificates needed to bootstrap a cluster.
5069
func NewCertificates() Certificates {
5170
return Certificates{
5271
&Certificate{Name: ClusterCAName},
@@ -56,6 +75,8 @@ func NewCertificates() Certificates {
5675
}
5776
}
5877

78+
// GetCertificateByName returns a certificate by the given name.
79+
// This could be removed if we use a map instead of a slice to hold certificates, however other code becomes more complex.
5980
func (c Certificates) GetCertificateByName(name string) *Certificate {
6081
for _, certificate := range c {
6182
if certificate.Name == name {
@@ -65,6 +86,7 @@ func (c Certificates) GetCertificateByName(name string) *Certificate {
6586
return nil
6687
}
6788

89+
// GetCertificates looks up each certificate from secrets and populates the certificate with the secret data.
6890
func (c Certificates) GetCertificates(ctx context.Context, ctrlclient client.Client, cluster *clusterv1.Cluster) error {
6991
// Look up each certificate as a secret and populate the certificate/key
7092
for _, certificate := range c {
@@ -95,6 +117,7 @@ func (c Certificates) GetCertificates(ctx context.Context, ctrlclient client.Cli
95117
return nil
96118
}
97119

120+
// GenerateCertificates will generate any certificates that do not have KeyPair data.
98121
func (c Certificates) GenerateCertificates() error {
99122
for _, certificate := range c {
100123
if certificate.KeyPair == nil {
@@ -109,6 +132,7 @@ func (c Certificates) GenerateCertificates() error {
109132
return nil
110133
}
111134

135+
// SaveGeneratedCertificates will save any certificates that have been generated as Kubernetes secrets.
112136
func (c Certificates) SaveGeneratedCertificates(ctx context.Context, ctrlclient client.Client, cluster *clusterv1.Cluster, config *bootstrapv1.KubeadmConfig) error {
113137
for _, certificate := range c {
114138
if !certificate.Generated {
@@ -122,6 +146,7 @@ func (c Certificates) SaveGeneratedCertificates(ctx context.Context, ctrlclient
122146
return nil
123147
}
124148

149+
// GetOrCreateCertificates is a convenience function that wraps cluster bootstrap certificate behavior.
125150
func (c Certificates) GetOrCreateCertificates(ctx context.Context, ctrlclient client.Client, cluster *clusterv1.Cluster, config *bootstrapv1.KubeadmConfig) error {
126151
// Get the certificates that exist
127152
if err := c.GetCertificates(ctx, ctrlclient, cluster); err != nil {
@@ -141,17 +166,19 @@ func (c Certificates) GetOrCreateCertificates(ctx context.Context, ctrlclient cl
141166
return nil
142167
}
143168

169+
// Certificate represents a single certificate CA.
144170
type Certificate struct {
145171
Generated bool
146172
Name string
147-
*KeyPair
173+
*certs.KeyPair
148174
}
149175

176+
// SecretName is the expected name of the Kubernetes secret the certificate is saved in.
150177
func (c *Certificate) SecretName(clustername string) string {
151178
return fmt.Sprintf("%s-%s", clustername, c.Name)
152179
}
153180

154-
// CertificateHashes hash all the certificates stored in a CA certificate.
181+
// Hashes hash all the certificates stored in a CA certificate.
155182
func (c *Certificate) Hashes() ([]string, error) {
156183
certificates, err := cert.ParseCertsPEM(c.Cert)
157184
if err != nil {
@@ -164,17 +191,13 @@ func (c *Certificate) Hashes() ([]string, error) {
164191
return out, nil
165192
}
166193

167-
// HashCert will calculate the sha256 of the incoming certificate.
194+
// hashCert will calculate the sha256 of the incoming certificate.
168195
func hashCert(certificate *x509.Certificate) string {
169196
spkiHash := sha256.Sum256(certificate.RawSubjectPublicKeyInfo)
170197
return "sha256:" + strings.ToLower(hex.EncodeToString(spkiHash[:]))
171198
}
172199

173-
// KeyPair holds the raw bytes for a certificate and key
174-
type KeyPair struct {
175-
Cert, Key []byte
176-
}
177-
200+
// AsSecret will convert a single certificate into a Kubernetes secret.
178201
func (c *Certificate) AsSecret(cluster *clusterv1.Cluster, config *bootstrapv1.KubeadmConfig) *corev1.Secret {
179202
secret := &corev1.Secret{
180203
ObjectMeta: metav1.ObjectMeta{
@@ -203,6 +226,7 @@ func (c *Certificate) AsSecret(cluster *clusterv1.Cluster, config *bootstrapv1.K
203226
return secret
204227
}
205228

229+
// AsFiles converts a slice of certificates into bootstrap files.
206230
func (c Certificates) AsFiles() []bootstrapv1.File {
207231
clusterCA := c.GetCertificateByName(ClusterCAName)
208232
etcdCA := c.GetCertificateByName(EtcdCAName)
@@ -264,19 +288,15 @@ func (c Certificates) AsFiles() []bootstrapv1.File {
264288
// Validate checks that all KeyPairs are valid
265289
func (c Certificates) Validate() error {
266290
for _, certificate := range c {
267-
if !certificate.KeyPair.isValid() {
291+
if !certificate.KeyPair.IsValid() {
268292
return errors.Errorf("CA cert material is missing cert/key for %s", certificate.Name)
269293
}
270294
}
271295
return nil
272296
}
273297

274-
func (kp *KeyPair) isValid() bool {
275-
return kp.Cert != nil && kp.Key != nil
276-
}
277-
278-
func secretToKeyPair(s *corev1.Secret) (*KeyPair, error) {
279-
cert, exists := s.Data[TLSCrtDataName]
298+
func secretToKeyPair(s *corev1.Secret) (*certs.KeyPair, error) {
299+
c, exists := s.Data[TLSCrtDataName]
280300
if !exists {
281301
return nil, errors.Errorf("missing data for key %s", TLSCrtDataName)
282302
}
@@ -286,47 +306,41 @@ func secretToKeyPair(s *corev1.Secret) (*KeyPair, error) {
286306
return nil, errors.Errorf("missing data for key %s", TLSKeyDataName)
287307
}
288308

289-
return &KeyPair{
290-
Cert: cert,
309+
return &certs.KeyPair{
310+
Cert: c,
291311
Key: key,
292312
}, nil
293313
}
294314

295-
func generateCACert() (*KeyPair, error) {
315+
func generateCACert() (*certs.KeyPair, error) {
296316
x509Cert, privKey, err := newCertificateAuthority()
297317
if err != nil {
298318
return nil, err
299319
}
300-
return &KeyPair{
301-
Cert: encodeCertPEM(x509Cert),
302-
Key: encodePrivateKeyPEM(privKey),
320+
return &certs.KeyPair{
321+
Cert: certs.EncodeCertPEM(x509Cert),
322+
Key: certs.EncodePrivateKeyPEM(privKey),
303323
}, nil
304324
}
305325

306326
// newCertificateAuthority creates new certificate and private key for the certificate authority
307327
func newCertificateAuthority() (*x509.Certificate, *rsa.PrivateKey, error) {
308-
key, err := newPrivateKey()
328+
key, err := certs.NewPrivateKey()
309329
if err != nil {
310330
return nil, nil, err
311331
}
312332

313-
cert, err := newSelfSignedCACert(key)
333+
c, err := newSelfSignedCACert(key)
314334
if err != nil {
315335
return nil, nil, err
316336
}
317337

318-
return cert, key, nil
319-
}
320-
321-
// newPrivateKey creates an RSA private key
322-
func newPrivateKey() (*rsa.PrivateKey, error) {
323-
pk, err := rsa.GenerateKey(rand.Reader, rsaKeySize)
324-
return pk, errors.WithStack(err)
338+
return c, key, nil
325339
}
326340

327341
// newSelfSignedCACert creates a CA certificate.
328342
func newSelfSignedCACert(key *rsa.PrivateKey) (*x509.Certificate, error) {
329-
cfg := config{
343+
cfg := certs.Config{
330344
CommonName: "kubernetes",
331345
}
332346

@@ -352,41 +366,6 @@ func newSelfSignedCACert(key *rsa.PrivateKey) (*x509.Certificate, error) {
352366
return nil, errors.Wrapf(err, "failed to create self signed CA certificate: %+v", tmpl)
353367
}
354368

355-
cert, err := x509.ParseCertificate(b)
356-
return cert, errors.WithStack(err)
357-
}
358-
359-
// encodeCertPEM returns PEM-endcoded certificate data.
360-
func encodeCertPEM(cert *x509.Certificate) []byte {
361-
block := pem.Block{
362-
Type: "CERTIFICATE",
363-
Bytes: cert.Raw,
364-
}
365-
return pem.EncodeToMemory(&block)
366-
}
367-
368-
// encodePrivateKeyPEM returns PEM-encoded private key data.
369-
func encodePrivateKeyPEM(key *rsa.PrivateKey) []byte {
370-
block := pem.Block{
371-
Type: "RSA PRIVATE KEY",
372-
Bytes: x509.MarshalPKCS1PrivateKey(key),
373-
}
374-
375-
return pem.EncodeToMemory(&block)
376-
}
377-
378-
// config contains the basic fields required for creating a certificate
379-
type config struct {
380-
CommonName string
381-
Organization []string
382-
AltNames altNames
383-
Usages []x509.ExtKeyUsage
384-
}
385-
386-
// AltNames contains the domain names and IP addresses that will be added
387-
// to the API Server's x509 certificate SubAltNames field. The values will
388-
// be passed directly to the x509.Certificate object.
389-
type altNames struct {
390-
DNSNames []string
391-
IPs []net.IP
369+
c, err := x509.ParseCertificate(b)
370+
return c, errors.WithStack(err)
392371
}

0 commit comments

Comments
 (0)