mirror of
https://github.com/netbirdio/netbird.git
synced 2026-04-16 07:16:38 +00:00
175 lines
4.5 KiB
Go
175 lines
4.5 KiB
Go
package main
|
|
|
|
import (
|
|
"flag"
|
|
"fmt"
|
|
"os"
|
|
"strconv"
|
|
|
|
"github.com/netbirdio/netbird/util"
|
|
)
|
|
|
|
type migrationConfig struct {
|
|
// Data
|
|
dashboardURL string
|
|
apiURL string
|
|
configPath string
|
|
dataDir string
|
|
idpSeedInfo string
|
|
|
|
// Options
|
|
dryRun bool
|
|
force bool
|
|
skipConfig bool
|
|
skipPopulateUserInfo bool
|
|
|
|
// Logging
|
|
logLevel string
|
|
}
|
|
|
|
func config() (*migrationConfig, error) {
|
|
cfg, err := configFromArgs(os.Args[1:])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err := util.InitLog(cfg.logLevel, util.LogConsole); err != nil {
|
|
return nil, fmt.Errorf("init logger: %w", err)
|
|
}
|
|
|
|
return cfg, nil
|
|
}
|
|
|
|
func configFromArgs(args []string) (*migrationConfig, error) {
|
|
var cfg migrationConfig
|
|
var domain string
|
|
|
|
fs := flag.NewFlagSet("netbird-idp-migrate", flag.ContinueOnError)
|
|
fs.StringVar(&domain, "domain", "", "domain for both dashboard and API")
|
|
fs.StringVar(&cfg.dashboardURL, "dashboard-url", "", "dashboard URL")
|
|
fs.StringVar(&cfg.apiURL, "api-url", "", "API URL")
|
|
fs.StringVar(&cfg.configPath, "config", "", "path to management.json (required)")
|
|
fs.StringVar(&cfg.dataDir, "datadir", "", "override data directory from config")
|
|
fs.StringVar(&cfg.idpSeedInfo, "idp-seed-info", "", "base64-encoded connector JSON (overrides auto-detection)")
|
|
fs.BoolVar(&cfg.dryRun, "dry-run", false, "preview changes without writing")
|
|
fs.BoolVar(&cfg.force, "force", false, "skip confirmation prompt")
|
|
fs.BoolVar(&cfg.skipConfig, "skip-config", false, "skip config generation (DB migration only)")
|
|
fs.BoolVar(&cfg.skipPopulateUserInfo, "skip-populate-user-info", false, "skip populating user info (user id migration only)")
|
|
fs.StringVar(&cfg.logLevel, "log-level", "info", "log level (debug, info, warn, error)")
|
|
|
|
if err := fs.Parse(args); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
applyOverrides(&cfg, domain)
|
|
|
|
if err := validateConfig(&cfg); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &cfg, nil
|
|
}
|
|
|
|
// applyOverrides resolves domain configuration from broad to narrow sources.
|
|
// The most granular value always wins:
|
|
//
|
|
// --domain flag (broadest, only fills blanks)
|
|
// NETBIRD_DOMAIN env (overrides flags, sets both)
|
|
// --api-domain / --dashboard-domain flags (more specific than --domain)
|
|
// NETBIRD_API_URL / NETBIRD_DASHBOARD_URL env (most specific, always wins)
|
|
//
|
|
// Other env vars unconditionally override their corresponding flags.
|
|
func applyOverrides(cfg *migrationConfig, domain string) {
|
|
// --domain is a convenience shorthand: only fills in values not already
|
|
// set by the more specific --api-domain / --dashboard-domain flags.
|
|
if domain != "" {
|
|
if cfg.apiURL == "" {
|
|
cfg.apiURL = domain
|
|
}
|
|
if cfg.dashboardURL == "" {
|
|
cfg.dashboardURL = domain
|
|
}
|
|
}
|
|
|
|
// Env vars override flags. Broad env var first, then narrow ones on top,
|
|
// so the most granular value always wins.
|
|
if val, ok := os.LookupEnv("NETBIRD_DOMAIN"); ok {
|
|
cfg.dashboardURL = val
|
|
cfg.apiURL = val
|
|
}
|
|
|
|
if val, ok := os.LookupEnv("NETBIRD_API_URL"); ok {
|
|
cfg.apiURL = val
|
|
}
|
|
|
|
if val, ok := os.LookupEnv("NETBIRD_DASHBOARD_URL"); ok {
|
|
cfg.dashboardURL = val
|
|
}
|
|
|
|
if val, ok := os.LookupEnv("NETBIRD_CONFIG_PATH"); ok {
|
|
cfg.configPath = val
|
|
}
|
|
|
|
if val, ok := os.LookupEnv("NETBIRD_DATA_DIR"); ok {
|
|
cfg.dataDir = val
|
|
}
|
|
|
|
if val, ok := os.LookupEnv("NETBIRD_IDP_SEED_INFO"); ok {
|
|
cfg.idpSeedInfo = val
|
|
}
|
|
|
|
// Enforce dry run if any value is provided
|
|
if sval, ok := os.LookupEnv("NETBIRD_DRY_RUN"); ok {
|
|
if val, err := strconv.ParseBool(sval); err == nil {
|
|
cfg.dryRun = val
|
|
}
|
|
}
|
|
|
|
cfg.dryRun = parseBool("NETBIRD_DRY_RUN", cfg.dryRun)
|
|
cfg.force = parseBool("NETBIRD_FORCE", cfg.force)
|
|
cfg.skipConfig = parseBool("NETBIRD_SKIP_CONFIG", cfg.skipConfig)
|
|
cfg.skipPopulateUserInfo = parseBool("NETBIRD_SKIP_POPULATE_USER_INFO", cfg.skipPopulateUserInfo)
|
|
|
|
if val, ok := os.LookupEnv("NETBIRD_LOG_LEVEL"); ok {
|
|
cfg.logLevel = val
|
|
}
|
|
}
|
|
|
|
func parseBool(varName string, defaultVal bool) bool {
|
|
stringValue, ok := os.LookupEnv(varName)
|
|
if !ok {
|
|
return defaultVal
|
|
}
|
|
|
|
boolValue, err := strconv.ParseBool(stringValue)
|
|
if err != nil {
|
|
return defaultVal
|
|
}
|
|
|
|
return boolValue
|
|
}
|
|
|
|
func validateConfig(cfg *migrationConfig) error {
|
|
if cfg.configPath == "" {
|
|
return fmt.Errorf("--config is required")
|
|
}
|
|
|
|
if cfg.dataDir == "" {
|
|
return fmt.Errorf("--datadir is required")
|
|
}
|
|
|
|
if cfg.idpSeedInfo == "" {
|
|
return fmt.Errorf("--idp-seed-info is required")
|
|
}
|
|
|
|
if cfg.apiURL == "" {
|
|
return fmt.Errorf("--api-domain is required")
|
|
}
|
|
|
|
if cfg.dashboardURL == "" {
|
|
return fmt.Errorf("--dashboard-domain is required")
|
|
}
|
|
|
|
return nil
|
|
}
|