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 }