1
0
mirror of synced 2026-03-04 12:16:57 +00:00

Compare commits

..

1 Commits

Author SHA1 Message Date
Max Jonas Werner
3f79b4dd57 [wip] Add audit command
Signed-off-by: Max Jonas Werner <mail@makk.es>
2023-12-01 17:29:31 +01:00
90 changed files with 1028 additions and 1434 deletions

View File

@@ -17,7 +17,7 @@ jobs:
with:
ref: ${{ github.event.pull_request.head.sha }}
- name: Create backport PRs
uses: korthout/backport-action@b982d297e31f500652b2246cf26714796312bd23 # v2.2.0
uses: korthout/backport-action@08bafb375e6e9a9a2b53a744b987e5d81a133191 # v2.1.1
# xref: https://github.com/korthout/backport-action#inputs
with:
# Use token to allow workflows to be triggered for the created PR

View File

@@ -23,7 +23,7 @@ jobs:
- name: Checkout
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Setup Go
uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0
with:
go-version: 1.20.x
cache-dependency-path: |

View File

@@ -32,7 +32,7 @@ jobs:
- name: Checkout
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Setup Go
uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0
with:
go-version: 1.20.x
cache-dependency-path: tests/azure/go.sum
@@ -77,7 +77,7 @@ jobs:
- name: CheckoutD
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Setup Go
uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0
with:
go-version: 1.20.x
cache-dependency-path: tests/integration/go.sum
@@ -92,7 +92,7 @@ jobs:
env:
SOPS_VER: 3.7.1
- name: Authenticate to Azure
uses: Azure/login@de95379fe4dadc2defb305917eaa7e5dde727294 # v1.4.6
uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.6
with:
creds: '{"clientId":"${{ secrets.AZ_ARM_CLIENT_ID }}","clientSecret":"${{ secrets.AZ_ARM_CLIENT_SECRET }}","subscriptionId":"${{ secrets.AZ_ARM_SUBSCRIPTION_ID }}","tenantId":"${{ secrets.AZ_ARM_TENANT_ID }}"}'
- name: Set dynamic variables in .env

View File

@@ -19,7 +19,7 @@ jobs:
- name: Checkout
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Setup Go
uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0
with:
go-version: 1.20.x
cache-dependency-path: |

View File

@@ -31,7 +31,7 @@ jobs:
- name: Checkout
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Setup Go
uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0
with:
go-version: 1.20.x
cache-dependency-path: tests/integration/go.sum
@@ -46,13 +46,13 @@ jobs:
env:
SOPS_VER: 3.7.1
- name: Authenticate to Google Cloud
uses: google-github-actions/auth@67e9c72af6e0492df856527b474995862b7b6591 # v2.0.0
uses: google-github-actions/auth@35b0e87d162680511bf346c299f71c9c5c379033 # v1.1.1
id: 'auth'
with:
credentials_json: '${{ secrets.FLUX2_E2E_GOOGLE_CREDENTIALS }}'
token_format: 'access_token'
- name: Setup gcloud
uses: google-github-actions/setup-gcloud@825196879a077b7efa50db2e88409f44de4635c2 # v2.0.0
uses: google-github-actions/setup-gcloud@e30db14379863a8c79331b04a9969f4c1e225e0b # v1.1.1
- name: Setup QEMU
uses: docker/setup-qemu-action@68827325e0b33c7199eb31dd4e31fbe9023e06e3 # v3.0.0
- name: Setup Docker Buildx

View File

@@ -23,7 +23,7 @@ jobs:
- name: Checkout
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Setup Go
uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0
with:
go-version: 1.20.x
cache-dependency-path: |

View File

@@ -24,7 +24,7 @@ jobs:
- name: Unshallow
run: git fetch --prune --unshallow
- name: Setup Go
uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0
with:
go-version: 1.20.x
cache: false
@@ -34,7 +34,7 @@ jobs:
id: buildx
uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0
- name: Setup Syft
uses: anchore/sbom-action/download-syft@5ecf649a417b8ae17dc8383dc32d46c03f2312df # v0.15.1
uses: anchore/sbom-action/download-syft@78fc58e266e87a38d4194b2137a3d4e9bcaf7ca1 # v0.14.3
- name: Setup Cosign
uses: sigstore/cosign-installer@1fc5bd396d372bee37d608f955b336615edf79c8 # v3.2.0
- name: Setup Kustomize

View File

@@ -19,7 +19,7 @@ jobs:
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Run FOSSA scan and upload build data
uses: fossa-contrib/fossa-action@cdc5065bcdee31a32e47d4585df72d66e8e941c2 # v3.0.0
uses: fossa-contrib/fossa-action@6728dc6fe9a068c648d080c33829ffbe56565023 # v2.0.0
with:
# FOSSA Push-Only API Token
fossa-api-key: 5ee8bf422db1471e0bcf2bcb289185de
@@ -35,7 +35,7 @@ jobs:
- name: Setup Kustomize
uses: fluxcd/pkg/actions/kustomize@main
- name: Setup Go
uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0
with:
go-version: 1.20.x
cache-dependency-path: |
@@ -66,7 +66,7 @@ jobs:
- name: Checkout repository
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Setup Go
uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0
with:
go-version: 1.20.x
cache-dependency-path: |

View File

@@ -20,7 +20,7 @@ jobs:
- name: Check out code
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Setup Go
uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0
with:
go-version: 1.20.x
cache-dependency-path: |

View File

@@ -19,7 +19,7 @@ package main
import (
"sigs.k8s.io/controller-runtime/pkg/client"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta3"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta2"
)
// notificationv1.Alert

View File

@@ -19,7 +19,7 @@ package main
import (
"sigs.k8s.io/controller-runtime/pkg/client"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta3"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta2"
)
// notificationv1.Provider

195
cmd/flux/audit.go Normal file
View File

