mirror of
https://github.com/argoproj/argo-cd
synced 2026-04-21 17:07:16 +00:00
feat: replace error message in webhook handler with metrics (#27215)
Signed-off-by: Alexander Matyushentsev <alexander@akuity.io>
This commit is contained in:
parent
6743cdf9cc
commit
ad310c2452
14 changed files with 1051 additions and 820 deletions
4
assets/swagger.json
generated
4
assets/swagger.json
generated
|
|
@ -9727,6 +9727,10 @@
|
|||
"username": {
|
||||
"type": "string",
|
||||
"title": "Username contains the user name used for authenticating at the remote repository"
|
||||
},
|
||||
"webhookManifestCacheWarmDisabled": {
|
||||
"description": "WebhookManifestCacheWarmDisabled disables manifest cache warming during webhook processing for this repository.\nWhen set, webhook handlers will only trigger reconciliation for affected applications and skip Redis cache\noperations for unaffected ones. Recommended for large monorepos with plain YAML manifests.",
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -149,6 +149,7 @@ func NewGenRepoSpecCommand() *cobra.Command {
|
|||
repoOpts.Repo.EnableOCI = repoOpts.EnableOci
|
||||
repoOpts.Repo.UseAzureWorkloadIdentity = repoOpts.UseAzureWorkloadIdentity
|
||||
repoOpts.Repo.InsecureOCIForceHttp = repoOpts.InsecureOCIForceHTTP
|
||||
repoOpts.Repo.WebhookManifestCacheWarmDisabled = repoOpts.WebhookManifestCacheWarmDisabled
|
||||
|
||||
if repoOpts.Repo.Type == "helm" && repoOpts.Repo.Name == "" {
|
||||
errors.CheckError(stderrors.New("must specify --name for repos of type 'helm'"))
|
||||
|
|
|
|||
|
|
@ -192,6 +192,7 @@ func NewRepoAddCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
|||
repoOpts.Repo.ForceHttpBasicAuth = repoOpts.ForceHttpBasicAuth
|
||||
repoOpts.Repo.UseAzureWorkloadIdentity = repoOpts.UseAzureWorkloadIdentity
|
||||
repoOpts.Repo.Depth = repoOpts.Depth
|
||||
repoOpts.Repo.WebhookManifestCacheWarmDisabled = repoOpts.WebhookManifestCacheWarmDisabled
|
||||
|
||||
if repoOpts.Repo.Type == "helm" && repoOpts.Repo.Name == "" {
|
||||
errors.Fatal(errors.ErrorGeneric, "Must specify --name for repos of type 'helm'")
|
||||
|
|
|
|||
|
|
@ -8,26 +8,27 @@ import (
|
|||
)
|
||||
|
||||
type RepoOptions struct {
|
||||
Repo appsv1.Repository
|
||||
Upsert bool
|
||||
SshPrivateKeyPath string //nolint:revive //FIXME(var-naming)
|
||||
InsecureOCIForceHTTP bool
|
||||
InsecureIgnoreHostKey bool
|
||||
InsecureSkipServerVerification bool
|
||||
TlsClientCertPath string //nolint:revive //FIXME(var-naming)
|
||||
TlsClientCertKeyPath string //nolint:revive //FIXME(var-naming)
|
||||
EnableLfs bool
|
||||
EnableOci bool
|
||||
GithubAppId int64
|
||||
GithubAppInstallationId int64
|
||||
GithubAppPrivateKeyPath string
|
||||
GitHubAppEnterpriseBaseURL string
|
||||
Proxy string
|
||||
NoProxy string
|
||||
GCPServiceAccountKeyPath string
|
||||
ForceHttpBasicAuth bool //nolint:revive //FIXME(var-naming)
|
||||
UseAzureWorkloadIdentity bool
|
||||
Depth int64
|
||||
Repo appsv1.Repository
|
||||
Upsert bool
|
||||
SshPrivateKeyPath string //nolint:revive //FIXME(var-naming)
|
||||
InsecureOCIForceHTTP bool
|
||||
InsecureIgnoreHostKey bool
|
||||
InsecureSkipServerVerification bool
|
||||
TlsClientCertPath string //nolint:revive //FIXME(var-naming)
|
||||
TlsClientCertKeyPath string //nolint:revive //FIXME(var-naming)
|
||||
EnableLfs bool
|
||||
EnableOci bool
|
||||
GithubAppId int64
|
||||
GithubAppInstallationId int64
|
||||
GithubAppPrivateKeyPath string
|
||||
GitHubAppEnterpriseBaseURL string
|
||||
Proxy string
|
||||
NoProxy string
|
||||
GCPServiceAccountKeyPath string
|
||||
ForceHttpBasicAuth bool //nolint:revive //FIXME(var-naming)
|
||||
UseAzureWorkloadIdentity bool
|
||||
Depth int64
|
||||
WebhookManifestCacheWarmDisabled bool
|
||||
}
|
||||
|
||||
func AddRepoFlags(command *cobra.Command, opts *RepoOptions) {
|
||||
|
|
@ -55,4 +56,5 @@ func AddRepoFlags(command *cobra.Command, opts *RepoOptions) {
|
|||
command.Flags().BoolVar(&opts.UseAzureWorkloadIdentity, "use-azure-workload-identity", false, "whether to use azure workload identity for authentication")
|
||||
command.Flags().BoolVar(&opts.InsecureOCIForceHTTP, "insecure-oci-force-http", false, "Use http when accessing an OCI repository")
|
||||
command.Flags().Int64Var(&opts.Depth, "depth", 0, "Specify a custom depth for git clone operations. Unless specified, a full clone is performed using the depth of 0")
|
||||
command.Flags().BoolVar(&opts.WebhookManifestCacheWarmDisabled, "webhook-manifest-cache-warm-disabled", false, "disable manifest cache warming during webhook processing for this repository (recommended for large monorepos with plain YAML manifests)")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -438,6 +438,55 @@ spec:
|
|||
> paths
|
||||
> provided in the annotation. The application path serves as the deepest path that can be selected as the root.
|
||||
|
||||
#### Measuring Annotation Efficiency
|
||||
|
||||
You can use the following metrics to evaluate how effectively the `argocd.argoproj.io/manifest-generate-paths`
|
||||
annotation is reducing unnecessary manifest regeneration:
|
||||
|
||||
- **`argocd_webhook_requests_total`** (label: `repo`) — counts incoming webhook events per repository. Use this as the
|
||||
baseline for how many push events Argo CD is receiving.
|
||||
|
||||
- **`argocd_webhook_store_cache_attempts_total`** (labels: `repo`, `successful`) — counts attempts to reuse the previously
|
||||
cached manifests for the new commit SHA when an application's refresh paths have _not_ changed. A `successful=true`
|
||||
result means the cache was warmed for the new revision without re-generating manifests, which is the desired outcome.
|
||||
|
||||
To assess efficiency, compare the rate of `successful=true` attempts against the total webhook rate. A high ratio
|
||||
indicates the annotation is working well and preventing unnecessary manifest regeneration.
|
||||
|
||||
Note that some `successful=false` results are expected and not a cause for concern — they occur when Argo CD has not
|
||||
yet cached manifests for an application (e.g. after a restart or first sync), so there is nothing to carry forward to
|
||||
the new revision.
|
||||
|
||||
#### Disabling Manifest Cache Warming in Webhooks
|
||||
|
||||
In some cases, the manifest cache warming done by the webhook handler can hurt performance rather than help it:
|
||||
|
||||
- **Plain YAML repositories**: if applications use plain YAML manifests (no Helm or Kustomize rendering), manifest
|
||||
generation is fast and caching provides little benefit. Attempting to warm the cache for thousands of unaffected
|
||||
applications on every commit adds significant overhead.
|
||||
- **Large monorepos**: with many applications sharing a single repository, each webhook event triggers a cache warm
|
||||
attempt for every application whose paths did not change. With thousands of applications, this can cause the webhook
|
||||
handler to spend significant time on Redis operations, delaying the actual reconciliation trigger for the affected
|
||||
application.
|
||||
|
||||
When disabled, the webhook handler will only trigger reconciliation for applications whose files have changed and
|
||||
will skip all Redis cache operations for unaffected applications. This is the recommended setting for large monorepos
|
||||
with plain YAML manifests.
|
||||
|
||||
**Per-repository setting (recommended)**: set `webhookManifestCacheWarmDisabled: true` on the repository via the
|
||||
ArgoCD CLI or UI:
|
||||
|
||||
```bash
|
||||
argocd repo edit https://github.com/org/repo.git --webhook-manifest-cache-warm-disabled
|
||||
```
|
||||
|
||||
**Global setting**: to disable cache warming for all repositories, set the following environment variable on
|
||||
`argocd-server`:
|
||||
|
||||
```
|
||||
ARGOCD_WEBHOOK_MANIFEST_CACHE_WARM_DISABLED=true
|
||||
```
|
||||
|
||||
### Application Sync Timeout & Jitter
|
||||
|
||||
Argo CD has a timeout for application syncs. It will trigger a refresh for each application periodically when the
|
||||
|
|
|
|||
|
|
@ -85,6 +85,7 @@ argocd admin repo generate-spec REPOURL [flags]
|
|||
--type string type of the repository, "git", "oci" or "helm" (default "git")
|
||||
--use-azure-workload-identity whether to use azure workload identity for authentication
|
||||
--username string username to the repository
|
||||
--webhook-manifest-cache-warm-disabled disable manifest cache warming during webhook processing for this repository (recommended for large monorepos with plain YAML manifests)
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
|
|
|||
1
docs/user-guide/commands/argocd_repo_add.md
generated
1
docs/user-guide/commands/argocd_repo_add.md
generated
|
|
@ -87,6 +87,7 @@ argocd repo add REPOURL [flags]
|
|||
--upsert Override an existing repository with the same name even if the spec differs
|
||||
--use-azure-workload-identity whether to use azure workload identity for authentication
|
||||
--username string username to the repository
|
||||
--webhook-manifest-cache-warm-disabled disable manifest cache warming during webhook processing for this repository (recommended for large monorepos with plain YAML manifests)
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
|
|
|||
1602
pkg/apis/application/v1alpha1/generated.pb.go
generated
1602
pkg/apis/application/v1alpha1/generated.pb.go
generated
File diff suppressed because it is too large
Load diff
|
|
@ -1968,6 +1968,11 @@ message Repository {
|
|||
|
||||
// Depth specifies the depth for shallow clones. A value of 0 or omitting the field indicates a full clone.
|
||||
optional int64 depth = 27;
|
||||
|
||||
// WebhookManifestCacheWarmDisabled disables manifest cache warming during webhook processing for this repository.
|
||||
// When set, webhook handlers will only trigger reconciliation for affected applications and skip Redis cache
|
||||
// operations for unaffected ones. Recommended for large monorepos with plain YAML manifests.
|
||||
optional bool webhookManifestCacheWarmDisabled = 28;
|
||||
}
|
||||
|
||||
// A RepositoryCertificate is either SSH known hosts entry or TLS certificate
|
||||
|
|
|
|||
|
|
@ -116,6 +116,10 @@ type Repository struct {
|
|||
InsecureOCIForceHttp bool `json:"insecureOCIForceHttp,omitempty" protobuf:"bytes,26,opt,name=insecureOCIForceHttp"` //nolint:revive //FIXME(var-naming)
|
||||
// Depth specifies the depth for shallow clones. A value of 0 or omitting the field indicates a full clone.
|
||||
Depth int64 `json:"depth,omitempty" protobuf:"bytes,27,opt,name=depth"`
|
||||
// WebhookManifestCacheWarmDisabled disables manifest cache warming during webhook processing for this repository.
|
||||
// When set, webhook handlers will only trigger reconciliation for affected applications and skip Redis cache
|
||||
// operations for unaffected ones. Recommended for large monorepos with plain YAML manifests.
|
||||
WebhookManifestCacheWarmDisabled bool `json:"webhookManifestCacheWarmDisabled,omitempty" protobuf:"varint,28,opt,name=webhookManifestCacheWarmDisabled"`
|
||||
}
|
||||
|
||||
// IsInsecure returns true if the repository has been configured to skip server verification or set to HTTP only
|
||||
|
|
|
|||
|
|
@ -407,6 +407,12 @@ func secretToRepository(secret *corev1.Secret) (*appsv1.Repository, error) {
|
|||
}
|
||||
repository.Depth = depth
|
||||
|
||||
webhookManifestCacheWarmDisabled, err := boolOrFalse(secret, "webhookManifestCacheWarmDisabled")
|
||||
if err != nil {
|
||||
return repository, err
|
||||
}
|
||||
repository.WebhookManifestCacheWarmDisabled = webhookManifestCacheWarmDisabled
|
||||
|
||||
return repository, nil
|
||||
}
|
||||
|
||||
|
|
@ -444,6 +450,7 @@ func (s *secretsRepositoryBackend) repositoryToSecret(repository *appsv1.Reposit
|
|||
updateSecretBool(secretCopy, "forceHttpBasicAuth", repository.ForceHttpBasicAuth)
|
||||
updateSecretBool(secretCopy, "useAzureWorkloadIdentity", repository.UseAzureWorkloadIdentity)
|
||||
updateSecretInt(secretCopy, "depth", repository.Depth)
|
||||
updateSecretBool(secretCopy, "webhookManifestCacheWarmDisabled", repository.WebhookManifestCacheWarmDisabled)
|
||||
addSecretMetadata(secretCopy, s.getSecretType())
|
||||
|
||||
return secretCopy
|
||||
|
|
|
|||
|
|
@ -1205,6 +1205,71 @@ func TestCreateReadAndWriteSecretForSameURL(t *testing.T) {
|
|||
assert.Equal(t, common.LabelValueSecretTypeRepositoryWrite, writeSecret.Labels[common.LabelKeySecretType])
|
||||
}
|
||||
|
||||
func TestRepositoryToSecret(t *testing.T) {
|
||||
clientset := getClientset()
|
||||
testee := &secretsRepositoryBackend{db: &db{
|
||||
ns: testNamespace,
|
||||
kubeclientset: clientset,
|
||||
settingsMgr: settings.NewSettingsManager(t.Context(), clientset, testNamespace),
|
||||
}}
|
||||
s := &corev1.Secret{}
|
||||
repo := &appsv1.Repository{
|
||||
Name: "Name",
|
||||
Repo: "git@github.com:argoproj/argo-cd.git",
|
||||
Username: "Username",
|
||||
Password: "Password",
|
||||
SSHPrivateKey: "SSHPrivateKey",
|
||||
InsecureIgnoreHostKey: true,
|
||||
Insecure: true,
|
||||
EnableLFS: true,
|
||||
EnableOCI: true,
|
||||
InsecureOCIForceHttp: true,
|
||||
TLSClientCertData: "TLSClientCertData",
|
||||
TLSClientCertKey: "TLSClientCertKey",
|
||||
Type: "Type",
|
||||
GithubAppPrivateKey: "GithubAppPrivateKey",
|
||||
GithubAppId: 123,
|
||||
GithubAppInstallationId: 456,
|
||||
GitHubAppEnterpriseBaseURL: "GitHubAppEnterpriseBaseURL",
|
||||
Proxy: "Proxy",
|
||||
NoProxy: "NoProxy",
|
||||
Project: "Project",
|
||||
GCPServiceAccountKey: "GCPServiceAccountKey",
|
||||
ForceHttpBasicAuth: true,
|
||||
UseAzureWorkloadIdentity: true,
|
||||
Depth: 1,
|
||||
WebhookManifestCacheWarmDisabled: true,
|
||||
}
|
||||
s = testee.repositoryToSecret(repo, s)
|
||||
assert.Equal(t, []byte(repo.Name), s.Data["name"])
|
||||
assert.Equal(t, []byte(repo.Repo), s.Data["url"])
|
||||
assert.Equal(t, []byte(repo.Username), s.Data["username"])
|
||||
assert.Equal(t, []byte(repo.Password), s.Data["password"])
|
||||
assert.Equal(t, []byte(repo.SSHPrivateKey), s.Data["sshPrivateKey"])
|
||||
assert.Equal(t, []byte(strconv.FormatBool(repo.InsecureIgnoreHostKey)), s.Data["insecureIgnoreHostKey"])
|
||||
assert.Equal(t, []byte(strconv.FormatBool(repo.Insecure)), s.Data["insecure"])
|
||||
assert.Equal(t, []byte(strconv.FormatBool(repo.EnableLFS)), s.Data["enableLfs"])
|
||||
assert.Equal(t, []byte(strconv.FormatBool(repo.EnableOCI)), s.Data["enableOCI"])
|
||||
assert.Equal(t, []byte(strconv.FormatBool(repo.InsecureOCIForceHttp)), s.Data["insecureOCIForceHttp"])
|
||||
assert.Equal(t, []byte(repo.TLSClientCertData), s.Data["tlsClientCertData"])
|
||||
assert.Equal(t, []byte(repo.TLSClientCertKey), s.Data["tlsClientCertKey"])
|
||||
assert.Equal(t, []byte(repo.Type), s.Data["type"])
|
||||
assert.Equal(t, []byte(repo.GithubAppPrivateKey), s.Data["githubAppPrivateKey"])
|
||||
assert.Equal(t, []byte(strconv.FormatInt(repo.GithubAppId, 10)), s.Data["githubAppID"])
|
||||
assert.Equal(t, []byte(strconv.FormatInt(repo.GithubAppInstallationId, 10)), s.Data["githubAppInstallationID"])
|
||||
assert.Equal(t, []byte(repo.GitHubAppEnterpriseBaseURL), s.Data["githubAppEnterpriseBaseUrl"])
|
||||
assert.Equal(t, []byte(repo.Proxy), s.Data["proxy"])
|
||||
assert.Equal(t, []byte(repo.NoProxy), s.Data["noProxy"])
|
||||
assert.Equal(t, []byte(repo.Project), s.Data["project"])
|
||||
assert.Equal(t, []byte(repo.GCPServiceAccountKey), s.Data["gcpServiceAccountKey"])
|
||||
assert.Equal(t, []byte(strconv.FormatBool(repo.ForceHttpBasicAuth)), s.Data["forceHttpBasicAuth"])
|
||||
assert.Equal(t, []byte(strconv.FormatBool(repo.UseAzureWorkloadIdentity)), s.Data["useAzureWorkloadIdentity"])
|
||||
assert.Equal(t, []byte(strconv.FormatInt(repo.Depth, 10)), s.Data["depth"])
|
||||
assert.Equal(t, []byte(strconv.FormatBool(repo.WebhookManifestCacheWarmDisabled)), s.Data["webhookManifestCacheWarmDisabled"])
|
||||
assert.Equal(t, map[string]string{common.AnnotationKeyManagedBy: common.AnnotationValueManagedByArgoCD}, s.Annotations)
|
||||
assert.Equal(t, map[string]string{common.LabelKeySecretType: common.LabelValueSecretTypeRepository}, s.Labels)
|
||||
}
|
||||
|
||||
func TestCreateReadAndWriteRepoCredsSecretForSameURL(t *testing.T) {
|
||||
clientset := getClientset()
|
||||
settingsMgr := settings.NewSettingsManager(t.Context(), clientset, testNamespace)
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import (
|
|||
"html"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
|
|
@ -25,6 +26,8 @@ import (
|
|||
"github.com/go-playground/webhooks/v6/gitlab"
|
||||
"github.com/go-playground/webhooks/v6/gogs"
|
||||
gogsclient "github.com/gogits/go-gogs-client"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/argoproj/argo-cd/v3/common"
|
||||
|
|
@ -55,6 +58,24 @@ const payloadQueueSize = 50000
|
|||
|
||||
const panicMsgServer = "panic while processing api-server webhook event"
|
||||
|
||||
var (
|
||||
webhookManifestCacheWarmDisabled = os.Getenv("ARGOCD_WEBHOOK_MANIFEST_CACHE_WARM_DISABLED") == "true"
|
||||
webhookRequestsTotal = promauto.NewCounterVec(prometheus.CounterOpts{
|
||||
Name: "argocd_webhook_requests_total",
|
||||
Help: "Number of webhook requests received by repo.",
|
||||
}, []string{"repo"})
|
||||
|
||||
webhookStoreCacheAttemptsTotal = promauto.NewCounterVec(prometheus.CounterOpts{
|
||||
Name: "argocd_webhook_store_cache_attempts_total",
|
||||
Help: "Number of attempts to store previously cached manifests triggered by a webhook event.",
|
||||
}, []string{"repo", "successful"})
|
||||
|
||||
webhookHandlersInFlight = promauto.NewGauge(prometheus.GaugeOpts{
|
||||
Name: "argocd_webhook_handlers_in_flight",
|
||||
Help: "Number of webhook HandleEvent calls currently in progress.",
|
||||
})
|
||||
)
|
||||
|
||||
var _ settingsSource = &settings.SettingsManager{}
|
||||
|
||||
type ArgoCDWebhookHandler struct {
|
||||
|
|
@ -313,6 +334,15 @@ type changeInfo struct {
|
|||
|
||||
// HandleEvent handles webhook events for repo push events
|
||||
func (a *ArgoCDWebhookHandler) HandleEvent(payload any) {
|
||||
webhookHandlersInFlight.Inc()
|
||||
defer webhookHandlersInFlight.Dec()
|
||||
|
||||
start := time.Now()
|
||||
log.Info("Webhook handler started")
|
||||
defer func() {
|
||||
log.Infof("Webhook handler completed in %v", time.Since(start))
|
||||
}()
|
||||
|
||||
webURLs, revision, change, touchedHead, changedFiles := a.affectedRevisionInfo(payload)
|
||||
// NOTE: the webURL does not include the .git extension
|
||||
if len(webURLs) == 0 {
|
||||
|
|
@ -321,6 +351,7 @@ func (a *ArgoCDWebhookHandler) HandleEvent(payload any) {
|
|||
}
|
||||
for _, webURL := range webURLs {
|
||||
log.Infof("Received push event repo: %s, revision: %s, touchedHead: %v", webURL, revision, touchedHead)
|
||||
webhookRequestsTotal.WithLabelValues(git.NormalizeGitURL(webURL)).Inc()
|
||||
}
|
||||
|
||||
nsFilter := a.ns
|
||||
|
|
@ -368,6 +399,16 @@ func (a *ArgoCDWebhookHandler) HandleEvent(payload any) {
|
|||
continue
|
||||
}
|
||||
|
||||
cacheWarmDisabled := webhookManifestCacheWarmDisabled
|
||||
if !cacheWarmDisabled {
|
||||
repo, err := a.lookupRepository(context.Background(), webURL)
|
||||
if err != nil {
|
||||
log.Debugf("Failed to look up repository for %s: %v", webURL, err)
|
||||
} else if repo != nil && repo.WebhookManifestCacheWarmDisabled {
|
||||
cacheWarmDisabled = true
|
||||
}
|
||||
}
|
||||
|
||||
// iterate over apps and check if any files specified in their sources have changed
|
||||
for _, app := range filteredApps {
|
||||
// get all sources, including sync source and dry source if source hydrator is configured
|
||||
|
|
@ -401,10 +442,13 @@ func (a *ArgoCDWebhookHandler) HandleEvent(payload any) {
|
|||
log.Errorf("Failed to refresh app '%s': %v", app.Name, err)
|
||||
}
|
||||
break // we don't need to check other sources
|
||||
} else if change.shaBefore != "" && change.shaAfter != "" {
|
||||
} else if change.shaBefore != "" && change.shaAfter != "" && !cacheWarmDisabled {
|
||||
// update the cached manifests with the new revision cache key
|
||||
if err := a.storePreviouslyCachedManifests(&app, change, trackingMethod, appInstanceLabelKey, installationID, source); err != nil {
|
||||
log.Errorf("Failed to store cached manifests of previous revision for app '%s': %v", app.Name, err)
|
||||
log.Debugf("Failed to store cached manifests of previous revision for app '%s': %v", app.Name, err)
|
||||
webhookStoreCacheAttemptsTotal.WithLabelValues(git.NormalizeGitURL(webURL), "false").Inc()
|
||||
} else {
|
||||
webhookStoreCacheAttemptsTotal.WithLabelValues(git.NormalizeGitURL(webURL), "true").Inc()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -70,13 +70,25 @@ type reactorDef struct {
|
|||
reaction kubetesting.ReactionFunc
|
||||
}
|
||||
|
||||
func assertLogContains(t *testing.T, hook *test.Hook, msg string) {
|
||||
t.Helper()
|
||||
for _, entry := range hook.Entries {
|
||||
if entry.Message == msg {
|
||||
return
|
||||
}
|
||||
}
|
||||
t.Errorf("log hook did not contain message: %q", msg)
|
||||
}
|
||||
|
||||
func NewMockHandler(reactor *reactorDef, applicationNamespaces []string, objects ...runtime.Object) *ArgoCDWebhookHandler {
|
||||
defaultMaxPayloadSize := int64(50) * 1024 * 1024
|
||||
return NewMockHandlerWithPayloadLimit(reactor, applicationNamespaces, defaultMaxPayloadSize, objects...)
|
||||
}
|
||||
|
||||
func NewMockHandlerWithPayloadLimit(reactor *reactorDef, applicationNamespaces []string, maxPayloadSize int64, objects ...runtime.Object) *ArgoCDWebhookHandler {
|
||||
return newMockHandler(reactor, applicationNamespaces, maxPayloadSize, &mocks.ArgoDB{}, &settings.ArgoCDSettings{}, objects...)
|
||||
mockDB := &mocks.ArgoDB{}
|
||||
mockDB.EXPECT().ListRepositories(mock.Anything).Return([]*v1alpha1.Repository{}, nil).Maybe()
|
||||
return newMockHandler(reactor, applicationNamespaces, maxPayloadSize, mockDB, &settings.ArgoCDSettings{}, objects...)
|
||||
}
|
||||
|
||||
func NewMockHandlerForBitbucketCallback(reactor *reactorDef, applicationNamespaces []string, objects ...runtime.Object) *ArgoCDWebhookHandler {
|
||||
|
|
@ -161,7 +173,7 @@ func TestGitHubCommitEvent(t *testing.T) {
|
|||
h.Wait()
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
expectedLogResult := "Received push event repo: https://github.com/jessesuen/test-repo, revision: master, touchedHead: true"
|
||||
assert.Equal(t, expectedLogResult, hook.LastEntry().Message)
|
||||
assertLogContains(t, hook, expectedLogResult)
|
||||
hook.Reset()
|
||||
}
|
||||
|
||||
|
|
@ -179,7 +191,7 @@ func TestAzureDevOpsCommitEvent(t *testing.T) {
|
|||
h.Wait()
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
expectedLogResult := "Received push event repo: https://dev.azure.com/alexander0053/alex-test/_git/alex-test, revision: master, touchedHead: true"
|
||||
assert.Equal(t, expectedLogResult, hook.LastEntry().Message)
|
||||
assertLogContains(t, hook, expectedLogResult)
|
||||
hook.Reset()
|
||||
}
|
||||
|
||||
|
|
@ -295,7 +307,7 @@ func TestGitHubTagEvent(t *testing.T) {
|
|||
h.Wait()
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
expectedLogResult := "Received push event repo: https://github.com/jessesuen/test-repo, revision: v1.0, touchedHead: false"
|
||||
assert.Equal(t, expectedLogResult, hook.LastEntry().Message)
|
||||
assertLogContains(t, hook, expectedLogResult)
|
||||
hook.Reset()
|
||||
}
|
||||
|
||||
|
|
@ -313,7 +325,7 @@ func TestGitHubPingEvent(t *testing.T) {
|
|||
h.Wait()
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
expectedLogResult := "Ignoring webhook event"
|
||||
assert.Equal(t, expectedLogResult, hook.LastEntry().Message)
|
||||
assertLogContains(t, hook, expectedLogResult)
|
||||
hook.Reset()
|
||||
}
|
||||
|
||||
|
|
@ -331,9 +343,9 @@ func TestBitbucketServerRepositoryReferenceChangedEvent(t *testing.T) {
|
|||
h.Wait()
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
expectedLogResultSSH := "Received push event repo: ssh://git@bitbucketserver:7999/myproject/test-repo.git, revision: master, touchedHead: true"
|
||||
assert.Equal(t, expectedLogResultSSH, hook.AllEntries()[len(hook.AllEntries())-2].Message)
|
||||
assertLogContains(t, hook, expectedLogResultSSH)
|
||||
expectedLogResultHTTPS := "Received push event repo: https://bitbucketserver/scm/myproject/test-repo.git, revision: master, touchedHead: true"
|
||||
assert.Equal(t, expectedLogResultHTTPS, hook.LastEntry().Message)
|
||||
assertLogContains(t, hook, expectedLogResultHTTPS)
|
||||
hook.Reset()
|
||||
}
|
||||
|
||||
|
|
@ -349,7 +361,7 @@ func TestBitbucketServerRepositoryDiagnosticPingEvent(t *testing.T) {
|
|||
h.Wait()
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
expectedLogResult := "Ignoring webhook event"
|
||||
assert.Equal(t, expectedLogResult, hook.LastEntry().Message)
|
||||
assertLogContains(t, hook, expectedLogResult)
|
||||
hook.Reset()
|
||||
}
|
||||
|
||||
|
|
@ -367,7 +379,7 @@ func TestGogsPushEvent(t *testing.T) {
|
|||
h.Wait()
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
expectedLogResult := "Received push event repo: http://gogs-server/john/repo-test, revision: master, touchedHead: true"
|
||||
assert.Equal(t, expectedLogResult, hook.LastEntry().Message)
|
||||
assertLogContains(t, hook, expectedLogResult)
|
||||
hook.Reset()
|
||||
}
|
||||
|
||||
|
|
@ -385,7 +397,7 @@ func TestGitLabPushEvent(t *testing.T) {
|
|||
h.Wait()
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
expectedLogResult := "Received push event repo: https://gitlab.com/group/name, revision: master, touchedHead: true"
|
||||
assert.Equal(t, expectedLogResult, hook.LastEntry().Message)
|
||||
assertLogContains(t, hook, expectedLogResult)
|
||||
hook.Reset()
|
||||
}
|
||||
|
||||
|
|
@ -403,7 +415,7 @@ func TestGitLabSystemEvent(t *testing.T) {
|
|||
h.Wait()
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
expectedLogResult := "Received push event repo: https://gitlab.com/group/name, revision: master, touchedHead: true"
|
||||
assert.Equal(t, expectedLogResult, hook.LastEntry().Message)
|
||||
assertLogContains(t, hook, expectedLogResult)
|
||||
hook.Reset()
|
||||
}
|
||||
|
||||
|
|
@ -418,7 +430,7 @@ func TestInvalidMethod(t *testing.T) {
|
|||
h.Wait()
|
||||
assert.Equal(t, http.StatusMethodNotAllowed, w.Code)
|
||||
expectedLogResult := "Webhook processing failed: invalid HTTP Method"
|
||||
assert.Equal(t, expectedLogResult, hook.LastEntry().Message)
|
||||
assertLogContains(t, hook, expectedLogResult)
|
||||
assert.Equal(t, expectedLogResult+"\n", w.Body.String())
|
||||
hook.Reset()
|
||||
}
|
||||
|
|
@ -434,7 +446,7 @@ func TestInvalidEvent(t *testing.T) {
|
|||
h.Wait()
|
||||
assert.Equal(t, http.StatusBadRequest, w.Code)
|
||||
expectedLogResult := "Webhook processing failed: The payload is either too large or corrupted. Please check the payload size (must be under 50 MB) and ensure it is valid JSON"
|
||||
assert.Equal(t, expectedLogResult, hook.LastEntry().Message)
|
||||
assertLogContains(t, hook, expectedLogResult)
|
||||
assert.Equal(t, expectedLogResult+"\n", w.Body.String())
|
||||
hook.Reset()
|
||||
}
|
||||
|
|
@ -752,7 +764,7 @@ func TestGitHubCommitEventMaxPayloadSize(t *testing.T) {
|
|||
h.Wait()
|
||||
assert.Equal(t, http.StatusBadRequest, w.Code)
|
||||
expectedLogResult := "Webhook processing failed: The payload is either too large or corrupted. Please check the payload size (must be under 0 MB) and ensure it is valid JSON"
|
||||
assert.Equal(t, expectedLogResult, hook.LastEntry().Message)
|
||||
assertLogContains(t, hook, expectedLogResult)
|
||||
hook.Reset()
|
||||
}
|
||||
|
||||
|
|
@ -1119,6 +1131,7 @@ func TestHandleEvent(t *testing.T) {
|
|||
APIVersions: []string{},
|
||||
},
|
||||
}, nil).Maybe()
|
||||
mockDB.EXPECT().ListRepositories(mock.Anything).Return([]*v1alpha1.Repository{}, nil).Maybe()
|
||||
|
||||
err := serverCache.SetClusterInfo(testClusterURL, &v1alpha1.ClusterInfo{
|
||||
ServerVersion: "1.28.0",
|
||||
|
|
|
|||
Loading…
Reference in a new issue