From a3e228fbbe2090847ec7ea3c057cd4c5841f40ce Mon Sep 17 00:00:00 2001 From: Nate Walck Date: Mon, 30 Oct 2017 23:37:12 -0400 Subject: [PATCH] Added ability to store multiple servers and switch between them (#275) Added ability to store multiple servers, switch between them and migrate old config format to new config format. Closes #189 --- cmd/mdmctl/apply.go | 4 +- cmd/mdmctl/config.go | 155 +++++++++++++++++++++++++++++++++++++----- cmd/mdmctl/get.go | 4 +- cmd/mdmctl/mdmcert.go | 2 +- cmd/mdmctl/mdmdctl.go | 3 + cmd/mdmctl/remove.go | 4 +- 6 files changed, 147 insertions(+), 25 deletions(-) diff --git a/cmd/mdmctl/apply.go b/cmd/mdmctl/apply.go index 21291f9d..d08bd43a 100644 --- a/cmd/mdmctl/apply.go +++ b/cmd/mdmctl/apply.go @@ -20,12 +20,12 @@ import ( ) type applyCommand struct { - config *ClientConfig + config *ServerConfig applysvc apply.Service } func (cmd *applyCommand) setup() error { - cfg, err := LoadClientConfig() + cfg, err := LoadServerConfig() if err != nil { return err } diff --git a/cmd/mdmctl/config.go b/cmd/mdmctl/config.go index 822e1397..0a792d7c 100644 --- a/cmd/mdmctl/config.go +++ b/cmd/mdmctl/config.go @@ -14,6 +14,8 @@ import ( "crypto/tls" "net/http" + + "github.com/pkg/errors" ) type configCommand struct { @@ -37,16 +39,18 @@ func (cmd *configCommand) Run(args []string) error { os.Exit(1) } - var config *ClientConfig - if cfg, err := LoadClientConfig(); err == nil { - config = cfg - } else { - config = new(ClientConfig) + if strings.ToLower(args[0]) != "migrate" { + checkForOldConfig() } - var run func(*ClientConfig, []string) error + + var run func([]string) error switch strings.ToLower(args[0]) { + case "migrate": + run = migrateCmd case "set": run = setCmd + case "switch": + run = switchCmd case "print": printConfig() return nil @@ -55,7 +59,7 @@ func (cmd *configCommand) Run(args []string) error { os.Exit(1) } - return run(config, args[1:]) + return run(args[1:]) } func printConfig() { @@ -74,14 +78,56 @@ func (cmd *configCommand) Usage() error { const help = ` mdmctl config print mdmctl config set -h +mdmctl config switch -h ` fmt.Println(help) return nil } -func setCmd(cfg *ClientConfig, args []string) error { +func checkForOldConfig() error { + configPath, err := namedClientConfigPath("default.json") + if err != nil { + return err + } + if _, err := os.Stat(configPath); !os.IsNotExist(err) { + fmt.Println("Found old style config. You must migrate it to continue") + fmt.Println("Run `mdmctl config migrate -name=myconfig`") + os.Exit(1) + } + return nil +} +func migrateServerConfig(configName string) error { + configPath, err := namedClientConfigPath("default.json") + if err != nil { + return err + } + cfgData, err := ioutil.ReadFile(configPath) + if err != nil { + return err + } + var serverCfg *ServerConfig + err = json.Unmarshal(cfgData, &serverCfg) + if err != nil { + return errors.Wrapf(err, "failed to unmarshal %s", configPath) + } + if err = saveServerConfig(serverCfg, configName); err != nil { + return err + } + if err = os.Remove(configPath); err != nil { + return err + } + err = switchServerConfig(configName) + if err != nil { + fmt.Errorf("Failed to set %s as active config", configName) + } + fmt.Println("Successfully migrated old config.") + return nil +} + +func setCmd(args []string) error { flagset := flag.NewFlagSet("set", flag.ExitOnError) var ( + flName = flagset.String("name", "", "name of the server") flToken = flagset.String("api-token", "", "api token to connect to micromdm server") flServerURL = flagset.String("server-url", "", "server url of micromdm server") flSkipVerify = flagset.Bool("skip-verify", false, "skip verification of server certificate (insecure)") @@ -92,6 +138,8 @@ func setCmd(cfg *ClientConfig, args []string) error { return err } + cfg := new(ServerConfig) + if *flToken != "" { cfg.APIToken = *flToken } @@ -104,7 +152,33 @@ func setCmd(cfg *ClientConfig, args []string) error { cfg.SkipVerify = *flSkipVerify - return SaveClientConfig(cfg) + return saveServerConfig(cfg, *flName) +} + +func switchCmd(args []string) error { + flagset := flag.NewFlagSet("switch", flag.ExitOnError) + var ( + flName = flagset.String("name", "", "name of the server to switch to") + ) + + flagset.Usage = usageFor(flagset, "mdmctl config switch [flags]") + if err := flagset.Parse(args); err != nil { + return err + } + + return switchServerConfig(*flName) +} +func migrateCmd(args []string) error { + flagset := flag.NewFlagSet("migrate", flag.ExitOnError) + var ( + flName = flagset.String("name", "", "name of the server to switch to") + ) + + if err := flagset.Parse(args); err != nil { + return err + } + + return migrateServerConfig(*flName) } func validateServerURL(serverURL string) (string, error) { @@ -125,14 +199,22 @@ func validateServerURL(serverURL string) (string, error) { } func clientConfigPath() (string, error) { + configPath, err := namedClientConfigPath("servers.json") + if err != nil { + return "", err + } + return configPath, err +} + +func namedClientConfigPath(fileName string) (string, error) { usr, err := user.Current() if err != nil { return "", err } - return filepath.Join(usr.HomeDir, ".micromdm", "default.json"), err + return filepath.Join(usr.HomeDir, ".micromdm", fileName), err } -func SaveClientConfig(cfg *ClientConfig) error { +func saveClientConfig(clientCfg *ClientConfig) error { configPath, err := clientConfigPath() if err != nil { return err @@ -147,23 +229,45 @@ func SaveClientConfig(cfg *ClientConfig) error { return err } defer f.Close() - - if cfg == nil { - cfg = new(ClientConfig) - } enc := json.NewEncoder(f) enc.SetIndent("", " ") - return enc.Encode(cfg) + return enc.Encode(clientCfg) } -func LoadClientConfig() (*ClientConfig, error) { +func saveServerConfig(cfg *ServerConfig, name string) error { + clientCfg, err := loadClientConfig() + if err != nil { + if os.IsNotExist(errors.Cause(err)) { + clientCfg = new(ClientConfig) + clientCfg.Servers = make(map[string]ServerConfig) + } else { + return err + } + } + if cfg == nil { + cfg = new(ServerConfig) + } + clientCfg.Servers[name] = *cfg + return saveClientConfig(clientCfg) +} + +func switchServerConfig(name string) error { + clientCfg, err := loadClientConfig() + if err != nil { + return err + } + clientCfg.Active = name + return saveClientConfig(clientCfg) +} + +func loadClientConfig() (*ClientConfig, error) { path, err := clientConfigPath() if err != nil { return nil, err } cfgData, err := ioutil.ReadFile(path) if err != nil { - return nil, fmt.Errorf("unable to load default config file: %s", err) + return nil, errors.Wrap(err, "unable to load default config file") } var cfg ClientConfig err = json.Unmarshal(cfgData, &cfg) @@ -173,7 +277,22 @@ func LoadClientConfig() (*ClientConfig, error) { return &cfg, nil } +func LoadServerConfig() (*ServerConfig, error) { + cfg, err := loadClientConfig() + if err != nil { + return nil, err + } + var serverCfg ServerConfig + serverCfg = cfg.Servers[cfg.Active] + return &serverCfg, nil +} + type ClientConfig struct { + Active string `json:"active"` + Servers map[string]ServerConfig `json:"servers"` +} + +type ServerConfig struct { APIToken string `json:"api_token"` ServerURL string `json:"server_url"` SkipVerify bool `json:"skip_verify"` diff --git a/cmd/mdmctl/get.go b/cmd/mdmctl/get.go index 1a8091e9..9be78020 100644 --- a/cmd/mdmctl/get.go +++ b/cmd/mdmctl/get.go @@ -20,12 +20,12 @@ import ( ) type getCommand struct { - config *ClientConfig + config *ServerConfig list list.Service } func (cmd *getCommand) setup() error { - cfg, err := LoadClientConfig() + cfg, err := LoadServerConfig() if err != nil { return err } diff --git a/cmd/mdmctl/mdmcert.go b/cmd/mdmctl/mdmcert.go index 5b1bd8e7..096376b5 100644 --- a/cmd/mdmctl/mdmcert.go +++ b/cmd/mdmctl/mdmcert.go @@ -197,7 +197,7 @@ func (cmd *mdmcertCommand) runUpload(args []string) error { return err } - cfg, err := LoadClientConfig() + cfg, err := LoadServerConfig() if err != nil { return errors.Wrap(err, "load mdmctl client config") } diff --git a/cmd/mdmctl/mdmdctl.go b/cmd/mdmctl/mdmdctl.go index 8a335261..1781bdb3 100644 --- a/cmd/mdmctl/mdmdctl.go +++ b/cmd/mdmctl/mdmdctl.go @@ -15,6 +15,9 @@ func main() { usage() os.Exit(1) } + if strings.ToLower(os.Args[1]) != "config" { + checkForOldConfig() + } var run func([]string) error switch strings.ToLower(os.Args[1]) { case "version", "-version": diff --git a/cmd/mdmctl/remove.go b/cmd/mdmctl/remove.go index 17c951f0..591cfa48 100644 --- a/cmd/mdmctl/remove.go +++ b/cmd/mdmctl/remove.go @@ -12,12 +12,12 @@ import ( ) type removeCommand struct { - config *ClientConfig + config *ServerConfig remove remove.Service } func (cmd *removeCommand) setup() error { - cfg, err := LoadClientConfig() + cfg, err := LoadServerConfig() if err != nil { return err }