87 lines
2.1 KiB
Go
87 lines
2.1 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"log/slog"
|
|
"os"
|
|
"os/signal"
|
|
"syscall"
|
|
"time"
|
|
|
|
"k8s.io/client-go/dynamic"
|
|
"k8s.io/client-go/rest"
|
|
)
|
|
|
|
func main() {
|
|
ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
|
|
defer stop()
|
|
if err := run(ctx, os.Args[1:]); err != nil && !errors.Is(err, context.Canceled) {
|
|
slog.Error("image mirror", "error", err)
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
|
|
func run(ctx context.Context, arguments []string) error {
|
|
if len(arguments) != 1 {
|
|
return errors.New("expected one command: run")
|
|
}
|
|
if arguments[0] != "run" {
|
|
return fmt.Errorf("unknown command %q", arguments[0])
|
|
}
|
|
clientConfig, err := rest.InClusterConfig()
|
|
if err != nil {
|
|
return fmt.Errorf("load Kubernetes configuration: %w", err)
|
|
}
|
|
client, err := dynamic.NewForConfig(clientConfig)
|
|
if err != nil {
|
|
return fmt.Errorf("create Kubernetes client: %w", err)
|
|
}
|
|
config, err := loadConfig()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
registry, err := loadRegistry()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return (&controller{client: client, registry: registry, config: config}).run(ctx)
|
|
}
|
|
|
|
func loadConfig() (controllerConfig, error) {
|
|
namespace := os.Getenv("POD_NAMESPACE")
|
|
if namespace == "" {
|
|
return controllerConfig{}, errors.New("POD_NAMESPACE is required")
|
|
}
|
|
pollInterval, err := durationEnv("POLL_INTERVAL", 20*time.Second)
|
|
if err != nil {
|
|
return controllerConfig{}, err
|
|
}
|
|
return controllerConfig{
|
|
namespace: namespace,
|
|
pollInterval: pollInterval,
|
|
prepareTemplate: valueOrDefault("PREPARE_TEMPLATE", "image-prepare"),
|
|
promotionTemplate: valueOrDefault("PROMOTION_TEMPLATE", "image-promote"),
|
|
cleanupTemplate: valueOrDefault("CLEANUP_TEMPLATE", "image-cleanup"),
|
|
}, nil
|
|
}
|
|
|
|
func durationEnv(name string, fallback time.Duration) (time.Duration, error) {
|
|
value := os.Getenv(name)
|
|
if value == "" {
|
|
return fallback, nil
|
|
}
|
|
duration, err := time.ParseDuration(value)
|
|
if err != nil || duration <= 0 {
|
|
return 0, fmt.Errorf("%s must be a positive duration", name)
|
|
}
|
|
return duration, nil
|
|
}
|
|
|
|
func valueOrDefault(name, fallback string) string {
|
|
if value := os.Getenv(name); value != "" {
|
|
return value
|
|
}
|
|
return fallback
|
|
}
|