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-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"
)
2023-03-15 23:10:18 +01:00
type ConfigurationCmd struct {
ContainerdRegistryConfigPath string ` arg:"--containerd-registry-config-path" default:"/etc/containerd/certs.d" help:"Directory where mirror configuration is written." `
2023-02-10 11:59:05 +01:00
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." `
2023-03-15 23:10:18 +01:00
}
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 {
2023-03-15 23:10:18 +01:00
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 )
2023-03-15 23:10:18 +01:00
ctx := logr . NewContext ( context . Background ( ) , log )
2023-01-24 15:47:27 +01:00
2023-03-15 23:10:18 +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 {
2023-03-15 23:10:18 +01:00
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 {
2023-03-15 23:10:18 +01:00
fs := afero . NewOsFs ( )
2023-03-17 13:41:43 +01:00
err := mirror . AddMirrorConfiguration ( ctx , fs , args . ContainerdRegistryConfigPath , args . Registries , args . MirrorRegistries )
2023-03-15 23:10:18 +01:00
if err != nil {
return err
}
return nil
}
2023-03-17 13:41:43 +01:00
func registryCommand ( ctx context . Context , args * RegistryCmd ) ( err error ) {
2023-03-15 23:10:18 +01:00
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
}
2023-03-10 18:15:10 +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
}