github.com/minio/mc@v0.0.0-20240503112107-b471de8d1882/cmd/config-migrate.go (about)

     1  // Copyright (c) 2015-2022 MinIO, Inc.
     2  //
     3  // This file is part of MinIO Object Storage stack
     4  //
     5  // This program is free software: you can redistribute it and/or modify
     6  // it under the terms of the GNU Affero General Public License as published by
     7  // the Free Software Foundation, either version 3 of the License, or
     8  // (at your option) any later version.
     9  //
    10  // This program is distributed in the hope that it will be useful
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13  // GNU Affero General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU Affero General Public License
    16  // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17  
    18  package cmd
    19  
    20  import (
    21  	"fmt"
    22  	"strings"
    23  
    24  	"github.com/minio/mc/pkg/probe"
    25  	"github.com/minio/pkg/v2/console"
    26  	"github.com/minio/pkg/v2/quick"
    27  )
    28  
    29  // migrate config files from the any older version to the latest.
    30  func migrateConfig() {
    31  	// Migrate config V1 to V101
    32  	migrateConfigV1ToV101()
    33  	// Migrate config V101 to V2
    34  	migrateConfigV101ToV2()
    35  	// Migrate config V2 to V3
    36  	migrateConfigV2ToV3()
    37  	// Migrate config V3 to V4
    38  	migrateConfigV3ToV4()
    39  	// Migrate config V4 to V5
    40  	migrateConfigV4ToV5()
    41  	// Migrate config V5 to V6
    42  	migrateConfigV5ToV6()
    43  	// Migrate config V6 to V7
    44  	migrateConfigV6ToV7()
    45  	// Migrate config V7 to V8
    46  	migrateConfigV7ToV8()
    47  	// Migrate config V8 to V9
    48  	migrateConfigV8ToV9()
    49  	// Migrate config V9 to V10
    50  	migrateConfigV9ToV10()
    51  }
    52  
    53  // Migrate from config version 1.0 to 1.0.1. Populate example entries and save it back.
    54  func migrateConfigV1ToV101() {
    55  	if !isMcConfigExists() {
    56  		return
    57  	}
    58  
    59  	// Check the config version and quit early if the actual version is out of this function scope
    60  	anyCfg, e := quick.LoadConfig(mustGetMcConfigPath(), nil, &ConfigAnyVersion{})
    61  	fatalIf(probe.NewError(e), "Unable to load config version `1`.")
    62  	if anyCfg.Version() != "1.0.0" {
    63  		return
    64  	}
    65  
    66  	mcCfgV1, e := quick.LoadConfig(mustGetMcConfigPath(), nil, newConfigV1())
    67  	fatalIf(probe.NewError(e), "Unable to load config version `1`.")
    68  
    69  	// 1.0.1 is compatible to 1.0.0. We are just adding new entries.
    70  	cfgV101 := newConfigV101()
    71  
    72  	// Copy aliases.
    73  	for k, v := range mcCfgV1.Data().(*configV1).Aliases {
    74  		cfgV101.Aliases[k] = v
    75  	}
    76  
    77  	// Copy hosts.
    78  	for k, hostCfgV1 := range mcCfgV1.Data().(*configV1).Hosts {
    79  		cfgV101.Hosts[k] = hostConfigV101(hostCfgV1)
    80  	}
    81  
    82  	// Example localhost entry.
    83  	if _, ok := cfgV101.Hosts["localhost:*"]; !ok {
    84  		cfgV101.Hosts["localhost:*"] = hostConfigV101{}
    85  	}
    86  
    87  	// Example loopback IP entry.
    88  	if _, ok := cfgV101.Hosts["127.0.0.1:*"]; !ok {
    89  		cfgV101.Hosts["127.0.0.1:*"] = hostConfigV101{}
    90  	}
    91  
    92  	// Example AWS entry.
    93  	// Look for glob string (not glob match). We used to support glob based key matching earlier.
    94  	if _, ok := cfgV101.Hosts["*.s3*.amazonaws.com"]; !ok {
    95  		cfgV101.Hosts["*.s3*.amazonaws.com"] = hostConfigV101{
    96  			AccessKeyID:     "YOUR-ACCESS-KEY-ID-HERE",
    97  			SecretAccessKey: "YOUR-SECRET-ACCESS-KEY-HERE",
    98  		}
    99  	}
   100  
   101  	// Save the new config back to the disk.
   102  	mcCfgV101, e := quick.NewConfig(cfgV101, nil)
   103  	fatalIf(probe.NewError(e), "Unable to initialize quick config for config version `1.0.1`.")
   104  	e = mcCfgV101.Save(mustGetMcConfigPath())
   105  	fatalIf(probe.NewError(e), "Unable to save config version `1.0.1`.")
   106  
   107  	console.Infof("Successfully migrated %s from version `1.0.0` to version `1.0.1`.\n", mustGetMcConfigPath())
   108  }
   109  
   110  // Migrate from config `1.0.1` to `2`. Drop semantic versioning and move to integer versioning. No other changes.
   111  func migrateConfigV101ToV2() {
   112  	if !isMcConfigExists() {
   113  		return
   114  	}
   115  
   116  	// Check the config version and quit early if the actual version is out of this function scope
   117  	anyCfg, e := quick.LoadConfig(mustGetMcConfigPath(), nil, &ConfigAnyVersion{})
   118  	fatalIf(probe.NewError(e), "Unable to load config version `1`.")
   119  	if anyCfg.Version() != "1.0.1" {
   120  		return
   121  	}
   122  
   123  	mcCfgV101, e := quick.LoadConfig(mustGetMcConfigPath(), nil, newConfigV101())
   124  	fatalIf(probe.NewError(e), "Unable to load config version `1.0.1`.")
   125  
   126  	// update to newer version
   127  
   128  	cfgV2 := newConfigV2()
   129  
   130  	// Copy aliases.
   131  	for k, v := range mcCfgV101.Data().(*configV101).Aliases {
   132  		cfgV2.Aliases[k] = v
   133  	}
   134  
   135  	// Copy hosts.
   136  	for k, hostCfgV101 := range mcCfgV101.Data().(*configV101).Hosts {
   137  		cfgV2.Hosts[k] = hostConfigV2(hostCfgV101)
   138  	}
   139  
   140  	mcCfgV2, e := quick.NewConfig(cfgV2, nil)
   141  	fatalIf(probe.NewError(e), "Unable to initialize quick config for config version `2`.")
   142  
   143  	e = mcCfgV2.Save(mustGetMcConfigPath())
   144  	fatalIf(probe.NewError(e), "Unable to save config version `2`.")
   145  
   146  	console.Infof("Successfully migrated %s from version `1.0.1` to version `2`.\n", mustGetMcConfigPath())
   147  }
   148  
   149  // Migrate from config `2` to `3`. Use `-` separated names for
   150  // hostConfig using struct json tags.
   151  func migrateConfigV2ToV3() {
   152  	if !isMcConfigExists() {
   153  		return
   154  	}
   155  
   156  	// Check the config version and quit early if the actual version is out of this function scope
   157  	anyCfg, e := quick.LoadConfig(mustGetMcConfigPath(), nil, &ConfigAnyVersion{})
   158  	fatalIf(probe.NewError(e), "Unable to load config version.")
   159  	if anyCfg.Version() != "2" {
   160  		return
   161  	}
   162  
   163  	mcCfgV2, e := quick.LoadConfig(mustGetMcConfigPath(), nil, newConfigV2())
   164  	fatalIf(probe.NewError(e), "Unable to load mc config V2.")
   165  
   166  	cfgV3 := newConfigV3()
   167  
   168  	// Copy aliases.
   169  	for k, v := range mcCfgV2.Data().(*configV2).Aliases {
   170  		cfgV3.Aliases[k] = v
   171  	}
   172  
   173  	// Copy hosts.
   174  	for k, hostCfgV2 := range mcCfgV2.Data().(*configV2).Hosts {
   175  		// New hostConfV3 uses struct json tags.
   176  		cfgV3.Hosts[k] = hostConfigV3(hostCfgV2)
   177  	}
   178  
   179  	mcNewCfgV3, e := quick.NewConfig(cfgV3, nil)
   180  	fatalIf(probe.NewError(e), "Unable to initialize quick config for config version `3`.")
   181  
   182  	e = mcNewCfgV3.Save(mustGetMcConfigPath())
   183  	fatalIf(probe.NewError(e), "Unable to save config version `3`.")
   184  
   185  	console.Infof("Successfully migrated %s from version `2` to version `3`.\n", mustGetMcConfigPath())
   186  }
   187  
   188  // Migrate from config version `3` to `4`. Introduce API Signature
   189  // field in host config. Also Use JavaScript notation for field names.
   190  func migrateConfigV3ToV4() {
   191  	if !isMcConfigExists() {
   192  		return
   193  	}
   194  
   195  	// Check the config version and quit early if the actual version is out of this function scope
   196  	anyCfg, e := quick.LoadConfig(mustGetMcConfigPath(), nil, &ConfigAnyVersion{})
   197  	fatalIf(probe.NewError(e), "Unable to load config version.")
   198  	if anyCfg.Version() != "3" {
   199  		return
   200  	}
   201  
   202  	mcCfgV3, e := quick.LoadConfig(mustGetMcConfigPath(), nil, newConfigV3())
   203  	fatalIf(probe.NewError(e), "Unable to load mc config V3.")
   204  
   205  	cfgV4 := newConfigV4()
   206  	for k, v := range mcCfgV3.Data().(*configV3).Aliases {
   207  		cfgV4.Aliases[k] = v
   208  	}
   209  	// New hostConfig has API signature. All older entries were V4
   210  	// only. So it is safe to assume V4 as default for all older
   211  	// entries.
   212  	// HostConfigV4 als uses JavaScript naming notation for struct JSON tags.
   213  	for host, hostCfgV3 := range mcCfgV3.Data().(*configV3).Hosts {
   214  		cfgV4.Hosts[host] = hostConfigV4{
   215  			AccessKeyID:     hostCfgV3.AccessKeyID,
   216  			SecretAccessKey: hostCfgV3.SecretAccessKey,
   217  			Signature:       "v4",
   218  		}
   219  	}
   220  
   221  	mcNewCfgV4, e := quick.NewConfig(cfgV4, nil)
   222  	fatalIf(probe.NewError(e), "Unable to initialize quick config for config version `4`.")
   223  
   224  	e = mcNewCfgV4.Save(mustGetMcConfigPath())
   225  	fatalIf(probe.NewError(e), "Unable to save config version `4`.")
   226  
   227  	console.Infof("Successfully migrated %s from version `3` to version `4`.\n", mustGetMcConfigPath())
   228  }
   229  
   230  // Migrate config version `4` to `5`. Rename hostConfigV4.Signature  -> hostConfigV5.API.
   231  func migrateConfigV4ToV5() {
   232  	if !isMcConfigExists() {
   233  		return
   234  	}
   235  
   236  	// Check the config version and quit early if the actual version is out of this function scope
   237  	anyCfg, e := quick.LoadConfig(mustGetMcConfigPath(), nil, &ConfigAnyVersion{})
   238  	fatalIf(probe.NewError(e), "Unable to load config version.")
   239  	if anyCfg.Version() != "4" {
   240  		return
   241  	}
   242  
   243  	mcCfgV4, e := quick.LoadConfig(mustGetMcConfigPath(), nil, newConfigV4())
   244  	fatalIf(probe.NewError(e), "Unable to load mc config V4.")
   245  
   246  	cfgV5 := newConfigV5()
   247  	for k, v := range mcCfgV4.Data().(*configV4).Aliases {
   248  		cfgV5.Aliases[k] = v
   249  	}
   250  	for host, hostCfgV4 := range mcCfgV4.Data().(*configV4).Hosts {
   251  		cfgV5.Hosts[host] = hostConfigV5{
   252  			AccessKeyID:     hostCfgV4.AccessKeyID,
   253  			SecretAccessKey: hostCfgV4.SecretAccessKey,
   254  			API:             "v4", // Rename from .Signature to .API
   255  		}
   256  	}
   257  
   258  	mcNewCfgV5, e := quick.NewConfig(cfgV5, nil)
   259  	fatalIf(probe.NewError(e), "Unable to initialize quick config for config version `5`.")
   260  
   261  	e = mcNewCfgV5.Save(mustGetMcConfigPath())
   262  	fatalIf(probe.NewError(e), "Unable to save config version `5`.")
   263  
   264  	console.Infof("Successfully migrated %s from version `4` to version `5`.\n", mustGetMcConfigPath())
   265  }
   266  
   267  // Migrate config version `5` to `6`. Add google cloud storage servers
   268  // to host config. Also remove "." from s3 aws glob rule.
   269  func migrateConfigV5ToV6() {
   270  	if !isMcConfigExists() {
   271  		return
   272  	}
   273  
   274  	// Check the config version and quit early if the actual version is out of this function scope
   275  	anyCfg, e := quick.LoadConfig(mustGetMcConfigPath(), nil, &ConfigAnyVersion{})
   276  	fatalIf(probe.NewError(e), "Unable to load config version.")
   277  	if anyCfg.Version() != "5" {
   278  		return
   279  	}
   280  
   281  	mcCfgV5, e := quick.LoadConfig(mustGetMcConfigPath(), nil, newConfigV5())
   282  	fatalIf(probe.NewError(e), "Unable to load mc config V5.")
   283  
   284  	cfgV6 := newConfigV6()
   285  
   286  	// Add new Google Cloud Storage alias.
   287  	cfgV6.Aliases["gcs"] = "https://storage.googleapis.com"
   288  
   289  	for k, v := range mcCfgV5.Data().(*configV5).Aliases {
   290  		cfgV6.Aliases[k] = v
   291  	}
   292  
   293  	// Add defaults.
   294  	cfgV6.Hosts["*s3*amazonaws.com"] = hostConfigV6{
   295  		AccessKeyID:     "YOUR-ACCESS-KEY-ID-HERE",
   296  		SecretAccessKey: "YOUR-SECRET-ACCESS-KEY-HERE",
   297  		API:             "S3v4",
   298  	}
   299  	cfgV6.Hosts["*storage.googleapis.com"] = hostConfigV6{
   300  		AccessKeyID:     "YOUR-ACCESS-KEY-ID-HERE",
   301  		SecretAccessKey: "YOUR-SECRET-ACCESS-KEY-HERE",
   302  		API:             "S3v2",
   303  	}
   304  
   305  	for host, hostCfgV5 := range mcCfgV5.Data().(*configV5).Hosts {
   306  		// Find any matching s3 entry and copy keys from it to newer generalized glob entry.
   307  		if strings.Contains(host, "s3") {
   308  			if (hostCfgV5.AccessKeyID == "YOUR-ACCESS-KEY-ID-HERE") ||
   309  				(hostCfgV5.SecretAccessKey == "YOUR-SECRET-ACCESS-KEY-HERE") ||
   310  				hostCfgV5.AccessKeyID == "" ||
   311  				hostCfgV5.SecretAccessKey == "" {
   312  				continue // Skip defaults.
   313  			}
   314  			// Now we have real keys set by the user. Copy
   315  			// them over to newer glob rule.
   316  			// Original host entry has "." in the glob rule.
   317  			host = "*s3*amazonaws.com" // Use this glob entry.
   318  		}
   319  
   320  		cfgV6.Hosts[host] = hostConfigV6(hostCfgV5)
   321  	}
   322  
   323  	mcNewCfgV6, e := quick.NewConfig(cfgV6, nil)
   324  	fatalIf(probe.NewError(e), "Unable to initialize quick config for config version `6`.")
   325  
   326  	e = mcNewCfgV6.Save(mustGetMcConfigPath())
   327  	fatalIf(probe.NewError(e), "Unable to save config version `6`.")
   328  
   329  	console.Infof("Successfully migrated %s from version `5` to version `6`.\n", mustGetMcConfigPath())
   330  }
   331  
   332  // Migrate config version `6` to `7'. Remove alias map and introduce
   333  // named Host config. Also no more glob match for host config entries.
   334  func migrateConfigV6ToV7() {
   335  	if !isMcConfigExists() {
   336  		return
   337  	}
   338  
   339  	// Check the config version and quit early if the actual version is out of this function scope
   340  	anyCfg, e := quick.LoadConfig(mustGetMcConfigPath(), nil, &ConfigAnyVersion{})
   341  	fatalIf(probe.NewError(e), "Unable to load config version.")
   342  	if anyCfg.Version() != "6" {
   343  		return
   344  	}
   345  
   346  	mcCfgV6, e := quick.LoadConfig(mustGetMcConfigPath(), nil, newConfigV6())
   347  	fatalIf(probe.NewError(e), "Unable to load mc config V6.")
   348  
   349  	cfgV7 := newConfigV7()
   350  	aliasIndex := 0
   351  
   352  	// old Aliases.
   353  	oldAliases := mcCfgV6.Data().(*configV6).Aliases
   354  
   355  	// We dropped alias support in v7. We only need to migrate host configs.
   356  	for host, hostCfgV6 := range mcCfgV6.Data().(*configV6).Hosts {
   357  		// Look through old aliases, if found any matching save those entries.
   358  		for aliasName, aliasedHost := range oldAliases {
   359  			if aliasedHost == host {
   360  				cfgV7.Hosts[aliasName] = hostConfigV7{
   361  					URL:       host,
   362  					AccessKey: hostCfgV6.AccessKeyID,
   363  					SecretKey: hostCfgV6.SecretAccessKey,
   364  					API:       hostCfgV6.API,
   365  				}
   366  				continue
   367  			}
   368  		}
   369  		if host == "https://s3.amazonaws.com" {
   370  			// Only one entry can exist for "s3" domain.
   371  			cfgV7.Hosts["s3"] = hostConfigV7{
   372  				URL:       host,
   373  				AccessKey: hostCfgV6.AccessKeyID,
   374  				SecretKey: hostCfgV6.SecretAccessKey,
   375  				API:       hostCfgV6.API,
   376  			}
   377  		} else if host == "https://storage.googleapis.com" {
   378  			// Only one entry can exist for "gcs" domain.
   379  			cfgV7.Hosts["gcs"] = hostConfigV7{
   380  				URL:       host,
   381  				AccessKey: hostCfgV6.AccessKeyID,
   382  				SecretKey: hostCfgV6.SecretAccessKey,
   383  				API:       hostCfgV6.API,
   384  			}
   385  		} else {
   386  			// Assign a generic "cloud1", cloud2..." key
   387  			// for all other entries that has valid keys set.
   388  			alias := fmt.Sprintf("cloud%d", aliasIndex)
   389  			aliasIndex++
   390  			cfgV7.Hosts[alias] = hostConfigV7{
   391  				URL:       host,
   392  				AccessKey: hostCfgV6.AccessKeyID,
   393  				SecretKey: hostCfgV6.SecretAccessKey,
   394  				API:       hostCfgV6.API,
   395  			}
   396  		}
   397  	}
   398  	// Load default settings.
   399  	cfgV7.loadDefaults()
   400  	mcNewCfgV7, e := quick.NewConfig(cfgV7, nil)
   401  	fatalIf(probe.NewError(e), "Unable to initialize quick config for config version `7`.")
   402  
   403  	e = mcNewCfgV7.Save(mustGetMcConfigPath())
   404  	fatalIf(probe.NewError(e), "Unable to save config version `7`.")
   405  
   406  	console.Infof("Successfully migrated %s from version `6` to version `7`.\n", mustGetMcConfigPath())
   407  }
   408  
   409  // Migrate config version `7` to `8'. Remove hosts
   410  // 'play.min.io:9002' and 'dl.min.io:9000'.
   411  func migrateConfigV7ToV8() {
   412  	if !isMcConfigExists() {
   413  		return
   414  	}
   415  
   416  	// Check the config version and quit early if the actual version is out of this function scope
   417  	anyCfg, e := quick.LoadConfig(mustGetMcConfigPath(), nil, &ConfigAnyVersion{})
   418  	fatalIf(probe.NewError(e), "Unable to load config version.")
   419  	if anyCfg.Version() != "7" {
   420  		return
   421  	}
   422  
   423  	mcCfgV7, e := quick.LoadConfig(mustGetMcConfigPath(), nil, newConfigV7())
   424  	fatalIf(probe.NewError(e), "Unable to load mc config V7.")
   425  
   426  	cfgV8 := newConfigV8()
   427  	// We dropped alias support in v7. We only need to migrate host configs.
   428  	for host, hostCfgV7 := range mcCfgV7.Data().(*configV7).Hosts {
   429  		// Ignore 'player', 'play' and 'dl' aliases.
   430  		if host == "player" || host == "dl" || host == "play" {
   431  			continue
   432  		}
   433  		hostCfgV8 := hostConfigV8{}
   434  		hostCfgV8.URL = hostCfgV7.URL
   435  		hostCfgV8.AccessKey = hostCfgV7.AccessKey
   436  		hostCfgV8.SecretKey = hostCfgV7.SecretKey
   437  		hostCfgV8.API = hostCfgV7.API
   438  		cfgV8.Hosts[host] = hostCfgV8
   439  	}
   440  	// Load default settings.
   441  	cfgV8.loadDefaults()
   442  	mcNewCfgV8, e := quick.NewConfig(cfgV8, nil)
   443  	fatalIf(probe.NewError(e), "Unable to initialize quick config for config version `8`.")
   444  
   445  	e = mcNewCfgV8.Save(mustGetMcConfigPath())
   446  	fatalIf(probe.NewError(e), "Unable to save config version `8`.")
   447  
   448  	console.Infof("Successfully migrated %s from version `7` to version `8`.\n", mustGetMcConfigPath())
   449  }
   450  
   451  // Migrate config version `8` to `9'. Add optional field virtual
   452  func migrateConfigV8ToV9() {
   453  	if !isMcConfigExists() {
   454  		return
   455  	}
   456  
   457  	// Check the config version and quit early if the actual version is out of this function scope
   458  	anyCfg, e := quick.LoadConfig(mustGetMcConfigPath(), nil, &ConfigAnyVersion{})
   459  	fatalIf(probe.NewError(e), "Unable to load config version.")
   460  	if anyCfg.Version() != "8" {
   461  		return
   462  	}
   463  
   464  	mcCfgV8, e := quick.LoadConfig(mustGetMcConfigPath(), nil, newConfigV8())
   465  	fatalIf(probe.NewError(e), "Unable to load mc config V8.")
   466  
   467  	cfgV9 := newConfigV9()
   468  	// We dropped alias support in v8. We only need to migrate host configs.
   469  	for host, hostCfgV8 := range mcCfgV8.Data().(*configV8).Hosts {
   470  		// Ignore 'player', 'play' and 'dl' aliases.
   471  		if host == "player" || host == "dl" || host == "play" {
   472  			continue
   473  		}
   474  		hostCfgV9 := hostConfigV9{}
   475  		hostCfgV9.URL = hostCfgV8.URL
   476  		hostCfgV9.AccessKey = hostCfgV8.AccessKey
   477  		hostCfgV9.SecretKey = hostCfgV8.SecretKey
   478  		hostCfgV9.API = hostCfgV8.API
   479  		hostCfgV9.Lookup = "auto"
   480  		cfgV9.Hosts[host] = hostCfgV9
   481  	}
   482  
   483  	mcNewCfgV9, e := quick.NewConfig(cfgV9, nil)
   484  	fatalIf(probe.NewError(e), "Unable to initialize quick config for config version `9`.")
   485  
   486  	e = mcNewCfgV9.Save(mustGetMcConfigPath())
   487  	fatalIf(probe.NewError(e), "Unable to save config version `9`.")
   488  
   489  	console.Infof("Successfully migrated %s from version `8` to version `9`.\n", mustGetMcConfigPath())
   490  }
   491  
   492  // Migrate config version `9` to `10'. Rename 'hosts' to 'aliases' and 'lookup' to 'path'
   493  func migrateConfigV9ToV10() {
   494  	if !isMcConfigExists() {
   495  		return
   496  	}
   497  
   498  	// Check the config version and quit early if the actual version is out of this function scope
   499  	anyCfg, e := quick.LoadConfig(mustGetMcConfigPath(), nil, &ConfigAnyVersion{})
   500  	fatalIf(probe.NewError(e), "Unable to load config version.")
   501  	if anyCfg.Version() != "9" {
   502  		return
   503  	}
   504  
   505  	mcCfgV9, e := quick.LoadConfig(mustGetMcConfigPath(), nil, newConfigV9())
   506  	fatalIf(probe.NewError(e), "Unable to load mc config V8.")
   507  
   508  	cfgV10 := newConfigV10()
   509  	isEmpty := true
   510  	// We dropped alias support in v8. We only need to migrate host configs.
   511  	for host, hostCfgV9 := range mcCfgV9.Data().(*configV9).Hosts {
   512  		isEmpty = false
   513  		hostCfgV10 := aliasConfigV10{}
   514  		hostCfgV10.URL = hostCfgV9.URL
   515  		hostCfgV10.AccessKey = hostCfgV9.AccessKey
   516  		hostCfgV10.SecretKey = hostCfgV9.SecretKey
   517  		hostCfgV10.API = hostCfgV9.API
   518  		switch hostCfgV9.Lookup {
   519  		case "dns":
   520  			hostCfgV10.Path = "off"
   521  		case "path":
   522  			hostCfgV10.Path = "on"
   523  		default:
   524  			hostCfgV10.Path = "auto"
   525  		}
   526  
   527  		cfgV10.Aliases[host] = hostCfgV10
   528  	}
   529  
   530  	if isEmpty {
   531  		// Load default settings.
   532  		cfgV10.loadDefaults()
   533  	}
   534  
   535  	mcNewCfgV10, e := quick.NewConfig(cfgV10, nil)
   536  	fatalIf(probe.NewError(e), "Unable to initialize quick config for config version `10`.")
   537  
   538  	e = mcNewCfgV10.Save(mustGetMcConfigPath())
   539  	fatalIf(probe.NewError(e), "Unable to save config version `10`.")
   540  
   541  	console.Infof("Successfully migrated %s from version `9` to version `10`.\n", mustGetMcConfigPath())
   542  }