File Coverage

blib/lib/OcToolkit.pm
Criterion Covered Total %
statement 44 492 8.9
branch 0 208 0.0
condition 0 94 0.0
subroutine 15 44 34.0
pod 6 10 60.0
total 65 848 7.6


line stmt bran cond sub pod time code
1             package OcToolkit;
2              
3 1     1   253885 use v5.16; # or newer
  1         5  
4 1     1   7 use strict;
  1         2  
  1         32  
5 1     1   6 use warnings;
  1         3  
  1         87  
6              
7             our $VERSION = "1.08";
8              
9 1     1   7 use JSON::PP;
  1         4  
  1         115  
10 1     1   701 use Text::Diff;
  1         12351  
  1         76  
11 1     1   824 use Template;
  1         26275  
  1         52  
12 1     1   801 use File::Slurp;
  1         42252  
  1         94  
13 1     1   745 use File::Find::Rule;
  1         11455  
  1         35  
14 1     1   85 use File::Path qw(make_path rmtree);
  1         2  
  1         80  
15 1     1   683 use MIME::Base64 qw(encode_base64 decode_base64);
  1         1102  
  1         122  
16 1     1   1701 use YAML qw(LoadFile);
  1         10372  
  1         75  
17 1     1   631 use YAML::Safe;
  1         2805  
  1         78  
18              
19 1     1   725 use Data::Dumper;
  1         10213  
  1         7653  
