-
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 <[email protected]>
- Loading branch information
Showing
5 changed files
with
307 additions
and
1 deletion.
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
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
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,84 @@ | ||
// Copyright The Mantle Authors. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
package brightbox | ||
|
||
import ( | ||
"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_OPENSTACK_IPV4_PUBLIC}", | ||
"$private_ipv4": "${COREOS_OPENSTACK_IPV4_LOCAL}", | ||
}) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
var cloudIP string | ||
select { | ||
case i := <-bc.flight.cloudIPs: | ||
cloudIP = i | ||
default: | ||
cloudIP = "" | ||
} | ||
|
||
instance, err := bc.flight.api.CreateServer(bc.vmname(), conf.String(), cloudIP) | ||
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, "user-data") | ||
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) | ||
} |
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,79 @@ | ||
// Copyright The Mantle Authors. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
package brightbox | ||
|
||
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/brightbox" | ||
) | ||
|
||
const ( | ||
Platform platform.Name = "brightbox" | ||
) | ||
|
||
var ( | ||
plog = capnslog.NewPackageLogger("github.com/flatcar/mantle", "platform/machine/brightbox") | ||
) | ||
|
||
type flight struct { | ||
*platform.BaseFlight | ||
api *brightbox.API | ||
cloudIPs chan string | ||
} | ||
|
||
func NewFlight(opts *brightbox.Options) (platform.Flight, error) { | ||
api, err := brightbox.New(opts) | ||
if err != nil { | ||
return nil, fmt.Errorf("creating brightbox API client: %w", err) | ||
} | ||
|
||
base, err := platform.NewBaseFlight(opts.Options, Platform, ctplatform.OpenStackMetadata) | ||
if err != nil { | ||
return nil, fmt.Errorf("creating base flight: %w", err) | ||
} | ||
|
||
bf := &flight{ | ||
BaseFlight: base, | ||
api: api, | ||
// Current CloudIPs limit is 5. | ||
cloudIPs: make(chan string, 5), | ||
} | ||
|
||
return bf, nil | ||
} | ||
|
||
// NewCluster creates an instance of a Cluster suitable for spawning | ||
// instances on the OpenStack 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 brightbox base cluster: %w", err) | ||
} | ||
|
||
c := &cluster{ | ||
BaseCluster: bc, | ||
flight: bf, | ||
} | ||
|
||
bf.AddCluster(c) | ||
|
||
return c, nil | ||
} | ||
|
||
func (bf *flight) Destroy() { | ||
// Clean the provisioned cloud IPs. | ||
close(bf.cloudIPs) | ||
for id := range bf.cloudIPs { | ||
if err := bf.api.DeleteCloudIP(id); err != nil { | ||
plog.Errorf("deleting cloud IP %s: %v", id, err) | ||
} | ||
} | ||
|
||
bf.BaseFlight.Destroy() | ||
} |
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,132 @@ | ||
// Copyright The Mantle Authors. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
package brightbox | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
"path/filepath" | ||
|
||
"golang.org/x/crypto/ssh" | ||
|
||
"github.com/flatcar/mantle/platform" | ||
"github.com/flatcar/mantle/platform/api/brightbox" | ||
) | ||
|
||
type machine struct { | ||
cluster *cluster | ||
mach *brightbox.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. | ||
// The machine should only get one "cloud" IP. | ||
func (bm *machine) IP() string { | ||
if bm.mach.Server != nil && len(bm.mach.Server.CloudIPs) >= 1 { | ||
return bm.mach.Server.CloudIPs[0].PublicIPv4 | ||
} | ||
|
||
return "" | ||
} | ||
|
||
func (bm *machine) PrivateIP() string { | ||
// Return the first IPv4 address, assuming it's the private one. | ||
for _, iface := range bm.mach.Server.Interfaces { | ||
return iface.IPv4Address | ||
} | ||
|
||
// Otherwise returns the public one in last resort. | ||
return bm.IP() | ||
} | ||
|
||
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() { | ||
// Keep the cloud IP ID to add it to the available pool after | ||
// machine deletion. | ||
var cloudIP string | ||
if bm.mach.Server != nil && len(bm.mach.Server.CloudIPs) >= 0 { | ||
cloudIP = bm.mach.Server.CloudIPs[0].ID | ||
} | ||
|
||
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(bm.ID()); err != nil { | ||
plog.Errorf("deleting server %v: %v", bm.ID(), err) | ||
} | ||
|
||
if cloudIP != "" { | ||
bm.cluster.flight.cloudIPs <- cloudIP | ||
} | ||
|
||
if bm.journal != nil { | ||
bm.journal.Destroy() | ||
} | ||
|
||
bm.cluster.DelMach(bm) | ||
} | ||
|
||
func (bm *machine) ConsoleOutput() string { | ||
return bm.console | ||
} | ||
|
||
func (bm *machine) saveConsole() error { | ||
var err error | ||
bm.console, err = bm.cluster.flight.api.GetConsoleOutput(bm.ID()) | ||
if err != nil { | ||
return fmt.Errorf("Error retrieving console log for %v: %v", bm.ID(), err) | ||
} | ||
|
||
path := filepath.Join(bm.dir, "console.txt") | ||
f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0644) | ||
if err != nil { | ||
return err | ||
} | ||
defer f.Close() | ||
f.WriteString(bm.console) | ||
|
||
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 | ||
} |