Skip to content

✨ KCP adopts existing machines #2489

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 29 additions & 1 deletion .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,36 @@ tilt-settings.json
tilt.d/
Tiltfile
**/.tiltbuild
test/infrastructure/docker/e2e/logs/**
**/config/**/*.yaml
**/config/**/*.yaml-e
_artifacts
Makefile
**/Makefile

# ignores changes to test-only code to avoid extra rebuilds
test/e2e/**
test/framework/**
test/infrastructure/docker/e2e/**

.dockerignore
# We want to ignore any frequently modified files to avoid cache-busting the COPY ./ ./
# Binaries for programs and plugins
**/*.exe
**/*.dll
**/*.so
**/*.dylib
cmd/clusterctl/clusterctl/**
**/bin/**
**/out/**

# Test binary, build with `go test -c`
**/*.test

# Output of the go coverage tool, specifically when used with LiteIDE
**/*.out

# Common editor / temporary files
**/*~
**/*.tmp
**/.DS_Store
**/*.swp
8 changes: 8 additions & 0 deletions api/v1alpha3/cluster_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package v1alpha3

import (
"fmt"
"strings"

corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
Expand Down Expand Up @@ -90,6 +91,13 @@ type NetworkRanges struct {
CIDRBlocks []string `json:"cidrBlocks"`
}

func (n *NetworkRanges) String() string {
if n == nil {
return ""
}
return strings.Join(n.CIDRBlocks, "")
}

// ANCHOR_END: NetworkRanges

// ANCHOR: ClusterStatus
Expand Down
28 changes: 28 additions & 0 deletions bootstrap/kubeadm/api/equality/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
Copyright 2020 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

/*
Package equality defines equality semantics for KubeadmConfigs, and utility tools for identifying
equivalent configurations.

There are a number of distinct but not different ways to express the "same" Kubeadm configuration,
and so this package attempts to sort out what differences are likely meaningful or intentional.

It is inspired by the observation that k/k no longer relies on hashing to identify "current" versions
ReplicaSets, instead using a semantic equality check that's more amenable to field modifications and
deletions: https://github.com/kubernetes/kubernetes/blob/0bb125e731/pkg/controller/deployment/util/deployment_util.go#L630-L634
*/
package equality
128 changes: 128 additions & 0 deletions bootstrap/kubeadm/api/equality/semantic.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
/*
Copyright 2020 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package equality

import (
clusterv1 "sigs.k8s.io/cluster-api/api/v1alpha3"
bootstrapv1 "sigs.k8s.io/cluster-api/bootstrap/kubeadm/api/v1alpha3"
kubeadmv1 "sigs.k8s.io/cluster-api/bootstrap/kubeadm/types/v1beta1"
"sigs.k8s.io/cluster-api/util/secret"
)

// SemanticMerge takes two KubeConfigSpecs and produces a third that is semantically equivalent to the other two
// by way of merging non-behavior-changing fields from incoming into current.
func SemanticMerge(current, incoming bootstrapv1.KubeadmConfigSpec, cluster *clusterv1.Cluster) bootstrapv1.KubeadmConfigSpec {
merged := bootstrapv1.KubeadmConfigSpec{}
current.DeepCopyInto(&merged)

merged = mergeClusterConfiguration(merged, incoming, cluster)

// Prefer non-nil init configurations: in most cases the init configuration is ignored and so this is purely representative
if merged.InitConfiguration == nil && incoming.InitConfiguration != nil {
merged.InitConfiguration = incoming.InitConfiguration.DeepCopy()
}

// The type meta for embedded types is currently ignored
if merged.ClusterConfiguration != nil && incoming.ClusterConfiguration != nil {
merged.ClusterConfiguration.TypeMeta = incoming.ClusterConfiguration.TypeMeta
}

if merged.InitConfiguration != nil && incoming.InitConfiguration != nil {
merged.InitConfiguration.TypeMeta = incoming.InitConfiguration.TypeMeta
}

if merged.JoinConfiguration != nil && incoming.JoinConfiguration != nil {
merged.JoinConfiguration.TypeMeta = incoming.JoinConfiguration.TypeMeta
}

// The default discovery injected by the kubeadm bootstrap controller is a unique, time-bounded token. However, any
// valid token has the same effect of joining the machine to the cluster. We consider the following scenarios:
// 1. current has no join configuration (meaning it was never reconciled) -> do nothing
// 2. current has a bootstrap token, and incoming has some configured discovery mechanism -> prefer incoming
// 3. current has a bootstrap token, and incoming has no discovery set -> delete current's discovery config
// 4. in all other cases, prefer current's configuration
if merged.JoinConfiguration == nil || merged.JoinConfiguration.Discovery.BootstrapToken == nil {
return merged
}

// current has a bootstrap token, check incoming
switch {
case incoming.JoinConfiguration == nil:
merged.JoinConfiguration.Discovery = kubeadmv1.Discovery{}
case incoming.JoinConfiguration.Discovery.BootstrapToken != nil:
fallthrough
case incoming.JoinConfiguration.Discovery.File != nil:
incoming.JoinConfiguration.Discovery.DeepCopyInto(&merged.JoinConfiguration.Discovery)
default:
// Neither token nor file is set on incoming's Discovery config
merged.JoinConfiguration.Discovery = kubeadmv1.Discovery{}
}

return merged
}

func mergeClusterConfiguration(merged, incoming bootstrapv1.KubeadmConfigSpec, cluster *clusterv1.Cluster) bootstrapv1.KubeadmConfigSpec {
if merged.ClusterConfiguration == nil && incoming.ClusterConfiguration != nil {
merged.ClusterConfiguration = incoming.ClusterConfiguration.DeepCopy()
} else if merged.ClusterConfiguration == nil {
// incoming's cluster configuration is also nil
return merged
}

// Attempt to reconstruct a cluster config in reverse of reconcileTopLevelObjectSettings
newCfg := incoming.ClusterConfiguration
if newCfg == nil {
newCfg = &kubeadmv1.ClusterConfiguration{}
}

if merged.ClusterConfiguration.ControlPlaneEndpoint == cluster.Spec.ControlPlaneEndpoint.String() &&
newCfg.ControlPlaneEndpoint == "" {
merged.ClusterConfiguration.ControlPlaneEndpoint = ""
}

if newCfg.ClusterName == "" {
merged.ClusterConfiguration.ClusterName = ""
}

if cluster.Spec.ClusterNetwork != nil {
if merged.ClusterConfiguration.Networking.DNSDomain == cluster.Spec.ClusterNetwork.ServiceDomain && newCfg.Networking.DNSDomain == "" {
merged.ClusterConfiguration.Networking.DNSDomain = ""
}

if merged.ClusterConfiguration.Networking.ServiceSubnet == cluster.Spec.ClusterNetwork.Services.String() &&
newCfg.Networking.ServiceSubnet == "" {
merged.ClusterConfiguration.Networking.ServiceSubnet = ""
}

if merged.ClusterConfiguration.Networking.PodSubnet == cluster.Spec.ClusterNetwork.Pods.String() &&
newCfg.Networking.PodSubnet == "" {
merged.ClusterConfiguration.Networking.PodSubnet = ""
}
}

// We defer to other sources for the version, such as the Machine
if newCfg.KubernetesVersion == "" {
merged.ClusterConfiguration.KubernetesVersion = ""
}

if merged.ClusterConfiguration.CertificatesDir == secret.DefaultCertificatesDir &&
newCfg.CertificatesDir == "" {
merged.ClusterConfiguration.CertificatesDir = ""
}

return merged
}
5 changes: 2 additions & 3 deletions bootstrap/kubeadm/controllers/kubeadmconfig_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import (
"context"
"fmt"
"strconv"
"strings"
"time"

"github.com/go-logr/logr"
Expand Down Expand Up @@ -688,13 +687,13 @@ func (r *KubeadmConfigReconciler) reconcileTopLevelObjectSettings(cluster *clust
if config.Spec.ClusterConfiguration.Networking.ServiceSubnet == "" &&
cluster.Spec.ClusterNetwork.Services != nil &&
len(cluster.Spec.ClusterNetwork.Services.CIDRBlocks) > 0 {
config.Spec.ClusterConfiguration.Networking.ServiceSubnet = strings.Join(cluster.Spec.ClusterNetwork.Services.CIDRBlocks, "")
config.Spec.ClusterConfiguration.Networking.ServiceSubnet = cluster.Spec.ClusterNetwork.Services.String()
log.Info("Altering ClusterConfiguration", "ServiceSubnet", config.Spec.ClusterConfiguration.Networking.ServiceSubnet)
}
if config.Spec.ClusterConfiguration.Networking.PodSubnet == "" &&
cluster.Spec.ClusterNetwork.Pods != nil &&
len(cluster.Spec.ClusterNetwork.Pods.CIDRBlocks) > 0 {
config.Spec.ClusterConfiguration.Networking.PodSubnet = strings.Join(cluster.Spec.ClusterNetwork.Pods.CIDRBlocks, "")
config.Spec.ClusterConfiguration.Networking.PodSubnet = cluster.Spec.ClusterNetwork.Pods.String()
log.Info("Altering ClusterConfiguration", "PodSubnet", config.Spec.ClusterConfiguration.Networking.PodSubnet)
}
}
Expand Down
2 changes: 1 addition & 1 deletion controllers/cluster_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -404,7 +404,7 @@ func (c clusterDescendants) filterOwnedDescendants(cluster *clusterv1.Cluster) (
return nil
}

if util.PointsTo(acc.GetOwnerReferences(), &cluster.ObjectMeta) {
if util.IsOwnedByObject(acc, cluster) {
ownedDescendants = append(ownedDescendants, o)
}

Expand Down
4 changes: 4 additions & 0 deletions controllers/cluster_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -703,6 +703,10 @@ func TestFilterOwnedDescendants(t *testing.T) {
g := NewWithT(t)

c := clusterv1.Cluster{
TypeMeta: metav1.TypeMeta{
APIVersion: clusterv1.GroupVersion.String(),
Kind: "Cluster",
},
Comment on lines +706 to +709
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this needed now?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because PointsToObject checks Kind and group (though not version) per Jason's suggestion to have it match on the object kind / group / name triple.

ObjectMeta: metav1.ObjectMeta{
Name: "c",
},
Expand Down
1 change: 1 addition & 0 deletions controlplane/kubeadm/config/rbac/role.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ rules:
- get
- list
- patch
- update
- watch

---
Expand Down
Loading