From 3d56a6723a173ce2f5b7aab3648675ed96140038 Mon Sep 17 00:00:00 2001 From: Mathieu Tortuyaux Date: Thu, 28 Nov 2024 11:49:41 +0100 Subject: [PATCH] platform/api: add akamai API Signed-off-by: Mathieu Tortuyaux --- platform/api/akamai/api.go | 48 +++++++++++++ platform/api/akamai/instance.go | 117 ++++++++++++++++++++++++++++++++ 2 files changed, 165 insertions(+) create mode 100644 platform/api/akamai/api.go create mode 100644 platform/api/akamai/instance.go diff --git a/platform/api/akamai/api.go b/platform/api/akamai/api.go new file mode 100644 index 000000000..97cf9ce16 --- /dev/null +++ b/platform/api/akamai/api.go @@ -0,0 +1,48 @@ +// Copyright The Mantle Authors. +// SPDX-License-Identifier: Apache-2.0 + +package akamai + +import ( + "net/http" + + "github.com/flatcar/mantle/platform" + "github.com/linode/linodego" + "golang.org/x/oauth2" +) + +// API is a wrapper around Akamai client API +type API struct { + opts *Options + client *linodego.Client +} + +// Options hold the specific Scaleway options. +type Options struct { + *platform.Options + // Token to access Akamai resources. + Token string + // Image is the ID of the Akamai image to deploy. + Image string + // Region where to deploy instances + Region string + // Type of the instance to deploy + Type string +} + +// New returns an Akamai API instance. +func New(opts *Options) (*API, error) { + tokenSource := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: opts.Token}) + oauth2Client := &http.Client{ + Transport: &oauth2.Transport{ + Source: tokenSource, + }, + } + + client := linodego.NewClient(oauth2Client) + + return &API{ + client: &client, + opts: opts, + }, nil +} diff --git a/platform/api/akamai/instance.go b/platform/api/akamai/instance.go new file mode 100644 index 000000000..9d5b1cf79 --- /dev/null +++ b/platform/api/akamai/instance.go @@ -0,0 +1,117 @@ +// Copyright The Mantle Authors. +// SPDX-License-Identifier: Apache-2.0 + +package akamai + +import ( + "context" + "encoding/base64" + "fmt" + "strconv" + "time" + + "github.com/linode/linodego" + + "github.com/coreos/pkg/capnslog" +) + +var ( + plog = capnslog.NewPackageLogger("github.com/flatcar/mantle", "platform/api/scaleway") + tags = []string{"mantle"} +) + +// Server is a wrapper around Akamai instance Server struct. +type Server struct { + *linodego.Instance +} + +func (a *API) CreateServer(ctx context.Context, name, userData string) (*Server, error) { + booted := false + opts := linodego.InstanceCreateOptions{ + Label: name, + Region: a.opts.Region, + Type: a.opts.Type, + Tags: tags, + Metadata: &linodego.InstanceMetadataOptions{ + UserData: base64.StdEncoding.EncodeToString([]byte(userData)), + }, + Booted: &booted, + PrivateIP: true, + } + + instance, err := a.client.CreateInstance(ctx, opts) + if err != nil { + return nil, fmt.Errorf("creating instance: %w", err) + } + + t, err := a.client.GetType(ctx, a.opts.Type) + if err != nil { + return nil, fmt.Errorf("getting instance type: %w", err) + } + + instanceDiskCreateOptions := linodego.InstanceDiskCreateOptions{ + Size: t.Disk, + Label: name, + Image: a.opts.Image, + RootPass: "ThisPasswordIsNotUsedButRequiredByAkamai1234", + } + + disk, err := a.client.CreateInstanceDisk(ctx, instance.ID, instanceDiskCreateOptions) + if err != nil { + return nil, fmt.Errorf("creating instance disk: %w", err) + } + + disk, err = a.client.WaitForInstanceDiskStatus(ctx, instance.ID, disk.ID, linodego.DiskReady, 100) + if err != nil { + return nil, fmt.Errorf("waiting for instance disk to be ready: %w", err) + } + + root := "sda" + instanceConfigCreateOptions := linodego.InstanceConfigCreateOptions{ + Label: "default", + Comments: "Created by Mantle", + Devices: linodego.InstanceConfigDeviceMap{ + SDA: &linodego.InstanceConfigDevice{ + DiskID: disk.ID, + }, + }, + Helpers: &linodego.InstanceConfigHelpers{ + DevTmpFsAutomount: false, + Network: false, + ModulesDep: false, + Distro: false, + UpdateDBDisabled: true, + }, + Kernel: "linode/direct-disk", + RootDevice: &root, + } + + cfg, err := a.client.CreateInstanceConfig(ctx, instance.ID, instanceConfigCreateOptions) + if err != nil { + return nil, fmt.Errorf("creating instance configuration: %w", err) + } + + if err := a.client.BootInstance(ctx, instance.ID, cfg.ID); err != nil { + return nil, fmt.Errorf("booting the instance: %w", err) + } + + return &Server{instance}, nil +} + +func (a *API) DeleteServer(ctx context.Context, id string) error { + instanceID, err := strconv.Atoi(id) + if err != nil { + return fmt.Errorf("converting instance ID to integer: %w", err) + } + + if err := a.client.DeleteInstance(ctx, instanceID); err != nil { + return fmt.Errorf("deleting the instance: %w", err) + } + + return nil +} + +func (a *API) GC(ctx context.Context, gracePeriod time.Duration) error { + plog.Infof("GC: not implemented") + return nil +}