From 9cec671f6c7c9c56b86a85cc54274cd243b180e6 Mon Sep 17 00:00:00 2001 From: stefanprodan Date: Sat, 25 Apr 2020 00:02:09 +0300 Subject: [PATCH] Add public repos and semver support to create cmd --- cmd/tk/create_source.go | 143 ++++++++++++++++++++++++++-------------- cmd/tk/main.go | 23 +++---- 2 files changed, 104 insertions(+), 62 deletions(-) diff --git a/cmd/tk/create_source.go b/cmd/tk/create_source.go index 8d73f628..22cb42a7 100644 --- a/cmd/tk/create_source.go +++ b/cmd/tk/create_source.go @@ -4,9 +4,12 @@ import ( "bufio" "bytes" "fmt" + "io" "io/ioutil" "net/url" "os" + "os/exec" + "strings" "text/template" "github.com/manifoldco/promptui" @@ -18,19 +21,29 @@ var createSourceCmd = &cobra.Command{ Short: "Create source resource", Long: ` The create source command generates a source.fluxcd.io resource and waits for it to sync. -If a Git repository is specified, it will create a SSH deploy key.`, - Example: ` create source podinfo --git-url ssh://git@github.com/stefanprodan/podinfo-deploy`, - RunE: createSourceCmdRun, +For Git over SSH, host and SSH keys are automatically generated.`, + Example: ` # Create a gitrepository.source.fluxcd.io for a public repository + create source podinfo --git-url https://github.com/stefanprodan/podinfo-deploy --git-branch master + + # Create a gitrepository.source.fluxcd.io that syncs tags based on a semver range + create source podinfo --git-url https://github.com/stefanprodan/podinfo-deploy --git-semver=">=0.0.1-rc.1 <0.1.0" + + # Create a gitrepository.source.fluxcd.io with SSH authentication + create source podinfo --git-url ssh://git@github.com/stefanprodan/podinfo-deploy +`, + RunE: createSourceCmdRun, } var ( sourceGitURL string sourceGitBranch string + sourceGitSemver string ) func init() { - createSourceCmd.Flags().StringVar(&sourceGitURL, "git-url", "", "git SSH address, in the format ssh://git@host/org/repository") + createSourceCmd.Flags().StringVar(&sourceGitURL, "git-url", "", "git address, e.g. ssh://git@host/org/repository") createSourceCmd.Flags().StringVar(&sourceGitBranch, "git-branch", "master", "git branch") + createSourceCmd.Flags().StringVar(&sourceGitSemver, "git-semver", "", "git tag semver range") createCmd.AddCommand(createSourceCmd) } @@ -56,44 +69,11 @@ func createSourceCmdRun(cmd *cobra.Command, args []string) error { return fmt.Errorf("git URL parse failed: %w", err) } - fmt.Println(`✚`, "generating host key for", u.Host) - - keyscan := fmt.Sprintf("ssh-keyscan %s > %s/known_hosts", u.Host, tmpDir) - if output, err := execCommand(keyscan); err != nil { - return fmt.Errorf("ssh-keyscan failed: %s", output) - } - - fmt.Println(`✚`, "generating deploy key") - - keygen := fmt.Sprintf("ssh-keygen -b 2048 -t rsa -f %s/identity -q -N \"\"", tmpDir) - if output, err := execCommand(keygen); err != nil { - return fmt.Errorf("ssh-keygen failed: %s", output) - } - - deployKey, err := execCommand(fmt.Sprintf("cat %s/identity.pub", tmpDir)) - if err != nil { - return fmt.Errorf("unable to read identity.pub: %w", err) - } - - fmt.Print(deployKey) - prompt := promptui.Prompt{ - Label: "Have you added the deploy key to your repository", - IsConfirm: true, - } - if _, err := prompt.Run(); err != nil { - fmt.Println(`✗`, "aborting") - return nil - } - - fmt.Println(`✚`, "saving deploy key") - files := fmt.Sprintf("--from-file=%s/identity --from-file=%s/identity.pub --from-file=%s/known_hosts", - tmpDir, tmpDir, tmpDir) - secret := fmt.Sprintf("kubectl -n %s create secret generic %s %s --dry-run=client -oyaml | kubectl apply -f-", - namespace, name, files) - if output, err := execCommand(secret); err != nil { - return fmt.Errorf("kubectl create secret failed: %s", output) - } else { - fmt.Print(output) + isSSH := strings.HasPrefix(sourceGitURL, "ssh") + if isSSH { + if err := generateSSH(name, u.Host, tmpDir); err != nil { + return err + } } fmt.Println(`✚`, "generating source resource") @@ -106,13 +86,19 @@ func createSourceCmdRun(cmd *cobra.Command, args []string) error { source := struct { Name string Namespace string - GitURL string + URL string + Branch string + Semver string Interval string + IsSSH bool }{ Name: name, Namespace: namespace, - GitURL: sourceGitURL, + URL: sourceGitURL, + Branch: sourceGitBranch, + Semver: sourceGitSemver, Interval: interval, + IsSSH: isSSH, } var data bytes.Buffer @@ -123,11 +109,19 @@ func createSourceCmdRun(cmd *cobra.Command, args []string) error { if err := writer.Flush(); err != nil { return fmt.Errorf("source flush failed: %w", err) } + fmt.Print(data.String()) - if output, err := execCommand(fmt.Sprintf("echo '%s' | kubectl apply -f-", data.String())); err != nil { - return fmt.Errorf("kubectl create source failed: %s", output) - } else { - fmt.Print(output) + command := fmt.Sprintf("echo '%s' | kubectl apply -f-", data.String()) + c := exec.Command("/bin/sh", "-c", command) + + var stdoutBuf, stderrBuf bytes.Buffer + c.Stdout = io.MultiWriter(os.Stdout, &stdoutBuf) + c.Stderr = io.MultiWriter(os.Stderr, &stderrBuf) + + err = c.Run() + if err != nil { + fmt.Println(`✗`, "source apply failed") + os.Exit(1) } fmt.Println(`✚`, "waiting for source sync") @@ -142,6 +136,49 @@ func createSourceCmdRun(cmd *cobra.Command, args []string) error { return nil } +func generateSSH(name, host, tmpDir string) error { + fmt.Println(`✚`, "generating host key for", host) + + keyscan := fmt.Sprintf("ssh-keyscan %s > %s/known_hosts", host, tmpDir) + if output, err := execCommand(keyscan); err != nil { + return fmt.Errorf("ssh-keyscan failed: %s", output) + } + + fmt.Println(`✚`, "generating deploy key") + + keygen := fmt.Sprintf("ssh-keygen -b 2048 -t rsa -f %s/identity -q -N \"\"", tmpDir) + if output, err := execCommand(keygen); err != nil { + return fmt.Errorf("ssh-keygen failed: %s", output) + } + + deployKey, err := execCommand(fmt.Sprintf("cat %s/identity.pub", tmpDir)) + if err != nil { + return fmt.Errorf("unable to read identity.pub: %w", err) + } + + fmt.Print(deployKey) + prompt := promptui.Prompt{ + Label: "Have you added the deploy key to your repository", + IsConfirm: true, + } + if _, err := prompt.Run(); err != nil { + fmt.Println(`✗`, "aborting") + os.Exit(1) + } + + fmt.Println(`✚`, "saving deploy key") + files := fmt.Sprintf("--from-file=%s/identity --from-file=%s/identity.pub --from-file=%s/known_hosts", + tmpDir, tmpDir, tmpDir) + secret := fmt.Sprintf("kubectl -n %s create secret generic %s %s --dry-run=client -oyaml | kubectl apply -f-", + namespace, name, files) + if output, err := execCommand(secret); err != nil { + return fmt.Errorf("kubectl create secret failed: %s", output) + } else { + fmt.Print(output) + } + return nil +} + var gitSource = `--- apiVersion: source.fluxcd.io/v1alpha1 kind: GitRepository @@ -150,7 +187,15 @@ metadata: namespace: {{.Namespace}} spec: interval: {{.Interval}} - url: {{.GitURL}} + url: {{.URL}} + ref: +{{- if .Semver }} + semver: "{{.Semver}}" +{{- else }} + branch: {{.Branch}} +{{- end }} +{{- if .IsSSH }} secretRef: name: {{.Name}} +{{- end }} ` diff --git a/cmd/tk/main.go b/cmd/tk/main.go index 3c6ac728..af1e8423 100644 --- a/cmd/tk/main.go +++ b/cmd/tk/main.go @@ -2,24 +2,24 @@ package main import ( "fmt" - "log" - "os" - "os/exec" - "path/filepath" - "strings" - "github.com/spf13/cobra" "k8s.io/client-go/kubernetes" _ "k8s.io/client-go/plugin/pkg/client/auth" "k8s.io/client-go/tools/clientcmd" + "log" + "os" + "os/exec" + "path/filepath" ) var VERSION = "0.0.1" var rootCmd = &cobra.Command{ - Use: "tk", - Short: "Kubernetes CD assembler", - Version: VERSION, + Use: "tk", + Short: "Kubernetes CD assembler", + Version: VERSION, + SilenceUsage: true, + SilenceErrors: true, } var ( @@ -41,11 +41,8 @@ func init() { func main() { log.SetFlags(0) - - rootCmd.SetArgs(os.Args[1:]) if err := rootCmd.Execute(); err != nil { - e := err.Error() - fmt.Println(strings.ToUpper(e[:1]) + e[1:]) + fmt.Println(err) os.Exit(1) } }