Skip to content

Commit

Permalink
validate ProviderID equality by comparing entire string
Browse files Browse the repository at this point in the history
  • Loading branch information
jackfrancis committed Oct 14, 2022
1 parent 61b22ba commit c5fd6f4
Show file tree
Hide file tree
Showing 5 changed files with 57 additions and 24 deletions.
13 changes: 5 additions & 8 deletions controllers/noderefutil/providerid.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ package noderefutil

import (
"errors"
"fmt"
"regexp"
"strings"
)
Expand Down Expand Up @@ -87,9 +86,9 @@ func (p *ProviderID) ID() string {
return p.id
}

// Equals returns true if both the CloudProvider and ID match.
// Equals returns true if this ProviderID string matches another ProviderID string.
func (p *ProviderID) Equals(o *ProviderID) bool {
return p.CloudProvider() == o.CloudProvider() && p.ID() == o.ID()
return p.String() == o.String()
}

// String returns the string representation of this object.
Expand All @@ -102,10 +101,8 @@ func (p *ProviderID) Validate() bool {
return p.CloudProvider() != "" && p.ID() != ""
}

// IndexKey returns a string concatenating the cloudProvider and the ID parts of the providerID.
// E.g Format: cloudProvider://optional/segments/etc/id. IndexKey: cloudProvider/id
// This is useful to use the providerID as a reliable index between nodes and machines
// as it guarantees the infra Providers contract.
// IndexKey returns the required level of uniqueness
// to represent and index machines uniquely from their node providerID.
func (p *ProviderID) IndexKey() string {
return fmt.Sprintf("%s/%s", p.CloudProvider(), p.ID())
return p.String()
}
57 changes: 46 additions & 11 deletions controllers/noderefutil/providerid_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
)

const aws = "aws"
const azure = "azure"

