wip
This commit is contained in:
		
							
								
								
									
										293
									
								
								cmd/dkl-dir2config/http.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										293
									
								
								cmd/dkl-dir2config/http.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,293 @@ | ||||
| package main | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"encoding/json" | ||||
| 	"flag" | ||||
| 	"log" | ||||
| 	"net" | ||||
| 	"net/http" | ||||
| 	"regexp" | ||||
| 	"strings" | ||||
|  | ||||
| 	yaml "gopkg.in/yaml.v2" | ||||
| 	"novit.nc/direktil/pkg/localconfig" | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| 	hostsToken = flag.String("hosts-token", "", "Token to give to access /hosts (open is none)") | ||||
|  | ||||
| 	reHost = regexp.MustCompile("^/hosts/([^/]+)/([^/]+)$") | ||||
|  | ||||
| 	trustXFF = flag.Bool("trust-xff", true, "Trust the X-Forwarded-For header") | ||||
| ) | ||||
|  | ||||
| func authorizeHosts(r *http.Request) bool { | ||||
| 	if *hostsToken == "" { | ||||
| 		// access is open | ||||
| 		return true | ||||
| 	} | ||||
|  | ||||
| 	reqToken := r.Header.Get("Authorization") | ||||
|  | ||||
| 	return reqToken == "Bearer "+*hostsToken | ||||
| } | ||||
|  | ||||
| func forbidden(w http.ResponseWriter, r *http.Request) { | ||||
| 	log.Printf("denied access to %s from %s", r.RequestURI, r.RemoteAddr) | ||||
| 	http.Error(w, "Forbidden", http.StatusForbidden) | ||||
| } | ||||
|  | ||||
| func serveHostByIP(w http.ResponseWriter, r *http.Request) { | ||||
| 	host, cfg := hostByIP(w, r) | ||||
| 	if host == nil { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	what := strings.TrimLeft(r.URL.Path, "/") | ||||
|  | ||||
| 	renderHost(w, r, what, host, cfg) | ||||
| } | ||||
|  | ||||
| func hostByIP(w http.ResponseWriter, r *http.Request) (*localconfig.Host, *localconfig.Config) { | ||||
| 	remoteAddr := r.RemoteAddr | ||||
|  | ||||
| 	if *trustXFF { | ||||
| 		if xff := r.Header.Get("X-Forwarded-For"); xff != "" { | ||||
| 			remoteAddr = strings.Split(xff, ",")[0] | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	hostIP, _, err := net.SplitHostPort(remoteAddr) | ||||
|  | ||||
| 	if err != nil { | ||||
| 		hostIP = remoteAddr | ||||
| 	} | ||||
|  | ||||
| 	cfg, err := readConfig() | ||||
| 	if err != nil { | ||||
| 		http.Error(w, "", http.StatusServiceUnavailable) | ||||
| 		return nil, nil | ||||
| 	} | ||||
|  | ||||
| 	host := cfg.HostByIP(hostIP) | ||||
|  | ||||
| 	if host == nil { | ||||
| 		log.Print("no host found for IP ", hostIP) | ||||
| 		http.NotFound(w, r) | ||||
| 		return nil, nil | ||||
| 	} | ||||
|  | ||||
| 	return host, cfg | ||||
| } | ||||
|  | ||||
| func serveHosts(w http.ResponseWriter, r *http.Request) { | ||||
| 	if !authorizeHosts(r) { | ||||
| 		forbidden(w, r) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	cfg, err := readConfig() | ||||
| 	if err != nil { | ||||
| 		http.Error(w, "", http.StatusServiceUnavailable) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	renderJSON(w, cfg.Hosts) | ||||
| } | ||||
|  | ||||
| func serveHost(w http.ResponseWriter, r *http.Request) { | ||||
| 	if !authorizeHosts(r) { | ||||
| 		forbidden(w, r) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	match := reHost.FindStringSubmatch(r.URL.Path) | ||||
| 	if match == nil { | ||||
| 		http.NotFound(w, r) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	hostName, what := match[1], match[2] | ||||
|  | ||||
| 	cfg, err := readConfig() | ||||
| 	if err != nil { | ||||
| 		http.Error(w, "", http.StatusServiceUnavailable) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	host := cfg.Host(hostName) | ||||
|  | ||||
| 	if host == nil { | ||||
| 		host = cfg.HostByMAC(hostName) | ||||
| 	} | ||||
|  | ||||
| 	if host == nil { | ||||
| 		log.Printf("no host with name or MAC %q", hostName) | ||||
| 		http.NotFound(w, r) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	renderHost(w, r, what, host, cfg) | ||||
| } | ||||
|  | ||||
| func renderHost(w http.ResponseWriter, r *http.Request, what string, host *localconfig.Host, cfg *localconfig.Config) { | ||||
| 	ctx, err := newRenderContext(host, cfg) | ||||
| 	if err != nil { | ||||
| 		log.Printf("host %s: %s: failed to render: %v", what, host.Name, err) | ||||
| 		http.Error(w, "", http.StatusServiceUnavailable) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	switch what { | ||||
| 	case "ipxe": | ||||
| 		w.Header().Set("Content-Type", "text/x-ipxe") | ||||
| 	case "config": | ||||
| 		w.Header().Set("Content-Type", "text/vnd.yaml") | ||||
| 	default: | ||||
| 		w.Header().Set("Content-Type", "application/octet-stream") | ||||
| 	} | ||||
|  | ||||
| 	switch what { | ||||
| 	case "ipxe": | ||||
| 		err = renderIPXE(w, ctx) | ||||
|  | ||||
| 	case "kernel": | ||||
| 		err = renderKernel(w, r, ctx) | ||||
|  | ||||
| 	case "initrd": | ||||
| 		err = renderCtx(w, r, ctx, what, buildInitrd) | ||||
|  | ||||
| 	case "boot.iso": | ||||
| 		err = renderCtx(w, r, ctx, what, buildBootISO) | ||||
|  | ||||
| 	case "boot.tar": | ||||
| 		err = renderCtx(w, r, ctx, what, buildBootTar) | ||||
|  | ||||
| 	case "boot.img": | ||||
| 		err = renderCtx(w, r, ctx, what, buildBootImg) | ||||
|  | ||||
| 	case "boot.img.gz": | ||||
| 		err = renderCtx(w, r, ctx, what, buildBootImgGZ) | ||||
|  | ||||
| 	case "boot.img.lz4": | ||||
| 		err = renderCtx(w, r, ctx, what, buildBootImgLZ4) | ||||
|  | ||||
| 	case "config": | ||||
| 		err = renderConfig(w, r, ctx) | ||||
|  | ||||
| 	default: | ||||
| 		http.NotFound(w, r) | ||||
| 	} | ||||
|  | ||||
| 	if err != nil { | ||||
| 		if isNotFound(err) { | ||||
| 			log.Printf("host %s: %s: %v", what, host.Name, err) | ||||
| 			http.NotFound(w, r) | ||||
| 		} else { | ||||
| 			log.Printf("host %s: %s: failed to render: %v", what, host.Name, err) | ||||
| 			http.Error(w, "", http.StatusServiceUnavailable) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func renderJSON(w http.ResponseWriter, v interface{}) { | ||||
| 	w.Header().Add("Content-Type", "application/json") | ||||
| 	json.NewEncoder(w).Encode(v) | ||||
| } | ||||
|  | ||||
| func serveClusters(w http.ResponseWriter, r *http.Request) { | ||||
| 	cfg, err := readConfig() | ||||
| 	if err != nil { | ||||
| 		http.Error(w, "", http.StatusServiceUnavailable) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	clusterNames := make([]string, len(cfg.Clusters)) | ||||
| 	for i, cluster := range cfg.Clusters { | ||||
| 		clusterNames[i] = cluster.Name | ||||
| 	} | ||||
|  | ||||
| 	renderJSON(w, clusterNames) | ||||
| } | ||||
|  | ||||
| func serveCluster(w http.ResponseWriter, r *http.Request) { | ||||
| 	// "/clusters/<name>/<what>" split => "", "clusters", "<name>", "<what>" | ||||
| 	p := strings.Split(r.URL.Path, "/") | ||||
|  | ||||
| 	if len(p) != 4 { | ||||
| 		http.NotFound(w, r) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	clusterName := p[2] | ||||
|  | ||||
| 	p = strings.SplitN(p[3], ".", 2) | ||||
| 	what := p[0] | ||||
| 	format := "" | ||||
| 	if len(p) > 1 { | ||||
| 		format = p[1] | ||||
| 	} | ||||
|  | ||||
| 	cfg, err := readConfig() | ||||
| 	if err != nil { | ||||
| 		http.Error(w, "", http.StatusServiceUnavailable) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	cluster := cfg.Cluster(clusterName) | ||||
| 	if cluster == nil { | ||||
| 		http.NotFound(w, r) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	switch what { | ||||
| 	case "addons": | ||||
| 		if len(cluster.Addons) == 0 { | ||||
| 			log.Printf("cluster %q has no addons defined", clusterName) | ||||
| 			http.NotFound(w, r) | ||||
| 			return | ||||
| 		} | ||||
|  | ||||
| 		addons := cluster.Addons | ||||
| 		if addons == nil { | ||||
| 			log.Printf("cluster %q: no addons with name %q", clusterName, cluster.Addons) | ||||
| 			http.NotFound(w, r) | ||||
| 			return | ||||
| 		} | ||||
|  | ||||
| 		clusterAsMap := asMap(cluster) | ||||
| 		clusterAsMap["kubernetes_svc_ip"] = cluster.KubernetesSvcIP().String() | ||||
| 		clusterAsMap["dns_svc_ip"] = cluster.DNSSvcIP().String() | ||||
|  | ||||
| 		cm := newConfigMap("cluster-addons") | ||||
|  | ||||
| 		for _, addon := range addons { | ||||
| 			buf := &bytes.Buffer{} | ||||
| 			err := addon.Execute(buf, clusterAsMap, nil) | ||||
|  | ||||
| 			if err != nil { | ||||
| 				log.Printf("cluster %q: addons %q: failed to render %q: %v", | ||||
| 					clusterName, cluster.Addons, addon.Name, err) | ||||
| 				http.Error(w, "", http.StatusServiceUnavailable) | ||||
| 				return | ||||
| 			} | ||||
|  | ||||
| 			cm.Data[addon.Name] = buf.String() | ||||
| 		} | ||||
|  | ||||
| 		switch format { | ||||
| 		case "yaml": | ||||
| 			for name, data := range cm.Data { | ||||
| 				w.Write([]byte("\n# addon: " + name + "\n---\n\n")) | ||||
| 				w.Write([]byte(data)) | ||||
| 			} | ||||
|  | ||||
| 		default: | ||||
| 			yaml.NewEncoder(w).Encode(cm) | ||||
| 		} | ||||
|  | ||||
| 	default: | ||||
| 		http.NotFound(w, r) | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										7
									
								
								cmd/dkl-dir2config/main.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								cmd/dkl-dir2config/main.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,7 @@ | ||||
| package main | ||||
|  | ||||
| import "fmt" | ||||
|  | ||||
| func main() { | ||||
| 	fmt.Println("vim-go") | ||||
| } | ||||
							
								
								
									
										402
									
								
								cmd/dkl-dir2config/render-context.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										402
									
								
								cmd/dkl-dir2config/render-context.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,402 @@ | ||||
| package main | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"crypto/sha256" | ||||
| 	"encoding/hex" | ||||
| 	"encoding/json" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"log" | ||||
| 	"path" | ||||
| 	"path/filepath" | ||||
|  | ||||
| 	cfsslconfig "github.com/cloudflare/cfssl/config" | ||||
| 	"github.com/cloudflare/cfssl/csr" | ||||
| 	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) (ctx *renderContext, err error) { | ||||
| 	cluster := cfg.Cluster(host.Cluster) | ||||
| 	if cluster == nil { | ||||
| 		err = fmt.Errorf("no cluster named %q", host.Cluster) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	group := cfg.Group(host.Group) | ||||
| 	if group == nil { | ||||
| 		err = fmt.Errorf("no group named %q", host.Group) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	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, | ||||
| 	}, nil | ||||
| } | ||||
|  | ||||
| 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() | ||||
|  | ||||
| 	secretData, err := ctx.secretData() | ||||
| 	if err != nil { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	templateFuncs := ctx.templateFuncs(secretData, ctxMap) | ||||
|  | ||||
| 	render := func(what string, t *clustersconfig.Template) (s string, err error) { | ||||
| 		buf := &bytes.Buffer{} | ||||
| 		err = t.Execute(buf, ctxMap, templateFuncs) | ||||
| 		if err != nil { | ||||
| 			log.Printf("host %s: failed to render %s [%q]: %v", ctx.Host.Name, what, t.Name, err) | ||||
| 			return | ||||
| 		} | ||||
|  | ||||
| 		s = buf.String() | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	extraFuncs := ctx.templateFuncs(secretData, ctxMap) | ||||
|  | ||||
| 	extraFuncs["static_pods"] = func(name string) (string, error) { | ||||
| 		t := ctx.clusterConfig.StaticPodsTemplate(name) | ||||
| 		if t == nil { | ||||
| 			return "", fmt.Errorf("no static pods template named %q", name) | ||||
| 		} | ||||
|  | ||||
| 		return render("static pods", t) | ||||
| 	} | ||||
|  | ||||
| 	buf := bytes.NewBuffer(make([]byte, 0, 4096)) | ||||
| 	if err = ctx.ConfigTemplate.Execute(buf, ctxMap, extraFuncs); err != nil { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	if secretData.Changed() { | ||||
| 		err = secretData.Save() | ||||
| 		if err != nil { | ||||
| 			return | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	ba = buf.Bytes() | ||||
|  | ||||
| 	cfg = &config.Config{} | ||||
|  | ||||
| 	if err = yaml.Unmarshal(buf.Bytes(), cfg); err != nil { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	return | ||||
| } | ||||
|  | ||||
| func (ctx *renderContext) secretData() (data *SecretData, err error) { | ||||
| 	var sslCfg *cfsslconfig.Config | ||||
|  | ||||
| 	if ctx.clusterConfig.SSLConfig == "" { | ||||
| 		sslCfg = &cfsslconfig.Config{} | ||||
| 	} else { | ||||
| 		sslCfg, err = cfsslconfig.LoadConfig([]byte(ctx.clusterConfig.SSLConfig)) | ||||
| 		if err != nil { | ||||
| 			return | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	data, err = loadSecretData(sslCfg) | ||||
| 	return | ||||
| } | ||||
|  | ||||
| func (ctx *renderContext) StaticPods() (ba []byte, err error) { | ||||
| 	secretData, err := ctx.secretData() | ||||
| 	if err != nil { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	if ctx.StaticPodsTemplate == nil { | ||||
| 		err = notFoundError{fmt.Sprintf("static-pods %q", ctx.Group.StaticPods)} | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	ctxMap := ctx.asMap() | ||||
|  | ||||
| 	buf := bytes.NewBuffer(make([]byte, 0, 4096)) | ||||
| 	if err = ctx.StaticPodsTemplate.Execute(buf, ctxMap, ctx.templateFuncs(secretData, ctxMap)); err != nil { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	if secretData.Changed() { | ||||
| 		err = secretData.Save() | ||||
| 		if err != nil { | ||||
| 			return | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	ba = buf.Bytes() | ||||
| 	return | ||||
| } | ||||
|  | ||||
| func (ctx *renderContext) templateFuncs(secretData *SecretData, ctxMap map[string]interface{}) map[string]interface{} { | ||||
| 	cluster := ctx.Cluster.Name | ||||
|  | ||||
| 	getKeyCert := func(name string) (kc *KeyCert, err error) { | ||||
| 		req := ctx.clusterConfig.CSR(name) | ||||
| 		if req == nil { | ||||
| 			err = errors.New("no such certificate request") | ||||
| 			return | ||||
| 		} | ||||
|  | ||||
| 		if req.CA == "" { | ||||
| 			err = errors.New("CA not defined") | ||||
| 			return | ||||
| 		} | ||||
|  | ||||
| 		buf := &bytes.Buffer{} | ||||
| 		err = req.Execute(buf, ctxMap, nil) | ||||
| 		if err != nil { | ||||
| 			return | ||||
| 		} | ||||
|  | ||||
| 		certReq := &csr.CertificateRequest{ | ||||
| 			KeyRequest: csr.NewBasicKeyRequest(), | ||||
| 		} | ||||
|  | ||||
| 		err = json.Unmarshal(buf.Bytes(), certReq) | ||||
| 		if err != nil { | ||||
| 			log.Print("unmarshal failed on: ", buf) | ||||
| 			return | ||||
| 		} | ||||
|  | ||||
| 		if req.PerHost { | ||||
| 			name = name + "/" + ctx.Host.Name | ||||
| 		} | ||||
|  | ||||
| 		return secretData.KeyCert(cluster, req.CA, name, req.Profile, req.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{}{ | ||||
| 		"token": func(name string) (s string, err error) { | ||||
| 			return secretData.Token(cluster, name) | ||||
| 		}, | ||||
|  | ||||
| 		"ca_key": func(name string) (s string, err error) { | ||||
| 			ca, err := secretData.CA(cluster, name) | ||||
| 			if err != nil { | ||||
| 				return | ||||
| 			} | ||||
|  | ||||
| 			s = string(ca.Key) | ||||
| 			return | ||||
| 		}, | ||||
|  | ||||
| 		"ca_crt": func(name string) (s string, err error) { | ||||
| 			ca, err := secretData.CA(cluster, name) | ||||
| 			if err != nil { | ||||
| 				return | ||||
| 			} | ||||
|  | ||||
| 			s = string(ca.Cert) | ||||
| 			return | ||||
| 		}, | ||||
|  | ||||
| 		"ca_dir": func(name string) (s string, err error) { | ||||
| 			ca, err := secretData.CA(cluster, name) | ||||
| 			if err != nil { | ||||
| 				return | ||||
| 			} | ||||
|  | ||||
| 			dir := "/" + path.Join("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(name string) (s string, err error) { | ||||
| 			kc, err := getKeyCert(name) | ||||
| 			if err != nil { | ||||
| 				return | ||||
| 			} | ||||
|  | ||||
| 			s = string(kc.Key) | ||||
| 			return | ||||
| 		}, | ||||
|  | ||||
| 		"tls_crt": func(name string) (s string, err error) { | ||||
| 			kc, err := getKeyCert(name) | ||||
| 			if err != nil { | ||||
| 				return | ||||
| 			} | ||||
|  | ||||
| 			s = string(kc.Cert) | ||||
| 			return | ||||
| 		}, | ||||
|  | ||||
| 		"tls_dir": func(name string) (s string, err error) { | ||||
| 			csr := ctx.clusterConfig.CSR(name) | ||||
| 			if csr == nil { | ||||
| 				err = fmt.Errorf("no CSR named %q", name) | ||||
| 				return | ||||
| 			} | ||||
|  | ||||
| 			ca, err := secretData.CA(cluster, csr.CA) | ||||
| 			if err != nil { | ||||
| 				return | ||||
| 			} | ||||
|  | ||||
| 			kc, err := getKeyCert(name) | ||||
| 			if err != nil { | ||||
| 				return | ||||
| 			} | ||||
|  | ||||
| 			dir := "/" + path.Join("etc", "tls", name) | ||||
|  | ||||
| 			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), | ||||
| 				}, | ||||
| 			}) | ||||
| 		}, | ||||
|  | ||||
| 		"hosts_of_group": func() (hosts []interface{}) { | ||||
| 			hosts = make([]interface{}, 0) | ||||
|  | ||||
| 			for _, host := range ctx.clusterConfig.Hosts { | ||||
| 				if host.Group != ctx.Host.Group { | ||||
| 					continue | ||||
| 				} | ||||
|  | ||||
| 				hosts = append(hosts, asMap(host)) | ||||
| 			} | ||||
|  | ||||
| 			return hosts | ||||
| 		}, | ||||
|  | ||||
| 		"hosts_of_group_count": func() (count int) { | ||||
| 			for _, host := range ctx.clusterConfig.Hosts { | ||||
| 				if host.Group != ctx.Host.Group { | ||||
| 					continue | ||||
| 				} | ||||
|  | ||||
| 				count++ | ||||
| 			} | ||||
| 			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{} { | ||||
| 	result := asMap(ctx) | ||||
|  | ||||
| 	// 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 | ||||
| } | ||||
|  | ||||
| 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 { | ||||
| 		panic(err) // shouldn't happen | ||||
| 	} | ||||
|  | ||||
| 	return result | ||||
| } | ||||
		Reference in New Issue
	
	Block a user