github.com/cilium/cilium@v1.16.2/cilium-health/launch/launcher.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Cilium
     3  
     4  package launch
     5  
     6  import (
     7  	"errors"
     8  	"fmt"
     9  	"net/http"
    10  	"os"
    11  
    12  	healthApi "github.com/cilium/cilium/api/v1/health/server"
    13  	"github.com/cilium/cilium/api/v1/models"
    14  	"github.com/cilium/cilium/pkg/api"
    15  	ciliumPkg "github.com/cilium/cilium/pkg/client"
    16  	"github.com/cilium/cilium/pkg/health/client"
    17  	"github.com/cilium/cilium/pkg/health/defaults"
    18  	"github.com/cilium/cilium/pkg/health/server"
    19  	"github.com/cilium/cilium/pkg/lock"
    20  	"github.com/cilium/cilium/pkg/logging"
    21  	"github.com/cilium/cilium/pkg/logging/logfields"
    22  	"github.com/cilium/cilium/pkg/option"
    23  	"github.com/cilium/cilium/pkg/time"
    24  )
    25  
    26  // CiliumHealth launches and polls the cilium-health daemon
    27  type CiliumHealth struct {
    28  	mutex  lock.RWMutex
    29  	server *server.Server
    30  	client *client.Client
    31  	status *models.Status
    32  }
    33  
    34  var log = logging.DefaultLogger.WithField(logfields.LogSubsys, "cilium-health-launcher")
    35  
    36  const (
    37  	serverProbeInterval  = 60 * time.Second
    38  	serverProbeDeadline  = 1 * time.Second
    39  	connectRetryInterval = 1 * time.Second
    40  	statusProbeInterval  = 5 * time.Second
    41  )
    42  
    43  // Launch starts the cilium-health server and returns a handle to obtain its status
    44  func Launch(spec *healthApi.Spec, initialized <-chan struct{}) (*CiliumHealth, error) {
    45  	var (
    46  		err error
    47  		ch  = &CiliumHealth{}
    48  	)
    49  
    50  	config := server.Config{
    51  		Debug:         option.Config.Opts.IsEnabled(option.Debug),
    52  		ProbeInterval: serverProbeInterval,
    53  		ProbeDeadline: serverProbeDeadline,
    54  		HTTPPathPort:  option.Config.ClusterHealthPort,
    55  		HealthAPISpec: spec,
    56  	}
    57  
    58  	ch.server, err = server.NewServer(config)
    59  	if err != nil {
    60  		return nil, fmt.Errorf("failed to instantiate cilium-health server: %w", err)
    61  	}
    62  
    63  	ch.client, err = client.NewDefaultClient()
    64  	if err != nil {
    65  		return nil, fmt.Errorf("failed to instantiate cilium-health client: %w", err)
    66  	}
    67  
    68  	go ch.runServer(initialized)
    69  
    70  	return ch, nil
    71  }
    72  
    73  func (ch *CiliumHealth) runServer(initialized <-chan struct{}) {
    74  	// Wait until the agent has initialized sufficiently
    75  	<-initialized
    76  
    77  	// Wait until Cilium API is available
    78  	for {
    79  		cli, err := ciliumPkg.NewDefaultClient()
    80  		if err == nil {
    81  			// Making sure that we can talk with the daemon.
    82  			if _, err = cli.Daemon.GetHealthz(nil); err == nil {
    83  				break
    84  			}
    85  		}
    86  		log.WithError(err).Debugf("Cannot establish connection to local cilium instance")
    87  		time.Sleep(connectRetryInterval)
    88  	}
    89  
    90  	// Launch cilium-health API server
    91  	os.Remove(defaults.SockPath)
    92  	go func() {
    93  		defer ch.server.Shutdown()
    94  		if err := ch.server.Serve(); err != nil && !errors.Is(err, http.ErrServerClosed) {
    95  			log.WithError(err).Error("Failed to serve cilium-health API")
    96  		}
    97  	}()
    98  
    99  	// When the unix socket is made available, set its permissions.
   100  	scopedLog := log.WithField(logfields.Path, defaults.SockPath)
   101  	for {
   102  		_, err := os.Stat(defaults.SockPath)
   103  		if err == nil {
   104  			break
   105  		}
   106  		scopedLog.WithError(err).Debugf("Cannot find socket")
   107  		time.Sleep(1 * time.Second)
   108  	}
   109  	if err := api.SetDefaultPermissions(defaults.SockPath); err != nil {
   110  		scopedLog.WithError(err).Fatal("Cannot set default permissions on socket")
   111  	}
   112  
   113  	// Periodically fetch status from cilium-health server
   114  	for {
   115  		status := &models.Status{
   116  			State: models.StatusStateOk,
   117  		}
   118  
   119  		_, err := ch.client.Restapi.GetHealthz(nil)
   120  		if err != nil {
   121  			status.Msg = err.Error()
   122  			status.State = models.StatusStateWarning
   123  		}
   124  
   125  		ch.setStatus(status)
   126  		time.Sleep(statusProbeInterval)
   127  	}
   128  }
   129  
   130  // GetStatus returns the status of the cilium-health daemon.
   131  func (ch *CiliumHealth) GetStatus() *models.Status {
   132  	ch.mutex.RLock()
   133  	status := ch.status
   134  	ch.mutex.RUnlock()
   135  	return status
   136  }
   137  
   138  // setStatus updates the status of the cilium-health daemon.
   139  func (ch *CiliumHealth) setStatus(status *models.Status) {
   140  	ch.mutex.Lock()
   141  	ch.status = status
   142  	ch.mutex.Unlock()
   143  }