mirror of
https://github.com/netbirdio/netbird.git
synced 2026-03-31 06:34:14 -04:00
202 lines
6.1 KiB
Go
202 lines
6.1 KiB
Go
//go:build !ios && !android
|
|
|
|
package cmd
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"maps"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"github.com/spf13/cobra"
|
|
|
|
"github.com/netbirdio/netbird/client/configs"
|
|
"github.com/netbirdio/netbird/util"
|
|
)
|
|
|
|
const serviceParamsFile = "service.json"
|
|
|
|
// serviceParams holds install-time service parameters that persist across
|
|
// uninstall/reinstall cycles. Saved to <stateDir>/service.json.
|
|
type serviceParams struct {
|
|
LogLevel string `json:"log_level"`
|
|
DaemonAddr string `json:"daemon_addr"`
|
|
ManagementURL string `json:"management_url,omitempty"`
|
|
ConfigPath string `json:"config_path,omitempty"`
|
|
LogFiles []string `json:"log_files,omitempty"`
|
|
DisableProfiles bool `json:"disable_profiles,omitempty"`
|
|
DisableUpdateSettings bool `json:"disable_update_settings,omitempty"`
|
|
ServiceEnvVars map[string]string `json:"service_env_vars,omitempty"`
|
|
}
|
|
|
|
// serviceParamsPath returns the path to the service params file.
|
|
func serviceParamsPath() string {
|
|
return filepath.Join(configs.StateDir, serviceParamsFile)
|
|
}
|
|
|
|
// loadServiceParams reads saved service parameters from disk.
|
|
// Returns nil with no error if the file does not exist.
|
|
func loadServiceParams() (*serviceParams, error) {
|
|
path := serviceParamsPath()
|
|
|
|
data, err := os.ReadFile(path)
|
|
if err != nil {
|
|
if os.IsNotExist(err) {
|
|
return nil, nil //nolint:nilnil
|
|
}
|
|
return nil, fmt.Errorf("read service params %s: %w", path, err)
|
|
}
|
|
|
|
var params serviceParams
|
|
if err := json.Unmarshal(data, ¶ms); err != nil {
|
|
return nil, fmt.Errorf("parse service params %s: %w", path, err)
|
|
}
|
|
|
|
return ¶ms, nil
|
|
}
|
|
|
|
// saveServiceParams writes current service parameters to disk atomically
|
|
// with restricted permissions.
|
|
func saveServiceParams(params *serviceParams) error {
|
|
path := serviceParamsPath()
|
|
if err := util.WriteJsonWithRestrictedPermission(context.Background(), path, params); err != nil {
|
|
return fmt.Errorf("save service params: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// currentServiceParams captures the current state of all package-level
|
|
// variables into a serviceParams struct.
|
|
func currentServiceParams() *serviceParams {
|
|
params := &serviceParams{
|
|
LogLevel: logLevel,
|
|
DaemonAddr: daemonAddr,
|
|
ManagementURL: managementURL,
|
|
ConfigPath: configPath,
|
|
LogFiles: logFiles,
|
|
DisableProfiles: profilesDisabled,
|
|
DisableUpdateSettings: updateSettingsDisabled,
|
|
}
|
|
|
|
if len(serviceEnvVars) > 0 {
|
|
parsed, err := parseServiceEnvVars(serviceEnvVars)
|
|
if err == nil && len(parsed) > 0 {
|
|
params.ServiceEnvVars = parsed
|
|
}
|
|
}
|
|
|
|
return params
|
|
}
|
|
|
|
// loadAndApplyServiceParams loads saved params from disk and applies them
|
|
// to any flags that were not explicitly set.
|
|
func loadAndApplyServiceParams(cmd *cobra.Command) error {
|
|
params, err := loadServiceParams()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
applyServiceParams(cmd, params)
|
|
return nil
|
|
}
|
|
|
|
// applyServiceParams merges saved parameters into package-level variables
|
|
// for any flag that was not explicitly set by the user (via CLI or env var).
|
|
// Flags that were Changed() are left untouched.
|
|
func applyServiceParams(cmd *cobra.Command, params *serviceParams) {
|
|
if params == nil {
|
|
return
|
|
}
|
|
|
|
// For fields with non-empty defaults (log-level, daemon-addr), keep the
|
|
// != "" guard so that an older service.json missing the field doesn't
|
|
// clobber the default with an empty string.
|
|
if !rootCmd.PersistentFlags().Changed("log-level") && params.LogLevel != "" {
|
|
logLevel = params.LogLevel
|
|
}
|
|
|
|
if !rootCmd.PersistentFlags().Changed("daemon-addr") && params.DaemonAddr != "" {
|
|
daemonAddr = params.DaemonAddr
|
|
}
|
|
|
|
// For optional fields where empty means "use default", always apply so
|
|
// that an explicit clear (--management-url "") persists across reinstalls.
|
|
if !rootCmd.PersistentFlags().Changed("management-url") {
|
|
managementURL = params.ManagementURL
|
|
}
|
|
|
|
if !rootCmd.PersistentFlags().Changed("config") {
|
|
configPath = params.ConfigPath
|
|
}
|
|
|
|
if !rootCmd.PersistentFlags().Changed("log-file") {
|
|
logFiles = params.LogFiles
|
|
}
|
|
|
|
if !serviceCmd.PersistentFlags().Changed("disable-profiles") {
|
|
profilesDisabled = params.DisableProfiles
|
|
}
|
|
|
|
if !serviceCmd.PersistentFlags().Changed("disable-update-settings") {
|
|
updateSettingsDisabled = params.DisableUpdateSettings
|
|
}
|
|
|
|
applyServiceEnvParams(cmd, params)
|
|
}
|
|
|
|
// applyServiceEnvParams merges saved service environment variables.
|
|
// If --service-env was explicitly set, explicit values win on key conflict
|
|
// but saved keys not in the explicit set are carried over.
|
|
// If --service-env was not set, saved env vars are used entirely.
|
|
func applyServiceEnvParams(cmd *cobra.Command, params *serviceParams) {
|
|
if len(params.ServiceEnvVars) == 0 {
|
|
return
|
|
}
|
|
|
|
if !cmd.Flags().Changed("service-env") {
|
|
// No explicit env vars: rebuild serviceEnvVars from saved params.
|
|
serviceEnvVars = envMapToSlice(params.ServiceEnvVars)
|
|
return
|
|
}
|
|
|
|
// Explicit env vars were provided: merge saved values underneath.
|
|
explicit, err := parseServiceEnvVars(serviceEnvVars)
|
|
if err != nil {
|
|
cmd.PrintErrf("Warning: parse explicit service env vars for merge: %v\n", err)
|
|
return
|
|
}
|
|
|
|
merged := make(map[string]string, len(params.ServiceEnvVars)+len(explicit))
|
|
maps.Copy(merged, params.ServiceEnvVars)
|
|
maps.Copy(merged, explicit) // explicit wins on conflict
|
|
serviceEnvVars = envMapToSlice(merged)
|
|
}
|
|
|
|
var resetParamsCmd = &cobra.Command{
|
|
Use: "reset-params",
|
|
Short: "Remove saved service install parameters",
|
|
Long: "Removes the saved service.json file so the next install uses default parameters.",
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
path := serviceParamsPath()
|
|
if err := os.Remove(path); err != nil {
|
|
if os.IsNotExist(err) {
|
|
cmd.Println("No saved service parameters found")
|
|
return nil
|
|
}
|
|
return fmt.Errorf("remove service params: %w", err)
|
|
}
|
|
cmd.Printf("Removed saved service parameters (%s)\n", path)
|
|
return nil
|
|
},
|
|
}
|
|
|
|
// envMapToSlice converts a map of env vars to a KEY=VALUE slice.
|
|
func envMapToSlice(m map[string]string) []string {
|
|
s := make([]string, 0, len(m))
|
|
for k, v := range m {
|
|
s = append(s, k+"="+v)
|
|
}
|
|
return s
|
|
}
|