Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Binary Cache Support #79

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions pkg/dogeboxd.go
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,12 @@ func (t Dogeboxd) jobDispatcher(j Job) {
case RemoveSSHKey:
t.enqueue(j)

case AddBinaryCache:
t.enqueue(j)

case RemoveBinaryCache:
t.enqueue(j)

// Pup router actions
case UpdateMetrics:
t.Pups.UpdateMetrics(a)
Expand Down
9 changes: 9 additions & 0 deletions pkg/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,15 @@ type RemoveSSHKey struct {
ID string
}

type AddBinaryCache struct {
Host string
Key string
}

type RemoveBinaryCache struct {
ID string
}

/* Updates are responses to Actions or simply
* internal state changes that the frontend needs,
* these are wrapped in a 'change' and sent via
Expand Down
18 changes: 14 additions & 4 deletions pkg/sys.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ type SystemUpdater interface {
AddSSHKey(key string, l SubLogger) error
EnableSSH(l SubLogger) error
ListSSHKeys() ([]DogeboxStateSSHKey, error)
AddBinaryCache(j AddBinaryCache, l SubLogger) error
}

// monitors systemd services and returns stats
Expand Down Expand Up @@ -64,12 +65,19 @@ type DogeboxStateSSHConfig struct {
Keys []DogeboxStateSSHKey `json:"keys"`
}

type DogeboxStateBinaryCache struct {
ID string `json:"id"`
Host string `json:"host"`
Key string `json:"key"`
}

type DogeboxState struct {
InitialState DogeboxStateInitialSetup
Hostname string
KeyMap string
SSH DogeboxStateSSHConfig
StorageDevice string
BinaryCaches []DogeboxStateBinaryCache
}

type NetworkState struct {
Expand Down Expand Up @@ -278,10 +286,12 @@ type NixFirewallTemplateValues struct {
}

type NixSystemTemplateValues struct {
SYSTEM_HOSTNAME string
KEYMAP string
SSH_ENABLED bool
SSH_KEYS []DogeboxStateSSHKey
SYSTEM_HOSTNAME string
KEYMAP string
SSH_ENABLED bool
SSH_KEYS []DogeboxStateSSHKey
BINARY_CACHE_SUBS []string
BINARY_CACHE_KEYS []string
}

type NixIncludesFileTemplateValues struct {
Expand Down
10 changes: 10 additions & 0 deletions pkg/system/nix/templates/system.nix
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,14 @@
};
};
};

{{ range .BINARY_CACHE_SUBS }}
nix.settings.substituters = [
"{{.}}"
];{{ end }}

{{ range .BINARY_CACHE_KEYS }}
nix.settings.trusted-public-keys = [
"{{.}}"
];{{ end }}
}
10 changes: 4 additions & 6 deletions pkg/system/ssh.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,15 @@ import (
"fmt"

dogeboxd "github.com/dogeorg/dogeboxd/pkg"
"github.com/dogeorg/dogeboxd/pkg/utils"
)

func (t SystemUpdater) sshUpdate(dbxState dogeboxd.DogeboxState, log dogeboxd.SubLogger) error {
patch := t.nix.NewPatch(log)
t.nix.UpdateFirewallRules(patch, dbxState)
t.nix.UpdateSystem(patch, dogeboxd.NixSystemTemplateValues{
SYSTEM_HOSTNAME: dbxState.Hostname,
SSH_ENABLED: dbxState.SSH.Enabled,
SSH_KEYS: dbxState.SSH.Keys,
KEYMAP: dbxState.KeyMap,
})

values := utils.GetNixSystemTemplateValues(dbxState)
t.nix.UpdateSystem(patch, values)

if err := patch.Apply(); err != nil {
log.Errf("Failed to enable SSH: %v", err)
Expand Down
61 changes: 61 additions & 0 deletions pkg/system/updater.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package system

import (
"context"
"crypto/rand"
"crypto/sha256"
_ "embed"
"fmt"
Expand All @@ -11,6 +12,7 @@ import (
"path/filepath"

dogeboxd "github.com/dogeorg/dogeboxd/pkg"
"github.com/dogeorg/dogeboxd/pkg/utils"
)

/*
Expand Down Expand Up @@ -125,6 +127,20 @@ func (t SystemUpdater) Run(started, stopped chan bool, stop chan context.Context
}
t.done <- j

case dogeboxd.AddBinaryCache:
err := t.AddBinaryCache(a, j.Logger.Step("Add binary cache"))
if err != nil {
j.Err = "Failed to add binary cache"
}
t.done <- j

case dogeboxd.RemoveBinaryCache:
err := t.removeBinaryCache(a)
if err != nil {
j.Err = "Failed to remove binary cache"
}
t.done <- j

default:
fmt.Printf("Unknown action type: %v\n", a)
}
Expand Down Expand Up @@ -380,3 +396,48 @@ func (t SystemUpdater) disablePup(j dogeboxd.Job) error {

return nil
}

func (t SystemUpdater) AddBinaryCache(j dogeboxd.AddBinaryCache, log dogeboxd.SubLogger) error {
dbxState := t.sm.Get().Dogebox

id := make([]byte, 8)
if _, err := rand.Read(id); err != nil {
return fmt.Errorf("failed to generate random ID for binary cache: %v", err)
}

dbxState.BinaryCaches = append(dbxState.BinaryCaches, dogeboxd.DogeboxStateBinaryCache{
ID: string(id),
Host: j.Host,
Key: j.Key,
})

if err := t.sm.SetDogebox(dbxState); err != nil {
return err
}

nixPatch := t.nix.NewPatch(log)

values := utils.GetNixSystemTemplateValues(dbxState)
t.nix.UpdateSystem(nixPatch, values)

return nixPatch.Apply()
}

func (t SystemUpdater) removeBinaryCache(j dogeboxd.RemoveBinaryCache) error {
dbxState := t.sm.Get().Dogebox

keyFound := false
for i, cache := range dbxState.BinaryCaches {
if cache.ID == j.ID {
dbxState.BinaryCaches = append(dbxState.BinaryCaches[:i], dbxState.BinaryCaches[i+1:]...)
keyFound = true
break
}
}

if !keyFound {
return fmt.Errorf("binary cache with ID %s not found", j.ID)
}

return t.sm.SetDogebox(dbxState)
}
20 changes: 20 additions & 0 deletions pkg/utils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import (
"os"
"path/filepath"
"strings"

dogeboxd "github.com/dogeorg/dogeboxd/pkg"
)

func ImageBytesToWebBase64(imgBytes []byte, filename string) (string, error) {
Expand Down Expand Up @@ -87,3 +89,21 @@ func CopyFiles(source string, destination string) error {

return err
}

func GetNixSystemTemplateValues(dbxState dogeboxd.DogeboxState) dogeboxd.NixSystemTemplateValues {
binaryCacheSubs := []string{}
binaryCacheKeys := []string{}
for _, cache := range dbxState.BinaryCaches {
binaryCacheSubs = append(binaryCacheSubs, cache.Host)
binaryCacheKeys = append(binaryCacheKeys, cache.Key)
}

return dogeboxd.NixSystemTemplateValues{
SYSTEM_HOSTNAME: dbxState.Hostname,
SSH_ENABLED: dbxState.SSH.Enabled,
SSH_KEYS: dbxState.SSH.Keys,
KEYMAP: dbxState.KeyMap,
BINARY_CACHE_SUBS: binaryCacheSubs,
BINARY_CACHE_KEYS: binaryCacheKeys,
}
}
75 changes: 75 additions & 0 deletions pkg/web/binary_caches.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package web

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

dogeboxd "github.com/dogeorg/dogeboxd/pkg"
)

type AddBinaryCacheRequest struct {
Host string `json:"host"`
Key string `json:"key"`
}

func (a api) getBinaryCaches(w http.ResponseWriter, r *http.Request) {
dbxState := a.sm.Get().Dogebox
sendResponse(w, dbxState.BinaryCaches)
}

func (a api) addBinaryCache(w http.ResponseWriter, r *http.Request) {
body, err := io.ReadAll(r.Body)
if err != nil {
sendErrorResponse(w, http.StatusBadRequest, "Error reading request body")
return
}

var req AddBinaryCacheRequest
if err := json.Unmarshal(body, &req); err != nil {
sendErrorResponse(w, http.StatusBadRequest, "Error unmarshalling JSON")
return
}

dbxState := a.sm.Get().Dogebox

for _, existingCache := range dbxState.BinaryCaches {
if existingCache.Host == req.Host {
sendErrorResponse(w, http.StatusBadRequest, "Binary cache with this host already exists")
return
}
if existingCache.Key == req.Key {
sendErrorResponse(w, http.StatusBadRequest, "Binary cache with this key already exists")
return
}
}

id := a.dbx.AddAction(dogeboxd.AddBinaryCache{Host: req.Host, Key: req.Key})
sendResponse(w, map[string]string{"id": id})
}

func (a api) removeBinaryCache(w http.ResponseWriter, r *http.Request) {
dbxState := a.sm.Get().Dogebox

cacheId := r.PathValue("id")
if cacheId == "" {
sendErrorResponse(w, http.StatusBadRequest, "Cache ID is required")
return
}

cacheFound := false
for _, cache := range dbxState.BinaryCaches {
if cache.ID == cacheId {
cacheFound = true
break
}
}

if !cacheFound {
sendErrorResponse(w, http.StatusBadRequest, "Binary cache with this ID does not exist")
return
}

id := a.dbx.AddAction(dogeboxd.RemoveBinaryCache{ID: cacheId})
sendResponse(w, map[string]string{"id": id})
}
4 changes: 4 additions & 0 deletions pkg/web/rest.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,10 @@ func RESTAPI(
"DELETE /source/{id}": a.deleteSource,
"/ws/log/{PupID}": a.getLogSocket,
"/ws/state/": a.getUpdateSocket,

"GET /system/binary-caches": a.getBinaryCaches,
"PUT /system/binary-cache": a.addBinaryCache,
"DELETE /system/binary-cache/{id}": a.removeBinaryCache,
}

// We always want to load recovery routes.
Expand Down
20 changes: 17 additions & 3 deletions pkg/web/setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@ import (
)

type InitialSystemBootstrapRequestBody struct {
ReflectorToken string `json:"reflectorToken"`
ReflectorHost string `json:"reflectorHost"`
InitialSSHKey string `json:"initialSSHKey"`
ReflectorToken string `json:"reflectorToken"`
ReflectorHost string `json:"reflectorHost"`
InitialSSHKey string `json:"initialSSHKey"`
UseFoundationBinaryCache bool `json:"useFoundationBinaryCache"`
}

type BootstrapFacts struct {
Expand Down Expand Up @@ -384,6 +385,19 @@ func (t api) initialBootstrap(w http.ResponseWriter, r *http.Request) {
}
}

if requestBody.UseFoundationBinaryCache {
// This is a bit of a hack until we can dispatch and then block,
// until a job has finished through the queue.
if err := t.dbx.SystemUpdater.AddBinaryCache(dogeboxd.AddBinaryCache{
Host: "https://nix.dogecoin.org",
Key: "nix.dogecoin.org:PeUX5ftpdp5W3h827irwXxMZZr/4PGfHvSmV+2o6rC4=",
}, log); err != nil {
log.Errf("Error adding foundation binary cache: %v", err)
sendErrorResponse(w, http.StatusInternalServerError, "Error adding foundation binary cache")
return
}
}

dbxs := t.sm.Get().Dogebox
dbxs.InitialState.HasFullyConfigured = true
if err := t.sm.SetDogebox(dbxs); err != nil {
Expand Down