spegel/main.go

163 lines
5.1 KiB
Go
Raw Normal View History

2023-01-24 15:47:27 +01:00
package main
import (
"context"
2023-01-24 16:40:11 +01:00
"errors"
2023-01-24 15:47:27 +01:00
"fmt"
2023-01-24 16:40:11 +01:00
"net/http"
2023-01-24 15:47:27 +01:00
"net/url"
"os"
"os/signal"
"syscall"
2023-01-24 16:40:11 +01:00
"time"
2023-01-24 15:47:27 +01:00
"github.com/alexflint/go-arg"
"github.com/containerd/containerd"
"github.com/go-logr/logr"
"github.com/go-logr/zapr"
2023-01-24 16:40:11 +01:00
"github.com/prometheus/client_golang/prometheus/promhttp"
2023-01-24 15:47:27 +01:00
"github.com/spf13/afero"
"go.uber.org/zap"
"golang.org/x/sync/errgroup"
2023-01-26 18:48:02 +01:00
pkgkubernetes "github.com/xenitab/pkg/kubernetes"
2023-05-17 23:05:30 +02:00
2023-01-24 15:47:27 +01:00
"github.com/xenitab/spegel/internal/mirror"
"github.com/xenitab/spegel/internal/registry"
2023-01-26 18:48:02 +01:00
"github.com/xenitab/spegel/internal/routing"
2023-01-24 15:47:27 +01:00
"github.com/xenitab/spegel/internal/state"
)
type ConfigurationCmd struct {
ContainerdRegistryConfigPath string `arg:"--containerd-registry-config-path" default:"/etc/containerd/certs.d" help:"Directory where mirror configuration is written."`
Registries []url.URL `arg:"--registries,required" help:"registries that are configured to be mirrored."`
MirrorRegistries []url.URL `arg:"--mirror-registries,required" help:"registries that are configured to act as mirrors."`
}
type RegistryCmd struct {
RegistryAddr string `arg:"--registry-addr,required" help:"address to server image registry."`
RouterAddr string `arg:"--router-addr,required" help:"address to serve router."`
MetricsAddr string `arg:"--metrics-addr,required" help:"address to serve metrics."`
Registries []url.URL `arg:"--registries,required" help:"registries that are configured to be mirrored."`
ImageFilter string `arg:"--image-filter" help:"inclusive image name filter."`
ContainerdSock string `arg:"--containerd-sock" default:"/run/containerd/containerd.sock" help:"Endpoint of containerd service."`
ContainerdNamespace string `arg:"--containerd-namespace" default:"k8s.io" help:"Containerd namespace to fetch images from."`
KubeconfigPath string `arg:"--kubeconfig-path" help:"Path to the kubeconfig file."`
LeaderElectionNamespace string `arg:"--leader-election-namespace" default:"spegel" help:"Kubernetes namespace to write leader election data."`
LeaderElectionName string `arg:"--leader-election-name" default:"spegel-leader-election" help:"Name of leader election."`
}
2023-03-17 13:41:43 +01:00
type Arguments struct {
Configuration *ConfigurationCmd `arg:"subcommand:configuration"`
Registry *RegistryCmd `arg:"subcommand:registry"`
2023-01-24 15:47:27 +01:00
}
func main() {
2023-03-17 13:41:43 +01:00
args := &Arguments{}
2023-01-24 15:47:27 +01:00
arg.MustParse(args)
zapLog, err := zap.NewProduction()
if err != nil {
panic(fmt.Sprintf("who watches the watchmen (%v)?", err))
}
log := zapr.NewLogger(zapLog)
ctx := logr.NewContext(context.Background(), log)
2023-01-24 15:47:27 +01:00
err = run(ctx, args)
2023-01-24 15:47:27 +01:00
if err != nil {
2023-01-31 23:17:06 +01:00
log.Error(err, "")
2023-01-24 15:47:27 +01:00
os.Exit(1)
}
2023-01-31 23:17:06 +01:00
log.Info("gracefully shutdown")
}
2023-01-24 15:47:27 +01:00
2023-03-17 13:41:43 +01:00
func run(ctx context.Context, args *Arguments) error {
ctx, cancel := signal.NotifyContext(ctx, syscall.SIGTERM)
defer cancel()
switch {
case args.Configuration != nil:
return configurationCommand(ctx, args.Configuration)
case args.Registry != nil:
return registryCommand(ctx, args.Registry)
default:
return fmt.Errorf("unknown subcommand")
}
}
2023-03-17 13:41:43 +01:00
func configurationCommand(ctx context.Context, args *ConfigurationCmd) error {
fs := afero.NewOsFs()
2023-03-17 13:41:43 +01:00
err := mirror.AddMirrorConfiguration(ctx, fs, args.ContainerdRegistryConfigPath, args.Registries, args.MirrorRegistries)
if err != nil {
return err
}
return nil
}
2023-03-17 13:41:43 +01:00
func registryCommand(ctx context.Context, args *RegistryCmd) (err error) {
log := logr.FromContextOrDiscard(ctx)
g, ctx := errgroup.WithContext(ctx)
2023-03-17 13:41:43 +01:00
cs, err := pkgkubernetes.GetKubernetesClientset(args.KubeconfigPath)
2023-01-26 18:48:02 +01:00
if err != nil {
2023-01-31 23:17:06 +01:00
return err
}
2023-03-17 13:41:43 +01:00
containerdClient, err := containerd.New(args.ContainerdSock, containerd.WithDefaultNamespace(args.ContainerdNamespace))
2023-01-31 23:17:06 +01:00
if err != nil {
return fmt.Errorf("could not create containerd client: %w", err)
2023-01-26 18:48:02 +01:00
}
defer func() {
err = errors.Join(err, containerdClient.Close())
}()
2023-01-31 23:17:06 +01:00
2023-01-24 16:40:11 +01:00
mux := http.NewServeMux()
mux.Handle("/metrics", promhttp.Handler())
srv := &http.Server{
2023-03-17 13:41:43 +01:00
Addr: args.MetricsAddr,
2023-01-24 16:40:11 +01:00
Handler: mux,
}
g.Go(func() error {
if err := srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
return err
}
return nil
})
g.Go(func() error {
<-ctx.Done()
2023-01-25 23:52:50 +01:00
shutdownCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
2023-01-24 16:40:11 +01:00
defer cancel()
return srv.Shutdown(shutdownCtx)
})
2023-03-17 13:41:43 +01:00
bootstrapper := routing.NewKubernetesBootstrapper(cs, args.LeaderElectionNamespace, args.LeaderElectionName)
router, err := routing.NewP2PRouter(ctx, args.RouterAddr, bootstrapper)
2023-01-24 15:47:27 +01:00
if err != nil {
2023-01-31 23:17:06 +01:00
return err
2023-01-24 15:47:27 +01:00
}
2023-01-25 19:08:33 +01:00
g.Go(func() error {
<-ctx.Done()
2023-01-26 18:48:02 +01:00
return router.Close()
2023-01-25 19:08:33 +01:00
})
2023-01-24 15:47:27 +01:00
g.Go(func() error {
2023-03-17 13:41:43 +01:00
return state.Track(ctx, containerdClient, router, args.Registries, args.ImageFilter)
2023-01-24 15:47:27 +01:00
})
2023-03-17 13:41:43 +01:00
reg, err := registry.NewRegistry(ctx, args.RegistryAddr, containerdClient, router)
2023-01-24 15:47:27 +01:00
if err != nil {
2023-01-31 23:17:06 +01:00
return err
2023-01-24 15:47:27 +01:00
}
g.Go(func() error {
return reg.ListenAndServe(ctx)
})
g.Go(func() error {
<-ctx.Done()
return reg.Shutdown()
})
2023-03-17 13:41:43 +01:00
log.Info("running registry", "addr", args.RegistryAddr)
2023-01-24 15:47:27 +01:00
err = g.Wait()
if err != nil {
2023-01-31 23:17:06 +01:00
return err
2023-01-24 15:47:27 +01:00
}
2023-01-31 23:17:06 +01:00
return nil
2023-01-24 15:47:27 +01:00
}