Skip to content
Snippets Groups Projects
Select Git revision
  • 9cec671f6c7c9c56b86a85cc54274cd243b180e6
  • main default protected
  • fix-manifestgen
  • release/v2.7.x
  • release/v2.6.x
  • conform-k8s-1.33
  • release/v2.5.x
  • release/v2.4.x
  • remove-notation-validation
  • release/v2.3.x
  • release/v2.2.x
  • RFC
  • fix-commit-log
  • flux-audit
  • release/v2.1.x
  • context-ns
  • ksm-dashboard
  • rfc-passwordless-git-auth
  • release/v2.0.x
  • rfc-gating
  • release/v0.27.4
  • v2.7.1 protected
  • v2.7.0 protected
  • v2.6.4 protected
  • v2.6.3 protected
  • v2.6.2 protected
  • v2.6.1 protected
  • v2.6.0 protected
  • v2.5.1 protected
  • v2.5.0 protected
  • v2.4.0 protected
  • v2.3.0 protected
  • v2.2.3 protected
  • v2.2.2 protected
  • v2.2.1 protected
  • v2.2.0 protected
  • v2.1.2 protected
  • v2.1.1 protected
  • v2.1.0 protected
  • v2.0.1 protected
  • v2.0.0 protected
41 results

create_source.go

  • create_source.go 5.10 KiB
    package main
    
    import (
    	"bufio"
    	"bytes"
    	"fmt"
    	"io"
    	"io/ioutil"
    	"net/url"
    	"os"
    	"os/exec"
    	"strings"
    	"text/template"
    
    	"github.com/manifoldco/promptui"
    	"github.com/spf13/cobra"
    )
    
    var createSourceCmd = &cobra.Command{
    	Use:   "source [name]",
    	Short: "Create source resource",
    	Long: `
    The create source command generates a source.fluxcd.io resource and waits for it to sync.
    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 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)
    }
    
    func createSourceCmdRun(cmd *cobra.Command, args []string) error {
    	if len(args) < 1 {
    		return fmt.Errorf("source name is required")
    	}
    	name := args[0]
    
    	if sourceGitURL == "" {
    		return fmt.Errorf("git-url is required")
    	}
    
    	tmpDir, err := ioutil.TempDir("", name)
    	if err != nil {
    		return err
    	}
    	defer os.RemoveAll(tmpDir)
    
    	u, err := url.Parse(sourceGitURL)
    	if err != nil {
    		return fmt.Errorf("git URL parse failed: %w", err)
    	}
    
    	isSSH := strings.HasPrefix(sourceGitURL, "ssh")
    	if isSSH {
    		if err := generateSSH(name, u.Host, tmpDir); err != nil {
    			return err
    		}
    	}
    
    	fmt.Println(`✚`, "generating source resource")
    
    	t, err := template.New("tmpl").Parse(gitSource)
    	if err != nil {
    		return fmt.Errorf("template parse error: %w", err)
    	}
    
    	source := struct {
    		Name      string
    		Namespace string
    		URL       string
    		Branch    string
    		Semver    string
    		Interval  string
    		IsSSH     bool
    	}{
    		Name:      name,
    		Namespace: namespace,
    		URL:       sourceGitURL,
    		Branch:    sourceGitBranch,
    		Semver:    sourceGitSemver,
    		Interval:  interval,
    		IsSSH:     isSSH,
    	}
    
    	var data bytes.Buffer
    	writer := bufio.NewWriter(&data)
    	if err := t.Execute(writer, source); err != nil {
    		return fmt.Errorf("template execution failed: %w", err)
    	}
    	if err := writer.Flush(); err != nil {
    		return fmt.Errorf("source flush failed: %w", err)
    	}
    	fmt.Print(data.String())
    
    	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")
    	if output, err := execCommand(fmt.Sprintf(
    		"kubectl -n %s wait gitrepository/%s --for=condition=ready --timeout=1m",
    		namespace, name)); err != nil {
    		return fmt.Errorf("source sync failed: %s", output)
    	} else {
    		fmt.Print(output)
    	}
    
    	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
    metadata:
      name: {{.Name}}
      namespace: {{.Namespace}}
    spec:
      interval: {{.Interval}}
      url: {{.URL}}
      ref:
    {{- if .Semver }}
        semver: "{{.Semver}}"
    {{- else }}
        branch: {{.Branch}}
    {{- end }}
    {{- if .IsSSH }}
      secretRef:
        name: {{.Name}}
    {{- end }}
    `