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/gce/tpm/gcetpmverifier"
"k8s.io/kops/upup/pkg/fi/cloudup/hetzner" "k8s.io/kops/upup/pkg/fi/cloudup/hetzner"
"k8s.io/kops/upup/pkg/fi/cloudup/openstack" "k8s.io/kops/upup/pkg/fi/cloudup/openstack"
"k8s.io/kops/upup/pkg/fi/cloudup/scaleway"
ctrl "sigs.k8s.io/controller-runtime" ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/manager" "sigs.k8s.io/controller-runtime/pkg/manager"
@ -146,6 +147,12 @@ func main() {
setupLog.Error(err, "unable to create verifier") setupLog.Error(err, "unable to create verifier")
os.Exit(1) 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 { } else {
klog.Fatalf("server cloud provider config not provided") 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" 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/hetzner"
"k8s.io/kops/upup/pkg/fi/cloudup/openstack" "k8s.io/kops/upup/pkg/fi/cloudup/openstack"
"k8s.io/kops/upup/pkg/fi/cloudup/scaleway"
) )
type Options struct { type Options struct {
@ -71,6 +72,7 @@ type ServerProviderOptions struct {
Hetzner *hetzner.HetznerVerifierOptions `json:"hetzner,omitempty"` Hetzner *hetzner.HetznerVerifierOptions `json:"hetzner,omitempty"`
OpenStack *openstack.OpenStackVerifierOptions `json:"openstack,omitempty"` OpenStack *openstack.OpenStackVerifierOptions `json:"openstack,omitempty"`
DigitalOcean *do.DigitalOceanVerifierOptions `json:"do,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) // 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-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-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-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_control-plane-fr-par-1_user_data",
"scaleway_instance_server_nodes-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/gce/tpm/gcetpmsigner"
"k8s.io/kops/upup/pkg/fi/cloudup/hetzner" "k8s.io/kops/upup/pkg/fi/cloudup/hetzner"
"k8s.io/kops/upup/pkg/fi/cloudup/openstack" "k8s.io/kops/upup/pkg/fi/cloudup/openstack"
"k8s.io/kops/upup/pkg/fi/cloudup/scaleway"
"k8s.io/kops/upup/pkg/fi/nodeup/nodetasks" "k8s.io/kops/upup/pkg/fi/nodeup/nodetasks"
) )
@ -80,13 +81,18 @@ func (b BootstrapClientBuilder) Build(c *fi.NodeupModelBuilderContext) error {
return err return err
} }
authenticator = a authenticator = a
case kops.CloudProviderDO: case kops.CloudProviderDO:
a, err := do.NewAuthenticator() a, err := do.NewAuthenticator()
if err != nil { if err != nil {
return err return err
} }
authenticator = a authenticator = a
case kops.CloudProviderScaleway:
a, err := scaleway.NewScalewayAuthenticator()
if err != nil {
return err
}
authenticator = a
default: default:
return fmt.Errorf("unsupported cloud provider for authenticator %q", b.CloudProvider()) 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 return true
case kops.CloudProviderDO: case kops.CloudProviderDO:
return true return true
case kops.CloudProviderScaleway:
return true
default: default:
return false return false
} }
@ -45,6 +47,8 @@ func UseChallengeCallback(cloudProvider kops.CloudProviderID) bool {
return true return true
case kops.CloudProviderDO: case kops.CloudProviderDO:
return true return true
case kops.CloudProviderScaleway:
return true
default: default:
return false return false
} }
@ -56,7 +60,7 @@ func UseKopsControllerForNodeConfig(cluster *kops.Cluster) bool {
switch cluster.Spec.GetCloudProvider() { switch cluster.Spec.GetCloudProvider() {
case kops.CloudProviderGCE: case kops.CloudProviderGCE:
// We can use cloud-discovery here. // 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, // 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 // but we assume that we're using a load balancer with a fixed IP address
default: 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() profile, err := scaleway.CreateValidScalewayProfile()
if err != nil { if err != nil {
return nil, err return nil, err

View File

@ -20,9 +20,11 @@ import (
"fmt" "fmt"
"github.com/scaleway/scaleway-sdk-go/api/lb/v1" "github.com/scaleway/scaleway-sdk-go/api/lb/v1"
"github.com/scaleway/scaleway-sdk-go/scw"
"k8s.io/klog/v2" "k8s.io/klog/v2"
"k8s.io/kops/pkg/apis/kops" "k8s.io/kops/pkg/apis/kops"
"k8s.io/kops/pkg/dns" "k8s.io/kops/pkg/dns"
"k8s.io/kops/pkg/wellknownports"
"k8s.io/kops/upup/pkg/fi" "k8s.io/kops/upup/pkg/fi"
"k8s.io/kops/upup/pkg/fi/cloudup/scaleway" "k8s.io/kops/upup/pkg/fi/cloudup/scaleway"
"k8s.io/kops/upup/pkg/fi/cloudup/scalewaytasks" "k8s.io/kops/upup/pkg/fi/cloudup/scalewaytasks"
@ -78,36 +80,48 @@ func (b *APILoadBalancerModelBuilder) Build(c *fi.CloudupModelBuilderContext) er
c.AddTask(loadBalancer) c.AddTask(loadBalancer)
lbBackend := &scalewaytasks.LBBackend{ lbBackendHttps, lbFrontendHttps := createLbBackendAndFrontend("https", wellknownports.KubeAPIServer, zone, loadBalancer)
Name: fi.PtrTo("lb-backend"), lbBackendHttps.Lifecycle = b.Lifecycle
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)), Zone: fi.PtrTo(string(zone)),
ForwardProtocol: fi.PtrTo(string(lb.ProtocolTCP)), ForwardProtocol: fi.PtrTo(string(lb.ProtocolTCP)),
ForwardPort: fi.PtrTo(int32(443)), ForwardPort: fi.PtrTo(int32(port)),
ForwardPortAlgorithm: fi.PtrTo(string(lb.ForwardPortAlgorithmRoundrobin)), ForwardPortAlgorithm: fi.PtrTo(string(lb.ForwardPortAlgorithmRoundrobin)),
StickySessions: fi.PtrTo(string(lb.StickySessionsTypeNone)), StickySessions: fi.PtrTo(string(lb.StickySessionsTypeNone)),
ProxyProtocol: fi.PtrTo(string(lb.ProxyProtocolProxyProtocolUnknown)), ProxyProtocol: fi.PtrTo(string(lb.ProxyProtocolProxyProtocolUnknown)),
LoadBalancer: loadBalancer, LoadBalancer: loadBalancer,
} }
c.AddTask(lbBackend) lbFrontendKopsController := &scalewaytasks.LBFrontend{
Name: fi.PtrTo("lb-frontend-" + name),
lbFrontend := &scalewaytasks.LBFrontend{
Name: fi.PtrTo("lb-frontend"),
Lifecycle: b.Lifecycle,
Zone: fi.PtrTo(string(zone)), Zone: fi.PtrTo(string(zone)),
InboundPort: fi.PtrTo(int32(443)), InboundPort: fi.PtrTo(int32(port)),
LoadBalancer: loadBalancer, LoadBalancer: loadBalancer,
LBBackend: lbBackend, LBBackend: lbBackendKopsController,
} }
c.AddTask(lbFrontend) return lbBackendKopsController, lbFrontendKopsController
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
} }

View File

@ -48,13 +48,17 @@ type nodeIdentifier struct {
// New creates and returns a nodeidentity.Identifier for Nodes running on Scaleway // New creates and returns a nodeidentity.Identifier for Nodes running on Scaleway
func New(CacheNodeidentityInfo bool) (nodeidentity.Identifier, error) { func New(CacheNodeidentityInfo bool) (nodeidentity.Identifier, error) {
scwClient, err := scw.NewClient( profile, err := scaleway.CreateValidScalewayProfile()
scw.WithUserAgent("kubernetes-kops/"+kopsv.Version),
scw.WithEnv(),
)
if err != nil { if err != nil {
return nil, err 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{ return &nodeIdentifier{
client: scwClient, client: scwClient,

View File

@ -66,14 +66,18 @@ func NewScwCloudProvider() (*ScwCloudProvider, error) {
privateIP := metadata.PrivateIP privateIP := metadata.PrivateIP
klog.V(4).Infof("Found first private net IP of the running server: %q", 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( scwClient, err := scw.NewClient(
scw.WithProfile(profile),
scw.WithUserAgent(scaleway.KopsUserAgentPrefix+kopsv.Version), scw.WithUserAgent(scaleway.KopsUserAgentPrefix+kopsv.Version),
scw.WithEnv(),
scw.WithDefaultZone(zone), scw.WithDefaultZone(zone),
scw.WithDefaultRegion(region), scw.WithDefaultRegion(region),
) )
if err != nil { 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) instanceAPI := instance.NewAPI(scwClient)

View File

@ -15,36 +15,13 @@ Assets:
- 54e79e4d48b9e191767e4abc08be1a8476a1c757e9a9f8c45c6ded001226867f@https://github.com/opencontainers/runc/releases/download/v1.1.5/runc.arm64 - 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 - 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 - 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: 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-----
ClusterName: scw-minimal.k8s.local ClusterName: scw-minimal.k8s.local
ContainerRuntime: containerd ContainerRuntime: containerd
Hooks: Hooks:
- null - null
- null - null
KeypairIDs: KeypairIDs:
kube-proxy: "6986354184403674830529235586"
kubelet: "6986354184404014133128804066"
kubernetes-ca: "6982820025135291416230495506" kubernetes-ca: "6982820025135291416230495506"
KubeProxy: null KubeProxy: null
KubeletConfig: KubeletConfig:

View File

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

View File

@ -1,7 +1,7 @@
apiVersion: v1 apiVersion: v1
data: data:
config.yaml: | 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 kind: ConfigMap
metadata: metadata:
creationTimestamp: null creationTimestamp: null
@ -32,6 +32,8 @@ spec:
k8s-app: kops-controller k8s-app: kops-controller
template: template:
metadata: metadata:
annotations:
dns.alpha.kubernetes.io/internal: kops-controller.internal.scw-minimal.k8s.local
creationTimestamp: null creationTimestamp: null
labels: labels:
k8s-addon: kops-controller.addons.k8s.io 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' cat > conf/kube_env.yaml << '__EOF_KUBE_ENV'
CloudProvider: scaleway CloudProvider: scaleway
ClusterName: scw-minimal.k8s.local 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 InstanceGroupName: nodes-fr-par-1
InstanceGroupRole: Node InstanceGroupRole: Node
NodeupConfigHash: ThbM3OQQUCmecnKq4GQW0fdWp6sjwEiAjfmqga3QcXY= NodeupConfigHash: l6ITXtzPONIgO+uyEAe0rYXGYgBVjPkJ9/Ov2xKxX/U=
__EOF_KUBE_ENV __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" 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" { resource "aws_s3_object" "scw-minimal-k8s-local-addons-scaleway-cloud-controller-addons-k8s-io-k8s-1-24" {
bucket = "testingBucket" 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") 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" type = "LB-S"
} }
resource "scaleway_lb_backend" "lb-backend" { resource "scaleway_lb_backend" "lb-backend-https" {
forward_port = 443 forward_port = 443
forward_protocol = "tcp" forward_protocol = "tcp"
lb_id = scaleway_lb.api-scw-minimal-k8s-local.id lb_id = scaleway_lb.api-scw-minimal-k8s-local.id
name = "lb-backend" name = "lb-backend-https"
} }
resource "scaleway_lb_frontend" "lb-frontend" { resource "scaleway_lb_backend" "lb-backend-kops-controller" {
backend_id = scaleway_lb_backend.lb-backend.id 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 inbound_port = 443
lb_id = scaleway_lb.api-scw-minimal-k8s-local.id 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" { resource "scaleway_lb_ip" "api-scw-minimal-k8s-local" {

View File

@ -1465,7 +1465,7 @@ func (n *nodeUpConfigBuilder) BuildConfig(ig *kops.InstanceGroup, apiserverAddit
} else { } else {
// If we do have a fixed IP, we use it (on some clouds, initially) // If we do have a fixed IP, we use it (on some clouds, initially)
switch cluster.Spec.GetCloudProvider() { switch cluster.Spec.GetCloudProvider() {
case kops.CloudProviderHetzner: case kops.CloudProviderHetzner, kops.CloudProviderScaleway:
bootConfig.APIServerIPs = controlPlaneIPs 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,42 +278,37 @@ func (_ *Instance) RenderScw(t *scaleway.ScwAPITarget, actual, expected, changes
if err != nil { if err != nil {
return fmt.Errorf("listing load-balancer's back-ends for instance creation: %w", err) 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]
// If we are adding instances, we also need to add them to the load-balancer's backend for _, backEnd := range backEnds.Backends {
if newInstanceCount > 0 { // If we are adding instances, we also need to add them to the load-balancer's backend
_, err = lbService.AddBackendServers(&lb.ZonedAPIAddBackendServersRequest{ if newInstanceCount > 0 {
Zone: zone, _, err = lbService.AddBackendServers(&lb.ZonedAPIAddBackendServersRequest{
BackendID: backEnd.ID, Zone: zone,
ServerIP: controlPlanePrivateIPs, BackendID: backEnd.ID,
ServerIP: controlPlanePrivateIPs,
})
if err != nil {
return fmt.Errorf("adding servers' IPs to load-balancer's back-end: %w", err)
}
} else {
// If we are deleting instances, we also need to delete them from the load-balancer's backend
_, err = lbService.RemoveBackendServers(&lb.ZonedAPIRemoveBackendServersRequest{
Zone: zone,
BackendID: backEnd.ID,
ServerIP: controlPlanePrivateIPs,
})
if err != nil {
return fmt.Errorf("removing servers' IPs from load-balancer's back-end: %w", err)
}
}
_, err = lbService.WaitForLb(&lb.ZonedAPIWaitForLBRequest{
LBID: loadBalancer.ID,
Zone: zone,
}) })
if err != nil { if err != nil {
return fmt.Errorf("adding servers' IPs to load-balancer's back-end: %w", err) return fmt.Errorf("waiting for load-balancer %s: %w", loadBalancer.ID, err)
} }
} else {
// If we are deleting instances, we also need to delete them from the load-balancer's backend
_, err = lbService.RemoveBackendServers(&lb.ZonedAPIRemoveBackendServersRequest{
Zone: zone,
BackendID: backEnd.ID,
ServerIP: controlPlanePrivateIPs,
})
if err != nil {
return fmt.Errorf("removing servers' IPs from load-balancer's back-end: %w", err)
}
}
_, err = lbService.WaitForLb(&lb.ZonedAPIWaitForLBRequest{
LBID: loadBalancer.ID,
Zone: zone,
})
if err != nil {
return fmt.Errorf("waiting for load-balancer %s: %w", loadBalancer.ID, err)
} }
} }
} }

View File

@ -136,7 +136,7 @@ func (l *LBBackend) RenderScw(t *scaleway.ScwAPITarget, actual, expected, change
backendCreated, err := lbService.CreateBackend(&lb.ZonedAPICreateBackendRequest{ backendCreated, err := lbService.CreateBackend(&lb.ZonedAPICreateBackendRequest{
Zone: scw.Zone(fi.ValueOf(expected.Zone)), 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), Name: fi.ValueOf(expected.Name),
ForwardProtocol: lb.Protocol(fi.ValueOf(expected.ForwardProtocol)), ForwardProtocol: lb.Protocol(fi.ValueOf(expected.ForwardProtocol)),
ForwardPort: fi.ValueOf(expected.ForwardPort), ForwardPort: fi.ValueOf(expected.ForwardPort),

View File

@ -738,6 +738,9 @@ func (tf *TemplateFunctions) KopsControllerConfig() (string, error) {
case kops.CloudProviderDO: case kops.CloudProviderDO:
config.Server.Provider.DigitalOcean = &do.DigitalOceanVerifierOptions{} config.Server.Provider.DigitalOcean = &do.DigitalOceanVerifierOptions{}
case kops.CloudProviderScaleway:
config.Server.Provider.Scaleway = &scaleway.ScalewayVerifierOptions{}
default: default:
return "", fmt.Errorf("unsupported cloud provider %s", cluster.Spec.GetCloudProvider()) 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/gce/tpm/gcetpmsigner"
"k8s.io/kops/upup/pkg/fi/cloudup/hetzner" "k8s.io/kops/upup/pkg/fi/cloudup/hetzner"
"k8s.io/kops/upup/pkg/fi/cloudup/openstack" "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/local"
"k8s.io/kops/upup/pkg/fi/nodeup/nodetasks" "k8s.io/kops/upup/pkg/fi/nodeup/nodetasks"
"k8s.io/kops/upup/pkg/fi/secrets" "k8s.io/kops/upup/pkg/fi/secrets"
@ -763,14 +764,18 @@ func getNodeConfigFromServers(ctx context.Context, bootConfig *nodeup.BootConfig
return nil, err return nil, err
} }
authenticator = a authenticator = a
case api.CloudProviderDO: case api.CloudProviderDO:
a, err := do.NewAuthenticator() a, err := do.NewAuthenticator()
if err != nil { if err != nil {
return nil, err return nil, err
} }
authenticator = a authenticator = a
case api.CloudProviderScaleway:
a, err := scaleway.NewScalewayAuthenticator()
if err != nil {
return nil, err
}
authenticator = a
default: default:
return nil, fmt.Errorf("unsupported cloud provider for node configuration %s", bootConfig.CloudProvider) return nil, fmt.Errorf("unsupported cloud provider for node configuration %s", bootConfig.CloudProvider)
} }