Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion Dockerfile.controller
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@ COPY api/ api/
COPY internal/ internal/
COPY pkg/ pkg/

RUN CGO_ENABLED=0 GOOS=linux GOEXPERIMENT=jsonv2 go build -a -o ./controller ./cmd/controller
ARG VERSION=dev

RUN CGO_ENABLED=0 GOOS=linux GOEXPERIMENT=jsonv2 go build -a \
-ldflags "-X github.com/kubewarden/sbomscanner/internal/version.Version=${VERSION}" \
-o ./controller ./cmd/controller

# Use distroless as minimal base image to package the manager binary
# Refer to https://github.com/GoogleContainerTools/distroless for more details
Expand Down
6 changes: 5 additions & 1 deletion Dockerfile.mcp
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@ COPY api/ api/
COPY internal/ internal/
COPY pkg/ pkg/

RUN CGO_ENABLED=0 GOOS=linux GOEXPERIMENT=jsonv2 go build -a -o ./mcp ./cmd/mcp
ARG VERSION=dev

RUN CGO_ENABLED=0 GOOS=linux GOEXPERIMENT=jsonv2 go build -a \
-ldflags "-X github.com/kubewarden/sbomscanner/internal/version.Version=${VERSION}" \
-o ./mcp ./cmd/mcp

# Use distroless as minimal base image to package the mcp binary
# Refer to https://github.com/GoogleContainerTools/distroless for more details
Expand Down
6 changes: 5 additions & 1 deletion Dockerfile.storage
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@ COPY api/ api/
COPY internal/ internal/
COPY pkg/ pkg/

RUN CGO_ENABLED=0 GOOS=linux GOEXPERIMENT=jsonv2 go build -a -o ./storage ./cmd/storage
ARG VERSION=dev

RUN CGO_ENABLED=0 GOOS=linux GOEXPERIMENT=jsonv2 go build -a \
-ldflags "-X github.com/kubewarden/sbomscanner/internal/version.Version=${VERSION}" \
-o ./storage ./cmd/storage

# Use distroless as minimal base image to package the manager binary
# Refer to https://github.com/GoogleContainerTools/distroless for more details
Expand Down
6 changes: 5 additions & 1 deletion Dockerfile.worker
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@ COPY api/ api/
COPY internal/ internal/
COPY pkg/ pkg/

RUN CGO_ENABLED=0 GOOS=linux GOEXPERIMENT=jsonv2 go build -a -o ./worker ./cmd/worker
ARG VERSION=dev

RUN CGO_ENABLED=0 GOOS=linux GOEXPERIMENT=jsonv2 go build -a \
-ldflags "-X github.com/kubewarden/sbomscanner/internal/version.Version=${VERSION}" \
-o ./worker ./cmd/worker

# Use distroless as minimal base image to package the manager binary
# Refer to https://github.com/GoogleContainerTools/distroless for more details
Expand Down
200 changes: 105 additions & 95 deletions cmd/controller/main.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package main

