Skip to content

Commit

Permalink
feature: refactor pod enhancer so all workload resource types are used (
Browse files Browse the repository at this point in the history
#74)

* refactor pod enhancer so all workload resource types are used

* refactor enhancer to process non-pod objects

* use object caching through informer indexers

* refactoring enhancer code

* use constant instead of string literal
  • Loading branch information
Jiahui-Zhang-20 committed Dec 19, 2023
1 parent 9086675 commit 5b0689b
Show file tree
Hide file tree
Showing 17 changed files with 614 additions and 332 deletions.
9 changes: 9 additions & 0 deletions constants.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package main

const (
KindPod string = "Pod"
KindJob string = "Job"
KindCronjob string = "CronJob"
KindReplicaset string = "ReplicaSet"
KindDeployment string = "Deployment"
)
142 changes: 1 addition & 141 deletions crons.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,10 @@ package main
import (
"context"
"errors"
"fmt"
"time"

"github.com/getsentry/sentry-go"
"github.com/rs/zerolog"
batchv1 "k8s.io/api/batch/v1"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/informers"
"k8s.io/client-go/tools/cache"
)

type EventHandlerType string
Expand All @@ -23,56 +17,6 @@ const (
EventHandlerDelete EventHandlerType = "DELETE"
)

var cronjobInformer cache.SharedIndexInformer
var jobInformer cache.SharedIndexInformer

// Starts the crons informer which has event handlers
// adds to the crons monitor data struct used for sending
// checkin events to Sentry
func startCronsInformers(ctx context.Context, namespace string) error {

clientset, err := getClientsetFromContext(ctx)
if err != nil {
return errors.New("failed to get clientset")
}

// Create factory that will produce both the cronjob informer and job informer
factory := informers.NewSharedInformerFactoryWithOptions(
clientset,
5*time.Second,
informers.WithNamespace(namespace),
)

// Create the cronjob informer
cronjobInformer, err = createCronjobInformer(ctx, factory, namespace)
if err != nil {
return err
}
// Create the job informer
jobInformer, err = createJobInformer(ctx, factory, namespace)
if err != nil {
return err
}

// Channel to tell the factory to stop the informers
doneChan := make(chan struct{})
factory.Start(doneChan)

// Sync the cronjob informer cache
if ok := cache.WaitForCacheSync(doneChan, cronjobInformer.HasSynced); !ok {
return errors.New("cronjob informer failed to sync")
}
// Sync the job informer cache
if ok := cache.WaitForCacheSync(doneChan, jobInformer.HasSynced); !ok {
return errors.New("job informer failed to sync")
}

// Wait for the channel to be closed
<-doneChan

return nil
}

// Captures sentry crons checkin event if appropriate
// by checking the job status to determine if the job just created pod (job starting)
// or if the job exited
Expand All @@ -92,7 +36,7 @@ func runSentryCronsCheckin(ctx context.Context, job *batchv1.Job, eventHandlerTy
return errors.New("job does not have cronjob reference")
}
cronjobRef := job.OwnerReferences[0]
if !*cronjobRef.Controller || cronjobRef.Kind != "CronJob" {
if !*cronjobRef.Controller || cronjobRef.Kind != KindCronjob {
return errors.New("job does not have cronjob reference")
}
cronsMonitorData, ok := cronsMetaData.getCronsMonitorData(cronjobRef.Name)
Expand Down Expand Up @@ -197,87 +141,3 @@ func checkinJobEnding(ctx context.Context, job *batchv1.Job, cronsMonitorData *C
)
return nil
}

// Adds to the sentry events whenever it is associated with a cronjob
// so the sentry event contains the corresponding slug monitor, cronjob name, timestamp of when the cronjob began, and
// the k8s cronjob metadata
func runCronsDataHandler(ctx context.Context, scope *sentry.Scope, pod *v1.Pod, sentryEvent *sentry.Event) (bool, error) {

// get owningCronJob if exists
owningCronJob, err := getOwningCronJob(ctx, pod)
if err != nil {
return false, err
}

// pod not part of a cronjob
if owningCronJob == nil {
return false, nil
}

scope.SetContext("Monitor", sentry.Context{
"Slug": owningCronJob.Name,
})

sentryEvent.Fingerprint = append(sentryEvent.Fingerprint, owningCronJob.Kind, owningCronJob.Name)

setTagIfNotEmpty(scope, "cronjob_name", owningCronJob.Name)

// add breadcrumb with cronJob timestamps
scope.AddBreadcrumb(&sentry.Breadcrumb{
Message: fmt.Sprintf("Created cronjob %s", owningCronJob.Name),
Level: sentry.LevelInfo,
Timestamp: owningCronJob.CreationTimestamp.Time,
}, breadcrumbLimit)

metadataJson, err := prettyJson(owningCronJob.ObjectMeta)

if err == nil {
scope.SetContext("Cronjob", sentry.Context{
"Metadata": metadataJson,
})
} else {
return false, err
}

return true, nil
}

// returns the cronjob that is the grandparent of a pod if exists
// but returns nil is no cronjob is found
func getOwningCronJob(ctx context.Context, pod *v1.Pod) (*batchv1.CronJob, error) {

clientset, err := getClientsetFromContext(ctx)
if err != nil {
return nil, err
}

namespace := pod.Namespace

// first attempt to group events by cronJobs
var owningCronJob *batchv1.CronJob = nil

// check if the pod corresponds to a cronJob
for _, podRef := range pod.ObjectMeta.OwnerReferences {
// check the pod has a job as an owner
if !*podRef.Controller || podRef.Kind != "Job" {
continue
}
// find the owning job
owningJob, err := clientset.BatchV1().Jobs(namespace).Get(context.Background(), podRef.Name, metav1.GetOptions{})
if err != nil {
continue
}
// check if owning job is owned by a cronJob
for _, jobRef := range owningJob.ObjectMeta.OwnerReferences {
if !*jobRef.Controller || jobRef.Kind != "CronJob" {
continue
}
owningCronJob, err = clientset.BatchV1().CronJobs(namespace).Get(context.Background(), jobRef.Name, metav1.GetOptions{})
if err != nil {
continue
}
}
}

return owningCronJob, nil
}
Loading

0 comments on commit 5b0689b

Please sign in to comment.