mirror of
https://github.com/outbackdingo/matchbox.git
synced 2026-01-27 10:19:35 +00:00
api: Add BootAdapter with an in-memory impl and UUID matching
This commit is contained in:
53
api/adapter.go
Normal file
53
api/adapter.go
Normal file
@@ -0,0 +1,53 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
)
|
||||
|
||||
// MapBootAdapter maps MachineAttrs to BootConfigs using an in-memory map.
|
||||
type MapBootAdapter struct {
|
||||
uuids map[string]*BootConfig
|
||||
macs map[string]*BootConfig
|
||||
fallback *BootConfig
|
||||
}
|
||||
|
||||
// NewMapBootAdapter returns a new in-memory BootAdapter.
|
||||
func NewMapBootAdapter() *MapBootAdapter {
|
||||
return &MapBootAdapter{
|
||||
uuids: make(map[string]*BootConfig),
|
||||
macs: make(map[string]*BootConfig),
|
||||
}
|
||||
}
|
||||
|
||||
// Get returns the BootConfig for the machine with the given attributes.
|
||||
// Matches are searched in priority order: UUID, MAC address, default.
|
||||
func (a *MapBootAdapter) Get(attrs MachineAttrs) (*BootConfig, error) {
|
||||
if config, ok := a.uuids[attrs.UUID]; ok {
|
||||
return config, nil
|
||||
}
|
||||
if config, ok := a.macs[attrs.MAC.String()]; ok {
|
||||
return config, nil
|
||||
}
|
||||
if a.fallback != nil {
|
||||
return a.fallback, nil
|
||||
}
|
||||
return nil, fmt.Errorf("no matching boot configuration")
|
||||
}
|
||||
|
||||
// AddUUID adds a BootConfig for the machine with the given UUID.
|
||||
func (a *MapBootAdapter) AddUUID(uuid string, config *BootConfig) {
|
||||
a.uuids[uuid] = config
|
||||
}
|
||||
|
||||
// AddMAC adds a BootConfig for the machine with NIC with the given MAC
|
||||
// address.
|
||||
func (a *MapBootAdapter) AddMAC(mac net.HardwareAddr, config *BootConfig) {
|
||||
a.macs[mac.String()] = config
|
||||
}
|
||||
|
||||
// SetDefault sets the default or fallback BootConfig to use if no machine
|
||||
// attribute matches are found.
|
||||
func (a *MapBootAdapter) SetDefault(config *BootConfig) {
|
||||
a.fallback = config
|
||||
}
|
||||
18
api/boot.go
Normal file
18
api/boot.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package api
|
||||
|
||||
// A BootAdapter maps MachineAttrs to a BootConfig which should be used.
|
||||
type BootAdapter interface {
|
||||
// Get returns the BootConfig to boot the machine with the given attributes
|
||||
Get(attrs MachineAttrs) (*BootConfig, error)
|
||||
}
|
||||
|
||||
// BootConfig defines the kernel image, kernel options, and initrds to boot
|
||||
// on a client machine.
|
||||
type BootConfig struct {
|
||||
// the URL of the kernel boot image
|
||||
Kernel string `json:"kernel"`
|
||||
// the initrd URLs which will be flattened into a single filesystem
|
||||
Initrd []string `json:"initrd"`
|
||||
// command line arguments to the kernel
|
||||
Cmdline map[string]interface{} `json:"cmdline"`
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// BootConfig describes the boot configuration of a host.
|
||||
type BootConfig struct {
|
||||
// the URL of the kernel boot image
|
||||
Kernel string `json:"kernel"`
|
||||
// the initrd URLs which will be flattened into a single filesystem
|
||||
Initrd []string `json:"initrd"`
|
||||
// command line arguments to the kernel
|
||||
Cmdline map[string]interface{} `json:"cmdline"`
|
||||
}
|
||||
|
||||
// A BootConfigProvider provides a mapping from MAC addresses to BootConfigs.
|
||||
type BootConfigProvider interface {
|
||||
Add(addr string, config *BootConfig)
|
||||
Get(addr string) (*BootConfig, error)
|
||||
}
|
||||
|
||||
// NewBootConfig returns a new memory map BootConfigProvider.
|
||||
func NewBootConfigProvider() BootConfigProvider {
|
||||
return &bootConfigProvider{
|
||||
mac2boot: make(map[string]*BootConfig),
|
||||
}
|
||||
}
|
||||
|
||||
const DefaultAddr = "default"
|
||||
|
||||
// bootConfigProvider implements a MAC address to Boot config map in memory.
|
||||
type bootConfigProvider struct {
|
||||
mac2boot map[string]*BootConfig
|
||||
}
|
||||
|
||||
func (p *bootConfigProvider) Add(addr string, config *BootConfig) {
|
||||
p.mac2boot[addr] = config
|
||||
}
|
||||
|
||||
func (p *bootConfigProvider) Get(addr string) (*BootConfig, error) {
|
||||
if config, ok := p.mac2boot[addr]; ok {
|
||||
return config, nil
|
||||
} else if config, ok := p.mac2boot[DefaultAddr]; ok {
|
||||
return config, nil
|
||||
}
|
||||
return nil, fmt.Errorf("no boot config for %s", addr)
|
||||
}
|
||||
@@ -18,7 +18,7 @@ boot
|
||||
`))
|
||||
|
||||
// ipxeMux handles iPXE requests for boot (config) scripts.
|
||||
func ipxeMux(bootConfigs BootConfigProvider) http.Handler {
|
||||
func ipxeMux(bootConfigs BootAdapter) http.Handler {
|
||||
mux := http.NewServeMux()
|
||||
mux.Handle("/ipxe/boot.ipxe", ipxeInspect())
|
||||
mux.Handle("/ipxe/config", ipxeBoot(bootConfigs))
|
||||
@@ -36,11 +36,11 @@ func ipxeInspect() http.Handler {
|
||||
|
||||
// ipxeBoot returns a handler which renders an iPXE boot config script based
|
||||
// on the machine attribtue query parameters.
|
||||
func ipxeBoot(bootConfigs BootConfigProvider) http.Handler {
|
||||
func ipxeBoot(bootConfigs BootAdapter) http.Handler {
|
||||
fn := func(w http.ResponseWriter, req *http.Request) {
|
||||
params := req.URL.Query()
|
||||
UUID := params.Get("uuid")
|
||||
bootConfig, err := bootConfigs.Get(UUID)
|
||||
attrs := MachineAttrs{UUID: params.Get("uuid")}
|
||||
bootConfig, err := bootConfigs.Get(attrs)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), 404)
|
||||
return
|
||||
|
||||
11
api/machine.go
Normal file
11
api/machine.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"net"
|
||||
)
|
||||
|
||||
// MachineAttrs collects machine identifiers and attributes.
|
||||
type MachineAttrs struct {
|
||||
UUID string
|
||||
MAC net.HardwareAddr
|
||||
}
|
||||
@@ -2,18 +2,21 @@ package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const pixiecorePath = "/v1/boot/"
|
||||
|
||||
// pixiecoreHandler implements the Pixiecore API Server Spec.
|
||||
func pixiecoreHandler(bootConfigs BootConfigProvider) http.Handler {
|
||||
// pixiecoreHandler returns a handler that renders Boot Configs as JSON to
|
||||
// implement the Pixiecore API specification.
|
||||
// https://github.com/danderson/pixiecore/blob/master/README.api.md
|
||||
func pixiecoreHandler(bootConfigs BootAdapter) http.Handler {
|
||||
fn := func(w http.ResponseWriter, req *http.Request) {
|
||||
remainder := strings.TrimPrefix(req.URL.String(), pixiecorePath)
|
||||
bootConfig, err := bootConfigs.Get(remainder)
|
||||
attrs := MachineAttrs{MAC: net.HardwareAddr(remainder)}
|
||||
bootConfig, err := bootConfigs.Get(attrs)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), 404)
|
||||
return
|
||||
|
||||
@@ -6,11 +6,11 @@ import (
|
||||
|
||||
// Server serves iPXE/Pixiecore boot configs and hosts images.
|
||||
type Server struct {
|
||||
bootConfigs BootConfigProvider
|
||||
bootConfigs BootAdapter
|
||||
}
|
||||
|
||||
// NewServer returns a new Server which uses the given BootConfigProvider.
|
||||
func NewServer(bootConfigs BootConfigProvider) *Server {
|
||||
// NewServer returns a new Server which uses the given BootAdapter.
|
||||
func NewServer(bootConfigs BootAdapter) *Server {
|
||||
return &Server{
|
||||
bootConfigs: bootConfigs,
|
||||
}
|
||||
|
||||
@@ -37,10 +37,10 @@ var CoreOSLocal = &api.BootConfig{
|
||||
|
||||
func main() {
|
||||
// initial boot configs
|
||||
bootConfigs := api.NewBootConfigProvider()
|
||||
bootConfigs.Add(api.DefaultAddr, CoreOSStable)
|
||||
bootAdapter := api.NewMapBootAdapter()
|
||||
bootAdapter.SetDefault(CoreOSStable)
|
||||
// api server
|
||||
server := api.NewServer(bootConfigs)
|
||||
server := api.NewServer(bootAdapter)
|
||||
h := server.HTTPHandler()
|
||||
log.Printf("Starting boot config server")
|
||||
err := http.ListenAndServe(address, h)
|
||||
|
||||
Reference in New Issue
Block a user