Passed
Pull Request — master (#59)
by
unknown
01:26
created

AnsiblePlaybook::execute()   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
     * @inheritDoc
169
     */
170
    public function extraVars($extraVars=''): AnsiblePlaybookInterface
171
    {
172
        if (empty($extraVars))
173
            return $this;
174
175
        // Building the key=>value parameter
176
        if (is_array($extraVars)){
177
            $vars = [];
178
            foreach ($extraVars as $key => $value){
179
                $vars[] = sprintf('%s=%s', $key, $value);
180
            }
181
            $this->addOption('--extra-vars', implode(' ', $vars));
182
            return $this;
183
        }
184
185
        // Should we consider $extraVars as a JSON/YML file?
186
        if (@is_file($extraVars)) {
187
            $this->addOption('--extra-vars', sprintf('@"%s"', $extraVars));
188
            return $this;
189
        }
190
191
        // At this point, the only allowed type is string.
192
        if (!is_string($extraVars))
193
            throw new InvalidArgumentException(sprintf('Expected string|array, got "%s"', gettype($extraVars)));
194
195
196
        if (strpos($extraVars, '=') === false) {
197
            throw new InvalidArgumentException('The extra vars raw string should be in the "key=value" form.');
198
        }
199
200
        $this->addOption('--extra-vars', $extraVars);
201
        return $this;
202
    }
203
204
    /**
205
     * Run handlers even if a task fails.
206
     *
207
     * @return AnsiblePlaybookInterface
208
     */
209
    public function forceHandlers(): AnsiblePlaybookInterface
210
    {
211
        $this->addParameter('--force-handlers');
212
213
        return $this;
214
    }
215
216
    /**
217
     * Specify number of parallel processes to use (default=5).
218
     *
219
     * @param int $forks
220
     * @return AnsiblePlaybookInterface
221
     */
222
    public function forks(int $forks = 5): AnsiblePlaybookInterface
223
    {
224
        $this->addOption('--forks', $forks);
225
226
        return $this;
227
    }
228
229
    /**
230
     * Show help message and exit.
231
     *
232
     * @return AnsiblePlaybookInterface
233
     */
234
    public function help(): AnsiblePlaybookInterface
235
    {
236
        $this->addParameter('--help');
237
238
        return $this;
239
    }
240
241
    /**
242
     * Specify inventory host file (default=/etc/ansible/hosts).
243
     *
244
     * @param string $inventory filename for hosts file
245
     * @return AnsiblePlaybookInterface
246
     */
247
    public function inventoryFile(string $inventory = '/etc/ansible/hosts'): AnsiblePlaybookInterface
248
    {
249
        $this->addOption('--inventory-file', $inventory);
250
        $this->hasInventory = true;
251
252
        return $this;
253
    }
254
255
    /**
256
     * @inheritDoc
257
     */
258
    public function inventory(array $hosts = []): AnsiblePlaybookInterface
259
    {
260
        if (empty($hosts))
261
            return $this;
262
263
        // In order to let ansible-playbook understand that the given option is a list of hosts, the list must end by
264
        // comma "," if it contains just an entry. For example, supposing just a single host, "localhosts":
265
        //
266
        //   Wrong: --inventory="locahost"
267
        // Correct: --inventory="locahost,"
268
        $hostList = implode(', ', $hosts);
269
270
        if (count($hosts) === 1)
271
            $hostList .= ',';
272
273
        $this->addOption('--inventory', sprintf('"%s"', $hostList));
274
        $this->hasInventory = true;
275
276
        return $this;
277
    }
278
279
    /**
280
     * Further limit selected hosts to an additional pattern.
281
     *
282
     * @param array|string $subset list of hosts
283
     * @return AnsiblePlaybookInterface
284
     */
285
    public function limit($subset = ''): AnsiblePlaybookInterface
286
    {
287
        $subset = $this->checkParam($subset, ',');
288
289
        $this->addOption('--limit', $subset);
290
291
        return $this;
292
    }
293
294
    /**
295
     * Outputs a list of matching hosts; does not execute anything else.
296
     *
297
     * @return AnsiblePlaybookInterface
298
     */
299
    public function listHosts(): AnsiblePlaybookInterface
300
    {
301
        $this->addParameter('--list-hosts');
302
303
        return $this;
304
    }
305
306
    /**
307
     * List all tasks that would be executed.
308
     *
309
     * @return AnsiblePlaybookInterface
310
     */
311
    public function listTasks(): AnsiblePlaybookInterface
312
    {
313
        $this->addParameter('--list-tasks');
314
315
        return $this;
316
    }
317
318
    /**
319
     * Specify path(s) to module library (default=/usr/share/ansible/).
320
     *
321
     * @param array $path list of paths for modules
322
     * @return AnsiblePlaybookInterface
323
     */
324
    public function modulePath(array $path = ['/usr/share/ansible/']): AnsiblePlaybookInterface
325
    {
326
        $this->addOption('--module-path', implode(',', $path));
327
328
        return $this;
329
    }
330
331
    /**
332
     * Disable cowsay
333
     *
334
     * @codeCoverageIgnore
335
     * @return AnsiblePlaybookInterface
336
     */
337
    public function noCows(): AnsiblePlaybookInterface
338
    {
339
        $this->processBuilder->setEnv('ANSIBLE_NOCOWS', 1);
340
341
        return $this;
342
    }
343
344
    /**
345
     * Enable/Disable Colors
346
     *
347
     * @param bool $colors
348
     * @return AnsiblePlaybookInterface
349
     */
350
    public function colors(bool $colors = true): AnsiblePlaybookInterface
351
    {
352
        $this->processBuilder->setEnv('ANSIBLE_FORCE_COLOR', intval($colors));
353
354
        return $this;
355
    }
356
357
    /**
358
     * Use this file to authenticate the connection.
359
     *
360
     * @param string $file private key file
361
     * @return AnsiblePlaybookInterface
362
     */
363
    public function privateKey(string $file): AnsiblePlaybookInterface
364
    {
365
        $this->addOption('--private-key', $file);
366
367
        return $this;
368
    }
369
370
    /**
371
     * Only run plays and tasks whose tags do not match these values.
372
     *
373
     * @param array|string $tags list of tags to skip
374
     * @return AnsiblePlaybookInterface
375
     */
376
    public function skipTags($tags = ''): AnsiblePlaybookInterface
377
    {
378
        $tags = $this->checkParam($tags, ',');
379
        $this->addOption('--skip-tags', $tags);
380
381
        return $this;
382
    }
383
384
    /**
385
     * Start the playbook at the task matching this name.
386
     *
387
     * @param string $task name of task
388
     * @return AnsiblePlaybookInterface
389
     */
390
    public function startAtTask(string $task): AnsiblePlaybookInterface
391
    {
392
        $this->addOption('--start-at-task', $task);
393
394
        return $this;
395
    }
396
397
    /**
398
     * One-step-at-a-time: confirm each task before running.
399
     *
400
     * @return AnsiblePlaybookInterface
401
     */
402
    public function step(): AnsiblePlaybookInterface
403
    {
404
        $this->addParameter('--step');
405
406
        return $this;
407
    }
408
409
    /**
410
     * Run operations with su.
411
     *
412
     * @return AnsiblePlaybookInterface
413
     */
414
    public function su(): AnsiblePlaybookInterface
415
    {
416
        $this->addParameter('--su');
417
418
        return $this;
419
    }
420
421
    /**
422
     * Run operations with su as this user (default=root).
423
     *
424
     * @param string $user
425
     * @return AnsiblePlaybookInterface
426
     */
427
    public function suUser(string $user = 'root'): AnsiblePlaybookInterface
428
    {
429
        $this->addOption('--su-user', $user);
430
431
        return $this;
432
    }
433
434
    /**
435
     * Perform a syntax check on the playbook, but do not execute it.
436
     *
437
     * @return AnsiblePlaybookInterface
438
     */
439
    public function syntaxCheck(): AnsiblePlaybookInterface
440
    {
441
        $this->addParameter('--syntax-check');
442
443
        return $this;
444
    }
445
446
    /**
447
     * Only run plays and tasks tagged with these values.
448
     *
449
     * @param string|array $tags list of tags
450
     * @return AnsiblePlaybookInterface
451
     */
452
    public function tags($tags): AnsiblePlaybookInterface
453
    {
454
        $tags = $this->checkParam($tags, ',');
455
        $this->addOption('--tags', $tags);
456
457
        return $this;
458
    }
459
460
    /**
461
     * Override the SSH timeout in seconds (default=10).
462
     *
463
     * @param int $timeout
464
     * @return AnsiblePlaybookInterface
465
     */
466
    public function timeout(int $timeout = 10): AnsiblePlaybookInterface
467
    {
468
        $this->addOption('--timeout', $timeout);
469
470
        return $this;
471
    }
472
473
    /**
474
     * Connect as this user.
475
     *
476
     * @param string $user
477
     * @return AnsiblePlaybookInterface
478
     */
479
    public function user(string $user): AnsiblePlaybookInterface
480
    {
481
        $this->addOption('--user', $user);
482
483
        return $this;
484
    }
485
486
    /**
487
     * Vault password file.
488
     *
489
     * @param string $file
490
     * @return AnsiblePlaybookInterface
491
     */
492
    public function vaultPasswordFile(string $file): AnsiblePlaybookInterface
493
    {
494
        $this->addoption('--vault-password-file', $file);
495
496
        return $this;
497
    }
498
499
    /**
500
     * Verbose mode (vvv for more, vvvv to enable connection debugging).
501
     *
502
     * @param string $verbose
503
     * @return AnsiblePlaybookInterface
504
     */
505
    public function verbose(string $verbose = 'v'): AnsiblePlaybookInterface
506
    {
507
        $this->addParameter('-' . $verbose);
508
509
        return $this;
510
    }
511
512
    /**
513
     * Show program's version number and exit.
514
     *
515
     * @return AnsiblePlaybookInterface
516
     */
517
    public function version(): AnsiblePlaybookInterface
518
    {
519
        $this->addParameter('--version');
520
521
        return $this;
522
    }
523
524
    /**
525
     * clear the fact cache
526
     *
527
     * @return AnsiblePlaybookInterface
528
     */
529
    public function flushCache(): AnsiblePlaybookInterface
530
    {
531
         $this->addParameter('--flush-cache');
532
533
         return $this;
534
    }
535
536
    /**
537
     * the new vault identity to use for rekey
538
     *
539
     * @param string $vaultId
540
     * @return AnsiblePlaybookInterface
541
     */
542
    public function newVaultId(string $vaultId): AnsiblePlaybookInterface
543
    {
544
        $this->addOption('--new-vault-id', $vaultId);
545
546
        return $this;
547
    }
548
549
    /**
550
     * new vault password file for rekey
551
     *
552
     * @param string $passwordFile
553
     * @return AnsiblePlaybookInterface
554
     */
555
    public function newVaultPasswordFile(string $passwordFile): AnsiblePlaybookInterface
556
    {
557
        $this->addOption('--new-vault-password-file', $passwordFile);
558
559
        return $this;
560
    }
561
562
    /**
563
     * specify extra arguments to pass to scp only (e.g. -l)
564
     *
565
     * @param string|array $scpExtraArgs
566
     * @return AnsiblePlaybookInterface
567
     */
568
    public function scpExtraArgs($scpExtraArgs): AnsiblePlaybookInterface
569
    {
570
        $scpExtraArgs = $this->checkParam($scpExtraArgs, ',');
571
        $this->addOption('--scp-extra-args', $scpExtraArgs);
572
573
        return $this;
574
    }
575
576
    /**
577
     * specify extra arguments to pass to sftp only (e.g. -f, -l)
578
     *
579
     * @param string|array $sftpExtraArgs
580
     * @return AnsiblePlaybookInterface
581
     */
582
    public function sftpExtraArgs($sftpExtraArgs): AnsiblePlaybookInterface
583
    {
584
        $sftpExtraArgs = $this->checkParam($sftpExtraArgs, ',');
585
        $this->addOption('--sftp-extra-args', $sftpExtraArgs);
586
587
        return $this;
588
    }
589
590
    /**
591
     * specify common arguments to pass to sftp/scp/ssh (e.g. ProxyCommand)
592
     *
593
     * @param string|array $sshArgs
594
     * @return AnsiblePlaybookInterface
595
     */
596
    public function sshCommonArgs($sshArgs): AnsiblePlaybookInterface
597
    {
598
        $sshArgs = $this->checkParam($sshArgs, ',');
599
        $this->addOption('--ssh-common-args', $sshArgs);
600
601
        return $this;
602
    }
603
604
    /**
605
     * specify extra arguments to pass to ssh only (e.g. -R)
606
     *
607
     * @param string|array $extraArgs
608
     * @return AnsiblePlaybookInterface
609
     */
610
    public function sshExtraArgs($extraArgs): AnsiblePlaybookInterface
611
    {
612
        $extraArgs = $this->checkParam($extraArgs, ',');
613
        $this->addOption('--ssh-extra-args', $extraArgs);
614
615
        return $this;
616
    }
617
618
    /**
619
     * the vault identity to use
620
     *
621
     * @param string $vaultId
622
     * @return AnsiblePlaybookInterface
623
     */
624
    public function vaultId(string $vaultId): AnsiblePlaybookInterface
625
    {
626
        $this->addOption('--vault-id', $vaultId);
627
628
        return $this;
629
    }
630
631
    /**
632
     * Get parameter string which will be used to call ansible.
633
     *
634
     * @param bool $asArray
635
     * @return string|array
636
     */
637
    public function getCommandlineArguments(bool $asArray = true)
638
    {
639
        $this->checkInventory();
640
641
        return $this->prepareArguments($asArray);
642
    }
643
644
    /**
645
     * @inheritDoc
646
     */
647
    public function rolesPath(string $path): AnsiblePlaybookInterface
648
    {
649
        if (empty($path))
650
            return $this;
651
652
        if (!file_exists($path))
653
            throw new InvalidArgumentException(sprintf('The path "%s" does not exist.', $path));
654
655
        $this->processBuilder->setEnv('ANSIBLE_ROLES_PATH', $path);
656
        return $this;
657
    }
658
659
    /**
660
     * @inheritDoc
661
     */
662
    public function hostKeyChecking(bool $enable = true): AnsiblePlaybookInterface
663
    {
664
        $enable ?
665
            $flag = 'True' :
666
            $flag = 'False';
667
668
        $this->processBuilder->setEnv('ANSIBLE_HOST_KEY_CHECKING', $flag);
669
        return $this;
670
    }
671
672
    /**
673
     * If no inventory file is given, assume
674
     */
675
    private function checkInventory(): void
676
    {
677
        if (!$this->hasInventory) {
678
            $inventory = str_replace('.yml', '', $this->getBaseOptions());
679
            $this->inventoryFile($inventory);
680
        }
681
    }
682
}
683