github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/worker/uniter/runner/debug/client.go (about) 1 // Copyright 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package debug 5 6 import ( 7 "encoding/base64" 8 "strings" 9 10 goyaml "gopkg.in/yaml.v2" 11 ) 12 13 type hookArgs struct { 14 Hooks []string `yaml:"hooks,omitempty"` 15 } 16 17 // ClientScript returns a bash script suitable for executing 18 // on the unit system to intercept hooks via tmux shell. 19 func ClientScript(c *HooksContext, hooks []string) string { 20 // If any hook is "*", then the client is interested in all. 21 for _, hook := range hooks { 22 if hook == "*" { 23 hooks = nil 24 break 25 } 26 } 27 28 s := strings.Replace(debugHooksClientScript, "{unit_name}", c.Unit, -1) 29 s = strings.Replace(s, "{tmux_conf}", tmuxConf, 1) 30 s = strings.Replace(s, "{entry_flock}", c.ClientFileLock(), -1) 31 s = strings.Replace(s, "{exit_flock}", c.ClientExitFileLock(), -1) 32 33 yamlArgs := encodeArgs(hooks) 34 base64Args := base64.StdEncoding.EncodeToString(yamlArgs) 35 s = strings.Replace(s, "{hook_args}", base64Args, 1) 36 return s 37 } 38 39 func encodeArgs(hooks []string) []byte { 40 // Marshal to YAML, then encode in base64 to avoid shell escapes. 41 yamlArgs, err := goyaml.Marshal(hookArgs{Hooks: hooks}) 42 if err != nil { 43 // This should not happen: we're in full control. 44 panic(err) 45 } 46 return yamlArgs 47 } 48 49 const debugHooksClientScript = `#!/bin/bash 50 ( 51 cleanup_on_exit() 52 { 53 echo "Cleaning up the debug session" 54 tmux kill-session -t {unit_name}; 55 } 56 trap cleanup_on_exit EXIT 57 58 # Lock the juju-<unit>-debug lockfile. 59 flock -n 8 || ( 60 echo "Found existing debug sessions, attempting to reconnect" 2>&1 61 exec tmux attach-session -t {unit_name} 62 exit $? 63 ) 64 ( 65 # Close the inherited lock FD, or tmux will keep it open. 66 exec 8>&- 67 68 # Write out the debug-hooks args. 69 echo "{hook_args}" | base64 -d > {entry_flock} 70 71 # Lock the juju-<unit>-debug-exit lockfile. 72 flock -n 9 || exit 1 73 74 # Wait for tmux to be installed. 75 while [ ! -f /usr/bin/tmux ]; do 76 sleep 1 77 done 78 79 if [ ! -f ~/.tmux.conf ]; then 80 if [ -f /usr/share/byobu/profiles/tmux ]; then 81 # Use byobu/tmux profile for familiar keybindings and branding 82 echo "source-file /usr/share/byobu/profiles/tmux" > ~/.tmux.conf 83 else 84 # Otherwise, use the legacy juju/tmux configuration 85 cat > ~/.tmux.conf <<END 86 {tmux_conf} 87 END 88 fi 89 fi 90 91 ( 92 # Close the inherited lock FD, or tmux will keep it open. 93 exec 9>&- 94 if ! tmux has-session -t {unit_name}; then 95 tmux new-session -d -s {unit_name} 96 fi 97 client_count=$(tmux list-clients | wc -l) 98 if [ $client_count -ge 1 ]; then 99 session_name={unit_name}"-"$client_cnt 100 exec tmux new-session -d -t {unit_name} -s $session_name 101 exec tmux attach-session -t $session_name \; set-option destroy-unattached 102 else 103 exec tmux attach-session -t {unit_name} 104 fi 105 ) 106 ) 9>{exit_flock} 107 ) 8>{entry_flock} 108 exit $? 109 ` 110 111 const tmuxConf = ` 112 # Status bar 113 set-option -g status-bg black 114 set-option -g status-fg white 115 116 set-window-option -g window-status-current-bg red 117 set-window-option -g window-status-current-attr bright 118 119 set-option -g status-right '' 120 121 # Panes 122 set-option -g pane-border-fg white 123 set-option -g pane-active-border-fg white 124 125 # Monitor activity on windows 126 set-window-option -g monitor-activity on 127 128 # Screen bindings, since people are more familiar with that. 129 set-option -g prefix C-a 130 bind C-a last-window 131 bind a send-key C-a 132 133 bind | split-window -h 134 bind - split-window -v 135 136 # Fix CTRL-PGUP/PGDOWN for vim 137 set-window-option -g xterm-keys on 138 139 # Prevent ESC key from adding delay and breaking Vim's ESC > arrow key 140 set-option -s escape-time 0 141 `