Passed
Pull Request — master (#66)
by
unknown
08:17
created

AnsiblePlaybook::getCommandlineArguments()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 1
dl 0
loc 5
rs 10
c 0
b 0
f 0
1
<?php
2
/*
3
 * This file is part of the php-ansible package.
4
 *
5
 * (c) Marc Aschmann <[email protected]>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
11
namespace Asm\Ansible\Command;
12
13
use InvalidArgumentException;
14
15
/**
16
 * Class AnsiblePlaybook
17
 *
18
 * @package Asm\Ansible\Command
19
 * @author Marc Aschmann <[email protected]>
20
 */
21
final class AnsiblePlaybook extends AbstractAnsibleCommand implements AnsiblePlaybookInterface
22
{
23
    /**
24
     * @var boolean
25
     */
26
    private $hasInventory = false;
27
28
    /**
29
     * Executes a command process.
30
     * Returns either exitcode or string output if no callback is given.
31
     *
32
     * @param callable|null $callback
33
     * @return integer|string
34
     */
35
    public function execute($callback = null)
36
    {
37
        $this->checkInventory();
38
39
        return $this->runProcess($callback);
40
    }
41
42
    /**
43
     * The play to be executed.
44
     *
45
     * @param string $playbook
46
     * @return AnsiblePlaybookInterface
47
     */
48
    public function play(string $playbook): AnsiblePlaybookInterface
49
    {
50
        $this->addBaseoption($playbook);
51
52
        return $this;
53
    }
54
55
    /**
56
     * Ask for SSH password.
57
     *
58
     * @return AnsiblePlaybookInterface
59
     */
60
    public function askPass(): AnsiblePlaybookInterface
61
    {
62
        $this->addParameter('--ask-pass');
63
64
        return $this;
65
    }
66
67
    /**
68
     * Ask for su password.
69
     *
70
     * @return AnsiblePlaybookInterface
71
     */
72
    public function askSuPass(): AnsiblePlaybookInterface
73
    {
74
        $this->addParameter('--ask-su-pass');
75
76
        return $this;
77
    }
78
79
    /**
80
     * Ask for sudo password.
81
     *
82
     * @return AnsiblePlaybookInterface
83
     */
84
    public function askBecomePass(): AnsiblePlaybookInterface
85
    {
86
        $this->addParameter('--ask-become-pass');
87
88
        return $this;
89
    }
90
91
    /**
92
     * Ask for vault password.
93
     *
94
     * @return AnsiblePlaybookInterface
95
     */
96
    public function askVaultPass(): AnsiblePlaybookInterface
97
    {
98
        $this->addParameter('--ask-vault-pass');
99
100
        return $this;
101
    }
102
103
    /**
104
     * Enable privilege escalation
105
     *
106
     * @return AnsiblePlaybookInterface
107
     * @see http://docs.ansible.com/ansible/become.html
108
     */
109
    public function become(): AnsiblePlaybookInterface
110
    {
111
        $this->addParameter('--become');
112
113
        return $this;
114
    }
115
116
    /**
117
     * Desired sudo user (default=root).
118
     *
119
     * @param string $user
120
     * @return AnsiblePlaybookInterface
121
     */
122
    public function becomeUser(string $user = 'root'): AnsiblePlaybookInterface
123
    {
124
        $this->addOption('--become-user', $user);
125
126
        return $this;
127
    }
128
129
    /**
130
     * Don't make any changes; instead, try to predict some of the changes that may occur.
131
     *
132
     * @return AnsiblePlaybookInterface
133
     */
134
    public function check(): AnsiblePlaybookInterface
135
    {
136
        $this->addParameter('--check');
137
138
        return $this;
139
    }
140
141
    /**
142
     * Connection type to use (default=smart).
143
     *
144
     * @param string $connection
145
     * @return AnsiblePlaybookInterface
146
     */
147
    public function connection(string $connection = 'smart'): AnsiblePlaybookInterface
148
    {
149
        $this->addOption('--connection', $connection);
150
151
        return $this;
152
    }
153
154
    /**
155
     * When changing (small) files and templates, show the
156
     * differences in those files; works great with --check.
157
     *
158
     * @return AnsiblePlaybookInterface
159
     */
160
    public function diff(): AnsiblePlaybookInterface
161
    {
162
        $this->addParameter('--diff');
163
164
        return $this;
165
    }
166
167
    /**
168
     * Example:
169
     * ```php
170
     * $ansible = new Ansible()->extraVars('path=/some/path');
171
     * ```
172
     * Sends extra variables to Ansible. The $extraVars parameter can be one of the following.
173
     *
174
     * ## Array
175
     * If an array is passed, it must contain the [ 'key' => 'value' ] pairs of the variables.
176
     *
177
     * Example:
178
     * ```php
179
     * $ansible = new Ansible()->playbook()->extraVars(['path' => 'some/path']);
180
     * ```
181
     *
182
     * ## File
183
     * As Ansible also supports extra vars loaded from an YML file, you can also pass a file path.
184
     *
185
     * Example:
186
     * ```php
187
     * $ansible = new Ansible()->playbook()->extraVars('/path/to/extra/vars.yml');
188
     * ```
189
     *
190
     * ## String
191
     * You can also pass the raw extra vars string directly.
192
193
     * Example:
194
     * ```php
195
     * $ansible = new Ansible()->playbook()->extraVars('path=/some/path');
196
     * ```
197
     *
198
     * @param string|array $extraVars
199
     * @return AnsiblePlaybookInterface
200
     */
201
    public function extraVars($extraVars = ''): AnsiblePlaybookInterface
202
    {
203
        if (empty($extraVars))
204
            return $this;
205
206
        // Throw exception when $extraVars is an object
207
        if (gettype($extraVars) === 'object') {
208
            throw new InvalidArgumentException(sprintf('Expected string|array, got "%s"', gettype($extraVars)));
209
        }
210
211
        // Building the key=>value parameter
212
        if (is_array($extraVars)) {
213
            $vars = [];
214
            foreach ($extraVars as $key => $value) {
215
                $vars[] = sprintf('%s=%s', $key, $value);
216
            }
217
            $this->addOption('--extra-vars', implode(' ', $vars));
218
            return $this;
219
        }
220
221
        // Should we consider $extraVars as a JSON/YML file?
222
        if (@is_file($extraVars)) {
223
            $this->addOption('--extra-vars', sprintf('@"%s"', $extraVars));
224
            return $this;
225
        }
226
227
        // At this point, the only allowed type is string.
228
        if (!is_string($extraVars))
0 ignored issues
show
introduced by
The condition is_string($extraVars) is always true.
Loading history...
229
            throw new InvalidArgumentException(sprintf('Expected string|array, got "%s"', gettype($extraVars)));
230
231
232
        if (strpos($extraVars, '=') === false) {
233
            throw new InvalidArgumentException('The extra vars raw string should be in the "key=value" form.');
234
        }
235
236
        $this->addOption('--extra-vars', $extraVars);
237
        return $this;
238
    }
239
240
    /**
241
     * Run handlers even if a task fails.
242
     *
243
     * @return AnsiblePlaybookInterface
244
     */
245
    public function forceHandlers(): AnsiblePlaybookInterface
246
    {
247
        $this->addParameter('--force-handlers');
248
249
        return $this;
250
    }
251
252
    /**
253
     * Specify number of parallel processes to use (default=5).
254
     *
255
     * @param int $forks
256
     * @return AnsiblePlaybookInterface
257
     */
258
    public function forks(int $forks = 5): AnsiblePlaybookInterface
259
    {
260
        $this->addOption('--forks', $forks);
261
262
        return $this;
263
    }
264
265
    /**
266
     * Show help message and exit.
267
     *
268
     * @return AnsiblePlaybookInterface
269
     */
270
    public function help(): AnsiblePlaybookInterface
271
    {
272
        $this->addParameter('--help');
273
274
        return $this;
275
    }
276
277
    /**
278
     * @inheritDoc
279
     */
280
    public function inventory(array $hosts = []): AnsiblePlaybookInterface
281
    {
282
        if (empty($hosts))
283
            return $this;
284
285
        // In order to let ansible-playbook understand that the given option is a list of hosts, the list must end by
286
        // comma "," if it contains just an entry. For example, supposing just a single host, "localhosts":
287
        //
288
        //   Wrong: --inventory="locahost"
289
        // Correct: --inventory="locahost,"
290
        $hostList = implode(', ', $hosts);
291
292
        if (count($hosts) === 1)
293
            $hostList .= ',';
294
295
        $this->addOption('--inventory', sprintf('"%s"', $hostList));
296
        $this->hasInventory = true;
297
298
        return $this;
299
    }
300
301
    /**
302
     * Specify inventory host file (default=/etc/ansible/hosts).
303
     *
304
     * @param string $inventory filename for hosts file
305
     * @return AnsiblePlaybookInterface
306
     */
307
    public function inventoryFile(string $inventory = '/etc/ansible/hosts'): AnsiblePlaybookInterface
308
    {
309
        $this->addOption('--inventory-file', $inventory);
310
        $this->hasInventory = true;
311
312
        return $this;
313
    }
314
315
    /**
316
     * Further limit selected hosts to an additional pattern.
317
     *
318
     * @param array|string $subset list of hosts
319
     * @return AnsiblePlaybookInterface
320
     */
321
    public function limit($subset = ''): AnsiblePlaybookInterface
322
    {
323
        $subset = $this->checkParam($subset, ',');
324
325
        $this->addOption('--limit', $subset);
326
327
        return $this;
328
    }
329
330
    /**
331
     * Outputs a list of matching hosts; does not execute anything else.
332
     *
333
     * @return AnsiblePlaybookInterface
334
     */
335
    public function listHosts(): AnsiblePlaybookInterface
336
    {
337
        $this->addParameter('--list-hosts');
338
339
        return $this;
340
    }
341
342
    /**
343
     * List all tasks that would be executed.
344
     *
345
     * @return AnsiblePlaybookInterface
346
     */
347
    public function listTasks(): AnsiblePlaybookInterface
348
    {
349
        $this->addParameter('--list-tasks');
350
351
        return $this;
352
    }
353
354
    /**
355
     * Specify path(s) to module library (default=/usr/share/ansible/).
356
     *
357
     * @param array $path list of paths for modules
358
     * @return AnsiblePlaybookInterface
359
     */
360
    public function modulePath(array $path = ['/usr/share/ansible/']): AnsiblePlaybookInterface
361
    {
362
        $this->addOption('--module-path', implode(',', $path));
363
364
        return $this;
365
    }
366
367
    /**
368
     * Disable cowsay
369
     *
370
     * @codeCoverageIgnore
371
     * @return AnsiblePlaybookInterface
372
     */
373
    public function noCows(): AnsiblePlaybookInterface
374
    {
375
        $this->processBuilder->setEnv('ANSIBLE_NOCOWS', 1);
376
377
        return $this;
378
    }
379
380
    /**
381
     * Enable/Disable Colors
382
     *
383
     * @param bool $colors
384
     * @return AnsiblePlaybookInterface
385
     */
386
    public function colors(bool $colors = true): AnsiblePlaybookInterface
387
    {
388
        $this->processBuilder->setEnv('ANSIBLE_FORCE_COLOR', intval($colors));
389
390
        return $this;
391
    }
392
393
    /**
394
     * Enable/Disable Json Output
395
     *
396
     * @return AnsiblePlaybookInterface
397
     */
398
    public function json(): AnsiblePlaybookInterface
399
    {
400
        $this->processBuilder->setEnv('ANSIBLE_STDOUT_CALLBACK', 'json');
401
402
        return $this;
403
    }
404
405
    /**
406
     * Use this file to authenticate the connection.
407
     *
408
     * @param string $file private key file
409
     * @return AnsiblePlaybookInterface
410
     */
411
    public function privateKey(string $file): AnsiblePlaybookInterface
412
    {
413
        $this->addOption('--private-key', $file);
414
415
        return $this;
416
    }
417
418
    /**
419
     * Only run plays and tasks whose tags do not match these values.
420
     *
421
     * @param array|string $tags list of tags to skip
422
     * @return AnsiblePlaybookInterface
423
     */
424
    public function skipTags($tags = ''): AnsiblePlaybookInterface
425
    {
426
        $tags = $this->checkParam($tags, ',');
427
        $this->addOption('--skip-tags', $tags);
428
429
        return $this;
430
    }
431
432
    /**
433
     * Start the playbook at the task matching this name.
434
     *
435
     * @param string $task name of task
436
     * @return AnsiblePlaybookInterface
437
     */
438
    public function startAtTask(string $task): AnsiblePlaybookInterface
439
    {
440
        $this->addOption('--start-at-task', $task);
441
442
        return $this;
443
    }
444
445
    /**
446
     * One-step-at-a-time: confirm each task before running.
447
     *
448
     * @return AnsiblePlaybookInterface
449
     */
450
    public function step(): AnsiblePlaybookInterface
451
    {
452
        $this->addParameter('--step');
453
454
        return $this;
455
    }
456
457
    /**
458
     * Run operations with su.
459
     *
460
     * @return AnsiblePlaybookInterface
461
     */
462
    public function su(): AnsiblePlaybookInterface
463
    {
464
        $this->addParameter('--su');
465
466
        return $this;
467
    }
468
469
    /**
470
     * Run operations with su as this user (default=root).
471
     *
472
     * @param string $user
473
     * @return AnsiblePlaybookInterface
474
     */
475
    public function suUser(string $user = 'root'): AnsiblePlaybookInterface
476
    {
477
        $this->addOption('--su-user', $user);
478
479
        return $this;
480
    }
481
482
    /**
483
     * Perform a syntax check on the playbook, but do not execute it.
484
     *
485
     * @return AnsiblePlaybookInterface
486
     */
487
    public function syntaxCheck(): AnsiblePlaybookInterface
488
    {
489
        $this->addParameter('--syntax-check');
490
491
        return $this;
492
    }
493
494
    /**
495
     * Only run plays and tasks tagged with these values.
496
     *
497
     * @param string|array $tags list of tags
498
     * @return AnsiblePlaybookInterface
499
     */
500
    public function tags($tags): AnsiblePlaybookInterface
501
    {
502
        $tags = $this->checkParam($tags, ',');
503
        $this->addOption('--tags', $tags);
504
505
        return $this;
506
    }
507
508
    /**
509
     * Override the SSH timeout in seconds (default=10).
510
     *
511
     * @param int $timeout
512
     * @return AnsiblePlaybookInterface
513
     */
514
    public function timeout(int $timeout = 10): AnsiblePlaybookInterface
515
    {
516
        $this->addOption('--timeout', $timeout);
517
518
        return $this;
519
    }
520
521
    /**
522
     * Connect as this user.
523
     *
524
     * @param string $user
525
     * @return AnsiblePlaybookInterface
526
     */
527
    public function user(string $user): AnsiblePlaybookInterface
528
    {
529
        $this->addOption('--user', $user);
530
531
        return $this;
532
    }
533
534
    /**
535
     * Vault password file.
536
     *
537
     * @param string $file
538
     * @return AnsiblePlaybookInterface
539
     */
540
    public function vaultPasswordFile(string $file): AnsiblePlaybookInterface
541
    {
542
        $this->addoption('--vault-password-file', $file);
543
544
        return $this;
545
    }
546
547
    /**
548
     * Verbose mode (vvv for more, vvvv to enable connection debugging).
549
     *
550
     * @param string $verbose
551
     * @return AnsiblePlaybookInterface
552
     */
553
    public function verbose(string $verbose = 'v'): AnsiblePlaybookInterface
554
    {
555
        $this->addParameter('-' . $verbose);
556
557
        return $this;
558
    }
559
560
    /**
561
     * Show program's version number and exit.
562
     *
563
     * @return AnsiblePlaybookInterface
564
     */
565
    public function version(): AnsiblePlaybookInterface
566
    {
567
        $this->addParameter('--version');
568
569
        return $this;
570
    }
571
572
    /**
573
     * clear the fact cache
574
     *
575
     * @return AnsiblePlaybookInterface
576
     */
577
    public function flushCache(): AnsiblePlaybookInterface
578
    {
579
        $this->addParameter('--flush-cache');
580
581
        return $this;
582
    }
583
584
    /**
585
     * the new vault identity to use for rekey
586
     *
587
     * @param string $vaultId
588
     * @return AnsiblePlaybookInterface
589
     */
590
    public function newVaultId(string $vaultId): AnsiblePlaybookInterface
591
    {
592
        $this->addOption('--new-vault-id', $vaultId);
593
594
        return $this;
595
    }
596
597
    /**
598
     * new vault password file for rekey
599
     *
600
     * @param string $passwordFile
601
     * @return AnsiblePlaybookInterface
602
     */
603
    public function newVaultPasswordFile(string $passwordFile): AnsiblePlaybookInterface
604
    {
605
        $this->addOption('--new-vault-password-file', $passwordFile);
606
607
        return $this;
608
    }
609
610
    /**
611
     * specify extra arguments to pass to scp only (e.g. -l)
612
     *
613
     * @param string|array $scpExtraArgs
614
     * @return AnsiblePlaybookInterface
615
     */
616
    public function scpExtraArgs($scpExtraArgs): AnsiblePlaybookInterface
617
    {
618
        $scpExtraArgs = $this->checkParam($scpExtraArgs, ',');
619
        $this->addOption('--scp-extra-args', $scpExtraArgs);
620
621
        return $this;
622
    }
623
624
    /**
625
     * specify extra arguments to pass to sftp only (e.g. -f, -l)
626
     *
627
     * @param string|array $sftpExtraArgs
628
     * @return AnsiblePlaybookInterface
629
     */
630
    public function sftpExtraArgs($sftpExtraArgs): AnsiblePlaybookInterface
631
    {
632
        $sftpExtraArgs = $this->checkParam($sftpExtraArgs, ',');
633
        $this->addOption('--sftp-extra-args', $sftpExtraArgs);
634
635
        return $this;
636
    }
637
638
    /**
639
     * specify common arguments to pass to sftp/scp/ssh (e.g. ProxyCommand)
640
     *
641
     * @param string|array $sshArgs
642
     * @return AnsiblePlaybookInterface
643
     */
644
    public function sshCommonArgs($sshArgs): AnsiblePlaybookInterface
645
    {
646
        $sshArgs = $this->checkParam($sshArgs, ',');
647
        $this->addOption('--ssh-common-args', $sshArgs);
648
649
        return $this;
650
    }
651
652
    /**
653
     * specify extra arguments to pass to ssh only (e.g. -R)
654
     *
655
     * @param string|array $extraArgs
656
     * @return AnsiblePlaybookInterface
657
     */
658
    public function sshExtraArgs($extraArgs): AnsiblePlaybookInterface
659
    {
660
        $extraArgs = $this->checkParam($extraArgs, ',');
661
        $this->addOption('--ssh-extra-args', $extraArgs);
662
663
        return $this;
664
    }
665
666
    /**
667
     * the vault identity to use
668
     *
669
     * @param string $vaultId
670
     * @return AnsiblePlaybookInterface
671
     */
672
    public function vaultId(string $vaultId): AnsiblePlaybookInterface
673
    {
674
        $this->addOption('--vault-id', $vaultId);
675
676
        return $this;
677
    }
678
679
    /**
680
     * Get parameter string which will be used to call ansible.
681
     *
682
     * @param bool $asArray
683
     * @return string|array
684
     */
685
    public function getCommandlineArguments(bool $asArray = true)
686
    {
687
        $this->checkInventory();
688
689
        return $this->prepareArguments($asArray);
690
    }
691
692
    /**
693
     * @inheritDoc
694
     */
695
    public function rolesPath(string $path): AnsiblePlaybookInterface
696
    {
697
        if (empty($path))
698
            return $this;
699
700
        if (!file_exists($path))
701
            throw new InvalidArgumentException(sprintf('The path "%s" does not exist.', $path));
702
703
        $this->processBuilder->setEnv('ANSIBLE_ROLES_PATH', $path);
704
        return $this;
705
    }
706
707
    /**
708
     * @inheritDoc
709
     */
710
    public function hostKeyChecking(bool $enable = true): AnsiblePlaybookInterface
711
    {
712
        $enable ?
713
            $flag = 'True' :
714
            $flag = 'False';
715
716
        $this->processBuilder->setEnv('ANSIBLE_HOST_KEY_CHECKING', $flag);
717
        return $this;
718
    }
719
    
720
    /**
721
    * Ansible SSH pipelining option
722
    * https://docs.ansible.com/ansible/latest/reference_appendices/config.html#ansible-pipelining
723
    *
724
    * @param bool $enable
725
    **/
726
    public function sshPipelining(bool $enable = false): AnsiblePlaybookInterface
727
    {
728
        $enable ?
729
            $flag = 'True' :
730
            $flag = 'False';
731
732
        $this->processBuilder->setEnv('ANSIBLE_SSH_PIPELINING', $flag);
733
        return $this;
734
    }
735
736
    /**
737
     * If no inventory file is given, assume
738
     */
739
    private function checkInventory(): void
740
    {
741
        if (!$this->hasInventory) {
742
            $inventory = str_replace('.yml', '', $this->getBaseOptions());
743
            $this->inventoryFile($inventory);
744
        }
745
    }
746
}
747