1
0
mirror of synced 2026-03-01 11:16:56 +00:00

Compare commits

...

36 Commits

Author SHA1 Message Date
Stefan Prodan
ca5732e586 Merge pull request #121 from fluxcd/helm-beta.3
Update helm-controller to v0.0.1-beta.3
2020-07-21 20:31:31 +03:00
stefanprodan
2463d72f3b Update helm-controller to v0.0.1-beta.3 2020-07-21 20:18:23 +03:00
stefanprodan
d6f7474200 Add get/export HelmRelease e2e tests 2020-07-21 14:25:31 +03:00
stefanprodan
0b2bc7ab3f Implement get/export HelmRelease for Helm releases 2020-07-21 14:22:20 +03:00
Stefan Prodan
0a4fac61d4 Merge pull request #118 from fluxcd/hr-cmd
Implement create/delete for Helm releases
2020-07-21 14:10:45 +03:00
stefanprodan
797aec5528 Add create/delete HelmRelease e2e tests 2020-07-21 14:02:29 +03:00
stefanprodan
5f0b95dc59 Implement create/delete for Helm releases 2020-07-21 12:20:41 +03:00
Stefan Prodan
b384c5f14c Merge pull request #116 from fluxcd/helm-repo-cmd
Implement Helm repository commands
2020-07-21 11:09:31 +03:00
stefanprodan
5254dca9d9 Add Helm repository cmd docs 2020-07-21 10:42:05 +03:00
stefanprodan
8534ccbf37 Implement Helm repository commands 2020-07-21 10:39:17 +03:00
stefanprodan
9af874d810 Add examples to all tk commands 2020-07-21 10:38:44 +03:00
Stefan Prodan
4e3dee15ce Merge pull request #114 from fluxcd/fix-kustomize-cmd
Rename kustomization arg from validate to validation
2020-07-20 15:30:00 +03:00
stefanprodan
aaad618e20 Rename kustomization arg from validate to validation
Fix `tk create kustomization` command args inconsistency with the Kustomize API
2020-07-20 15:10:33 +03:00
Stefan Prodan
10bb50bd82 Merge pull request #113 from fluxcd/crd-v1
Drop support for Kubernetes <1.16
2020-07-20 14:56:45 +03:00
stefanprodan
6d2ff6e019 Update controllers and APIs 2020-07-20 14:41:23 +03:00
stefanprodan
670070a879 Drop support for Kubernetes <1.16 2020-07-20 14:37:50 +03:00
Stefan Prodan
dcec8007d4 Merge pull request #88 from luxas/go_git_provider
Add go-git-providers proposal
2020-07-20 11:53:36 +03:00
Michael Bridgen
3abf4a49cd Merge pull request #108 from fluxcd/link-to-image-update-discussion
Link to image update discussion
2020-07-17 13:03:03 +01:00
Michael Bridgen
38825bf96a Cosmetic: indent sublist 2020-07-17 12:55:24 +01:00
Michael Bridgen
faa69da28d Link to image update discussion
.. and rearrange the line items a little. I removed the mention of source-controller because it doesn't feature in the design as drafted.
2020-07-17 12:08:42 +01:00
Hidde Beydals
5cf524e2fd Merge pull request #106 from fluxcd/docs/update-helm-roadmap
Make nested list work correctly in roadmap
2020-07-17 12:45:44 +02:00
Hidde Beydals
88802a44e7 Make nested list work correctly in roadmap 2020-07-17 12:34:16 +02:00
Stefan Prodan
94498d862d Merge pull request #105 from fluxcd/static-manifests
Use semver manifests as kustomize base
2020-07-17 13:27:51 +03:00
Hidde Beydals
9418b24e8f Merge pull request #104 from fluxcd/docs/update-helm-roadmap
Update "Helm Operator v2" roadmap
2020-07-17 12:11:57 +02:00
stefanprodan
b92cbcd7e7 Use semver manifests as kustomize base
Instead of cloning the components repositories to download the base manifests, we build them in CI at release time and download them in tk install/bootstrap based on the provided semver. This speeds up the manifests generation from minutes to milliseconds.
2020-07-17 13:10:19 +03:00
Hidde Beydals
9ef2ff92df Update "Helm Operator v2" roadmap 2020-07-17 12:04:09 +02:00
stefanprodan
422724bd2d Publish manifests as release assets 2020-07-17 12:00:15 +03:00
Stefan Prodan
6cb7897f25 Merge pull request #99 from fluxcd/export-install-manifests
Add export option to tk install
2020-07-16 16:07:13 +03:00
stefanprodan
499ba15004 Add export option to tk install 2020-07-16 15:56:05 +03:00
Stefan Prodan
b04abe989e Merge pull request #97 from fluxcd/helm-webhooks
Add webhook receivers section to Helm guide
2020-07-16 12:46:49 +03:00
stefanprodan
ea576179f9 Add webhook receivers section to Helm guide 2020-07-16 12:39:53 +03:00
Lucas Käldström
b1993d2fb7 Add Reconcile methods and TeamAccess struct. Mention ErrNotFound and ErrAlreadyExists. Add List() to TeamAccess. Rename GetTitle() to GetName() 2020-07-14 16:39:31 +03:00
Lucas Käldström
e8096dec88 Rename the Repository Teams and Credential clients 2020-07-14 16:39:25 +03:00
Lucas Käldström
39eee51ec8 Add extra goals of best practices 2020-07-14 16:39:14 +03:00
Lucas Käldström
7bb3a10795 Remove the URLParser, and Provider from the *Ref types 2020-07-14 16:39:02 +03:00
Lucas Käldström
4c684df653 Add go-git-providers proposal 2020-07-14 12:49:29 +03:00
78 changed files with 2468 additions and 151 deletions

View File

@@ -24,7 +24,9 @@ jobs:
with:
go-version: 1.14.x
- name: Setup Kubernetes
uses: engineerd/setup-kind@v0.3.0
uses: engineerd/setup-kind@v0.4.0
with:
image: kindest/node:v1.16.9
- name: Run test
run: make test
- name: Check if working tree is dirty
@@ -63,7 +65,7 @@ jobs:
--path="./deploy/overlays/dev" \
--prune=true \
--interval=5m \
--validate=client \
--validation=client \
--health-check="Deployment/frontend.dev" \
--health-check="Deployment/backend.dev" \
--health-check-timeout=3m
@@ -89,6 +91,29 @@ jobs:
- name: tk delete source git
run: |
./bin/tk delete source git podinfo --silent
- name: tk create source helm
run: |
./bin/tk create source helm podinfo \
--url https://stefanprodan.github.io/podinfo
- name: tk create helmrelease
run: |
./bin/tk create hr podinfo \
--target-namespace=default \
--source=podinfo \
--chart-name=podinfo \
--chart-version=">4.0.0 <5.0.0"
- name: tk get helmreleases
run: |
./bin/tk get helmreleases
- name: tk export helmrelease
run: |
./bin/tk export hr --all
- name: tk delete helmrelease
run: |
./bin/tk delete hr podinfo --silent
- name: tk delete source helm
run: |
./bin/tk delete source helm podinfo --silent
- name: tk check
run: |
./bin/tk check

View File

