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  }