GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

AnsibleProvisioner::provisionHosts()   D
last analyzed

Complexity

Conditions 13
Paths 90

Size

Total Lines 99
Code Lines 41

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 99
rs 4.9922
cc 13
eloc 41
nc 90
nop 2

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * Copyright (c) 2011-present Mediasift Ltd
5
 * All rights reserved.
6
 *
7
 * Redistribution and use in source and binary forms, with or without
8
 * modification, are permitted provided that the following conditions
9
 * are met:
10
 *
11
 *   * Redistributions of source code must retain the above copyright
12
 *     notice, this list of conditions and the following disclaimer.
13
 *
14
 *   * Redistributions in binary form must reproduce the above copyright
15
 *     notice, this list of conditions and the following disclaimer in
16
 *     the documentation and/or other materials provided with the
17
 *     distribution.
18
 *
19
 *   * Neither the names of the copyright holders nor the names of his
20
 *     contributors may be used to endorse or promote products derived
21
 *     from this software without specific prior written permission.
22
 *
23
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
26
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
27
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
28
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
29
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
31
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
33
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34
 * POSSIBILITY OF SUCH DAMAGE.
35
 *
36
 * @category  Libraries
37
 * @package   Storyplayer/ProvisioningLib
38
 * @author    Stuart Herbert <[email protected]>
39
 * @copyright 2011-present Mediasift Ltd www.datasift.com
40
 * @license   http://www.opensource.org/licenses/bsd-license.php  BSD License
41
 * @link      http://datasift.github.io/storyplayer
42
 */
43
44
namespace DataSift\Storyplayer\ProvisioningLib\Provisioners;
45
46
use DataSift\Storyplayer\CommandLib\CommandResult;
47
use DataSift\Storyplayer\CommandLib\CommandRunner;
48
use DataSift\Storyplayer\PlayerLib\StoryTeller;
49
use DataSift\Storyplayer\ProvisioningLib\ProvisioningDefinition;
50
use Storyplayer\SPv2\Modules\Exceptions;
51
use Storyplayer\SPv2\Modules\Log;
52
53
/**
54
 * support for provisioning via Ansible
55
 *
56
 * @category  Libraries
57
 * @package   Storyplayer/ProvisioningLib
58
 * @author    Stuart Herbert <[email protected]>
59
 * @copyright 2011-present Mediasift Ltd www.datasift.com
60
 * @license   http://www.opensource.org/licenses/bsd-license.php  BSD License
61
 * @link      http://datasift.github.io/storyplayer
62
 */
