github.com/kubeshop/testkube@v1.17.23/cmd/logs/main.go (about)

     1  package main
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  
     7  	"os"
     8  	"os/signal"
     9  	"syscall"
    10  
    11  	"github.com/nats-io/nats.go/jetstream"
    12  	"github.com/oklog/run"
    13  	"go.uber.org/zap"
    14  	"google.golang.org/grpc/credentials"
    15  
    16  	"github.com/kubeshop/testkube/internal/common"
    17  	"github.com/kubeshop/testkube/pkg/agent"
    18  	"github.com/kubeshop/testkube/pkg/event/bus"
    19  	"github.com/kubeshop/testkube/pkg/log"
    20  	"github.com/kubeshop/testkube/pkg/logs"
    21  	"github.com/kubeshop/testkube/pkg/logs/adapter"
    22  	"github.com/kubeshop/testkube/pkg/logs/client"
    23  	"github.com/kubeshop/testkube/pkg/logs/config"
    24  	"github.com/kubeshop/testkube/pkg/logs/pb"
    25  	"github.com/kubeshop/testkube/pkg/logs/repository"
    26  	"github.com/kubeshop/testkube/pkg/logs/state"
    27  	"github.com/kubeshop/testkube/pkg/storage/minio"
    28  	"github.com/kubeshop/testkube/pkg/ui"
    29  )
    30  
    31  func newStorageClient(cfg *config.Config) *minio.Client {
    32  	opts := minio.GetTLSOptions(cfg.StorageSSL, cfg.StorageSkipVerify, cfg.StorageCertFile, cfg.StorageKeyFile, cfg.StorageCAFile)
    33  	return minio.NewClient(
    34  		cfg.StorageEndpoint,
    35  		cfg.StorageAccessKeyID,
    36  		cfg.StorageSecretAccessKey,
    37  		cfg.StorageRegion,
    38  		cfg.StorageToken,
    39  		cfg.StorageBucket,
    40  		opts...,
    41  	)
    42  }
    43  
    44  func main() {
    45  	var g run.Group
    46  
    47  	log := log.DefaultLogger.With("service", "logs-service-init")
    48  
    49  	ctx, cancel := context.WithCancel(context.Background())
    50  
    51  	cfg := Must(config.Get())
    52  
    53  	mode := common.ModeStandalone
    54  	if cfg.TestkubeProAPIKey != "" {
    55  		mode = common.ModeAgent
    56  	}
    57  
    58  	// Event bus
    59  	nc := Must(bus.NewNATSConnection(bus.ConnectionConfig{
    60  		NatsURI:            cfg.NatsURI,
    61  		NatsSecure:         cfg.NatsSecure,
    62  		NatsSkipVerify:     cfg.NatsSkipVerify,
    63  		NatsCertFile:       cfg.NatsCertFile,
    64  		NatsKeyFile:        cfg.NatsKeyFile,
    65  		NatsCAFile:         cfg.NatsCAFile,
    66  		NatsConnectTimeout: cfg.NatsConnectTimeout,
    67  	}))
    68  	defer func() {
    69  		log.Infof("closing nats connection")
    70  		nc.Close()
    71  	}()
    72  
    73  	js := Must(jetstream.New(nc))
    74  	logStream := Must(client.NewNatsLogStream(nc))
    75  
    76  	minioClient := newStorageClient(cfg)
    77  	if err := minioClient.Connect(); err != nil {
    78  		log.Fatalw("error connecting to minio", "error", err)
    79  	}
    80  
    81  	if err := minioClient.SetExpirationPolicy(cfg.StorageExpiration); err != nil {
    82  		log.Warnw("error setting expiration policy", "error", err)
    83  	}
    84  
    85  	kv := Must(js.CreateKeyValue(ctx, jetstream.KeyValueConfig{Bucket: cfg.KVBucketName}))
    86  	state := state.NewState(kv)
    87  
    88  	svc := logs.NewLogsService(nc, js, state, logStream).
    89  		WithHttpAddress(cfg.HttpAddress).
    90  		WithGrpcAddress(cfg.GrpcAddress).
    91  		WithLogsRepositoryFactory(repository.NewJsMinioFactory(minioClient, cfg.StorageBucket, logStream)).
    92  		WithMessageTracing(cfg.TraceMessages)
    93  
    94  	// quite noisy in logs - will echo all messages incoming from logs
    95  	if cfg.AttachDebugAdapter {
    96  		svc.AddAdapter(adapter.NewDebugAdapter())
    97  	}
    98  
    99  	creds, err := newGRPCTransportCredentials(cfg)
   100  	if err != nil {
   101  		log.Fatalw("error getting tls credentials", "error", err)
   102  	}
   103  
   104  	log.Infow("starting logs service", "mode", mode)
   105  
   106  	// add given log adapter depends from mode
   107  	switch mode {
   108  
   109  	case common.ModeAgent:
   110  		grpcConn, err := agent.NewGRPCConnection(
   111  			ctx,
   112  			cfg.TestkubeProTLSInsecure,
   113  			cfg.TestkubeProSkipVerify,
   114  			cfg.TestkubeProURL,
   115  			cfg.TestkubeProCertFile,
   116  			cfg.TestkubeProKeyFile,
   117  			cfg.TestkubeProCAFile,
   118  			log,
   119  		)
   120  		ui.ExitOnError("error creating gRPC connection for logs service", err)
   121  		defer grpcConn.Close()
   122  		grpcClient := pb.NewCloudLogsServiceClient(grpcConn)
   123  		cloudAdapter := adapter.NewCloudAdapter(grpcClient, cfg.TestkubeProAPIKey)
   124  		log.Infow("cloud adapter created", "endpoint", cfg.TestkubeProURL)
   125  		svc.AddAdapter(cloudAdapter)
   126  
   127  	case common.ModeStandalone:
   128  		minioAdapter, err := adapter.NewMinioV2Adapter(cfg.StorageEndpoint,
   129  			cfg.StorageAccessKeyID,
   130  			cfg.StorageSecretAccessKey,
   131  			cfg.StorageRegion,
   132  			cfg.StorageToken,
   133  			cfg.StorageBucket,
   134  			cfg.StorageSSL,
   135  			cfg.StorageSkipVerify,
   136  			cfg.StorageCertFile,
   137  			cfg.StorageKeyFile,
   138  			cfg.StorageCAFile)
   139  
   140  		minioAdapter.
   141  			WithTraceMessages(cfg.TraceMessages).
   142  			WithPath(cfg.StorageFilePath)
   143  
   144  		if err != nil {
   145  			log.Errorw("error creating minio adapter", "error", err)
   146  		}
   147  		log.Infow("minio adapter created", "bucket", cfg.StorageBucket, "endpoint", cfg.StorageEndpoint)
   148  		svc.AddAdapter(minioAdapter)
   149  	}
   150  
   151  	g.Add(func() error {
   152  		err := interrupt(log, ctx)
   153  		return err
   154  	}, func(error) {
   155  		log.Warnf("interrupt signal received, canceling context")
   156  		cancel()
   157  	})
   158  
   159  	g.Add(func() error {
   160  		return svc.Run(ctx)
   161  	}, func(error) {
   162  		err := svc.Shutdown(ctx)
   163  		if err != nil {
   164  			log.Errorw("error shutting down logs service", "error", err)
   165  		}
   166  		log.Warn("logs service shutdown")
   167  	})
   168  
   169  	g.Add(func() error {
   170  		return svc.RunGRPCServer(ctx, creds)
   171  	}, func(error) {
   172  		cancel()
   173  	})
   174  
   175  	// We need to do a http health check to be backward compatible with Kubernetes below 1.25
   176  	g.Add(func() error {
   177  		return svc.RunHealthCheckHandler(ctx)
   178  	}, func(error) {
   179  		cancel()
   180  	})
   181  
   182  	if err := g.Run(); err != nil {
   183  		log.Warnw("logs service run group returned an error", "error", err)
   184  	}
   185  
   186  	log.Infof("exiting")
   187  }
   188  
   189  func interrupt(logger *zap.SugaredLogger, ctx context.Context) error {
   190  	c := make(chan os.Signal, 1)
   191  	signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
   192  	select {
   193  	case s := <-c:
   194  		return errors.New("signal received" + s.String())
   195  	case <-ctx.Done():
   196  		logger.Info("stopping because of context done")
   197  		return context.Canceled
   198  	}
   199  }
   200  
   201  // Must helper function to panic on error
   202  func Must[T any](val T, err error) T {
   203  	if err != nil {
   204  		panic(err)
   205  	}
   206  	return val
   207  }
   208  
   209  func newGRPCTransportCredentials(cfg *config.Config) (credentials.TransportCredentials, error) {
   210  	return logs.GetGrpcTransportCredentials(logs.GrpcConnectionConfig{
   211  		Secure:       cfg.GrpcSecure,
   212  		ClientAuth:   cfg.GrpcClientAuth,
   213  		CertFile:     cfg.GrpcCertFile,
   214  		KeyFile:      cfg.GrpcKeyFile,
   215  		ClientCAFile: cfg.GrpcClientCAFile,
   216  	})
   217  }