Skip to content

Commit

Permalink
Configure provider-id for the machines/nodes (kubermatic#1723)
Browse files Browse the repository at this point in the history
* Configure provider-id for the machines/nodes

MC will inject provider-id for the machines that are created against cloud providers that don't have in-tree or external CCM support

Signed-off-by: Waleed Malik <ahmedwaleedmalik@gmail.com>

* Add exception in golangci-lint

Signed-off-by: Waleed Malik <ahmedwaleedmalik@gmail.com>

* Refactored code

Signed-off-by: Waleed Malik <ahmedwaleedmalik@gmail.com>

---------

Signed-off-by: Waleed Malik <ahmedwaleedmalik@gmail.com>
  • Loading branch information
ahmedwaleedmalik committed Mar 4, 2024
1 parent b3fe8a2 commit 1ef6369
Show file tree
Hide file tree
Showing 17 changed files with 111 additions and 2 deletions.
1 change: 1 addition & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,4 @@ issues:
- 'cyclomatic complexity 34 of func `\(\*provider\)\.getConfig` is high'
- 'cyclomatic complexity 31 of func `\(\*provider\)\.Validate` is high'
- 'cyclomatic complexity 33 of func `\(\*provider\)\.Create` is high'
- 'cyclomatic complexity 32 of func `\(\*Reconciler\)\.ensureInstanceExistsForMachine` is high'
10 changes: 10 additions & 0 deletions pkg/admission/machines.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,16 @@ func (ad *admissionData) mutateMachines(ctx context.Context, ar admissionv1.Admi
if oldMachine.Spec.Name != machine.Spec.Name && machine.Spec.Name == machine.Name {
oldMachine.Spec.Name = machine.Spec.Name
}

if oldMachine.Spec.ProviderID != nil && machine.Spec.ProviderID != nil && *oldMachine.Spec.ProviderID != *machine.Spec.ProviderID {
return nil, fmt.Errorf("providerID is immutable")
}

// Allow mutation of the ProviderID field, as it can only be computed after the machine is created.
if oldMachine.Spec.ProviderID == nil && machine.Spec.ProviderID != nil {
oldMachine.Spec.ProviderID = machine.Spec.ProviderID
}

// Allow mutation when:
// * machine has the `MigrationBypassSpecNoModificationRequirementAnnotation` annotation (used for type migration)
bypassValidationForMigration := machine.Annotations[BypassSpecNoModificationRequirementAnnotation] == "true"
Expand Down
3 changes: 3 additions & 0 deletions pkg/cloudprovider/provider/anexia/instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ func (ai *anexiaInstance) ID() string {
}

func (ai *anexiaInstance) ProviderID() string {
if ai == nil || ai.ID() == "" {
return ""
}
return ai.ID()
}

Expand Down
3 changes: 3 additions & 0 deletions pkg/cloudprovider/provider/digitalocean/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -506,6 +506,9 @@ func (d *doInstance) ID() string {
}

func (d *doInstance) ProviderID() string {
if d.droplet == nil || d.droplet.Name == "" {
return ""
}
return fmt.Sprintf("digitalocean://%d", d.droplet.ID)
}

Expand Down
3 changes: 3 additions & 0 deletions pkg/cloudprovider/provider/equinixmetal/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,9 @@ func (s *metalDevice) ID() string {
}

func (s *metalDevice) ProviderID() string {
if s.device == nil || s.device.ID == "" {
return ""
}
return "equinixmetal://" + s.device.ID
}

Expand Down
3 changes: 3 additions & 0 deletions pkg/cloudprovider/provider/gce/instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ func (gi *googleInstance) ID() string {
}

func (gi *googleInstance) ProviderID() string {
if gi.ci == nil || gi.ci.Name == "" {
return ""
}
return fmt.Sprintf("gce://%s/%s/%s", gi.projectID, gi.zone, gi.ci.Name)
}

Expand Down
3 changes: 3 additions & 0 deletions pkg/cloudprovider/provider/hetzner/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -553,6 +553,9 @@ func (s *hetznerServer) ID() string {
}

func (s *hetznerServer) ProviderID() string {
if s.server == nil || s.server.ID == 0 {
return ""
}
return fmt.Sprintf("hcloud://%d", s.server.ID)
}

Expand Down
3 changes: 3 additions & 0 deletions pkg/cloudprovider/provider/kubevirt/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,9 @@ func (k *kubeVirtServer) ID() string {
}

func (k *kubeVirtServer) ProviderID() string {
if k.vmi.Name == "" {
return ""
}
return "kubevirt://" + k.vmi.Name
}

Expand Down
3 changes: 3 additions & 0 deletions pkg/cloudprovider/provider/linode/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,9 @@ func (d *linodeInstance) ID() string {
}

func (d *linodeInstance) ProviderID() string {
if d == nil || d.ID() == "" {
return ""
}
return fmt.Sprintf("linode://%s", d.ID())
}

Expand Down
3 changes: 3 additions & 0 deletions pkg/cloudprovider/provider/nutanix/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,9 @@ func (nutanixServer Server) ID() string {
}

func (nutanixServer Server) ProviderID() string {
if nutanixServer.ID() == "" {
return ""
}
return fmt.Sprintf("nutanix://%s", nutanixServer.ID())
}

Expand Down
3 changes: 3 additions & 0 deletions pkg/cloudprovider/provider/opennebula/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,9 @@ func (i *openNebulaInstance) ID() string {
}

func (i *openNebulaInstance) ProviderID() string {
if i.vm == nil || i.vm.ID == 0 {
return ""
}
return "opennebula://" + strconv.Itoa(i.vm.ID)
}

Expand Down
3 changes: 3 additions & 0 deletions pkg/cloudprovider/provider/openstack/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -936,6 +936,9 @@ func (d *osInstance) ID() string {
}

func (d *osInstance) ProviderID() string {
if d.server == nil || d.server.ID == "" {
return ""
}
return "openstack:///" + d.server.ID
}

Expand Down
3 changes: 3 additions & 0 deletions pkg/cloudprovider/provider/vmwareclouddirector/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,9 @@ func (s Server) ID() string {
}

func (s Server) ProviderID() string {
if s.ID() == "" {
return ""
}
return fmt.Sprintf("vmware-cloud-director://%s", s.ID())
}

Expand Down
3 changes: 3 additions & 0 deletions pkg/cloudprovider/provider/vsphere/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,9 @@ func (vsphereServer Server) ID() string {
}

func (vsphereServer Server) ProviderID() string {
if vsphereServer.uuid == "" {
return ""
}
return "vsphere://" + vsphereServer.uuid
}

Expand Down
3 changes: 3 additions & 0 deletions pkg/cloudprovider/provider/vultr/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,9 @@ func (v *vultrInstance) ID() string {
}

func (v *vultrInstance) ProviderID() string {
if v.instance == nil || v.instance.ID == "" {
return ""
}
return "vultr://" + v.instance.ID
}

Expand Down
40 changes: 38 additions & 2 deletions pkg/controller/machine/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,9 @@ const (
// AnnotationAutoscalerIdentifier is used by the cluster-autoscaler
// cluster-api provider to match Nodes to Machines.
AnnotationAutoscalerIdentifier = "cluster.k8s.io/machine"

// ProviderID pattern.
ProviderIDPattern = "kubermatic://%s/%s"
)

// Reconciler is the controller implementation for machine resources.
Expand Down Expand Up @@ -478,7 +481,24 @@ func (r *Reconciler) reconcile(ctx context.Context, log *zap.SugaredLogger, mach
return r.ensureInstanceExistsForMachine(ctx, log, prov, machine, userdataPlugin, providerConfig)
}

// case 3.3: if the node exists make sure if it has labels and taints attached to it.
// case 3.3: if the node exists and both external and internal CCM are not available. Then set the provider-id for the node.
inTree, err := providerconfigtypes.IntreeCloudProviderImplementationSupported(providerConfig.CloudProvider, machine.Spec.Versions.Kubelet)
if err != nil {
return nil, fmt.Errorf("failed to check if cloud provider %q has in-tree implementation: %w", providerConfig.CloudProvider, err)
}

if !inTree && !r.nodeSettings.ExternalCloudProvider && node.Spec.ProviderID == "" {
providerID := fmt.Sprintf(ProviderIDPattern, providerConfig.CloudProvider, machine.UID)
if err := r.updateNode(ctx, node, func(n *corev1.Node) {
n.Spec.ProviderID = providerID
}); err != nil {
return nil, fmt.Errorf("failed to update node %s with the ProviderID: %w", node.Name, err)
}

r.recorder.Event(machine, corev1.EventTypeNormal, "ProviderIDUpdated", "Successfully updated providerID on node")
nodeLog.Info("Added ProviderID to the node")
}
// case 3.4: if the node exists make sure if it has labels and taints attached to it.
return nil, r.ensureNodeLabelsAnnotationsAndTaints(ctx, nodeLog, node, machine)
}

Expand Down Expand Up @@ -978,10 +998,26 @@ func (r *Reconciler) ensureInstanceExistsForMachine(
return a.Type < b.Type
})

