-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: Mathieu Tortuyaux <mtortuyaux@microsoft.com>
- Loading branch information
Showing
1 changed file
with
152 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |