Skip to content

Commit

Permalink
Merge pull request #905 from rancher/ephemeral-dispatcher
Browse files Browse the repository at this point in the history
ephemeral dispatchers & update alert fix
  • Loading branch information
alexandreLamarre authored Dec 14, 2022
2 parents 41265e7 + b4c8c8c commit 05b1be2
Show file tree
Hide file tree
Showing 8 changed files with 625 additions and 256 deletions.
467 changes: 323 additions & 144 deletions pkg/apis/alerting/v1/alerting.pb.go

Large diffs are not rendered by default.

12 changes: 12 additions & 0 deletions pkg/apis/alerting/v1/alerting.proto
Original file line number Diff line number Diff line change
Expand Up @@ -526,4 +526,16 @@ message ActiveWindows {
message CloneToRequest {
AlertCondition alertCondition = 1;
repeated string toClusters = 2;
}

message EphemeralDispatcherRequest {
google.protobuf.Duration ttl = 1;
int64 numDispatches = 2;
string prefix = 3;
AlertEndpoint endpoint = 4;
EndpointImplementation details = 5;
}

message EphemeralDispatcherResponse{
TriggerAlertsRequest triggerAlertsRequest = 1;
}
19 changes: 19 additions & 0 deletions pkg/apis/alerting/v1/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -376,3 +376,22 @@ func (c *CloneToRequest) Validate() error {
}
return nil
}

func (e *EphemeralDispatcherRequest) Validate() error {
if e.GetPrefix() == "" {
return validation.Error("prefix must be set for ephemeral dispatchers")
}
if e.GetNumDispatches() <= 0 {
return validation.Error("numDispatches must be non-zero for ephemeral dispatchers")
}
if e.GetTtl().AsDuration() == 0 {
return validation.Error("numDuration must be non-zero for ephemeral dispatchers")
}
if err := e.GetDetails().Validate(); err != nil {
return validation.Errorf("details must be valid for ephemeral dispatchers: %s", err)
}
if err := e.GetEndpoint().Validate(); err != nil {
return validation.Errorf("endpoints must be valid for ephemeral dispatchers: %s", err)
}
return nil
}
88 changes: 53 additions & 35 deletions plugins/alerting/pkg/alerting/api_endpoints.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,10 @@ func (p *Plugin) GetAlertEndpoint(ctx context.Context, ref *corev1.Reference) (*
}

