Passed
Pull Request — master (#50)
by
unknown
01:19
created

Ansible   A

Complexity

Total Complexity 18

Size/Duplication

Total Lines 155
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 40
dl 0
loc 155
rs 10
c 0
b 0
f 0
wmc 18

8 Methods

Rating   Name   Duplication   Size   Complexity  
A createProcess() 0 5 1
A setTimeout() 0 5 1
A isExecutable() 0 14 5
A checkDir() 0 7 2
A checkCommand() 0 25 6
A galaxy() 0 4 1
A playbook() 0 4 1
A __construct() 0 10 1
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;
12
13
use Asm\Ansible\Command\AnsibleGalaxy;
14
use Asm\Ansible\Command\AnsibleGalaxyInterface;
15
use Asm\Ansible\Command\AnsiblePlaybook;
16
use Asm\Ansible\Command\AnsiblePlaybookInterface;
17
use Asm\Ansible\Exception\CommandException;
18
use Asm\Ansible\Process\ProcessBuilder;
19
use Asm\Ansible\Process\ProcessBuilderInterface;
20
use Asm\Ansible\Utils\Env;
21
22
/**
23
 * Ansible command factory
24
 *
25
 * @package Asm\Ansible
26
 * @author Marc Aschmann <[email protected]>
27
 */
28
final class Ansible
29
{
30
31
    const DEFAULT_TIMEOUT = 300;
32
33
    /**
34
     * @var string
35
     */
36
    private $playbookCommand;
37
38
    /**
39
     * @var string
40
     */
41
    private $galaxyCommand;
42
43
    /**
44
     * @var string
45
     */
46
    private $ansibleBaseDir;
47
48
    /**
49
     * @var integer
50
     */
51
    private $timeout;
52
53
    /**
54
     * @param string $ansibleBaseDir base directory of ansible project structure
55
     * @param string $playbookCommand path to playbook executable, default ansible-playbook
56
     * @param string $galaxyCommand path to galaxy executable, default ansible-galaxy
57
     */
58
    public function __construct(
59
        string $ansibleBaseDir,
60
        string $playbookCommand = '',
61
        string $galaxyCommand = ''
62
    ) {
63
        $this->ansibleBaseDir = $this->checkDir($ansibleBaseDir);
64
        $this->playbookCommand = $this->checkCommand($playbookCommand, 'ansible-playbook');
65
        $this->galaxyCommand = $this->checkCommand($galaxyCommand, 'ansible-galaxy');
66
67
        $this->timeout = Ansible::DEFAULT_TIMEOUT;
68
    }
69
70
    /**
71
     * AnsiblePlaybook instance creator
72
     *
73
     * @return AnsiblePlaybookInterface
74
     */
75
    public function playbook(): AnsiblePlaybookInterface
76
    {
77
        return new AnsiblePlaybook(
78
            $this->createProcess($this->playbookCommand)
79
        );
80
    }
81
82
    /**
83
     * AnsibleGalaxy instance creator
84
     *
85
     * @return AnsibleGalaxyInterface
86
     */
87
    public function galaxy(): AnsibleGalaxyInterface
88
    {
89
        return new AnsibleGalaxy(
90
            $this->createProcess($this->galaxyCommand)
91
        );
92
    }
93
94
    /**
95
     * Set process timeout in seconds.
96
     *
97
     * @param int $timeout
98
     * @return Ansible
99
     */
100
    public function setTimeout(int $timeout): Ansible
101
    {
102
        $this->timeout = $timeout;
103
104
        return $this;
105
    }
106
107
    /**
108
     * @param string $prefix base command
109
     * @return ProcessBuilderInterface
110
     */
111
    private function createProcess(string  $prefix): ProcessBuilderInterface
112
    {
113
        $process = new ProcessBuilder($prefix, $this->ansibleBaseDir);
114
115
        return $process->setTimeout($this->timeout);
116
    }
117
118
    /**
119
     * @param string $command
120
     * @param string $default
121
     * @return string
122
     * @throws CommandException
123
     */
124
    private function checkCommand(string $command, string $default): string
125
    {
126
        // normally ansible is in /usr/local/bin/*
127
        if (empty($command)) {
128
            if (Env::isWindows())
129
                return $default;
130
131
            // not testable without ansible installation
132
            if (null === shell_exec('which ' . $default)) {
133
                throw new CommandException(sprintf('No "%s" executable present in PATH!', $default));
134
            }
135
136
            return $default;
137
        }
138
139
        // Here: we have a given command, just need to check it exists and it's executable
140
        if (!is_file($command)) {
141
            throw new CommandException(sprintf('Command "%s" does not exist!', $command));
142
        }
143
144
        if (!$this->isExecutable($command)) {
145
            throw new CommandException(sprintf('Command "%s" is not executable!', $command));
146
        }
147
148
        return $command;
149
    }
150
151
    /**
152
     * @param string $dir directory to check
153
     * @return string
154
     * @throws CommandException
155
     */
156
    private function checkDir(string $dir): string
157
    {
158
        if (!is_dir($dir)) {
159
            throw new CommandException('Ansible project root ' . $dir . ' not found!');
160
        }
161
162
        return $dir;
163
    }
164
165
    /**
166
     * @param string $command
167
     * @return bool
168
     */
169
    private function isExecutable($command): bool
170
    {
171
        if (empty($command))
172
            return false;
173
174
        if (!Env::isWindows())
175
            return is_executable($command);
176
177
        foreach (['exe', 'com','bat','cmd','ps1'] as $ext){
178
            if (strtolower(substr($command, -3,3))=== $ext)
179
                return true;
180
        }
181
182
        return false;
183
    }
184
}
185