Skip to content

Commit

Permalink
feat: Add informer metrics
Browse files Browse the repository at this point in the history
Signed-off-by: jannfis <[email protected]>
  • Loading branch information
jannfis committed Oct 30, 2024
1 parent 701d19a commit c6295f4
Show file tree
Hide file tree
Showing 12 changed files with 462 additions and 104 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ jobs:
- name: Run all unit tests
run: make test
- name: Upload coverage results
uses: codecov/codecov-action@v4.0.1
uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}
file: test/out/coverage.out
Expand Down
3 changes: 1 addition & 2 deletions hack/demo-env/start-agent-autonomous.sh
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,4 @@ if ! kubectl config get-contexts | tail -n +2 | awk '{ print $2 }' | grep -qE '^
exit 1
fi
SCRIPTPATH="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"
test -f cmd/agent/main.go || (echo "Script should be run from argocd-agent's root path" >&2; exit 1)
go run ./cmd/agent/main.go --agent-mode autonomous --creds userpass:${SCRIPTPATH}/creds/creds.agent-autonomous --server-address 127.0.0.1 --server-port 8443 --insecure-tls --kubecontext vcluster-agent-autonomous --namespace argocd $ARGS
go run github.com/argoproj-labs/argocd-agent/cmd/agent --agent-mode autonomous --creds userpass:${SCRIPTPATH}/creds/creds.agent-autonomous --server-address 127.0.0.1 --server-port 8443 --insecure-tls --kubecontext vcluster-agent-autonomous --namespace argocd --log-level trace $ARGS
3 changes: 1 addition & 2 deletions hack/demo-env/start-agent-managed.sh
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,4 @@ if ! kubectl config get-contexts | tail -n +2 | awk '{ print $2 }' | grep -qE '^
exit 1
fi
SCRIPTPATH="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"
test -f cmd/agent/main.go || (echo "Script should be run from argocd-agent's root path" >&2; exit 1)
go run ./cmd/agent/main.go --agent-mode managed --creds userpass:${SCRIPTPATH}/creds/creds.agent-managed --server-address 127.0.0.1 --server-port 8443 --insecure-tls --kubecontext vcluster-agent-managed --namespace agent-managed $ARGS
go run github.com/argoproj-labs/argocd-agent/cmd/agent --agent-mode managed --creds userpass:${SCRIPTPATH}/creds/creds.agent-managed --server-address 127.0.0.1 --server-port 8443 --insecure-tls --kubecontext vcluster-agent-managed --namespace agent-managed --log-level trace $ARGS
3 changes: 1 addition & 2 deletions hack/demo-env/start-principal.sh
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,4 @@ if ! kubectl config get-contexts | tail -n +2 | awk '{ print $2 }' | grep -qE '^
exit 1
fi
SCRIPTPATH="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"
test -f cmd/principal/main.go || (echo "Script should be run from argocd-agent's root path" >&2; exit 1)
go run ./cmd/principal --allowed-namespaces '*' --insecure-tls-generate --insecure-jwt-generate --kubecontext vcluster-control-plane --log-level trace --passwd ${SCRIPTPATH}/creds/users.control-plane $ARGS
go run github.com/argoproj-labs/argocd-agent/cmd/principal --allowed-namespaces '*' --insecure-tls-generate --insecure-jwt-generate --kubecontext vcluster-control-plane --log-level trace --passwd ${SCRIPTPATH}/creds/users.control-plane $ARGS
153 changes: 85 additions & 68 deletions internal/informer/appproject/projectinformer.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
"k8s.io/apimachinery/pkg/watch"

"github.com/argoproj-labs/argocd-agent/internal/informer"
"github.com/argoproj-labs/argocd-agent/internal/metrics"
)

type AppProjectInformer struct {
Expand All @@ -41,6 +42,8 @@ type AppProjectInformer struct {
addFunc func(proj *v1alpha1.AppProject)
updateFunc func(oldProj *v1alpha1.AppProject, newProj *v1alpha1.AppProject)
deleteFunc func(proj *v1alpha1.AppProject)

metrics *metrics.AppProjectWatcherMetrics
}

type AppProjectInformerOption func(pi *AppProjectInformer) error
Expand Down Expand Up @@ -98,6 +101,15 @@ func WithNamespaces(namespaces ...string) AppProjectInformerOption {
}
}

// WithMetrics sets the AppProject watcher metrics to be used with this
// informer.
func WithMetrics(m *metrics.AppProjectWatcherMetrics) AppProjectInformerOption {
return func(pi *AppProjectInformer) error {
pi.metrics = m
return nil
}
}