var providerID string
if machine.Spec.ProviderID == nil {
inTree, err := providerconfigtypes.IntreeCloudProviderImplementationSupported(providerConfig.CloudProvider, machine.Spec.Versions.Kubelet)
if err != nil {
return nil, fmt.Errorf("failed to check if cloud provider %q has in-tree implementation: %w", providerConfig.CloudProvider, err)
}

// If both external and internal CCM are not available. We set provider-id for the machine explicitly.
if !inTree && !r.nodeSettings.ExternalCloudProvider {
providerID = fmt.Sprintf(ProviderIDPattern, providerConfig.CloudProvider, machine.UID)
}
}

if err := r.updateMachine(machine, func(m *clusterv1alpha1.Machine) {
m.Status.Addresses = machineAddresses
if providerID != "" {
m.Spec.ProviderID = &providerID
}
}); err != nil {
return nil, fmt.Errorf("failed to update machine after setting .status.addresses: %w", err)
return nil, fmt.Errorf("failed to update machine after setting .status.addresses and providerID: %w", err)
}

return r.ensureNodeOwnerRef(ctx, log, providerInstance, machine, providerConfig)
Expand Down
23 changes: 23 additions & 0 deletions pkg/providerconfig/types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import (
"fmt"
"strconv"

"github.com/Masterminds/semver/v3"

clusterv1alpha1 "github.com/kubermatic/machine-controller/pkg/apis/cluster/v1alpha1"
"github.com/kubermatic/machine-controller/pkg/cloudprovider/util"
"github.com/kubermatic/machine-controller/pkg/jsonutil"
Expand Down Expand Up @@ -107,6 +109,27 @@ var (
}
)

func IntreeCloudProviderImplementationSupported(cloudProvider CloudProvider, version string) (inTree bool, err error) {
kubeletVer, err := semver.NewVersion(version)
if err != nil {
return false, fmt.Errorf("failed to parse kubelet version: %w", err)
}

switch cloudProvider {
case CloudProviderAzure, CloudProviderVsphere, CloudProviderGoogle:
return true, nil
case CloudProviderAWS:
// In-tree AWS support was removed in Kubernetes 1.27.
ltKube127Condition, _ := semver.NewConstraint("< 1.27")
if ltKube127Condition.Check(kubeletVer) {
return true, nil
}
return false, nil
default:
return false, nil
}
}

// DNSConfig contains a machine's DNS configuration.
type DNSConfig struct {
Servers []string `json:"servers"`
Expand Down

0 comments on commit 1ef6369

Please sign in to comment.