blob: 67231c06bda3135109eb505b42415222e15d7675 [file] [log] [blame]
package main
import (
"context"
"encoding/json"
"flag"
"fmt"
"io"
"net/http"
"net/url"
"os"
"strings"
"k8s.io/klog/v2"
"source.monogon.dev/cloud/bmaas/bmdb"
"source.monogon.dev/cloud/bmaas/bmdb/model"
"source.monogon.dev/cloud/bmaas/bmdb/webug"
"source.monogon.dev/cloud/lib/component"
"source.monogon.dev/cloud/shepherd"
"source.monogon.dev/cloud/shepherd/manager"
clicontext "source.monogon.dev/metropolis/cli/pkg/context"
)
type Config struct {
Component component.ComponentConfig
BMDB bmdb.BMDB
WebugConfig webug.Config
InitializerConfig manager.InitializerConfig
ProvisionerConfig manager.ProvisionerConfig
RecovererConfig manager.RecovererConfig
SSHConfig sshConfig
DeviceListSource string
ProviderType model.Provider
}
// TODO(q3k): factor this out to BMDB library?
func runtimeInfo() string {
hostname, _ := os.Hostname()
if hostname == "" {
hostname = "UNKNOWN"
}
return fmt.Sprintf("host %s", hostname)
}
func (c *Config) RegisterFlags() {
c.Component.RegisterFlags("shepherd")
c.BMDB.ComponentName = "shepherd-mini"
c.BMDB.RuntimeInfo = runtimeInfo()
c.BMDB.Database.RegisterFlags("bmdb")
c.WebugConfig.RegisterFlags()
c.InitializerConfig.RegisterFlags()
c.ProvisionerConfig.RegisterFlags()
c.RecovererConfig.RegisterFlags()
c.SSHConfig.RegisterFlags()
flag.StringVar(&c.DeviceListSource, "mini_device_list_url", "", "The url from where to fetch the device list. For local paths use file:// as scheme")
flag.Func("mini_provider", "The provider this mini shepherd should emulate. Supported values are: lumen,equinix", func(s string) error {
switch s {
case strings.ToLower(string(model.ProviderEquinix)):
c.ProviderType = model.ProviderEquinix
case strings.ToLower(string(model.ProviderLumen)):
c.ProviderType = model.ProviderLumen
default:
return fmt.Errorf("invalid provider name")
}
return nil
})
}
type deviceList []machine
func (dl deviceList) asMap() map[shepherd.ProviderID]machine {
mm := make(map[shepherd.ProviderID]machine)
for _, m := range dl {
mm[m.ProviderID] = m
}
return mm
}
func fetchDeviceList(s string) (deviceList, error) {
var r io.Reader
u, err := url.Parse(s)
if err != nil {
return nil, fmt.Errorf("failed parsing device list url: %v", err)
}
if u.Scheme != "file" {
resp, err := http.Get(u.String())
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("invalid status code: %d != %v", http.StatusOK, resp.StatusCode)
}
r = resp.Body
} else {
f, err := os.Open(u.Path)
if err != nil {
return nil, err
}
defer f.Close()
r = f
}
var d deviceList
dec := json.NewDecoder(r)
dec.DisallowUnknownFields()
if err := dec.Decode(&d); err != nil {
return nil, err
}
klog.Infof("Fetched device list with %d entries", len(d))
return d, nil
}
func main() {
var c Config
c.RegisterFlags()
flag.Parse()
if flag.NArg() > 0 {
klog.Exitf("unexpected positional arguments: %v", flag.Args())
}
registry := c.Component.PrometheusRegistry()
c.BMDB.EnableMetrics(registry)
ctx := clicontext.WithInterrupt(context.Background())
c.Component.StartPrometheus(ctx)
conn, err := c.BMDB.Open(true)
if err != nil {
klog.Exitf("Failed to open BMDB connection: %v", err)
}
sshClient, err := c.SSHConfig.NewClient()
if err != nil {
klog.Exitf("Failed to create SSH client: %v", err)
}
if c.DeviceListSource == "" {
klog.Exitf("-mini_device_list_source must be set")
}
list, err := fetchDeviceList(c.DeviceListSource)
if err != nil {
klog.Exitf("Failed to fetch device list: %v", err)
}
mini := &provider{
providerType: c.ProviderType,
machines: list.asMap(),
}
provisioner, err := manager.NewProvisioner(mini, c.ProvisionerConfig)
if err != nil {
klog.Exitf("%v", err)
}
initializer, err := manager.NewInitializer(mini, sshClient, c.InitializerConfig)
if err != nil {
klog.Exitf("%v", err)
}
go func() {
err = provisioner.Run(ctx, conn)
if err != nil {
klog.Exit(err)
}
}()
go func() {
err = manager.RunControlLoop(ctx, conn, initializer)
if err != nil {
klog.Exit(err)
}
}()
go func() {
if err := c.WebugConfig.Start(ctx, conn); err != nil && err != ctx.Err() {
klog.Exitf("Failed to start webug: %v", err)
}
}()
<-ctx.Done()
}