mirror of
https://github.com/outbackdingo/matchbox.git
synced 2026-01-27 10:19:35 +00:00
api: Add iPXE initial support
This commit is contained in:
2
Makefile
2
Makefile
@@ -11,7 +11,7 @@ build-aci:
|
||||
./acifile
|
||||
|
||||
run-docker:
|
||||
docker run -p 8080:8080 --name=bcs -v $(shell echo $$PWD)/static:/static dghubble/bcs:latest
|
||||
docker run -p 8080:8080 --name=bcs --rm -v $(shell echo $$PWD)/static:/static dghubble/bcs:latest
|
||||
|
||||
run-rkt:
|
||||
rkt --insecure-options=image run --no-overlay bin/bcs-0.0.1-linux-amd64.aci
|
||||
|
||||
@@ -1,19 +1,12 @@
|
||||
package server
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// CoreOSBootConfig references a CoreOS pproduction image and initrd.
|
||||
var CoreOSBootConfig = &BootConfig{
|
||||
Kernel: "/static/coreos_production_pxe.vmlinuz",
|
||||
Initrd: []string{"/static/coreos_production_pxe_image.cpio.gz"},
|
||||
Cmdline: map[string]interface{}{},
|
||||
}
|
||||
|
||||
// BootConfig describes the boot configuration of a host.
|
||||
type BootConfig struct {
|
||||
// the URL of the kernel boot image
|
||||
// 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"`
|
||||
@@ -21,7 +14,7 @@ type BootConfig struct {
|
||||
Cmdline map[string]interface{} `json:"cmdline"`
|
||||
}
|
||||
|
||||
// A BootConfigProvider provides a mapping from MAC addresses to BootConfigs.
|
||||
// A BootConfigProvider provides a mapping from MAC addresses to BootConfigs.
|
||||
type BootConfigProvider interface {
|
||||
Add(addr string, config *BootConfig)
|
||||
Get(addr string) (*BootConfig, error)
|
||||
@@ -53,4 +46,3 @@ func (p *bootConfigProvider) Get(addr string) (*BootConfig, error) {
|
||||
}
|
||||
return nil, fmt.Errorf("no boot config for %s", addr)
|
||||
}
|
||||
|
||||
58
api/ipxe.go
Normal file
58
api/ipxe.go
Normal file
@@ -0,0 +1,58 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
const ipxeBootstrap = `#!ipxe
|
||||
chain config?uuid=${uuid}
|
||||
`
|
||||
|
||||
var ipxeTemplate = template.Must(template.New("ipxe boot").Parse(`#!ipxe
|
||||
kernel {{.Kernel}} cloud-config-url=cloud/config?uuid=${uuid} {{range $key, $value := .Cmdline}} {{if $value}}{{$key}}={{$value}}{{else}}{{$key}}{{end}}{{end}}
|
||||
initrd {{ range $element := .Initrd }} {{$element}}{{end}}
|
||||
boot
|
||||
`))
|
||||
|
||||
// ipxeMux handles iPXE requests for boot (config) scripts.
|
||||
func ipxeMux(bootConfigs BootConfigProvider) http.Handler {
|
||||
mux := http.NewServeMux()
|
||||
mux.Handle("/ipxe/boot.ipxe", ipxeInspect())
|
||||
mux.Handle("/ipxe/config", ipxeBoot(bootConfigs))
|
||||
return mux
|
||||
}
|
||||
|
||||
// ipxeInspect returns a handler that responds with an iPXE script to gather
|
||||
// client machine data and chain load the real boot script.
|
||||
func ipxeInspect() http.Handler {
|
||||
fn := func(w http.ResponseWriter, req *http.Request) {
|
||||
fmt.Fprintf(w, ipxeBootstrap)
|
||||
}
|
||||
return http.HandlerFunc(fn)
|
||||
}
|
||||
|
||||
// ipxeBoot returns a handler which renders an iPXE boot config script based
|
||||
// on the machine attribtue query parameters.
|
||||
func ipxeBoot(bootConfigs BootConfigProvider) http.Handler {
|
||||
fn := func(w http.ResponseWriter, req *http.Request) {
|
||||
params := req.URL.Query()
|
||||
UUID := params.Get("uuid")
|
||||
bootConfig, err := bootConfigs.Get(UUID)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), 404)
|
||||
return
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
err = ipxeTemplate.Execute(&buf, bootConfig)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), 404)
|
||||
return
|
||||
}
|
||||
buf.WriteTo(w)
|
||||
}
|
||||
return http.HandlerFunc(fn)
|
||||
}
|
||||
24
api/pixiecore.go
Normal file
24
api/pixiecore.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"log"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const pixiecorePath = "/v1/boot/"
|
||||
|
||||
// pixiecoreHandler implements the Pixiecore API Server Spec.
|
||||
func pixiecoreHandler(bootConfigs BootConfigProvider) http.Handler {
|
||||
fn := func(w http.ResponseWriter, req *http.Request) {
|
||||
remainder := strings.TrimPrefix(req.URL.String(), pixiecorePath)
|
||||
bootConfig, err := bootConfigs.Get(remainder)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), 404)
|
||||
return
|
||||
}
|
||||
json.NewEncoder(w).Encode(bootConfig)
|
||||
}
|
||||
return http.HandlerFunc(fn)
|
||||
}
|
||||
29
api/server.go
Normal file
29
api/server.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// Server serves iPXE/Pixiecore boot configs and hosts images.
|
||||
type Server struct {
|
||||
bootConfigs BootConfigProvider
|
||||
}
|
||||
|
||||
// NewServer returns a new Server which uses the given BootConfigProvider.
|
||||
func NewServer(bootConfigs BootConfigProvider) *Server {
|
||||
return &Server{
|
||||
bootConfigs: bootConfigs,
|
||||
}
|
||||
}
|
||||
|
||||
// HTTPHandler returns a HTTP handler for the server.
|
||||
func (s *Server) HTTPHandler() http.Handler {
|
||||
mux := http.NewServeMux()
|
||||
// iPXE
|
||||
mux.Handle("/ipxe/", ipxeMux(s.bootConfigs))
|
||||
// Pixiecore API Server
|
||||
mux.Handle(pixiecorePath, pixiecoreHandler(s.bootConfigs))
|
||||
// Kernel and Initrd Images
|
||||
mux.Handle("/images/", http.StripPrefix("/images/", http.FileServer(http.Dir("static"))))
|
||||
return mux
|
||||
}
|
||||
40
cmd/main.go
40
cmd/main.go
@@ -4,19 +4,47 @@ import (
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/coreos/coreos-baremetal/server"
|
||||
"github.com/coreos/coreos-baremetal/api"
|
||||
)
|
||||
|
||||
const address = ":8080"
|
||||
|
||||
// Example Boot Configs
|
||||
|
||||
var CoreOSStable = &api.BootConfig{
|
||||
Kernel: "http://stable.release.core-os.net/amd64-usr/current/coreos_production_pxe.vmlinuz",
|
||||
Initrd: []string{"http://stable.release.core-os.net/amd64-usr/current/coreos_production_pxe_image.cpio.gz"},
|
||||
Cmdline: map[string]interface{}{},
|
||||
}
|
||||
|
||||
var CoreOSBeta = &api.BootConfig{
|
||||
Kernel: "http://beta.release.core-os.net/amd64-usr/current/coreos_production_pxe.vmlinuz",
|
||||
Initrd: []string{"http://beta.release.core-os.net/amd64-usr/current/coreos_production_pxe_image.cpio.gz"},
|
||||
Cmdline: map[string]interface{}{},
|
||||
}
|
||||
|
||||
var CoreOSAlpha = &api.BootConfig{
|
||||
Kernel: "http://alpha.release.core-os.net/amd64-usr/current/coreos_production_pxe.vmlinuz",
|
||||
Initrd: []string{"http://alpha.release.core-os.net/amd64-usr/current/coreos_production_pxe_image.cpio.gz"},
|
||||
Cmdline: map[string]interface{}{},
|
||||
}
|
||||
|
||||
var CoreOSLocal = &api.BootConfig{
|
||||
Kernel: "/images/kernel/coreos_production_pxe.vmlinuz",
|
||||
Initrd: []string{"/images/initrd/coreos_production_pxe_image.cpio.gz"},
|
||||
Cmdline: map[string]interface{}{},
|
||||
}
|
||||
|
||||
func main() {
|
||||
bootConfigProvider := server.NewBootConfigProvider()
|
||||
bootConfigProvider.Add(server.DefaultAddr, server.CoreOSBootConfig)
|
||||
srv := server.NewServer(bootConfigProvider)
|
||||
h := srv.HTTPHandler()
|
||||
// initial boot configs
|
||||
bootConfigs := api.NewBootConfigProvider()
|
||||
bootConfigs.Add(api.DefaultAddr, CoreOSStable)
|
||||
// api server
|
||||
server := api.NewServer(bootConfigs)
|
||||
h := server.HTTPHandler()
|
||||
log.Printf("Starting boot config server")
|
||||
err := http.ListenAndServe(address, h)
|
||||
if err != nil {
|
||||
log.Fatal("ListenAndServe: ", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"encoding/json"
|
||||
"log"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Server manages boot and cloud configs for hosts by MAC address or UUID.
|
||||
type Server struct {
|
||||
bootConfigProvider BootConfigProvider
|
||||
}
|
||||
|
||||
// NewServer returns a new Server which uses the given BootConfigProvider.
|
||||
func NewServer(bootConfigProvider BootConfigProvider) *Server {
|
||||
return &Server{
|
||||
bootConfigProvider: bootConfigProvider,
|
||||
}
|
||||
}
|
||||
|
||||
// HTTPHandler returns a HTTP handler for the server.
|
||||
func (s *Server) HTTPHandler() http.Handler {
|
||||
mux := http.NewServeMux()
|
||||
// Pixiecore API Server
|
||||
mux.Handle("/v1/boot/", pixiecoreHandler(s.bootConfigProvider))
|
||||
// Kernel and File Server
|
||||
mux.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))
|
||||
return mux
|
||||
}
|
||||
|
||||
// pixiecoreHandler implements the Pixiecore API Server Spec.
|
||||
func pixiecoreHandler(bootConfigs BootConfigProvider) http.Handler {
|
||||
fn := func(w http.ResponseWriter, req *http.Request) {
|
||||
remainder := strings.TrimPrefix(req.URL.String(), "/v1/boot/")
|
||||
log.Printf("render boot config for %s", remainder)
|
||||
bootConfig, err := bootConfigs.Get(remainder)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), 404)
|
||||
return
|
||||
}
|
||||
json.NewEncoder(w).Encode(bootConfig)
|
||||
}
|
||||
return http.HandlerFunc(fn)
|
||||
}
|
||||
Reference in New Issue
Block a user