From 1aa8ed763646c8c13cbabfc5e77589bf17e83755 Mon Sep 17 00:00:00 2001 From: Lewis Marshall Date: Tue, 24 Jul 2018 09:25:55 +0100 Subject: [PATCH] Add download for CA - fix debug for subcommands --- Gopkg.lock | 9 +++++ main.go | 103 ++++++++++++++++++++++++++++++++++++++++++----------- 2 files changed, 92 insertions(+), 20 deletions(-) diff --git a/Gopkg.lock b/Gopkg.lock index d552762..9b74453 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -25,6 +25,14 @@ revision = "3391d3790d23d03408670993e957e8f408993c34" version = "v1.0.1" +[[projects]] + digest = "1:18d3339287ed981065cbffedb0dc81196638666d04c83141e973a9bfad1be4a4" + name = "github.com/cavaliercoder/grab" + packages = ["."] + pruneopts = "UT" + revision = "466b5c0dd1d077be6de521d4e753734b17899a4d" + version = "v2.0.0" + [[projects]] digest = "1:8f8811f9be822914c3a25c6a071e93beb4c805d7b026cbf298bc577bc1cc945b" name = "github.com/google/uuid" @@ -87,6 +95,7 @@ analyzer-version = 1 input-imports = [ "github.com/Masterminds/sprig", + "github.com/cavaliercoder/grab", "github.com/joho/godotenv", "github.com/urfave/cli", "gopkg.in/yaml.v2", diff --git a/main.go b/main.go index 0c6fcd0..938f15a 100644 --- a/main.go +++ b/main.go @@ -6,6 +6,7 @@ import ( "fmt" "io/ioutil" "log" + "net/url" "os" "os/exec" "path/filepath" @@ -13,6 +14,7 @@ import ( "strings" "time" + "github.com/cavaliercoder/grab" "github.com/joho/godotenv" "github.com/urfave/cli" yaml "gopkg.in/yaml.v2" @@ -30,24 +32,39 @@ const ( FlagCreateOnlyResources = "create-only-resource" // FlagCreateOnly is the flag syntax to specify create only for all resources FlagCreateOnly = "create-only" + // FlagCa specifies the synatx for specifying a CA to trust + FlagCa = "certificate-authority" + // FlagCaData is the flag to specify that a PEM encoded CA is being specified + FlagCaData = "certificate-authority-data" + // FlagCaFile is the sytax to specify a CA file when FlagCa specifies a URL or + // when FlagCaData is set + FlagCaFile = "certificate-authority-file" ) var ( // Version is set at compile time, passing -ldflags "-X main.Version=" Version string - logInfo *log.Logger - logError *log.Logger - logDebug *log.Logger + logInfo *log.Logger + logError *log.Logger + logDebug *log.Logger + logDebugIf *log.Logger // dryRun Defaults to false dryRun bool + + // Files to delete on exit + tmpDir string = "" + + // caFile + caFile string = "" ) func init() { logInfo = log.New(os.Stdout, "[INFO] ", log.Ldate|log.Ltime|log.Lshortfile) logError = log.New(os.Stderr, "[ERROR] ", log.Ldate|log.Ltime|log.Lshortfile) - logDebug = log.New(os.Stderr, "[DEBUG] ", log.Ldate|log.Ltime|log.Lshortfile) + logDebugIf = log.New(os.Stderr, "[DEBUG] ", log.Ldate|log.Ltime|log.Lshortfile) + logDebug = log.New(ioutil.Discard, "", log.Lshortfile) } func main() { @@ -121,18 +138,18 @@ func main() { EnvVar: "FAIL_SUPERSEDED,PLUGIN_FAIL_SUPERSEDED", }, cli.StringFlag{ - Name: "certificate-authority", - Usage: "the path to a file containing the CA for kubernetes API `PATH`", + Name: FlagCa, + Usage: "the path (or URL) to a file containing the CA for kubernetes API `PATH`", EnvVar: "KUBE_CERTIFICATE_AUTHORITY,PLUGIN_KUBE_CERTIFICATE_AUHORITY", }, cli.StringFlag{ - Name: "certificate-authority-data", + Name: FlagCaData, Usage: "the certificate authority data for the kubernetes API `PATH`", EnvVar: "KUBE_CERTIFICATE_AUTHORITY_DATA,PLUGIN_KUBE_CERTIFICATE_AUHORITY_DATA", }, cli.StringFlag{ - Name: "certificate-authority-file", - Usage: "the path to file the certificate authority file from certifacte-authority-data option", + Name: FlagCaFile, + Usage: "the path to save certificate authority data when data or a URL is specified", Value: "/tmp/kube-ca.pem", }, cli.StringSliceFlag{ @@ -166,9 +183,6 @@ func main() { } app.Action = func(cx *cli.Context) error { - if !cx.Bool("debug") { - logDebug = log.New(ioutil.Discard, "", log.Lshortfile) - } if err := run(cx); err != nil { logError.Print(err) return cli.NewExitError("", 1) @@ -176,12 +190,24 @@ func main() { return nil } + defer cleanup() if err := app.Run(os.Args); err != nil { logError.Fatal(err) } } +// Delete any temparay files +func cleanup() { + if len(tmpDir) > 0 { + logDebug.Printf("cleaning up %s", tmpDir) + os.RemoveAll(tmpDir) + } +} + func runKubectl(c *cli.Context) error { + if c.Parent().Bool("debug") { + logDebug = logDebugIf + } if c.Parent().IsSet(FlagCreateOnlyResources) { if len(c.Parent().StringSlice(FlagCreateOnlyResources)) > 1 { return fmt.Errorf("can only specify a single resource when using run") @@ -216,9 +242,7 @@ func runKubectl(c *cli.Context) error { if err != nil { return err } - if c.Parent().Bool("debug") { - logDebug.Printf("About to run %s", cmd.Args) - } + logDebug.Printf("About to run %s", cmd.Args) cmd.Stdin = os.Stdin cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr @@ -233,6 +257,9 @@ func runKubectl(c *cli.Context) error { } func run(c *cli.Context) error { + if c.Bool("debug") { + logDebug = logDebugIf + } // Check we have some files to process if len(c.StringSlice("file")) == 0 { return errors.New("no kubernetes resource files specified") @@ -607,14 +634,18 @@ func newKubeCmdSub(c *cli.Context, args []string, subCommand bool) (*exec.Cmd, e if c.IsSet("kube-token") { args = append([]string{"--token=" + c.String("kube-token")}, args...) } - if c.IsSet("certificate-authority-data") { - if err := createCertificateAuthority(c.String("certificate-authority-file"), c.String("certificate-authority-data")); err != nil { + if c.IsSet(FlagCaData) { + if err := createCertificateAuthority(c.String(FlagCaFile), c.String(FlagCaData)); err != nil { return nil, err } - args = append([]string{"--certificate-authority=" + c.String("certificate-authority-file")}, args...) + args = append([]string{"--certificate-authority=" + c.String(FlagCaFile)}, args...) } - if c.IsSet("certificate-authority") { - args = append([]string{"--certificate-authority=" + c.String("certificate-authority")}, args...) + if c.IsSet(FlagCa) { + caFile, err := getCaFileAndDownloadIfRequired(c) + if err != nil { + return nil, err + } + args = append([]string{"--certificate-authority=" + caFile}, args...) } if c.IsSet("insecure-skip-tls-verify") { args = append([]string{"--insecure-skip-tls-verify"}, args...) @@ -632,6 +663,38 @@ func newKubeCmdSub(c *cli.Context, args []string, subCommand bool) (*exec.Cmd, e return exec.Command(kube, args...), nil } +// getCaFileAndDownloadIfRequired will obtain a CA file on disk - if required +func getCaFileAndDownloadIfRequired(c *cli.Context) (string, error) { + // have we done this already? + if len(caFile) > 0 { + return caFile, nil + } + ca := c.String(FlagCa) + // Detect if using a URL scheme + if uri, _ := url.ParseRequestURI(ca); uri != nil { + if uri.Scheme == "" { + // Not a URL, get out of here + return ca, nil + } + } + // Where should we save the ca? + if c.IsSet(FlagCaFile) { + caFile = c.String(FlagCaFile) + } else { + // This is used by cleanup + tmpDir, _ = ioutil.TempDir("", "kd") + caFile = filepath.Join(tmpDir, "kube-ca.pem") + } + logDebug.Printf("ca file specified as %s, to download from %s", caFile, ca) + // download the ca... + resp, err := grab.Get(caFile, ca) + if err != nil { + return "", fmt.Errorf( + "problem downloading ca from %s:%s", resp.Filename, err) + } + return caFile, nil +} + // extraFlags will parse out the -- args portion func extraFlags(c *cli.Context, subCommand bool) ([]string, error) { var a []string