File Coverage

blib/lib/Ixchel/Actions/sagan_base.pm
Criterion Covered Total %
statement 29 101 28.7
branch 0 28 0.0
condition n/a
subroutine 10 14 71.4
pod 2 4 50.0
total 41 147 27.8


line stmt bran cond sub pod time code
1             package Ixchel::Actions::sagan_base;
2              
3 1     1   127012 use 5.006;
  1         3  
4 1     1   3 use strict;
  1         2  
  1         21  
5 1     1   3 use warnings;
  1         1  
  1         41  
6 1     1   395 use File::Slurp;
  1         40877  
  1         96  
7 1     1   553 use YAML::XS qw(Dump Load);
  1         3866  
  1         79  
8 1     1   597 use Ixchel::functions::file_get;
  1         4  
  1         84  
9 1     1   544 use utf8;
  1         379  
  1         9  
10 1     1   63 use File::Temp qw/ tempfile tempdir /;
  1         2  
  1         69  
11 1     1   8 use File::Spec;
  1         2  
  1         31  
12 1     1   17 use base 'Ixchel::Actions::base';
  1         2  
  1         604  
13              
14             =head1 NAME
15              
16             Ixchel::Actions::sagan_base - Generates the base config for a sagan instance.
17              
18             =head1 VERSION
19              
20             Version 0.2.0
21              
22             =cut
23              
24             our $VERSION = '0.2.0';
25              
26             =head1 CLI SYNOPSIS
27              
28             ixchel -a sagan_base [B<-w>] [B<-i> ]
29              
30             =head1 CODE SYNOPSIS
31              
32             use Data::Dumper;
33              
34             my $results=$ixchel->action(action=>'sagan_base', opts=>{np=>1, w=>1, });
35              
36             print Dumper($results);
37              
38             =head1 DESCRIPTION
39              
40             The following keys are removed from the file.
41              
42             .rules-files
43             .processors
44             .outputs
45              
46             These are removed as they are array based, making it very awkward to deal with with having
47             them previously defined.
48              
49             .sagan.base_config is used as the URL for the config to use and needs to be something
50             understood by L. By default
51             https://raw.githubusercontent.com/quadrantsec/sagan/main/etc/sagan.yaml is used.
52              
53             .include is set to .sagan.config_base.'/sagan-include.yaml' in the case of single
54             instance setups if .sagan.multi_instance is set to 1 then
55             .sagan.config_base."/sagan-include-$instance.yaml"
56              
57             =head1 FLAGS
58              
59             =head2 -w
60              
61             Write the generated services to service files.
62              
63             =head2 -i instance
64              
65             A instance to operate on.
66              
67             =head1 RESULT HASH REF
68              
69             .errors :: A array of errors encountered.
70             .status_text :: A string description of what was done and teh results.
71             .ok :: Set to zero if any of the above errored.
72              
73             =cut
74              
75       0 0   sub new_extra { }
76              
77             sub action_extra {
78 0     0 0   my $self = $_[0];
79              
80 0           my $config_base = $self->{config}{sagan}{config_base};
81              
82 0           my $have_config = 0;
83 0           my ( $tmp_fh, $tmp_file ) = tempfile();
84 0           eval {
85 0           my $fetched_raw_yaml;
86             my $parsed_yaml;
87 0           $fetched_raw_yaml = file_get( url => $self->{config}{sagan}{base_config} );
88 0 0         if ( !defined($fetched_raw_yaml) ) {
89 0           $self->status_add( error => 1, status_add => 'file_get returned undef' );
90 0           return undef;
91             }
92 0           utf8::encode($fetched_raw_yaml);
93 0           $parsed_yaml = Load($fetched_raw_yaml);
94 0 0         if ( !defined($parsed_yaml) ) {
95 0           $self->status_add( error => 1, status_add => 'Attempting to parse the returned data as YAML failed' );
96 0           return undef;
97             }
98              
99 0           eval { write_file( $tmp_file, $fetched_raw_yaml ); };
  0            
100 0 0         if ($@) {
101 0           $self->status_add(
102             error => 1,
103             status_add => 'Failed to write out tmp file to "' . $tmp_file . '" ...'
104             );
105 0           return undef;
106             }
107              
108             # removes array based items that are problematic to deal with
109 0           my $output = `yq -i 'del(.rules-files)' $tmp_file 2>&1`;
110 0 0         if ( $? ne 0 ) {
111 0           $self->status_add(
112             error => 1,
113             status_add => 'Fetched YAML saved to "'
114             . $tmp_file
115             . '" and could not be parsed by yq to delete .rules-files ... '
116             . $output
117             );
118 0           return undef;
119             } ## end if ( $? ne 0 )
120 0           $output = `yq -i 'del(.outputs)' $tmp_file 2>&1`;
121 0 0         if ( $? ne 0 ) {
122 0           $self->status_add(
123             error => 1,
124             status_add => 'Fetched YAML saved to "'
125             . $tmp_file
126             . '" and could not be parsed by yq to delete .outputs ... '
127             . $output
128             );
129 0           return undef;
130             } ## end if ( $? ne 0 )
131 0           $output = `yq -i 'del(.processors)' $tmp_file`;
132 0 0         if ( $? ne 0 ) {
133 0           $self->status_add(
134             error => 1,
135             status_add => 'Fetched YAML saved to "'
136             . $tmp_file
137             . '" and could not be parsed by yq to delete .processors ... '
138             . $output
139             );
140 0           return undef;
141             } ## end if ( $? ne 0 )
142              
143 0           $have_config = 1;
144             };
145 0 0         if ($@) {
146             $self->status_add(
147             error => 1,
148 0           status_add => 'Fetching ' . $self->{config}{sagan}{base_config} . ' failed... ' . $@
149             );
150 0           return undef;
151             }
152              
153 0 0         if ($have_config) {
154 0 0         if ( $self->{config}{sagan}{multi_instance} ) {
155 0           my @instances;
156              
157 0 0         if ( defined( $self->{opts}{i} ) ) {
158 0           @instances = ( $self->{opts}{i} );
159             } else {
160 0           @instances = keys( %{ $self->{config}{sagan}{instances} } );
  0            
161             }
162 0           foreach my $instance (@instances) {
163             # clean it up so there is less likely of a chance of some one deciding to do that by hand and borking the file
164             my $include_path = File::Spec->canonpath(
165 0           $self->{config}{sagan}{config_base} . '/sagan-include-' . $instance . '.yaml' );
166              
167 0           system( 'yq', '-i', '.include="' . $include_path . '"', $tmp_file );
168              
169 0           my $config_file = $self->{config}{sagan}{config_base} . '/sagan-' . $instance . '.yaml';
170 0           my $raw_yaml;
171 0           eval {
172              
173 0           $raw_yaml = read_file($tmp_file);
174              
175 0 0         if ( $self->{opts}{w} ) {
176 0           write_file( $config_file, $raw_yaml );
177             }
178              
179 0           $self->status_add( status_add => '-----[ Instance '
180             . $instance
181             . ' ]-------------------------------------' . "\n"
182             . $raw_yaml
183             . "\n" );
184             };
185 0 0         if ($@) {
186 0           $self->status_add(
187             error => 1,
188             status_add => '-----[ Error: Instance '
189             . $instance
190             . ' ]-------------------------------------'
191             . "\nWriting "
192             . $config_file
193             . ' failed... '
194             . $@
195             );
196             } ## end if ($@)
197             } ## end foreach my $instance (@instances)
198             } else {
199             # clean it up so there is less likely of a chance of some one deciding to do that by hand and borking the file
200 0           my $include_path = File::Spec->canonpath( $self->{config}{sagan}{config_base} . '/sagan-include.yaml' );
201              
202 0           system( 'yq', '-i', '.include="' . $include_path . '"', $tmp_file );
203              
204 0           my $config_file = $self->{config}{sagan}{config_base} . '/sagan.yaml';
205              
206 0           my $raw_yaml;
207 0           eval {
208 0           $raw_yaml = read_file($tmp_file);
209              
210 0 0         if ( $self->{opts}{w} ) {
211 0           write_file( $config_file, $raw_yaml );
212             }
213              
214             $self->status_add(
215 0           status_add => "\n" . $raw_yaml
216              
217             );
218             };
219 0 0         if ($@) {
220 0           $self->status_add(
221             status_add => 'Writing ' . $config_file . ' failed... ' . $@,
222             error => 1,
223             );
224             }
225             } ## end else [ if ( $self->{config}{sagan}{multi_instance...})]
226             } ## end if ($have_config)
227              
228 0           eval { unlink($tmp_file); };
  0            
229 0           $self->status_add(
230             status_add => 'Unlinkg tmp file, "' . $tmp_file . '" failed ... ' . $@,
231             error => 1,
232             );
233              
234 0           return undef;
235             } ## end sub action_extra
236              
237             sub short {
238 0     0 1   return 'Generates the base config for a sagan instance.';
239             }
240              
241             sub opts_data {
242 0     0 1   return 'i=s
243             w
244             ';
245             }
246              
247             1;