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

Commit 2a88c3e

Browse files
committed
Generate Controlplane Init Bootstrap data
This PR generates the `kubeadm init` bootstrap data Signed-off-by: Chuck Ha <[email protected]>
1 parent 9e9c219 commit 2a88c3e

File tree

6 files changed

+116
-38
lines changed

6 files changed

+116
-38
lines changed

cloudinit/controlplane_certs.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ limitations under the License.
1616

1717
package cloudinit
1818

19-
import "errors"
19+
import "github.com/pkg/errors"
2020

2121
// Certificates is a template struct to hold certificate data
2222
type Certificates struct {

cloudinit/controlplane_init.go

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,6 @@ limitations under the License.
1616

1717
package cloudinit
1818

19-
import (
20-
"github.com/pkg/errors"
21-
)
22-
2319
const (
2420
controlPlaneCloudInit = `{{.Header}}
2521
{{template "files" .WriteFiles}}
@@ -50,15 +46,15 @@ type ControlPlaneInput struct {
5046
func NewInitControlPlane(input *ControlPlaneInput) (string, error) {
5147
input.Header = cloudConfigHeader
5248
if err := input.Certificates.validate(); err != nil {
53-
return "", errors.Wrapf(err, "ControlPlaneInput is invalid")
49+
return "", err
5450
}
5551

5652
input.WriteFiles = certificatesToFiles(input.Certificates)
5753
input.WriteFiles = append(input.WriteFiles, input.AdditionalFiles...)
5854
userData, err := generate("InitControlplane", controlPlaneCloudInit, input)
5955
if err != nil {
60-
return "", errors.Wrapf(err, "failed to generate user data for new control plane machine")
56+
return "", err
6157
}
6258

63-
return userData, err
59+
return userData, nil
6460
}

controllers/bootstrapdata.go

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
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+
17+
package controllers
18+
19+
import (
20+
"encoding/json"
21+
22+
"github.com/go-logr/logr"
23+
"github.com/pkg/errors"
24+
"sigs.k8s.io/cluster-api-bootstrap-provider-kubeadm/api/v1alpha2"
25+
"sigs.k8s.io/cluster-api-bootstrap-provider-kubeadm/cloudinit"
26+
)
27+
28+
// BootstrapDataGenerator is responsible for generating bootstrap data for a given config
29+
type BootstrapDataGenerator struct {
30+
logr.Logger
31+
}
32+
33+
// GenerateControlPlaneInit will generate the bootstrap data and set the fields necessary on the config object.
34+
func (u *BootstrapDataGenerator) GenerateControlPlaneInit(config *v1alpha2.KubeadmConfig) error {
35+
u.Info("Generating user data for first control plane")
36+
37+
// TODO: use secrets for certs, plug into kubeadm
38+
// TODO: sub in things in the cluster configuration like the cluster name and other data we can extract from the cluster object
39+
config.Spec.ClusterConfiguration.APIVersion = "v1beta1"
40+
config.Spec.ClusterConfiguration.Kind = "ClusterConfiguration"
41+
clusterdata, err := json.Marshal(config.Spec.ClusterConfiguration)
42+
if err != nil {
43+
u.Error(err, "failed to marshal cluster configuration")
44+
return errors.WithStack(err)
45+
}
46+
47+
config.Spec.ClusterConfiguration.Kind = "v1beta1"
48+
config.Spec.ClusterConfiguration.APIVersion = "InitConfiguration"
49+
initdata, err := json.Marshal(config.Spec.InitConfiguration)
50+
if err != nil {
51+
u.Error(err, "failed to marshal init configuration")
52+
return errors.WithStack(err)
53+
}
54+
55+
cicfg, err := cloudinit.NewInitControlPlane(&cloudinit.ControlPlaneInput{
56+
ClusterConfiguration: string(clusterdata),
57+
InitConfiguration: string(initdata),
58+
})
59+
if err != nil {
60+
u.Error(err, "could not create new init control plane")
61+
return err
62+
}
63+
64+
config.Status.BootstrapData = []byte(cicfg)
65+
config.Status.Ready = true
66+
return nil
67+
}

controllers/kubeadmconfig_controller.go

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2626
kubeadmv1alpha2 "sigs.k8s.io/cluster-api-bootstrap-provider-kubeadm/api/v1alpha2"
2727
"sigs.k8s.io/cluster-api/pkg/apis/cluster/v1alpha2"
28+
"sigs.k8s.io/cluster-api/pkg/util"
2829
ctrl "sigs.k8s.io/controller-runtime"
2930
"sigs.k8s.io/controller-runtime/pkg/client"
3031
)
@@ -33,10 +34,23 @@ var (
3334
machineKind = v1alpha2.SchemeGroupVersion.WithKind("Machine")
3435
)
3536

37+
38+
// Locker are the methods necessary to add locking around the first control plane init.
39+
type Locker interface {
40+
Acquire(*v1alpha2.Cluster) bool
41+
Release(*v1alpha2.Cluster) bool
42+
}
43+
44+
type bootstrapDataGenerator interface {
45+
GenerateControlPlaneInit(config *kubeadmv1alpha2.KubeadmConfig) error
46+
}
47+
3648
// KubeadmConfigReconciler reconciles a KubeadmConfig object
3749
type KubeadmConfigReconciler struct {
3850
client.Client
39-
Log logr.Logger
51+
ControlPlaneInitLock Locker
52+
Log logr.Logger
53+
BootstrapDataGenerator bootstrapDataGenerator
4054
}
4155

4256
// +kubebuilder:rbac:groups=bootstrap.cluster.x-k8s.io,resources=kubeadmconfigs,verbs=get;list;watch;create;update;patch;delete
@@ -108,16 +122,29 @@ func (r *KubeadmConfigReconciler) Reconcile(req ctrl.Request) (ctrl.Result, erro
108122
return ctrl.Result{}, err
109123
}
110124

111-
// maybe do something with cluster some day
112-
// maybe do something interesting here some day
113-
config.Status.BootstrapData = []byte("hello world")
114-
config.Status.Ready = true
125+
// Generate the cloud-init for controlplane init
126+
if r.BootstrapDataGenerator == nil {
127+
r.BootstrapDataGenerator = &BootstrapDataGenerator{
128+
Logger: r.Log.WithName("user-data-generator"),
129+
}
130+
}
131+
132+
if util.IsControlPlaneMachine(machine) {
133+
locked := r.ControlPlaneInitLock.Acquire(cluster)
134+
if locked {
135+
// Generate control plane init
136+
if err := r.BootstrapDataGenerator.GenerateControlPlaneInit(&config); err != nil {
137+
log.Error(err, "error generating control plane init")
138+
return ctrl.Result{}, err
139+
}
140+
}
141+
142+
}
115143

116144
if err := r.Update(ctx, &config); err != nil {
117145
log.Error(err, "failed to update config")
118146
return ctrl.Result{}, err
119147
}
120-
log.Info("Updated config with bootstrap data")
121148
return ctrl.Result{}, nil
122149
}
123150

controllers/control_plane_init_locker.go renamed to locker/control_plane_init_locker.go

Lines changed: 9 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
1414
limitations under the License.
1515
*/
1616

17-
package controllers
17+
package locker
1818

1919
import (
2020
"fmt"
@@ -27,28 +27,15 @@ import (
2727
clusterv2 "sigs.k8s.io/cluster-api/pkg/apis/cluster/v1alpha2"
2828
)
2929

30-
// ControlPlaneInitLocker provides a locking mechanism for cluster initialization.
31-
type ControlPlaneInitLocker interface {
32-
// Acquire returns true if it acquires the lock for the cluster.
33-
Acquire(cluster *clusterv2.Cluster) bool
34-
}
35-
36-
// controlPlaneInitLocker uses a ConfigMap to synchronize cluster initialization.
37-
type controlPlaneInitLocker struct {
30+
// ControlPlaneInitLocker uses a ConfigMap to synchronize cluster initialization.
31+
type ControlPlaneInitLocker struct {
3832
log logr.Logger
3933
configMapClient corev1.ConfigMapsGetter
4034
}
4135

42-
var _ ControlPlaneInitLocker = &controlPlaneInitLocker{}
43-
44-
func newControlPlaneInitLocker(log logr.Logger, configMapClient corev1.ConfigMapsGetter) *controlPlaneInitLocker {
45-
return &controlPlaneInitLocker{
46-
log: log,
47-
configMapClient: configMapClient,
48-
}
49-
}
50-
51-
func (l *controlPlaneInitLocker) Acquire(cluster *clusterv2.Cluster) bool {
36+
// Acquire will attempt to acquire a lock that guarantees this is the first control plane to be created.
37+
// TODO Consider adding a short circuit if there is already a control plane node to reduce the calls to the API server.
38+
func (l *ControlPlaneInitLocker) Acquire(cluster *clusterv2.Cluster) bool {
5239
configMapName := fmt.Sprintf("%s-controlplane", cluster.UID)
5340
log := l.log.WithValues("namespace", cluster.Namespace, "cluster-name", cluster.Name, "configmap-name", configMapName)
5441

@@ -94,7 +81,8 @@ func (l *controlPlaneInitLocker) Acquire(cluster *clusterv2.Cluster) bool {
9481
return true
9582
}
9683

97-
func (l *controlPlaneInitLocker) Release(cluster *clusterv2.Cluster) bool {
84+
// Release releases the lock for someone else to get.
85+
func (l *ControlPlaneInitLocker) Release(cluster *clusterv2.Cluster) bool {
9886
configMapName := fmt.Sprintf("%s-controlplane", cluster.UID)
9987
log := l.log.WithValues("namespace", cluster.Namespace, "cluster-name", cluster.Name, "configmap-name", configMapName)
10088

@@ -116,7 +104,7 @@ func (l *controlPlaneInitLocker) Release(cluster *clusterv2.Cluster) bool {
116104
return true
117105
}
118106

119-
func (l *controlPlaneInitLocker) configMapExists(namespace, name string) (bool, error) {
107+
func (l *ControlPlaneInitLocker) configMapExists(namespace, name string) (bool, error) {
120108
_, err := l.configMapClient.ConfigMaps(namespace).Get(name, metav1.GetOptions{})
121109
if apierrors.IsNotFound(err) {
122110
return false, nil

controllers/control_plane_init_locker_test.go renamed to locker/control_plane_init_locker_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
1414
limitations under the License.
1515
*/
1616

17-
package controllers
17+
package locker
1818

1919
import (
2020
"testing"
@@ -64,7 +64,7 @@ func TestControlPlaneInitLockerAcquire(t *testing.T) {
6464

6565
for _, tc := range tests {
6666
t.Run(tc.name, func(t *testing.T) {
67-
l := &controlPlaneInitLocker{
67+
l := &ControlPlaneInitLocker{
6868
log: log.ZapLogger(true),
6969
configMapClient: &configMapsGetter{
7070
configMap: tc.configMap,
@@ -120,7 +120,7 @@ func TestControlPlaneInitLockerRelease(t *testing.T) {
120120

121121
for _, tc := range tests {
122122
t.Run(tc.name, func(t *testing.T) {
123-
l := &controlPlaneInitLocker{
123+
l := &ControlPlaneInitLocker{
124124
log: log.ZapLogger(true),
125125
configMapClient: &configMapsGetter{
126126
configMap: tc.configMap,

0 commit comments

Comments
 (0)