tvl-depot/main.go
Vincent Ambo f8b6ad652d
Version 1.2.0
This release comes with some new features, usability improvements and
a better build & release process.

Features:

* Documentation has been improved significantly, check out the new
  [README][] and follow the links within!
* Extra variables can now be loaded from files on disk. Simply specify
  a list of YAML/JSON files under the 'import' key in your cluster
  context file. Check out #66 for details!
* Resource set paths can now be overridden by users. By default it is
  assumed that the path to a resource set is the same as its name,
  however this is now user-controllable.
  This means the same resource set can be included multiple times
  under different names, for easier including/excluding. See #71 for
  details!
* Kontemplate is currently getting a website that is under construction
  at [kontemplate.works][] - feel free to check it out and
  [give feedback][]!

Fixes:

* Windows release binaries now have the correct filename
* Several potential warning and error messages have been improved

Release binaries are signed with GPG key `66F505681DB8F43B` which is
verified on my Github profile.

[README]: https://github.com/tazjin/kontemplate/blob/master/README.md
[kontemplate.works]: http://kontemplate.works/
[give feedback]: https://github.com/tazjin/kontemplate-website/issues
2017-08-22 19:18:14 +02:00

195 lines
5.1 KiB
Go

package main
import (
"fmt"
"os"
"os/exec"
"github.com/polydawn/meep"
"github.com/tazjin/kontemplate/context"
"github.com/tazjin/kontemplate/templater"
"gopkg.in/alecthomas/kingpin.v2"
)
const version string = "1.2.0"
// This variable will be initialised by the Go linker during the builder
var gitHash string
type KubeCtlError struct {
meep.AllTraits
}
var (
app = kingpin.New("kontemplate", "simple Kubernetes resource templating")
// Global flags
includes = app.Flag("include", "Resource sets to include explicitly").Short('i').Strings()
excludes = app.Flag("exclude", "Resource sets to exclude explicitly").Short('e').Strings()
// Commands
template = app.Command("template", "Template resource sets and print them")
templateFile = template.Arg("file", "Cluster configuration file to use").Required().String()
apply = app.Command("apply", "Template resources and pass to 'kubectl apply'")
applyFile = apply.Arg("file", "Cluster configuration file to use").Required().String()
applyDryRun = apply.Flag("dry-run", "Print remote operations without executing them").Default("false").Bool()
replace = app.Command("replace", "Template resources and pass to 'kubectl replace'")
replaceFile = replace.Arg("file", "Cluster configuration file to use").Required().String()
delete = app.Command("delete", "Template resources and pass to 'kubectl delete'")
deleteFile = delete.Arg("file", "Cluster configuration file to use").Required().String()
create = app.Command("create", "Template resources and pass to 'kubectl create'")
createFile = create.Arg("file", "Cluster configuration file to use").Required().String()
versionCmd = app.Command("version", "Show kontemplate version")
)
func main() {
app.HelpFlag.Short('h')
switch kingpin.MustParse(app.Parse(os.Args[1:])) {
case template.FullCommand():
templateCommand()
case apply.FullCommand():
applyCommand()
case replace.FullCommand():
replaceCommand()
case delete.FullCommand():
deleteCommand()
case create.FullCommand():
createCommand()
case versionCmd.FullCommand():
versionCommand()
}
}
func versionCommand() {
if gitHash == "" {
fmt.Printf("Kontemplate version %s (git commit unknown)\n", version)
} else {
fmt.Printf("Kontemplate version %s (git commit: %s)\n", version, gitHash)
}
}
func templateCommand() {
_, resourceSets := loadContextAndResources(templateFile)
for _, rs := range *resourceSets {
if len(rs.Resources) == 0 {
fmt.Fprintf(os.Stderr, "Warning: Resource set '%s' contains no valid templates\n", rs.Name)
break
}
for _, r := range rs.Resources {
fmt.Fprintf(os.Stderr, "Rendered file %s/%s:\n", rs.Name, r.Filename)
fmt.Println(r.Rendered)
}
}
}
func applyCommand() {
ctx, resources := loadContextAndResources(applyFile)
var kubectlArgs []string
if *applyDryRun {
kubectlArgs = []string{"apply", "-f", "-", "--dry-run"}
} else {
kubectlArgs = []string{"apply", "-f", "-"}
}
if err := runKubectlWithResources(ctx, &kubectlArgs, resources); err != nil {
failWithKubectlError(err)
}
}
func replaceCommand() {
ctx, resources := loadContextAndResources(replaceFile)
args := []string{"replace", "--save-config=true", "-f", "-"}
if err := runKubectlWithResources(ctx, &args, resources); err != nil {
failWithKubectlError(err)
}
}
func deleteCommand() {
ctx, resources := loadContextAndResources(deleteFile)
args := []string{"delete", "-f", "-"}
if err := runKubectlWithResources(ctx, &args, resources); err != nil {
failWithKubectlError(err)
}
}
func createCommand() {
ctx, resources := loadContextAndResources(createFile)
args := []string{"create", "--save-config=true", "-f", "-"}
if err := runKubectlWithResources(ctx, &args, resources); err != nil {
failWithKubectlError(err)
}
}
func loadContextAndResources(file *string) (*context.Context, *[]templater.RenderedResourceSet) {
ctx, err := context.LoadContextFromFile(*file)
if err != nil {
app.Fatalf("Error loading context: %v\n", err)
}
resources, err := templater.LoadAndApplyTemplates(includes, excludes, ctx)
if err != nil {
app.Fatalf("Error templating resource sets: %v\n", err)
}
return ctx, &resources
}
func runKubectlWithResources(c *context.Context, kubectlArgs *[]string, resourceSets *[]templater.RenderedResourceSet) error {
args := append(*kubectlArgs, fmt.Sprintf("--context=%s", c.Name))
for _, rs := range *resourceSets {
if len(rs.Resources) == 0 {
fmt.Fprintf(os.Stderr, "Warning: Resource set '%s' contains no valid templates\n", rs.Name)
continue
}
kubectl := exec.Command("kubectl", args...)
stdin, err := kubectl.StdinPipe()
if err != nil {
return meep.New(&KubeCtlError{}, meep.Cause(err))
}
kubectl.Stdout = os.Stdout
kubectl.Stderr = os.Stderr
if err = kubectl.Start(); err != nil {
return meep.New(&KubeCtlError{}, meep.Cause(err))
}
for _, r := range rs.Resources {
fmt.Printf("Passing file %s/%s to kubectl\n", rs.Name, r.Filename)
fmt.Fprintln(stdin, r.Rendered)
}
stdin.Close()
if err = kubectl.Wait(); err != nil {
return err
}
}
return nil
}
func failWithKubectlError(err error) {
fmt.Fprintf(os.Stderr, "Kubectl error: %v\n", err)
os.Exit(1)
}