github.com/argoproj/argo-cd/v3@v3.2.1/cmpserver/server.go (about)

     1  package cmpserver
     2  
     3  import (
     4  	"fmt"
     5  	"net"
     6  	"os"
     7  	"os/signal"
     8  	"syscall"
     9  
    10  	grpc_prometheus "github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus"
    11  	"github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/logging"
    12  	"github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/recovery"
    13  	"github.com/prometheus/client_golang/prometheus"
    14  	log "github.com/sirupsen/logrus"
    15  	"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
    16  	"google.golang.org/grpc"
    17  	"google.golang.org/grpc/health"
    18  	"google.golang.org/grpc/health/grpc_health_v1"
    19  	"google.golang.org/grpc/keepalive"
    20  	"google.golang.org/grpc/reflection"
    21  
    22  	"github.com/argoproj/argo-cd/v3/cmpserver/apiclient"
    23  	"github.com/argoproj/argo-cd/v3/cmpserver/plugin"
    24  	"github.com/argoproj/argo-cd/v3/common"
    25  	versionpkg "github.com/argoproj/argo-cd/v3/pkg/apiclient/version"
    26  	"github.com/argoproj/argo-cd/v3/server/version"
    27  	"github.com/argoproj/argo-cd/v3/util/errors"
    28  	grpc_util "github.com/argoproj/argo-cd/v3/util/grpc"
    29  )
    30  
    31  // ArgoCDCMPServer is the config management plugin server implementation
    32  type ArgoCDCMPServer struct {
    33  	opts          []grpc.ServerOption
    34  	initConstants plugin.CMPServerInitConstants
    35  	stopCh        chan os.Signal
    36  	doneCh        chan any
    37  	sig           os.Signal
    38  }
    39  
    40  // NewServer returns a new instance of the Argo CD config management plugin server
    41  func NewServer(initConstants plugin.CMPServerInitConstants) (*ArgoCDCMPServer, error) {
    42  	var serverMetricsOptions []grpc_prometheus.ServerMetricsOption
    43  	if os.Getenv(common.EnvEnableGRPCTimeHistogramEnv) == "true" {
    44  		serverMetricsOptions = append(serverMetricsOptions, grpc_prometheus.WithServerHandlingTimeHistogram())
    45  	}
    46  	serverMetrics := grpc_prometheus.NewServerMetrics(serverMetricsOptions...)
    47  	reg := prometheus.NewRegistry()
    48  	reg.MustRegister(serverMetrics)
    49  
    50  	serverLog := log.NewEntry(log.StandardLogger())
    51  	streamInterceptors := []grpc.StreamServerInterceptor{
    52  		logging.StreamServerInterceptor(grpc_util.InterceptorLogger(serverLog)),
    53  		serverMetrics.StreamServerInterceptor(),
    54  		recovery.StreamServerInterceptor(recovery.WithRecoveryHandler(grpc_util.LoggerRecoveryHandler(serverLog))),
    55  	}
    56  	unaryInterceptors := []grpc.UnaryServerInterceptor{
    57  		logging.UnaryServerInterceptor(grpc_util.InterceptorLogger(serverLog)),
    58  		serverMetrics.UnaryServerInterceptor(),
    59  		recovery.UnaryServerInterceptor(recovery.WithRecoveryHandler(grpc_util.LoggerRecoveryHandler(serverLog))),
    60  	}
    61  
    62  	serverOpts := []grpc.ServerOption{
    63  		grpc.ChainUnaryInterceptor(unaryInterceptors...),
    64  		grpc.ChainStreamInterceptor(streamInterceptors...),
    65  		grpc.MaxRecvMsgSize(apiclient.MaxGRPCMessageSize),
    66  		grpc.MaxSendMsgSize(apiclient.MaxGRPCMessageSize),
    67  		grpc.KeepaliveEnforcementPolicy(
    68  			keepalive.EnforcementPolicy{
    69  				MinTime: common.GetGRPCKeepAliveEnforcementMinimum(),
    70  			},
    71  		),
    72  		grpc.StatsHandler(otelgrpc.NewServerHandler()),
    73  	}
    74  
    75  	return &ArgoCDCMPServer{
    76  		opts:          serverOpts,
    77  		stopCh:        make(chan os.Signal),
    78  		doneCh:        make(chan any),
    79  		initConstants: initConstants,
    80  	}, nil
    81  }
    82  
    83  func (a *ArgoCDCMPServer) Run() {
    84  	config := a.initConstants.PluginConfig
    85  
    86  	// Listen on the socket address
    87  	_ = os.Remove(config.Address())
    88  	listener, err := net.Listen("unix", config.Address())
    89  	errors.CheckError(err)
    90  	log.Infof("argocd-cmp-server %s serving on %s", common.GetVersion(), listener.Addr())
    91  
    92  	signal.Notify(a.stopCh, syscall.SIGINT, syscall.SIGTERM)
    93  	go a.Shutdown(config.Address())
    94  
    95  	grpcServer, err := a.CreateGRPC()
    96  	errors.CheckError(err)
    97  	err = grpcServer.Serve(listener)
    98  	errors.CheckError(err)
    99  
   100  	if a.sig != nil {
   101  		<-a.doneCh
   102  	}
   103  }
   104  
   105  // CreateGRPC creates new configured grpc server
   106  func (a *ArgoCDCMPServer) CreateGRPC() (*grpc.Server, error) {
   107  	server := grpc.NewServer(a.opts...)
   108  	versionpkg.RegisterVersionServiceServer(server, version.NewServer(nil, func() (bool, error) {
   109  		return true, nil
   110  	}))
   111  	pluginService := plugin.NewService(a.initConstants)
   112  	err := pluginService.Init(common.GetCMPWorkDir())
   113  	if err != nil {
   114  		return nil, fmt.Errorf("error initializing plugin service: %w", err)
   115  	}
   116  	apiclient.RegisterConfigManagementPluginServiceServer(server, pluginService)
   117  
   118  	healthService := health.NewServer()
   119  	grpc_health_v1.RegisterHealthServer(server, healthService)
   120  
   121  	// Register reflection service on gRPC server.
   122  	reflection.Register(server)
   123  
   124  	return server, nil
   125  }
   126  
   127  func (a *ArgoCDCMPServer) Shutdown(address string) {
   128  	defer signal.Stop(a.stopCh)
   129  	a.sig = <-a.stopCh
   130  	_ = os.Remove(address)
   131  	close(a.doneCh)
   132  }