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

Compare commits

...

33 Commits

Author SHA1 Message Date
Stefan Prodan
a37be432a9 Merge pull request #333 from fluxcd/docs-update-v0.1.6
Update docs website
2020-10-14 12:11:30 +03:00
Stefan Prodan
90591e852d Update docs website
- add notification-controller/api commands to index
- move diagrams to docs website
- update CRDs docs

Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2020-10-14 10:35:29 +03:00
Stefan Prodan
13f7d44a06 Merge pull request #327 from nanikjava/f-fix-306
Add ARM instruction to get started guide
2020-10-13 23:55:58 +03:00
Nanik
7dc8aa66a6 Add ARM instruction under staging and production bootstrap 2020-10-14 07:44:16 +11:00
Stefan Prodan
6cf28ab718 Merge pull request #331 from fluxcd/update-components
Update toolkit components
2020-10-13 23:41:39 +03:00
fluxcdbot
f461c5e8b7 Update toolkit components 2020-10-13 17:47:46 +00:00
Stefan Prodan
9433bdf4ad Merge pull request #332 from fluxcd/uninstall-fix
Uninstall improvements
2020-10-13 20:43:48 +03:00
Stefan Prodan
d2d494e079 Uninstall improvements
- ignore not found errors when deleting objects
- remove the CR/CRDs before deleting the cluster role binding
- capture kubectl exist code

Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2020-10-13 20:30:17 +03:00
Stefan Prodan
e64b35cde8 Merge pull request #302 from phillebaba/feature/notification-controller
Add notification controller CLI commands
2020-10-13 18:31:16 +03:00
Philip Laine
d32e8c6e98 Remove aliases 2020-10-13 12:21:45 +02:00
Philip Laine
55cee488bc Update docs 2020-10-13 11:11:55 +02:00
Philip Laine
65b8942416 Fix minor issues 2020-10-13 11:11:55 +02:00
Philip Laine
94cf7c329c Update docs 2020-10-13 11:11:55 +02:00
Philip Laine
8eac7d6b4d Implement table output 2020-10-13 11:11:55 +02:00
Philip Laine
7ebb34de80 Add receiver command 2020-10-13 11:11:55 +02:00
Philip Laine
6ea84906ac Add alert commands 2020-10-13 11:11:54 +02:00
Philip Laine
f7971a871a Add alert provider commands 2020-10-13 11:11:54 +02:00
Stefan Prodan
54b35b7c2b Merge pull request #323 from fluxcd/monitoring
docs: Install the monitoring stack with gotk
2020-10-12 13:04:43 +03:00
Stefan Prodan
ca970b4ffb docs: Install the monitoring stack with gotk
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2020-10-12 09:50:43 +03:00
Stefan Prodan
8a96e32679 Update Prometheus and Grafana
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2020-10-11 15:13:16 +03:00
Stefan Prodan
fc4d01b3e5 Allow scraping and webhooks
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2020-10-11 13:48:06 +03:00
Stefan Prodan
b6afc8f6ce Merge pull request #317 from staceypotter/patch-1
Add featured talks and meetups section
2020-10-09 09:03:14 +03:00
Stefan Prodan
c481a431be Merge branch 'main' into patch-1 2020-10-09 08:54:45 +03:00
Stacey Potter
0a7b82793e removed header link
Removed "(Check out our [Upcoming Meetups](#upcoming-meetups)!)" from the top of the page.
2020-10-08 22:54:04 -07:00
Stacey Potter
3653236bcb added "featured talks" section
+ upcoming meetups & header link
2020-10-08 15:31:10 -07:00
Stefan Prodan
a2eee72015 Merge pull request #318 from fluxcd/install-pkg
Introduce install package
2020-10-08 14:01:25 +03:00
Stefan Prodan
5672646278 Use install pkg in CLI
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2020-10-08 12:47:25 +03:00
Stefan Prodan
c4d3fa7a48 init install pkg
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2020-10-08 02:31:33 +03:00
Stacey Potter
32b0267b9f Added GOTK guide walk-through
Added invite for Leigh's GOTK guide walk-through on Oct 19 (10am PT/18:00 BST) to the Community Section. LMK if this should be somewhere else - or condensed (wasn't sure how much info to include). Thanks! :)
2020-10-07 11:43:14 -07:00
Stefan Prodan
a7b41a4b8c Merge pull request #315 from rieger-jared/bug/update-notification-api-in-docs
Update api versions in guides
2020-10-07 18:10:38 +03:00
Jared Rieger
bfd6d14bf3 Update api versions in guides 2020-10-07 17:00:43 +02:00
Daniel Holbach
469de31218 Merge pull request #313 from dholbach/update-frontpage
close parentheses, make link to guide more obvious
2020-10-06 14:51:18 +02:00
Daniel Holbach
2ce9823b3a close parentheses, make link to guide more obvious 2020-10-06 14:34:21 +02:00
89 changed files with 3879 additions and 387 deletions

View File

