diff --git a/cmd/dkl-local-server/cluster-render-context.go b/cmd/dkl-local-server/cluster-render-context.go new file mode 100644 index 0000000..4602747 --- /dev/null +++ b/cmd/dkl-local-server/cluster-render-context.go @@ -0,0 +1,168 @@ +package main + +import ( + "encoding/json" + "fmt" + "log" + "path" + + "github.com/cloudflare/cfssl/csr" + yaml "gopkg.in/yaml.v2" + "novit.nc/direktil/pkg/config" +) + +var templateFuncs = map[string]interface{}{ + "password": func(cluster, name string) (password string, err error) { + password = secretData.Password(cluster, name) + if len(password) == 0 { + err = fmt.Errorf("password %q not defined for cluster %q", name, cluster) + } + return + }, + + "token": func(cluster, name string) (s string, err error) { + return secretData.Token(cluster, name) + }, + + "ca_key": func(cluster, name string) (s string, err error) { + ca, err := secretData.CA(cluster, name) + if err != nil { + return + } + + s = string(ca.Key) + return + }, + + "ca_crt": func(cluster, name string) (s string, err error) { + ca, err := secretData.CA(cluster, name) + if err != nil { + return + } + + s = string(ca.Cert) + return + }, + + "ca_dir": func(cluster, name string) (s string, err error) { + ca, err := secretData.CA(cluster, name) + if err != nil { + return + } + + dir := "/etc/tls-ca/" + name + + return asYaml([]config.FileDef{ + { + Path: path.Join(dir, "ca.crt"), + Mode: 0644, + Content: string(ca.Cert), + }, + { + Path: path.Join(dir, "ca.key"), + Mode: 0600, + Content: string(ca.Key), + }, + }) + }, + + "tls_key": func(cluster, caName, name, profile, label, reqJson string) (s string, err error) { + kc, err := getKeyCert(cluster, caName, name, profile, label, reqJson) + if err != nil { + return + } + + s = string(kc.Key) + return + }, + + "tls_crt": func(cluster, caName, name, profile, label, reqJson string) (s string, err error) { + kc, err := getKeyCert(cluster, caName, name, profile, label, reqJson) + if err != nil { + return + } + + s = string(kc.Cert) + return + }, + + "tls_dir": func(dir, cluster, caName, name, profile, label, reqJson string) (s string, err error) { + ca, err := secretData.CA(cluster, caName) + if err != nil { + return + } + + kc, err := getKeyCert(cluster, caName, name, profile, label, reqJson) + if err != nil { + return + } + + return asYaml([]config.FileDef{ + { + Path: path.Join(dir, "ca.crt"), + Mode: 0644, + Content: string(ca.Cert), + }, + { + Path: path.Join(dir, "tls.crt"), + Mode: 0644, + Content: string(kc.Cert), + }, + { + Path: path.Join(dir, "tls.key"), + Mode: 0600, + Content: string(kc.Key), + }, + }) + }, + + "ssh_host_keys": func(dir, cluster, host string) (s string, err error) { + pairs, err := secretData.SSHKeyPairs(cluster, 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) + }, +} + +func getKeyCert(cluster, caName, name, profile, label, reqJson string) (kc *KeyCert, err error) { + certReq := &csr.CertificateRequest{ + KeyRequest: csr.NewBasicKeyRequest(), + } + + err = json.Unmarshal([]byte(reqJson), certReq) + if err != nil { + log.Print("CSR unmarshal failed on: ", reqJson) + return + } + + return secretData.KeyCert(cluster, caName, name, profile, label, certReq) +} + +func asYaml(v interface{}) (string, error) { + ba, err := yaml.Marshal(v) + if err != nil { + return "", err + } + + return string(ba), nil +} diff --git a/cmd/dkl-local-server/render-context.go b/cmd/dkl-local-server/render-context.go index 5b17522..4a9c76e 100644 --- a/cmd/dkl-local-server/render-context.go +++ b/cmd/dkl-local-server/render-context.go @@ -4,17 +4,13 @@ import ( "bytes" "crypto/sha256" "encoding/hex" - "encoding/json" - "fmt" "io" "log" "net/http" - "path" "path/filepath" "text/template" cfsslconfig "github.com/cloudflare/cfssl/config" - "github.com/cloudflare/cfssl/csr" yaml "gopkg.in/yaml.v2" "novit.nc/direktil/pkg/config" @@ -81,7 +77,7 @@ func newRenderContext(host *localconfig.Host, cfg *localconfig.Config) (ctx *ren func (ctx *renderContext) Config() (ba []byte, cfg *config.Config, err error) { tmpl, err := template.New(ctx.Host.Name + "/config"). - Funcs(ctx.templateFuncs()). + Funcs(templateFuncs). Parse(ctx.Host.Config) if err != nil { @@ -111,164 +107,6 @@ func (ctx *renderContext) Config() (ba []byte, cfg *config.Config, err error) { return } -func (ctx *renderContext) templateFuncs() map[string]interface{} { - getKeyCert := func(cluster, caName, name, profile, label, reqJson string) (kc *KeyCert, err error) { - certReq := &csr.CertificateRequest{ - KeyRequest: csr.NewBasicKeyRequest(), - } - - err = json.Unmarshal([]byte(reqJson), certReq) - if err != nil { - log.Print("CSR unmarshal failed on: ", reqJson) - return - } - - return secretData.KeyCert(cluster, caName, name, profile, label, certReq) - } - - asYaml := func(v interface{}) (string, error) { - ba, err := yaml.Marshal(v) - if err != nil { - return "", err - } - - return string(ba), nil - } - - return map[string]interface{}{ - "password": func(cluster, name string) (password string, err error) { - password = secretData.Password(cluster, name) - if len(password) == 0 { - err = fmt.Errorf("password %q not defined for cluster %q", name, cluster) - } - return - }, - - "token": func(cluster, name string) (s string, err error) { - return secretData.Token(cluster, name) - }, - - "ca_key": func(cluster, name string) (s string, err error) { - ca, err := secretData.CA(cluster, name) - if err != nil { - return - } - - s = string(ca.Key) - return - }, - - "ca_crt": func(cluster, name string) (s string, err error) { - ca, err := secretData.CA(cluster, name) - if err != nil { - return - } - - s = string(ca.Cert) - return - }, - - "ca_dir": func(cluster, name string) (s string, err error) { - ca, err := secretData.CA(cluster, name) - if err != nil { - return - } - - dir := "/etc/tls-ca/" + name - - return asYaml([]config.FileDef{ - { - Path: path.Join(dir, "ca.crt"), - Mode: 0644, - Content: string(ca.Cert), - }, - { - Path: path.Join(dir, "ca.key"), - Mode: 0600, - Content: string(ca.Key), - }, - }) - }, - - "tls_key": func(cluster, caName, name, profile, label, reqJson string) (s string, err error) { - kc, err := getKeyCert(cluster, caName, name, profile, label, reqJson) - if err != nil { - return - } - - s = string(kc.Key) - return - }, - - "tls_crt": func(cluster, caName, name, profile, label, reqJson string) (s string, err error) { - kc, err := getKeyCert(cluster, caName, name, profile, label, reqJson) - if err != nil { - return - } - - s = string(kc.Cert) - return - }, - - "tls_dir": func(dir, cluster, caName, name, profile, label, reqJson string) (s string, err error) { - ca, err := secretData.CA(cluster, caName) - if err != nil { - return - } - - kc, err := getKeyCert(cluster, caName, name, profile, label, reqJson) - if err != nil { - return - } - - return asYaml([]config.FileDef{ - { - Path: path.Join(dir, "ca.crt"), - Mode: 0644, - Content: string(ca.Cert), - }, - { - Path: path.Join(dir, "tls.crt"), - Mode: 0644, - Content: string(kc.Cert), - }, - { - Path: path.Join(dir, "tls.key"), - Mode: 0600, - Content: string(kc.Key), - }, - }) - }, - - "ssh_host_keys": func(dir, cluster, host string) (s string, err error) { - pairs, err := secretData.SSHKeyPairs(cluster, 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) - }, - } -} - func (ctx *renderContext) distFilePath(path ...string) string { return filepath.Join(append([]string{*dataDir, "dist"}, path...)...) } diff --git a/cmd/dkl-local-server/ws-clusters.go b/cmd/dkl-local-server/ws-clusters.go index 66bdba3..763debf 100644 --- a/cmd/dkl-local-server/ws-clusters.go +++ b/cmd/dkl-local-server/ws-clusters.go @@ -59,7 +59,7 @@ func wsClusterAddons(req *restful.Request, resp *restful.Response) { return } - resp.Write([]byte(cluster.Addons)) + wsRender(resp, cluster.Addons, cluster) } func wsClusterPasswords(req *restful.Request, resp *restful.Response) { @@ -70,6 +70,7 @@ func wsClusterPasswords(req *restful.Request, resp *restful.Response) { resp.WriteEntity(secretData.Passwords(cluster.Name)) } + func wsClusterPassword(req *restful.Request, resp *restful.Response) { cluster := wsReadCluster(req, resp) if cluster == nil { @@ -80,6 +81,7 @@ func wsClusterPassword(req *restful.Request, resp *restful.Response) { resp.WriteEntity(secretData.Password(cluster.Name, name)) } + func wsClusterSetPassword(req *restful.Request, resp *restful.Response) { cluster := wsReadCluster(req, resp) if cluster == nil { @@ -98,6 +100,7 @@ func wsClusterSetPassword(req *restful.Request, resp *restful.Response) { if err := secretData.Save(); err != nil { wsError(resp, err) + return } } @@ -130,5 +133,5 @@ func wsClusterBootstrapPods(req *restful.Request, resp *restful.Response) { return } - resp.Write([]byte(cluster.BootstrapPods)) + wsRender(resp, cluster.BootstrapPods, cluster) } diff --git a/cmd/dkl-local-server/ws.go b/cmd/dkl-local-server/ws.go index b861cd1..2c248ee 100644 --- a/cmd/dkl-local-server/ws.go +++ b/cmd/dkl-local-server/ws.go @@ -6,6 +6,7 @@ import ( "net" "net/http" "strings" + "text/template" "github.com/emicklei/go-restful" "novit.nc/direktil/local-server/pkg/mime" @@ -133,3 +134,17 @@ func wsError(resp *restful.Response, err error) { http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) } + +func wsRender(resp *restful.Response, tmplStr string, value interface{}) { + tmpl, err := template.New("wsRender").Funcs(templateFuncs).Parse(tmplStr) + if err != nil { + wsError(resp, err) + return + } + + err = tmpl.Execute(resp, value) + if err != nil { + wsError(resp, err) + return + } +}