Compare commits
8 Commits
v0.0.1-bet
...
v0.0.1-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
62e4b03342 | ||
|
|
dca7b0ba91 | ||
|
|
2a61befbfd | ||
|
|
287be26190 | ||
|
|
4992e11383 | ||
|
|
727734850e | ||
|
|
e042d25062 | ||
|
|
ba7a11d0e5 |
@@ -43,6 +43,9 @@ the bootstrap command will perform an upgrade if needed.`,
|
|||||||
# Run bootstrap for a private repo owned by a GitHub organization
|
# Run bootstrap for a private repo owned by a GitHub organization
|
||||||
bootstrap github --owner=<organization> --repository=<repo name>
|
bootstrap github --owner=<organization> --repository=<repo name>
|
||||||
|
|
||||||
|
# Run bootstrap for a repository path
|
||||||
|
bootstrap github --owner=<organization> --repository=<repo name> --path=dev-cluster
|
||||||
|
|
||||||
# Run bootstrap for a public repository on a personal account
|
# Run bootstrap for a public repository on a personal account
|
||||||
bootstrap github --owner=<user> --repository=<repo name> --private=false --personal=true
|
bootstrap github --owner=<user> --repository=<repo name> --private=false --personal=true
|
||||||
|
|
||||||
@@ -59,12 +62,13 @@ var (
|
|||||||
ghPersonal bool
|
ghPersonal bool
|
||||||
ghPrivate bool
|
ghPrivate bool
|
||||||
ghHostname string
|
ghHostname string
|
||||||
|
ghPath string
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ghTokenName = "GITHUB_TOKEN"
|
ghTokenName = "GITHUB_TOKEN"
|
||||||
ghBranch = "master"
|
ghBranch = "master"
|
||||||
ghInstallManifest = "toolkit.yaml"
|
ghInstallManifest = "toolkit-components.yaml"
|
||||||
ghSourceManifest = "toolkit-source.yaml"
|
ghSourceManifest = "toolkit-source.yaml"
|
||||||
ghKustomizationManifest = "toolkit-kustomization.yaml"
|
ghKustomizationManifest = "toolkit-kustomization.yaml"
|
||||||
ghDefaultHostname = "github.com"
|
ghDefaultHostname = "github.com"
|
||||||
@@ -77,6 +81,8 @@ func init() {
|
|||||||
bootstrapGitHubCmd.Flags().BoolVar(&ghPrivate, "private", true, "is private repository")
|
bootstrapGitHubCmd.Flags().BoolVar(&ghPrivate, "private", true, "is private repository")
|
||||||
bootstrapGitHubCmd.Flags().DurationVar(&ghInterval, "interval", time.Minute, "sync interval")
|
bootstrapGitHubCmd.Flags().DurationVar(&ghInterval, "interval", time.Minute, "sync interval")
|
||||||
bootstrapGitHubCmd.Flags().StringVar(&ghHostname, "hostname", ghDefaultHostname, "GitHub hostname")
|
bootstrapGitHubCmd.Flags().StringVar(&ghHostname, "hostname", ghDefaultHostname, "GitHub hostname")
|
||||||
|
bootstrapGitHubCmd.Flags().StringVar(&ghPath, "path", "", "repository path, when specified the cluster sync will be scoped to this path")
|
||||||
|
|
||||||
bootstrapCmd.AddCommand(bootstrapGitHubCmd)
|
bootstrapCmd.AddCommand(bootstrapGitHubCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,13 +127,13 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
|
|
||||||
// generate install manifests
|
// generate install manifests
|
||||||
logGenerate("generating manifests")
|
logGenerate("generating manifests")
|
||||||
manifest, err := generateGitHubInstall(namespace, tmpDir)
|
manifest, err := generateGitHubInstall(ghPath, namespace, tmpDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// stage install manifests
|
// stage install manifests
|
||||||
changed, err := commitGitHubManifests(repo, namespace)
|
changed, err := commitGitHubManifests(repo, ghPath, namespace)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -180,7 +186,7 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
return fmt.Errorf("generating deploy key failed: %w", err)
|
return fmt.Errorf("generating deploy key failed: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := createGitHubDeployKey(ctx, key, ghHostname, ghOwner, ghRepository, ghToken, ghPersonal); err != nil {
|
if err := createGitHubDeployKey(ctx, key, ghHostname, ghOwner, ghRepository, ghPath, ghToken); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
logSuccess("deploy key configured")
|
logSuccess("deploy key configured")
|
||||||
@@ -190,12 +196,12 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
if isInstall {
|
if isInstall {
|
||||||
// generate source and kustomization manifests
|
// generate source and kustomization manifests
|
||||||
logAction("generating sync manifests")
|
logAction("generating sync manifests")
|
||||||
if err := generateGitHubKustomization(sshURL, namespace, namespace, tmpDir, ghInterval); err != nil {
|
if err := generateGitHubKustomization(sshURL, namespace, namespace, ghPath, tmpDir, ghInterval); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// stage manifests
|
// stage manifests
|
||||||
changed, err = commitGitHubManifests(repo, namespace)
|
changed, err = commitGitHubManifests(repo, ghPath, namespace)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -210,7 +216,7 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
|
|
||||||
// apply manifests and waiting for sync
|
// apply manifests and waiting for sync
|
||||||
logAction("applying sync manifests")
|
logAction("applying sync manifests")
|
||||||
if err := applyGitHubKustomization(ctx, kubeClient, namespace, namespace, tmpDir); err != nil {
|
if err := applyGitHubKustomization(ctx, kubeClient, namespace, namespace, ghPath, tmpDir); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -296,7 +302,7 @@ func checkoutGitHubRepository(ctx context.Context, url, branch, token, path stri
|
|||||||
return repo, nil
|
return repo, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func generateGitHubInstall(namespace, tmpDir string) (string, error) {
|
func generateGitHubInstall(targetPath, namespace, tmpDir string) (string, error) {
|
||||||
tkDir := path.Join(tmpDir, ".tk")
|
tkDir := path.Join(tmpDir, ".tk")
|
||||||
defer os.RemoveAll(tkDir)
|
defer os.RemoveAll(tkDir)
|
||||||
|
|
||||||
@@ -308,7 +314,7 @@ func generateGitHubInstall(namespace, tmpDir string) (string, error) {
|
|||||||
return "", fmt.Errorf("generating manifests failed: %w", err)
|
return "", fmt.Errorf("generating manifests failed: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
manifestsDir := path.Join(tmpDir, namespace)
|
manifestsDir := path.Join(tmpDir, targetPath, namespace)
|
||||||
if err := os.MkdirAll(manifestsDir, os.ModePerm); err != nil {
|
if err := os.MkdirAll(manifestsDir, os.ModePerm); err != nil {
|
||||||
return "", fmt.Errorf("generating manifests failed: %w", err)
|
return "", fmt.Errorf("generating manifests failed: %w", err)
|
||||||
}
|
}
|
||||||
@@ -321,13 +327,13 @@ func generateGitHubInstall(namespace, tmpDir string) (string, error) {
|
|||||||
return manifest, nil
|
return manifest, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func commitGitHubManifests(repo *git.Repository, namespace string) (bool, error) {
|
func commitGitHubManifests(repo *git.Repository, targetPath, namespace string) (bool, error) {
|
||||||
w, err := repo.Worktree()
|
w, err := repo.Worktree()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = w.Add(namespace)
|
_, err = w.Add(path.Join(targetPath, namespace))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
@@ -368,7 +374,7 @@ func pushGitHubRepository(ctx context.Context, repo *git.Repository, token strin
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func generateGitHubKustomization(url, name, namespace, tmpDir string, interval time.Duration) error {
|
func generateGitHubKustomization(url, name, namespace, targetPath, tmpDir string, interval time.Duration) error {
|
||||||
gvk := sourcev1.GroupVersion.WithKind("GitRepository")
|
gvk := sourcev1.GroupVersion.WithKind("GitRepository")
|
||||||
gitRepository := sourcev1.GitRepository{
|
gitRepository := sourcev1.GitRepository{
|
||||||
TypeMeta: metav1.TypeMeta{
|
TypeMeta: metav1.TypeMeta{
|
||||||
@@ -398,7 +404,7 @@ func generateGitHubKustomization(url, name, namespace, tmpDir string, interval t
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := utils.writeFile(string(gitData), filepath.Join(tmpDir, namespace, ghSourceManifest)); err != nil {
|
if err := utils.writeFile(string(gitData), filepath.Join(tmpDir, targetPath, namespace, ghSourceManifest)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -417,7 +423,7 @@ func generateGitHubKustomization(url, name, namespace, tmpDir string, interval t
|
|||||||
Interval: metav1.Duration{
|
Interval: metav1.Duration{
|
||||||
Duration: 10 * time.Minute,
|
Duration: 10 * time.Minute,
|
||||||
},
|
},
|
||||||
Path: "./",
|
Path: "./" + strings.TrimPrefix("./", targetPath),
|
||||||
Prune: true,
|
Prune: true,
|
||||||
SourceRef: corev1.TypedLocalObjectReference{
|
SourceRef: corev1.TypedLocalObjectReference{
|
||||||
APIGroup: &emptyAPIGroup,
|
APIGroup: &emptyAPIGroup,
|
||||||
@@ -432,15 +438,15 @@ func generateGitHubKustomization(url, name, namespace, tmpDir string, interval t
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := utils.writeFile(string(ksData), filepath.Join(tmpDir, namespace, ghKustomizationManifest)); err != nil {
|
if err := utils.writeFile(string(ksData), filepath.Join(tmpDir, targetPath, namespace, ghKustomizationManifest)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func applyGitHubKustomization(ctx context.Context, kubeClient client.Client, name, namespace, tmpDir string) error {
|
func applyGitHubKustomization(ctx context.Context, kubeClient client.Client, name, namespace, targetPath, tmpDir string) error {
|
||||||
command := fmt.Sprintf("kubectl apply -f %s", filepath.Join(tmpDir, namespace))
|
command := fmt.Sprintf("kubectl apply -f %s", filepath.Join(tmpDir, targetPath, namespace))
|
||||||
if _, err := utils.execCommand(ctx, ModeStderrOS, command); err != nil {
|
if _, err := utils.execCommand(ctx, ModeStderrOS, command); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -515,20 +521,62 @@ func generateGitHubDeployKey(ctx context.Context, kubeClient client.Client, url
|
|||||||
return string(pair.PublicKey), nil
|
return string(pair.PublicKey), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func createGitHubDeployKey(ctx context.Context, key, hostname, owner, name, token string, isPersonal bool) error {
|
func createGitHubDeployKey(ctx context.Context, key, hostname, owner, repository, targetPath, token string) error {
|
||||||
gh, err := makeGitHubClient(hostname, token)
|
gh, err := makeGitHubClient(hostname, token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
keyName := fmt.Sprintf("tk-%s", namespace)
|
keyName := "tk"
|
||||||
isReadOnly := true
|
if targetPath != "" {
|
||||||
_, _, err = gh.Repositories.CreateKey(ctx, owner, name, &github.Key{
|
keyName = fmt.Sprintf("tk-%s", targetPath)
|
||||||
Title: &keyName,
|
|
||||||
Key: &key,
|
|
||||||
ReadOnly: &isReadOnly,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("github create deploy key error: %w", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// list deploy keys
|
||||||
|
keys, resp, err := gh.Repositories.ListKeys(ctx, owner, repository, nil)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("github list deploy keys error: %w", err)
|
||||||
|
}
|
||||||
|
if resp.StatusCode >= 300 {
|
||||||
|
return fmt.Errorf("github list deploy keys failed with status code: %s", resp.Status)
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if the key exists
|
||||||
|
shouldCreateKey := true
|
||||||
|
var existingKey *github.Key
|
||||||
|
for _, k := range keys {
|
||||||
|
if k.Title != nil && k.Key != nil && *k.Title == keyName {
|
||||||
|
if *k.Key != key {
|
||||||
|
existingKey = k
|
||||||
|
} else {
|
||||||
|
shouldCreateKey = false
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// delete existing key if the value differs
|
||||||
|
if existingKey != nil {
|
||||||
|
resp, err := gh.Repositories.DeleteKey(ctx, owner, repository, *existingKey.ID)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("github delete deploy key error: %w", err)
|
||||||
|
}
|
||||||
|
if resp.StatusCode >= 300 {
|
||||||
|
return fmt.Errorf("github delete deploy key failed with status code: %s", resp.Status)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// create key
|
||||||
|
if shouldCreateKey {
|
||||||
|
isReadOnly := true
|
||||||
|
_, _, err = gh.Repositories.CreateKey(ctx, owner, repository, &github.Key{
|
||||||
|
Title: &keyName,
|
||||||
|
Key: &key,
|
||||||
|
ReadOnly: &isReadOnly,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("github create deploy key error: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/manifoldco/promptui"
|
"github.com/manifoldco/promptui"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
@@ -24,12 +25,15 @@ cluster role bindings and CRDs.`,
|
|||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
uninstallCRDs bool
|
uninstallCRDs bool
|
||||||
uninstallDryRun bool
|
uninstallKustomizations bool
|
||||||
uninstallSilent bool
|
uninstallDryRun bool
|
||||||
|
uninstallSilent bool
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
uninstallCmd.Flags().BoolVarP(&uninstallKustomizations, "kustomizations", "", false,
|
||||||
|
"removes all kustomizations previously installed")
|
||||||
uninstallCmd.Flags().BoolVarP(&uninstallCRDs, "crds", "", false,
|
uninstallCmd.Flags().BoolVarP(&uninstallCRDs, "crds", "", false,
|
||||||
"removes all CRDs previously installed")
|
"removes all CRDs previously installed")
|
||||||
uninstallCmd.Flags().BoolVarP(&uninstallDryRun, "dry-run", "", false,
|
uninstallCmd.Flags().BoolVarP(&uninstallDryRun, "dry-run", "", false,
|
||||||
@@ -57,6 +61,20 @@ func uninstallCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if uninstallKustomizations {
|
||||||
|
logAction("uninstalling kustomizations")
|
||||||
|
command := fmt.Sprintf("kubectl -n %s delete kustomizations --all --timeout=%s %s",
|
||||||
|
namespace, timeout.String(), dryRun)
|
||||||
|
if _, err := utils.execCommand(ctx, ModeOS, command); err != nil {
|
||||||
|
return fmt.Errorf("uninstall failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: use the kustomizations snapshots to create a list of objects
|
||||||
|
// that are subject to deletion and wait for all of them to be terminated
|
||||||
|
logWaiting("waiting on GC")
|
||||||
|
time.Sleep(30 * time.Second)
|
||||||
|
}
|
||||||
|
|
||||||
kinds := "namespace,clusterroles,clusterrolebindings"
|
kinds := "namespace,clusterroles,clusterrolebindings"
|
||||||
if uninstallCRDs {
|
if uninstallCRDs {
|
||||||
kinds += ",crds"
|
kinds += ",crds"
|
||||||
|
|||||||
27
pkg/ssh/knownhosts/LICENSE
Normal file
27
pkg/ssh/knownhosts/LICENSE
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are
|
||||||
|
met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above
|
||||||
|
copyright notice, this list of conditions and the following disclaimer
|
||||||
|
in the documentation and/or other materials provided with the
|
||||||
|
distribution.
|
||||||
|
* Neither the name of Google Inc. nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
Reference in New Issue
Block a user