Skip to content

Commit

Permalink
clean up requests files, add view for oci cache
Browse files Browse the repository at this point in the history
  • Loading branch information
jeefy committed Dec 21, 2023
1 parent 37d3284 commit ca770cb
Show file tree
Hide file tree
Showing 8 changed files with 343 additions and 197 deletions.
1 change: 1 addition & 0 deletions pkg/http/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ func StartHTTP() {
myHandler.HandleFunc("/unregister", handleUnregistrationRequest)
myHandler.HandleFunc("/booty.json", handleDataRequest)
myHandler.HandleFunc("/info", handleInfoRequest)
myHandler.HandleFunc("/registry", handleRegistryRequest)
myHandler.Handle("/data/", http.StripPrefix("/data/", http.FileServer(http.Dir(viper.GetString(config.DataDir)))))
myHandler.Handle("/ui/", http.StripPrefix("/ui/", http.FileServer(http.Dir("./web/dist"))))

Expand Down
160 changes: 160 additions & 0 deletions pkg/http/ignition.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
package http

import (
"bytes"
"encoding/json"
"fmt"
"log"
"net"
"net/http"
"text/template"

butaneConfig "github.com/coreos/butane/config"
butaneCommon "github.com/coreos/butane/config/common"
coreOSType "github.com/coreos/ignition/v2/config/v3_5_experimental/types"
"github.com/google/go-containerregistry/pkg/crane"
"github.com/j-keck/arping"
"github.com/jeefy/booty/pkg/config"
"github.com/jeefy/booty/pkg/hardware"
"github.com/spf13/viper"
)

func handleIgnitionRequest(w http.ResponseWriter, r *http.Request) {
// If we don't identify the host, tell FlatCar to reboot
// Reboot the host till we identify it
// Cool so, we want to have logic based around a recognized MAC address
// Therefore what we need to do is collect the MAC address

log.Printf("Ignition Request URI: %s", r.RequestURI)

macAddress := ""
ip, _, err := net.SplitHostPort(r.RemoteAddr)
if err != nil {
log.Printf("Error splitting user ip: %v is not IP:port", r.RemoteAddr)
}
remoteIP := net.ParseIP(ip)

if hwAddr, _, err := arping.Ping(remoteIP); err != nil {
log.Printf("Error with ARP request: %s", err)
} else {
if viper.GetBool("debug") {
log.Printf("Mac address from ARP `%s`", macAddress)
}
macAddress = hwAddr.String()
}

if r.URL.Query().Get("mac") != "" {
macAddress = r.URL.Query().Get("mac")
if viper.GetBool("debug") {
log.Printf("Mac address url override `%s`", macAddress)
}
}

if viper.GetBool("debug") {
log.Printf("Using mac address `%s`", macAddress)
}
host := hardware.GetMacAddress(macAddress)

var tpl bytes.Buffer

templateData := struct {
JoinString string
ServerIP string
OSTreeImage string
Hostname string
}{
JoinString: viper.GetString(config.JoinString),
ServerIP: fmt.Sprintf("%s:%s", viper.GetString(config.ServerIP), viper.GetString(config.ServerHttpPort)),
}

ignitionFile := viper.GetString(config.IgnitionFile)
if host != nil {
if host.IgnitionFile != "" {
ignitionFile = host.IgnitionFile
}
templateData.Hostname = host.Hostname

// Ingelligently rewrite what image to send to the client depending on cache state
// First, default to the remote image location
templateData.OSTreeImage = host.OSTreeImage

// If we have a local image, use that instead
localImage := fmt.Sprintf("%s:%s/%s", viper.GetString(config.ServerIP), viper.GetString(config.HttpPort), host.OSTreeImage)
digest, err := crane.Digest(localImage)
if err != nil {
log.Printf("Error getting %s from cache: %s", localImage, err)
}
if digest == "" {
log.Printf("Image (%s) not found in local cache yet...", localImage)
} else {
templateData.OSTreeImage = localImage
}
}
t, err := template.ParseFiles(fmt.Sprintf("%s/%s", viper.GetString(config.DataDir), ignitionFile))
if err != nil {
w.Write([]byte(err.Error()))
return
}

err = t.Execute(&tpl, templateData)
if err != nil {
w.Write([]byte(err.Error()))
return
}

if host == nil {
coreosConfig := coreOSType.Config{}
coreosConfig.Ignition.Version = "3.4.0"
truePointer := true
contentsPointer := `
[Service]
Type=simple
ExecStart=reboot
[Install]
WantedBy=default.target
`
coreosConfig.Systemd.Units = append(coreosConfig.Systemd.Units, coreOSType.Unit{
Name: "Reboot now please",
Enabled: &truePointer,
Contents: &contentsPointer,
})
var dataOut []byte
dataOut, err := json.Marshal(&coreosConfig)
if err != nil {
w.Write([]byte(fmt.Sprintf("Failed to marshal output: %v", err)))
return
}
w.Write(dataOut)
return
}

ignCfg, report, err := butaneConfig.TranslateBytes(tpl.Bytes(), butaneCommon.TranslateBytesOptions{
Pretty: true,
})
if err != nil {
errMsg := fmt.Sprintf("Error parsing coreos ignition: %s", err.Error())
log.Println(errMsg)
log.Printf("%s", tpl.Bytes())
for _, entry := range report.Entries {
log.Printf("%s", entry.String())
}
w.Write([]byte(errMsg))
return
}
if len(report.Entries) > 0 {
errMsg := fmt.Sprintf("Problems parsing coreos ignition: %s", report.String())
log.Println(errMsg)
log.Printf("%s", tpl.Bytes())
for _, entry := range report.Entries {
log.Printf("%s", entry.String())
}
if report.IsFatal() {
w.Write([]byte(errMsg))
return

}
}

w.Write(ignCfg)
}
59 changes: 59 additions & 0 deletions pkg/http/registration.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package http

import (
"encoding/json"
"fmt"
"log"
"net/http"

"github.com/jeefy/booty/pkg/config"
"github.com/jeefy/booty/pkg/hardware"
"github.com/jeefy/booty/pkg/versions"
"github.com/spf13/viper"
)

func handleRegistrationRequest(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
w.Write([]byte("Incorrect method"))
return
}
decoder := json.NewDecoder(r.Body)
var h hardware.Host
err := decoder.Decode(&h)
if err != nil {
w.Write([]byte(fmt.Sprintf("Error decoding JSON request: %s", err.Error())))
return
}

if h.OSTreeImage != "" {
ociImage := fmt.Sprintf("%s:%s/%s", viper.GetString(config.ServerIP), viper.GetString(config.HttpPort), h.OSTreeImage)

go func() {
err := versions.OSTreeImagePull(h.OSTreeImage)
if err != nil {
log.Printf("Error pulling %s: %s", ociImage, err.Error())
}
}()
}

hardware.WriteMacAddress(h.MAC, h)

w.Write([]byte("OK"))
}