func (p *Plugin) UpdateAlertEndpoint(ctx context.Context, req *alertingv1.UpdateAlertEndpointRequest) (*alertingv1.InvolvedConditions, error) {
if err := req.Validate(); err != nil {
if err := unredactSecrets(ctx, p.storageNode, req.Id.Id, req.GetUpdateAlert()); err != nil {
return nil, err
}
if err := unredactSecrets(ctx, p.storageNode, req.Id.Id, req.GetUpdateAlert()); err != nil {
if err := req.Validate(); err != nil {
return nil, err
}
// List relationships
Expand Down Expand Up @@ -188,10 +188,6 @@ func (p *Plugin) TestAlertEndpoint(ctx context.Context, req *alertingv1.TestAler
if err := req.Validate(); err != nil {
return nil, err
}
// - Create dummy Endpoint id
dummyConditionId := "test-" + uuid.New().String()
dummyEndpointId := "test-" + uuid.New().String()
lg.Debugf("dummy id : %s", dummyConditionId)
var typeName string
if s := req.Endpoint.GetSlack(); s != nil {
typeName = "slack"
Expand All @@ -200,30 +196,19 @@ func (p *Plugin) TestAlertEndpoint(ctx context.Context, req *alertingv1.TestAler
typeName = "email"
}
if typeName == "" {
typeName = "unknwon"
typeName = "unknown"
}

details := &alertingv1.EndpointImplementation{
Title: fmt.Sprintf("Test Alert - %s (%s)", typeName, time.Now().Format("2006-01-02T15:04:05 -07:00:00")),
Body: "Opni-alerting is sending you a test alert",
}

createImpl := &alertingv1.RoutingNode{
ConditionId: &corev1.Reference{Id: dummyConditionId}, // is used as a unique identifier
FullAttachedEndpoints: &alertingv1.FullAttachedEndpoints{
InitialDelay: durationpb.New(time.Duration(time.Second * 0)),
Items: []*alertingv1.FullAttachedEndpoint{
{
EndpointId: dummyEndpointId,
AlertEndpoint: req.Endpoint,
Details: details,
},
},
Details: details,
},
}
// create condition routing node
_, err := p.CreateConditionRoutingNode(ctx, createImpl)
triggerReq, err := p.EphemeralDispatcher(ctx, &alertingv1.EphemeralDispatcherRequest{
Prefix: "test",
Ttl: durationpb.New(time.Duration(time.Second * 60)),
NumDispatches: 10,
Endpoint: req.Endpoint,
Details: details,
})
if err != nil {
return nil, err
}
Expand All @@ -232,7 +217,8 @@ func (p *Plugin) TestAlertEndpoint(ctx context.Context, req *alertingv1.TestAler
go func() {
time.Sleep(time.Second * 30)
// can't use request context here because it will be cancelled
_, err := p.DeleteConditionRoutingNode(context.Background(), &corev1.Reference{Id: dummyConditionId})
_, err := p.DeleteConditionRoutingNode(context.Background(),
triggerReq.TriggerAlertsRequest.ConditionId)
if err != nil {
lg.Errorf("failed to delete dummy condition node : %v", err)
}
Expand All @@ -248,21 +234,18 @@ func (p *Plugin) TestAlertEndpoint(ctx context.Context, req *alertingv1.TestAler
if err != nil {
return nil, err
}
// need to trigger multiple alerts here, a reload can cause a context.Cancel()
// to any pending post requests, resulting in the alert never being sent
apiNode := backend.NewAlertManagerPostAlertClient(
ctx,
availableEndpoint,
backend.WithLogger(lg),
backend.WithPostAlertBody(dummyConditionId, nil),
backend.WithPostAlertBody(triggerReq.TriggerAlertsRequest.ConditionId.GetId(), nil),
backend.WithDefaultRetrier(),
backend.WithExpectClosure(backend.NewExpectStatusCodes([]int{200, 422})))
// need to trigger multiple alerts here, a reload can cause a context.Cancel()
// to any pending post requests, resulting in the alert never being sent
for i := 0; i < 5; i++ {
err = apiNode.DoRequest()
if err != nil {
lg.Errorf("Failed to post alert to alertmanager : %s", err)
}
time.Sleep(time.Second * 1)
err = apiNode.DoRequest()
if err != nil {
lg.Errorf("Failed to post alert to alertmanager : %s", err)
}

return &alertingv1.TestAlertEndpointResponse{}, nil
Expand Down Expand Up @@ -492,3 +475,38 @@ func (p *Plugin) DeleteIndividualEndpointInRoutingNode(ctx context.Context, refe

return &emptypb.Empty{}, nil
}

func (p *Plugin) EphemeralDispatcher(ctx context.Context, req *alertingv1.EphemeralDispatcherRequest) (*alertingv1.EphemeralDispatcherResponse, error) {
if err := req.Validate(); err != nil {
return nil, err
}
ephemeralId := uuid.New().String()
ephemeralId = req.GetPrefix() + "-" + ephemeralId

createImpl := &alertingv1.RoutingNode{
ConditionId: &corev1.Reference{Id: ephemeralId}, // is used as a unique identifier
FullAttachedEndpoints: &alertingv1.FullAttachedEndpoints{
InitialDelay: durationpb.New(time.Duration(time.Second * 0)),
Items: []*alertingv1.FullAttachedEndpoint{
{
EndpointId: ephemeralId,
AlertEndpoint: req.GetEndpoint(),
Details: req.GetDetails(),
},
},
Details: req.GetDetails(),
},
}
// create condition routing node
_, err := p.CreateConditionRoutingNode(ctx, createImpl)
if err != nil {
return nil, err
}

return &alertingv1.EphemeralDispatcherResponse{
TriggerAlertsRequest: &alertingv1.TriggerAlertsRequest{
ConditionId: &corev1.Reference{Id: ephemeralId},
Annotations: map[string]string{},
},
}, nil
}
Loading

0 comments on commit 05b1be2

Please sign in to comment.