@@ -14,7 +14,7 @@ jobs:
- name: Unshallow
run: git fetch --prune --unshallow
- name: Setup Go
uses: actions/setup-go@v2-beta
uses: actions/setup-go@v2
with:
go-version: 1.14.x
- name: Download release notes utility
@@ -25,10 +25,60 @@ jobs:
run: |
echo 'CHANGELOG' > /tmp/release.txt
github-release-notes -org fluxcd -repo toolkit -since-latest-release >> /tmp/release.txt
- name: Setup Kustomize
uses: ./.github/actions/kustomize
- name: Generate manifests tarball
run: |
mkdir -p ./output
files=""
# build controllers
for controller in ./manifests/bases/*/; do
output_path="./output/$(basename $controller).yaml"
echo "building $controller to $output_path"
kustomize build $controller > $output_path
files+=" $(basename $output_path)"
done
# build rbac
rbac_path="./manifests/rbac"
rbac_output_path="./output/rbac.yaml"
echo "building $rbac_path to $rbac_output_path"
kustomize build $rbac_path > $rbac_output_path
files+=" $(basename $rbac_output_path)"
# build policies
policies_path="./manifests/policies"
policies_output_path="./output/policies.yaml"
echo "building $policies_path to $policies_output_path"
kustomize build $policies_path > $policies_output_path
files+=" $(basename $policies_output_path)"
# create tarball
cd ./output && tar -cvzf manifests.tar.gz $files
- name: Create release
id: create_release
uses: actions/create-release@latest
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ github.ref }}
release_name: ${{ github.ref }}
- name: Upload artifacts
id: upload-release-asset
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./output/manifests.tar.gz
asset_name: manifests.tar.gz
asset_content_type: application/gzip
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v1
with:
version: latest
args: release --release-notes=/tmp/release.txt
args: release --release-notes=/tmp/release.txt --skip-validate
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

3
.gitignore vendored
View File

@@ -13,4 +13,5 @@
# Dependency directories (remove the comment below to include it)
# vendor/
bin/
bin/
output/

View File

@@ -58,7 +58,7 @@ const (
func init() {
bootstrapCmd.PersistentFlags().StringVarP(&bootstrapVersion, "version", "v", defaultVersion,
"toolkit tag or branch")
"toolkit version")
bootstrapCmd.PersistentFlags().StringSliceVar(&bootstrapComponents, "components", defaultComponents,
"list of components, accepts comma-separated values")

View File

@@ -42,19 +42,19 @@ the bootstrap command will perform an upgrade if needed.`,
export GITHUB_TOKEN=<my-token>
# Run bootstrap for a private repo owned by a GitHub organization
bootstrap github --owner=<organization> --repository=<repo name>
tk bootstrap github --owner=<organization> --repository=<repo name>
# Run bootstrap for a private repo and assign organization teams to it
bootstrap github --owner=<organization> --repository=<repo name> --team=<team1 slug> --team=<team2 slug>
tk bootstrap github --owner=<organization> --repository=<repo name> --team=<team1 slug> --team=<team2 slug>
# Run bootstrap for a repository path
bootstrap github --owner=<organization> --repository=<repo name> --path=dev-cluster
tk bootstrap github --owner=<organization> --repository=<repo name> --path=dev-cluster
# Run bootstrap for a public repository on a personal account
bootstrap github --owner=<user> --repository=<repo name> --private=false --personal=true
tk bootstrap github --owner=<user> --repository=<repo name> --private=false --personal=true
# Run bootstrap for a private repo hosted on GitHub Enterprise
bootstrap github --owner=<organization> --repository=<repo name> --hostname=<domain>
tk bootstrap github --owner=<organization> --repository=<repo name> --hostname=<domain>
`,
RunE: bootstrapGitHubCmdRun,
}

View File

@@ -42,16 +42,16 @@ the bootstrap command will perform an upgrade if needed.`,
export GITLAB_TOKEN=<my-token>
# Run bootstrap for a private repo owned by a GitLab group
bootstrap gitlab --owner=<group> --repository=<repo name>
tk bootstrap gitlab --owner=<group> --repository=<repo name>
# Run bootstrap for a repository path
bootstrap gitlab --owner=<group> --repository=<repo name> --path=dev-cluster
tk bootstrap gitlab --owner=<group> --repository=<repo name> --path=dev-cluster
# Run bootstrap for a public repository on a personal account
bootstrap gitlab --owner=<user> --repository=<repo name> --private=false --personal=true
tk bootstrap gitlab --owner=<user> --repository=<repo name> --private=false --personal=true
# Run bootstrap for a private repo hosted on a GitLab server
bootstrap gitlab --owner=<group> --repository=<repo name> --hostname=<domain>
tk bootstrap gitlab --owner=<group> --repository=<repo name> --hostname=<domain>
`,
RunE: bootstrapGitLabCmdRun,
}

View File

@@ -67,7 +67,7 @@ func runCheckCmd(cmd *cobra.Command, args []string) error {
checkFailed = true
}
if !kubernetesCheck(">=1.14.0") {
if !kubernetesCheck(">=1.16.0") {
checkFailed = true
}

View File

@@ -0,0 +1,256 @@
/*
Copyright 2020 The Flux CD contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package main
import (
"context"
"fmt"
"io/ioutil"
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"k8s.io/apimachinery/pkg/api/errors"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/yaml"
helmv2 "github.com/fluxcd/helm-controller/api/v2alpha1"
sourcev1 "github.com/fluxcd/source-controller/api/v1alpha1"
)
var createHelmReleaseCmd = &cobra.Command{
Use: "helmrelease [name]",
Aliases: []string{"hr"},
Short: "Create or update a HelmRelease resource",
Long: "The helmrelease create command generates a HelmRelease resource for a given HelmRepository source.",
Example: ` # Create a HelmRelease from a source
tk create hr podinfo \
--interval=10m \
--release-name=podinfo \
--target-namespace=default \
--source=podinfo \
--chart-name=podinfo \
--chart-version=">4.0.0"
# Create a HelmRelease with values for a local YAML file
tk create hr podinfo \
--target-namespace=default \
--source=podinfo \
--chart-name=podinfo \
--chart-version=4.0.5 \
--values=./my-values.yaml
# Create a HelmRelease definition on disk without applying it on the cluster
tk create hr podinfo \
--target-namespace=default \
--source=podinfo \
--chart-name=podinfo \
--chart-version=4.0.5 \
--values=./values.yaml \
--export > podinfo-release.yaml
`,
RunE: createHelmReleaseCmdRun,
}
var (
hrName string
hrSource string
hrDependsOn []string
hrChartName string
hrChartVersion string
hrTargetNamespace string
hrValuesFile string
)
func init() {
createHelmReleaseCmd.Flags().StringVar(&hrName, "release-name", "", "name used for the Helm release, defaults to a composition of '<target-namespace>-<hr-name>'")
createHelmReleaseCmd.Flags().StringVar(&hrSource, "source", "", "HelmRepository name")
createHelmReleaseCmd.Flags().StringVar(&hrChartName, "chart-name", "", "Helm chart name")
createHelmReleaseCmd.Flags().StringVar(&hrChartVersion, "chart-version", "", "Helm chart version, accepts semver range")
createHelmReleaseCmd.Flags().StringArrayVar(&hrDependsOn, "depends-on", nil, "HelmReleases that must be ready before this release can be installed")
createHelmReleaseCmd.Flags().StringVar(&hrTargetNamespace, "target-namespace", "", "namespace to install this release, defaults to the HelmRelease namespace")
createHelmReleaseCmd.Flags().StringVar(&hrValuesFile, "values", "", "local path to the values.yaml file")
createCmd.AddCommand(createHelmReleaseCmd)
}
func createHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("release name is required")
}
name := args[0]
if hrSource == "" {
return fmt.Errorf("source is required")
}
if hrChartName == "" {
return fmt.Errorf("chart name is required")
}
if hrChartVersion == "" {
return fmt.Errorf("chart version is required")
}
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
if err != nil {
return err
}
if !export {
logger.Generatef("generating release")
}
helmRelease := helmv2.HelmRelease{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: namespace,
},
Spec: helmv2.HelmReleaseSpec{
ReleaseName: hrName,
DependsOn: hrDependsOn,
Interval: metav1.Duration{
Duration: interval,
},
TargetNamespace: hrTargetNamespace,
Chart: helmv2.HelmChartTemplate{
Name: hrChartName,
Version: hrChartVersion,
SourceRef: helmv2.CrossNamespaceObjectReference{
Kind: sourcev1.HelmRepositoryKind,
Name: hrSource,
},
},
Suspend: false,
},
}
if hrValuesFile != "" {
data, err := ioutil.ReadFile(hrValuesFile)
if err != nil {
return fmt.Errorf("reading values from %s failed: %w", hrValuesFile, err)
}
json, err := yaml.YAMLToJSON(data)
if err != nil {
return fmt.Errorf("converting values to JSON from %s failed: %w", hrValuesFile, err)
}
helmRelease.Spec.Values = apiextensionsv1.JSON{Raw: json}
}
if export {
return exportHelmRelease(helmRelease)
}
logger.Actionf("applying release")
if err := upsertHelmRelease(ctx, kubeClient, helmRelease); err != nil {
return err
}
logger.Waitingf("waiting for reconciliation")
chartName := fmt.Sprintf("%s-%s", namespace, name)
if err := wait.PollImmediate(pollInterval, timeout,
isHelmChartReady(ctx, kubeClient, chartName, namespace)); err != nil {
return err
}
if err := wait.PollImmediate(pollInterval, timeout,
isHelmReleaseReady(ctx, kubeClient, name, namespace)); err != nil {
return err
}
logger.Successf("release %s is ready", name)
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
err = kubeClient.Get(ctx, namespacedName, &helmRelease)
if err != nil {
return fmt.Errorf("release failed: %w", err)
}
if helmRelease.Status.LastAppliedRevision != "" {
logger.Successf("applied revision %s", helmRelease.Status.LastAppliedRevision)
} else {
return fmt.Errorf("reconciliation failed")
}
return nil
}
func upsertHelmRelease(ctx context.Context, kubeClient client.Client, helmRelease helmv2.HelmRelease) error {
namespacedName := types.NamespacedName{
Namespace: helmRelease.GetNamespace(),
Name: helmRelease.GetName(),
}
var existing helmv2.HelmRelease
err := kubeClient.Get(ctx, namespacedName, &existing)
if err != nil {
if errors.IsNotFound(err) {
if err := kubeClient.Create(ctx, &helmRelease); err != nil {
return err
} else {
logger.Successf("release created")
return nil
}
}
return err
}
existing.Spec = helmRelease.Spec
if err := kubeClient.Update(ctx, &existing); err != nil {
return err
}
logger.Successf("release updated")
return nil
}
func isHelmChartReady(ctx context.Context, kubeClient client.Client, name, namespace string) wait.ConditionFunc {
return func() (bool, error) {
var helmChart sourcev1.HelmChart
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
err := kubeClient.Get(ctx, namespacedName, &helmChart)
if err != nil {
if apierrors.IsNotFound(err) {
return false, nil
}
return false, err
}
for _, condition := range helmChart.Status.Conditions {
if condition.Type == helmv2.ReadyCondition {
if condition.Status == corev1.ConditionTrue {
return true, nil
} else if condition.Status == corev1.ConditionFalse {
return false, fmt.Errorf(condition.Message)
}
}
}
return false, nil
}
}

View File

@@ -40,32 +40,32 @@ var createKsCmd = &cobra.Command{
Short: "Create or update a Kustomization resource",
Long: "The kustomization source create command generates a Kustomize resource for a given GitRepository source.",
Example: ` # Create a Kustomization resource from a source at a given path
create kustomization contour \
tk create kustomization contour \
--source=contour \
--path="./examples/contour/" \
--prune=true \
--interval=10m \
--validate=client \
--validation=client \
--health-check="Deployment/contour.projectcontour" \
--health-check="DaemonSet/envoy.projectcontour" \
--health-check-timeout=3m
# Create a Kustomization resource that depends on the previous one
create kustomization webapp \
tk create kustomization webapp \
--depends-on=contour \
--source=webapp \
--path="./deploy/overlays/dev" \
--prune=true \
--interval=5m \
--validate=client
--validation=client
# Create a Kustomization resource that runs under a service account
create kustomization webapp \
tk create kustomization webapp \
--source=webapp \
--path="./deploy/overlays/staging" \
--prune=true \
--interval=5m \
--validate=client \
--validation=client \
--sa-name=reconclier \
--sa-namespace=staging
`,
@@ -77,7 +77,7 @@ var (
ksPath string
ksPrune bool
ksDependsOn []string
ksValidate string
ksValidation string
ksHealthCheck []string
ksHealthTimeout time.Duration
ksSAName string
@@ -90,7 +90,7 @@ func init() {
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().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(&ksValidation, "validation", "", "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().StringVar(&ksSAName, "sa-name", "", "service account name")
createKsCmd.Flags().StringVar(&ksSANamespace, "sa-namespace", "", "service account namespace")
@@ -142,7 +142,7 @@ func createKsCmdRun(cmd *cobra.Command, args []string) error {
Name: ksSource,
},
Suspend: false,
Validation: ksValidate,
Validation: ksValidation,
},
}

View File

@@ -46,35 +46,35 @@ The create source git command generates a GitRepository resource and waits for i
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.`,
Example: ` # Create a source from a public Git repository master branch
create source git podinfo \
tk create source git podinfo \
--url=https://github.com/stefanprodan/podinfo \
--branch=master
# Create a source from a Git repository pinned to specific git tag
create source git podinfo \
tk create source git podinfo \
--url=https://github.com/stefanprodan/podinfo \
--tag="3.2.3"
# Create a source from a public Git repository tag that matches a semver range
create source git podinfo \
tk create source git podinfo \
--url=https://github.com/stefanprodan/podinfo \
--tag-semver=">=3.2.0 <3.3.0"
# Create a source from a Git repository using SSH authentication
create source git podinfo \
tk create source git podinfo \
--url=ssh://git@github.com/stefanprodan/podinfo \
--branch=master
# Create a source from a Git repository using SSH authentication and an
# ECDSA P-521 curve public key
create source git podinfo \
tk create source git podinfo \
--url=ssh://git@github.com/stefanprodan/podinfo \
--branch=master \
--ssh-key-algorithm=ecdsa \
--ssh-ecdsa-curve=p521
# Create a source from a Git repository using basic authentication
create source git podinfo \
tk create source git podinfo \
--url=https://github.com/stefanprodan/podinfo \
--username=username \
--password=password
@@ -115,7 +115,7 @@ func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
name := args[0]
if sourceGitURL == "" {
return fmt.Errorf("git-url is required")
return fmt.Errorf("url is required")
}
tmpDir, err := ioutil.TempDir("", name)

View File

@@ -0,0 +1,229 @@
/*
Copyright 2020 The Flux CD contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package main
import (
"context"
"fmt"
sourcev1 "github.com/fluxcd/source-controller/api/v1alpha1"
"github.com/spf13/cobra"
"io/ioutil"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait"
"net/url"
"os"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/yaml"
)
var createSourceHelmCmd = &cobra.Command{
Use: "helm [name]",
Short: "Create or update a HelmRepository source",
Long: `
The create source helm command generates a HelmRepository resource and waits for it to fetch the index.
For private Helm repositories, the basic authentication credentials are stored in a Kubernetes secret.`,
Example: ` # Create a source from a public Helm repository
tk create source helm podinfo \
--url=https://stefanprodan.github.io/podinfo \
--interval=10m
# Create a source from a Helm repository using basic authentication
tk create source helm podinfo \
--url=https://stefanprodan.github.io/podinfo \
--username=username \
--password=password
`,
RunE: createSourceHelmCmdRun,
}
var (
sourceHelmURL string
sourceHelmUsername string
sourceHelmPassword string
)
func init() {
createSourceHelmCmd.Flags().StringVar(&sourceHelmURL, "url", "", "Helm repository address")
createSourceHelmCmd.Flags().StringVarP(&sourceHelmUsername, "username", "u", "", "basic authentication username")
createSourceHelmCmd.Flags().StringVarP(&sourceHelmPassword, "password", "p", "", "basic authentication password")
createSourceCmd.AddCommand(createSourceHelmCmd)
}
func createSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("source name is required")
}
name := args[0]
secretName := fmt.Sprintf("helm-%s", name)
if sourceHelmURL == "" {
return fmt.Errorf("url is required")
}
tmpDir, err := ioutil.TempDir("", name)
if err != nil {
return err
}
defer os.RemoveAll(tmpDir)
if _, err := url.Parse(sourceHelmURL); err != nil {
return fmt.Errorf("url parse failed: %w", err)
}
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
if err != nil {
return err
}
helmRepository := sourcev1.HelmRepository{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: namespace,
},
Spec: sourcev1.HelmRepositorySpec{
URL: sourceHelmURL,
Interval: metav1.Duration{
Duration: interval,
},
},
}
if export {
return exportHelmRepository(helmRepository)
}
withAuth := false
if sourceHelmUsername != "" && sourceHelmPassword != "" {
logger.Actionf("applying secret with basic auth credentials")
secret := corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: secretName,
Namespace: namespace,
},
StringData: map[string]string{
"username": sourceHelmUsername,
"password": sourceHelmPassword,
},
}
if err := upsertSecret(ctx, kubeClient, secret); err != nil {
return err
}
withAuth = true
}
if withAuth {
logger.Successf("authentication configured")
}
logger.Generatef("generating source")
if withAuth {
helmRepository.Spec.SecretRef = &corev1.LocalObjectReference{
Name: secretName,
}
}
logger.Actionf("applying source")
if err := upsertHelmRepository(ctx, kubeClient, helmRepository); err != nil {
return err
}
logger.Waitingf("waiting for index download")
if err := wait.PollImmediate(pollInterval, timeout,
isHelmRepositoryReady(ctx, kubeClient, name, namespace)); err != nil {
return err
}
logger.Successf("index download completed")
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
err = kubeClient.Get(ctx, namespacedName, &helmRepository)
if err != nil {
return fmt.Errorf("helm index failed: %w", err)
}
if helmRepository.Status.Artifact != nil {
logger.Successf("fetched revision: %s", helmRepository.Status.Artifact.Revision)
} else {
return fmt.Errorf("index download failed, artifact not found")
}
return nil
}
func upsertHelmRepository(ctx context.Context, kubeClient client.Client, helmRepository sourcev1.HelmRepository) error {
namespacedName := types.NamespacedName{
Namespace: helmRepository.GetNamespace(),
Name: helmRepository.GetName(),
}
var existing sourcev1.HelmRepository
err := kubeClient.Get(ctx, namespacedName, &existing)
if err != nil {
if errors.IsNotFound(err) {
if err := kubeClient.Create(ctx, &helmRepository); err != nil {
return err
} else {
logger.Successf("source created")
return nil
}
}
return err
}
existing.Spec = helmRepository.Spec
if err := kubeClient.Update(ctx, &existing); err != nil {
return err
}
logger.Successf("source updated")
return nil
}
func exportHelmRepository(source sourcev1.HelmRepository) error {
gvk := sourcev1.GroupVersion.WithKind(sourcev1.HelmRepositoryKind)
export := sourcev1.HelmRepository{
TypeMeta: metav1.TypeMeta{
Kind: gvk.Kind,
APIVersion: gvk.GroupVersion().String(),
},
ObjectMeta: metav1.ObjectMeta{
Name: source.Name,
Namespace: source.Namespace,
},
Spec: source.Spec,
}
data, err := yaml.Marshal(export)
if err != nil {
return err
}
fmt.Println("---")
fmt.Println(string(data))
return nil
}

View File

@@ -0,0 +1,91 @@
/*
Copyright 2020 The Flux CD contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package main
import (
"context"
"fmt"
"github.com/manifoldco/promptui"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/types"
helmv2 "github.com/fluxcd/helm-controller/api/v2alpha1"
)
var deleteHelmReleaseCmd = &cobra.Command{
Use: "helmrelease [name]",
Aliases: []string{"hr"},
Short: "Delete a HelmRelease resource",
Long: "The delete helmrelease command removes the given HelmRelease from the cluster.",
Example: ` # Delete a Helm release and the Kubernetes resources created by it
tk delete hr podinfo
`,
RunE: deleteHelmReleaseCmdRun,
}
func init() {
deleteCmd.AddCommand(deleteHelmReleaseCmd)
}
func deleteHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("release name is required")
}
name := args[0]
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
if err != nil {
return err
}
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
var helmRelease helmv2.HelmRelease
err = kubeClient.Get(ctx, namespacedName, &helmRelease)
if err != nil {
return err
}
if !deleteSilent {
if !helmRelease.Spec.Suspend {
logger.Waitingf("This action will remove the Kubernetes objects previously applied by the %s Helm release!", name)
}
prompt := promptui.Prompt{
Label: "Are you sure you want to delete this Helm release",
IsConfirm: true,
}
if _, err := prompt.Run(); err != nil {
return fmt.Errorf("aborting")
}
}
logger.Actionf("deleting release %s in %s namespace", name, namespace)
err = kubeClient.Delete(ctx, &helmRelease)
if err != nil {
return err
}
logger.Successf("release deleted")
return nil
}

View File

@@ -31,7 +31,10 @@ var deleteKsCmd = &cobra.Command{
Aliases: []string{"ks"},
Short: "Delete a Kustomization resource",
Long: "The delete kustomization command deletes the given Kustomization from the cluster.",
RunE: deleteKsCmdRun,
Example: ` # Delete a kustomization and the Kubernetes resources created by it
tk delete kustomization podinfo
`,
RunE: deleteKsCmdRun,
}
func init() {

View File

@@ -30,7 +30,10 @@ var deleteSourceGitCmd = &cobra.Command{
Use: "git [name]",
Short: "Delete a GitRepository source",
Long: "The delete source git command deletes the given GitRepository from the cluster.",
RunE: deleteSourceGitCmdRun,
Example: ` # Delete a Git repository
tk delete source git podinfo
`,
RunE: deleteSourceGitCmdRun,
}
func init() {

View File

@@ -0,0 +1,86 @@
/*
Copyright 2020 The Flux CD contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package main
import (
"context"
"fmt"
sourcev1 "github.com/fluxcd/source-controller/api/v1alpha1"
"github.com/manifoldco/promptui"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/types"
)
var deleteSourceHelmCmd = &cobra.Command{
Use: "helm [name]",
Short: "Delete a HelmRepository source",
Long: "The delete source helm command deletes the given HelmRepository from the cluster.",
Example: ` # Delete a Helm repository
tk delete source helm podinfo
`,
RunE: deleteSourceHelmCmdRun,
}
func init() {
deleteSourceCmd.AddCommand(deleteSourceHelmCmd)
}
func deleteSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("name is required")
}
name := args[0]
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
if err != nil {
return err
}
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
var helmRepository sourcev1.HelmRepository
err = kubeClient.Get(ctx, namespacedName, &helmRepository)
if err != nil {
return err
}
if !deleteSilent {
prompt := promptui.Prompt{
Label: "Are you sure you want to delete this source",
IsConfirm: true,
}
if _, err := prompt.Run(); err != nil {
return fmt.Errorf("aborting")
}
}
logger.Actionf("deleting source %s in %s namespace", name, namespace)
err = kubeClient.Delete(ctx, &helmRepository)
if err != nil {
return err
}
logger.Successf("source deleted")
return nil
}

View File

@@ -0,0 +1,118 @@
/*
Copyright 2020 The Flux CD contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package main
import (
"context"
"fmt"
"github.com/spf13/cobra"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/yaml"
helmv2 "github.com/fluxcd/helm-controller/api/v2alpha1"
)
var exportHelmReleaseCmd = &cobra.Command{
Use: "helmrelease [name]",
Aliases: []string{"hr"},
Short: "Export HelmRelease resources in YAML format",
Long: "The export helmrelease command exports one or all HelmRelease resources in YAML format.",
Example: ` # Export all HelmRelease resources
tk export helmrelease --all > kustomizations.yaml
# Export a HelmRelease
tk export hr my-app > app-release.yaml
`,
RunE: exportHelmReleaseCmdRun,
}
func init() {
exportCmd.AddCommand(exportHelmReleaseCmd)
}
func exportHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
if !exportAll && len(args) < 1 {
return fmt.Errorf("name is required")
}
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
if err != nil {
return err
}
if exportAll {
var list helmv2.HelmReleaseList
err = kubeClient.List(ctx, &list, client.InNamespace(namespace))
if err != nil {
return err
}
if len(list.Items) == 0 {
logger.Failuref("no kustomizations found in %s namespace", namespace)
return nil
}
for _, helmRelease := range list.Items {
if err := exportHelmRelease(helmRelease); err != nil {
return err
}
}
} else {
name := args[0]
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
var helmRelease helmv2.HelmRelease
err = kubeClient.Get(ctx, namespacedName, &helmRelease)
if err != nil {
return err
}
return exportHelmRelease(helmRelease)
}
return nil
}
func exportHelmRelease(helmRelease helmv2.HelmRelease) error {
gvk := helmv2.GroupVersion.WithKind(helmv2.HelmReleaseKind)
export := helmv2.HelmRelease{
TypeMeta: metav1.TypeMeta{
Kind: gvk.Kind,
APIVersion: gvk.GroupVersion().String(),
},
ObjectMeta: metav1.ObjectMeta{
Name: helmRelease.Name,
Namespace: helmRelease.Namespace,
},
Spec: helmRelease.Spec,
}
data, err := yaml.Marshal(export)
if err != nil {
return err
}
fmt.Println("---")
fmt.Println(string(data))
return nil
}

View File

@@ -34,10 +34,10 @@ var exportKsCmd = &cobra.Command{
Short: "Export Kustomization resources in YAML format",
Long: "The export kustomization command exports one or all Kustomization resources in YAML format.",
Example: ` # Export all Kustomization resources
export kustomization --all > kustomizations.yaml
tk export kustomization --all > kustomizations.yaml
# Export a Kustomization
export kustomization my-app > kustomization.yaml
tk export kustomization my-app > kustomization.yaml
`,
RunE: exportKsCmdRun,
}

View File

@@ -34,10 +34,10 @@ var exportSourceGitCmd = &cobra.Command{
Short: "Export GitRepository sources in YAML format",
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
tk export source git --all > sources.yaml
# Export a GitRepository source including the SSH key pair or basic auth credentials
export source git my-private-repo --with-credentials > source.yaml
tk export source git my-private-repo --with-credentials > source.yaml
`,
RunE: exportSourceGitCmdRun,
}
@@ -48,7 +48,7 @@ func init() {
func exportSourceGitCmdRun(cmd *cobra.Command, args []string) error {
if !exportAll && len(args) < 1 {
return fmt.Errorf("kustomization name is required")
return fmt.Errorf("name is required")
}
ctx, cancel := context.WithTimeout(context.Background(), timeout)
@@ -103,7 +103,7 @@ func exportSourceGitCmdRun(cmd *cobra.Command, args []string) error {
}
func exportGit(source sourcev1.GitRepository) error {
gvk := sourcev1.GroupVersion.WithKind("GitRepository")
gvk := sourcev1.GroupVersion.WithKind(sourcev1.GitRepositoryKind)
export := sourcev1.GitRepository{
TypeMeta: metav1.TypeMeta{
Kind: gvk.Kind,

View File

@@ -0,0 +1,139 @@
/*
Copyright 2020 The Flux CD contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package main
import (
"context"
"fmt"
sourcev1 "github.com/fluxcd/source-controller/api/v1alpha1"
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/yaml"
)
var exportSourceHelmCmd = &cobra.Command{
Use: "helm [name]",
Short: "Export HelmRepository sources in YAML format",
Long: "The export source git command exports on or all HelmRepository sources in YAML format.",
Example: ` # Export all HelmRepository sources
tk export source helm --all > sources.yaml
# Export a HelmRepository source including the basic auth credentials
tk export source helm my-private-repo --with-credentials > source.yaml
`,
RunE: exportSourceHelmCmdRun,
}
func init() {
exportSourceCmd.AddCommand(exportSourceHelmCmd)
}
func exportSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
if !exportAll && len(args) < 1 {
return fmt.Errorf("name is required")
}
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
if err != nil {
return err
}
if exportAll {
var list sourcev1.HelmRepositoryList
err = kubeClient.List(ctx, &list, client.InNamespace(namespace))
if err != nil {
return err
}
if len(list.Items) == 0 {
logger.Failuref("no source found in %s namespace", namespace)
return nil
}
for _, repository := range list.Items {
if err := exportHelmRepository(repository); err != nil {
return err
}
if exportSourceWithCred {
if err := exportHelmCredentials(ctx, kubeClient, repository); err != nil {
return err
}
}
}
} else {
name := args[0]
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
var repository sourcev1.HelmRepository
err = kubeClient.Get(ctx, namespacedName, &repository)
if err != nil {
return err
}
if err := exportHelmRepository(repository); err != nil {
return err
}
if exportSourceWithCred {
return exportHelmCredentials(ctx, kubeClient, repository)
}
}
return nil
}
func exportHelmCredentials(ctx context.Context, kubeClient client.Client, source sourcev1.HelmRepository) error {
if source.Spec.SecretRef != nil {
namespacedName := types.NamespacedName{
Namespace: source.Namespace,
Name: source.Spec.SecretRef.Name,
}
var cred corev1.Secret
err := kubeClient.Get(ctx, namespacedName, &cred)
if err != nil {
return fmt.Errorf("failed to retrieve secret %s, error: %w", namespacedName.Name, err)
}
exported := corev1.Secret{
TypeMeta: metav1.TypeMeta{
APIVersion: "v1",
Kind: "Secret",
},
ObjectMeta: metav1.ObjectMeta{
Name: namespacedName.Name,
Namespace: namespacedName.Namespace,
},
Data: cred.Data,
Type: cred.Type,
}
data, err := yaml.Marshal(exported)
if err != nil {
return err
}
fmt.Println("---")
fmt.Println(string(data))
}
return nil
}

90
cmd/tk/get_helmrelease.go Normal file
View File

@@ -0,0 +1,90 @@
/*
Copyright 2020 The Flux CD contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package main
import (
"context"
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
helmv2 "github.com/fluxcd/helm-controller/api/v2alpha1"
)
var getHelmReleaseCmd = &cobra.Command{
Use: "helmreleases",
Aliases: []string{"hr"},
Short: "Get HelmRelease statuses",
Long: "The get helmreleases command prints the statuses of the resources.",
Example: ` # List all Helm releases and their status
tk get helmreleases
`,
RunE: getHelmReleaseCmdRun,
}
func init() {
getCmd.AddCommand(getHelmReleaseCmd)
}
func getHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
if err != nil {
return err
}
var list helmv2.HelmReleaseList
err = kubeClient.List(ctx, &list, client.InNamespace(namespace))
if err != nil {
return err
}
if len(list.Items) == 0 {
logger.Failuref("no releases found in %s namespace", namespace)
return nil
}
for _, helmRelease := range list.Items {
if helmRelease.Spec.Suspend {
logger.Successf("%s is suspended", helmRelease.GetName())
continue
}
isInitialized := false
for _, condition := range helmRelease.Status.Conditions {
if condition.Type == helmv2.ReadyCondition {
if condition.Status != corev1.ConditionFalse {
if helmRelease.Status.LastAppliedRevision != "" {
logger.Successf("%s last applied revision %s", helmRelease.GetName(), helmRelease.Status.LastAppliedRevision)
} else {
logger.Successf("%s reconciling", helmRelease.GetName())
}
} else {
logger.Failuref("%s %s", helmRelease.GetName(), condition.Message)
}
isInitialized = true
break
}
}
if !isInitialized {
logger.Failuref("%s is not ready", helmRelease.GetName())
}
}
return nil
}

View File

@@ -28,9 +28,12 @@ import (
var getKsCmd = &cobra.Command{
Use: "kustomizations",
Aliases: []string{"ks"},
Short: "Get Kustomization source statuses",
Short: "Get Kustomization statuses",
Long: "The get kustomizations command prints the statuses of the resources.",
RunE: getKsCmdRun,
Example: ` # List all kustomizations and their status
tk get kustomizations
`,
RunE: getKsCmdRun,
}
func init() {

View File

@@ -29,7 +29,10 @@ var getSourceGitCmd = &cobra.Command{
Use: "git",
Short: "Get GitRepository source statuses",
Long: "The get sources git command prints the status of the GitRepository sources.",
RunE: getSourceGitCmdRun,
Example: ` # List all Git repositories and their status
tk get sources git
`,
RunE: getSourceGitCmdRun,
}
func init() {

80
cmd/tk/get_source_helm.go Normal file
View File

@@ -0,0 +1,80 @@
/*
Copyright 2020 The Flux CD contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package main
import (
"context"
sourcev1 "github.com/fluxcd/source-controller/api/v1alpha1"
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
)
var getSourceHelmCmd = &cobra.Command{
Use: "helm",
Short: "Get HelmRepository source statuses",
Long: "The get sources helm command prints the status of the HelmRepository sources.",
Example: ` # List all Helm repositories and their status
tk get sources helm
`,
RunE: getSourceHelmCmdRun,
}
func init() {
getSourceCmd.AddCommand(getSourceHelmCmd)
}
func getSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
if err != nil {
return err
}
var list sourcev1.HelmRepositoryList
err = kubeClient.List(ctx, &list, client.InNamespace(namespace))
if err != nil {
return err
}
if len(list.Items) == 0 {
logger.Failuref("no sources found in %s namespace", namespace)
return nil
}
for _, source := range list.Items {
isInitialized := false
for _, condition := range source.Status.Conditions {
if condition.Type == sourcev1.ReadyCondition {
if condition.Status != corev1.ConditionFalse {
logger.Successf("%s last fetched revision: %s", source.GetName(), source.Status.Artifact.Revision)
} else {
logger.Failuref("%s %s", source.GetName(), condition.Message)
}
isInitialized = true
break
}
}
if !isInitialized {
logger.Failuref("%s is not ready", source.GetName())
}
}
return nil
}

View File

@@ -19,11 +19,14 @@ package main
import (
"context"
"fmt"
"github.com/fluxcd/pkg/untar"
"io/ioutil"
"net/http"
"os"
"path"
"path/filepath"
"strings"
"time"
"github.com/spf13/cobra"
"sigs.k8s.io/kustomize/api/filesys"
@@ -36,18 +39,22 @@ var installCmd = &cobra.Command{
Long: `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.`,
Example: ` # Install the latest version in the gitops-systems namespace
tk install --version=master --namespace=gitops-systems
tk install --version=latest --namespace=gitops-systems
# Dry-run install for a specific version and a series of components
tk install --dry-run --version=0.0.1 --components="source-controller,kustomize-controller"
tk install --dry-run --version=v0.0.7 --components="source-controller,kustomize-controller"
# Dry-run install with manifests preview
tk install --dry-run --verbose
# Write install manifests to file
tk install --export > gitops-system.yaml
`,
RunE: installCmdRun,
}
var (
installExport bool
installDryRun bool
installManifestsPath string
installVersion string
@@ -55,10 +62,12 @@ var (
)
func init() {
installCmd.Flags().BoolVar(&installExport, "export", false,
"write the install manifests to stdout and exit")
installCmd.Flags().BoolVarP(&installDryRun, "dry-run", "", false,
"only print the object that would be applied")
installCmd.Flags().StringVarP(&installVersion, "version", "v", defaultVersion,
"toolkit tag or branch")
"toolkit version")
installCmd.Flags().StringSliceVar(&installComponents, "components", defaultComponents,
"list of components, accepts comma-separated values")
installCmd.Flags().StringVarP(&installManifestsPath, "manifests", "", "",
@@ -84,7 +93,9 @@ func installCmdRun(cmd *cobra.Command, args []string) error {
}
defer os.RemoveAll(tmpDir)
logger.Generatef("generating manifests")
if !installExport {
logger.Generatef("generating manifests")
}
if kustomizePath == "" {
err = genInstallManifests(installVersion, namespace, installComponents, tmpDir)
if err != nil {
@@ -104,6 +115,12 @@ func installCmdRun(cmd *cobra.Command, args []string) error {
} else {
if verbose {
fmt.Print(yaml)
} else if installExport {
fmt.Println("---")
fmt.Println("# GitOps Toolkit revision", installVersion, time.Now().Format(time.RFC3339))
fmt.Print(yaml)
fmt.Println("---")
return nil
}
}
logger.Successf("manifests build completed")
@@ -174,10 +191,10 @@ transformers:
- labels.yaml
resources:
- namespace.yaml
- policies.yaml
- roles
- github.com/fluxcd/toolkit/manifests/policies?ref={{$version}}
{{- range .Components }}
- github.com/fluxcd/toolkit/manifests/bases/{{.}}?ref={{$version}}
- {{.}}.yaml
{{- end }}
`
@@ -185,10 +202,44 @@ var kustomizationRolesTmpl = `---
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- github.com/fluxcd/toolkit/manifests/rbac?ref={{.Version}}
- rbac.yaml
nameSuffix: -{{.Namespace}}
`
func downloadManifests(version string, tmpDir string) error {
ghURL := "https://github.com/fluxcd/toolkit/releases/latest/download/manifests.tar.gz"
if strings.HasPrefix(version, "v") {
ghURL = fmt.Sprintf("https://github.com/fluxcd/toolkit/releases/download/%s/manifests.tar.gz", version)
}
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
req, err := http.NewRequest("GET", ghURL, nil)
if err != nil {
return fmt.Errorf("failed to create HTTP request for %s, error: %w", ghURL, err)
}
// download
resp, err := http.DefaultClient.Do(req.WithContext(ctx))
if err != nil {
return fmt.Errorf("failed to download artifact from %s, error: %w", ghURL, err)
}
defer resp.Body.Close()
// check response
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("faild to download artifact from %s, status: %s", ghURL, resp.Status)
}
// extract
if _, err = untar.Untar(resp.Body, tmpDir); err != nil {
return fmt.Errorf("faild to untar manifests from %s, error: %w", ghURL, err)
}
return nil
}
func genInstallManifests(version string, namespace string, components []string, tmpDir string) error {
model := struct {
Version string
@@ -200,6 +251,10 @@ func genInstallManifests(version string, namespace string, components []string,
Components: components,
}
if err := downloadManifests(version, tmpDir); err != nil {
return err
}
if err := utils.execTemplate(model, namespaceTmpl, path.Join(tmpDir, "namespace.yaml")); err != nil {
return fmt.Errorf("generate namespace failed: %w", err)
}
@@ -220,6 +275,10 @@ func genInstallManifests(version string, namespace string, components []string,
return fmt.Errorf("generate roles failed: %w", err)
}
if err := utils.copyFile(filepath.Join(tmpDir, "rbac.yaml"), filepath.Join(tmpDir, "roles/rbac.yaml")); err != nil {
return fmt.Errorf("generate rbac failed: %w", err)
}
return nil
}

View File

@@ -65,7 +65,7 @@ var rootCmd = &cobra.Command{
--path="./deploy/webapp/" \
--prune=true \
--interval=5m \
--validate=client \
--validation=client \
--health-check="Deployment/backend.webapp" \
--health-check="Deployment/frontend.webapp" \
--health-check-timeout=2m
@@ -105,7 +105,7 @@ var (
var (
defaultComponents = []string{"source-controller", "kustomize-controller", "helm-controller", "notification-controller"}
defaultVersion = "master"
defaultVersion = "latest"
defaultNamespace = "gitops-system"
)

View File

@@ -34,7 +34,7 @@ var reconcileSourceHelmCmd = &cobra.Command{
Use: "helm [name]",
Short: "Reconcile a HelmRepository source",
Long: `The reconcile source command triggers a reconciliation of a HelmRepository resource and waits for it to finish.`,
Example: ` # Trigger a helm repo update for an existing source
Example: ` # Trigger a reconciliation for an existing source
tk reconcile source helm podinfo
`,
RunE: syncSourceHelmCmdRun,

View File

@@ -35,6 +35,9 @@ var resumeHrCmd = &cobra.Command{
Short: "Resume a suspended HelmRelease",
Long: `The resume command marks a previously suspended HelmRelease resource for reconciliation and waits for it to
finish the apply.`,
Example: ` # Resume reconciliation for an existing Helm release
tk resume hr podinfo
`,
RunE: resumeHrCmdRun,
}

View File

@@ -35,6 +35,9 @@ var resumeKsCmd = &cobra.Command{
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.`,
Example: ` # Resume reconciliation for an existing Kustomization
tk resume ks podinfo
`,
RunE: resumeKsCmdRun,
}

View File

@@ -31,7 +31,10 @@ var suspendHrCmd = &cobra.Command{
Aliases: []string{"hr"},
Short: "Suspend reconciliation of HelmRelease",
Long: "The suspend command disables the reconciliation of a HelmRelease resource.",
RunE: suspendHrCmdRun,
Example: ` # Suspend reconciliation for an existing Helm release
tk suspend hr podinfo
`,
RunE: suspendHrCmdRun,
}
func init() {

View File

@@ -29,7 +29,10 @@ var suspendKsCmd = &cobra.Command{
Aliases: []string{"ks"},
Short: "Suspend reconciliation of Kustomization",
Long: "The suspend command disables the reconciliation of a Kustomization resource.",
RunE: suspendKsCmdRun,
Example: ` # Suspend reconciliation for an existing Kustomization
tk suspend ks podinfo
`,
RunE: suspendKsCmdRun,
}
func init() {

View File

@@ -30,10 +30,10 @@ var uninstallCmd = &cobra.Command{
Short: "Uninstall the toolkit components",
Long: "The uninstall command removes the namespace, cluster roles, cluster role bindings and CRDs from the cluster.",
Example: ` # Dry-run uninstall of all components
uninstall --dry-run --namespace=gitops-system
tk uninstall --dry-run --namespace=gitops-system
# Uninstall all components and delete custom resource definitions
uninstall --crds --namespace=gitops-system
tk uninstall --crds --namespace=gitops-system
`,
RunE: uninstallCmdRun,
}

View File

@@ -26,12 +26,14 @@ import (
"os/exec"
"text/template"
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1alpha1"
sourcev1 "github.com/fluxcd/source-controller/api/v1alpha1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/tools/clientcmd"
"sigs.k8s.io/controller-runtime/pkg/client"
helmv2 "github.com/fluxcd/helm-controller/api/v2alpha1"
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1alpha1"
sourcev1 "github.com/fluxcd/source-controller/api/v1alpha1"
)
type Utils struct {
@@ -118,6 +120,7 @@ func (*Utils) kubeClient(config string) (client.Client, error) {
_ = corev1.AddToScheme(scheme)
_ = sourcev1.AddToScheme(scheme)
_ = kustomizev1.AddToScheme(scheme)
_ = helmv2.AddToScheme(scheme)
kubeClient, err := client.New(cfg, client.Options{
Scheme: scheme,
@@ -143,3 +146,23 @@ func (*Utils) writeFile(content, filename string) error {
return file.Sync()
}
func (*Utils) copyFile(src, dst string) error {
in, err := os.Open(src)
if err != nil {
return err
}
defer in.Close()
out, err := os.Create(dst)
if err != nil {
return err
}
defer out.Close()
_, err = io.Copy(out, in)
if err != nil {
return err
}
return out.Close()
}

View File

@@ -36,7 +36,7 @@ Command line utility for assembling Kubernetes CD pipelines the GitOps way.
--path="./deploy/webapp/" \
--prune=true \
--interval=5m \
--validate=client \
--validation=client \
--health-check="Deployment/backend.webapp" \
--health-check="Deployment/frontend.webapp" \
--health-check-timeout=2m

View File

@@ -11,7 +11,7 @@ The bootstrap sub-commands bootstrap the toolkit components on the targeted Git
```
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller,helm-controller,notification-controller])
-h, --help help for bootstrap
-v, --version string toolkit tag or branch (default "master")
-v, --version string toolkit version (default "latest")
```
### Options inherited from parent commands

View File

@@ -21,19 +21,19 @@ tk bootstrap github [flags]
export GITHUB_TOKEN=<my-token>
# Run bootstrap for a private repo owned by a GitHub organization
bootstrap github --owner=<organization> --repository=<repo name>
tk bootstrap github --owner=<organization> --repository=<repo name>
# Run bootstrap for a private repo and assign organization teams to it
bootstrap github --owner=<organization> --repository=<repo name> --team=<team1 slug> --team=<team2 slug>
tk bootstrap github --owner=<organization> --repository=<repo name> --team=<team1 slug> --team=<team2 slug>
# Run bootstrap for a repository path
bootstrap github --owner=<organization> --repository=<repo name> --path=dev-cluster
tk bootstrap github --owner=<organization> --repository=<repo name> --path=dev-cluster
# Run bootstrap for a public repository on a personal account
bootstrap github --owner=<user> --repository=<repo name> --private=false --personal=true
tk bootstrap github --owner=<user> --repository=<repo name> --private=false --personal=true
# Run bootstrap for a private repo hosted on GitHub Enterprise
bootstrap github --owner=<organization> --repository=<repo name> --hostname=<domain>
tk bootstrap github --owner=<organization> --repository=<repo name> --hostname=<domain>
```
@@ -59,7 +59,7 @@ tk bootstrap github [flags]
--namespace string the namespace scope for this operation (default "gitops-system")
--timeout duration timeout for this operation (default 5m0s)
--verbose print generated objects
-v, --version string toolkit tag or branch (default "master")
-v, --version string toolkit version (default "latest")
```
### SEE ALSO

View File

@@ -21,16 +21,16 @@ tk bootstrap gitlab [flags]
export GITLAB_TOKEN=<my-token>
# Run bootstrap for a private repo owned by a GitLab group
bootstrap gitlab --owner=<group> --repository=<repo name>
tk bootstrap gitlab --owner=<group> --repository=<repo name>
# Run bootstrap for a repository path
bootstrap gitlab --owner=<group> --repository=<repo name> --path=dev-cluster
tk bootstrap gitlab --owner=<group> --repository=<repo name> --path=dev-cluster
# Run bootstrap for a public repository on a personal account
bootstrap gitlab --owner=<user> --repository=<repo name> --private=false --personal=true
tk bootstrap gitlab --owner=<user> --repository=<repo name> --private=false --personal=true
# Run bootstrap for a private repo hosted on a GitLab server
bootstrap gitlab --owner=<group> --repository=<repo name> --hostname=<domain>
tk bootstrap gitlab --owner=<group> --repository=<repo name> --hostname=<domain>
```
@@ -55,7 +55,7 @@ tk bootstrap gitlab [flags]
--namespace string the namespace scope for this operation (default "gitops-system")
--timeout duration timeout for this operation (default 5m0s)
--verbose print generated objects
-v, --version string toolkit tag or branch (default "master")
-v, --version string toolkit version (default "latest")
```
### SEE ALSO

View File

@@ -26,6 +26,7 @@ The create sub-commands generate sources and resources.
### SEE ALSO
* [tk](tk.md) - Command line utility for assembling Kubernetes CD pipelines
* [tk create helmrelease](tk_create_helmrelease.md) - Create or update a HelmRelease resource
* [tk create kustomization](tk_create_kustomization.md) - Create or update a Kustomization resource
* [tk create source](tk_create_source.md) - Create or update sources

View File

@@ -0,0 +1,71 @@
## tk create helmrelease
Create or update a HelmRelease resource
### Synopsis
The helmrelease create command generates a HelmRelease resource for a given HelmRepository source.
```
tk create helmrelease [name] [flags]
```
### Examples
```
# Create a HelmRelease from a source
tk create hr podinfo \
--interval=10m \
--release-name=podinfo \
--target-namespace=default \
--source=podinfo \
--chart-name=podinfo \
--chart-version=">4.0.0"
# Create a HelmRelease with values for a local YAML file
tk create hr podinfo \
--target-namespace=default \
--source=podinfo \
--chart-name=podinfo \
--chart-version=4.0.5 \
--values=./my-values.yaml
# Create a HelmRelease definition on disk without applying it on the cluster
tk create hr podinfo \
--target-namespace=default \
--source=podinfo \
--chart-name=podinfo \
--chart-version=4.0.5 \
--values=./values.yaml \
--export > podinfo-release.yaml
```
### Options
```
--chart-name string Helm chart name
--chart-version string Helm chart version, accepts semver range
--depends-on stringArray HelmReleases that must be ready before this release can be installed
-h, --help help for helmrelease
--release-name string name used for the Helm release, defaults to a composition of '<target-namespace>-<hr-name>'
--source string HelmRepository name
--target-namespace string namespace to install this release, defaults to the HelmRelease namespace
--values string local path to the values.yaml file
```
### Options inherited from parent commands
```
--export export in YAML format to stdout
--interval duration source sync interval (default 1m0s)
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
--namespace string the namespace scope for this operation (default "gitops-system")
--timeout duration timeout for this operation (default 5m0s)
--verbose print generated objects
```
### SEE ALSO
* [tk create](tk_create.md) - Create or update sources and resources

View File

@@ -14,32 +14,32 @@ tk create kustomization [name] [flags]
```
# Create a Kustomization resource from a source at a given path
create kustomization contour \
tk create kustomization contour \
--source=contour \
--path="./examples/contour/" \
--prune=true \
--interval=10m \
--validate=client \
--validation=client \
--health-check="Deployment/contour.projectcontour" \
--health-check="DaemonSet/envoy.projectcontour" \
--health-check-timeout=3m
# Create a Kustomization resource that depends on the previous one
create kustomization webapp \
tk create kustomization webapp \
--depends-on=contour \
--source=webapp \
--path="./deploy/overlays/dev" \
--prune=true \
--interval=5m \
--validate=client
--validation=client
# Create a Kustomization resource that runs under a service account
create kustomization webapp \
tk create kustomization webapp \
--source=webapp \
--path="./deploy/overlays/staging" \
--prune=true \
--interval=5m \
--validate=client \
--validation=client \
--sa-name=reconclier \
--sa-namespace=staging
@@ -57,7 +57,7 @@ tk create kustomization [name] [flags]
--sa-name string service account name
--sa-namespace string service account namespace
--source string GitRepository name
--validate string validate the manifests before applying them on the cluster, can be 'client' or 'server'
--validation string validate the manifests before applying them on the cluster, can be 'client' or 'server'
```
### Options inherited from parent commands

View File

@@ -27,4 +27,5 @@ The create source sub-commands generate sources.
* [tk create](tk_create.md) - Create or update sources and resources
* [tk create source git](tk_create_source_git.md) - Create or update a GitRepository source
* [tk create source helm](tk_create_source_helm.md) - Create or update a HelmRepository source

View File

@@ -17,35 +17,35 @@ tk create source git [name] [flags]
```
# Create a source from a public Git repository master branch
create source git podinfo \
tk create source git podinfo \
--url=https://github.com/stefanprodan/podinfo \
--branch=master
# Create a source from a Git repository pinned to specific git tag
create source git podinfo \
tk create source git podinfo \
--url=https://github.com/stefanprodan/podinfo \
--tag="3.2.3"
# Create a source from a public Git repository tag that matches a semver range
create source git podinfo \
tk create source git podinfo \
--url=https://github.com/stefanprodan/podinfo \
--tag-semver=">=3.2.0 <3.3.0"
# Create a source from a Git repository using SSH authentication
create source git podinfo \
tk create source git podinfo \
--url=ssh://git@github.com/stefanprodan/podinfo \
--branch=master
# Create a source from a Git repository using SSH authentication and an
# ECDSA P-521 curve public key
create source git podinfo \
tk create source git podinfo \
--url=ssh://git@github.com/stefanprodan/podinfo \
--branch=master \
--ssh-key-algorithm=ecdsa \
--ssh-ecdsa-curve=p521
# Create a source from a Git repository using basic authentication
create source git podinfo \
tk create source git podinfo \
--url=https://github.com/stefanprodan/podinfo \
--username=username \
--password=password

View File

@@ -0,0 +1,54 @@
## tk create source helm
Create or update a HelmRepository source
### Synopsis
The create source helm command generates a HelmRepository resource and waits for it to fetch the index.
For private Helm repositories, the basic authentication credentials are stored in a Kubernetes secret.
```
tk create source helm [name] [flags]
```
### Examples
```
# Create a source from a public Helm repository
tk create source helm podinfo \
--url=https://stefanprodan.github.io/podinfo \
--interval=10m
# Create a source from a Helm repository using basic authentication
tk create source helm podinfo \
--url=https://stefanprodan.github.io/podinfo \
--username=username \
--password=password
```
### Options
```
-h, --help help for helm
-p, --password string basic authentication password
--url string Helm repository address
-u, --username string basic authentication username
```
### Options inherited from parent commands
```
--export export in YAML format to stdout
--interval duration source sync interval (default 1m0s)
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
--namespace string the namespace scope for this operation (default "gitops-system")
--timeout duration timeout for this operation (default 5m0s)
--verbose print generated objects
```
### SEE ALSO
* [tk create source](tk_create_source.md) - Create or update sources

View File

@@ -25,6 +25,7 @@ The delete sub-commands delete sources and resources.
### SEE ALSO
* [tk](tk.md) - Command line utility for assembling Kubernetes CD pipelines
* [tk delete helmrelease](tk_delete_helmrelease.md) - Delete a HelmRelease resource
* [tk delete kustomization](tk_delete_kustomization.md) - Delete a Kustomization resource
* [tk delete source](tk_delete_source.md) - Delete sources

View File

@@ -0,0 +1,40 @@
## tk delete helmrelease
Delete a HelmRelease resource
### Synopsis
The delete helmrelease command removes the given HelmRelease from the cluster.
```
tk delete helmrelease [name] [flags]
```
### Examples
```
# Delete a Helm release and the Kubernetes resources created by it
tk delete hr podinfo
```
### Options
```
-h, --help help for helmrelease
```
### Options inherited from parent commands
```
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
--namespace string the namespace scope for this operation (default "gitops-system")
-s, --silent delete resource without asking for confirmation
--timeout duration timeout for this operation (default 5m0s)
--verbose print generated objects
```
### SEE ALSO
* [tk delete](tk_delete.md) - Delete sources and resources

View File

@@ -10,6 +10,14 @@ The delete kustomization command deletes the given Kustomization from the cluste
tk delete kustomization [name] [flags]
```
### Examples
```
# Delete a kustomization and the Kubernetes resources created by it
tk delete kustomization podinfo
```
### Options
```

View File

@@ -26,4 +26,5 @@ The delete source sub-commands delete sources.
* [tk delete](tk_delete.md) - Delete sources and resources
* [tk delete source git](tk_delete_source_git.md) - Delete a GitRepository source
* [tk delete source helm](tk_delete_source_helm.md) - Delete a HelmRepository source

View File

@@ -10,6 +10,14 @@ The delete source git command deletes the given GitRepository from the cluster.
tk delete source git [name] [flags]
```
### Examples
```
# Delete a Git repository
tk delete source git podinfo
```
### Options
```

View File

@@ -0,0 +1,40 @@
## tk delete source helm
Delete a HelmRepository source
### Synopsis
The delete source helm command deletes the given HelmRepository from the cluster.
```
tk delete source helm [name] [flags]
```
### Examples
```
# Delete a Helm repository
tk delete source helm podinfo
```
### Options
```
-h, --help help for helm
```
### Options inherited from parent commands
```
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
--namespace string the namespace scope for this operation (default "gitops-system")
-s, --silent delete resource without asking for confirmation
--timeout duration timeout for this operation (default 5m0s)
--verbose print generated objects
```
### SEE ALSO
* [tk delete source](tk_delete_source.md) - Delete sources

View File

@@ -25,6 +25,7 @@ The export sub-commands export resources in YAML format.
### SEE ALSO
* [tk](tk.md) - Command line utility for assembling Kubernetes CD pipelines
* [tk export helmrelease](tk_export_helmrelease.md) - Export HelmRelease resources in YAML format
* [tk export kustomization](tk_export_kustomization.md) - Export Kustomization resources in YAML format
* [tk export source](tk_export_source.md) - Export sources

View File

@@ -0,0 +1,43 @@
## tk export helmrelease
Export HelmRelease resources in YAML format
### Synopsis
The export helmrelease command exports one or all HelmRelease resources in YAML format.
```
tk export helmrelease [name] [flags]
```
### Examples
```
# Export all HelmRelease resources
tk export helmrelease --all > kustomizations.yaml
# Export a HelmRelease
tk export hr my-app > app-release.yaml
```
### Options
```
-h, --help help for helmrelease
```
### Options inherited from parent commands
```
--all select all resources
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
--namespace string the namespace scope for this operation (default "gitops-system")
--timeout duration timeout for this operation (default 5m0s)
--verbose print generated objects
```
### SEE ALSO
* [tk export](tk_export.md) - Export resources in YAML format

View File

@@ -14,10 +14,10 @@ tk export kustomization [name] [flags]
```
# Export all Kustomization resources
export kustomization --all > kustomizations.yaml
tk export kustomization --all > kustomizations.yaml
# Export a Kustomization
export kustomization my-app > kustomization.yaml
tk export kustomization my-app > kustomization.yaml
```

View File

@@ -27,4 +27,5 @@ The export source sub-commands export sources in YAML format.
* [tk export](tk_export.md) - Export resources in YAML format
* [tk export source git](tk_export_source_git.md) - Export GitRepository sources in YAML format
* [tk export source helm](tk_export_source_helm.md) - Export HelmRepository sources in YAML format

View File

@@ -14,10 +14,10 @@ tk export source git [name] [flags]
```
# Export all GitRepository sources
export source git --all > sources.yaml
tk export source git --all > sources.yaml
# Export a GitRepository source including the SSH key pair or basic auth credentials
export source git my-private-repo --with-credentials > source.yaml
tk export source git my-private-repo --with-credentials > source.yaml
```

View File

@@ -0,0 +1,44 @@
## tk export source helm
Export HelmRepository sources in YAML format
### Synopsis
The export source git command exports on or all HelmRepository sources in YAML format.
```
tk export source helm [name] [flags]
```
### Examples
```
# Export all HelmRepository sources
tk export source helm --all > sources.yaml
# Export a HelmRepository source including the basic auth credentials
tk export source helm my-private-repo --with-credentials > source.yaml
```
### Options
```
-h, --help help for helm
```
### Options inherited from parent commands
```
--all select all resources
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
--namespace string the namespace scope for this operation (default "gitops-system")
--timeout duration timeout for this operation (default 5m0s)
--verbose print generated objects
--with-credentials include credential secrets
```
### SEE ALSO
* [tk export source](tk_export_source.md) - Export sources

View File

@@ -24,6 +24,7 @@ The get sub-commands print the statuses of sources and resources.
### SEE ALSO
* [tk](tk.md) - Command line utility for assembling Kubernetes CD pipelines
* [tk get kustomizations](tk_get_kustomizations.md) - Get Kustomization source statuses
* [tk get helmreleases](tk_get_helmreleases.md) - Get HelmRelease statuses
* [tk get kustomizations](tk_get_kustomizations.md) - Get Kustomization statuses
* [tk get sources](tk_get_sources.md) - Get source statuses

View File

@@ -0,0 +1,39 @@
## tk get helmreleases
Get HelmRelease statuses
### Synopsis
The get helmreleases command prints the statuses of the resources.
```
tk get helmreleases [flags]
```
### Examples
```
# List all Helm releases and their status
tk get helmreleases
```
### Options
```
-h, --help help for helmreleases
```
### Options inherited from parent commands
```
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
--namespace string the namespace scope for this operation (default "gitops-system")
--timeout duration timeout for this operation (default 5m0s)
--verbose print generated objects
```
### SEE ALSO
* [tk get](tk_get.md) - Get sources and resources

View File

@@ -1,6 +1,6 @@
## tk get kustomizations
Get Kustomization source statuses
Get Kustomization statuses
### Synopsis
@@ -10,6 +10,14 @@ The get kustomizations command prints the statuses of the resources.
tk get kustomizations [flags]
```
### Examples
```
# List all kustomizations and their status
tk get kustomizations
```
### Options
```

View File

@@ -25,4 +25,5 @@ The get source sub-commands print the statuses of the sources.
* [tk get](tk_get.md) - Get sources and resources
* [tk get sources git](tk_get_sources_git.md) - Get GitRepository source statuses
* [tk get sources helm](tk_get_sources_helm.md) - Get HelmRepository source statuses

View File

@@ -10,6 +10,14 @@ The get sources git command prints the status of the GitRepository sources.
tk get sources git [flags]
```
### Examples
```
# List all Git repositories and their status
tk get sources git
```
### Options
```

View File

@@ -0,0 +1,39 @@
## tk get sources helm
Get HelmRepository source statuses
### Synopsis
The get sources helm command prints the status of the HelmRepository sources.
```
tk get sources helm [flags]
```
### Examples
```
# List all Helm repositories and their status
tk get sources helm
```
### Options
```
-h, --help help for helm
```
### Options inherited from parent commands
```
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
--namespace string the namespace scope for this operation (default "gitops-system")
--timeout duration timeout for this operation (default 5m0s)
--verbose print generated objects
```
### SEE ALSO
* [tk get sources](tk_get_sources.md) - Get source statuses

View File

@@ -15,14 +15,17 @@ tk install [flags]
```
# Install the latest version in the gitops-systems namespace
tk install --version=master --namespace=gitops-systems
tk install --version=latest --namespace=gitops-systems
# Dry-run install for a specific version and a series of components
tk install --dry-run --version=0.0.1 --components="source-controller,kustomize-controller"
tk install --dry-run --version=v0.0.7 --components="source-controller,kustomize-controller"
# Dry-run install with manifests preview
tk install --dry-run --verbose
# Write install manifests to file
tk install --export > gitops-system.yaml
```
### Options
@@ -30,9 +33,10 @@ tk install [flags]
```
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller,helm-controller,notification-controller])
--dry-run only print the object that would be applied
--export write the install manifests to stdout and exit
-h, --help help for install
--manifests string path to the manifest directory, dev only
-v, --version string toolkit tag or branch (default "master")
-v, --version string toolkit version (default "latest")
```
### Options inherited from parent commands

View File

@@ -13,7 +13,7 @@ tk reconcile source helm [name] [flags]
### Examples
```
# Trigger a helm repo update for an existing source
# Trigger a reconciliation for an existing source
tk reconcile source helm podinfo
```

View File

@@ -11,6 +11,14 @@ finish the apply.
tk resume helmrelease [name] [flags]
```
### Examples
```
# Resume reconciliation for an existing Helm release
tk resume hr podinfo
```
### Options
```

View File

@@ -11,6 +11,14 @@ finish the apply.
tk resume kustomization [name] [flags]
```
### Examples
```
# Resume reconciliation for an existing Kustomization
tk resume ks podinfo
```
### Options
```

View File

@@ -10,6 +10,14 @@ The suspend command disables the reconciliation of a HelmRelease resource.
tk suspend helmrelease [name] [flags]
```
### Examples
```
# Suspend reconciliation for an existing Helm release
tk suspend hr podinfo
```
### Options
```

View File

@@ -10,6 +10,14 @@ The suspend command disables the reconciliation of a Kustomization resource.
tk suspend kustomization [name] [flags]
```
### Examples
```
# Suspend reconciliation for an existing Kustomization
tk suspend ks podinfo
```
### Options
```

View File

@@ -14,10 +14,10 @@ tk uninstall [flags]
```
# Dry-run uninstall of all components
uninstall --dry-run --namespace=gitops-system
tk uninstall --dry-run --namespace=gitops-system
# Uninstall all components and delete custom resource definitions
uninstall --crds --namespace=gitops-system
tk uninstall --crds --namespace=gitops-system
```

View File

@@ -2,7 +2,7 @@
## Prerequisites
You will need two Kubernetes clusters version 1.14 or newer and kubectl version 1.18.
You will need two Kubernetes clusters version 1.16 or newer and kubectl version 1.18.
For a quick local test, you can use [Kubernetes kind](https://kind.sigs.k8s.io/docs/user/quick-start/).
Any other Kubernetes setup will work as well though.
@@ -63,7 +63,7 @@ $ tk check --pre
► checking prerequisites
✔ kubectl 1.18.3 >=1.18.0
✔ kubernetes 1.18.2 >=1.14.0
✔ kubernetes 1.18.2 >=1.16.0
✔ prerequisites checks passed
```
@@ -152,7 +152,7 @@ tk create kustomization webapp-common \
--source=webapp \
--path="./deploy/webapp/common" \
--prune=true \
--validate=client \
--validation=client \
--interval=1h \
--export > ./staging-cluster/webapp-common.yaml
```
@@ -165,7 +165,7 @@ tk create kustomization webapp-backend \
--source=webapp \
--path="./deploy/webapp/backend" \
--prune=true \
--validate=client \
--validation=client \
--interval=10m \
--health-check="Deployment/backend.webapp" \
--health-check-timeout=2m \
@@ -180,7 +180,7 @@ tk create kustomization webapp-frontend \
--source=webapp \
--path="./deploy/webapp/frontend" \
--prune=true \
--validate=client \
--validation=client \
--interval=10m \
--health-check="Deployment/frontend.webapp" \
--health-check-timeout=2m \
@@ -227,7 +227,7 @@ If you delete a kustomization from the `fleet-infra` repo, the reconciler will r
were previously applied from that kustomization.
If you alter the webapp deployment using `kubectl edit`, the changes will be reverted to match
the state described in git. When dealing with an incident, you can pause the recitation of a
the state described in git. When dealing with an incident, you can pause the reconciliation of a
kustomization with `tk suspend kustomization <name>`. Once the debugging session
is over, you can re-enable the reconciliation with `tk resume kustomization <name>`.
@@ -278,7 +278,7 @@ tk create kustomization webapp \
--source=webapp \
--path="./deploy/overlays/production" \
--prune=true \
--validate=client \
--validation=client \
--interval=10m \
--health-check="Deployment/frontend.production" \
--health-check="Deployment/backend.production" \

View File

@@ -1,4 +1,4 @@
# Manage Helm releases
# Manage Helm Releases
The [helm-controller](../components/helm/controller.md) allows you to
declaratively manage Helm chart releases with Kubernetes manifests.
@@ -93,7 +93,7 @@ helm-controller.
See the [`HelmRelease` CRD docs](../components/helm/helmreleases.md)
for more details.
## Receive notifications
## Configure notifications
The default toolkit installation configures the helm-controller to
broadcast events to the [notification-controller](../components/notification/controller.md).
@@ -126,3 +126,67 @@ apiVersion: notification.fluxcd.io/v1alpha1
```
![helm-controller alerts](../diagrams/helm-controller-alerts.png)
## Configure webhook receivers
When using semver ranges for Helm releases, you may want to trigger an update
as soon as a new chart version is published to your Helm repository.
In order to notify source-controller about a chart update,
you can [setup webhook receivers](webhook-receivers.md).
First generate a random string and create a secret with a `token` field:
```sh
TOKEN=$(head -c 12 /dev/urandom | shasum | cut -d ' ' -f1)
echo $TOKEN
kubectl -n gitops-system create secret generic webhook-token \
--from-literal=token=$TOKEN
```
When using [Harbor](https://goharbor.io/) as your Helm repository, you can define a receiver with:
```yaml
apiVersion: notification.fluxcd.io/v1alpha1
kind: Receiver
metadata:
name: helm-podinfo
namespace: gitops-system
spec:
type: harbor
secretRef:
name: webhook-token
resources:
- kind: HelmRepository
name: podinfo
```
The notification-controller generates a unique URL using the provided token and the receiver name/namespace.
Find the URL with:
```console
$ kubectl -n gitops-system get receiver/helm-podinfo
NAME READY STATUS
helm-podinfo True Receiver initialised with URL: /hook/bed6d00b5555b1603e1f59b94d7fdbca58089cb5663633fb83f2815dc626d92b
```
Log in to the Harbor interface, go to Projects, select a project, and select Webhooks.
Fill the form with:
* Endpoint URL: compose the address using the receiver LB and the generated URL `http://<LoadBalancerAddress>/<ReceiverURL>`
* Auth Header: use the `token` string
With the above settings, when you upload a chart, the following happens:
* Harbor sends the chart push event to the receiver address
* Notification controller validates the authenticity of the payload using the auth header
* Source controller is notified about the changes
* Source controller pulls the changes into the cluster and updates the `HelmChart` version
* Helm controller is notified about the version change and upgrades the release
!!! hint "Note"
Besides Harbor, you can define receivers for **GitHub**, **GitLab**, **Bitbucket**
and any other system that supports webhooks e.g. Jenkins, CircleCI, etc.
See the [Receiver CRD docs](../components/notification/receiver.md) for more details.

View File

@@ -0,0 +1,510 @@
# go-git-providers
## Abstract
This proposal aims to create a library with the import path `github.com/fluxcd/go-git-providers`'
(import name: `gitprovider`), which provides an abstraction layer for talking to Git providers
like GitHub, GitLab and Bitbucket.
This would become a new repository, specifically targeted at being a general-purpose Git provider
client for multiple providers and domains.
## Goals
- Support multiple Git provider backends (e.g. GitHub, GitLab, Bitbucket, etc.) using the same interface
- Support talking to multiple domains at once, including custom domains (e.g. talking to "gitlab.com" and "version.aalto.fi" from the same client)
- Support both no authentication (for public repos), basic auth, and OAuth2 for authentication
- Manipulating the following resources:
- **Organizations**: `GET`, `LIST` (both all accessible top-level orgs and sub-orgs)
- For a given **Organization**:
- **Teams**: `GET` and `LIST`
- **Repositories**: `GET`, `LIST` and `POST`
- **Team Access**: `LIST`, `POST` and `DELETE`
- **Credentials**: `LIST`, `POST` and `DELETE`
- Support sub-organizations (or "sub-groups" in GitLab) if possible
- Support reconciling an object for idempotent operations
- Pagination is automatically handled for `LIST` requests
- Transparently can manage teams (collections of users, sub-groups in Gitlab) with varying access to repos
- Follow library best practices in order to be easy to vendor (e.g. use major `vX` versioning & go.mod)
## Non-goals
- Support for features not mentioned above
## Design decisions
- A `context.Context` should be passed to every request as the first argument
- There should be two interfaces per resource, if applicable:
- one collection-specific interface, with a plural name (e.g. `OrganizationsClient`), that has methods like `Get()` and `List()`
- one instance-specific interface, with a singular name (e.g. `OrganizationClient`), that operates on that instance, e.g. allowing access to child resources, e.g. `Teams()`
- Every `Create()` signature shall have a `{Resource}CreateOptions` struct as the last argument.
- `Delete()` and similar methods may use the same pattern if needed
- All `*Options` structs shall be passed by value (i.e. non-nillable) and contain only nillable, optional fields
- All optional fields in the type structs shall be nillable
- It should be possible to create a fake API client for testing, implementing the same interfaces
- All type structs shall have a `Validate()` method, and optionally a `Default()` one
- All type structs shall expose their internal representation (from the underlying library) through the `InternalGetter` interface with a method `GetInternal() interface{}`
- Typed errors shall be returned, wrapped using Go 1.14's new features
- Go-style enums are used when there are only a few supported values for a field
- Every field is documented using Godoc comment, including `+required` or `+optional` to clearly signify its importance
- Support serializing the types to JSON (if needed for e.g. debugging) by adding tags
## Implementation
### Provider package
The provider package, e.g. at `github.com/fluxcd/go-git-providers/github`, will have constructor methods so a client can be created, e.g. as follows:
```go
// Create a client for github.com without any authentication
c := github.NewClient()
// Create a client for an enterprise GitHub account, without any authentication
c = github.NewClient(github.WithBaseURL("enterprise.github.com"))
// Create a client for github.com using a personal oauth2 token
c = github.NewClient(github.WithOAuth2("<token-here>"))
```
### Client
The definition of a `Client` is as follows:
```go
// Client is an interface that allows talking to a Git provider
type Client interface {
// The Client allows accessing all known resources
ResourceClient
// SupportedDomain returns the supported domain
// This field is set at client creation time, and can't be changed
SupportedDomain() string
// ProviderID returns the provider ID (e.g. "github", "gitlab") for this client
// This field is set at client creation time, and can't be changed
ProviderID() ProviderID
// Raw returns the Go client used under the hood for accessing the Git provider
Raw() interface{}
}
```
As one can see, the `Client` is scoped for a single backing domain. `ProviderID` is a typed string, and every
implementation package defines their own constant, e.g. `const ProviderName = gitprovider.ProviderID("github")`.
The `ResourceClient` actually allows talking to resources of the API, both for single objects, and collections:
```go
// ResourceClient allows access to resource-specific clients
type ResourceClient interface {
// Organization gets the OrganizationClient for the specific top-level organization
// ErrNotTopLevelOrganization will be returned if the organization is not top-level when using
Organization(o OrganizationRef) OrganizationClient
// Organizations returns the OrganizationsClient handling sets of organizations
Organizations() OrganizationsClient
// Repository gets the RepositoryClient for the specified RepositoryRef
Repository(r RepositoryRef) RepositoryClient
// Repositories returns the RepositoriesClient handling sets of organizations
Repositories() RepositoriesClient
}
```
In order to reference organizations and repositories, there are the `OrganizationRef` and `RepositoryRef`
interfaces:
```go
// OrganizationRef references an organization in a Git provider
type OrganizationRef interface {
// String returns the HTTPS URL
fmt.Stringer
// GetDomain returns the URL-domain for the Git provider backend, e.g. gitlab.com or version.aalto.fi
GetDomain() string
// GetOrganization returns the top-level organization, i.e. "weaveworks" or "kubernetes-sigs"
GetOrganization() string
// GetSubOrganizations returns the names of sub-organizations (or sub-groups),
// e.g. ["engineering", "frontend"] would be returned for gitlab.com/weaveworks/engineering/frontend
GetSubOrganizations() []string
}
// RepositoryRef references a repository hosted by a Git provider
type RepositoryRef interface {
// RepositoryRef requires an OrganizationRef to fully-qualify a repo reference
OrganizationRef
// GetRepository returns the name of the repository
GetRepository() string
}
```
Along with these, there is `OrganizationInfo` and `RepositoryInfo` which implement the above mentioned interfaces in a straightforward way.
If you want to create an `OrganizationRef` or `RepositoryRef`, you can either use `NewOrganizationInfo()` or `NewRepositoryInfo()`, filling in all parts of the reference, or use the `ParseRepositoryURL(r string) (RepositoryRef, error)` or `ParseOrganizationURL(o string) (OrganizationRef, error)` methods.
As mentioned above, only one target domain is supported by the `Client`. This means e.g. that if the `Client` is configured for GitHub, and you feed it a GitLab URL to parse, `ErrDomainUnsupported` will be returned.
This brings us to a higher-level client abstraction, `MultiClient`.
### MultiClient
In order to automatically support multiple domains and providers using the same interface, `MultiClient` is introduced.
The user would use the `MultiClient` as follows:
```go
// Create a client to github.com without authentication
gh := github.NewClient()
// Create a client to gitlab.com, authenticating with basic auth
gl := gitlab.NewClient(gitlab.WithBasicAuth("<username>", "<password"))
// Create a client to the GitLab instance at version.aalto.fi, with a given OAuth2 token
aalto := gitlab.NewClient(gitlab.WithBaseURL("version.aalto.fi"), gitlab.WithOAuth2Token("<your-token>"))
// Create a MultiClient which supports talking to any of these backends
client := gitprovider.NewMultiClient(gh, gl, aalto)
```
The interface definition of `MultiClient` is similar to that one of `Client`, both embedding `ResourceClient`, but it also allows access to domain-specific underlying `Client`'s:
```go
// MultiClient allows talking to multiple Git providers at once
type MultiClient interface {
// The MultiClient allows accessing all known resources, automatically choosing the right underlying
// Client based on the resource's domain
ResourceClient
// SupportedDomains returns a list of known domains
SupportedDomains() []string
// ClientForDomain returns the Client used for a specific domain
ClientForDomain(domain string) (Client, bool)
}
```
### OrganizationsClient
The `OrganizationsClient` provides access to a set of organizations, as follows:
```go
// OrganizationsClient operates on organizations the user has access to
type OrganizationsClient interface {
// Get a specific organization the user has access to
// This might also refer to a sub-organization
// ErrNotFound is returned if the resource does not exist
Get(ctx context.Context, o OrganizationRef) (*Organization, error)
// List all top-level organizations the specific user has access to
// List should return all available organizations, using multiple paginated requests if needed
List(ctx context.Context) ([]Organization, error)
// Children returns the immediate child-organizations for the specific OrganizationRef o.
// The OrganizationRef may point to any sub-organization that exists
// This is not supported in GitHub
// Children should return all available organizations, using multiple paginated requests if needed
Children(ctx context.Context, o OrganizationRef) ([]Organization, error)
// Possibly add Create/Update/Delete methods later
}
```
The `Organization` struct is fairly straightforward for now:
```go
// Organization represents an (top-level- or sub-) organization
type Organization struct {
// OrganizationInfo provides the required fields
// (Domain, Organization and SubOrganizations) required for being an OrganizationRef
OrganizationInfo `json:",inline"`
// InternalHolder implements the InternalGetter interface
// +optional
InternalHolder `json:",inline"`
// Name is the human-friendly name of this organization, e.g. "Weaveworks" or "Kubernetes SIGs"
// +required
Name string `json:"name"`
// Description returns a description for the organization
// No default value at POST-time
// +optional
Description *string `json:"description"`
}
```
The `OrganizationInfo` struct is a straightforward struct just implementing the `OrganizationRef` interface
with basic fields & getters. `InternalHolder` is implementing the `InternalGetter` interface as follows, and is
embedded into all main structs:
```go
// InternalGetter allows access to the underlying object
type InternalGetter interface {
// GetInternal returns the underlying struct that's used
GetInternal() interface{}
}
// InternalHolder can be embedded into other structs to implement the InternalGetter interface
type InternalHolder struct {
// Internal contains the underlying object.
// +optional
Internal interface{} `json:"-"`
}
```
### OrganizationClient
`OrganizationClient` allows access to a specific organization's underlying resources as follows:
```go
// OrganizationClient operates on a given/specific organization
type OrganizationClient interface {
// Teams gives access to the TeamsClient for this specific organization
Teams() OrganizationTeamsClient
}
```
#### Organization Teams
Teams belonging to a certain organization can at this moment be fetched on an individual basis, or listed.
```go
// OrganizationTeamsClient handles teams organization-wide
type OrganizationTeamsClient interface {
// Get a team within the specific organization
// teamName may include slashes, to point to e.g. "sub-teams" i.e. subgroups in Gitlab
// teamName must not be an empty string
// ErrNotFound is returned if the resource does not exist
Get(ctx context.Context, teamName string) (*Team, error)
// List all teams (recursively, in terms of subgroups) within the specific organization
// List should return all available organizations, using multiple paginated requests if needed
List(ctx context.Context) ([]Team, error)
// Possibly add Create/Update/Delete methods later
}
```
The `Team` struct is defined as follows:
```go
// Team is a representation for a team of users inside of an organization
type Team struct {
// Team embeds OrganizationInfo which makes it automatically comply with OrganizationRef
OrganizationInfo `json:",inline"`
// Team embeds InternalHolder for accessing the underlying object
// +optional
InternalHolder `json:",inline"`
// Name describes the name of the team. The team name may contain slashes
// +required
Name string `json:"name"`
// Members points to a set of user names (logins) of the members of this team
// +required
Members []string `json:"members"`
}
```
In GitLab, teams could be modelled as users in a sub-group. Those users can later be added as a single unit
to access a given repository.
### RepositoriesClient
`RepositoriesClient` provides access to a set of repositories for the user.
```go
// RepositoriesClient operates on repositories the user has access to
type RepositoriesClient interface {
// Get returns the repository at the given path
// ErrNotFound is returned if the resource does not exist
Get(ctx context.Context, r RepositoryRef) (*Repository, error)
// List all repositories in the given organization
// List should return all available organizations, using multiple paginated requests if needed
List(ctx context.Context, o OrganizationRef) ([]Repository, error)
// Create creates a repository at the given organization path, with the given URL-encoded name and options
// ErrAlreadyExists will be returned if the resource already exists
Create(ctx context.Context, r *Repository, opts RepositoryCreateOptions) (*Repository, error)
// Reconcile makes sure r is the actual state in the backing Git provider. If r doesn't exist
// under the hood, it is created. If r is already the actual state, this is a no-op. If r isn't
// the actual state, the resource will either be updated or deleted/recreated.
Reconcile(ctx context.Context, r *Repository) error
}
```
`RepositoryCreateOptions` has options like `AutoInit *bool`, `LicenseTemplate *string` and so forth to allow an
one-time initialization step.
The `Repository` struct is defined as follows:
```go
// Repository represents a Git repository provided by a Git provider
type Repository struct {
// RepositoryInfo provides the required fields
// (Domain, Organization, SubOrganizations and RepositoryName)
// required for being an RepositoryRef
RepositoryInfo `json:",inline"`
// InternalHolder implements the InternalGetter interface
// +optional
InternalHolder `json:",inline"`
// Description returns a description for the repository
// No default value at POST-time
// +optional
Description *string `json:"description"`
// Visibility returns the desired visibility for the repository
// Default value at POST-time: RepoVisibilityPrivate
// +optional
Visibility *RepoVisibility
}
// GetCloneURL gets the clone URL for the specified transport type
func (r *Repository) GetCloneURL(transport TransportType) string {
return GetCloneURL(r, transport)
}
```
As can be seen, there is also a `GetCloneURL` function for the repository which allows
resolving the URL from which to clone the repo, for a given transport method (`ssh` and `https`
are supported `TransportType`s)
### RepositoryClient
`RepositoryClient` allows access to a given repository's underlying resources, like follows:
```go
// RepositoryClient operates on a given/specific repository
type RepositoryClient interface {
// TeamAccess gives access to what teams have access to this specific repository
TeamAccess() RepositoryTeamAccessClient
// Credentials gives access to manipulating credentials for accessing this specific repository
Credentials() RepositoryCredentialsClient
}
```
#### Repository Teams
`RepositoryTeamAccessClient` allows adding & removing teams from the list of authorized persons to access a repository.
```go
// RepositoryTeamAccessClient operates on the teams list for a specific repository
type RepositoryTeamAccessClient interface {
// Create adds a given team to the repo's team access control list
// ErrAlreadyExists will be returned if the resource already exists
// The embedded RepositoryInfo of ta does not need to be populated, but if it is,
// it must equal to the RepositoryRef given to the RepositoryClient.
Create(ctx context.Context, ta *TeamAccess, opts RepositoryAddTeamOptions) error
// Lists the team access control list for this repo
List(ctx context.Context) ([]TeamAccess, error)
// Reconcile makes sure ta is the actual state in the backing Git provider. If ta doesn't exist
// under the hood, it is created. If ta is already the actual state, this is a no-op. If ta isn't
// the actual state, the resource will either be updated or deleted/recreated.
// The embedded RepositoryInfo of ta does not need to be populated, but if it is,
// it must equal to the RepositoryRef given to the RepositoryClient.
Reconcile(ctx context.Context, ta *TeamAccess) error
// Delete removes the given team from the repo's team access control list
// ErrNotFound is returned if the resource does not exist
Delete(ctx context.Context, teamName string) error
}
```
The `TeamAccess` struct looks as follows:
```go
// TeamAccess describes a binding between a repository and a team
type TeamAccess struct {
// TeamAccess embeds RepositoryInfo which makes it automatically comply with RepositoryRef
// +optional
RepositoryInfo `json:",inline"`
// TeamAccess embeds InternalHolder for accessing the underlying object
// +optional
InternalHolder `json:",inline"`
// Name describes the name of the team. The team name may contain slashes
// +required
Name string `json:"name"`
// Permission describes the permission level for which the team is allowed to operate
// Default: read
// Available options: See the TeamRepositoryPermission enum
// +optional
Permission *TeamRepositoryPermission
}
```
#### Repository Credentials
`RepositoryCredentialsClient` allows adding & removing credentials (e.g. deploy keys) from accessing a specific repository.
```go
// RepositoryCredentialsClient operates on the access credential list for a specific repository
type RepositoryCredentialsClient interface {
// Create a credential with the given human-readable name, the given bytes and optional options
// ErrAlreadyExists will be returned if the resource already exists
Create(ctx context.Context, c RepositoryCredential, opts CredentialCreateOptions) error
// Lists all credentials for the given credential type
List(ctx context.Context, t RepositoryCredentialType) ([]RepositoryCredential, error)
// Reconcile makes sure c is the actual state in the backing Git provider. If c doesn't exist
// under the hood, it is created. If c is already the actual state, this is a no-op. If c isn't
// the actual state, the resource will either be updated or deleted/recreated.
Reconcile(ctx context.Context, c RepositoryCredential) error
// Deletes a credential from the repo. name corresponds to GetName() of the credential
// ErrNotFound is returned if the resource does not exist
Delete(ctx context.Context, t RepositoryCredentialType, name string) error
}
```
In order to support multiple different types of credentials, `RepositoryCredential` is an interface:
```go
// RepositoryCredential is a credential that allows access (either read-only or read-write) to the repo
type RepositoryCredential interface {
// GetType returns the type of the credential
GetType() RepositoryCredentialType
// GetName returns a name (or title/description) of the credential
GetName() string
// GetData returns the key that will be authorized to access the repo, this can e.g. be a SSH public key
GetData() []byte
// IsReadOnly returns whether this credential is authorized to write to the repository or not
IsReadOnly() bool
}
```
The default implementation of `RepositoryCredential` is `DeployKey`:
```go
// DeployKey represents a short-lived credential (e.g. an SSH public key) used for accessing a repository
type DeployKey struct {
// DeployKey embeds InternalHolder for accessing the underlying object
// +optional
InternalHolder `json:",inline"`
// Title is the human-friendly interpretation of what the key is for (and does)
// +required
Title string `json:"title"`
// Key specifies the public part of the deploy (e.g. SSH) key
// +required
Key []byte `json:"key"`
// ReadOnly specifies whether this DeployKey can write to the repository or not
// Default value at POST-time: true
// +optional
ReadOnly *bool `json:"readOnly"`
}
```

View File

@@ -41,7 +41,7 @@ Tasks
Goals
- Offer a dedicated component that can replace Flux v1 image update feature
- Offer components that can replace Flux v1 image update feature
Non-Goals
@@ -49,11 +49,9 @@ Non-Goals
Tasks
- Design the Git push API
- Implement Git push in source controller
- Design the image scanning API
- [Design the image scanning and automation API](https://github.com/fluxcd/toolkit/discussions/107)
- Implement an image scanning controller
- Design the manifests patching component
- Design the automation component
- Implement the image scan/patch/push workflow
- Integrate the new components in the toolkit assembler
- Create a migration guide from Flux annotations
@@ -69,13 +67,19 @@ Goals
Non-Goals
- Migrate users that are using Helm v2
- Migrate users that are using Helm charts from Git
Stretch-Goals
- [Migrate users that are using Helm charts from Git](https://github.com/fluxcd/toolkit/discussions/75#discussioncomment-38589)
Tasks
- ~~Implement a Helm controller for Helm v3 covering all the current release options~~
- Discuss and design Helm releases based on source API:
+ [Providing values from sources](https://github.com/fluxcd/toolkit/discussions/100)
+ [Conditional remediation on failed Helm actions](https://github.com/fluxcd/toolkit/discussions/102)
+ [Support running Helm test actions on an interval](https://github.com/fluxcd/toolkit/discussions/103)
- Review the Helm release, chart and repository APIs
- Design Helm releases based on source API
- Implement a Helm controller for Helm v3 covering all the current release options
- Implement events in Helm controller
- ~~Implement events in Helm controller~~
- Implement Prometheus metrics in Helm controller
- Create a migration guide for Helm Operator users

10
go.mod
View File

@@ -4,11 +4,10 @@ go 1.14
require (
github.com/blang/semver v3.5.1+incompatible
github.com/fluxcd/helm-controller v0.0.1-alpha.2
github.com/fluxcd/kustomize-controller v0.0.4
github.com/fluxcd/helm-controller v0.0.1-beta.3
github.com/fluxcd/kustomize-controller v0.0.5
github.com/fluxcd/pkg v0.0.3
github.com/fluxcd/source-controller v0.0.5
github.com/golang/protobuf v1.4.2 // indirect
github.com/fluxcd/source-controller v0.0.6
github.com/manifoldco/promptui v0.7.0
github.com/spf13/cobra v1.0.0
golang.org/x/net v0.0.0-20200602114024-627f9648deb9 // indirect
@@ -17,9 +16,10 @@ require (
google.golang.org/appengine v1.6.6 // indirect
google.golang.org/protobuf v1.24.0 // indirect
k8s.io/api v0.18.4
k8s.io/apiextensions-apiserver v0.18.4
k8s.io/apimachinery v0.18.4
k8s.io/client-go v0.18.4
sigs.k8s.io/controller-runtime v0.6.0
sigs.k8s.io/controller-runtime v0.6.1
sigs.k8s.io/kustomize/api v0.5.1
sigs.k8s.io/yaml v1.2.0
)

58
go.sum
View File

@@ -68,6 +68,8 @@ github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas=
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
@@ -170,18 +172,20 @@ github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d h1:105gxyaGwC
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4=
github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fluxcd/helm-controller v0.0.1-alpha.2 h1:X5ycFeobLnv8EHVWC8hvw0fT6QZhbmM8y2ShM1XF4mk=
github.com/fluxcd/helm-controller v0.0.1-alpha.2/go.mod h1:ya+BGrB1Md5DaxQIQmXwf6qNaydZZoa3EZJGpqFFu/Y=
github.com/fluxcd/kustomize-controller v0.0.4 h1:QZNygZXWeM5nOn0iRmzOaaoXvQuZ+YZhx+QgZjuX2z0=
github.com/fluxcd/kustomize-controller v0.0.4/go.mod h1:JU62CExAWDlITSfLabTe7ilsq1DnJB3yaffv80nYlVs=
github.com/fluxcd/helm-controller v0.0.1-beta.3 h1:S6XOwAM0IbJxYDbasv85Zk7W1gJ51S3ZqYylMtGcuEE=
github.com/fluxcd/helm-controller v0.0.1-beta.3/go.mod h1:asoN9pG8J0oQ9iXpkxNwvch1EKspus6RxH818ZYVo+4=
github.com/fluxcd/kustomize-controller v0.0.5 h1:jjBJT/UbblMaeQpYn5TjH/oXXnORO6C3Cka77bs9K3Q=
github.com/fluxcd/kustomize-controller v0.0.5/go.mod h1:1O78f9Qigs74BMxO/ThzLt5XGGQnwQPgzi+47ntie5M=
github.com/fluxcd/pkg v0.0.3 h1:yhjtpGtD9LxFo8JtwTuUxJyFcX2wSSb0TPptIEpGSmA=
github.com/fluxcd/pkg v0.0.3/go.mod h1:rtlppQU+9DNikyDZptLdOeTf+wBvQQiQQ/J113FPoeU=
github.com/fluxcd/source-controller v0.0.5 h1:flUhdZh00/+jzqf9/sSRUMrewp+VlNFDITnoQC7pO4A=
github.com/fluxcd/source-controller v0.0.5/go.mod h1:RhIhH7PE7jqPEaarhtMvCq9zKZp/qTslYtZsJuww9OI=
github.com/fluxcd/source-controller v0.0.6 h1:8yBdy5ZQmM4jZWHDBDgysftZnC1mybyfkV7NRzCo5Kc=
github.com/fluxcd/source-controller v0.0.6/go.mod h1:XZR988ahVLjbqfe0EUq2Zl7bYH2NBly3u0n7DY5XtyU=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY=
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
@@ -400,13 +404,14 @@ github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/imdario/mergo v0.3.5 h1:JboBksRwiiAJWvIYJVo46AfV+IAIKZpfrSzVKj42R4Q=
github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/imdario/mergo v0.3.8/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=
@@ -425,6 +430,8 @@ github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCV
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.8 h1:QiWkFLKq0T7mpzwOTu6BzNDbfTE8OLrYhVKYMLF46Ok=
github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a h1:FaWFmfWdAUKbSCtOU2QjDaorUexogfaMgbipgYATUMU=
@@ -523,6 +530,8 @@ github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d/go.mod h1:o96d
github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
@@ -532,10 +541,13 @@ github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W
github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.11.0 h1:JAKSXpt1YjtLA7YpPiqO9ss6sNXEsPfSGdwN0UHqzrw=
github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.1 h1:mFwc4LvZ0xpSvDZ3E+k8Yte0hLOMxXUlP+yXtJqkYfQ=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.8.1 h1:C5Dqfs/LeauYDX0jJXIe2SWmwCbGzx9yF8C8xy3Lh34=
github.com/onsi/gomega v1.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ=
@@ -586,6 +598,8 @@ github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7z
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.5 h1:3+auTFlqw+ZaQYJARz6ArODtkaIwtvBTx3N2NehQlL8=
github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
github.com/prometheus/procfs v0.0.11 h1:DhHlBtkHWPYi8O2y31JkK0TF+DGM+51OopZjH/Ia5qI=
github.com/prometheus/procfs v0.0.11/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/qri-io/starlib v0.4.2-0.20200213133954-ff2e8cd5ef8d h1:K6eOUihrFLdZjZnA4XlRp864fmWXv9YTIk7VPLhRacA=
github.com/qri-io/starlib v0.4.2-0.20200213133954-ff2e8cd5ef8d/go.mod h1:7DPO4domFU579Ga6E61sB9VFNaniPVwJP5C4bBCu3wA=
@@ -768,6 +782,7 @@ golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a h1:GuSPYbZzB5/dcLNCwLQLsg3obCJtX9IJhpXkvY7kzk0=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200602114024-627f9648deb9 h1:pNX+40auqi2JqRfOP1akLGtYcn15TUbkhwuCO3foqqM=
golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be h1:vEDujvNQGv4jgYKudGeI/+DAX4Jffq6hpD55MmoEvKs=
@@ -808,11 +823,15 @@ golang.org/x/sys v0.0.0-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191002063906-3421d5a6bb1c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7 h1:HmbHVPwrPEKPGLAcHSrMe6+hqSUlvZU0rab6x5EXfGU=
golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527 h1:uYVVQ9WP/Ds2ROhcaGPeIdVq0RIXVLwsHlnvJ+cT1So=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=
@@ -823,6 +842,8 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ=
@@ -944,37 +965,26 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
k8s.io/api v0.17.0/go.mod h1:npsyOePkeP0CPwyGfXDHxvypiYMJxBWAMpQxCaJ4ZxI=
k8s.io/api v0.18.0/go.mod h1:q2HRQkfDzHMBZL9l/y9rH63PkQl4vae0xRT+8prbrK8=
k8s.io/api v0.18.2 h1:wG5g5ZmSVgm5B+eHMIbI9EGATS2L8Z72rda19RIEgY8=
k8s.io/api v0.18.2/go.mod h1:SJCWI7OLzhZSvbY7U8zwNl9UA4o1fizoug34OV/2r78=
k8s.io/api v0.18.4 h1:8x49nBRxuXGUlDlwlWd3RMY1SayZrzFfxea3UZSkFw4=
k8s.io/api v0.18.4/go.mod h1:lOIQAKYgai1+vz9J7YcDZwC26Z0zQewYOGWdyIPUUQ4=
k8s.io/apiextensions-apiserver v0.18.0/go.mod h1:18Cwn1Xws4xnWQNC00FLq1E350b9lUF+aOdIWDOZxgo=
k8s.io/apiextensions-apiserver v0.18.2 h1:I4v3/jAuQC+89L3Z7dDgAiN4EOjN6sbm6iBqQwHTah8=
k8s.io/apiextensions-apiserver v0.18.2/go.mod h1:q3faSnRGmYimiocj6cHQ1I3WpLqmDgJFlKL37fC4ZvY=
k8s.io/apiextensions-apiserver v0.18.4 h1:Y3HGERmS8t9u12YNUFoOISqefaoGRuTc43AYCLzWmWE=
k8s.io/apiextensions-apiserver v0.18.4/go.mod h1:NYeyeYq4SIpFlPxSAB6jHPIdvu3hL0pc36wuRChybio=
k8s.io/apimachinery v0.17.0/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZg=
k8s.io/apimachinery v0.18.0/go.mod h1:9SnR/e11v5IbyPCGbvJViimtJ0SwHG4nfZFjU77ftcA=
k8s.io/apimachinery v0.18.2 h1:44CmtbmkzVDAhCpRVSiP2R5PPrC2RtlIv/MoB8xpdRA=
k8s.io/apimachinery v0.18.2/go.mod h1:9SnR/e11v5IbyPCGbvJViimtJ0SwHG4nfZFjU77ftcA=
k8s.io/apimachinery v0.18.4 h1:ST2beySjhqwJoIFk6p7Hp5v5O0hYY6Gngq/gUYXTPIA=
k8s.io/apimachinery v0.18.4/go.mod h1:OaXp26zu/5J7p0f92ASynJa1pZo06YlV9fG7BoWbCko=
k8s.io/apiserver v0.18.0/go.mod h1:3S2O6FeBBd6XTo0njUrLxiqk8GNy6wWOftjhJcXYnjw=
k8s.io/apiserver v0.18.2/go.mod h1:Xbh066NqrZO8cbsoenCwyDJ1OSi8Ag8I2lezeHxzwzw=
k8s.io/apiserver v0.18.4/go.mod h1:q+zoFct5ABNnYkGIaGQ3bcbUNdmPyOCoEBcg51LChY8=
k8s.io/cli-runtime v0.18.0/go.mod h1:1eXfmBsIJosjn9LjEBUd2WVPoPAY9XGTqTFcPMIBsUQ=
k8s.io/cli-runtime v0.18.4/go.mod h1:9/hS/Cuf7NVzWR5F/5tyS6xsnclxoPLVtwhnkJG1Y4g=
k8s.io/client-go v0.17.0/go.mod h1:TYgR6EUHs6k45hb6KWjVD6jFZvJV4gHDikv/It0xz+k=
k8s.io/client-go v0.18.0/go.mod h1:uQSYDYs4WhVZ9i6AIoEZuwUggLVEF64HOD37boKAtF8=
k8s.io/client-go v0.18.2 h1:aLB0iaD4nmwh7arT2wIn+lMnAq7OswjaejkQ8p9bBYE=
k8s.io/client-go v0.18.2/go.mod h1:Xcm5wVGXX9HAA2JJ2sSBUn3tCJ+4SVlCbl2MNNv+CIU=
k8s.io/client-go v0.18.4 h1:un55V1Q/B3JO3A76eS0kUSywgGK/WR3BQ8fHQjNa6Zc=
k8s.io/client-go v0.18.4/go.mod h1:f5sXwL4yAZRkAtzOxRWUhA/N8XzGCb+nPZI8PfobZ9g=
k8s.io/code-generator v0.18.0/go.mod h1:+UHX5rSbxmR8kzS+FAv7um6dtYrZokQvjHpDSYRVkTc=
k8s.io/code-generator v0.18.2/go.mod h1:+UHX5rSbxmR8kzS+FAv7um6dtYrZokQvjHpDSYRVkTc=
k8s.io/code-generator v0.18.4/go.mod h1:TgNEVx9hCyPGpdtCWA34olQYLkh3ok9ar7XfSsr8b6c=
k8s.io/component-base v0.18.0/go.mod h1:u3BCg0z1uskkzrnAKFzulmYaEpZF7XC9Pf/uFyb1v2c=
k8s.io/component-base v0.18.2/go.mod h1:kqLlMuhJNHQ9lz8Z7V5bxUUtjFZnrypArGl58gmDfUM=
k8s.io/component-base v0.18.4/go.mod h1:7jr/Ef5PGmKwQhyAz/pjByxJbC58mhKAhiaDu0vXfPk=
k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
k8s.io/gengo v0.0.0-20200114144118-36b2048a9120/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
@@ -982,6 +992,8 @@ k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUc
k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8=
k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
k8s.io/klog/v2 v2.0.0 h1:Foj74zO6RbjjP4hBEKjnYtjjAhGg4jNynUdYF6fJrok=
k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E=
k8s.io/kube-openapi v0.0.0-20200121204235-bf4fb3bd569c h1:/KUFqjjqAcY4Us6luF5RDNZ16KJtb49HfR3ZHB9qYXM=
k8s.io/kube-openapi v0.0.0-20200121204235-bf4fb3bd569c/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E=
@@ -993,13 +1005,15 @@ k8s.io/metrics v0.18.0/go.mod h1:8aYTW18koXqjLVKL7Ds05RPMX9ipJZI3mywYvBOxXd4=
k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89 h1:d4vVOjXm687F1iLSP2q3lyPPuyvTUt3aVoBpi2DqRsU=
k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
k8s.io/utils v0.0.0-20200603063816-c1c6865ac451 h1:v8ud2Up6QK1lNOKFgiIVrZdMg7MpmSnvtrOieolJKoE=
k8s.io/utils v0.0.0-20200603063816-c1c6865ac451/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc=
mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4=
mvdan.cc/unparam v0.0.0-20190720180237-d51796306d8f/go.mod h1:4G1h5nDURzA3bwVMZIVpwbkw+04kSxk3rAtzlimaUJw=
rsc.io/letsencrypt v0.0.3/go.mod h1:buyQKZ6IXrRnB7TdkHP0RyEybLx18HHyOSoTyoOLqNY=
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.7/go.mod h1:PHgbrJT7lCHcxMU+mDHEm+nx46H4zuuHZkDP6icnhu0=
sigs.k8s.io/controller-runtime v0.6.0 h1:Fzna3DY7c4BIP6KwfSlrfnj20DJ+SeMBK8HSFvOk9NM=
sigs.k8s.io/controller-runtime v0.6.0/go.mod h1:CpYf5pdNY/B352A1TFLAS2JVSlnGQ5O2cftPHndTroo=
sigs.k8s.io/controller-runtime v0.6.1 h1:LcK2+nk0kmaOnKGN+vBcWHqY5WDJNJNB/c5pW+sU8fc=
sigs.k8s.io/controller-runtime v0.6.1/go.mod h1:XRYBPdbf5XJu9kpS84VJiZ7h/u1hF3gEORz0efEja7A=
sigs.k8s.io/kustomize v2.0.3+incompatible h1:JUufWFNlI44MdtnjUqVnvh29rR37PQFzPbLXqhyOyX0=
sigs.k8s.io/kustomize v2.0.3+incompatible/go.mod h1:MkjgH3RdOWrievjo6c9T245dYlB5QeXV4WCbnt/PEpU=
sigs.k8s.io/kustomize/api v0.5.1 h1:iHGTs5LcnJGqHstUSxWD/kX6XZgmd82x79LLlZwDU0I=

View File

@@ -1,8 +1,8 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- github.com/fluxcd/helm-controller/config//crd?ref=v0.0.1-alpha.2
- github.com/fluxcd/helm-controller/config//manager?ref=v0.0.1-alpha.2
- github.com/fluxcd/helm-controller/config//crd?ref=v0.0.1-beta.3
- github.com/fluxcd/helm-controller/config//manager?ref=v0.0.1-beta.3
patchesJson6902:
- target:
group: apps

View File

@@ -1,8 +1,8 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- github.com/fluxcd/kustomize-controller/config//crd?ref=v0.0.4
- github.com/fluxcd/kustomize-controller/config//manager?ref=v0.0.4
- github.com/fluxcd/kustomize-controller/config//crd?ref=v0.0.5
- github.com/fluxcd/kustomize-controller/config//manager?ref=v0.0.5
patchesJson6902:
- target:
group: apps

View File

@@ -1,5 +1,5 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- github.com/fluxcd/notification-controller/config//crd?ref=v0.0.4
- github.com/fluxcd/notification-controller/config//manager?ref=v0.0.4
- github.com/fluxcd/notification-controller/config//crd?ref=v0.0.5
- github.com/fluxcd/notification-controller/config//manager?ref=v0.0.5

View File

@@ -1,8 +1,8 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- github.com/fluxcd/source-controller/config//crd?ref=v0.0.5
- github.com/fluxcd/source-controller/config//manager?ref=v0.0.5
- github.com/fluxcd/source-controller/config//crd?ref=v0.0.6
- github.com/fluxcd/source-controller/config//manager?ref=v0.0.6
patchesJson6902:
- target:
group: apps

View File

@@ -40,7 +40,7 @@ nav:
- Introduction: index.md
- Get Started: get-started/index.md
- Guides:
- Manage Helm releases: guides/helmreleases.md
- Manage Helm Releases: guides/helmreleases.md
- Setup Notifications: guides/notifications.md
- Setup Webhook Receivers: guides/webhook-receivers.md
- Toolkit Components:
@@ -73,20 +73,28 @@ nav:
- Check: cmd/tk_check.md
- Create: cmd/tk_create.md
- Create kustomization: cmd/tk_create_kustomization.md
- Create helmrelease: cmd/tk_create_helmrelease.md
- Create source: cmd/tk_create_source.md
- Create source git: cmd/tk_create_source_git.md
- Create source helm: cmd/tk_create_source_helm.md
- Delete: cmd/tk_delete.md
- Delete kustomization: cmd/tk_delete_kustomization.md
- Delete helmrelease: cmd/tk_delete_helmrelease.md
- Delete source: cmd/tk_delete_source.md
- Delete source git: cmd/tk_delete_source_git.md
- Delete source helm: cmd/tk_delete_source_helm.md
- Export: cmd/tk_export.md
- Export kustomization: cmd/tk_export_kustomization.md
- Export helmrelease: cmd/tk_export_helmrelease.md
- Export source: cmd/tk_export_source.md
- Export source git: cmd/tk_export_source_git.md
- Export source helm: cmd/tk_export_source_helm.md
- Get: cmd/tk_get.md
- Get kustomizations: cmd/tk_get_kustomizations.md
- Get helmreleases: cmd/tk_get_helmreleases.md
- Get sources: cmd/tk_get_sources.md
- Get sources git: cmd/tk_get_sources_git.md
- Get sources helm: cmd/tk_get_sources_helm.md
- Install: cmd/tk_install.md
- Resume: cmd/tk_resume.md
- Resume kustomization: cmd/tk_resume_kustomization.md