func handleUnregistrationRequest(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
w.Write([]byte("Incorrect method"))
return
}
decoder := json.NewDecoder(r.Body)
var h hardware.Host
err := decoder.Decode(&h)
if err != nil {
w.Write([]byte(fmt.Sprintf("Error decoding JSON request: %s", err.Error())))
return
}

hardware.RemoveMacAddress(h.MAC)
w.Write([]byte("OK"))
}
65 changes: 65 additions & 0 deletions pkg/http/registry.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package http

import (
"encoding/json"
"fmt"
"net/http"

"github.com/google/go-containerregistry/pkg/crane"
"github.com/jeefy/booty/pkg/config"
"github.com/spf13/viper"
)

type Image struct {
Registry string `json:"registry"`
Image string `json:"image"`
Tag string `json:"tag"`
Digest string `json:"digest"`
UpToDate bool `json:"upToDate"`
}

func handleRegistryRequest(w http.ResponseWriter, r *http.Request) {
imageList := []Image{}
registry := fmt.Sprintf("%s:%s", viper.GetString(config.ServerIP), viper.GetString(config.HttpPort))
images, err := crane.Catalog(registry)
if err != nil {
w.Write([]byte(fmt.Sprintf("Error getting catalog from %s: %s", registry, err.Error())))
return
}
for _, image := range images {
tags, err := crane.ListTags(fmt.Sprintf("%s/%s", registry, image))
if err != nil {
w.Write([]byte(fmt.Sprintf("Error getting tags for %s: %s", image, err.Error())))
return
}
for _, tag := range tags {
cacheDesc, err := crane.Get(fmt.Sprintf("%s/%s:%s", registry, image, tag))
if err != nil {
continue
}

remoteDesc, err := crane.Get(fmt.Sprintf("%s:%s", image, tag))
if err != nil {
continue
}

imageList = append(imageList, Image{
Registry: registry,
Image: image,
Tag: tag,
Digest: cacheDesc.Digest.String(),
UpToDate: cacheDesc.Digest == remoteDesc.Digest,
})
}
}

v, err := json.Marshal(imageList)
if err != nil {
w.Write([]byte(fmt.Sprintf("Error marshalling image list: %s", err.Error())))
return
}

w.Header().Set("Content-Type", "application/json")

w.Write(v)
}
Loading

0 comments on commit ca770cb

Please sign in to comment.