52ec0f18e2e5f1268d28a6dbd47a12831279918d
[scpubgit/Tak.git] / lib / Tak / Script.pm
1 package Tak::Script;
2
3 use Getopt::Long qw(GetOptionsFromArray :config posix_defaults bundling);
4 use Config::Settings;
5 use IO::Handle;
6 use Tak::Client::Router;
7 use Tak::Client::RemoteRouter;
8 use Tak::Router;
9 use Moo;
10
11 has options => (is => 'ro', required => 1);
12 has env => (is => 'ro', required => 1);
13
14 has stdin => (is => 'lazy');
15 has stdout => (is => 'lazy');
16 has stderr => (is => 'lazy');
17
18 sub _build_stdin { shift->env->{stdin} }
19 sub _build_stdout { shift->env->{stdout} }
20 sub _build_stderr { shift->env->{stderr} }
21
22 has config => (is => 'lazy');
23
24 sub _build_config {
25   my ($self) = @_;
26   my $file = $self->options->{config} || '.tak/default.conf';
27   if (-e $file) {
28     Config::Settings->new->parse_file($file);
29   } else {
30     {};
31   }
32 }
33
34 has local_client => (is => 'lazy');
35
36 sub _build_local_client {
37   my ($self) = @_;
38   Tak::Client::Router->new(service => Tak::Router->new);
39 }
40
41 sub BUILD {
42   shift->setup_logger;
43 }
44
45 sub setup_logger { }
46
47 sub _parse_options {
48   my ($self, $string, $argv) = @_;
49   my @spec = split ';', $string;
50   my %opt;
51   GetOptionsFromArray($argv, \%opt, @spec);
52   return \%opt;
53 }
54
55 sub run {
56   my ($self) = @_;
57   my @argv = @{$self->env->{argv}};
58   unless (@argv && $argv[0]) {
59     return $self->local_help;
60   }
61   my $cmd = shift(@argv);
62   if (my $code = $self->can("local_$cmd")) {
63     return $self->_run($cmd, $code, @argv);
64   } elsif ($code = $self->can("each_$cmd")) {
65     return $self->_run_each($cmd, $code, @argv);
66   }
67   $self->stderr->print("No such command: ${cmd}\n");
68   return $self->local_help;
69 }
70
71 sub _load_file {
72   my ($self, $file) = @_;
73   $self->_load_file_in_my_script($file);
74 }
75
76 sub local_help {
77   my ($self) = @_;
78   $self->stderr->print("Help unimplemented\n");
79 }
80
81 sub _maybe_parse_options {
82   my ($self, $code, $argv) = @_;
83   if (my $proto = prototype($code)) {
84     $self->_parse_options($proto, $argv);
85   } else {
86     {};
87   }
88 }
89
90 sub _run_local {
91   my ($self, $cmd, $code, @argv) = @_;
92   my $opt = $self->_maybe_parse_options($code, \@argv);
93   $self->$code($opt, @argv);
94 }
95
96 sub _run_each {
97   my ($self, $cmd, $code, @argv) = @_;
98   my @targets = $self->_host_list_for($cmd);
99   unless (@targets) {
100     $self->stderr->print("No targets for ${cmd}\n");
101     return;
102   }
103   my $opt = $self->_maybe_parse_options($code, \@argv);
104   if (my $prepare = $self->can("prepare_$cmd")) {
105     $self->$prepare($opt, @argv);
106   }
107   $self->local_client->ensure(connector => 'Tak::ConnectorService');
108   foreach my $target (@targets) {
109     my $remote = $self->_connection_to($target);
110     $self->$code($remote, $opt, @argv);
111   }
112 }
113
114 sub _host_list_for {
115   my ($self, $command) = @_;
116   my @host_spec = map split(' ', $_), @{$self->options->{host}};
117 }
118
119 sub _connection_to {
120   my ($self, $target) = @_;
121   my @path = $self->local_client->do(connector => create => $target);
122   my ($local, $remote) =
123     map $self->local_client->curry(connector => connection => @path => $_),
124       qw(local remote);
125   $local->ensure(module_sender => 'Tak::ModuleSender');
126   $remote->ensure(
127     module_loader => 'Tak::ModuleLoader',
128     expose => { module_sender => [ 'remote', 'module_sender' ] }
129   );
130   $remote->do(module_loader => 'enable');
131   Tak::Client::RemoteRouter->new(
132     %$remote, host => $target
133   );
134 }
135
136 1;