Compare commits
5 Commits
436be67bfd
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 512177cab0 | |||
| 26d3d0faeb | |||
| 834510760f | |||
| a2a970f93b | |||
| 350e753ae0 |
@ -1,6 +1,6 @@
|
|||||||
from novit.tech/direktil/dkl:bbea9b9 as dkl
|
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
|
run apt-get update && apt-get install -y git
|
||||||
|
|
||||||
|
|||||||
@ -1,10 +1,12 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"flag"
|
"flag"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/go-git/go-git/v5"
|
"github.com/go-git/go-git/v5"
|
||||||
@ -127,12 +129,22 @@ func main() {
|
|||||||
|
|
||||||
defer out.Close()
|
defer out.Close()
|
||||||
|
|
||||||
out.Write([]byte("# dkl-dir2config " + Version + "\n"))
|
switch ext := path.Ext(*outPath); ext {
|
||||||
|
case ".yaml":
|
||||||
|
out.Write([]byte("# dkl-dir2config " + Version + "\n"))
|
||||||
|
|
||||||
if err = yaml.NewEncoder(out).Encode(dst); err != nil {
|
if err = yaml.NewEncoder(out).Encode(dst); err != nil {
|
||||||
log.Fatal("failed to render output: ", err)
|
log.Fatal("failed to render output: ", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
case ".json":
|
||||||
|
if err = json.NewEncoder(out).Encode(dst); err != nil {
|
||||||
|
log.Fatal("failed to render output: ", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
log.Fatal("unknown output file extension: ", ext)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func cfgPath(subPath string) string { return filepath.Join(*dir, subPath) }
|
func cfgPath(subPath string) string { return filepath.Join(*dir, subPath) }
|
||||||
|
|||||||
@ -224,6 +224,7 @@ func (ctx *renderContext) templateFuncs(ctxMap map[string]any) map[string]any {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
reqJson := cleanJsonObject(buf.String())
|
||||||
|
|
||||||
key := name
|
key := name
|
||||||
if req.PerHost {
|
if req.PerHost {
|
||||||
@ -236,11 +237,11 @@ func (ctx *renderContext) templateFuncs(ctxMap map[string]any) map[string]any {
|
|||||||
dir := "/etc/tls/" + name
|
dir := "/etc/tls/" + name
|
||||||
|
|
||||||
s = fmt.Sprintf("{{ %s %q %q %q %q %q %q %q }}", funcName,
|
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:
|
default:
|
||||||
s = fmt.Sprintf("{{ %s %q %q %q %q %q %q }}", funcName,
|
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
|
return
|
||||||
}
|
}
|
||||||
@ -281,11 +282,11 @@ func (ctx *renderContext) templateFuncs(ctxMap map[string]any) map[string]any {
|
|||||||
},
|
},
|
||||||
|
|
||||||
"ssh_user_ca": func(path string) (s string) {
|
"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)
|
path, cluster)
|
||||||
},
|
},
|
||||||
"ssh_host_keys": func(dir string) (s string) {
|
"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)
|
dir, cluster)
|
||||||
},
|
},
|
||||||
"host_download_token": func() (s string) {
|
"host_download_token": func() (s string) {
|
||||||
|
|||||||
@ -29,7 +29,7 @@ func (ctx *renderContext) renderStaticPods() (pods []namePod) {
|
|||||||
for n := 0; ; n++ {
|
for n := 0; ; n++ {
|
||||||
str := buf.String()
|
str := buf.String()
|
||||||
|
|
||||||
podMap := map[string]interface{}{}
|
podMap := map[string]any{}
|
||||||
err := dec.Decode(podMap)
|
err := dec.Decode(podMap)
|
||||||
|
|
||||||
if err == io.EOF {
|
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())
|
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)
|
namespace := md["namespace"].(string)
|
||||||
name := md["name"].(string)
|
name := md["name"].(string)
|
||||||
|
|||||||
15
cmd/dkl-dir2config/utils.go
Normal file
15
cmd/dkl-dir2config/utils.go
Normal 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)
|
||||||
|
}
|
||||||
@ -81,7 +81,7 @@ func buildInitrd(out io.Writer, ctx *renderContext) (err error) {
|
|||||||
cat.AppendDir("/etc/ssh", 0o700)
|
cat.AppendDir("/etc/ssh", 0o700)
|
||||||
|
|
||||||
// XXX do we want bootstrap-stage keys instead of the real host key?
|
// XXX do we want bootstrap-stage keys instead of the real host key?
|
||||||
for _, format := range []string{"rsa", "dsa", "ecdsa", "ed25519"} {
|
for _, format := range []string{"rsa", "ecdsa", "ed25519"} {
|
||||||
keyPath := "/etc/ssh/ssh_host_" + format + "_key"
|
keyPath := "/etc/ssh/ssh_host_" + format + "_key"
|
||||||
cat.AppendBytes(cfg.FileContent(keyPath), keyPath, 0o600)
|
cat.AppendBytes(cfg.FileContent(keyPath), keyPath, 0o600)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -202,6 +202,7 @@ func (ctx *renderContext) TemplateFuncs() map[string]any {
|
|||||||
}
|
}
|
||||||
|
|
||||||
reqJson = strings.ReplaceAll(reqJson, "${host_ip}", ctx.Host.IPs[0])
|
reqJson = strings.ReplaceAll(reqJson, "${host_ip}", ctx.Host.IPs[0])
|
||||||
|
reqJson = strings.ReplaceAll(reqJson, "${host_name}", ctx.Host.Name)
|
||||||
|
|
||||||
kc, err := getKeyCert(cluster, caName, name, profile, label, reqJson)
|
kc, err := getKeyCert(cluster, caName, name, profile, label, reqJson)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@ -32,7 +32,6 @@ func getSSHKeyPairs(host string) (pairs []SSHKeyPair, err error) {
|
|||||||
genLoop:
|
genLoop:
|
||||||
for _, keyType := range []string{
|
for _, keyType := range []string{
|
||||||
"rsa",
|
"rsa",
|
||||||
"dsa",
|
|
||||||
"ecdsa",
|
"ecdsa",
|
||||||
"ed25519",
|
"ed25519",
|
||||||
} {
|
} {
|
||||||
|
|||||||
@ -1,20 +1,32 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"compress/gzip"
|
"compress/gzip"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
restful "github.com/emicklei/go-restful"
|
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) {
|
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)
|
cfgBytes, err := yaml.Marshal(cfg)
|
||||||
body.Close()
|
if err != nil {
|
||||||
|
wsError(resp, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = writeNewConfig(cfgBytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
wsError(resp, err)
|
wsError(resp, err)
|
||||||
return
|
return
|
||||||
@ -23,7 +35,7 @@ func wsUploadConfig(req *restful.Request, resp *restful.Response) {
|
|||||||
resp.WriteEntity(true)
|
resp.WriteEntity(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeNewConfig(reader io.Reader) (err error) {
|
func writeNewConfig(cfgBytes []byte) (err error) {
|
||||||
out, err := os.CreateTemp(*dataDir, ".config-upload")
|
out, err := os.CreateTemp(*dataDir, ".config-upload")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
@ -31,7 +43,7 @@ func writeNewConfig(reader io.Reader) (err error) {
|
|||||||
|
|
||||||
defer os.Remove(out.Name())
|
defer os.Remove(out.Name())
|
||||||
|
|
||||||
_, err = io.Copy(out, reader)
|
_, err = io.Copy(out, bytes.NewReader(cfgBytes))
|
||||||
out.Close()
|
out.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
|
|||||||
@ -11,6 +11,7 @@ import (
|
|||||||
|
|
||||||
cfsslconfig "github.com/cloudflare/cfssl/config"
|
cfsslconfig "github.com/cloudflare/cfssl/config"
|
||||||
"github.com/emicklei/go-restful"
|
"github.com/emicklei/go-restful"
|
||||||
|
"gopkg.in/yaml.v2"
|
||||||
"m.cluseau.fr/go/httperr"
|
"m.cluseau.fr/go/httperr"
|
||||||
|
|
||||||
"novit.tech/direktil/pkg/localconfig"
|
"novit.tech/direktil/pkg/localconfig"
|
||||||
@ -75,7 +76,7 @@ func registerWS(rest *restful.Container) {
|
|||||||
|
|
||||||
// - configs API
|
// - configs API
|
||||||
ws.Route(ws.POST("/configs").To(wsUploadConfig).
|
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).
|
Produces(mime.JSON).Writes(true).
|
||||||
Doc("Upload a new current configuration, archiving the previous one"))
|
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
|
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)
|
||||||
|
}
|
||||||
|
|||||||
@ -1,37 +1,23 @@
|
|||||||
#! /bin/sh
|
#! /bin/sh
|
||||||
|
|
||||||
if [ $# -ne 2 ]; then
|
if [ $# -ne 2 ]; then
|
||||||
echo "USAGE: $0 <device> <base url>"
|
echo "USAGE: $0 <device>"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
dev=$1
|
dev=$1
|
||||||
base_url=$2
|
|
||||||
|
|
||||||
: ${MP:=/mnt}
|
|
||||||
|
|
||||||
set -ex
|
set -ex
|
||||||
|
|
||||||
mkdir -p $MP
|
zcat boot.img.gz | dd of=$dev
|
||||||
|
|
||||||
|
apk add sgdisk
|
||||||
|
|
||||||
[[ $dev =~ nvme ]] &&
|
[[ $dev =~ nvme ]] &&
|
||||||
devp=${dev}p ||
|
devp=${dev}p ||
|
||||||
devp=${dev}
|
devp=${dev}
|
||||||
|
|
||||||
if vgdisplay storage; then
|
sgdisk --move-second-header --new=3:0:0 $dev
|
||||||
# the system is already installed, just upgrade
|
|
||||||
mount -t vfat ${devp}1 $MP
|
|
||||||
curl ${base_url}/boot.tar |tar xv -C $MP
|
|
||||||
umount $MP
|
|
||||||
|
|
||||||
else
|
pvcreate ${devp}3
|
||||||
sgdisk --clear $dev
|
vgcreate storage ${devp}3
|
||||||
|
|
||||||
curl ${base_url}/boot.img.lz4 |lz4cat >$dev
|
|
||||||
|
|
||||||
sgdisk --move-second-header --new=3:0:0 $dev
|
|
||||||
|
|
||||||
pvcreate ${devp}3
|
|
||||||
vgcreate storage ${devp}3
|
|
||||||
fi
|
|
||||||
|
|
||||||
while umount $MP; do true; done
|
|
||||||
|
|||||||
@ -2,7 +2,6 @@ package clustersconfig
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@ -178,7 +177,7 @@ type dirStore struct {
|
|||||||
|
|
||||||
// listDir
|
// listDir
|
||||||
func (b *dirStore) listDir(prefix string) (subDirs []string, err error) {
|
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 {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -226,7 +225,7 @@ func (b *dirStore) List(prefix string) ([]string, error) {
|
|||||||
|
|
||||||
// Load is part of the DataBackend interface
|
// Load is part of the DataBackend interface
|
||||||
func (b *dirStore) Get(key string) (ba []byte, err error) {
|
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) {
|
if os.IsNotExist(err) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user