local-server/render-context.go
2018-06-13 14:12:12 +11:00

169 lines
3.5 KiB
Go

package main
import (
"bytes"
"crypto/sha256"
"encoding/hex"
"fmt"
"log"
"path/filepath"
yaml "gopkg.in/yaml.v2"
"novit.nc/direktil/pkg/clustersconfig"
"novit.nc/direktil/pkg/config"
)
type renderContext struct {
Host *clustersconfig.Host
Group *clustersconfig.Group
Cluster *clustersconfig.Cluster
Vars map[string]interface{}
ConfigTemplate *clustersconfig.Template
StaticPodsTemplate *clustersconfig.Template
clusterConfig *clustersconfig.Config
}
func newRenderContext(host *clustersconfig.Host, cfg *clustersconfig.Config) *renderContext {
group := cfg.Group(host.Group)
cluster := cfg.Cluster(host.Cluster)
vars := make(map[string]interface{})
for _, oVars := range []map[string]interface{}{
cluster.Vars,
group.Vars,
host.Vars,
} {
for k, v := range oVars {
vars[k] = v
}
}
return &renderContext{
Host: host,
Group: group,
Cluster: cluster,
Vars: vars,
ConfigTemplate: cfg.ConfigTemplate(group.Config),
StaticPodsTemplate: cfg.StaticPodsTemplate(group.StaticPods),
clusterConfig: cfg,
}
}
func (ctx *renderContext) Config() (ba []byte, cfg *config.Config, err error) {
if ctx.ConfigTemplate == nil {
err = notFoundError{fmt.Sprintf("config %q", ctx.Group.Config)}
return
}
ctxMap := ctx.asMap()
extraFuncs := map[string]interface{}{
"static_pods": func(name string) (string, error) {
t := ctx.clusterConfig.StaticPodsTemplate(name)
if t == nil {
return "", nil
}
buf := &bytes.Buffer{}
err := t.Execute(buf, ctxMap, nil)
if err != nil {
log.Printf("host %s: failed to render static pods: %v", ctx.Host.Name, err)
return "", err
}
return buf.String(), nil
},
}
buf := bytes.NewBuffer(make([]byte, 0, 4096))
if err = ctx.ConfigTemplate.Execute(buf, ctxMap, extraFuncs); err != nil {
return
}
ba = buf.Bytes()
cfg = &config.Config{}
if err = yaml.Unmarshal(buf.Bytes(), cfg); err != nil {
return
}
// bind secrets in config
for idx, file := range cfg.Files {
if file.Secret == "" {
continue
}
v, err2 := getSecret(file.Secret, ctx)
if err2 != nil {
err = err2
return
}
cfg.Files[idx].Content = v
}
return
}
func (ctx *renderContext) StaticPods() (ba []byte, err error) {
if ctx.StaticPodsTemplate == nil {
err = notFoundError{fmt.Sprintf("static-pods %q", ctx.Group.StaticPods)}
return
}
buf := bytes.NewBuffer(make([]byte, 0, 4096))
if err = ctx.StaticPodsTemplate.Execute(buf, ctx.asMap(), nil); err != nil {
return
}
ba = buf.Bytes()
return
}
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
}
func (ctx *renderContext) asMap() map[string]interface{} {
ba, err := yaml.Marshal(ctx)
if err != nil {
panic(err) // shouldn't happen
}
result := make(map[string]interface{})
if err := yaml.Unmarshal(ba, result); err != nil {
panic(err)
}
// also expand cluster:
cluster := result["cluster"].(map[interface{}]interface{})
cluster["kubernetes_svc_ip"] = ctx.Cluster.KubernetesSvcIP().String()
cluster["dns_svc_ip"] = ctx.Cluster.DNSSvcIP().String()
return result
}