Commit | Line | Data |
e367483d |
1 | package Tak::Script; |
2 | |
3 | use Getopt::Long qw(GetOptionsFromArray :config posix_defaults bundling); |
4 | use Config::Settings; |
5 | use IO::Handle; |
e99bc141 |
6 | use Tak::Client::Router; |
7 | use Tak::Client::RemoteRouter; |
8 | use Tak::Router; |
7b71b06e |
9 | use Log::Contextual qw(:log); |
10 | use Log::Contextual::SimpleLogger; |
e367483d |
11 | use Moo; |
12 | |
5e5069ca |
13 | with 'Tak::Role::ScriptActions'; |
14 | |
e367483d |
15 | has options => (is => 'ro', required => 1); |
16 | has env => (is => 'ro', required => 1); |
17 | |
7b71b06e |
18 | has log_level => (is => 'rw'); |
19 | |
e367483d |
20 | has stdin => (is => 'lazy'); |
21 | has stdout => (is => 'lazy'); |
22 | has stderr => (is => 'lazy'); |
23 | |
24 | sub _build_stdin { shift->env->{stdin} } |
25 | sub _build_stdout { shift->env->{stdout} } |
26 | sub _build_stderr { shift->env->{stderr} } |
27 | |
28 | has config => (is => 'lazy'); |
29 | |
30 | sub _build_config { |
31 | my ($self) = @_; |
e99bc141 |
32 | my $file = $self->options->{config} || '.tak/default.conf'; |
33 | if (-e $file) { |
34 | Config::Settings->new->parse_file($file); |
35 | } else { |
36 | {}; |
37 | } |
38 | } |
39 | |
40 | has local_client => (is => 'lazy'); |
41 | |
42 | sub _build_local_client { |
43 | my ($self) = @_; |
44 | Tak::Client::Router->new(service => Tak::Router->new); |
e367483d |
45 | } |
46 | |
47 | sub BUILD { |
48 | shift->setup_logger; |
49 | } |
50 | |
7b71b06e |
51 | sub setup_logger { |
52 | my ($self) = @_; |
53 | my @level_names = qw(fatal error warn info debug trace); |
54 | my $options = $self->options; |
55 | my $level = 2 + ($options->{verbose}||0) - ($options->{quiet}||0); |
56 | my $upto = $level_names[$level]; |
57 | $self->log_level($upto); |
58 | Log::Contextual::set_logger( |
59 | Log::Contextual::SimpleLogger->new({ |
60 | levels_upto => $upto, |
61 | coderef => sub { print STDERR '<local> ', @_ }, |
62 | }) |
63 | ); |
64 | } |
e367483d |
65 | |
66 | sub _parse_options { |
e99bc141 |
67 | my ($self, $string, $argv) = @_; |
e367483d |
68 | my @spec = split ';', $string; |
e99bc141 |
69 | my %opt; |
70 | GetOptionsFromArray($argv, \%opt, @spec); |
71 | return \%opt; |
72 | } |
e367483d |
73 | |
74 | sub run { |
75 | my ($self) = @_; |
76 | my @argv = @{$self->env->{argv}}; |
77 | unless (@argv && $argv[0]) { |
78 | return $self->local_help; |
79 | } |
80 | my $cmd = shift(@argv); |
5e5069ca |
81 | $cmd =~ s/-/_/g; |
e367483d |
82 | if (my $code = $self->can("local_$cmd")) { |
83 | return $self->_run($cmd, $code, @argv); |
84 | } elsif ($code = $self->can("each_$cmd")) { |
85 | return $self->_run_each($cmd, $code, @argv); |
934a93db |
86 | } elsif ($code = $self->can("every_$cmd")) { |
87 | return $self->_run_every($cmd, $code, @argv); |
e367483d |
88 | } |
89 | $self->stderr->print("No such command: ${cmd}\n"); |
90 | return $self->local_help; |
91 | } |
92 | |
93 | sub _load_file { |
94 | my ($self, $file) = @_; |
95 | $self->_load_file_in_my_script($file); |
96 | } |
97 | |
98 | sub local_help { |
99 | my ($self) = @_; |
100 | $self->stderr->print("Help unimplemented\n"); |
101 | } |
102 | |
e99bc141 |
103 | sub _maybe_parse_options { |
104 | my ($self, $code, $argv) = @_; |
105 | if (my $proto = prototype($code)) { |
106 | $self->_parse_options($proto, $argv); |
107 | } else { |
108 | {}; |
109 | } |
110 | } |
111 | |
e367483d |
112 | sub _run_local { |
113 | my ($self, $cmd, $code, @argv) = @_; |
e99bc141 |
114 | my $opt = $self->_maybe_parse_options($code, \@argv); |
e367483d |
115 | $self->$code($opt, @argv); |
116 | } |
117 | |
e99bc141 |
118 | sub _run_each { |
119 | my ($self, $cmd, $code, @argv) = @_; |
120 | my @targets = $self->_host_list_for($cmd); |
8facab5f |
121 | unless (@targets) { |
e99bc141 |
122 | $self->stderr->print("No targets for ${cmd}\n"); |
123 | return; |
124 | } |
125 | my $opt = $self->_maybe_parse_options($code, \@argv); |
e99bc141 |
126 | $self->local_client->ensure(connector => 'Tak::ConnectorService'); |
127 | foreach my $target (@targets) { |
128 | my $remote = $self->_connection_to($target); |
129 | $self->$code($remote, $opt, @argv); |
130 | } |
131 | } |
132 | |
934a93db |
133 | sub _run_every { |
134 | my ($self, $cmd, $code, @argv) = @_; |
135 | my @targets = $self->_host_list_for($cmd); |
8facab5f |
136 | unless (@targets) { |
934a93db |
137 | $self->stderr->print("No targets for ${cmd}\n"); |
138 | return; |
139 | } |
140 | my $opt = $self->_maybe_parse_options($code, \@argv); |
141 | $self->local_client->ensure(connector => 'Tak::ConnectorService'); |
142 | my @remotes = map $self->_connection_to($_), @targets; |
143 | $self->$code(\@remotes, $opt, @argv); |
144 | } |
145 | |
e99bc141 |
146 | sub _host_list_for { |
147 | my ($self, $command) = @_; |
148 | my @host_spec = map split(' ', $_), @{$self->options->{host}}; |
8facab5f |
149 | unshift(@host_spec, '-') if $self->options->{local}; |
150 | return @host_spec; |
e99bc141 |
151 | } |
152 | |
153 | sub _connection_to { |
154 | my ($self, $target) = @_; |
7b71b06e |
155 | log_debug { "Connecting to ${target}" }; |
156 | my @path = $self->local_client->do( |
157 | connector => create => $target, log_level => $self->log_level |
158 | ); |
e99bc141 |
159 | my ($local, $remote) = |
160 | map $self->local_client->curry(connector => connection => @path => $_), |
161 | qw(local remote); |
162 | $local->ensure(module_sender => 'Tak::ModuleSender'); |
163 | $remote->ensure( |
164 | module_loader => 'Tak::ModuleLoader', |
165 | expose => { module_sender => [ 'remote', 'module_sender' ] } |
166 | ); |
167 | $remote->do(module_loader => 'enable'); |
7b71b06e |
168 | log_debug { "Setup connection to ${target}" }; |
e99bc141 |
169 | Tak::Client::RemoteRouter->new( |
170 | %$remote, host => $target |
171 | ); |
172 | } |
173 | |
e367483d |
174 | 1; |