api: Add BootAdapter with an in-memory impl and UUID matching

This commit is contained in:
Dalton Hubble
2015-12-11 03:07:00 -08:00
parent cb52319285
commit 633f441021
8 changed files with 99 additions and 62 deletions

53
api/adapter.go Normal file
View 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
View 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"`
}

View File

@@ -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)
}

View File

@@ -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
View File

@@ -0,0 +1,11 @@
package api
import (
"net"
)
// MachineAttrs collects machine identifiers and attributes.
type MachineAttrs struct {
UUID string
MAC net.HardwareAddr
}

View File

@@ -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

View File

@@ -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,
}

View File

@@ -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)