github.com/freiheit-com/kuberpult@v1.24.2-0.20240328135542-315d5630abe6/services/rollout-service/pkg/notifier/subscriber.go (about)

     1  /*This file is part of kuberpult.
     2  
     3  Kuberpult is free software: you can redistribute it and/or modify
     4  it under the terms of the Expat(MIT) License as published by
     5  the Free Software Foundation.
     6  
     7  Kuberpult is distributed in the hope that it will be useful,
     8  but WITHOUT ANY WARRANTY; without even the implied warranty of
     9  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    10  MIT License for more details.
    11  
    12  You should have received a copy of the MIT License
    13  along with kuberpult. If not, see <https://directory.fsf.org/wiki/License:Expat>.
    14  
    15  Copyright 2023 freiheit.com*/
    16  
    17  package notifier
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  
    23  	"github.com/freiheit-com/kuberpult/pkg/setup"
    24  	"github.com/freiheit-com/kuberpult/services/rollout-service/pkg/service"
    25  )
    26  
    27  var errChannelClosed error = fmt.Errorf("subscriber: channel closed")
    28  
    29  func Subscribe(ctx context.Context, notifier Notifier, broadcast *service.Broadcast, health *setup.HealthReporter) error {
    30  	s := subscriber{notifier: notifier, notifyStatus: map[key]*notifyStatus{}}
    31  	return health.Retry(ctx, func() error {
    32  		initial, ch, unsubscribe := broadcast.Start()
    33  		health.ReportReady("notifying")
    34  		defer unsubscribe()
    35  		for _, ev := range initial {
    36  			s.maybeSend(ctx, ev)
    37  		}
    38  		for {
    39  			select {
    40  			case <-ctx.Done():
    41  				return nil
    42  			case ev, ok := <-ch:
    43  				if !ok {
    44  					// channel closed
    45  					// this can happen in two cases
    46  					select {
    47  					// 1. we are shutting down. Then it's expected and not an error
    48  					case <-ctx.Done():
    49  						return nil
    50  					// 2. when this subscriber fell behind too much when consuming. Then it's an error and should be handled
    51  					default:
    52  						return errChannelClosed
    53  					}
    54  				}
    55  				s.maybeSend(ctx, ev)
    56  			}
    57  		}
    58  	})
    59  }
    60  
    61  type key struct {
    62  	environment string
    63  	application string
    64  }
    65  
    66  type notifyStatus struct {
    67  	targetVersion uint64
    68  }
    69  
    70  type subscriber struct {
    71  	notifier     Notifier
    72  	notifyStatus map[key]*notifyStatus
    73  }
    74  
    75  func (s *subscriber) maybeSend(ctx context.Context, ev *service.BroadcastEvent) {
    76  	// skip cases where we don't know the kuberpult version
    77  	if ev.KuberpultVersion == nil {
    78  		return
    79  	}
    80  	// also don't notify when the version in argocd is already the right one
    81  	if ev.ArgocdVersion == ev.KuberpultVersion {
    82  		return
    83  	}
    84  	// also don't send events for the same version again
    85  	k := key{ev.Environment, ev.Application}
    86  	ns := s.notifyStatus[k]
    87  	if ns != nil && ns.targetVersion == ev.KuberpultVersion.Version {
    88  		return
    89  	}
    90  	s.notifyStatus[k] = &notifyStatus{
    91  		targetVersion: ev.KuberpultVersion.Version,
    92  	}
    93  	// finally send the request
    94  	s.notifier.NotifyArgoCd(ctx, ev.Environment, ev.Application)
    95  }