210 lines
4.4 KiB
Go
210 lines
4.4 KiB
Go
package main
|
|
|
|
import (
|
|
"flag"
|
|
"io/fs"
|
|
"log"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"github.com/go-git/go-git/v5"
|
|
"github.com/go-git/go-git/v5/plumbing"
|
|
yaml "gopkg.in/yaml.v2"
|
|
|
|
"novit.tech/direktil/pkg/localconfig"
|
|
|
|
"novit.tech/direktil/local-server/pkg/clustersconfig"
|
|
)
|
|
|
|
var Version = "dev"
|
|
|
|
var (
|
|
Debug = false
|
|
|
|
dir = flag.String("in", ".", "Source directory")
|
|
outPath = flag.String("out", "config.yaml", "Output file")
|
|
|
|
base fs.FS
|
|
|
|
src *clustersconfig.Config
|
|
dst *localconfig.Config
|
|
)
|
|
|
|
func init() {
|
|
flag.BoolVar(&Debug, "debug", Debug, "debug")
|
|
}
|
|
|
|
func loadSrc() {
|
|
var err error
|
|
src, err = clustersconfig.FromDir(read, assemble, listBase, listMerged)
|
|
if err != nil {
|
|
log.Fatal("failed to load config from dir: ", err)
|
|
}
|
|
}
|
|
|
|
func main() {
|
|
flag.Parse()
|
|
|
|
log.SetFlags(log.Ltime | log.Lmicroseconds | log.Lshortfile)
|
|
|
|
base = os.DirFS(*dir)
|
|
searchList = append(searchList, fsFS{base})
|
|
|
|
openIncludes()
|
|
|
|
loadSrc()
|
|
|
|
dst = &localconfig.Config{
|
|
SSLConfig: src.SSLConfig,
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
for _, cluster := range src.Clusters {
|
|
dst.Clusters = append(dst.Clusters, &localconfig.Cluster{
|
|
Name: cluster.Name,
|
|
Addons: renderAddons(cluster),
|
|
})
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
for _, host := range src.Hosts {
|
|
log.Print("rendering host ", host.Name)
|
|
ctx, err := newRenderContext(host, src)
|
|
|
|
if err != nil {
|
|
log.Fatal("failed to create render context for host ", host.Name, ": ", err)
|
|
}
|
|
|
|
macs := make([]string, 0)
|
|
if host.MAC != "" {
|
|
macs = append(macs, host.MAC)
|
|
}
|
|
|
|
ips := make([]string, 0)
|
|
if len(host.IP) != 0 {
|
|
ips = append(ips, host.IP)
|
|
}
|
|
ips = append(ips, host.IPs...)
|
|
|
|
if ctx.Host.Versions["modules"] == "" {
|
|
// default modules' version to kernel's version
|
|
ctx.Host.Versions["modules"] = ctx.Host.Kernel
|
|
}
|
|
|
|
renderedHost := &localconfig.Host{
|
|
Name: host.Name,
|
|
|
|
ClusterName: ctx.Cluster.Name,
|
|
|
|
Labels: ctx.Labels,
|
|
Annotations: ctx.Annotations,
|
|
|
|
MACs: macs,
|
|
IPs: ips,
|
|
|
|
IPXE: ctx.Host.IPXE, // TODO render
|
|
|
|
Kernel: ctx.Host.Kernel,
|
|
Initrd: ctx.Host.Initrd,
|
|
Versions: ctx.Host.Versions,
|
|
|
|
BootstrapConfig: ctx.BootstrapConfig(),
|
|
Config: ctx.Config(),
|
|
}
|
|
|
|
if host.Template {
|
|
dst.HostTemplates = append(dst.HostTemplates, renderedHost)
|
|
} else {
|
|
dst.Hosts = append(dst.Hosts, renderedHost)
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
out, err := os.Create(*outPath)
|
|
if err != nil {
|
|
log.Fatal("failed to create output: ", err)
|
|
}
|
|
|
|
defer out.Close()
|
|
|
|
out.Write([]byte("# dkl-dir2config " + Version + "\n"))
|
|
|
|
if err = yaml.NewEncoder(out).Encode(dst); err != nil {
|
|
log.Fatal("failed to render output: ", err)
|
|
}
|
|
|
|
}
|
|
|
|
func cfgPath(subPath string) string { return filepath.Join(*dir, subPath) }
|
|
|
|
func openIncludes() {
|
|
includesFile, err := base.Open("includes.yaml")
|
|
|
|
if os.IsNotExist(err) {
|
|
return
|
|
}
|
|
if err != nil {
|
|
log.Fatal("failed to open includes: ", err)
|
|
}
|
|
|
|
includes := make([]struct {
|
|
Path string
|
|
Branch string
|
|
Tag string
|
|
}, 0)
|
|
|
|
err = yaml.NewDecoder(includesFile).Decode(&includes)
|
|
if err != nil {
|
|
log.Fatal("failed to parse includes: ", err)
|
|
}
|
|
|
|
for _, include := range includes {
|
|
switch {
|
|
case include.Branch != "" || include.Tag != "":
|
|
p := cfgPath(include.Path) // FIXME parse git path to allow remote repos
|
|
|
|
var rev plumbing.Revision
|
|
|
|
switch {
|
|
case include.Branch != "":
|
|
log.Printf("opening include path %q as git, branch %q", p, include.Branch)
|
|
rev = plumbing.Revision(plumbing.NewBranchReferenceName(include.Branch))
|
|
|
|
case include.Tag != "":
|
|
log.Printf("opening include path %q as git, tag %q", p, include.Branch)
|
|
rev = plumbing.Revision(plumbing.NewTagReferenceName(include.Branch))
|
|
}
|
|
|
|
repo, err := git.PlainOpen(p)
|
|
if err != nil {
|
|
log.Fatal("failed to open: ", err)
|
|
}
|
|
|
|
revH, err := repo.ResolveRevision(rev)
|
|
if err != nil {
|
|
log.Fatalf("failed to resolve revision %s: %v", rev, err)
|
|
}
|
|
|
|
log.Print(" -> resolved to commit ", *revH)
|
|
|
|
commit, err := repo.CommitObject(*revH)
|
|
if err != nil {
|
|
log.Fatal("failed to get commit object: ", err)
|
|
}
|
|
|
|
tree, err := commit.Tree()
|
|
if err != nil {
|
|
log.Fatal("failed to open git tree: ", err)
|
|
}
|
|
|
|
searchList = append(searchList, gitFS{tree})
|
|
|
|
default:
|
|
p := cfgPath(include.Path)
|
|
log.Printf("opening include path %q as raw dir", p)
|
|
|
|
searchList = append(searchList, fsFS{os.DirFS(p)})
|
|
}
|
|
}
|
|
}
|