mirror of
https://github.com/fluxcd/flux2.git
synced 2026-02-08 00:37:27 +00:00
Implement SSH deploy key bootstrap
This commit is contained in:
parent
e70d86e843
commit
e612a8a496
3 changed files with 109 additions and 15 deletions
|
|
@ -4,6 +4,7 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
|
|
@ -40,13 +41,13 @@ the bootstrap command will perform an upgrade if needed.`,
|
|||
export GITHUB_TOKEN=<my-token>
|
||||
|
||||
# Run bootstrap for a private repo owned by a GitHub organization
|
||||
tk bootstrap github --owner=<organization> --repository=<repo name>
|
||||
bootstrap github --owner=<organization> --repository=<repo name>
|
||||
|
||||
# Run bootstrap for a public repository on a personal account
|
||||
tk bootstrap github --owner=<user> --repository=<repo name> --private=false --personal=true
|
||||
bootstrap github --owner=<user> --repository=<repo name> --private=false --personal=true
|
||||
|
||||
# Run bootstrap for a private repo hosted on GitHub Enterprise
|
||||
tk bootstrap github --owner=<organization> --repository=<repo name> --hostname=<domain>
|
||||
bootstrap github --owner=<organization> --repository=<repo name> --hostname=<domain>
|
||||
`,
|
||||
RunE: bootstrapGitHubCmdRun,
|
||||
}
|
||||
|
|
@ -86,6 +87,7 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
|
|||
}
|
||||
|
||||
ghURL := fmt.Sprintf("https://%s/%s/%s", ghHostname, ghOwner, ghRepository)
|
||||
sshURL := fmt.Sprintf("ssh://git@%s/%s/%s", ghHostname, ghOwner, ghRepository)
|
||||
if ghOwner == "" || ghRepository == "" {
|
||||
return fmt.Errorf("owner and repository are required")
|
||||
}
|
||||
|
|
@ -165,18 +167,30 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
|
|||
}
|
||||
}
|
||||
|
||||
// create or update auth secret
|
||||
// TODO: replace this with SSH deploy key
|
||||
if err := generateBasicAuth(ctx, namespace, namespace, "git", ghToken); err != nil {
|
||||
return err
|
||||
// setup SSH deploy key
|
||||
if shouldCreateGitHubDeployKey(ctx, kubeClient, namespace) {
|
||||
logAction("configuring deploy key")
|
||||
u, err := url.Parse(sshURL)
|
||||
if err != nil {
|
||||
return fmt.Errorf("git URL parse failed: %w", err)
|
||||
}
|
||||
|
||||
key, err := generateGitHubDeployKey(ctx, kubeClient, u, namespace)
|
||||
if err != nil {
|
||||
return fmt.Errorf("generating deploy key failed: %w", err)
|
||||
}
|
||||
|
||||
if err := createGitHubDeployKey(ctx, key, ghHostname, ghOwner, ghRepository, ghToken, ghPersonal); err != nil {
|
||||
return nil
|
||||
}
|
||||
logSuccess("deploy key configured")
|
||||
}
|
||||
logSuccess("authentication configured")
|
||||
|
||||
// configure repo synchronization
|
||||
if isInstall {
|
||||
// generate source and kustomization manifests
|
||||
logAction("generating sync manifests")
|
||||
if err := generateGitHubKustomization(ghURL, namespace, namespace, tmpDir, ghInterval); err != nil {
|
||||
if err := generateGitHubKustomization(sshURL, namespace, namespace, tmpDir, ghInterval); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
@ -205,12 +219,12 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func createGitHubRepository(ctx context.Context, hostname, owner, name, token string, isPrivate, isPersonal bool) error {
|
||||
autoInit := true
|
||||
func makeGitHubClient(hostname, token string) (*github.Client, error) {
|
||||
auth := github.BasicAuthTransport{
|
||||
Username: "git",
|
||||
Password: token,
|
||||
}
|
||||
|
||||
gh := github.NewClient(auth.Client())
|
||||
if hostname != ghDefaultHostname {
|
||||
baseURL := fmt.Sprintf("https://%s/api/v3/", hostname)
|
||||
|
|
@ -218,9 +232,18 @@ func createGitHubRepository(ctx context.Context, hostname, owner, name, token st
|
|||
if g, err := github.NewEnterpriseClient(baseURL, uploadURL, auth.Client()); err == nil {
|
||||
gh = g
|
||||
} else {
|
||||
return fmt.Errorf("github client error: %w", err)
|
||||
return nil, fmt.Errorf("github client error: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return gh, nil
|
||||
}
|
||||
|
||||
func createGitHubRepository(ctx context.Context, hostname, owner, name, token string, isPrivate, isPersonal bool) error {
|
||||
gh, err := makeGitHubClient(hostname, token)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
org := ""
|
||||
if !isPersonal {
|
||||
org = owner
|
||||
|
|
@ -230,7 +253,8 @@ func createGitHubRepository(ctx context.Context, hostname, owner, name, token st
|
|||
return nil
|
||||
}
|
||||
|
||||
_, _, err := gh.Repositories.Create(ctx, org, &github.Repository{
|
||||
autoInit := true
|
||||
_, _, err = gh.Repositories.Create(ctx, org, &github.Repository{
|
||||
AutoInit: &autoInit,
|
||||
Name: &name,
|
||||
Private: &isPrivate,
|
||||
|
|
@ -442,3 +466,67 @@ func shouldInstallGitHub(ctx context.Context, kubeClient client.Client, namespac
|
|||
|
||||
return kustomization.Status.LastAppliedRevision == ""
|
||||
}
|
||||
|
||||
func shouldCreateGitHubDeployKey(ctx context.Context, kubeClient client.Client, namespace string) bool {
|
||||
namespacedName := types.NamespacedName{
|
||||
Namespace: namespace,
|
||||
Name: namespace,
|
||||
}
|
||||
|
||||
var existing corev1.Secret
|
||||
if err := kubeClient.Get(ctx, namespacedName, &existing); err != nil {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func generateGitHubDeployKey(ctx context.Context, kubeClient client.Client, url *url.URL, namespace string) (string, error) {
|
||||
pair, err := generateKeyPair(ctx)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
hostKey, err := scanHostKey(ctx, url)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
secret := corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: namespace,
|
||||
Namespace: namespace,
|
||||
},
|
||||
StringData: map[string]string{
|
||||
"identity": string(pair.PrivateKey),
|
||||
"identity.pub": string(pair.PublicKey),
|
||||
"known_hosts": string(hostKey),
|
||||
},
|
||||
}
|
||||
if err := upsertSecret(ctx, kubeClient, secret); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return string(pair.PublicKey), nil
|
||||
}
|
||||
|
||||
func createGitHubDeployKey(ctx context.Context, key, hostname, owner, name, token string, isPersonal bool) error {
|
||||
gh, err := makeGitHubClient(hostname, token)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
keyName := fmt.Sprintf("tk-%s", namespace)
|
||||
org := ""
|
||||
if !isPersonal {
|
||||
org = owner
|
||||
}
|
||||
isReadOnly := true
|
||||
_, _, err = gh.Repositories.CreateKey(ctx, org, name, &github.Key{
|
||||
Title: &keyName,
|
||||
Key: &key,
|
||||
ReadOnly: &isReadOnly,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("github create deploy key error: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
2
go.mod
2
go.mod
|
|
@ -6,6 +6,8 @@ require (
|
|||
github.com/blang/semver v3.5.1+incompatible
|
||||
github.com/fluxcd/kustomize-controller v0.0.1-beta.1
|
||||
github.com/fluxcd/source-controller v0.0.1-beta.1
|
||||
github.com/go-git/go-git/v5 v5.0.0
|
||||
github.com/google/go-github/v32 v32.0.0
|
||||
github.com/manifoldco/promptui v0.7.0
|
||||
github.com/spf13/cobra v1.0.0
|
||||
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073
|
||||
|
|
|
|||
8
go.sum
8
go.sum
|
|
@ -95,6 +95,7 @@ github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:z
|
|||
github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko=
|
||||
github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw=
|
||||
github.com/containerd/containerd v1.3.0-beta.2.0.20190828155532-0293cbd26c69/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
|
||||
github.com/containerd/containerd v1.3.2 h1:ForxmXkA6tPIvffbrDAcPUIB32QgXkt2XFj+F0UxetA=
|
||||
github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
|
||||
github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
|
||||
github.com/containerd/continuity v0.0.0-20200107194136-26c1120b8d41/go.mod h1:Dq467ZllaHgAtVp4p1xUQWBrFXR9s/wyoTpG8zOJGkY=
|
||||
|
|
@ -125,6 +126,7 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
|
|||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/daviddengcn/go-colortext v0.0.0-20160507010035-511bcaf42ccd/go.mod h1:dv4zxwHi5C/8AeI+4gX4dCWOIvNi7I6JCSX0HvlKPgE=
|
||||
github.com/deislabs/oras v0.8.1 h1:If674KraJVpujYR00rzdi0QAmW4BxzMJPVAZJKuhQ0c=
|
||||
github.com/deislabs/oras v0.8.1/go.mod h1:Mx0rMSbBNaNfY9hjpccEnxkOqJL6KGjtxNHPLC4G4As=
|
||||
github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0=
|
||||
github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
|
|
@ -491,8 +493,10 @@ github.com/onsi/gomega v1.8.1 h1:C5Dqfs/LeauYDX0jJXIe2SWmwCbGzx9yF8C8xy3Lh34=
|
|||
github.com/onsi/gomega v1.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
|
||||
github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
||||
github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
||||
github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ=
|
||||
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
||||
github.com/opencontainers/image-spec v1.0.0/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
|
||||
github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI=
|
||||
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
|
||||
github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
|
||||
github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
|
||||
|
|
@ -667,8 +671,6 @@ golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975 h1:/Tl7pH94bvbAAHBdZJT947
|
|||
golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073 h1:xMPOj6Pz6UipU1wXLkrtqpHbR0AVFnyPEQq/wRWz9lM=
|
||||
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9 h1:vEg9joUBmeBcK9iSJftGNf3coIG4HqZElCPehJsfAYM=
|
||||
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190312203227-4b39c73a6495/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
|
|
@ -717,6 +719,7 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ
|
|||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
|
|
@ -814,6 +817,7 @@ google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ij
|
|||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.0 h1:rRYRFMVgRv6E0D70Skyfsr28tDXIuuPZyWGMPdMcnXg=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
|
|
|
|||
Loading…
Reference in a new issue