20              
21             sub new {
22 0     0 0   my $class = shift;
23 0           my $self = {@_};
24            
25 0           $self->{tt} = Template->new({INTERPOLATE => 1});
26 0           $self->{json} = JSON::PP->new;
27 0           $self->{json}->convert_blessed();
28            
29 0 0         $self->{secretsDir} = "secrets" if not defined $self->{secretsDir};
30 0 0         $self->{ocConfigFile} = "oc_config.json" if not defined $self->{ocConfigFile};
31 0 0         $self->{templatesTTDir} = "templates_tt" if not defined $self->{templatesTTDir};
32 0 0         $self->{templatesYamlDir} = "templates_yaml" if not defined $self->{templatesYamlDir};
33 0 0         $self->{validationReportFile} = "validation_report.txt" if not defined $self->{validationReportFile};
34 0 0         $self->{cliCommand} = "oc" if not defined $self->{cloudCommand};;
35 0 0 0       if((defined $self->{advanceFeatures}) && ($self->{advanceFeatures} =~ /kubectl/)){
36 0           $self->{cliCommand} = "kubectl";
37             }
38              
39 0           qx/mkdir tmp 2>&1/;
40 0           qx/mkdir tmp\/$self->{secretsDir} 2>&1/;
41 0           qx/mkdir tmp\/$self->{templatesTTDir} 2>&1/;
42            
43 0 0         if(not defined $self->{cluster}){
44 0           my $ocConfigFiletext = read_file($self->{ocConfigFile});
45 0           my $oCconfig = $self->{json}->utf8->decode($ocConfigFiletext);
46 0           $self->{cluster} = $oCconfig->{project}->{default_cluster};
47 0 0         $self->{cluster} = "unknown" if not defined $self->{cluster};
48             }
49            
50 0           return bless $self, $class;
51             }
52              
53             sub backup{
54 0     0 1   my ($self, $instance) = @_;
55              
56 0           $self->{instance} = $instance;
57 0 0         return if not defined $self->generateYaml();
58            
59 0           qx/mkdir backups 2>&1/;
60 0           qx/mkdir backups\/$instance 2>&1/;
61            
62 0           $self->_loopDir($self->{config}->{templates_yaml_dir}, "*", "_backupInstance");
63             }
64              
65             sub backupWholeOCProject{
66 0     0 1   my ($self) = @_;
67              
68 0           qx/mkdir backups 2>&1/;
69 0           qx/mkdir backups\/wholeProject 2>&1/;
70 0           $self->_clearDir("backups/wholeProject");
71            
72 0           my $ocConfigFileText = read_file($self->{ocConfigFile});
73 0           my $ocConfig = $self->{json}->utf8->decode($ocConfigFileText);
74            
75 0           my $ocResourceKinds = $ocConfig->{project}->{oc_resource_kinds};
76 0 0         $ocResourceKinds = $self->{ocResourceKinds} if defined $self->{ocResourceKinds};
77 0           my @ocResourceKindsArray = split(';', $ocResourceKinds);
78            
79 0           foreach my $ocResourceKind (@ocResourceKindsArray){
80 0           my $ocResourceKinds = $ocResourceKind;
81 0 0 0       if($ocResourceKind ne "Ingress" &&
      0        
82             $ocResourceKind ne "StorageClass" &&
83             $ocResourceKind ne "NetworkPolicy"
84             ){
85 0           $ocResourceKinds .= "s";
86             }
87 0           print "$ocResourceKinds:\n";
88 0           my $text = qx/$self->{cliCommand} get $ocResourceKinds/;
89 0           my @textArray = split('\n', $text);
90 0           shift @textArray;
91 0           foreach my $line (@textArray){
92 0           my @lineArray = split(" ", $line);
93 0           my $ocItem = $lineArray[0];
94 0           print "kind: $ocResourceKind item: $ocItem\n";
95 0           qx/mkdir backups\/wholeProject\/$ocResourceKinds 2>&1/;
96 0           eval {
97 0           my $ocItemJson = qx/$self->{cliCommand} get $ocResourceKind $ocItem -o json/;
98 0           my $ocItemHash = $self->{json}->utf8->decode($ocItemJson);
99 0 0 0       if((defined $self->{advanceFeatures}) &&
      0        
100             ($self->{advanceFeatures} =~ /removeClutter/) &&
101             (defined $self->{removeClutterBackup})){
102 0           my $subParams = {"ocKind" => $ocResourceKind, "ocName" => $ocItem};
103 0           $ocItemHash = $self->{removeClutterBackup}->($ocItemHash, $subParams);
104             }
105 0           my $yamlSaveObj = YAML::Safe->new->boolean("JSON::PP");
106 0           my $yamlText = $yamlSaveObj->Dump($ocItemHash);
107 0           $yamlText =~ s/---\n//;
108 0           write_file("backups\/wholeProject\/$ocResourceKinds/$ocItem".".yaml", $yamlText);
109             };
110 0 0         if($@){
111             # if error occured take yaml without calling '->removeClutterBackup()'
112 0           print "Removing clutter has failed, writing yaml file: $ocResourceKinds/$ocItem.yaml without removing clutter.\n";
113 0           my $yamlText = qx/$self->{cliCommand} get $ocResourceKind $ocItem -o yaml/;
114 0           write_file("backups\/wholeProject\/$ocResourceKinds/$ocItem".".yaml", $yamlText);
115             }
116             }
117             }
118             }
119              
120             sub convertYamlToTTExtention{
121 0     0 0   my ($self, $yamlToTTconvertDir) = @_;
122              
123 0           $self->_loopDir($yamlToTTconvertDir, "yaml", "_convertYamlToTTExtention",
124             {_convertYamlToTTExtention => {yamlToTTconvertDir => $yamlToTTconvertDir}});
125             }
126              
127             sub delete{
128 0     0 1   my ($self, $instance) = @_;
129            
130 0           $self->{instance} = $instance;
131 0 0         return if not defined $self->generateYaml();
132              
133 0           my $cluster = $self->{config}->{cluster};
134 0           my $namespace = $self->{config}->{namespace};
135 0           print "\nDeleting Openshift components from instance: '$instance' in cluster: '$cluster' in namespace: '$namespace'\n";
136 0           $self->_loopDir($self->{config}->{templates_yaml_dir}, "yaml", "_deleteOc");
137            
138 0           return;
139             }
140              
141             sub generateYaml{
142 0     0 0   my ($self) = @_;
143              
144 0 0 0       print "Instance is missing.\n" and return if not defined $self->{instance};
145              
146 0           $self->_selectInstanceSpecificSecrets();
147 0           $self->{config} = $self->_generateConfig();
148 0 0         $self->_removeInitFromComponentDirs() if $self->{omit} =~ /init/;
149 0           $self->_loopDir($self->{config}->{templates_tt_dir}, "*", "_selectClusterSpecificTemplates");
150 0           $self->_clearDir($self->{config}->{templates_yaml_dir});
151 0           $self->_loopDir($self->{config}->{templates_tt_dir}, "tt", "_generateYaml");
152 0           $self->_loopDir($self->{config}->{templates_tt_dir}, "*", "_removeClusterSpecificTemplates");
153 0           $self->_removeInstanceSpecificSecrets();
154            
155 0           return 1;
156             }
157              
158             sub install{
159 0     0 1   my ($self, $instance) = @_;
160              
161 0           $self->{instance} = $instance;
162 0 0         return if not defined $self->generateYaml();
163              
164 0           my $cluster = $self->{config}->{cluster};
165 0           my $namespace = $self->{config}->{namespace};
166 0           print "\nInstalling Openshift components for instance: '$instance' in cluster: '$cluster' in namespace: '$namespace'\n";
167             # sent custom params
168             # $self->_loopDir($self->{config}->{templates_tt_dir}, "tt", "_callOc", {_callOc => {{param1 : "value1"}, {param2 : "value2"} }});
169 0 0         $self->_loopDir($self->{config}->{templates_yaml_dir}, "yaml", "_callOc") if $self->{omit} !~ /oc/;
170            
171 0           print qq^\n\nTo get build and deployment status run: $self->{cliCommand} get pods | grep Running | grep 'build\|deploy'\n\n^;
172            
173 0           return;
174             }
175              
176             sub setParams{
177 0     0 0   my ($self, $params) = @_;
178              
179 0 0         if (@_ == 2) {
180 0 0         $self->{advanceFeatures} = $params->{advanceFeatures} if defined $params->{advanceFeatures};
181 0 0         $self->{clusterBaseAddress} = $params->{clusterBaseAddress} if defined $params->{clusterBaseAddress};
182 0 0         $self->{cluster} = $params->{cluster} if defined $params->{cluster};
183 0 0         $self->{ocConfigFile} = $params->{ocConfigFile} if defined $params->{ocConfigFile};
184 0 0         $self->{host} = $params->{host} if defined $params->{host};
185 0 0         $self->{ocResourceKinds} = $params->{ocResourceKinds} if defined $params->{ocResourceKinds};
186 0 0         $self->{componentDirs} = $params->{componentDirs} if defined $params->{componentDirs};
187 0 0         $self->{namespace} = $params->{namespace} if defined $params->{namespace};
188 0 0         $self->{projectName} = $params->{projectName} if defined $params->{projectName};
189 0 0         $self->{omit} = $params->{omit} if defined $params->{omit};
190 0 0         $self->{urlPrefix} = $params->{urlPrefix} if defined $params->{urlPrefix};
191 0 0         $self->{clusterIpRange} = $params->{clusterIpRange} if defined $params->{clusterIpRange};
192 0 0         $self->{secretsDir} = $params->{secretsDir} if defined $params->{secretsDir};
193 0 0         $self->{sortType} = $params->{sortType} if defined $params->{sortType};
194 0 0         $self->{templatesTTDir} = $params->{templatesTTDir} if defined $params->{templatesTTDir};
195 0 0         $self->{yamlToTTconvertDir} = $params->{yamlToTTconvertDir} if defined $params->{yamlToTTconvertDir};
196 0 0         $self->{specificYamlFile} = $params->{specificYamlFile} if defined $params->{specificYamlFile};
197 0 0         $self->{templatesYamlDir} = $params->{templatesYamlDir} if defined $params->{templatesYamlDir};
198 0 0         $self->{validationReportFile} = $params->{validationReportFile} if defined $params->{validationReportFile};
199             }
200 0           return;
201             }
202              
203             sub upgrade{
204 0     0 1   my ($self, $instance) = @_;
205            
206 0           $self->validate($instance);
207            
208 0           my $validationReport = read_file($self->{validationReportFile});
209 0           my @validationReportLines = split /\n/, $validationReport;
210 0           foreach my $line (@validationReportLines){
211 0           my @items = split /;/, $line;
212 0 0         if($items[-1] eq "MODIFIED"){
213 0           my $pathAndFile = $items[0];
214 0           my $ocKind = $items[1];
215 0           my $ocName = $items[2];
216 0 0         if($ocKind eq "PersistentVolumeClaim"){
217 0           print "You are trying to update PersistentVolumeClaim.
218             Please make sure that all PODs that use this Persisten Volume are turned down before update.
219             If update operation start hanging at this step press ctrl+c to abort. \n\n";
220             }
221 0           print "Upgrading ocKind:$ocKind, ocName: $ocName from $pathAndFile\n";
222 0           qx/$self->{cliCommand} delete $ocKind $ocName/;
223 0           qx/$self->{cliCommand} create -f .\/$self->{config}->{templates_yaml_dir}\/$pathAndFile/;
224             }
225             }
226             }
227              
228             sub validate{
229 0     0 1   my ($self, $instance) = @_;
230              
231 0           qx/> $self->{validationReportFile}/; # clear file
232 0           $self->{instance} = $instance;
233 0 0         return if not defined $self->generateYaml();
234            
235 0           my $cluster = $self->{config}->{cluster};
236 0           my $namespace = $self->{config}->{namespace};
237 0           print "\nValidating Openshift components for instance: '$instance' in cluster: '$cluster' in namespace: '$namespace'\n";
238 0           $self->_loopDir($self->{config}->{templates_yaml_dir}, "yaml", "_validateInstance");
239             }
240              
241             sub _backupInstance{
242 0     0     my ($self, $params) = @_;
243              
244 0           my $dir = $params->{dir};
245 0           my $templateName = $params->{templateName};
246              
247 0           my $instance = $self->{config}->{instance};
248 0           qx/mkdir backups\/$instance\/$dir 2>&1/;
249 0           my $pathToYamlFile = "$self->{config}->{templates_yaml_dir}/$dir/$templateName".".yaml";
250 0           my $templateData = LoadFile($pathToYamlFile);
251            
252 0           my $yamlText;
253 0 0 0       if((defined $self->{advanceFeatures}) &&
      0        
254             ($self->{advanceFeatures} =~ /removeClutter/) &&
255             (defined $self->{removeClutterBackup})){
256 0           print "$self->{cliCommand} get $templateData->{kind} $templateData->{metadata}->{name} -o json\n";
257 0           my $ocJson = qx/$self->{cliCommand} get $templateData->{kind} $templateData->{metadata}->{name} -o json/;
258 0           my $ocHash = $self->{json}->utf8->decode($ocJson);
259 0           my $subParams = {"ocKind" => $templateData->{kind}, "ocName" => $templateData->{metadata}->{name}};
260 0           $ocHash = $self->{removeClutterBackup}->($ocHash, $subParams);
261 0           my $yamlSaveObj = YAML::Safe->new->boolean("JSON::PP");
262 0           $yamlText = $yamlSaveObj->Dump($ocHash);
263 0           $yamlText =~ s/---\n//;
264             }else{
265 0           print "$self->{cliCommand} get $templateData->{kind} $templateData->{metadata}->{name} -o yaml\n";
266 0           $yamlText = qx/$self->{cliCommand} get $templateData->{kind} $templateData->{metadata}->{name} -o yaml/;
267             }
268              
269 0           write_file("backups\/$instance\/$dir/$templateName".".yaml", $yamlText);
270             }
271              
272             sub _callOc{
273 0     0     my ($self, $params) = @_;
274            
275 0           my @funcName = split /::/, (caller(0))[3];
276 0           my $dir = $params->{dir};
277 0           my $templateName = $params->{templateName};
278 0           my $customParams = $params->{params}->{$funcName[-1]};
279              
280             return if (defined $self->{componentIsAllowed}) &&
281 0 0 0       (not $self->{componentIsAllowed}->($templateName, $dir, $self->{cluster}, $self->{instance}));
282              
283 0           my $templateNameYaml = $templateName.".yaml";
284            
285 0           my $yamlData = LoadFile("$self->{config}->{templates_yaml_dir}/$dir/$templateNameYaml");
286 0 0         if($yamlData->{kind} eq "CronJob"){
287 0           print "$self->{cliCommand} apply -f templates_yaml/$dir/$templateNameYaml\n";
288 0           qx/$self->{cliCommand} apply -f .\/$self->{config}->{templates_yaml_dir}\/$dir\/$templateNameYaml/;
289             }else{
290 0           print "$self->{cliCommand} create -f templates_yaml/$dir/$templateNameYaml\n";
291 0           qx/$self->{cliCommand} create -f .\/$self->{config}->{templates_yaml_dir}\/$dir\/$templateNameYaml/;
292             }
293             }
294              
295             sub _clusterDirExist{
296 0     0     my ($self, $templateName) = @_;
297              
298 0           my @allowedClustersArr = split(';', $self->{config}->{allowed_clusters});
299 0           foreach my $myCluster (@allowedClustersArr){
300 0 0 0       return 1 if ($templateName eq $myCluster) && ($self->{cluster} eq $myCluster);
301             }
302 0           return 0;
303             }
304              
305             sub _clearDir{
306 0     0     my ($self, $dir) = @_;
307              
308 0           rmtree $dir;
309 0           make_path $dir;
310             }
311              
312             sub _convertYamlToTTExtention{
313 0     0     my ($self, $params) = @_;
314              
315 0           my @funcName = split /::/, (caller(0))[3];
316 0           my $dir = $params->{dir};
317 0           my $templateName = $params->{templateName};
318 0           my $customParams = $params->{params}->{$funcName[-1]};
319 0           my $yamlToTTconvertDir = $customParams->{yamlToTTconvertDir};
320            
321 0           my $path = "$yamlToTTconvertDir/$dir";
322 0           qx/mv $path\/$templateName\.yaml $path\/$templateName\.tt 2>&1/;
323             }
324              
325             sub _deleteOc{
326 0     0     my ($self, $params) = @_;
327              
328 0           my $dir = $params->{dir};
329 0           my $templateName = $params->{templateName};
330              
331 0           my $data = LoadFile("$self->{config}->{templates_yaml_dir}/$dir/$templateName".".yaml");
332 0           print "$self->{cliCommand} delete $data->{kind} $data->{metadata}->{name}\n";
333 0           qx/$self->{cliCommand} delete $data->{kind} $data->{metadata}->{name}/;
334             }
335              
336             sub _generateYaml{
337 0     0     my ($self, $params) = @_;
338              
339 0           my @funcName = split /::/, (caller(0))[3];
340 0           my $dir = $params->{dir};
341 0           my $templateName = $params->{templateName};
342 0           my $customParams = $params->{params}->{$funcName[-1]};
343              
344 0 0 0       return if $dir =~ /init/ && $self->{omit} =~ /init/;
345 0 0 0       return if (defined $self->{specificYamlFile}) && ($templateName !~ $self->{specificYamlFile});
346             return if (defined $self->{componentIsAllowed}) &&
347 0 0 0       (not $self->{componentIsAllowed}->($templateName, $dir, $self->{cluster}, $self->{instance}));
348            
349 0           my $yamlText;
350 0           my $templatesYamlPath = "$self->{config}->{templates_yaml_dir}/$dir";
351 0           my $templatesTTPath = "$self->{config}->{templates_tt_dir}/$dir";
352 0 0 0       make_path $templatesYamlPath or die("Failed to create path: $templatesYamlPath") if !-d $templatesYamlPath;
353 0           $self->{tt}->process("$templatesTTPath/$templateName\.tt", $self->{config}, \$yamlText);
354            
355 0           my $yamlHash;
356 0           eval { $yamlHash = Load($yamlText);};
  0            
357 0 0         if($@){
358 0           print "Error occured during conversion to yaml in: $dir $templateName\n", Dumper($@), "\n";
359 0           return;
360             }
361            
362 0 0 0       return if (defined $self->{ocResourceKinds}) && ($self->{ocResourceKinds} !~ $yamlHash->{kind});
363              
364 0           write_file("$templatesYamlPath/$templateName\.yaml", $yamlText);
365 0 0         $self->{config}->{ip_last_number}++ if $yamlHash->{kind} eq "Service";
366             }
367              
368             sub _generateConfig{
369 0     0     my ($self) = @_;
370              
371 0           my $ocConfigJson = read_file($self->{ocConfigFile});
372 0           my $config->{oc_config} = $self->{json}->utf8->decode($ocConfigJson);
373              
374 0 0         $config = $self->{addFlagValuesToConfig}->($config) if defined $self->{addFlagValuesToConfig};
375            
376             # set/generate instance specific names
377 0           foreach my $entry (keys %{$config->{oc_config}->{instance_specific_name}}){
  0            
378 0           $config->{oc_config}->{instance_specific_name}->{$entry} .= "-$self->{instance}";
379             }
380              
381 0           $config->{allowed_clusters} = $config->{oc_config}->{project}->{allowed_clusters};
382 0 0         if(not defined $config->{allowed_clusters}){
383 0           print "Warning: oc_config->project->allowed_clusters json node is empty. Marking 'allowed_clusters' as '$self->{cluster}'\n";
384 0           $config->{allowed_clusters} = $self->{cluster};
385             }else{
386 0 0         print "Warning: Unknown cluster $self->{cluster}\n" if $config->{allowed_clusters} !~ $self->{cluster};
387             }
388            
389 0           $config->{cluster_ip_range} = $config->{oc_config}->{project}->{cluster_ip_range};
390 0 0         $config->{cluster_ip_range} = $self->{clusterIpRange} if defined $self->{clusterIpRange};
391 0           $config->{project_name} = $config->{oc_config}->{project}->{name};
392 0 0         $config->{project_name} = $self->{projectName} if defined $self->{projectName};
393 0           $config->{host} = $config->{oc_config}->{project}->{host};
394 0 0         $config->{host} = $self->{host} if defined $self->{host};
395 0           $config->{namespace} = $self->_getCurrentProject();
396 0 0         $config->{namespace} = $self->{namespace} if defined $self->{namespace};
397             # default component dirs are set here, dirs in 'templates_tt' not set as default will be omitted
398 0           $config->{component_dirs} = $config->{oc_config}->{project}->{component_dirs};
399 0 0         $config->{component_dirs} = $self->{componentDirs} if defined $self->{componentDirs};
400             # component dirs can contains numbers e.g.: '50-solr' so regexp match is used =>
401             # separate 'init' components in order to avoid false matches (e.g.: 20-init-api vs 50-api when 'api' searched)
402 0           my @componentDirs = split(';', $config->{component_dirs});
403 0           my $standardComponentDirs = "";
404 0           my $initComponentDirs = "";
405 0           foreach my $componentDir (@componentDirs){
406 0 0         if($componentDir =~ /init/){
407 0           $initComponentDirs .= $componentDir.";";
408             }else{
409 0           $standardComponentDirs .= $componentDir.";";
410             }
411             }
412 0           chop($initComponentDirs);
413 0           chop($standardComponentDirs);
414 0           $config->{init_component_dirs} = $initComponentDirs;
415 0           $config->{standard_component_dirs} = $standardComponentDirs;
416 0           $config->{oc_resource_kinds} = $config->{oc_config}->{project}->{oc_resource_kinds};
417 0 0         $config->{oc_resource_kinds} = $self->{ocResourceKinds} if defined $self->{ocResourceKinds};
418 0           $config->{templates_yaml_dir} = $self->{templatesYamlDir};
419 0           $config->{templates_tt_dir} = $self->{templatesTTDir};
420 0           $config->{cluster_camelcase} = $self->{cluster};
421 0           $config->{cluster} = lc $self->{cluster};
422 0           $config->{instance} = lc $self->{instance};
423 0           $config->{instance_capitalized_first} = ucfirst $config->{instance};
424 0 0         $config->{url_prefix} = $self->{urlPrefix} if defined $self->{urlPrefix};
425 0 0         $config->{cluster_base_address} = $self->{clusterBaseAddress} if defined $self->{clusterBaseAddress};
426            
427 0           my $componentConfigNodes = $self->_getComponentConfigNodes($config);
428 0           foreach my $componentConfNode (@$componentConfigNodes){
429             # set/generate default urls
430 0 0 0       if((defined $config->{oc_config}->{instance_specific_data}->{$componentConfNode}) &&
431             (ref($config->{oc_config}->{instance_specific_data}->{$componentConfNode}) eq 'HASH')){
432 0           foreach my $instanceKey (keys %{$config->{oc_config}->{instance_specific_data}->{$componentConfNode}}){
  0            
433 0 0         if(not defined $config->{oc_config}->{instance_specific_data}->{$componentConfNode}->{$instanceKey}->{url}){
434 0           my $componentNameKebab = $componentConfNode;
435 0           $componentNameKebab =~ s/_/\-/;
436 0           my $lcInstanceKey = lc $instanceKey;
437 0           my $url = "";
438 0 0         if(defined $self->{generateUrl}){
439             $url = $self->{generateUrl}->($config->{url_prefix},
440             $config->{project_name},
441             $componentNameKebab,
442             $lcInstanceKey,
443             $config->{cluster_base_address},
444 0           $config->{host});
445             }
446 0           $config->{oc_config}->{instance_specific_data}->{$componentConfNode}->{$instanceKey}->{url} = $url;
447             }
448             }
449             }
450             # select instance specific data
451 0 0         if(defined $config->{oc_config}->{instance_specific_data}->{$componentConfNode}->{$self->{instance}}){
452             $config->{oc_config}->{instance_specific_data}->{$componentConfNode} =
453 0           $config->{oc_config}->{instance_specific_data}->{$componentConfNode}->{$self->{instance}};
454             }
455             }
456             # in worse case 36 available IP addresses(see _generateYaml), make number smaller if more needed(max is 256)
457 0           $config->{ip_last_number} = int(rand(220));
458            
459 0 0         print "'Info: component_dirs' parameter is missing\n" if not defined $config->{component_dirs};
460 0 0         print "'Info: cluster_ip_range' parameter is missing\n" if not defined $config->{cluster_ip_range};
461 0 0         print "'Info: host' parameter is missing\n" if not defined $config->{host};
462              
463 0           $self->_getSecrets($config);
464            
465 0           return $config;
466             }
467              
468              
469             sub _getSecrets{
470 0     0     my ($self, $config) = @_;
471              
472 0           for my $dirFileName (File::Find::Rule->file()->name("*")->in($self->{secretsDir})) {
473 0           my @dirFileNameArr = split('/', $dirFileName);
474 0 0         if(scalar @dirFileNameArr == 2){
475 0           my $secretText = read_file($dirFileName);
476 0           $secretText =~ s/\n//g;
477 0           $secretText =~ s/\r//g;
478 0           my $secretTextBase64 = encode_base64($secretText);
479 0           $secretTextBase64 =~ s/\n//g;
480 0           $secretTextBase64 =~ s/\r//g;
481 0           $config->{secrets}->{$dirFileNameArr[1]} = $secretTextBase64;
482             }
483             }
484             # instance specific secrets e.g.: secrets/instance/test/my-secret.txt
485 0           for my $dirFileName (File::Find::Rule->file()->name("*")->in("$self->{secretsDir}/instance/$self->{instance}")) {
486 0           my @dirFileNameArr = split('/', $dirFileName);
487 0 0         if(scalar @dirFileNameArr == 4){
488 0           my $secretText = read_file($dirFileName);
489 0           $secretText =~ s/\n//g;
490 0           $secretText =~ s/\r//g;
491 0           my $secretTextBase64 = encode_base64($secretText);
492 0           $secretTextBase64 =~ s/\n//g;
493 0           $secretTextBase64 =~ s/\r//g;
494 0           $config->{secrets}->{instance_specific}->{$dirFileNameArr[3]} = $secretTextBase64;
495             }
496             }
497             }
498              
499             sub _getComponentConfigNodes{
500 0     0     my ($self, $config) = @_;
501            
502 0           my @componentConfigNodes;
503             my $componentConfigNodesString;
504 0 0 0       if((defined $config->{oc_config}) && (defined $config->{oc_config}->{instance_specific_data})){
505 0           foreach my $componentConfigNode (keys %{$config->{oc_config}->{instance_specific_data}}){
  0            
506 0           push @componentConfigNodes, $componentConfigNode;
507             }
508             }
509              
510 0           return \@componentConfigNodes;
511             }
512              
513             sub _getCurrentProject{
514 0     0     my ($self) = @_;
515              
516 0           my $projectCmdLine = qx/$self->{cliCommand} config current-context/;
517 0           my @projectCmdLineArr = split('/', $projectCmdLine);
518 0           my $project = $projectCmdLineArr[0];
519 0 0         $project = "unknown" if not defined $project;
520              
521 0           return $project;
522             }
523              
524             sub _loopDir {
525 0     0     my ($self, $dirToLoop, $extention, $injectedSubName, $params) = @_;
526              
527 0           my $templates = {};
528 0           for my $dirFileName (File::Find::Rule->file()->name("*.$extention")->in($dirToLoop)) {
529 0           $dirFileName =~ s/$dirToLoop\///;
530 0           my @dirFile = split('\/', $dirFileName);
531 0           my @dir = split('\-', $dirFile[0]);
532 0           my @file = split('\-', $dirFile[1]);
533             # e.g: $templates->{30-solr}->{dirNumber} = 30 # in order so sort as integer
534             # ->{20-build-config-solr.tt} = 20
535 0           $templates->{$dirFile[0]}->{dirNumber} = $dir[0];
536 0           $templates->{$dirFile[0]}->{$dirFile[1]} = $file[0];
537             }
538              
539 0           my @dirArray;
540 0 0 0       if((defined $self->{sortType}) && ($self->{sortType}) eq "alphabetic"){
541 0           foreach my $dir (sort { lc $templates->{$a}->{dirNumber} cmp lc $templates->{$b}->{dirNumber} } keys %{$templates}){
  0            
  0            
542 0           push @dirArray, $dir;
543             }
544             }else{
545             # numeric
546 1     1   14 no warnings 'numeric';
  1         2  
  1         264  
547 0           foreach my $dir (sort { $templates->{$a}->{dirNumber} <=> $templates->{$b}->{dirNumber} } keys %{$templates}){
  0            
  0            
548 0           push @dirArray, $dir;
549             }
550             }
551 0 0         @dirArray = reverse @dirArray if $injectedSubName eq "_deleteOc";
552              
553 0           foreach my $dir (@dirArray){
554 0 0         print "\nInstalling components from: $dir:\n" if $injectedSubName eq "_callOc";
555            
556 0           my @fileArray;
557 0 0 0       if((defined $self->{sortType}) && ($self->{sortType}) eq "alphabetic"){
558 0           foreach my $file (sort { lc $templates->{$dir}->{$a} cmp lc $templates->{$dir}->{$b} } keys %{$templates->{$dir}}){
  0            
  0            
559 0           push @fileArray, $file;
560             }
561             }else{
562             # numeric
563 1     1   17 no warnings 'numeric';
  1         2  
  1         2702  
564 0           foreach my $file (sort { $templates->{$dir}->{$a} <=> $templates->{$dir}->{$b} } keys %{$templates->{$dir}}){
  0            
  0            
565 0           push @fileArray, $file;
566             }
567             }
568 0 0         @fileArray = reverse @fileArray if $injectedSubName eq "_deleteOc";
569              
570 0           foreach my $file (@fileArray){
571             # when calling '_generateYaml' skip cluster directories defined in oc_project.json->project->allowed_clusters,
572             # cluster specific tt files are already copied to the component directory
573 0 0 0       if((($injectedSubName eq "_generateYaml") && ($self->{config}->{allowed_clusters} =~ /$file/)) ||
      0        
      0        
574             $file eq "dirNumber" ||
575             $self->_skipComponent($dir)) {
576 0           next;
577             }
578 0           my @fileArr = split('\.', $file);
579 0 0 0       if(($injectedSubName eq "_generateYaml") &&
      0        
580             (not defined $fileArr[1]) &&
581             ($self->{config}->{allowed_clusters} !~ /$file/)){
582 0           print "Warning : Unknown cluster: $file\n";
583             }
584 0           my $injectedSub = \&$injectedSubName;
585 0           $injectedSub->($self, {dir => $dir, templateName => $fileArr[0], params => $params});
586             }
587             }
588             }
589              
590             sub _removeClusterSpecificTemplates{
591 0     0     my ($self, $params) = @_;
592              
593 0 0 0       return if (not defined $self->{advanceFeatures}) || ($self->{advanceFeatures} !~ /multipleClusters/);
594              
595 0           my $dir = $params->{dir};
596 0           my $templateName = $params->{templateName};
597            
598 0 0         if($self->_clusterDirExist($templateName)){
599 0           qx/rm templates_tt\/$dir\/*.tt 2>&1/;
600 0           qx/cp tmp\/templates_tt\/*.tt templates_tt\/$dir\/ 2>&1/;
601 0           qx/rm tmp\/templates_tt\/*.tt 2>&1/;
602             }
603             }
604              
605             sub _removeInitFromComponentDirs{
606 0     0     my ($self) = @_;
607            
608 0           my @componentsYamlDirArray = split(';', $self->{config}->{component_dirs});
609 0           @componentsYamlDirArray = (grep {$_ !~ /init/} @componentsYamlDirArray);
  0            
610 0           $self->{config}->{component_dirs} = join( ';', @componentsYamlDirArray);
611             }
612              
613             sub _removeInstanceSpecificSecrets{
614 0     0     my ($self) = @_;
615            
616 0 0 0       return if (not defined $self->{advanceFeatures}) || ($self->{advanceFeatures} !~ /multipleClusters/);
617            
618 0           qx/rm $self->{secretsDir}\/* 2>&1/;
619 0           qx/cp tmp\/$self->{secretsDir}\/* $self->{secretsDir}\/ 2>&1/;
620 0           qx/rm tmp\/$self->{secretsDir}\/* 2>&1/;
621             }
622              
623             sub _selectClusterSpecificTemplates{
624 0     0     my ($self, $params) = @_;
625              
626 0 0 0       return if (not defined $self->{advanceFeatures}) || ($self->{advanceFeatures} !~ /multipleClusters/);
627            
628 0           my $dir = $params->{dir};
629 0           my $templateName = $params->{templateName};
630            
631 0 0         if($self->_clusterDirExist($templateName)){
632 0           qx/cp templates_tt\/$dir\/*.tt tmp\/templates_tt\/ 2>&1/;
633 0           qx/cp templates_tt\/$dir\/$self->{cluster}\/*.tt templates_tt\/$dir\//;
634             }
635             }
636              
637             sub _selectInstanceSpecificSecrets{
638 0     0     my ($self) = @_;
639            
640 0 0 0       return if (not defined $self->{advanceFeatures}) || ($self->{advanceFeatures} !~ /multipleClusters/);
641            
642 0           qx/cp $self->{secretsDir}\/* tmp\/$self->{secretsDir}\/ 2>&1/;
643 0           qx/cp $self->{secretsDir}\/$self->{cluster}\/* $self->{secretsDir}\/ 2>&1/;
644             }
645              
646             sub _skipComponent{
647 0     0     my ($self, $dir) = @_;
648              
649             # ignore skiping if called by this flag
650 0 0         return 0 if defined $self->{yamlToTTconvertDir};
651            
652 0           my @installComponents = [];
653 0 0         if($dir =~ /init/){
654 0 0         if(defined $self->{config}->{init_component_dirs}){
655 0           @installComponents = split(';', $self->{config}->{init_component_dirs});
656             }
657 0           foreach my $installComponent (@installComponents){
658 0 0         return 0 if $dir =~ /$installComponent/;
659             }
660             }else{
661 0 0         if(defined $self->{config}->{standard_component_dirs}){
662 0           @installComponents = split(';', $self->{config}->{standard_component_dirs});
663             }
664 0           foreach my $installComponent (@installComponents){
665 0 0         return 0 if $dir =~ /$installComponent/;
666             }
667             }
668              
669 0           return 1;
670             }
671              
672             sub _validateInstance{
673 0     0     my ($self, $params) = @_;
674            
675 0           my @funcName = split /::/, (caller(0))[3];
676 0           my $dir = $params->{dir};
677 0           my $templateName = $params->{templateName};
678            
679 0           my $templateNameYaml = $templateName.".yaml";
680 0           my $yamlData = LoadFile("$self->{config}->{templates_yaml_dir}/$dir/$templateNameYaml");
681 0           my $ocName = $yamlData->{metadata}->{name};
682 0           my $ocKind = $yamlData->{kind};
683 0           my $ocJson = qx/$self->{cliCommand} get $ocKind $ocName -o json/;
684 0           my $ocHash = {};
685 0           eval {
686 0           $ocHash = $self->{json}->utf8->decode($ocJson);
687 0 0 0       if(($ocKind eq "Secret") && (ref($ocHash->{data}) eq 'HASH')){
688 0           foreach my $key (keys %{$ocHash->{data}}){
  0            
689 0           my $secret = decode_base64($ocHash->{data}->{$key});
690 0           $secret =~ s/\n//g;
691 0           $secret =~ s/\r//g;
692 0           $ocHash->{data}->{$key} = $secret;
693             }
694             }
695             };
696 0 0         print Dumper($@) if $@;
697            
698 0           my $templateYamlText = read_file("$self->{config}->{templates_yaml_dir}/$dir/$templateNameYaml");
699 0           my $yamlObj = YAML::Safe->new->boolean("JSON::PP");
700 0           my $templateHash = $yamlObj->Load($templateYamlText);
701              
702 0           my $subParams = {"dir" => $dir, "templateName" => $templateName, "ocKind" => $ocKind, "ocName" => $ocName};
703 0 0         $ocHash = $self->{removeClutter}->($ocHash, $subParams) if defined $self->{removeClutter};
704 0 0         $templateHash = $self->{removeClutter}->($templateHash, $subParams) if defined $self->{removeClutter};
705            
706 0 0         if($ocKind eq "Secret"){
707 0 0 0       if((defined $templateHash->{data}) && (ref($templateHash->{data}) eq 'HASH')){
708 0           foreach my $key (keys %{$templateHash->{data}}){
  0            
709 0           my $dataBase64Encoded = $templateHash->{data}->{$key};
710 0           my $dataBase64Decoded = decode_base64($dataBase64Encoded);
711 0           $dataBase64Decoded =~ s/\n//g;
712 0           $dataBase64Decoded =~ s/\r//g;
713 0           $templateHash->{data}->{$key} = $dataBase64Decoded;
714             }
715             }
716             }
717            
718 0           $ocJson = $self->{json}->utf8->pretty->canonical->encode($ocHash);
719 0           my $templateJson = $self->{json}->utf8->pretty->canonical->encode($templateHash);
720 0           my @ocJsonArr = split /\n/, $ocJson;
721 0           my @templateJsonArr = split /\n/, $templateJson;
722 0           my $diff = diff \@ocJsonArr, \@templateJsonArr, { STYLE => "Table", CONTEXT => 0 };
723 0 0 0       if(($ocKind eq "Secret") && ($diff ne "")){
724 0           $diff = "+---+-----------------------------------------------------+---+---------------------------------+
725             * | different secret, not displayed * | different secret, not displayed |
726             +---+-----------------------------------------------------+---+---------------------------------+
727             ";
728             }
729 0           $diff =~ s/\\ No newline at end of file\s//g;
730            
731 0           my $diffStatus;
732 0 0 0       if((defined $diff) && ($diff ne "")){
733 0           $diffStatus = "MODIFIED";
734             }else{
735 0           $diffStatus = "OK";
736             }
737 0           my $line = "$dir/$templateNameYaml;$ocKind;$ocName;$diffStatus\n";
738 0           print $line;
739 0 0         print $diff if $diff ne "";
740 0           write_file($self->{validationReportFile}, {append => 1}, $line);
741             }
742              
743             1;
744              
745             __END__