func TestNewProviderID(t *testing.T) {
tests := []struct {
Expand Down Expand Up @@ -115,19 +116,53 @@ func TestInvalidProviderID(t *testing.T) {
func TestProviderIDEquals(t *testing.T) {
g := NewWithT(t)

input1 := "aws:////instance-id1"
parsed1, err := NewProviderID(input1)
inputAWS1 := "aws:////instance-id1"
parsedAWS1, err := NewProviderID(inputAWS1)
g.Expect(err).NotTo(HaveOccurred())
g.Expect(parsed1.String()).To(Equal(input1))
g.Expect(parsed1.ID()).To(Equal("instance-id1"))
g.Expect(parsed1.CloudProvider()).To(Equal(aws))
g.Expect(parsedAWS1.String()).To(Equal(inputAWS1))
g.Expect(parsedAWS1.ID()).To(Equal("instance-id1"))
g.Expect(parsedAWS1.CloudProvider()).To(Equal(aws))

input2 := "aws:///us-west-1/instance-id1"
parsed2, err := NewProviderID(input2)
inputAWS2 := "aws:///us-west-1/instance-id1"
parsedAWS2, err := NewProviderID(inputAWS2)
g.Expect(err).NotTo(HaveOccurred())
g.Expect(parsed2.String()).To(Equal(input2))
g.Expect(parsed2.ID()).To(Equal("instance-id1"))
g.Expect(parsed2.CloudProvider()).To(Equal(aws))
g.Expect(parsedAWS2.String()).To(Equal(inputAWS2))
g.Expect(parsedAWS2.ID()).To(Equal("instance-id1"))
g.Expect(parsedAWS2.CloudProvider()).To(Equal(aws))

g.Expect(parsed1.Equals(parsed2)).To(BeTrue())
// Test for inequality
g.Expect(parsedAWS1.Equals(parsedAWS2)).To(BeFalse())

inputAzure1 := "azure:///subscriptions/4920076a-ba9f-11ec-8422-0242ac120002/resourceGroups/default-template/providers/Microsoft.Compute/virtualMachines/default-template-control-plane-fhrvh"
parsedAzure1, err := NewProviderID(inputAzure1)
g.Expect(err).NotTo(HaveOccurred())
g.Expect(parsedAzure1.String()).To(Equal(inputAzure1))
g.Expect(parsedAzure1.ID()).To(Equal("default-template-control-plane-fhrvh"))
g.Expect(parsedAzure1.CloudProvider()).To(Equal(azure))

inputAzure2 := inputAzure1
parsedAzure2, err := NewProviderID(inputAzure2)
g.Expect(err).NotTo(HaveOccurred())

// Test for equality
g.Expect(parsedAzure1.Equals(parsedAzure2)).To(BeTrue())

// Here we ensure that two different ProviderID strings that happen to have the same 'ID' are not equal
// We use Azure VMSS as an example, two different '0' VMs in different pools: k8s-pool1-vmss, and k8s-pool2-vmss
inputAzureVMFromOneVMSS := "azure:///subscriptions/4920076a-ba9f-11ec-8422-0242ac120002/resourceGroups/default-template/providers/Microsoft.Compute/virtualMachineScaleSets/k8s-pool1-vmss/virtualMachines/0"
inputAzureVMFromAnotherVMSS := "azure:///subscriptions/4920076a-ba9f-11ec-8422-0242ac120002/resourceGroups/default-template/providers/Microsoft.Compute/virtualMachineScaleSets/k8s-pool2-vmss/virtualMachines/0"
parsedAzureVMFromOneVMSS, err := NewProviderID(inputAzureVMFromOneVMSS)
g.Expect(err).NotTo(HaveOccurred())
g.Expect(parsedAzureVMFromOneVMSS.String()).To(Equal(inputAzureVMFromOneVMSS))
g.Expect(parsedAzureVMFromOneVMSS.ID()).To(Equal("0"))
g.Expect(parsedAzureVMFromOneVMSS.CloudProvider()).To(Equal(azure))

parsedAzureVMFromAnotherVMSS, err := NewProviderID(inputAzureVMFromAnotherVMSS)
g.Expect(err).NotTo(HaveOccurred())
g.Expect(parsedAzureVMFromAnotherVMSS.String()).To(Equal(inputAzureVMFromAnotherVMSS))
g.Expect(parsedAzureVMFromAnotherVMSS.ID()).To(Equal("0"))
g.Expect(parsedAzureVMFromAnotherVMSS.CloudProvider()).To(Equal(azure))

// Test for inequality
g.Expect(parsedAzureVMFromOneVMSS.Equals(parsedAzureVMFromAnotherVMSS)).To(BeFalse())
}
1 change: 1 addition & 0 deletions docs/book/src/developer/providers/v1.2-to-v1.3.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,4 @@ The default value is 0, meaning that the volume can be detached without any time
* `ETCD_VERSION_UPGRADE_TO`
* `COREDNS_VERSION_UPGRADE_TO`
The variable `SkipUpgrade` could be set to revert to the old behaviour by making use of the `KUBERNETES_VERSION` variable and skipping the kubernetes upgrade.
- Machine `providerID` is now being strictly checked for equality when compared against Kubernetes node `providerID` data. This is the expected criteria for correlating a Cluster API machine to its corresponding Kubernetes node, but historically this comparison was not strict, and instead compared only against the `ID` substring part of the full `providerID` string. Because different providers construct `providerID` strings differently, the `ID` substring is not uniformly defined and implemented across providers, and thus the existing `providerID` equality can not guarantee the correct Machine-Node correlation. It is very unlikely that this new behavior will break existing providers, but FYI: if strict `providerID` equality will degrade expected behaviors, you may need to update your provider implementation prior to adopting Cluster API v1.3.
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ func TestGetNode(t *testing.T) {
ProviderID: "aws://us-west-2/test-get-node-2",
},
},
providerIDInput: "aws:///test-get-node-2",
providerIDInput: "aws://us-west-2/test-get-node-2",
},
{
name: "gce prefix, cloudProvider and ID matches",
Expand All @@ -93,7 +93,7 @@ func TestGetNode(t *testing.T) {
ProviderID: "gce://us-central1/test-get-node-2",
},
},
providerIDInput: "gce:///test-get-node-2",
providerIDInput: "gce://us-central1/test-get-node-2",
},
{
name: "Node is not found",
Expand Down
6 changes: 3 additions & 3 deletions internal/controllers/machine/machine_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ func TestWatches(t *testing.T) {
Namespace: ns.Name,
},
Spec: corev1.NodeSpec{
ProviderID: "test:///id-1",
ProviderID: "test://id-1",
},
}

Expand Down Expand Up @@ -1800,7 +1800,7 @@ func TestNodeToMachine(t *testing.T) {
Name: "test-node-to-machine-1",
},
Spec: corev1.NodeSpec{
ProviderID: "test:///id-1",
ProviderID: "test://id-1",
},
}

Expand All @@ -1809,7 +1809,7 @@ func TestNodeToMachine(t *testing.T) {
Name: "test-node-to-machine-node-2",
},
Spec: corev1.NodeSpec{
ProviderID: "test:///id-2",
ProviderID: "test://id-2",
},
}

Expand Down

0 comments on commit c5fd6f4

Please sign in to comment.