refactor main: Move to Kingpin CLI library
Replace urfave/cli with the kingpin[1] library. It has slightly more sensible argument validation than the other Go libraries. Additionally I've opted for removing the '-f / --file' flag in favour of positional arguments to commands. A previous command like `kontemplate template -f somefile.yml` is now just `kontemplate template somefile.yml`. All other arguments remain the same. [1]: https://github.com/alecthomas/kingpin
This commit is contained in:
		
							parent
							
								
									32ca64c50a
								
							
						
					
					
						commit
						c8a63861ae
					
				
					 1 changed files with 77 additions and 145 deletions
				
			
		
							
								
								
									
										222
									
								
								main.go
									
										
									
									
									
								
							
							
						
						
									
										222
									
								
								main.go
									
										
									
									
									
								
							| 
						 | 
					@ -8,141 +8,111 @@ import (
 | 
				
			||||||
	"github.com/polydawn/meep"
 | 
						"github.com/polydawn/meep"
 | 
				
			||||||
	"github.com/tazjin/kontemplate/context"
 | 
						"github.com/tazjin/kontemplate/context"
 | 
				
			||||||
	"github.com/tazjin/kontemplate/templater"
 | 
						"github.com/tazjin/kontemplate/templater"
 | 
				
			||||||
	"github.com/urfave/cli"
 | 
						"gopkg.in/alecthomas/kingpin.v2"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type KubeCtlError struct {
 | 
					type KubeCtlError struct {
 | 
				
			||||||
	meep.AllTraits
 | 
						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()
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func main() {
 | 
					func main() {
 | 
				
			||||||
	app := cli.NewApp()
 | 
						app.HelpFlag.Short('h')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	app.Name = "kontemplate"
 | 
						switch kingpin.MustParse(app.Parse(os.Args[1:])) {
 | 
				
			||||||
	app.Usage = "simple Kubernetes resource templating"
 | 
						case template.FullCommand():
 | 
				
			||||||
	app.Version = "v1.0.0-beta1"
 | 
							templateCommand()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	app.Commands = []cli.Command{
 | 
						case apply.FullCommand():
 | 
				
			||||||
		templateCommand(),
 | 
							applyCommand()
 | 
				
			||||||
		applyCommand(),
 | 
					
 | 
				
			||||||
		replaceCommand(),
 | 
						case replace.FullCommand():
 | 
				
			||||||
		deleteCommand(),
 | 
							replaceCommand()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						case delete.FullCommand():
 | 
				
			||||||
 | 
							deleteCommand()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						case create.FullCommand():
 | 
				
			||||||
 | 
							createCommand()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	app.Run(os.Args)
 | 
					func templateCommand() {
 | 
				
			||||||
}
 | 
						_, resources := loadContextAndResources(templateFile)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func templateCommand() cli.Command {
 | 
						for _, r := range *resources {
 | 
				
			||||||
	return cli.Command{
 | 
					 | 
				
			||||||
		Name:  "template",
 | 
					 | 
				
			||||||
		Usage: "Interpolate and print templates",
 | 
					 | 
				
			||||||
		Flags: commonFlags(),
 | 
					 | 
				
			||||||
		Action: func(c *cli.Context) error {
 | 
					 | 
				
			||||||
			include := c.StringSlice("include")
 | 
					 | 
				
			||||||
			exclude := c.StringSlice("exclude")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			ctx, err := loadContext(c)
 | 
					 | 
				
			||||||
			if err != nil {
 | 
					 | 
				
			||||||
				return err
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			resources, err := templater.LoadAndPrepareTemplates(&include, &exclude, ctx)
 | 
					 | 
				
			||||||
			if err != nil {
 | 
					 | 
				
			||||||
				return err
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			for _, r := range resources {
 | 
					 | 
				
			||||||
		fmt.Println(r)
 | 
							fmt.Println(r)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					 | 
				
			||||||
			return nil
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func applyCommand() cli.Command {
 | 
					func applyCommand() {
 | 
				
			||||||
	dryRun := false
 | 
						ctx, resources := loadContextAndResources(applyFile)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return cli.Command{
 | 
						var kubectlArgs []string
 | 
				
			||||||
		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 {
 | 
					 | 
				
			||||||
			include := c.StringSlice("include")
 | 
					 | 
				
			||||||
			exclude := c.StringSlice("exclude")
 | 
					 | 
				
			||||||
			ctx, err := loadContext(c)
 | 
					 | 
				
			||||||
			if err != nil {
 | 
					 | 
				
			||||||
				return err
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
			resources, err := templater.LoadAndPrepareTemplates(&include, &exclude, ctx)
 | 
						if *applyDryRun {
 | 
				
			||||||
			if err != nil {
 | 
							kubectlArgs = []string{"apply", "-f", "-", "--dry-run"}
 | 
				
			||||||
				return err
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			var args []string
 | 
					 | 
				
			||||||
			if dryRun {
 | 
					 | 
				
			||||||
				args = []string{"apply", "-f", "-", "--dry-run"}
 | 
					 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
				args = []string{"apply", "-f", "-"}
 | 
							kubectlArgs = []string{"apply", "-f", "-"}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			return runKubectlWithResources(ctx, &args, &resources)
 | 
						runKubectlWithResources(ctx, &kubectlArgs, 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 {
 | 
					 | 
				
			||||||
			include := c.StringSlice("include")
 | 
					 | 
				
			||||||
			exclude := c.StringSlice("exclude")
 | 
					 | 
				
			||||||
			ctx, err := loadContext(c)
 | 
					 | 
				
			||||||
			if err != nil {
 | 
					 | 
				
			||||||
				return err
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			resources, err := templater.LoadAndPrepareTemplates(&include, &exclude, ctx)
 | 
					 | 
				
			||||||
			if err != nil {
 | 
					 | 
				
			||||||
				return err
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func replaceCommand() {
 | 
				
			||||||
 | 
						ctx, resources := loadContextAndResources(replaceFile)
 | 
				
			||||||
	args := []string{"replace", "--save-config=true", "-f", "-"}
 | 
						args := []string{"replace", "--save-config=true", "-f", "-"}
 | 
				
			||||||
			return runKubectlWithResources(ctx, &args, &resources)
 | 
						runKubectlWithResources(ctx, &args, resources)
 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
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)
 | 
					 | 
				
			||||||
			if err != nil {
 | 
					 | 
				
			||||||
				return err
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			resources, err := templater.LoadAndPrepareTemplates(&include, &exclude, ctx)
 | 
					 | 
				
			||||||
			if err != nil {
 | 
					 | 
				
			||||||
				return err
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func deleteCommand() {
 | 
				
			||||||
 | 
						ctx, resources := loadContextAndResources(deleteFile)
 | 
				
			||||||
	args := []string{"delete", "-f", "-"}
 | 
						args := []string{"delete", "-f", "-"}
 | 
				
			||||||
			return runKubectlWithResources(ctx, &args, &resources)
 | 
						runKubectlWithResources(ctx, &args, resources)
 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func createCommand() {
 | 
				
			||||||
 | 
						ctx, resources := loadContextAndResources(createFile)
 | 
				
			||||||
 | 
						args := []string{"create", "--save-config=true", "-f", "-"}
 | 
				
			||||||
 | 
						runKubectlWithResources(ctx, &args, resources)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func loadContextAndResources(file *string) (*context.Context, *[]string) {
 | 
				
			||||||
 | 
						ctx, err := context.LoadContextFromFile(*file)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							app.Fatalf("Error loading context: %v\n", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						resources, err := templater.LoadAndPrepareTemplates(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, resources *[]string) error {
 | 
					func runKubectlWithResources(c *context.Context, kubectlArgs *[]string, resources *[]string) error {
 | 
				
			||||||
| 
						 | 
					@ -171,41 +141,3 @@ func runKubectlWithResources(c *context.Context, kubectlArgs *[]string, resource
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
func commonFlags() []cli.Flag {
 | 
					 | 
				
			||||||
	return []cli.Flag{
 | 
					 | 
				
			||||||
		cli.StringFlag{
 | 
					 | 
				
			||||||
			Name:  "file, f",
 | 
					 | 
				
			||||||
			Usage: "Cluster configuration file to use",
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		cli.StringSliceFlag{
 | 
					 | 
				
			||||||
			Name:  "include, i",
 | 
					 | 
				
			||||||
			Usage: "Limit templating to explicitly included resource sets",
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		cli.StringSliceFlag{
 | 
					 | 
				
			||||||
			Name:  "exclude, e",
 | 
					 | 
				
			||||||
			Usage: "Exclude certain resource sets from templating",
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func loadContext(c *cli.Context) (*context.Context, error) {
 | 
					 | 
				
			||||||
	f := c.String("file")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if f == "" {
 | 
					 | 
				
			||||||
		return nil, meep.New(
 | 
					 | 
				
			||||||
			&meep.ErrInvalidParam{
 | 
					 | 
				
			||||||
				Param:  "file",
 | 
					 | 
				
			||||||
				Reason: "Cluster config file must be specified (-f)",
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
		)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	ctx, err := context.LoadContextFromFile(f)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return ctx, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue