Skip to content

Commit

Permalink
api/brightbox: initial commit
Browse files Browse the repository at this point in the history
Signed-off-by: Mathieu Tortuyaux <mtortuyaux@microsoft.com>
  • Loading branch information
tormath1 committed Nov 15, 2023
1 parent 930a7ef commit c7aab09
Showing 1 changed file with 152 additions and 0 deletions.
152 changes: 152 additions & 0 deletions platform/api/brightbox/api.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
// Copyright The Mantle Authors.
// SPDX-License-Identifier: Apache-2.0
package brightbox

import (
"context"
"encoding/base64"
"fmt"
"time"

brightbox "github.com/brightbox/gobrightbox/v2"
"github.com/brightbox/gobrightbox/v2/clientcredentials"
"github.com/brightbox/gobrightbox/v2/enums/arch"
"github.com/brightbox/gobrightbox/v2/enums/imagestatus"

"github.com/coreos/pkg/capnslog"
"github.com/flatcar/mantle/platform"
"github.com/flatcar/mantle/util"
)

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

type Options struct {
*platform.Options

// ClientID is the ID of the API client.
ClientID string
// ClientSecret is the secret of the API client.
ClientSecret string
// Image is the image to deploy.
Image string
}

type API struct {
client *brightbox.Client
opts *Options
}

type Server struct{ *brightbox.Server }

func New(opts *Options) (*API, error) {
// Setup OAuth2 authentication
conf := &clientcredentials.Config{
ID: opts.ClientID,
Secret: opts.ClientSecret,
}

ctx := context.Background()

client, err := brightbox.Connect(ctx, conf)
if err != nil {
return nil, fmt.Errorf("connecting to Brightbox: %w", err)
}

return &API{
client: client,
opts: opts,
}, nil
}

func (a *API) AddKey(name, key string) error {
return nil
}

// CreateServer using the Brightbox API.
// NOTE: SSH Key ID is not used because Brightbox does not allow to create SSH keys for an account.
func (a *API) CreateServer(name, sshKeyID, userdata string) (*Server, error) {
// TODO: Create a dedicated server group.

// Not in the spec, but userdata needs to be base64 encoded.
userdata = base64.StdEncoding.EncodeToString([]byte(userdata))

// TODO: Investigate on how to assign a cloud IP on creation.
// This option creates a new IP for each new VM.
cloudIP := true
s, err := a.client.CreateServer(context.TODO(), brightbox.ServerOptions{
Image: &a.opts.Image,
Name: &name,
UserData: &userdata,
CloudIP: &cloudIP,
})
if err != nil {
return nil, fmt.Errorf("creating server from API: %w", err)
}

return &Server{s}, nil
}

func (a *API) DeleteKey(name string) error {
return nil
}

// DeleteImage will remove the image from Brightbox.
func (a *API) DeleteImage(id string) error {
if _, err := a.client.DestroyImage(context.TODO(), id); err != nil {
return fmt.Errorf("destroying image from API: %w", err)
}

return nil
}

func (a *API) DeleteServer(id string) error {
if _, err := a.client.DestroyServer(context.TODO(), id); err != nil {
return fmt.Errorf("destroying server from API: %w", err)
}

return nil
}

func (a *API) GC(gracePeriod time.Duration) error {
return nil
}

func (a *API) GetConsoleOutput(id string) (string, error) {
// NOTE: There is no way to get console output from the API.
// A workaround would be the fetch the console_url + console_token to read from this
// endpoint.
return "", nil
}

// UploadImage will uploade an image from the URL on Brightbox and wait for it to become
// available.
func (a *API) UploadImage(name, URL string) (string, error) {
defaultUsername := "core"

img, err := a.client.CreateImage(context.TODO(), brightbox.ImageOptions{
Name: &name,
URL: URL,
Username: &defaultUsername,
Arch: arch.X86_64,
})
if err != nil {
return "", fmt.Errorf("creating image from API: %w", err)
}

// It usually takes around 20 seconds to extract the image.
if err := util.WaitUntilReady(1*time.Minute, 5*time.Second, func() (bool, error) {
image, err := a.client.Image(context.TODO(), img.ID)
if err != nil {
return false, fmt.Errorf("getting image status: %w", err)
}

return image.Status == imagestatus.Available, nil
}); err != nil {
a.DeleteImage(img.ID)
return "", fmt.Errorf("getting image active: %w", err)
}

return img.ID, nil
}

0 comments on commit c7aab09

Please sign in to comment.