260 lines
4.8 KiB
Go
260 lines
4.8 KiB
Go
package clustersconfig
|
|
|
|
import (
|
|
"flag"
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"net"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"text/template"
|
|
|
|
yaml "gopkg.in/yaml.v2"
|
|
)
|
|
|
|
var (
|
|
templateDetailsDir = flag.String("template-details-dir",
|
|
filepath.Join(os.TempDir(), "dkl-dir2config"),
|
|
"write details of template execute in this dir")
|
|
|
|
templateID = 0
|
|
)
|
|
|
|
type Config struct {
|
|
Hosts []*Host
|
|
Clusters []*Cluster
|
|
Configs []*Template
|
|
StaticPods map[string][]*Template `yaml:"static_pods"`
|
|
Addons map[string][]*Template
|
|
SSLConfig string `yaml:"ssl_config"`
|
|
CertRequests []*CertRequest `yaml:"cert_requests"`
|
|
}
|
|
|
|
func FromBytes(data []byte) (*Config, error) {
|
|
config := &Config{Addons: make(map[string][]*Template)}
|
|
if err := yaml.Unmarshal(data, config); err != nil {
|
|
return nil, err
|
|
}
|
|
return config, nil
|
|
}
|
|
|
|
func FromFile(path string) (*Config, error) {
|
|
ba, err := os.ReadFile(path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return FromBytes(ba)
|
|
}
|
|
|
|
func (c *Config) Host(name string) *Host {
|
|
for _, host := range c.Hosts {
|
|
if host.Name == name {
|
|
return host
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (c *Config) HostByIP(ip string) *Host {
|
|
for _, host := range c.Hosts {
|
|
if host.IP == ip {
|
|
return host
|
|
}
|
|
|
|
for _, otherIP := range host.IPs {
|
|
if otherIP == ip {
|
|
return host
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (c *Config) HostByMAC(mac string) *Host {
|
|
// a bit of normalization
|
|
mac = strings.Replace(strings.ToLower(mac), "-", ":", -1)
|
|
|
|
for _, host := range c.Hosts {
|
|
if strings.ToLower(host.MAC) == mac {
|
|
return host
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (c *Config) Cluster(name string) *Cluster {
|
|
for _, cluster := range c.Clusters {
|
|
if cluster.Name == name {
|
|
return cluster
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (c *Config) ConfigTemplate(name string) *Template {
|
|
for _, cfg := range c.Configs {
|
|
if cfg.Name == name {
|
|
return cfg
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (c *Config) CSR(name string) *CertRequest {
|
|
for _, s := range c.CertRequests {
|
|
if s.Name == name {
|
|
return s
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (c *Config) SaveTo(path string) error {
|
|
ba, err := yaml.Marshal(c)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return os.WriteFile(path, ba, 0600)
|
|
}
|
|
|
|
type Template struct {
|
|
Name string
|
|
Template string
|
|
}
|
|
|
|
func (t *Template) Execute(contextName, elementName string, wr io.Writer, data interface{}, extraFuncs map[string]interface{}) error {
|
|
var templateFuncs = map[string]interface{}{
|
|
"indent": func(indent, s string) (indented string) {
|
|
indented = indent + strings.Replace(s, "\n", "\n"+indent, -1)
|
|
return
|
|
},
|
|
"yaml": func(v any) (s string, err error) {
|
|
ba, err := yaml.Marshal(v)
|
|
s = string(ba)
|
|
return
|
|
},
|
|
}
|
|
|
|
for name, f := range extraFuncs {
|
|
templateFuncs[name] = f
|
|
}
|
|
|
|
tmpl, err := template.New(t.Name).
|
|
Funcs(templateFuncs).
|
|
Parse(t.Template)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if *templateDetailsDir != "" {
|
|
templateID++
|
|
|
|
base := filepath.Join(*templateDetailsDir, contextName, fmt.Sprintf("%s-%03d", elementName, templateID))
|
|
os.MkdirAll(base, 0700)
|
|
|
|
base += string(filepath.Separator)
|
|
log.Print("writing template details: ", base, "{in,data,out}")
|
|
|
|
if err := os.WriteFile(base+"in", []byte(t.Template), 0600); err != nil {
|
|
return err
|
|
}
|
|
|
|
yamlBytes, err := yaml.Marshal(data)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := os.WriteFile(base+"data", yamlBytes, 0600); err != nil {
|
|
return err
|
|
}
|
|
|
|
out, err := os.Create(base + "out")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
defer out.Close()
|
|
|
|
wr = io.MultiWriter(wr, out)
|
|
}
|
|
|
|
return tmpl.Execute(wr, data)
|
|
}
|
|
|
|
// Host represents a host served by this server.
|
|
type Host struct {
|
|
WithRev
|
|
|
|
Template bool `json:",omitempty"`
|
|
|
|
Name string
|
|
Labels map[string]string `json:",omitempty"`
|
|
Annotations map[string]string `json:",omitempty"`
|
|
|
|
MAC string `json:",omitempty"`
|
|
IP string
|
|
IPs []string `json:",omitempty"`
|
|
Cluster string
|
|
Group string
|
|
|
|
Net string
|
|
IPFrom map[string]string `json:",omitempty" yaml:"ip_from"`
|
|
|
|
IPXE string `json:",omitempty"`
|
|
Kernel string
|
|
Initrd string
|
|
BootstrapConfig string `yaml:"bootstrap_config"`
|
|
Config string
|
|
Versions map[string]string
|
|
|
|
StaticPods string `yaml:"static_pods"`
|
|
|
|
Vars Vars
|
|
}
|
|
|
|
// Vars store user-defined key-values
|
|
type Vars map[string]interface{}
|
|
|
|
// Cluster represents a cluster of hosts, allowing for cluster-wide variables.
|
|
type Cluster struct {
|
|
WithRev
|
|
|
|
Name string
|
|
Labels map[string]string
|
|
Annotations map[string]string
|
|
|
|
Domain string
|
|
Addons []string
|
|
Subnets struct {
|
|
Services string
|
|
Pods string
|
|
}
|
|
|
|
Vars Vars
|
|
}
|
|
|
|
func (c *Cluster) KubernetesSvcIP() net.IP {
|
|
return c.NthSvcIP(1)
|
|
}
|
|
|
|
func (c *Cluster) DNSSvcIP() net.IP {
|
|
return c.NthSvcIP(2)
|
|
}
|
|
|
|
func (c *Cluster) NthSvcIP(n byte) net.IP {
|
|
_, cidr, err := net.ParseCIDR(c.Subnets.Services)
|
|
if err != nil {
|
|
panic(fmt.Errorf("invalid services CIDR: %v", err))
|
|
}
|
|
|
|
ip := cidr.IP
|
|
ip[len(ip)-1] += n
|
|
|
|
return ip
|
|
}
|