mirror of
https://github.com/hanzoai/deploy.git
synced 2026-10-07 08:44:50 +00:00
go.mod still declared `module github.com/hanzoai/deploy` after the repo was renamed. A Go module path is not cosmetic — it is the resolvable location. So `go get github.com/hanzoai/cd` could not work, and anything importing this module had to spell a name that no longer exists at that address. The build log of v3.6.0 is where it surfaced: the compiler was still emitting github.com/hanzoai/deploy/cmd/cd/commands. 3541 import paths across 690 .go, 19 .proto, 4 .yaml, 3 .sh, 2 .mod, Procfile and Makefile. Plus the 62 ghcr.io/hanzoai/deploy image references, moved to ghcr.io/hanzoai/cd to match hanzo.yml's build target — a distinct string from the module path, replaced separately so neither could clobber the other. go mod tidy clean, CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build ./... clean. Co-authored-by: Hanzo Dev <[email protected]>
253 lines
7.3 KiB
Go
253 lines
7.3 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/hanzoai/cd/applicationset/utils"
|
|
argoappv1 "github.com/hanzoai/cd/pkg/apis/application/v1alpha1"
|
|
metricsutil "github.com/hanzoai/cd/util/metrics"
|
|
)
|
|
|
|
var (
|
|
applicationsetNamespaces = []string{"cd", "test-namespace1"}
|
|
|
|
filter = func(appset *argoappv1.ApplicationSet) bool {
|
|
return utils.IsNamespaceAllowed(applicationsetNamespaces, appset.Namespace)
|
|
}
|
|
|
|
collectedLabels = []string{"included/test"}
|
|
)
|
|
|
|
const fakeAppsetList = `
|
|
apiVersion: apps.hanzo.ai/v1alpha1
|
|
kind: ApplicationSet
|
|
metadata:
|
|
name: test1
|
|
namespace: cd
|
|
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: apps.hanzo.ai
|
|
health:
|
|
status: Missing
|
|
kind: Application
|
|
name: test-app1
|
|
namespace: cd
|
|
status: OutOfSync
|
|
version: v1alpha1
|
|
- group: apps.hanzo.ai
|
|
health:
|
|
status: Missing
|
|
kind: Application
|
|
name: test-app2
|
|
namespace: cd
|
|
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: apps.hanzo.ai/v1alpha1
|
|
kind: ApplicationSet
|
|
metadata:
|
|
name: test2
|
|
namespace: cd
|
|
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: apps.hanzo.ai/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.SplitSeq(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(), `
|
|
cd_appset_info{name="test1",namespace="cd",resource_update_status="ApplicationSetUpToDate"} 1
|
|
`)
|
|
assert.Contains(t, rr.Body.String(), `
|
|
cd_appset_owned_applications{name="test1",namespace="cd"} 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(), `
|
|
cd_appset_labels{label_included_test="test",name="test1",namespace="cd"} 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(), `
|
|
cd_appset_labels{label_included_test="",name="test2",namespace="cd"} 1
|
|
`)
|
|
// If ResourcesUpToDate condition is not present on the applicationset the status should be reported as 'Unknown'
|
|
assert.Contains(t, rr.Body.String(), `
|
|
cd_appset_info{name="test2",namespace="cd",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(), `
|
|
cd_appset_owned_applications{name="test2",namespace="cd"} 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(), `
|
|
cd_appset_reconcile_sum{name="test1",namespace="cd"} 5
|
|
`)
|
|
// If there are no resources on the applicationset the owned application gague should return 0
|
|
assert.Contains(t, rr.Body.String(), `
|
|
cd_appset_reconcile_count{name="test1",namespace="cd"} 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]
|
|
}
|