Skip to content

Commit 0e81500

Browse files
authored
Merge pull request #18177 from moshevayner/linode-support
feat(cloud): add Linode (Akamai) cloud provider API registration
2 parents 69da156 + 59822ba commit 0e81500

19 files changed

Lines changed: 410 additions & 2 deletions

pkg/apis/kops/cluster.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,8 @@ type CloudProviderSpec struct {
204204
Openstack *OpenstackSpec `json:"openstack,omitempty"`
205205
// Scaleway configures the Scaleway cloud provider.
206206
Scaleway *ScalewaySpec `json:"scaleway,omitempty"`
207+
// Linode configures the Linode (Akamai) cloud provider.
208+
Linode *LinodeSpec `json:"linode,omitempty"`
207209
}
208210

209211
// AWSSpec configures the AWS cloud provider.
@@ -265,6 +267,9 @@ type HetznerSpec struct{}
265267
type ScalewaySpec struct {
266268
}
267269

270+
// LinodeSpec configures the Linode (Akamai) cloud provider.
271+
type LinodeSpec struct{}
272+
268273
type KarpenterConfig struct {
269274
Enabled bool `json:"enabled,omitempty"`
270275
LogEncoding string `json:"logFormat,omitempty"`
@@ -1013,6 +1018,8 @@ func (c *Cluster) GetCloudProvider() CloudProviderID {
10131018
return CloudProviderOpenstack
10141019
} else if spec.CloudProvider.Scaleway != nil {
10151020
return CloudProviderScaleway
1021+
} else if spec.CloudProvider.Linode != nil {
1022+
return CloudProviderLinode
10161023
}
10171024
return ""
10181025
}

pkg/apis/kops/model/features.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ func UseChallengeCallback(cloudProvider kops.CloudProviderID) bool {
3131
return true
3232
case kops.CloudProviderAzure:
3333
return true
34+
case kops.CloudProviderLinode:
35+
return true
3436
default:
3537
return false
3638
}

