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"
|
2023-01-24 16:56:17 +01:00
|
|
|
"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-25 15:05:40 +01:00
|
|
|
"github.com/xenitab/spegel/internal/discover"
|
2023-01-24 15:47:27 +01:00
|
|
|
"github.com/xenitab/spegel/internal/mirror"
|
|
|
|
"github.com/xenitab/spegel/internal/registry"
|
|
|
|
"github.com/xenitab/spegel/internal/state"
|
|
|
|
"github.com/xenitab/spegel/internal/store"
|
|
|
|
)
|
|
|
|
|
|
|
|
type arguments struct {
|
|
|
|
PodIP string `arg:"--pod-ip,required"`
|
|
|
|
ServiceName string `arg:"--service-name,required"`
|
|
|
|
RegistryAddr string `arg:"--registry-addr" default:":5000"`
|
2023-01-24 16:40:11 +01:00
|
|
|
MetricsAddr string `arg:"--metrics-addr" default:":9090"`
|
2023-01-25 13:14:41 +01:00
|
|
|
RedisAddr string `arg:"--redis-addr, required"`
|
|
|
|
MirrorRegistries []url.URL `arg:"--mirror-registries,required"`
|
|
|
|
ImageFilter string `arg:"--image-filter"`
|
2023-01-24 15:47:27 +01:00
|
|
|
ContainerdSock string `arg:"--containerd-sock" default:"/run/containerd/containerd.sock"`
|
|
|
|
ContainerdNamespace string `arg:"--containerd-namespace" default:"k8s.io"`
|
|
|
|
ContainerdRegistryConfigPath string `arg:"--containerd-registry-config-path" default:"/etc/containerd/certs.d"`
|
|
|
|
ContainerdMirrorAdd bool `arg:"--containerd-mirror-add" default:"true"`
|
|
|
|
ContainerdMirrorRemove bool `arg:"--containerd-mirror-remove" default:"true"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
args := &arguments{}
|
|
|
|
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 16:56:17 +01:00
|
|
|
ctx, cancel := signal.NotifyContext(ctx, syscall.SIGTERM)
|
2023-01-24 15:47:27 +01:00
|
|
|
defer cancel()
|
|
|
|
g, ctx := errgroup.WithContext(ctx)
|
|
|
|
|
|
|
|
containerdClient, err := containerd.New(args.ContainerdSock, containerd.WithDefaultNamespace(args.ContainerdNamespace))
|
|
|
|
if err != nil {
|
|
|
|
log.Error(err, "could not create containerd client")
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
defer containerdClient.Close()
|
|
|
|
|
2023-01-24 16:40:11 +01:00
|
|
|
// Start metrics server
|
|
|
|
mux := http.NewServeMux()
|
|
|
|
mux.Handle("/metrics", promhttp.Handler())
|
|
|
|
srv := &http.Server{
|
|
|
|
Addr: args.MetricsAddr,
|
|
|
|
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()
|
|
|
|
shutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
|
|
defer cancel()
|
|
|
|
return srv.Shutdown(shutdownCtx)
|
|
|
|
})
|
|
|
|
|
2023-01-24 15:47:27 +01:00
|
|
|
// Setup and run store
|
2023-01-25 15:05:40 +01:00
|
|
|
store, err := store.NewRedisStore(args.PodIP, discover.NewDNS(args.ServiceName), args.RedisAddr)
|
2023-01-24 15:47:27 +01:00
|
|
|
if err != nil {
|
|
|
|
log.Error(err, "could not create store")
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
g.Go(func() error {
|
2023-01-25 00:24:38 +01:00
|
|
|
return state.Track(ctx, containerdClient, store, args.ImageFilter)
|
2023-01-24 15:47:27 +01:00
|
|
|
})
|
|
|
|
|
|
|
|
// Configure mirrors
|
2023-01-25 11:23:30 +01:00
|
|
|
// TODO: Wait to write mirror configuration until registry is up and running.
|
2023-01-24 15:47:27 +01:00
|
|
|
if args.ContainerdMirrorAdd {
|
|
|
|
fs := afero.NewOsFs()
|
|
|
|
err := mirror.AddMirrorConfiguration(ctx, fs, args.ContainerdRegistryConfigPath, args.RegistryAddr, args.MirrorRegistries)
|
|
|
|
if err != nil {
|
|
|
|
log.Error(err, "could not configure containerd mirror")
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
// TODO: Validate clean up is run if error occurs before start.
|
|
|
|
if args.ContainerdMirrorRemove {
|
|
|
|
g.Go(func() error {
|
|
|
|
<-ctx.Done()
|
|
|
|
return mirror.RemoveMirrorConfiguration(ctx, fs, args.ContainerdRegistryConfigPath, args.MirrorRegistries)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Setup and run registry
|
|
|
|
reg, err := registry.NewRegistry(ctx, args.RegistryAddr, containerdClient, store)
|
|
|
|
if err != nil {
|
|
|
|
log.Error(err, "could not create registry")
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
g.Go(func() error {
|
|
|
|
return reg.ListenAndServe(ctx)
|
|
|
|
})
|
|
|
|
g.Go(func() error {
|
|
|
|
<-ctx.Done()
|
|
|
|
return reg.Shutdown()
|
|
|
|
})
|
|
|
|
|
|
|
|
log.Info("running registry", "addr", args.RegistryAddr)
|
|
|
|
err = g.Wait()
|
|
|
|
if err != nil {
|
|
|
|
log.Error(err, "exiting with error")
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
log.Info("gracefully shutdown registry")
|
|
|
|
}
|