diff --git a/pkg/apis/kops/v1alpha2/cluster.go b/pkg/apis/kops/v1alpha2/cluster.go index de495ace50..1d6ed7fbcc 100644 --- a/pkg/apis/kops/v1alpha2/cluster.go +++ b/pkg/apis/kops/v1alpha2/cluster.go @@ -218,12 +218,6 @@ type ClusterSpec struct { Karpenter *KarpenterConfig `json:"karpenter,omitempty"` // PodIdentityWebhook determines the EKS Pod Identity Webhook configuration. PodIdentityWebhook *PodIdentityWebhookConfig `json:"podIdentityWebhook,omitempty"` - // Scaleway configures the Scaleway cloud provider. - Scaleway *ScalewaySpec `json:"scaleway,omitempty"` -} - -// ScalewaySpec configures the Scaleway cloud provider -type ScalewaySpec struct { } // PodIdentityWebhookConfig configures an EKS Pod Identity Webhook. diff --git a/protokube/cmd/protokube/main.go b/protokube/cmd/protokube/main.go index 4c93afb134..05bc69a9f2 100644 --- a/protokube/cmd/protokube/main.go +++ b/protokube/cmd/protokube/main.go @@ -141,6 +141,14 @@ func run() error { } cloudProvider = azureVolumes + } else if cloud == "scaleway" { + scwCloudProvider, err := protokube.NewScwCloudProvider() + if err != nil { + klog.Errorf("Error initializing Scaleway: %q", err) + os.Exit(1) + } + cloudProvider = scwCloudProvider + } else { klog.Errorf("Unknown cloud %q", cloud) os.Exit(1) diff --git a/protokube/pkg/gossip/scaleway/seeds.go b/protokube/pkg/gossip/scaleway/seeds.go new file mode 100644 index 0000000000..f00d942274 --- /dev/null +++ b/protokube/pkg/gossip/scaleway/seeds.go @@ -0,0 +1,71 @@ +/* +Copyright 2022 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" + "github.com/scaleway/scaleway-sdk-go/scw" + "k8s.io/klog/v2" + "k8s.io/kops/protokube/pkg/gossip" + "k8s.io/kops/upup/pkg/fi/cloudup/scaleway" +) + +type SeedProvider struct { + scwClient *scw.Client + tag string +} + +var _ gossip.SeedProvider = &SeedProvider{} + +func NewSeedProvider(scwClient *scw.Client, clusterName string) (*SeedProvider, error) { + return &SeedProvider{ + scwClient: scwClient, + tag: clusterName, + }, nil +} + +func (p *SeedProvider) GetSeeds() ([]string, error) { + var seeds []string + + instanceAPI := instance.NewAPI(p.scwClient) + zone, ok := p.scwClient.GetDefaultZone() + if !ok { + return nil, fmt.Errorf("could not determine default region from client") + } + servers, err := instanceAPI.ListServers(&instance.ListServersRequest{ + Zone: zone, + Tags: []string{fmt.Sprintf("%s=%s", scaleway.TagClusterName, p.tag)}, + }, scw.WithAllPages()) + if err != nil { + return nil, fmt.Errorf("failed to get matching servers: %s", err) + } + + for _, server := range servers.Servers { + if server.PrivateIP == nil || *server.PrivateIP == "" { + klog.Warningf("failed to find private ip of the server %s(%s)", server.Name, server.ID) + continue + } + + klog.V(4).Infof("Appending gossip seed %s(%s): %q", server.Name, server.ID, *server.PrivateIP) + seeds = append(seeds, *server.PrivateIP) + } + + klog.V(4).Infof("Get seeds function done now") + return seeds, nil +} diff --git a/protokube/pkg/protokube/scaleway_volumes.go b/protokube/pkg/protokube/scaleway_volumes.go new file mode 100644 index 0000000000..77f43671c4 --- /dev/null +++ b/protokube/pkg/protokube/scaleway_volumes.go @@ -0,0 +1,108 @@ +/* +Copyright 2022 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 protokube + +import ( + "fmt" + "net" + "strings" + + "github.com/scaleway/scaleway-sdk-go/api/instance/v1" + "github.com/scaleway/scaleway-sdk-go/scw" + "k8s.io/klog/v2" + kopsv "k8s.io/kops" + "k8s.io/kops/protokube/pkg/gossip" + gossipscw "k8s.io/kops/protokube/pkg/gossip/scaleway" + "k8s.io/kops/upup/pkg/fi/cloudup/scaleway" +) + +// ScwCloudProvider defines the Scaleway Cloud volume implementation. +type ScwCloudProvider struct { + scwClient *scw.Client + server *instance.Server + serverIP net.IP +} + +var _ CloudProvider = &ScwCloudProvider{} + +// NewScwCloudProvider returns a new Scaleway Cloud volume provider. +func NewScwCloudProvider() (*ScwCloudProvider, error) { + metadataAPI := instance.NewMetadataAPI() + metadata, err := metadataAPI.GetMetadata() + if err != nil { + return nil, fmt.Errorf("failed to retrieve server metadata: %s", err) + } + + serverID := metadata.ID + klog.V(4).Infof("Found ID of the running server: %v", serverID) + + zoneID := metadata.Location.ZoneID + zone, err := scw.ParseZone(zoneID) + if err != nil { + return nil, fmt.Errorf("unable to parse Scaleway zone: %s", err) + } + klog.V(4).Infof("Found zone of the running server: %v", zone) + + privateIP := metadata.PrivateIP + klog.V(4).Infof("Found first private net IP of the running server: %q", privateIP) + + scwClient, err := scw.NewClient( + scw.WithUserAgent(scaleway.KopsUserAgentPrefix+kopsv.Version), + scw.WithEnv(), + scw.WithDefaultZone(zone), + ) + if err != nil { + return nil, fmt.Errorf("error creating client: %w", err) + } + + instanceAPI := instance.NewAPI(scwClient) + server, err := instanceAPI.GetServer(&instance.GetServerRequest{ + ServerID: serverID, + Zone: zone, + }) + if err != nil || server == nil { + return nil, fmt.Errorf("failed to get the running server: %s", err) + } + klog.V(4).Infof("Found the running server: %q", server.Server.Name) + + s := &ScwCloudProvider{ + scwClient: scwClient, + server: server.Server, + serverIP: net.IP(privateIP), + } + + return s, nil +} + +func (s *ScwCloudProvider) InstanceID() string { + return fmt.Sprintf("%s-%s", s.server.Name, s.server.ID) +} + +func (s ScwCloudProvider) InstanceInternalIP() net.IP { + return s.serverIP +} + +func (s *ScwCloudProvider) GossipSeeds() (gossip.SeedProvider, error) { + for _, tag := range s.server.Tags { + if !strings.HasPrefix(tag, scaleway.TagClusterName) { + continue + } + clusterName := strings.TrimPrefix(tag, scaleway.TagClusterName+"=") + return gossipscw.NewSeedProvider(s.scwClient, clusterName) + } + return nil, fmt.Errorf("failed to find cluster name label for running server: %v", s.server.Tags) +} diff --git a/upup/pkg/fi/cloudup/scaleway/cloud.go b/upup/pkg/fi/cloudup/scaleway/cloud.go new file mode 100644 index 0000000000..d3ea38c4fd --- /dev/null +++ b/upup/pkg/fi/cloudup/scaleway/cloud.go @@ -0,0 +1,22 @@ +/* +Copyright 2022 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 + +const ( + TagClusterName = "kops.k8s.io/cluster" + KopsUserAgentPrefix = "kubernetes-kops/" +)