import (
"context"
"crypto/tls"
"flag"
"fmt"
"log/slog"
"os"
"time"
Expand Down Expand Up @@ -38,6 +40,8 @@ import (
"github.com/kubewarden/sbomscanner/internal/controller"
"github.com/kubewarden/sbomscanner/internal/messaging"
"github.com/kubewarden/sbomscanner/internal/storage"
"github.com/kubewarden/sbomscanner/internal/telemetry"
"github.com/kubewarden/sbomscanner/internal/version"
webhookv1alpha1 "github.com/kubewarden/sbomscanner/internal/webhook/v1alpha1"
// +kubebuilder:scaffold:imports
)
Expand Down Expand Up @@ -91,28 +95,45 @@ func parseFlags() Config {
}

func main() {
var tlsOpts []func(*tls.Config)
cfg := parseFlags()
if err := run(cfg); err != nil {
//nolint:sloglint // No structured logger is available at this scope: run() owns the logger lifecycle.
slog.Error("controller exited with error", "error", err)
os.Exit(1)
}
}

func run(cfg Config) error {
var tlsOpts []func(*tls.Config)

slogLevel, err := cmdutil.ParseLogLevel(cfg.LogLevel)
if err != nil {
//nolint:sloglint // Use the global logger since the logger is not yet initialized
slog.Error(
"error parsing log level",
"error",
err,
)
os.Exit(1)
return fmt.Errorf("parsing log level: %w", err)
}
opts := slog.HandlerOptions{
Level: slogLevel,
}
slogHandler := slog.NewJSONHandler(os.Stdout, &opts)
slogHandler := telemetry.NewTraceContextHandler(slog.NewJSONHandler(os.Stdout, &opts))
slogger := slog.New(slogHandler)
logger := logr.FromSlogHandler(slogHandler).WithValues("component", "controller")
ctrl.SetLogger(logger)
setupLog := logger.WithName("setup")

signalHandler := ctrl.SetupSignalHandler()

// Initialize OpenTelemetry. No-op when OTEL_EXPORTER_OTLP_ENDPOINT is unset.
shutdownTelemetry, err := telemetry.Setup(signalHandler, "sbomscanner-controller", version.Version)
if err != nil {
return fmt.Errorf("initializing telemetry: %w", err)
}
defer func() {
shutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := shutdownTelemetry(shutdownCtx); err != nil {
setupLog.Error(err, "telemetry shutdown error")
}
}()

// if the enable-http2 flag is false (the default), http/2 should be disabled
// due to its vulnerabilities. More specifically, disabling http/2 will
// prevent from being vulnerable to the HTTP/2 Stream Cancellation and
Expand Down Expand Up @@ -163,8 +184,6 @@ func main() {

// +kubebuilder:scaffold:scheme

signalHandler := ctrl.SetupSignalHandler()

natsOpts := []nats.Option{
nats.RootCAs(cfg.NatsCAFile),
nats.ClientCert(cfg.NatsCertFile, cfg.NatsKeyFile),
Expand All @@ -175,29 +194,25 @@ func main() {
slogger = slogger.With("task", "init")

if err := cmdutil.WaitForStorageTypes(signalHandler, ctrl.GetConfigOrDie(), slogger); err != nil {
slogger.Error("Storage types are not available.", "error", err)
os.Exit(1)
return fmt.Errorf("waiting for storage types: %w", err)
}

if err := cmdutil.WaitForJetStream(signalHandler, cfg.NatsURL, natsOpts, slogger); err != nil {
slogger.Error("JetStream is not available.", "error", err)
os.Exit(1)
return fmt.Errorf("waiting for JetStream: %w", err)
}

slogger.Info("Initialization tasks completed successfully.")
os.Exit(0)
return nil
}

nc, err := nats.Connect(cfg.NatsURL, natsOpts...)
if err != nil {
setupLog.Error(err, "unable to connect to NATS server", "natsURL", cfg.NatsURL)
os.Exit(1)
return fmt.Errorf("connecting to NATS server %q: %w", cfg.NatsURL, err)
}

publisher, err := messaging.NewNatsPublisher(signalHandler, nc, slogger)
if err != nil {
setupLog.Error(err, "unable to create NATS publisher")
os.Exit(1)
return fmt.Errorf("creating NATS publisher: %w", err)
}

cacheByObject := buildCacheByObject(cfg)
Expand Down Expand Up @@ -230,131 +245,126 @@ func main() {
},
})
if err != nil {
setupLog.Error(err, "unable to start manager")
os.Exit(1)
return fmt.Errorf("creating manager: %w", err)
}

if err := controller.SetupIndexer(signalHandler, mgr); err != nil {
setupLog.Error(err, "unable to set up indexer")
os.Exit(1)
return fmt.Errorf("setting up indexer: %w", err)
}

if err := (&controller.ScanJobReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
Publisher: publisher,
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "ScanJob")
os.Exit(1)
return fmt.Errorf("creating ScanJob controller: %w", err)
}

if err := (&controller.VulnerabilityReportReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "VulnerabilityReport")
os.Exit(1)
return fmt.Errorf("creating VulnerabilityReport controller: %w", err)
}

