Skip to content

Commit

Permalink
platform: implement the kola elements for Scaleway
Browse files Browse the repository at this point in the history
Signed-off-by: Mathieu Tortuyaux <[email protected]>
  • Loading branch information
tormath1 committed Apr 9, 2024
1 parent 78fbf31 commit b409e86
Show file tree
Hide file tree
Showing 5 changed files with 277 additions and 1 deletion.
13 changes: 12 additions & 1 deletion cmd/kola/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ var (
kolaOffering string
defaultTargetBoard = sdk.DefaultBoard()
kolaArchitectures = []string{"amd64"}
kolaPlatforms = []string{"aws", "azure", "brightbox", "do", "esx", "external", "gce", "openstack", "equinixmetal", "qemu", "qemu-unpriv"}
kolaPlatforms = []string{"aws", "azure", "brightbox", "do", "esx", "external", "gce", "openstack", "equinixmetal", "qemu", "qemu-unpriv", "scaleway"}
kolaDistros = []string{"cl", "fcos", "rhcos"}
kolaChannels = []string{"alpha", "beta", "stable", "edge", "lts"}
kolaOfferings = []string{"basic", "pro"}
Expand Down Expand Up @@ -233,6 +233,16 @@ func init() {
sv(&kola.BrightboxOptions.ClientSecret, "brightbox-client-secret", "", "Brightbox client secret")
sv(&kola.BrightboxOptions.Image, "brightbox-image", "", "Brightbox image ref")
sv(&kola.BrightboxOptions.ServerType, "brightbox-server-type", "2gb.ssd", "Brightbox server type")

// Scaleway specific options
sv(&kola.ScalewayOptions.OrganizationID, "scaleway-organization-id", "", "Scaleway organization ID")
sv(&kola.ScalewayOptions.ProjectID, "scaleway-project-id", "", "Scaleway organization ID")
sv(&kola.ScalewayOptions.Region, "scaleway-region", "fr-par", "Scaleway region")
sv(&kola.ScalewayOptions.Zone, "scaleway-zone", "fr-par-1", "Scaleway region")
sv(&kola.ScalewayOptions.AccessKey, "scaleway-access-key", "", "Scaleway credentials access key")
sv(&kola.ScalewayOptions.SecretKey, "scaleway-secret-key", "", "Scaleway credentials secret key")
sv(&kola.ScalewayOptions.Image, "scaleway-image", "", "Scaleway image ID")
sv(&kola.ScalewayOptions.InstanceType, "scaleway-instance-type", "DEV1-S", "Scaleway instance type")
}

// Sync up the command line options if there is dependency
Expand All @@ -252,6 +262,7 @@ func syncOptions() error {
kola.EquinixMetalOptions.Board = board
kola.EquinixMetalOptions.GSOptions = &kola.GCEOptions
kola.BrightboxOptions.Board = board
kola.ScalewayOptions.Board = board

validateOption := func(name, item string, valid []string) error {
for _, v := range valid {
Expand Down
5 changes: 5 additions & 0 deletions kola/harness.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import (
esxapi "github.com/flatcar/mantle/platform/api/esx"
gcloudapi "github.com/flatcar/mantle/platform/api/gcloud"
openstackapi "github.com/flatcar/mantle/platform/api/openstack"
scalewayapi "github.com/flatcar/mantle/platform/api/scaleway"
"github.com/flatcar/mantle/platform/conf"
"github.com/flatcar/mantle/platform/machine/aws"
"github.com/flatcar/mantle/platform/machine/azure"
Expand All @@ -57,6 +58,7 @@ import (
"github.com/flatcar/mantle/platform/machine/gcloud"
"github.com/flatcar/mantle/platform/machine/openstack"
"github.com/flatcar/mantle/platform/machine/qemu"
"github.com/flatcar/mantle/platform/machine/scaleway"
"github.com/flatcar/mantle/platform/machine/unprivqemu"
"github.com/flatcar/mantle/system"
)
Expand All @@ -75,6 +77,7 @@ var (
OpenStackOptions = openstackapi.Options{Options: &Options} // glue to set platform options from main
EquinixMetalOptions = equinixmetalapi.Options{Options: &Options} // glue to set platform options from main
QEMUOptions = qemu.Options{Options: &Options} // glue to set platform options from main
ScalewayOptions = scalewayapi.Options{Options: &Options} // glue to set platform options from main

TestParallelism int //glue var to set test parallelism from main
TAPFile string // if not "", write TAP results here
Expand Down Expand Up @@ -246,6 +249,8 @@ func NewFlight(pltfrm string) (flight platform.Flight, err error) {
flight, err = qemu.NewFlight(&QEMUOptions)
case "qemu-unpriv":
flight, err = unprivqemu.NewFlight(&QEMUOptions)
case "scaleway":
flight, err = scaleway.NewFlight(&ScalewayOptions)
default:
err = fmt.Errorf("invalid platform %q", pltfrm)
}
Expand Down
86 changes: 86 additions & 0 deletions platform/machine/scaleway/cluster.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// Copyright The Mantle Authors.
// SPDX-License-Identifier: Apache-2.0

package scaleway

import (
"context"
"crypto/rand"
"fmt"
"os"
"path/filepath"

"github.com/flatcar/mantle/platform"
"github.com/flatcar/mantle/platform/conf"
)

type cluster struct {
*platform.BaseCluster
flight *flight
}

func (bc *cluster) NewMachine(userdata *conf.UserData) (platform.Machine, error) {
conf, err := bc.RenderUserData(userdata, map[string]string{
"$public_ipv4": "${COREOS_CUSTOM_PUBLIC_IPV4}",
"$private_ipv4": "${COREOS_CUSTOM_PRIVATE_IPV4}",
})
if err != nil {
return nil, err
}

// Hack to workaround CT inheritance.
// Can be dropped once we remove CT dependency.
// https://github.com/flatcar/Flatcar/issues/1386
conf.AddSystemdUnitDropin("coreos-metadata.service", "00-custom-metadata.conf", `[Service]
ExecStartPost=/usr/bin/sed -i "s/SCALEWAY/CUSTOM/" /run/metadata/flatcar
ExecStartPost=/usr/bin/sed -i "s/IPV4_PRIVATE/PRIVATE_IPV4/" /run/metadata/flatcar
ExecStartPost=/usr/bin/sed -i "s/IPV4_PUBLIC/PUBLIC_IPV4/" /run/metadata/flatcar
`)

instance, err := bc.flight.api.CreateServer(context.TODO(), bc.vmname(), conf.String())
if err != nil {
return nil, err
}

mach := &machine{
cluster: bc,
mach: instance,
}

mach.dir = filepath.Join(bc.RuntimeConf().OutputDir, mach.ID())
if err := os.Mkdir(mach.dir, 0777); err != nil {
mach.Destroy()
return nil, err
}

confPath := filepath.Join(mach.dir, "ignition.json")
if err := conf.WriteFile(confPath); err != nil {
mach.Destroy()
return nil, err
}

if mach.journal, err = platform.NewJournal(mach.dir); err != nil {
mach.Destroy()
return nil, err
}

if err := platform.StartMachine(mach, mach.journal); err != nil {
mach.Destroy()
return nil, err
}

bc.AddMach(mach)

return mach, nil
}

func (bc *cluster) vmname() string {
b := make([]byte, 5)
rand.Read(b)
return fmt.Sprintf("%s-%x", bc.Name()[0:13], b)
}

func (bc *cluster) Destroy() {
bc.BaseCluster.Destroy()
bc.flight.DelCluster(bc)
}
69 changes: 69 additions & 0 deletions platform/machine/scaleway/flight.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// Copyright The Mantle Authors.
// SPDX-License-Identifier: Apache-2.0

package scaleway

import (
"fmt"

"github.com/coreos/pkg/capnslog"
ctplatform "github.com/flatcar/container-linux-config-transpiler/config/platform"

"github.com/flatcar/mantle/platform"
"github.com/flatcar/mantle/platform/api/scaleway"
)

const (
Platform platform.Name = "scaleway"
)

var (
plog = capnslog.NewPackageLogger("github.com/flatcar/mantle", "platform/machine/scaleway")
)

type flight struct {
*platform.BaseFlight
api *scaleway.API
}

func NewFlight(opts *scaleway.Options) (platform.Flight, error) {
api, err := scaleway.New(opts)
if err != nil {
return nil, fmt.Errorf("creating scaleway API client: %w", err)
}

// TODO: Rework the Base Flight to remove the CT dependency.
base, err := platform.NewBaseFlight(opts.Options, Platform, ctplatform.Custom)
if err != nil {
return nil, fmt.Errorf("creating base flight: %w", err)
}

bf := &flight{
BaseFlight: base,
api: api,
}

return bf, nil
}

// NewCluster creates an instance of a Cluster suitable for spawning
// instances on the Scaleway platform.
func (bf *flight) NewCluster(rconf *platform.RuntimeConfig) (platform.Cluster, error) {
bc, err := platform.NewBaseCluster(bf.BaseFlight, rconf)
if err != nil {
return nil, fmt.Errorf("creating scaleway base cluster: %w", err)
}

c := &cluster{
BaseCluster: bc,
flight: bf,
}

bf.AddCluster(c)

return c, nil
}

func (bf *flight) Destroy() {
bf.BaseFlight.Destroy()
}
105 changes: 105 additions & 0 deletions platform/machine/scaleway/machine.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
// Copyright The Mantle Authors.
// SPDX-License-Identifier: Apache-2.0

package scaleway

import (
"context"

"golang.org/x/crypto/ssh"

"github.com/flatcar/mantle/platform"
"github.com/flatcar/mantle/platform/api/scaleway"
)

type machine struct {
cluster *cluster
mach *scaleway.Server
dir string
journal *platform.Journal
console string
}

// ID returns the ID of the machine.
func (bm *machine) ID() string {
return bm.mach.Server.ID
}

// IP returns the IP of the machine.
func (bm *machine) IP() string {
if bm.mach.Server.PublicIP != nil {
return bm.mach.Server.PublicIP.Address.String()
}

return ""
}

// IP returns the private IP of the machine.
func (bm *machine) PrivateIP() string {
if bm.mach.Server.PrivateIP != nil {
return *bm.mach.Server.PrivateIP
}

return ""
}

// RuntimeConf returns the runtime configuration of the cluster.
func (bm *machine) RuntimeConf() *platform.RuntimeConfig {
return bm.cluster.RuntimeConf()
}

func (bm *machine) SSHClient() (*ssh.Client, error) {
return bm.cluster.SSHClient(bm.IP())
}

func (bm *machine) PasswordSSHClient(user string, password string) (*ssh.Client, error) {
return bm.cluster.PasswordSSHClient(bm.IP(), user, password)
}

func (bm *machine) SSH(cmd string) ([]byte, []byte, error) {
return bm.cluster.SSH(bm, cmd)
}

func (bm *machine) Reboot() error {
return platform.RebootMachine(bm, bm.journal)
}

func (bm *machine) Destroy() {
if err := bm.saveConsole(); err != nil {
plog.Errorf("Error saving console for instance %v: %v", bm.ID(), err)
}

if err := bm.cluster.flight.api.DeleteServer(context.TODO(), bm.ID()); err != nil {
plog.Errorf("deleting server %v: %v", bm.ID(), err)
}

if bm.journal != nil {
bm.journal.Destroy()
}

bm.cluster.DelMach(bm)
}

func (bm *machine) ConsoleOutput() string {
return bm.console
}

func (bm *machine) saveConsole() error {
return nil
}

func (bm *machine) JournalOutput() string {
if bm.journal == nil {
return ""
}

data, err := bm.journal.Read()
if err != nil {
plog.Errorf("Reading journal for instance %v: %v", bm.ID(), err)
}
return string(data)
}

func (bm *machine) Board() string {
return bm.cluster.flight.Options().Board
}

0 comments on commit b409e86

Please sign in to comment.