File Coverage

blib/lib/Kubectl/CLIWrapper.pm
Criterion Covered Total %
statement 55 56 98.2
branch 19 24 79.1
condition n/a
subroutine 11 11 100.0
pod 3 5 60.0
total 88 96 91.6


line stmt bran cond sub pod time code
1             package Kubectl::CLIWrapper {
2 1     1   67529 use Moo;
  1         11156  
  1         5  
3 1     1   1895 use IPC::Open3;
  1         3971  
  1         54  
4 1     1   460 use JSON::MaybeXS;
  1         7962  
  1         62  
5 1     1   430 use Kubectl::CLIWrapper::Result;
  1         4  
  1         38  
6 1     1   521 use Type::Tiny::Union;
  1         2168  
  1         35  
7 1     1   10 use Types::Standard qw/Str CodeRef Bool/;
  1         2  
  1         8  
8              
9             our $VERSION = '0.06';
10              
11             my $TOKEN_TYPE = Type::Tiny::Union->new(
12             type_constraints => [Str, CodeRef]
13             );
14              
15             has kubeconfig => (is => 'ro', isa => Str, predicate => 'has_kubeconfig');
16             has kubectl => (is => 'ro', isa => Str, default => 'kubectl');
17             has namespace => (is => 'ro', isa => Str, predicate => 'has_namespace');
18             has password => (is => 'ro', isa => Str, predicate => 'has_password');
19             has server => (is => 'ro', isa => Str, predicate => 'has_server');
20             has token => (is => 'ro', isa => $TOKEN_TYPE, predicate => 'has_token');
21             has username => (is => 'ro', isa => Str, predicate => 'has_username');
22              
23             has insecure_tls => (is => 'ro', isa => Bool, default => 0);
24              
25             sub kube_options {
26 12     12 0 27 my $self = shift;
27 12         47 my %options = ();
28              
29 12 100       100 $options{server} = $self->server if $self->has_server;
30 12 100       63 $options{username} = $self->username if $self->has_username;
31 12 100       67 $options{password} = $self->password if $self->has_password;
32 12 50       58 $options{namespace} = $self->namespace if $self->has_namespace;
33 12 100       39 $options{kubeconfig} = $self->kubeconfig if $self->has_kubeconfig;
34 12 50       38 $options{'insecure-skip-tls-verify'} = 'true' if $self->insecure_tls;
35              
36 12 100       33 if ($self->has_token) {
37             $options{token} = ref($self->token) eq 'CODE'
38 3 100       15 ? &{$self->token}()
  2         9  
39             : $self->token;
40             }
41              
42 12         51 return [ map { "--$_=$options{ $_ }" } keys %options ];
  22         136  
43             }
44              
45             sub command_for {
46 12     12 0 14186 my ($self, @params) = @_;
47 12         46 return ($self->kubectl, @{ $self->kube_options }, @params);
  12         88  
48             }
49              
50             sub run {
51 4     4 1 6349 my ($self, @command) = @_;
52 4         17 return $self->input(undef, @command);
53             }
54              
55             sub json {
56 1     1 1 2052 my ($self, @command) = @_;
57              
58 1         8 push @command, '-o=json';
59              
60 1         18 my $result = $self->run(@command);
61 1         196 my $struct = eval {
62 1         39 JSON->new->decode($result->output);
63             };
64 1 50       328 if ($@) {
65 0         0 return Kubectl::CLIWrapper::Result->new(
66             rc => $result->rc,
67             output => $result->output,
68             success => 0
69             );
70             }
71              
72 1         41 return Kubectl::CLIWrapper::Result->new(
73             rc => $result->rc,
74             output => $result->output,
75             json => $struct
76             );
77             }
78              
79             sub input {
80 7     7 1 6039 my ($self, $input, @params) = @_;
81              
82 7         145 my @final_command = $self->command_for(@params);
83              
84 7         19 my ($stdin, $stdout, $stderr);
85 7         35 my $pid = open3($stdin, $stdout, $stderr, @final_command);
86 7 100       27836 print $stdin $input if(defined $input);
87 7         88 close $stdin;
88              
89 7         23046 my $out = join '', <$stdout>;
90 7 50       172 my $err = join '', <$stderr> if ($stderr);
91              
92 7 50       46 die "Unexpected contents in stderr $err" if ($err);
93              
94 7         199 waitpid( $pid, 0 );
95 7         106 my $rc = $? >> 8;
96              
97 7         879 return Kubectl::CLIWrapper::Result->new(
98             rc => $rc,
99             output => $out,
100             );
101             }
102              
103             }
104             1;
105             ### main pod documentation begin ###
106              
107             =encoding UTF-8
108              
109             =head1 NAME
110              
111             Kubectl::CLIWrapper - Module to use the Kubernetes API via the kubectl CLI
112              
113             =head1 SYNOPSIS
114              
115             use Kubectl::CLIWrapper;
116              
117             my $kube = Kubectl::CLIWrapper->new(
118             server => 'https://kubernetes.example.org/',
119             username => 'user',
120             password => 'pass',
121             );
122              
123             my $result = $kube->run('explain', 'service');
124             # $result->success == 1 if the command executed correctly
125             # $result->output contains the output of the command
126              
127             my $result = $kube->json('get', 'pods');
128             # $result->success == 1 if the command executed correctly
129             # $result->output contains the output of the command
130             # $result->json is a hashref with the result of the parsed JSON output of the command
131              
132             my $result = $kube->input('{"kind":"Service" ... }', 'create', '-f', '-');
133             # $result->success == 1 if the command executed correctly
134             # $result->output contains the output of the command
135              
136             =head1 DESCRIPTION
137              
138             This module helps you use the Kubernetes API. It sends all it's commands
139             via the CLI command line tool C. You can find kubectl installation instructions
140             here L.
141              
142             =head1 CREDENTIALS
143              
144             Kubectl::CLIWrapper attributes are mainly the options you can pass C to control
145             how it authenticates to the Kubernetes server. Run C to discover what these
146             options do. If you don't initialize any attributes, kubectl will behave just like on the command
147             line (loading ~/.kube/config) which may be already set to point to a Kubernetes server
148              
149             =head1 ATTRIBUTES
150              
151             =head2 kubectl
152              
153             By default initialized to C. It will try to find kubectl in the PATH. You can
154             set it explicitly to specific kubectl excecutable.
155              
156             =head2 kubeconfig
157              
158             Path to your kube configuration, defaults to C<$HOME/.kube/config> via kubectl.
159              
160             =head2 server
161              
162             The URL of the Kubernetes service
163              
164             =head2 username
165              
166             The username for Basic Authentication
167              
168             =head2 password
169              
170             The password for Basic Authentication
171              
172             =head2 token
173              
174             The Bearer token for authentication. If it's a scalar, that value will be used. If it's
175             a coderef (sub {}), it will be invoked each time kubectl is called and it's return value
176             used as the value of the --token option
177              
178             =head2 insecure_tls
179              
180             A Boolean flag that tells kubectl to not verify the certificate of the server it connects to
181              
182             =head2 namespace
183              
184             The Kubernetes namespace to operate in.
185              
186             =head1 METHODS
187              
188             =head2 run(@parameters)
189              
190             Will run kubectl with the parameters. Returns a L object
191             with C set to the output of the command, and C a Boolean to indicate
192             if the command reported successful execution.
193              
194             =head2 json(@parameters)
195              
196             Will run kubectl with the parameters, and C<'-o=json'>. Returns a L object
197             with C set to the output of the command, and C set to a hashref with the parsed
198             JSON. C will be false if JSON parsing fails.
199              
200             =head2 input($input_to_kubectl, @parameters)
201              
202             Will run kubectl with the parametes, sending $input_to_kubectl on it's STDIN.
203             Returns a L object with C set to the output of the command.
204             C will be set accordingly.
205              
206             =head1 SEE ALSO
207              
208             L
209              
210             L
211              
212             L
213              
214             L
215              
216             =head1 CONTRIBUTORS
217              
218             waterkip:
219             - adding the possiblity to set a kubeconfig file
220             - helping port to Moose
221              
222             ureesoriano:
223             - fix for token attribute being ignored
224             - dynamic generation of the "token" command-line option
225              
226             =head1 AUTHOR
227              
228             Jose Luis Martinez
229             CAPSiDE
230             jlmartinez@capside.com
231              
232             =head1 BUGS and SOURCE
233              
234             The source code is located here: L
235              
236             Please report bugs to: L
237              
238             =head1 COPYRIGHT and LICENSE
239              
240             Copyright (c) 2018 by CAPSiDE
241             This code is distributed under the Apache 2 License. The full text of the
242             license can be found in the LICENSE file included with this module.
243              
244             =cut