client/cmd/client/main.go (104 lines of code) (raw):

// Copyright (c) Microsoft Corporation. // Licensed under the MIT license. package main import ( "context" "flag" "fmt" "os" "os/signal" "path/filepath" "syscall" "github.com/Azure/aks-secure-tls-bootstrap/client/internal/bootstrap" "go.uber.org/zap" "go.uber.org/zap/zapcore" "k8s.io/client-go/tools/clientcmd" ) var ( bootstrapConfig bootstrap.Config configFile string logFile string verbose bool ) func init() { flag.StringVar(&configFile, "config-file", "", "path to the configuration file, settings in this file will take priority over command line arguments") flag.StringVar(&logFile, "log-file", "", "path to a file where logs will be written, will be created if it does not already exist") flag.BoolVar(&verbose, "verbose", false, "enable verbose log output") flag.StringVar(&bootstrapConfig.AzureConfigPath, "azure-config", "", "path to the azure config file") flag.StringVar(&bootstrapConfig.APIServerFQDN, "apiserver-fqdn", "", "FQDN of the apiserver") flag.StringVar(&bootstrapConfig.CustomClientID, "custom-client-id", "", "client ID of the user-assigned managed identity to use when requesting a token from IMDS - if not specified the kubelet identity will be used") flag.StringVar(&bootstrapConfig.AADResource, "aad-resource", "", "resource (audience) used to request JWT tokens from AAD for authentication") flag.StringVar(&bootstrapConfig.NextProto, "next-proto", "", "ALPN next proto value") flag.StringVar(&bootstrapConfig.KubeconfigPath, "kubeconfig", "", "path to the kubeconfig - if this file does not exist, the generated kubeconfig will be placed there") flag.StringVar(&bootstrapConfig.ClusterCAFilePath, "cluster-ca-file", "", "path to the cluster CA file") flag.StringVar(&bootstrapConfig.CertFilePath, "cert-file", "", "path to the file which will contain the PEM-encoded client certificate, referenced by the generated kubeconfig") flag.StringVar(&bootstrapConfig.KeyFilePath, "key-file", "", "path to the file which will contain the PEM-encoded client key, referenced by the generated kubeconfig.") flag.BoolVar(&bootstrapConfig.InsecureSkipTLSVerify, "insecure-skip-tls-verify", false, "skip TLS verification when connecting to the control plane") flag.BoolVar(&bootstrapConfig.EnsureAuthorizedClient, "ensure-authorized", false, "ensure the specified kubeconfig contains an authorized clientset before bootstrapping") flag.Parse() } func main() { if configFile != "" { if err := bootstrapConfig.LoadFromFile(configFile); err != nil { fmt.Printf("unable to load configuration file: %s\n", err) os.Exit(1) } } if err := bootstrapConfig.Validate(); err != nil { fmt.Println(err) os.Exit(1) } logger, err := configureLogging(logFile, verbose) if err != nil { fmt.Printf("unable to construct zap logger: %s\n", err) os.Exit(1) } // defer calls are not executed on os.Exit ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) exitCode := run(ctx, logger) cancel() flush(logger) os.Exit(exitCode) } func run(ctx context.Context, logger *zap.Logger) int { client, err := bootstrap.NewClient(logger) if err != nil { logger.Error("error constructing bootstrap client", zap.Error(err)) return 1 } kubeconfigData, err := client.GetKubeletClientCredential(ctx, &bootstrapConfig) if err != nil { logger.Error("error generating kubelet client credential", zap.Error(err)) return 1 } if kubeconfigData != nil { if err := clientcmd.WriteToFile(*kubeconfigData, bootstrapConfig.KubeconfigPath); err != nil { logger.Error("error writing generated kubeconfig to disk", zap.Error(err)) return 1 } } return 0 } func configureLogging(logFile string, verbose bool) (*zap.Logger, error) { if err := os.MkdirAll(filepath.Dir(logFile), 0755); err != nil { return nil, fmt.Errorf("failed to create log directory: %w", err) } logFileHandle, err := os.OpenFile(logFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) if err != nil { return nil, fmt.Errorf("failed to open log file: %w", err) } encoderConfig := zap.NewProductionEncoderConfig() encoderConfig.TimeKey = "timestamp" encoderConfig.EncodeTime = zapcore.RFC3339NanoTimeEncoder fileEncoder := zapcore.NewJSONEncoder(encoderConfig) consoleEncoder := zapcore.NewConsoleEncoder(encoderConfig) level := zap.InfoLevel if verbose { level = zap.DebugLevel } core := zapcore.NewTee( zapcore.NewCore(fileEncoder, zapcore.AddSync(logFileHandle), level), zapcore.NewCore(consoleEncoder, zapcore.AddSync(os.Stdout), level), ) return zap.New(core), nil } func flush(logger *zap.Logger) { // per guidance from: https://github.com/uber-go/zap/issues/328 _ = logger.Sync() }