2016-04-01 11:36:56 +00:00
|
|
|
/*
|
|
|
|
Copyright 2016 GitHub Inc.
|
2016-05-16 09:09:17 +00:00
|
|
|
See https://github.com/github/gh-ost/blob/master/LICENSE
|
2016-04-01 11:36:56 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
package base
|
|
|
|
|
2016-04-04 13:29:02 +00:00
|
|
|
import (
|
|
|
|
"fmt"
|
2016-04-08 08:34:44 +00:00
|
|
|
"strings"
|
2016-04-11 15:27:16 +00:00
|
|
|
"sync"
|
2016-04-08 08:34:44 +00:00
|
|
|
"sync/atomic"
|
2016-04-07 13:57:12 +00:00
|
|
|
"time"
|
2016-04-04 13:29:02 +00:00
|
|
|
|
2016-05-16 09:09:17 +00:00
|
|
|
"github.com/github/gh-ost/go/mysql"
|
|
|
|
"github.com/github/gh-ost/go/sql"
|
2016-05-03 07:28:48 +00:00
|
|
|
|
|
|
|
"gopkg.in/gcfg.v1"
|
2016-04-04 13:29:02 +00:00
|
|
|
)
|
2016-04-01 11:36:56 +00:00
|
|
|
|
2016-04-05 07:14:22 +00:00
|
|
|
// RowsEstimateMethod is the type of row number estimation
|
2016-04-04 10:27:51 +00:00
|
|
|
type RowsEstimateMethod string
|
|
|
|
|
|
|
|
const (
|
|
|
|
TableStatusRowsEstimate RowsEstimateMethod = "TableStatusRowsEstimate"
|
|
|
|
ExplainRowsEstimate = "ExplainRowsEstimate"
|
|
|
|
CountRowsEstimate = "CountRowsEstimate"
|
|
|
|
)
|
|
|
|
|
2016-06-06 10:33:05 +00:00
|
|
|
type CutOver int
|
|
|
|
|
|
|
|
const (
|
2016-06-14 07:01:06 +00:00
|
|
|
CutOverSafe CutOver = iota
|
|
|
|
CutOverTwoStep = iota
|
2016-06-06 10:33:05 +00:00
|
|
|
)
|
|
|
|
|
2016-04-05 07:14:22 +00:00
|
|
|
// MigrationContext has the general, global state of migration. It is used by
|
|
|
|
// all components throughout the migration process.
|
2016-04-01 11:36:56 +00:00
|
|
|
type MigrationContext struct {
|
2016-04-14 11:37:56 +00:00
|
|
|
DatabaseName string
|
|
|
|
OriginalTableName string
|
|
|
|
AlterStatement string
|
|
|
|
|
2016-05-20 10:52:14 +00:00
|
|
|
CountTableRows bool
|
|
|
|
AllowedRunningOnMaster bool
|
|
|
|
SwitchToRowBinlogFormat bool
|
|
|
|
NullableUniqueKeyAllowed bool
|
2016-06-17 06:03:18 +00:00
|
|
|
ApproveRenamedColumns bool
|
|
|
|
SkipRenamedColumns bool
|
2016-04-14 11:37:56 +00:00
|
|
|
|
2016-05-17 13:35:44 +00:00
|
|
|
config ContextConfig
|
|
|
|
configMutex *sync.Mutex
|
|
|
|
ConfigFile string
|
|
|
|
CliUser string
|
|
|
|
CliPassword string
|
2016-05-03 07:28:48 +00:00
|
|
|
|
2016-06-19 15:55:37 +00:00
|
|
|
defaultNumRetries int64
|
2016-04-08 08:34:44 +00:00
|
|
|
ChunkSize int64
|
2016-04-07 13:57:12 +00:00
|
|
|
MaxLagMillisecondsThrottleThreshold int64
|
2016-05-01 18:36:36 +00:00
|
|
|
ReplictionLagQuery string
|
|
|
|
ThrottleControlReplicaKeys *mysql.InstanceKeyMap
|
2016-04-08 08:34:44 +00:00
|
|
|
ThrottleFlagFile string
|
2016-04-11 15:27:16 +00:00
|
|
|
ThrottleAdditionalFlagFile string
|
2016-06-18 19:12:07 +00:00
|
|
|
ThrottleQuery string
|
2016-06-07 09:59:17 +00:00
|
|
|
ThrottleCommandedByUser int64
|
2016-06-18 19:12:07 +00:00
|
|
|
maxLoad LoadMap
|
|
|
|
criticalLoad LoadMap
|
2016-06-07 12:05:25 +00:00
|
|
|
PostponeCutOverFlagFile string
|
2016-04-23 02:46:34 +00:00
|
|
|
SwapTablesTimeoutSeconds int64
|
2016-06-17 09:40:08 +00:00
|
|
|
PanicFlagFile string
|
2016-04-08 12:35:06 +00:00
|
|
|
|
2016-06-07 09:59:17 +00:00
|
|
|
ServeSocketFile string
|
|
|
|
ServeTCPPort int64
|
|
|
|
|
2016-04-22 20:18:56 +00:00
|
|
|
Noop bool
|
|
|
|
TestOnReplica bool
|
2016-06-15 10:18:59 +00:00
|
|
|
MigrateOnReplica bool
|
2016-04-22 20:18:56 +00:00
|
|
|
OkToDropTable bool
|
2016-05-03 09:55:17 +00:00
|
|
|
InitiallyDropOldTable bool
|
|
|
|
InitiallyDropGhostTable bool
|
2016-06-06 10:33:05 +00:00
|
|
|
CutOverType CutOver
|
2016-04-18 17:57:18 +00:00
|
|
|
|
|
|
|
TableEngine string
|
|
|
|
RowsEstimate int64
|
|
|
|
UsedRowsEstimateMethod RowsEstimateMethod
|
|
|
|
OriginalBinlogFormat string
|
|
|
|
OriginalBinlogRowImage string
|
|
|
|
InspectorConnectionConfig *mysql.ConnectionConfig
|
|
|
|
ApplierConnectionConfig *mysql.ConnectionConfig
|
|
|
|
StartTime time.Time
|
|
|
|
RowCopyStartTime time.Time
|
2016-06-19 15:55:37 +00:00
|
|
|
RowCopyEndTime time.Time
|
2016-04-18 17:57:18 +00:00
|
|
|
LockTablesStartTime time.Time
|
|
|
|
RenameTablesStartTime time.Time
|
|
|
|
RenameTablesEndTime time.Time
|
2016-05-23 12:58:53 +00:00
|
|
|
pointOfInterestTime time.Time
|
|
|
|
pointOfInterestTimeMutex *sync.Mutex
|
2016-04-18 17:57:18 +00:00
|
|
|
CurrentLag int64
|
|
|
|
TotalRowsCopied int64
|
2016-04-19 11:25:32 +00:00
|
|
|
TotalDMLEventsApplied int64
|
2016-04-18 17:57:18 +00:00
|
|
|
isThrottled bool
|
|
|
|
throttleReason string
|
|
|
|
throttleMutex *sync.Mutex
|
2016-06-13 16:36:29 +00:00
|
|
|
IsPostponingCutOver int64
|
2016-04-18 17:57:18 +00:00
|
|
|
|
2016-04-11 15:27:16 +00:00
|
|
|
OriginalTableColumns *sql.ColumnList
|
2016-04-08 12:35:06 +00:00
|
|
|
OriginalTableUniqueKeys [](*sql.UniqueKey)
|
2016-04-11 15:27:16 +00:00
|
|
|
GhostTableColumns *sql.ColumnList
|
2016-04-08 12:35:06 +00:00
|
|
|
GhostTableUniqueKeys [](*sql.UniqueKey)
|
|
|
|
UniqueKey *sql.UniqueKey
|
2016-04-11 15:27:16 +00:00
|
|
|
SharedColumns *sql.ColumnList
|
2016-06-17 06:03:18 +00:00
|
|
|
ColumnRenameMap map[string]string
|
|
|
|
MappedSharedColumns *sql.ColumnList
|
2016-04-08 12:35:06 +00:00
|
|
|
MigrationRangeMinValues *sql.ColumnValues
|
|
|
|
MigrationRangeMaxValues *sql.ColumnValues
|
|
|
|
Iteration int64
|
|
|
|
MigrationIterationRangeMinValues *sql.ColumnValues
|
|
|
|
MigrationIterationRangeMaxValues *sql.ColumnValues
|
2016-04-07 13:57:12 +00:00
|
|
|
|
|
|
|
CanStopStreaming func() bool
|
2016-04-01 11:36:56 +00:00
|
|
|
}
|
2016-04-01 14:05:44 +00:00
|
|
|
|
2016-05-17 13:35:44 +00:00
|
|
|
type ContextConfig struct {
|
|
|
|
Client struct {
|
|
|
|
User string
|
|
|
|
Password string
|
|
|
|
}
|
|
|
|
Osc struct {
|
|
|
|
Chunk_Size int64
|
|
|
|
Max_Lag_Millis int64
|
|
|
|
Replication_Lag_Query string
|
|
|
|
Max_Load string
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-04 10:27:51 +00:00
|
|
|
var context *MigrationContext
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
context = newMigrationContext()
|
|
|
|
}
|
2016-04-01 14:05:44 +00:00
|
|
|
|
|
|
|
func newMigrationContext() *MigrationContext {
|
2016-04-04 10:27:51 +00:00
|
|
|
return &MigrationContext{
|
2016-06-19 15:55:37 +00:00
|
|
|
defaultNumRetries: 60,
|
2016-04-07 13:57:12 +00:00
|
|
|
ChunkSize: 1000,
|
|
|
|
InspectorConnectionConfig: mysql.NewConnectionConfig(),
|
2016-04-14 11:37:56 +00:00
|
|
|
ApplierConnectionConfig: mysql.NewConnectionConfig(),
|
2016-04-07 13:57:12 +00:00
|
|
|
MaxLagMillisecondsThrottleThreshold: 1000,
|
2016-04-23 02:46:34 +00:00
|
|
|
SwapTablesTimeoutSeconds: 3,
|
2016-06-18 19:12:07 +00:00
|
|
|
maxLoad: NewLoadMap(),
|
|
|
|
criticalLoad: NewLoadMap(),
|
2016-04-23 02:46:34 +00:00
|
|
|
throttleMutex: &sync.Mutex{},
|
2016-05-01 18:36:36 +00:00
|
|
|
ThrottleControlReplicaKeys: mysql.NewInstanceKeyMap(),
|
2016-05-17 13:35:44 +00:00
|
|
|
configMutex: &sync.Mutex{},
|
2016-05-23 12:58:53 +00:00
|
|
|
pointOfInterestTimeMutex: &sync.Mutex{},
|
2016-06-17 06:03:18 +00:00
|
|
|
ColumnRenameMap: make(map[string]string),
|
2016-04-04 10:27:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-04 16:19:46 +00:00
|
|
|
// GetMigrationContext
|
2016-04-04 10:27:51 +00:00
|
|
|
func GetMigrationContext() *MigrationContext {
|
|
|
|
return context
|
|
|
|
}
|
|
|
|
|
2016-04-05 07:14:22 +00:00
|
|
|
// GetGhostTableName generates the name of ghost table, based on original table name
|
2016-04-04 13:29:02 +00:00
|
|
|
func (this *MigrationContext) GetGhostTableName() string {
|
2016-05-16 09:03:15 +00:00
|
|
|
return fmt.Sprintf("_%s_gst", this.OriginalTableName)
|
2016-04-04 13:29:02 +00:00
|
|
|
}
|
|
|
|
|
2016-04-11 15:27:16 +00:00
|
|
|
// GetOldTableName generates the name of the "old" table, into which the original table is renamed.
|
|
|
|
func (this *MigrationContext) GetOldTableName() string {
|
2016-05-16 09:03:15 +00:00
|
|
|
return fmt.Sprintf("_%s_old", this.OriginalTableName)
|
2016-04-11 15:27:16 +00:00
|
|
|
}
|
|
|
|
|
2016-04-07 13:57:12 +00:00
|
|
|
// GetChangelogTableName generates the name of changelog table, based on original table name
|
|
|
|
func (this *MigrationContext) GetChangelogTableName() string {
|
2016-05-16 09:03:15 +00:00
|
|
|
return fmt.Sprintf("_%s_osc", this.OriginalTableName)
|
2016-04-07 13:57:12 +00:00
|
|
|
}
|
|
|
|
|
2016-04-22 20:41:20 +00:00
|
|
|
// GetVoluntaryLockName returns a name of a voluntary lock to be used throughout
|
|
|
|
// the swap-tables process.
|
|
|
|
func (this *MigrationContext) GetVoluntaryLockName() string {
|
|
|
|
return fmt.Sprintf("%s.%s.lock", this.DatabaseName, this.OriginalTableName)
|
|
|
|
}
|
|
|
|
|
2016-04-05 07:14:22 +00:00
|
|
|
// RequiresBinlogFormatChange is `true` when the original binlog format isn't `ROW`
|
2016-04-04 10:27:51 +00:00
|
|
|
func (this *MigrationContext) RequiresBinlogFormatChange() bool {
|
|
|
|
return this.OriginalBinlogFormat != "ROW"
|
2016-04-01 14:05:44 +00:00
|
|
|
}
|
2016-04-04 13:29:02 +00:00
|
|
|
|
2016-04-14 11:37:56 +00:00
|
|
|
// InspectorIsAlsoApplier is `true` when the both inspector and applier are the
|
|
|
|
// same database instance. This would be true when running directly on master or when
|
|
|
|
// testing on replica.
|
|
|
|
func (this *MigrationContext) InspectorIsAlsoApplier() bool {
|
|
|
|
return this.InspectorConnectionConfig.Equals(this.ApplierConnectionConfig)
|
2016-04-04 13:29:02 +00:00
|
|
|
}
|
2016-04-04 16:19:46 +00:00
|
|
|
|
2016-04-05 07:14:22 +00:00
|
|
|
// HasMigrationRange tells us whether there's a range to iterate for copying rows.
|
|
|
|
// It will be `false` if the table is initially empty
|
2016-04-04 16:19:46 +00:00
|
|
|
func (this *MigrationContext) HasMigrationRange() bool {
|
2016-04-05 07:14:22 +00:00
|
|
|
return this.MigrationRangeMinValues != nil && this.MigrationRangeMaxValues != nil
|
2016-04-04 16:19:46 +00:00
|
|
|
}
|
2016-04-07 13:57:12 +00:00
|
|
|
|
2016-06-19 15:55:37 +00:00
|
|
|
func (this *MigrationContext) SetDefaultNumRetries(retries int64) {
|
|
|
|
this.throttleMutex.Lock()
|
|
|
|
defer this.throttleMutex.Unlock()
|
|
|
|
if retries > 0 {
|
|
|
|
this.defaultNumRetries = retries
|
|
|
|
}
|
|
|
|
}
|
|
|
|
func (this *MigrationContext) MaxRetries() int64 {
|
|
|
|
this.throttleMutex.Lock()
|
|
|
|
defer this.throttleMutex.Unlock()
|
|
|
|
retries := this.defaultNumRetries
|
|
|
|
return retries
|
2016-04-07 13:57:12 +00:00
|
|
|
}
|
2016-04-08 08:34:44 +00:00
|
|
|
|
|
|
|
func (this *MigrationContext) IsTransactionalTable() bool {
|
|
|
|
switch strings.ToLower(this.TableEngine) {
|
|
|
|
case "innodb":
|
|
|
|
{
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
case "tokudb":
|
|
|
|
{
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
// ElapsedTime returns time since very beginning of the process
|
|
|
|
func (this *MigrationContext) ElapsedTime() time.Duration {
|
|
|
|
return time.Now().Sub(this.StartTime)
|
|
|
|
}
|
|
|
|
|
|
|
|
// ElapsedRowCopyTime returns time since starting to copy chunks of rows
|
|
|
|
func (this *MigrationContext) ElapsedRowCopyTime() time.Duration {
|
2016-06-19 15:55:37 +00:00
|
|
|
this.throttleMutex.Lock()
|
|
|
|
defer this.throttleMutex.Unlock()
|
|
|
|
|
|
|
|
if this.RowCopyEndTime.IsZero() {
|
|
|
|
return time.Now().Sub(this.RowCopyStartTime)
|
|
|
|
}
|
|
|
|
return this.RowCopyEndTime.Sub(this.RowCopyStartTime)
|
|
|
|
}
|
|
|
|
|
|
|
|
// ElapsedRowCopyTime returns time since starting to copy chunks of rows
|
|
|
|
func (this *MigrationContext) MarkRowCopyEndTime() {
|
|
|
|
this.throttleMutex.Lock()
|
|
|
|
defer this.throttleMutex.Unlock()
|
|
|
|
this.RowCopyEndTime = time.Now()
|
2016-04-08 08:34:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// GetTotalRowsCopied returns the accurate number of rows being copied (affected)
|
|
|
|
// This is not exactly the same as the rows being iterated via chunks, but potentially close enough
|
|
|
|
func (this *MigrationContext) GetTotalRowsCopied() int64 {
|
|
|
|
return atomic.LoadInt64(&this.TotalRowsCopied)
|
|
|
|
}
|
2016-04-08 12:35:06 +00:00
|
|
|
|
|
|
|
func (this *MigrationContext) GetIteration() int64 {
|
|
|
|
return atomic.LoadInt64(&this.Iteration)
|
|
|
|
}
|
|
|
|
|
2016-05-23 12:58:53 +00:00
|
|
|
func (this *MigrationContext) MarkPointOfInterest() int64 {
|
|
|
|
this.pointOfInterestTimeMutex.Lock()
|
|
|
|
defer this.pointOfInterestTimeMutex.Unlock()
|
|
|
|
|
|
|
|
this.pointOfInterestTime = time.Now()
|
|
|
|
return atomic.LoadInt64(&this.Iteration)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (this *MigrationContext) TimeSincePointOfInterest() time.Duration {
|
|
|
|
this.pointOfInterestTimeMutex.Lock()
|
|
|
|
defer this.pointOfInterestTimeMutex.Unlock()
|
|
|
|
|
|
|
|
return time.Now().Sub(this.pointOfInterestTime)
|
|
|
|
}
|
|
|
|
|
2016-06-07 09:59:17 +00:00
|
|
|
func (this *MigrationContext) SetChunkSize(chunkSize int64) {
|
|
|
|
if chunkSize < 100 {
|
|
|
|
chunkSize = 100
|
|
|
|
}
|
|
|
|
if chunkSize > 100000 {
|
|
|
|
chunkSize = 100000
|
|
|
|
}
|
|
|
|
atomic.StoreInt64(&this.ChunkSize, chunkSize)
|
|
|
|
}
|
|
|
|
|
2016-04-11 15:27:16 +00:00
|
|
|
func (this *MigrationContext) SetThrottled(throttle bool, reason string) {
|
|
|
|
this.throttleMutex.Lock()
|
2016-05-23 12:58:53 +00:00
|
|
|
defer this.throttleMutex.Unlock()
|
2016-04-11 15:27:16 +00:00
|
|
|
this.isThrottled = throttle
|
|
|
|
this.throttleReason = reason
|
2016-04-08 12:35:06 +00:00
|
|
|
}
|
|
|
|
|
2016-04-11 15:27:16 +00:00
|
|
|
func (this *MigrationContext) IsThrottled() (bool, string) {
|
|
|
|
this.throttleMutex.Lock()
|
2016-05-23 12:58:53 +00:00
|
|
|
defer this.throttleMutex.Unlock()
|
2016-04-11 15:27:16 +00:00
|
|
|
return this.isThrottled, this.throttleReason
|
2016-04-08 12:35:06 +00:00
|
|
|
}
|
|
|
|
|
2016-06-18 19:12:07 +00:00
|
|
|
func (this *MigrationContext) GetThrottleQuery() string {
|
|
|
|
var query string
|
2016-06-09 09:25:01 +00:00
|
|
|
|
2016-06-18 19:12:07 +00:00
|
|
|
this.throttleMutex.Lock()
|
|
|
|
defer this.throttleMutex.Unlock()
|
|
|
|
|
|
|
|
query = this.ThrottleQuery
|
|
|
|
return query
|
|
|
|
}
|
|
|
|
|
|
|
|
func (this *MigrationContext) SetThrottleQuery(newQuery string) {
|
|
|
|
this.throttleMutex.Lock()
|
|
|
|
defer this.throttleMutex.Unlock()
|
|
|
|
|
|
|
|
this.ThrottleQuery = newQuery
|
|
|
|
}
|
|
|
|
|
|
|
|
func (this *MigrationContext) GetMaxLoad() LoadMap {
|
|
|
|
this.throttleMutex.Lock()
|
|
|
|
defer this.throttleMutex.Unlock()
|
|
|
|
|
|
|
|
return this.maxLoad.Duplicate()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (this *MigrationContext) GetCriticalLoad() LoadMap {
|
|
|
|
this.throttleMutex.Lock()
|
|
|
|
defer this.throttleMutex.Unlock()
|
|
|
|
|
|
|
|
return this.criticalLoad.Duplicate()
|
2016-06-09 09:25:01 +00:00
|
|
|
}
|
|
|
|
|
2016-05-17 13:35:44 +00:00
|
|
|
// ReadMaxLoad parses the `--max-load` flag, which is in multiple key-value format,
|
|
|
|
// such as: 'Threads_running=100,Threads_connected=500'
|
2016-06-09 09:25:01 +00:00
|
|
|
// It only applies changes in case there's no parsing error.
|
2016-04-08 12:35:06 +00:00
|
|
|
func (this *MigrationContext) ReadMaxLoad(maxLoadList string) error {
|
2016-06-18 19:12:07 +00:00
|
|
|
loadMap, err := ParseLoadMap(maxLoadList)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
2016-04-08 12:35:06 +00:00
|
|
|
}
|
2016-06-18 19:12:07 +00:00
|
|
|
this.throttleMutex.Lock()
|
|
|
|
defer this.throttleMutex.Unlock()
|
2016-06-09 09:25:01 +00:00
|
|
|
|
2016-06-18 19:12:07 +00:00
|
|
|
this.maxLoad = loadMap
|
|
|
|
return nil
|
|
|
|
}
|
2016-06-09 09:25:01 +00:00
|
|
|
|
2016-06-18 19:12:07 +00:00
|
|
|
// ReadMaxLoad parses the `--max-load` flag, which is in multiple key-value format,
|
|
|
|
// such as: 'Threads_running=100,Threads_connected=500'
|
|
|
|
// It only applies changes in case there's no parsing error.
|
|
|
|
func (this *MigrationContext) ReadCriticalLoad(criticalLoadList string) error {
|
|
|
|
loadMap, err := ParseLoadMap(criticalLoadList)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
2016-04-08 12:35:06 +00:00
|
|
|
}
|
2016-06-18 19:12:07 +00:00
|
|
|
this.throttleMutex.Lock()
|
|
|
|
defer this.throttleMutex.Unlock()
|
2016-06-09 09:25:01 +00:00
|
|
|
|
2016-06-18 19:12:07 +00:00
|
|
|
this.criticalLoad = loadMap
|
2016-04-08 12:35:06 +00:00
|
|
|
return nil
|
|
|
|
}
|
2016-05-03 07:28:48 +00:00
|
|
|
|
2016-05-17 13:35:44 +00:00
|
|
|
// ApplyCredentials sorts out the credentials between the config file and the CLI flags
|
|
|
|
func (this *MigrationContext) ApplyCredentials() {
|
|
|
|
this.configMutex.Lock()
|
|
|
|
defer this.configMutex.Unlock()
|
|
|
|
|
|
|
|
if this.config.Client.User != "" {
|
|
|
|
this.InspectorConnectionConfig.User = this.config.Client.User
|
|
|
|
}
|
|
|
|
if this.CliUser != "" {
|
|
|
|
// Override
|
|
|
|
this.InspectorConnectionConfig.User = this.CliUser
|
|
|
|
}
|
|
|
|
if this.config.Client.Password != "" {
|
|
|
|
this.InspectorConnectionConfig.Password = this.config.Client.Password
|
|
|
|
}
|
|
|
|
if this.CliPassword != "" {
|
|
|
|
// Override
|
|
|
|
this.InspectorConnectionConfig.Password = this.CliPassword
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ReadConfigFile attempts to read the config file, if it exists
|
2016-05-03 07:28:48 +00:00
|
|
|
func (this *MigrationContext) ReadConfigFile() error {
|
2016-05-17 13:35:44 +00:00
|
|
|
this.configMutex.Lock()
|
|
|
|
defer this.configMutex.Unlock()
|
|
|
|
|
2016-05-03 07:28:48 +00:00
|
|
|
if this.ConfigFile == "" {
|
|
|
|
return nil
|
|
|
|
}
|
2016-05-17 13:35:44 +00:00
|
|
|
if err := gcfg.ReadFileInto(&this.config, this.ConfigFile); err != nil {
|
2016-05-03 07:28:48 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|