Skip to content

Commit

Permalink
platform: implement the kola elements for Akamai
Browse files Browse the repository at this point in the history
Signed-off-by: Mathieu Tortuyaux <[email protected]>
  • Loading branch information
tormath1 committed Nov 28, 2024
1 parent 9f63475 commit f75ce3b
Show file tree
Hide file tree
Showing 5 changed files with 270 additions and 1 deletion.
9 changes: 8 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", "hetzner", "openstack", "equinixmetal", "qemu", "qemu-unpriv", "scaleway"}
kolaPlatforms = []string{"akamai", "aws", "azure", "brightbox", "do", "esx", "external", "gce", "hetzner", "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 @@ -253,6 +253,12 @@ func init() {
sv(&kola.HetznerOptions.Location, "hetzner-location", "fsn1", "Hetzner location name")
sv(&kola.HetznerOptions.Image, "hetzner-image", "", "Hetzner image ID")
sv(&kola.HetznerOptions.ServerType, "hetzner-server-type", "cx22", "Hetzner instance type")

// Akamai specific options
sv(&kola.AkamaiOptions.Token, "akamai-token", "", "Akamai access token")
sv(&kola.AkamaiOptions.Image, "akamai-image", "", "Akamai image ID")
sv(&kola.AkamaiOptions.Region, "akamai-region", "", "Akamai region")
sv(&kola.AkamaiOptions.Type, "akamai-type", "g6-nanode-1", "Akamai instance type")
}

// Sync up the command line options if there is dependency
Expand All @@ -274,6 +280,7 @@ func syncOptions() error {
kola.BrightboxOptions.Board = board
kola.ScalewayOptions.Board = board
kola.HetznerOptions.Board = board
kola.AkamaiOptions.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 @@ -38,6 +38,7 @@ import (
"github.com/flatcar/mantle/kola/register"
"github.com/flatcar/mantle/kola/torcx"
"github.com/flatcar/mantle/platform"
akamaiapi "github.com/flatcar/mantle/platform/api/akamai"
awsapi "github.com/flatcar/mantle/platform/api/aws"
azureapi "github.com/flatcar/mantle/platform/api/azure"
brightboxapi "github.com/flatcar/mantle/platform/api/brightbox"
Expand All @@ -49,6 +50,7 @@ import (
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/akamai"
"github.com/flatcar/mantle/platform/machine/aws"
"github.com/flatcar/mantle/platform/machine/azure"
"github.com/flatcar/mantle/platform/machine/brightbox"
Expand All @@ -69,6 +71,7 @@ var (
plog = capnslog.NewPackageLogger("github.com/flatcar/mantle", "kola")

Options = platform.Options{}
AkamaiOptions = akamaiapi.Options{Options: &Options} // glue to set platform options from main
AWSOptions = awsapi.Options{Options: &Options} // glue to set platform options from main
AzureOptions = azureapi.Options{Options: &Options} // glue to set platform options from main
BrightboxOptions = brightboxapi.Options{Options: &Options} // glue to set platform options from main
Expand Down Expand Up @@ -234,6 +237,8 @@ type NativeRunner func(funcName string, m platform.Machine) error

func NewFlight(pltfrm string) (flight platform.Flight, err error) {
switch pltfrm {
case "akamai":
flight, err = akamai.NewFlight(&AkamaiOptions)
case "aws":
flight, err = aws.NewFlight(&AWSOptions)
case "azure":
Expand Down
92 changes: 92 additions & 0 deletions platform/machine/akamai/cluster.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
// Copyright The Mantle Authors.
// SPDX-License-Identifier: Apache-2.0

package akamai

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/AKAMAI/CUSTOM/" /run/metadata/flatcar
ExecStartPost=/usr/bin/sed -i "s/PRIVATE_IPV4_0/PRIVATE_IPV4/" /run/metadata/flatcar
ExecStartPost=/usr/bin/sed -i "s/PUBLIC_IPV4_0/PUBLIC_IPV4/" /run/metadata/flatcar
ExecStartPost=/usr/bin/sed -i "s#/32##" /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,
}

// machine to destroy
m := mach
defer func() {
if m != nil {
m.Destroy()
}
}()

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

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

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

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

m = nil
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/akamai/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 akamai

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/akamai"
)

const (
Platform platform.Name = "akamai"
)

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

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

func NewFlight(opts *akamai.Options) (platform.Flight, error) {
api, err := akamai.New(opts)
if err != nil {
return nil, fmt.Errorf("creating akamai 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 Akamai 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 akamai base cluster: %w", err)
}

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

bf.AddCluster(c)

return c, nil
}

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

package akamai

import (
"context"
"strconv"

"golang.org/x/crypto/ssh"

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

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

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

// IP returns the IP of the machine.
func (bm *machine) IP() string {
if len(bm.mach.Instance.IPv4) > 0 {
return bm.mach.Instance.IPv4[0].String()
}

return ""
}

// IP returns the private IP of the machine.
func (bm *machine) PrivateIP() string {
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() {
// TODO: Add "saveConsole" logic here when Akamai API will support fetching the console output.

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) 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 f75ce3b

Please sign in to comment.