63
class AnsibleProvisioner extends Provisioner
64
{
65
    public function __construct(StoryTeller $st)
66
    {
67
        // remember for the future
68
        $this->st = $st;
69
    }
70
71 View Code Duplication
    public function buildDefinitionFor($env)
72
    {
73
        // our return value
74
        $provDef = new ProvisioningDefinition;
75
76
        // what are we doing?
77
        $log = Log::usingLog()->startAction("build Ansible provisioning definition");
78
79
        // add in each machine in the environment
80
        foreach ($env->details->machines as $hostId => $machine) {
81
            usingProvisioningDefinition($provDef)->addHost($hostId);
82
83
            foreach ($machine->roles as $role) {
84
                usingProvisioningDefinition($provDef)->addRole($role)->toHost($hostId);
85
            }
86
87
            if (isset($machine->params)) {
88
                $params = [];
89
                foreach ($machine->params as $paramName => $paramValue) {
90
                    $params[$paramName] = fromConfig()->get('hosts.' . $hostId . '.params.'.$paramName);
91
                }
92
                if (count($params)) {
93
                    usingProvisioningDefinition($provDef)->addParams($params)->toHost($hostId);
94
                }
95
            }
96
        }
97
98
        // all done
99
        $log->endAction($provDef);
100
        return $provDef;
101
    }
102
103
    public function provisionHosts(ProvisioningDefinition $hosts, $provConf)
104
    {
105
        // what are we doing?
106
        $log = Log::usingLog()->startAction("use Ansible to provision host(s)");
107
108
        // get our ansible configuration
109
        $ansibleSettings = fromConfig()->getModuleSetting('ansible');
110
111
        // our reverse list of roles => hosts
112
        $rolesToHosts = array();
113
114
        // build up the list of roles
115
        foreach($hosts as $hostId => $hostProps) {
116
            // what is the host's IP address?
117
            $ipAddress = fromHost($hostId)->getIpAddress();
118
119
            // add the host to the required roles
120
            if (isset($hostProps->roles)) {
121
                foreach ($hostProps->roles as $role) {
122
                    if (!isset($rolesToHosts[$role])) {
123
                        $rolesToHosts[$role] = array();
124
                    }
125
                    $rolesToHosts[$role][] = $ipAddress;
126
                }
127
            }
128
        }
129
130
        // at this point, we know which roles need applying to which hosts
131
        //
132
        // build up the inventory file
133
        $inventory = "";
134
        foreach ($rolesToHosts as $role => $hostsForRole) {
135
            // add the role marker
136
            $inventory .= "[{$role}]" . PHP_EOL;
137
138
            // add the list of hosts
139
            foreach ($hostsForRole as $host) {
140
                $inventory .= $host . PHP_EOL;
141
            }
142
143
            // add an extra blank line for readability
144
            $inventory .= PHP_EOL;
145
        }
146
147
        // write out the inventory
148
        $inventoryFile = $this->writeInventoryFile($inventory);
149
150
        // where should we create the host_vars?
151
        $inventoryFolder = dirname($inventoryFile);
152
153
        // we set these in the foreach() loop
154
        $sshUsername = null;
155
        $sshKeyFile  = null;
156
157
        // now we need to write out the host files
158
        foreach($hosts as $hostId => $hostProps) {
159
            // what is the host's IP address?
160
            $ipAddress   = fromHost($hostId)->getIpAddress();
161
            $sshUsername = fromHost($hostId)->getSshUsername();
162
            $sshKeyFile  = fromHost($hostId)->getSshKeyFile();
163
164
            // do we have any vars to write?
165
            if (!isset($hostProps->params) || $hostProps->params === null || (is_array($hostProps) && count($hostProps) == 0)) {
166
                // we'd better remove any host_vars file that exists,
167
                // in case what's there (if anything) is left over from
168
                // a different test run
169
                $this->removeHostVarsFile($inventoryFolder, $ipAddress);
170
            }
171
            else {
172
                // write the host vars file
173
                $this->writeHostVarsFile($inventoryFolder, $ipAddress, $hostProps->params);
174
            }
175
        }
176
177
        // build the command for Ansible
178
        $command = 'ansible-playbook -i "' . $inventoryFile . '"'
179
                 . ' "--private-key=' . $sshKeyFile . '"'
180
                 . ' "--user=' . $sshUsername . '"';
181
182
        $command .= ' "' . $ansibleSettings->dir . DIRECTORY_SEPARATOR . $ansibleSettings->playbook . '"';
183
184
        // let's run the command
185
        //
186
        // this looks like a hack, but it is the only way to work with Ansible
187
        // if there's an ansible.cfg in the root of the playbook :(
188
        $cwd = getcwd();
189
        chdir($ansibleSettings->dir);
190
        $commandRunner = new CommandRunner();
191
        $result = $commandRunner->runSilently($command);
192
        chdir($cwd);
193
194
        // what happened?
195
        if (!$result->didCommandSucceed()) {
196
            throw Exceptions::newActionFailedException(__METHOD__, "provisioning failed");
197
        }
198
199
        // all done
200
        $log->endAction();
201
    }
202
203
    /**
204
     * @param string $inventoryFolder
205
     */
206
    protected function removeHostVarsFile($inventoryFolder, $ipAddress)
207
    {
208
        // what are we doing?
209
        $log = Log::usingLog()->startAction("remove host_vars file for '{$ipAddress}'");
210
211
        // what is the path to the file?
212
        $filename = $this->getHostVarsFilename($inventoryFolder, $ipAddress);
213
214
        // remove the file
215
        if (file_exists($filename)) {
216
            unlink($filename);
217
            $log->endAction("removed file '{$filename}'");
218
        }
219
        else {
220
            $log->endAction("no file to remove; skipping");
221
        }
222
223
        // all done
224
    }
225
226
    /**
227
     * @param string $inventoryFolder
228
     */
229
    protected function writeHostVarsFile($inventoryFolder, $ipAddress, $vars)
230
    {
231
        // what are we doing?
232
        $log = Log::usingLog()->startAction("write host_vars file for '{$ipAddress}'");
233
234
        // what is the path to the file?
235
        $filename = $this->getHostVarsFilename($inventoryFolder, $ipAddress);
236
237
        // does the target folder exist?
238
        $hostVarsFolder = dirname($filename);
239
        if (!file_exists($hostVarsFolder)) {
240
            mkdir ($hostVarsFolder);
241
        }
242
243
        // write the data
244
        usingYamlFile($filename)->writeDataToFile($vars);
245
246
        // all done
247
        $log->endAction("written to file '{$filename}'");
248
    }
249
250
    /**
251
     * @param string $inventory
252
     *
253
     * @return string
254
     */
255
    protected function writeInventoryFile($inventory)
256
    {
257
        // what are we doing?
258
        $log = Log::usingLog()->startAction("write temporary inventory file");
259
260
        // what are we going to call the inventory file?
261
        $filename = fromFile()->getTmpFilename();
262
263
        // write the data
264
        file_put_contents($filename, $inventory);
265
266
        // all done
267
        $log->endAction("written to file '{$filename}'");
268
        return $filename;
269
    }
270
271
    /**
272
     * @param string $inventoryFolder
273
     */
274
    protected function getHostVarsFilename($inventoryFolder, $hostId)
275
    {
276
        // what are we doing?
277
        $log = Log::usingLog()->startAction("determine host_vars filename for host '{$hostId}'");
278
279
        // get our ansible settings
280
        $ansibleSettings = fromConfig()->getModuleSetting('ansible');
281
282
        // get our inventory folder
283
        $invFolder = $this->getInventoryFolder($ansibleSettings, $inventoryFolder);
284
285
        // what is the path to the file?
286
        $filename = $invFolder . DIRECTORY_SEPARATOR . 'host_vars' . DIRECTORY_SEPARATOR . $hostId;
287
288
        // all done
289
        $log->endAction("filename is: " . $filename);
290
        return $filename;
291
    }
292
293
    /**
294
     * @param string $inventoryFolder
295
     */
296
    protected function getInventoryFolder($ansibleSettings, $inventoryFolder)
297
    {
298
        // is there an Ansible.cfg file?
299
        $cfgFile = $ansibleSettings->dir . DIRECTORY_SEPARATOR . 'ansible.cfg';
300
301
        if (!file_exists($cfgFile)) {
302
            return $inventoryFolder;
303
        }
304
305
        // if we get here, there is a config file to parse
306
        $ansibleCfg = parse_ini_file($cfgFile, true);
307
308
        if (!is_array($ansibleCfg)) {
309
            // we can't parse the file
310
            return $inventoryFolder;
311
        }
312
313
        if (!isset($ansibleCfg['defaults'], $ansibleCfg['defaults']['hostfile'])) {
314
            // there's no inventory in the config file
315
            return $inventoryFolder;
316
        }
317
318
        // is the inventory a folder?
319
        $invDir = $inventoryFolder . DIRECTORY_SEPARATOR . $ansibleCfg['defaults']['hostfile'];
320
        if (is_dir($invDir)) {
321
            // this is where we need to write our variables to
322
            return $invDir;
323
        }
324
325
        // give up
326
        return $inventoryFolder;
327
    }
328
}
329