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] = ¬ifyStatus{ 91 targetVersion: ev.KuberpultVersion.Version, 92 } 93 // finally send the request 94 s.notifier.NotifyArgoCd(ctx, ev.Environment, ev.Application) 95 }