if err := (&controller.RegistryScanRunner{
Client: mgr.GetClient(),
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create runner", "runner", "RegistryScanRunner")
os.Exit(1)
return fmt.Errorf("creating RegistryScanRunner: %w", err)
}

if cfg.WorkloadScan {
if err := (&controller.WorkloadScanReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "WorkloadScan")
os.Exit(1)
}

if err := (&controller.ImageWorkloadScanReconciler{
Client: mgr.GetClient(),
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "ImageWorkloadScan")
os.Exit(1)
if err := setupWorkloadScanControllers(mgr); err != nil {
return err
}
}

//nolint: nestif // The node scan controllers and webhook are related and should be grouped together
if cfg.NodeScan {
if err = (&controller.NodeScanRunner{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create runner", "runner", "NodeScanRunner")
os.Exit(1)
}

if err = (&controller.NodeScanReconciler{
Client: mgr.GetClient(),
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "NodeScan")
os.Exit(1)
}

if err = (&controller.NodeScanConfigurationReconciler{
Client: mgr.GetClient(),
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "NodeScanConfiguration")
os.Exit(1)
}

if err = (&controller.NodeScanJobReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
Publisher: publisher,
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "NodeScanJob")
os.Exit(1)
}

if err = webhookv1alpha1.SetupNodeScanConfigurationWebhookWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create webhook", "webhook", "NodeScanConfiguration")
os.Exit(1)
}

if err = webhookv1alpha1.SetupNodeScanJobWebhookWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create webhook", "webhook", "NodeScanJob")
os.Exit(1)
if err := setupNodeScanControllers(mgr, publisher); err != nil {
return err
}
}

if err = webhookv1alpha1.SetupRegistryWebhookWithManager(mgr, cfg.ServiceAccountNamespace, cfg.ServiceAccountName); err != nil {
setupLog.Error(err, "unable to create webhook", "webhook", "Registry")
os.Exit(1)
return fmt.Errorf("creating Registry webhook: %w", err)
}

if err = webhookv1alpha1.SetupScanJobWebhookWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create webhook", "webhook", "ScanJob")
os.Exit(1)
return fmt.Errorf("creating ScanJob webhook: %w", err)
}

if err = webhookv1alpha1.SetupWorkloadScanConfigurationWebhookWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create webhook", "webhook", "WorkloadScanConfiguration")
os.Exit(1)
return fmt.Errorf("creating WorkloadScanConfiguration webhook: %w", err)
}

// +kubebuilder:scaffold:builder

if err = mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
setupLog.Error(err, "unable to set up health check")
os.Exit(1)
return fmt.Errorf("adding healthz check: %w", err)
}
if err = mgr.AddReadyzCheck("readyz", healthz.Ping); err != nil {
setupLog.Error(err, "unable to set up ready check")
os.Exit(1)
return fmt.Errorf("adding readyz check: %w", err)
}

setupLog.Info("starting manager")
if err = mgr.Start(signalHandler); err != nil {
setupLog.Error(err, "problem running manager")
os.Exit(1)
return fmt.Errorf("running manager: %w", err)
}
return nil
}

func setupWorkloadScanControllers(mgr ctrl.Manager) error {
if err := (&controller.WorkloadScanReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
}).SetupWithManager(mgr); err != nil {
return fmt.Errorf("creating WorkloadScan controller: %w", err)
}

if err := (&controller.ImageWorkloadScanReconciler{
Client: mgr.GetClient(),
}).SetupWithManager(mgr); err != nil {
return fmt.Errorf("creating ImageWorkloadScan controller: %w", err)
}
return nil
}

func setupNodeScanControllers(mgr ctrl.Manager, publisher *messaging.NatsPublisher) error {
if err := (&controller.NodeScanRunner{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
}).SetupWithManager(mgr); err != nil {
return fmt.Errorf("creating NodeScanRunner: %w", err)
}

if err := (&controller.NodeScanReconciler{
Client: mgr.GetClient(),
}).SetupWithManager(mgr); err != nil {
return fmt.Errorf("creating NodeScan controller: %w", err)
}

if err := (&controller.NodeScanConfigurationReconciler{
Client: mgr.GetClient(),
}).SetupWithManager(mgr); err != nil {
return fmt.Errorf("creating NodeScanConfiguration controller: %w", err)
}

if err := (&controller.NodeScanJobReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
Publisher: publisher,
}).SetupWithManager(mgr); err != nil {
return fmt.Errorf("creating NodeScanJob controller: %w", err)
}

if err := webhookv1alpha1.SetupNodeScanConfigurationWebhookWithManager(mgr); err != nil {
return fmt.Errorf("creating NodeScanConfiguration webhook: %w", err)
}

if err := webhookv1alpha1.SetupNodeScanJobWebhookWithManager(mgr); err != nil {
return fmt.Errorf("creating NodeScanJob webhook: %w", err)
}
return nil
}

func buildCacheByObject(cfg Config) map[client.Object]cache.ByObject {
Expand Down
Loading