Compare commits
56 Commits
v0.0.1-bet
...
v0.0.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ebcbe98684 | ||
|
|
0e437c1105 | ||
|
|
4b18f56f00 | ||
|
|
fd7ab0a7fd | ||
|
|
c3c84d0ea0 | ||
|
|
43b00c5696 | ||
|
|
a9f4536de4 | ||
|
|
892230280c | ||
|
|
0969a0c42a | ||
|
|
185143ecf6 | ||
|
|
6e06f94a91 | ||
|
|
9af0bc31c3 | ||
|
|
6bcbb13bfb | ||
|
|
9a6d48d038 | ||
|
|
0e81f6c3af | ||
|
|
353a2dd92b | ||
|
|
ee749236e1 | ||
|
|
cf45401250 | ||
|
|
15429160c7 | ||
|
|
3ea7f31797 | ||
|
|
dbfa2f9944 | ||
|
|
98cf176c81 | ||
|
|
39b00797d6 | ||
|
|
1df5a36f84 | ||
|
|
1db3bb3d33 | ||
|
|
807848b6bb | ||
|
|
917598acf8 | ||
|
|
b8bddf6a39 | ||
|
|
da95b7559d | ||
|
|
041b475f49 | ||
|
|
c0a8604f90 | ||
|
|
ce80bd521a | ||
|
|
e92615b3a8 | ||
|
|
3d7349ee53 | ||
|
|
0935a637f9 | ||
|
|
c23d40ca71 | ||
|
|
9ffe908c66 | ||
|
|
45003436bf | ||
|
|
5074ff2889 | ||
|
|
c1d7582461 | ||
|
|
186c7d5786 | ||
|
|
7a87d353cb | ||
|
|
badd2a102f | ||
|
|
d0a79c2b4c | ||
|
|
bd781bbcfb | ||
|
|
f3d50e158a | ||
|
|
34ada4113f | ||
|
|
4f43b504f4 | ||
|
|
62e4b03342 | ||
|
|
dca7b0ba91 | ||
|
|
2a61befbfd | ||
|
|
287be26190 | ||
|
|
4992e11383 | ||
|
|
727734850e | ||
|
|
e042d25062 | ||
|
|
ba7a11d0e5 |
27
.github/workflows/docs.yaml
vendored
Normal file
27
.github/workflows/docs.yaml
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
name: Publish docs via GitHub Pages
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- docs*
|
||||
- master
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Deploy docs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout master
|
||||
uses: actions/checkout@v1
|
||||
- name: Copy assets
|
||||
run: |
|
||||
cp install/tk.sh docs/install.sh
|
||||
curl https://raw.githubusercontent.com/fluxcd/source-controller/master/docs/api/source.md > docs/components/source/api.md
|
||||
curl https://raw.githubusercontent.com/fluxcd/source-controller/master/docs/spec/v1alpha1/gitrepositories.md > docs/components/source/gitrepositories.md
|
||||
curl https://raw.githubusercontent.com/fluxcd/source-controller/master/docs/spec/v1alpha1/helmrepositories.md > docs/components/source/helmrepositories.md
|
||||
curl https://raw.githubusercontent.com/fluxcd/kustomize-controller/master/docs/api/kustomize.md > docs/components/kustomize/api.md
|
||||
curl https://raw.githubusercontent.com/fluxcd/kustomize-controller/master/docs/spec/v1alpha1/kustomization.md > docs/components/kustomize/kustomization.md
|
||||
- name: Deploy docs
|
||||
uses: mhausenblas/mkdocs-deploy-gh-pages@master
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
CUSTOM_DOMAIN: toolkit.fluxcd.io
|
||||
1
.github/workflows/e2e.yaml
vendored
1
.github/workflows/e2e.yaml
vendored
@@ -30,6 +30,7 @@ jobs:
|
||||
- name: Check if working tree is dirty
|
||||
run: |
|
||||
if [[ $(git diff --stat) != '' ]]; then
|
||||
git diff --stat
|
||||
echo 'run make test and commit changes'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
2
Makefile
2
Makefile
@@ -11,7 +11,7 @@ fmt:
|
||||
vet:
|
||||
go vet ./...
|
||||
|
||||
test: tidy fmt vet
|
||||
test: tidy fmt vet docs
|
||||
go test ./... -coverprofile cover.out
|
||||
|
||||
build:
|
||||
|
||||
@@ -7,11 +7,6 @@
|
||||
|
||||
Experimental toolkit for assembling CD pipelines the GitOps way.
|
||||
|
||||

|
||||

