Initial commit

This commit is contained in:
Mikaël Cluseau
2018-06-12 20:52:20 +11:00
commit 1293dd0444
13 changed files with 1157 additions and 0 deletions

View File

@ -0,0 +1,209 @@
package clustersconfig
import (
"fmt"
"io"
"io/ioutil"
"net"
"strings"
"text/template"
yaml "gopkg.in/yaml.v2"
)
type Config struct {
Hosts []*Host
Groups []*Group
Clusters []*Cluster
Configs []*Template
StaticPods []*Template `yaml:"static_pods"`
}
func FromBytes(data []byte) (*Config, error) {
config := &Config{}
if err := yaml.Unmarshal(data, config); err != nil {
return nil, err
}
return config, nil
}
func FromFile(path string) (*Config, error) {
ba, err := ioutil.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) Group(name string) *Group {
for _, group := range c.Groups {
if group.Name == name {
return group
}
}
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) StaticPodsTemplate(name string) *Template {
for _, s := range c.StaticPods {
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 ioutil.WriteFile(path, ba, 0600)
}
type Template struct {
Name string
Template string
parsedTemplate *template.Template
}
func (t *Template) Execute(wr io.Writer, data interface{}, extraFuncs map[string]interface{}) error {
if t.parsedTemplate == nil {
var templateFuncs = map[string]interface{}{
"indent": func(indent, s string) (indented string) {
indented = indent + strings.Replace(s, "\n", "\n"+indent, -1)
return
},
}
for name, f := range extraFuncs {
templateFuncs[name] = f
}
tmpl, err := template.New("tmpl").
Funcs(templateFuncs).
Parse(t.Template)
if err != nil {
return err
}
t.parsedTemplate = tmpl
}
return t.parsedTemplate.Execute(wr, data)
}
// Host represents a host served by this server.
type Host struct {
Name string
MAC string
IP string
IPs []string
Cluster string
Group string
Vars Vars
}
// Group represents a group of hosts and provides their configuration.
type Group struct {
Name string
Master bool
IPXE string
Kernel string
Initrd string
Config string
StaticPods string `yaml:"static_pods"`
Versions map[string]string
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 {
Name string
Domain 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
}

129
clustersconfig/dir.go Normal file
View File

@ -0,0 +1,129 @@
package clustersconfig
import (
"io/ioutil"
"path"
"path/filepath"
"strings"
yaml "gopkg.in/yaml.v2"
)
func FromDir(dirPath string) (*Config, error) {
config := &Config{}
store := dirStore{dirPath}
load := func(dir, name string, out interface{}) error {
ba, err := store.Get(path.Join(dir, name))
if err != nil {
return err
}
return yaml.Unmarshal(ba, out)
}
// load clusters
names, err := store.List("clusters")
if err != nil {
return nil, err
}
for _, name := range names {
cluster := &Cluster{Name: name}
if err := load("clusters", name, cluster); err != nil {
return nil, err
}
config.Clusters = append(config.Clusters, cluster)
}
// load groups
names, err = store.List("groups")
if err != nil {
return nil, err
}
for _, name := range names {
o := &Group{Name: name}
if err := load("groups", name, o); err != nil {
return nil, err
}
config.Groups = append(config.Groups, o)
}
// load hosts
names, err = store.List("hosts")
if err != nil {
return nil, err
}
for _, name := range names {
o := &Host{Name: name}
if err := load("hosts", name, o); err != nil {
return nil, err
}
config.Hosts = append(config.Hosts, o)
}
// load config templates
loadTemplates := func(dir string, templates *[]*Template) error {
names, err = store.List(dir)
if err != nil {
return err
}
for _, name := range names {
ba, err := store.Get(path.Join(dir, name))
if err != nil {
return err
}
o := &Template{Name: name, Template: string(ba)}
*templates = append(*templates, o)
}
return nil
}
if err := loadTemplates("configs", &config.Configs); err != nil {
return nil, err
}
if err := loadTemplates("static-pods", &config.StaticPods); err != nil {
return nil, err
}
return config, nil
}
type dirStore struct {
path string
}
// Names is part of the kvStore interface
func (b *dirStore) List(prefix string) ([]string, error) {
files, err := filepath.Glob(filepath.Join(b.path, filepath.Join(path.Split(prefix)), "*.yaml"))
if err != nil {
return nil, err
}
names := make([]string, 0, len(files))
for _, f := range files {
f2 := strings.TrimSuffix(f, ".yaml")
f2 = filepath.Base(f2)
if f2[0] == '.' { // ignore hidden files
continue
}
names = append(names, f2)
}
return names, nil
}
// Load is part of the DataBackend interface
func (b *dirStore) Get(key string) ([]byte, error) {
return ioutil.ReadFile(filepath.Join(b.path, filepath.Join(path.Split(key))+".yaml"))
}