// NewAppProjectInformer returns a new instance of a GenericInformer set up to
// handle AppProjects. It will be configured with the given options, using the
// given appclientset.
Expand All @@ -114,77 +126,82 @@ func NewAppProjectInformer(ctx context.Context, client appclientset.Interface, n
if pi.logger == nil {
pi.logger = logrus.WithField("module", "AppProjectInformer")
}
iopts := []informer.InformerOption{}
if pi.metrics != nil {
iopts = append(iopts, informer.WithMetrics(pi.metrics.ProjectsAdded, pi.metrics.ProjectsUpdated, pi.metrics.ProjectsRemoved, pi.metrics.ProjectsWatched))
}
i, err := informer.NewGenericInformer(&v1alpha1.AppProject{},
informer.WithListCallback(func(options v1.ListOptions, namespace string) (runtime.Object, error) {
log().Infof("Listing AppProjects in namespace %s", namespace)
projects, err := client.ArgoprojV1alpha1().AppProjects(namespace).List(ctx, options)
log().Infof("Lister returned %d AppProjects", len(projects.Items))
if pi.filterFunc != nil {
newItems := make([]v1alpha1.AppProject, 0)
for _, p := range projects.Items {
if pi.filterFunc(&p) {
newItems = append(newItems, p)
append([]informer.InformerOption{
informer.WithListCallback(func(options v1.ListOptions, namespace string) (runtime.Object, error) {
log().Infof("Listing AppProjects in namespace %s", namespace)
projects, err := client.ArgoprojV1alpha1().AppProjects(namespace).List(ctx, options)
log().Infof("Lister returned %d AppProjects", len(projects.Items))
if pi.filterFunc != nil {
newItems := make([]v1alpha1.AppProject, 0)
for _, p := range projects.Items {
if pi.filterFunc(&p) {
newItems = append(newItems, p)
}
}
pi.logger.Debugf("Lister has %d AppProjects after filtering", len(newItems))
projects.Items = newItems
}
return projects, err
}),
informer.WithNamespaces(pi.namespaces...),
informer.WithWatchCallback(func(options v1.ListOptions, namespace string) (watch.Interface, error) {
log().Info("Watching AppProjects")
return client.ArgoprojV1alpha1().AppProjects(namespace).Watch(ctx, options)
}),
informer.WithAddCallback(func(obj interface{}) {
log().Info("Add AppProject Callback")
proj, ok := obj.(*v1alpha1.AppProject)
if !ok {
pi.logger.Errorf("Received add event for unknown type %T", obj)
return
}
pi.logger.Debugf("AppProject add event: %s", proj.Name)
if pi.addFunc != nil {
pi.addFunc(proj)
}
}),
informer.WithUpdateCallback(func(oldObj, newObj interface{}) {
log().Info("Update AppProject Callback")
oldProj, oldProjOk := oldObj.(*v1alpha1.AppProject)
newProj, newProjOk := newObj.(*v1alpha1.AppProject)
if !newProjOk || !oldProjOk {
pi.logger.Errorf("Received update event for unknown type old:%T new:%T", oldObj, newObj)
return
}
pi.logger.Debugf("AppProject update event: old:%s new:%s", oldProj.Name, newProj.Name)
if pi.updateFunc != nil {
pi.updateFunc(oldProj, newProj)
}
}),
informer.WithDeleteCallback(func(obj interface{}) {
log().Info("Delete AppProject Callback")
proj, ok := obj.(*v1alpha1.AppProject)
if !ok {
pi.logger.Errorf("Received delete event for unknown type %T", obj)
return
}
pi.logger.Debugf("AppProject delete event: %s", proj.Name)
if pi.deleteFunc != nil {
pi.deleteFunc(proj)
}
}),
informer.WithFilterFunc(func(obj interface{}) bool {
if pi.filterFunc == nil {
return true
}
o, ok := obj.(*v1alpha1.AppProject)
if !ok {
pi.logger.Errorf("Failed type conversion for unknown type %T", obj)
return false
}
pi.logger.Debugf("Lister has %d AppProjects after filtering", len(newItems))
projects.Items = newItems
}
return projects, err
}),
informer.WithNamespaces(pi.namespaces...),
informer.WithWatchCallback(func(options v1.ListOptions, namespace string) (watch.Interface, error) {
log().Info("Watching AppProjects")
return client.ArgoprojV1alpha1().AppProjects(namespace).Watch(ctx, options)
}),
informer.WithAddCallback(func(obj interface{}) {
log().Info("Add AppProject Callback")
proj, ok := obj.(*v1alpha1.AppProject)
if !ok {
pi.logger.Errorf("Received add event for unknown type %T", obj)
return
}
pi.logger.Debugf("AppProject add event: %s", proj.Name)
if pi.addFunc != nil {
pi.addFunc(proj)
}
}),
informer.WithUpdateCallback(func(oldObj, newObj interface{}) {
log().Info("Update AppProject Callback")
oldProj, oldProjOk := oldObj.(*v1alpha1.AppProject)
newProj, newProjOk := newObj.(*v1alpha1.AppProject)
if !newProjOk || !oldProjOk {
pi.logger.Errorf("Received update event for unknown type old:%T new:%T", oldObj, newObj)
return
}
pi.logger.Debugf("AppProject update event: old:%s new:%s", oldProj.Name, newProj.Name)
if pi.updateFunc != nil {
pi.updateFunc(oldProj, newProj)
}
}),
informer.WithDeleteCallback(func(obj interface{}) {
log().Info("Delete AppProject Callback")
proj, ok := obj.(*v1alpha1.AppProject)
if !ok {
pi.logger.Errorf("Received delete event for unknown type %T", obj)
return
}
pi.logger.Debugf("AppProject delete event: %s", proj.Name)
if pi.deleteFunc != nil {
pi.deleteFunc(proj)
}
}),
informer.WithFilterFunc(func(obj interface{}) bool {
if pi.filterFunc == nil {
return true
}
o, ok := obj.(*v1alpha1.AppProject)
if !ok {
pi.logger.Errorf("Failed type conversion for unknown type %T", obj)
return false
}
return pi.filterFunc(o)
}),
)
return pi.filterFunc(o)
}),
}, iopts...)...)
pi.projectInformer = i
pi.projectLister = applisters.NewAppProjectLister(i.Indexer())
return pi, err
Expand Down
51 changes: 51 additions & 0 deletions internal/informer/informer.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,30 @@ type GenericInformer struct {
fieldSelector string
// mutex should be owned before accessing namespaces
namespaces map[string]interface{}
// metrics is the metrics provider for this informer
metrics metrics
}

// metrics is a simplified and specialized metrics provider for the informer
type metrics struct {
added metricsCounter
updated metricsCounter
removed metricsCounter
watched metricsGauge
}

// metricsCounter is an interface for a metrics implementation of type counter.
// The reason we use an oversimplified abstraction is for testing purposes.
type metricsCounter interface {
Inc()
}

// metricsGauge is an interface for a metrics implementation of type gauge.
// The reason we use an oversimplified abstraction is for testing purposes.
type metricsGauge interface {
Inc()
Dec()
Set(float64)
}

type InformerOption func(i *GenericInformer) error
Expand Down Expand Up @@ -124,6 +148,17 @@ func WithFieldSelector(sel string) InformerOption {
}
}

// WithMetrics sets the metrics functions to use with this informer.
func WithMetrics(added, updated, removed metricsCounter, watched metricsGauge) InformerOption {
return func(i *GenericInformer) error {
i.metrics.added = added
i.metrics.updated = updated
i.metrics.removed = removed
i.metrics.watched = watched
return nil
}
}

// WithNamespaces sets the namespaces for which the informer will process any
// event. If an event is seen for an object in a namespace that is not in this
// list, the event will be ignored. If either zero or multiple namespaces are
Expand Down Expand Up @@ -166,6 +201,9 @@ func NewGenericInformer(objType runtime.Object, options ...InformerOption) (*Gen
&cache.ListWatch{
ListFunc: func(options v1.ListOptions) (runtime.Object, error) {
logCtx.Trace("Executing list")
if i.metrics.watched != nil {
i.metrics.watched.Set(0)
}
return i.listFunc(options, i.watchAndListNamespace())
},
WatchFunc: func(options v1.ListOptions) (watch.Interface, error) {
Expand Down Expand Up @@ -202,6 +240,10 @@ func NewGenericInformer(objType runtime.Object, options ...InformerOption) (*Gen
if i.addFunc != nil {
i.addFunc(obj)
}
if i.metrics.added != nil && i.metrics.watched != nil {
i.metrics.added.Inc()
i.metrics.watched.Inc()
}
},
UpdateFunc: func(oldObj, newObj interface{}) {
mobj, err := meta.Accessor(newObj)
Expand All @@ -221,6 +263,9 @@ func NewGenericInformer(objType runtime.Object, options ...InformerOption) (*Gen
if i.updateFunc != nil {
i.updateFunc(oldObj, newObj)
}
if i.metrics.updated != nil {
i.metrics.updated.Inc()
}
},
DeleteFunc: func(obj interface{}) {
mobj, err := meta.Accessor(obj)
Expand All @@ -240,6 +285,10 @@ func NewGenericInformer(objType runtime.Object, options ...InformerOption) (*Gen
if i.deleteFunc != nil {
i.deleteFunc(obj)
}
if i.metrics.removed != nil && i.metrics.watched != nil {
i.metrics.removed.Inc()
i.metrics.watched.Dec()
}
},
})
if err != nil {
Expand Down Expand Up @@ -342,6 +391,8 @@ func (i *GenericInformer) watchAndListNamespace() string {
// isNamespaceAllowed returns whether the namespace of an event's object is
// permitted.
func (i *GenericInformer) isNamespaceAllowed(obj v1.Object) bool {
i.mutex.RLock()
defer i.mutex.RUnlock()
if len(i.namespaces) == 0 {
return true
}
Expand Down
Loading

0 comments on commit c6295f4

Please sign in to comment.