local-server/cmd/dkl-dir2config/main.go

210 lines
4.4 KiB
Go
Raw Normal View History

2018-12-10 10:59:24 +00:00
package main
2018-12-10 13:44:05 +00:00
import (
"flag"
"io/fs"
2018-12-10 13:44:05 +00:00
"log"
"os"
"path/filepath"
2018-12-10 13:44:05 +00:00
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing"
2018-12-10 13:44:05 +00:00
yaml "gopkg.in/yaml.v2"
2018-12-10 21:23:20 +00:00
2022-04-28 01:33:19 +00:00
"novit.tech/direktil/pkg/localconfig"
"novit.tech/direktil/local-server/pkg/clustersconfig"
2018-12-10 13:44:05 +00:00
)
2023-11-25 15:21:47 +00:00
var Version = "dev"
2018-12-10 13:44:05 +00:00
var (
Debug = false
dir = flag.String("in", ".", "Source directory")
outPath = flag.String("out", "config.yaml", "Output file")
base fs.FS
2019-01-21 03:53:45 +00:00
2018-12-10 13:44:05 +00:00
src *clustersconfig.Config
dst *localconfig.Config
)
2018-12-10 10:59:24 +00:00
func init() {
flag.BoolVar(&Debug, "debug", Debug, "debug")
}
2019-01-21 03:53:45 +00:00
func loadSrc() {
2018-12-10 13:44:05 +00:00
var err error
src, err = clustersconfig.FromDir(read, assemble, listBase, listMerged)
2018-12-10 13:44:05 +00:00
if err != nil {
log.Fatal("failed to load config from dir: ", err)
}
2019-01-21 03:53:45 +00:00
}
func main() {
flag.Parse()
2019-03-01 01:37:51 +00:00
log.SetFlags(log.Ltime | log.Lmicroseconds | log.Lshortfile)
base = os.DirFS(*dir)
searchList = append(searchList, fsFS{base})
openIncludes()
2019-01-21 03:53:45 +00:00
loadSrc()
2018-12-10 13:44:05 +00:00
dst = &localconfig.Config{
SSLConfig: src.SSLConfig,
}
// ----------------------------------------------------------------------
for _, cluster := range src.Clusters {
dst.Clusters = append(dst.Clusters, &localconfig.Cluster{
Name: cluster.Name,
Addons: renderAddons(cluster),
2018-12-10 13:44:05 +00:00
})
}
// ----------------------------------------------------------------------
for _, host := range src.Hosts {
2019-01-21 03:53:45 +00:00
log.Print("rendering host ", host.Name)
2018-12-10 13:44:05 +00:00
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"] == "" {
2019-02-06 03:27:20 +00:00
// default modules' version to kernel's version
ctx.Host.Versions["modules"] = ctx.Host.Kernel
2019-02-06 03:27:20 +00:00
}
2024-04-15 13:32:43 +00:00
renderedHost := &localconfig.Host{
2018-12-10 13:44:05 +00:00
Name: host.Name,
2019-12-16 07:00:57 +00:00
ClusterName: ctx.Cluster.Name,
Labels: ctx.Labels,
Annotations: ctx.Annotations,
2018-12-10 13:44:05 +00:00
MACs: macs,
IPs: ips,
IPXE: ctx.Host.IPXE, // TODO render
2018-12-10 13:44:05 +00:00
Kernel: ctx.Host.Kernel,
Initrd: ctx.Host.Initrd,
Versions: ctx.Host.Versions,
2018-12-10 13:44:05 +00:00
2022-04-28 01:33:19 +00:00
BootstrapConfig: ctx.BootstrapConfig(),
Config: ctx.Config(),
2024-04-15 13:32:43 +00:00
}
if host.Template {
dst.HostTemplates = append(dst.HostTemplates, renderedHost)
} else {
dst.Hosts = append(dst.Hosts, renderedHost)
}
2018-12-10 13:44:05 +00:00
}
// ----------------------------------------------------------------------
out, err := os.Create(*outPath)
if err != nil {
log.Fatal("failed to create output: ", err)
}
defer out.Close()
2023-11-25 15:21:47 +00:00
out.Write([]byte("# dkl-dir2config " + Version + "\n"))
2018-12-10 13:44:05 +00:00
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)})
}
}
}