@@ -0,0 +1,195 @@
/*
Copyright 2023 The Flux authors
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"
"strings"
"github.com/fluxcd/flux2/v2/internal/utils"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
v1 "k8s.io/api/apps/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1"
)
var ctrlChecks = map[string]map[string]bool{
"helm-controller": {
"insecure-kubeconfig-exec": false,
"insecure-kubeconfig-tls": false,
},
"kustomize-controller": {
"insecure-kubeconfig-exec": false,
"insecure-kubeconfig-tls": false,
"no-remote-bases": true,
},
}
var multiTenancyCtrlChecks = map[string]map[string]bool{
"helm-controller": {
"no-cross-namespace-refs": true,
},
"kustomize-controller": {
"no-cross-namespace-refs": true,
},
"notification-controller": {
"no-cross-namespace-refs": true,
},
"image-reflector-controller": {
"no-cross-namespace-refs": true,
},
"image-automation-controller": {
"no-cross-namespace-refs": true,
},
}
var multiTenancyFlag bool
var auditCmd = &cobra.Command{
Use: "audit",
Short: "Audit the Flux installation for security best practices",
Long: withPreviewNote("TBD"),
Example: ` TBD`,
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel()
kubeClient, err := utils.KubeClient(kubeconfigArgs, kubeclientOptions)
if err != nil {
return err
}
logger.Actionf("Starting audit")
for ctrl, checks := range ctrlChecks {
if err := auditController(ctx, kubeClient, ctrl, checks); err != nil {
return fmt.Errorf("failed auditing %s: %w", ctrl, err)
}
}
if err := auditSecretDecryption(ctx, kubeClient); err != nil {
return fmt.Errorf("failed auditing Secret decryption: %w", err)
}
if multiTenancyFlag {
logger.Actionf("Multi-tenancy lock-down")
for ctrl, checks := range multiTenancyCtrlChecks {
if err := auditController(ctx, kubeClient, ctrl, checks); err != nil {
return fmt.Errorf("failed auditing %s for multi-tenancy lock-down: %w", ctrl, err)
}
}
}
return nil
},
}
func auditSecretDecryption(ctx context.Context, c client.Client) error {
var ksl kustomizev1.KustomizationList
if err := c.List(ctx, &ksl); err != nil {
return fmt.Errorf("failed to retrieve Kustomizations: %w", err)
}
success := true
for _, ks := range ksl.Items {
if ks.Status.Inventory == nil {
continue
}
if ks.Spec.Decryption != nil {
continue
}
for _, e := range ks.Status.Inventory.Entries {
parts := strings.Split(e.ID, "_")
if parts[2] == "" && parts[3] == "Secret" {
success = false
logger.Warningf("%s/%s doesn't have Secret decryption configured", ks.Namespace, ks.Name)
}
}
}
if success {
logger.Successf("Secret decryption is configured for all Kustomizations that create Secrets")
}
return nil
}
func auditController(ctx context.Context, c client.Client, name string, flags map[string]bool) error {
hcDeploys, err := getManagerArgs(ctx, c, name)
if err != nil {
return fmt.Errorf("failed to get %s flags: %w", name, err)
}
if len(hcDeploys) == 0 {
logger.Warningf("No %s Deployment found, auditing skipped", name)
} else {
for name, args := range hcDeploys {
for flag, desired := range flags {
hcExec, err := assertBoolFlagValue(args, flag, desired)
if err != nil {
return fmt.Errorf("failed parsing %q args: %w", name, err)
}
if hcExec == desired {
logger.Successf("%s: %s is %t", name, flag, desired)
} else {
logger.Warningf("%s: %s should be %t", name, flag, desired)
}
}
}
}
return nil
}
func getManagerArgs(ctx context.Context, c client.Client, component string) (map[string][]string, error) {
var deploys v1.DeploymentList
if err := c.List(ctx, &deploys, client.MatchingLabels{
"app.kubernetes.io/component": component,
}); err != nil {
return nil, fmt.Errorf("failed to retrieve %s deployments: %w", component, err)
}
res := make(map[string][]string, 0)
for _, deploy := range deploys.Items {
for _, ctr := range deploy.Spec.Template.Spec.Containers {
if ctr.Name == "manager" {
res[deploy.Name] = ctr.Args
}
}
}
return res, nil
}
func assertBoolFlagValue(args []string, flagName string, value bool) (bool, error) {
fs := pflag.NewFlagSet("tmp", pflag.ContinueOnError)
fs.ParseErrorsWhitelist.UnknownFlags = true
f := fs.BoolP(flagName, "", false, "")
if err := fs.Parse(args); err != nil {
return false, fmt.Errorf("failed parsing args: %w", err)
}
return *f, nil
}
func init() {
auditCmd.Flags().BoolVar(&multiTenancyFlag, "multi-tenancy", false, "Enable additional audit checks for multi-tenant clusters.")
rootCmd.AddCommand(auditCmd)
}

View File

@@ -56,7 +56,7 @@ the bootstrap command will perform an upgrade if needed.`,
# Run bootstrap for a public repository on a personal account
flux bootstrap bitbucket-server --owner=<user> --repository=<repository name> --private=false --personal --hostname=<domain> --token-auth --path=clusters/my-cluster
# Run bootstrap for an existing repository with a branch named main
# Run bootstrap for a an existing repository with a branch named main
flux bootstrap bitbucket-server --owner=<project> --username=<user> --repository=<repository name> --branch=main --hostname=<domain> --token-auth --path=clusters/my-cluster`,
RunE: bootstrapBServerCmdRun,
}

View File

@@ -64,7 +64,7 @@ the bootstrap command will perform an upgrade if needed.`,
# Run bootstrap for a private repository hosted on a GitLab server
flux bootstrap gitlab --owner=<group> --repository=<repository name> --hostname=<domain> --token-auth
# Run bootstrap for an existing repository with a branch named main
# Run bootstrap for a an existing repository with a branch named main
flux bootstrap gitlab --owner=<organization> --repository=<repository name> --branch=main --token-auth
# Run bootstrap for a private repository using Deploy Token authentication

View File

@@ -18,7 +18,6 @@ package main
import (
"context"
"fmt"
"os"
"time"
@@ -27,7 +26,6 @@ import (
v1 "k8s.io/api/apps/v1"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/fluxcd/pkg/version"
@@ -82,20 +80,7 @@ func runCheckCmd(cmd *cobra.Command, args []string) error {
fluxCheck()
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel()
cfg, err := utils.KubeConfig(kubeconfigArgs, kubeclientOptions)
if err != nil {
return fmt.Errorf("Kubernetes client initialization failed: %s", err.Error())
}
kubeClient, err := client.New(cfg, client.Options{Scheme: utils.NewScheme()})
if err != nil {
return err
}
if !kubernetesCheck(cfg, kubernetesConstraints) {
if !kubernetesCheck(kubernetesConstraints) {
checkFailed = true
}
@@ -107,18 +92,13 @@ func runCheckCmd(cmd *cobra.Command, args []string) error {
return nil
}
logger.Actionf("checking version in cluster")
if !fluxClusterVersionCheck(ctx, kubeClient) {
checkFailed = true
}
logger.Actionf("checking controllers")
if !componentsCheck(ctx, kubeClient) {
if !componentsCheck() {
checkFailed = true
}
logger.Actionf("checking crds")
if !crdsCheck(ctx, kubeClient) {
if !crdsCheck() {
checkFailed = true
}
@@ -149,11 +129,17 @@ func fluxCheck() {
return
}
if latestSv.GreaterThan(curSv) {
logger.Failuref("flux %s <%s (new CLI version is available, please upgrade)", curSv, latestSv)
logger.Failuref("flux %s <%s (new version is available, please upgrade)", curSv, latestSv)
}
}
func kubernetesCheck(cfg *rest.Config, constraints []string) bool {
func kubernetesCheck(constraints []string) bool {
cfg, err := utils.KubeConfig(kubeconfigArgs, kubeclientOptions)
if err != nil {
logger.Failuref("Kubernetes client initialization failed: %s", err.Error())
return false
}
clientSet, err := kubernetes.NewForConfig(cfg)
if err != nil {
logger.Failuref("Kubernetes client initialization failed: %s", err.Error())
@@ -192,8 +178,21 @@ func kubernetesCheck(cfg *rest.Config, constraints []string) bool {
return true
}
func componentsCheck(ctx context.Context, kubeClient client.Client) bool {
statusChecker, err := status.NewStatusCheckerWithClient(kubeClient, checkArgs.pollInterval, rootArgs.timeout, logger)
func componentsCheck() bool {
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel()
kubeConfig, err := utils.KubeConfig(kubeconfigArgs, kubeclientOptions)
if err != nil {
return false
}
statusChecker, err := status.NewStatusChecker(kubeConfig, checkArgs.pollInterval, rootArgs.timeout, logger)
if err != nil {
return false
}
kubeClient, err := utils.KubeClient(kubeconfigArgs, kubeclientOptions)
if err != nil {
return false
}
@@ -223,7 +222,15 @@ func componentsCheck(ctx context.Context, kubeClient client.Client) bool {
return ok
}
func crdsCheck(ctx context.Context, kubeClient client.Client) bool {
func crdsCheck() bool {
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel()
kubeClient, err := utils.KubeClient(kubeconfigArgs, kubeclientOptions)
if err != nil {
return false
}
ok := true
selector := client.MatchingLabels{manifestgen.PartOfLabelKey: manifestgen.PartOfLabelValue}
var list apiextensionsv1.CustomResourceDefinitionList
@@ -246,17 +253,3 @@ func crdsCheck(ctx context.Context, kubeClient client.Client) bool {
}
return ok
}
func fluxClusterVersionCheck(ctx context.Context, kubeClient client.Client) bool {
clusterInfo, err := getFluxClusterInfo(ctx, kubeClient)
if err != nil {
logger.Failuref("checking failed: %s", err.Error())
return false
}
if clusterInfo.distribution() != "" {
logger.Successf("distribution: %s", clusterInfo.distribution())
}
logger.Successf("bootstrapped: %t", clusterInfo.bootstrapped)
return true
}

View File

@@ -27,8 +27,6 @@ import (
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1"
sourcev1 "github.com/fluxcd/source-controller/api/v1"
"github.com/fluxcd/flux2/v2/pkg/manifestgen"
)
// bootstrapLabels are labels put on a resource by kustomize-controller. These labels on the CRD indicates
@@ -44,8 +42,6 @@ type fluxClusterInfo struct {
bootstrapped bool
// managedBy is the name of the tool being used to manage the installation of Flux.
managedBy string
// partOf indicates which distribution the instance is a part of.
partOf string
// version is the Flux version number in semver format.
version string
}
@@ -72,7 +68,7 @@ func getFluxClusterInfo(ctx context.Context, c client.Client) (fluxClusterInfo,
return info, err
}
info.version = crdMetadata.Labels[manifestgen.VersionLabelKey]
info.version = crdMetadata.Labels["app.kubernetes.io/version"]
var present bool
for _, l := range bootstrapLabels {
@@ -82,15 +78,11 @@ func getFluxClusterInfo(ctx context.Context, c client.Client) (fluxClusterInfo,
info.bootstrapped = true
}
// the `app.kubernetes.io/managed-by` label is not set by flux but might be set by other
// the `app.kubernetes.io` label is not set by flux but might be set by other
// tools used to install Flux e.g Helm.
if manager, ok := crdMetadata.Labels["app.kubernetes.io/managed-by"]; ok {
info.managedBy = manager
}
if partOf, ok := crdMetadata.Labels[manifestgen.PartOfLabelKey]; ok {
info.partOf = partOf
}
return info, nil
}
@@ -113,14 +105,6 @@ func confirmFluxInstallOverride(info fluxClusterInfo) error {
return err
}
func (info fluxClusterInfo) distribution() string {
distribution := info.version
if info.partOf != "" {
distribution = fmt.Sprintf("%s-%s", info.partOf, info.version)
}
return distribution
}
func installManagedByFlux(manager string) bool {
return manager == "" || manager == "flux"
}

View File

@@ -102,17 +102,6 @@ func Test_getFluxClusterInfo(t *testing.T) {
version: "v2.1.0",
},
},
{
name: "CRD with version and part-of labels",
labels: map[string]string{
"app.kubernetes.io/version": "v2.1.0",
"app.kubernetes.io/part-of": "flux",
},
wantInfo: fluxClusterInfo{
version: "v2.1.0",
partOf: "flux",
},
},
}
for _, tt := range tests {

View File

@@ -132,7 +132,7 @@ func (names apiType) upsertAndWait(object upsertWaitable, mutate func() error) e
logger.Waitingf("waiting for %s reconciliation", names.kind)
if err := wait.PollUntilContextTimeout(ctx, rootArgs.pollInterval, rootArgs.timeout, true,
isObjectReadyConditionFunc(kubeClient, namespacedName, object.asClientObject())); err != nil {
isReady(kubeClient, namespacedName, object)); err != nil {
return err
}
logger.Successf("%s reconciliation completed", names.kind)

View File

@@ -22,13 +22,14 @@ import (
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/api/errors"
apimeta "k8s.io/apimachinery/pkg/api/meta"
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/v1"
notificationv1b3 "github.com/fluxcd/notification-controller/api/v1beta3"
notificationv1b2 "github.com/fluxcd/notification-controller/api/v1beta2"
"github.com/fluxcd/pkg/apis/meta"
"github.com/fluxcd/flux2/v2/internal/utils"
@@ -96,13 +97,13 @@ func createAlertCmdRun(cmd *cobra.Command, args []string) error {
logger.Generatef("generating Alert")
}
alert := notificationv1b3.Alert{
alert := notificationv1b2.Alert{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: *kubeconfigArgs.Namespace,
Labels: sourceLabels,
},
Spec: notificationv1b3.AlertSpec{
Spec: notificationv1b2.AlertSpec{
ProviderRef: meta.LocalObjectReference{
Name: alertArgs.providerRef,
},
@@ -132,7 +133,7 @@ func createAlertCmdRun(cmd *cobra.Command, args []string) error {
logger.Waitingf("waiting for Alert reconciliation")
if err := wait.PollUntilContextTimeout(ctx, rootArgs.pollInterval, rootArgs.timeout, true,
isStaticObjectReadyConditionFunc(kubeClient, namespacedName, &alert)); err != nil {
isAlertReady(kubeClient, namespacedName, &alert)); err != nil {
return err
}
logger.Successf("Alert %s is ready", name)
@@ -140,13 +141,13 @@ func createAlertCmdRun(cmd *cobra.Command, args []string) error {
}
func upsertAlert(ctx context.Context, kubeClient client.Client,
alert *notificationv1b3.Alert) (types.NamespacedName, error) {
alert *notificationv1b2.Alert) (types.NamespacedName, error) {
namespacedName := types.NamespacedName{
Namespace: alert.GetNamespace(),
Name: alert.GetName(),
}
var existing notificationv1b3.Alert
var existing notificationv1b2.Alert
err := kubeClient.Get(ctx, namespacedName, &existing)
if err != nil {
if errors.IsNotFound(err) {
@@ -169,3 +170,22 @@ func upsertAlert(ctx context.Context, kubeClient client.Client,
logger.Successf("Alert updated")
return namespacedName, nil
}
func isAlertReady(kubeClient client.Client, namespacedName types.NamespacedName, alert *notificationv1b2.Alert) wait.ConditionWithContextFunc {
return func(ctx context.Context) (bool, error) {
err := kubeClient.Get(ctx, namespacedName, alert)
if err != nil {
return false, err
}
if c := apimeta.FindStatusCondition(alert.Status.Conditions, meta.ReadyCondition); c != nil {
switch c.Status {
case metav1.ConditionTrue:
return true, nil
case metav1.ConditionFalse:
return false, fmt.Errorf(c.Message)
}
}
return false, nil
}
}

View File

@@ -22,12 +22,13 @@ import (
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/api/errors"
apimeta "k8s.io/apimachinery/pkg/api/meta"
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/v1beta3"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta2"
"github.com/fluxcd/pkg/apis/meta"
"github.com/fluxcd/flux2/v2/internal/utils"
@@ -127,7 +128,7 @@ func createAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
logger.Waitingf("waiting for Provider reconciliation")
if err := wait.PollUntilContextTimeout(ctx, rootArgs.pollInterval, rootArgs.timeout, true,
isStaticObjectReadyConditionFunc(kubeClient, namespacedName, &provider)); err != nil {
isAlertProviderReady(kubeClient, namespacedName, &provider)); err != nil {
return err
}
@@ -166,3 +167,22 @@ func upsertAlertProvider(ctx context.Context, kubeClient client.Client,
logger.Successf("Provider updated")
return namespacedName, nil
}
func isAlertProviderReady(kubeClient client.Client, namespacedName types.NamespacedName, provider *notificationv1.Provider) wait.ConditionWithContextFunc {
return func(ctx context.Context) (bool, error) {
err := kubeClient.Get(ctx, namespacedName, provider)
if err != nil {
return false, err
}
if c := apimeta.FindStatusCondition(provider.Status.Conditions, meta.ReadyCondition); c != nil {
switch c.Status {
case metav1.ConditionTrue:
return true, nil
case metav1.ConditionFalse:
return false, fmt.Errorf(c.Message)
}
}
return false, nil
}
}

View File

@@ -32,6 +32,7 @@ import (
"github.com/spf13/cobra"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"k8s.io/apimachinery/pkg/api/errors"
apimeta "k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait"
@@ -303,7 +304,7 @@ func createHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
logger.Waitingf("waiting for HelmRelease reconciliation")
if err := wait.PollUntilContextTimeout(ctx, rootArgs.pollInterval, rootArgs.timeout, true,
isObjectReadyConditionFunc(kubeClient, namespacedName, &helmRelease)); err != nil {
isHelmReleaseReady(kubeClient, namespacedName, &helmRelease)); err != nil {
return err
}
logger.Successf("HelmRelease %s is ready", name)
@@ -343,6 +344,22 @@ func upsertHelmRelease(ctx context.Context, kubeClient client.Client,
return namespacedName, nil
}
func isHelmReleaseReady(kubeClient client.Client, namespacedName types.NamespacedName, helmRelease *helmv2.HelmRelease) wait.ConditionWithContextFunc {
return func(ctx context.Context) (bool, error) {
err := kubeClient.Get(ctx, namespacedName, helmRelease)
if err != nil {
return false, err
}
// Confirm the state we are observing is for the current generation
if helmRelease.Generation != helmRelease.Status.ObservedGeneration {
return false, nil
}
return apimeta.IsStatusConditionTrue(helmRelease.Status.Conditions, meta.ReadyCondition), nil
}
}
func validateStrategy(input string) bool {
allowedStrategy := []string{"Revision", "ChartVersion"}

View File

@@ -24,6 +24,7 @@ import (
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/api/errors"
apimeta "k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait"
@@ -263,7 +264,7 @@ func createKsCmdRun(cmd *cobra.Command, args []string) error {
logger.Waitingf("waiting for Kustomization reconciliation")
if err := wait.PollUntilContextTimeout(ctx, rootArgs.pollInterval, rootArgs.timeout, true,
isObjectReadyConditionFunc(kubeClient, namespacedName, &kustomization)); err != nil {
isKustomizationReady(kubeClient, namespacedName, &kustomization)); err != nil {
return err
}
logger.Successf("Kustomization %s is ready", name)
@@ -302,3 +303,27 @@ func upsertKustomization(ctx context.Context, kubeClient client.Client,
logger.Successf("Kustomization updated")
return namespacedName, nil
}
func isKustomizationReady(kubeClient client.Client, namespacedName types.NamespacedName, kustomization *kustomizev1.Kustomization) wait.ConditionWithContextFunc {
return func(ctx context.Context) (bool, error) {
err := kubeClient.Get(ctx, namespacedName, kustomization)
if err != nil {
return false, err
}
// Confirm the state we are observing is for the current generation
if kustomization.Generation != kustomization.Status.ObservedGeneration {
return false, nil
}
if c := apimeta.FindStatusCondition(kustomization.Status.Conditions, meta.ReadyCondition); c != nil {
switch c.Status {
case metav1.ConditionTrue:
return true, nil
case metav1.ConditionFalse:
return false, fmt.Errorf(c.Message)
}
}
return false, nil
}
}

View File

@@ -22,6 +22,7 @@ import (
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/api/errors"
apimeta "k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait"
@@ -139,7 +140,7 @@ func createReceiverCmdRun(cmd *cobra.Command, args []string) error {
logger.Waitingf("waiting for Receiver reconciliation")
if err := wait.PollUntilContextTimeout(ctx, rootArgs.pollInterval, rootArgs.timeout, true,
isObjectReadyConditionFunc(kubeClient, namespacedName, &receiver)); err != nil {
isReceiverReady(kubeClient, namespacedName, &receiver)); err != nil {
return err
}
logger.Successf("Receiver %s is ready", name)
@@ -178,3 +179,22 @@ func upsertReceiver(ctx context.Context, kubeClient client.Client,
logger.Successf("Receiver updated")
return namespacedName, nil
}
func isReceiverReady(kubeClient client.Client, namespacedName types.NamespacedName, receiver *notificationv1.Receiver) wait.ConditionWithContextFunc {
return func(ctx context.Context) (bool, error) {
err := kubeClient.Get(ctx, namespacedName, receiver)
if err != nil {
return false, err
}
if c := apimeta.FindStatusCondition(receiver.Status.Conditions, meta.ReadyCondition); c != nil {
switch c.Status {
case metav1.ConditionTrue:
return true, nil
case metav1.ConditionFalse:
return false, fmt.Errorf(c.Message)
}
}
return false, nil
}
}

View File

@@ -31,6 +31,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/fluxcd/pkg/apis/meta"
"github.com/fluxcd/pkg/runtime/conditions"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta2"
@@ -204,7 +205,7 @@ func createSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
logger.Waitingf("waiting for Bucket source reconciliation")
if err := wait.PollUntilContextTimeout(ctx, rootArgs.pollInterval, rootArgs.timeout, true,
isObjectReadyConditionFunc(kubeClient, namespacedName, bucket)); err != nil {
isBucketReady(kubeClient, namespacedName, bucket)); err != nil {
return err
}
logger.Successf("Bucket source reconciliation completed")
@@ -246,3 +247,29 @@ func upsertBucket(ctx context.Context, kubeClient client.Client,
logger.Successf("Bucket source updated")
return namespacedName, nil
}
func isBucketReady(kubeClient client.Client, namespacedName types.NamespacedName, bucket *sourcev1.Bucket) wait.ConditionWithContextFunc {
return func(ctx context.Context) (bool, error) {
err := kubeClient.Get(ctx, namespacedName, bucket)
if err != nil {
return false, err
}
if c := conditions.Get(bucket, meta.ReadyCondition); c != nil {
// Confirm the Ready condition we are observing is for the
// current generation
if c.ObservedGeneration != bucket.GetGeneration() {
return false, nil
}
// Further check the Status
switch c.Status {
case metav1.ConditionTrue:
return true, nil
case metav1.ConditionFalse:
return false, fmt.Errorf(c.Message)
}
}
return false, nil
}
}

View File

@@ -35,6 +35,7 @@ import (
"sigs.k8s.io/yaml"
"github.com/fluxcd/pkg/apis/meta"
"github.com/fluxcd/pkg/runtime/conditions"
sourcev1 "github.com/fluxcd/source-controller/api/v1"
@@ -325,7 +326,7 @@ func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
logger.Waitingf("waiting for GitRepository source reconciliation")
if err := wait.PollUntilContextTimeout(ctx, rootArgs.pollInterval, rootArgs.timeout, true,
isObjectReadyConditionFunc(kubeClient, namespacedName, &gitRepository)); err != nil {
isGitRepositoryReady(kubeClient, namespacedName, &gitRepository)); err != nil {
return err
}
logger.Successf("GitRepository source reconciliation completed")
@@ -367,3 +368,29 @@ func upsertGitRepository(ctx context.Context, kubeClient client.Client,
logger.Successf("GitRepository source updated")
return namespacedName, nil
}
func isGitRepositoryReady(kubeClient client.Client, namespacedName types.NamespacedName, gitRepository *sourcev1.GitRepository) wait.ConditionWithContextFunc {
return func(ctx context.Context) (bool, error) {
err := kubeClient.Get(ctx, namespacedName, gitRepository)
if err != nil {
return false, err
}
if c := conditions.Get(gitRepository, meta.ReadyCondition); c != nil {
// Confirm the Ready condition we are observing is for the
// current generation
if c.ObservedGeneration != gitRepository.GetGeneration() {
return false, nil
}
// Further check the Status
switch c.Status {
case metav1.ConditionTrue:
return true, nil
case metav1.ConditionFalse:
return false, fmt.Errorf(c.Message)
}
}
return false, nil
}
}

View File

@@ -181,21 +181,12 @@ func TestCreateSourceGit(t *testing.T) {
Time: time.Now(),
},
}
repo.Status.ObservedGeneration = repo.GetGeneration()
},
}, {
"Failed",
command,
assertError("failed message"),
func(repo *sourcev1.GitRepository) {
stalledCondition := metav1.Condition{
Type: meta.StalledCondition,
Status: metav1.ConditionTrue,
Reason: sourcev1.URLInvalidReason,
Message: "failed message",
ObservedGeneration: repo.GetGeneration(),
}
apimeta.SetStatusCondition(&repo.Status.Conditions, stalledCondition)
newCondition := metav1.Condition{
Type: meta.ReadyCondition,
Status: metav1.ConditionFalse,
@@ -204,7 +195,6 @@ func TestCreateSourceGit(t *testing.T) {
ObservedGeneration: repo.GetGeneration(),
}
apimeta.SetStatusCondition(&repo.Status.Conditions, newCondition)
repo.Status.ObservedGeneration = repo.GetGeneration()
},
}, {
"NoArtifact",
@@ -220,7 +210,6 @@ func TestCreateSourceGit(t *testing.T) {
ObservedGeneration: repo.GetGeneration(),
}
apimeta.SetStatusCondition(&repo.Status.Conditions, newCondition)
repo.Status.ObservedGeneration = repo.GetGeneration()
},
},
}

View File

@@ -23,6 +23,7 @@ import (
"os"
"github.com/fluxcd/pkg/apis/meta"
"github.com/fluxcd/pkg/runtime/conditions"
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
@@ -230,12 +231,8 @@ func createSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
}
logger.Waitingf("waiting for HelmRepository source reconciliation")
readyConditionFunc := isObjectReadyConditionFunc(kubeClient, namespacedName, helmRepository)
if helmRepository.Spec.Type == sourcev1.HelmRepositoryTypeOCI {
// HelmRepository type OCI is a static object.
readyConditionFunc = isStaticObjectReadyConditionFunc(kubeClient, namespacedName, helmRepository)
}
if err := wait.PollUntilContextTimeout(ctx, rootArgs.pollInterval, rootArgs.timeout, true, readyConditionFunc); err != nil {
if err := wait.PollUntilContextTimeout(ctx, rootArgs.pollInterval, rootArgs.timeout, true,
isHelmRepositoryReady(kubeClient, namespacedName, helmRepository)); err != nil {
return err
}
logger.Successf("HelmRepository source reconciliation completed")
@@ -282,3 +279,29 @@ func upsertHelmRepository(ctx context.Context, kubeClient client.Client,
logger.Successf("source updated")
return namespacedName, nil
}
func isHelmRepositoryReady(kubeClient client.Client, namespacedName types.NamespacedName, helmRepository *sourcev1.HelmRepository) wait.ConditionWithContextFunc {
return func(ctx context.Context) (bool, error) {
err := kubeClient.Get(ctx, namespacedName, helmRepository)
if err != nil {
return false, err
}
if c := conditions.Get(helmRepository, meta.ReadyCondition); c != nil {
// Confirm the Ready condition we are observing is for the
// current generation
if c.ObservedGeneration != helmRepository.GetGeneration() {
return false, nil
}
// Further check the Status
switch c.Status {
case metav1.ConditionTrue:
return true, nil
case metav1.ConditionFalse:
return false, fmt.Errorf(c.Message)
}
}
return false, nil
}
}

View File

@@ -29,6 +29,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/fluxcd/pkg/apis/meta"
"github.com/fluxcd/pkg/runtime/conditions"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta2"
@@ -192,7 +193,7 @@ func createSourceOCIRepositoryCmdRun(cmd *cobra.Command, args []string) error {
logger.Waitingf("waiting for OCIRepository reconciliation")
if err := wait.PollUntilContextTimeout(ctx, rootArgs.pollInterval, rootArgs.timeout, true,
isObjectReadyConditionFunc(kubeClient, namespacedName, repository)); err != nil {
isOCIRepositoryReady(kubeClient, namespacedName, repository)); err != nil {
return err
}
logger.Successf("OCIRepository reconciliation completed")
@@ -234,3 +235,29 @@ func upsertOCIRepository(ctx context.Context, kubeClient client.Client,
logger.Successf("OCIRepository updated")
return namespacedName, nil
}
func isOCIRepositoryReady(kubeClient client.Client, namespacedName types.NamespacedName, ociRepository *sourcev1.OCIRepository) wait.ConditionWithContextFunc {
return func(ctx context.Context) (bool, error) {
err := kubeClient.Get(ctx, namespacedName, ociRepository)
if err != nil {
return false, err
}
if c := conditions.Get(ociRepository, meta.ReadyCondition); c != nil {
// Confirm the Ready condition we are observing is for the
// current generation
if c.ObservedGeneration != ociRepository.GetGeneration() {
return false, nil
}
// Further check the Status
switch c.Status {
case metav1.ConditionTrue:
return true, nil
case metav1.ConditionFalse:
return false, fmt.Errorf(c.Message)
}
}
return false, nil
}
}

View File

@@ -19,7 +19,7 @@ package main
import (
"github.com/spf13/cobra"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta3"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta2"
)
var deleteAlertCmd = &cobra.Command{

View File

@@ -19,7 +19,7 @@ package main
import (
"github.com/spf13/cobra"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta3"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta2"
)
var deleteAlertProviderCmd = &cobra.Command{

View File

@@ -44,7 +44,7 @@ import (
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1beta2"
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1"
notificationv1 "github.com/fluxcd/notification-controller/api/v1"
notificationv1b3 "github.com/fluxcd/notification-controller/api/v1beta3"
notificationv1b2 "github.com/fluxcd/notification-controller/api/v1beta2"
sourcev1 "github.com/fluxcd/source-controller/api/v1"
sourcev1b2 "github.com/fluxcd/source-controller/api/v1beta2"
@@ -426,14 +426,14 @@ var fluxKindMap = refMap{
},
field: []string{"spec", "chart", "spec", "sourceRef"},
},
notificationv1b3.AlertKind: {
gvk: notificationv1b3.GroupVersion.WithKind(notificationv1b3.AlertKind),
kind: notificationv1b3.ProviderKind,
notificationv1b2.AlertKind: {
gvk: notificationv1b2.GroupVersion.WithKind(notificationv1b2.AlertKind),
kind: notificationv1b2.ProviderKind,
crossNamespaced: false,
field: []string{"spec", "providerRef"},
},
notificationv1.ReceiverKind: {gvk: notificationv1.GroupVersion.WithKind(notificationv1.ReceiverKind)},
notificationv1b3.ProviderKind: {gvk: notificationv1b3.GroupVersion.WithKind(notificationv1b3.ProviderKind)},
notificationv1b2.ProviderKind: {gvk: notificationv1b2.GroupVersion.WithKind(notificationv1b2.ProviderKind)},
imagev1.ImagePolicyKind: {
gvk: imagev1.GroupVersion.WithKind(imagev1.ImagePolicyKind),
kind: imagev1.ImageRepositoryKind,

View File

@@ -27,11 +27,20 @@ import (
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/client/fake"
helmv2beta1 "github.com/fluxcd/helm-controller/api/v2beta1"
autov1 "github.com/fluxcd/image-automation-controller/api/v1beta1"
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1beta2"
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1"
notificationv1 "github.com/fluxcd/notification-controller/api/v1"
notificationv1b2 "github.com/fluxcd/notification-controller/api/v1beta2"
eventv1 "github.com/fluxcd/pkg/apis/event/v1beta1"
"github.com/fluxcd/pkg/ssa"
sourcev1 "github.com/fluxcd/source-controller/api/v1"
sourcev1b2 "github.com/fluxcd/source-controller/api/v1beta2"
"github.com/fluxcd/flux2/v2/internal/utils"
)
@@ -118,7 +127,7 @@ spec:
name: podinfo-chart
version: '*'
---
apiVersion: notification.toolkit.fluxcd.io/v1beta3
apiVersion: notification.toolkit.fluxcd.io/v1beta2
kind: Alert
metadata:
name: webapp
@@ -131,7 +140,7 @@ spec:
providerRef:
name: slack
---
apiVersion: notification.toolkit.fluxcd.io/v1beta3
apiVersion: notification.toolkit.fluxcd.io/v1beta2
kind: Provider
metadata:
name: slack
@@ -163,7 +172,7 @@ func Test_getObjectRef(t *testing.T) {
objs, err := ssa.ReadObjects(strings.NewReader(objects))
g.Expect(err).To(Not(HaveOccurred()))
builder := fake.NewClientBuilder().WithScheme(utils.NewScheme())
builder := fake.NewClientBuilder().WithScheme(getScheme())
for _, obj := range objs {
builder = builder.WithObjects(obj)
}
@@ -247,7 +256,7 @@ func Test_getRows(t *testing.T) {
objs, err := ssa.ReadObjects(strings.NewReader(objects))
g.Expect(err).To(Not(HaveOccurred()))
builder := fake.NewClientBuilder().WithScheme(utils.NewScheme())
builder := fake.NewClientBuilder().WithScheme(getScheme())
for _, obj := range objs {
builder = builder.WithObjects(obj)
}
@@ -401,6 +410,21 @@ func getTestListOpt(kind, name string) client.ListOption {
return client.MatchingFieldsSelector{Selector: sel}
}
func getScheme() *runtime.Scheme {
newscheme := runtime.NewScheme()
corev1.AddToScheme(newscheme)
kustomizev1.AddToScheme(newscheme)
helmv2beta1.AddToScheme(newscheme)
notificationv1.AddToScheme(newscheme)
notificationv1b2.AddToScheme(newscheme)
imagev1.AddToScheme(newscheme)
autov1.AddToScheme(newscheme)
sourcev1.AddToScheme(newscheme)
sourcev1b2.AddToScheme(newscheme)
return newscheme
}
func createEvent(obj client.Object, eventType, msg, reason string) corev1.Event {
return corev1.Event{
ObjectMeta: metav1.ObjectMeta{

View File

@@ -20,7 +20,7 @@ import (
"github.com/spf13/cobra"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta3"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta2"
)
var exportAlertCmd = &cobra.Command{

View File

@@ -20,7 +20,7 @@ import (
"github.com/spf13/cobra"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta3"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta2"
)
var exportAlertProviderCmd = &cobra.Command{

View File

@@ -23,10 +23,9 @@ import (
"github.com/spf13/cobra"
"golang.org/x/text/cases"
"golang.org/x/text/language"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta3"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta2"
)
var getAlertCmd = &cobra.Command{
@@ -78,7 +77,7 @@ func init() {
func (s alertListAdapter) summariseItem(i int, includeNamespace bool, includeKind bool) []string {
item := s.Items[i]
status, msg := string(metav1.ConditionTrue), "Alert is Ready"
status, msg := statusAndMessage(item.Status.Conditions)
return append(nameColumns(&item, includeNamespace, includeKind),
cases.Title(language.English).String(strconv.FormatBool(item.Spec.Suspend)), status, msg)
}
@@ -92,5 +91,6 @@ func (s alertListAdapter) headers(includeNamespace bool) []string {
}
func (s alertListAdapter) statusSelectorMatches(i int, conditionType, conditionStatus string) bool {
return false
item := s.Items[i]
return statusMatches(conditionType, conditionStatus, item.Status.Conditions)
}

View File

@@ -20,10 +20,9 @@ import (
"fmt"
"github.com/spf13/cobra"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta3"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta2"
)
var getAlertProviderCmd = &cobra.Command{
@@ -75,7 +74,7 @@ func init() {
func (s alertProviderListAdapter) summariseItem(i int, includeNamespace bool, includeKind bool) []string {
item := s.Items[i]
status, msg := string(metav1.ConditionTrue), "Provider is Ready"
status, msg := statusAndMessage(item.Status.Conditions)
return append(nameColumns(&item, includeNamespace, includeKind), status, msg)
}
@@ -88,5 +87,6 @@ func (s alertProviderListAdapter) headers(includeNamespace bool) []string {
}
func (s alertProviderListAdapter) statusSelectorMatches(i int, conditionType, conditionStatus string) bool {
return false
item := s.Items[i]
return statusMatches(conditionType, conditionStatus, item.Status.Conditions)
}

View File

@@ -24,7 +24,7 @@ import (
helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1"
notificationv1 "github.com/fluxcd/notification-controller/api/v1"
notificationv1b3 "github.com/fluxcd/notification-controller/api/v1beta3"
notificationv1b2 "github.com/fluxcd/notification-controller/api/v1beta2"
)
var getAllCmd = &cobra.Command{
@@ -63,11 +63,11 @@ var getAllCmd = &cobra.Command{
},
{
apiType: alertProviderType,
list: alertProviderListAdapter{&notificationv1b3.ProviderList{}},
list: alertProviderListAdapter{&notificationv1b2.ProviderList{}},
},
{
apiType: alertType,
list: &alertListAdapter{&notificationv1b3.AlertList{}},
list: &alertListAdapter{&notificationv1b2.AlertList{}},
},
}

View File

@@ -23,7 +23,6 @@ import (
"github.com/spf13/cobra"
"golang.org/x/text/cases"
"golang.org/x/text/language"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta2"
@@ -83,12 +82,7 @@ func (a *helmRepositoryListAdapter) summariseItem(i int, includeNamespace bool,
if item.GetArtifact() != nil {
revision = item.GetArtifact().Revision
}
var status, msg string
if item.Spec.Type == sourcev1.HelmRepositoryTypeOCI {
status, msg = string(metav1.ConditionTrue), "Helm repository is Ready"
} else {
status, msg = statusAndMessage(item.Status.Conditions)
}
status, msg := statusAndMessage(item.Status.Conditions)
revision = utils.TruncateHex(revision)
msg = utils.TruncateHex(msg)
return append(nameColumns(&item, includeNamespace, includeKind),

View File

@@ -25,15 +25,10 @@ import (
"os"
"testing"
"github.com/go-logr/logr"
"sigs.k8s.io/controller-runtime/pkg/log"
"github.com/fluxcd/flux2/v2/internal/utils"
)
func TestMain(m *testing.M) {
log.SetLogger(logr.New(log.NullLogSink{}))
// Ensure tests print consistent timestamps regardless of timezone
os.Setenv("TZ", "UTC")

View File

@@ -34,7 +34,6 @@ import (
"github.com/fluxcd/flux2/v2/internal/utils"
"github.com/google/go-cmp/cmp"
"github.com/mattn/go-shellwords"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
k8syaml "k8s.io/apimachinery/pkg/util/yaml"
"k8s.io/client-go/tools/clientcmd"
@@ -113,8 +112,7 @@ func (m *testEnvKubeManager) CreateObjects(clientObjects []*unstructured.Unstruc
}
obj.SetResourceVersion(createObj.GetResourceVersion())
err = m.client.Status().Update(context.Background(), obj)
// Updating status of static objects results in not found error.
if err != nil && !errors.IsNotFound(err) {
if err != nil {
return err
}
}

View File

@@ -22,13 +22,10 @@ package main
import (
"context"
"fmt"
"os"
"testing"
"github.com/go-logr/logr"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/controller-runtime/pkg/log"
"os"
"testing"
)
// The test environment is long running process shared between tests, initialized
@@ -37,8 +34,6 @@ import (
var testEnv *testEnvKubeManager
func TestMain(m *testing.M) {
log.SetLogger(logr.New(log.NullLogSink{}))
// Ensure tests print consistent timestamps regardless of timezone
os.Setenv("TZ", "UTC")

View File

@@ -1,149 +0,0 @@
/*
Copyright 2023 The Flux authors
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"
kstatus "github.com/fluxcd/cli-utils/pkg/kstatus/status"
apimeta "k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait"
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/fluxcd/pkg/apis/meta"
"github.com/fluxcd/pkg/runtime/object"
"github.com/fluxcd/pkg/runtime/patch"
)
// objectStatusType is the type of object in terms of status when computing the
// readiness of an object. Readiness check method depends on the type of object.
// For a dynamic object, Ready status condition is considered only for the
// latest generation of the object. For a static object that don't have any
// condition, the object generation is not considered.
type objectStatusType int
const (
objectStatusDynamic objectStatusType = iota
objectStatusStatic
)
// isObjectReady determines if an object is ready using the kstatus.Compute()
// result. statusType helps differenciate between static and dynamic objects to
// accurately check the object's readiness. A dynamic object may have some extra
// considerations depending on the object.
func isObjectReady(obj client.Object, statusType objectStatusType) (bool, error) {
observedGen, err := object.GetStatusObservedGeneration(obj)
if err != nil && err != object.ErrObservedGenerationNotFound {
return false, err
}
if statusType == objectStatusDynamic {
// Object not reconciled yet.
if observedGen < 1 {
return false, nil
}
cobj, ok := obj.(meta.ObjectWithConditions)
if !ok {
return false, fmt.Errorf("unable to get conditions from object")
}
if c := apimeta.FindStatusCondition(cobj.GetConditions(), meta.ReadyCondition); c != nil {
// Ensure that the ready condition is for the latest generation of
// the object.
// NOTE: Some APIs like ImageUpdateAutomation and HelmRelease don't
// support per condition observed generation yet. Per condition
// observed generation for them are always zero.
// There are two strategies used across different object kinds to
// check the latest ready condition:
// - check that the ready condition's generation matches the
// object's generation.
// - check that the observed generation of the object in the
// status matches the object's generation.
//
// TODO: Once ImageUpdateAutomation and HelmRelease APIs have per
// condition observed generation, remove the object's observed
// generation and object's generation check (the second condition
// below). Also, try replacing this readiness check function with
// fluxcd/pkg/ssa's ResourceManager.Wait(), which uses kstatus
// internally to check readiness of the objects.
if c.ObservedGeneration != 0 && c.ObservedGeneration != obj.GetGeneration() {
return false, nil
}
if c.ObservedGeneration == 0 && observedGen != obj.GetGeneration() {
return false, nil
}
} else {
return false, nil
}
}
u, err := patch.ToUnstructured(obj)
if err != nil {
return false, err
}
result, err := kstatus.Compute(u)
if err != nil {
return false, err
}
switch result.Status {
case kstatus.CurrentStatus:
return true, nil
case kstatus.InProgressStatus:
return false, nil
default:
return false, fmt.Errorf(result.Message)
}
}
// isObjectReadyConditionFunc returns a wait.ConditionFunc to be used with
// wait.Poll* while polling for an object with dynamic status to be ready.
func isObjectReadyConditionFunc(kubeClient client.Client, namespaceName types.NamespacedName, obj client.Object) wait.ConditionWithContextFunc {
return func(ctx context.Context) (bool, error) {
err := kubeClient.Get(ctx, namespaceName, obj)
if err != nil {
return false, err
}
return isObjectReady(obj, objectStatusDynamic)
}
}
// isStaticObjectReadyConditionFunc returns a wait.ConditionFunc to be used with
// wait.Poll* while polling for an object with static or no status to be
// ready.
func isStaticObjectReadyConditionFunc(kubeClient client.Client, namespaceName types.NamespacedName, obj client.Object) wait.ConditionWithContextFunc {
return func(ctx context.Context) (bool, error) {
err := kubeClient.Get(ctx, namespaceName, obj)
if err != nil {
return false, err
}
return isObjectReady(obj, objectStatusStatic)
}
}
// kstatusCompute returns the kstatus computed result of a given object.
func kstatusCompute(obj client.Object) (result *kstatus.Result, err error) {
u, err := patch.ToUnstructured(obj)
if err != nil {
return result, err
}
return kstatus.Compute(u)
}

View File

@@ -1,139 +0,0 @@
/*
Copyright 2023 The Flux authors
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 (
"testing"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta3"
"github.com/fluxcd/pkg/apis/meta"
"github.com/fluxcd/pkg/runtime/conditions"
sourcev1 "github.com/fluxcd/source-controller/api/v1"
)
func Test_isObjectReady(t *testing.T) {
// Ready object.
readyObj := &sourcev1.GitRepository{}
readyObj.Generation = 1
readyObj.Status.ObservedGeneration = 1
conditions.MarkTrue(readyObj, meta.ReadyCondition, "foo1", "bar1")
// Not ready object.
notReadyObj := readyObj.DeepCopy()
conditions.MarkFalse(notReadyObj, meta.ReadyCondition, "foo2", "bar2")
// Not reconciled object.
notReconciledObj := readyObj.DeepCopy()
notReconciledObj.Status = sourcev1.GitRepositoryStatus{ObservedGeneration: -1}
// No condition.
noConditionObj := readyObj.DeepCopy()
noConditionObj.Status = sourcev1.GitRepositoryStatus{ObservedGeneration: 1}
// Outdated condition.
readyObjOutdated := readyObj.DeepCopy()
readyObjOutdated.Generation = 2
// Object without per condition observed generation.
oldObj := readyObj.DeepCopy()
readyTrueCondn := conditions.TrueCondition(meta.ReadyCondition, "foo3", "bar3")
oldObj.Status.Conditions = []metav1.Condition{*readyTrueCondn}
// Outdated object without per condition observed generation.
oldObjOutdated := oldObj.DeepCopy()
oldObjOutdated.Generation = 2
// Empty status object.
staticObj := readyObj.DeepCopy()
staticObj.Status = sourcev1.GitRepositoryStatus{}
// No status object.
noStatusObj := &notificationv1.Provider{}
noStatusObj.Generation = 1
type args struct {
obj client.Object
statusType objectStatusType
}
tests := []struct {
name string
args args
want bool
wantErr bool
}{
{
name: "dynamic ready",
args: args{obj: readyObj, statusType: objectStatusDynamic},
want: true,
},
{
name: "dynamic not ready",
args: args{obj: notReadyObj, statusType: objectStatusDynamic},
want: false,
},
{
name: "dynamic not reconciled",
args: args{obj: notReconciledObj, statusType: objectStatusDynamic},
want: false,
},
{
name: "dynamic not condition",
args: args{obj: noConditionObj, statusType: objectStatusDynamic},
want: false,
},
{
name: "dynamic ready outdated",
args: args{obj: readyObjOutdated, statusType: objectStatusDynamic},
want: false,
},
{
name: "dynamic ready without per condition gen",
args: args{obj: oldObj, statusType: objectStatusDynamic},
want: true,
},
{
name: "dynamic outdated ready status without per condition gen",
args: args{obj: oldObjOutdated, statusType: objectStatusDynamic},
want: false,
},
{
name: "static empty status",
args: args{obj: staticObj, statusType: objectStatusStatic},
want: true,
},
{
name: "static no status",
args: args{obj: noStatusObj, statusType: objectStatusStatic},
want: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := isObjectReady(tt.args.obj, tt.args.statusType)
if (err != nil) != tt.wantErr {
t.Errorf("isObjectReady() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("isObjectReady() = %v, want %v", got, tt.want)
}
})
}
}

View File

@@ -21,7 +21,6 @@ import (
"fmt"
"time"
kstatus "github.com/fluxcd/cli-utils/pkg/kstatus/status"
"github.com/spf13/cobra"
apimeta "k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -31,6 +30,8 @@ import (
"k8s.io/client-go/util/retry"
"sigs.k8s.io/controller-runtime/pkg/client"
notificationv1 "github.com/fluxcd/notification-controller/api/v1"
notificationv1b2 "github.com/fluxcd/notification-controller/api/v1beta2"
"github.com/fluxcd/pkg/apis/meta"
"github.com/fluxcd/flux2/v2/internal/utils"
@@ -60,7 +61,6 @@ type reconcilable interface {
GetAnnotations() map[string]string
SetAnnotations(map[string]string)
isStatic() bool // is it a static object that does not have a reconciler?
lastHandledReconcileRequest() string // what was the last handled reconcile request?
successMessage() string // what do you want to tell people when successfully reconciled?
}
@@ -101,11 +101,6 @@ func (reconcile reconcileCommand) run(cmd *cobra.Command, args []string) error {
return err
}
if reconcile.object.isStatic() {
logger.Successf("reconciliation not supported by the object")
return nil
}
if reconcile.object.isSuspended() {
return fmt.Errorf("resource is suspended")
}
@@ -117,6 +112,16 @@ func (reconcile reconcileCommand) run(cmd *cobra.Command, args []string) error {
}
logger.Successf("%s annotated", reconcile.kind)
if reconcile.kind == notificationv1b2.AlertKind || reconcile.kind == notificationv1.ReceiverKind {
if err = wait.PollUntilContextTimeout(ctx, rootArgs.pollInterval, rootArgs.timeout, true,
isReconcileReady(kubeClient, namespacedName, reconcile.object)); err != nil {
return err
}
logger.Successf(reconcile.object.successMessage())
return nil
}
lastHandledReconcileAt := reconcile.object.lastHandledReconcileRequest()
logger.Waitingf("waiting for %s reconciliation", reconcile.kind)
if err := wait.PollUntilContextTimeout(ctx, rootArgs.pollInterval, rootArgs.timeout, true,
@@ -141,17 +146,9 @@ func reconciliationHandled(kubeClient client.Client, namespacedName types.Namesp
if err != nil {
return false, err
}
if obj.lastHandledReconcileRequest() == lastHandledReconcileAt {
return false, nil
}
result, err := kstatusCompute(obj.asClientObject())
if err != nil {
return false, err
}
return result.Status == kstatus.CurrentStatus, nil
isProgressing := apimeta.IsStatusConditionPresentAndEqual(reconcilableConditions(obj),
meta.ReadyCondition, metav1.ConditionUnknown)
return obj.lastHandledReconcileRequest() != lastHandledReconcileAt && !isProgressing, nil
}
}
@@ -177,3 +174,22 @@ func requestReconciliation(ctx context.Context, kubeClient client.Client,
return kubeClient.Patch(ctx, object, patch)
})
}
func isReconcileReady(kubeClient client.Client, namespacedName types.NamespacedName, obj reconcilable) wait.ConditionWithContextFunc {
return func(ctx context.Context) (bool, error) {
err := kubeClient.Get(ctx, namespacedName, obj.asClientObject())
if err != nil {
return false, err
}
if c := apimeta.FindStatusCondition(reconcilableConditions(obj), meta.ReadyCondition); c != nil {
switch c.Status {
case metav1.ConditionTrue:
return true, nil
case metav1.ConditionFalse:
return false, fmt.Errorf(c.Message)
}
}
return false, nil
}
}

View File

@@ -0,0 +1,44 @@
/*
Copyright 2020 The Flux authors
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 (
"github.com/spf13/cobra"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta2"
)
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
flux reconcile alert main`,
ValidArgsFunction: resourceNamesCompletionFunc(notificationv1.GroupVersion.WithKind(notificationv1.AlertKind)),
RunE: reconcileCommand{
apiType: alertType,
object: alertAdapter{&notificationv1.Alert{}},
}.run,
}
func init() {
reconcileCmd.AddCommand(reconcileAlertCmd)
}
func (obj alertAdapter) lastHandledReconcileRequest() string {
return ""
}

View File

@@ -0,0 +1,93 @@
/*
Copyright 2020 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package main
import (
"context"
"fmt"
"time"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta2"
"github.com/fluxcd/pkg/apis/meta"
"github.com/fluxcd/flux2/v2/internal/utils"
)
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
flux reconcile alert-provider slack`,
ValidArgsFunction: resourceNamesCompletionFunc(notificationv1.GroupVersion.WithKind(notificationv1.ProviderKind)),
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(), rootArgs.timeout)
defer cancel()
kubeClient, err := utils.KubeClient(kubeconfigArgs, kubeclientOptions)
if err != nil {
return err
}
namespacedName := types.NamespacedName{
Namespace: *kubeconfigArgs.Namespace,
Name: name,
}
logger.Actionf("annotating Provider %s in %s namespace", name, *kubeconfigArgs.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.ReconcileRequestAnnotation: time.Now().Format(time.RFC3339Nano),
}
} else {
alertProvider.Annotations[meta.ReconcileRequestAnnotation] = 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.PollUntilContextTimeout(ctx, rootArgs.pollInterval, rootArgs.timeout, true,
isAlertProviderReady(kubeClient, namespacedName, &alertProvider)); err != nil {
return err
}
logger.Successf("Provider reconciliation completed")
return nil
}

View File

@@ -81,7 +81,3 @@ func (obj helmReleaseAdapter) getSource() (reconcileSource, types.NamespacedName
Namespace: ns,
}
}
func (obj helmReleaseAdapter) isStatic() bool {
return false
}

View File

@@ -48,7 +48,3 @@ func (obj imageRepositoryAdapter) lastHandledReconcileRequest() string {
func (obj imageRepositoryAdapter) successMessage() string {
return fmt.Sprintf("scan fetched %d tags", obj.Status.LastScanResult.TagCount)
}
func (obj imageRepositoryAdapter) isStatic() bool {
return false
}

View File

@@ -56,7 +56,3 @@ func (obj imageUpdateAutomationAdapter) successMessage() string {
}
return "automation not yet run"
}
func (obj imageUpdateAutomationAdapter) isStatic() bool {
return false
}

View File

@@ -88,7 +88,3 @@ func (obj kustomizationAdapter) getSource() (reconcileSource, types.NamespacedNa
Namespace: obj.Spec.SourceRef.Namespace,
}
}
func (obj kustomizationAdapter) isStatic() bool {
return false
}

View File

@@ -17,9 +17,18 @@ limitations under the License.
package main
import (
"context"
"fmt"
"time"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait"
notificationv1 "github.com/fluxcd/notification-controller/api/v1"
"github.com/fluxcd/pkg/apis/meta"
"github.com/fluxcd/flux2/v2/internal/utils"
)
var reconcileReceiverCmd = &cobra.Command{
@@ -29,20 +38,62 @@ var reconcileReceiverCmd = &cobra.Command{
Example: ` # Trigger a reconciliation for an existing receiver
flux reconcile receiver main`,
ValidArgsFunction: resourceNamesCompletionFunc(notificationv1.GroupVersion.WithKind(notificationv1.ReceiverKind)),
RunE: reconcileCommand{
apiType: receiverType,
object: receiverAdapter{&notificationv1.Receiver{}},
}.run,
RunE: reconcileReceiverCmdRun,
}
func init() {
reconcileCmd.AddCommand(reconcileReceiverCmd)
}
func (obj receiverAdapter) lastHandledReconcileRequest() string {
return obj.Status.GetLastHandledReconcileRequest()
}
func reconcileReceiverCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("receiver name is required")
}
name := args[0]
func (obj receiverAdapter) isStatic() bool {
return false
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel()
kubeClient, err := utils.KubeClient(kubeconfigArgs, kubeclientOptions)
if err != nil {
return err
}
namespacedName := types.NamespacedName{
Namespace: *kubeconfigArgs.Namespace,
Name: name,
}
var receiver notificationv1.Receiver
err = kubeClient.Get(ctx, namespacedName, &receiver)
if err != nil {
return err
}
if receiver.Spec.Suspend {
return fmt.Errorf("resource is suspended")
}
logger.Actionf("annotating Receiver %s in %s namespace", name, *kubeconfigArgs.Namespace)
if receiver.Annotations == nil {
receiver.Annotations = map[string]string{
meta.ReconcileRequestAnnotation: time.Now().Format(time.RFC3339Nano),
}
} else {
receiver.Annotations[meta.ReconcileRequestAnnotation] = time.Now().Format(time.RFC3339Nano)
}
if err := kubeClient.Update(ctx, &receiver); err != nil {
return err
}
logger.Successf("Receiver annotated")
logger.Waitingf("waiting for Receiver reconciliation")
if err := wait.PollUntilContextTimeout(ctx, rootArgs.pollInterval, rootArgs.timeout, true,
isReceiverReady(kubeClient, namespacedName, &receiver)); err != nil {
return err
}
logger.Successf("Receiver reconciliation completed")
return nil
}

View File

@@ -48,7 +48,3 @@ func (obj bucketAdapter) lastHandledReconcileRequest() string {
func (obj bucketAdapter) successMessage() string {
return fmt.Sprintf("fetched revision %s", obj.Status.Artifact.Revision)
}
func (obj bucketAdapter) isStatic() bool {
return false
}

View File

@@ -84,7 +84,3 @@ func (obj helmChartAdapter) getSource() (reconcileSource, types.NamespacedName)
Namespace: obj.Namespace,
}
}
func (obj helmChartAdapter) isStatic() bool {
return false
}

View File

@@ -48,7 +48,3 @@ func (obj gitRepositoryAdapter) lastHandledReconcileRequest() string {
func (obj gitRepositoryAdapter) successMessage() string {
return fmt.Sprintf("fetched revision %s", obj.Status.Artifact.Revision)
}
func (obj gitRepositoryAdapter) isStatic() bool {
return false
}

View File

@@ -60,7 +60,3 @@ func (obj helmRepositoryAdapter) successMessage() string {
}
return fmt.Sprintf("fetched revision %s", obj.Status.Artifact.Revision)
}
func (obj helmRepositoryAdapter) isStatic() bool {
return obj.Spec.Type == sourcev1.HelmRepositoryTypeOCI
}

View File

@@ -48,7 +48,3 @@ func (obj ociRepositoryAdapter) lastHandledReconcileRequest() string {
func (obj ociRepositoryAdapter) successMessage() string {
return fmt.Sprintf("fetched revision %s", obj.Status.Artifact.Revision)
}
func (obj ociRepositoryAdapter) isStatic() bool {
return false
}

View File

@@ -56,7 +56,6 @@ type resumable interface {
copyable
statusable
setUnsuspended()
isStatic() bool
successMessage() string
}
@@ -213,12 +212,8 @@ func (resume resumeCommand) reconcile(ctx context.Context, res resumable) reconc
logger.Waitingf("waiting for %s reconciliation", resume.kind)
readyConditionFunc := isObjectReadyConditionFunc(resume.client, namespacedName, res.asClientObject())
if res.isStatic() {
readyConditionFunc = isStaticObjectReadyConditionFunc(resume.client, namespacedName, res.asClientObject())
}
if err := wait.PollUntilContextTimeout(ctx, rootArgs.pollInterval, rootArgs.timeout, true, readyConditionFunc); err != nil {
if err := wait.PollUntilContextTimeout(ctx, rootArgs.pollInterval, rootArgs.timeout, true,
isReady(resume.client, namespacedName, res)); err != nil {
return reconcileResponse{
resumable: res,
err: err,

View File

@@ -19,7 +19,7 @@ package main
import (
"github.com/spf13/cobra"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta3"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta2"
)
var resumeAlertCmd = &cobra.Command{
@@ -44,7 +44,7 @@ func init() {
}
func (obj alertAdapter) getObservedGeneration() int64 {
return 0
return obj.Alert.Status.ObservedGeneration
}
func (obj alertAdapter) setUnsuspended() {
@@ -55,10 +55,6 @@ func (obj alertAdapter) successMessage() string {
return "Alert reconciliation completed"
}
func (a alertAdapter) isStatic() bool {
return true
}
func (a alertListAdapter) resumeItem(i int) resumable {
return &alertAdapter{&a.AlertList.Items[i]}
}

View File

@@ -1,64 +0,0 @@
/*
Copyright 2023 The Flux authors
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 (
"github.com/spf13/cobra"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta3"
)
var resumeAlertProviderCmd = &cobra.Command{
Use: "alert-provider [name]",
Short: "Resume a suspended Provider",
Long: `The resume command marks a previously suspended Provider resource for reconciliation and waits for it to
finish the apply.`,
Example: ` # Resume reconciliation for an existing Provider
flux resume alert-provider main
# Resume reconciliation for multiple Providers
flux resume alert-provider main-1 main-2`,
ValidArgsFunction: resourceNamesCompletionFunc(notificationv1.GroupVersion.WithKind(notificationv1.ProviderKind)),
RunE: resumeCommand{
apiType: alertProviderType,
list: &alertProviderListAdapter{&notificationv1.ProviderList{}},
}.run,
}
func init() {
resumeCmd.AddCommand(resumeAlertProviderCmd)
}
func (obj alertProviderAdapter) getObservedGeneration() int64 {
return 0
}
func (obj alertProviderAdapter) setUnsuspended() {
obj.Provider.Spec.Suspend = false
}
func (obj alertProviderAdapter) successMessage() string {
return "Provider reconciliation completed"
}
func (a alertProviderAdapter) isStatic() bool {
return true
}
func (a alertProviderListAdapter) resumeItem(i int) resumable {
return &alertProviderAdapter{&a.ProviderList.Items[i]}
}

View File

@@ -32,7 +32,7 @@ import (
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1beta2"
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1"
notificationv1 "github.com/fluxcd/notification-controller/api/v1"
notificationv1b3 "github.com/fluxcd/notification-controller/api/v1beta3"
notificationv1b2 "github.com/fluxcd/notification-controller/api/v1beta2"
sourcev1 "github.com/fluxcd/source-controller/api/v1"
sourcev1b2 "github.com/fluxcd/source-controller/api/v1beta2"
@@ -111,14 +111,14 @@ func runStatsCmd(cmd *cobra.Command, args []string) error {
Group: helmv2.GroupVersion.Group,
},
{
Kind: notificationv1b3.AlertKind,
Version: notificationv1b3.GroupVersion.Version,
Group: notificationv1b3.GroupVersion.Group,
Kind: notificationv1b2.AlertKind,
Version: notificationv1b2.GroupVersion.Version,
Group: notificationv1b2.GroupVersion.Group,
},
{
Kind: notificationv1b3.ProviderKind,
Version: notificationv1b3.GroupVersion.Version,
Group: notificationv1b3.GroupVersion.Group,
Kind: notificationv1b2.ProviderKind,
Version: notificationv1b2.GroupVersion.Version,
Group: notificationv1b2.GroupVersion.Group,
},
{
Kind: notificationv1.ReceiverKind,

View File

@@ -17,9 +17,18 @@ limitations under the License.
package main
import (
"github.com/fluxcd/cli-utils/pkg/object"
"context"
"fmt"
apimeta "k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait"
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/fluxcd/cli-utils/pkg/object"
"github.com/fluxcd/pkg/apis/meta"
)
// statusable is used to see if a resource is considered ready in the usual way
@@ -36,6 +45,42 @@ type oldConditions interface {
GetStatusConditions() *[]metav1.Condition
}
func statusableConditions(object statusable) []metav1.Condition {
if s, ok := object.(meta.ObjectWithConditions); ok {
return s.GetConditions()
}
if s, ok := object.(oldConditions); ok {
return *s.GetStatusConditions()
}
return []metav1.Condition{}
}
func isReady(kubeClient client.Client, namespacedName types.NamespacedName, object statusable) wait.ConditionWithContextFunc {
return func(ctx context.Context) (bool, error) {
err := kubeClient.Get(ctx, namespacedName, object.asClientObject())
if err != nil {
return false, err
}
// Confirm the state we are observing is for the current generation
if object.GetGeneration() != object.getObservedGeneration() {
return false, nil
}
if c := apimeta.FindStatusCondition(statusableConditions(object), meta.ReadyCondition); c != nil {
switch c.Status {
case metav1.ConditionTrue:
return true, nil
case metav1.ConditionFalse:
return false, fmt.Errorf(c.Message)
}
}
return false, nil
}
}
func buildComponentObjectRefs(components ...string) ([]object.ObjMetadata, error) {
var objRefs []object.ObjMetadata
for _, deployment := range components {

View File

@@ -19,7 +19,7 @@ package main
import (
"github.com/spf13/cobra"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta3"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta2"
)
var suspendAlertCmd = &cobra.Command{

View File

@@ -1,56 +0,0 @@
/*
Copyright 2023 The Flux authors
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 (
"github.com/spf13/cobra"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta3"
)
var suspendAlertProviderCmd = &cobra.Command{
Use: "alert-provider [name]",
Short: "Suspend reconciliation of Provider",
Long: `The suspend command disables the reconciliation of a Provider resource.`,
Example: ` # Suspend reconciliation for an existing Provider
flux suspend alert-provider main
# Suspend reconciliation for multiple Providers
flux suspend alert-providers main-1 main-2`,
ValidArgsFunction: resourceNamesCompletionFunc(notificationv1.GroupVersion.WithKind(notificationv1.ProviderKind)),
RunE: suspendCommand{
apiType: alertProviderType,
object: &alertProviderAdapter{&notificationv1.Provider{}},
list: &alertProviderListAdapter{&notificationv1.ProviderList{}},
}.run,
}
func init() {
suspendCmd.AddCommand(suspendAlertProviderCmd)
}
func (obj alertProviderAdapter) isSuspended() bool {
return obj.Provider.Spec.Suspend
}
func (obj alertProviderAdapter) setSuspended() {
obj.Provider.Spec.Suspend = true
}
func (a alertProviderListAdapter) item(i int) suspendable {
return &alertProviderAdapter{&a.ProviderList.Items[i]}
}

View File

@@ -1,5 +1,5 @@
---
apiVersion: notification.toolkit.fluxcd.io/v1beta3
apiVersion: notification.toolkit.fluxcd.io/v1beta2
kind: Alert
metadata:
name: flux-system

View File

@@ -4,7 +4,7 @@ kind: Namespace
metadata:
name: {{ .fluxns }}
---
apiVersion: notification.toolkit.fluxcd.io/v1beta3
apiVersion: notification.toolkit.fluxcd.io/v1beta2
kind: Provider
metadata:
name: slack
@@ -14,7 +14,7 @@ spec:
channel: 'A channel with spacess'
address: https://hooks.slack.com/services/mock
---
apiVersion: notification.toolkit.fluxcd.io/v1beta3
apiVersion: notification.toolkit.fluxcd.io/v1beta2
kind: Alert
metadata:
name: flux-system

View File

@@ -1,5 +1,5 @@
---
apiVersion: notification.toolkit.fluxcd.io/v1beta3
apiVersion: notification.toolkit.fluxcd.io/v1beta2
kind: Provider
metadata:
name: slack

View File

@@ -25,9 +25,8 @@ import (
"github.com/google/go-containerregistry/pkg/name"
"github.com/spf13/cobra"
v1 "k8s.io/api/apps/v1"
"k8s.io/apimachinery/pkg/api/errors"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/yaml/goyaml.v2"
"sigs.k8s.io/yaml"
"github.com/fluxcd/flux2/v2/internal/utils"
"github.com/fluxcd/flux2/v2/pkg/manifestgen"
@@ -56,12 +55,6 @@ type versionFlags struct {
var versionArgs versionFlags
type versionInfo struct {
Flux string `yaml:"flux"`
Distribution string `yaml:"distribution,omitempty"`
Controller map[string]string `yaml:"controller,inline"`
}
func init() {
versionCmd.Flags().BoolVar(&versionArgs.client, "client", false,
"print only client version")
@@ -78,12 +71,8 @@ func versionCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel()
// versionInfo struct and goyaml is used because we care about the order.
// Without this `distribution` is printed before `flux` when the struct is marshalled.
info := &versionInfo{
Controller: map[string]string{},
}
info.Flux = rootArgs.defaults.Version
info := map[string]string{}
info["flux"] = rootArgs.defaults.Version
if !versionArgs.client {
kubeClient, err := utils.KubeClient(kubeconfigArgs, kubeclientOptions)
@@ -91,16 +80,6 @@ func versionCmdRun(cmd *cobra.Command, args []string) error {
return err
}
clusterInfo, err := getFluxClusterInfo(ctx, kubeClient)
// ignoring not found errors because it means that the GitRepository CRD isn't installed but a user might
// have other controllers(e.g notification-controller), and we want to still return information for them.
if err != nil && !errors.IsNotFound(err) {
return err
}
if clusterInfo.distribution() != "" {
info.Distribution = clusterInfo.distribution()
}
selector := client.MatchingLabels{manifestgen.PartOfLabelKey: manifestgen.PartOfLabelValue}
var list v1.DeploymentList
if err := kubeClient.List(ctx, &list, client.InNamespace(*kubeconfigArgs.Namespace), selector); err != nil {
@@ -117,7 +96,7 @@ func versionCmdRun(cmd *cobra.Command, args []string) error {
if err != nil {
return err
}
info.Controller[name] = tag
info[name] = tag
}
}
}
@@ -126,7 +105,7 @@ func versionCmdRun(cmd *cobra.Command, args []string) error {
var err error
if versionArgs.output == "json" {
marshalled, err = info.toJSON()
marshalled, err = json.MarshalIndent(&info, "", " ")
marshalled = append(marshalled, "\n"...)
} else {
marshalled, err = yaml.Marshal(&info)
@@ -140,20 +119,6 @@ func versionCmdRun(cmd *cobra.Command, args []string) error {
return nil
}
func (info versionInfo) toJSON() ([]byte, error) {
mapInfo := map[string]string{
"flux": info.Flux,
}
if info.Distribution != "" {
mapInfo["distribution"] = info.Distribution
}
for k, v := range info.Controller {
mapInfo[k] = v
}
return json.MarshalIndent(&mapInfo, "", " ")
}
func splitImageStr(image string) (string, string, error) {
ref, err := name.ParseReference(image)
if err != nil {

34
go.mod
View File

@@ -14,9 +14,9 @@ require (
github.com/fluxcd/go-git-providers v0.19.1
github.com/fluxcd/helm-controller/api v0.36.2
github.com/fluxcd/image-automation-controller/api v0.36.1
github.com/fluxcd/image-reflector-controller/api v0.31.0
github.com/fluxcd/image-reflector-controller/api v0.30.0
github.com/fluxcd/kustomize-controller/api v1.1.1
github.com/fluxcd/notification-controller/api v1.2.1
github.com/fluxcd/notification-controller/api v1.1.0
github.com/fluxcd/pkg/apis/event v0.6.0
github.com/fluxcd/pkg/apis/meta v1.2.0
github.com/fluxcd/pkg/git v0.15.0
@@ -26,10 +26,10 @@ require (
github.com/fluxcd/pkg/runtime v0.43.0
github.com/fluxcd/pkg/sourceignore v0.3.5
github.com/fluxcd/pkg/ssa v0.34.0
github.com/fluxcd/pkg/ssh v0.10.0
github.com/fluxcd/pkg/ssh v0.9.0
github.com/fluxcd/pkg/tar v0.4.0
github.com/fluxcd/pkg/version v0.2.2
github.com/fluxcd/source-controller/api v1.2.1
github.com/fluxcd/source-controller/api v1.1.2
github.com/go-git/go-git/v5 v5.10.0
github.com/go-logr/logr v1.3.0
github.com/gonvenience/bunt v1.3.5
@@ -47,8 +47,8 @@ require (
github.com/spf13/cobra v1.8.0
github.com/spf13/pflag v1.0.5
github.com/theckman/yacspin v0.13.12
golang.org/x/crypto v0.16.0
golang.org/x/term v0.15.0
golang.org/x/crypto v0.15.0
golang.org/x/term v0.14.0
golang.org/x/text v0.14.0
k8s.io/api v0.28.4
k8s.io/apiextensions-apiserver v0.28.4
@@ -119,9 +119,9 @@ require (
github.com/go-fed/httpsig v1.1.0 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-git/go-billy/v5 v5.5.0 // indirect
github.com/go-openapi/jsonpointer v0.20.0 // indirect
github.com/go-openapi/jsonpointer v0.19.6 // indirect
github.com/go-openapi/jsonreference v0.20.2 // indirect
github.com/go-openapi/swag v0.22.4 // indirect
github.com/go-openapi/swag v0.22.3 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang-jwt/jwt/v5 v5.0.0 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
@@ -144,7 +144,7 @@ require (
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/go-retryablehttp v0.7.5 // indirect
github.com/hashicorp/go-version v1.6.0 // indirect
github.com/hashicorp/go-version v1.5.0 // indirect
github.com/hashicorp/golang-lru/arc/v2 v2.0.5 // indirect
github.com/hashicorp/golang-lru/v2 v2.0.5 // indirect
github.com/imdario/mergo v0.3.15 // indirect
@@ -197,14 +197,14 @@ require (
go.starlark.net v0.0.0-20230525235612-a134d8f9ddca // indirect
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e // indirect
golang.org/x/mod v0.14.0 // indirect
golang.org/x/net v0.19.0 // indirect
golang.org/x/oauth2 v0.15.0 // indirect
golang.org/x/net v0.18.0 // indirect
golang.org/x/oauth2 v0.14.0 // indirect
golang.org/x/sync v0.5.0 // indirect
golang.org/x/sys v0.15.0 // indirect
golang.org/x/time v0.5.0 // indirect
golang.org/x/tools v0.16.0 // indirect
golang.org/x/sys v0.14.0 // indirect
golang.org/x/time v0.4.0 // indirect
golang.org/x/tools v0.15.0 // indirect
gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
google.golang.org/appengine v1.6.8 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/protobuf v1.31.0 // indirect
gopkg.in/evanphx/json-patch.v5 v5.6.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
@@ -212,9 +212,9 @@ require (
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/component-base v0.28.4 // indirect
k8s.io/klog/v2 v2.110.1 // indirect
k8s.io/klog/v2 v2.100.1 // indirect
k8s.io/kube-openapi v0.0.0-20231113174909-778a5567bc1e // indirect
k8s.io/utils v0.0.0-20231127182322-b307cd553661 // indirect
k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
)

70
go.sum
View File

@@ -141,12 +141,12 @@ github.com/fluxcd/helm-controller/api v0.36.2 h1:9JaTc91yocG1oQkM/GMfpZ/nGPpliRj
github.com/fluxcd/helm-controller/api v0.36.2/go.mod h1:zkcRy3PxG0NoxSY5SjiSA5tWOGa6spIbWsChQY8FXqM=
github.com/fluxcd/image-automation-controller/api v0.36.1 h1:Knd4SSm/bJ0iqvYcQq87uDaT/mW9poM1jOvHpJ/4tzk=
github.com/fluxcd/image-automation-controller/api v0.36.1/go.mod h1:IsjdBtgm71KHRGDTZZZiGYdWGoJ5VjenE9F4ueADM/o=
github.com/fluxcd/image-reflector-controller/api v0.31.0 h1:9t5zj7ufo9/d57gpcZsD6YAIxFz0vtLH4jXCj379srE=
github.com/fluxcd/image-reflector-controller/api v0.31.0/go.mod h1:/EcjOa/EYi8sSBHcGrBkLjMjNNfC7xXMxqG9phBORCo=
github.com/fluxcd/image-reflector-controller/api v0.30.0 h1:DiWj+4tcbnaSqZs1Pfkyt3uSy47wg3dsNMgbFE50pYc=
github.com/fluxcd/image-reflector-controller/api v0.30.0/go.mod h1:hv57KwIzoPyy7Vu4PFcIf21eu0N3p/HbijygcuNgf8c=
github.com/fluxcd/kustomize-controller/api v1.1.1 h1:pQcAzvBC3cFGOCgk0zrcsO1Kjtal1tvd6rHXkyp2R78=
github.com/fluxcd/kustomize-controller/api v1.1.1/go.mod h1:FHJTX6c0+CznUNGMol5+Uc4lQsYRxWgpmIRK/xDCghA=
github.com/fluxcd/notification-controller/api v1.2.1 h1:yPQZ7ppYvazxl/Vsespbx9VlVyopH7p8pKGzZTNxI0c=
github.com/fluxcd/notification-controller/api v1.2.1/go.mod h1:1sWTpAXkga5zkXOgFoR+NIJgHNclBcfAHUJ/rMAjnYY=
github.com/fluxcd/notification-controller/api v1.1.0 h1:qx5t5DmArfPLxG2bsYZXIqDXLnijUddlHeRfciALwQw=
github.com/fluxcd/notification-controller/api v1.1.0/go.mod h1:6MqWVQeI5yrYR7zp0GFqsfXitNPGJrnfOWxO2w3jylg=
github.com/fluxcd/pkg/apis/acl v0.1.0 h1:EoAl377hDQYL3WqanWCdifauXqXbMyFuK82NnX6pH4Q=
github.com/fluxcd/pkg/apis/acl v0.1.0/go.mod h1:zfEZzz169Oap034EsDhmCAGgnWlcWmIObZjYMusoXS8=
github.com/fluxcd/pkg/apis/event v0.6.0 h1:AUaeee1CGWb65BLqVximHXG8Gcu6vWuYONIq6tVpjgo=
@@ -170,14 +170,14 @@ github.com/fluxcd/pkg/sourceignore v0.3.5 h1:omcHTH5X5tlPr9w1b9T7WuJTOP+o/KdVdar
github.com/fluxcd/pkg/sourceignore v0.3.5/go.mod h1:6Xz3jErz8RsidsdrjUBBUGKes24rbdp/F38MnTGibEw=
github.com/fluxcd/pkg/ssa v0.34.0 h1:hpMo0D7G3faieRYH39e9YD8Jl+aC2hTgUep8ojG5+LE=
github.com/fluxcd/pkg/ssa v0.34.0/go.mod h1:rhVh0EtYVUOznKXlz6E7JOSgdc8xWbIwA4L5HVtJRLA=
github.com/fluxcd/pkg/ssh v0.10.0 h1:JFz0u/CPEf3hXvmrEvUvXsc70eKh8xphqjXxZuSby9g=
github.com/fluxcd/pkg/ssh v0.10.0/go.mod h1:1lFTj3MhU9xQuaJ5PJJoh/FyRYzK54ll9NY/s2KqOZM=
github.com/fluxcd/pkg/ssh v0.9.0 h1:egRvg4AKarObFKXsBDZ5oBZ5PCjmQ4Q6hX+6GmrdbH0=
github.com/fluxcd/pkg/ssh v0.9.0/go.mod h1:3KKbfcFn4l+HqYdHKqcu2LO8RvFv0Kh6tYRSUtONC/8=
github.com/fluxcd/pkg/tar v0.4.0 h1:SuXpfXBIcSJ5R/yqQi2CBxBmV/i/LH0agqNAh2PWBZg=
github.com/fluxcd/pkg/tar v0.4.0/go.mod h1:SyJBaQvuv2VA/rv4d1OHhCV6R8+9QKc9np193EzNHBc=
github.com/fluxcd/pkg/version v0.2.2 h1:ZpVXECeLA5hIQMft11iLp6gN3cKcz6UNuVTQPw/bRdI=
github.com/fluxcd/pkg/version v0.2.2/go.mod h1:NGnh/no8S6PyfCDxRFrPY3T5BUnqP48MxfxNRU0z8C0=
github.com/fluxcd/source-controller/api v1.2.1 h1:zWRfwMB/BkV0A/duZQwHEcrE/abWJQt7DUgYTzn4IzM=
github.com/fluxcd/source-controller/api v1.2.1/go.mod h1:n+PMCJXl/AYCjWHn5N/MUuoIFKfDg4kCDviFIa7fDvI=
github.com/fluxcd/source-controller/api v1.1.2 h1:FfKDKVWnopo+Q2pOAxgHEjrtr4MP41L8aapR4mqBhBk=
github.com/fluxcd/source-controller/api v1.1.2/go.mod h1:ZLkaUd1KQIjtLPCvO63Ni5zpnSTVBAkeRgFBzMItbDQ=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY=
@@ -195,17 +195,16 @@ github.com/go-git/go-git/v5 v5.10.0/go.mod h1:1FOZ/pQnqw24ghP2n7cunVl0ON55BsjPYv
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY=
github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/zapr v1.2.4 h1:QHVo+6stLbfJmYGkQ7uGHUCu5hnAFAj6mDe6Ea0SeOo=
github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE=
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
github.com/go-openapi/jsonpointer v0.20.0 h1:ESKJdU9ASRfaPNOPRx12IUyA1vn3R9GiE3KYD14BXdQ=
github.com/go-openapi/jsonpointer v0.20.0/go.mod h1:6PGzBjjIIumbLYysB73Klnms1mwnU4G3YHOECG3CedA=
github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=
github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g=
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU=
github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA=
@@ -228,7 +227,6 @@ github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:W
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/gomodule/redigo v1.8.2 h1:H5XSIre1MB5NbPYFp+i1NBbb5qN1W8Y8YAQoAYbkm8k=
@@ -293,9 +291,8 @@ github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+l
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
github.com/hashicorp/go-retryablehttp v0.7.5 h1:bJj+Pj19UZMIweq/iie+1u5YCdGrnxCT9yvm0e+Nd5M=
github.com/hashicorp/go-retryablehttp v0.7.5/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8=
github.com/hashicorp/go-version v1.5.0 h1:O293SZ2Eg+AAYijkVK3jR786Am1bhDEh2GHT0tIVE5E=
github.com/hashicorp/go-version v1.5.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek=
github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/golang-lru/arc/v2 v2.0.5 h1:l2zaLDubNhW4XO3LnliVj0GXO3+/CGNJAg1dcN2Fpfw=
github.com/hashicorp/golang-lru/arc/v2 v2.0.5/go.mod h1:ny6zBSQZi2JxIeYcv7kt2sH2PXJtirBN7RDhRpxPkxU=
github.com/hashicorp/golang-lru/v2 v2.0.5 h1:wW7h1TG88eUIJ2i69gaE3uNVtEPIagzhGvHgwfx2Vm4=
@@ -497,8 +494,8 @@ golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY=
golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA=
golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e h1:+WEEuIdZHnUeJJmEUjyYC2gfUMj69yZXw17EnHg/otA=
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e/go.mod h1:Kr81I6Kryrl9sr8s2FK3vxD90NdsKWRuOIl2O4CvYbA=
@@ -517,6 +514,7 @@ golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73r
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@@ -527,11 +525,11 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg=
golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.15.0 h1:s8pnnxNVzjWyrvYdFUQq5llS1PX2zhPXmccZv99h7uQ=
golang.org/x/oauth2 v0.15.0/go.mod h1:q48ptWNTY5XWf+JNten23lcvHpLJ0ZSxF5ttTHKVCAM=
golang.org/x/oauth2 v0.14.0 h1:P0Vrf/2538nmC0H+pEQ3MNFRRnVR7RlqyVw+bvm26z0=
golang.org/x/oauth2 v0.14.0/go.mod h1:lAtNWgaWfL4cm7j2OV8TxGi9Qb7ECORx8DktCY74OwM=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -571,28 +569,28 @@ golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q=
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4=
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
golang.org/x/term v0.14.0 h1:LGK9IlZ8T9jvdy6cTdfKUCltatMFOehAQo9SRC46UQ8=
golang.org/x/term v0.14.0/go.mod h1:TySc+nGkYR6qt8km8wUhuFRTVSMIX3XPR58y2lC8vww=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/time v0.4.0 h1:Z81tqI5ddIoXDPvVQ7/7CC9TnLM7ubaFG2qXYd5BbYY=
golang.org/x/time v0.4.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
@@ -603,8 +601,8 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.16.0 h1:GO788SKMRunPIBCXiQyo2AaexLstOrVhuAL5YwsckQM=
golang.org/x/tools v0.16.0/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0=
golang.org/x/tools v0.15.0 h1:zdAyfUGbYmuVokhzVmghFl2ZJh5QhcfebBgmVPFYA+8=
golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -613,8 +611,8 @@ gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw
gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
@@ -668,14 +666,14 @@ k8s.io/client-go v0.28.4 h1:Np5ocjlZcTrkyRJ3+T3PkXDpe4UpatQxj85+xjaD2wY=
k8s.io/client-go v0.28.4/go.mod h1:0VDZFpgoZfelyP5Wqu0/r/TRYcLYuJ2U1KEeoaPa1N4=
k8s.io/component-base v0.28.4 h1:c/iQLWPdUgI90O+T9TeECg8o7N3YJTiuz2sKxILYcYo=
k8s.io/component-base v0.28.4/go.mod h1:m9hR0uvqXDybiGL2nf/3Lf0MerAfQXzkfWhUY58JUbU=
k8s.io/klog/v2 v2.110.1 h1:U/Af64HJf7FcwMcXyKm2RPM22WZzyR7OSpYj5tg3cL0=
k8s.io/klog/v2 v2.110.1/go.mod h1:YGtd1984u+GgbuZ7e08/yBuAfKLSO0+uR1Fhi6ExXjo=
k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg=
k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=
k8s.io/kube-openapi v0.0.0-20231113174909-778a5567bc1e h1:snPmy96t93RredGRjKfMFt+gvxuVAncqSAyBveJtr4Q=
k8s.io/kube-openapi v0.0.0-20231113174909-778a5567bc1e/go.mod h1:AsvuZPBlUDVuCdzJ87iajxtXuR9oktsTctW/R9wwouA=
k8s.io/kubectl v0.28.4 h1:gWpUXW/T7aFne+rchYeHkyB8eVDl5UZce8G4X//kjUQ=
k8s.io/kubectl v0.28.4/go.mod h1:CKOccVx3l+3MmDbkXtIUtibq93nN2hkDR99XDCn7c/c=
k8s.io/utils v0.0.0-20231127182322-b307cd553661 h1:FepOBzJ0GXm8t0su67ln2wAZjbQ6RxQGZDnzuLcrUTI=
k8s.io/utils v0.0.0-20231127182322-b307cd553661/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI=
k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
sigs.k8s.io/controller-runtime v0.16.3 h1:2TuvuokmfXvDUamSx1SuAOO3eTyye+47mJCigwG62c4=
sigs.k8s.io/controller-runtime v0.16.3/go.mod h1:j7bialYoSn142nv9sCOJmQgDXQXxnroFU4VnX/brVJ0=
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=

View File

@@ -46,7 +46,7 @@ import (
imagereflectv1 "github.com/fluxcd/image-reflector-controller/api/v1beta2"
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1"
notificationv1 "github.com/fluxcd/notification-controller/api/v1"
notificationv1b3 "github.com/fluxcd/notification-controller/api/v1beta3"
notificationv1b2 "github.com/fluxcd/notification-controller/api/v1beta2"
"github.com/fluxcd/pkg/apis/meta"
runclient "github.com/fluxcd/pkg/runtime/client"
"github.com/fluxcd/pkg/version"
@@ -133,7 +133,7 @@ func NewScheme() *apiruntime.Scheme {
_ = kustomizev1.AddToScheme(scheme)
_ = helmv2.AddToScheme(scheme)
_ = notificationv1.AddToScheme(scheme)
_ = notificationv1b3.AddToScheme(scheme)
_ = notificationv1b2.AddToScheme(scheme)
_ = imagereflectv1.AddToScheme(scheme)
_ = imageautov1.AddToScheme(scheme)
return scheme

View File

@@ -1,8 +1,8 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- https://github.com/fluxcd/image-reflector-controller/releases/download/v0.31.0/image-reflector-controller.crds.yaml
- https://github.com/fluxcd/image-reflector-controller/releases/download/v0.31.0/image-reflector-controller.deployment.yaml
- https://github.com/fluxcd/image-reflector-controller/releases/download/v0.30.0/image-reflector-controller.crds.yaml
- https://github.com/fluxcd/image-reflector-controller/releases/download/v0.30.0/image-reflector-controller.deployment.yaml
- account.yaml
transformers:
- labels.yaml

View File

@@ -1,8 +1,8 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- https://github.com/fluxcd/notification-controller/releases/download/v1.2.1/notification-controller.crds.yaml
- https://github.com/fluxcd/notification-controller/releases/download/v1.2.1/notification-controller.deployment.yaml
- https://github.com/fluxcd/notification-controller/releases/download/v1.1.0/notification-controller.crds.yaml
- https://github.com/fluxcd/notification-controller/releases/download/v1.1.0/notification-controller.deployment.yaml
- account.yaml
transformers:
- labels.yaml

View File

@@ -1,8 +1,8 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- https://github.com/fluxcd/source-controller/releases/download/v1.2.1/source-controller.crds.yaml
- https://github.com/fluxcd/source-controller/releases/download/v1.2.1/source-controller.deployment.yaml
- https://github.com/fluxcd/source-controller/releases/download/v1.1.2/source-controller.crds.yaml
- https://github.com/fluxcd/source-controller/releases/download/v1.1.2/source-controller.deployment.yaml
- account.yaml
transformers:
- labels.yaml

View File

@@ -1,9 +1,9 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- https://github.com/fluxcd/source-controller/releases/download/v1.2.1/source-controller.crds.yaml
- https://github.com/fluxcd/source-controller/releases/download/v1.1.2/source-controller.crds.yaml
- https://github.com/fluxcd/kustomize-controller/releases/download/v1.1.1/kustomize-controller.crds.yaml
- https://github.com/fluxcd/helm-controller/releases/download/v0.36.2/helm-controller.crds.yaml
- https://github.com/fluxcd/notification-controller/releases/download/v1.2.1/notification-controller.crds.yaml
- https://github.com/fluxcd/image-reflector-controller/releases/download/v0.31.0/image-reflector-controller.crds.yaml
- https://github.com/fluxcd/notification-controller/releases/download/v1.1.0/notification-controller.crds.yaml
- https://github.com/fluxcd/image-reflector-controller/releases/download/v0.30.0/image-reflector-controller.crds.yaml
- https://github.com/fluxcd/image-automation-controller/releases/download/v0.36.1/image-automation-controller.crds.yaml

View File

@@ -20,6 +20,7 @@ import (
"context"
"errors"
"fmt"
"k8s.io/apimachinery/pkg/util/wait"
"strings"
"time"
@@ -27,17 +28,12 @@ import (
apierr "k8s.io/apimachinery/pkg/api/errors"
apimeta "k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
apierrors "k8s.io/apimachinery/pkg/util/errors"
"k8s.io/apimachinery/pkg/util/wait"
"sigs.k8s.io/controller-runtime/pkg/client"
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1"
"github.com/fluxcd/pkg/apis/meta"
sourcev1 "github.com/fluxcd/source-controller/api/v1"
sourcev1b2 "github.com/fluxcd/source-controller/api/v1beta2"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta2"
"github.com/fluxcd/flux2/v2/pkg/manifestgen/install"
"github.com/fluxcd/flux2/v2/pkg/manifestgen/sourcesecret"
@@ -48,11 +44,6 @@ var (
ErrReconciledWithWarning = errors.New("reconciled with warning")
)
// Reconciler reconciles and reports the health of different
// components and kubernetes resources involved in the installation of Flux.
//
// It is recommended use the `ReconcilerWithSyncCheck` interface that also
// reports the health of the GitRepository.
type Reconciler interface {
// ReconcileComponents reconciles the components by generating the
// manifests with the provided values, committing them to Git and
@@ -85,14 +76,6 @@ type RepositoryReconciler interface {
ReconcileRepository(ctx context.Context) error
}
// ReconcilerWithSyncCheck extends the Reconciler interface to also report the health of the GitReposiotry
// that syncs Flux on the cluster
type ReconcilerWithSyncCheck interface {
Reconciler
// ReportGitRepoHealth reports about the health of the GitRepository synchronizing the components.
ReportGitRepoHealth(ctx context.Context, options sync.Options, pollInterval, timeout time.Duration) error
}
type PostGenerateSecretFunc func(ctx context.Context, secret corev1.Secret, options sourcesecret.Options) error
func Run(ctx context.Context, reconciler Reconciler, manifestsBase string,
@@ -116,22 +99,18 @@ func Run(ctx context.Context, reconciler Reconciler, manifestsBase string,
return err
}
var errs []error
if r, ok := reconciler.(ReconcilerWithSyncCheck); ok {
if err := r.ReportGitRepoHealth(ctx, syncOpts, pollInterval, timeout); err != nil {
errs = append(errs, err)
}
}
var healthErrCount int
if err := reconciler.ReportKustomizationHealth(ctx, syncOpts, pollInterval, timeout); err != nil {
errs = append(errs, err)
healthErrCount++
}
if err := reconciler.ReportComponentsHealth(ctx, installOpts, timeout); err != nil {
errs = append(errs, err)
healthErrCount++
}
if len(errs) > 0 {
err = fmt.Errorf("bootstrap failed with %d health check failure(s): %w", len(errs), apierrors.NewAggregate(errs))
if healthErrCount > 0 {
// Composing a "smart" error message here from the returned
// errors does not result in any useful information for the
// user, as both methods log the failures they run into.
err = fmt.Errorf("bootstrap failed with %d health check failure(s)", healthErrCount)
}
return err
@@ -194,47 +173,32 @@ func kustomizationPathDiffers(ctx context.Context, kube client.Client, objKey cl
return k.Spec.Path, nil
}
type objectWithConditions interface {
client.Object
GetConditions() []metav1.Condition
}
func objectReconciled(kube client.Client, objKey client.ObjectKey, clientObject objectWithConditions, expectRevision string) wait.ConditionWithContextFunc {
func kustomizationReconciled(kube client.Client, objKey client.ObjectKey, kustomization *kustomizev1.Kustomization, expectRevision string) wait.ConditionWithContextFunc {
return func(ctx context.Context) (bool, error) {
// for some reason, TypeMeta gets unset after kube.Get so we want to store the GVK and set it after
// ref https://github.com/kubernetes-sigs/controller-runtime/issues/1517#issuecomment-844703142
gvk := clientObject.GetObjectKind().GroupVersionKind()
if err := kube.Get(ctx, objKey, clientObject); err != nil {
return false, err
}
clientObject.GetObjectKind().SetGroupVersionKind(gvk)
kind := gvk.Kind
obj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(clientObject)
if err != nil {
if err := kube.Get(ctx, objKey, kustomization); err != nil {
return false, err
}
// Detect suspended object, as this would result in an endless wait
if suspended, ok, _ := unstructured.NestedBool(obj, "spec", "suspend"); ok && suspended {
return false, fmt.Errorf("%s '%s' is suspended", kind, objKey.String())
// Detect suspended Kustomization, as this would result in an endless wait
if kustomization.Spec.Suspend {
return false, fmt.Errorf("Kustomization is suspended")
}
// Confirm the state we are observing is for the current generation
if generation, ok, _ := unstructured.NestedInt64(obj, "status", "observedGeneration"); ok && generation != clientObject.GetGeneration() {
if kustomization.Generation != kustomization.Status.ObservedGeneration {
return false, nil
}
// Confirm the given revision has been attempted by the controller
if sourcev1.TransformLegacyRevision(kustomization.Status.LastAttemptedRevision) != expectRevision {
return false, nil
}
// Confirm the resource is healthy
if c := apimeta.FindStatusCondition(clientObject.GetConditions(), meta.ReadyCondition); c != nil {
if c := apimeta.FindStatusCondition(kustomization.Status.Conditions, meta.ReadyCondition); c != nil {
switch c.Status {
case metav1.ConditionTrue:
// Confirm the given revision has been attempted by the controller
hasRev, err := hasRevision(kind, obj, expectRevision)
if err != nil {
return false, err
}
return hasRev, nil
return true, nil
case metav1.ConditionFalse:
return false, fmt.Errorf(c.Message)
}
@@ -243,21 +207,6 @@ func objectReconciled(kube client.Client, objKey client.ObjectKey, clientObject
}
}
// hasRevision checks that the reconciled revision (for Kustomization this is `.status.lastAttemptedRevision`
// and for Source APIs, it is stored in `.status.artifact.revision`) is the same as the expectedRev
func hasRevision(kind string, obj map[string]interface{}, expectedRev string) (bool, error) {
var rev string
switch kind {
case sourcev1.GitRepositoryKind, sourcev1b2.OCIRepositoryKind, sourcev1b2.BucketKind, sourcev1b2.HelmChartKind:
rev, _, _ = unstructured.NestedString(obj, "status", "artifact", "revision")
case kustomizev1.KustomizationKind:
rev, _, _ = unstructured.NestedString(obj, "status", "lastAttemptedRevision")
default:
return false, fmt.Errorf("cannot get status revision for kind: '%s'", kind)
}
return sourcev1b2.TransformLegacyRevision(rev) == expectedRev, nil
}
func retry(retries int, wait time.Duration, fn func() error) (err error) {
for i := 0; ; i++ {
err = fn()

View File

@@ -29,8 +29,6 @@ import (
"github.com/ProtonMail/go-crypto/openpgp"
gogit "github.com/go-git/go-git/v5"
corev1 "k8s.io/api/core/v1"
apimeta "k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/cli-runtime/pkg/genericclioptions"
@@ -40,12 +38,10 @@ import (
"github.com/fluxcd/cli-utils/pkg/object"
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1"
"github.com/fluxcd/pkg/apis/meta"
"github.com/fluxcd/pkg/git"
"github.com/fluxcd/pkg/git/repository"
"github.com/fluxcd/pkg/kustomize/filesys"
runclient "github.com/fluxcd/pkg/runtime/client"
sourcev1 "github.com/fluxcd/source-controller/api/v1"
"github.com/fluxcd/flux2/v2/internal/utils"
"github.com/fluxcd/flux2/v2/pkg/log"
@@ -174,11 +170,11 @@ func (b *PlainGitBootstrapper) ReconcileComponents(ctx context.Context, manifest
manifests.Path: strings.NewReader(manifests.Content),
}), repository.WithSigner(signer))
if err != nil && err != git.ErrNoStagedFiles {
return fmt.Errorf("failed to commit component manifests: %w", err)
return fmt.Errorf("failed to commit sync manifests: %w", err)
}
if err == nil {
b.logger.Successf("committed component manifests to %q (%q)", b.branch, commit)
b.logger.Successf("committed sync manifests to %q (%q)", b.branch, commit)
b.logger.Actionf("pushing component manifests to %q", b.url)
if err = b.gitClient.Push(ctx, repository.PushConfig{}); err != nil {
return fmt.Errorf("failed to push manifests: %w", err)
@@ -401,62 +397,20 @@ func (b *PlainGitBootstrapper) ReportKustomizationHealth(ctx context.Context, op
objKey := client.ObjectKey{Name: options.Name, Namespace: options.Namespace}
expectRevision := fmt.Sprintf("%s@%s", options.Branch, git.Hash(head).Digest())
b.logger.Waitingf("waiting for Kustomization %q to be reconciled", objKey.String())
k := &kustomizev1.Kustomization{
TypeMeta: metav1.TypeMeta{
Kind: kustomizev1.KustomizationKind,
},
}
expectRevision := fmt.Sprintf("%s@%s", options.Branch, git.Hash(head).Digest())
var k kustomizev1.Kustomization
if err := wait.PollUntilContextTimeout(ctx, pollInterval, timeout, true,
objectReconciled(b.kube, objKey, k, expectRevision)); err != nil {
// If the poll timed out, we want to log the ready condition message as
// that likely contains the reason
if errors.Is(err, context.DeadlineExceeded) {
readyCondition := apimeta.FindStatusCondition(k.Status.Conditions, meta.ReadyCondition)
if readyCondition != nil && readyCondition.Status != metav1.ConditionTrue {
err = fmt.Errorf("kustomization '%s' not ready: '%s'", objKey, readyCondition.Message)
}
}
kustomizationReconciled(b.kube, objKey, &k, expectRevision)); err != nil {
b.logger.Failuref(err.Error())
return fmt.Errorf("error while waiting for Kustomization to be ready: '%s'", err)
return err
}
b.logger.Successf("Kustomization reconciled successfully")
return nil
}
func (b *PlainGitBootstrapper) ReportGitRepoHealth(ctx context.Context, options sync.Options, pollInterval, timeout time.Duration) error {
head, err := b.gitClient.Head()
if err != nil {
return err
}
objKey := client.ObjectKey{Name: options.Name, Namespace: options.Namespace}
b.logger.Waitingf("waiting for GitRepository %q to be reconciled", objKey.String())
expectRevision := fmt.Sprintf("%s@%s", options.Branch, git.Hash(head).Digest())
g := &sourcev1.GitRepository{
TypeMeta: metav1.TypeMeta{
Kind: sourcev1.GitRepositoryKind,
APIVersion: sourcev1.GroupVersion.String(),
},
}
if err := wait.PollUntilContextTimeout(ctx, pollInterval, timeout, true,
objectReconciled(b.kube, objKey, g, expectRevision)); err != nil {
// If the poll timed out, we want to log the ready condition message as
// that likely contains the reason
if errors.Is(err, context.DeadlineExceeded) {
readyCondition := apimeta.FindStatusCondition(g.Status.Conditions, meta.ReadyCondition)
if readyCondition != nil && readyCondition.Status != metav1.ConditionTrue {
err = fmt.Errorf("gitrepository '%s' not ready: '%s'", objKey, readyCondition.Message)
}
}
b.logger.Failuref(err.Error())
return fmt.Errorf("error while waiting for GitRepository to be ready: '%s'", err)
}
b.logger.Successf("GitRepsoitory reconciled successfully")
return nil
}
func (b *PlainGitBootstrapper) ReportComponentsHealth(ctx context.Context, install install.Options, timeout time.Duration) error {
cfg, err := utils.KubeConfig(b.restClientGetter, b.restClientOptions)
if err != nil {

View File

@@ -1,469 +0,0 @@
/*
Copyright 2023 The Flux authors
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 bootstrap
import (
"context"
"testing"
"github.com/fluxcd/pkg/apis/meta"
. "github.com/onsi/gomega"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/client/fake"
"github.com/fluxcd/flux2/v2/internal/utils"
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta2"
sourcev1 "github.com/fluxcd/source-controller/api/v1"
sourcev1b2 "github.com/fluxcd/source-controller/api/v1beta2"
)
func Test_hasRevision(t *testing.T) {
var revision = "main@sha1:5bf3a8f9bb0aa5ae8afd6208f43757ab73fc033a"
tests := []struct {
name string
obj objectWithConditions
rev string
expectErr bool
expectedBool bool
}{
{
name: "Kustomization revision",
obj: &kustomizev1.Kustomization{
TypeMeta: metav1.TypeMeta{
Kind: kustomizev1.KustomizationKind,
},
Status: kustomizev1.KustomizationStatus{
LastAttemptedRevision: "main@sha1:5bf3a8f9bb0aa5ae8afd6208f43757ab73fc033a",
},
},
expectedBool: true,
},
{
name: "GitRepository revision",
obj: &sourcev1.GitRepository{
TypeMeta: metav1.TypeMeta{
Kind: sourcev1.GitRepositoryKind,
APIVersion: sourcev1.GroupVersion.String(),
},
Status: sourcev1.GitRepositoryStatus{
Artifact: &sourcev1.Artifact{
Revision: "main@sha1:5bf3a8f9bb0aa5ae8afd6208f43757ab73fc033a",
},
},
},
expectedBool: true,
},
{
name: "GitRepository revision (wrong revision)",
obj: &sourcev1.GitRepository{
TypeMeta: metav1.TypeMeta{
Kind: sourcev1.GitRepositoryKind,
APIVersion: sourcev1.GroupVersion.String(),
},
Status: sourcev1.GitRepositoryStatus{
Artifact: &sourcev1.Artifact{
Revision: "main@sha1:e7f3a8f9bb0aa5ae8afd6208f43757ab73fc043a",
},
},
},
},
{
name: "Kustomization revision (empty revision)",
obj: &kustomizev1.Kustomization{
TypeMeta: metav1.TypeMeta{
Kind: kustomizev1.KustomizationKind,
},
Status: kustomizev1.KustomizationStatus{
LastAttemptedRevision: "",
},
},
},
{
name: "OCIRepository revision",
obj: &sourcev1b2.OCIRepository{
TypeMeta: metav1.TypeMeta{
Kind: sourcev1b2.OCIRepositoryKind,
},
Status: sourcev1b2.OCIRepositoryStatus{
Artifact: &sourcev1.Artifact{
Revision: "main@sha1:5bf3a8f9bb0aa5ae8afd6208f43757ab73fc033a",
},
},
},
expectedBool: true,
},
{
name: "Alert revision(Not supported)",
obj: &notificationv1.Alert{
TypeMeta: metav1.TypeMeta{
Kind: notificationv1.AlertKind,
},
Status: notificationv1.AlertStatus{
ObservedGeneration: 1,
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
g := NewWithT(t)
obj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(tt.obj)
g.Expect(err).To(BeNil())
got, err := hasRevision(tt.obj.GetObjectKind().GroupVersionKind().Kind, obj, revision)
if tt.expectErr {
g.Expect(err).To(HaveOccurred())
return
}
g.Expect(got).To(Equal(tt.expectedBool))
})
}
}
func Test_objectReconciled(t *testing.T) {
expectedRev := "main@sha1:5bf3a8f9bb0aa5ae8afd6208f43757ab73fc033a"
type updateStatus struct {
statusFn func(o client.Object)
expectedErr bool
expectedBool bool
}
tests := []struct {
name string
obj objectWithConditions
statuses []updateStatus
}{
{
name: "GitRepository with no status",
obj: &sourcev1.GitRepository{
TypeMeta: metav1.TypeMeta{
Kind: sourcev1.GitRepositoryKind,
APIVersion: sourcev1.GroupVersion.String(),
},
ObjectMeta: metav1.ObjectMeta{
Name: "flux-system",
Namespace: "flux-system",
},
},
statuses: []updateStatus{
{
expectedErr: false,
expectedBool: false,
},
},
},
{
name: "suspended Kustomization",
obj: &kustomizev1.Kustomization{
TypeMeta: metav1.TypeMeta{
Kind: kustomizev1.KustomizationKind,
APIVersion: kustomizev1.GroupVersion.String(),
},
ObjectMeta: metav1.ObjectMeta{
Name: "flux-system",
Namespace: "flux-system",
},
Spec: kustomizev1.KustomizationSpec{
Suspend: true,
},
},
statuses: []updateStatus{
{
expectedErr: true,
},
},
},
{
name: "Kustomization - status with old generation",
obj: &kustomizev1.Kustomization{
TypeMeta: metav1.TypeMeta{
Kind: kustomizev1.KustomizationKind,
APIVersion: kustomizev1.GroupVersion.String(),
},
ObjectMeta: metav1.ObjectMeta{
Name: "flux-system",
Namespace: "flux-system",
Generation: 1,
},
Status: kustomizev1.KustomizationStatus{
ObservedGeneration: -1,
},
},
statuses: []updateStatus{
{
expectedErr: false,
expectedBool: false,
},
},
},
{
name: "GitRepository - status with same generation but no conditions",
obj: &sourcev1.GitRepository{
TypeMeta: metav1.TypeMeta{
Kind: sourcev1.GitRepositoryKind,
APIVersion: sourcev1.GroupVersion.String(),
},
ObjectMeta: metav1.ObjectMeta{
Name: "flux-system",
Namespace: "flux-system",
Generation: 1,
},
Status: sourcev1.GitRepositoryStatus{
ObservedGeneration: 1,
},
},
statuses: []updateStatus{
{
expectedErr: false,
expectedBool: false,
},
},
},
{
name: "GitRepository - status with conditions but no ready condition",
obj: &sourcev1.GitRepository{
TypeMeta: metav1.TypeMeta{
Kind: sourcev1.GitRepositoryKind,
APIVersion: sourcev1.GroupVersion.String(),
},
ObjectMeta: metav1.ObjectMeta{
Name: "flux-system",
Namespace: "flux-system",
Generation: 1,
},
Status: sourcev1.GitRepositoryStatus{
ObservedGeneration: 1,
Conditions: []metav1.Condition{
{Type: meta.ReconcilingCondition, Status: metav1.ConditionTrue, ObservedGeneration: 1, Reason: "Progressing", Message: "Progressing"},
},
},
},
statuses: []updateStatus{
{
expectedErr: false,
expectedBool: false,
},
},
},
{
name: "Kustomization - status with false ready condition",
obj: &kustomizev1.Kustomization{
TypeMeta: metav1.TypeMeta{
Kind: kustomizev1.KustomizationKind,
APIVersion: kustomizev1.GroupVersion.String(),
},
ObjectMeta: metav1.ObjectMeta{
Name: "flux-system",
Namespace: "flux-system",
Generation: 1,
},
Status: kustomizev1.KustomizationStatus{
ObservedGeneration: 1,
Conditions: []metav1.Condition{
{Type: meta.ReadyCondition, Status: metav1.ConditionFalse, ObservedGeneration: 1, Reason: "Failing", Message: "Failed to clone"},
},
},
},
statuses: []updateStatus{
{
expectedErr: true,
expectedBool: false,
},
},
},
{
name: "Kustomization - status with true ready condition but different revision",
obj: &kustomizev1.Kustomization{
TypeMeta: metav1.TypeMeta{
Kind: kustomizev1.KustomizationKind,
APIVersion: kustomizev1.GroupVersion.String(),
},
ObjectMeta: metav1.ObjectMeta{
Name: "flux-system",
Namespace: "flux-system",
Generation: 1,
},
Status: kustomizev1.KustomizationStatus{
ObservedGeneration: 1,
Conditions: []metav1.Condition{
{Type: meta.ReadyCondition, Status: metav1.ConditionTrue, ObservedGeneration: 1, Reason: "Passing", Message: "Applied revision"},
},
LastAttemptedRevision: "main@sha1:e7f3a8f9bb0aa5ae8afd6208f43757ab73fc043a",
},
},
statuses: []updateStatus{
{
expectedErr: false,
expectedBool: false,
},
},
},
{
name: "GitRepository - status with true ready condition but different revision",
obj: &sourcev1.GitRepository{
TypeMeta: metav1.TypeMeta{
Kind: sourcev1.GitRepositoryKind,
APIVersion: sourcev1.GroupVersion.String(),
},
ObjectMeta: metav1.ObjectMeta{
Name: "flux-system",
Namespace: "flux-system",
Generation: 1,
},
Status: sourcev1.GitRepositoryStatus{
ObservedGeneration: 1,
Conditions: []metav1.Condition{
{Type: meta.ReadyCondition, Status: metav1.ConditionTrue, ObservedGeneration: 1, Reason: "Readyyy", Message: "Cloned successfully"},
},
Artifact: &sourcev1.Artifact{
Revision: "main@sha1:e7f3a8f9bb0aa5ae8afd6208f43757ab73fc043a",
},
},
},
statuses: []updateStatus{
{
expectedErr: false,
expectedBool: false,
},
},
},
{
name: "GitRepository - ready with right revision",
obj: &sourcev1.GitRepository{
TypeMeta: metav1.TypeMeta{
Kind: sourcev1.GitRepositoryKind,
APIVersion: sourcev1.GroupVersion.String(),
},
ObjectMeta: metav1.ObjectMeta{
Name: "flux-system",
Namespace: "flux-system",
Generation: 1,
},
Status: sourcev1.GitRepositoryStatus{
ObservedGeneration: 1,
Conditions: []metav1.Condition{
{Type: meta.ReadyCondition, Status: metav1.ConditionTrue, ObservedGeneration: 1, Reason: "Readyyy", Message: "Cloned successfully"},
},
Artifact: &sourcev1.Artifact{
Revision: expectedRev,
},
},
},
statuses: []updateStatus{
{
expectedErr: false,
expectedBool: true,
},
},
},
{
name: "GitRepository - sequence of status updates before ready",
obj: &sourcev1.GitRepository{
TypeMeta: metav1.TypeMeta{
Kind: sourcev1.GitRepositoryKind,
APIVersion: sourcev1.GroupVersion.String(),
},
ObjectMeta: metav1.ObjectMeta{
Name: "flux-system",
Namespace: "flux-system",
Generation: 1,
},
},
statuses: []updateStatus{
{
// observed gen different
statusFn: func(o client.Object) {
gitRepo := o.(*sourcev1.GitRepository)
gitRepo.Status = sourcev1.GitRepositoryStatus{
ObservedGeneration: -1,
}
},
},
{
// ready failing
statusFn: func(o client.Object) {
gitRepo := o.(*sourcev1.GitRepository)
gitRepo.Status = sourcev1.GitRepositoryStatus{
ObservedGeneration: 1,
Conditions: []metav1.Condition{
{Type: meta.ReadyCondition, Status: metav1.ConditionFalse, ObservedGeneration: 1, Reason: "Not Ready", Message: "Transient connection issue"},
},
}
},
expectedErr: true,
},
{
// updated to a different revision
statusFn: func(o client.Object) {
gitRepo := o.(*sourcev1.GitRepository)
gitRepo.Status = sourcev1.GitRepositoryStatus{
ObservedGeneration: 1,
Conditions: []metav1.Condition{
{Type: meta.ReadyCondition, Status: metav1.ConditionTrue, ObservedGeneration: 1, Reason: "Readyyy", Message: "Cloned successfully"},
},
Artifact: &sourcev1.Artifact{
Revision: "wrong rev",
},
}
},
},
{
// updated to the expected revision
statusFn: func(o client.Object) {
gitRepo := o.(*sourcev1.GitRepository)
gitRepo.Status = sourcev1.GitRepositoryStatus{
ObservedGeneration: 1,
Conditions: []metav1.Condition{
{Type: meta.ReadyCondition, Status: metav1.ConditionTrue, ObservedGeneration: 1, Reason: "Readyyy", Message: "Cloned successfully"},
},
Artifact: &sourcev1.Artifact{
Revision: expectedRev,
},
}
},
expectedBool: true,
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
g := NewWithT(t)
builder := fake.NewClientBuilder().WithScheme(utils.NewScheme())
builder.WithObjects(tt.obj)
kubeClient := builder.Build()
for _, updates := range tt.statuses {
if updates.statusFn != nil {
updates.statusFn(tt.obj)
g.Expect(kubeClient.Update(context.TODO(), tt.obj)).To(Succeed())
}
waitFunc := objectReconciled(kubeClient, client.ObjectKeyFromObject(tt.obj), tt.obj, expectedRev)
got, err := waitFunc(context.TODO())
g.Expect(err != nil).To(Equal(updates.expectedErr))
g.Expect(got).To(Equal(updates.expectedBool))
}
})
}
}

View File

@@ -45,16 +45,6 @@ type StatusChecker struct {
logger log.Logger
}
func NewStatusCheckerWithClient(c client.Client, pollInterval time.Duration, timeout time.Duration, log log.Logger) (*StatusChecker, error) {
return &StatusChecker{
pollInterval: pollInterval,
timeout: timeout,
client: c,
statusPoller: polling.NewStatusPoller(c, c.RESTMapper(), polling.Options{}),
logger: log,
}, nil
}
func NewStatusChecker(kubeConfig *rest.Config, pollInterval time.Duration, timeout time.Duration, log log.Logger) (*StatusChecker, error) {
restMapper, err := runtimeclient.NewDynamicRESTMapper(kubeConfig)
if err != nil {
@@ -65,7 +55,13 @@ func NewStatusChecker(kubeConfig *rest.Config, pollInterval time.Duration, timeo
return nil, err
}
return NewStatusCheckerWithClient(c, pollInterval, timeout, log)
return &StatusChecker{
pollInterval: pollInterval,
timeout: timeout,
client: c,
statusPoller: polling.NewStatusPoller(c, restMapper, polling.Options{}),
logger: log,
}, nil
}
func (sc *StatusChecker) Assess(identifiers ...object.ObjMetadata) error {
@@ -98,7 +94,7 @@ func (sc *StatusChecker) Assess(identifiers ...object.ObjMetadata) error {
}
if coll.Error != nil || ctx.Err() == context.DeadlineExceeded {
return fmt.Errorf("timed out waiting for all resources to be ready")
return fmt.Errorf("timed out waiting for condition")
}
return nil
}

View File

@@ -35,7 +35,7 @@ import (
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1beta2"
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1"
notificationv1 "github.com/fluxcd/notification-controller/api/v1"
notificationv1b3 "github.com/fluxcd/notification-controller/api/v1beta3"
notificationv1b2 "github.com/fluxcd/notification-controller/api/v1beta2"
sourcev1 "github.com/fluxcd/source-controller/api/v1"
sourcev1b2 "github.com/fluxcd/source-controller/api/v1beta2"
)
@@ -243,7 +243,7 @@ func Finalizers(ctx context.Context, logger log.Logger, kubeClient client.Client
}
}
{
var list notificationv1b3.AlertList
var list notificationv1b2.AlertList
if err := kubeClient.List(ctx, &list, client.InNamespace("")); err == nil {
for i := range list.Items {
r := list.Items[i]
@@ -258,7 +258,7 @@ func Finalizers(ctx context.Context, logger log.Logger, kubeClient client.Client
}
}
{
var list notificationv1b3.ProviderList
var list notificationv1b2.ProviderList
if err := kubeClient.List(ctx, &list, client.InNamespace("")); err == nil {
for i := range list.Items {
r := list.Items[i]

View File

@@ -4,7 +4,7 @@
**Creation date:** 2022-03-30
**Last update:** 2023-11-28
**Last update:** 2022-10-20
## Summary
@@ -252,15 +252,17 @@ Bucket API design, where the same Kind servers different implementations: AWS S3
## Design Details
Unlike the default `HelmRepository`, the OCI `HelmRepository` does not need to
download any repository index file. The associated HelmChart can pull the chart
directly from the OCI registry based on the registry information in the
`HelmRepository` object. This makes the `HelmRepository` of type `oci` static,
not backed by a reconciler to move to a desired state. It becomes a data
container with information about the OCI registry.
In source-controller we'll add a new predicate for filtering `HelmRepositories` based on the `spec.type` field.
In source-controller, the `HelmRepositoryReconciler` will be updated to check
the `.spec.type` field of `HelmRepository` and do nothing if it is `oci`.
The current `HelmRepositoryReconciler` will handle only objects with `type: default`,
it's scope remains unchanged.
We'll introduce a new reconciler named `HelmRepositoryOCIReconciler`, that will handle
objects with `type: oci`. This reconciler will set the `HelmRepository` Ready status to
`False` if:
- the URL is not prefixed with `oci://`
- the URL is malformed and can't be parsed
- the specified credentials result in an authentication error
The current `HelmChartReconciler` will be adapted to handle both types.
@@ -275,7 +277,6 @@ The feature is enabled by default.
* **2022-08-11** Resolve chart dependencies from OCI released with [flux2 v0.32.0](https://github.com/fluxcd/flux2/releases/tag/v0.32.0)
* **2022-08-29** Contextual login for AWS, Azure and GCP released with [flux2 v0.33.0](https://github.com/fluxcd/flux2/releases/tag/v0.33.0)
* **2022-10-21** Verifying Helm charts with Cosign released with [flux2 v0.36.0](https://github.com/fluxcd/flux2/releases/tag/v0.36.0)
* **2023-11-28** Update the design of HelmRepository of type OCI to be static object [flux2 v2.2.0](https://github.com/fluxcd/flux2/releases/tag/v2.2.0)
### TODOs

View File

@@ -53,7 +53,7 @@ import (
reflectorv1beta2 "github.com/fluxcd/image-reflector-controller/api/v1beta2"
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1"
notiv1 "github.com/fluxcd/notification-controller/api/v1"
notiv1beta3 "github.com/fluxcd/notification-controller/api/v1beta3"
notiv1beta2 "github.com/fluxcd/notification-controller/api/v1beta2"
eventv1 "github.com/fluxcd/pkg/apis/event/v1beta1"
"github.com/fluxcd/pkg/apis/meta"
sourcev1 "github.com/fluxcd/source-controller/api/v1"
@@ -681,14 +681,14 @@ func TestAzureDevOpsCommitStatus(t *testing.T) {
}
return nil
})
provider := notiv1beta3.Provider{
provider := notiv1beta2.Provider{
ObjectMeta: metav1.ObjectMeta{
Name: "azuredevops",
Namespace: name,
},
}
_, err = controllerutil.CreateOrUpdate(ctx, cfg.kubeClient, &provider, func() error {
provider.Spec = notiv1beta3.ProviderSpec{
provider.Spec = notiv1beta2.ProviderSpec{
Type: "azuredevops",
Address: repoUrl,
SecretRef: &meta.LocalObjectReference{
@@ -698,14 +698,14 @@ func TestAzureDevOpsCommitStatus(t *testing.T) {
return nil
})
require.NoError(t, err)
alert := notiv1beta3.Alert{
alert := notiv1beta2.Alert{
ObjectMeta: metav1.ObjectMeta{
Name: "azuredevops",
Namespace: name,
},
}
_, err = controllerutil.CreateOrUpdate(ctx, cfg.kubeClient, &alert, func() error {
alert.Spec = notiv1beta3.AlertSpec{
alert.Spec = notiv1beta2.AlertSpec{
ProviderRef: meta.LocalObjectReference{
Name: provider.Name,
},
@@ -809,14 +809,14 @@ func TestEventHubNotification(t *testing.T) {
}
return nil
})
provider := notiv1beta3.Provider{
provider := notiv1beta2.Provider{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: name,
},
}
_, err = controllerutil.CreateOrUpdate(ctx, cfg.kubeClient, &provider, func() error {
provider.Spec = notiv1beta3.ProviderSpec{
provider.Spec = notiv1beta2.ProviderSpec{
Type: "azureeventhub",
Address: repoUrl,
SecretRef: &meta.LocalObjectReference{
@@ -826,14 +826,14 @@ func TestEventHubNotification(t *testing.T) {
return nil
})
require.NoError(t, err)
alert := notiv1beta3.Alert{
alert := notiv1beta2.Alert{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: name,
},
}
_, err = controllerutil.CreateOrUpdate(ctx, cfg.kubeClient, &alert, func() error {
alert.Spec = notiv1beta3.AlertSpec{
alert.Spec = notiv1beta2.AlertSpec{
ProviderRef: meta.LocalObjectReference{
Name: provider.Name,
},

View File

@@ -9,14 +9,14 @@ require (
github.com/Azure/azure-event-hubs-go/v3 v3.6.1
github.com/fluxcd/helm-controller/api v0.36.2
github.com/fluxcd/image-automation-controller/api v0.36.1
github.com/fluxcd/image-reflector-controller/api v0.31.0
github.com/fluxcd/image-reflector-controller/api v0.30.0
github.com/fluxcd/kustomize-controller/api v1.1.1
github.com/fluxcd/notification-controller/api v1.2.1
github.com/fluxcd/notification-controller/api v1.1.0
github.com/fluxcd/pkg/apis/event v0.6.0
github.com/fluxcd/pkg/apis/meta v1.2.0
github.com/fluxcd/pkg/git v0.15.0
github.com/fluxcd/pkg/git/gogit v0.15.0
github.com/fluxcd/source-controller/api v1.2.1
github.com/fluxcd/source-controller/api v1.1.2
github.com/go-git/go-git/v5 v5.10.0
github.com/hashicorp/hc-install v0.5.2
github.com/hashicorp/terraform-exec v0.18.1

View File

@@ -71,12 +71,12 @@ github.com/fluxcd/helm-controller/api v0.36.2 h1:9JaTc91yocG1oQkM/GMfpZ/nGPpliRj
github.com/fluxcd/helm-controller/api v0.36.2/go.mod h1:zkcRy3PxG0NoxSY5SjiSA5tWOGa6spIbWsChQY8FXqM=
github.com/fluxcd/image-automation-controller/api v0.36.1 h1:Knd4SSm/bJ0iqvYcQq87uDaT/mW9poM1jOvHpJ/4tzk=
github.com/fluxcd/image-automation-controller/api v0.36.1/go.mod h1:IsjdBtgm71KHRGDTZZZiGYdWGoJ5VjenE9F4ueADM/o=
github.com/fluxcd/image-reflector-controller/api v0.31.0 h1:9t5zj7ufo9/d57gpcZsD6YAIxFz0vtLH4jXCj379srE=
github.com/fluxcd/image-reflector-controller/api v0.31.0/go.mod h1:/EcjOa/EYi8sSBHcGrBkLjMjNNfC7xXMxqG9phBORCo=
github.com/fluxcd/image-reflector-controller/api v0.30.0 h1:DiWj+4tcbnaSqZs1Pfkyt3uSy47wg3dsNMgbFE50pYc=
github.com/fluxcd/image-reflector-controller/api v0.30.0/go.mod h1:hv57KwIzoPyy7Vu4PFcIf21eu0N3p/HbijygcuNgf8c=
github.com/fluxcd/kustomize-controller/api v1.1.1 h1:pQcAzvBC3cFGOCgk0zrcsO1Kjtal1tvd6rHXkyp2R78=
github.com/fluxcd/kustomize-controller/api v1.1.1/go.mod h1:FHJTX6c0+CznUNGMol5+Uc4lQsYRxWgpmIRK/xDCghA=
github.com/fluxcd/notification-controller/api v1.2.1 h1:yPQZ7ppYvazxl/Vsespbx9VlVyopH7p8pKGzZTNxI0c=
github.com/fluxcd/notification-controller/api v1.2.1/go.mod h1:1sWTpAXkga5zkXOgFoR+NIJgHNclBcfAHUJ/rMAjnYY=
github.com/fluxcd/notification-controller/api v1.1.0 h1:qx5t5DmArfPLxG2bsYZXIqDXLnijUddlHeRfciALwQw=
github.com/fluxcd/notification-controller/api v1.1.0/go.mod h1:6MqWVQeI5yrYR7zp0GFqsfXitNPGJrnfOWxO2w3jylg=
github.com/fluxcd/pkg/apis/acl v0.1.0 h1:EoAl377hDQYL3WqanWCdifauXqXbMyFuK82NnX6pH4Q=
github.com/fluxcd/pkg/apis/acl v0.1.0/go.mod h1:zfEZzz169Oap034EsDhmCAGgnWlcWmIObZjYMusoXS8=
github.com/fluxcd/pkg/apis/event v0.6.0 h1:AUaeee1CGWb65BLqVximHXG8Gcu6vWuYONIq6tVpjgo=
@@ -94,8 +94,8 @@ github.com/fluxcd/pkg/ssh v0.9.0 h1:egRvg4AKarObFKXsBDZ5oBZ5PCjmQ4Q6hX+6GmrdbH0=
github.com/fluxcd/pkg/ssh v0.9.0/go.mod h1:3KKbfcFn4l+HqYdHKqcu2LO8RvFv0Kh6tYRSUtONC/8=
github.com/fluxcd/pkg/version v0.2.2 h1:ZpVXECeLA5hIQMft11iLp6gN3cKcz6UNuVTQPw/bRdI=
github.com/fluxcd/pkg/version v0.2.2/go.mod h1:NGnh/no8S6PyfCDxRFrPY3T5BUnqP48MxfxNRU0z8C0=
github.com/fluxcd/source-controller/api v1.2.1 h1:zWRfwMB/BkV0A/duZQwHEcrE/abWJQt7DUgYTzn4IzM=
github.com/fluxcd/source-controller/api v1.2.1/go.mod h1:n+PMCJXl/AYCjWHn5N/MUuoIFKfDg4kCDviFIa7fDvI=
github.com/fluxcd/source-controller/api v1.1.2 h1:FfKDKVWnopo+Q2pOAxgHEjrtr4MP41L8aapR4mqBhBk=
github.com/fluxcd/source-controller/api v1.1.2/go.mod h1:ZLkaUd1KQIjtLPCvO63Ni5zpnSTVBAkeRgFBzMItbDQ=
github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY=

View File

@@ -40,7 +40,7 @@ import (
reflectorv1beta2 "github.com/fluxcd/image-reflector-controller/api/v1beta2"
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1"
notiv1 "github.com/fluxcd/notification-controller/api/v1"
notiv1beta3 "github.com/fluxcd/notification-controller/api/v1beta3"
notiv1beta2 "github.com/fluxcd/notification-controller/api/v1beta2"
"github.com/fluxcd/pkg/apis/meta"
"github.com/fluxcd/pkg/git"
"github.com/fluxcd/pkg/git/gogit"
@@ -96,7 +96,7 @@ func getKubernetesCredentials(kubeconfig, aksHost, aksCert, aksKey, aksCa string
if err != nil {
return "", nil, err
}
err = notiv1beta3.AddToScheme(scheme.Scheme)
err = notiv1beta2.AddToScheme(scheme.Scheme)
if err != nil {
return "", nil, err
}

View File

@@ -38,7 +38,7 @@ import (
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1"
notiv1 "github.com/fluxcd/notification-controller/api/v1"
notiv1beta3 "github.com/fluxcd/notification-controller/api/v1beta3"
notiv1beta2 "github.com/fluxcd/notification-controller/api/v1beta2"
"github.com/fluxcd/pkg/apis/meta"
sourcev1 "github.com/fluxcd/source-controller/api/v1"
)
@@ -109,12 +109,12 @@ metadata:
g.Expect(testEnv.Create(ctx, &secret)).To(Succeed())
defer testEnv.Delete(ctx, &secret)
provider := notiv1beta3.Provider{
provider := notiv1beta2.Provider{
ObjectMeta: metav1.ObjectMeta{
Name: "azuredevops",
Namespace: testID,
},
Spec: notiv1beta3.ProviderSpec{
Spec: notiv1beta2.ProviderSpec{
Type: "azuredevops",
Address: repoUrl,
SecretRef: &meta.LocalObjectReference{
@@ -125,12 +125,12 @@ metadata:
g.Expect(testEnv.Create(ctx, &provider)).To(Succeed())
defer testEnv.Delete(ctx, &provider)
alert := notiv1beta3.Alert{
alert := notiv1beta2.Alert{
ObjectMeta: metav1.ObjectMeta{
Name: "azuredevops",
Namespace: testID,
},
Spec: notiv1beta3.AlertSpec{
Spec: notiv1beta2.AlertSpec{
ProviderRef: meta.LocalObjectReference{
Name: provider.Name,
},

View File

@@ -7,15 +7,15 @@ require (
github.com/Azure/azure-event-hubs-go/v3 v3.6.0
github.com/fluxcd/helm-controller/api v0.36.2
github.com/fluxcd/image-automation-controller/api v0.36.1
github.com/fluxcd/image-reflector-controller/api v0.31.0
github.com/fluxcd/image-reflector-controller/api v0.30.0
github.com/fluxcd/kustomize-controller/api v1.1.1
github.com/fluxcd/notification-controller/api v1.2.1
github.com/fluxcd/notification-controller/api v1.1.0
github.com/fluxcd/pkg/apis/event v0.6.0
github.com/fluxcd/pkg/apis/meta v1.2.0
github.com/fluxcd/pkg/git v0.15.0
github.com/fluxcd/pkg/git/gogit v0.15.0
github.com/fluxcd/pkg/runtime v0.43.0
github.com/fluxcd/source-controller/api v1.2.1
github.com/fluxcd/source-controller/api v1.1.2
github.com/fluxcd/test-infra/tftestenv v0.0.0-20230831142147-627bca8e7916
github.com/go-git/go-git/v5 v5.10.0
github.com/google/go-containerregistry v0.16.1

View File

@@ -126,12 +126,12 @@ github.com/fluxcd/helm-controller/api v0.36.2 h1:9JaTc91yocG1oQkM/GMfpZ/nGPpliRj
github.com/fluxcd/helm-controller/api v0.36.2/go.mod h1:zkcRy3PxG0NoxSY5SjiSA5tWOGa6spIbWsChQY8FXqM=
github.com/fluxcd/image-automation-controller/api v0.36.1 h1:Knd4SSm/bJ0iqvYcQq87uDaT/mW9poM1jOvHpJ/4tzk=
github.com/fluxcd/image-automation-controller/api v0.36.1/go.mod h1:IsjdBtgm71KHRGDTZZZiGYdWGoJ5VjenE9F4ueADM/o=
github.com/fluxcd/image-reflector-controller/api v0.31.0 h1:9t5zj7ufo9/d57gpcZsD6YAIxFz0vtLH4jXCj379srE=
github.com/fluxcd/image-reflector-controller/api v0.31.0/go.mod h1:/EcjOa/EYi8sSBHcGrBkLjMjNNfC7xXMxqG9phBORCo=
github.com/fluxcd/image-reflector-controller/api v0.30.0 h1:DiWj+4tcbnaSqZs1Pfkyt3uSy47wg3dsNMgbFE50pYc=
github.com/fluxcd/image-reflector-controller/api v0.30.0/go.mod h1:hv57KwIzoPyy7Vu4PFcIf21eu0N3p/HbijygcuNgf8c=
github.com/fluxcd/kustomize-controller/api v1.1.1 h1:pQcAzvBC3cFGOCgk0zrcsO1Kjtal1tvd6rHXkyp2R78=
github.com/fluxcd/kustomize-controller/api v1.1.1/go.mod h1:FHJTX6c0+CznUNGMol5+Uc4lQsYRxWgpmIRK/xDCghA=
github.com/fluxcd/notification-controller/api v1.2.1 h1:yPQZ7ppYvazxl/Vsespbx9VlVyopH7p8pKGzZTNxI0c=
github.com/fluxcd/notification-controller/api v1.2.1/go.mod h1:1sWTpAXkga5zkXOgFoR+NIJgHNclBcfAHUJ/rMAjnYY=
github.com/fluxcd/notification-controller/api v1.1.0 h1:qx5t5DmArfPLxG2bsYZXIqDXLnijUddlHeRfciALwQw=
github.com/fluxcd/notification-controller/api v1.1.0/go.mod h1:6MqWVQeI5yrYR7zp0GFqsfXitNPGJrnfOWxO2w3jylg=
github.com/fluxcd/pkg/apis/acl v0.1.0 h1:EoAl377hDQYL3WqanWCdifauXqXbMyFuK82NnX6pH4Q=
github.com/fluxcd/pkg/apis/acl v0.1.0/go.mod h1:zfEZzz169Oap034EsDhmCAGgnWlcWmIObZjYMusoXS8=
github.com/fluxcd/pkg/apis/event v0.6.0 h1:AUaeee1CGWb65BLqVximHXG8Gcu6vWuYONIq6tVpjgo=
@@ -151,8 +151,8 @@ github.com/fluxcd/pkg/ssh v0.9.0 h1:egRvg4AKarObFKXsBDZ5oBZ5PCjmQ4Q6hX+6GmrdbH0=
github.com/fluxcd/pkg/ssh v0.9.0/go.mod h1:3KKbfcFn4l+HqYdHKqcu2LO8RvFv0Kh6tYRSUtONC/8=
github.com/fluxcd/pkg/version v0.2.2 h1:ZpVXECeLA5hIQMft11iLp6gN3cKcz6UNuVTQPw/bRdI=
github.com/fluxcd/pkg/version v0.2.2/go.mod h1:NGnh/no8S6PyfCDxRFrPY3T5BUnqP48MxfxNRU0z8C0=
github.com/fluxcd/source-controller/api v1.2.1 h1:zWRfwMB/BkV0A/duZQwHEcrE/abWJQt7DUgYTzn4IzM=
github.com/fluxcd/source-controller/api v1.2.1/go.mod h1:n+PMCJXl/AYCjWHn5N/MUuoIFKfDg4kCDviFIa7fDvI=
github.com/fluxcd/source-controller/api v1.1.2 h1:FfKDKVWnopo+Q2pOAxgHEjrtr4MP41L8aapR4mqBhBk=
github.com/fluxcd/source-controller/api v1.1.2/go.mod h1:ZLkaUd1KQIjtLPCvO63Ni5zpnSTVBAkeRgFBzMItbDQ=
github.com/fluxcd/test-infra/tftestenv v0.0.0-20230831142147-627bca8e7916 h1:w9UGknpfAGbiObQALZiuWYGeriAU1wKCFTmI2tj/96M=
github.com/fluxcd/test-infra/tftestenv v0.0.0-20230831142147-627bca8e7916/go.mod h1:liFlLEXgambGVdWSJ4JzbIHf1Vjpp1HwUyPazPIVZug=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=

View File

@@ -27,10 +27,11 @@ import (
. "github.com/onsi/gomega"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1"
notiv1 "github.com/fluxcd/notification-controller/api/v1"
notiv1beta3 "github.com/fluxcd/notification-controller/api/v1beta3"
notiv1beta2 "github.com/fluxcd/notification-controller/api/v1beta2"
events "github.com/fluxcd/pkg/apis/event/v1beta1"
"github.com/fluxcd/pkg/apis/meta"
sourcev1 "github.com/fluxcd/source-controller/api/v1"
@@ -65,12 +66,12 @@ metadata:
g.Expect(testEnv.Create(ctx, &namespace)).To(Succeed())
defer testEnv.Delete(ctx, &namespace)
provider := notiv1beta3.Provider{
provider := notiv1beta2.Provider{
ObjectMeta: metav1.ObjectMeta{
Name: testID,
Namespace: testID,
},
Spec: notiv1beta3.ProviderSpec{
Spec: notiv1beta2.ProviderSpec{
Type: cfg.notificationCfg.providerType,
Address: cfg.notificationCfg.providerAddress,
Channel: cfg.notificationCfg.providerChannel,
@@ -97,12 +98,12 @@ metadata:
g.Expect(testEnv.Create(ctx, &provider)).To(Succeed())
defer testEnv.Delete(ctx, &provider)
alert := notiv1beta3.Alert{
alert := notiv1beta2.Alert{
ObjectMeta: metav1.ObjectMeta{
Name: testID,
Namespace: testID,
},
Spec: notiv1beta3.AlertSpec{
Spec: notiv1beta2.AlertSpec{
ProviderRef: meta.LocalObjectReference{
Name: provider.Name,
},
@@ -118,6 +119,32 @@ metadata:
g.Expect(testEnv.Create(ctx, &alert)).ToNot(HaveOccurred())
defer testEnv.Delete(ctx, &alert)
g.Eventually(func() bool {
nn := types.NamespacedName{Name: provider.Name, Namespace: provider.Namespace}
obj := &notiv1beta2.Provider{}
err := testEnv.Get(ctx, nn, obj)
if err != nil {
return false
}
if err := checkReadyCondition(obj); err != nil {
t.Log(err)
return false
}
nn = types.NamespacedName{Name: alert.Name, Namespace: alert.Namespace}
alertObj := &notiv1beta2.Alert{}
err = testEnv.Get(ctx, nn, alertObj)
if err != nil {
return false
}
if err := checkReadyCondition(alertObj); err != nil {
t.Log(err)
return false
}
return true
}, testTimeout, testInterval).Should(BeTrue())
modifyKsSpec := func(spec *kustomizev1.KustomizationSpec) {
spec.Interval = metav1.Duration{Duration: 30 * time.Second}
spec.HealthChecks = []meta.NamespacedObjectKindReference{

View File

@@ -49,7 +49,7 @@ func TestOCIHelmRelease(t *testing.T) {
err := pushImagesFromURL(repoURL, "ghcr.io/stefanprodan/charts/podinfo:6.2.0", []string{"6.2.0"})
g.Expect(err).ToNot(HaveOccurred())
// Create HelmRepository.
// Create HelmRepository and wait for it to sync
helmRepository := sourcev1.HelmRepository{
ObjectMeta: metav1.ObjectMeta{Name: testID, Namespace: testID},
Spec: sourcev1.HelmRepositorySpec{
@@ -66,6 +66,21 @@ func TestOCIHelmRelease(t *testing.T) {
g.Expect(testEnv.Create(ctx, &helmRepository)).To(Succeed())
defer testEnv.Delete(ctx, &helmRepository)
g.Eventually(func() bool {
obj := &sourcev1.HelmRepository{}
nn := types.NamespacedName{Name: helmRepository.Name, Namespace: helmRepository.Namespace}
err := testEnv.Get(ctx, nn, obj)
if err != nil {
t.Logf("error getting helm repository %s", err.Error())
return false
}
if err := checkReadyCondition(obj); err != nil {
t.Logf("%v", err)
return false
}
return true
}, testTimeout, testInterval).Should(BeTrue())
// create helm release
helmRelease := helmv2.HelmRelease{
ObjectMeta: metav1.ObjectMeta{Name: testID, Namespace: testID},

View File

@@ -34,7 +34,7 @@ import (
automationv1beta1 "github.com/fluxcd/image-automation-controller/api/v1beta1"
reflectorv1beta2 "github.com/fluxcd/image-reflector-controller/api/v1beta2"
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1"
notiv1beta3 "github.com/fluxcd/notification-controller/api/v1beta3"
notiv1beta2 "github.com/fluxcd/notification-controller/api/v1beta2"
"github.com/fluxcd/pkg/git"
sourcev1 "github.com/fluxcd/source-controller/api/v1"
sourcev1beta2 "github.com/fluxcd/source-controller/api/v1beta2"
@@ -166,7 +166,7 @@ func init() {
utilruntime.Must(helmv2beta1.AddToScheme(scheme.Scheme))
utilruntime.Must(reflectorv1beta2.AddToScheme(scheme.Scheme))
utilruntime.Must(automationv1beta1.AddToScheme(scheme.Scheme))
utilruntime.Must(notiv1beta3.AddToScheme(scheme.Scheme))
utilruntime.Must(notiv1beta2.AddToScheme(scheme.Scheme))
random = rand.New(rand.NewSource(time.Now().UnixNano()))
}