local-server/cmd/dkl-local-server/render-context.go

239 lines
4.7 KiB
Go
Raw Normal View History

2018-06-12 10:09:47 +00:00
package main
import (
"bytes"
"crypto/sha256"
"encoding/hex"
2023-02-13 14:57:30 +00:00
"fmt"
2018-12-10 10:59:24 +00:00
"io"
2018-06-12 10:09:47 +00:00
"log"
2018-12-10 10:59:24 +00:00
"net/http"
2020-03-04 23:06:49 +00:00
"net/url"
2023-02-13 14:57:30 +00:00
"path"
2018-06-12 10:09:47 +00:00
"path/filepath"
2018-12-10 13:44:05 +00:00
"text/template"
2018-06-12 10:09:47 +00:00
2018-06-16 12:04:58 +00:00
cfsslconfig "github.com/cloudflare/cfssl/config"
2020-03-05 09:55:59 +00:00
restful "github.com/emicklei/go-restful"
2018-06-12 10:09:47 +00:00
yaml "gopkg.in/yaml.v2"
2018-06-16 12:04:58 +00:00
2022-04-28 01:33:19 +00:00
"novit.tech/direktil/pkg/config"
"novit.tech/direktil/pkg/localconfig"
bsconfig "novit.tech/direktil/pkg/bootstrapconfig"
2018-06-12 10:09:47 +00:00
)
2020-03-05 09:55:59 +00:00
var cmdlineParam = restful.QueryParameter("cmdline", "Linux kernel cmdline addition")
2018-06-12 10:09:47 +00:00
type renderContext struct {
2018-12-10 10:59:24 +00:00
Host *localconfig.Host
SSLConfig *cfsslconfig.Config
2020-03-04 23:06:49 +00:00
// Linux kernel extra cmdline
CmdLine string `yaml:"-"`
2018-06-12 10:09:47 +00:00
}
2018-12-10 10:59:24 +00:00
func renderCtx(w http.ResponseWriter, r *http.Request, ctx *renderContext, what string,
create func(out io.Writer, ctx *renderContext) error) error {
2018-07-03 07:35:52 +00:00
2018-12-10 10:59:24 +00:00
tag, err := ctx.Tag()
if err != nil {
return err
2018-07-03 07:35:52 +00:00
}
2018-06-12 10:09:47 +00:00
2020-03-05 09:55:59 +00:00
ctx.CmdLine = r.URL.Query().Get(cmdlineParam.Data().Name)
2020-03-04 23:06:49 +00:00
if ctx.CmdLine != "" {
what = what + "?cmdline=" + url.QueryEscape(ctx.CmdLine)
}
2018-12-10 10:59:24 +00:00
// get it or create it
content, meta, err := casStore.GetOrCreate(tag, what, func(out io.Writer) error {
log.Printf("building %s for %q", what, ctx.Host.Name)
return create(out, ctx)
})
2018-06-12 10:09:47 +00:00
2018-12-10 10:59:24 +00:00
if err != nil {
return err
2018-06-12 10:09:47 +00:00
}
2018-12-10 10:59:24 +00:00
// serve it
2020-03-04 23:06:49 +00:00
log.Printf("sending %s for %q", what, ctx.Host.Name)
2018-12-10 10:59:24 +00:00
http.ServeContent(w, r, what, meta.ModTime(), content)
return nil
}
func sslConfigFromLocalConfig(cfg *localconfig.Config) (sslCfg *cfsslconfig.Config, err error) {
if len(cfg.SSLConfig) == 0 {
sslCfg = &cfsslconfig.Config{}
} else {
sslCfg, err = cfsslconfig.LoadConfig([]byte(cfg.SSLConfig))
2019-01-21 22:44:11 +00:00
if err != nil {
return
}
}
return
}
2019-01-21 22:44:11 +00:00
func newRenderContext(host *localconfig.Host, cfg *localconfig.Config) (ctx *renderContext, err error) {
sslCfg, err := sslConfigFromLocalConfig(cfg)
if err != nil {
return
2019-01-21 22:44:11 +00:00
}
2018-06-12 10:09:47 +00:00
return &renderContext{
2018-12-10 10:59:24 +00:00
Host: host,
SSLConfig: sslCfg,
2018-07-03 07:35:52 +00:00
}, nil
2018-06-12 10:09:47 +00:00
}
func (ctx *renderContext) Config() (ba []byte, cfg *config.Config, err error) {
2022-04-28 01:33:19 +00:00
ba, err = ctx.render(ctx.Host.Config)
if err != nil {
return
}
cfg = &config.Config{}
if err = yaml.Unmarshal(ba, cfg); err != nil {
return
}
return
}
func (ctx *renderContext) BootstrapConfig() (ba []byte, cfg *bsconfig.Config, err error) {
ba, err = ctx.render(ctx.Host.BootstrapConfig)
if err != nil {
return
}
cfg = &bsconfig.Config{}
if err = yaml.Unmarshal(ba, cfg); err != nil {
return
}
return
}
func (ctx *renderContext) render(templateText string) (ba []byte, err error) {
2018-12-10 10:59:24 +00:00
tmpl, err := template.New(ctx.Host.Name + "/config").
Funcs(templateFuncs(ctx.SSLConfig)).
2022-04-28 01:33:19 +00:00
Parse(templateText)
2018-07-07 01:22:35 +00:00
2018-12-10 10:59:24 +00:00
if err != nil {
2018-07-07 01:22:35 +00:00
return
}
2018-06-20 03:01:10 +00:00
buf := bytes.NewBuffer(make([]byte, 0, 4096))
2018-12-10 10:59:24 +00:00
if err = tmpl.Execute(buf, nil); err != nil {
2018-06-20 03:01:10 +00:00
return
}
ba = buf.Bytes()
return
}
2018-06-12 10:09:47 +00:00
func (ctx *renderContext) distFilePath(path ...string) string {
return filepath.Join(append([]string{*dataDir, "dist"}, path...)...)
}
func (ctx *renderContext) Tag() (string, error) {
h := sha256.New()
_, cfg, err := ctx.Config()
if err != nil {
return "", err
}
enc := yaml.NewEncoder(h)
for _, o := range []interface{}{cfg, ctx} {
if err := enc.Encode(o); err != nil {
return "", err
}
}
return hex.EncodeToString(h.Sum(nil)), nil
}
2018-06-20 03:01:10 +00:00
func asMap(v interface{}) map[string]interface{} {
ba, err := yaml.Marshal(v)
if err != nil {
panic(err) // shouldn't happen
}
result := make(map[string]interface{})
if err := yaml.Unmarshal(ba, result); err != nil {
2018-06-20 03:01:10 +00:00
panic(err) // shouldn't happen
}
return result
}
2023-02-13 14:57:30 +00:00
func (ctx *renderContext) TemplateFuncs() map[string]any {
funcs := templateFuncs(ctx.SSLConfig)
for name, method := range map[string]any{
"ssh_host_keys": func(dir, cluster, host string) (s string, err error) {
if host == "" {
host = ctx.Host.Name
}
if host != ctx.Host.Name {
err = fmt.Errorf("wrong host name")
return
}
pairs, err := getSSHKeyPairs(host)
if err != nil {
return
}
files := make([]config.FileDef, 0, len(pairs)*2)
for _, pair := range pairs {
basePath := path.Join(dir, "ssh_host_"+pair.Type+"_key")
files = append(files, []config.FileDef{
{
Path: basePath,
Mode: 0600,
Content: pair.Private,
},
{
Path: basePath + ".pub",
Mode: 0644,
Content: pair.Public,
},
}...)
}
return asYaml(files)
},
"host_download_token": func() (token string, err error) {
key := ctx.Host.Name
token, found, err := hostDownloadTokens.Get(key)
if err != nil {
return
}
if !found {
token, err = newToken(32)
if err != nil {
return
}
err = hostDownloadTokens.Put(key, token)
if err != nil {
return
}
}
return
},
} {
funcs[name] = method
}
return funcs
}