mirror of
https://github.com/hanzoai/deploy.git
synced 2026-10-07 08:44:50 +00:00
253 lines
7.4 KiB
Go
253 lines
7.4 KiB
Go
package metrics
|
|
|
|
import (
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/prometheus/client_golang/prometheus"
|
|
"github.com/prometheus/client_golang/prometheus/promhttp"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"k8s.io/apimachinery/pkg/runtime"
|
|
ctrlclient "sigs.k8s.io/controller-runtime/pkg/client"
|
|
"sigs.k8s.io/controller-runtime/pkg/client/fake"
|
|
"sigs.k8s.io/controller-runtime/pkg/metrics"
|
|
"sigs.k8s.io/yaml"
|
|
|
|
"github.com/argoproj/argo-cd/v3/applicationset/utils"
|
|
argoappv1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
|
|
metricsutil "github.com/argoproj/argo-cd/v3/util/metrics"
|
|
)
|
|
|
|
var (
|
|
applicationsetNamespaces = []string{"argocd", "test-namespace1"}
|
|
|
|
filter = func(appset *argoappv1.ApplicationSet) bool {
|
|
return utils.IsNamespaceAllowed(applicationsetNamespaces, appset.Namespace)
|
|
}
|
|
|
|
collectedLabels = []string{"included/test"}
|
|
)
|
|
|
|
const fakeAppsetList = `
|
|
apiVersion: argoproj.io/v1alpha1
|
|
kind: ApplicationSet
|
|
metadata:
|
|
name: test1
|
|
namespace: argocd
|
|
labels:
|
|
included/test: test
|
|
not-included.label/test: test
|
|
spec:
|
|
generators:
|
|
- git:
|
|
directories:
|
|
- path: test/*
|
|
repoURL: https://github.com/test/test.git
|
|
revision: HEAD
|
|
template:
|
|
metadata:
|
|
name: '{{.path.basename}}'
|
|
spec:
|
|
destination:
|
|
namespace: '{{.path.basename}}'
|
|
server: https://kubernetes.default.svc
|
|
project: default
|
|
source:
|
|
path: '{{.path.path}}'
|
|
repoURL: https://github.com/test/test.git
|
|
targetRevision: HEAD
|
|
status:
|
|
resources:
|
|
- group: argoproj.io
|
|
health:
|
|
status: Missing
|
|
kind: Application
|
|
name: test-app1
|
|
namespace: argocd
|
|
status: OutOfSync
|
|
version: v1alpha1
|
|
- group: argoproj.io
|
|
health:
|
|
status: Missing
|
|
kind: Application
|
|
name: test-app2
|
|
namespace: argocd
|
|
status: OutOfSync
|
|
version: v1alpha1
|
|
conditions:
|
|
- lastTransitionTime: "2024-01-01T00:00:00Z"
|
|
message: Successfully generated parameters for all Applications
|
|
reason: ApplicationSetUpToDate
|
|
status: "False"
|
|
type: ErrorOccurred
|
|
- lastTransitionTime: "2024-01-01T00:00:00Z"
|
|
message: Successfully generated parameters for all Applications
|
|
reason: ParametersGenerated
|
|
status: "True"
|
|
type: ParametersGenerated
|
|
- lastTransitionTime: "2024-01-01T00:00:00Z"
|
|
message: ApplicationSet up to date
|
|
reason: ApplicationSetUpToDate
|
|
status: "True"
|
|
type: ResourcesUpToDate
|
|
---
|
|
apiVersion: argoproj.io/v1alpha1
|
|
kind: ApplicationSet
|
|
metadata:
|
|
name: test2
|
|
namespace: argocd
|
|
labels:
|
|
not-included.label/test: test
|
|
spec:
|
|
generators:
|
|
- git:
|
|
directories:
|
|
- path: test/*
|
|
repoURL: https://github.com/test/test.git
|
|
revision: HEAD
|
|
template:
|
|
metadata:
|
|
name: '{{.path.basename}}'
|
|
spec:
|
|
destination:
|
|
namespace: '{{.path.basename}}'
|
|
server: https://kubernetes.default.svc
|
|
project: default
|
|
source:
|
|
path: '{{.path.path}}'
|
|
repoURL: https://github.com/test/test.git
|
|
targetRevision: HEAD
|
|
---
|
|
apiVersion: argoproj.io/v1alpha1
|
|
kind: ApplicationSet
|
|
metadata:
|
|
name: should-be-filtered-out
|
|
namespace: not-allowed
|
|
spec:
|
|
generators:
|
|
- git:
|
|
directories:
|
|
- path: test/*
|
|
repoURL: https://github.com/test/test.git
|
|
revision: HEAD
|
|
template:
|
|
metadata:
|
|
name: '{{.path.basename}}'
|
|
spec:
|
|
destination:
|
|
namespace: '{{.path.basename}}'
|
|
server: https://kubernetes.default.svc
|
|
project: default
|
|
source:
|
|
path: '{{.path.path}}'
|
|
repoURL: https://github.com/test/test.git
|
|
targetRevision: HEAD
|
|
`
|
|
|
|
func newFakeAppsets(fakeAppsetYAML string) []argoappv1.ApplicationSet {
|
|
var results []argoappv1.ApplicationSet
|
|
|
|
appsetRawYamls := strings.Split(fakeAppsetYAML, "---")
|
|
|
|
for _, appsetRawYaml := range appsetRawYamls {
|
|
var appset argoappv1.ApplicationSet
|
|
err := yaml.Unmarshal([]byte(appsetRawYaml), &appset)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
results = append(results, appset)
|
|
}
|
|
|
|
return results
|
|
}
|
|
|
|
func TestApplicationsetCollector(t *testing.T) {
|
|
appsetList := newFakeAppsets(fakeAppsetList)
|
|
client := initializeClient(appsetList)
|
|
metrics.Registry = prometheus.NewRegistry()
|
|
|
|
appsetCollector := newAppsetCollector(utils.NewAppsetLister(client), collectedLabels, filter)
|
|
|
|
metrics.Registry.MustRegister(appsetCollector)
|
|
req, err := http.NewRequestWithContext(t.Context(), http.MethodGet, "/metrics", http.NoBody)
|
|
require.NoError(t, err)
|
|
rr := httptest.NewRecorder()
|
|
handler := promhttp.HandlerFor(metrics.Registry, promhttp.HandlerOpts{})
|
|
handler.ServeHTTP(rr, req)
|
|
|
|
assert.Equal(t, http.StatusOK, rr.Code)
|
|
// Test correct appset_info and owned applications
|
|
assert.Contains(t, rr.Body.String(), `
|
|
argocd_appset_info{name="test1",namespace="argocd",resource_update_status="ApplicationSetUpToDate"} 1
|
|
`)
|
|
assert.Contains(t, rr.Body.String(), `
|
|
argocd_appset_owned_applications{name="test1",namespace="argocd"} 2
|
|
`)
|
|
// Test labels collection - should not include labels not included in the list of collected labels and include the ones that do.
|
|
assert.Contains(t, rr.Body.String(), `
|
|
argocd_appset_labels{label_included_test="test",name="test1",namespace="argocd"} 1
|
|
`)
|
|
assert.NotContains(t, rr.Body.String(), normalizeLabel("not-included.label/test"))
|
|
// If collected label is not present on the applicationset the value should be empty
|
|
assert.Contains(t, rr.Body.String(), `
|
|
argocd_appset_labels{label_included_test="",name="test2",namespace="argocd"} 1
|
|
`)
|
|
// If ResourcesUpToDate condition is not present on the applicationset the status should be reported as 'Unknown'
|
|
assert.Contains(t, rr.Body.String(), `
|
|
argocd_appset_info{name="test2",namespace="argocd",resource_update_status="Unknown"} 1
|
|
`)
|
|
// If there are no resources on the applicationset the owned application gague should return 0
|
|
assert.Contains(t, rr.Body.String(), `
|
|
argocd_appset_owned_applications{name="test2",namespace="argocd"} 0
|
|
`)
|
|
// Test that filter is working
|
|
assert.NotContains(t, rr.Body.String(), `name="should-be-filtered-out"`)
|
|
}
|
|
|
|
func TestObserveReconcile(t *testing.T) {
|
|
appsetList := newFakeAppsets(fakeAppsetList)
|
|
client := initializeClient(appsetList)
|
|
metrics.Registry = prometheus.NewRegistry()
|
|
|
|
appsetMetrics := NewApplicationsetMetrics(utils.NewAppsetLister(client), collectedLabels, filter)
|
|
|
|
req, err := http.NewRequestWithContext(t.Context(), http.MethodGet, "/metrics", http.NoBody)
|
|
require.NoError(t, err)
|
|
rr := httptest.NewRecorder()
|
|
handler := promhttp.HandlerFor(metrics.Registry, promhttp.HandlerOpts{})
|
|
appsetMetrics.ObserveReconcile(&appsetList[0], 5*time.Second)
|
|
handler.ServeHTTP(rr, req)
|
|
assert.Contains(t, rr.Body.String(), `
|
|
argocd_appset_reconcile_sum{name="test1",namespace="argocd"} 5
|
|
`)
|
|
// If there are no resources on the applicationset the owned application gague should return 0
|
|
assert.Contains(t, rr.Body.String(), `
|
|
argocd_appset_reconcile_count{name="test1",namespace="argocd"} 1
|
|
`)
|
|
}
|
|
|
|
func initializeClient(appsets []argoappv1.ApplicationSet) ctrlclient.WithWatch {
|
|
scheme := runtime.NewScheme()
|
|
err := argoappv1.AddToScheme(scheme)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
var clientObjects []ctrlclient.Object
|
|
|
|
for _, appset := range appsets {
|
|
clientObjects = append(clientObjects, appset.DeepCopy())
|
|
}
|
|
|
|
return fake.NewClientBuilder().WithScheme(scheme).WithObjects(clientObjects...).Build()
|
|
}
|
|
|
|
func normalizeLabel(label string) string {
|
|
return metricsutil.NormalizeLabels("label", []string{label})[0]
|
|
}
|