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