commit
c58ce7e2ab
5 changed files with 113 additions and 45 deletions
1
.travis.yml
Normal file
1
.travis.yml
Normal file
|
@ -0,0 +1 @@
|
|||
language: go
|
|
@ -1,6 +1,8 @@
|
|||
KonTemplate - A simple Kubernetes templater
|
||||
===========================================
|
||||
|
||||
[![Build Status](https://travis-ci.org/tazjin/kontemplate.svg?branch=master)](https://travis-ci.org/tazjin/kontemplate)
|
||||
|
||||
I made this tool out of frustration with the available ways to template Kubernetes resource files. All I want out of
|
||||
such a tool is a way to specify lots of resources with placeholders that get filled in with specific values, based on
|
||||
which context (i.e. k8s cluster) is specified.
|
||||
|
|
|
@ -2,9 +2,10 @@ package context
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/polydawn/meep"
|
||||
"io/ioutil"
|
||||
"path"
|
||||
|
||||
"github.com/polydawn/meep"
|
||||
)
|
||||
|
||||
type ResourceSet struct {
|
||||
|
|
72
main.go
72
main.go
|
@ -4,36 +4,70 @@ import (
|
|||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/polydawn/meep"
|
||||
"github.com/tazjin/kontemplate/context"
|
||||
"github.com/tazjin/kontemplate/templater"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
func main() {
|
||||
args := os.Args[1:]
|
||||
if len(args) == 0 {
|
||||
fmt.Fprintln(os.Stderr, "Usage: kontemplate <cluster-config>")
|
||||
os.Exit(1)
|
||||
app := cli.NewApp()
|
||||
|
||||
app.Name = "kontemplate"
|
||||
app.Usage = "simple Kubernetes resource templating"
|
||||
app.Version = "0.0.1"
|
||||
|
||||
app.Commands = []cli.Command{
|
||||
ApplyCommand(),
|
||||
}
|
||||
|
||||
c, err := context.LoadContextFromFile(os.Args[1])
|
||||
app.Run(os.Args)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "%v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
func ApplyCommand() cli.Command {
|
||||
return cli.Command{
|
||||
Name: "run",
|
||||
Usage: "Interpolate and print templates",
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "file, f",
|
||||
Usage: "Cluster configuration file to use",
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
Name: "limit, l",
|
||||
Usage: "Limit templating to certain resource sets",
|
||||
},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
limit := c.StringSlice("limit")
|
||||
f := c.String("file")
|
||||
|
||||
fmt.Fprintf(os.Stderr, "Applying cluster %s\n", c.Name)
|
||||
if f == "" {
|
||||
return meep.New(
|
||||
&meep.ErrInvalidParam{
|
||||
Param: "file",
|
||||
Reason: "Cluster config file must be specified",
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
for _, rs := range c.ResourceSets {
|
||||
fmt.Fprintf(os.Stderr, "Applying resource %s with values %v\n", rs.Name, rs.Values)
|
||||
resources, err := templater.LoadAndPrepareTemplates(c)
|
||||
ctx, err := context.LoadContextFromFile(f)
|
||||
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, r := range resources {
|
||||
fmt.Print(r)
|
||||
}
|
||||
resources, err := templater.LoadAndPrepareTemplates(&limit, ctx)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, r := range resources {
|
||||
fmt.Println(r)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,37 +24,67 @@ type TemplatingError struct {
|
|||
meep.AllTraits
|
||||
}
|
||||
|
||||
func LoadAndPrepareTemplates(c *context.Context) ([]string, error) {
|
||||
output := make([]string, 0)
|
||||
|
||||
func LoadAndPrepareTemplates(limit *[]string, c *context.Context) (output []string, err error) {
|
||||
for _, rs := range c.ResourceSets {
|
||||
fmt.Fprintf(os.Stderr, "Loading resources for %s\n", rs.Name)
|
||||
if resourceSetIncluded(limit, &rs.Name) {
|
||||
err = processResourceSet(c, &rs, &output)
|
||||
|
||||
rp := path.Join(c.BaseDir, rs.Name)
|
||||
files, err := ioutil.ReadDir(rp)
|
||||
|
||||
if err != nil {
|
||||
return nil, meep.New(
|
||||
&TemplateNotFoundError{Name: rs.Name},
|
||||
meep.Cause(err),
|
||||
)
|
||||
}
|
||||
|
||||
for _, file := range files {
|
||||
if !file.IsDir() && isResourceFile(file) {
|
||||
p := path.Join(rp, file.Name())
|
||||
o, err := templateFile(c, &rs, p)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
output = append(output, o)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return output, nil
|
||||
return
|
||||
}
|
||||
|
||||
func resourceSetIncluded(limit *[]string, resourceSetName *string) bool {
|
||||
if len(*limit) == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
for _, name := range *limit {
|
||||
if name == *resourceSetName {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func processResourceSet(c *context.Context, rs *context.ResourceSet, output *[]string) error {
|
||||
fmt.Fprintf(os.Stderr, "Loading resources for %s\n", rs.Name)
|
||||
|
||||
rp := path.Join(c.BaseDir, rs.Name)
|
||||
files, err := ioutil.ReadDir(rp)
|
||||
|
||||
err = processFiles(c, rs, rp, files, output)
|
||||
|
||||
if err != nil {
|
||||
return meep.New(
|
||||
&TemplateNotFoundError{Name: rs.Name},
|
||||
meep.Cause(err),
|
||||
)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func processFiles(c *context.Context, rs *context.ResourceSet, rp string, files []os.FileInfo, output *[]string) error {
|
||||
for _, file := range files {
|
||||
if !file.IsDir() && isResourceFile(file) {
|
||||
p := path.Join(rp, file.Name())
|
||||
o, err := templateFile(c, rs, p)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*output = append(*output, o)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func templateFile(c *context.Context, rs *context.ResourceSet, filename string) (string, error) {
|
||||
|
|
Loading…
Reference in a new issue