cmd/interceptor/main.go (171 lines of code) (raw):

package main import ( "context" "crypto/tls" "errors" "fmt" "log" "net/http" "os" "os/signal" "syscall" "time" triggersApi "github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1" "go.uber.org/zap" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/runtime" utilruntime "k8s.io/apimachinery/pkg/util/runtime" ctrl "sigs.k8s.io/controller-runtime" ctrlClient "sigs.k8s.io/controller-runtime/pkg/client" codebaseApiV1 "github.com/epam/edp-codebase-operator/v2/api/v1" buildInfo "github.com/epam/edp-common/pkg/config" "github.com/epam/edp-tekton/pkg/event_processor/bitbucket" "github.com/epam/edp-tekton/pkg/event_processor/gerrit" "github.com/epam/edp-tekton/pkg/event_processor/github" "github.com/epam/edp-tekton/pkg/event_processor/gitlab" "github.com/epam/edp-tekton/pkg/interceptor" ) const ( // httpsPort is the port where the interceptor service listens. Use 8443 as it does not require root privileges. httpsPort = 8443 readTimeout = 5 * time.Second writeTimeout = 20 * time.Second idleTimeout = 60 * time.Second shutDownTimeout = 5 * time.Second ) type edpInterceptorHandler struct { EDPInterceptor interceptor.EDPInterceptorInterface Logger *zap.SugaredLogger } type config struct { Namespace string InterceptorName string } func main() { zapLogger, err := zap.NewProduction() if err != nil { log.Fatalf("Failed to initialize logger: %s", err) } logger := zapLogger.Sugar() logBuildInfo(logger) var conf *config if conf, err = initEnv(); err != nil { logger.Fatalf("failed to init env: %v", err) } clusterConfig := ctrl.GetConfigOrDie() scheme := runtime.NewScheme() utilruntime.Must(codebaseApiV1.AddToScheme(scheme)) utilruntime.Must(corev1.AddToScheme(scheme)) utilruntime.Must(triggersApi.AddToScheme(scheme)) client, err := ctrlClient.New(clusterConfig, ctrlClient.Options{Scheme: scheme}) if err != nil { logger.Fatalf("Failed to get client: %v", err) } secretService := interceptor.NewSecretService(client) ctx := context.Background() certData, err := secretService.CreateCertsSecret(ctx, conf.Namespace, conf.InterceptorName) if err != nil { logger.Fatalf("Failed to create certs secret: %v", err) } logger.Infof("The secret %s was populated with certs ", interceptor.SecretCertsName) if err = secretService.UpdateCABundle(ctx, conf.Namespace, conf.InterceptorName, certData.CaCert); err != nil { logger.Fatalf("Failed to update cABundle: %v", err) } logger.Infof("Interceptor %s caBundle updated successfully", conf.InterceptorName) mux := http.NewServeMux() mux.Handle( "/", &edpInterceptorHandler{ EDPInterceptor: interceptor.NewEDPInterceptor( client, github.NewEventProcessor(client, &github.EventProcessorOptions{Logger: logger}), gitlab.NewEventProcessor(client, logger), gerrit.NewEventProcessor(client, logger), bitbucket.NewEventProcessor(client, &bitbucket.EventProcessorOptions{Logger: logger}), logger, ), Logger: logger, }, ) mux.HandleFunc("/ready", readinessHandler) tlsData := &tls.Config{ MinVersion: tls.VersionTLS13, GetCertificate: func(*tls.ClientHelloInfo) (*tls.Certificate, error) { cert, err := tls.X509KeyPair(certData.ServerCert, certData.ServerKey) if err != nil { return nil, err } return &cert, nil }, } srv := &http.Server{ Addr: fmt.Sprintf(":%d", httpsPort), ReadTimeout: readTimeout, WriteTimeout: writeTimeout, IdleTimeout: idleTimeout, Handler: mux, TLSConfig: tlsData, } go func() { if err := srv.ListenAndServeTLS("", ""); err != nil && !errors.Is(err, http.ErrServerClosed) { logger.Fatalf("Server failed: %v", err) } }() logger.Infof("Listen and serve on port %d", httpsPort) done := make(chan os.Signal, 1) signal.Notify(done, os.Interrupt, syscall.SIGINT, syscall.SIGTERM) <-done logger.Info("Server stopped") ctx, cancel := context.WithTimeout(context.Background(), shutDownTimeout) defer cancel() if err := srv.Shutdown(ctx); err != nil { logger.Fatalf("Server shutdown failed: %+v", err) } logger.Info("Server exited properly") } func (h *edpInterceptorHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { b, err := h.EDPInterceptor.Execute(r) if err != nil { interceptorErr := &interceptor.HTTPError{} if errors.As(err, interceptorErr) { h.Logger.Infof("HTTP %d - %s", interceptorErr.Status(), interceptorErr) http.Error(w, interceptorErr.Error(), interceptorErr.Status()) return } h.Logger.Errorf("Non Status Error: %s", err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return } w.Header().Add("Content-Type", "application/json") if _, err := w.Write(b); err != nil { h.Logger.Errorf("Failed to write response: %s", err) } } func readinessHandler(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusOK) } func logBuildInfo(logger *zap.SugaredLogger) { v := buildInfo.Get() logger.Info("Starting the EDP interceptor", "version", v.Version, "git-commit", v.GitCommit, "git-tag", v.GitTag, "build-date", v.BuildDate, "go-version", v.Go, "go-client", v.KubectlVersion, "platform", v.Platform, ) } func initEnv() (*config, error) { namespace, ok := os.LookupEnv("SYSTEM_NAMESPACE") if !ok { return nil, errors.New("env SYSTEM_NAMESPACE is required") } interceptorName, ok := os.LookupEnv("INTERCEPTOR_NAME") if !ok { return nil, errors.New("env INTERCEPTOR_NAME is required") } return &config{ Namespace: namespace, InterceptorName: interceptorName, }, nil }