3 use Getopt::Long qw(GetOptionsFromArray :config posix_defaults bundling);
6 use Tak::Client::Router;
7 use Tak::Client::RemoteRouter;
9 use Log::Contextual qw(:log);
10 use Log::Contextual::SimpleLogger;
13 has options => (is => 'ro', required => 1);
14 has env => (is => 'ro', required => 1);
16 has log_level => (is => 'rw');
18 has stdin => (is => 'lazy');
19 has stdout => (is => 'lazy');
20 has stderr => (is => 'lazy');
22 sub _build_stdin { shift->env->{stdin} }
23 sub _build_stdout { shift->env->{stdout} }
24 sub _build_stderr { shift->env->{stderr} }
26 has config => (is => 'lazy');
30 my $file = $self->options->{config} || '.tak/default.conf';
32 Config::Settings->new->parse_file($file);
38 has local_client => (is => 'lazy');
40 sub _build_local_client {
42 Tak::Client::Router->new(service => Tak::Router->new);
51 my @level_names = qw(fatal error warn info debug trace);
52 my $options = $self->options;
53 my $level = 2 + ($options->{verbose}||0) - ($options->{quiet}||0);
54 my $upto = $level_names[$level];
55 $self->log_level($upto);
56 Log::Contextual::set_logger(
57 Log::Contextual::SimpleLogger->new({
59 coderef => sub { print STDERR '<local> ', @_ },
65 my ($self, $string, $argv) = @_;
66 my @spec = split ';', $string;
68 GetOptionsFromArray($argv, \%opt, @spec);
74 my @argv = @{$self->env->{argv}};
75 unless (@argv && $argv[0]) {
76 return $self->local_help;
78 my $cmd = shift(@argv);
79 if (my $code = $self->can("local_$cmd")) {
80 return $self->_run($cmd, $code, @argv);
81 } elsif ($code = $self->can("each_$cmd")) {
82 return $self->_run_each($cmd, $code, @argv);
83 } elsif ($code = $self->can("every_$cmd")) {
84 return $self->_run_every($cmd, $code, @argv);
86 $self->stderr->print("No such command: ${cmd}\n");
87 return $self->local_help;
91 my ($self, $file) = @_;
92 $self->_load_file_in_my_script($file);
97 $self->stderr->print("Help unimplemented\n");
100 sub _maybe_parse_options {
101 my ($self, $code, $argv) = @_;
102 if (my $proto = prototype($code)) {
103 $self->_parse_options($proto, $argv);
110 my ($self, $cmd, $code, @argv) = @_;
111 my $opt = $self->_maybe_parse_options($code, \@argv);
112 $self->$code($opt, @argv);
116 my ($self, $cmd, $code, @argv) = @_;
117 my @targets = $self->_host_list_for($cmd);
119 $self->stderr->print("No targets for ${cmd}\n");
122 my $opt = $self->_maybe_parse_options($code, \@argv);
123 $self->local_client->ensure(connector => 'Tak::ConnectorService');
124 foreach my $target (@targets) {
125 my $remote = $self->_connection_to($target);
126 $self->$code($remote, $opt, @argv);
131 my ($self, $cmd, $code, @argv) = @_;
132 my @targets = $self->_host_list_for($cmd);
134 $self->stderr->print("No targets for ${cmd}\n");
137 my $opt = $self->_maybe_parse_options($code, \@argv);
138 $self->local_client->ensure(connector => 'Tak::ConnectorService');
139 my @remotes = map $self->_connection_to($_), @targets;
140 $self->$code(\@remotes, $opt, @argv);
144 my ($self, $command) = @_;
145 my @host_spec = map split(' ', $_), @{$self->options->{host}};
149 my ($self, $target) = @_;
150 log_debug { "Connecting to ${target}" };
151 my @path = $self->local_client->do(
152 connector => create => $target, log_level => $self->log_level
154 my ($local, $remote) =
155 map $self->local_client->curry(connector => connection => @path => $_),
157 $local->ensure(module_sender => 'Tak::ModuleSender');
159 module_loader => 'Tak::ModuleLoader',
160 expose => { module_sender => [ 'remote', 'module_sender' ] }
162 $remote->do(module_loader => 'enable');
163 log_debug { "Setup connection to ${target}" };
164 Tak::Client::RemoteRouter->new(
165 %$remote, host => $target