|
| 1 | +/* |
| 2 | +/* |
| 3 | + Copyright 2016 GitHub Inc. |
| 4 | + See https://github.com/github/gh-ost/blob/master/LICENSE |
| 5 | +*/ |
| 6 | + |
| 7 | +package logic |
| 8 | + |
| 9 | +import ( |
| 10 | + "fmt" |
| 11 | + "os" |
| 12 | + "os/exec" |
| 13 | + "path/filepath" |
| 14 | + |
| 15 | + "github.com/github/gh-ost/go/base" |
| 16 | + "github.com/openark/golib/log" |
| 17 | +) |
| 18 | + |
| 19 | +const ( |
| 20 | + onStartup = "gh-ost-on-startup" |
| 21 | + onValidated = "gh-ost-on-validated" |
| 22 | + onRowCountComplete = "gh-ost-on-rowcount-complete" |
| 23 | + onBeforeRowCopy = "gh-ost-on-before-row-copy" |
| 24 | + onRowCopyComplete = "gh-ost-on-row-copy-complete" |
| 25 | + onBeginPostponed = "gh-ost-on-begin-postponed" |
| 26 | + onBeforeCutOver = "gh-ost-on-before-cut-over" |
| 27 | + onInteractiveCommand = "gh-ost-on-interactive-command" |
| 28 | + onSuccess = "gh-ost-on-success" |
| 29 | + onFailure = "gh-ost-on-failure" |
| 30 | + onStatus = "gh-ost-on-status" |
| 31 | + onStopReplication = "gh-ost-on-stop-replication" |
| 32 | +) |
| 33 | + |
| 34 | +type HooksExecutor struct { |
| 35 | + migrationContext *base.MigrationContext |
| 36 | +} |
| 37 | + |
| 38 | +func NewHooksExecutor() *HooksExecutor { |
| 39 | + return &HooksExecutor{ |
| 40 | + migrationContext: base.GetMigrationContext(), |
| 41 | + } |
| 42 | +} |
| 43 | + |
| 44 | +func (this *HooksExecutor) initHooks() error { |
| 45 | + return nil |
| 46 | +} |
| 47 | + |
| 48 | +func (this *HooksExecutor) applyEnvironmentVairables(extraVariables ...string) []string { |
| 49 | + env := os.Environ() |
| 50 | + env = append(env, fmt.Sprintf("GH_OST_DATABASE_NAME=%s", this.migrationContext.DatabaseName)) |
| 51 | + env = append(env, fmt.Sprintf("GH_OST_TABLE_NAME=%s", this.migrationContext.OriginalTableName)) |
| 52 | + env = append(env, fmt.Sprintf("GH_OST_GHOST_TABLE_NAME=%s", this.migrationContext.GetGhostTableName())) |
| 53 | + env = append(env, fmt.Sprintf("GH_OST_OLD_TABLE_NAME=%s", this.migrationContext.GetOldTableName())) |
| 54 | + env = append(env, fmt.Sprintf("GH_OST_DDL=%s", this.migrationContext.AlterStatement)) |
| 55 | + env = append(env, fmt.Sprintf("GH_OST_ELAPSED_SECONDS=%f", this.migrationContext.ElapsedTime().Seconds())) |
| 56 | + env = append(env, fmt.Sprintf("GH_OST_MIGRATED_HOST=%s", this.migrationContext.ApplierConnectionConfig.ImpliedKey.Hostname)) |
| 57 | + env = append(env, fmt.Sprintf("GH_OST_INSPECTED_HOST=%s", this.migrationContext.InspectorConnectionConfig.ImpliedKey.Hostname)) |
| 58 | + env = append(env, fmt.Sprintf("GH_OST_EXECUTING_HOST=%s", this.migrationContext.Hostname)) |
| 59 | + env = append(env, fmt.Sprintf("GH_OST_HOOKS_HINT=%s", this.migrationContext.HooksHintMessage)) |
| 60 | + |
| 61 | + for _, variable := range extraVariables { |
| 62 | + env = append(env, variable) |
| 63 | + } |
| 64 | + return env |
| 65 | +} |
| 66 | + |
| 67 | +// executeHook executes a command, and sets relevant environment variables |
| 68 | +// combined output & error are printed to gh-ost's standard error. |
| 69 | +func (this *HooksExecutor) executeHook(hook string, extraVariables ...string) error { |
| 70 | + cmd := exec.Command(hook) |
| 71 | + cmd.Env = this.applyEnvironmentVairables(extraVariables...) |
| 72 | + |
| 73 | + combinedOutput, err := cmd.CombinedOutput() |
| 74 | + fmt.Fprintln(os.Stderr, string(combinedOutput)) |
| 75 | + return log.Errore(err) |
| 76 | +} |
| 77 | + |
| 78 | +func (this *HooksExecutor) detectHooks(baseName string) (hooks []string, err error) { |
| 79 | + if this.migrationContext.HooksPath == "" { |
| 80 | + return hooks, err |
| 81 | + } |
| 82 | + pattern := fmt.Sprintf("%s/%s*", this.migrationContext.HooksPath, baseName) |
| 83 | + hooks, err = filepath.Glob(pattern) |
| 84 | + return hooks, err |
| 85 | +} |
| 86 | + |
| 87 | +func (this *HooksExecutor) executeHooks(baseName string, extraVariables ...string) error { |
| 88 | + hooks, err := this.detectHooks(baseName) |
| 89 | + if err != nil { |
| 90 | + return err |
| 91 | + } |
| 92 | + for _, hook := range hooks { |
| 93 | + log.Infof("executing %+v hook: %+v", baseName, hook) |
| 94 | + if err := this.executeHook(hook, extraVariables...); err != nil { |
| 95 | + return err |
| 96 | + } |
| 97 | + } |
| 98 | + return nil |
| 99 | +} |
| 100 | + |
| 101 | +func (this *HooksExecutor) onStartup() error { |
| 102 | + return this.executeHooks(onStartup) |
| 103 | +} |
| 104 | + |
| 105 | +func (this *HooksExecutor) onValidated() error { |
| 106 | + return this.executeHooks(onValidated) |
| 107 | +} |
| 108 | + |
| 109 | +func (this *HooksExecutor) onRowCountComplete() error { |
| 110 | + return this.executeHooks(onRowCountComplete) |
| 111 | +} |
| 112 | +func (this *HooksExecutor) onBeforeRowCopy() error { |
| 113 | + return this.executeHooks(onBeforeRowCopy) |
| 114 | +} |
| 115 | + |
| 116 | +func (this *HooksExecutor) onRowCopyComplete() error { |
| 117 | + return this.executeHooks(onRowCopyComplete) |
| 118 | +} |
| 119 | + |
| 120 | +func (this *HooksExecutor) onBeginPostponed() error { |
| 121 | + return this.executeHooks(onBeginPostponed) |
| 122 | +} |
| 123 | + |
| 124 | +func (this *HooksExecutor) onBeforeCutOver() error { |
| 125 | + return this.executeHooks(onBeforeCutOver) |
| 126 | +} |
| 127 | + |
| 128 | +func (this *HooksExecutor) onInteractiveCommand(command string) error { |
| 129 | + v := fmt.Sprintf("GH_OST_COMMAND='%s'", command) |
| 130 | + return this.executeHooks(onInteractiveCommand, v) |
| 131 | +} |
| 132 | + |
| 133 | +func (this *HooksExecutor) onSuccess() error { |
| 134 | + return this.executeHooks(onSuccess) |
| 135 | +} |
| 136 | + |
| 137 | +func (this *HooksExecutor) onFailure() error { |
| 138 | + return this.executeHooks(onFailure) |
| 139 | +} |
| 140 | + |
| 141 | +func (this *HooksExecutor) onStatus(statusMessage string) error { |
| 142 | + v := fmt.Sprintf("GH_OST_STATUS='%s'", statusMessage) |
| 143 | + return this.executeHooks(onStatus, v) |
| 144 | +} |
| 145 | + |
| 146 | +func (this *HooksExecutor) onStopReplication() error { |
| 147 | + return this.executeHooks(onStopReplication) |
| 148 | +} |
0 commit comments