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 }