Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b7727e2659 | ||
|
|
229d1d8c6e | ||
|
|
c0b18f85aa | ||
|
|
b11b9588f8 | ||
|
|
633d028841 | ||
|
|
a744b304a0 | ||
|
|
e594350307 | ||
|
|
ecf7a1eac1 | ||
|
|
4621afcb31 | ||
|
|
006c949941 | ||
|
|
72e06ab337 | ||
|
|
f4b2a32a23 | ||
|
|
329e21fd78 | ||
|
|
5499d15402 | ||
|
|
d7f8a05612 | ||
|
|
1f99a75049 | ||
|
|
21fd436621 |
4
.github/workflows/docs.yaml
vendored
4
.github/workflows/docs.yaml
vendored
@@ -20,6 +20,10 @@ jobs:
|
|||||||
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/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/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
|
curl https://raw.githubusercontent.com/fluxcd/kustomize-controller/master/docs/spec/v1alpha1/kustomization.md > docs/components/kustomize/kustomization.md
|
||||||
|
curl https://raw.githubusercontent.com/fluxcd/notification-controller/master/docs/api/notification.md > docs/components/notification/api.md
|
||||||
|
curl https://raw.githubusercontent.com/fluxcd/notification-controller/master/docs/spec/v1alpha1/event.md > docs/components/notification/event.md
|
||||||
|
curl https://raw.githubusercontent.com/fluxcd/notification-controller/master/docs/spec/v1alpha1/alert.md > docs/components/notification/alert.md
|
||||||
|
curl https://raw.githubusercontent.com/fluxcd/notification-controller/master/docs/spec/v1alpha1/provider.md > docs/components/notification/provider.md
|
||||||
- name: Deploy docs
|
- name: Deploy docs
|
||||||
uses: mhausenblas/mkdocs-deploy-gh-pages@master
|
uses: mhausenblas/mkdocs-deploy-gh-pages@master
|
||||||
env:
|
env:
|
||||||
|
|||||||
4
.github/workflows/e2e.yaml
vendored
4
.github/workflows/e2e.yaml
vendored
@@ -30,7 +30,7 @@ jobs:
|
|||||||
- name: Check if working tree is dirty
|
- name: Check if working tree is dirty
|
||||||
run: |
|
run: |
|
||||||
if [[ $(git diff --stat) != '' ]]; then
|
if [[ $(git diff --stat) != '' ]]; then
|
||||||
git diff --stat
|
git diff
|
||||||
echo 'run make test and commit changes'
|
echo 'run make test and commit changes'
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
@@ -41,7 +41,7 @@ jobs:
|
|||||||
./bin/tk check --pre
|
./bin/tk check --pre
|
||||||
- name: tk install --version
|
- name: tk install --version
|
||||||
run: |
|
run: |
|
||||||
./bin/tk install --version=master --namespace=test --verbose
|
./bin/tk install --version=master --namespace=test --verbose --components="source-controller,kustomize-controller"
|
||||||
- name: tk uninstall
|
- name: tk uninstall
|
||||||
run: |
|
run: |
|
||||||
./bin/tk uninstall --namespace=test --crds --silent
|
./bin/tk uninstall --namespace=test --crds --silent
|
||||||
|
|||||||
@@ -40,7 +40,8 @@ import (
|
|||||||
|
|
||||||
var bootstrapCmd = &cobra.Command{
|
var bootstrapCmd = &cobra.Command{
|
||||||
Use: "bootstrap",
|
Use: "bootstrap",
|
||||||
Short: "Bootstrap commands",
|
Short: "Bootstrap toolkit components",
|
||||||
|
Long: "The bootstrap sub-commands bootstrap the toolkit components on the targeted Git provider.",
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -178,7 +179,7 @@ func applySyncManifests(ctx context.Context, kubeClient client.Client, name, nam
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
logWaiting("waiting for cluster sync")
|
logger.Waitingf("waiting for cluster sync")
|
||||||
|
|
||||||
if err := wait.PollImmediate(pollInterval, timeout,
|
if err := wait.PollImmediate(pollInterval, timeout,
|
||||||
isGitRepositoryReady(ctx, kubeClient, name, namespace)); err != nil {
|
isGitRepositoryReady(ctx, kubeClient, name, namespace)); err != nil {
|
||||||
|
|||||||
@@ -27,16 +27,15 @@ import (
|
|||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/fluxcd/toolkit/pkg/git"
|
"github.com/fluxcd/pkg/git"
|
||||||
)
|
)
|
||||||
|
|
||||||
var bootstrapGitHubCmd = &cobra.Command{
|
var bootstrapGitHubCmd = &cobra.Command{
|
||||||
Use: "github",
|
Use: "github",
|
||||||
Short: "Bootstrap GitHub repository",
|
Short: "Bootstrap toolkit components in a GitHub repository",
|
||||||
Long: `
|
Long: `The bootstrap github command creates the GitHub repository if it doesn't exists and
|
||||||
The bootstrap command creates the GitHub repository if it doesn't exists and
|
|
||||||
commits the toolkit components manifests to the master branch.
|
commits the toolkit components manifests to the master branch.
|
||||||
Then it configure the target cluster to synchronize with the repository.
|
Then it configures the target cluster to synchronize with the repository.
|
||||||
If the toolkit components are present on the cluster,
|
If the toolkit components are present on the cluster,
|
||||||
the bootstrap command will perform an upgrade if needed.`,
|
the bootstrap command will perform an upgrade if needed.`,
|
||||||
Example: ` # Create a GitHub personal access token and export it as an env var
|
Example: ` # Create a GitHub personal access token and export it as an env var
|
||||||
@@ -119,13 +118,13 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
// create GitHub repository if doesn't exists
|
// create GitHub repository if doesn't exists
|
||||||
logAction("connecting to %s", ghHostname)
|
logger.Actionf("connecting to %s", ghHostname)
|
||||||
changed, err := provider.CreateRepository(ctx, repository)
|
changed, err := provider.CreateRepository(ctx, repository)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if changed {
|
if changed {
|
||||||
logSuccess("repository created")
|
logger.Successf("repository created")
|
||||||
}
|
}
|
||||||
|
|
||||||
withErrors := false
|
withErrors := false
|
||||||
@@ -133,10 +132,10 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
if !ghPersonal {
|
if !ghPersonal {
|
||||||
for _, team := range ghTeams {
|
for _, team := range ghTeams {
|
||||||
if changed, err := provider.AddTeam(ctx, repository, team, ghDefaultPermission); err != nil {
|
if changed, err := provider.AddTeam(ctx, repository, team, ghDefaultPermission); err != nil {
|
||||||
logFailure(err.Error())
|
logger.Failuref(err.Error())
|
||||||
withErrors = true
|
withErrors = true
|
||||||
} else if changed {
|
} else if changed {
|
||||||
logSuccess("%s team access granted", team)
|
logger.Successf("%s team access granted", team)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -145,10 +144,10 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
if err := repository.Checkout(ctx, bootstrapBranch, tmpDir); err != nil {
|
if err := repository.Checkout(ctx, bootstrapBranch, tmpDir); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
logSuccess("repository cloned")
|
logger.Successf("repository cloned")
|
||||||
|
|
||||||
// generate install manifests
|
// generate install manifests
|
||||||
logGenerate("generating manifests")
|
logger.Generatef("generating manifests")
|
||||||
manifest, err := generateInstallManifests(ghPath, namespace, tmpDir)
|
manifest, err := generateInstallManifests(ghPath, namespace, tmpDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -165,9 +164,9 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
if err := repository.Push(ctx); err != nil {
|
if err := repository.Push(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
logSuccess("components manifests pushed")
|
logger.Successf("components manifests pushed")
|
||||||
} else {
|
} else {
|
||||||
logSuccess("components are up to date")
|
logger.Successf("components are up to date")
|
||||||
}
|
}
|
||||||
|
|
||||||
// determine if repo synchronization is working
|
// determine if repo synchronization is working
|
||||||
@@ -175,16 +174,16 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
|
|
||||||
if isInstall {
|
if isInstall {
|
||||||
// apply install manifests
|
// apply install manifests
|
||||||
logAction("installing components in %s namespace", namespace)
|
logger.Actionf("installing components in %s namespace", namespace)
|
||||||
if err := applyInstallManifests(ctx, manifest, components); err != nil {
|
if err := applyInstallManifests(ctx, manifest, components); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
logSuccess("install completed")
|
logger.Successf("install completed")
|
||||||
}
|
}
|
||||||
|
|
||||||
// setup SSH deploy key
|
// setup SSH deploy key
|
||||||
if shouldCreateDeployKey(ctx, kubeClient, namespace) {
|
if shouldCreateDeployKey(ctx, kubeClient, namespace) {
|
||||||
logAction("configuring deploy key")
|
logger.Actionf("configuring deploy key")
|
||||||
u, err := url.Parse(repository.GetSSH())
|
u, err := url.Parse(repository.GetSSH())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("git URL parse failed: %w", err)
|
return fmt.Errorf("git URL parse failed: %w", err)
|
||||||
@@ -203,14 +202,14 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
if changed, err := provider.AddDeployKey(ctx, repository, key, keyName); err != nil {
|
if changed, err := provider.AddDeployKey(ctx, repository, key, keyName); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if changed {
|
} else if changed {
|
||||||
logSuccess("deploy key configured")
|
logger.Successf("deploy key configured")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// configure repo synchronization
|
// configure repo synchronization
|
||||||
if isInstall {
|
if isInstall {
|
||||||
// generate source and kustomization manifests
|
// generate source and kustomization manifests
|
||||||
logAction("generating sync manifests")
|
logger.Actionf("generating sync manifests")
|
||||||
if err := generateSyncManifests(repository.GetSSH(), namespace, namespace, ghPath, tmpDir, ghInterval); err != nil {
|
if err := generateSyncManifests(repository.GetSSH(), namespace, namespace, ghPath, tmpDir, ghInterval); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -222,11 +221,11 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
if err := repository.Push(ctx); err != nil {
|
if err := repository.Push(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
logSuccess("sync manifests pushed")
|
logger.Successf("sync manifests pushed")
|
||||||
}
|
}
|
||||||
|
|
||||||
// apply manifests and waiting for sync
|
// apply manifests and waiting for sync
|
||||||
logAction("applying sync manifests")
|
logger.Actionf("applying sync manifests")
|
||||||
if err := applySyncManifests(ctx, kubeClient, namespace, namespace, ghPath, tmpDir); err != nil {
|
if err := applySyncManifests(ctx, kubeClient, namespace, namespace, ghPath, tmpDir); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -236,6 +235,6 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
return fmt.Errorf("bootstrap completed with errors")
|
return fmt.Errorf("bootstrap completed with errors")
|
||||||
}
|
}
|
||||||
|
|
||||||
logSuccess("bootstrap finished")
|
logger.Successf("bootstrap finished")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,16 +27,15 @@ import (
|
|||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/fluxcd/toolkit/pkg/git"
|
"github.com/fluxcd/pkg/git"
|
||||||
)
|
)
|
||||||
|
|
||||||
var bootstrapGitLabCmd = &cobra.Command{
|
var bootstrapGitLabCmd = &cobra.Command{
|
||||||
Use: "gitlab",
|
Use: "gitlab",
|
||||||
Short: "Bootstrap GitLab repository",
|
Short: "Bootstrap toolkit components in a GitLab repository",
|
||||||
Long: `
|
Long: `The bootstrap gitlab command creates the GitLab repository if it doesn't exists and
|
||||||
The bootstrap command creates the GitLab repository if it doesn't exists and
|
|
||||||
commits the toolkit components manifests to the master branch.
|
commits the toolkit components manifests to the master branch.
|
||||||
Then it configure the target cluster to synchronize with the repository.
|
Then it configures the target cluster to synchronize with the repository.
|
||||||
If the toolkit components are present on the cluster,
|
If the toolkit components are present on the cluster,
|
||||||
the bootstrap command will perform an upgrade if needed.`,
|
the bootstrap command will perform an upgrade if needed.`,
|
||||||
Example: ` # Create a GitLab API token and export it as an env var
|
Example: ` # Create a GitLab API token and export it as an env var
|
||||||
@@ -110,23 +109,23 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
// create GitLab project if doesn't exists
|
// create GitLab project if doesn't exists
|
||||||
logAction("connecting to %s", glHostname)
|
logger.Actionf("connecting to %s", glHostname)
|
||||||
changed, err := provider.CreateRepository(ctx, repository)
|
changed, err := provider.CreateRepository(ctx, repository)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if changed {
|
if changed {
|
||||||
logSuccess("repository created")
|
logger.Successf("repository created")
|
||||||
}
|
}
|
||||||
|
|
||||||
// clone repository and checkout the master branch
|
// clone repository and checkout the master branch
|
||||||
if err := repository.Checkout(ctx, bootstrapBranch, tmpDir); err != nil {
|
if err := repository.Checkout(ctx, bootstrapBranch, tmpDir); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
logSuccess("repository cloned")
|
logger.Successf("repository cloned")
|
||||||
|
|
||||||
// generate install manifests
|
// generate install manifests
|
||||||
logGenerate("generating manifests")
|
logger.Generatef("generating manifests")
|
||||||
manifest, err := generateInstallManifests(glPath, namespace, tmpDir)
|
manifest, err := generateInstallManifests(glPath, namespace, tmpDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -143,9 +142,9 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
if err := repository.Push(ctx); err != nil {
|
if err := repository.Push(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
logSuccess("components manifests pushed")
|
logger.Successf("components manifests pushed")
|
||||||
} else {
|
} else {
|
||||||
logSuccess("components are up to date")
|
logger.Successf("components are up to date")
|
||||||
}
|
}
|
||||||
|
|
||||||
// determine if repo synchronization is working
|
// determine if repo synchronization is working
|
||||||
@@ -153,16 +152,16 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
|
|
||||||
if isInstall {
|
if isInstall {
|
||||||
// apply install manifests
|
// apply install manifests
|
||||||
logAction("installing components in %s namespace", namespace)
|
logger.Actionf("installing components in %s namespace", namespace)
|
||||||
if err := applyInstallManifests(ctx, manifest, components); err != nil {
|
if err := applyInstallManifests(ctx, manifest, components); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
logSuccess("install completed")
|
logger.Successf("install completed")
|
||||||
}
|
}
|
||||||
|
|
||||||
// setup SSH deploy key
|
// setup SSH deploy key
|
||||||
if shouldCreateDeployKey(ctx, kubeClient, namespace) {
|
if shouldCreateDeployKey(ctx, kubeClient, namespace) {
|
||||||
logAction("configuring deploy key")
|
logger.Actionf("configuring deploy key")
|
||||||
u, err := url.Parse(repository.GetSSH())
|
u, err := url.Parse(repository.GetSSH())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("git URL parse failed: %w", err)
|
return fmt.Errorf("git URL parse failed: %w", err)
|
||||||
@@ -181,14 +180,14 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
if changed, err := provider.AddDeployKey(ctx, repository, key, keyName); err != nil {
|
if changed, err := provider.AddDeployKey(ctx, repository, key, keyName); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if changed {
|
} else if changed {
|
||||||
logSuccess("deploy key configured")
|
logger.Successf("deploy key configured")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// configure repo synchronization
|
// configure repo synchronization
|
||||||
if isInstall {
|
if isInstall {
|
||||||
// generate source and kustomization manifests
|
// generate source and kustomization manifests
|
||||||
logAction("generating sync manifests")
|
logger.Actionf("generating sync manifests")
|
||||||
if err := generateSyncManifests(repository.GetSSH(), namespace, namespace, glPath, tmpDir, glInterval); err != nil {
|
if err := generateSyncManifests(repository.GetSSH(), namespace, namespace, glPath, tmpDir, glInterval); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -200,16 +199,16 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
if err := repository.Push(ctx); err != nil {
|
if err := repository.Push(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
logSuccess("sync manifests pushed")
|
logger.Successf("sync manifests pushed")
|
||||||
}
|
}
|
||||||
|
|
||||||
// apply manifests and waiting for sync
|
// apply manifests and waiting for sync
|
||||||
logAction("applying sync manifests")
|
logger.Actionf("applying sync manifests")
|
||||||
if err := applySyncManifests(ctx, kubeClient, namespace, namespace, glPath, tmpDir); err != nil {
|
if err := applySyncManifests(ctx, kubeClient, namespace, namespace, glPath, tmpDir); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
logSuccess("bootstrap finished")
|
logger.Successf("bootstrap finished")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,8 +32,7 @@ import (
|
|||||||
var checkCmd = &cobra.Command{
|
var checkCmd = &cobra.Command{
|
||||||
Use: "check",
|
Use: "check",
|
||||||
Short: "Check requirements and installation",
|
Short: "Check requirements and installation",
|
||||||
Long: `
|
Long: `The check command will perform a series of checks to validate that
|
||||||
The check command will perform a series of checks to validate that
|
|
||||||
the local environment is configured correctly and if the installed components are healthy.`,
|
the local environment is configured correctly and if the installed components are healthy.`,
|
||||||
Example: ` # Run pre-installation checks
|
Example: ` # Run pre-installation checks
|
||||||
check --pre
|
check --pre
|
||||||
@@ -59,7 +58,7 @@ func runCheckCmd(cmd *cobra.Command, args []string) error {
|
|||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
logAction("checking prerequisites")
|
logger.Actionf("checking prerequisites")
|
||||||
checkFailed := false
|
checkFailed := false
|
||||||
|
|
||||||
if !kubectlCheck(ctx, ">=1.18.0") {
|
if !kubectlCheck(ctx, ">=1.18.0") {
|
||||||
@@ -74,83 +73,83 @@ func runCheckCmd(cmd *cobra.Command, args []string) error {
|
|||||||
if checkFailed {
|
if checkFailed {
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
logSuccess("prerequisites checks passed")
|
logger.Successf("prerequisites checks passed")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
logAction("checking controllers")
|
logger.Actionf("checking controllers")
|
||||||
if !componentsCheck() {
|
if !componentsCheck() {
|
||||||
checkFailed = true
|
checkFailed = true
|
||||||
}
|
}
|
||||||
if checkFailed {
|
if checkFailed {
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
logSuccess("all checks passed")
|
logger.Successf("all checks passed")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func kubectlCheck(ctx context.Context, version string) bool {
|
func kubectlCheck(ctx context.Context, version string) bool {
|
||||||
_, err := exec.LookPath("kubectl")
|
_, err := exec.LookPath("kubectl")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logFailure("kubectl not found")
|
logger.Failuref("kubectl not found")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
command := "kubectl version --client --short | awk '{ print $3 }'"
|
command := "kubectl version --client --short | awk '{ print $3 }'"
|
||||||
output, err := utils.execCommand(ctx, ModeCapture, command)
|
output, err := utils.execCommand(ctx, ModeCapture, command)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logFailure("kubectl version can't be determined")
|
logger.Failuref("kubectl version can't be determined")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
v, err := semver.ParseTolerant(output)
|
v, err := semver.ParseTolerant(output)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logFailure("kubectl version can't be parsed")
|
logger.Failuref("kubectl version can't be parsed")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
rng, _ := semver.ParseRange(version)
|
rng, _ := semver.ParseRange(version)
|
||||||
if !rng(v) {
|
if !rng(v) {
|
||||||
logFailure("kubectl version must be %s", version)
|
logger.Failuref("kubectl version must be %s", version)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
logSuccess("kubectl %s %s", v.String(), version)
|
logger.Successf("kubectl %s %s", v.String(), version)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func kubernetesCheck(version string) bool {
|
func kubernetesCheck(version string) bool {
|
||||||
cfg, err := clientcmd.BuildConfigFromFlags("", kubeconfig)
|
cfg, err := clientcmd.BuildConfigFromFlags("", kubeconfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logFailure("Kubernetes client initialization failed: %s", err.Error())
|
logger.Failuref("Kubernetes client initialization failed: %s", err.Error())
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
client, err := kubernetes.NewForConfig(cfg)
|
client, err := kubernetes.NewForConfig(cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logFailure("Kubernetes client initialization failed: %s", err.Error())
|
logger.Failuref("Kubernetes client initialization failed: %s", err.Error())
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
ver, err := client.Discovery().ServerVersion()
|
ver, err := client.Discovery().ServerVersion()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logFailure("Kubernetes API call failed: %s", err.Error())
|
logger.Failuref("Kubernetes API call failed: %s", err.Error())
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
v, err := semver.ParseTolerant(ver.String())
|
v, err := semver.ParseTolerant(ver.String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logFailure("Kubernetes version can't be determined")
|
logger.Failuref("Kubernetes version can't be determined")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
rng, _ := semver.ParseRange(version)
|
rng, _ := semver.ParseRange(version)
|
||||||
if !rng(v) {
|
if !rng(v) {
|
||||||
logFailure("Kubernetes version must be %s", version)
|
logger.Failuref("Kubernetes version must be %s", version)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
logSuccess("Kubernetes %s %s", v.String(), version)
|
logger.Successf("Kubernetes %s %s", v.String(), version)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,10 +162,10 @@ func componentsCheck() bool {
|
|||||||
command := fmt.Sprintf("kubectl -n %s rollout status deployment %s --timeout=%s",
|
command := fmt.Sprintf("kubectl -n %s rollout status deployment %s --timeout=%s",
|
||||||
namespace, deployment, timeout.String())
|
namespace, deployment, timeout.String())
|
||||||
if output, err := utils.execCommand(ctx, ModeCapture, command); err != nil {
|
if output, err := utils.execCommand(ctx, ModeCapture, command); err != nil {
|
||||||
logFailure("%s: %s", deployment, strings.TrimSuffix(output, "\n"))
|
logger.Failuref("%s: %s", deployment, strings.TrimSuffix(output, "\n"))
|
||||||
ok = false
|
ok = false
|
||||||
} else {
|
} else {
|
||||||
logSuccess("%s is healthy", deployment)
|
logger.Successf("%s is healthy", deployment)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ok
|
return ok
|
||||||
|
|||||||
@@ -24,7 +24,8 @@ import (
|
|||||||
|
|
||||||
var createCmd = &cobra.Command{
|
var createCmd = &cobra.Command{
|
||||||
Use: "create",
|
Use: "create",
|
||||||
Short: "Create commands",
|
Short: "Create or update sources and resources",
|
||||||
|
Long: "The create sub-commands generate sources and resources.",
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -34,6 +35,6 @@ var (
|
|||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
createCmd.PersistentFlags().DurationVarP(&interval, "interval", "", time.Minute, "source sync interval")
|
createCmd.PersistentFlags().DurationVarP(&interval, "interval", "", time.Minute, "source sync interval")
|
||||||
createCmd.PersistentFlags().BoolVar(&export, "export", false, "export in yaml format to stdout")
|
createCmd.PersistentFlags().BoolVar(&export, "export", false, "export in YAML format to stdout")
|
||||||
rootCmd.AddCommand(createCmd)
|
rootCmd.AddCommand(createCmd)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,11 +37,9 @@ import (
|
|||||||
var createKsCmd = &cobra.Command{
|
var createKsCmd = &cobra.Command{
|
||||||
Use: "kustomization [name]",
|
Use: "kustomization [name]",
|
||||||
Aliases: []string{"ks"},
|
Aliases: []string{"ks"},
|
||||||
Short: "Create or update a kustomization resource",
|
Short: "Create or update a Kustomization resource",
|
||||||
Long: `
|
Long: "The kustomization source create command generates a Kustomize resource for a given GitRepository source.",
|
||||||
The kustomization source command generates a kustomization.kustomize.fluxcd.io resource for a given GitRepository source.
|
Example: ` # Create a Kustomization resource from a source at a given path
|
||||||
API spec: https://github.com/fluxcd/kustomize-controller/tree/master/docs/spec/v1alpha1`,
|
|
||||||
Example: ` # Create a kustomization from a source at a given path
|
|
||||||
create kustomization contour \
|
create kustomization contour \
|
||||||
--source=contour \
|
--source=contour \
|
||||||
--path="./examples/contour/" \
|
--path="./examples/contour/" \
|
||||||
@@ -52,7 +50,7 @@ API spec: https://github.com/fluxcd/kustomize-controller/tree/master/docs/spec/v
|
|||||||
--health-check="DaemonSet/envoy.projectcontour" \
|
--health-check="DaemonSet/envoy.projectcontour" \
|
||||||
--health-check-timeout=3m
|
--health-check-timeout=3m
|
||||||
|
|
||||||
# Create a kustomization that depends on the previous one
|
# Create a Kustomization resource that depends on the previous one
|
||||||
create kustomization webapp \
|
create kustomization webapp \
|
||||||
--depends-on=contour \
|
--depends-on=contour \
|
||||||
--source=webapp \
|
--source=webapp \
|
||||||
@@ -61,7 +59,7 @@ API spec: https://github.com/fluxcd/kustomize-controller/tree/master/docs/spec/v
|
|||||||
--interval=5m \
|
--interval=5m \
|
||||||
--validate=client
|
--validate=client
|
||||||
|
|
||||||
# Create a kustomization that runs under a service account
|
# Create a Kustomization resource that runs under a service account
|
||||||
create kustomization webapp \
|
create kustomization webapp \
|
||||||
--source=webapp \
|
--source=webapp \
|
||||||
--path="./deploy/overlays/staging" \
|
--path="./deploy/overlays/staging" \
|
||||||
@@ -88,12 +86,12 @@ var (
|
|||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
createKsCmd.Flags().StringVar(&ksSource, "source", "", "GitRepository name")
|
createKsCmd.Flags().StringVar(&ksSource, "source", "", "GitRepository name")
|
||||||
createKsCmd.Flags().StringVar(&ksPath, "path", "./", "path to the directory containing the kustomization file")
|
createKsCmd.Flags().StringVar(&ksPath, "path", "./", "path to the directory containing the Kustomization file")
|
||||||
createKsCmd.Flags().BoolVar(&ksPrune, "prune", false, "enable garbage collection")
|
createKsCmd.Flags().BoolVar(&ksPrune, "prune", false, "enable garbage collection")
|
||||||
createKsCmd.Flags().StringArrayVar(&ksHealthCheck, "health-check", nil, "workload to be included in the health assessment, in the format '<kind>/<name>.<namespace>'")
|
createKsCmd.Flags().StringArrayVar(&ksHealthCheck, "health-check", nil, "workload to be included in the health assessment, in the format '<kind>/<name>.<namespace>'")
|
||||||
createKsCmd.Flags().DurationVar(&ksHealthTimeout, "health-check-timeout", 2*time.Minute, "timeout of health checking operations")
|
createKsCmd.Flags().DurationVar(&ksHealthTimeout, "health-check-timeout", 2*time.Minute, "timeout of health checking operations")
|
||||||
createKsCmd.Flags().StringVar(&ksValidate, "validate", "", "validate the manifests before applying them on the cluster, can be 'client' or 'server'")
|
createKsCmd.Flags().StringVar(&ksValidate, "validate", "", "validate the manifests before applying them on the cluster, can be 'client' or 'server'")
|
||||||
createKsCmd.Flags().StringArrayVar(&ksDependsOn, "depends-on", nil, "kustomization that must be ready before this kustomization can be applied")
|
createKsCmd.Flags().StringArrayVar(&ksDependsOn, "depends-on", nil, "Kustomization that must be ready before this Kustomization can be applied")
|
||||||
createKsCmd.Flags().StringVar(&ksSAName, "sa-name", "", "service account name")
|
createKsCmd.Flags().StringVar(&ksSAName, "sa-name", "", "service account name")
|
||||||
createKsCmd.Flags().StringVar(&ksSANamespace, "sa-namespace", "", "service account namespace")
|
createKsCmd.Flags().StringVar(&ksSANamespace, "sa-namespace", "", "service account namespace")
|
||||||
createCmd.AddCommand(createKsCmd)
|
createCmd.AddCommand(createKsCmd)
|
||||||
@@ -124,7 +122,7 @@ func createKsCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !export {
|
if !export {
|
||||||
logGenerate("generating kustomization")
|
logger.Generatef("generating kustomization")
|
||||||
}
|
}
|
||||||
|
|
||||||
emptyAPIGroup := ""
|
emptyAPIGroup := ""
|
||||||
@@ -194,18 +192,18 @@ func createKsCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
return exportKs(kustomization)
|
return exportKs(kustomization)
|
||||||
}
|
}
|
||||||
|
|
||||||
logAction("applying kustomization")
|
logger.Actionf("applying kustomization")
|
||||||
if err := upsertKustomization(ctx, kubeClient, kustomization); err != nil {
|
if err := upsertKustomization(ctx, kubeClient, kustomization); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
logWaiting("waiting for kustomization sync")
|
logger.Waitingf("waiting for kustomization sync")
|
||||||
if err := wait.PollImmediate(pollInterval, timeout,
|
if err := wait.PollImmediate(pollInterval, timeout,
|
||||||
isKustomizationReady(ctx, kubeClient, name, namespace)); err != nil {
|
isKustomizationReady(ctx, kubeClient, name, namespace)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
logSuccess("kustomization %s is ready", name)
|
logger.Successf("kustomization %s is ready", name)
|
||||||
|
|
||||||
namespacedName := types.NamespacedName{
|
namespacedName := types.NamespacedName{
|
||||||
Namespace: namespace,
|
Namespace: namespace,
|
||||||
@@ -217,7 +215,7 @@ func createKsCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if kustomization.Status.LastAppliedRevision != "" {
|
if kustomization.Status.LastAppliedRevision != "" {
|
||||||
logSuccess("applied revision %s", kustomization.Status.LastAppliedRevision)
|
logger.Successf("applied revision %s", kustomization.Status.LastAppliedRevision)
|
||||||
} else {
|
} else {
|
||||||
return fmt.Errorf("kustomization sync failed")
|
return fmt.Errorf("kustomization sync failed")
|
||||||
}
|
}
|
||||||
@@ -238,7 +236,7 @@ func upsertKustomization(ctx context.Context, kubeClient client.Client, kustomiz
|
|||||||
if err := kubeClient.Create(ctx, &kustomization); err != nil {
|
if err := kubeClient.Create(ctx, &kustomization); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
logSuccess("kustomization created")
|
logger.Successf("kustomization created")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -250,7 +248,7 @@ func upsertKustomization(ctx context.Context, kubeClient client.Client, kustomiz
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
logSuccess("kustomization updated")
|
logger.Successf("kustomization updated")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,8 @@ import (
|
|||||||
|
|
||||||
var createSourceCmd = &cobra.Command{
|
var createSourceCmd = &cobra.Command{
|
||||||
Use: "source",
|
Use: "source",
|
||||||
Short: "Create source commands",
|
Short: "Create or update sources",
|
||||||
|
Long: "The create source sub-commands generate sources.",
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|||||||
@@ -35,14 +35,14 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/util/wait"
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
|
||||||
"github.com/fluxcd/toolkit/pkg/ssh"
|
"github.com/fluxcd/pkg/ssh"
|
||||||
)
|
)
|
||||||
|
|
||||||
var createSourceGitCmd = &cobra.Command{
|
var createSourceGitCmd = &cobra.Command{
|
||||||
Use: "git [name]",
|
Use: "git [name]",
|
||||||
Short: "Create or update a git source",
|
Short: "Create or update a GitRepository source",
|
||||||
Long: `
|
Long: `
|
||||||
The create source command generates a GitRepository resource and waits for it to sync.
|
The create source git command generates a GitRepository resource and waits for it to sync.
|
||||||
For Git over SSH, host and SSH keys are automatically generated and stored in a Kubernetes secret.
|
For Git over SSH, host and SSH keys are automatically generated and stored in a Kubernetes secret.
|
||||||
For private Git repositories, the basic authentication credentials are stored in a Kubernetes secret.`,
|
For private Git repositories, the basic authentication credentials are stored in a Kubernetes secret.`,
|
||||||
Example: ` # Create a source from a public Git repository master branch
|
Example: ` # Create a source from a public Git repository master branch
|
||||||
@@ -166,7 +166,7 @@ func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
withAuth := false
|
withAuth := false
|
||||||
// TODO(hidde): move all auth prep to separate func?
|
// TODO(hidde): move all auth prep to separate func?
|
||||||
if u.Scheme == "ssh" {
|
if u.Scheme == "ssh" {
|
||||||
logAction("generating deploy key pair")
|
logger.Actionf("generating deploy key pair")
|
||||||
pair, err := generateKeyPair(ctx)
|
pair, err := generateKeyPair(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -181,15 +181,15 @@ func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
return fmt.Errorf("aborting")
|
return fmt.Errorf("aborting")
|
||||||
}
|
}
|
||||||
|
|
||||||
logAction("collecting preferred public key from SSH server")
|
logger.Actionf("collecting preferred public key from SSH server")
|
||||||
hostKey, err := scanHostKey(ctx, u)
|
hostKey, err := scanHostKey(ctx, u)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
logSuccess("collected public key from SSH server:")
|
logger.Successf("collected public key from SSH server:")
|
||||||
fmt.Printf("%s", hostKey)
|
fmt.Printf("%s", hostKey)
|
||||||
|
|
||||||
logAction("applying secret with keys")
|
logger.Actionf("applying secret with keys")
|
||||||
secret := corev1.Secret{
|
secret := corev1.Secret{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: name,
|
Name: name,
|
||||||
@@ -206,7 +206,7 @@ func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
withAuth = true
|
withAuth = true
|
||||||
} else if sourceGitUsername != "" && sourceGitPassword != "" {
|
} else if sourceGitUsername != "" && sourceGitPassword != "" {
|
||||||
logAction("applying secret with basic auth credentials")
|
logger.Actionf("applying secret with basic auth credentials")
|
||||||
secret := corev1.Secret{
|
secret := corev1.Secret{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: name,
|
Name: name,
|
||||||
@@ -224,10 +224,10 @@ func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if withAuth {
|
if withAuth {
|
||||||
logSuccess("authentication configured")
|
logger.Successf("authentication configured")
|
||||||
}
|
}
|
||||||
|
|
||||||
logGenerate("generating source")
|
logger.Generatef("generating source")
|
||||||
|
|
||||||
if withAuth {
|
if withAuth {
|
||||||
gitRepository.Spec.SecretRef = &corev1.LocalObjectReference{
|
gitRepository.Spec.SecretRef = &corev1.LocalObjectReference{
|
||||||
@@ -235,18 +235,18 @@ func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
logAction("applying source")
|
logger.Actionf("applying source")
|
||||||
if err := upsertGitRepository(ctx, kubeClient, gitRepository); err != nil {
|
if err := upsertGitRepository(ctx, kubeClient, gitRepository); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
logWaiting("waiting for git sync")
|
logger.Waitingf("waiting for git sync")
|
||||||
if err := wait.PollImmediate(pollInterval, timeout,
|
if err := wait.PollImmediate(pollInterval, timeout,
|
||||||
isGitRepositoryReady(ctx, kubeClient, name, namespace)); err != nil {
|
isGitRepositoryReady(ctx, kubeClient, name, namespace)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
logSuccess("git sync completed")
|
logger.Successf("git sync completed")
|
||||||
|
|
||||||
namespacedName := types.NamespacedName{
|
namespacedName := types.NamespacedName{
|
||||||
Namespace: namespace,
|
Namespace: namespace,
|
||||||
@@ -258,7 +258,7 @@ func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if gitRepository.Status.Artifact != nil {
|
if gitRepository.Status.Artifact != nil {
|
||||||
logSuccess("fetched revision: %s", gitRepository.Status.Artifact.Revision)
|
logger.Successf("fetched revision: %s", gitRepository.Status.Artifact.Revision)
|
||||||
} else {
|
} else {
|
||||||
return fmt.Errorf("git sync failed, artifact not found")
|
return fmt.Errorf("git sync failed, artifact not found")
|
||||||
}
|
}
|
||||||
@@ -336,7 +336,7 @@ func upsertGitRepository(ctx context.Context, kubeClient client.Client, gitRepos
|
|||||||
if err := kubeClient.Create(ctx, &gitRepository); err != nil {
|
if err := kubeClient.Create(ctx, &gitRepository); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
logSuccess("source created")
|
logger.Successf("source created")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -348,7 +348,7 @@ func upsertGitRepository(ctx context.Context, kubeClient client.Client, gitRepos
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
logSuccess("source updated")
|
logger.Successf("source updated")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,8 @@ import (
|
|||||||
|
|
||||||
var deleteCmd = &cobra.Command{
|
var deleteCmd = &cobra.Command{
|
||||||
Use: "delete",
|
Use: "delete",
|
||||||
Short: "Delete commands",
|
Short: "Delete sources and resources",
|
||||||
|
Long: "The delete sub-commands delete sources and resources.",
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|||||||
@@ -29,7 +29,8 @@ import (
|
|||||||
var deleteKsCmd = &cobra.Command{
|
var deleteKsCmd = &cobra.Command{
|
||||||
Use: "kustomization [name]",
|
Use: "kustomization [name]",
|
||||||
Aliases: []string{"ks"},
|
Aliases: []string{"ks"},
|
||||||
Short: "Delete kustomization",
|
Short: "Delete a Kustomization resource",
|
||||||
|
Long: "The delete kustomization command deletes the given Kustomization from the cluster.",
|
||||||
RunE: deleteKsCmdRun,
|
RunE: deleteKsCmdRun,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,7 +65,7 @@ func deleteKsCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
|
|
||||||
if !deleteSilent {
|
if !deleteSilent {
|
||||||
if !kustomization.Spec.Suspend {
|
if !kustomization.Spec.Suspend {
|
||||||
logWaiting("This action will remove the Kubernetes objects previously applied by the %s kustomization!", name)
|
logger.Waitingf("This action will remove the Kubernetes objects previously applied by the %s kustomization!", name)
|
||||||
}
|
}
|
||||||
prompt := promptui.Prompt{
|
prompt := promptui.Prompt{
|
||||||
Label: "Are you sure you want to delete this kustomization",
|
Label: "Are you sure you want to delete this kustomization",
|
||||||
@@ -75,12 +76,12 @@ func deleteKsCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
logAction("deleting kustomization %s in %s namespace", name, namespace)
|
logger.Actionf("deleting kustomization %s in %s namespace", name, namespace)
|
||||||
err = kubeClient.Delete(ctx, &kustomization)
|
err = kubeClient.Delete(ctx, &kustomization)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
logSuccess("kustomization deleted")
|
logger.Successf("kustomization deleted")
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,8 @@ import (
|
|||||||
|
|
||||||
var deleteSourceCmd = &cobra.Command{
|
var deleteSourceCmd = &cobra.Command{
|
||||||
Use: "source",
|
Use: "source",
|
||||||
Short: "Delete sources commands",
|
Short: "Delete sources",
|
||||||
|
Long: "The delete source sub-commands delete sources.",
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|||||||
@@ -28,7 +28,8 @@ import (
|
|||||||
|
|
||||||
var deleteSourceGitCmd = &cobra.Command{
|
var deleteSourceGitCmd = &cobra.Command{
|
||||||
Use: "git [name]",
|
Use: "git [name]",
|
||||||
Short: "Delete git source",
|
Short: "Delete a GitRepository source",
|
||||||
|
Long: "The delete source git command deletes the given GitRepository from the cluster.",
|
||||||
RunE: deleteSourceGitCmdRun,
|
RunE: deleteSourceGitCmdRun,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,12 +72,12 @@ func deleteSourceGitCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
logAction("deleting source %s in %s namespace", name, namespace)
|
logger.Actionf("deleting source %s in %s namespace", name, namespace)
|
||||||
err = kubeClient.Delete(ctx, &git)
|
err = kubeClient.Delete(ctx, &git)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
logSuccess("source deleted")
|
logger.Successf("source deleted")
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,8 @@ import (
|
|||||||
|
|
||||||
var exportCmd = &cobra.Command{
|
var exportCmd = &cobra.Command{
|
||||||
Use: "export",
|
Use: "export",
|
||||||
Short: "Export commands",
|
Short: "Export resources in YAML format",
|
||||||
|
Long: "The export sub-commands export resources in YAML format.",
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|||||||
@@ -31,11 +31,12 @@ import (
|
|||||||
var exportKsCmd = &cobra.Command{
|
var exportKsCmd = &cobra.Command{
|
||||||
Use: "kustomization [name]",
|
Use: "kustomization [name]",
|
||||||
Aliases: []string{"ks"},
|
Aliases: []string{"ks"},
|
||||||
Short: "Export kustomization in YAML format",
|
Short: "Export Kustomization resources in YAML format",
|
||||||
Example: ` # Export all kustomizations
|
Long: "The export kustomization command exports one or all Kustomization resources in YAML format.",
|
||||||
|
Example: ` # Export all Kustomization resources
|
||||||
export kustomization --all > kustomizations.yaml
|
export kustomization --all > kustomizations.yaml
|
||||||
|
|
||||||
# Export a kustomization
|
# Export a Kustomization
|
||||||
export kustomization my-app > kustomization.yaml
|
export kustomization my-app > kustomization.yaml
|
||||||
`,
|
`,
|
||||||
RunE: exportKsCmdRun,
|
RunE: exportKsCmdRun,
|
||||||
@@ -66,7 +67,7 @@ func exportKsCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(list.Items) == 0 {
|
if len(list.Items) == 0 {
|
||||||
logFailure("no kustomizations found in %s namespace", namespace)
|
logger.Failuref("no kustomizations found in %s namespace", namespace)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,8 @@ import (
|
|||||||
|
|
||||||
var exportSourceCmd = &cobra.Command{
|
var exportSourceCmd = &cobra.Command{
|
||||||
Use: "source",
|
Use: "source",
|
||||||
Short: "Export source commands",
|
Short: "Export sources",
|
||||||
|
Long: "The export source sub-commands export sources in YAML format.",
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|||||||
@@ -31,11 +31,12 @@ import (
|
|||||||
|
|
||||||
var exportSourceGitCmd = &cobra.Command{
|
var exportSourceGitCmd = &cobra.Command{
|
||||||
Use: "git [name]",
|
Use: "git [name]",
|
||||||
Short: "Export git sources in YAML format",
|
Short: "Export GitRepository sources in YAML format",
|
||||||
Example: ` # Export all git sources
|
Long: "The export source git command exports on or all GitRepository sources in YAML format.",
|
||||||
|
Example: ` # Export all GitRepository sources
|
||||||
export source git --all > sources.yaml
|
export source git --all > sources.yaml
|
||||||
|
|
||||||
# Export a git source including the SSH keys or basic auth credentials
|
# Export a GitRepository source including the SSH key pair or basic auth credentials
|
||||||
export source git my-private-repo --with-credentials > source.yaml
|
export source git my-private-repo --with-credentials > source.yaml
|
||||||
`,
|
`,
|
||||||
RunE: exportSourceGitCmdRun,
|
RunE: exportSourceGitCmdRun,
|
||||||
@@ -66,7 +67,7 @@ func exportSourceGitCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(list.Items) == 0 {
|
if len(list.Items) == 0 {
|
||||||
logFailure("no source found in %s namespace", namespace)
|
logger.Failuref("no source found in %s namespace", namespace)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"crypto/elliptic"
|
"crypto/elliptic"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
@@ -124,5 +125,6 @@ func ecdsaCurves() []string {
|
|||||||
for k := range supportedECDSACurves {
|
for k := range supportedECDSACurves {
|
||||||
keys = append(keys, k)
|
keys = append(keys, k)
|
||||||
}
|
}
|
||||||
|
sort.Strings(keys)
|
||||||
return keys
|
return keys
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,8 @@ import (
|
|||||||
|
|
||||||
var getCmd = &cobra.Command{
|
var getCmd = &cobra.Command{
|
||||||
Use: "get",
|
Use: "get",
|
||||||
Short: "Get commands",
|
Short: "Get sources and resources",
|
||||||
|
Long: "The get sub-commands print the statuses of sources and resources.",
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|||||||
@@ -28,10 +28,9 @@ import (
|
|||||||
var getKsCmd = &cobra.Command{
|
var getKsCmd = &cobra.Command{
|
||||||
Use: "kustomizations",
|
Use: "kustomizations",
|
||||||
Aliases: []string{"ks"},
|
Aliases: []string{"ks"},
|
||||||
Short: "Get kustomizations status",
|
Short: "Get Kustomization source statuses",
|
||||||
Long: `
|
Long: "The get kustomizations command prints the statuses of the resources.",
|
||||||
The get kustomizations command prints the status of the resources.`,
|
RunE: getKsCmdRun,
|
||||||
RunE: getKsCmdRun,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -54,13 +53,13 @@ func getKsCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(list.Items) == 0 {
|
if len(list.Items) == 0 {
|
||||||
logFailure("no kustomizations found in %s namespace", namespace)
|
logger.Failuref("no kustomizations found in %s namespace", namespace)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, kustomization := range list.Items {
|
for _, kustomization := range list.Items {
|
||||||
if kustomization.Spec.Suspend {
|
if kustomization.Spec.Suspend {
|
||||||
logSuccess("%s is suspended", kustomization.GetName())
|
logger.Successf("%s is suspended", kustomization.GetName())
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
isInitialized := false
|
isInitialized := false
|
||||||
@@ -68,19 +67,19 @@ func getKsCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
if condition.Type == kustomizev1.ReadyCondition {
|
if condition.Type == kustomizev1.ReadyCondition {
|
||||||
if condition.Status != corev1.ConditionFalse {
|
if condition.Status != corev1.ConditionFalse {
|
||||||
if kustomization.Status.LastAppliedRevision != "" {
|
if kustomization.Status.LastAppliedRevision != "" {
|
||||||
logSuccess("%s last applied revision %s", kustomization.GetName(), kustomization.Status.LastAppliedRevision)
|
logger.Successf("%s last applied revision %s", kustomization.GetName(), kustomization.Status.LastAppliedRevision)
|
||||||
} else {
|
} else {
|
||||||
logSuccess("%s reconciling", kustomization.GetName())
|
logger.Successf("%s reconciling", kustomization.GetName())
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
logFailure("%s %s", kustomization.GetName(), condition.Message)
|
logger.Failuref("%s %s", kustomization.GetName(), condition.Message)
|
||||||
}
|
}
|
||||||
isInitialized = true
|
isInitialized = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !isInitialized {
|
if !isInitialized {
|
||||||
logFailure("%s is not ready", kustomization.GetName())
|
logger.Failuref("%s is not ready", kustomization.GetName())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -22,7 +22,8 @@ import (
|
|||||||
|
|
||||||
var getSourceCmd = &cobra.Command{
|
var getSourceCmd = &cobra.Command{
|
||||||
Use: "sources",
|
Use: "sources",
|
||||||
Short: "Get sources commands",
|
Short: "Get source statuses",
|
||||||
|
Long: "The get source sub-commands print the statuses of the sources.",
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|||||||
@@ -27,10 +27,9 @@ import (
|
|||||||
|
|
||||||
var getSourceGitCmd = &cobra.Command{
|
var getSourceGitCmd = &cobra.Command{
|
||||||
Use: "git",
|
Use: "git",
|
||||||
Short: "Get git sources status",
|
Short: "Get GitRepository source statuses",
|
||||||
Long: `
|
Long: "The get sources git command prints the status of the GitRepository sources.",
|
||||||
The get sources command prints the status of the git resources.`,
|
RunE: getSourceGitCmdRun,
|
||||||
RunE: getSourceGitCmdRun,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -53,7 +52,7 @@ func getSourceGitCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(list.Items) == 0 {
|
if len(list.Items) == 0 {
|
||||||
logFailure("no sources found in %s namespace", namespace)
|
logger.Failuref("no sources found in %s namespace", namespace)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,16 +61,16 @@ func getSourceGitCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
for _, condition := range source.Status.Conditions {
|
for _, condition := range source.Status.Conditions {
|
||||||
if condition.Type == sourcev1.ReadyCondition {
|
if condition.Type == sourcev1.ReadyCondition {
|
||||||
if condition.Status != corev1.ConditionFalse {
|
if condition.Status != corev1.ConditionFalse {
|
||||||
logSuccess("%s last fetched revision: %s", source.GetName(), source.Status.Artifact.Revision)
|
logger.Successf("%s last fetched revision: %s", source.GetName(), source.Status.Artifact.Revision)
|
||||||
} else {
|
} else {
|
||||||
logFailure("%s %s", source.GetName(), condition.Message)
|
logger.Failuref("%s %s", source.GetName(), condition.Message)
|
||||||
}
|
}
|
||||||
isInitialized = true
|
isInitialized = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !isInitialized {
|
if !isInitialized {
|
||||||
logFailure("%s is not ready", source.GetName())
|
logger.Failuref("%s is not ready", source.GetName())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -33,8 +33,7 @@ import (
|
|||||||
var installCmd = &cobra.Command{
|
var installCmd = &cobra.Command{
|
||||||
Use: "install",
|
Use: "install",
|
||||||
Short: "Install the toolkit components",
|
Short: "Install the toolkit components",
|
||||||
Long: `
|
Long: `The install command deploys the toolkit components in the specified namespace.
|
||||||
The install command deploys the toolkit components in the specified namespace.
|
|
||||||
If a previous version is installed, then an in-place upgrade will be performed.`,
|
If a previous version is installed, then an in-place upgrade will be performed.`,
|
||||||
Example: ` # Install the latest version in the gitops-systems namespace
|
Example: ` # Install the latest version in the gitops-systems namespace
|
||||||
install --version=master --namespace=gitops-systems
|
install --version=master --namespace=gitops-systems
|
||||||
@@ -82,7 +81,7 @@ func installCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
defer os.RemoveAll(tmpDir)
|
defer os.RemoveAll(tmpDir)
|
||||||
|
|
||||||
logGenerate("generating manifests")
|
logger.Generatef("generating manifests")
|
||||||
if kustomizePath == "" {
|
if kustomizePath == "" {
|
||||||
err = genInstallManifests(installVersion, namespace, components, tmpDir)
|
err = genInstallManifests(installVersion, namespace, components, tmpDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -104,9 +103,9 @@ func installCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
fmt.Print(yaml)
|
fmt.Print(yaml)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
logSuccess("manifests build completed")
|
logger.Successf("manifests build completed")
|
||||||
|
|
||||||
logAction("installing components in %s namespace", namespace)
|
logger.Actionf("installing components in %s namespace", namespace)
|
||||||
applyOutput := ModeStderrOS
|
applyOutput := ModeStderrOS
|
||||||
if verbose {
|
if verbose {
|
||||||
applyOutput = ModeOS
|
applyOutput = ModeOS
|
||||||
@@ -122,24 +121,24 @@ func installCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if installDryRun {
|
if installDryRun {
|
||||||
logSuccess("install dry-run finished")
|
logger.Successf("install dry-run finished")
|
||||||
return nil
|
return nil
|
||||||
} else {
|
} else {
|
||||||
logSuccess("install completed")
|
logger.Successf("install completed")
|
||||||
}
|
}
|
||||||
|
|
||||||
logWaiting("verifying installation")
|
logger.Waitingf("verifying installation")
|
||||||
for _, deployment := range components {
|
for _, deployment := range components {
|
||||||
command = fmt.Sprintf("kubectl -n %s rollout status deployment %s --timeout=%s",
|
command = fmt.Sprintf("kubectl -n %s rollout status deployment %s --timeout=%s",
|
||||||
namespace, deployment, timeout.String())
|
namespace, deployment, timeout.String())
|
||||||
if _, err := utils.execCommand(ctx, applyOutput, command); err != nil {
|
if _, err := utils.execCommand(ctx, applyOutput, command); err != nil {
|
||||||
return fmt.Errorf("install failed")
|
return fmt.Errorf("install failed")
|
||||||
} else {
|
} else {
|
||||||
logSuccess("%s ready", deployment)
|
logger.Successf("%s ready", deployment)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
logSuccess("install finished")
|
logger.Successf("install finished")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,22 +18,24 @@ package main
|
|||||||
|
|
||||||
import "fmt"
|
import "fmt"
|
||||||
|
|
||||||
func logAction(format string, a ...interface{}) {
|
type printLogger struct{}
|
||||||
|
|
||||||
|
func (l printLogger) Actionf(format string, a ...interface{}) {
|
||||||
fmt.Println(`►`, fmt.Sprintf(format, a...))
|
fmt.Println(`►`, fmt.Sprintf(format, a...))
|
||||||
}
|
}
|
||||||
|
|
||||||
func logGenerate(format string, a ...interface{}) {
|
func (l printLogger) Generatef(format string, a ...interface{}) {
|
||||||
fmt.Println(`✚`, fmt.Sprintf(format, a...))
|
fmt.Println(`✚`, fmt.Sprintf(format, a...))
|
||||||
}
|
}
|
||||||
|
|
||||||
func logWaiting(format string, a ...interface{}) {
|
func (l printLogger) Waitingf(format string, a ...interface{}) {
|
||||||
fmt.Println(`◎`, fmt.Sprintf(format, a...))
|
fmt.Println(`◎`, fmt.Sprintf(format, a...))
|
||||||
}
|
}
|
||||||
|
|
||||||
func logSuccess(format string, a ...interface{}) {
|
func (l printLogger) Successf(format string, a ...interface{}) {
|
||||||
fmt.Println(`✔`, fmt.Sprintf(format, a...))
|
fmt.Println(`✔`, fmt.Sprintf(format, a...))
|
||||||
}
|
}
|
||||||
|
|
||||||
func logFailure(format string, a ...interface{}) {
|
func (l printLogger) Failuref(format string, a ...interface{}) {
|
||||||
fmt.Println(`✗`, fmt.Sprintf(format, a...))
|
fmt.Println(`✗`, fmt.Sprintf(format, a...))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ import (
|
|||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/cobra/doc"
|
"github.com/spf13/cobra/doc"
|
||||||
_ "k8s.io/client-go/plugin/pkg/client/auth"
|
_ "k8s.io/client-go/plugin/pkg/client/auth"
|
||||||
|
|
||||||
|
tklog "github.com/fluxcd/toolkit/pkg/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
var VERSION = "0.0.0-dev.0"
|
var VERSION = "0.0.0-dev.0"
|
||||||
@@ -48,16 +50,16 @@ var rootCmd = &cobra.Command{
|
|||||||
--branch=master \
|
--branch=master \
|
||||||
--interval=3m
|
--interval=3m
|
||||||
|
|
||||||
# List git sources and their status
|
# List GitRepository sources and their status
|
||||||
tk get sources git
|
tk get sources git
|
||||||
|
|
||||||
# Trigger a git sync
|
# Trigger a GitRepository source sync
|
||||||
tk sync source git webapp-latest
|
tk sync source git webapp-latest
|
||||||
|
|
||||||
# Export git sources in YAML format
|
# Export GitRepository sources in YAML format
|
||||||
tk export source git --all > sources.yaml
|
tk export source git --all > sources.yaml
|
||||||
|
|
||||||
# Create a kustomization for deploying a series of microservices
|
# Create a Kustomization for deploying a series of microservices
|
||||||
tk create kustomization webapp-dev \
|
tk create kustomization webapp-dev \
|
||||||
--source=webapp-latest \
|
--source=webapp-latest \
|
||||||
--path="./deploy/webapp/" \
|
--path="./deploy/webapp/" \
|
||||||
@@ -68,22 +70,22 @@ var rootCmd = &cobra.Command{
|
|||||||
--health-check="Deployment/frontend.webapp" \
|
--health-check="Deployment/frontend.webapp" \
|
||||||
--health-check-timeout=2m
|
--health-check-timeout=2m
|
||||||
|
|
||||||
# Trigger a git sync and apply changes if any
|
# Trigger a git sync of the Kustomization's source and apply changes
|
||||||
tk sync kustomization webapp-dev --with-source
|
tk sync kustomization webapp-dev --with-source
|
||||||
|
|
||||||
# Suspend a kustomization reconciliation
|
# Suspend a Kustomization reconciliation
|
||||||
tk suspend kustomization webapp-dev
|
tk suspend kustomization webapp-dev
|
||||||
|
|
||||||
# Export kustomizations in YAML format
|
# Export Kustomizations in YAML format
|
||||||
tk export kustomization --all > kustomizations.yaml
|
tk export kustomization --all > kustomizations.yaml
|
||||||
|
|
||||||
# Resume a kustomization reconciliation
|
# Resume a Kustomization reconciliation
|
||||||
tk resume kustomization webapp-dev
|
tk resume kustomization webapp-dev
|
||||||
|
|
||||||
# Delete a kustomization
|
# Delete a Kustomization
|
||||||
tk delete kustomization webapp-dev
|
tk delete kustomization webapp-dev
|
||||||
|
|
||||||
# Delete a git source
|
# Delete a GitRepository source
|
||||||
tk delete source git webapp-latest
|
tk delete source git webapp-latest
|
||||||
|
|
||||||
# Uninstall the toolkit and delete CRDs
|
# Uninstall the toolkit and delete CRDs
|
||||||
@@ -98,7 +100,8 @@ var (
|
|||||||
verbose bool
|
verbose bool
|
||||||
components []string
|
components []string
|
||||||
utils Utils
|
utils Utils
|
||||||
pollInterval = 2 * time.Second
|
pollInterval = 2 * time.Second
|
||||||
|
logger tklog.Logger = printLogger{}
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -109,7 +112,7 @@ func init() {
|
|||||||
rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "", false,
|
rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "", false,
|
||||||
"print generated objects")
|
"print generated objects")
|
||||||
rootCmd.PersistentFlags().StringSliceVar(&components, "components",
|
rootCmd.PersistentFlags().StringSliceVar(&components, "components",
|
||||||
[]string{"source-controller", "kustomize-controller"},
|
[]string{"source-controller", "kustomize-controller", "notification-controller"},
|
||||||
"list of components, accepts comma-separated values")
|
"list of components, accepts comma-separated values")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,7 +121,7 @@ func main() {
|
|||||||
generateDocs()
|
generateDocs()
|
||||||
kubeconfigFlag()
|
kubeconfigFlag()
|
||||||
if err := rootCmd.Execute(); err != nil {
|
if err := rootCmd.Execute(); err != nil {
|
||||||
logFailure("%v", err)
|
logger.Failuref("%v", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,8 @@ import (
|
|||||||
|
|
||||||
var resumeCmd = &cobra.Command{
|
var resumeCmd = &cobra.Command{
|
||||||
Use: "resume",
|
Use: "resume",
|
||||||
Short: "Resume commands",
|
Short: "Resume suspended resources",
|
||||||
|
Long: "The resume sub-commands resume a suspended resource.",
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|||||||
@@ -32,9 +32,10 @@ import (
|
|||||||
var resumeKsCmd = &cobra.Command{
|
var resumeKsCmd = &cobra.Command{
|
||||||
Use: "kustomization [name]",
|
Use: "kustomization [name]",
|
||||||
Aliases: []string{"ks"},
|
Aliases: []string{"ks"},
|
||||||
Short: "Resume kustomization",
|
Short: "Resume a suspended Kustomization",
|
||||||
Long: "The resume command marks a previously suspended Kustomization resource for reconciliation and waits for it to finish the apply.",
|
Long: `The resume command marks a previously suspended Kustomization resource for reconciliation and waits for it to
|
||||||
RunE: resumeKsCmdRun,
|
finish the apply.`,
|
||||||
|
RunE: resumeKsCmdRun,
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -65,20 +66,20 @@ func resumeKsCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
logAction("resuming kustomization %s in %s namespace", name, namespace)
|
logger.Actionf("resuming kustomization %s in %s namespace", name, namespace)
|
||||||
kustomization.Spec.Suspend = false
|
kustomization.Spec.Suspend = false
|
||||||
if err := kubeClient.Update(ctx, &kustomization); err != nil {
|
if err := kubeClient.Update(ctx, &kustomization); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
logSuccess("kustomization resumed")
|
logger.Successf("kustomization resumed")
|
||||||
|
|
||||||
logWaiting("waiting for kustomization sync")
|
logger.Waitingf("waiting for kustomization sync")
|
||||||
if err := wait.PollImmediate(pollInterval, timeout,
|
if err := wait.PollImmediate(pollInterval, timeout,
|
||||||
isKustomizationResumed(ctx, kubeClient, name, namespace)); err != nil {
|
isKustomizationResumed(ctx, kubeClient, name, namespace)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
logSuccess("kustomization sync completed")
|
logger.Successf("kustomization sync completed")
|
||||||
|
|
||||||
err = kubeClient.Get(ctx, namespacedName, &kustomization)
|
err = kubeClient.Get(ctx, namespacedName, &kustomization)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -86,7 +87,7 @@ func resumeKsCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if kustomization.Status.LastAppliedRevision != "" {
|
if kustomization.Status.LastAppliedRevision != "" {
|
||||||
logSuccess("applied revision %s", kustomization.Status.LastAppliedRevision)
|
logger.Successf("applied revision %s", kustomization.Status.LastAppliedRevision)
|
||||||
} else {
|
} else {
|
||||||
return fmt.Errorf("kustomization sync failed")
|
return fmt.Errorf("kustomization sync failed")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,8 @@ import (
|
|||||||
|
|
||||||
var suspendCmd = &cobra.Command{
|
var suspendCmd = &cobra.Command{
|
||||||
Use: "suspend",
|
Use: "suspend",
|
||||||
Short: "Suspend commands",
|
Short: "Suspend resources",
|
||||||
|
Long: "The suspend sub-commands suspend the reconciliation of a resource.",
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ import (
|
|||||||
var suspendKsCmd = &cobra.Command{
|
var suspendKsCmd = &cobra.Command{
|
||||||
Use: "kustomization [name]",
|
Use: "kustomization [name]",
|
||||||
Aliases: []string{"ks"},
|
Aliases: []string{"ks"},
|
||||||
Short: "Suspend kustomization",
|
Short: "Suspend reconciliation of Kustomization",
|
||||||
Long: "The suspend command disables the reconciliation of a Kustomization resource.",
|
Long: "The suspend command disables the reconciliation of a Kustomization resource.",
|
||||||
RunE: suspendKsCmdRun,
|
RunE: suspendKsCmdRun,
|
||||||
}
|
}
|
||||||
@@ -60,12 +60,12 @@ func suspendKsCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
logAction("suspending kustomization %s in %s namespace", name, namespace)
|
logger.Actionf("suspending kustomization %s in %s namespace", name, namespace)
|
||||||
kustomization.Spec.Suspend = true
|
kustomization.Spec.Suspend = true
|
||||||
if err := kubeClient.Update(ctx, &kustomization); err != nil {
|
if err := kubeClient.Update(ctx, &kustomization); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
logSuccess("kustomization suspended")
|
logger.Successf("kustomization suspended")
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,8 @@ import (
|
|||||||
|
|
||||||
var syncCmd = &cobra.Command{
|
var syncCmd = &cobra.Command{
|
||||||
Use: "sync",
|
Use: "sync",
|
||||||
Short: "Synchronize commands",
|
Short: "Synchronize sources and resources",
|
||||||
|
Long: "The sync sub-commands trigger a reconciliation of sources and resources.",
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|||||||
@@ -30,13 +30,13 @@ import (
|
|||||||
var syncKsCmd = &cobra.Command{
|
var syncKsCmd = &cobra.Command{
|
||||||
Use: "kustomization [name]",
|
Use: "kustomization [name]",
|
||||||
Aliases: []string{"ks"},
|
Aliases: []string{"ks"},
|
||||||
Short: "Synchronize kustomization",
|
Short: "Synchronize a Kustomization resource",
|
||||||
Long: `
|
Long: `
|
||||||
The sync kustomization command triggers a reconciliation of a Kustomization resource and waits for it to finish.`,
|
The sync kustomization command triggers a reconciliation of a Kustomization resource and waits for it to finish.`,
|
||||||
Example: ` # Trigger a kustomization apply outside of the reconciliation interval
|
Example: ` # Trigger a Kustomization apply outside of the reconciliation interval
|
||||||
sync kustomization podinfo
|
sync kustomization podinfo
|
||||||
|
|
||||||
# Trigger a git sync of the kustomization source and apply changes
|
# Trigger a sync of the Kustomization's source and apply changes
|
||||||
sync kustomization podinfo --with-source
|
sync kustomization podinfo --with-source
|
||||||
`,
|
`,
|
||||||
RunE: syncKsCmdRun,
|
RunE: syncKsCmdRun,
|
||||||
@@ -83,7 +83,7 @@ func syncKsCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
logAction("annotating kustomization %s in %s namespace", name, namespace)
|
logger.Actionf("annotating kustomization %s in %s namespace", name, namespace)
|
||||||
if kustomization.Annotations == nil {
|
if kustomization.Annotations == nil {
|
||||||
kustomization.Annotations = map[string]string{
|
kustomization.Annotations = map[string]string{
|
||||||
kustomizev1.SyncAtAnnotation: time.Now().String(),
|
kustomizev1.SyncAtAnnotation: time.Now().String(),
|
||||||
@@ -94,16 +94,16 @@ func syncKsCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
if err := kubeClient.Update(ctx, &kustomization); err != nil {
|
if err := kubeClient.Update(ctx, &kustomization); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
logSuccess("kustomization annotated")
|
logger.Successf("kustomization annotated")
|
||||||
}
|
}
|
||||||
|
|
||||||
logWaiting("waiting for kustomization sync")
|
logger.Waitingf("waiting for kustomization sync")
|
||||||
if err := wait.PollImmediate(pollInterval, timeout,
|
if err := wait.PollImmediate(pollInterval, timeout,
|
||||||
isKustomizationReady(ctx, kubeClient, name, namespace)); err != nil {
|
isKustomizationReady(ctx, kubeClient, name, namespace)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
logSuccess("kustomization sync completed")
|
logger.Successf("kustomization sync completed")
|
||||||
|
|
||||||
err = kubeClient.Get(ctx, namespacedName, &kustomization)
|
err = kubeClient.Get(ctx, namespacedName, &kustomization)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -111,7 +111,7 @@ func syncKsCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if kustomization.Status.LastAppliedRevision != "" {
|
if kustomization.Status.LastAppliedRevision != "" {
|
||||||
logSuccess("applied revision %s", kustomization.Status.LastAppliedRevision)
|
logger.Successf("applied revision %s", kustomization.Status.LastAppliedRevision)
|
||||||
} else {
|
} else {
|
||||||
return fmt.Errorf("kustomization sync failed")
|
return fmt.Errorf("kustomization sync failed")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,8 @@ import (
|
|||||||
|
|
||||||
var syncSourceCmd = &cobra.Command{
|
var syncSourceCmd = &cobra.Command{
|
||||||
Use: "source",
|
Use: "source",
|
||||||
Short: "Synchronize source commands",
|
Short: "Synchronize sources",
|
||||||
|
Long: "The sync source sub-commands trigger a reconciliation of sources.",
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|||||||
@@ -28,9 +28,8 @@ import (
|
|||||||
|
|
||||||
var syncSourceGitCmd = &cobra.Command{
|
var syncSourceGitCmd = &cobra.Command{
|
||||||
Use: "git [name]",
|
Use: "git [name]",
|
||||||
Short: "Synchronize git source",
|
Short: "Synchronize a GitRepository source",
|
||||||
Long: `
|
Long: `The sync source command triggers a reconciliation of a GitRepository resource and waits for it to finish.`,
|
||||||
The sync source command triggers a reconciliation of a GitRepository resource and waits for it to finish.`,
|
|
||||||
Example: ` # Trigger a git pull for an existing source
|
Example: ` # Trigger a git pull for an existing source
|
||||||
sync source git podinfo
|
sync source git podinfo
|
||||||
`,
|
`,
|
||||||
@@ -60,7 +59,7 @@ func syncSourceGitCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
Name: name,
|
Name: name,
|
||||||
}
|
}
|
||||||
|
|
||||||
logAction("annotating source %s in %s namespace", name, namespace)
|
logger.Actionf("annotating source %s in %s namespace", name, namespace)
|
||||||
var gitRepository sourcev1.GitRepository
|
var gitRepository sourcev1.GitRepository
|
||||||
err = kubeClient.Get(ctx, namespacedName, &gitRepository)
|
err = kubeClient.Get(ctx, namespacedName, &gitRepository)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -77,15 +76,15 @@ func syncSourceGitCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
if err := kubeClient.Update(ctx, &gitRepository); err != nil {
|
if err := kubeClient.Update(ctx, &gitRepository); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
logSuccess("source annotated")
|
logger.Successf("source annotated")
|
||||||
|
|
||||||
logWaiting("waiting for git sync")
|
logger.Waitingf("waiting for git sync")
|
||||||
if err := wait.PollImmediate(pollInterval, timeout,
|
if err := wait.PollImmediate(pollInterval, timeout,
|
||||||
isGitRepositoryReady(ctx, kubeClient, name, namespace)); err != nil {
|
isGitRepositoryReady(ctx, kubeClient, name, namespace)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
logSuccess("git sync completed")
|
logger.Successf("git sync completed")
|
||||||
|
|
||||||
err = kubeClient.Get(ctx, namespacedName, &gitRepository)
|
err = kubeClient.Get(ctx, namespacedName, &gitRepository)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -93,7 +92,7 @@ func syncSourceGitCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if gitRepository.Status.Artifact != nil {
|
if gitRepository.Status.Artifact != nil {
|
||||||
logSuccess("fetched revision: %s", gitRepository.Status.Artifact.Revision)
|
logger.Successf("fetched revision %s", gitRepository.Status.Artifact.Revision)
|
||||||
} else {
|
} else {
|
||||||
return fmt.Errorf("git sync failed, artifact not found")
|
return fmt.Errorf("git sync failed, artifact not found")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,9 +28,7 @@ import (
|
|||||||
var uninstallCmd = &cobra.Command{
|
var uninstallCmd = &cobra.Command{
|
||||||
Use: "uninstall",
|
Use: "uninstall",
|
||||||
Short: "Uninstall the toolkit components",
|
Short: "Uninstall the toolkit components",
|
||||||
Long: `
|
Long: "The uninstall command removes the namespace, cluster roles, cluster role bindings and CRDs from the cluster.",
|
||||||
The uninstall command removes the namespace, cluster roles,
|
|
||||||
cluster role bindings and CRDs.`,
|
|
||||||
Example: ` # Dry-run uninstall of all components
|
Example: ` # Dry-run uninstall of all components
|
||||||
uninstall --dry-run --namespace=gitops-system
|
uninstall --dry-run --namespace=gitops-system
|
||||||
|
|
||||||
@@ -49,7 +47,7 @@ var (
|
|||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
uninstallCmd.Flags().BoolVarP(&uninstallKustomizations, "kustomizations", "", false,
|
uninstallCmd.Flags().BoolVarP(&uninstallKustomizations, "kustomizations", "", false,
|
||||||
"removes all kustomizations previously installed")
|
"removes all Kustomizations previously installed")
|
||||||
uninstallCmd.Flags().BoolVarP(&uninstallCRDs, "crds", "", false,
|
uninstallCmd.Flags().BoolVarP(&uninstallCRDs, "crds", "", false,
|
||||||
"removes all CRDs previously installed")
|
"removes all CRDs previously installed")
|
||||||
uninstallCmd.Flags().BoolVarP(&uninstallDryRun, "dry-run", "", false,
|
uninstallCmd.Flags().BoolVarP(&uninstallDryRun, "dry-run", "", false,
|
||||||
@@ -78,7 +76,7 @@ func uninstallCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if uninstallKustomizations {
|
if uninstallKustomizations {
|
||||||
logAction("uninstalling kustomizations")
|
logger.Actionf("uninstalling kustomizations")
|
||||||
command := fmt.Sprintf("kubectl -n %s delete kustomizations --all --timeout=%s %s",
|
command := fmt.Sprintf("kubectl -n %s delete kustomizations --all --timeout=%s %s",
|
||||||
namespace, timeout.String(), dryRun)
|
namespace, timeout.String(), dryRun)
|
||||||
if _, err := utils.execCommand(ctx, ModeOS, command); err != nil {
|
if _, err := utils.execCommand(ctx, ModeOS, command); err != nil {
|
||||||
@@ -87,7 +85,7 @@ func uninstallCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
|
|
||||||
// TODO: use the kustomizations snapshots to create a list of objects
|
// 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
|
// that are subject to deletion and wait for all of them to be terminated
|
||||||
logWaiting("waiting on GC")
|
logger.Waitingf("waiting on GC")
|
||||||
time.Sleep(30 * time.Second)
|
time.Sleep(30 * time.Second)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,13 +94,13 @@ func uninstallCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
kinds += ",crds"
|
kinds += ",crds"
|
||||||
}
|
}
|
||||||
|
|
||||||
logAction("uninstalling components")
|
logger.Actionf("uninstalling components")
|
||||||
command := fmt.Sprintf("kubectl delete %s -l app.kubernetes.io/instance=%s --timeout=%s %s",
|
command := fmt.Sprintf("kubectl delete %s -l app.kubernetes.io/instance=%s --timeout=%s %s",
|
||||||
kinds, namespace, timeout.String(), dryRun)
|
kinds, namespace, timeout.String(), dryRun)
|
||||||
if _, err := utils.execCommand(ctx, ModeOS, command); err != nil {
|
if _, err := utils.execCommand(ctx, ModeOS, command); err != nil {
|
||||||
return fmt.Errorf("uninstall failed")
|
return fmt.Errorf("uninstall failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
logSuccess("uninstall finished")
|
logger.Successf("uninstall finished")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
docs/_files/toolkit-icon.png
Normal file
BIN
docs/_files/toolkit-icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 56 KiB |
@@ -21,16 +21,16 @@ Command line utility for assembling Kubernetes CD pipelines the GitOps way.
|
|||||||
--branch=master \
|
--branch=master \
|
||||||
--interval=3m
|
--interval=3m
|
||||||
|
|
||||||
# List git sources and their status
|
# List GitRepository sources and their status
|
||||||
tk get sources git
|
tk get sources git
|
||||||
|
|
||||||
# Trigger a git sync
|
# Trigger a GitRepository source sync
|
||||||
tk sync source git webapp-latest
|
tk sync source git webapp-latest
|
||||||
|
|
||||||
# Export git sources in YAML format
|
# Export GitRepository sources in YAML format
|
||||||
tk export source git --all > sources.yaml
|
tk export source git --all > sources.yaml
|
||||||
|
|
||||||
# Create a kustomization for deploying a series of microservices
|
# Create a Kustomization for deploying a series of microservices
|
||||||
tk create kustomization webapp-dev \
|
tk create kustomization webapp-dev \
|
||||||
--source=webapp-latest \
|
--source=webapp-latest \
|
||||||
--path="./deploy/webapp/" \
|
--path="./deploy/webapp/" \
|
||||||
@@ -41,22 +41,22 @@ Command line utility for assembling Kubernetes CD pipelines the GitOps way.
|
|||||||
--health-check="Deployment/frontend.webapp" \
|
--health-check="Deployment/frontend.webapp" \
|
||||||
--health-check-timeout=2m
|
--health-check-timeout=2m
|
||||||
|
|
||||||
# Trigger a git sync and apply changes if any
|
# Trigger a git sync of the Kustomization's source and apply changes
|
||||||
tk sync kustomization webapp-dev --with-source
|
tk sync kustomization webapp-dev --with-source
|
||||||
|
|
||||||
# Suspend a kustomization reconciliation
|
# Suspend a Kustomization reconciliation
|
||||||
tk suspend kustomization webapp-dev
|
tk suspend kustomization webapp-dev
|
||||||
|
|
||||||
# Export kustomizations in YAML format
|
# Export Kustomizations in YAML format
|
||||||
tk export kustomization --all > kustomizations.yaml
|
tk export kustomization --all > kustomizations.yaml
|
||||||
|
|
||||||
# Resume a kustomization reconciliation
|
# Resume a Kustomization reconciliation
|
||||||
tk resume kustomization webapp-dev
|
tk resume kustomization webapp-dev
|
||||||
|
|
||||||
# Delete a kustomization
|
# Delete a Kustomization
|
||||||
tk delete kustomization webapp-dev
|
tk delete kustomization webapp-dev
|
||||||
|
|
||||||
# Delete a git source
|
# Delete a GitRepository source
|
||||||
tk delete source git webapp-latest
|
tk delete source git webapp-latest
|
||||||
|
|
||||||
# Uninstall the toolkit and delete CRDs
|
# Uninstall the toolkit and delete CRDs
|
||||||
@@ -67,7 +67,7 @@ Command line utility for assembling Kubernetes CD pipelines the GitOps way.
|
|||||||
### Options
|
### Options
|
||||||
|
|
||||||
```
|
```
|
||||||
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller])
|
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller,notification-controller])
|
||||||
-h, --help help for tk
|
-h, --help help for tk
|
||||||
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
||||||
--namespace string the namespace scope for this operation (default "gitops-system")
|
--namespace string the namespace scope for this operation (default "gitops-system")
|
||||||
@@ -77,16 +77,16 @@ Command line utility for assembling Kubernetes CD pipelines the GitOps way.
|
|||||||
|
|
||||||
### SEE ALSO
|
### SEE ALSO
|
||||||
|
|
||||||
* [tk bootstrap](tk_bootstrap.md) - Bootstrap commands
|
* [tk bootstrap](tk_bootstrap.md) - Bootstrap toolkit components
|
||||||
* [tk check](tk_check.md) - Check requirements and installation
|
* [tk check](tk_check.md) - Check requirements and installation
|
||||||
* [tk completion](tk_completion.md) - Generates bash completion scripts
|
* [tk completion](tk_completion.md) - Generates bash completion scripts
|
||||||
* [tk create](tk_create.md) - Create commands
|
* [tk create](tk_create.md) - Create or update sources and resources
|
||||||
* [tk delete](tk_delete.md) - Delete commands
|
* [tk delete](tk_delete.md) - Delete sources and resources
|
||||||
* [tk export](tk_export.md) - Export commands
|
* [tk export](tk_export.md) - Export resources in YAML format
|
||||||
* [tk get](tk_get.md) - Get commands
|
* [tk get](tk_get.md) - Get sources and resources
|
||||||
* [tk install](tk_install.md) - Install the toolkit components
|
* [tk install](tk_install.md) - Install the toolkit components
|
||||||
* [tk resume](tk_resume.md) - Resume commands
|
* [tk resume](tk_resume.md) - Resume suspended resources
|
||||||
* [tk suspend](tk_suspend.md) - Suspend commands
|
* [tk suspend](tk_suspend.md) - Suspend resources
|
||||||
* [tk sync](tk_sync.md) - Synchronize commands
|
* [tk sync](tk_sync.md) - Synchronize sources and resources
|
||||||
* [tk uninstall](tk_uninstall.md) - Uninstall the toolkit components
|
* [tk uninstall](tk_uninstall.md) - Uninstall the toolkit components
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
## tk bootstrap
|
## tk bootstrap
|
||||||
|
|
||||||
Bootstrap commands
|
Bootstrap toolkit components
|
||||||
|
|
||||||
### Synopsis
|
### Synopsis
|
||||||
|
|
||||||
Bootstrap commands
|
The bootstrap sub-commands bootstrap the toolkit components on the targeted Git provider.
|
||||||
|
|
||||||
### Options
|
### Options
|
||||||
|
|
||||||
@@ -16,7 +16,7 @@ Bootstrap commands
|
|||||||
### Options inherited from parent commands
|
### Options inherited from parent commands
|
||||||
|
|
||||||
```
|
```
|
||||||
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller])
|
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller,notification-controller])
|
||||||
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
||||||
--namespace string the namespace scope for this operation (default "gitops-system")
|
--namespace string the namespace scope for this operation (default "gitops-system")
|
||||||
--timeout duration timeout for this operation (default 5m0s)
|
--timeout duration timeout for this operation (default 5m0s)
|
||||||
@@ -26,6 +26,6 @@ Bootstrap commands
|
|||||||
### SEE ALSO
|
### SEE ALSO
|
||||||
|
|
||||||
* [tk](tk.md) - Command line utility for assembling Kubernetes CD pipelines
|
* [tk](tk.md) - Command line utility for assembling Kubernetes CD pipelines
|
||||||
* [tk bootstrap github](tk_bootstrap_github.md) - Bootstrap GitHub repository
|
* [tk bootstrap github](tk_bootstrap_github.md) - Bootstrap toolkit components in a GitHub repository
|
||||||
* [tk bootstrap gitlab](tk_bootstrap_gitlab.md) - Bootstrap GitLab repository
|
* [tk bootstrap gitlab](tk_bootstrap_gitlab.md) - Bootstrap toolkit components in a GitLab repository
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
## tk bootstrap github
|
## tk bootstrap github
|
||||||
|
|
||||||
Bootstrap GitHub repository
|
Bootstrap toolkit components in a GitHub repository
|
||||||
|
|
||||||
### Synopsis
|
### Synopsis
|
||||||
|
|
||||||
|
The bootstrap github command creates the GitHub repository if it doesn't exists and
|
||||||
The bootstrap command creates the GitHub repository if it doesn't exists and
|
|
||||||
commits the toolkit components manifests to the master branch.
|
commits the toolkit components manifests to the master branch.
|
||||||
Then it configure the target cluster to synchronize with the repository.
|
Then it configures the target cluster to synchronize with the repository.
|
||||||
If the toolkit components are present on the cluster,
|
If the toolkit components are present on the cluster,
|
||||||
the bootstrap command will perform an upgrade if needed.
|
the bootstrap command will perform an upgrade if needed.
|
||||||
|
|
||||||
@@ -55,7 +54,7 @@ tk bootstrap github [flags]
|
|||||||
### Options inherited from parent commands
|
### Options inherited from parent commands
|
||||||
|
|
||||||
```
|
```
|
||||||
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller])
|
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller,notification-controller])
|
||||||
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
||||||
--namespace string the namespace scope for this operation (default "gitops-system")
|
--namespace string the namespace scope for this operation (default "gitops-system")
|
||||||
--timeout duration timeout for this operation (default 5m0s)
|
--timeout duration timeout for this operation (default 5m0s)
|
||||||
@@ -65,5 +64,5 @@ tk bootstrap github [flags]
|
|||||||
|
|
||||||
### SEE ALSO
|
### SEE ALSO
|
||||||
|
|
||||||
* [tk bootstrap](tk_bootstrap.md) - Bootstrap commands
|
* [tk bootstrap](tk_bootstrap.md) - Bootstrap toolkit components
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
## tk bootstrap gitlab
|
## tk bootstrap gitlab
|
||||||
|
|
||||||
Bootstrap GitLab repository
|
Bootstrap toolkit components in a GitLab repository
|
||||||
|
|
||||||
### Synopsis
|
### Synopsis
|
||||||
|
|
||||||
|
The bootstrap gitlab command creates the GitLab repository if it doesn't exists and
|
||||||
The bootstrap command creates the GitLab repository if it doesn't exists and
|
|
||||||
commits the toolkit components manifests to the master branch.
|
commits the toolkit components manifests to the master branch.
|
||||||
Then it configure the target cluster to synchronize with the repository.
|
Then it configures the target cluster to synchronize with the repository.
|
||||||
If the toolkit components are present on the cluster,
|
If the toolkit components are present on the cluster,
|
||||||
the bootstrap command will perform an upgrade if needed.
|
the bootstrap command will perform an upgrade if needed.
|
||||||
|
|
||||||
@@ -51,7 +50,7 @@ tk bootstrap gitlab [flags]
|
|||||||
### Options inherited from parent commands
|
### Options inherited from parent commands
|
||||||
|
|
||||||
```
|
```
|
||||||
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller])
|
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller,notification-controller])
|
||||||
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
||||||
--namespace string the namespace scope for this operation (default "gitops-system")
|
--namespace string the namespace scope for this operation (default "gitops-system")
|
||||||
--timeout duration timeout for this operation (default 5m0s)
|
--timeout duration timeout for this operation (default 5m0s)
|
||||||
@@ -61,5 +60,5 @@ tk bootstrap gitlab [flags]
|
|||||||
|
|
||||||
### SEE ALSO
|
### SEE ALSO
|
||||||
|
|
||||||
* [tk bootstrap](tk_bootstrap.md) - Bootstrap commands
|
* [tk bootstrap](tk_bootstrap.md) - Bootstrap toolkit components
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ Check requirements and installation
|
|||||||
|
|
||||||
### Synopsis
|
### Synopsis
|
||||||
|
|
||||||
|
|
||||||
The check command will perform a series of checks to validate that
|
The check command will perform a series of checks to validate that
|
||||||
the local environment is configured correctly and if the installed components are healthy.
|
the local environment is configured correctly and if the installed components are healthy.
|
||||||
|
|
||||||
@@ -33,7 +32,7 @@ tk check [flags]
|
|||||||
### Options inherited from parent commands
|
### Options inherited from parent commands
|
||||||
|
|
||||||
```
|
```
|
||||||
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller])
|
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller,notification-controller])
|
||||||
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
||||||
--namespace string the namespace scope for this operation (default "gitops-system")
|
--namespace string the namespace scope for this operation (default "gitops-system")
|
||||||
--timeout duration timeout for this operation (default 5m0s)
|
--timeout duration timeout for this operation (default 5m0s)
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ To configure your bash shell to load completions for each session add to your ba
|
|||||||
### Options inherited from parent commands
|
### Options inherited from parent commands
|
||||||
|
|
||||||
```
|
```
|
||||||
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller])
|
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller,notification-controller])
|
||||||
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
||||||
--namespace string the namespace scope for this operation (default "gitops-system")
|
--namespace string the namespace scope for this operation (default "gitops-system")
|
||||||
--timeout duration timeout for this operation (default 5m0s)
|
--timeout duration timeout for this operation (default 5m0s)
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
## tk create
|
## tk create
|
||||||
|
|
||||||
Create commands
|
Create or update sources and resources
|
||||||
|
|
||||||
### Synopsis
|
### Synopsis
|
||||||
|
|
||||||
Create commands
|
The create sub-commands generate sources and resources.
|
||||||
|
|
||||||
### Options
|
### Options
|
||||||
|
|
||||||
```
|
```
|
||||||
--export export in yaml format to stdout
|
--export export in YAML format to stdout
|
||||||
-h, --help help for create
|
-h, --help help for create
|
||||||
--interval duration source sync interval (default 1m0s)
|
--interval duration source sync interval (default 1m0s)
|
||||||
```
|
```
|
||||||
@@ -17,7 +17,7 @@ Create commands
|
|||||||
### Options inherited from parent commands
|
### Options inherited from parent commands
|
||||||
|
|
||||||
```
|
```
|
||||||
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller])
|
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller,notification-controller])
|
||||||
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
||||||
--namespace string the namespace scope for this operation (default "gitops-system")
|
--namespace string the namespace scope for this operation (default "gitops-system")
|
||||||
--timeout duration timeout for this operation (default 5m0s)
|
--timeout duration timeout for this operation (default 5m0s)
|
||||||
@@ -27,6 +27,6 @@ Create commands
|
|||||||
### SEE ALSO
|
### SEE ALSO
|
||||||
|
|
||||||
* [tk](tk.md) - Command line utility for assembling Kubernetes CD pipelines
|
* [tk](tk.md) - Command line utility for assembling Kubernetes CD pipelines
|
||||||
* [tk create kustomization](tk_create_kustomization.md) - Create or update a kustomization resource
|
* [tk create kustomization](tk_create_kustomization.md) - Create or update a Kustomization resource
|
||||||
* [tk create source](tk_create_source.md) - Create source commands
|
* [tk create source](tk_create_source.md) - Create or update sources
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
## tk create kustomization
|
## tk create kustomization
|
||||||
|
|
||||||
Create or update a kustomization resource
|
Create or update a Kustomization resource
|
||||||
|
|
||||||
### Synopsis
|
### Synopsis
|
||||||
|
|
||||||
|
The kustomization source create command generates a Kustomize resource for a given GitRepository source.
|
||||||
The kustomization source command generates a kustomization.kustomize.fluxcd.io resource for a given GitRepository source.
|
|
||||||
API spec: https://github.com/fluxcd/kustomize-controller/tree/master/docs/spec/v1alpha1
|
|
||||||
|
|
||||||
```
|
```
|
||||||
tk create kustomization [name] [flags]
|
tk create kustomization [name] [flags]
|
||||||
@@ -15,7 +13,7 @@ tk create kustomization [name] [flags]
|
|||||||
### Examples
|
### Examples
|
||||||
|
|
||||||
```
|
```
|
||||||
# Create a kustomization from a source at a given path
|
# Create a Kustomization resource from a source at a given path
|
||||||
create kustomization contour \
|
create kustomization contour \
|
||||||
--source=contour \
|
--source=contour \
|
||||||
--path="./examples/contour/" \
|
--path="./examples/contour/" \
|
||||||
@@ -26,7 +24,7 @@ tk create kustomization [name] [flags]
|
|||||||
--health-check="DaemonSet/envoy.projectcontour" \
|
--health-check="DaemonSet/envoy.projectcontour" \
|
||||||
--health-check-timeout=3m
|
--health-check-timeout=3m
|
||||||
|
|
||||||
# Create a kustomization that depends on the previous one
|
# Create a Kustomization resource that depends on the previous one
|
||||||
create kustomization webapp \
|
create kustomization webapp \
|
||||||
--depends-on=contour \
|
--depends-on=contour \
|
||||||
--source=webapp \
|
--source=webapp \
|
||||||
@@ -35,7 +33,7 @@ tk create kustomization [name] [flags]
|
|||||||
--interval=5m \
|
--interval=5m \
|
||||||
--validate=client
|
--validate=client
|
||||||
|
|
||||||
# Create a kustomization that runs under a service account
|
# Create a Kustomization resource that runs under a service account
|
||||||
create kustomization webapp \
|
create kustomization webapp \
|
||||||
--source=webapp \
|
--source=webapp \
|
||||||
--path="./deploy/overlays/staging" \
|
--path="./deploy/overlays/staging" \
|
||||||
@@ -50,11 +48,11 @@ tk create kustomization [name] [flags]
|
|||||||
### Options
|
### Options
|
||||||
|
|
||||||
```
|
```
|
||||||
--depends-on stringArray kustomization that must be ready before this kustomization can be applied
|
--depends-on stringArray Kustomization that must be ready before this Kustomization can be applied
|
||||||
--health-check stringArray workload to be included in the health assessment, in the format '<kind>/<name>.<namespace>'
|
--health-check stringArray workload to be included in the health assessment, in the format '<kind>/<name>.<namespace>'
|
||||||
--health-check-timeout duration timeout of health checking operations (default 2m0s)
|
--health-check-timeout duration timeout of health checking operations (default 2m0s)
|
||||||
-h, --help help for kustomization
|
-h, --help help for kustomization
|
||||||
--path string path to the directory containing the kustomization file (default "./")
|
--path string path to the directory containing the Kustomization file (default "./")
|
||||||
--prune enable garbage collection
|
--prune enable garbage collection
|
||||||
--sa-name string service account name
|
--sa-name string service account name
|
||||||
--sa-namespace string service account namespace
|
--sa-namespace string service account namespace
|
||||||
@@ -65,8 +63,8 @@ tk create kustomization [name] [flags]
|
|||||||
### Options inherited from parent commands
|
### Options inherited from parent commands
|
||||||
|
|
||||||
```
|
```
|
||||||
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller])
|
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller,notification-controller])
|
||||||
--export export in yaml format to stdout
|
--export export in YAML format to stdout
|
||||||
--interval duration source sync interval (default 1m0s)
|
--interval duration source sync interval (default 1m0s)
|
||||||
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
||||||
--namespace string the namespace scope for this operation (default "gitops-system")
|
--namespace string the namespace scope for this operation (default "gitops-system")
|
||||||
@@ -76,5 +74,5 @@ tk create kustomization [name] [flags]
|
|||||||
|
|
||||||
### SEE ALSO
|
### SEE ALSO
|
||||||
|
|
||||||
* [tk create](tk_create.md) - Create commands
|
* [tk create](tk_create.md) - Create or update sources and resources
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
## tk create source
|
## tk create source
|
||||||
|
|
||||||
Create source commands
|
Create or update sources
|
||||||
|
|
||||||
### Synopsis
|
### Synopsis
|
||||||
|
|
||||||
Create source commands
|
The create source sub-commands generate sources.
|
||||||
|
|
||||||
### Options
|
### Options
|
||||||
|
|
||||||
@@ -15,8 +15,8 @@ Create source commands
|
|||||||
### Options inherited from parent commands
|
### Options inherited from parent commands
|
||||||
|
|
||||||
```
|
```
|
||||||
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller])
|
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller,notification-controller])
|
||||||
--export export in yaml format to stdout
|
--export export in YAML format to stdout
|
||||||
--interval duration source sync interval (default 1m0s)
|
--interval duration source sync interval (default 1m0s)
|
||||||
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
||||||
--namespace string the namespace scope for this operation (default "gitops-system")
|
--namespace string the namespace scope for this operation (default "gitops-system")
|
||||||
@@ -26,6 +26,6 @@ Create source commands
|
|||||||
|
|
||||||
### SEE ALSO
|
### SEE ALSO
|
||||||
|
|
||||||
* [tk create](tk_create.md) - Create commands
|
* [tk create](tk_create.md) - Create or update sources and resources
|
||||||
* [tk create source git](tk_create_source_git.md) - Create or update a git source
|
* [tk create source git](tk_create_source_git.md) - Create or update a GitRepository source
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
## tk create source git
|
## tk create source git
|
||||||
|
|
||||||
Create or update a git source
|
Create or update a GitRepository source
|
||||||
|
|
||||||
### Synopsis
|
### Synopsis
|
||||||
|
|
||||||
|
|
||||||
The create source command generates a GitRepository resource and waits for it to sync.
|
The create source git command generates a GitRepository resource and waits for it to sync.
|
||||||
For Git over SSH, host and SSH keys are automatically generated and stored in a Kubernetes secret.
|
For Git over SSH, host and SSH keys are automatically generated and stored in a Kubernetes secret.
|
||||||
For private Git repositories, the basic authentication credentials are stored in a Kubernetes secret.
|
For private Git repositories, the basic authentication credentials are stored in a Kubernetes secret.
|
||||||
|
|
||||||
@@ -70,8 +70,8 @@ tk create source git [name] [flags]
|
|||||||
### Options inherited from parent commands
|
### Options inherited from parent commands
|
||||||
|
|
||||||
```
|
```
|
||||||
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller])
|
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller,notification-controller])
|
||||||
--export export in yaml format to stdout
|
--export export in YAML format to stdout
|
||||||
--interval duration source sync interval (default 1m0s)
|
--interval duration source sync interval (default 1m0s)
|
||||||
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
||||||
--namespace string the namespace scope for this operation (default "gitops-system")
|
--namespace string the namespace scope for this operation (default "gitops-system")
|
||||||
@@ -81,5 +81,5 @@ tk create source git [name] [flags]
|
|||||||
|
|
||||||
### SEE ALSO
|
### SEE ALSO
|
||||||
|
|
||||||
* [tk create source](tk_create_source.md) - Create source commands
|
* [tk create source](tk_create_source.md) - Create or update sources
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
## tk delete
|
## tk delete
|
||||||
|
|
||||||
Delete commands
|
Delete sources and resources
|
||||||
|
|
||||||
### Synopsis
|
### Synopsis
|
||||||
|
|
||||||
Delete commands
|
The delete sub-commands delete sources and resources.
|
||||||
|
|
||||||
### Options
|
### Options
|
||||||
|
|
||||||
@@ -16,7 +16,7 @@ Delete commands
|
|||||||
### Options inherited from parent commands
|
### Options inherited from parent commands
|
||||||
|
|
||||||
```
|
```
|
||||||
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller])
|
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller,notification-controller])
|
||||||
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
||||||
--namespace string the namespace scope for this operation (default "gitops-system")
|
--namespace string the namespace scope for this operation (default "gitops-system")
|
||||||
--timeout duration timeout for this operation (default 5m0s)
|
--timeout duration timeout for this operation (default 5m0s)
|
||||||
@@ -26,6 +26,6 @@ Delete commands
|
|||||||
### SEE ALSO
|
### SEE ALSO
|
||||||
|
|
||||||
* [tk](tk.md) - Command line utility for assembling Kubernetes CD pipelines
|
* [tk](tk.md) - Command line utility for assembling Kubernetes CD pipelines
|
||||||
* [tk delete kustomization](tk_delete_kustomization.md) - Delete kustomization
|
* [tk delete kustomization](tk_delete_kustomization.md) - Delete a Kustomization resource
|
||||||
* [tk delete source](tk_delete_source.md) - Delete sources commands
|
* [tk delete source](tk_delete_source.md) - Delete sources
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
## tk delete kustomization
|
## tk delete kustomization
|
||||||
|
|
||||||
Delete kustomization
|
Delete a Kustomization resource
|
||||||
|
|
||||||
### Synopsis
|
### Synopsis
|
||||||
|
|
||||||
Delete kustomization
|
The delete kustomization command deletes the given Kustomization from the cluster.
|
||||||
|
|
||||||
```
|
```
|
||||||
tk delete kustomization [name] [flags]
|
tk delete kustomization [name] [flags]
|
||||||
@@ -19,7 +19,7 @@ tk delete kustomization [name] [flags]
|
|||||||
### Options inherited from parent commands
|
### Options inherited from parent commands
|
||||||
|
|
||||||
```
|
```
|
||||||
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller])
|
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller,notification-controller])
|
||||||
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
||||||
--namespace string the namespace scope for this operation (default "gitops-system")
|
--namespace string the namespace scope for this operation (default "gitops-system")
|
||||||
-s, --silent delete resource without asking for confirmation
|
-s, --silent delete resource without asking for confirmation
|
||||||
@@ -29,5 +29,5 @@ tk delete kustomization [name] [flags]
|
|||||||
|
|
||||||
### SEE ALSO
|
### SEE ALSO
|
||||||
|
|
||||||
* [tk delete](tk_delete.md) - Delete commands
|
* [tk delete](tk_delete.md) - Delete sources and resources
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
## tk delete source
|
## tk delete source
|
||||||
|
|
||||||
Delete sources commands
|
Delete sources
|
||||||
|
|
||||||
### Synopsis
|
### Synopsis
|
||||||
|
|
||||||
Delete sources commands
|
The delete source sub-commands delete sources.
|
||||||
|
|
||||||
### Options
|
### Options
|
||||||
|
|
||||||
@@ -15,7 +15,7 @@ Delete sources commands
|
|||||||
### Options inherited from parent commands
|
### Options inherited from parent commands
|
||||||
|
|
||||||
```
|
```
|
||||||
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller])
|
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller,notification-controller])
|
||||||
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
||||||
--namespace string the namespace scope for this operation (default "gitops-system")
|
--namespace string the namespace scope for this operation (default "gitops-system")
|
||||||
-s, --silent delete resource without asking for confirmation
|
-s, --silent delete resource without asking for confirmation
|
||||||
@@ -25,6 +25,6 @@ Delete sources commands
|
|||||||
|
|
||||||
### SEE ALSO
|
### SEE ALSO
|
||||||
|
|
||||||
* [tk delete](tk_delete.md) - Delete commands
|
* [tk delete](tk_delete.md) - Delete sources and resources
|
||||||
* [tk delete source git](tk_delete_source_git.md) - Delete git source
|
* [tk delete source git](tk_delete_source_git.md) - Delete a GitRepository source
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
## tk delete source git
|
## tk delete source git
|
||||||
|
|
||||||
Delete git source
|
Delete a GitRepository source
|
||||||
|
|
||||||
### Synopsis
|
### Synopsis
|
||||||
|
|
||||||
Delete git source
|
The delete source git command deletes the given GitRepository from the cluster.
|
||||||
|
|
||||||
```
|
```
|
||||||
tk delete source git [name] [flags]
|
tk delete source git [name] [flags]
|
||||||
@@ -19,7 +19,7 @@ tk delete source git [name] [flags]
|
|||||||
### Options inherited from parent commands
|
### Options inherited from parent commands
|
||||||
|
|
||||||
```
|
```
|
||||||
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller])
|
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller,notification-controller])
|
||||||
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
||||||
--namespace string the namespace scope for this operation (default "gitops-system")
|
--namespace string the namespace scope for this operation (default "gitops-system")
|
||||||
-s, --silent delete resource without asking for confirmation
|
-s, --silent delete resource without asking for confirmation
|
||||||
@@ -29,5 +29,5 @@ tk delete source git [name] [flags]
|
|||||||
|
|
||||||
### SEE ALSO
|
### SEE ALSO
|
||||||
|
|
||||||
* [tk delete source](tk_delete_source.md) - Delete sources commands
|
* [tk delete source](tk_delete_source.md) - Delete sources
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
## tk export
|
## tk export
|
||||||
|
|
||||||
Export commands
|
Export resources in YAML format
|
||||||
|
|
||||||
### Synopsis
|
### Synopsis
|
||||||
|
|
||||||
Export commands
|
The export sub-commands export resources in YAML format.
|
||||||
|
|
||||||
### Options
|
### Options
|
||||||
|
|
||||||
@@ -16,7 +16,7 @@ Export commands
|
|||||||
### Options inherited from parent commands
|
### Options inherited from parent commands
|
||||||
|
|
||||||
```
|
```
|
||||||
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller])
|
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller,notification-controller])
|
||||||
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
||||||
--namespace string the namespace scope for this operation (default "gitops-system")
|
--namespace string the namespace scope for this operation (default "gitops-system")
|
||||||
--timeout duration timeout for this operation (default 5m0s)
|
--timeout duration timeout for this operation (default 5m0s)
|
||||||
@@ -26,6 +26,6 @@ Export commands
|
|||||||
### SEE ALSO
|
### SEE ALSO
|
||||||
|
|
||||||
* [tk](tk.md) - Command line utility for assembling Kubernetes CD pipelines
|
* [tk](tk.md) - Command line utility for assembling Kubernetes CD pipelines
|
||||||
* [tk export kustomization](tk_export_kustomization.md) - Export kustomization in YAML format
|
* [tk export kustomization](tk_export_kustomization.md) - Export Kustomization resources in YAML format
|
||||||
* [tk export source](tk_export_source.md) - Export source commands
|
* [tk export source](tk_export_source.md) - Export sources
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
## tk export kustomization
|
## tk export kustomization
|
||||||
|
|
||||||
Export kustomization in YAML format
|
Export Kustomization resources in YAML format
|
||||||
|
|
||||||
### Synopsis
|
### Synopsis
|
||||||
|
|
||||||
Export kustomization in YAML format
|
The export kustomization command exports one or all Kustomization resources in YAML format.
|
||||||
|
|
||||||
```
|
```
|
||||||
tk export kustomization [name] [flags]
|
tk export kustomization [name] [flags]
|
||||||
@@ -13,10 +13,10 @@ tk export kustomization [name] [flags]
|
|||||||
### Examples
|
### Examples
|
||||||
|
|
||||||
```
|
```
|
||||||
# Export all kustomizations
|
# Export all Kustomization resources
|
||||||
export kustomization --all > kustomizations.yaml
|
export kustomization --all > kustomizations.yaml
|
||||||
|
|
||||||
# Export a kustomization
|
# Export a Kustomization
|
||||||
export kustomization my-app > kustomization.yaml
|
export kustomization my-app > kustomization.yaml
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -31,7 +31,7 @@ tk export kustomization [name] [flags]
|
|||||||
|
|
||||||
```
|
```
|
||||||
--all select all resources
|
--all select all resources
|
||||||
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller])
|
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller,notification-controller])
|
||||||
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
||||||
--namespace string the namespace scope for this operation (default "gitops-system")
|
--namespace string the namespace scope for this operation (default "gitops-system")
|
||||||
--timeout duration timeout for this operation (default 5m0s)
|
--timeout duration timeout for this operation (default 5m0s)
|
||||||
@@ -40,5 +40,5 @@ tk export kustomization [name] [flags]
|
|||||||
|
|
||||||
### SEE ALSO
|
### SEE ALSO
|
||||||
|
|
||||||
* [tk export](tk_export.md) - Export commands
|
* [tk export](tk_export.md) - Export resources in YAML format
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
## tk export source
|
## tk export source
|
||||||
|
|
||||||
Export source commands
|
Export sources
|
||||||
|
|
||||||
### Synopsis
|
### Synopsis
|
||||||
|
|
||||||
Export source commands
|
The export source sub-commands export sources in YAML format.
|
||||||
|
|
||||||
### Options
|
### Options
|
||||||
|
|
||||||
@@ -17,7 +17,7 @@ Export source commands
|
|||||||
|
|
||||||
```
|
```
|
||||||
--all select all resources
|
--all select all resources
|
||||||
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller])
|
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller,notification-controller])
|
||||||
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
||||||
--namespace string the namespace scope for this operation (default "gitops-system")
|
--namespace string the namespace scope for this operation (default "gitops-system")
|
||||||
--timeout duration timeout for this operation (default 5m0s)
|
--timeout duration timeout for this operation (default 5m0s)
|
||||||
@@ -26,6 +26,6 @@ Export source commands
|
|||||||
|
|
||||||
### SEE ALSO
|
### SEE ALSO
|
||||||
|
|
||||||
* [tk export](tk_export.md) - Export commands
|
* [tk export](tk_export.md) - Export resources in YAML format
|
||||||
* [tk export source git](tk_export_source_git.md) - Export git sources in YAML format
|
* [tk export source git](tk_export_source_git.md) - Export GitRepository sources in YAML format
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
## tk export source git
|
## tk export source git
|
||||||
|
|
||||||
Export git sources in YAML format
|
Export GitRepository sources in YAML format
|
||||||
|
|
||||||
### Synopsis
|
### Synopsis
|
||||||
|
|
||||||
Export git sources in YAML format
|
The export source git command exports on or all GitRepository sources in YAML format.
|
||||||
|
|
||||||
```
|
```
|
||||||
tk export source git [name] [flags]
|
tk export source git [name] [flags]
|
||||||
@@ -13,10 +13,10 @@ tk export source git [name] [flags]
|
|||||||
### Examples
|
### Examples
|
||||||
|
|
||||||
```
|
```
|
||||||
# Export all git sources
|
# Export all GitRepository sources
|
||||||
export source git --all > sources.yaml
|
export source git --all > sources.yaml
|
||||||
|
|
||||||
# Export a git source including the SSH keys or basic auth credentials
|
# Export a GitRepository source including the SSH key pair or basic auth credentials
|
||||||
export source git my-private-repo --with-credentials > source.yaml
|
export source git my-private-repo --with-credentials > source.yaml
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -31,7 +31,7 @@ tk export source git [name] [flags]
|
|||||||
|
|
||||||
```
|
```
|
||||||
--all select all resources
|
--all select all resources
|
||||||
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller])
|
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller,notification-controller])
|
||||||
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
||||||
--namespace string the namespace scope for this operation (default "gitops-system")
|
--namespace string the namespace scope for this operation (default "gitops-system")
|
||||||
--timeout duration timeout for this operation (default 5m0s)
|
--timeout duration timeout for this operation (default 5m0s)
|
||||||
@@ -41,5 +41,5 @@ tk export source git [name] [flags]
|
|||||||
|
|
||||||
### SEE ALSO
|
### SEE ALSO
|
||||||
|
|
||||||
* [tk export source](tk_export_source.md) - Export source commands
|
* [tk export source](tk_export_source.md) - Export sources
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
## tk get
|
## tk get
|
||||||
|
|
||||||
Get commands
|
Get sources and resources
|
||||||
|
|
||||||
### Synopsis
|
### Synopsis
|
||||||
|
|
||||||
Get commands
|
The get sub-commands print the statuses of sources and resources.
|
||||||
|
|
||||||
### Options
|
### Options
|
||||||
|
|
||||||
@@ -15,7 +15,7 @@ Get commands
|
|||||||
### Options inherited from parent commands
|
### Options inherited from parent commands
|
||||||
|
|
||||||
```
|
```
|
||||||
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller])
|
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller,notification-controller])
|
||||||
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
||||||
--namespace string the namespace scope for this operation (default "gitops-system")
|
--namespace string the namespace scope for this operation (default "gitops-system")
|
||||||
--timeout duration timeout for this operation (default 5m0s)
|
--timeout duration timeout for this operation (default 5m0s)
|
||||||
@@ -25,6 +25,6 @@ Get commands
|
|||||||
### SEE ALSO
|
### SEE ALSO
|
||||||
|
|
||||||
* [tk](tk.md) - Command line utility for assembling Kubernetes CD pipelines
|
* [tk](tk.md) - Command line utility for assembling Kubernetes CD pipelines
|
||||||
* [tk get kustomizations](tk_get_kustomizations.md) - Get kustomizations status
|
* [tk get kustomizations](tk_get_kustomizations.md) - Get Kustomization source statuses
|
||||||
* [tk get sources](tk_get_sources.md) - Get sources commands
|
* [tk get sources](tk_get_sources.md) - Get source statuses
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
## tk get kustomizations
|
## tk get kustomizations
|
||||||
|
|
||||||
Get kustomizations status
|
Get Kustomization source statuses
|
||||||
|
|
||||||
### Synopsis
|
### Synopsis
|
||||||
|
|
||||||
|
The get kustomizations command prints the statuses of the resources.
|
||||||
The get kustomizations command prints the status of the resources.
|
|
||||||
|
|
||||||
```
|
```
|
||||||
tk get kustomizations [flags]
|
tk get kustomizations [flags]
|
||||||
@@ -20,7 +19,7 @@ tk get kustomizations [flags]
|
|||||||
### Options inherited from parent commands
|
### Options inherited from parent commands
|
||||||
|
|
||||||
```
|
```
|
||||||
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller])
|
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller,notification-controller])
|
||||||
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
||||||
--namespace string the namespace scope for this operation (default "gitops-system")
|
--namespace string the namespace scope for this operation (default "gitops-system")
|
||||||
--timeout duration timeout for this operation (default 5m0s)
|
--timeout duration timeout for this operation (default 5m0s)
|
||||||
@@ -29,5 +28,5 @@ tk get kustomizations [flags]
|
|||||||
|
|
||||||
### SEE ALSO
|
### SEE ALSO
|
||||||
|
|
||||||
* [tk get](tk_get.md) - Get commands
|
* [tk get](tk_get.md) - Get sources and resources
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
## tk get sources
|
## tk get sources
|
||||||
|
|
||||||
Get sources commands
|
Get source statuses
|
||||||
|
|
||||||
### Synopsis
|
### Synopsis
|
||||||
|
|
||||||
Get sources commands
|
The get source sub-commands print the statuses of the sources.
|
||||||
|
|
||||||
### Options
|
### Options
|
||||||
|
|
||||||
@@ -15,7 +15,7 @@ Get sources commands
|
|||||||
### Options inherited from parent commands
|
### Options inherited from parent commands
|
||||||
|
|
||||||
```
|
```
|
||||||
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller])
|
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller,notification-controller])
|
||||||
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
||||||
--namespace string the namespace scope for this operation (default "gitops-system")
|
--namespace string the namespace scope for this operation (default "gitops-system")
|
||||||
--timeout duration timeout for this operation (default 5m0s)
|
--timeout duration timeout for this operation (default 5m0s)
|
||||||
@@ -24,6 +24,6 @@ Get sources commands
|
|||||||
|
|
||||||
### SEE ALSO
|
### SEE ALSO
|
||||||
|
|
||||||
* [tk get](tk_get.md) - Get commands
|
* [tk get](tk_get.md) - Get sources and resources
|
||||||
* [tk get sources git](tk_get_sources_git.md) - Get git sources status
|
* [tk get sources git](tk_get_sources_git.md) - Get GitRepository source statuses
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
## tk get sources git
|
## tk get sources git
|
||||||
|
|
||||||
Get git sources status
|
Get GitRepository source statuses
|
||||||
|
|
||||||
### Synopsis
|
### Synopsis
|
||||||
|
|
||||||
|
The get sources git command prints the status of the GitRepository sources.
|
||||||
The get sources command prints the status of the git resources.
|
|
||||||
|
|
||||||
```
|
```
|
||||||
tk get sources git [flags]
|
tk get sources git [flags]
|
||||||
@@ -20,7 +19,7 @@ tk get sources git [flags]
|
|||||||
### Options inherited from parent commands
|
### Options inherited from parent commands
|
||||||
|
|
||||||
```
|
```
|
||||||
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller])
|
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller,notification-controller])
|
||||||
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
||||||
--namespace string the namespace scope for this operation (default "gitops-system")
|
--namespace string the namespace scope for this operation (default "gitops-system")
|
||||||
--timeout duration timeout for this operation (default 5m0s)
|
--timeout duration timeout for this operation (default 5m0s)
|
||||||
@@ -29,5 +28,5 @@ tk get sources git [flags]
|
|||||||
|
|
||||||
### SEE ALSO
|
### SEE ALSO
|
||||||
|
|
||||||
* [tk get sources](tk_get_sources.md) - Get sources commands
|
* [tk get sources](tk_get_sources.md) - Get source statuses
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ Install the toolkit components
|
|||||||
|
|
||||||
### Synopsis
|
### Synopsis
|
||||||
|
|
||||||
|
|
||||||
The install command deploys the toolkit components in the specified namespace.
|
The install command deploys the toolkit components in the specified namespace.
|
||||||
If a previous version is installed, then an in-place upgrade will be performed.
|
If a previous version is installed, then an in-place upgrade will be performed.
|
||||||
|
|
||||||
@@ -38,7 +37,7 @@ tk install [flags]
|
|||||||
### Options inherited from parent commands
|
### Options inherited from parent commands
|
||||||
|
|
||||||
```
|
```
|
||||||
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller])
|
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller,notification-controller])
|
||||||
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
||||||
--namespace string the namespace scope for this operation (default "gitops-system")
|
--namespace string the namespace scope for this operation (default "gitops-system")
|
||||||
--timeout duration timeout for this operation (default 5m0s)
|
--timeout duration timeout for this operation (default 5m0s)
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
## tk resume
|
## tk resume
|
||||||
|
|
||||||
Resume commands
|
Resume suspended resources
|
||||||
|
|
||||||
### Synopsis
|
### Synopsis
|
||||||
|
|
||||||
Resume commands
|
The resume sub-commands resume a suspended resource.
|
||||||
|
|
||||||
### Options
|
### Options
|
||||||
|
|
||||||
@@ -15,7 +15,7 @@ Resume commands
|
|||||||
### Options inherited from parent commands
|
### Options inherited from parent commands
|
||||||
|
|
||||||
```
|
```
|
||||||
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller])
|
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller,notification-controller])
|
||||||
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
||||||
--namespace string the namespace scope for this operation (default "gitops-system")
|
--namespace string the namespace scope for this operation (default "gitops-system")
|
||||||
--timeout duration timeout for this operation (default 5m0s)
|
--timeout duration timeout for this operation (default 5m0s)
|
||||||
@@ -25,5 +25,5 @@ Resume commands
|
|||||||
### SEE ALSO
|
### SEE ALSO
|
||||||
|
|
||||||
* [tk](tk.md) - Command line utility for assembling Kubernetes CD pipelines
|
* [tk](tk.md) - Command line utility for assembling Kubernetes CD pipelines
|
||||||
* [tk resume kustomization](tk_resume_kustomization.md) - Resume kustomization
|
* [tk resume kustomization](tk_resume_kustomization.md) - Resume a suspended Kustomization
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
## tk resume kustomization
|
## tk resume kustomization
|
||||||
|
|
||||||
Resume kustomization
|
Resume a suspended Kustomization
|
||||||
|
|
||||||
### Synopsis
|
### Synopsis
|
||||||
|
|
||||||
The resume command marks a previously suspended Kustomization resource for reconciliation and waits for it to finish the apply.
|
The resume command marks a previously suspended Kustomization resource for reconciliation and waits for it to
|
||||||
|
finish the apply.
|
||||||
|
|
||||||
```
|
```
|
||||||
tk resume kustomization [name] [flags]
|
tk resume kustomization [name] [flags]
|
||||||
@@ -19,7 +20,7 @@ tk resume kustomization [name] [flags]
|
|||||||
### Options inherited from parent commands
|
### Options inherited from parent commands
|
||||||
|
|
||||||
```
|
```
|
||||||
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller])
|
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller,notification-controller])
|
||||||
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
||||||
--namespace string the namespace scope for this operation (default "gitops-system")
|
--namespace string the namespace scope for this operation (default "gitops-system")
|
||||||
--timeout duration timeout for this operation (default 5m0s)
|
--timeout duration timeout for this operation (default 5m0s)
|
||||||
@@ -28,5 +29,5 @@ tk resume kustomization [name] [flags]
|
|||||||
|
|
||||||
### SEE ALSO
|
### SEE ALSO
|
||||||
|
|
||||||
* [tk resume](tk_resume.md) - Resume commands
|
* [tk resume](tk_resume.md) - Resume suspended resources
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
## tk suspend
|
## tk suspend
|
||||||
|
|
||||||
Suspend commands
|
Suspend resources
|
||||||
|
|
||||||
### Synopsis
|
### Synopsis
|
||||||
|
|
||||||
Suspend commands
|
The suspend sub-commands suspend the reconciliation of a resource.
|
||||||
|
|
||||||
### Options
|
### Options
|
||||||
|
|
||||||
@@ -15,7 +15,7 @@ Suspend commands
|
|||||||
### Options inherited from parent commands
|
### Options inherited from parent commands
|
||||||
|
|
||||||
```
|
```
|
||||||
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller])
|
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller,notification-controller])
|
||||||
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
||||||
--namespace string the namespace scope for this operation (default "gitops-system")
|
--namespace string the namespace scope for this operation (default "gitops-system")
|
||||||
--timeout duration timeout for this operation (default 5m0s)
|
--timeout duration timeout for this operation (default 5m0s)
|
||||||
@@ -25,5 +25,5 @@ Suspend commands
|
|||||||
### SEE ALSO
|
### SEE ALSO
|
||||||
|
|
||||||
* [tk](tk.md) - Command line utility for assembling Kubernetes CD pipelines
|
* [tk](tk.md) - Command line utility for assembling Kubernetes CD pipelines
|
||||||
* [tk suspend kustomization](tk_suspend_kustomization.md) - Suspend kustomization
|
* [tk suspend kustomization](tk_suspend_kustomization.md) - Suspend reconciliation of Kustomization
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
## tk suspend kustomization
|
## tk suspend kustomization
|
||||||
|
|
||||||
Suspend kustomization
|
Suspend reconciliation of Kustomization
|
||||||
|
|
||||||
### Synopsis
|
### Synopsis
|
||||||
|
|
||||||
@@ -19,7 +19,7 @@ tk suspend kustomization [name] [flags]
|
|||||||
### Options inherited from parent commands
|
### Options inherited from parent commands
|
||||||
|
|
||||||
```
|
```
|
||||||
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller])
|
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller,notification-controller])
|
||||||
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
||||||
--namespace string the namespace scope for this operation (default "gitops-system")
|
--namespace string the namespace scope for this operation (default "gitops-system")
|
||||||
--timeout duration timeout for this operation (default 5m0s)
|
--timeout duration timeout for this operation (default 5m0s)
|
||||||
@@ -28,5 +28,5 @@ tk suspend kustomization [name] [flags]
|
|||||||
|
|
||||||
### SEE ALSO
|
### SEE ALSO
|
||||||
|
|
||||||
* [tk suspend](tk_suspend.md) - Suspend commands
|
* [tk suspend](tk_suspend.md) - Suspend resources
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
## tk sync
|
## tk sync
|
||||||
|
|
||||||
Synchronize commands
|
Synchronize sources and resources
|
||||||
|
|
||||||
### Synopsis
|
### Synopsis
|
||||||
|
|
||||||
Synchronize commands
|
The sync sub-commands trigger a reconciliation of sources and resources.
|
||||||
|
|
||||||
### Options
|
### Options
|
||||||
|
|
||||||
@@ -15,7 +15,7 @@ Synchronize commands
|
|||||||
### Options inherited from parent commands
|
### Options inherited from parent commands
|
||||||
|
|
||||||
```
|
```
|
||||||
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller])
|
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller,notification-controller])
|
||||||
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
||||||
--namespace string the namespace scope for this operation (default "gitops-system")
|
--namespace string the namespace scope for this operation (default "gitops-system")
|
||||||
--timeout duration timeout for this operation (default 5m0s)
|
--timeout duration timeout for this operation (default 5m0s)
|
||||||
@@ -25,6 +25,6 @@ Synchronize commands
|
|||||||
### SEE ALSO
|
### SEE ALSO
|
||||||
|
|
||||||
* [tk](tk.md) - Command line utility for assembling Kubernetes CD pipelines
|
* [tk](tk.md) - Command line utility for assembling Kubernetes CD pipelines
|
||||||
* [tk sync kustomization](tk_sync_kustomization.md) - Synchronize kustomization
|
* [tk sync kustomization](tk_sync_kustomization.md) - Synchronize a Kustomization resource
|
||||||
* [tk sync source](tk_sync_source.md) - Synchronize source commands
|
* [tk sync source](tk_sync_source.md) - Synchronize sources
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
## tk sync kustomization
|
## tk sync kustomization
|
||||||
|
|
||||||
Synchronize kustomization
|
Synchronize a Kustomization resource
|
||||||
|
|
||||||
### Synopsis
|
### Synopsis
|
||||||
|
|
||||||
@@ -14,10 +14,10 @@ tk sync kustomization [name] [flags]
|
|||||||
### Examples
|
### Examples
|
||||||
|
|
||||||
```
|
```
|
||||||
# Trigger a kustomization apply outside of the reconciliation interval
|
# Trigger a Kustomization apply outside of the reconciliation interval
|
||||||
sync kustomization podinfo
|
sync kustomization podinfo
|
||||||
|
|
||||||
# Trigger a git sync of the kustomization source and apply changes
|
# Trigger a sync of the Kustomization's source and apply changes
|
||||||
sync kustomization podinfo --with-source
|
sync kustomization podinfo --with-source
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -32,7 +32,7 @@ tk sync kustomization [name] [flags]
|
|||||||
### Options inherited from parent commands
|
### Options inherited from parent commands
|
||||||
|
|
||||||
```
|
```
|
||||||
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller])
|
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller,notification-controller])
|
||||||
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
||||||
--namespace string the namespace scope for this operation (default "gitops-system")
|
--namespace string the namespace scope for this operation (default "gitops-system")
|
||||||
--timeout duration timeout for this operation (default 5m0s)
|
--timeout duration timeout for this operation (default 5m0s)
|
||||||
@@ -41,5 +41,5 @@ tk sync kustomization [name] [flags]
|
|||||||
|
|
||||||
### SEE ALSO
|
### SEE ALSO
|
||||||
|
|
||||||
* [tk sync](tk_sync.md) - Synchronize commands
|
* [tk sync](tk_sync.md) - Synchronize sources and resources
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
## tk sync source
|
## tk sync source
|
||||||
|
|
||||||
Synchronize source commands
|
Synchronize sources
|
||||||
|
|
||||||
### Synopsis
|
### Synopsis
|
||||||
|
|
||||||
Synchronize source commands
|
The sync source sub-commands trigger a reconciliation of sources.
|
||||||
|
|
||||||
### Options
|
### Options
|
||||||
|
|
||||||
@@ -15,7 +15,7 @@ Synchronize source commands
|
|||||||
### Options inherited from parent commands
|
### Options inherited from parent commands
|
||||||
|
|
||||||
```
|
```
|
||||||
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller])
|
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller,notification-controller])
|
||||||
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
||||||
--namespace string the namespace scope for this operation (default "gitops-system")
|
--namespace string the namespace scope for this operation (default "gitops-system")
|
||||||
--timeout duration timeout for this operation (default 5m0s)
|
--timeout duration timeout for this operation (default 5m0s)
|
||||||
@@ -24,6 +24,6 @@ Synchronize source commands
|
|||||||
|
|
||||||
### SEE ALSO
|
### SEE ALSO
|
||||||
|
|
||||||
* [tk sync](tk_sync.md) - Synchronize commands
|
* [tk sync](tk_sync.md) - Synchronize sources and resources
|
||||||
* [tk sync source git](tk_sync_source_git.md) - Synchronize git source
|
* [tk sync source git](tk_sync_source_git.md) - Synchronize a GitRepository source
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
## tk sync source git
|
## tk sync source git
|
||||||
|
|
||||||
Synchronize git source
|
Synchronize a GitRepository source
|
||||||
|
|
||||||
### Synopsis
|
### Synopsis
|
||||||
|
|
||||||
|
|
||||||
The sync source command triggers a reconciliation of a GitRepository resource and waits for it to finish.
|
The sync source command triggers a reconciliation of a GitRepository resource and waits for it to finish.
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -28,7 +27,7 @@ tk sync source git [name] [flags]
|
|||||||
### Options inherited from parent commands
|
### Options inherited from parent commands
|
||||||
|
|
||||||
```
|
```
|
||||||
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller])
|
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller,notification-controller])
|
||||||
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
||||||
--namespace string the namespace scope for this operation (default "gitops-system")
|
--namespace string the namespace scope for this operation (default "gitops-system")
|
||||||
--timeout duration timeout for this operation (default 5m0s)
|
--timeout duration timeout for this operation (default 5m0s)
|
||||||
@@ -37,5 +36,5 @@ tk sync source git [name] [flags]
|
|||||||
|
|
||||||
### SEE ALSO
|
### SEE ALSO
|
||||||
|
|
||||||
* [tk sync source](tk_sync_source.md) - Synchronize source commands
|
* [tk sync source](tk_sync_source.md) - Synchronize sources
|
||||||
|
|
||||||
|
|||||||
@@ -4,9 +4,7 @@ Uninstall the toolkit components
|
|||||||
|
|
||||||
### Synopsis
|
### Synopsis
|
||||||
|
|
||||||
|
The uninstall command removes the namespace, cluster roles, cluster role bindings and CRDs from the cluster.
|
||||||
The uninstall command removes the namespace, cluster roles,
|
|
||||||
cluster role bindings and CRDs.
|
|
||||||
|
|
||||||
```
|
```
|
||||||
tk uninstall [flags]
|
tk uninstall [flags]
|
||||||
@@ -29,14 +27,14 @@ tk uninstall [flags]
|
|||||||
--crds removes all CRDs previously installed
|
--crds removes all CRDs previously installed
|
||||||
--dry-run only print the object that would be deleted
|
--dry-run only print the object that would be deleted
|
||||||
-h, --help help for uninstall
|
-h, --help help for uninstall
|
||||||
--kustomizations removes all kustomizations previously installed
|
--kustomizations removes all Kustomizations previously installed
|
||||||
-s, --silent delete components without asking for confirmation
|
-s, --silent delete components without asking for confirmation
|
||||||
```
|
```
|
||||||
|
|
||||||
### Options inherited from parent commands
|
### Options inherited from parent commands
|
||||||
|
|
||||||
```
|
```
|
||||||
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller])
|
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller,notification-controller])
|
||||||
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
||||||
--namespace string the namespace scope for this operation (default "gitops-system")
|
--namespace string the namespace scope for this operation (default "gitops-system")
|
||||||
--timeout duration timeout for this operation (default 5m0s)
|
--timeout duration timeout for this operation (default 5m0s)
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ Features:
|
|||||||
- Health assessment of the deployed workloads
|
- Health assessment of the deployed workloads
|
||||||
- Runs pipelines in a specific order (depends-on relationship)
|
- Runs pipelines in a specific order (depends-on relationship)
|
||||||
- Prunes objects removed from source (garbage collection)
|
- Prunes objects removed from source (garbage collection)
|
||||||
- Reports cluster state changes (Slack/Discord)
|
- Reports cluster state changes (alerting provided by notification-controller)
|
||||||
|
|
||||||
Links:
|
Links:
|
||||||
|
|
||||||
|
|||||||
16
docs/components/notification/controller.md
Normal file
16
docs/components/notification/controller.md
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
# Notification Controller
|
||||||
|
|
||||||
|
The Notification Controller is a Kubernetes operator,
|
||||||
|
specialized in dispatching events to external systems such as
|
||||||
|
Slack, Microsoft Teams, Discord and Rocket chat.
|
||||||
|
|
||||||
|
The controller receives events via HTTP and dispatch them to external
|
||||||
|
webhooks based on event severity and involved objects.
|
||||||
|
|
||||||
|
The controller can be configured with Kubernetes custom resources that
|
||||||
|
define how events are processed and where to dispatch them.
|
||||||
|
|
||||||
|
Links:
|
||||||
|
|
||||||
|
- Source code [fluxcd/notification-controller](https://github.com/fluxcd/notification-controller)
|
||||||
|
- Specification [docs](https://github.com/fluxcd/notification-controller/tree/master/docs/spec)
|
||||||
BIN
docs/diagrams/slack-error-alert.png
Normal file
BIN
docs/diagrams/slack-error-alert.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 34 KiB |
BIN
docs/diagrams/slack-info-alert.png
Normal file
BIN
docs/diagrams/slack-info-alert.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 67 KiB |
@@ -21,7 +21,7 @@ export GITHUB_USER=<your-username>
|
|||||||
|
|
||||||
To install the latest `tk` release run:
|
To install the latest `tk` release run:
|
||||||
|
|
||||||
```bash
|
```sh
|
||||||
curl -s https://toolkit.fluxcd.io/install.sh | sudo bash
|
curl -s https://toolkit.fluxcd.io/install.sh | sudo bash
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -58,7 +58,7 @@ kubectl cluster-info --context kind-staging
|
|||||||
|
|
||||||
Verify that your staging cluster satisfies the prerequisites with:
|
Verify that your staging cluster satisfies the prerequisites with:
|
||||||
|
|
||||||
```text
|
```console
|
||||||
$ tk check --pre
|
$ tk check --pre
|
||||||
|
|
||||||
► checking prerequisites
|
► checking prerequisites
|
||||||
@@ -104,22 +104,9 @@ $ tk bootstrap github --owner=gitopsrun --repository=fleet-infra --path=staging-
|
|||||||
✚ generating manifests
|
✚ generating manifests
|
||||||
✔ components manifests pushed
|
✔ components manifests pushed
|
||||||
► installing components in gitops-system namespace
|
► 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 "source-controller" successfully rolled out
|
||||||
deployment "kustomize-controller" successfully rolled out
|
deployment "kustomize-controller" successfully rolled out
|
||||||
|
deployment "notification-controller" successfully rolled out
|
||||||
✔ install completed
|
✔ install completed
|
||||||
► configuring deploy key
|
► configuring deploy key
|
||||||
✔ deploy key configured
|
✔ deploy key configured
|
||||||
@@ -208,7 +195,7 @@ git add -A && git commit -m "add staging webapp" && git push
|
|||||||
|
|
||||||
In about 30s the synchronization should start:
|
In about 30s the synchronization should start:
|
||||||
|
|
||||||
```text
|
```console
|
||||||
$ watch tk get kustomizations
|
$ watch tk get kustomizations
|
||||||
|
|
||||||
✔ gitops-system last applied revision master/35d5765a1acb9e9ce66cad7274c6fe03eee1e8eb
|
✔ gitops-system last applied revision master/35d5765a1acb9e9ce66cad7274c6fe03eee1e8eb
|
||||||
@@ -219,7 +206,7 @@ $ watch tk get kustomizations
|
|||||||
|
|
||||||
When the synchronization finishes you can check that the webapp services are running:
|
When the synchronization finishes you can check that the webapp services are running:
|
||||||
|
|
||||||
```text
|
```console
|
||||||
$ kubectl -n webapp get deployments,services
|
$ kubectl -n webapp get deployments,services
|
||||||
|
|
||||||
NAME READY UP-TO-DATE AVAILABLE AGE
|
NAME READY UP-TO-DATE AVAILABLE AGE
|
||||||
@@ -307,7 +294,7 @@ git add -A && git commit -m "add prod webapp" && git push
|
|||||||
|
|
||||||
List git sources:
|
List git sources:
|
||||||
|
|
||||||
```text
|
```console
|
||||||
$ tk get sources git
|
$ tk get sources git
|
||||||
|
|
||||||
✔ gitops-system last fetched revision master/99072ee132abdead8b7799d7891eae2f524eb73d
|
✔ gitops-system last fetched revision master/99072ee132abdead8b7799d7891eae2f524eb73d
|
||||||
@@ -318,7 +305,7 @@ The kubectl equivalent is `kubectl -n gitops-system get gitrepositories`.
|
|||||||
|
|
||||||
List kustomization:
|
List kustomization:
|
||||||
|
|
||||||
```text
|
```console
|
||||||
$ tk get kustomizations
|
$ tk get kustomizations
|
||||||
|
|
||||||
✔ gitops-system last applied revision master/99072ee132abdead8b7799d7891eae2f524eb73d
|
✔ gitops-system last applied revision master/99072ee132abdead8b7799d7891eae2f524eb73d
|
||||||
@@ -341,7 +328,7 @@ git add -A && git commit -m "update prod webapp" && git push
|
|||||||
|
|
||||||
Trigger a git sync:
|
Trigger a git sync:
|
||||||
|
|
||||||
```text
|
```console
|
||||||
$ tk sync ks gitops-system --with-source
|
$ tk sync ks gitops-system --with-source
|
||||||
|
|
||||||
► annotating source gitops-system
|
► annotating source gitops-system
|
||||||
@@ -358,7 +345,7 @@ The kubectl equivalent is `kubectl -n gitops-system annotate gitrepository/gitop
|
|||||||
|
|
||||||
Wait for the webapp to be upgraded:
|
Wait for the webapp to be upgraded:
|
||||||
|
|
||||||
```text
|
```console
|
||||||
$ watch tk get kustomizations
|
$ watch tk get kustomizations
|
||||||
|
|
||||||
✔ gitops-system last applied revision master/d751ea264d48bf0db8b588d1d08184834ac8fec9
|
✔ gitops-system last applied revision master/d751ea264d48bf0db8b588d1d08184834ac8fec9
|
||||||
|
|||||||
101
docs/guides/notifications.md
Normal file
101
docs/guides/notifications.md
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
# Setup Notifications
|
||||||
|
|
||||||
|
When operating a cluster, different teams may wish to receive notifications about
|
||||||
|
the status of their GitOps pipelines.
|
||||||
|
For example, the on-call team would receive alerts about reconciliation
|
||||||
|
failures in the cluster, while the dev team may wish to be alerted when a new version
|
||||||
|
of an app was deployed and if the deployment is healthy.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
* [Get started guide](../get-started/index.md)
|
||||||
|
|
||||||
|
The GitOps toolkit controllers emit Kubernetes events whenever a resource status changes.
|
||||||
|
You can use the [notification-controller](../components/notification/controller.md)
|
||||||
|
to forward these events to Slack, Microsoft Teams, Discord or Rocket chart.
|
||||||
|
The notification controller is part of the default toolkit installation.
|
||||||
|
|
||||||
|
## Define a provider
|
||||||
|
|
||||||
|
First create a secret with your Slack incoming webhook:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
kubectl -n gitops-system create secret generic slack-url \
|
||||||
|
--from-literal=address=https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that the secret must contain an `address` field,
|
||||||
|
it can be a Slack, Microsoft Teams, Discord or Rocket webhook URL.
|
||||||
|
|
||||||
|
Create a notification provider for Slack by referencing the above secret:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
apiVersion: notification.fluxcd.io/v1alpha1
|
||||||
|
kind: Provider
|
||||||
|
metadata:
|
||||||
|
name: slack
|
||||||
|
namespace: gitops-system
|
||||||
|
spec:
|
||||||
|
type: slack
|
||||||
|
channel: general
|
||||||
|
secretRef:
|
||||||
|
name: slack-url
|
||||||
|
```
|
||||||
|
|
||||||
|
The provider type can be `slack`, `msteams`, `discord`, `rocket` or `webhook`.
|
||||||
|
|
||||||
|
When type `webhook` is specified, the notification controller will post the incoming
|
||||||
|
[event](../components/notification/event.md) in JSON format to the webhook address.
|
||||||
|
This way you can create custom handlers that can store the events in
|
||||||
|
Elasticsearch, CloudWatch, Stackdriver, etc.
|
||||||
|
|
||||||
|
## Define an alert
|
||||||
|
|
||||||
|
Create an alert definition for the webapp kustomizations:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
apiVersion: notification.fluxcd.io/v1alpha1
|
||||||
|
kind: Alert
|
||||||
|
metadata:
|
||||||
|
name: on-call-webapp
|
||||||
|
namespace: gitops-system
|
||||||
|
spec:
|
||||||
|
providerRef:
|
||||||
|
name: slack
|
||||||
|
eventSeverity: info
|
||||||
|
eventSources:
|
||||||
|
- kind: Kustomization
|
||||||
|
name: webapp-backend
|
||||||
|
- kind: Kustomization
|
||||||
|
name: webapp-frontend
|
||||||
|
```
|
||||||
|
|
||||||
|
Apply the above files or commit them to the `fleet-infra` repository.
|
||||||
|
|
||||||
|
To verify that the alert has been acknowledge by the notification controller do:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ kubectl -n gitops-system get alerts
|
||||||
|
|
||||||
|
NAME READY STATUS AGE
|
||||||
|
on-call-webapp True Initialized 1m
|
||||||
|
```
|
||||||
|
|
||||||
|
Multiple alerts can be used to send notifications to different channels or Slack organizations.
|
||||||
|
|
||||||
|
The event severity can be set to `info` or `error`.
|
||||||
|
When the severity is set to `error`, the kustomize controller will alert on any error
|
||||||
|
encountered during the reconciliation process.
|
||||||
|
This includes kustomize build and validation errors,
|
||||||
|
apply errors and health check failures.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
When the verbosity is set to `info`, the controller will alert if:
|
||||||
|
|
||||||
|
* a Kubernetes object was created, updated or deleted
|
||||||
|
* heath checks are passing
|
||||||
|
* a dependency is delaying the execution
|
||||||
|
* an error occurs
|
||||||
|
|
||||||
|

|
||||||
@@ -41,6 +41,9 @@ Components:
|
|||||||
- [HelmRepository CRD](components/source/helmrepositories.md)
|
- [HelmRepository CRD](components/source/helmrepositories.md)
|
||||||
- [Kustomize Controller](components/kustomize/controller.md)
|
- [Kustomize Controller](components/kustomize/controller.md)
|
||||||
- [Kustomization CRD](components/kustomize/kustomization.md)
|
- [Kustomization CRD](components/kustomize/kustomization.md)
|
||||||
|
- [Notification Controller](components/notification/controller.md)
|
||||||
|
- [Provider CRD](components/notification/provider.md)
|
||||||
|
- [Alert CRD](components/notification/alert.md)
|
||||||
- Helm Controller (TBA)
|
- Helm Controller (TBA)
|
||||||
|
|
||||||
To get started with the toolkit please follow this [guide](get-started/index.md).
|
To get started with the toolkit please follow this [guide](get-started/index.md).
|
||||||
|
|||||||
@@ -28,12 +28,12 @@ Non-Goals
|
|||||||
Tasks
|
Tasks
|
||||||
|
|
||||||
- Review the git source and kustomize APIs
|
- Review the git source and kustomize APIs
|
||||||
- Design the events API
|
- ~~Design the events API~~
|
||||||
- Implement events in source and kustomize controllers
|
- Implement events in source and kustomize controllers
|
||||||
- Implement Prometheus metrics 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
|
- Make the kustomize-controller apply/gc events on-par with Flux v1 apply events
|
||||||
- Design the notifications and events filtering API
|
- ~~Design the notifications and events filtering API~~
|
||||||
- Implement a notification controller for Slack, MS Teams, Discord, Rocket
|
- ~~Implement a notification controller for Slack, MS Teams, Discord, Rocket~~
|
||||||
- Implement the migration command in tk
|
- Implement the migration command in tk
|
||||||
- Create a migration guide for `flux.yaml` kustomize users
|
- Create a migration guide for `flux.yaml` kustomize users
|
||||||
|
|
||||||
|
|||||||
5
go.mod
5
go.mod
@@ -5,15 +5,12 @@ go 1.14
|
|||||||
require (
|
require (
|
||||||
github.com/blang/semver v3.5.1+incompatible
|
github.com/blang/semver v3.5.1+incompatible
|
||||||
github.com/fluxcd/kustomize-controller v0.0.1
|
github.com/fluxcd/kustomize-controller v0.0.1
|
||||||
|
github.com/fluxcd/pkg v0.0.1
|
||||||
github.com/fluxcd/source-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/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/hashicorp/go-retryablehttp v0.6.6 // indirect
|
||||||
github.com/manifoldco/promptui v0.7.0
|
github.com/manifoldco/promptui v0.7.0
|
||||||
github.com/spf13/cobra v1.0.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/net v0.0.0-20200602114024-627f9648deb9 // indirect
|
||||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d // indirect
|
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d // indirect
|
||||||
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 // indirect
|
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 // indirect
|
||||||
|
|||||||
8
go.sum
8
go.sum
@@ -170,6 +170,8 @@ github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwo
|
|||||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||||
github.com/fluxcd/kustomize-controller v0.0.1 h1:F2wg9c5nMUEnPHgs44HMY1/2UAXXaYcmpj7WeOzf9p0=
|
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/kustomize-controller v0.0.1/go.mod h1:sSIy+Y924OGHW2anzZvD53BbgjSOO4mONTTG2+UTEhM=
|
||||||
|
github.com/fluxcd/pkg v0.0.1 h1:yECp5SBjX7vUBOjd3KYBoVQwt22A0u1SZJjYV4PduAk=
|
||||||
|
github.com/fluxcd/pkg v0.0.1/go.mod h1:3DgEcVmkVYrA/BDb/fyDIJllxK++c/ovLCMPRlkAp9Y=
|
||||||
github.com/fluxcd/source-controller v0.0.1 h1:17/b/Zcb3OUkUoo03W+L7TGwkCKG23K9HrgL+d5WMXE=
|
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/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 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ=
|
||||||
@@ -195,6 +197,8 @@ github.com/go-git/go-git-fixtures/v4 v4.0.1 h1:q+IFMfLx200Q3scvt2hN79JsEzy4AmBTp
|
|||||||
github.com/go-git/go-git-fixtures/v4 v4.0.1/go.mod h1:m+ICp2rF3jDhFgEZ/8yziagdT1C+ZpZcrJjappBCDSw=
|
github.com/go-git/go-git-fixtures/v4 v4.0.1/go.mod h1:m+ICp2rF3jDhFgEZ/8yziagdT1C+ZpZcrJjappBCDSw=
|
||||||
github.com/go-git/go-git/v5 v5.0.0 h1:k5RWPm4iJwYtfWoxIJy4wJX9ON7ihPeZZYC1fLYDnpg=
|
github.com/go-git/go-git/v5 v5.0.0 h1:k5RWPm4iJwYtfWoxIJy4wJX9ON7ihPeZZYC1fLYDnpg=
|
||||||
github.com/go-git/go-git/v5 v5.0.0/go.mod h1:oYD8y9kWsGINPFJoLdaScGCN6dlKg23blmClfZwtUVA=
|
github.com/go-git/go-git/v5 v5.0.0/go.mod h1:oYD8y9kWsGINPFJoLdaScGCN6dlKg23blmClfZwtUVA=
|
||||||
|
github.com/go-git/go-git/v5 v5.1.0 h1:HxJn9g/E7eYvKW3Fm7Jt4ee8LXfPOm/H1cdDu8vEssk=
|
||||||
|
github.com/go-git/go-git/v5 v5.1.0/go.mod h1:ZKfuPUoY1ZqIG4QG9BDBh3G4gLM5zvPuSJAozQrZuyM=
|
||||||
github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
|
github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
|
||||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||||
github.com/go-lintpack/lintpack v0.5.2/go.mod h1:NwZuYi2nUHho8XEIZ6SIxihrnPoqBTDqfpXvXAN0sXM=
|
github.com/go-lintpack/lintpack v0.5.2/go.mod h1:NwZuYi2nUHho8XEIZ6SIxihrnPoqBTDqfpXvXAN0sXM=
|
||||||
@@ -394,6 +398,8 @@ github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJ
|
|||||||
github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||||
github.com/imdario/mergo v0.3.7 h1:Y+UAYTZ7gDEuOfhxKWy+dvb5dRQ6rJjFSdX2HZY1/gI=
|
github.com/imdario/mergo v0.3.7 h1:Y+UAYTZ7gDEuOfhxKWy+dvb5dRQ6rJjFSdX2HZY1/gI=
|
||||||
github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||||
|
github.com/imdario/mergo v0.3.9 h1:UauaLniWCFHWd+Jp9oCEkTBj8VO/9DKg3PV3VCNMDIg=
|
||||||
|
github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||||
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
|
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
|
||||||
@@ -688,6 +694,8 @@ golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975 h1:/Tl7pH94bvbAAHBdZJT947
|
|||||||
golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073 h1:xMPOj6Pz6UipU1wXLkrtqpHbR0AVFnyPEQq/wRWz9lM=
|
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073 h1:xMPOj6Pz6UipU1wXLkrtqpHbR0AVFnyPEQq/wRWz9lM=
|
||||||
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
|
||||||
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20190312203227-4b39c73a6495/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
golang.org/x/exp v0.0.0-20190312203227-4b39c73a6495/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||||
|
|||||||
@@ -1,5 +1,12 @@
|
|||||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
kind: Kustomization
|
kind: Kustomization
|
||||||
resources:
|
resources:
|
||||||
- github.com/fluxcd/kustomize-controller/config//crd?ref=v0.0.1
|
- github.com/fluxcd/kustomize-controller/config//crd?ref=v0.0.2
|
||||||
- github.com/fluxcd/kustomize-controller/config//manager?ref=v0.0.1
|
- github.com/fluxcd/kustomize-controller/config//manager?ref=v0.0.2
|
||||||
|
patchesJson6902:
|
||||||
|
- target:
|
||||||
|
group: apps
|
||||||
|
version: v1
|
||||||
|
kind: Deployment
|
||||||
|
name: kustomize-controller
|
||||||
|
path: patch.yaml
|
||||||
|
|||||||
3
manifests/bases/kustomize-controller/patch.yaml
Normal file
3
manifests/bases/kustomize-controller/patch.yaml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
- op: add
|
||||||
|
path: /spec/template/spec/containers/0/args/0
|
||||||
|
value: --events-addr=http://notification-controller/
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
|
kind: Kustomization
|
||||||
|
resources:
|
||||||
|
- github.com/fluxcd/notification-controller/config//crd?ref=v0.0.1-alpha.2
|
||||||
|
- github.com/fluxcd/notification-controller/config//manager?ref=v0.0.1-alpha.2
|
||||||
@@ -5,6 +5,7 @@ resources:
|
|||||||
- namespace.yaml
|
- namespace.yaml
|
||||||
- ../bases/source-controller
|
- ../bases/source-controller
|
||||||
- ../bases/kustomize-controller
|
- ../bases/kustomize-controller
|
||||||
|
- ../bases/notification-controller
|
||||||
- ../rbac
|
- ../rbac
|
||||||
- ../policies
|
- ../policies
|
||||||
transformers:
|
transformers:
|
||||||
|
|||||||
@@ -9,6 +9,9 @@ rules:
|
|||||||
- apiGroups: ['kustomize.fluxcd.io']
|
- apiGroups: ['kustomize.fluxcd.io']
|
||||||
resources: ['*']
|
resources: ['*']
|
||||||
verbs: ['*']
|
verbs: ['*']
|
||||||
|
- apiGroups: ['notification.fluxcd.io']
|
||||||
|
resources: ['*']
|
||||||
|
verbs: ['*']
|
||||||
- apiGroups:
|
- apiGroups:
|
||||||
- ""
|
- ""
|
||||||
resources:
|
resources:
|
||||||
|
|||||||
15
mkdocs.yml
15
mkdocs.yml
@@ -1,7 +1,7 @@
|
|||||||
site_name: GitOps Toolkit
|
site_name: GitOps Toolkit
|
||||||
site_description: Documentation for GitOps Toolkit.
|
site_description: A toolkit for assembling GitOps pipelines on Kubernetes
|
||||||
site_author: The Flux CD contributors
|
site_author: The Flux CD contributors
|
||||||
site_url: https://fluxcd.github.io/toolkit/
|
site_url: https://toolkit.fluxcd.io
|
||||||
|
|
||||||
repo_name: fluxcd/toolkit
|
repo_name: fluxcd/toolkit
|
||||||
repo_url: https://github.com/fluxcd/toolkit
|
repo_url: https://github.com/fluxcd/toolkit
|
||||||
@@ -14,6 +14,7 @@ theme:
|
|||||||
palette:
|
palette:
|
||||||
primary: blue
|
primary: blue
|
||||||
accent: indigo
|
accent: indigo
|
||||||
|
custom_dir: mkdocs/
|
||||||
|
|
||||||
docs_dir: docs
|
docs_dir: docs
|
||||||
|
|
||||||
@@ -25,6 +26,7 @@ plugins:
|
|||||||
|
|
||||||
markdown_extensions:
|
markdown_extensions:
|
||||||
- admonition
|
- admonition
|
||||||
|
- meta
|
||||||
- codehilite:
|
- codehilite:
|
||||||
guess_lang: false
|
guess_lang: false
|
||||||
- toc:
|
- toc:
|
||||||
@@ -32,10 +34,13 @@ markdown_extensions:
|
|||||||
- pymdownx.superfences:
|
- pymdownx.superfences:
|
||||||
highlight_code: true
|
highlight_code: true
|
||||||
- pymdownx.tabbed
|
- pymdownx.tabbed
|
||||||
|
- pymdownx.tilde
|
||||||
|
|
||||||
nav:
|
nav:
|
||||||
- Introduction: index.md
|
- Introduction: index.md
|
||||||
- Get Started: get-started/index.md
|
- Get Started: get-started/index.md
|
||||||
|
- Guides:
|
||||||
|
- Setup Notifications: guides/notifications.md
|
||||||
- Toolkit Components:
|
- Toolkit Components:
|
||||||
- Source Controller:
|
- Source Controller:
|
||||||
- Overview: components/source/controller.md
|
- Overview: components/source/controller.md
|
||||||
@@ -46,6 +51,12 @@ nav:
|
|||||||
- Overview: components/kustomize/controller.md
|
- Overview: components/kustomize/controller.md
|
||||||
- Kustomization CRD: components/kustomize/kustomization.md
|
- Kustomization CRD: components/kustomize/kustomization.md
|
||||||
- Kustomize API Reference: components/kustomize/api.md
|
- Kustomize API Reference: components/kustomize/api.md
|
||||||
|
- Notification Controller:
|
||||||
|
- Overview: components/notification/controller.md
|
||||||
|
- Provider CRD: components/notification/provider.md
|
||||||
|
- Alert CRD: components/notification/alert.md
|
||||||
|
- Event: components/notification/event.md
|
||||||
|
- Notification API Reference: components/notification/api.md
|
||||||
- Toolkit CLI:
|
- Toolkit CLI:
|
||||||
- Overview: cmd/tk.md
|
- Overview: cmd/tk.md
|
||||||
- Bootstrap: cmd/tk_bootstrap.md
|
- Bootstrap: cmd/tk_bootstrap.md
|
||||||
|
|||||||
32
mkdocs/main.html
Normal file
32
mkdocs/main.html
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block extrahead %}
|
||||||
|
|
||||||
|
<meta property="og:url" content="{{ page.canonical_url }}">
|
||||||
|
{% if page and page.meta and page.meta.title %}
|
||||||
|
<meta property="og:title" content="{{ page.meta.title }}">
|
||||||
|
{% elif page and page.title and not page.is_homepage %}
|
||||||
|
<meta property="og:title" content="{{ page.title }} - {{ config.site_name }}">
|
||||||
|
{% else %}
|
||||||
|
<meta property="og:title" content="{{ config.site_name }}">
|
||||||
|
{% endif %}
|
||||||
|
<meta property="og:description" content="{{ config.site_description }}">
|
||||||
|
<meta property="og:image" content="https://toolkit.fluxcd.io/_files/toolkit-icon.png">
|
||||||
|
<meta property="og:image:alt" content="GitOps Toolkit">
|
||||||
|
<meta property="og:image:type" content="image/png">
|
||||||
|
|
||||||
|
<meta name="twitter:card" content="summary">
|
||||||
|
<meta name="twitter:site" content="@stefanprodan">
|
||||||
|
<meta name="twitter:creator" content="@stefanprodan">
|
||||||
|
{% if page and page.meta and page.meta.title %}
|
||||||
|
<meta property="twitter:title" content="{{ page.meta.title }}">
|
||||||
|
{% elif page and page.title and not page.is_homepage %}
|
||||||
|
<meta property="twitter:title" content="{{ page.title }} - {{ config.site_name }}">
|
||||||
|
{% else %}
|
||||||
|
<meta property="twitter:title" content="{{ config.site_name }}">
|
||||||
|
{% endif %}
|
||||||
|
<meta name="twitter:description" content="{{ config.site_description }}">
|
||||||
|
<meta name="twitter:image" content="https://toolkit.fluxcd.io/_files/toolkit-icon.png">
|
||||||
|
<meta name="twitter:image:alt" content="GitOps Toolkit">
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
@@ -1,177 +0,0 @@
|
|||||||
/*
|
|
||||||
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
|
|
||||||
}
|
|
||||||
@@ -1,163 +0,0 @@
|
|||||||
/*
|
|
||||||
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
|
|
||||||
}
|
|
||||||
@@ -1,167 +0,0 @@
|
|||||||
/*
|
|
||||||
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
|
|
||||||
}
|
|
||||||
@@ -13,14 +13,17 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
package log
|
||||||
|
|
||||||
package git
|
type Logger interface {
|
||||||
|
// Actionf logs a formatted action message.
|
||||||
import "context"
|
Actionf(format string, a ...interface{})
|
||||||
|
// Generatef logs a formatted generate message.
|
||||||
// Provider is the interface that a git provider should implement
|
Generatef(format string, a ...interface{})
|
||||||
type Provider interface {
|
// Waitingf logs a formatted waiting message.
|
||||||
CreateRepository(ctx context.Context, r *Repository) (bool, error)
|
Waitingf(format string, a ...interface{})
|
||||||
AddTeam(ctx context.Context, r *Repository, name, permission string) (bool, error)
|
// Waitingf logs a formatted success message.
|
||||||
AddDeployKey(ctx context.Context, r *Repository, key, keyName string) (bool, error)
|
Successf(format string, a ...interface{})
|
||||||
|
// Failuref logs a formatted failure message.
|
||||||
|
Failuref(format string, a ...interface{})
|
||||||
}
|
}
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
/*
|
|
||||||
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 (
|
|
||||||
"encoding/base64"
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"golang.org/x/crypto/ssh"
|
|
||||||
"golang.org/x/crypto/ssh/knownhosts"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ScanHostKey collects the given host's preferred public key for the
|
|
||||||
// Any errors (e.g. authentication failures) are ignored, except if
|
|
||||||
// no key could be collected from the host.
|
|
||||||
func ScanHostKey(host string, timeout time.Duration) ([]byte, error) {
|
|
||||||
col := &HostKeyCollector{}
|
|
||||||
config := &ssh.ClientConfig{
|
|
||||||
HostKeyCallback: col.StoreKey(),
|
|
||||||
Timeout: timeout,
|
|
||||||
}
|
|
||||||
client, err := ssh.Dial("tcp", host, config)
|
|
||||||
if err == nil {
|
|
||||||
defer client.Close()
|
|
||||||
}
|
|
||||||
if len(col.knownKeys) > 0 {
|
|
||||||
return col.knownKeys, nil
|
|
||||||
}
|
|
||||||
return col.knownKeys, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// HostKeyCollector offers a StoreKey method which provides an
|
|
||||||
// HostKeyCallBack to collect public keys from an SSH server.
|
|
||||||
type HostKeyCollector struct {
|
|
||||||
knownKeys []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
// StoreKey stores the public key in bytes as returned by the host.
|
|
||||||
// To collect multiple public key types from the host, multiple
|
|
||||||
// SSH dials need with the ClientConfig HostKeyAlgorithms set to
|
|
||||||
// the algorithm you want to collect.
|
|
||||||
func (c *HostKeyCollector) StoreKey() ssh.HostKeyCallback {
|
|
||||||
return func(hostname string, remote net.Addr, key ssh.PublicKey) error {
|
|
||||||
c.knownKeys = append(
|
|
||||||
c.knownKeys,
|
|
||||||
fmt.Sprintf("%s %s %s\n", knownhosts.Normalize(hostname), key.Type(), base64.StdEncoding.EncodeToString(key.Marshal()))...,
|
|
||||||
)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetKnownKeys returns the collected public keys in bytes.
|
|
||||||
func (c *HostKeyCollector) GetKnownKeys() []byte {
|
|
||||||
return c.knownKeys
|
|
||||||
}
|
|
||||||
@@ -1,146 +0,0 @@
|
|||||||
/*
|
|
||||||
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 (
|
|
||||||
"crypto/ecdsa"
|
|
||||||
"crypto/ed25519"
|
|
||||||
"crypto/elliptic"
|
|
||||||
"crypto/rand"
|
|
||||||
"crypto/rsa"
|
|
||||||
"crypto/x509"
|
|
||||||
"encoding/pem"
|
|
||||||
|
|
||||||
"golang.org/x/crypto/ssh"
|
|
||||||
)
|
|
||||||
|
|
||||||
// KeyPair holds the public and private key PEM block bytes.
|
|
||||||
type KeyPair struct {
|
|
||||||
PublicKey []byte
|
|
||||||
PrivateKey []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
type KeyPairGenerator interface {
|
|
||||||
Generate() (*KeyPair, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
type RSAGenerator struct {
|
|
||||||
bits int
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewRSAGenerator(bits int) KeyPairGenerator {
|
|
||||||
return &RSAGenerator{bits}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g *RSAGenerator) Generate() (*KeyPair, error) {
|
|
||||||
pk, err := rsa.GenerateKey(rand.Reader, g.bits)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
err = pk.Validate()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
pub, err := generatePublicKey(&pk.PublicKey)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
priv, err := encodePrivateKeyToPEM(pk)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &KeyPair{
|
|
||||||
PublicKey: pub,
|
|
||||||
PrivateKey: priv,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type ECDSAGenerator struct {
|
|
||||||
c elliptic.Curve
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewECDSAGenerator(c elliptic.Curve) KeyPairGenerator {
|
|
||||||
return &ECDSAGenerator{c}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g *ECDSAGenerator) Generate() (*KeyPair, error) {
|
|
||||||
pk, err := ecdsa.GenerateKey(g.c, rand.Reader)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
pub, err := generatePublicKey(&pk.PublicKey)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
priv, err := encodePrivateKeyToPEM(pk)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &KeyPair{
|
|
||||||
PublicKey: pub,
|
|
||||||
PrivateKey: priv,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type Ed25519Generator struct{}
|
|
||||||
|
|
||||||
func NewEd25519Generator() KeyPairGenerator {
|
|
||||||
return &Ed25519Generator{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g *Ed25519Generator) Generate() (*KeyPair, error) {
|
|
||||||
pk, pv, err := ed25519.GenerateKey(rand.Reader)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
pub, err := generatePublicKey(pk)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
priv, err := encodePrivateKeyToPEM(pv)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &KeyPair{
|
|
||||||
PublicKey: pub,
|
|
||||||
PrivateKey: priv,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func generatePublicKey(pk interface{}) ([]byte, error) {
|
|
||||||
b, err := ssh.NewPublicKey(pk)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
k := ssh.MarshalAuthorizedKey(b)
|
|
||||||
return k, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// encodePrivateKeyToPEM encodes the given private key to a PEM block.
|
|
||||||
// The encoded format is PKCS#8 for universal support of the most
|
|
||||||
// common key types (rsa, ecdsa, ed25519).
|
|
||||||
func encodePrivateKeyToPEM(pk interface{}) ([]byte, error) {
|
|
||||||
b, err := x509.MarshalPKCS8PrivateKey(pk)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
block := pem.Block{
|
|
||||||
Type: "PRIVATE KEY",
|
|
||||||
Bytes: b,
|
|
||||||
}
|
|
||||||
return pem.EncodeToMemory(&block), nil
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
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.
|
|
||||||
@@ -1,446 +0,0 @@
|
|||||||
// Copyright 2017 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// Copyright 2020 The FluxCD contributors. All rights reserved.
|
|
||||||
// This package provides an in-memory known hosts database
|
|
||||||
// derived from the golang.org/x/crypto/ssh/knownhosts
|
|
||||||
// package.
|
|
||||||
// It has been slightly modified and adapted to work with
|
|
||||||
// in-memory host keys not related to any known_hosts files
|
|
||||||
// on disk, and the database can be initialized with just a
|
|
||||||
// known_hosts byte blob.
|
|
||||||
// https://pkg.go.dev/golang.org/x/crypto/ssh/knownhosts
|
|
||||||
|
|
||||||
package knownhosts
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"bytes"
|
|
||||||
"crypto/hmac"
|
|
||||||
"crypto/sha1"
|
|
||||||
"encoding/base64"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"net"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"golang.org/x/crypto/ssh"
|
|
||||||
"golang.org/x/crypto/ssh/knownhosts"
|
|
||||||
)
|
|
||||||
|
|
||||||
// See the sshd manpage
|
|
||||||
// (http://man.openbsd.org/sshd#SSH_KNOWN_HOSTS_FILE_FORMAT) for
|
|
||||||
// background.
|
|
||||||
|
|
||||||
type addr struct{ host, port string }
|
|
||||||
|
|
||||||
func (a *addr) String() string {
|
|
||||||
h := a.host
|
|
||||||
if strings.Contains(h, ":") {
|
|
||||||
h = "[" + h + "]"
|
|
||||||
}
|
|
||||||
return h + ":" + a.port
|
|
||||||
}
|
|
||||||
|
|
||||||
type matcher interface {
|
|
||||||
match(addr) bool
|
|
||||||
}
|
|
||||||
|
|
||||||
type hostPattern struct {
|
|
||||||
negate bool
|
|
||||||
addr addr
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *hostPattern) String() string {
|
|
||||||
n := ""
|
|
||||||
if p.negate {
|
|
||||||
n = "!"
|
|
||||||
}
|
|
||||||
|
|
||||||
return n + p.addr.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
type hostPatterns []hostPattern
|
|
||||||
|
|
||||||
func (ps hostPatterns) match(a addr) bool {
|
|
||||||
matched := false
|
|
||||||
for _, p := range ps {
|
|
||||||
if !p.match(a) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if p.negate {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
matched = true
|
|
||||||
}
|
|
||||||
return matched
|
|
||||||
}
|
|
||||||
|
|
||||||
// See
|
|
||||||
// https://android.googlesource.com/platform/external/openssh/+/ab28f5495c85297e7a597c1ba62e996416da7c7e/addrmatch.c
|
|
||||||
// The matching of * has no regard for separators, unlike filesystem globs
|
|
||||||
func wildcardMatch(pat []byte, str []byte) bool {
|
|
||||||
for {
|
|
||||||
if len(pat) == 0 {
|
|
||||||
return len(str) == 0
|
|
||||||
}
|
|
||||||
if len(str) == 0 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if pat[0] == '*' {
|
|
||||||
if len(pat) == 1 {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
for j := range str {
|
|
||||||
if wildcardMatch(pat[1:], str[j:]) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if pat[0] == '?' || pat[0] == str[0] {
|
|
||||||
pat = pat[1:]
|
|
||||||
str = str[1:]
|
|
||||||
} else {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *hostPattern) match(a addr) bool {
|
|
||||||
return wildcardMatch([]byte(p.addr.host), []byte(a.host)) && p.addr.port == a.port
|
|
||||||
}
|
|
||||||
|
|
||||||
type inMemoryHostKeyDB struct {
|
|
||||||
hostKeys []hostKey
|
|
||||||
revoked map[string]*ssh.PublicKey
|
|
||||||
}
|
|
||||||
|
|
||||||
func newInMemoryHostKeyDB() *inMemoryHostKeyDB {
|
|
||||||
db := &inMemoryHostKeyDB{
|
|
||||||
revoked: make(map[string]*ssh.PublicKey),
|
|
||||||
}
|
|
||||||
|
|
||||||
return db
|
|
||||||
}
|
|
||||||
|
|
||||||
func keyEq(a, b ssh.PublicKey) bool {
|
|
||||||
return bytes.Equal(a.Marshal(), b.Marshal())
|
|
||||||
}
|
|
||||||
|
|
||||||
type hostKey struct {
|
|
||||||
matcher matcher
|
|
||||||
cert bool
|
|
||||||
key ssh.PublicKey
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *hostKey) match(a addr) bool {
|
|
||||||
return l.matcher.match(a)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsAuthorityForHost can be used as a callback in ssh.CertChecker
|
|
||||||
func (db *inMemoryHostKeyDB) IsHostAuthority(remote ssh.PublicKey, address string) bool {
|
|
||||||
h, p, err := net.SplitHostPort(address)
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
a := addr{host: h, port: p}
|
|
||||||
|
|
||||||
for _, l := range db.hostKeys {
|
|
||||||
if l.cert && keyEq(l.key, remote) && l.match(a) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsRevoked can be used as a callback in ssh.CertChecker
|
|
||||||
func (db *inMemoryHostKeyDB) IsRevoked(key *ssh.Certificate) bool {
|
|
||||||
_, ok := db.revoked[string(key.Marshal())]
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
const markerCert = "@cert-authority"
|
|
||||||
const markerRevoked = "@revoked"
|
|
||||||
|
|
||||||
func nextWord(line []byte) (string, []byte) {
|
|
||||||
i := bytes.IndexAny(line, "\t ")
|
|
||||||
if i == -1 {
|
|
||||||
return string(line), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return string(line[:i]), bytes.TrimSpace(line[i:])
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseLine(line []byte) (marker, host string, key ssh.PublicKey, err error) {
|
|
||||||
if w, next := nextWord(line); w == markerCert || w == markerRevoked {
|
|
||||||
marker = w
|
|
||||||
line = next
|
|
||||||
}
|
|
||||||
|
|
||||||
host, line = nextWord(line)
|
|
||||||
if len(line) == 0 {
|
|
||||||
return "", "", nil, errors.New("knownhosts: missing host pattern")
|
|
||||||
}
|
|
||||||
|
|
||||||
// ignore the keytype as it's in the key blob anyway.
|
|
||||||
_, line = nextWord(line)
|
|
||||||
if len(line) == 0 {
|
|
||||||
return "", "", nil, errors.New("knownhosts: missing key type pattern")
|
|
||||||
}
|
|
||||||
|
|
||||||
keyBlob, _ := nextWord(line)
|
|
||||||
|
|
||||||
keyBytes, err := base64.StdEncoding.DecodeString(keyBlob)
|
|
||||||
if err != nil {
|
|
||||||
return "", "", nil, err
|
|
||||||
}
|
|
||||||
key, err = ssh.ParsePublicKey(keyBytes)
|
|
||||||
if err != nil {
|
|
||||||
return "", "", nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return marker, host, key, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db *inMemoryHostKeyDB) parseLine(line []byte) error {
|
|
||||||
marker, pattern, key, err := parseLine(line)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if marker == markerRevoked {
|
|
||||||
db.revoked[string(key.Marshal())] = &key
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
entry := hostKey{
|
|
||||||
key: key,
|
|
||||||
cert: marker == markerCert,
|
|
||||||
}
|
|
||||||
|
|
||||||
if pattern[0] == '|' {
|
|
||||||
entry.matcher, err = newHashedHost(pattern)
|
|
||||||
} else {
|
|
||||||
entry.matcher, err = newHostnameMatcher(pattern)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
db.hostKeys = append(db.hostKeys, entry)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func newHostnameMatcher(pattern string) (matcher, error) {
|
|
||||||
var hps hostPatterns
|
|
||||||
for _, p := range strings.Split(pattern, ",") {
|
|
||||||
if len(p) == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
var a addr
|
|
||||||
var negate bool
|
|
||||||
if p[0] == '!' {
|
|
||||||
negate = true
|
|
||||||
p = p[1:]
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(p) == 0 {
|
|
||||||
return nil, errors.New("knownhosts: negation without following hostname")
|
|
||||||
}
|
|
||||||
|
|
||||||
var err error
|
|
||||||
if p[0] == '[' {
|
|
||||||
a.host, a.port, err = net.SplitHostPort(p)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
a.host, a.port, err = net.SplitHostPort(p)
|
|
||||||
if err != nil {
|
|
||||||
a.host = p
|
|
||||||
a.port = "22"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
hps = append(hps, hostPattern{
|
|
||||||
negate: negate,
|
|
||||||
addr: a,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return hps, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// check checks a key against the host database. This should not be
|
|
||||||
// used for verifying certificates.
|
|
||||||
func (db *inMemoryHostKeyDB) check(address string, remote net.Addr, remoteKey ssh.PublicKey) error {
|
|
||||||
if revoked := db.revoked[string(remoteKey.Marshal())]; revoked != nil {
|
|
||||||
return &knownhosts.RevokedError{Revoked: knownhosts.KnownKey{Key: *revoked}}
|
|
||||||
}
|
|
||||||
|
|
||||||
host, port, err := net.SplitHostPort(remote.String())
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("knownhosts: SplitHostPort(%s): %v", remote, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
hostToCheck := addr{host, port}
|
|
||||||
if address != "" {
|
|
||||||
// Give preference to the hostname if available.
|
|
||||||
host, port, err := net.SplitHostPort(address)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("knownhosts: SplitHostPort(%s): %v", address, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
hostToCheck = addr{host, port}
|
|
||||||
}
|
|
||||||
|
|
||||||
return db.checkAddr(hostToCheck, remoteKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
// checkAddr checks if we can find the given public key for the
|
|
||||||
// given address. If we only find an entry for the IP address,
|
|
||||||
// or only the hostname, then this still succeeds.
|
|
||||||
func (db *inMemoryHostKeyDB) checkAddr(a addr, remoteKey ssh.PublicKey) error {
|
|
||||||
// TODO(hanwen): are these the right semantics? What if there
|
|
||||||
// is just a key for the IP address, but not for the
|
|
||||||
// hostname?
|
|
||||||
|
|
||||||
// Algorithm => key.
|
|
||||||
knownKeys := map[string]ssh.PublicKey{}
|
|
||||||
for _, l := range db.hostKeys {
|
|
||||||
if l.match(a) {
|
|
||||||
typ := l.key.Type()
|
|
||||||
if _, ok := knownKeys[typ]; !ok {
|
|
||||||
knownKeys[typ] = l.key
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
keyErr := &knownhosts.KeyError{}
|
|
||||||
for _, v := range knownKeys {
|
|
||||||
keyErr.Want = append(keyErr.Want, knownhosts.KnownKey{Key: v})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unknown remote host.
|
|
||||||
if len(knownKeys) == 0 {
|
|
||||||
return keyErr
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the remote host starts using a different, unknown key type, we
|
|
||||||
// also interpret that as a mismatch.
|
|
||||||
if known, ok := knownKeys[remoteKey.Type()]; !ok || !keyEq(known, remoteKey) {
|
|
||||||
return keyErr
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// The Read function parses file contents.
|
|
||||||
func (db *inMemoryHostKeyDB) Read(r io.Reader) error {
|
|
||||||
scanner := bufio.NewScanner(r)
|
|
||||||
|
|
||||||
lineNum := 0
|
|
||||||
for scanner.Scan() {
|
|
||||||
lineNum++
|
|
||||||
line := scanner.Bytes()
|
|
||||||
line = bytes.TrimSpace(line)
|
|
||||||
if len(line) == 0 || line[0] == '#' {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := db.parseLine(line); err != nil {
|
|
||||||
return fmt.Errorf("knownhosts: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return scanner.Err()
|
|
||||||
}
|
|
||||||
|
|
||||||
// New creates a host key callback from the given OpenSSH host key
|
|
||||||
// file bytes. The returned callback is for use in
|
|
||||||
// ssh.ClientConfig.HostKeyCallback. By preference, the key check
|
|
||||||
// operates on the hostname if available, i.e. if a server changes its
|
|
||||||
// IP address, the host key check will still succeed, even though a
|
|
||||||
// record of the new IP address is not available.
|
|
||||||
func New(b []byte) (ssh.HostKeyCallback, error) {
|
|
||||||
db := newInMemoryHostKeyDB()
|
|
||||||
r := bytes.NewReader(b)
|
|
||||||
if err := db.Read(r); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var certChecker ssh.CertChecker
|
|
||||||
certChecker.IsHostAuthority = db.IsHostAuthority
|
|
||||||
certChecker.IsRevoked = db.IsRevoked
|
|
||||||
certChecker.HostKeyFallback = db.check
|
|
||||||
|
|
||||||
return certChecker.CheckHostKey, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func decodeHash(encoded string) (hashType string, salt, hash []byte, err error) {
|
|
||||||
if len(encoded) == 0 || encoded[0] != '|' {
|
|
||||||
err = errors.New("knownhosts: hashed host must start with '|'")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
components := strings.Split(encoded, "|")
|
|
||||||
if len(components) != 4 {
|
|
||||||
err = fmt.Errorf("knownhosts: got %d components, want 3", len(components))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
hashType = components[1]
|
|
||||||
if salt, err = base64.StdEncoding.DecodeString(components[2]); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if hash, err = base64.StdEncoding.DecodeString(components[3]); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func encodeHash(typ string, salt []byte, hash []byte) string {
|
|
||||||
return strings.Join([]string{"",
|
|
||||||
typ,
|
|
||||||
base64.StdEncoding.EncodeToString(salt),
|
|
||||||
base64.StdEncoding.EncodeToString(hash),
|
|
||||||
}, "|")
|
|
||||||
}
|
|
||||||
|
|
||||||
// See https://android.googlesource.com/platform/external/openssh/+/ab28f5495c85297e7a597c1ba62e996416da7c7e/hostfile.c#120
|
|
||||||
func hashHost(hostname string, salt []byte) []byte {
|
|
||||||
mac := hmac.New(sha1.New, salt)
|
|
||||||
mac.Write([]byte(hostname))
|
|
||||||
return mac.Sum(nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
type hashedHost struct {
|
|
||||||
salt []byte
|
|
||||||
hash []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
const sha1HashType = "1"
|
|
||||||
|
|
||||||
func newHashedHost(encoded string) (*hashedHost, error) {
|
|
||||||
typ, salt, hash, err := decodeHash(encoded)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// The type field seems for future algorithm agility, but it's
|
|
||||||
// actually hardcoded in openssh currently, see
|
|
||||||
// https://android.googlesource.com/platform/external/openssh/+/ab28f5495c85297e7a597c1ba62e996416da7c7e/hostfile.c#120
|
|
||||||
if typ != sha1HashType {
|
|
||||||
return nil, fmt.Errorf("knownhosts: got hash type %s, must be '1'", typ)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &hashedHost{salt: salt, hash: hash}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *hashedHost) match(a addr) bool {
|
|
||||||
return bytes.Equal(hashHost(knownhosts.Normalize(a.String()), h.salt), h.hash)
|
|
||||||
}
|
|
||||||
@@ -1,327 +0,0 @@
|
|||||||
// Copyright 2017 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// Copyright 2020 The FluxCD contributors. All rights reserved.
|
|
||||||
// This package provides an in-memory known hosts database
|
|
||||||
// derived from the golang.org/x/crypto/ssh/knownhosts
|
|
||||||
// package.
|
|
||||||
// It has been slightly modified and adapted to work with
|
|
||||||
// in-memory host keys not related to any known_hosts files
|
|
||||||
// on disk, and the database can be initialized with just a
|
|
||||||
// known_hosts byte blob.
|
|
||||||
// https://pkg.go.dev/golang.org/x/crypto/ssh/knownhosts
|
|
||||||
|
|
||||||
package knownhosts
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"golang.org/x/crypto/ssh"
|
|
||||||
"golang.org/x/crypto/ssh/knownhosts"
|
|
||||||
)
|
|
||||||
|
|
||||||
const edKeyStr = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGBAarftlLeoyf+v+nVchEZII/vna2PCV8FaX4vsF5BX"
|
|
||||||
const alternateEdKeyStr = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIXffBYeYL+WVzVru8npl5JHt2cjlr4ornFTWzoij9sx"
|
|
||||||
const ecKeyStr = "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBNLCu01+wpXe3xB5olXCN4SqU2rQu0qjSRKJO4Bg+JRCPU+ENcgdA5srTU8xYDz/GEa4dzK5ldPw4J/gZgSXCMs="
|
|
||||||
|
|
||||||
var ecKey, alternateEdKey, edKey ssh.PublicKey
|
|
||||||
var testAddr = &net.TCPAddr{
|
|
||||||
IP: net.IP{198, 41, 30, 196},
|
|
||||||
Port: 22,
|
|
||||||
}
|
|
||||||
|
|
||||||
var testAddr6 = &net.TCPAddr{
|
|
||||||
IP: net.IP{198, 41, 30, 196,
|
|
||||||
1, 2, 3, 4,
|
|
||||||
1, 2, 3, 4,
|
|
||||||
1, 2, 3, 4,
|
|
||||||
},
|
|
||||||
Port: 22,
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
var err error
|
|
||||||
ecKey, _, _, _, err = ssh.ParseAuthorizedKey([]byte(ecKeyStr))
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
edKey, _, _, _, err = ssh.ParseAuthorizedKey([]byte(edKeyStr))
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
alternateEdKey, _, _, _, err = ssh.ParseAuthorizedKey([]byte(alternateEdKeyStr))
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func testDB(t *testing.T, s string) *inMemoryHostKeyDB {
|
|
||||||
db := newInMemoryHostKeyDB()
|
|
||||||
if err := db.Read(bytes.NewBufferString(s)); err != nil {
|
|
||||||
t.Fatalf("Read: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return db
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRevoked(t *testing.T) {
|
|
||||||
db := testDB(t, "\n\n@revoked * "+edKeyStr+"\n")
|
|
||||||
want := &knownhosts.RevokedError{
|
|
||||||
Revoked: knownhosts.KnownKey{
|
|
||||||
Key: edKey,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
if err := db.check("", &net.TCPAddr{
|
|
||||||
Port: 42,
|
|
||||||
}, edKey); err == nil {
|
|
||||||
t.Fatal("no error for revoked key")
|
|
||||||
} else if !reflect.DeepEqual(want, err) {
|
|
||||||
t.Fatalf("got %#v, want %#v", want, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestHostAuthority(t *testing.T) {
|
|
||||||
for _, m := range []struct {
|
|
||||||
authorityFor string
|
|
||||||
address string
|
|
||||||
|
|
||||||
good bool
|
|
||||||
}{
|
|
||||||
{authorityFor: "localhost", address: "localhost:22", good: true},
|
|
||||||
{authorityFor: "localhost", address: "localhost", good: false},
|
|
||||||
{authorityFor: "localhost", address: "localhost:1234", good: false},
|
|
||||||
{authorityFor: "[localhost]:1234", address: "localhost:1234", good: true},
|
|
||||||
{authorityFor: "[localhost]:1234", address: "localhost:22", good: false},
|
|
||||||
{authorityFor: "[localhost]:1234", address: "localhost", good: false},
|
|
||||||
} {
|
|
||||||
db := testDB(t, `@cert-authority `+m.authorityFor+` `+edKeyStr)
|
|
||||||
if ok := db.IsHostAuthority(db.hostKeys[0].key, m.address); ok != m.good {
|
|
||||||
t.Errorf("IsHostAuthority: authority %s, address %s, wanted good = %v, got good = %v",
|
|
||||||
m.authorityFor, m.address, m.good, ok)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBracket(t *testing.T) {
|
|
||||||
db := testDB(t, `[git.eclipse.org]:29418,[198.41.30.196]:29418 `+edKeyStr)
|
|
||||||
|
|
||||||
if err := db.check("git.eclipse.org:29418", &net.TCPAddr{
|
|
||||||
IP: net.IP{198, 41, 30, 196},
|
|
||||||
Port: 29418,
|
|
||||||
}, edKey); err != nil {
|
|
||||||
t.Errorf("got error %v, want none", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := db.check("git.eclipse.org:29419", &net.TCPAddr{
|
|
||||||
Port: 42,
|
|
||||||
}, edKey); err == nil {
|
|
||||||
t.Fatalf("no error for unknown address")
|
|
||||||
} else if ke, ok := err.(*knownhosts.KeyError); !ok {
|
|
||||||
t.Fatalf("got type %T, want *KeyError", err)
|
|
||||||
} else if len(ke.Want) > 0 {
|
|
||||||
t.Fatalf("got Want %v, want []", ke.Want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNewKeyType(t *testing.T) {
|
|
||||||
str := fmt.Sprintf("%s %s", testAddr, edKeyStr)
|
|
||||||
db := testDB(t, str)
|
|
||||||
if err := db.check("", testAddr, ecKey); err == nil {
|
|
||||||
t.Fatalf("no error for unknown address")
|
|
||||||
} else if ke, ok := err.(*knownhosts.KeyError); !ok {
|
|
||||||
t.Fatalf("got type %T, want *KeyError", err)
|
|
||||||
} else if len(ke.Want) == 0 {
|
|
||||||
t.Fatalf("got empty KeyError.Want")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSameKeyType(t *testing.T) {
|
|
||||||
str := fmt.Sprintf("%s %s", testAddr, edKeyStr)
|
|
||||||
db := testDB(t, str)
|
|
||||||
if err := db.check("", testAddr, alternateEdKey); err == nil {
|
|
||||||
t.Fatalf("no error for unknown address")
|
|
||||||
} else if ke, ok := err.(*knownhosts.KeyError); !ok {
|
|
||||||
t.Fatalf("got type %T, want *KeyError", err)
|
|
||||||
} else if len(ke.Want) == 0 {
|
|
||||||
t.Fatalf("got empty KeyError.Want")
|
|
||||||
} else if got, want := ke.Want[0].Key.Marshal(), edKey.Marshal(); !bytes.Equal(got, want) {
|
|
||||||
t.Fatalf("got key %q, want %q", got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIPAddress(t *testing.T) {
|
|
||||||
str := fmt.Sprintf("%s %s", testAddr, edKeyStr)
|
|
||||||
db := testDB(t, str)
|
|
||||||
if err := db.check("", testAddr, edKey); err != nil {
|
|
||||||
t.Errorf("got error %q, want none", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIPv6Address(t *testing.T) {
|
|
||||||
str := fmt.Sprintf("%s %s", testAddr6, edKeyStr)
|
|
||||||
db := testDB(t, str)
|
|
||||||
|
|
||||||
if err := db.check("", testAddr6, edKey); err != nil {
|
|
||||||
t.Errorf("got error %q, want none", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBasic(t *testing.T) {
|
|
||||||
str := fmt.Sprintf("#comment\n\nserver.org,%s %s\notherhost %s", testAddr, edKeyStr, ecKeyStr)
|
|
||||||
db := testDB(t, str)
|
|
||||||
if err := db.check("server.org:22", testAddr, edKey); err != nil {
|
|
||||||
t.Errorf("got error %v, want none", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
want := knownhosts.KnownKey{
|
|
||||||
Key: edKey,
|
|
||||||
}
|
|
||||||
if err := db.check("server.org:22", testAddr, ecKey); err == nil {
|
|
||||||
t.Errorf("succeeded, want KeyError")
|
|
||||||
} else if ke, ok := err.(*knownhosts.KeyError); !ok {
|
|
||||||
t.Errorf("got %T, want *KeyError", err)
|
|
||||||
} else if len(ke.Want) != 1 {
|
|
||||||
t.Errorf("got %v, want 1 entry", ke)
|
|
||||||
} else if !reflect.DeepEqual(ke.Want[0], want) {
|
|
||||||
t.Errorf("got %v, want %v", ke.Want[0], want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestHostNamePrecedence(t *testing.T) {
|
|
||||||
var evilAddr = &net.TCPAddr{
|
|
||||||
IP: net.IP{66, 66, 66, 66},
|
|
||||||
Port: 22,
|
|
||||||
}
|
|
||||||
|
|
||||||
str := fmt.Sprintf("server.org,%s %s\nevil.org,%s %s", testAddr, edKeyStr, evilAddr, ecKeyStr)
|
|
||||||
db := testDB(t, str)
|
|
||||||
|
|
||||||
if err := db.check("server.org:22", evilAddr, ecKey); err == nil {
|
|
||||||
t.Errorf("check succeeded")
|
|
||||||
} else if _, ok := err.(*knownhosts.KeyError); !ok {
|
|
||||||
t.Errorf("got %T, want *KeyError", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDBOrderingPrecedenceKeyType(t *testing.T) {
|
|
||||||
str := fmt.Sprintf("server.org,%s %s\nserver.org,%s %s", testAddr, edKeyStr, testAddr, alternateEdKeyStr)
|
|
||||||
db := testDB(t, str)
|
|
||||||
|
|
||||||
if err := db.check("server.org:22", testAddr, alternateEdKey); err == nil {
|
|
||||||
t.Errorf("check succeeded")
|
|
||||||
} else if _, ok := err.(*knownhosts.KeyError); !ok {
|
|
||||||
t.Errorf("got %T, want *KeyError", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNegate(t *testing.T) {
|
|
||||||
str := fmt.Sprintf("%s,!server.org %s", testAddr, edKeyStr)
|
|
||||||
db := testDB(t, str)
|
|
||||||
if err := db.check("server.org:22", testAddr, ecKey); err == nil {
|
|
||||||
t.Errorf("succeeded")
|
|
||||||
} else if ke, ok := err.(*knownhosts.KeyError); !ok {
|
|
||||||
t.Errorf("got error type %T, want *KeyError", err)
|
|
||||||
} else if len(ke.Want) != 0 {
|
|
||||||
t.Errorf("got expected keys %d (first of type %s), want []", len(ke.Want), ke.Want[0].Key.Type())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestWildcard(t *testing.T) {
|
|
||||||
str := fmt.Sprintf("server*.domain %s", edKeyStr)
|
|
||||||
db := testDB(t, str)
|
|
||||||
|
|
||||||
want := &knownhosts.KeyError{
|
|
||||||
Want: []knownhosts.KnownKey{{
|
|
||||||
Key: edKey,
|
|
||||||
}},
|
|
||||||
}
|
|
||||||
|
|
||||||
got := db.check("server.domain:22", &net.TCPAddr{}, ecKey)
|
|
||||||
if !reflect.DeepEqual(got, want) {
|
|
||||||
t.Errorf("got %s, want %s", got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestWildcardMatch(t *testing.T) {
|
|
||||||
for _, c := range []struct {
|
|
||||||
pat, str string
|
|
||||||
want bool
|
|
||||||
}{
|
|
||||||
{"a?b", "abb", true},
|
|
||||||
{"ab", "abc", false},
|
|
||||||
{"abc", "ab", false},
|
|
||||||
{"a*b", "axxxb", true},
|
|
||||||
{"a*b", "axbxb", true},
|
|
||||||
{"a*b", "axbxbc", false},
|
|
||||||
{"a*?", "axbxc", true},
|
|
||||||
{"a*b*", "axxbxxxxxx", true},
|
|
||||||
{"a*b*c", "axxbxxxxxxc", true},
|
|
||||||
{"a*b*?", "axxbxxxxxxc", true},
|
|
||||||
{"a*b*z", "axxbxxbxxxz", true},
|
|
||||||
{"a*b*z", "axxbxxzxxxz", true},
|
|
||||||
{"a*b*z", "axxbxxzxxx", false},
|
|
||||||
} {
|
|
||||||
got := wildcardMatch([]byte(c.pat), []byte(c.str))
|
|
||||||
if got != c.want {
|
|
||||||
t.Errorf("wildcardMatch(%q, %q) = %v, want %v", c.pat, c.str, got, c.want)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(hanwen): test coverage for certificates.
|
|
||||||
|
|
||||||
const testHostname = "hostname"
|
|
||||||
|
|
||||||
// generated with keygen -H -f
|
|
||||||
const encodedTestHostnameHash = "|1|IHXZvQMvTcZTUU29+2vXFgx8Frs=|UGccIWfRVDwilMBnA3WJoRAC75Y="
|
|
||||||
|
|
||||||
func TestHostHash(t *testing.T) {
|
|
||||||
testHostHash(t, testHostname, encodedTestHostnameHash)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestHashList(t *testing.T) {
|
|
||||||
encoded := knownhosts.HashHostname(testHostname)
|
|
||||||
testHostHash(t, testHostname, encoded)
|
|
||||||
}
|
|
||||||
|
|
||||||
func testHostHash(t *testing.T, hostname, encoded string) {
|
|
||||||
typ, salt, hash, err := decodeHash(encoded)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("decodeHash: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if got := encodeHash(typ, salt, hash); got != encoded {
|
|
||||||
t.Errorf("got encoding %s want %s", got, encoded)
|
|
||||||
}
|
|
||||||
|
|
||||||
if typ != sha1HashType {
|
|
||||||
t.Fatalf("got hash type %q, want %q", typ, sha1HashType)
|
|
||||||
}
|
|
||||||
|
|
||||||
got := hashHost(hostname, salt)
|
|
||||||
if !bytes.Equal(got, hash) {
|
|
||||||
t.Errorf("got hash %x want %x", got, hash)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestHashedHostkeyCheck(t *testing.T) {
|
|
||||||
str := fmt.Sprintf("%s %s", knownhosts.HashHostname(testHostname), edKeyStr)
|
|
||||||
db := testDB(t, str)
|
|
||||||
if err := db.check(testHostname+":22", testAddr, edKey); err != nil {
|
|
||||||
t.Errorf("check(%s): %v", testHostname, err)
|
|
||||||
}
|
|
||||||
want := &knownhosts.KeyError{
|
|
||||||
Want: []knownhosts.KnownKey{{
|
|
||||||
Key: edKey,
|
|
||||||
}},
|
|
||||||
}
|
|
||||||
if got := db.check(testHostname+":22", testAddr, alternateEdKey); !reflect.DeepEqual(got, want) {
|
|
||||||
t.Errorf("got error %v, want %v", got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user