internal/rosa: mirror service via external cache
Test / Create distribution (push) Successful in 51s
Test / Sandbox (push) Successful in 2m43s
Test / ShareFS (push) Successful in 3m58s
Test / Hakurei (push) Successful in 4m2s
Test / Sandbox (race detector) (push) Successful in 5m29s
Test / Hakurei (race detector) (push) Successful in 6m43s
Test / Flake checks (push) Successful in 1m18s
Test / Create distribution (push) Successful in 51s
Test / Sandbox (push) Successful in 2m43s
Test / ShareFS (push) Successful in 3m58s
Test / Hakurei (push) Successful in 4m2s
Test / Sandbox (race detector) (push) Successful in 5m29s
Test / Hakurei (race detector) (push) Successful in 6m43s
Test / Flake checks (push) Successful in 1m18s
This provides an authenticated implementation of the external cache. Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
+18
-1
@@ -2,6 +2,7 @@ package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
@@ -9,6 +10,7 @@ import (
|
||||
"hakurei.app/check"
|
||||
"hakurei.app/container"
|
||||
"hakurei.app/internal/pkg"
|
||||
"hakurei.app/internal/rosa"
|
||||
"hakurei.app/message"
|
||||
)
|
||||
|
||||
@@ -30,7 +32,7 @@ type cache struct {
|
||||
// Loaded artifact of [rosa.QEMU].
|
||||
qemu pkg.Artifact
|
||||
|
||||
base string
|
||||
base, mirror string
|
||||
}
|
||||
|
||||
// open opens the underlying [pkg.Cache].
|
||||
@@ -86,6 +88,21 @@ func (cache *cache) open() (err error) {
|
||||
}
|
||||
done <- struct{}{}
|
||||
|
||||
if cache.mirror != "" {
|
||||
var pub []byte
|
||||
pub, err = os.ReadFile(base.Append("ed25519.pub").String())
|
||||
if err != nil {
|
||||
cache.c.Close()
|
||||
return
|
||||
}
|
||||
var r rosa.Remote
|
||||
if r, err = rosa.NewRemote(cache.mirror, pub, http.DefaultClient); err != nil {
|
||||
cache.c.Close()
|
||||
return err
|
||||
}
|
||||
cache.c.SetExternal(r)
|
||||
}
|
||||
|
||||
if cache.qemu != nil {
|
||||
var pathname *check.Absolute
|
||||
pathname, _, err = cache.c.Cure(cache.qemu)
|
||||
|
||||
@@ -14,6 +14,7 @@ package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/ed25519"
|
||||
"crypto/sha512"
|
||||
"errors"
|
||||
"fmt"
|
||||
@@ -47,6 +48,19 @@ import (
|
||||
"hakurei.app/cmd/mbf/internal/pkgserver/ui"
|
||||
)
|
||||
|
||||
// writeFileExcl is like [os.WriteFile], but sets [os.O_EXCL] instead.
|
||||
func writeFileExcl(name string, data []byte, perm os.FileMode) error {
|
||||
f, err := os.OpenFile(name, os.O_WRONLY|os.O_CREATE|os.O_EXCL, perm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = f.Write(data)
|
||||
if err1 := f.Close(); err1 != nil && err == nil {
|
||||
err = err1
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func main() {
|
||||
container.TryArgv0(nil)
|
||||
|
||||
@@ -186,6 +200,10 @@ func main() {
|
||||
&cm.base,
|
||||
"d", command.StringFlag("$MBF_CACHE_DIR"),
|
||||
"Directory to store cured artifacts",
|
||||
).Flag(
|
||||
&cm.mirror,
|
||||
"r", command.StringFlag("$MBF_REMOTE"),
|
||||
"URL of mirror service",
|
||||
).Flag(
|
||||
&cm.idle,
|
||||
"sched-idle", command.BoolFlag(false),
|
||||
@@ -462,6 +480,70 @@ func main() {
|
||||
},
|
||||
)
|
||||
|
||||
c.NewCommand(
|
||||
"keygen",
|
||||
"Create keypair for local cache",
|
||||
func([]string) error {
|
||||
pub, priv, err := ed25519.GenerateKey(nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return errors.Join(writeFileExcl(filepath.Join(
|
||||
cm.base,
|
||||
"ed25519.pub",
|
||||
), pub, 0444), writeFileExcl(filepath.Join(
|
||||
cm.base,
|
||||
"ed25519",
|
||||
), priv, 0400))
|
||||
},
|
||||
)
|
||||
|
||||
c.NewCommand(
|
||||
"serve",
|
||||
"Export local cache as mirror",
|
||||
func(args []string) error {
|
||||
const shutdownTimeout = 15 * time.Second
|
||||
|
||||
if len(args) != 1 {
|
||||
return errors.New("serve requires 1 argument")
|
||||
}
|
||||
|
||||
var key ed25519.PrivateKey
|
||||
if p, err := os.ReadFile(filepath.Join(cm.base, "ed25519")); err != nil {
|
||||
return err
|
||||
} else if len(p) != ed25519.PrivateKeySize {
|
||||
return errors.New("invalid private key")
|
||||
} else {
|
||||
key = p
|
||||
}
|
||||
|
||||
var h http.Handler
|
||||
if base, err := os.OpenRoot(cm.base); err != nil {
|
||||
return err
|
||||
} else {
|
||||
h = rosa.NewMirror(msg, base, key)
|
||||
}
|
||||
|
||||
server := http.Server{Addr: args[0], Handler: h}
|
||||
go func() {
|
||||
<-ctx.Done()
|
||||
cc, cancel := context.WithTimeout(context.Background(), shutdownTimeout)
|
||||
defer cancel()
|
||||
if err := server.Shutdown(cc); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}()
|
||||
|
||||
msg.Verbosef("listening on %q", args[0])
|
||||
err := server.ListenAndServe()
|
||||
if errors.Is(err, http.ErrServerClosed) {
|
||||
err = nil
|
||||
}
|
||||
return err
|
||||
},
|
||||
)
|
||||
|
||||
{
|
||||
var (
|
||||
flagGentoo string
|
||||
|
||||
Reference in New Issue
Block a user