github.com/thanos-io/thanos@v0.32.5/internal/cortex/util/services/services.go (about) 1 // Copyright (c) The Cortex Authors. 2 // Licensed under the Apache License 2.0. 3 4 package services 5 6 import ( 7 "context" 8 "fmt" 9 "time" 10 ) 11 12 // NewIdleService initializes basic service as an "idle" service -- it doesn't do anything in its Running state, 13 // but still supports all state transitions. 14 func NewIdleService(up StartingFn, down StoppingFn) *BasicService { 15 run := func(ctx context.Context) error { 16 <-ctx.Done() 17 return nil 18 } 19 20 return NewBasicService(up, run, down) 21 } 22 23 // OneIteration is one iteration of the timer service. Called repeatedly until service is stopped, or this function returns error 24 // in which case, service will fail. 25 type OneIteration func(ctx context.Context) error 26 27 // NewTimerService runs iteration function on every interval tick. When iteration returns error, service fails. 28 func NewTimerService(interval time.Duration, start StartingFn, iter OneIteration, stop StoppingFn) *BasicService { 29 run := func(ctx context.Context) error { 30 t := time.NewTicker(interval) 31 defer t.Stop() 32 33 for { 34 select { 35 case <-t.C: 36 err := iter(ctx) 37 if err != nil { 38 return err 39 } 40 41 case <-ctx.Done(): 42 return nil 43 } 44 } 45 } 46 47 return NewBasicService(start, run, stop) 48 } 49 50 // NewListener provides a simple way to build service listener from supplied functions. 51 // Functions are only called when not nil. 52 func NewListener(starting, running func(), stopping, terminated func(from State), failed func(from State, failure error)) Listener { 53 return &funcBasedListener{ 54 startingFn: starting, 55 runningFn: running, 56 stoppingFn: stopping, 57 terminatedFn: terminated, 58 failedFn: failed, 59 } 60 } 61 62 type funcBasedListener struct { 63 startingFn func() 64 runningFn func() 65 stoppingFn func(from State) 66 terminatedFn func(from State) 67 failedFn func(from State, failure error) 68 } 69 70 func (f *funcBasedListener) Starting() { 71 if f.startingFn != nil { 72 f.startingFn() 73 } 74 } 75 76 func (f funcBasedListener) Running() { 77 if f.runningFn != nil { 78 f.runningFn() 79 } 80 } 81 82 func (f funcBasedListener) Stopping(from State) { 83 if f.stoppingFn != nil { 84 f.stoppingFn(from) 85 } 86 } 87 88 func (f funcBasedListener) Terminated(from State) { 89 if f.terminatedFn != nil { 90 f.terminatedFn(from) 91 } 92 } 93 94 func (f funcBasedListener) Failed(from State, failure error) { 95 if f.failedFn != nil { 96 f.failedFn(from, failure) 97 } 98 } 99 100 // StartAndAwaitRunning starts the service, and then waits until it reaches Running state. 101 // If service fails to start, its failure case is returned. 102 // Service must be in New state when this function is called. 103 // 104 // Notice that context passed to the service for starting is the same as context used for waiting! 105 // If you need these contexts to be different, please use StartAsync and AwaitRunning directly. 106 func StartAndAwaitRunning(ctx context.Context, service Service) error { 107 err := service.StartAsync(ctx) 108 if err != nil { 109 return err 110 } 111 112 err = service.AwaitRunning(ctx) 113 if e := service.FailureCase(); e != nil { 114 return e 115 } 116 117 return err 118 } 119 120 // StopAndAwaitTerminated asks service to stop, and then waits until service reaches Terminated 121 // or Failed state. If service ends in Terminated state, this function returns error. On Failed state, 122 // it returns the failure case. Other errors are possible too (eg. if context stops before service does). 123 func StopAndAwaitTerminated(ctx context.Context, service Service) error { 124 service.StopAsync() 125 err := service.AwaitTerminated(ctx) 126 if err == nil { 127 return nil 128 } 129 130 if e := service.FailureCase(); e != nil { 131 return e 132 } 133 134 // can happen e.g. if context was canceled 135 return err 136 } 137 138 // DescribeService returns name of the service, if it has one, or returns string representation of the service. 139 func DescribeService(service Service) string { 140 name := "" 141 if named, ok := service.(NamedService); ok { 142 name = named.ServiceName() 143 } 144 if name == "" { 145 name = fmt.Sprintf("%v", service) 146 } 147 return name 148 }