Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature: refactor pod enhancer so all workload resource types are used #74

Merged
merged 5 commits into from
Dec 19, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 != CRONJOB {
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