pkg/apis/kops/v1alpha2/conversion.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,8 @@ func Convert_v1alpha2_ClusterSpec_To_kops_ClusterSpec(in *ClusterSpec, out *kops
171171
}
172172
case kops.CloudProviderScaleway:
173173
out.CloudProvider.Scaleway = &kops.ScalewaySpec{}
174+
case kops.CloudProviderLinode:
175+
out.CloudProvider.Linode = &kops.LinodeSpec{}
174176
case "":
175177
default:
176178
return field.NotSupported(field.NewPath("spec").Child("cloudProvider"), in.LegacyCloudProvider, []string{
@@ -179,6 +181,7 @@ func Convert_v1alpha2_ClusterSpec_To_kops_ClusterSpec(in *ClusterSpec, out *kops
179181
string(kops.CloudProviderAzure),
180182
string(kops.CloudProviderAWS),
181183
string(kops.CloudProviderHetzner),
184+
string(kops.CloudProviderLinode),
182185
string(kops.CloudProviderOpenstack),
183186
string(kops.CloudProviderScaleway),
184187
})
@@ -440,6 +443,9 @@ func Convert_kops_ClusterSpec_To_v1alpha2_ClusterSpec(in *kops.ClusterSpec, out
440443
if in.CloudProvider.Scaleway != nil {
441444
out.LegacyCloudProvider = string(kops.CloudProviderScaleway)
442445
}
446+
if in.CloudProvider.Linode != nil {
447+
out.LegacyCloudProvider = string(kops.CloudProviderLinode)
448+
}
443449
switch kops.CloudProviderID(out.LegacyCloudProvider) {
444450
case kops.CloudProviderAWS:
445451
aws := in.CloudProvider.AWS

pkg/apis/kops/v1alpha3/cluster.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,8 @@ type CloudProviderSpec struct {
195195
Openstack *OpenstackSpec `json:"openstack,omitempty"`
196196
// Scaleway configures the Scaleway cloud provider.
197197
Scaleway *ScalewaySpec `json:"scaleway,omitempty"`
198+
// Linode configures the Linode (Akamai) cloud provider.
199+
Linode *LinodeSpec `json:"linode,omitempty"`
198200
}
199201

200202
// AWSSpec configures the AWS cloud provider.
@@ -256,6 +258,9 @@ type HetznerSpec struct{}
256258
type ScalewaySpec struct {
257259
}
258260

261+
// LinodeSpec configures the Linode (Akamai) cloud provider.
262+
type LinodeSpec struct{}
263+
259264
type KarpenterConfig struct {
260265
Enabled bool `json:"enabled,omitempty"`
261266
LogEncoding string `json:"logEncoding,omitempty"`

pkg/apis/kops/v1alpha3/zz_generated.conversion.go

Lines changed: 46 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/apis/kops/v1alpha3/zz_generated.deepcopy.go

Lines changed: 21 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/apis/kops/validation/legacy.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,8 @@ func ValidateCluster(c *kops.Cluster, strict bool, vfsContext *vfs.VFSContext) f
170170
k8sCloudProvider = "azure"
171171
case kops.CloudProviderScaleway:
172172
k8sCloudProvider = "external"
173+
case kops.CloudProviderLinode:
174+
k8sCloudProvider = "external"
173175
default:
174176
// We already added an error above
175177
k8sCloudProvider = "ignore"

pkg/apis/kops/validation/validation.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,16 @@ func validateCloudProvider(c *kops.Cluster, provider *kops.CloudProviderSpec, fi
380380
constraints.requiresNetworkCIDR = false
381381
constraints.requiresSubnetCIDR = false
382382
}
383+
if c.Spec.CloudProvider.Linode != nil {
384+
if optionTaken {
385+
allErrs = append(allErrs, field.Forbidden(fieldSpec.Child("linode"), "only one cloudProvider option permitted"))
386+
}
387+
optionTaken = true
388+
constraints.requiresSubnets = false
389+
constraints.requiresSubnetCIDR = false
390+
constraints.requiresSubnetRegion = true
391+
constraints.requiresNetworkCIDR = false
392+
}
383393
if c.GetCloudProvider() == kops.CloudProviderMetal {
384394
if optionTaken {
385395
allErrs = append(allErrs, field.Forbidden(fieldSpec.Child("metal"), "only one cloudProvider option permitted"))
@@ -1018,6 +1028,17 @@ func validateNetworking(cluster *kops.Cluster, v *kops.NetworkingSpec, fldPath *
10181028
allErrs = append(allErrs, field.Forbidden(fldPath.Child("networkCIDR"), "DO doesn't support specifying both NetworkID and NetworkCIDR"))
10191029
}
10201030
}
1031+
1032+
if cluster.GetCloudProvider() == kops.CloudProviderLinode {
1033+
// verify if the NetworkCIDR is in a private range as per RFC1918
1034+
if networkCIDR != nil && !networkCIDR.IP.IsPrivate() {
1035+
allErrs = append(allErrs, field.Invalid(fldPath.Child("networkCIDR"), v.NetworkCIDR, "networkCIDR must be within a private IP range"))
1036+
}
1037+
// verify if networkID is not specified. In case of Linode (Akamai), this is mutually exclusive.
1038+
if v.NetworkID != "" {
1039+
allErrs = append(allErrs, field.Forbidden(fldPath.Child("networkCIDR"), "Linode (Akamai) doesn't support specifying both NetworkID and NetworkCIDR"))
1040+
}
1041+
}
10211042
}
10221043

10231044
if len(v.AdditionalNetworkCIDRs) > 0 && providerConstraints.prohibitsMultipleNetworkCIDRs {

pkg/apis/kops/validation/validation_test.go

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1851,3 +1851,83 @@ func Test_Validate_NriConfig(t *testing.T) {
18511851
testErrors(t, g.Input.Containerd, errs, g.ExpectedErrors)
18521852
}
18531853
}
1854+
1855+
func newLinodeClusterForNetworkingValidation(networking kops.NetworkingSpec) *kops.Cluster {
1856+
return &kops.Cluster{
1857+
Spec: kops.ClusterSpec{
1858+
CloudProvider: kops.CloudProviderSpec{
1859+
Linode: &kops.LinodeSpec{},
1860+
},
1861+
Networking: networking,
1862+
},
1863+
}
1864+
}
1865+
1866+
func validLinodeNetworkingSpec() kops.NetworkingSpec {
1867+
return kops.NetworkingSpec{
1868+
NetworkCIDR: "10.0.0.0/8",
1869+
NonMasqueradeCIDR: "100.64.0.0/10",
1870+
PodCIDR: "100.96.0.0/11",
1871+
ServiceClusterIPRange: "100.64.0.0/13",
1872+
Subnets: []kops.ClusterSubnetSpec{
1873+
{
1874+
Name: "subnet-us-east",
1875+
CIDR: "10.11.0.0/16",
1876+
Type: kops.SubnetTypePublic,
1877+
Region: "us-east",
1878+
},
1879+
},
1880+
}
1881+
}
1882+
1883+
func TestValidateNetworkingLinode(t *testing.T) {
1884+
tests := []struct {
1885+
name string
1886+
network kops.NetworkingSpec
1887+
expected []*field.Error
1888+
}{
1889+
{
1890+
name: "accepts private network CIDR",
1891+
network: validLinodeNetworkingSpec(),
1892+
},
1893+
{
1894+
name: "rejects public network CIDR",
1895+
network: func() kops.NetworkingSpec {
1896+
n := validLinodeNetworkingSpec()
1897+
n.NetworkCIDR = "8.8.8.0/24"
1898+
n.Subnets[0].CIDR = "8.8.8.0/25"
1899+
return n
1900+
}(),
1901+
expected: []*field.Error{
1902+
{
1903+
Type: field.ErrorTypeInvalid,
1904+
Field: "networking.networkCIDR",
1905+
Detail: "networkCIDR must be within a private IP range",
1906+
},
1907+
},
1908+
},
1909+
{
1910+
name: "rejects networkID with networkCIDR",
1911+
network: func() kops.NetworkingSpec {
1912+
n := validLinodeNetworkingSpec()
1913+
n.NetworkID = "123456"
1914+
return n
1915+
}(),
1916+
expected: []*field.Error{
1917+
{
1918+
Type: field.ErrorTypeForbidden,
1919+
Field: "networking.networkCIDR",
1920+
Detail: "Linode (Akamai) doesn't support specifying both NetworkID and NetworkCIDR",
1921+
},
1922+
},
1923+
},
1924+
}
1925+
1926+
for _, tt := range tests {
1927+
t.Run(tt.name, func(t *testing.T) {
1928+
cluster := newLinodeClusterForNetworkingValidation(tt.network)
1929+
errList := validateNetworking(cluster, &cluster.Spec.Networking, field.NewPath("networking"), true, &cloudProviderConstraints{})
1930+
testFieldErrors(t, errList, tt.expected)
1931+
})
1932+
}
1933+
}

pkg/apis/kops/zz_generated.deepcopy.go

Lines changed: 21 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)