2017-02-08 11:51:08 +01:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2017-02-08 11:55:59 +01:00
|
|
|
"os"
|
2017-02-08 15:32:44 +01:00
|
|
|
"os/exec"
|
2017-02-08 11:51:08 +01:00
|
|
|
|
2017-02-08 12:58:53 +01:00
|
|
|
"github.com/polydawn/meep"
|
2017-02-08 11:51:08 +01:00
|
|
|
"github.com/tazjin/kontemplate/context"
|
|
|
|
"github.com/tazjin/kontemplate/templater"
|
2017-02-08 12:58:53 +01:00
|
|
|
"github.com/urfave/cli"
|
2017-02-08 11:51:08 +01:00
|
|
|
)
|
|
|
|
|
2017-02-08 15:32:44 +01:00
|
|
|
type KubeCtlError struct {
|
|
|
|
meep.AllTraits
|
|
|
|
}
|
|
|
|
|
2017-02-08 11:51:08 +01:00
|
|
|
func main() {
|
2017-02-08 12:58:53 +01:00
|
|
|
app := cli.NewApp()
|
2017-02-08 11:51:08 +01:00
|
|
|
|
2017-02-08 12:58:53 +01:00
|
|
|
app.Name = "kontemplate"
|
|
|
|
app.Usage = "simple Kubernetes resource templating"
|
2017-02-08 22:15:07 +01:00
|
|
|
app.Version = "v1.0.0-beta1"
|
2017-02-08 11:51:08 +01:00
|
|
|
|
2017-02-08 12:58:53 +01:00
|
|
|
app.Commands = []cli.Command{
|
2017-02-08 15:32:44 +01:00
|
|
|
templateCommand(),
|
|
|
|
applyCommand(),
|
2017-02-08 16:19:10 +01:00
|
|
|
replaceCommand(),
|
2017-02-08 18:06:47 +01:00
|
|
|
deleteCommand(),
|
2017-02-08 11:51:08 +01:00
|
|
|
}
|
|
|
|
|
2017-02-08 12:58:53 +01:00
|
|
|
app.Run(os.Args)
|
|
|
|
}
|
|
|
|
|
2017-02-08 15:32:44 +01:00
|
|
|
func templateCommand() cli.Command {
|
2017-02-08 12:58:53 +01:00
|
|
|
return cli.Command{
|
2017-02-08 15:32:44 +01:00
|
|
|
Name: "template",
|
2017-02-08 12:58:53 +01:00
|
|
|
Usage: "Interpolate and print templates",
|
2017-02-08 15:32:44 +01:00
|
|
|
Flags: commonFlags(),
|
2017-02-08 12:58:53 +01:00
|
|
|
Action: func(c *cli.Context) error {
|
2017-02-08 16:44:55 +01:00
|
|
|
include := c.StringSlice("include")
|
|
|
|
exclude := c.StringSlice("exclude")
|
2017-02-08 16:19:10 +01:00
|
|
|
ctx, err := loadContext(c)
|
2017-02-08 16:44:55 +01:00
|
|
|
resources, err := templater.LoadAndPrepareTemplates(&include, &exclude, ctx)
|
2017-02-08 15:32:44 +01:00
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return err
|
2017-02-08 12:58:53 +01:00
|
|
|
}
|
|
|
|
|
2017-02-08 15:32:44 +01:00
|
|
|
for _, r := range resources {
|
|
|
|
fmt.Println(r)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func applyCommand() cli.Command {
|
|
|
|
dryRun := false
|
|
|
|
|
|
|
|
return cli.Command{
|
|
|
|
Name: "apply",
|
|
|
|
Usage: "Interpolate templates and run 'kubectl apply'",
|
|
|
|
Flags: append(commonFlags(), cli.BoolFlag{
|
|
|
|
Name: "dry-run",
|
|
|
|
Usage: "Only print objects that would be sent, without sending them",
|
|
|
|
Destination: &dryRun,
|
|
|
|
}),
|
|
|
|
Action: func(c *cli.Context) error {
|
2017-02-08 16:44:55 +01:00
|
|
|
include := c.StringSlice("include")
|
|
|
|
exclude := c.StringSlice("exclude")
|
2017-02-08 16:19:10 +01:00
|
|
|
ctx, err := loadContext(c)
|
2017-02-08 16:44:55 +01:00
|
|
|
resources, err := templater.LoadAndPrepareTemplates(&include, &exclude, ctx)
|
2017-02-08 12:58:53 +01:00
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2017-02-08 16:19:10 +01:00
|
|
|
var args []string
|
2017-02-08 15:32:44 +01:00
|
|
|
if dryRun {
|
2017-02-08 16:19:10 +01:00
|
|
|
args = []string{"apply", "-f", "-", "--dry-run"}
|
2017-02-08 15:32:44 +01:00
|
|
|
} else {
|
2017-02-08 16:19:10 +01:00
|
|
|
args = []string{"apply", "-f", "-"}
|
2017-02-08 15:32:44 +01:00
|
|
|
}
|
2017-02-08 11:51:08 +01:00
|
|
|
|
2017-02-08 16:19:10 +01:00
|
|
|
return runKubectlWithResources(ctx, &args, &resources)
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func replaceCommand() cli.Command {
|
|
|
|
return cli.Command{
|
|
|
|
Name: "replace",
|
|
|
|
Usage: "Interpolate templates and run 'kubectl replace'",
|
|
|
|
Flags: commonFlags(),
|
|
|
|
Action: func(c *cli.Context) error {
|
2017-02-08 16:44:55 +01:00
|
|
|
include := c.StringSlice("include")
|
|
|
|
exclude := c.StringSlice("exclude")
|
2017-02-08 16:19:10 +01:00
|
|
|
ctx, err := loadContext(c)
|
2017-02-08 16:44:55 +01:00
|
|
|
resources, err := templater.LoadAndPrepareTemplates(&include, &exclude, ctx)
|
2017-02-08 16:19:10 +01:00
|
|
|
|
2017-02-08 12:58:53 +01:00
|
|
|
if err != nil {
|
2017-02-08 16:19:10 +01:00
|
|
|
return err
|
2017-02-08 15:32:44 +01:00
|
|
|
}
|
|
|
|
|
2017-02-08 16:19:10 +01:00
|
|
|
args := []string{"replace", "--save-config=true", "-f", "-"}
|
|
|
|
return runKubectlWithResources(ctx, &args, &resources)
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
2017-02-08 15:32:44 +01:00
|
|
|
|
2017-02-08 18:06:47 +01:00
|
|
|
func deleteCommand() cli.Command {
|
|
|
|
return cli.Command{
|
|
|
|
Name: "delete",
|
|
|
|
Usage: "Interpolate templates and run 'kubectl delete'",
|
|
|
|
Flags: commonFlags(),
|
|
|
|
Action: func(c *cli.Context) error {
|
|
|
|
include := c.StringSlice("include")
|
|
|
|
exclude := c.StringSlice("exclude")
|
|
|
|
ctx, err := loadContext(c)
|
|
|
|
resources, err := templater.LoadAndPrepareTemplates(&include, &exclude, ctx)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
args := []string{"delete", "-f", "-"}
|
|
|
|
return runKubectlWithResources(ctx, &args, &resources)
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-08 16:19:10 +01:00
|
|
|
func runKubectlWithResources(c *context.Context, kubectlArgs *[]string, resources *[]string) error {
|
|
|
|
args := append(*kubectlArgs, fmt.Sprintf("--context=%s", c.Name))
|
2017-02-08 11:51:08 +01:00
|
|
|
|
2017-02-08 16:19:10 +01:00
|
|
|
kubectl := exec.Command("kubectl", args...)
|
2017-02-08 11:51:08 +01:00
|
|
|
|
2017-02-08 16:19:10 +01:00
|
|
|
stdin, err := kubectl.StdinPipe()
|
|
|
|
if err != nil {
|
|
|
|
return meep.New(&KubeCtlError{}, meep.Cause(err))
|
|
|
|
}
|
2017-02-08 15:32:44 +01:00
|
|
|
|
2017-02-08 16:19:10 +01:00
|
|
|
kubectl.Stdout = os.Stdout
|
|
|
|
kubectl.Stderr = os.Stderr
|
2017-02-08 15:32:44 +01:00
|
|
|
|
2017-02-08 16:19:10 +01:00
|
|
|
if err = kubectl.Start(); err != nil {
|
|
|
|
return meep.New(&KubeCtlError{}, meep.Cause(err))
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, r := range *resources {
|
|
|
|
fmt.Fprintln(stdin, r)
|
2017-02-08 11:51:08 +01:00
|
|
|
}
|
2017-02-08 16:19:10 +01:00
|
|
|
stdin.Close()
|
|
|
|
|
|
|
|
kubectl.Wait()
|
|
|
|
|
|
|
|
return nil
|
2017-02-08 11:51:08 +01:00
|
|
|
}
|
2017-02-08 15:32:44 +01:00
|
|
|
|
|
|
|
func commonFlags() []cli.Flag {
|
|
|
|
return []cli.Flag{
|
|
|
|
cli.StringFlag{
|
|
|
|
Name: "file, f",
|
|
|
|
Usage: "Cluster configuration file to use",
|
|
|
|
},
|
|
|
|
cli.StringSliceFlag{
|
2017-02-08 16:44:55 +01:00
|
|
|
Name: "include, i",
|
|
|
|
Usage: "Limit templating to explicitly included resource sets",
|
|
|
|
},
|
|
|
|
cli.StringSliceFlag{
|
|
|
|
Name: "exclude, e",
|
|
|
|
Usage: "Exclude certain resource sets from templating",
|
2017-02-08 15:32:44 +01:00
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-08 16:19:10 +01:00
|
|
|
func loadContext(c *cli.Context) (*context.Context, error) {
|
2017-02-08 15:32:44 +01:00
|
|
|
f := c.String("file")
|
|
|
|
|
|
|
|
if f == "" {
|
|
|
|
return nil, meep.New(
|
|
|
|
&meep.ErrInvalidParam{
|
|
|
|
Param: "file",
|
|
|
|
Reason: "Cluster config file must be specified",
|
|
|
|
},
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx, err := context.LoadContextFromFile(f)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2017-02-08 16:19:10 +01:00
|
|
|
return ctx, nil
|
2017-02-08 15:32:44 +01:00
|
|
|
}
|