|
||||
|
||||
Components:
|
||||
* [Toolkit CLI](docs/cmd/tk.md)
|
||||
* [Source Controller](https://github.com/fluxcd/source-controller)
|
||||
* [Kustomize Controller](https://github.com/fluxcd/kustomize-controller)
|
||||
|
||||
To install the toolkit CLI, follow the [instructions](install/README.md).
|
||||
To get started with the toolkit please read the [docs](https://toolkit.fluxcd.io/).
|
||||
|
||||
@@ -1,7 +1,41 @@
|
||||
/*
|
||||
Copyright 2020 The Flux CD contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"sigs.k8s.io/yaml"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1alpha1"
|
||||
sourcev1 "github.com/fluxcd/source-controller/api/v1alpha1"
|
||||
)
|
||||
|
||||
var bootstrapCmd = &cobra.Command{
|
||||
@@ -13,8 +47,203 @@ var (
|
||||
bootstrapVersion string
|
||||
)
|
||||
|
||||
const (
|
||||
bootstrapBranch = "master"
|
||||
bootstrapInstallManifest = "toolkit-components.yaml"
|
||||
bootstrapSourceManifest = "toolkit-source.yaml"
|
||||
bootstrapKustomizationManifest = "toolkit-kustomization.yaml"
|
||||
)
|
||||
|
||||
func init() {
|
||||
bootstrapCmd.PersistentFlags().StringVar(&bootstrapVersion, "version", "master", "toolkit tag or branch")
|
||||
|
||||
rootCmd.AddCommand(bootstrapCmd)
|
||||
}
|
||||
|
||||
func generateInstallManifests(targetPath, namespace, tmpDir string) (string, error) {
|
||||
tkDir := path.Join(tmpDir, ".tk")
|
||||
defer os.RemoveAll(tkDir)
|
||||
|
||||
if err := os.MkdirAll(tkDir, os.ModePerm); err != nil {
|
||||
return "", fmt.Errorf("generating manifests failed: %w", err)
|
||||
}
|
||||
|
||||
if err := genInstallManifests(bootstrapVersion, namespace, components, tkDir); err != nil {
|
||||
return "", fmt.Errorf("generating manifests failed: %w", err)
|
||||
}
|
||||
|
||||
manifestsDir := path.Join(tmpDir, targetPath, namespace)
|
||||
if err := os.MkdirAll(manifestsDir, os.ModePerm); err != nil {
|
||||
return "", fmt.Errorf("generating manifests failed: %w", err)
|
||||
}
|
||||
|
||||
manifest := path.Join(manifestsDir, bootstrapInstallManifest)
|
||||
if err := buildKustomization(tkDir, manifest); err != nil {
|
||||
return "", fmt.Errorf("build kustomization failed: %w", err)
|
||||
}
|
||||
|
||||
return manifest, nil
|
||||
}
|
||||
|
||||
func applyInstallManifests(ctx context.Context, manifestPath string, components []string) error {
|
||||
command := fmt.Sprintf("kubectl apply -f %s", manifestPath)
|
||||
if _, err := utils.execCommand(ctx, ModeOS, command); err != nil {
|
||||
return fmt.Errorf("install failed")
|
||||
}
|
||||
|
||||
for _, deployment := range components {
|
||||
command = fmt.Sprintf("kubectl -n %s rollout status deployment %s --timeout=%s",
|
||||
namespace, deployment, timeout.String())
|
||||
if _, err := utils.execCommand(ctx, ModeOS, command); err != nil {
|
||||
return fmt.Errorf("install failed")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func generateSyncManifests(url, name, namespace, targetPath, tmpDir string, interval time.Duration) error {
|
||||
gvk := sourcev1.GroupVersion.WithKind("GitRepository")
|
||||
gitRepository := sourcev1.GitRepository{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: gvk.Kind,
|
||||
APIVersion: gvk.GroupVersion().String(),
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: namespace,
|
||||
},
|
||||
Spec: sourcev1.GitRepositorySpec{
|
||||
URL: url,
|
||||
Interval: metav1.Duration{
|
||||
Duration: interval,
|
||||
},
|
||||
Reference: &sourcev1.GitRepositoryRef{
|
||||
Branch: "master",
|
||||
},
|
||||
SecretRef: &corev1.LocalObjectReference{
|
||||
Name: name,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
gitData, err := yaml.Marshal(gitRepository)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := utils.writeFile(string(gitData), filepath.Join(tmpDir, targetPath, namespace, bootstrapSourceManifest)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
gvk = kustomizev1.GroupVersion.WithKind("Kustomization")
|
||||
emptyAPIGroup := ""
|
||||
kustomization := kustomizev1.Kustomization{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: gvk.Kind,
|
||||
APIVersion: gvk.GroupVersion().String(),
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: namespace,
|
||||
},
|
||||
Spec: kustomizev1.KustomizationSpec{
|
||||
Interval: metav1.Duration{
|
||||
Duration: 10 * time.Minute,
|
||||
},
|
||||
Path: fmt.Sprintf("./%s", strings.TrimPrefix(targetPath, "./")),
|
||||
Prune: true,
|
||||
SourceRef: corev1.TypedLocalObjectReference{
|
||||
APIGroup: &emptyAPIGroup,
|
||||
Kind: "GitRepository",
|
||||
Name: name,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
ksData, err := yaml.Marshal(kustomization)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := utils.writeFile(string(ksData), filepath.Join(tmpDir, targetPath, namespace, bootstrapKustomizationManifest)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func applySyncManifests(ctx context.Context, kubeClient client.Client, name, namespace, targetPath, tmpDir string) error {
|
||||
command := fmt.Sprintf("kubectl apply -f %s", filepath.Join(tmpDir, targetPath, namespace))
|
||||
if _, err := utils.execCommand(ctx, ModeStderrOS, command); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
logWaiting("waiting for cluster sync")
|
||||
|
||||
if err := wait.PollImmediate(pollInterval, timeout,
|
||||
isGitRepositoryReady(ctx, kubeClient, name, namespace)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := wait.PollImmediate(pollInterval, timeout,
|
||||
isKustomizationReady(ctx, kubeClient, name, namespace)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func shouldInstallManifests(ctx context.Context, kubeClient client.Client, namespace string) bool {
|
||||
namespacedName := types.NamespacedName{
|
||||
Namespace: namespace,
|
||||
Name: namespace,
|
||||
}
|
||||
var kustomization kustomizev1.Kustomization
|
||||
if err := kubeClient.Get(ctx, namespacedName, &kustomization); err != nil {
|
||||
return true
|
||||
}
|
||||
|
||||
return kustomization.Status.LastAppliedRevision == ""
|
||||
}
|
||||
|
||||
func shouldCreateDeployKey(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 generateDeployKey(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
|
||||
}
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
Copyright 2020 The Flux CD contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
@@ -7,25 +23,11 @@ import (
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"sigs.k8s.io/yaml"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-git/go-git/v5"
|
||||
"github.com/go-git/go-git/v5/plumbing"
|
||||
"github.com/go-git/go-git/v5/plumbing/object"
|
||||
"github.com/go-git/go-git/v5/plumbing/transport/http"
|
||||
"github.com/google/go-github/v32/github"
|
||||
"github.com/spf13/cobra"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1alpha1"
|
||||
sourcev1 "github.com/fluxcd/source-controller/api/v1alpha1"
|
||||
"github.com/fluxcd/toolkit/pkg/git"
|
||||
)
|
||||
|
||||
var bootstrapGitHubCmd = &cobra.Command{
|
||||
@@ -43,6 +45,12 @@ the bootstrap command will perform an upgrade if needed.`,
|
||||
# Run bootstrap for a private repo owned by a GitHub organization
|
||||
bootstrap github --owner=<organization> --repository=<repo name>
|
||||
|
||||
# Run bootstrap for a private repo and assign organization teams to it
|
||||
bootstrap github --owner=<organization> --repository=<repo name> --team=<team1 slug> --team=<team2 slug>
|
||||
|
||||
# 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
|
||||
bootstrap github --owner=<user> --repository=<repo name> --private=false --personal=true
|
||||
|
||||
@@ -59,37 +67,41 @@ var (
|
||||
ghPersonal bool
|
||||
ghPrivate bool
|
||||
ghHostname string
|
||||
ghPath string
|
||||
ghTeams []string
|
||||
)
|
||||
|
||||
const (
|
||||
ghTokenName = "GITHUB_TOKEN"
|
||||
ghBranch = "master"
|
||||
ghInstallManifest = "toolkit.yaml"
|
||||
ghSourceManifest = "toolkit-source.yaml"
|
||||
ghKustomizationManifest = "toolkit-kustomization.yaml"
|
||||
ghDefaultHostname = "github.com"
|
||||
ghDefaultPermission = "maintain"
|
||||
)
|
||||
|
||||
func init() {
|
||||
bootstrapGitHubCmd.Flags().StringVar(&ghOwner, "owner", "", "GitHub user or organization name")
|
||||
bootstrapGitHubCmd.Flags().StringVar(&ghRepository, "repository", "", "GitHub repository name")
|
||||
bootstrapGitHubCmd.Flags().StringArrayVar(&ghTeams, "team", []string{}, "GitHub team to be given maintainer access")
|
||||
bootstrapGitHubCmd.Flags().BoolVar(&ghPersonal, "personal", false, "is personal repository")
|
||||
bootstrapGitHubCmd.Flags().BoolVar(&ghPrivate, "private", true, "is private repository")
|
||||
bootstrapGitHubCmd.Flags().DurationVar(&ghInterval, "interval", time.Minute, "sync interval")
|
||||
bootstrapGitHubCmd.Flags().StringVar(&ghHostname, "hostname", ghDefaultHostname, "GitHub hostname")
|
||||
bootstrapGitHubCmd.Flags().StringVar(&ghHostname, "hostname", git.GitHubDefaultHostname, "GitHub hostname")
|
||||
bootstrapGitHubCmd.Flags().StringVar(&ghPath, "path", "", "repository path, when specified the cluster sync will be scoped to this path")
|
||||
|
||||
bootstrapCmd.AddCommand(bootstrapGitHubCmd)
|
||||
}
|
||||
|
||||
func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
|
||||
ghToken := os.Getenv(ghTokenName)
|
||||
ghToken := os.Getenv(git.GitHubTokenName)
|
||||
if ghToken == "" {
|
||||
return fmt.Errorf("%s environment variable not found", ghTokenName)
|
||||
return fmt.Errorf("%s environment variable not found", git.GitHubTokenName)
|
||||
}
|
||||
|
||||
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")
|
||||
repository, err := git.NewRepository(ghRepository, ghOwner, ghHostname, ghToken, "tk", ghOwner+"@users.noreply.github.com")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
provider := &git.GithubProvider{
|
||||
IsPrivate: ghPrivate,
|
||||
IsPersonal: ghPersonal,
|
||||
}
|
||||
|
||||
kubeClient, err := utils.kubeClient(kubeconfig)
|
||||
@@ -108,33 +120,49 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
|
||||
|
||||
// create GitHub repository if doesn't exists
|
||||
logAction("connecting to %s", ghHostname)
|
||||
if err := createGitHubRepository(ctx, ghHostname, ghOwner, ghRepository, ghToken, ghPrivate, ghPersonal); err != nil {
|
||||
changed, err := provider.CreateRepository(ctx, repository)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if changed {
|
||||
logSuccess("repository created")
|
||||
}
|
||||
|
||||
withErrors := false
|
||||
// add teams to org repository
|
||||
if !ghPersonal {
|
||||
for _, team := range ghTeams {
|
||||
if changed, err := provider.AddTeam(ctx, repository, team, ghDefaultPermission); err != nil {
|
||||
logFailure(err.Error())
|
||||
withErrors = true
|
||||
} else if changed {
|
||||
logSuccess("%s team access granted", team)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// clone repository and checkout the master branch
|
||||
repo, err := checkoutGitHubRepository(ctx, ghURL, ghBranch, ghToken, tmpDir)
|
||||
if err != nil {
|
||||
if err := repository.Checkout(ctx, bootstrapBranch, tmpDir); err != nil {
|
||||
return err
|
||||
}
|
||||
logSuccess("repository cloned")
|
||||
|
||||
// generate install manifests
|
||||
logGenerate("generating manifests")
|
||||
manifest, err := generateGitHubInstall(namespace, tmpDir)
|
||||
manifest, err := generateInstallManifests(ghPath, namespace, tmpDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// stage install manifests
|
||||
changed, err := commitGitHubManifests(repo, namespace)
|
||||
changed, err = repository.Commit(ctx, path.Join(ghPath, namespace), "Add manifests")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// push install manifests
|
||||
if changed {
|
||||
if err := pushGitHubRepository(ctx, repo, ghToken); err != nil {
|
||||
if err := repository.Push(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
logSuccess("components manifests pushed")
|
||||
@@ -143,392 +171,71 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
|
||||
}
|
||||
|
||||
// determine if repo synchronization is working
|
||||
isInstall := shouldInstallGitHub(ctx, kubeClient, namespace)
|
||||
isInstall := shouldInstallManifests(ctx, kubeClient, namespace)
|
||||
|
||||
if isInstall {
|
||||
// apply install manifests
|
||||
logAction("installing components in %s namespace", namespace)
|
||||
command := fmt.Sprintf("kubectl apply -f %s", manifest)
|
||||
if _, err := utils.execCommand(ctx, ModeOS, command); err != nil {
|
||||
return fmt.Errorf("install failed")
|
||||
if err := applyInstallManifests(ctx, manifest, components); err != nil {
|
||||
return err
|
||||
}
|
||||
logSuccess("install completed")
|
||||
|
||||
// check installation
|
||||
logWaiting("verifying installation")
|
||||
for _, deployment := range components {
|
||||
command = fmt.Sprintf("kubectl -n %s rollout status deployment %s --timeout=%s",
|
||||
namespace, deployment, timeout.String())
|
||||
if _, err := utils.execCommand(ctx, ModeOS, command); err != nil {
|
||||
return fmt.Errorf("install failed")
|
||||
} else {
|
||||
logSuccess("%s ready", deployment)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// setup SSH deploy key
|
||||
if shouldCreateGitHubDeployKey(ctx, kubeClient, namespace) {
|
||||
if shouldCreateDeployKey(ctx, kubeClient, namespace) {
|
||||
logAction("configuring deploy key")
|
||||
u, err := url.Parse(sshURL)
|
||||
u, err := url.Parse(repository.GetSSH())
|
||||
if err != nil {
|
||||
return fmt.Errorf("git URL parse failed: %w", err)
|
||||
}
|
||||
|
||||
key, err := generateGitHubDeployKey(ctx, kubeClient, u, namespace)
|
||||
key, err := generateDeployKey(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 err
|
||||
keyName := "tk"
|
||||
if ghPath != "" {
|
||||
keyName = fmt.Sprintf("tk-%s", ghPath)
|
||||
}
|
||||
|
||||
if changed, err := provider.AddDeployKey(ctx, repository, key, keyName); err != nil {
|
||||
return err
|
||||
} else if changed {
|
||||
logSuccess("deploy key configured")
|
||||
}
|
||||
logSuccess("deploy key configured")
|
||||
}
|
||||
|
||||
// configure repo synchronization
|
||||
if isInstall {
|
||||
// generate source and kustomization manifests
|
||||
logAction("generating sync manifests")
|
||||
if err := generateGitHubKustomization(sshURL, namespace, namespace, tmpDir, ghInterval); err != nil {
|
||||
if err := generateSyncManifests(repository.GetSSH(), namespace, namespace, ghPath, tmpDir, ghInterval); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// stage manifests
|
||||
changed, err = commitGitHubManifests(repo, namespace)
|
||||
if err != nil {
|
||||
// commit and push manifests
|
||||
if changed, err = repository.Commit(ctx, path.Join(ghPath, namespace), "Add manifests"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// push manifests
|
||||
if changed {
|
||||
if err := pushGitHubRepository(ctx, repo, ghToken); err != nil {
|
||||
} else if changed {
|
||||
if err := repository.Push(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
logSuccess("sync manifests pushed")
|
||||
}
|
||||
logSuccess("sync manifests pushed")
|
||||
|
||||
// apply manifests and waiting for sync
|
||||
logAction("applying sync manifests")
|
||||
if err := applyGitHubKustomization(ctx, kubeClient, namespace, namespace, tmpDir); err != nil {
|
||||
if err := applySyncManifests(ctx, kubeClient, namespace, namespace, ghPath, tmpDir); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if withErrors {
|
||||
return fmt.Errorf("bootstrap completed with errors")
|
||||
}
|
||||
|
||||
logSuccess("bootstrap finished")
|
||||
return nil
|
||||
}
|
||||
|
||||
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)
|
||||
uploadURL := fmt.Sprintf("https://%s/api/uploads/", hostname)
|
||||
if g, err := github.NewEnterpriseClient(baseURL, uploadURL, auth.Client()); err == nil {
|
||||
gh = g
|
||||
} else {
|
||||
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
|
||||
}
|
||||
|
||||
if _, _, err := gh.Repositories.Get(ctx, org, name); err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
autoInit := true
|
||||
_, _, err = gh.Repositories.Create(ctx, org, &github.Repository{
|
||||
AutoInit: &autoInit,
|
||||
Name: &name,
|
||||
Private: &isPrivate,
|
||||
})
|
||||
if err != nil {
|
||||
if !strings.Contains(err.Error(), "name already exists on this account") {
|
||||
return fmt.Errorf("github create repository error: %w", err)
|
||||
}
|
||||
} else {
|
||||
logSuccess("repository created")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkoutGitHubRepository(ctx context.Context, url, branch, token, path string) (*git.Repository, error) {
|
||||
auth := &http.BasicAuth{
|
||||
Username: "git",
|
||||
Password: token,
|
||||
}
|
||||
repo, err := git.PlainCloneContext(ctx, path, false, &git.CloneOptions{
|
||||
URL: url,
|
||||
Auth: auth,
|
||||
RemoteName: git.DefaultRemoteName,
|
||||
ReferenceName: plumbing.NewBranchReferenceName(branch),
|
||||
SingleBranch: true,
|
||||
NoCheckout: false,
|
||||
Progress: nil,
|
||||
Tags: git.NoTags,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("git clone error: %w", err)
|
||||
}
|
||||
|
||||
_, err = repo.Head()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("git resolve HEAD error: %w", err)
|
||||
}
|
||||
|
||||
return repo, nil
|
||||
}
|
||||
|
||||
func generateGitHubInstall(namespace, tmpDir string) (string, error) {
|
||||
tkDir := path.Join(tmpDir, ".tk")
|
||||
defer os.RemoveAll(tkDir)
|
||||
|
||||
if err := os.MkdirAll(tkDir, os.ModePerm); err != nil {
|
||||
return "", fmt.Errorf("generating manifests failed: %w", err)
|
||||
}
|
||||
|
||||
if err := genInstallManifests(bootstrapVersion, namespace, components, tkDir); err != nil {
|
||||
return "", fmt.Errorf("generating manifests failed: %w", err)
|
||||
}
|
||||
|
||||
manifestsDir := path.Join(tmpDir, namespace)
|
||||
if err := os.MkdirAll(manifestsDir, os.ModePerm); err != nil {
|
||||
return "", fmt.Errorf("generating manifests failed: %w", err)
|
||||
}
|
||||
|
||||
manifest := path.Join(manifestsDir, ghInstallManifest)
|
||||
if err := buildKustomization(tkDir, manifest); err != nil {
|
||||
return "", fmt.Errorf("build kustomization failed: %w", err)
|
||||
}
|
||||
|
||||
return manifest, nil
|
||||
}
|
||||
|
||||
func commitGitHubManifests(repo *git.Repository, namespace string) (bool, error) {
|
||||
w, err := repo.Worktree()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
_, err = w.Add(namespace)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
status, err := w.Status()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if !status.IsClean() {
|
||||
if _, err := w.Commit("Add manifests", &git.CommitOptions{
|
||||
Author: &object.Signature{
|
||||
Name: "tk",
|
||||
Email: "tk@users.noreply.github.com",
|
||||
When: time.Now(),
|
||||
},
|
||||
}); err != nil {
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func pushGitHubRepository(ctx context.Context, repo *git.Repository, token string) error {
|
||||
auth := &http.BasicAuth{
|
||||
Username: "git",
|
||||
Password: token,
|
||||
}
|
||||
err := repo.PushContext(ctx, &git.PushOptions{
|
||||
Auth: auth,
|
||||
Progress: nil,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("git push error: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func generateGitHubKustomization(url, name, namespace, tmpDir string, interval time.Duration) error {
|
||||
gvk := sourcev1.GroupVersion.WithKind("GitRepository")
|
||||
gitRepository := sourcev1.GitRepository{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: gvk.Kind,
|
||||
APIVersion: gvk.GroupVersion().String(),
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: namespace,
|
||||
},
|
||||
Spec: sourcev1.GitRepositorySpec{
|
||||
URL: url,
|
||||
Interval: metav1.Duration{
|
||||
Duration: interval,
|
||||
},
|
||||
Reference: &sourcev1.GitRepositoryRef{
|
||||
Branch: "master",
|
||||
},
|
||||
SecretRef: &corev1.LocalObjectReference{
|
||||
Name: name,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
gitData, err := yaml.Marshal(gitRepository)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := utils.writeFile(string(gitData), filepath.Join(tmpDir, namespace, ghSourceManifest)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
gvk = kustomizev1.GroupVersion.WithKind("Kustomization")
|
||||
emptyAPIGroup := ""
|
||||
kustomization := kustomizev1.Kustomization{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: gvk.Kind,
|
||||
APIVersion: gvk.GroupVersion().String(),
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: namespace,
|
||||
},
|
||||
Spec: kustomizev1.KustomizationSpec{
|
||||
Interval: metav1.Duration{
|
||||
Duration: 10 * time.Minute,
|
||||
},
|
||||
Path: "./",
|
||||
Prune: true,
|
||||
SourceRef: corev1.TypedLocalObjectReference{
|
||||
APIGroup: &emptyAPIGroup,
|
||||
Kind: "GitRepository",
|
||||
Name: name,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
ksData, err := yaml.Marshal(kustomization)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := utils.writeFile(string(ksData), filepath.Join(tmpDir, namespace, ghKustomizationManifest)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func applyGitHubKustomization(ctx context.Context, kubeClient client.Client, name, namespace, tmpDir string) error {
|
||||
command := fmt.Sprintf("kubectl apply -f %s", filepath.Join(tmpDir, namespace))
|
||||
if _, err := utils.execCommand(ctx, ModeStderrOS, command); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
logWaiting("waiting for cluster sync")
|
||||
|
||||
if err := wait.PollImmediate(pollInterval, timeout,
|
||||
isGitRepositoryReady(ctx, kubeClient, name, namespace)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := wait.PollImmediate(pollInterval, timeout,
|
||||
isKustomizationReady(ctx, kubeClient, name, namespace)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func shouldInstallGitHub(ctx context.Context, kubeClient client.Client, namespace string) bool {
|
||||
namespacedName := types.NamespacedName{
|
||||
Namespace: namespace,
|
||||
Name: namespace,
|
||||
}
|
||||
var kustomization kustomizev1.Kustomization
|
||||
if err := kubeClient.Get(ctx, namespacedName, &kustomization); err != nil {
|
||||
return true
|
||||
}
|
||||
|
||||
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)
|
||||
isReadOnly := true
|
||||
_, _, err = gh.Repositories.CreateKey(ctx, owner, name, &github.Key{
|
||||
Title: &keyName,
|
||||
Key: &key,
|
||||
ReadOnly: &isReadOnly,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("github create deploy key error: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
215
cmd/tk/bootstrap_gitlab.go
Normal file
215
cmd/tk/bootstrap_gitlab.go
Normal file
@@ -0,0 +1,215 @@
|
||||
/*
|
||||
Copyright 2020 The Flux CD contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/fluxcd/toolkit/pkg/git"
|
||||
)
|
||||
|
||||
var bootstrapGitLabCmd = &cobra.Command{
|
||||
Use: "gitlab",
|
||||
Short: "Bootstrap GitLab repository",
|
||||
Long: `
|
||||
The bootstrap command creates the GitLab repository if it doesn't exists and
|
||||
commits the toolkit components manifests to the master branch.
|
||||
Then it configure the target cluster to synchronize with the repository.
|
||||
If the toolkit components are present on the cluster,
|
||||
the bootstrap command will perform an upgrade if needed.`,
|
||||
Example: ` # Create a GitLab API token and export it as an env var
|
||||
export GITLAB_TOKEN=<my-token>
|
||||
|
||||
# Run bootstrap for a private repo owned by a GitLab group
|
||||
bootstrap gitlab --owner=<group> --repository=<repo name>
|
||||
|
||||
# Run bootstrap for a repository path
|
||||
bootstrap gitlab --owner=<group> --repository=<repo name> --path=dev-cluster
|
||||
|
||||
# Run bootstrap for a public repository on a personal account
|
||||
bootstrap gitlab --owner=<user> --repository=<repo name> --private=false --personal=true
|
||||
|
||||
# Run bootstrap for a private repo hosted on a GitLab server
|
||||
bootstrap gitlab --owner=<group> --repository=<repo name> --hostname=<domain>
|
||||
`,
|
||||
RunE: bootstrapGitLabCmdRun,
|
||||
}
|
||||
|
||||
var (
|
||||
glOwner string
|
||||
glRepository string
|
||||
glInterval time.Duration
|
||||
glPersonal bool
|
||||
glPrivate bool
|
||||
glHostname string
|
||||
glPath string
|
||||
)
|
||||
|
||||
func init() {
|
||||
bootstrapGitLabCmd.Flags().StringVar(&glOwner, "owner", "", "GitLab user or group name")
|
||||
bootstrapGitLabCmd.Flags().StringVar(&glRepository, "repository", "", "GitLab repository name")
|
||||
bootstrapGitLabCmd.Flags().BoolVar(&glPersonal, "personal", false, "is personal repository")
|
||||
bootstrapGitLabCmd.Flags().BoolVar(&glPrivate, "private", true, "is private repository")
|
||||
bootstrapGitLabCmd.Flags().DurationVar(&glInterval, "interval", time.Minute, "sync interval")
|
||||
bootstrapGitLabCmd.Flags().StringVar(&glHostname, "hostname", git.GitLabDefaultHostname, "GitLab hostname")
|
||||
bootstrapGitLabCmd.Flags().StringVar(&glPath, "path", "", "repository path, when specified the cluster sync will be scoped to this path")
|
||||
|
||||
bootstrapCmd.AddCommand(bootstrapGitLabCmd)
|
||||
}
|
||||
|
||||
func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
|
||||
glToken := os.Getenv(git.GitLabTokenName)
|
||||
if glToken == "" {
|
||||
return fmt.Errorf("%s environment variable not found", git.GitLabTokenName)
|
||||
}
|
||||
|
||||
repository, err := git.NewRepository(glRepository, glOwner, glHostname, glToken, "tk", glOwner+"@users.noreply.gitlab.com")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
provider := &git.GitLabProvider{
|
||||
IsPrivate: glPrivate,
|
||||
IsPersonal: glPersonal,
|
||||
}
|
||||
|
||||
kubeClient, err := utils.kubeClient(kubeconfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tmpDir, err := ioutil.TempDir("", namespace)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
||||
defer cancel()
|
||||
|
||||
// create GitLab project if doesn't exists
|
||||
logAction("connecting to %s", glHostname)
|
||||
changed, err := provider.CreateRepository(ctx, repository)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if changed {
|
||||
logSuccess("repository created")
|
||||
}
|
||||
|
||||
// clone repository and checkout the master branch
|
||||
if err := repository.Checkout(ctx, bootstrapBranch, tmpDir); err != nil {
|
||||
return err
|
||||
}
|
||||
logSuccess("repository cloned")
|
||||
|
||||
// generate install manifests
|
||||
logGenerate("generating manifests")
|
||||
manifest, err := generateInstallManifests(glPath, namespace, tmpDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// stage install manifests
|
||||
changed, err = repository.Commit(ctx, path.Join(glPath, namespace), "Add manifests")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// push install manifests
|
||||
if changed {
|
||||
if err := repository.Push(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
logSuccess("components manifests pushed")
|
||||
} else {
|
||||
logSuccess("components are up to date")
|
||||
}
|
||||
|
||||
// determine if repo synchronization is working
|
||||
isInstall := shouldInstallManifests(ctx, kubeClient, namespace)
|
||||
|
||||
if isInstall {
|
||||
// apply install manifests
|
||||
logAction("installing components in %s namespace", namespace)
|
||||
if err := applyInstallManifests(ctx, manifest, components); err != nil {
|
||||
return err
|
||||
}
|
||||
logSuccess("install completed")
|
||||
}
|
||||
|
||||
// setup SSH deploy key
|
||||
if shouldCreateDeployKey(ctx, kubeClient, namespace) {
|
||||
logAction("configuring deploy key")
|
||||
u, err := url.Parse(repository.GetSSH())
|
||||
if err != nil {
|
||||
return fmt.Errorf("git URL parse failed: %w", err)
|
||||
}
|
||||
|
||||
key, err := generateDeployKey(ctx, kubeClient, u, namespace)
|
||||
if err != nil {
|
||||
return fmt.Errorf("generating deploy key failed: %w", err)
|
||||
}
|
||||
|
||||
keyName := "tk"
|
||||
if glPath != "" {
|
||||
keyName = fmt.Sprintf("tk-%s", glPath)
|
||||
}
|
||||
|
||||
if changed, err := provider.AddDeployKey(ctx, repository, key, keyName); err != nil {
|
||||
return err
|
||||
} else if changed {
|
||||
logSuccess("deploy key configured")
|
||||
}
|
||||
}
|
||||
|
||||
// configure repo synchronization
|
||||
if isInstall {
|
||||
// generate source and kustomization manifests
|
||||
logAction("generating sync manifests")
|
||||
if err := generateSyncManifests(repository.GetSSH(), namespace, namespace, glPath, tmpDir, glInterval); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// commit and push manifests
|
||||
if changed, err = repository.Commit(ctx, path.Join(glPath, namespace), "Add manifests"); err != nil {
|
||||
return err
|
||||
} else if changed {
|
||||
if err := repository.Push(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
logSuccess("sync manifests pushed")
|
||||
}
|
||||
|
||||
// apply manifests and waiting for sync
|
||||
logAction("applying sync manifests")
|
||||
if err := applySyncManifests(ctx, kubeClient, namespace, namespace, glPath, tmpDir); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
logSuccess("bootstrap finished")
|
||||
return nil
|
||||
}
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
Copyright 2020 The Flux CD contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
@@ -106,35 +122,35 @@ func kubectlCheck(ctx context.Context, version string) bool {
|
||||
func kubernetesCheck(version string) bool {
|
||||
cfg, err := clientcmd.BuildConfigFromFlags("", kubeconfig)
|
||||
if err != nil {
|
||||
logFailure("kubernetes client initialization failed: %s", err.Error())
|
||||
logFailure("Kubernetes client initialization failed: %s", err.Error())
|
||||
return false
|
||||
}
|
||||
|
||||
client, err := kubernetes.NewForConfig(cfg)
|
||||
if err != nil {
|
||||
logFailure("kubernetes client initialization failed: %s", err.Error())
|
||||
logFailure("Kubernetes client initialization failed: %s", err.Error())
|
||||
return false
|
||||
}
|
||||
|
||||
ver, err := client.Discovery().ServerVersion()
|
||||
if err != nil {
|
||||
logFailure("kubernetes API call failed %s", err.Error())
|
||||
logFailure("Kubernetes API call failed: %s", err.Error())
|
||||
return false
|
||||
}
|
||||
|
||||
v, err := semver.ParseTolerant(ver.String())
|
||||
if err != nil {
|
||||
logFailure("kubernetes version can't be determined")
|
||||
logFailure("Kubernetes version can't be determined")
|
||||
return false
|
||||
}
|
||||
|
||||
rng, _ := semver.ParseRange(version)
|
||||
if !rng(v) {
|
||||
logFailure("kubernetes version must be %s", version)
|
||||
logFailure("Kubernetes version must be %s", version)
|
||||
return false
|
||||
}
|
||||
|
||||
logSuccess("kubernetes %s %s", v.String(), version)
|
||||
logSuccess("Kubernetes %s %s", v.String(), version)
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
Copyright 2020 The Flux CD contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
Copyright 2020 The Flux CD contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
Copyright 2020 The Flux CD contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
Copyright 2020 The Flux CD contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
Copyright 2020 The Flux CD contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
@@ -242,7 +258,7 @@ func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
|
||||
}
|
||||
|
||||
if gitRepository.Status.Artifact != nil {
|
||||
logSuccess("fetched revision %s", gitRepository.Status.Artifact.Revision)
|
||||
logSuccess("fetched revision: %s", gitRepository.Status.Artifact.Revision)
|
||||
} else {
|
||||
return fmt.Errorf("git sync failed, artifact not found")
|
||||
}
|
||||
@@ -252,7 +268,7 @@ func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
|
||||
|
||||
func generateKeyPair(ctx context.Context) (*ssh.KeyPair, error) {
|
||||
var keyGen ssh.KeyPairGenerator
|
||||
switch sourceGitKeyAlgorithm.String() {
|
||||
switch algorithm := sourceGitKeyAlgorithm.String(); algorithm {
|
||||
case "rsa":
|
||||
keyGen = ssh.NewRSAGenerator(int(sourceGitRSABits))
|
||||
case "ecdsa":
|
||||
@@ -260,11 +276,11 @@ func generateKeyPair(ctx context.Context) (*ssh.KeyPair, error) {
|
||||
case "ed25519":
|
||||
keyGen = ssh.NewEd25519Generator()
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported public key algorithm '%s'", sourceGitKeyAlgorithm.String())
|
||||
return nil, fmt.Errorf("unsupported public key algorithm: %s", algorithm)
|
||||
}
|
||||
pair, err := keyGen.Generate()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("key pair generation failed: %w", err)
|
||||
return nil, fmt.Errorf("key pair generation failed, error: %w", err)
|
||||
}
|
||||
return pair, nil
|
||||
}
|
||||
@@ -276,7 +292,7 @@ func scanHostKey(ctx context.Context, url *url.URL) ([]byte, error) {
|
||||
}
|
||||
hostKey, err := ssh.ScanHostKey(host, 30*time.Second)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("SSH key scan for host '%s' failed: %w", host, err)
|
||||
return nil, fmt.Errorf("SSH key scan for host %s failed, error: %w", host, err)
|
||||
}
|
||||
return hostKey, nil
|
||||
}
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
Copyright 2020 The Flux CD contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
Copyright 2020 The Flux CD contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
Copyright 2020 The Flux CD contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
Copyright 2020 The Flux CD contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
Copyright 2020 The Flux CD contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
Copyright 2020 The Flux CD contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
Copyright 2020 The Flux CD contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
Copyright 2020 The Flux CD contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
@@ -109,16 +125,16 @@ func exportGit(source sourcev1.GitRepository) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func exportGitCredentials(ctx context.Context, kubeClinet client.Client, source sourcev1.GitRepository) error {
|
||||
func exportGitCredentials(ctx context.Context, kubeClient client.Client, source sourcev1.GitRepository) error {
|
||||
if source.Spec.SecretRef != nil {
|
||||
namespacedName := types.NamespacedName{
|
||||
Namespace: source.Namespace,
|
||||
Name: source.Spec.SecretRef.Name,
|
||||
}
|
||||
var cred corev1.Secret
|
||||
err := kubeClinet.Get(ctx, namespacedName, &cred)
|
||||
err := kubeClient.Get(ctx, namespacedName, &cred)
|
||||
if err != nil {
|
||||
return fmt.Errorf("get secret failed: %w", err)
|
||||
return fmt.Errorf("failed to retrieve secret %s, error: %w", namespacedName.Name, err)
|
||||
}
|
||||
|
||||
exported := corev1.Secret{
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
Copyright 2020 The Flux CD contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
Copyright 2020 The Flux CD contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
Copyright 2020 The Flux CD contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
@@ -51,7 +67,11 @@ func getKsCmdRun(cmd *cobra.Command, args []string) error {
|
||||
for _, condition := range kustomization.Status.Conditions {
|
||||
if condition.Type == kustomizev1.ReadyCondition {
|
||||
if condition.Status != corev1.ConditionFalse {
|
||||
logSuccess("%s last applied revision %s", kustomization.GetName(), kustomization.Status.LastAppliedRevision)
|
||||
if kustomization.Status.LastAppliedRevision != "" {
|
||||
logSuccess("%s last applied revision %s", kustomization.GetName(), kustomization.Status.LastAppliedRevision)
|
||||
} else {
|
||||
logSuccess("%s reconciling", kustomization.GetName())
|
||||
}
|
||||
} else {
|
||||
logFailure("%s %s", kustomization.GetName(), condition.Message)
|
||||
}
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
Copyright 2020 The Flux CD contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
Copyright 2020 The Flux CD contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
@@ -46,7 +62,7 @@ func getSourceGitCmdRun(cmd *cobra.Command, args []string) error {
|
||||
for _, condition := range source.Status.Conditions {
|
||||
if condition.Type == sourcev1.ReadyCondition {
|
||||
if condition.Status != corev1.ConditionFalse {
|
||||
logSuccess("%s last fetched revision %s", source.GetName(), source.Status.Artifact.Revision)
|
||||
logSuccess("%s last fetched revision: %s", source.GetName(), source.Status.Artifact.Revision)
|
||||
} else {
|
||||
logFailure("%s %s", source.GetName(), condition.Message)
|
||||
}
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
Copyright 2020 The Flux CD contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
Copyright 2020 The Flux CD contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
Copyright 2020 The Flux CD contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
@@ -115,6 +131,10 @@ func kubeconfigFlag() {
|
||||
rootCmd.PersistentFlags().StringVarP(&kubeconfig, "kubeconfig", "", "",
|
||||
"absolute path to the kubeconfig file")
|
||||
}
|
||||
|
||||
if len(os.Getenv("KUBECONFIG")) > 0 {
|
||||
kubeconfig = os.Getenv("KUBECONFIG")
|
||||
}
|
||||
}
|
||||
|
||||
func generateDocs() {
|
||||
@@ -122,6 +142,7 @@ func generateDocs() {
|
||||
if len(args) > 0 && args[0] == "docgen" {
|
||||
rootCmd.PersistentFlags().StringVarP(&kubeconfig, "kubeconfig", "", "~/.kube/config",
|
||||
"path to the kubeconfig file")
|
||||
rootCmd.DisableAutoGenTag = true
|
||||
err := doc.GenMarkdownTree(rootCmd, "./docs/cmd")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
Copyright 2020 The Flux CD contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
Copyright 2020 The Flux CD contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
Copyright 2020 The Flux CD contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
Copyright 2020 The Flux CD contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
Copyright 2020 The Flux CD contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
Copyright 2020 The Flux CD contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
Copyright 2020 The Flux CD contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
Copyright 2020 The Flux CD contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
@@ -77,7 +93,7 @@ func syncSourceGitCmdRun(cmd *cobra.Command, args []string) error {
|
||||
}
|
||||
|
||||
if gitRepository.Status.Artifact != nil {
|
||||
logSuccess("fetched revision %s", gitRepository.Status.Artifact.Revision)
|
||||
logSuccess("fetched revision: %s", gitRepository.Status.Artifact.Revision)
|
||||
} else {
|
||||
return fmt.Errorf("git sync failed, artifact not found")
|
||||
}
|
||||
|
||||
@@ -1,8 +1,25 @@
|
||||
/*
|
||||
Copyright 2020 The Flux CD contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/manifoldco/promptui"
|
||||
"github.com/spf13/cobra"
|
||||
@@ -24,12 +41,15 @@ cluster role bindings and CRDs.`,
|
||||
}
|
||||
|
||||
var (
|
||||
uninstallCRDs bool
|
||||
uninstallDryRun bool
|
||||
uninstallSilent bool
|
||||
uninstallCRDs bool
|
||||
uninstallKustomizations bool
|
||||
uninstallDryRun bool
|
||||
uninstallSilent bool
|
||||
)
|
||||
|
||||
func init() {
|
||||
uninstallCmd.Flags().BoolVarP(&uninstallKustomizations, "kustomizations", "", false,
|
||||
"removes all kustomizations previously installed")
|
||||
uninstallCmd.Flags().BoolVarP(&uninstallCRDs, "crds", "", false,
|
||||
"removes all CRDs previously installed")
|
||||
uninstallCmd.Flags().BoolVarP(&uninstallDryRun, "dry-run", "", false,
|
||||
@@ -57,6 +77,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"
|
||||
if uninstallCRDs {
|
||||
kinds += ",crds"
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
Copyright 2020 The Flux CD contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
@@ -95,7 +111,7 @@ func (*Utils) execTemplate(obj interface{}, tmpl, filename string) error {
|
||||
func (*Utils) kubeClient(config string) (client.Client, error) {
|
||||
cfg, err := clientcmd.BuildConfigFromFlags("", config)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("kubernetes client initialization failed: %w", err)
|
||||
return nil, fmt.Errorf("Kubernetes client initialization failed: %w", err)
|
||||
}
|
||||
|
||||
scheme := runtime.NewScheme()
|
||||
@@ -107,7 +123,7 @@ func (*Utils) kubeClient(config string) (client.Client, error) {
|
||||
Scheme: scheme,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("kubernetes client initialization failed: %w", err)
|
||||
return nil, fmt.Errorf("Kubernetes client initialization failed: %w", err)
|
||||
}
|
||||
|
||||
return kubeClient, nil
|
||||
|
||||
22
docs/_files/flux-icon.svg
Normal file
22
docs/_files/flux-icon.svg
Normal file
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="64px" height="64px" viewBox="0 0 64 64" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch 56.3 (81716) - https://sketch.com -->
|
||||
<title>flux-icon</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<g id="flux-icon" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="Group" transform="translate(11.000000, 2.000000)">
|
||||
<path d="M0.803134615,15.7791346 C-0.246288462,15.0966346 -0.246288462,13.5602885 0.803134615,12.8783654 L20.1819808,0.279519231 C20.7554423,-0.0931730769 21.4944808,-0.0931730769 22.0679423,0.279519231 L41.4473654,12.8783654 C42.4967885,13.5602885 42.4967885,15.0966346 41.4473654,15.7791346 L22.0679423,28.3779808 C21.4944808,28.7506731 20.7554423,28.7506731 20.1819808,28.3779808 L0.803134615,15.7791346 Z" id="Fill-1" fill="#326CE5"></path>
|
||||
<path d="M24.1851346,18.0023077 L25.5293654,18.0023077 C26.3145577,18.0023077 26.8055192,17.1525 26.4126346,16.4728846 L22.0084038,8.84423077 C21.6160962,8.16461538 20.63475,8.16461538 20.2418654,8.84423077 L15.8376346,16.4728846 C15.4453269,17.1525 15.9357115,18.0023077 16.7209038,18.0023077 L18.0657115,18.0023077 C18.6287885,18.0023077 19.0851346,18.4592308 19.0851346,19.0223077 L19.0851346,27.7298077 L19.9874423,28.3165385 C20.6791731,28.7665385 21.5710962,28.7665385 22.2628269,28.3165385 L23.1651346,27.7298077 L23.1651346,19.0223077 C23.1651346,18.4592308 23.6214808,18.0023077 24.1851346,18.0023077" id="Fill-3" fill="#C1D2F7"></path>
|
||||
<path d="M27.8390769,34.8375577 L23.1648462,31.7989038 L23.1648462,33.2389038 C24.6902308,33.8919808 26.2588846,34.4008269 27.8390769,34.8375577" id="Fill-5" fill="#326CE5"></path>
|
||||
<path d="M23.1650769,35.8280192 L23.1650769,37.8495577 C24.7095,38.3209038 26.2723846,38.7080192 27.8191154,39.0893654 C32.8706538,40.3349423 37.6418077,41.5107115 41.4783462,45.3478269 C41.6733462,45.54225 41.8562308,45.7407115 42.0373846,45.93975 C42.4308462,45.1880192 42.2335385,44.1957115 41.4466154,43.6845577 L33.8560385,38.7489808 C32.0133462,38.1409038 30.1360385,37.6759038 28.2806538,37.2189808 C26.5308462,36.7874423 24.8196923,36.3570577 23.1650769,35.8280192" id="Fill-7" fill="#326CE5"></path>
|
||||
<path d="M19.08525,34.1699423 C18.4304423,33.8318654 17.7854423,33.4689808 17.1629423,33.0489808 L15.4269808,34.1774423 C16.5975577,35.0382115 17.8235192,35.7362885 19.08525,36.3212885 L19.08525,34.1699423 Z" id="Fill-9" fill="#326CE5"></path>
|
||||
<path d="M24.8941731,40.6051154 C24.3137885,40.4620385 23.7374423,40.3195385 23.1651346,40.1735769 L23.1651346,42.1605 C23.5885962,42.2666538 24.0114808,42.3722308 24.4326346,42.4760769 C29.4841731,43.7210769 34.2553269,44.8968462 38.0924423,48.7339615 C38.0987885,48.7408846 38.1045577,48.7472308 38.1114808,48.7541538 L39.75225,47.6868462 C39.6524423,47.5824231 39.5584038,47.4751154 39.4545577,47.3718462 C35.2384038,43.1551154 29.9791731,41.8587692 24.8941731,40.6051154" id="Fill-11" fill="#326CE5"></path>
|
||||
<path d="M19.08525,38.9907115 C16.8900577,38.2389808 14.8096731,37.2714808 12.9115962,35.8124423 L11.2119808,36.9178269 C13.6287115,38.9110962 16.3194808,40.1203269 19.08525,41.0168654 L19.08525,38.9907115 Z" id="Fill-13" fill="#326CE5"></path>
|
||||
<path d="M19.08525,43.3809808 C15.3069808,42.3909808 11.7537115,41.18175 8.71794231,38.5388654 L7.04717308,39.6252115 C10.6125577,42.9102115 14.8540962,44.2832885 19.08525,45.3707885 L19.08525,43.3809808 Z" id="Fill-15" fill="#326CE5"></path>
|
||||
<path d="M23.1650769,46.3935 C27.1175769,47.4140769 30.8341154,48.6342692 33.9823846,51.4381154 L35.6439231,50.3581154 C31.9654615,46.9000385 27.5514231,45.5194615 23.1650769,44.4048462 L23.1650769,46.3935 Z" id="Fill-17" fill="#326CE5"></path>
|
||||
<path d="M4.57875,41.2299231 L2.92990385,42.3018462 C2.98759615,42.3612692 3.04009615,42.423 3.09951923,42.4818462 C7.31625,46.6985769 12.5743269,47.9949231 17.6599038,49.2485769 C22.0641346,50.3337692 26.2543269,51.3687692 29.7989423,54.1581923 L31.4893269,53.0591538 C27.4958654,49.6968462 22.7385577,48.5158846 18.1214423,47.3781923 C13.1206731,46.1453077 8.39567308,44.9758846 4.57875,41.2299231" id="Fill-19" fill="#326CE5"></path>
|
||||
<path d="M1.07555769,44.5060962 C0.883442308,44.3139808 0.702865385,44.1184038 0.524019231,43.9216731 C-0.227711538,44.6745577 -0.139442308,45.9726346 0.80325,46.5853269 L6.50959615,50.2955192 C9.03536538,51.3409038 11.6765192,51.9945577 14.2738269,52.6349423 C18.3284423,53.6341731 22.2019038,54.5924423 25.5578654,56.9157115 L27.2834423,55.7930192 C23.4676731,52.9245577 19.0403654,51.8255192 14.7347885,50.7639808 C9.68382692,49.5189808 4.91267308,48.3432115 1.07555769,44.5060962" id="Fill-21" fill="#326CE5"></path>
|
||||
<path d="M19.6441154,58.8342692 C20.0243077,59.0188846 20.3998846,59.2133077 20.7691154,59.4221538 C21.2093077,59.5150385 21.6771923,59.4383077 22.0683462,59.1838846 L23.0260385,58.5613846 C19.9493077,56.5035 16.5287308,55.461 13.1196923,54.5927308 L19.6441154,58.8342692 Z" id="Fill-23" fill="#326CE5"></path>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.1 KiB |
BIN
docs/_files/flux-icon@2x.png
Normal file
BIN
docs/_files/flux-icon@2x.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.6 KiB |
BIN
docs/_files/kustomize-controller.png
Normal file
BIN
docs/_files/kustomize-controller.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 52 KiB |
BIN
docs/_files/source-controller.png
Normal file
BIN
docs/_files/source-controller.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 33 KiB |
24
docs/_static/custom.css
vendored
Normal file
24
docs/_static/custom.css
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
@import url("https://fonts.googleapis.com/css?family=Montserrat&display=swap");
|
||||
|
||||
body {
|
||||
font-family: "Montserrat", sans-serif;
|
||||
}
|
||||
|
||||
.md-logo {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
padding-bottom: 2px;
|
||||
padding-top: 2px;
|
||||
}
|
||||
.md-logo img {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.md-header, .md-footer-nav {
|
||||
background-image: linear-gradient(45deg, rgb(0, 150, 225) 0%, rgb(27, 141, 226) 24%, rgb(42, 125, 227) 53%, rgb(53, 112, 227) 78%, rgb(53, 112, 227) 100%);
|
||||
}
|
||||
|
||||
.md-header-nav__title {
|
||||
font-size: .85rem;
|
||||
}
|
||||
@@ -90,4 +90,3 @@ Command line utility for assembling Kubernetes CD pipelines the GitOps way.
|
||||
* [tk sync](tk_sync.md) - Synchronize commands
|
||||
* [tk uninstall](tk_uninstall.md) - Uninstall the toolkit components
|
||||
|
||||
###### Auto generated by spf13/cobra on 9-Jun-2020
|
||||
|
||||
@@ -27,5 +27,5 @@ Bootstrap commands
|
||||
|
||||
* [tk](tk.md) - Command line utility for assembling Kubernetes CD pipelines
|
||||
* [tk bootstrap github](tk_bootstrap_github.md) - Bootstrap GitHub repository
|
||||
* [tk bootstrap gitlab](tk_bootstrap_gitlab.md) - Bootstrap GitLab repository
|
||||
|
||||
###### Auto generated by spf13/cobra on 9-Jun-2020
|
||||
|
||||
@@ -24,6 +24,12 @@ tk bootstrap github [flags]
|
||||
# Run bootstrap for a private repo owned by a GitHub organization
|
||||
bootstrap github --owner=<organization> --repository=<repo name>
|
||||
|
||||
# Run bootstrap for a private repo and assign organization teams to it
|
||||
bootstrap github --owner=<organization> --repository=<repo name> --team=<team1 slug> --team=<team2 slug>
|
||||
|
||||
# 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
|
||||
bootstrap github --owner=<user> --repository=<repo name> --private=false --personal=true
|
||||
|
||||
@@ -39,9 +45,11 @@ tk bootstrap github [flags]
|
||||
--hostname string GitHub hostname (default "github.com")
|
||||
--interval duration sync interval (default 1m0s)
|
||||
--owner string GitHub user or organization name
|
||||
--path string repository path, when specified the cluster sync will be scoped to this path
|
||||
--personal is personal repository
|
||||
--private is private repository (default true)
|
||||
--repository string GitHub repository name
|
||||
--team stringArray GitHub team to be given maintainer access
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
@@ -59,4 +67,3 @@ tk bootstrap github [flags]
|
||||
|
||||
* [tk bootstrap](tk_bootstrap.md) - Bootstrap commands
|
||||
|
||||
###### Auto generated by spf13/cobra on 9-Jun-2020
|
||||
|
||||
65
docs/cmd/tk_bootstrap_gitlab.md
Normal file
65
docs/cmd/tk_bootstrap_gitlab.md
Normal file
@@ -0,0 +1,65 @@
|
||||
## tk bootstrap gitlab
|
||||
|
||||
Bootstrap GitLab repository
|
||||
|
||||
### Synopsis
|
||||
|
||||
|
||||
The bootstrap command creates the GitLab repository if it doesn't exists and
|
||||
commits the toolkit components manifests to the master branch.
|
||||
Then it configure the target cluster to synchronize with the repository.
|
||||
If the toolkit components are present on the cluster,
|
||||
the bootstrap command will perform an upgrade if needed.
|
||||
|
||||
```
|
||||
tk bootstrap gitlab [flags]
|
||||
```
|
||||
|
||||
### Examples
|
||||
|
||||
```
|
||||
# Create a GitLab API token and export it as an env var
|
||||
export GITLAB_TOKEN=<my-token>
|
||||
|
||||
# Run bootstrap for a private repo owned by a GitLab group
|
||||
bootstrap gitlab --owner=<group> --repository=<repo name>
|
||||
|
||||
# Run bootstrap for a repository path
|
||||
bootstrap gitlab --owner=<group> --repository=<repo name> --path=dev-cluster
|
||||
|
||||
# Run bootstrap for a public repository on a personal account
|
||||
bootstrap gitlab --owner=<user> --repository=<repo name> --private=false --personal=true
|
||||
|
||||
# Run bootstrap for a private repo hosted on a GitLab server
|
||||
bootstrap gitlab --owner=<group> --repository=<repo name> --hostname=<domain>
|
||||
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
-h, --help help for gitlab
|
||||
--hostname string GitLab hostname (default "gitlab.com")
|
||||
--interval duration sync interval (default 1m0s)
|
||||
--owner string GitLab user or group name
|
||||
--path string repository path, when specified the cluster sync will be scoped to this path
|
||||
--personal is personal repository
|
||||
--private is private repository (default true)
|
||||
--repository string GitLab repository name
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller])
|
||||
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
||||
--namespace string the namespace scope for this operation (default "gitops-system")
|
||||
--timeout duration timeout for this operation (default 5m0s)
|
||||
--verbose print generated objects
|
||||
--version string toolkit tag or branch (default "master")
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [tk bootstrap](tk_bootstrap.md) - Bootstrap commands
|
||||
|
||||
@@ -44,4 +44,3 @@ tk check [flags]
|
||||
|
||||
* [tk](tk.md) - Command line utility for assembling Kubernetes CD pipelines
|
||||
|
||||
###### Auto generated by spf13/cobra on 9-Jun-2020
|
||||
|
||||
@@ -44,4 +44,3 @@ To configure your bash shell to load completions for each session add to your ba
|
||||
|
||||
* [tk](tk.md) - Command line utility for assembling Kubernetes CD pipelines
|
||||
|
||||
###### Auto generated by spf13/cobra on 9-Jun-2020
|
||||
|
||||
@@ -30,4 +30,3 @@ Create commands
|
||||
* [tk create kustomization](tk_create_kustomization.md) - Create or update a kustomization resource
|
||||
* [tk create source](tk_create_source.md) - Create source commands
|
||||
|
||||
###### Auto generated by spf13/cobra on 9-Jun-2020
|
||||
|
||||
@@ -78,4 +78,3 @@ tk create kustomization [name] [flags]
|
||||
|
||||
* [tk create](tk_create.md) - Create commands
|
||||
|
||||
###### Auto generated by spf13/cobra on 9-Jun-2020
|
||||
|
||||
@@ -29,4 +29,3 @@ Create source commands
|
||||
* [tk create](tk_create.md) - Create commands
|
||||
* [tk create source git](tk_create_source_git.md) - Create or update a git source
|
||||
|
||||
###### Auto generated by spf13/cobra on 9-Jun-2020
|
||||
|
||||
@@ -58,7 +58,7 @@ tk create source git [name] [flags]
|
||||
--branch string git branch (default "master")
|
||||
-h, --help help for git
|
||||
-p, --password string basic authentication password
|
||||
--ssh-ecdsa-curve ecdsaCurve SSH ECDSA public key curve (p521, p256, p384) (default p384)
|
||||
--ssh-ecdsa-curve ecdsaCurve SSH ECDSA public key curve (p256, p384, p521) (default p384)
|
||||
--ssh-key-algorithm publicKeyAlgorithm SSH public key algorithm (rsa, ecdsa, ed25519) (default rsa)
|
||||
--ssh-rsa-bits rsaKeyBits SSH RSA public key bit size (multiplies of 8) (default 2048)
|
||||
--tag string git tag
|
||||
@@ -83,4 +83,3 @@ tk create source git [name] [flags]
|
||||
|
||||
* [tk create source](tk_create_source.md) - Create source commands
|
||||
|
||||
###### Auto generated by spf13/cobra on 9-Jun-2020
|
||||
|
||||
@@ -29,4 +29,3 @@ Delete commands
|
||||
* [tk delete kustomization](tk_delete_kustomization.md) - Delete kustomization
|
||||
* [tk delete source](tk_delete_source.md) - Delete sources commands
|
||||
|
||||
###### Auto generated by spf13/cobra on 9-Jun-2020
|
||||
|
||||
@@ -31,4 +31,3 @@ tk delete kustomization [name] [flags]
|
||||
|
||||
* [tk delete](tk_delete.md) - Delete commands
|
||||
|
||||
###### Auto generated by spf13/cobra on 9-Jun-2020
|
||||
|
||||
@@ -28,4 +28,3 @@ Delete sources commands
|
||||
* [tk delete](tk_delete.md) - Delete commands
|
||||
* [tk delete source git](tk_delete_source_git.md) - Delete git source
|
||||
|
||||
###### Auto generated by spf13/cobra on 9-Jun-2020
|
||||
|
||||
@@ -31,4 +31,3 @@ tk delete source git [name] [flags]
|
||||
|
||||
* [tk delete source](tk_delete_source.md) - Delete sources commands
|
||||
|
||||
###### Auto generated by spf13/cobra on 9-Jun-2020
|
||||
|
||||
@@ -29,4 +29,3 @@ Export commands
|
||||
* [tk export kustomization](tk_export_kustomization.md) - Export kustomization in YAML format
|
||||
* [tk export source](tk_export_source.md) - Export source commands
|
||||
|
||||
###### Auto generated by spf13/cobra on 9-Jun-2020
|
||||
|
||||
@@ -42,4 +42,3 @@ tk export kustomization [name] [flags]
|
||||
|
||||
* [tk export](tk_export.md) - Export commands
|
||||
|
||||
###### Auto generated by spf13/cobra on 9-Jun-2020
|
||||
|
||||
@@ -29,4 +29,3 @@ Export source commands
|
||||
* [tk export](tk_export.md) - Export commands
|
||||
* [tk export source git](tk_export_source_git.md) - Export git sources in YAML format
|
||||
|
||||
###### Auto generated by spf13/cobra on 9-Jun-2020
|
||||
|
||||
@@ -43,4 +43,3 @@ tk export source git [name] [flags]
|
||||
|
||||
* [tk export source](tk_export_source.md) - Export source commands
|
||||
|
||||
###### Auto generated by spf13/cobra on 9-Jun-2020
|
||||
|
||||
@@ -28,4 +28,3 @@ Get commands
|
||||
* [tk get kustomizations](tk_get_kustomizations.md) - Get kustomizations status
|
||||
* [tk get sources](tk_get_sources.md) - Get sources commands
|
||||
|
||||
###### Auto generated by spf13/cobra on 9-Jun-2020
|
||||
|
||||
@@ -31,4 +31,3 @@ tk get kustomizations [flags]
|
||||
|
||||
* [tk get](tk_get.md) - Get commands
|
||||
|
||||
###### Auto generated by spf13/cobra on 9-Jun-2020
|
||||
|
||||
@@ -27,4 +27,3 @@ Get sources commands
|
||||
* [tk get](tk_get.md) - Get commands
|
||||
* [tk get sources git](tk_get_sources_git.md) - Get git sources status
|
||||
|
||||
###### Auto generated by spf13/cobra on 9-Jun-2020
|
||||
|
||||
@@ -31,4 +31,3 @@ tk get sources git [flags]
|
||||
|
||||
* [tk get sources](tk_get_sources.md) - Get sources commands
|
||||
|
||||
###### Auto generated by spf13/cobra on 9-Jun-2020
|
||||
|
||||
@@ -49,4 +49,3 @@ tk install [flags]
|
||||
|
||||
* [tk](tk.md) - Command line utility for assembling Kubernetes CD pipelines
|
||||
|
||||
###### Auto generated by spf13/cobra on 9-Jun-2020
|
||||
|
||||
@@ -27,4 +27,3 @@ Resume commands
|
||||
* [tk](tk.md) - Command line utility for assembling Kubernetes CD pipelines
|
||||
* [tk resume kustomization](tk_resume_kustomization.md) - Resume kustomization
|
||||
|
||||
###### Auto generated by spf13/cobra on 9-Jun-2020
|
||||
|
||||
@@ -30,4 +30,3 @@ tk resume kustomization [name] [flags]
|
||||
|
||||
* [tk resume](tk_resume.md) - Resume commands
|
||||
|
||||
###### Auto generated by spf13/cobra on 9-Jun-2020
|
||||
|
||||
@@ -27,4 +27,3 @@ Suspend commands
|
||||
* [tk](tk.md) - Command line utility for assembling Kubernetes CD pipelines
|
||||
* [tk suspend kustomization](tk_suspend_kustomization.md) - Suspend kustomization
|
||||
|
||||
###### Auto generated by spf13/cobra on 9-Jun-2020
|
||||
|
||||
@@ -30,4 +30,3 @@ tk suspend kustomization [name] [flags]
|
||||
|
||||
* [tk suspend](tk_suspend.md) - Suspend commands
|
||||
|
||||
###### Auto generated by spf13/cobra on 9-Jun-2020
|
||||
|
||||
@@ -28,4 +28,3 @@ Synchronize commands
|
||||
* [tk sync kustomization](tk_sync_kustomization.md) - Synchronize kustomization
|
||||
* [tk sync source](tk_sync_source.md) - Synchronize source commands
|
||||
|
||||
###### Auto generated by spf13/cobra on 9-Jun-2020
|
||||
|
||||
@@ -43,4 +43,3 @@ tk sync kustomization [name] [flags]
|
||||
|
||||
* [tk sync](tk_sync.md) - Synchronize commands
|
||||
|
||||
###### Auto generated by spf13/cobra on 9-Jun-2020
|
||||
|
||||
@@ -27,4 +27,3 @@ Synchronize source commands
|
||||
* [tk sync](tk_sync.md) - Synchronize commands
|
||||
* [tk sync source git](tk_sync_source_git.md) - Synchronize git source
|
||||
|
||||
###### Auto generated by spf13/cobra on 9-Jun-2020
|
||||
|
||||
@@ -39,4 +39,3 @@ tk sync source git [name] [flags]
|
||||
|
||||
* [tk sync source](tk_sync_source.md) - Synchronize source commands
|
||||
|
||||
###### Auto generated by spf13/cobra on 9-Jun-2020
|
||||
|
||||
@@ -26,10 +26,11 @@ tk uninstall [flags]
|
||||
### Options
|
||||
|
||||
```
|
||||
--crds removes all CRDs previously installed
|
||||
--dry-run only print the object that would be deleted
|
||||
-h, --help help for uninstall
|
||||
-s, --silent delete components without asking for confirmation
|
||||
--crds removes all CRDs previously installed
|
||||
--dry-run only print the object that would be deleted
|
||||
-h, --help help for uninstall
|
||||
--kustomizations removes all kustomizations previously installed
|
||||
-s, --silent delete components without asking for confirmation
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
@@ -46,4 +47,3 @@ tk uninstall [flags]
|
||||
|
||||
* [tk](tk.md) - Command line utility for assembling Kubernetes CD pipelines
|
||||
|
||||
###### Auto generated by spf13/cobra on 9-Jun-2020
|
||||
|
||||
23
docs/components/kustomize/controller.md
Normal file
23
docs/components/kustomize/controller.md
Normal file
@@ -0,0 +1,23 @@
|
||||
# Kustomize Controller
|
||||
|
||||
The kustomize-controller is a Kubernetes operator,
|
||||
specialized in running continuous delivery pipelines for infrastructure and
|
||||
workloads defined with Kubernetes manifests and assembled with Kustomize.
|
||||
|
||||

|
||||
|
||||
Features:
|
||||
|
||||
- Reconciles the cluster state from multiple sources (provided by source-controller)
|
||||
- Generates manifests with Kustomize (from plain Kubernetes yamls or Kustomize overlays)
|
||||
- Validates manifests against Kubernetes API
|
||||
- Impersonates service accounts (multi-tenancy RBAC)
|
||||
- Health assessment of the deployed workloads
|
||||
- Runs pipelines in a specific order (depends-on relationship)
|
||||
- Prunes objects removed from source (garbage collection)
|
||||
- Reports cluster state changes (Slack/Discord)
|
||||
|
||||
Links:
|
||||
|
||||
- Source code [fluxcd/kustomize-controller](https://github.com/fluxcd/kustomize-controller)
|
||||
- Specification [docs](https://github.com/fluxcd/kustomize-controller/tree/master/docs/spec)
|
||||
24
docs/components/source/controller.md
Normal file
24
docs/components/source/controller.md
Normal file
@@ -0,0 +1,24 @@
|
||||
# Source Controller
|
||||
|
||||
The main role of the source management component is to provide a common interface for artifacts acquisition.
|
||||
The source API defines a set of Kubernetes objects that cluster admins and various automated operators can
|
||||
interact with to offload the Git and Helm repositories operations to a dedicated controller.
|
||||
|
||||

|
||||
|
||||
Features:
|
||||
|
||||
- Validate source definitions
|
||||
- Authenticate to sources (SSH, user/password, API token)
|
||||
- Validate source authenticity (PGP)
|
||||
- Detect source changes based on update policies (semver)
|
||||
- Fetch resources on-demand and on-a-schedule
|
||||
- Package the fetched resources into a well-known format (tar.gz, yaml)
|
||||
- Make the artifacts addressable by their source identifier (sha, version, ts)
|
||||
- Make the artifacts available in-cluster to interested 3rd parties
|
||||
- Notify interested 3rd parties of source changes and availability (status conditions, events, hooks)
|
||||
|
||||
Links:
|
||||
|
||||
- Source code [fluxcd/source-controller](https://github.com/fluxcd/source-controller)
|
||||
- Specification [docs](https://github.com/fluxcd/source-controller/tree/master/docs/spec)
|
||||
BIN
docs/diagrams/tk-feature.png
Normal file
BIN
docs/diagrams/tk-feature.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 61 KiB |
366
docs/get-started/index.md
Normal file
366
docs/get-started/index.md
Normal file
@@ -0,0 +1,366 @@
|
||||
# Get started with GitOps Toolkit
|
||||
|
||||
## Prerequisites
|
||||
|
||||
You will need two Kubernetes clusters version 1.14 or newer and kubectl version 1.18.
|
||||
For a quick local test, you can use [Kubernetes kind](https://kind.sigs.k8s.io/docs/user/quick-start/).
|
||||
Any other Kubernetes setup will work as well though.
|
||||
|
||||
In order to follow the guide you'll need a GitHub account and a
|
||||
[personal access token](https://help.github.com/en/github/authenticating-to-github/creating-a-personal-access-token-for-the-command-line)
|
||||
that can create repositories (check all permissions under `repo`).
|
||||
|
||||
Export your GitHub personal access token and username:
|
||||
|
||||
```sh
|
||||
export GITHUB_TOKEN=<your-token>
|
||||
export GITHUB_USER=<your-username>
|
||||
```
|
||||
|
||||
## Install the toolkit CLI
|
||||
|
||||
To install the latest `tk` release run:
|
||||
|
||||
```bash
|
||||
curl -s https://toolkit.fluxcd.io/install.sh | sudo bash
|
||||
```
|
||||
|
||||
The install script downloads the tk binary to `/usr/local/bin`.
|
||||
Binaries for macOS and Linux AMD64 are available for download on the
|
||||
[release page](https://github.com/fluxcd/toolkit/releases).
|
||||
|
||||
To configure your shell to load tk completions add to your bash profile:
|
||||
|
||||
```sh
|
||||
# ~/.bashrc or ~/.bash_profile
|
||||
. <(tk completion)
|
||||
```
|
||||
|
||||
## GitOps workflow
|
||||
|
||||
You'll be using a dedicated Git repository e.g. `fleet-infra` to manage one or more Kubernetes clusters.
|
||||
This guide assumes that you have two clusters, one for staging and one for production.
|
||||
|
||||
Using the toolkit CLI you'll do the following:
|
||||
|
||||
- configure each cluster to synchronise with a directory inside the fleet repository
|
||||
- register app sources (git repositories) that contain plain Kubernetes manifests or Kustomize overlays
|
||||
- configure app deployments on both clusters (pre-releases on staging, semver releases on production)
|
||||
|
||||
## Staging bootstrap
|
||||
|
||||
Create the staging cluster using Kubernetes kind or set the kubectl context to an existing cluster:
|
||||
|
||||
```sh
|
||||
kind create cluster --name staging
|
||||
kubectl cluster-info --context kind-staging
|
||||
```
|
||||
|
||||
Verify that your staging cluster satisfies the prerequisites with:
|
||||
|
||||
```text
|
||||
$ tk check --pre
|
||||
|
||||
► checking prerequisites
|
||||
✔ kubectl 1.18.3 >=1.18.0
|
||||
✔ kubernetes 1.18.2 >=1.14.0
|
||||
✔ prerequisites checks passed
|
||||
```
|
||||
|
||||
Run the bootstrap command:
|
||||
|
||||
```sh
|
||||
tk bootstrap github \
|
||||
--owner=$GITHUB_USER \
|
||||
--repository=fleet-infra \
|
||||
--path=staging-cluster \
|
||||
--personal
|
||||
```
|
||||
|
||||
The bootstrap command creates a repository if one doesn't exist and
|
||||
commits the toolkit components manifests to the master branch at the specified path.
|
||||
Then it configures the target cluster to synchronize with the specified path inside the repository.
|
||||
|
||||
If you wish to create the repository under a GitHub organization:
|
||||
|
||||
```sh
|
||||
tk bootstrap github \
|
||||
--owner=<organization> \
|
||||
--repository=<repo-name> \
|
||||
--team=<team1-slug> \
|
||||
--team=<team2-slug> \
|
||||
--path=staging-cluster
|
||||
```
|
||||
|
||||
Example output:
|
||||
|
||||
```text
|
||||
$ tk bootstrap github --owner=gitopsrun --repository=fleet-infra --path=staging-cluster --team=devs
|
||||
|
||||
► connecting to github.com
|
||||
✔ repository created
|
||||
✔ devs team access granted
|
||||
✔ repository cloned
|
||||
✚ generating manifests
|
||||
✔ components manifests pushed
|
||||
► installing components in gitops-system namespace
|
||||
namespace/gitops-system created
|
||||
customresourcedefinition.apiextensions.k8s.io/gitrepositories.source.fluxcd.io created
|
||||
customresourcedefinition.apiextensions.k8s.io/helmcharts.source.fluxcd.io created
|
||||
customresourcedefinition.apiextensions.k8s.io/helmrepositories.source.fluxcd.io created
|
||||
customresourcedefinition.apiextensions.k8s.io/kustomizations.kustomize.fluxcd.io created
|
||||
customresourcedefinition.apiextensions.k8s.io/profiles.kustomize.fluxcd.io created
|
||||
role.rbac.authorization.k8s.io/crd-controller-gitops-system created
|
||||
rolebinding.rbac.authorization.k8s.io/crd-controller-gitops-system created
|
||||
clusterrolebinding.rbac.authorization.k8s.io/cluster-reconciler-gitops-system created
|
||||
service/source-controller created
|
||||
deployment.apps/kustomize-controller created
|
||||
deployment.apps/source-controller created
|
||||
networkpolicy.networking.k8s.io/deny-ingress created
|
||||
Waiting for deployment "source-controller" rollout to finish: 0 of 1 updated replicas are available...
|
||||
deployment "source-controller" successfully rolled out
|
||||
deployment "kustomize-controller" successfully rolled out
|
||||
✔ install completed
|
||||
► configuring deploy key
|
||||
✔ deploy key configured
|
||||
► generating sync manifests
|
||||
✔ sync manifests pushed
|
||||
► applying sync manifests
|
||||
◎ waiting for cluster sync
|
||||
✔ bootstrap finished
|
||||
```
|
||||
|
||||
If you prefer GitLab, export `GITLAB_TOKEN` env var and use the command [tk bootstrap gitlab](../cmd/tk_bootstrap_gitlab.md).
|
||||
|
||||
!!! hint "Idempotency"
|
||||
It is safe to run the bootstrap command as many times as you want.
|
||||
If the toolkit components are present on the cluster,
|
||||
the bootstrap command will perform an upgrade if needed.
|
||||
You can target a specific toolkit [version](https://github.com/fluxcd/toolkit/releases)
|
||||
with `tk bootstrap --version=<semver>`.
|
||||
|
||||
## Staging workflow
|
||||
|
||||
Clone the repository with:
|
||||
|
||||
```sh
|
||||
git clone https://github.com/$GITHUB_USER/fleet-infra
|
||||
cd fleet-infra
|
||||
```
|
||||
|
||||
Create a git source pointing to a public repository master branch:
|
||||
|
||||
```sh
|
||||
tk create source git webapp \
|
||||
--url=https://github.com/stefanprodan/podinfo \
|
||||
--branch=master \
|
||||
--interval=30s \
|
||||
--export > ./staging-cluster/webapp-source.yaml
|
||||
```
|
||||
|
||||
Create a kustomization for synchronizing the common manifests on the cluster:
|
||||
|
||||
```sh
|
||||
tk create kustomization webapp-common \
|
||||
--source=webapp \
|
||||
--path="./deploy/webapp/common" \
|
||||
--prune=true \
|
||||
--validate=client \
|
||||
--interval=1h \
|
||||
--export > ./staging-cluster/webapp-common.yaml
|
||||
```
|
||||
|
||||
Create a kustomization for the backend service that depends on common:
|
||||
|
||||
```sh
|
||||
tk create kustomization webapp-backend \
|
||||
--depends-on=webapp-common \
|
||||
--source=webapp \
|
||||
--path="./deploy/webapp/backend" \
|
||||
--prune=true \
|
||||
--validate=client \
|
||||
--interval=10m \
|
||||
--health-check="Deployment/backend.webapp" \
|
||||
--health-check-timeout=2m \
|
||||
--export > ./staging-cluster/webapp-backend.yaml
|
||||
```
|
||||
|
||||
Create a kustomization for the frontend service that depends on backend:
|
||||
|
||||
```sh
|
||||
tk create kustomization webapp-frontend \
|
||||
--depends-on=webapp-backend \
|
||||
--source=webapp \
|
||||
--path="./deploy/webapp/frontend" \
|
||||
--prune=true \
|
||||
--validate=client \
|
||||
--interval=10m \
|
||||
--health-check="Deployment/frontend.webapp" \
|
||||
--health-check-timeout=2m \
|
||||
--export > ./staging-cluster/webapp-frontend.yaml
|
||||
```
|
||||
|
||||
Push changes to origin:
|
||||
|
||||
```sh
|
||||
git add -A && git commit -m "add staging webapp" && git push
|
||||
```
|
||||
|
||||
In about 30s the synchronization should start:
|
||||
|
||||
```text
|
||||
$ watch tk get kustomizations
|
||||
|
||||
✔ gitops-system last applied revision master/35d5765a1acb9e9ce66cad7274c6fe03eee1e8eb
|
||||
✔ webapp-backend reconciling
|
||||
✔ webapp-common last applied revision master/f43f9b2eb6766e07f318d266a99d2ec7c940b0cf
|
||||
✗ webapp-frontend dependency 'gitops-system/webapp-backend' is not ready
|
||||
```
|
||||
|
||||
When the synchronization finishes you can check that the webapp services are running:
|
||||
|
||||
```text
|
||||
$ kubectl -n webapp get deployments,services
|
||||
|
||||
NAME READY UP-TO-DATE AVAILABLE AGE
|
||||
deployment.apps/backend 1/1 1 1 4m1s
|
||||
deployment.apps/frontend 1/1 1 1 3m31s
|
||||
|
||||
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
|
||||
service/backend ClusterIP 10.52.10.22 <none> 9898/TCP,9999/TCP 4m1s
|
||||
service/frontend ClusterIP 10.52.9.85 <none> 80/TCP 3m31s
|
||||
```
|
||||
|
||||
!!! tip
|
||||
From this moment forward, any changes made to the webapp
|
||||
Kubernetes manifests in the master branch will be synchronised with the staging cluster.
|
||||
|
||||
If a Kubernetes manifest is removed from the webapp repository, the reconciler will remove it from your cluster.
|
||||
If you delete a kustomization from the `fleet-infra` repo, the reconciler will remove all Kubernetes objects that
|
||||
were previously applied from that kustomization.
|
||||
|
||||
If you alter the webapp deployment using `kubectl edit`, the changes will be reverted to match
|
||||
the state described in git. When dealing with an incident, you can pause the recitation of a
|
||||
kustomization with `tk suspend kustomization <name>`. Once the debugging session
|
||||
is over, you can re-enable the reconciliation with `tk resume kustomization <name>`.
|
||||
|
||||
## Production bootstrap
|
||||
|
||||
On production clusters, you may wish to deploy stable releases of an application.
|
||||
When creating a git source instead of a branch, you can specify a git tag or a semver expression.
|
||||
|
||||
Create the production cluster using Kubernetes kind or set the kubectl context to an existing cluster:
|
||||
|
||||
```sh
|
||||
kind create cluster --name production
|
||||
kubectl cluster-info --context kind-production
|
||||
```
|
||||
|
||||
Run the bootstrap for the production environment:
|
||||
|
||||
```sh
|
||||
tk bootstrap github \
|
||||
--owner=$GITHUB_USER \
|
||||
--repository=fleet-infra \
|
||||
--path=prod-cluster \
|
||||
--personal
|
||||
```
|
||||
|
||||
Pull the changes locally:
|
||||
|
||||
```sh
|
||||
git pull
|
||||
```
|
||||
|
||||
## Production workflow
|
||||
|
||||
Create a git source using a semver range to target stable releases:
|
||||
|
||||
```sh
|
||||
tk create source git webapp \
|
||||
--url=https://github.com/stefanprodan/podinfo \
|
||||
--tag-semver=">=4.0.0 <4.0.2" \
|
||||
--interval=30s \
|
||||
--export > ./prod-cluster/webapp-source.yaml
|
||||
```
|
||||
|
||||
Create a kustomization for webapp pointing to the production overlay:
|
||||
|
||||
```sh
|
||||
tk create kustomization webapp \
|
||||
--source=webapp \
|
||||
--path="./deploy/overlays/production" \
|
||||
--prune=true \
|
||||
--validate=client \
|
||||
--interval=10m \
|
||||
--health-check="Deployment/frontend.production" \
|
||||
--health-check="Deployment/backend.production" \
|
||||
--health-check-timeout=2m \
|
||||
--export > ./prod-cluster/webapp-production.yaml
|
||||
```
|
||||
|
||||
Push changes to origin:
|
||||
|
||||
```sh
|
||||
git add -A && git commit -m "add prod webapp" && git push
|
||||
```
|
||||
|
||||
List git sources:
|
||||
|
||||
```text
|
||||
$ tk get sources git
|
||||
|
||||
✔ gitops-system last fetched revision master/99072ee132abdead8b7799d7891eae2f524eb73d
|
||||
✔ webapp last fetched revision 4.0.1/113360052b3153e439a0cf8de76b8e3d2a7bdf27
|
||||
```
|
||||
|
||||
The kubectl equivalent is `kubectl -n gitops-system get gitrepositories`.
|
||||
|
||||
List kustomization:
|
||||
|
||||
```text
|
||||
$ tk get kustomizations
|
||||
|
||||
✔ gitops-system last applied revision master/99072ee132abdead8b7799d7891eae2f524eb73d
|
||||
✔ webapp last applied revision 4.0.1/113360052b3153e439a0cf8de76b8e3d2a7bdf27
|
||||
```
|
||||
|
||||
The kubectl equivalent is `kubectl -n gitops-system get kustomizations`.
|
||||
|
||||
If you want to upgrade to the latest 4.x version, you can change the semver expression to:
|
||||
|
||||
```sh
|
||||
tk create source git webapp \
|
||||
--url=https://github.com/stefanprodan/podinfo \
|
||||
--tag-semver=">=4.0.0 <5.0.0" \
|
||||
--interval=30s \
|
||||
--export > ./prod-cluster/webapp-source.yaml
|
||||
|
||||
git add -A && git commit -m "update prod webapp" && git push
|
||||
```
|
||||
|
||||
Trigger a git sync:
|
||||
|
||||
```text
|
||||
$ tk sync ks gitops-system --with-source
|
||||
|
||||
► annotating source gitops-system
|
||||
✔ source annotated
|
||||
◎ waiting for git sync
|
||||
✔ git sync completed
|
||||
✔ fetched revision master/d751ea264d48bf0db8b588d1d08184834ac8fec9
|
||||
◎ waiting for kustomization sync
|
||||
✔ kustomization sync completed
|
||||
✔ applied revision master/d751ea264d48bf0db8b588d1d08184834ac8fec9
|
||||
```
|
||||
|
||||
The kubectl equivalent is `kubectl -n gitops-system annotate gitrepository/gitops-system source.fluxcd.io/syncAt="$(date +%s)"`.
|
||||
|
||||
Wait for the webapp to be upgraded:
|
||||
|
||||
```text
|
||||
$ watch tk get kustomizations
|
||||
|
||||
✔ gitops-system last applied revision master/d751ea264d48bf0db8b588d1d08184834ac8fec9
|
||||
✔ webapp last applied revision 4.0.5/f43f9b2eb6766e07f318d266a99d2ec7c940b0cf
|
||||
```
|
||||
46
docs/index.md
Normal file
46
docs/index.md
Normal file
@@ -0,0 +1,46 @@
|
||||
# GitOps Toolkit
|
||||
|
||||
The GitOps Toolkit is a set of composable APIs and specialized tools
|
||||
that can be used to build a Continuous Delivery platform on top of Kubernetes.
|
||||
|
||||
These tools are built with Kubernetes controller-runtime libraries and they
|
||||
can be dynamically configured with Kubernetes custom resources either by
|
||||
cluster admins or by other automated tools.
|
||||
The GitOps Toolkit components interact with each other via Kubernetes
|
||||
events and are responsible for the reconciliation of their designated API objects.
|
||||
|
||||
!!! hint "Work in Progress"
|
||||
We envision a future where **Flux v2** and **Helm Operator v2** will be assembled from
|
||||
the GitOps Toolkit components. The Flux CD team is looking for feedback and help as
|
||||
the toolkit is in an active experimentation phase.
|
||||
If you wish to take part in this quest please reach out to us on Slack and GitHub.
|
||||
|
||||

|
||||
|
||||
Target features:
|
||||
|
||||
- Source management
|
||||
- Kustomize and Helm support
|
||||
- Event-based and on-a-schedule reconciliation
|
||||
- Role-based reconciliation (multi-tenancy)
|
||||
- Health assessment (clusters and workloads)
|
||||
- Dependency management (infra and workloads)
|
||||
- Alerting to external systems (webhook senders)
|
||||
- External events handling (webhook receivers)
|
||||
- Source write-back (automated patching)
|
||||
- Policy driven validation (OPA, admission controllers)
|
||||
- Seamless integration with Git providers (GitHub, GitLab, BitBucket)
|
||||
- Interoperability with workflow providers (GitHub Actions, Tekton, Argo)
|
||||
- Interoperability with CAPI providers
|
||||
|
||||
Components:
|
||||
|
||||
- [Toolkit CLI](https://github.com/fluxcd/toolkit)
|
||||
- [Source Controller](components/source/controller.md)
|
||||
- [GitRepository CRD](components/source/gitrepositories.md)
|
||||
- [HelmRepository CRD](components/source/helmrepositories.md)
|
||||
- [Kustomize Controller](components/kustomize/controller.md)
|
||||
- [Kustomization CRD](components/kustomize/kustomization.md)
|
||||
- Helm Controller (TBA)
|
||||
|
||||
To get started with the toolkit please follow this [guide](get-started/index.md).
|
||||
@@ -2,11 +2,6 @@
|
||||
|
||||
To release a new version the following steps should be followed:
|
||||
|
||||
1. Create a new branch from `master` i.e. `release-<next semver>`. This
|
||||
will function as your release preparation branch.
|
||||
1. Change the `VERSION` value in `cmd/tk/main.go` to that of the
|
||||
semver release you are going to make. Commit and push your changes.
|
||||
1. Create a PR for your release branch and get it merged into `master`.
|
||||
1. Create a `<next semver>` tag for the merge commit in `master` and
|
||||
1. Create a `<next semver>` tag form `master` and
|
||||
push it to remote.
|
||||
1. Confirm CI builds and releases the newly tagged version.
|
||||
|
||||
81
docs/roadmap/index.md
Normal file
81
docs/roadmap/index.md
Normal file
@@ -0,0 +1,81 @@
|
||||
# Roadmap
|
||||
|
||||
!!! hint "Work in Progress"
|
||||
We will be building the roadmap together with the Flux community,
|
||||
our end-users and everyone who is interested in integrating with us.
|
||||
So a lot of this is still TBD - read this as our shopping list of
|
||||
ideas after some brainstorming as Flux maintainers.
|
||||
|
||||
## The road to Flux v2
|
||||
|
||||
### Flux read-only feature parity
|
||||
|
||||
This would be the first stepping stone: we want the GitOps Toolkit to be on-par with today's Flux in
|
||||
[read-only mode](https://github.com/fluxcd/flux/blob/master/docs/faq.md#can-i-run-flux-with-readonly-git-access)
|
||||
and [FluxCloud](https://github.com/justinbarrick/fluxcloud) notifications.
|
||||
|
||||
Goals
|
||||
|
||||
- Offer an in-place migration tool for those that are using Flux in read-only mode to synchronize plain manifests
|
||||
- Offer a migration guide for those that are using Flux in read-only mode to synchronize Kustomize overlays
|
||||
- Offer a dedicated component for forwarding events to external messaging platforms
|
||||
|
||||
Non-Goals
|
||||
|
||||
- Migrate users that are using Flux to run custom scripts with `flux.yaml`
|
||||
- Automate the migration of `flux.yaml` kustomize users
|
||||
|
||||
Tasks
|
||||
|
||||
- Review the git source and kustomize APIs
|
||||
- Design the events API
|
||||
- Implement events in source and kustomize controllers
|
||||
- Implement Prometheus metrics in source and kustomize controllers
|
||||
- Make the kustomize-controller apply/gc events on-par with Flux v1 apply events
|
||||
- Design the notifications and events filtering API
|
||||
- Implement a notification controller for Slack, MS Teams, Discord, Rocket
|
||||
- Implement the migration command in tk
|
||||
- Create a migration guide for `flux.yaml` kustomize users
|
||||
|
||||
### Flux image update feature parity
|
||||
|
||||
Goals
|
||||
|
||||
- Offer a dedicated component that can replace Flux v1 image update feature
|
||||
|
||||
Non-Goals
|
||||
|
||||
- Maintain backwards compatibility with Flux v1 annotations
|
||||
|
||||
Tasks
|
||||
|
||||
- Design the Git push API
|
||||
- Implement Git push in source controller
|
||||
- Design the image scanning API
|
||||
- Implement an image scanning controller
|
||||
- Design the manifests patching component
|
||||
- Implement the image scan/patch/push workflow
|
||||
- Integrate the new components in the toolkit assembler
|
||||
- Create a migration guide from Flux annotations
|
||||
|
||||
## The road to Helm Operator v2
|
||||
|
||||
### Helm v3 feature parity
|
||||
|
||||
Goals
|
||||
|
||||
- Offer a migration guide for those that are using Helm Operator with Helm v3 and Helm repositories
|
||||
|
||||
Non-Goals
|
||||
|
||||
- Migrate users that are using Helm v2
|
||||
- Migrate users that are using Helm charts from Git
|
||||
|
||||
Tasks
|
||||
|
||||
- Review the Helm release, chart and repository APIs
|
||||
- Design Helm releases based on source API
|
||||
- Implement a Helm controller for Helm v3 covering all the current release options
|
||||
- Implement events in Helm controller
|
||||
- Implement Prometheus metrics in Helm controller
|
||||
- Create a migration guide for Helm Operator users
|
||||
12
go.mod
12
go.mod
@@ -4,13 +4,21 @@ go 1.14
|
||||
|
||||
require (
|
||||
github.com/blang/semver v3.5.1+incompatible
|
||||
github.com/fluxcd/kustomize-controller v0.0.1-beta.2
|
||||
github.com/fluxcd/source-controller v0.0.1-beta.2
|
||||
github.com/fluxcd/kustomize-controller v0.0.1
|
||||
github.com/fluxcd/source-controller v0.0.1
|
||||
github.com/go-git/go-git/v5 v5.0.0
|
||||
github.com/golang/protobuf v1.4.2 // indirect
|
||||
github.com/google/go-github/v32 v32.0.0
|
||||
github.com/hashicorp/go-retryablehttp v0.6.6 // indirect
|
||||
github.com/manifoldco/promptui v0.7.0
|
||||
github.com/spf13/cobra v1.0.0
|
||||
github.com/xanzy/go-gitlab v0.32.1
|
||||
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073
|
||||
golang.org/x/net v0.0.0-20200602114024-627f9648deb9 // indirect
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d // indirect
|
||||
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 // indirect
|
||||
google.golang.org/appengine v1.6.6 // indirect
|
||||
google.golang.org/protobuf v1.24.0 // indirect
|
||||
k8s.io/api v0.18.2
|
||||
k8s.io/apimachinery v0.18.2
|
||||
k8s.io/client-go v0.18.2
|
||||
|
||||
55
go.sum
55
go.sum
@@ -168,10 +168,10 @@ github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d h1:105gxyaGwC
|
||||
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4=
|
||||
github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/fluxcd/kustomize-controller v0.0.1-beta.2 h1:3EM2OFR9z8FiILDgzqHHqzSx3mO4KT8xG1gKTevD42E=
|
||||
github.com/fluxcd/kustomize-controller v0.0.1-beta.2/go.mod h1:mLeipvpQkyof6b5IHNtqeA8CmbjfVIf92UyKkpeBY98=
|
||||
github.com/fluxcd/source-controller v0.0.1-beta.2 h1:XOtc+tSf/8Q0bTVla2L5FdCMMjJWFSPWX/o4/h4OUv0=
|
||||
github.com/fluxcd/source-controller v0.0.1-beta.2/go.mod h1:tmscNdCxEt7+Xt2g1+bI38hMPw2leYMFAaCn4UlMGuw=
|
||||
github.com/fluxcd/kustomize-controller v0.0.1 h1:F2wg9c5nMUEnPHgs44HMY1/2UAXXaYcmpj7WeOzf9p0=
|
||||
github.com/fluxcd/kustomize-controller v0.0.1/go.mod h1:sSIy+Y924OGHW2anzZvD53BbgjSOO4mONTTG2+UTEhM=
|
||||
github.com/fluxcd/source-controller v0.0.1 h1:17/b/Zcb3OUkUoo03W+L7TGwkCKG23K9HrgL+d5WMXE=
|
||||
github.com/fluxcd/source-controller v0.0.1/go.mod h1:tmscNdCxEt7+Xt2g1+bI38hMPw2leYMFAaCn4UlMGuw=
|
||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ=
|
||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
||||
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
||||
@@ -293,6 +293,14 @@ github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2/go.mod h1:k9Qvh+8juN+UKMCS/3jFtGICgW8O96FVaZsaxdzDkR4=
|
||||
github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a/go.mod h1:ryS0uhF+x9jgbj/N71xsEqODy9BN81/GonCZiOzirOk=
|
||||
github.com/golangci/errcheck v0.0.0-20181223084120-ef45e06d44b6/go.mod h1:DbHgvLiFKX1Sh2T1w8Q/h4NAI8MHIpzCdnBUDTXU3I0=
|
||||
@@ -317,6 +325,7 @@ github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-github/v32 v32.0.0 h1:q74KVb22spUq0U5HqZ9VCYqQz8YRuOtL/39ZnfwO+NM=
|
||||
@@ -360,7 +369,15 @@ github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t
|
||||
github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.0 h1:wvCrVc9TjDls6+YGAF2hAifE1E5U1+b4tH6KdvN3Gig=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxCsHI=
|
||||
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
|
||||
github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I=
|
||||
github.com/hashicorp/go-retryablehttp v0.6.4 h1:BbgctKO892xEyOXnGiaAwIoSq1QZ/SS4AhjoAh9DnfY=
|
||||
github.com/hashicorp/go-retryablehttp v0.6.4/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
|
||||
github.com/hashicorp/go-retryablehttp v0.6.6 h1:HJunrbHTDDbBb/ay4kxa1n+dLmttUlnP3V9oNE4hmsM=
|
||||
github.com/hashicorp/go-retryablehttp v0.6.6/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
|
||||
github.com/hashicorp/go-safetemp v1.0.0 h1:2HR189eFNrjHQyENnQMMpCiBAsRxzbTMIgBhEyExpmo=
|
||||
github.com/hashicorp/go-safetemp v1.0.0/go.mod h1:oaerMy3BhqiTbVye6QuFhFtIceqFoDHxNAB65b+Rj1I=
|
||||
github.com/hashicorp/go-version v1.1.0 h1:bPIoEKD27tNdebFGGxxYwcL4nepeY4j1QP23PFRGzg0=
|
||||
@@ -617,6 +634,8 @@ github.com/valyala/fasthttp v1.2.0/go.mod h1:4vX61m6KN+xDduDNwXrhIAVZaZaZiQ1luJk
|
||||
github.com/valyala/quicktemplate v1.2.0/go.mod h1:EH+4AkTd43SvgIbQHYu59/cJyxDoOVRUAfrukLPuGJ4=
|
||||
github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio=
|
||||
github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw=
|
||||
github.com/xanzy/go-gitlab v0.32.1 h1:eKGfAP2FWbqStD7DtGoRBb18IYwjuCxdtEVea2rNge4=
|
||||
github.com/xanzy/go-gitlab v0.32.1/go.mod h1:sPLojNBn68fMUWSxIJtdVVIP8uSBYqesTfDUseX11Ug=
|
||||
github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70=
|
||||
github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||
@@ -686,6 +705,7 @@ golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73r
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180911220305-26e67e76b6c3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181108082009-03003ca0c849/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@@ -696,6 +716,7 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
|
||||
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190522155817-f3200d17e092 h1:4QSRKanuywn15aTZvI/mIDEgPQpswuFndXpOj3rKEco=
|
||||
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190619014844-b5b0513f8c1b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
@@ -707,11 +728,16 @@ golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLL
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a h1:GuSPYbZzB5/dcLNCwLQLsg3obCJtX9IJhpXkvY7kzk0=
|
||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200602114024-627f9648deb9 h1:pNX+40auqi2JqRfOP1akLGtYcn15TUbkhwuCO3foqqM=
|
||||
golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be h1:vEDujvNQGv4jgYKudGeI/+DAX4Jffq6hpD55MmoEvKs=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@@ -748,6 +774,8 @@ golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7 h1:HmbHVPwrPEKPGLAcHSrMe6+hq
|
||||
golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527 h1:uYVVQ9WP/Ds2ROhcaGPeIdVq0RIXVLwsHlnvJ+cT1So=
|
||||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@@ -758,6 +786,10 @@ golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxb
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 h1:NusfzzA6yGQ+ua51ck7E3omNUX/JuqbFSaRGqU8CcLI=
|
||||
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
@@ -797,9 +829,12 @@ google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff/go.mod h1:4mhQ8q/RsB7i+
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/appengine v1.1.0 h1:igQkv0AAhEIvTEpD5LIpAfav2eeVO9HBTjvKHVJPRSs=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.5.0 h1:KxkO13IPW4Lslp2bz+KHP2E3gtFlrIGNThxkZQ3g+4c=
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc=
|
||||
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/cloud v0.0.0-20151119220103-975617b05ea8/go.mod h1:0H1ncTHf11KCFhTc/+EFRbzSCOZx+VUbRMk55Yv5MYk=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
@@ -808,6 +843,7 @@ google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRn
|
||||
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
@@ -817,6 +853,17 @@ google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyac
|
||||
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=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.24.0 h1:UhZDfRO8JRQru4/+LlLE0BRKGF8L+PICnvYZmx/fEGA=
|
||||
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
||||
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=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
||||
@@ -15,6 +15,12 @@ The install script does the following:
|
||||
* copies the tk binary to `/usr/local/bin`
|
||||
* removes the temporary directory
|
||||
|
||||
If you want to use tk as a kubectl plugin, rename the binary to `kubectl-tk`:
|
||||
|
||||
```sh
|
||||
mv /usr/local/bin/tk /usr/local/bin/kubectl-tk
|
||||
```
|
||||
|
||||
## Build from source
|
||||
|
||||
Clone the repository:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- github.com/fluxcd/kustomize-controller/config//crd?ref=v0.0.1-beta.2
|
||||
- github.com/fluxcd/kustomize-controller/config//manager?ref=v0.0.1-beta.2
|
||||
- github.com/fluxcd/kustomize-controller/config//crd?ref=v0.0.1
|
||||
- github.com/fluxcd/kustomize-controller/config//manager?ref=v0.0.1
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- github.com/fluxcd/source-controller/config//crd?ref=v0.0.1-beta.2
|
||||
- github.com/fluxcd/source-controller/config//manager?ref=v0.0.1-beta.2
|
||||
- github.com/fluxcd/source-controller/config//crd?ref=v0.0.1
|
||||
- github.com/fluxcd/source-controller/config//manager?ref=v0.0.1
|
||||
|
||||
81
mkdocs.yml
Normal file
81
mkdocs.yml
Normal file
@@ -0,0 +1,81 @@
|
||||
site_name: GitOps Toolkit
|
||||
site_description: Documentation for GitOps Toolkit.
|
||||
site_author: The Flux CD contributors
|
||||
site_url: https://fluxcd.github.io/toolkit/
|
||||
|
||||
repo_name: fluxcd/toolkit
|
||||
repo_url: https://github.com/fluxcd/toolkit
|
||||
edit_uri: ""
|
||||
|
||||
theme:
|
||||
name: material
|
||||
logo: _files/flux-icon@2x.png
|
||||
language: en
|
||||
palette:
|
||||
primary: blue
|
||||
accent: indigo
|
||||
|
||||
docs_dir: docs
|
||||
|
||||
extra_css:
|
||||
- _static/custom.css
|
||||
|
||||
plugins:
|
||||
- search
|
||||
|
||||
markdown_extensions:
|
||||
- admonition
|
||||
- codehilite:
|
||||
guess_lang: false
|
||||
- toc:
|
||||
permalink: true
|
||||
- pymdownx.superfences:
|
||||
highlight_code: true
|
||||
- pymdownx.tabbed
|
||||
|
||||
nav:
|
||||
- Introduction: index.md
|
||||
- Get Started: get-started/index.md
|
||||
- Toolkit Components:
|
||||
- Source Controller:
|
||||
- Overview: components/source/controller.md
|
||||
- GitRepository CRD: components/source/gitrepositories.md
|
||||
- HelmRepository CRD: components/source/helmrepositories.md
|
||||
- Source API Reference: components/source/api.md
|
||||
- Kustomize Controller:
|
||||
- Overview: components/kustomize/controller.md
|
||||
- Kustomization CRD: components/kustomize/kustomization.md
|
||||
- Kustomize API Reference: components/kustomize/api.md
|
||||
- Toolkit CLI:
|
||||
- Overview: cmd/tk.md
|
||||
- Bootstrap: cmd/tk_bootstrap.md
|
||||
- Bootstrap github: cmd/tk_bootstrap_github.md
|
||||
- Bootstrap gitlab: cmd/tk_bootstrap_gitlab.md
|
||||
- Check: cmd/tk_check.md
|
||||
- Create: cmd/tk_create.md
|
||||
- Create kustomization: cmd/tk_create_kustomization.md
|
||||
- Create source: cmd/tk_create_source.md
|
||||
- Create source git: cmd/tk_create_source_git.md
|
||||
- Delete: cmd/tk_delete.md
|
||||
- Delete kustomization: cmd/tk_delete_kustomization.md
|
||||
- Delete source: cmd/tk_delete_source.md
|
||||
- Delete source git: cmd/tk_delete_source_git.md
|
||||
- Export: cmd/tk_export.md
|
||||
- Export kustomization: cmd/tk_export_kustomization.md
|
||||
- Export source: cmd/tk_export_source.md
|
||||
- Export source git: cmd/tk_export_source_git.md
|
||||
- Get: cmd/tk_get.md
|
||||
- Get kustomizations: cmd/tk_get_kustomizations.md
|
||||
- Get sources: cmd/tk_get_sources.md
|
||||
- Get sources git: cmd/tk_get_sources_git.md
|
||||
- Install: cmd/tk_install.md
|
||||
- Resume: cmd/tk_resume.md
|
||||
- Resume kustomization: cmd/tk_resume_kustomization.md
|
||||
- Suspend: cmd/tk_suspend.md
|
||||
- Suspend kustomization: cmd/tk_suspend_kustomization.md
|
||||
- Sync: cmd/tk_sync.md
|
||||
- Sync kustomization: cmd/tk_sync_kustomization.md
|
||||
- Sync source: cmd/tk_sync_source.md
|
||||
- Sync source git: cmd/tk_sync_source_git.md
|
||||
- Uninstall: cmd/tk_uninstall.md
|
||||
- Roadmap: roadmap/index.md
|
||||
26
pkg/git/provider.go
Normal file
26
pkg/git/provider.go
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
Copyright 2020 The Flux CD contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package git
|
||||
|
||||
import "context"
|
||||
|
||||
// Provider is the interface that a git provider should implement
|
||||
type Provider interface {
|
||||
CreateRepository(ctx context.Context, r *Repository) (bool, error)
|
||||
AddTeam(ctx context.Context, r *Repository, name, permission string) (bool, error)
|
||||
AddDeployKey(ctx context.Context, r *Repository, key, keyName string) (bool, error)
|
||||
}
|
||||
177
pkg/git/provider_github.go
Normal file
177
pkg/git/provider_github.go
Normal file
@@ -0,0 +1,177 @@
|
||||
/*
|
||||
Copyright 2020 The Flux CD contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package git
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/google/go-github/v32/github"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// GithubProvider represents a GitHub API wrapper
|
||||
type GithubProvider struct {
|
||||
IsPrivate bool
|
||||
IsPersonal bool
|
||||
}
|
||||
|
||||
const (
|
||||
GitHubTokenName = "GITHUB_TOKEN"
|
||||
GitHubDefaultHostname = "github.com"
|
||||
)
|
||||
|
||||
func (p *GithubProvider) newClient(r *Repository) (*github.Client, error) {
|
||||
auth := github.BasicAuthTransport{
|
||||
Username: "git",
|
||||
Password: r.Token,
|
||||
}
|
||||
|
||||
gh := github.NewClient(auth.Client())
|
||||
if r.Host != GitHubDefaultHostname {
|
||||
baseURL := fmt.Sprintf("https://%s/api/v3/", r.Host)
|
||||
uploadURL := fmt.Sprintf("https://%s/api/uploads/", r.Host)
|
||||
if g, err := github.NewEnterpriseClient(baseURL, uploadURL, auth.Client()); err == nil {
|
||||
gh = g
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return gh, nil
|
||||
}
|
||||
|
||||
// CreateRepository returns false if the repository exists
|
||||
func (p *GithubProvider) CreateRepository(ctx context.Context, r *Repository) (bool, error) {
|
||||
gh, err := p.newClient(r)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("client error: %w", err)
|
||||
}
|
||||
org := ""
|
||||
if !p.IsPersonal {
|
||||
org = r.Owner
|
||||
}
|
||||
|
||||
if _, _, err := gh.Repositories.Get(ctx, org, r.Name); err == nil {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
autoInit := true
|
||||
_, _, err = gh.Repositories.Create(ctx, org, &github.Repository{
|
||||
AutoInit: &autoInit,
|
||||
Name: &r.Name,
|
||||
Private: &p.IsPrivate,
|
||||
})
|
||||
if err != nil {
|
||||
if !strings.Contains(err.Error(), "name already exists on this account") {
|
||||
return false, fmt.Errorf("failed to create repository, error: %w", err)
|
||||
}
|
||||
} else {
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// AddTeam returns false if the team is already assigned to the repository
|
||||
func (p *GithubProvider) AddTeam(ctx context.Context, r *Repository, name, permission string) (bool, error) {
|
||||
gh, err := p.newClient(r)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("client error: %w", err)
|
||||
}
|
||||
|
||||
// check team exists
|
||||
_, _, err = gh.Teams.GetTeamBySlug(ctx, r.Owner, name)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to retrieve team '%s', error: %w", name, err)
|
||||
}
|
||||
|
||||
// check if team is assigned to the repo
|
||||
_, resp, err := gh.Teams.IsTeamRepoBySlug(ctx, r.Owner, name, r.Owner, r.Name)
|
||||
if resp == nil && err != nil {
|
||||
return false, fmt.Errorf("failed to determine if team '%s' is assigned to the repository, error: %w", name, err)
|
||||
}
|
||||
|
||||
// add team to the repo
|
||||
if resp.StatusCode == 404 {
|
||||
_, err = gh.Teams.AddTeamRepoBySlug(ctx, r.Owner, name, r.Owner, r.Name, &github.TeamAddTeamRepoOptions{
|
||||
Permission: permission,
|
||||
})
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to add team '%s' to the repository, error: %w", name, err)
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// AddDeployKey returns false if the key exists and the content is the same
|
||||
func (p *GithubProvider) AddDeployKey(ctx context.Context, r *Repository, key, keyName string) (bool, error) {
|
||||
gh, err := p.newClient(r)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("client error: %w", err)
|
||||
}
|
||||
|
||||
// list deploy keys
|
||||
keys, resp, err := gh.Repositories.ListKeys(ctx, r.Owner, r.Name, nil)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to list deploy keys, error: %w", err)
|
||||
}
|
||||
if resp.StatusCode >= 300 {
|
||||
return false, fmt.Errorf("failed to list deploy keys (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, r.Owner, r.Name, *existingKey.ID)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to delete deploy key '%s', error: %w", keyName, err)
|
||||
}
|
||||
if resp.StatusCode >= 300 {
|
||||
return false, fmt.Errorf("failed to delete deploy key '%s' (status code: %s)", keyName, resp.Status)
|
||||
}
|
||||
}
|
||||
|
||||
// create key
|
||||
if shouldCreateKey {
|
||||
isReadOnly := true
|
||||
_, _, err = gh.Repositories.CreateKey(ctx, r.Owner, r.Name, &github.Key{
|
||||
Title: &keyName,
|
||||
Key: &key,
|
||||
ReadOnly: &isReadOnly,
|
||||
})
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to create deploy key '%s', error: %w", keyName, err)
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
163
pkg/git/provider_gitlab.go
Normal file
163
pkg/git/provider_gitlab.go
Normal file
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
Copyright 2020 The Flux CD contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package git
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/xanzy/go-gitlab"
|
||||
)
|
||||
|
||||
// GitLabProvider represents a GitLab API wrapper
|
||||
type GitLabProvider struct {
|
||||
IsPrivate bool
|
||||
IsPersonal bool
|
||||
}
|
||||
|
||||
const (
|
||||
GitLabTokenName = "GITLAB_TOKEN"
|
||||
GitLabDefaultHostname = "gitlab.com"
|
||||
)
|
||||
|
||||
func (p *GitLabProvider) newClient(r *Repository) (*gitlab.Client, error) {
|
||||
gl, err := gitlab.NewClient(r.Token)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if r.Host != GitLabDefaultHostname {
|
||||
gl, err = gitlab.NewClient(r.Token, gitlab.WithBaseURL(fmt.Sprintf("https://%s/api/v4", r.Host)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return gl, nil
|
||||
}
|
||||
|
||||
// CreateRepository returns false if the repository already exists
|
||||
func (p *GitLabProvider) CreateRepository(ctx context.Context, r *Repository) (bool, error) {
|
||||
gl, err := p.newClient(r)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("client error: %w", err)
|
||||
}
|
||||
|
||||
var id *int
|
||||
if !p.IsPersonal {
|
||||
groups, _, err := gl.Groups.ListGroups(&gitlab.ListGroupsOptions{Search: gitlab.String(r.Owner)}, gitlab.WithContext(ctx))
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to list groups, error: %w", err)
|
||||
}
|
||||
|
||||
if len(groups) > 0 {
|
||||
id = &groups[0].ID
|
||||
}
|
||||
}
|
||||
|
||||
visibility := gitlab.PublicVisibility
|
||||
if p.IsPrivate {
|
||||
visibility = gitlab.PrivateVisibility
|
||||
}
|
||||
|
||||
projects, _, err := gl.Projects.ListProjects(&gitlab.ListProjectsOptions{Search: gitlab.String(r.Name)}, gitlab.WithContext(ctx))
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to list projects, error: %w", err)
|
||||
}
|
||||
|
||||
if len(projects) == 0 {
|
||||
p := &gitlab.CreateProjectOptions{
|
||||
Name: gitlab.String(r.Name),
|
||||
NamespaceID: id,
|
||||
Visibility: &visibility,
|
||||
InitializeWithReadme: gitlab.Bool(true),
|
||||
}
|
||||
|
||||
_, _, err := gl.Projects.CreateProject(p)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to create project, error: %w", err)
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// AddTeam returns false if the team is already assigned to the repository
|
||||
func (p *GitLabProvider) AddTeam(ctx context.Context, r *Repository, name, permission string) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// AddDeployKey returns false if the key exists and the content is the same
|
||||
func (p *GitLabProvider) AddDeployKey(ctx context.Context, r *Repository, key, keyName string) (bool, error) {
|
||||
gl, err := p.newClient(r)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("client error: %w", err)
|
||||
}
|
||||
|
||||
// list deploy keys
|
||||
var projId int
|
||||
projects, _, err := gl.Projects.ListProjects(&gitlab.ListProjectsOptions{Search: gitlab.String(r.Name)}, gitlab.WithContext(ctx))
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to list projects, error: %w", err)
|
||||
}
|
||||
if len(projects) > 0 {
|
||||
projId = projects[0].ID
|
||||
} else {
|
||||
return false, fmt.Errorf("no project found")
|
||||
}
|
||||
|
||||
// check if the key exists
|
||||
keys, _, err := gl.DeployKeys.ListProjectDeployKeys(projId, &gitlab.ListProjectDeployKeysOptions{})
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to list deploy keys, error: %w", err)
|
||||
}
|
||||
|
||||
shouldCreateKey := true
|
||||
var existingKey *gitlab.DeployKey
|
||||
for _, k := range keys {
|
||||
if k.Title == keyName {
|
||||
if k.Key != key {
|
||||
existingKey = k
|
||||
} else {
|
||||
shouldCreateKey = false
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// delete existing key if the value differs
|
||||
if existingKey != nil {
|
||||
_, err := gl.DeployKeys.DeleteDeployKey(projId, existingKey.ID, gitlab.WithContext(ctx))
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to delete deploy key '%s', error: %w", keyName, err)
|
||||
}
|
||||
}
|
||||
|
||||
// create key
|
||||
if shouldCreateKey {
|
||||
_, _, err := gl.DeployKeys.AddDeployKey(projId, &gitlab.AddDeployKeyOptions{
|
||||
Title: gitlab.String(keyName),
|
||||
Key: gitlab.String(key),
|
||||
CanPush: gitlab.Bool(false),
|
||||
}, gitlab.WithContext(ctx))
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to create deploy key '%s', error: %w", keyName, err)
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
167
pkg/git/repository.go
Normal file
167
pkg/git/repository.go
Normal file
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
Copyright 2020 The Flux CD contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package git
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/go-git/go-git/v5"
|
||||
"github.com/go-git/go-git/v5/plumbing"
|
||||
"github.com/go-git/go-git/v5/plumbing/object"
|
||||
"github.com/go-git/go-git/v5/plumbing/transport"
|
||||
"github.com/go-git/go-git/v5/plumbing/transport/http"
|
||||
)
|
||||
|
||||
// Repository represents a git repository wrapper
|
||||
type Repository struct {
|
||||
Name string
|
||||
Owner string
|
||||
Host string
|
||||
Token string
|
||||
AuthorName string
|
||||
AuthorEmail string
|
||||
|
||||
repo *git.Repository
|
||||
}
|
||||
|
||||
// NewRepository returns a git repository wrapper
|
||||
func NewRepository(name, owner, host, token, authorName, authorEmail string) (*Repository, error) {
|
||||
if name == "" {
|
||||
return nil, fmt.Errorf("name required")
|
||||
}
|
||||
if owner == "" {
|
||||
return nil, fmt.Errorf("owner required")
|
||||
}
|
||||
if host == "" {
|
||||
return nil, fmt.Errorf("host required")
|
||||
}
|
||||
if token == "" {
|
||||
return nil, fmt.Errorf("token required")
|
||||
}
|
||||
if authorName == "" {
|
||||
return nil, fmt.Errorf("author name required")
|
||||
}
|
||||
if authorEmail == "" {
|
||||
return nil, fmt.Errorf("author email required")
|
||||
}
|
||||
|
||||
return &Repository{
|
||||
Name: name,
|
||||
Owner: owner,
|
||||
Host: host,
|
||||
Token: token,
|
||||
AuthorName: authorName,
|
||||
AuthorEmail: authorEmail,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetURL returns the repository HTTPS address
|
||||
func (r *Repository) GetURL() string {
|
||||
return fmt.Sprintf("https://%s/%s/%s", r.Host, r.Owner, r.Name)
|
||||
}
|
||||
|
||||
// GetSSH returns the repository SSH address
|
||||
func (r *Repository) GetSSH() string {
|
||||
return fmt.Sprintf("ssh://git@%s/%s/%s", r.Host, r.Owner, r.Name)
|
||||
}
|
||||
|
||||
func (r *Repository) auth() transport.AuthMethod {
|
||||
return &http.BasicAuth{
|
||||
Username: "git",
|
||||
Password: r.Token,
|
||||
}
|
||||
}
|
||||
|
||||
// Checkout repository branch at specified path
|
||||
func (r *Repository) Checkout(ctx context.Context, branch, path string) error {
|
||||
repo, err := git.PlainCloneContext(ctx, path, false, &git.CloneOptions{
|
||||
URL: r.GetURL(),
|
||||
Auth: r.auth(),
|
||||
RemoteName: git.DefaultRemoteName,
|
||||
ReferenceName: plumbing.NewBranchReferenceName(branch),
|
||||
SingleBranch: true,
|
||||
NoCheckout: false,
|
||||
Progress: nil,
|
||||
Tags: git.NoTags,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("git clone error: %w", err)
|
||||
}
|
||||
|
||||
_, err = repo.Head()
|
||||
if err != nil {
|
||||
return fmt.Errorf("git resolve HEAD error: %w", err)
|
||||
}
|
||||
|
||||
r.repo = repo
|
||||
return nil
|
||||
}
|
||||
|
||||
// Commit changes for the specified path, returns false if no changes are detected
|
||||
func (r *Repository) Commit(ctx context.Context, path, message string) (bool, error) {
|
||||
if r.repo == nil {
|
||||
return false, fmt.Errorf("repository hasn't been cloned")
|
||||
}
|
||||
|
||||
w, err := r.repo.Worktree()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
_, err = w.Add(path)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
status, err := w.Status()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if !status.IsClean() {
|
||||
if _, err := w.Commit(message, &git.CommitOptions{
|
||||
Author: &object.Signature{
|
||||
Name: r.AuthorName,
|
||||
Email: r.AuthorEmail,
|
||||
When: time.Now(),
|
||||
},
|
||||
}); err != nil {
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// Push commits to origin
|
||||
func (r *Repository) Push(ctx context.Context) error {
|
||||
if r.repo == nil {
|
||||
return fmt.Errorf("repository hasn't been cloned")
|
||||
}
|
||||
|
||||
err := r.repo.PushContext(ctx, &git.PushOptions{
|
||||
Auth: r.auth(),
|
||||
Progress: nil,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("git push error: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
Copyright 2020 The Flux CD contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package ssh
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
Copyright 2020 The Flux CD contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package ssh
|
||||
|
||||
import (
|
||||
|
||||
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