@@ -15,10 +15,10 @@ jobs:
- name: Copy assets
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SOURCE_VER: ${{ 'v0.1.0' }}
KUSTOMIZE_VER: ${{ 'v0.1.0' }}
HELM_VER: ${{ 'v0.1.1' }}
NOTIFICATION_VER: ${{ 'v0.1.0' }}
SOURCE_VER: ${{ 'v0.1.1' }}
KUSTOMIZE_VER: ${{ 'v0.1.1' }}
HELM_VER: ${{ 'v0.1.2' }}
NOTIFICATION_VER: ${{ 'v0.1.1' }}
run: |
controller_version() {
sed -n "s/\(.*$1\/.*?ref=\)//p;n" "manifests/bases/$1/kustomization.yaml"

View File

@@ -4,8 +4,7 @@
[![report](https://goreportcard.com/badge/github.com/fluxcd/toolkit)](https://goreportcard.com/report/github.com/fluxcd/toolkit)
[![license](https://img.shields.io/github/license/fluxcd/toolkit.svg)](https://github.com/fluxcd/toolkit/blob/main/LICENSE)
[![release](https://img.shields.io/github/release/fluxcd/toolkit/all.svg)](https://github.com/fluxcd/toolkit/releases)
![overview](docs/diagrams/gotk-feature.png)
![overview](docs/diagrams/gitops-toolkit.png)
The GitOps Toolkit is a set of composable APIs and specialized tools
that can be used to build a Continuous Delivery platform on top of Kubernetes.
@@ -82,4 +81,18 @@ The GitOps Toolkit is always looking for new contributors and there are a multit
- To be part of the conversation about Flux's development, [join the flux-dev mailing list](https://lists.cncf.io/g/cncf-flux-dev).
- Check out [how to contribute](CONTRIBUTING.md) to the project
## Featured Talks
- [12 Oct 2020 - Rawkode Live: Introduction to GitOps Toolkit with Stefan Prodan](https://youtu.be/HqTzuOBP0eY)
- [4 Sep 2020 - KubeCon/CloudNativeCon Europe: The road to Flux v2 and Progressive Delivery with Stefan Prodan & Hidde Beydals](https://youtu.be/8v94nUkXsxU)
- [25 June 2020 - Cloud Native Nordics: Introduction to GitOps & GitOps Toolkit with Alexis Richardson & Stefan Prodan](https://youtu.be/qQBtSkgl7tI)
- [7 May 2020 - GitOps Days - Community Special: GitOps Toolkit Experimentation with Stefan Prodan](https://youtu.be/WHzxunv4DKk?t=6521)
### Upcoming Meetups
- [19 October 2020 - GitOps Toolkit Guide Walk-through](https://www.meetup.com/GitOps-Community/events/273640196/)
Join us 10am PT / 18:00 BST) for to this special walk-through of the GitOps Toolkit!
Through this talk you'll be able to see how the upcoming Flux v2 and GitOps Toolkit will bring
great improvements to the Flux that you love! Watch or follow along as Leigh Capili shares some
highlights and then goes through Getting Started with GitOps Toolkit.
- 2 November 2020 - GitOps Toolkit Guide Walk-through - Part 2 (TBD)
We are looking forward to seeing you with us!

View File

@@ -19,6 +19,7 @@ package main
import (
"context"
"fmt"
"io/ioutil"
"net/url"
"os"
"path"
@@ -36,6 +37,8 @@ import (
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
"github.com/fluxcd/toolkit/pkg/install"
)
var bootstrapCmd = &cobra.Command{
@@ -111,31 +114,36 @@ func generateInstallManifests(targetPath, namespace, tmpDir string, localManifes
if err := os.MkdirAll(manifestsDir, os.ModePerm); err != nil {
return "", fmt.Errorf("creating manifests dir failed: %w", err)
}
manifest := path.Join(manifestsDir, bootstrapInstallManifest)
if localManifests != "" {
if err := buildKustomization(localManifests, manifest); err != nil {
return "", fmt.Errorf("build kustomization failed: %w", err)
}
return manifest, nil
opts := install.Options{
BaseURL: localManifests,
Version: bootstrapVersion,
Namespace: namespace,
Components: bootstrapComponents,
Registry: bootstrapRegistry,
ImagePullSecret: bootstrapImagePullSecret,
Arch: bootstrapArch,
WatchAllNamespaces: bootstrapWatchAllNamespaces,
NetworkPolicy: bootstrapNetworkPolicy,
LogLevel: bootstrapLogLevel,
NotificationController: defaultNotification,
ManifestsFile: fmt.Sprintf("%s.yaml", namespace),
Timeout: timeout,
}
gotkDir := path.Join(tmpDir, ".gotk")
defer os.RemoveAll(gotkDir)
if err := os.MkdirAll(gotkDir, os.ModePerm); err != nil {
return "", fmt.Errorf("generating manifests failed: %w", err)
if localManifests == "" {
opts.BaseURL = install.MakeDefaultOptions().BaseURL
}
if err := genInstallManifests(bootstrapVersion, namespace, bootstrapComponents,
bootstrapWatchAllNamespaces, bootstrapNetworkPolicy, bootstrapRegistry, bootstrapImagePullSecret,
bootstrapArch, bootstrapLogLevel, gotkDir); err != nil {
return "", fmt.Errorf("generating manifests failed: %w", err)
output, err := install.Generate(opts)
if err != nil {
return "", fmt.Errorf("generating install manifests failed: %w", err)
}
if err := buildKustomization(gotkDir, manifest); err != nil {
return "", fmt.Errorf("build kustomization failed: %w", err)
if err := ioutil.WriteFile(manifest, output, os.ModePerm); err != nil {
return "", fmt.Errorf("generating install manifests failed: %w", err)
}
return manifest, nil

195
cmd/gotk/create_alert.go Normal file
View File

@@ -0,0 +1,195 @@
/*
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/fluxcd/pkg/apis/meta"
"github.com/spf13/cobra"
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"
"sigs.k8s.io/controller-runtime/pkg/client"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
)
var createAlertCmd = &cobra.Command{
Use: "alert [name]",
Short: "Create or update a Alert resource",
Long: "The create alert command generates a Alert resource.",
Example: ` # Create an Alert for kustomization events
gotk create alert \
--event-severity info \
--event-source Kustomization/gotk-system \
--provider-ref slack \
gotk-system
`,
RunE: createAlertCmdRun,
}
var (
aProviderRef string
aEventSeverity string
aEventSources []string
)
func init() {
createAlertCmd.Flags().StringVar(&aProviderRef, "provider-ref", "", "reference to provider")
createAlertCmd.Flags().StringVar(&aEventSeverity, "event-severity", "", "severity of events to send alerts for")
createAlertCmd.Flags().StringArrayVar(&aEventSources, "event-source", []string{}, "sources that should generate alerts (<kind>/<name>)")
createCmd.AddCommand(createAlertCmd)
}
func createAlertCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("alert name is required")
}
name := args[0]
if aProviderRef == "" {
return fmt.Errorf("provider ref is required")
}
eventSources := []notificationv1.CrossNamespaceObjectReference{}
for _, eventSource := range aEventSources {
kind, name := utils.parseObjectKindName(eventSource)
if kind == "" {
return fmt.Errorf("invalid event source '%s', must be in format <kind>/<name>", eventSource)
}
eventSources = append(eventSources, notificationv1.CrossNamespaceObjectReference{
Kind: kind,
Name: name,
})
}
if len(eventSources) == 0 {
return fmt.Errorf("at least one event source is required")
}
sourceLabels, err := parseLabels()
if err != nil {
return err
}
if !export {
logger.Generatef("generating alert")
}
alert := notificationv1.Alert{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: namespace,
Labels: sourceLabels,
},
Spec: notificationv1.AlertSpec{
ProviderRef: corev1.LocalObjectReference{
Name: aProviderRef,
},
EventSeverity: aEventSeverity,
EventSources: eventSources,
Suspend: false,
},
}
if export {
return exportAlert(alert)
}
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
if err != nil {
return err
}
logger.Actionf("applying alert")
if err := upsertAlert(ctx, kubeClient, alert); err != nil {
return err
}
logger.Waitingf("waiting for reconciliation")
if err := wait.PollImmediate(pollInterval, timeout,
isAlertReady(ctx, kubeClient, name, namespace)); err != nil {
return err
}
logger.Successf("alert %s is ready", name)
return nil
}
func upsertAlert(ctx context.Context, kubeClient client.Client, alert notificationv1.Alert) error {
namespacedName := types.NamespacedName{
Namespace: alert.GetNamespace(),
Name: alert.GetName(),
}
var existing notificationv1.Alert
err := kubeClient.Get(ctx, namespacedName, &existing)
if err != nil {
if errors.IsNotFound(err) {
if err := kubeClient.Create(ctx, &alert); err != nil {
return err
} else {
logger.Successf("alert created")
return nil
}
}
return err
}
existing.Labels = alert.Labels
existing.Spec = alert.Spec
if err := kubeClient.Update(ctx, &existing); err != nil {
return err
}
logger.Successf("alert updated")
return nil
}
func isAlertReady(ctx context.Context, kubeClient client.Client, name, namespace string) wait.ConditionFunc {
return func() (bool, error) {
var alert notificationv1.Alert
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
err := kubeClient.Get(ctx, namespacedName, &alert)
if err != nil {
return false, err
}
if c := meta.GetCondition(alert.Status.Conditions, meta.ReadyCondition); c != nil {
switch c.Status {
case corev1.ConditionTrue:
return true, nil
case corev1.ConditionFalse:
return false, fmt.Errorf(c.Message)
}
}
return false, nil
}
}

View File

@@ -0,0 +1,189 @@
/*
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/fluxcd/pkg/apis/meta"
"github.com/spf13/cobra"
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"
"sigs.k8s.io/controller-runtime/pkg/client"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
)
var createAlertProviderCmd = &cobra.Command{
Use: "alert-provider [name]",
Short: "Create or update a Provider resource",
Long: "The create alert-provider command generates a Provider resource.",
Example: ` # Create a Provider for a Slack channel
gotk create alert-provider slack \
--type slack \
--channel general \
--address https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK \
--secret-ref webhook-url
# Create a Provider for a Github repository
gotk create alert-provider github-podinfo \
--type github \
--address https://github.com/stefanprodan/podinfo \
--secret-ref github-token
`,
RunE: createAlertProviderCmdRun,
}
var (
apType string
apChannel string
apUsername string
apAddress string
apSecretRef string
)
func init() {
createAlertProviderCmd.Flags().StringVar(&apType, "type", "", "type of provider")
createAlertProviderCmd.Flags().StringVar(&apChannel, "channel", "", "channel to send messages to in the case of a chat provider")
createAlertProviderCmd.Flags().StringVar(&apUsername, "username", "", "bot username used by the provider")
createAlertProviderCmd.Flags().StringVar(&apAddress, "address", "", "path to either the git repository, chat provider or webhook")
createAlertProviderCmd.Flags().StringVar(&apSecretRef, "secret-ref", "", "name of secret containing authentication token")
createCmd.AddCommand(createAlertProviderCmd)
}
func createAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("provider name is required")
}
name := args[0]
if apType == "" {
return fmt.Errorf("type is required")
}
sourceLabels, err := parseLabels()
if err != nil {
return err
}
if !export {
logger.Generatef("generating provider")
}
alertProvider := notificationv1.Provider{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: namespace,
Labels: sourceLabels,
},
Spec: notificationv1.ProviderSpec{
Type: apType,
Channel: apChannel,
Username: apUsername,
Address: apAddress,
SecretRef: &corev1.LocalObjectReference{
Name: apSecretRef,
},
},
}
if export {
return exportAlertProvider(alertProvider)
}
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
if err != nil {
return err
}
logger.Actionf("applying provider")
if err := upsertAlertProvider(ctx, kubeClient, alertProvider); err != nil {
return err
}
logger.Waitingf("waiting for reconciliation")
if err := wait.PollImmediate(pollInterval, timeout,
isAlertProviderReady(ctx, kubeClient, name, namespace)); err != nil {
return err
}
logger.Successf("provider %s is ready", name)
return nil
}
func upsertAlertProvider(ctx context.Context, kubeClient client.Client, alertProvider notificationv1.Provider) error {
namespacedName := types.NamespacedName{
Namespace: alertProvider.GetNamespace(),
Name: alertProvider.GetName(),
}
var existing notificationv1.Provider
err := kubeClient.Get(ctx, namespacedName, &existing)
if err != nil {
if errors.IsNotFound(err) {
if err := kubeClient.Create(ctx, &alertProvider); err != nil {
return err
} else {
logger.Successf("provider created")
return nil
}
}
return err
}
existing.Labels = alertProvider.Labels
existing.Spec = alertProvider.Spec
if err := kubeClient.Update(ctx, &existing); err != nil {
return err
}
logger.Successf("provider updated")
return nil
}
func isAlertProviderReady(ctx context.Context, kubeClient client.Client, name, namespace string) wait.ConditionFunc {
return func() (bool, error) {
var alertProvider notificationv1.Provider
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
err := kubeClient.Get(ctx, namespacedName, &alertProvider)
if err != nil {
return false, err
}
if c := meta.GetCondition(alertProvider.Status.Conditions, meta.ReadyCondition); c != nil {
switch c.Status {
case corev1.ConditionTrue:
return true, nil
case corev1.ConditionFalse:
return false, fmt.Errorf(c.Message)
}
}
return false, nil
}
}

215
cmd/gotk/create_receiver.go Normal file
View File

@@ -0,0 +1,215 @@
/*
Copyright 2020 The Flux CD contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package main
import (
"context"
"fmt"
"github.com/spf13/cobra"
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"
"sigs.k8s.io/controller-runtime/pkg/client"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
"github.com/fluxcd/pkg/apis/meta"
)
var createReceiverCmd = &cobra.Command{
Use: "receiver [name]",
Short: "Create or update a Receiver resource",
Long: "The create receiver command generates a Receiver resource.",
Example: ` # Create a Receiver
gotk create receiver github-receiver \
--type github \
--event ping \
--event push \
--secret-ref webhook-token \
--resource GitRepository/webapp \
--resource HelmRepository/webapp
`,
RunE: createReceiverCmdRun,
}
var (
rcvType string
rcvSecretRef string
rcvEvents []string
rcvResources []string
)
func init() {
createReceiverCmd.Flags().StringVar(&rcvType, "type", "", "")
createReceiverCmd.Flags().StringVar(&rcvSecretRef, "secret-ref", "", "")
createReceiverCmd.Flags().StringArrayVar(&rcvEvents, "event", []string{}, "")
createReceiverCmd.Flags().StringArrayVar(&rcvResources, "resource", []string{}, "")
createCmd.AddCommand(createReceiverCmd)
}
func createReceiverCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("receiver name is required")
}
name := args[0]
if rcvType == "" {
return fmt.Errorf("type is required")
}
if rcvSecretRef == "" {
return fmt.Errorf("secret ref is required")
}
resources := []notificationv1.CrossNamespaceObjectReference{}
for _, resource := range rcvResources {
kind, name := utils.parseObjectKindName(resource)
if kind == "" {
return fmt.Errorf("invalid event source '%s', must be in format <kind>/<name>", resource)
}
resources = append(resources, notificationv1.CrossNamespaceObjectReference{
Kind: kind,
Name: name,
})
}
if len(resources) == 0 {
return fmt.Errorf("atleast one resource is required")
}
sourceLabels, err := parseLabels()
if err != nil {
return err
}
if !export {
logger.Generatef("generating receiver")
}
receiver := notificationv1.Receiver{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: namespace,
Labels: sourceLabels,
},
Spec: notificationv1.ReceiverSpec{
Type: rcvType,
Events: rcvEvents,
Resources: resources,
SecretRef: corev1.LocalObjectReference{
Name: rcvSecretRef,
},
Suspend: false,
},
}
if export {
return exportReceiver(receiver)
}
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
if err != nil {
return err
}
logger.Actionf("applying receiver")
if err := upsertReceiver(ctx, kubeClient, receiver); err != nil {
return err
}
logger.Waitingf("waiting for reconciliation")
if err := wait.PollImmediate(pollInterval, timeout,
isReceiverReady(ctx, kubeClient, name, namespace)); err != nil {
return err
}
logger.Successf("receiver %s is ready", name)
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
err = kubeClient.Get(ctx, namespacedName, &receiver)
if err != nil {
return fmt.Errorf("receiver sync failed: %w", err)
}
logger.Successf("generated webhook URL %s", receiver.Status.URL)
return nil
}
func upsertReceiver(ctx context.Context, kubeClient client.Client, receiver notificationv1.Receiver) error {
namespacedName := types.NamespacedName{
Namespace: receiver.GetNamespace(),
Name: receiver.GetName(),
}
var existing notificationv1.Receiver
err := kubeClient.Get(ctx, namespacedName, &existing)
if err != nil {
if errors.IsNotFound(err) {
if err := kubeClient.Create(ctx, &receiver); err != nil {
return err
} else {
logger.Successf("receiver created")
return nil
}
}
return err
}
existing.Labels = receiver.Labels
existing.Spec = receiver.Spec
if err := kubeClient.Update(ctx, &existing); err != nil {
return err
}
logger.Successf("receiver updated")
return nil
}
func isReceiverReady(ctx context.Context, kubeClient client.Client, name, namespace string) wait.ConditionFunc {
return func() (bool, error) {
var receiver notificationv1.Receiver
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
err := kubeClient.Get(ctx, namespacedName, &receiver)
if err != nil {
return false, err
}
if c := meta.GetCondition(receiver.Status.Conditions, meta.ReadyCondition); c != nil {
switch c.Status {
case corev1.ConditionTrue:
return true, nil
case corev1.ConditionFalse:
return false, fmt.Errorf(c.Message)
}
}
return false, nil
}
}

87
cmd/gotk/delete_alert.go Normal file
View File

@@ -0,0 +1,87 @@
/*
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"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
)
var deleteAlertCmd = &cobra.Command{
Use: "alert [name]",
Short: "Delete a Alert resource",
Long: "The delete alert command removes the given Alert from the cluster.",
Example: ` # Delete an Alert and the Kubernetes resources created by it
gotk delete alert main
`,
RunE: deleteAlertCmdRun,
}
func init() {
deleteCmd.AddCommand(deleteAlertCmd)
}
func deleteAlertCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("alert 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 alert notificationv1.Alert
err = kubeClient.Get(ctx, namespacedName, &alert)
if err != nil {
return err
}
if !deleteSilent {
prompt := promptui.Prompt{
Label: "Are you sure you want to delete this Alert",
IsConfirm: true,
}
if _, err := prompt.Run(); err != nil {
return fmt.Errorf("aborting")
}
}
logger.Actionf("deleting alert %s in %s namespace", name, namespace)
err = kubeClient.Delete(ctx, &alert)
if err != nil {
return err
}
logger.Successf("alert deleted")
return nil
}

View File

@@ -0,0 +1,87 @@
/*
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"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
)
var deleteAlertProviderCmd = &cobra.Command{
Use: "alert-provider [name]",
Short: "Delete a Provider resource",
Long: "The delete alert-provider command removes the given Provider from the cluster.",
Example: ` # Delete a Provider and the Kubernetes resources created by it
gotk delete alert-provider slack
`,
RunE: deleteAlertProviderCmdRun,
}
func init() {
deleteCmd.AddCommand(deleteAlertProviderCmd)
}
func deleteAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("provider 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 alertProvider notificationv1.Provider
err = kubeClient.Get(ctx, namespacedName, &alertProvider)
if err != nil {
return err
}
if !deleteSilent {
prompt := promptui.Prompt{
Label: "Are you sure you want to delete this Provider",
IsConfirm: true,
}
if _, err := prompt.Run(); err != nil {
return fmt.Errorf("aborting")
}
}
logger.Actionf("deleting provider %s in %s namespace", name, namespace)
err = kubeClient.Delete(ctx, &alertProvider)
if err != nil {
return err
}
logger.Successf("provider deleted")
return nil
}

View File

@@ -0,0 +1,87 @@
/*
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"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
)
var deleteReceiverCmd = &cobra.Command{
Use: "receiver [name]",
Short: "Delete a Receiver resource",
Long: "The delete receiver command removes the given Receiver from the cluster.",
Example: ` # Delete an Receiver and the Kubernetes resources created by it
gotk delete receiver main
`,
RunE: deleteReceiverCmdRun,
}
func init() {
deleteCmd.AddCommand(deleteReceiverCmd)
}
func deleteReceiverCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("receiver 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 receiver notificationv1.Receiver
err = kubeClient.Get(ctx, namespacedName, &receiver)
if err != nil {
return err
}
if !deleteSilent {
prompt := promptui.Prompt{
Label: "Are you sure you want to delete this Receiver",
IsConfirm: true,
}
if _, err := prompt.Run(); err != nil {
return fmt.Errorf("aborting")
}
}
logger.Actionf("deleting receiver %s in %s namespace", name, namespace)
err = kubeClient.Delete(ctx, &receiver)
if err != nil {
return err
}
logger.Successf("receiver deleted")
return nil
}

119
cmd/gotk/export_alert.go Normal file
View File

@@ -0,0 +1,119 @@
/*
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"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
)
var exportAlertCmd = &cobra.Command{
Use: "alert [name]",
Short: "Export Alert resources in YAML format",
Long: "The export alert command exports one or all Alert resources in YAML format.",
Example: ` # Export all Alert resources
gotk export alert --all > alerts.yaml
# Export a Alert
gotk export alert main > main.yaml
`,
RunE: exportAlertCmdRun,
}
func init() {
exportCmd.AddCommand(exportAlertCmd)
}
func exportAlertCmdRun(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 notificationv1.AlertList
err = kubeClient.List(ctx, &list, client.InNamespace(namespace))
if err != nil {
return err
}
if len(list.Items) == 0 {
logger.Failuref("no alerts found in %s namespace", namespace)
return nil
}
for _, alert := range list.Items {
if err := exportAlert(alert); err != nil {
return err
}
}
} else {
name := args[0]
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
var alert notificationv1.Alert
err = kubeClient.Get(ctx, namespacedName, &alert)
if err != nil {
return err
}
return exportAlert(alert)
}
return nil
}
func exportAlert(alert notificationv1.Alert) error {
gvk := notificationv1.GroupVersion.WithKind("Alert")
export := notificationv1.Alert{
TypeMeta: metav1.TypeMeta{
Kind: gvk.Kind,
APIVersion: gvk.GroupVersion().String(),
},
ObjectMeta: metav1.ObjectMeta{
Name: alert.Name,
Namespace: alert.Namespace,
Labels: alert.Labels,
Annotations: alert.Annotations,
},
Spec: alert.Spec,
}
data, err := yaml.Marshal(export)
if err != nil {
return err
}
fmt.Println("---")
fmt.Println(resourceToString(data))
return nil
}

View File

@@ -0,0 +1,119 @@
/*
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"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
)
var exportAlertProviderCmd = &cobra.Command{
Use: "alert-provider [name]",
Short: "Export Provider resources in YAML format",
Long: "The export alert-provider command exports one or all Provider resources in YAML format.",
Example: ` # Export all Provider resources
gotk export alert-provider --all > alert-providers.yaml
# Export a Provider
gotk export alert-provider slack > slack.yaml
`,
RunE: exportAlertProviderCmdRun,
}
func init() {
exportCmd.AddCommand(exportAlertProviderCmd)
}
func exportAlertProviderCmdRun(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 notificationv1.ProviderList
err = kubeClient.List(ctx, &list, client.InNamespace(namespace))
if err != nil {
return err
}
if len(list.Items) == 0 {
logger.Failuref("no alertproviders found in %s namespace", namespace)
return nil
}
for _, alertProvider := range list.Items {
if err := exportAlertProvider(alertProvider); err != nil {
return err
}
}
} else {
name := args[0]
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
var alertProvider notificationv1.Provider
err = kubeClient.Get(ctx, namespacedName, &alertProvider)
if err != nil {
return err
}
return exportAlertProvider(alertProvider)
}
return nil
}
func exportAlertProvider(alertProvider notificationv1.Provider) error {
gvk := notificationv1.GroupVersion.WithKind("Provider")
export := notificationv1.Provider{
TypeMeta: metav1.TypeMeta{
Kind: gvk.Kind,
APIVersion: gvk.GroupVersion().String(),
},
ObjectMeta: metav1.ObjectMeta{
Name: alertProvider.Name,
Namespace: alertProvider.Namespace,
Labels: alertProvider.Labels,
Annotations: alertProvider.Annotations,
},
Spec: alertProvider.Spec,
}
data, err := yaml.Marshal(export)
if err != nil {
return err
}
fmt.Println("---")
fmt.Println(resourceToString(data))
return nil
}

119
cmd/gotk/export_receiver.go Normal file
View File

@@ -0,0 +1,119 @@
/*
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"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
)
var exportReceiverCmd = &cobra.Command{
Use: "receiver [name]",
Short: "Export Receiver resources in YAML format",
Long: "The export receiver command exports one or all Receiver resources in YAML format.",
Example: ` # Export all Receiver resources
gotk export receiver --all > receivers.yaml
# Export a Receiver
gotk export receiver main > main.yaml
`,
RunE: exportReceiverCmdRun,
}
func init() {
exportCmd.AddCommand(exportReceiverCmd)
}
func exportReceiverCmdRun(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 notificationv1.ReceiverList
err = kubeClient.List(ctx, &list, client.InNamespace(namespace))
if err != nil {
return err
}
if len(list.Items) == 0 {
logger.Failuref("no receivers found in %s namespace", namespace)
return nil
}
for _, receiver := range list.Items {
if err := exportReceiver(receiver); err != nil {
return err
}
}
} else {
name := args[0]
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
var receiver notificationv1.Receiver
err = kubeClient.Get(ctx, namespacedName, &receiver)
if err != nil {
return err
}
return exportReceiver(receiver)
}
return nil
}
func exportReceiver(receiver notificationv1.Receiver) error {
gvk := notificationv1.GroupVersion.WithKind("Receiver")
export := notificationv1.Receiver{
TypeMeta: metav1.TypeMeta{
Kind: gvk.Kind,
APIVersion: gvk.GroupVersion().String(),
},
ObjectMeta: metav1.ObjectMeta{
Name: receiver.Name,
Namespace: receiver.Namespace,
Labels: receiver.Labels,
Annotations: receiver.Annotations,
},
Spec: receiver.Spec,
}
data, err := yaml.Marshal(export)
if err != nil {
return err
}
fmt.Println("---")
fmt.Println(resourceToString(data))
return nil
}

102
cmd/gotk/get_alert.go Normal file
View File

@@ -0,0 +1,102 @@
/*
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"
"os"
"strconv"
"strings"
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
"github.com/fluxcd/pkg/apis/meta"
)
var getAlertCmd = &cobra.Command{
Use: "alerts",
Short: "Get Alert statuses",
Long: "The get alert command prints the statuses of the resources.",
Example: ` # List all Alerts and their status
gotk get alerts
`,
RunE: getAlertCmdRun,
}
func init() {
getCmd.AddCommand(getAlertCmd)
}
func getAlertCmdRun(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 listOpts []client.ListOption
if !allNamespaces {
listOpts = append(listOpts, client.InNamespace(namespace))
}
var list notificationv1.AlertList
err = kubeClient.List(ctx, &list, listOpts...)
if err != nil {
return err
}
if len(list.Items) == 0 {
logger.Failuref("no alerts found in %s namespace", namespace)
return nil
}
header := []string{"Name", "Suspended", "Ready", "Message"}
if allNamespaces {
header = append([]string{"Namespace"}, header...)
}
var rows [][]string
for _, alert := range list.Items {
row := []string{}
if c := meta.GetCondition(alert.Status.Conditions, meta.ReadyCondition); c != nil {
row = []string{
alert.GetName(),
//alert.Status.LastAppliedRevision,
strings.Title(strconv.FormatBool(alert.Spec.Suspend)),
string(c.Status),
c.Message,
}
} else {
row = []string{
alert.GetName(),
//alert.Status.LastAppliedRevision,
strings.Title(strconv.FormatBool(alert.Spec.Suspend)),
string(corev1.ConditionFalse),
"waiting to be reconciled",
}
}
if allNamespaces {
row = append([]string{alert.Namespace}, row...)
}
rows = append(rows, row)
}
utils.printTable(os.Stdout, header, rows)
return nil
}

View File

@@ -0,0 +1,96 @@
/*
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"
"os"
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
"github.com/fluxcd/pkg/apis/meta"
)
var getAlertProviderCmd = &cobra.Command{
Use: "alert-providers",
Short: "Get Provider statuses",
Long: "The get alert-provider command prints the statuses of the resources.",
Example: ` # List all Providers and their status
gotk get alert-providers
`,
RunE: getAlertProviderCmdRun,
}
func init() {
getCmd.AddCommand(getAlertProviderCmd)
}
func getAlertProviderCmdRun(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 listOpts []client.ListOption
if !allNamespaces {
listOpts = append(listOpts, client.InNamespace(namespace))
}
var list notificationv1.ProviderList
err = kubeClient.List(ctx, &list, listOpts...)
if err != nil {
return err
}
if len(list.Items) == 0 {
logger.Failuref("no providers found in %s namespace", namespace)
return nil
}
header := []string{"Name", "Ready", "Message"}
if allNamespaces {
header = append([]string{"Namespace"}, header...)
}
var rows [][]string
for _, provider := range list.Items {
row := []string{}
if c := meta.GetCondition(provider.Status.Conditions, meta.ReadyCondition); c != nil {
row = []string{
provider.GetName(),
string(c.Status),
c.Message,
}
} else {
row = []string{
provider.GetName(),
string(corev1.ConditionFalse),
"waiting to be reconciled",
}
}
if allNamespaces {
row = append([]string{provider.Namespace}, row...)
}
rows = append(rows, row)
}
utils.printTable(os.Stdout, header, rows)
return nil
}

97
cmd/gotk/get_receiver.go Normal file
View File

@@ -0,0 +1,97 @@
/*
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"
"os"
"strconv"
"strings"
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
"github.com/fluxcd/pkg/apis/meta"
)
var getReceiverCmd = &cobra.Command{
Use: "receivers",
Short: "Get Receiver statuses",
Long: "The get receiver command prints the statuses of the resources.",
Example: ` # List all Receiver and their status
gotk get receivers
`,
RunE: getReceiverCmdRun,
}
func init() {
getCmd.AddCommand(getReceiverCmd)
}
func getReceiverCmdRun(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 listOpts []client.ListOption
if !allNamespaces {
listOpts = append(listOpts, client.InNamespace(namespace))
}
var list notificationv1.ReceiverList
err = kubeClient.List(ctx, &list, listOpts...)
if err != nil {
return err
}
if len(list.Items) == 0 {
logger.Failuref("no receivers found in %s namespace", namespace)
return nil
}
header := []string{"Name", "Suspended", "Ready", "Message"}
if allNamespaces {
header = append([]string{"Namespace"}, header...)
}
var rows [][]string
for _, receiver := range list.Items {
row := []string{}
if c := meta.GetCondition(receiver.Status.Conditions, meta.ReadyCondition); c != nil {
row = []string{
receiver.GetName(),
strings.Title(strconv.FormatBool(receiver.Spec.Suspend)),
string(c.Status),
c.Message,
}
} else {
row = []string{
receiver.GetName(),
strings.Title(strconv.FormatBool(receiver.Spec.Suspend)),
string(corev1.ConditionFalse),
"waiting to be reconciled",
}
}
rows = append(rows, row)
}
utils.printTable(os.Stdout, header, rows)
return nil
}

View File

@@ -20,18 +20,13 @@ import (
"context"
"fmt"
"io/ioutil"
"net/http"
"os"
"path"
"path/filepath"
"strings"
"time"
"github.com/spf13/cobra"
"sigs.k8s.io/kustomize/api/filesys"
"sigs.k8s.io/kustomize/api/krusty"
"github.com/fluxcd/pkg/untar"
"github.com/fluxcd/toolkit/pkg/install"
)
var installCmd = &cobra.Command{
@@ -114,38 +109,50 @@ func installCmdRun(cmd *cobra.Command, args []string) error {
if !installExport {
logger.Generatef("generating manifests")
}
opts := install.Options{
BaseURL: installManifestsPath,
Version: installVersion,
Namespace: namespace,
Components: installComponents,
Registry: installRegistry,
ImagePullSecret: installImagePullSecret,
Arch: installArch,
WatchAllNamespaces: installWatchAllNamespaces,
NetworkPolicy: installNetworkPolicy,
LogLevel: installLogLevel,
NotificationController: defaultNotification,
ManifestsFile: fmt.Sprintf("%s.yaml", namespace),
Timeout: timeout,
}
if installManifestsPath == "" {
err = genInstallManifests(installVersion, namespace, installComponents,
installWatchAllNamespaces, installNetworkPolicy, installRegistry, installImagePullSecret,
installArch, installLogLevel, tmpDir)
if err != nil {
return fmt.Errorf("install failed: %w", err)
}
installManifestsPath = tmpDir
opts.BaseURL = install.MakeDefaultOptions().BaseURL
}
output, err := install.Generate(opts)
if err != nil {
return fmt.Errorf("install failed: %w", err)
}
manifest := path.Join(tmpDir, fmt.Sprintf("%s.yaml", namespace))
if err := buildKustomization(installManifestsPath, manifest); err != nil {
if err := ioutil.WriteFile(manifest, output, os.ModePerm); err != nil {
return fmt.Errorf("install failed: %w", err)
}
command := fmt.Sprintf("cat %s", manifest)
if yaml, err := utils.execCommand(ctx, ModeCapture, command); err != nil {
return fmt.Errorf("install failed: %w", err)
} else {
if verbose {
fmt.Print(yaml)
} else if installExport {
fmt.Println("---")
fmt.Println("# GitOps Toolkit revision", installVersion, time.Now().Format(time.RFC3339))
fmt.Println("# Components:", strings.Join(installComponents, ","))
fmt.Print(yaml)
fmt.Println("---")
return nil
}
yaml := string(output)
if verbose {
fmt.Print(yaml)
} else if installExport {
fmt.Println("---")
fmt.Println("# GitOps Toolkit revision", installVersion)
fmt.Println("# Components:", strings.Join(installComponents, ","))
fmt.Print(yaml)
fmt.Println("---")
return nil
}
logger.Successf("manifests build completed")
logger.Actionf("installing components in %s namespace", namespace)
applyOutput := ModeStderrOS
if verbose {
@@ -156,7 +163,8 @@ func installCmdRun(cmd *cobra.Command, args []string) error {
dryRun = "--dry-run=client"
applyOutput = ModeOS
}
command = fmt.Sprintf("cat %s | kubectl apply -f- %s", manifest, dryRun)
command := fmt.Sprintf("kubectl apply -f %s %s", manifest, dryRun)
if _, err := utils.execCommand(ctx, applyOutput, command); err != nil {
return fmt.Errorf("install failed")
}
@@ -182,250 +190,3 @@ func installCmdRun(cmd *cobra.Command, args []string) error {
logger.Successf("install finished")
return nil
}
var namespaceTmpl = `---
apiVersion: v1
kind: Namespace
metadata:
name: {{.Namespace}}
`
var labelsTmpl = `---
apiVersion: builtin
kind: LabelTransformer
metadata:
name: labels
labels:
app.kubernetes.io/instance: {{.Namespace}}
app.kubernetes.io/version: "{{.Version}}"
fieldSpecs:
- path: metadata/labels
create: true
`
var kustomizationTmpl = `---
{{- $eventsAddr := .EventsAddr }}
{{- $watchAllNamespaces := .WatchAllNamespaces }}
{{- $registry := .Registry }}
{{- $arch := .Arch }}
{{- $logLevel := .LogLevel }}
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: {{.Namespace}}
transformers:
- labels.yaml
resources:
- namespace.yaml
{{- if .NetworkPolicy }}
- policies.yaml
{{- end }}
- roles
{{- range .Components }}
- {{.}}.yaml
{{- end }}
patches:
- path: node-selector.yaml
target:
kind: Deployment
patchesJson6902:
{{- range $i, $component := .Components }}
{{- if eq $component "notification-controller" }}
- target:
group: apps
version: v1
kind: Deployment
name: {{$component}}
patch: |-
- op: replace
path: /spec/template/spec/containers/0/args/0
value: --watch-all-namespaces={{$watchAllNamespaces}}
- op: replace
path: /spec/template/spec/containers/0/args/1
value: --log-level={{$logLevel}}
{{- else }}
- target:
group: apps
version: v1
kind: Deployment
name: {{$component}}
patch: |-
- op: replace
path: /spec/template/spec/containers/0/args/0
value: --events-addr={{$eventsAddr}}
- op: replace
path: /spec/template/spec/containers/0/args/1
value: --watch-all-namespaces={{$watchAllNamespaces}}
- op: replace
path: /spec/template/spec/containers/0/args/2
value: --log-level={{$logLevel}}
{{- end }}
{{- end }}
{{- if $registry }}
images:
{{- range $i, $component := .Components }}
- name: fluxcd/{{$component}}
{{- if eq $arch "amd64" }}
newName: {{$registry}}/{{$component}}
{{- else }}
newName: {{$registry}}/{{$component}}-arm64
{{- end }}
{{- end }}
{{- end }}
`
var kustomizationRolesTmpl = `---
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- rbac.yaml
nameSuffix: -{{.Namespace}}
`
var nodeSelectorTmpl = `---
apiVersion: apps/v1
kind: Deployment
metadata:
name: all
spec:
template:
spec:
nodeSelector:
kubernetes.io/arch: {{.Arch}}
kubernetes.io/os: linux
{{- if .ImagePullSecret }}
imagePullSecrets:
- name: {{.ImagePullSecret}}
{{- end }}
`
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,
watchAllNamespaces, networkPolicy bool, registry, imagePullSecret, arch, logLevel, tmpDir string) error {
eventsAddr := ""
if utils.containsItemString(components, defaultNotification) {
eventsAddr = fmt.Sprintf("http://%s/", defaultNotification)
}
model := struct {
Version string
Namespace string
Components []string
EventsAddr string
Registry string
ImagePullSecret string
Arch string
WatchAllNamespaces bool
NetworkPolicy bool
LogLevel string
}{
Version: version,
Namespace: namespace,
Components: components,
EventsAddr: eventsAddr,
Registry: registry,
ImagePullSecret: imagePullSecret,
Arch: arch,
WatchAllNamespaces: watchAllNamespaces,
NetworkPolicy: networkPolicy,
LogLevel: logLevel,
}
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)
}
if err := utils.execTemplate(model, labelsTmpl, path.Join(tmpDir, "labels.yaml")); err != nil {
return fmt.Errorf("generate labels failed: %w", err)
}
if err := utils.execTemplate(model, nodeSelectorTmpl, path.Join(tmpDir, "node-selector.yaml")); err != nil {
return fmt.Errorf("generate node selector failed: %w", err)
}
if err := utils.execTemplate(model, kustomizationTmpl, path.Join(tmpDir, "kustomization.yaml")); err != nil {
return fmt.Errorf("generate kustomization failed: %w", err)
}
if err := os.MkdirAll(path.Join(tmpDir, "roles"), os.ModePerm); err != nil {
return fmt.Errorf("generate roles failed: %w", err)
}
if err := utils.execTemplate(model, kustomizationRolesTmpl, path.Join(tmpDir, "roles/kustomization.yaml")); err != nil {
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
}
func buildKustomization(base, manifests string) error {
kfile := filepath.Join(base, "kustomization.yaml")
fs := filesys.MakeFsOnDisk()
if !fs.Exists(kfile) {
return fmt.Errorf("%s not found", kfile)
}
opt := krusty.MakeDefaultOptions()
k := krusty.MakeKustomizer(fs, opt)
m, err := k.Run(base)
if err != nil {
return err
}
resources, err := m.AsYaml()
if err != nil {
return err
}
if err := fs.WriteFile(manifests, resources); err != nil {
return err
}
return nil
}

View File

@@ -0,0 +1,93 @@
/*
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/fluxcd/pkg/apis/meta"
"time"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
)
var reconcileAlertCmd = &cobra.Command{
Use: "alert [name]",
Short: "Reconcile an Alert",
Long: `The reconcile alert command triggers a reconciliation of an Alert resource and waits for it to finish.`,
Example: ` # Trigger a reconciliation for an existing alert
gotk reconcile alert main
`,
RunE: reconcileAlertCmdRun,
}
func init() {
reconcileCmd.AddCommand(reconcileAlertCmd)
}
func reconcileAlertCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("alert 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,
}
logger.Actionf("annotating alert %s in %s namespace", name, namespace)
var alert notificationv1.Alert
err = kubeClient.Get(ctx, namespacedName, &alert)
if err != nil {
return err
}
if alert.Annotations == nil {
alert.Annotations = map[string]string{
meta.ReconcileAtAnnotation: time.Now().Format(time.RFC3339Nano),
}
} else {
alert.Annotations[meta.ReconcileAtAnnotation] = time.Now().Format(time.RFC3339Nano)
}
if err := kubeClient.Update(ctx, &alert); err != nil {
return err
}
logger.Successf("alert annotated")
logger.Waitingf("waiting for reconciliation")
if err := wait.PollImmediate(pollInterval, timeout,
isAlertReady(ctx, kubeClient, name, namespace)); err != nil {
return err
}
logger.Successf("alert reconciliation completed")
return nil
}

View File

@@ -0,0 +1,93 @@
/*
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/fluxcd/pkg/apis/meta"
"time"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
)
var reconcileAlertProviderCmd = &cobra.Command{
Use: "alert-provider [name]",
Short: "Reconcile a Provider",
Long: `The reconcile alert-provider command triggers a reconciliation of a Provider resource and waits for it to finish.`,
Example: ` # Trigger a reconciliation for an existing provider
gotk reconcile alert-provider slack
`,
RunE: reconcileAlertProviderCmdRun,
}
func init() {
reconcileCmd.AddCommand(reconcileAlertProviderCmd)
}
func reconcileAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("provider 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,
}
logger.Actionf("annotating provider %s in %s namespace", name, namespace)
var alertProvider notificationv1.Provider
err = kubeClient.Get(ctx, namespacedName, &alertProvider)
if err != nil {
return err
}
if alertProvider.Annotations == nil {
alertProvider.Annotations = map[string]string{
meta.ReconcileAtAnnotation: time.Now().Format(time.RFC3339Nano),
}
} else {
alertProvider.Annotations[meta.ReconcileAtAnnotation] = time.Now().Format(time.RFC3339Nano)
}
if err := kubeClient.Update(ctx, &alertProvider); err != nil {
return err
}
logger.Successf("provider annotated")
logger.Waitingf("waiting for reconciliation")
if err := wait.PollImmediate(pollInterval, timeout,
isAlertProviderReady(ctx, kubeClient, name, namespace)); err != nil {
return err
}
logger.Successf("provider reconciliation completed")
return nil
}

View File

@@ -0,0 +1,93 @@
/*
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/fluxcd/pkg/apis/meta"
"time"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
)
var reconcileReceiverCmd = &cobra.Command{
Use: "receiver [name]",
Short: "Reconcile a Receiver",
Long: `The reconcile receiver command triggers a reconciliation of a Receiver resource and waits for it to finish.`,
Example: ` # Trigger a reconciliation for an existing receiver
gotk reconcile receiver main
`,
RunE: reconcileReceiverCmdRun,
}
func init() {
reconcileCmd.AddCommand(reconcileReceiverCmd)
}
func reconcileReceiverCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("receiver 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,
}
logger.Actionf("annotating receiver %s in %s namespace", name, namespace)
var receiver notificationv1.Receiver
err = kubeClient.Get(ctx, namespacedName, &receiver)
if err != nil {
return err
}
if receiver.Annotations == nil {
receiver.Annotations = map[string]string{
meta.ReconcileAtAnnotation: time.Now().Format(time.RFC3339Nano),
}
} else {
receiver.Annotations[meta.ReconcileAtAnnotation] = time.Now().Format(time.RFC3339Nano)
}
if err := kubeClient.Update(ctx, &receiver); err != nil {
return err
}
logger.Successf("receiver annotated")
logger.Waitingf("waiting for reconciliation")
if err := wait.PollImmediate(pollInterval, timeout,
isReceiverReady(ctx, kubeClient, name, namespace)); err != nil {
return err
}
logger.Successf("receiver reconciliation completed")
return nil
}

116
cmd/gotk/resume_alert.go Normal file
View File

@@ -0,0 +1,116 @@
/*
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/fluxcd/pkg/apis/meta"
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait"
"sigs.k8s.io/controller-runtime/pkg/client"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
)
var resumeAlertCmd = &cobra.Command{
Use: "alert [name]",
Short: "Resume a suspended Alert",
Long: `The resume command marks a previously suspended Alert resource for reconciliation and waits for it to
finish the apply.`,
Example: ` # Resume reconciliation for an existing Alert
gotk resume alert main
`,
RunE: resumeAlertCmdRun,
}
func init() {
resumeCmd.AddCommand(resumeAlertCmd)
}
func resumeAlertCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("Alert 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 alert notificationv1.Alert
err = kubeClient.Get(ctx, namespacedName, &alert)
if err != nil {
return err
}
logger.Actionf("resuming Alert %s in %s namespace", name, namespace)
alert.Spec.Suspend = false
if err := kubeClient.Update(ctx, &alert); err != nil {
return err
}
logger.Successf("Alert resumed")
logger.Waitingf("waiting for Alert reconciliation")
if err := wait.PollImmediate(pollInterval, timeout,
isAlertResumed(ctx, kubeClient, name, namespace)); err != nil {
return err
}
logger.Successf("Alert reconciliation completed")
return nil
}
func isAlertResumed(ctx context.Context, kubeClient client.Client, name, namespace string) wait.ConditionFunc {
return func() (bool, error) {
var alert notificationv1.Alert
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
err := kubeClient.Get(ctx, namespacedName, &alert)
if err != nil {
return false, err
}
if c := meta.GetCondition(alert.Status.Conditions, meta.ReadyCondition); c != nil {
switch c.Status {
case corev1.ConditionTrue:
return true, nil
case corev1.ConditionFalse:
if c.Reason == meta.SuspendedReason {
return false, nil
}
return false, fmt.Errorf(c.Message)
}
}
return false, nil
}
}

116
cmd/gotk/resume_receiver.go Normal file
View File

@@ -0,0 +1,116 @@
/*
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/fluxcd/pkg/apis/meta"
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait"
"sigs.k8s.io/controller-runtime/pkg/client"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
)
var resumeReceiverCmd = &cobra.Command{
Use: "receiver [name]",
Short: "Resume a suspended Receiver",
Long: `The resume command marks a previously suspended Receiver resource for reconciliation and waits for it to
finish the apply.`,
Example: ` # Resume reconciliation for an existing Receiver
gotk resume receiver main
`,
RunE: resumeReceiverCmdRun,
}
func init() {
resumeCmd.AddCommand(resumeReceiverCmd)
}
func resumeReceiverCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("Receiver 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 receiver notificationv1.Receiver
err = kubeClient.Get(ctx, namespacedName, &receiver)
if err != nil {
return err
}
logger.Actionf("resuming Receiver %s in %s namespace", name, namespace)
receiver.Spec.Suspend = false
if err := kubeClient.Update(ctx, &receiver); err != nil {
return err
}
logger.Successf("Receiver resumed")
logger.Waitingf("waiting for Receiver reconciliation")
if err := wait.PollImmediate(pollInterval, timeout,
isReceiverResumed(ctx, kubeClient, name, namespace)); err != nil {
return err
}
logger.Successf("Receiver reconciliation completed")
return nil
}
func isReceiverResumed(ctx context.Context, kubeClient client.Client, name, namespace string) wait.ConditionFunc {
return func() (bool, error) {
var receiver notificationv1.Receiver
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
err := kubeClient.Get(ctx, namespacedName, &receiver)
if err != nil {
return false, err
}
if c := meta.GetCondition(receiver.Status.Conditions, meta.ReadyCondition); c != nil {
switch c.Status {
case corev1.ConditionTrue:
return true, nil
case corev1.ConditionFalse:
if c.Reason == meta.SuspendedReason {
return false, nil
}
return false, fmt.Errorf(c.Message)
}
}
return false, nil
}
}

75
cmd/gotk/suspend_alert.go Normal file
View File

@@ -0,0 +1,75 @@
/*
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"
"k8s.io/apimachinery/pkg/types"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
)
var suspendAlertCmd = &cobra.Command{
Use: "alert [name]",
Short: "Suspend reconciliation of Alert",
Long: "The suspend command disables the reconciliation of a Alert resource.",
Example: ` # Suspend reconciliation for an existing Alert
gotk suspend alert main
`,
RunE: suspendAlertCmdRun,
}
func init() {
suspendCmd.AddCommand(suspendAlertCmd)
}
func suspendAlertCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("Alert 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 alert notificationv1.Alert
err = kubeClient.Get(ctx, namespacedName, &alert)
if err != nil {
return err
}
logger.Actionf("suspending Alert %s in %s namespace", name, namespace)
alert.Spec.Suspend = true
if err := kubeClient.Update(ctx, &alert); err != nil {
return err
}
logger.Successf("Alert suspended")
return nil
}

View File

@@ -0,0 +1,75 @@
/*
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"
"k8s.io/apimachinery/pkg/types"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
)
var suspendReceiverCmd = &cobra.Command{
Use: "receiver [name]",
Short: "Suspend reconciliation of Receiver",
Long: "The suspend command disables the reconciliation of a Receiver resource.",
Example: ` # Suspend reconciliation for an existing Receiver
gotk suspend receiver main
`,
RunE: suspendReceiverCmdRun,
}
func init() {
suspendCmd.AddCommand(suspendReceiverCmd)
}
func suspendReceiverCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("Receiver 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 receiver notificationv1.Receiver
err = kubeClient.Get(ctx, namespacedName, &receiver)
if err != nil {
return err
}
logger.Actionf("suspending Receiver %s in %s namespace", name, namespace)
receiver.Spec.Suspend = true
if err := kubeClient.Update(ctx, &receiver); err != nil {
return err
}
logger.Successf("Receiver suspended")
return nil
}

View File

@@ -24,6 +24,7 @@ import (
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/types"
helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
)
@@ -72,7 +73,7 @@ func uninstallCmdRun(cmd *cobra.Command, args []string) error {
dryRun := ""
if uninstallDryRun {
dryRun = "--dry-run=client"
dryRun = "--dry-run=server"
} else if !uninstallSilent {
prompt := promptui.Prompt{
Label: fmt.Sprintf("Are you sure you want to delete the %s namespace", namespace),
@@ -102,25 +103,31 @@ func uninstallCmdRun(cmd *cobra.Command, args []string) error {
kustomizev1.KustomizationKind,
sourcev1.GitRepositoryKind,
sourcev1.HelmRepositoryKind,
helmv2.HelmReleaseKind,
} {
command := fmt.Sprintf("kubectl -n %s delete %s --all --timeout=%s %s",
command := fmt.Sprintf("kubectl -n %s delete %s --all --ignore-not-found --timeout=%s %s",
namespace, kind, timeout.String(), dryRun)
if _, err := utils.execCommand(ctx, ModeOS, command); err != nil {
return fmt.Errorf("uninstall failed")
return fmt.Errorf("uninstall failed: %w", err)
}
}
}
kinds := "namespace,clusterroles,clusterrolebindings"
var kinds []string
if uninstallCRDs {
kinds += ",crds"
kinds = append(kinds, "crds")
}
kinds = append(kinds, "clusterroles,clusterrolebindings", "namespace")
logger.Actionf("uninstalling components")
command := fmt.Sprintf("kubectl delete %s -l app.kubernetes.io/instance=%s --timeout=%s %s",
kinds, namespace, timeout.String(), dryRun)
if _, err := utils.execCommand(ctx, ModeOS, command); err != nil {
return fmt.Errorf("uninstall failed")
for _, kind := range kinds {
command := fmt.Sprintf("kubectl delete %s -l app.kubernetes.io/instance=%s --ignore-not-found --timeout=%s %s",
kind, namespace, timeout.String(), dryRun)
if _, err := utils.execCommand(ctx, ModeOS, command); err != nil {
return fmt.Errorf("uninstall failed: %w", err)
}
}
logger.Successf("uninstall finished")

View File

@@ -43,6 +43,7 @@ import (
helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
"github.com/fluxcd/pkg/runtime/dependency"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
"github.com/olekukonko/tablewriter"
@@ -137,6 +138,7 @@ func (*Utils) kubeClient(kubeConfigPath string) (client.Client, error) {
_ = sourcev1.AddToScheme(scheme)
_ = kustomizev1.AddToScheme(scheme)
_ = helmv2.AddToScheme(scheme)
_ = notificationv1.AddToScheme(scheme)
kubeClient, err := client.New(cfg, client.Options{
Scheme: scheme,

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 53 KiB

View File

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 34 KiB

View File

Before

Width:  |  Height:  |  Size: 67 KiB

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 44 KiB

View File

@@ -27,7 +27,10 @@ The create sub-commands generate sources and resources.
### SEE ALSO
* [gotk](gotk.md) - Command line utility for assembling Kubernetes CD pipelines
* [gotk create alert](gotk_create_alert.md) - Create or update a Alert resource
* [gotk create alert-provider](gotk_create_alert-provider.md) - Create or update a Provider resource
* [gotk create helmrelease](gotk_create_helmrelease.md) - Create or update a HelmRelease resource
* [gotk create kustomization](gotk_create_kustomization.md) - Create or update a Kustomization resource
* [gotk create receiver](gotk_create_receiver.md) - Create or update a Receiver resource
* [gotk create source](gotk_create_source.md) - Create or update sources

View File

@@ -0,0 +1,57 @@
## gotk create alert-provider
Create or update a Provider resource
### Synopsis
The create alert-provider command generates a Provider resource.
```
gotk create alert-provider [name] [flags]
```
### Examples
```
# Create a Provider for a Slack channel
gotk create alert-provider slack \
--type slack \
--channel general \
--address https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK \
--secret-ref webhook-url
# Create a Provider for a Github repository
gotk create alert-provider github-podinfo \
--type github \
--address https://github.com/stefanprodan/podinfo \
--secret-ref github-token
```
### Options
```
--address string path to either the git repository, chat provider or webhook
--channel string channel to send messages to in the case of a chat provider
-h, --help help for alert-provider
--secret-ref string name of secret containing authentication token
--type string type of provider
--username string bot username used by the provider
```
### 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")
--label strings set labels on the resource (can specify multiple labels with commas: label1=value1,label2=value2)
-n, --namespace string the namespace scope for this operation (default "gotk-system")
--timeout duration timeout for this operation (default 5m0s)
--verbose print generated objects
```
### SEE ALSO
* [gotk create](gotk_create.md) - Create or update sources and resources

View File

@@ -0,0 +1,49 @@
## gotk create alert
Create or update a Alert resource
### Synopsis
The create alert command generates a Alert resource.
```
gotk create alert [name] [flags]
```
### Examples
```
# Create an Alert for kustomization events
gotk create alert \
--event-severity info \
--event-source Kustomization/gotk-system \
--provider-ref slack \
gotk-system
```
### Options
```
--event-severity string severity of events to send alerts for
--event-source stringArray sources that should generate alerts (<kind>/<name>)
-h, --help help for alert
--provider-ref string reference to provider
```
### 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")
--label strings set labels on the resource (can specify multiple labels with commas: label1=value1,label2=value2)
-n, --namespace string the namespace scope for this operation (default "gotk-system")
--timeout duration timeout for this operation (default 5m0s)
--verbose print generated objects
```
### SEE ALSO
* [gotk create](gotk_create.md) - Create or update sources and resources

View File

@@ -0,0 +1,52 @@
## gotk create receiver
Create or update a Receiver resource
### Synopsis
The create receiver command generates a Receiver resource.
```
gotk create receiver [name] [flags]
```
### Examples
```
# Create a Receiver
gotk create receiver github-receiver \
--type github \
--event ping \
--event push \
--secret-ref webhook-token \
--resource GitRepository/webapp \
--resource HelmRepository/webapp
```
### Options
```
--event stringArray
-h, --help help for receiver
--resource stringArray
--secret-ref string
--type string
```
### 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")
--label strings set labels on the resource (can specify multiple labels with commas: label1=value1,label2=value2)
-n, --namespace string the namespace scope for this operation (default "gotk-system")
--timeout duration timeout for this operation (default 5m0s)
--verbose print generated objects
```
### SEE ALSO
* [gotk create](gotk_create.md) - Create or update sources and resources

View File

@@ -25,7 +25,10 @@ The delete sub-commands delete sources and resources.
### SEE ALSO
* [gotk](gotk.md) - Command line utility for assembling Kubernetes CD pipelines
* [gotk delete alert](gotk_delete_alert.md) - Delete a Alert resource
* [gotk delete alert-provider](gotk_delete_alert-provider.md) - Delete a Provider resource
* [gotk delete helmrelease](gotk_delete_helmrelease.md) - Delete a HelmRelease resource
* [gotk delete kustomization](gotk_delete_kustomization.md) - Delete a Kustomization resource
* [gotk delete receiver](gotk_delete_receiver.md) - Delete a Receiver resource
* [gotk delete source](gotk_delete_source.md) - Delete sources

View File

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

View File

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

View File

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

View File

@@ -25,7 +25,10 @@ The export sub-commands export resources in YAML format.
### SEE ALSO
* [gotk](gotk.md) - Command line utility for assembling Kubernetes CD pipelines
* [gotk export alert](gotk_export_alert.md) - Export Alert resources in YAML format
* [gotk export alert-provider](gotk_export_alert-provider.md) - Export Provider resources in YAML format
* [gotk export helmrelease](gotk_export_helmrelease.md) - Export HelmRelease resources in YAML format
* [gotk export kustomization](gotk_export_kustomization.md) - Export Kustomization resources in YAML format
* [gotk export receiver](gotk_export_receiver.md) - Export Receiver resources in YAML format
* [gotk export source](gotk_export_source.md) - Export sources

View File

@@ -0,0 +1,43 @@
## gotk export alert-provider
Export Provider resources in YAML format
### Synopsis
The export alert-provider command exports one or all Provider resources in YAML format.
```
gotk export alert-provider [name] [flags]
```
### Examples
```
# Export all Provider resources
gotk export alert-provider --all > alert-providers.yaml
# Export a Provider
gotk export alert-provider slack > slack.yaml
```
### Options
```
-h, --help help for alert-provider
```
### Options inherited from parent commands
```
--all select all resources
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
-n, --namespace string the namespace scope for this operation (default "gotk-system")
--timeout duration timeout for this operation (default 5m0s)
--verbose print generated objects
```
### SEE ALSO
* [gotk export](gotk_export.md) - Export resources in YAML format

View File

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

View File

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

View File

@@ -25,7 +25,10 @@ The get sub-commands print the statuses of sources and resources.
### SEE ALSO
* [gotk](gotk.md) - Command line utility for assembling Kubernetes CD pipelines
* [gotk get alert-providers](gotk_get_alert-providers.md) - Get Provider statuses
* [gotk get alerts](gotk_get_alerts.md) - Get Alert statuses
* [gotk get helmreleases](gotk_get_helmreleases.md) - Get HelmRelease statuses
* [gotk get kustomizations](gotk_get_kustomizations.md) - Get Kustomization statuses
* [gotk get receivers](gotk_get_receivers.md) - Get Receiver statuses
* [gotk get sources](gotk_get_sources.md) - Get source statuses

View File

@@ -0,0 +1,40 @@
## gotk get alert-provider
Get Provider statuses
### Synopsis
The get alert-provider command prints the statuses of the resources.
```
gotk get alert-provider [flags]
```
### Examples
```
# List all Providers and their status
gotk get alert-provider
```
### Options
```
-h, --help help for alert-provider
```
### Options inherited from parent commands
```
-A, --all-namespaces list the requested object(s) across all namespaces
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
-n, --namespace string the namespace scope for this operation (default "gotk-system")
--timeout duration timeout for this operation (default 5m0s)
--verbose print generated objects
```
### SEE ALSO
* [gotk get](gotk_get.md) - Get sources and resources

View File

@@ -0,0 +1,40 @@
## gotk get alert-providers
Get Provider statuses
### Synopsis
The get alert-provider command prints the statuses of the resources.
```
gotk get alert-providers [flags]
```
### Examples
```
# List all Providers and their status
gotk get alert-providers
```
### Options
```
-h, --help help for alert-providers
```
### Options inherited from parent commands
```
-A, --all-namespaces list the requested object(s) across all namespaces
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
-n, --namespace string the namespace scope for this operation (default "gotk-system")
--timeout duration timeout for this operation (default 5m0s)
--verbose print generated objects
```
### SEE ALSO
* [gotk get](gotk_get.md) - Get sources and resources

View File

@@ -0,0 +1,40 @@
## gotk get alert
Get Alert statuses
### Synopsis
The get alert command prints the statuses of the resources.
```
gotk get alert [flags]
```
### Examples
```
# List all Alerts and their status
gotk get alert
```
### Options
```
-h, --help help for alert
```
### Options inherited from parent commands
```
-A, --all-namespaces list the requested object(s) across all namespaces
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
-n, --namespace string the namespace scope for this operation (default "gotk-system")
--timeout duration timeout for this operation (default 5m0s)
--verbose print generated objects
```
### SEE ALSO
* [gotk get](gotk_get.md) - Get sources and resources

View File

@@ -0,0 +1,40 @@
## gotk get alerts
Get Alert statuses
### Synopsis
The get alert command prints the statuses of the resources.
```
gotk get alerts [flags]
```
### Examples
```
# List all Alerts and their status
gotk get alerts
```
### Options
```
-h, --help help for alerts
```
### Options inherited from parent commands
```
-A, --all-namespaces list the requested object(s) across all namespaces
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
-n, --namespace string the namespace scope for this operation (default "gotk-system")
--timeout duration timeout for this operation (default 5m0s)
--verbose print generated objects
```
### SEE ALSO
* [gotk get](gotk_get.md) - Get sources and resources

View File

@@ -0,0 +1,40 @@
## gotk get receiver
Get Receiver statuses
### Synopsis
The get receiver command prints the statuses of the resources.
```
gotk get receiver [flags]
```
### Examples
```
# List all Receiver and their status
gotk get receiver
```
### Options
```
-h, --help help for receiver
```
### Options inherited from parent commands
```
-A, --all-namespaces list the requested object(s) across all namespaces
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
-n, --namespace string the namespace scope for this operation (default "gotk-system")
--timeout duration timeout for this operation (default 5m0s)
--verbose print generated objects
```
### SEE ALSO
* [gotk get](gotk_get.md) - Get sources and resources

View File

@@ -0,0 +1,40 @@
## gotk get receivers
Get Receiver statuses
### Synopsis
The get receiver command prints the statuses of the resources.
```
gotk get receivers [flags]
```
### Examples
```
# List all Receiver and their status
gotk get receivers
```
### Options
```
-h, --help help for receivers
```
### Options inherited from parent commands
```
-A, --all-namespaces list the requested object(s) across all namespaces
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
-n, --namespace string the namespace scope for this operation (default "gotk-system")
--timeout duration timeout for this operation (default 5m0s)
--verbose print generated objects
```
### SEE ALSO
* [gotk get](gotk_get.md) - Get sources and resources

View File

@@ -24,7 +24,10 @@ The reconcile sub-commands trigger a reconciliation of sources and resources.
### SEE ALSO
* [gotk](gotk.md) - Command line utility for assembling Kubernetes CD pipelines
* [gotk reconcile alert](gotk_reconcile_alert.md) - Reconcile an Alert
* [gotk reconcile alert-provider](gotk_reconcile_alert-provider.md) - Reconcile a Provider
* [gotk reconcile helmrelease](gotk_reconcile_helmrelease.md) - Reconcile a HelmRelease resource
* [gotk reconcile kustomization](gotk_reconcile_kustomization.md) - Reconcile a Kustomization resource
* [gotk reconcile receiver](gotk_reconcile_receiver.md) - Reconcile a Receiver
* [gotk reconcile source](gotk_reconcile_source.md) - Reconcile sources

View File

@@ -0,0 +1,39 @@
## gotk reconcile alert-provider
Reconcile a Provider
### Synopsis
The reconcile alert-provider command triggers a reconciliation of a Provider resource and waits for it to finish.
```
gotk reconcile alert-provider [name] [flags]
```
### Examples
```
# Trigger a reconciliation for an existing provider
gotk reconcile alert-provider slack
```
### Options
```
-h, --help help for alert-provider
```
### Options inherited from parent commands
```
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
-n, --namespace string the namespace scope for this operation (default "gotk-system")
--timeout duration timeout for this operation (default 5m0s)
--verbose print generated objects
```
### SEE ALSO
* [gotk reconcile](gotk_reconcile.md) - Reconcile sources and resources

View File

@@ -0,0 +1,39 @@
## gotk reconcile alert
Reconcile an Alert
### Synopsis
The reconcile alert command triggers a reconciliation of an Alert resource and waits for it to finish.
```
gotk reconcile alert [name] [flags]
```
### Examples
```
# Trigger a reconciliation for an existing alert
gotk reconcile alert main
```
### Options
```
-h, --help help for alert
```
### Options inherited from parent commands
```
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
-n, --namespace string the namespace scope for this operation (default "gotk-system")
--timeout duration timeout for this operation (default 5m0s)
--verbose print generated objects
```
### SEE ALSO
* [gotk reconcile](gotk_reconcile.md) - Reconcile sources and resources

View File

@@ -0,0 +1,39 @@
## gotk reconcile receiver
Reconcile a Receiver
### Synopsis
The reconcile receiver command triggers a reconciliation of a Receiver resource and waits for it to finish.
```
gotk reconcile receiver [name] [flags]
```
### Examples
```
# Trigger a reconciliation for an existing receiver
gotk reconcile receiver main
```
### Options
```
-h, --help help for receiver
```
### Options inherited from parent commands
```
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
-n, --namespace string the namespace scope for this operation (default "gotk-system")
--timeout duration timeout for this operation (default 5m0s)
--verbose print generated objects
```
### SEE ALSO
* [gotk reconcile](gotk_reconcile.md) - Reconcile sources and resources

View File

@@ -24,6 +24,8 @@ The resume sub-commands resume a suspended resource.
### SEE ALSO
* [gotk](gotk.md) - Command line utility for assembling Kubernetes CD pipelines
* [gotk resume alert](gotk_resume_alert.md) - Resume a suspended Alert
* [gotk resume helmrelease](gotk_resume_helmrelease.md) - Resume a suspended HelmRelease
* [gotk resume kustomization](gotk_resume_kustomization.md) - Resume a suspended Kustomization
* [gotk resume receiver](gotk_resume_receiver.md) - Resume a suspended Receiver

View File

@@ -0,0 +1,40 @@
## gotk resume alert
Resume a suspended Alert
### Synopsis
The resume command marks a previously suspended Alert resource for reconciliation and waits for it to
finish the apply.
```
gotk resume alert [name] [flags]
```
### Examples
```
# Resume reconciliation for an existing Alert
gotk resume alert main
```
### Options
```
-h, --help help for alert
```
### Options inherited from parent commands
```
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
-n, --namespace string the namespace scope for this operation (default "gotk-system")
--timeout duration timeout for this operation (default 5m0s)
--verbose print generated objects
```
### SEE ALSO
* [gotk resume](gotk_resume.md) - Resume suspended resources

View File

@@ -0,0 +1,40 @@
## gotk resume receiver
Resume a suspended Receiver
### Synopsis
The resume command marks a previously suspended Receiver resource for reconciliation and waits for it to
finish the apply.
```
gotk resume receiver [name] [flags]
```
### Examples
```
# Resume reconciliation for an existing Receiver
gotk resume receiver main
```
### Options
```
-h, --help help for receiver
```
### Options inherited from parent commands
```
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
-n, --namespace string the namespace scope for this operation (default "gotk-system")
--timeout duration timeout for this operation (default 5m0s)
--verbose print generated objects
```
### SEE ALSO
* [gotk resume](gotk_resume.md) - Resume suspended resources

View File

@@ -24,6 +24,8 @@ The suspend sub-commands suspend the reconciliation of a resource.
### SEE ALSO
* [gotk](gotk.md) - Command line utility for assembling Kubernetes CD pipelines
* [gotk suspend alert](gotk_suspend_alert.md) - Suspend reconciliation of Alert
* [gotk suspend helmrelease](gotk_suspend_helmrelease.md) - Suspend reconciliation of HelmRelease
* [gotk suspend kustomization](gotk_suspend_kustomization.md) - Suspend reconciliation of Kustomization
* [gotk suspend receiver](gotk_suspend_receiver.md) - Suspend reconciliation of Receiver

View File

@@ -0,0 +1,39 @@
## gotk suspend alert
Suspend reconciliation of Alert
### Synopsis
The suspend command disables the reconciliation of a Alert resource.
```
gotk suspend alert [name] [flags]
```
### Examples
```
# Suspend reconciliation for an existing Alert
gotk suspend alert main
```
### Options
```
-h, --help help for alert
```
### Options inherited from parent commands
```
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
-n, --namespace string the namespace scope for this operation (default "gotk-system")
--timeout duration timeout for this operation (default 5m0s)
--verbose print generated objects
```
### SEE ALSO
* [gotk suspend](gotk_suspend.md) - Suspend resources

View File

@@ -0,0 +1,39 @@
## gotk suspend receiver
Suspend reconciliation of Receiver
### Synopsis
The suspend command disables the reconciliation of a Receiver resource.
```
gotk suspend receiver [name] [flags]
```
### Examples
```
# Suspend reconciliation for an existing Receiver
gotk suspend receiver main
```
### Options
```
-h, --help help for receiver
```
### Options inherited from parent commands
```
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
-n, --namespace string the namespace scope for this operation (default "gotk-system")
--timeout duration timeout for this operation (default 5m0s)
--verbose print generated objects
```
### SEE ALSO
* [gotk suspend](gotk_suspend.md) - Suspend resources

View File

Before

Width:  |  Height:  |  Size: 61 KiB

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 289 KiB

View File

@@ -87,6 +87,11 @@ gotk bootstrap github \
--personal
```
!!! hint "ARM"
When deploying to a Kubernetes cluster with ARM architecture,
you can use `--arch=arm` for ARMv7 32-bit container images
and `--arch=arm64` for ARMv8 64-bit container images.
The bootstrap command creates a repository if one doesn't exist and
commits the toolkit components manifests to the default branch at the specified path.
Then it configures the target cluster to synchronize with the specified path inside the repository.
@@ -261,7 +266,6 @@ gotk bootstrap github \
--path=prod-cluster \
--personal
```
Pull the changes locally:
```sh

View File

@@ -225,7 +225,7 @@ the `gotk-system` to start receiving notifications about the Helm
release:
```yaml
apiVersion: notification.toolkit.fluxcd.io/v1alpha1
apiVersion: notification.toolkit.fluxcd.io/v1beta1
kind: Alert
metadata:
generation: 2
@@ -245,7 +245,7 @@ apiVersion: notification.toolkit.fluxcd.io/v1alpha1
namespace: default
```
![helm-controller alerts](../diagrams/helm-controller-alerts.png)
![helm-controller alerts](../_files/helm-controller-alerts.png)
## Configure webhook receivers
@@ -267,7 +267,7 @@ kubectl -n gotk-system create secret generic webhook-token \
When using [Harbor](https://goharbor.io/) as your Helm repository, you can define a receiver with:
```yaml
apiVersion: notification.toolkit.fluxcd.io/v1alpha1
apiVersion: notification.toolkit.fluxcd.io/v1beta1
kind: Receiver
metadata:
name: helm-podinfo

View File

@@ -345,44 +345,66 @@ Then you can register Helm repositories and create Helm releases:
```sh
gotk create source helm stable \
--interval=1h \
--url=https://kubernetes-charts.storage.googleapis.com
--interval=1h \
--url=https://kubernetes-charts.storage.googleapis.com
gotk create helmrelease sealed-secrets \
--interval=1h \
--release-name=sealed-secrets \
--target-namespace=gotk-system \
--source=HelmRepository/stable \
--chart=sealed-secrets \
--chart-version="1.10.x"
--interval=1h \
--release-name=sealed-secrets \
--target-namespace=gotk-system \
--source=HelmRepository/stable \
--chart=sealed-secrets \
--chart-version="1.10.x"
```
## Monitoring with Prometheus and Grafana
The GitOps Toolkit comes with an optional monitoring stack.
You can install the stack in the `gotk-system` namespace with:
The GitOps Toolkit comes with a monitoring stack composed of:
```yaml
kustomize build github.com/fluxcd/toolkit/manifests/monitoring | kubectl apply -f-
* **Prometheus** server - collects metrics from the toolkit controllers and stores them for 2h
* **Grafana** dashboards - displays the control plane resource usage and reconciliation stats
To install the monitoring stack with `gotk`, first register the toolkit Git repository on your cluster:
```sh
gotk create source git monitoring \
--interval=30m \
--url=https://github.com/fluxcd/toolkit \
--branch=main
```
The monitoring stack is composed of:
Then apply the [manifests/monitoring](https://github.com/fluxcd/toolkit/tree/main/manifests/monitoring)
kustomization:
* Prometheus server - collects metrics from the toolkit controllers and stores them for 2h
* Grafana dashboards - displays the control plane resource usage and reconciliation stats
```sh
gotk create kustomization monitoring \
--interval=1h \
--prune=true \
--source=monitoring \
--path="./manifests/monitoring" \
--health-check="Deployment/prometheus.gotk-system" \
--health-check="Deployment/grafana.gotk-system"
```
You can access Grafana using port forwarding:
```sh
kubectl -n gotk-system port-forward svc/grafana 3000:3000
```
Navigate to [http://localhost:3000/d/gitops-toolkit-control-plane](http://localhost:3000/d/gitops-toolkit-control-plane/gitops-toolkit-control-plane)
for the control plane dashboards:
![](../_files/cp-dashboard-p1.png)
![](../_files/cp-dashboard-p2.png)
If you wish to use your own Prometheus and Grafana instances, then you can import the dashboards from
[GitHub](https://github.com/fluxcd/toolkit/tree/master/manifests/monitoring/grafana/dashboards).
[GitHub](https://github.com/fluxcd/toolkit/tree/main/manifests/monitoring/grafana/dashboards).
!!! hint
Note that the toolkit controllers expose the `/metrics` endpoint on port `8080`.
When using Prometheus Operator you should create `PodMonitor` objects to configure scraping.
When Prometheus is running outside of the `gotk-system` namespace, you have to create a network policy
that allows traffic on port `8080` from the namespace where Prometheus is deployed.
## Uninstall

View File

@@ -128,7 +128,7 @@ spec:
Kustomization manifest:
```yaml
apiVersion: kustomize.toolkit.fluxcd.io/v1alpha1
apiVersion: kustomize.toolkit.fluxcd.io/v1beta1
kind: Kustomization
metadata:
name: my-secrets

View File

@@ -33,7 +33,7 @@ it can be a Slack, Microsoft Teams, Discord or Rocket webhook URL.
Create a notification provider for Slack by referencing the above secret:
```yaml
apiVersion: notification.toolkit.fluxcd.io/v1alpha1
apiVersion: notification.toolkit.fluxcd.io/v1beta1
kind: Provider
metadata:
name: slack
@@ -57,7 +57,7 @@ Elasticsearch, CloudWatch, Stackdriver, etc.
Create an alert definition for all repositories and kustomizations:
```yaml
apiVersion: notification.toolkit.fluxcd.io/v1alpha1
apiVersion: notification.toolkit.fluxcd.io/v1beta1
kind: Alert
metadata:
name: on-call-webapp
@@ -92,7 +92,7 @@ encountered during the reconciliation process.
This includes kustomize build and validation errors,
apply errors and health check failures.
![error alert](../diagrams/slack-error-alert.png)
![error alert](../_files/slack-error-alert.png)
When the verbosity is set to `info`, the controller will alert if:
@@ -101,7 +101,7 @@ When the verbosity is set to `info`, the controller will alert if:
* a dependency is delaying the execution
* an error occurs
![info alert](../diagrams/slack-info-alert.png)
![info alert](../_files/slack-info-alert.png)
## Git commit status
@@ -111,8 +111,8 @@ commit status works, refer to the [GitHub](https://docs.github.com/en/github/col
or [GitLab](https://docs.gitlab.com/ee/api/commits.html) documentation.
The first image is an example of how it may look like in GitHub while the one below is an example for GitLab.
![github commit status](../diagrams/github-commit-status.png)
![gitlab commit status](../diagrams/gitlab-commit-status.png)
![github commit status](../_files/github-commit-status.png)
![gitlab commit status](../_files/gitlab-commit-status.png)
Currently the provider will only work with Alerts for Kustomization resources as the events have to be linked with a
specific git commit. Any other event that does not contain a commit reference will be ignored by the provider.
@@ -137,7 +137,7 @@ Creating a git provider is very similar to creating other types of providers.
The only caveat being that the provider address needs to point to the same
git repository as the Kustomization resource refers to.
```yaml
apiVersion: notification.toolkit.fluxcd.io/v1alpha1
apiVersion: notification.toolkit.fluxcd.io/v1beta1
kind: Provider
metadata:
name: podinfo
@@ -149,7 +149,7 @@ spec:
secretRef:
name: github
---
apiVersion: notification.toolkit.fluxcd.io/v1alpha1
apiVersion: notification.toolkit.fluxcd.io/v1beta1
kind: Alert
metadata:
name: podinfo

View File

@@ -89,7 +89,7 @@ kubectl -n gotk-system create secret generic webhook-token \
Create a receiver for GitHub and specify the `GitRepository` object:
```yaml
apiVersion: notification.toolkit.fluxcd.io/v1alpha1
apiVersion: notification.toolkit.fluxcd.io/v1beta1
kind: Receiver
metadata:
name: webapp

View File

@@ -15,7 +15,7 @@ events and are responsible for the reconciliation of their designated API object
the toolkit is in an active experimentation phase.
If you wish to take part in this quest please reach out to us on Slack or GitHub.
![overview](diagrams/gotk-feature.png)
![overview](diagrams/gitops-toolkit.png)
Target features:
@@ -50,13 +50,16 @@ Components:
- [Alert CRD](components/notification/alert.md)
- [Receiver CRD](components/notification/receiver.md)
To get started with the toolkit please follow this [guide](get-started/index.md).
## Get Started
!!!hint "Get started with the GitOps Toolkit!"
Following this [guide](get-started/index.md) will just take a couple of minutes to complete: After installing the `gotk` binary and running a couple of very simple commands, you will have a GitOps workflow setup which involves a staging and a production cluster.
## Community
The GitOps Toolkit is always looking for new contributors and there are a multitude of ways to get involved. Depending on what you want to do, some of the following bits might be your first steps:
- Join our upcoming dev meetings ([meeting access and agenda](https://docs.google.com/document/d/1l_M0om0qUEN_NNiGgpqJ2tvsF2iioHkaARDeh6b70B0/view)
- Join our upcoming dev meetings ([meeting access and agenda](https://docs.google.com/document/d/1l_M0om0qUEN_NNiGgpqJ2tvsF2iioHkaARDeh6b70B0/view))
- Talk to us in the #flux channel on [CNCF Slack](https://slack.cncf.io/)
- Join the [planning discussions](https://github.com/fluxcd/toolkit/discussions)
- And if you are completely new to the GitOps Toolkit, take a look at our [Get Started guide](get-started/index.md) and give us feedback

9
go.mod
View File

@@ -4,14 +4,15 @@ go 1.15
require (
github.com/blang/semver/v4 v4.0.0
github.com/fluxcd/helm-controller/api v0.1.1
github.com/fluxcd/kustomize-controller/api v0.1.0
github.com/fluxcd/helm-controller/api v0.1.2
github.com/fluxcd/kustomize-controller/api v0.1.1
github.com/fluxcd/notification-controller/api v0.1.1
github.com/fluxcd/pkg/apis/meta v0.0.2
github.com/fluxcd/pkg/git v0.0.7
github.com/fluxcd/pkg/runtime v0.0.6
github.com/fluxcd/pkg/runtime v0.1.0
github.com/fluxcd/pkg/ssh v0.0.5
github.com/fluxcd/pkg/untar v0.0.5
github.com/fluxcd/source-controller/api v0.1.0
github.com/fluxcd/source-controller/api v0.1.1
github.com/manifoldco/promptui v0.7.0
github.com/olekukonko/tablewriter v0.0.4
github.com/spf13/cobra v1.0.0

20
go.sum
View File

@@ -111,22 +111,24 @@ github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLi
github.com/evanphx/json-patch v4.9.0+incompatible h1:kLcOMZeuLAJvL2BPWLMIj5oaZQobrkAqrL+WFZwQses=
github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fluxcd/helm-controller/api v0.1.1 h1:iKskkLGRYRi5hiZg/+Rn+rpneGPayGQPnmilM3bok44=
github.com/fluxcd/helm-controller/api v0.1.1/go.mod h1:orwdS+iYGcM8BReUQfIb5CJ+jiFdlKmnLnzp6K3FK2U=
github.com/fluxcd/kustomize-controller/api v0.1.0 h1:dPowX408q0jO7wnWBj5Dglc22euAQBLxDhPS8XHlLM0=
github.com/fluxcd/kustomize-controller/api v0.1.0/go.mod h1:upR7/OzX/wXJlKgiBLUn7ez4XG4Lo5edep2WKSx0u7c=
github.com/fluxcd/helm-controller/api v0.1.2 h1:gBky+nMpDaUT8mhLSaRkHEWczOvLR/JT6L5iRhu4CIs=
github.com/fluxcd/helm-controller/api v0.1.2/go.mod h1:eMkEzQrgDnOFa/iUey4VVjdqmPJFwcWb+3SFPDX9lJ0=
github.com/fluxcd/kustomize-controller/api v0.1.1 h1:hg9koO2YD5VLetwT/Xsaq4MWJ5uXKdjKhx9xDFOJxmo=
github.com/fluxcd/kustomize-controller/api v0.1.1/go.mod h1:84YzQnJ2DShfIE842HYHqB48i0vhpZMJ9XQsdgOEkfM=
github.com/fluxcd/notification-controller/api v0.1.1 h1:tu6+bi28vfHoSp2MUD9h42SIvqY+YtEwS9toH9k7cRA=
github.com/fluxcd/notification-controller/api v0.1.1/go.mod h1:w1gILYTSqt3dFMYRmCihA/K84yDBfIkL5m5dcbaUyUY=
github.com/fluxcd/pkg/apis/meta v0.0.2 h1:kyA4Y0IzNjf1joBOnFqpWG7aNDHvtLExZcaHQM7qhRI=
github.com/fluxcd/pkg/apis/meta v0.0.2/go.mod h1:nCNps5JJOcEQr3MNDmZqI4o0chjePSUYL6Q2ktDtotU=
github.com/fluxcd/pkg/git v0.0.7 h1:tFSYPy7tcIYfOt8H5EUERXIRz7fk0id302oQZde1NtU=
github.com/fluxcd/pkg/git v0.0.7/go.mod h1:5Vu92x6Q3CpxDUllmB69kAkVY5jAtPpXcY2TSZ/oCJI=
github.com/fluxcd/pkg/runtime v0.0.6 h1:m7qwr2wRePs1vzVlM0Y88vitXSsv1lb3QCJflRpa3qQ=
github.com/fluxcd/pkg/runtime v0.0.6/go.mod h1:iLjncjktQVpqpb1NsY2fW+UYDFOtVyt+yJrxqrrK8A0=
github.com/fluxcd/pkg/runtime v0.1.0 h1:mCLj5GlQZqWtK3tvtZTmfgFOLsTUY1iqg3CmEyS1nRs=
github.com/fluxcd/pkg/runtime v0.1.0/go.mod h1:OXkrYtDLw3GhclbzvnzfSktUyxRmC3FFhXj0tVVaIX8=
github.com/fluxcd/pkg/ssh v0.0.5 h1:rnbFZ7voy2JBlUfMbfyqArX2FYaLNpDhccGFC3qW83A=
github.com/fluxcd/pkg/ssh v0.0.5/go.mod h1:7jXPdXZpc0ttMNz2kD9QuMi3RNn/e0DOFbj0Tij/+Hs=
github.com/fluxcd/pkg/untar v0.0.5 h1:UGI3Ch1UIEIaqQvMicmImL1s9npQa64DJ/ozqHKB7gk=
github.com/fluxcd/pkg/untar v0.0.5/go.mod h1:O6V9+rtl8c1mHBafgqFlJN6zkF1HS5SSYn7RpQJ/nfw=
github.com/fluxcd/source-controller/api v0.1.0 h1:ky3gMs3mnkDl6ClX+7uT2BNxU+sLzW/6a8B/M1KfySw=
github.com/fluxcd/source-controller/api v0.1.0/go.mod h1:1ac/vj49YVPKF+xBHTo/9pfFj64TcLc1RLaxi4MwVEM=
github.com/fluxcd/source-controller/api v0.1.1 h1:BYxl9qc8pCx3/Bn1885TlkJPwvXqz+rAL9mzpnCrj9A=
github.com/fluxcd/source-controller/api v0.1.1/go.mod h1:1ac/vj49YVPKF+xBHTo/9pfFj64TcLc1RLaxi4MwVEM=
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/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
@@ -307,6 +309,8 @@ github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxC
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
github.com/hashicorp/go-retryablehttp v0.6.4 h1:BbgctKO892xEyOXnGiaAwIoSq1QZ/SS4AhjoAh9DnfY=
github.com/hashicorp/go-retryablehttp v0.6.4/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
github.com/hashicorp/go-retryablehttp v0.6.7 h1:8/CAEZt/+F7kR7GevNHulKkUjLht3CPmn7egmhieNKo=
github.com/hashicorp/go-retryablehttp v0.6.7/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
github.com/hashicorp/go-safetemp v1.0.0 h1:2HR189eFNrjHQyENnQMMpCiBAsRxzbTMIgBhEyExpmo=
github.com/hashicorp/go-safetemp v1.0.0/go.mod h1:oaerMy3BhqiTbVye6QuFhFtIceqFoDHxNAB65b+Rj1I=
github.com/hashicorp/go-version v1.1.0 h1:bPIoEKD27tNdebFGGxxYwcL4nepeY4j1QP23PFRGzg0=

View File

@@ -1,8 +1,8 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- https://github.com/fluxcd/helm-controller/archive/v0.1.1.zip//helm-controller-0.1.1/config/crd
- https://github.com/fluxcd/helm-controller/archive/v0.1.1.zip//helm-controller-0.1.1/config/manager
- https://github.com/fluxcd/helm-controller/archive/v0.1.2.zip//helm-controller-0.1.2/config/crd
- https://github.com/fluxcd/helm-controller/archive/v0.1.2.zip//helm-controller-0.1.2/config/manager
patchesJson6902:
- target:
group: apps

View File

@@ -1,8 +1,8 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- https://github.com/fluxcd/kustomize-controller/archive/v0.1.0.zip//kustomize-controller-0.1.0/config/crd
- https://github.com/fluxcd/kustomize-controller/archive/v0.1.0.zip//kustomize-controller-0.1.0/config/manager
- https://github.com/fluxcd/kustomize-controller/archive/v0.1.1.zip//kustomize-controller-0.1.1/config/crd
- https://github.com/fluxcd/kustomize-controller/archive/v0.1.1.zip//kustomize-controller-0.1.1/config/manager
patchesJson6902:
- target:
group: apps

View File

@@ -1,5 +1,5 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- https://github.com/fluxcd/notification-controller/archive/v0.1.0.zip//notification-controller-0.1.0/config/crd
- https://github.com/fluxcd/notification-controller/archive/v0.1.0.zip//notification-controller-0.1.0/config/manager
- https://github.com/fluxcd/notification-controller/archive/v0.1.1.zip//notification-controller-0.1.1/config/crd
- https://github.com/fluxcd/notification-controller/archive/v0.1.1.zip//notification-controller-0.1.1/config/manager

View File

@@ -1,8 +1,8 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- https://github.com/fluxcd/source-controller/archive/v0.1.0.zip//source-controller-0.1.0/config/crd
- https://github.com/fluxcd/source-controller/archive/v0.1.0.zip//source-controller-0.1.0/config/manager
- https://github.com/fluxcd/source-controller/archive/v0.1.1.zip//source-controller-0.1.1/config/crd
- https://github.com/fluxcd/source-controller/archive/v0.1.1.zip//source-controller-0.1.1/config/manager
patchesJson6902:
- target:
group: apps

View File

@@ -19,7 +19,7 @@
"links": [],
"panels": [
{
"datasource": null,
"datasource": "${DS_PROMETHEUS}",
"description": "",
"fieldConfig": {
"defaults": {
@@ -79,7 +79,7 @@
"type": "stat"
},
{
"datasource": null,
"datasource": "${DS_PROMETHEUS}",
"description": "",
"fieldConfig": {
"defaults": {
@@ -143,7 +143,7 @@
"type": "stat"
},
{
"datasource": null,
"datasource": "${DS_PROMETHEUS}",
"description": "",
"fieldConfig": {
"defaults": {
@@ -204,7 +204,7 @@
"type": "gauge"
},
{
"datasource": null,
"datasource": "${DS_PROMETHEUS}",
"description": "",
"fieldConfig": {
"defaults": {
@@ -269,7 +269,7 @@
},
{
"collapsed": false,
"datasource": null,
"datasource": "${DS_PROMETHEUS}",
"gridPos": {
"h": 1,
"w": 24,
@@ -286,7 +286,7 @@
"bars": false,
"dashLength": 10,
"dashes": false,
"datasource": null,
"datasource": "${DS_PROMETHEUS}",
"description": "",
"fieldConfig": {
"defaults": {
@@ -397,7 +397,7 @@
"bars": false,
"dashLength": 10,
"dashes": false,
"datasource": null,
"datasource": "${DS_PROMETHEUS}",
"decimals": null,
"description": "",
"fieldConfig": {
@@ -504,7 +504,7 @@
"bars": false,
"dashLength": 10,
"dashes": false,
"datasource": null,
"datasource": "${DS_PROMETHEUS}",
"fieldConfig": {
"defaults": {
"custom": {}
@@ -599,7 +599,7 @@
"bars": false,
"dashLength": 10,
"dashes": false,
"datasource": null,
"datasource": "${DS_PROMETHEUS}",
"fieldConfig": {
"defaults": {
"custom": {}
@@ -693,7 +693,7 @@
},
{
"collapsed": false,
"datasource": null,
"datasource": "${DS_PROMETHEUS}",
"gridPos": {
"h": 1,
"w": 24,
@@ -710,7 +710,7 @@
"bars": false,
"dashLength": 10,
"dashes": false,
"datasource": null,
"datasource": "${DS_PROMETHEUS}",
"fieldConfig": {
"defaults": {
"custom": {}
@@ -807,7 +807,7 @@
"bars": true,
"dashLength": 10,
"dashes": false,
"datasource": null,
"datasource": "${DS_PROMETHEUS}",
"decimals": 2,
"description": "",
"fieldConfig": {
@@ -913,7 +913,7 @@
"bars": true,
"dashLength": 10,
"dashes": false,
"datasource": null,
"datasource": "${DS_PROMETHEUS}",
"decimals": 2,
"description": "",
"fieldConfig": {
@@ -1016,7 +1016,7 @@
},
{
"collapsed": false,
"datasource": null,
"datasource": "${DS_PROMETHEUS}",
"gridPos": {
"h": 1,
"w": 24,
@@ -1033,7 +1033,7 @@
"bars": false,
"dashLength": 10,
"dashes": false,
"datasource": null,
"datasource": "${DS_PROMETHEUS}",
"fieldConfig": {
"defaults": {
"custom": {}
@@ -1144,7 +1144,7 @@
"bars": true,
"dashLength": 10,
"dashes": false,
"datasource": null,
"datasource": "${DS_PROMETHEUS}",
"decimals": 2,
"description": "",
"fieldConfig": {
@@ -1250,7 +1250,7 @@
"bars": true,
"dashLength": 10,
"dashes": false,
"datasource": null,
"datasource": "${DS_PROMETHEUS}",
"decimals": 2,
"description": "",
"fieldConfig": {
@@ -1360,6 +1360,24 @@
],
"templating": {
"list": [
{
"current": {
"selected": false,
"text": "Prometheus",
"value": "Prometheus"
},
"hide": 2,
"includeAll": false,
"label": null,
"multi": false,
"name": "DS_PROMETHEUS",
"options": [],
"query": "prometheus",
"refresh": 1,
"regex": "",
"skipUrlSync": false,
"type": "datasource"
},
{
"allValue": null,
"current": {
@@ -1367,7 +1385,7 @@
"text": "gotk-system",
"value": "gotk-system"
},
"datasource": "prometheus",
"datasource": "${DS_PROMETHEUS}",
"definition": "workqueue_work_duration_seconds_count",
"hide": 0,
"includeAll": false,

View File

@@ -18,7 +18,7 @@ spec:
spec:
containers:
- name: grafana
image: "grafana/grafana:7.1.1"
image: "grafana/grafana:7.2.1"
imagePullPolicy: IfNotPresent
ports:
- name: http
@@ -33,8 +33,8 @@ spec:
value: "true"
- name: GF_AUTH_ANONYMOUS_ORG_ROLE
value: Admin
- name: GF_DEFAULT_THEME
value: "Light"
- name: GF_USERS_DEFAULT_THEME
value: "light"
volumeMounts:
- name: grafana
mountPath: /var/lib/grafana

View File

@@ -19,7 +19,7 @@ spec:
serviceAccountName: prometheus
containers:
- name: prometheus
image: prom/prometheus:v2.20.0
image: prom/prometheus:v2.21.0
imagePullPolicy: IfNotPresent
args:
- '--storage.tsdb.retention=2h'

View File

@@ -0,0 +1,14 @@
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-scraping
spec:
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector: {}
ports:
- protocol: TCP
port: 8080
podSelector: {}

View File

@@ -0,0 +1,13 @@
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-webhooks
spec:
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector: {}
podSelector:
matchLabels:
app: notification-controller

View File

@@ -3,9 +3,9 @@ kind: NetworkPolicy
metadata:
name: deny-ingress
spec:
podSelector: {}
policyTypes:
- Ingress
ingress:
- from:
- podSelector: {}
podSelector: {}

View File

@@ -2,3 +2,5 @@ apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- deny-ingress.yaml
- allow-scraping.yaml
- allow-webhooks.yaml

View File

@@ -87,6 +87,9 @@ nav:
- Create source git: cmd/gotk_create_source_git.md
- Create source helm: cmd/gotk_create_source_helm.md
- Create source bucket: cmd/gotk_create_source_bucket.md
- Create alert provider: cmd/gotk_create_alert-provider.md
- Create alert: cmd/gotk_create_alert.md
- Create receiver: cmd/gotk_create_receiver.md
#- Create tenant: cmd/gotk_create_tenant.md
- Delete: cmd/gotk_delete.md
- Delete kustomization: cmd/gotk_delete_kustomization.md
@@ -102,6 +105,9 @@ nav:
- Export source git: cmd/gotk_export_source_git.md
- Export source helm: cmd/gotk_export_source_helm.md
- Export source bucket: cmd/gotk_export_source_bucket.md
- Export alert provider: cmd/gotk_export_alert-provider.md
- Export alert: cmd/gotk_export_alert.md
- Export receiver: cmd/gotk_export_receiver.md
- Get: cmd/gotk_get.md
- Get kustomizations: cmd/gotk_get_kustomizations.md
- Get helmreleases: cmd/gotk_get_helmreleases.md
@@ -109,13 +115,22 @@ nav:
- Get sources git: cmd/gotk_get_sources_git.md
- Get sources helm: cmd/gotk_get_sources_helm.md
- Get sources bucket: cmd/gotk_get_sources_bucket.md
- Get alert provider: cmd/gotk_get_alert-provider.md
- Get alert: cmd/gotk_get_alert.md
- Get receiver: cmd/gotk_get_receiver.md
- Install: cmd/gotk_install.md
- Resume: cmd/gotk_resume.md
- Resume kustomization: cmd/gotk_resume_kustomization.md
- Resume helmrelease: cmd/gotk_resume_helmrelease.md
- Resume alert provider: cmd/gotk_resume_alert-provider.md
- Resume alert: cmd/gotk_resume_alert.md
- Resume receiver: cmd/gotk_resume_receiver.md
- Suspend: cmd/gotk_suspend.md
- Suspend kustomization: cmd/gotk_suspend_kustomization.md
- Suspend helmrelease: cmd/gotk_suspend_helmrelease.md
- Suspend alert provider: cmd/gotk_suspend_alert-provider.md
- Suspend alert: cmd/gotk_suspend_alert.md
- Suspend receiver: cmd/gotk_suspend_receiver.md
- Reconcile: cmd/gotk_reconcile.md
- Reconcile kustomization: cmd/gotk_reconcile_kustomization.md
- Reconcile helmrelease: cmd/gotk_reconcile_helmrelease.md

67
pkg/install/install.go Normal file
View File

@@ -0,0 +1,67 @@
/*
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 install
import (
"context"
"fmt"
"io/ioutil"
"os"
"path"
"strings"
)
// Generate returns the install manifests as a multi-doc YAML.
// The manifests are built from a GitHub release or from a
// Kustomize overlay if the supplied Options.BaseURL is a local path.
func Generate(options Options) ([]byte, error) {
ctx, cancel := context.WithTimeout(context.Background(), options.Timeout)
defer cancel()
tmpDir, err := ioutil.TempDir("", options.Namespace)
if err != nil {
return nil, fmt.Errorf("temp dir error: %w", err)
}
defer os.RemoveAll(tmpDir)
output := path.Join(tmpDir, options.ManifestsFile)
if !strings.HasPrefix(options.BaseURL, "http") {
if err := build(options.BaseURL, output); err != nil {
return nil, err
}
} else {
if err := fetch(ctx, options.BaseURL, options.Version, tmpDir); err != nil {
return nil, err
}
if err := generate(tmpDir, options); err != nil {
return nil, err
}
if err := build(tmpDir, output); err != nil {
return nil, err
}
}
content, err := ioutil.ReadFile(output)
if err != nil {
return nil, err
}
return content, nil
}

View File

@@ -0,0 +1,40 @@
/*
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 install
import (
"fmt"
"strings"
"testing"
)
func TestGenerate(t *testing.T) {
opts := MakeDefaultOptions()
output, err := Generate(opts)
if err != nil {
t.Fatal(err)
}
for _, component := range opts.Components {
img := fmt.Sprintf("%s/%s", opts.Registry, component)
if !strings.Contains(string(output), img) {
t.Errorf("component image '%s' not found", img)
}
}
fmt.Println(string(output))
}

125
pkg/install/manifests.go Normal file
View File

@@ -0,0 +1,125 @@
/*
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 install
import (
"context"
"fmt"
"net/http"
"os"
"path"
"path/filepath"
"strings"
"sigs.k8s.io/kustomize/api/filesys"
"sigs.k8s.io/kustomize/api/krusty"
"github.com/fluxcd/pkg/untar"
)
func fetch(ctx context.Context, url, version, dir string) error {
ghURL := fmt.Sprintf("%s/latest/download/manifests.tar.gz", url)
if strings.HasPrefix(version, "v") {
ghURL = fmt.Sprintf("%s/download/%s/manifests.tar.gz", url, version)
}
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 manifests.tar.gz from %s, error: %w", ghURL, err)
}
defer resp.Body.Close()
// check response
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("faild to download manifests.tar.gz from %s, status: %s", ghURL, resp.Status)
}
// extract
if _, err = untar.Untar(resp.Body, dir); err != nil {
return fmt.Errorf("faild to untar manifests.tar.gz from %s, error: %w", ghURL, err)
}
return nil
}
func generate(base string, options Options) error {
if containsItemString(options.Components, options.NotificationController) {
options.EventsAddr = fmt.Sprintf("http://%s/", options.NotificationController)
}
if err := execTemplate(options, namespaceTmpl, path.Join(base, "namespace.yaml")); err != nil {
return fmt.Errorf("generate namespace failed: %w", err)
}
if err := execTemplate(options, labelsTmpl, path.Join(base, "labels.yaml")); err != nil {
return fmt.Errorf("generate labels failed: %w", err)
}
if err := execTemplate(options, nodeSelectorTmpl, path.Join(base, "node-selector.yaml")); err != nil {
return fmt.Errorf("generate node selector failed: %w", err)
}
if err := execTemplate(options, kustomizationTmpl, path.Join(base, "kustomization.yaml")); err != nil {
return fmt.Errorf("generate kustomization failed: %w", err)
}
if err := os.MkdirAll(path.Join(base, "roles"), os.ModePerm); err != nil {
return fmt.Errorf("generate roles failed: %w", err)
}
if err := execTemplate(options, kustomizationRolesTmpl, path.Join(base, "roles/kustomization.yaml")); err != nil {
return fmt.Errorf("generate roles kustomization failed: %w", err)
}
if err := copyFile(filepath.Join(base, "rbac.yaml"), filepath.Join(base, "roles/rbac.yaml")); err != nil {
return fmt.Errorf("generate rbac failed: %w", err)
}
return nil
}
func build(base, output string) error {
kfile := filepath.Join(base, "kustomization.yaml")
fs := filesys.MakeFsOnDisk()
if !fs.Exists(kfile) {
return fmt.Errorf("%s not found", kfile)
}
opt := krusty.MakeDefaultOptions()
k := krusty.MakeKustomizer(fs, opt)
m, err := k.Run(base)
if err != nil {
return err
}
resources, err := m.AsYaml()
if err != nil {
return err
}
if err := fs.WriteFile(output, resources); err != nil {
return err
}
return nil
}

64
pkg/install/options.go Normal file
View File

@@ -0,0 +1,64 @@
/*
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 install
import "time"
type Options struct {
BaseURL string
Version string
Namespace string
Components []string
EventsAddr string
Registry string
ImagePullSecret string
Arch string
WatchAllNamespaces bool
NetworkPolicy bool
LogLevel string
NotificationController string
ManifestsFile string
Timeout time.Duration
}
func MakeDefaultOptions() Options {
return Options{
Version: "latest",
Namespace: "gotk-system",
Components: []string{"source-controller", "kustomize-controller", "helm-controller", "notification-controller"},
EventsAddr: "",
Registry: "ghcr.io/fluxcd",
ImagePullSecret: "",
Arch: "amd64",
WatchAllNamespaces: true,
NetworkPolicy: true,
LogLevel: "info",
BaseURL: "https://github.com/fluxcd/toolkit/releases",
NotificationController: "notification-controller",
ManifestsFile: "toolkit-components.yaml",
Timeout: time.Minute,
}
}
func containsItemString(s []string, e string) bool {
for _, a := range s {
if a == e {
return true
}
}
return false
}

195
pkg/install/templates.go Normal file
View File

@@ -0,0 +1,195 @@
/*
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 install
import (
"bufio"
"bytes"
"io"
"os"
"text/template"
)
var kustomizationTmpl = `---
{{- $eventsAddr := .EventsAddr }}
{{- $watchAllNamespaces := .WatchAllNamespaces }}
{{- $registry := .Registry }}
{{- $arch := .Arch }}
{{- $logLevel := .LogLevel }}
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: {{.Namespace}}
transformers:
- labels.yaml
resources:
- namespace.yaml
{{- if .NetworkPolicy }}
- policies.yaml
{{- end }}
- roles
{{- range .Components }}
- {{.}}.yaml
{{- end }}
patches:
- path: node-selector.yaml
target:
kind: Deployment
patchesJson6902:
{{- range $i, $component := .Components }}
{{- if eq $component "notification-controller" }}
- target:
group: apps
version: v1
kind: Deployment
name: {{$component}}
patch: |-
- op: replace
path: /spec/template/spec/containers/0/args/0
value: --watch-all-namespaces={{$watchAllNamespaces}}
- op: replace
path: /spec/template/spec/containers/0/args/1
value: --log-level={{$logLevel}}
{{- else }}
- target:
group: apps
version: v1
kind: Deployment
name: {{$component}}
patch: |-
- op: replace
path: /spec/template/spec/containers/0/args/0
value: --events-addr={{$eventsAddr}}
- op: replace
path: /spec/template/spec/containers/0/args/1
value: --watch-all-namespaces={{$watchAllNamespaces}}
- op: replace
path: /spec/template/spec/containers/0/args/2
value: --log-level={{$logLevel}}
{{- end }}
{{- end }}
{{- if $registry }}
images:
{{- range $i, $component := .Components }}
- name: fluxcd/{{$component}}
{{- if eq $arch "amd64" }}
newName: {{$registry}}/{{$component}}
{{- else }}
newName: {{$registry}}/{{$component}}-arm64
{{- end }}
{{- end }}
{{- end }}
`
var kustomizationRolesTmpl = `---
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- rbac.yaml
nameSuffix: -{{.Namespace}}
`
var nodeSelectorTmpl = `---
apiVersion: apps/v1
kind: Deployment
metadata:
name: all
spec:
template:
spec:
nodeSelector:
kubernetes.io/arch: {{.Arch}}
kubernetes.io/os: linux
{{- if .ImagePullSecret }}
imagePullSecrets:
- name: {{.ImagePullSecret}}
{{- end }}
`
var labelsTmpl = `---
apiVersion: builtin
kind: LabelTransformer
metadata:
name: labels
labels:
app.kubernetes.io/instance: {{.Namespace}}
app.kubernetes.io/version: "{{.Version}}"
fieldSpecs:
- path: metadata/labels
create: true
`
var namespaceTmpl = `---
apiVersion: v1
kind: Namespace
metadata:
name: {{.Namespace}}
`
func execTemplate(obj interface{}, tmpl, filename string) error {
t, err := template.New("tmpl").Parse(tmpl)
if err != nil {
return err
}
var data bytes.Buffer
writer := bufio.NewWriter(&data)
if err := t.Execute(writer, obj); err != nil {
return err
}
if err := writer.Flush(); err != nil {
return err
}
file, err := os.Create(filename)
if err != nil {
return err
}
defer file.Close()
_, err = io.WriteString(file, data.String())
if err != nil {
return err
}
return file.Sync()
}
func 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()
}