Merge pull request #15474 from scaleway/scw_use_kops_controller_for_bootstrap

scaleway: use kops controller for bootstrap
This commit is contained in:
Kubernetes Prow Robot 2023-06-14 08:22:17 -07:00 committed by GitHub
commit 07627306ed
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 324 additions and 133 deletions

View File

@ -46,6 +46,7 @@ import (
"k8s.io/kops/upup/pkg/fi/cloudup/gce/tpm/gcetpmverifier"
"k8s.io/kops/upup/pkg/fi/cloudup/hetzner"
"k8s.io/kops/upup/pkg/fi/cloudup/openstack"
"k8s.io/kops/upup/pkg/fi/cloudup/scaleway"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/manager"
@ -146,6 +147,12 @@ func main() {
setupLog.Error(err, "unable to create verifier")
os.Exit(1)
}
} else if opt.Server.Provider.Scaleway != nil {
verifier, err = scaleway.NewScalewayVerifier(ctx, opt.Server.Provider.Scaleway)
if err != nil {
setupLog.Error(err, "unable to create verifier")
os.Exit(1)
}
} else {
klog.Fatalf("server cloud provider config not provided")
}

View File

@ -22,6 +22,7 @@ import (
gcetpm "k8s.io/kops/upup/pkg/fi/cloudup/gce/tpm"
"k8s.io/kops/upup/pkg/fi/cloudup/hetzner"
"k8s.io/kops/upup/pkg/fi/cloudup/openstack"
"k8s.io/kops/upup/pkg/fi/cloudup/scaleway"
)
type Options struct {
@ -71,6 +72,7 @@ type ServerProviderOptions struct {
Hetzner *hetzner.HetznerVerifierOptions `json:"hetzner,omitempty"`
OpenStack *openstack.OpenStackVerifierOptions `json:"openstack,omitempty"`
DigitalOcean *do.DigitalOceanVerifierOptions `json:"do,omitempty"`
Scaleway *scaleway.ScalewayVerifierOptions `json:"scaleway,omitempty"`
}
// DiscoveryOptions configures our support for discovery, particularly gossip DNS (i.e. k8s.local)

View File

@ -1810,7 +1810,6 @@ func (i *integrationTest) runTestTerraformScaleway(t *testing.T) {
"aws_s3_object_"+i.clusterName+"-addons-kubelet-api.rbac.addons.k8s.io-k8s-1.9_content",
"aws_s3_object_"+i.clusterName+"-addons-limit-range.addons.k8s.io_content",
"aws_s3_object_"+i.clusterName+"-addons-networking.cilium.io-k8s-1.16_content",
"aws_s3_object_"+i.clusterName+"-addons-rbac.addons.k8s.io-k8s-1.8_content",
"scaleway_instance_server_control-plane-fr-par-1_user_data",
"scaleway_instance_server_nodes-fr-par-1_user_data",
)

View File

@ -34,6 +34,7 @@ import (
"k8s.io/kops/upup/pkg/fi/cloudup/gce/tpm/gcetpmsigner"
"k8s.io/kops/upup/pkg/fi/cloudup/hetzner"
"k8s.io/kops/upup/pkg/fi/cloudup/openstack"
"k8s.io/kops/upup/pkg/fi/cloudup/scaleway"
"k8s.io/kops/upup/pkg/fi/nodeup/nodetasks"
)
@ -80,13 +81,18 @@ func (b BootstrapClientBuilder) Build(c *fi.NodeupModelBuilderContext) error {
return err
}
authenticator = a
case kops.CloudProviderDO:
a, err := do.NewAuthenticator()
if err != nil {
return err
}
authenticator = a
case kops.CloudProviderScaleway:
a, err := scaleway.NewScalewayAuthenticator()
if err != nil {
return err
}
authenticator = a
default:
return fmt.Errorf("unsupported cloud provider for authenticator %q", b.CloudProvider())

View File

@ -33,6 +33,8 @@ func UseKopsControllerForNodeBootstrap(cluster *kops.Cluster) bool {
return true
case kops.CloudProviderDO:
return true
case kops.CloudProviderScaleway:
return true
default:
return false
}
@ -45,6 +47,8 @@ func UseChallengeCallback(cloudProvider kops.CloudProviderID) bool {
return true
case kops.CloudProviderDO:
return true
case kops.CloudProviderScaleway:
return true
default:
return false
}
@ -56,7 +60,7 @@ func UseKopsControllerForNodeConfig(cluster *kops.Cluster) bool {
switch cluster.Spec.GetCloudProvider() {
case kops.CloudProviderGCE:
// We can use cloud-discovery here.
case kops.CloudProviderHetzner:
case kops.CloudProviderHetzner, kops.CloudProviderScaleway:
// We don't have a cloud-discovery mechanism implemented in nodeup for hetzner,
// but we assume that we're using a load balancer with a fixed IP address
default:

View File

@ -231,7 +231,7 @@ func (b *BootstrapScript) buildEnvironmentVariables() (map[string]string, error)
}
}
if cluster.Spec.GetCloudProvider() == kops.CloudProviderScaleway {
if cluster.Spec.GetCloudProvider() == kops.CloudProviderScaleway && (b.ig.IsControlPlane() || cluster.UsesLegacyGossip()) {
profile, err := scaleway.CreateValidScalewayProfile()
if err != nil {
return nil, err

View File

@ -20,9 +20,11 @@ import (
"fmt"
"github.com/scaleway/scaleway-sdk-go/api/lb/v1"
"github.com/scaleway/scaleway-sdk-go/scw"
"k8s.io/klog/v2"
"k8s.io/kops/pkg/apis/kops"
"k8s.io/kops/pkg/dns"
"k8s.io/kops/pkg/wellknownports"
"k8s.io/kops/upup/pkg/fi"
"k8s.io/kops/upup/pkg/fi/cloudup/scaleway"
"k8s.io/kops/upup/pkg/fi/cloudup/scalewaytasks"
@ -78,36 +80,48 @@ func (b *APILoadBalancerModelBuilder) Build(c *fi.CloudupModelBuilderContext) er
c.AddTask(loadBalancer)
lbBackend := &scalewaytasks.LBBackend{
Name: fi.PtrTo("lb-backend"),
Lifecycle: b.Lifecycle,
lbBackendHttps, lbFrontendHttps := createLbBackendAndFrontend("https", wellknownports.KubeAPIServer, zone, loadBalancer)
lbBackendHttps.Lifecycle = b.Lifecycle
c.AddTask(lbBackendHttps)
lbFrontendHttps.Lifecycle = b.Lifecycle
c.AddTask(lbFrontendHttps)
if dns.IsGossipClusterName(b.Cluster.Name) || b.Cluster.UsesPrivateDNS() || b.Cluster.UsesNoneDNS() {
// Ensure the LB hostname is included in the TLS certificate,
// if we're not going to use an alias for it
loadBalancer.ForAPIServer = true
if b.Cluster.UsesNoneDNS() || b.UseKopsControllerForNodeBootstrap() {
lbBackendKopsController, lbFrontendKopsController := createLbBackendAndFrontend("kops-controller", wellknownports.KopsControllerPort, zone, loadBalancer)
lbBackendKopsController.Lifecycle = b.Lifecycle
c.AddTask(lbBackendKopsController)
lbFrontendKopsController.Lifecycle = b.Lifecycle
c.AddTask(lbFrontendKopsController)
}
}
return nil
}
func createLbBackendAndFrontend(name string, port int, zone scw.Zone, loadBalancer *scalewaytasks.LoadBalancer) (*scalewaytasks.LBBackend, *scalewaytasks.LBFrontend) {
lbBackendKopsController := &scalewaytasks.LBBackend{
Name: fi.PtrTo("lb-backend-" + name),
Zone: fi.PtrTo(string(zone)),
ForwardProtocol: fi.PtrTo(string(lb.ProtocolTCP)),
ForwardPort: fi.PtrTo(int32(443)),
ForwardPort: fi.PtrTo(int32(port)),
ForwardPortAlgorithm: fi.PtrTo(string(lb.ForwardPortAlgorithmRoundrobin)),
StickySessions: fi.PtrTo(string(lb.StickySessionsTypeNone)),
ProxyProtocol: fi.PtrTo(string(lb.ProxyProtocolProxyProtocolUnknown)),
LoadBalancer: loadBalancer,
}
c.AddTask(lbBackend)
lbFrontend := &scalewaytasks.LBFrontend{
Name: fi.PtrTo("lb-frontend"),
Lifecycle: b.Lifecycle,
lbFrontendKopsController := &scalewaytasks.LBFrontend{
Name: fi.PtrTo("lb-frontend-" + name),
Zone: fi.PtrTo(string(zone)),
InboundPort: fi.PtrTo(int32(443)),
InboundPort: fi.PtrTo(int32(port)),
LoadBalancer: loadBalancer,
LBBackend: lbBackend,
LBBackend: lbBackendKopsController,
}
c.AddTask(lbFrontend)
if dns.IsGossipClusterName(b.Cluster.Name) || b.Cluster.UsesPrivateDNS() || b.Cluster.UsesNoneDNS() {
// Ensure the LB hostname is included in the TLS certificate,
// if we're not going to use an alias for it
loadBalancer.ForAPIServer = true
}
return nil
return lbBackendKopsController, lbFrontendKopsController
}

View File

@ -48,13 +48,17 @@ type nodeIdentifier struct {
// New creates and returns a nodeidentity.Identifier for Nodes running on Scaleway
func New(CacheNodeidentityInfo bool) (nodeidentity.Identifier, error) {
scwClient, err := scw.NewClient(
scw.WithUserAgent("kubernetes-kops/"+kopsv.Version),
scw.WithEnv(),
)
profile, err := scaleway.CreateValidScalewayProfile()
if err != nil {
return nil, err
}
scwClient, err := scw.NewClient(
scw.WithProfile(profile),
scw.WithUserAgent(scaleway.KopsUserAgentPrefix+kopsv.Version),
)
if err != nil {
return nil, fmt.Errorf("creating client for Scaleway NodeIdentifier: %w", err)
}
return &nodeIdentifier{
client: scwClient,

View File

@ -66,14 +66,18 @@ func NewScwCloudProvider() (*ScwCloudProvider, error) {
privateIP := metadata.PrivateIP
klog.V(4).Infof("Found first private net IP of the running server: %q", privateIP)
profile, err := scaleway.CreateValidScalewayProfile()
if err != nil {
return nil, err
}
scwClient, err := scw.NewClient(
scw.WithProfile(profile),
scw.WithUserAgent(scaleway.KopsUserAgentPrefix+kopsv.Version),
scw.WithEnv(),
scw.WithDefaultZone(zone),
scw.WithDefaultRegion(region),
)
if err != nil {
return nil, fmt.Errorf("error creating client: %w", err)
return nil, fmt.Errorf("error creating client for Protokube: %w", err)
}
instanceAPI := instance.NewAPI(scwClient)

View File

@ -15,36 +15,13 @@ Assets:
- 54e79e4d48b9e191767e4abc08be1a8476a1c757e9a9f8c45c6ded001226867f@https://github.com/opencontainers/runc/releases/download/v1.1.5/runc.arm64
- 2f599c3d54f4c4bdbcc95aaf0c7b513a845d8f9503ec5b34c9f86aa1bc34fc0c@https://artifacts.k8s.io/binaries/kops/1.21.0-alpha.1/linux/arm64/protokube,https://github.com/kubernetes/kops/releases/download/v1.21.0-alpha.1/protokube-linux-arm64
- 9d842e3636a95de2315cdea2be7a282355aac0658ef0b86d5dc2449066538f13@https://artifacts.k8s.io/binaries/kops/1.21.0-alpha.1/linux/arm64/channels,https://github.com/kubernetes/kops/releases/download/v1.21.0-alpha.1/channels-linux-arm64
CAs:
kubernetes-ca: |
-----BEGIN CERTIFICATE-----
MIIBbjCCARigAwIBAgIMFpANqBD8NSD82AUSMA0GCSqGSIb3DQEBCwUAMBgxFjAU
BgNVBAMTDWt1YmVybmV0ZXMtY2EwHhcNMjEwNzA3MDcwODAwWhcNMzEwNzA3MDcw
ODAwWjAYMRYwFAYDVQQDEw1rdWJlcm5ldGVzLWNhMFwwDQYJKoZIhvcNAQEBBQAD
SwAwSAJBANFI3zr0Tk8krsW8vwjfMpzJOlWQ8616vG3YPa2qAgI7V4oKwfV0yIg1
jt+H6f4P/wkPAPTPTfRp9Iy8oHEEFw0CAwEAAaNCMEAwDgYDVR0PAQH/BAQDAgEG
MA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNG3zVjTcLlJwDsJ4/K9DV7KohUA
MA0GCSqGSIb3DQEBCwUAA0EAB8d03fY2w7WKpfO29qI295pu2C4ca9AiVGOpgSc8
tmQsq6rcxt3T+rb589PVtz0mw/cKTxOk6gH2CCC+yHfy2w==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIBbjCCARigAwIBAgIMFpANvmSa0OAlYmXKMA0GCSqGSIb3DQEBCwUAMBgxFjAU
BgNVBAMTDWt1YmVybmV0ZXMtY2EwHhcNMjEwNzA3MDcwOTM2WhcNMzEwNzA3MDcw
OTM2WjAYMRYwFAYDVQQDEw1rdWJlcm5ldGVzLWNhMFwwDQYJKoZIhvcNAQEBBQAD
SwAwSAJBAMF6F4aZdpe0RUpyykaBpWwZCnwbffhYGOw+fs6RdLuUq7QCNmJm/Eq7
WWOziMYDiI9SbclpD+6QiJ0N3EqppVUCAwEAAaNCMEAwDgYDVR0PAQH/BAQDAgEG
MA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFLImp6ARjPDAH6nhI+scWVt3Q9bn
MA0GCSqGSIb3DQEBCwUAA0EAVQVx5MUtuAIeePuP9o51xtpT2S6Fvfi8J4ICxnlA
9B7UD2ushcVFPtaeoL9Gfu8aY4KJBeqqg5ojl4qmRnThjw==
-----END CERTIFICATE-----
CAs: {}
ClusterName: scw-minimal.k8s.local
ContainerRuntime: containerd
Hooks:
- null
- null
KeypairIDs:
kube-proxy: "6986354184403674830529235586"
kubelet: "6986354184404014133128804066"
kubernetes-ca: "6982820025135291416230495506"
KubeProxy: null
KubeletConfig:

View File

@ -6,7 +6,7 @@ spec:
addons:
- id: k8s-1.16
manifest: kops-controller.addons.k8s.io/k8s-1.16.yaml
manifestHash: bffd10e2291b38f02725cd04366b8029878272a79e68825e88d98a681068cd6c
manifestHash: 0652ed8a25e088a043a9f162f3b73dba1b9b5dcbb8efcb2b6550826d81f32a2b
name: kops-controller.addons.k8s.io
needsRollingUpdate: control-plane
selector:
@ -19,13 +19,6 @@ spec:
selector:
k8s-addon: coredns.addons.k8s.io
version: 9.99.0
- id: k8s-1.8
manifest: rbac.addons.k8s.io/k8s-1.8.yaml
manifestHash: f81bd7c57bc1902ca342635d7ad7d01b82dfeaff01a1192b076e66907d87871e
name: rbac.addons.k8s.io
selector:
k8s-addon: rbac.addons.k8s.io
version: 9.99.0
- id: k8s-1.9
manifest: kubelet-api.rbac.addons.k8s.io/k8s-1.9.yaml
manifestHash: 01c120e887bd98d82ef57983ad58a0b22bc85efb48108092a24c4b82e4c9ea81

View File

@ -1,7 +1,7 @@
apiVersion: v1
data:
config.yaml: |
{"clusterName":"scw-minimal.k8s.local","cloud":"scaleway","configBase":"memfs://tests/scw-minimal.k8s.local","secretStore":"memfs://tests/scw-minimal.k8s.local/secrets","discovery":{"enabled":true}}
{"clusterName":"scw-minimal.k8s.local","cloud":"scaleway","configBase":"memfs://tests/scw-minimal.k8s.local","secretStore":"memfs://tests/scw-minimal.k8s.local/secrets","server":{"Listen":":3988","provider":{"scaleway":{}},"serverKeyPath":"/etc/kubernetes/kops-controller/pki/kops-controller.key","serverCertificatePath":"/etc/kubernetes/kops-controller/pki/kops-controller.crt","caBasePath":"/etc/kubernetes/kops-controller/pki","signingCAs":["kubernetes-ca"],"certNames":["kubelet","kubelet-server"]},"discovery":{"enabled":true}}
kind: ConfigMap
metadata:
creationTimestamp: null
@ -32,6 +32,8 @@ spec:
k8s-app: kops-controller
template:
metadata:
annotations:
dns.alpha.kubernetes.io/internal: kops-controller.internal.scw-minimal.k8s.local
creationTimestamp: null
labels:
k8s-addon: kops-controller.addons.k8s.io

View File

@ -1,19 +0,0 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
creationTimestamp: null
labels:
addon.kops.k8s.io/name: rbac.addons.k8s.io
addonmanager.kubernetes.io/mode: Reconcile
app.kubernetes.io/managed-by: kops
k8s-addon: rbac.addons.k8s.io
kubernetes.io/cluster-service: "true"
name: kubelet-cluster-admin
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: system:node
subjects:
- apiGroup: rbac.authorization.k8s.io
kind: User
name: kubelet

View File

@ -150,10 +150,33 @@ __EOF_CLUSTER_SPEC
cat > conf/kube_env.yaml << '__EOF_KUBE_ENV'
CloudProvider: scaleway
ClusterName: scw-minimal.k8s.local
ConfigBase: memfs://tests/scw-minimal.k8s.local
ConfigServer:
CACertificates: |
-----BEGIN CERTIFICATE-----
MIIBbjCCARigAwIBAgIMFpANqBD8NSD82AUSMA0GCSqGSIb3DQEBCwUAMBgxFjAU
BgNVBAMTDWt1YmVybmV0ZXMtY2EwHhcNMjEwNzA3MDcwODAwWhcNMzEwNzA3MDcw
ODAwWjAYMRYwFAYDVQQDEw1rdWJlcm5ldGVzLWNhMFwwDQYJKoZIhvcNAQEBBQAD
SwAwSAJBANFI3zr0Tk8krsW8vwjfMpzJOlWQ8616vG3YPa2qAgI7V4oKwfV0yIg1
jt+H6f4P/wkPAPTPTfRp9Iy8oHEEFw0CAwEAAaNCMEAwDgYDVR0PAQH/BAQDAgEG
MA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNG3zVjTcLlJwDsJ4/K9DV7KohUA
MA0GCSqGSIb3DQEBCwUAA0EAB8d03fY2w7WKpfO29qI295pu2C4ca9AiVGOpgSc8
tmQsq6rcxt3T+rb589PVtz0mw/cKTxOk6gH2CCC+yHfy2w==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIBbjCCARigAwIBAgIMFpANvmSa0OAlYmXKMA0GCSqGSIb3DQEBCwUAMBgxFjAU
BgNVBAMTDWt1YmVybmV0ZXMtY2EwHhcNMjEwNzA3MDcwOTM2WhcNMzEwNzA3MDcw
OTM2WjAYMRYwFAYDVQQDEw1rdWJlcm5ldGVzLWNhMFwwDQYJKoZIhvcNAQEBBQAD
SwAwSAJBAMF6F4aZdpe0RUpyykaBpWwZCnwbffhYGOw+fs6RdLuUq7QCNmJm/Eq7
WWOziMYDiI9SbclpD+6QiJ0N3EqppVUCAwEAAaNCMEAwDgYDVR0PAQH/BAQDAgEG
MA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFLImp6ARjPDAH6nhI+scWVt3Q9bn
MA0GCSqGSIb3DQEBCwUAA0EAVQVx5MUtuAIeePuP9o51xtpT2S6Fvfi8J4ICxnlA
9B7UD2ushcVFPtaeoL9Gfu8aY4KJBeqqg5ojl4qmRnThjw==
-----END CERTIFICATE-----
servers:
- https://kops-controller.internal.scw-minimal.k8s.local:3988/
InstanceGroupName: nodes-fr-par-1
InstanceGroupRole: Node
NodeupConfigHash: ThbM3OQQUCmecnKq4GQW0fdWp6sjwEiAjfmqga3QcXY=
NodeupConfigHash: l6ITXtzPONIgO+uyEAe0rYXGYgBVjPkJ9/Ov2xKxX/U=
__EOF_KUBE_ENV

View File

@ -148,14 +148,6 @@ resource "aws_s3_object" "scw-minimal-k8s-local-addons-networking-cilium-io-k8s-
server_side_encryption = "AES256"
}
resource "aws_s3_object" "scw-minimal-k8s-local-addons-rbac-addons-k8s-io-k8s-1-8" {
bucket = "testingBucket"
content = file("${path.module}/data/aws_s3_object_scw-minimal.k8s.local-addons-rbac.addons.k8s.io-k8s-1.8_content")
key = "tests/scw-minimal.k8s.local/addons/rbac.addons.k8s.io/k8s-1.8.yaml"
provider = aws.files
server_side_encryption = "AES256"
}
resource "aws_s3_object" "scw-minimal-k8s-local-addons-scaleway-cloud-controller-addons-k8s-io-k8s-1-24" {
bucket = "testingBucket"
content = file("${path.module}/data/aws_s3_object_scw-minimal.k8s.local-addons-scaleway-cloud-controller.addons.k8s.io-k8s-1.24_content")
@ -226,18 +218,32 @@ resource "scaleway_lb" "api-scw-minimal-k8s-local" {
type = "LB-S"
}
resource "scaleway_lb_backend" "lb-backend" {
resource "scaleway_lb_backend" "lb-backend-https" {
forward_port = 443
forward_protocol = "tcp"
lb_id = scaleway_lb.api-scw-minimal-k8s-local.id
name = "lb-backend"
name = "lb-backend-https"
}
resource "scaleway_lb_frontend" "lb-frontend" {
backend_id = scaleway_lb_backend.lb-backend.id
resource "scaleway_lb_backend" "lb-backend-kops-controller" {
forward_port = 3988
forward_protocol = "tcp"
lb_id = scaleway_lb.api-scw-minimal-k8s-local.id
name = "lb-backend-kops-controller"
}
resource "scaleway_lb_frontend" "lb-frontend-https" {
backend_id = scaleway_lb_backend.lb-backend-https.id
inbound_port = 443
lb_id = scaleway_lb.api-scw-minimal-k8s-local.id
name = "lb-frontend"
name = "lb-frontend-https"
}
resource "scaleway_lb_frontend" "lb-frontend-kops-controller" {
backend_id = scaleway_lb_backend.lb-backend-kops-controller.id
inbound_port = 3988
lb_id = scaleway_lb.api-scw-minimal-k8s-local.id
name = "lb-frontend-kops-controller"
}
resource "scaleway_lb_ip" "api-scw-minimal-k8s-local" {

View File

@ -1465,7 +1465,7 @@ func (n *nodeUpConfigBuilder) BuildConfig(ig *kops.InstanceGroup, apiserverAddit
} else {
// If we do have a fixed IP, we use it (on some clouds, initially)
switch cluster.Spec.GetCloudProvider() {
case kops.CloudProviderHetzner:
case kops.CloudProviderHetzner, kops.CloudProviderScaleway:
bootConfig.APIServerIPs = controlPlaneIPs
}
}

View File

@ -0,0 +1,43 @@
/*
Copyright 2023 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 scaleway
import (
"fmt"
"github.com/scaleway/scaleway-sdk-go/api/instance/v1"
"k8s.io/kops/pkg/bootstrap"
)
const ScalewayAuthenticationTokenPrefix = "x-scaleway-instance-server-id "
type scalewayAuthenticator struct{}
var _ bootstrap.Authenticator = &scalewayAuthenticator{}
func NewScalewayAuthenticator() (bootstrap.Authenticator, error) {
return &scalewayAuthenticator{}, nil
}
func (a *scalewayAuthenticator) CreateToken(body []byte) (string, error) {
metadataAPI := instance.NewMetadataAPI()
metadata, err := metadataAPI.GetMetadata()
if err != nil {
return "", fmt.Errorf("failed to retrieve server metadata: %w", err)
}
return ScalewayAuthenticationTokenPrefix + metadata.ID, nil
}

View File

@ -0,0 +1,123 @@
/*
Copyright 2023 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 scaleway
import (
"context"
"fmt"
"net"
"net/http"
"strconv"
"strings"
"github.com/scaleway/scaleway-sdk-go/api/instance/v1"
"github.com/scaleway/scaleway-sdk-go/scw"
kopsv "k8s.io/kops"
"k8s.io/kops/pkg/bootstrap"
"k8s.io/kops/pkg/wellknownports"
)
type ScalewayVerifierOptions struct{}
type scalewayVerifier struct {
scwClient *scw.Client
}
var _ bootstrap.Verifier = &scalewayVerifier{}
func NewScalewayVerifier(ctx context.Context, opt *ScalewayVerifierOptions) (bootstrap.Verifier, error) {
profile, err := CreateValidScalewayProfile()
if err != nil {
return nil, fmt.Errorf("creating client for Scaleway Verifier: %w", err)
}
scwClient, err := scw.NewClient(
scw.WithProfile(profile),
scw.WithUserAgent(KopsUserAgentPrefix+kopsv.Version),
)
if err != nil {
return nil, err
}
return &scalewayVerifier{
scwClient: scwClient,
}, nil
}
func (v scalewayVerifier) VerifyToken(ctx context.Context, rawRequest *http.Request, token string, body []byte, useInstanceIDForNodeName bool) (*bootstrap.VerifyResult, error) {
if !strings.HasPrefix(token, ScalewayAuthenticationTokenPrefix) {
return nil, fmt.Errorf("incorrect authorization type")
}
serverID := strings.TrimPrefix(token, ScalewayAuthenticationTokenPrefix)
metadataAPI := instance.NewMetadataAPI()
metadata, err := metadataAPI.GetMetadata()
if err != nil {
return nil, fmt.Errorf("failed to retrieve server metadata: %w", err)
}
zone, err := scw.ParseZone(metadata.Location.ZoneID)
if err != nil {
return nil, fmt.Errorf("unable to parse Scaleway zone %q: %w", metadata.Location.ZoneID, err)
}
profile, err := CreateValidScalewayProfile()
if err != nil {
return nil, err
}
scwClient, err := scw.NewClient(
scw.WithProfile(profile),
scw.WithUserAgent(KopsUserAgentPrefix+kopsv.Version),
)
if err != nil {
return nil, fmt.Errorf("creating client for Scaleway Verifier: %w", err)
}
instanceAPI := instance.NewAPI(scwClient)
serverResponse, err := instanceAPI.GetServer(&instance.GetServerRequest{
ServerID: serverID,
Zone: zone,
}, scw.WithContext(ctx))
if err != nil || serverResponse == nil {
return nil, fmt.Errorf("failed to get server %s: %w", serverID, err)
}
server := serverResponse.Server
addresses := []string(nil)
challengeEndPoints := []string(nil)
if server.PrivateIP != nil {
addresses = append(addresses, *server.PrivateIP)
challengeEndPoints = append(challengeEndPoints, net.JoinHostPort(*server.PrivateIP, strconv.Itoa(wellknownports.NodeupChallenge)))
}
if server.IPv6 != nil {
addresses = append(addresses, server.IPv6.Address.String())
challengeEndPoints = append(challengeEndPoints, net.JoinHostPort(server.IPv6.Address.String(), strconv.Itoa(wellknownports.NodeupChallenge)))
}
igName := ""
for _, tag := range server.Tags {
if strings.HasPrefix(tag, TagInstanceGroup) {
igName = strings.TrimPrefix(tag, TagInstanceGroup+"=")
}
}
result := &bootstrap.VerifyResult{
NodeName: server.Name,
InstanceGroupName: igName,
CertificateNames: addresses,
ChallengeEndpoint: challengeEndPoints[0],
}
return result, nil
}

View File

@ -278,13 +278,8 @@ func (_ *Instance) RenderScw(t *scaleway.ScwAPITarget, actual, expected, changes
if err != nil {
return fmt.Errorf("listing load-balancer's back-ends for instance creation: %w", err)
}
if backEnds.TotalCount > 1 {
return fmt.Errorf("cannot have multiple back-ends for load-balancer %s", loadBalancer.Name)
} else if backEnds.TotalCount < 1 {
return fmt.Errorf("load-balancer %s should have 1 back-end, got 0", loadBalancer.Name)
}
backEnd := backEnds.Backends[0]
for _, backEnd := range backEnds.Backends {
// If we are adding instances, we also need to add them to the load-balancer's backend
if newInstanceCount > 0 {
_, err = lbService.AddBackendServers(&lb.ZonedAPIAddBackendServersRequest{
@ -307,7 +302,6 @@ func (_ *Instance) RenderScw(t *scaleway.ScwAPITarget, actual, expected, changes
return fmt.Errorf("removing servers' IPs from load-balancer's back-end: %w", err)
}
}
_, err = lbService.WaitForLb(&lb.ZonedAPIWaitForLBRequest{
LBID: loadBalancer.ID,
Zone: zone,
@ -317,6 +311,7 @@ func (_ *Instance) RenderScw(t *scaleway.ScwAPITarget, actual, expected, changes
}
}
}
}
return nil
}

View File

@ -136,7 +136,7 @@ func (l *LBBackend) RenderScw(t *scaleway.ScwAPITarget, actual, expected, change
backendCreated, err := lbService.CreateBackend(&lb.ZonedAPICreateBackendRequest{
Zone: scw.Zone(fi.ValueOf(expected.Zone)),
LBID: fi.ValueOf(expected.LoadBalancer.LBID), // try expected instead of l
LBID: fi.ValueOf(expected.LoadBalancer.LBID),
Name: fi.ValueOf(expected.Name),
ForwardProtocol: lb.Protocol(fi.ValueOf(expected.ForwardProtocol)),
ForwardPort: fi.ValueOf(expected.ForwardPort),

View File

@ -738,6 +738,9 @@ func (tf *TemplateFunctions) KopsControllerConfig() (string, error) {
case kops.CloudProviderDO:
config.Server.Provider.DigitalOcean = &do.DigitalOceanVerifierOptions{}
case kops.CloudProviderScaleway:
config.Server.Provider.Scaleway = &scaleway.ScalewayVerifierOptions{}
default:
return "", fmt.Errorf("unsupported cloud provider %s", cluster.Spec.GetCloudProvider())
}

View File

@ -59,6 +59,7 @@ import (
"k8s.io/kops/upup/pkg/fi/cloudup/gce/tpm/gcetpmsigner"
"k8s.io/kops/upup/pkg/fi/cloudup/hetzner"
"k8s.io/kops/upup/pkg/fi/cloudup/openstack"
"k8s.io/kops/upup/pkg/fi/cloudup/scaleway"
"k8s.io/kops/upup/pkg/fi/nodeup/local"
"k8s.io/kops/upup/pkg/fi/nodeup/nodetasks"
"k8s.io/kops/upup/pkg/fi/secrets"
@ -763,14 +764,18 @@ func getNodeConfigFromServers(ctx context.Context, bootConfig *nodeup.BootConfig
return nil, err
}
authenticator = a
case api.CloudProviderDO:
a, err := do.NewAuthenticator()
if err != nil {
return nil, err
}
authenticator = a
case api.CloudProviderScaleway:
a, err := scaleway.NewScalewayAuthenticator()
if err != nil {
return nil, err
}
authenticator = a
default:
return nil, fmt.Errorf("unsupported cloud provider for node configuration %s", bootConfig.CloudProvider)
}