more semantic config upload

This commit is contained in:
Mikaël Cluseau
2025-12-16 14:43:59 +01:00
parent 26d3d0faeb
commit 512177cab0
8 changed files with 69 additions and 18 deletions

View File

@ -1,6 +1,6 @@
from novit.tech/direktil/dkl:bbea9b9 as dkl
# ------------------------------------------------------------------------
from golang:1.25.0-trixie as build
from golang:1.25.5-trixie as build
run apt-get update && apt-get install -y git

View File

@ -224,6 +224,7 @@ func (ctx *renderContext) templateFuncs(ctxMap map[string]any) map[string]any {
if err != nil {
return
}
reqJson := cleanJsonObject(buf.String())
key := name
if req.PerHost {
@ -236,11 +237,11 @@ func (ctx *renderContext) templateFuncs(ctxMap map[string]any) map[string]any {
dir := "/etc/tls/" + name
s = fmt.Sprintf("{{ %s %q %q %q %q %q %q %q }}", funcName,
dir, cluster, req.CA, key, req.Profile, req.Label, buf.String())
dir, cluster, req.CA, key, req.Profile, req.Label, reqJson)
default:
s = fmt.Sprintf("{{ %s %q %q %q %q %q %q }}", funcName,
cluster, req.CA, key, req.Profile, req.Label, buf.String())
cluster, req.CA, key, req.Profile, req.Label, reqJson)
}
return
}
@ -281,11 +282,11 @@ func (ctx *renderContext) templateFuncs(ctxMap map[string]any) map[string]any {
},
"ssh_user_ca": func(path string) (s string) {
return fmt.Sprintf("{{ ssh_user_ca %q %q}}",
return fmt.Sprintf("{{ ssh_user_ca %q %q }}",
path, cluster)
},
"ssh_host_keys": func(dir string) (s string) {
return fmt.Sprintf("{{ ssh_host_keys %q %q \"\"}}",
return fmt.Sprintf("{{ ssh_host_keys %q %q \"\" }}",
dir, cluster)
},
"host_download_token": func() (s string) {

View File

@ -29,7 +29,7 @@ func (ctx *renderContext) renderStaticPods() (pods []namePod) {
for n := 0; ; n++ {
str := buf.String()
podMap := map[string]interface{}{}
podMap := map[string]any{}
err := dec.Decode(podMap)
if err == io.EOF {
@ -46,7 +46,7 @@ func (ctx *renderContext) renderStaticPods() (pods []namePod) {
log.Fatalf("static pod %d: no metadata\n%s", n, buf.String())
}
md := podMap["metadata"].(map[interface{}]interface{})
md := podMap["metadata"].(map[any]any)
namespace := md["namespace"].(string)
name := md["name"].(string)

View File

@ -0,0 +1,15 @@
package main
import (
"encoding/json"
"fmt"
)
func cleanJsonObject(raw string) string {
v := map[string]any{}
if err := json.Unmarshal([]byte(raw), &v); err != nil {
panic(fmt.Errorf("invalid json: %w\n%s", err, raw))
}
clean, _ := json.Marshal(v)
return string(clean)
}

View File

@ -1,20 +1,32 @@
package main
import (
"bytes"
"compress/gzip"
"io"
"os"
"path/filepath"
restful "github.com/emicklei/go-restful"
"gopkg.in/yaml.v2"
"m.cluseau.fr/go/httperr"
"novit.tech/direktil/pkg/localconfig"
)
func wsUploadConfig(req *restful.Request, resp *restful.Response) {
body := req.Request.Body
cfg := &localconfig.Config{}
if err := req.ReadEntity(cfg); err != nil {
wsError(resp, httperr.BadRequest(err.Error()))
return
}
err := writeNewConfig(body)
body.Close()
cfgBytes, err := yaml.Marshal(cfg)
if err != nil {
wsError(resp, err)
return
}
err = writeNewConfig(cfgBytes)
if err != nil {
wsError(resp, err)
return
@ -23,7 +35,7 @@ func wsUploadConfig(req *restful.Request, resp *restful.Response) {
resp.WriteEntity(true)
}
func writeNewConfig(reader io.Reader) (err error) {
func writeNewConfig(cfgBytes []byte) (err error) {
out, err := os.CreateTemp(*dataDir, ".config-upload")
if err != nil {
return
@ -31,7 +43,7 @@ func writeNewConfig(reader io.Reader) (err error) {
defer os.Remove(out.Name())
_, err = io.Copy(out, reader)
_, err = io.Copy(out, bytes.NewReader(cfgBytes))
out.Close()
if err != nil {
return

View File

@ -11,6 +11,7 @@ import (
cfsslconfig "github.com/cloudflare/cfssl/config"
"github.com/emicklei/go-restful"
"gopkg.in/yaml.v2"
"m.cluseau.fr/go/httperr"
"novit.tech/direktil/pkg/localconfig"
@ -75,7 +76,7 @@ func registerWS(rest *restful.Container) {
// - configs API
ws.Route(ws.POST("/configs").To(wsUploadConfig).
Consumes(mime.YAML).Param(ws.BodyParameter("config", "The new full configuration")).
Consumes(mime.YAML, mime.JSON).Param(ws.BodyParameter("config", "The new full configuration")).
Produces(mime.JSON).Writes(true).
Doc("Upload a new current configuration, archiving the previous one"))
@ -307,3 +308,26 @@ func wsRender(resp *restful.Response, sslCfg *cfsslconfig.Config, tmplStr string
return
}
}
func init() {
restful.RegisterEntityAccessor(mime.YAML, yamlEntityAccessor{})
}
type yamlEntityAccessor struct{}
var _ restful.EntityReaderWriter = yamlEntityAccessor{}
func (_ yamlEntityAccessor) Read(req *restful.Request, v any) error {
decoder := yaml.NewDecoder(req.Request.Body)
return decoder.Decode(v)
}
func (_ yamlEntityAccessor) Write(resp *restful.Response, status int, v any) error {
if v == nil {
resp.WriteHeader(status)
// do not write a nil representation
return nil
}
resp.Header().Set("Content-Type", mime.YAML)
resp.WriteHeader(status)
return yaml.NewEncoder(resp.ResponseWriter).Encode(v)
}

View File

@ -8,14 +8,14 @@ dev=$1
set -ex
zcat boot.img.gz | dd of=$dev
apk add sgdisk
[[ $dev =~ nvme ]] &&
devp=${dev}p ||
devp=${dev}
zcat boot.img.gz | dd of=$dev
sgdisk --move-second-header --new=3:0:0 $dev
pvcreate ${devp}3

View File

@ -2,7 +2,6 @@ package clustersconfig
import (
"fmt"
"io/ioutil"
"os"
"path"
"path/filepath"
@ -178,7 +177,7 @@ type dirStore struct {
// listDir
func (b *dirStore) listDir(prefix string) (subDirs []string, err error) {
entries, err := ioutil.ReadDir(filepath.Join(b.path, prefix))
entries, err := os.ReadDir(filepath.Join(b.path, prefix))
if err != nil {
return
}
@ -226,7 +225,7 @@ func (b *dirStore) List(prefix string) ([]string, error) {
// Load is part of the DataBackend interface
func (b *dirStore) Get(key string) (ba []byte, err error) {
ba, err = ioutil.ReadFile(filepath.Join(b.path, filepath.Join(path.Split(key))+".yaml"))
ba, err = os.ReadFile(filepath.Join(b.path, filepath.Join(path.Split(key))+".yaml"))
if os.IsNotExist(err) {
return nil, nil
}