move exec code to Client.pm, add get_homedir example to Takfile
[scpubgit/Tak.git] / lib / Tak / Script.pm
CommitLineData
e367483d 1package Tak::Script;
2
3use Getopt::Long qw(GetOptionsFromArray :config posix_defaults bundling);
4use Config::Settings;
5use IO::Handle;
e99bc141 6use Tak::Client::Router;
7use Tak::Client::RemoteRouter;
8use Tak::Router;
7b71b06e 9use Log::Contextual qw(:log);
10use Log::Contextual::SimpleLogger;
e367483d 11use Moo;
12
5e5069ca 13with 'Tak::Role::ScriptActions';
14
e367483d 15has options => (is => 'ro', required => 1);
16has env => (is => 'ro', required => 1);
17
7b71b06e 18has log_level => (is => 'rw');
19
e367483d 20has stdin => (is => 'lazy');
21has stdout => (is => 'lazy');
22has stderr => (is => 'lazy');
23
24sub _build_stdin { shift->env->{stdin} }
25sub _build_stdout { shift->env->{stdout} }
26sub _build_stderr { shift->env->{stderr} }
27
28has config => (is => 'lazy');
29
30sub _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
40has local_client => (is => 'lazy');
41
42sub _build_local_client {
43 my ($self) = @_;
44 Tak::Client::Router->new(service => Tak::Router->new);
e367483d 45}
46
47sub BUILD {
48 shift->setup_logger;
49}
50
7b71b06e 51sub 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
66sub _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
74sub 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
93sub _load_file {
94 my ($self, $file) = @_;
95 $self->_load_file_in_my_script($file);
96}
97
98sub local_help {
99 my ($self) = @_;
100 $self->stderr->print("Help unimplemented\n");
101}
102
e99bc141 103sub _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 112sub _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 118sub _run_each {
119 my ($self, $cmd, $code, @argv) = @_;
120 my @targets = $self->_host_list_for($cmd);
121 unless (@targets) {
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 133sub _run_every {
134 my ($self, $cmd, $code, @argv) = @_;
135 my @targets = $self->_host_list_for($cmd);
136 unless (@targets) {
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 146sub _host_list_for {
147 my ($self, $command) = @_;
148 my @host_spec = map split(' ', $_), @{$self->options->{host}};
149}
150
151sub _connection_to {
152 my ($self, $target) = @_;
7b71b06e 153 log_debug { "Connecting to ${target}" };
154 my @path = $self->local_client->do(
155 connector => create => $target, log_level => $self->log_level
156 );
e99bc141 157 my ($local, $remote) =
158 map $self->local_client->curry(connector => connection => @path => $_),
159 qw(local remote);
160 $local->ensure(module_sender => 'Tak::ModuleSender');
161 $remote->ensure(
162 module_loader => 'Tak::ModuleLoader',
163 expose => { module_sender => [ 'remote', 'module_sender' ] }
164 );
165 $remote->do(module_loader => 'enable');
7b71b06e 166 log_debug { "Setup connection to ${target}" };
e99bc141 167 Tak::Client::RemoteRouter->new(
168 %$remote, host => $target
169 );
170}
171
e367483d 1721;