Passed
Push — code-quality-badges ( 1c308c...b32c3e )
by Christian
02:02 queued 26s
created

InstallCommand::__construct()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 27
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 13
nc 1
nop 12
dl 0
loc 27
rs 8.8571
c 0
b 0
f 0

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php declare(strict_types=1);
2
3
namespace Cocotte\Command;
4
5
use Cocotte\Console\AbstractCommand;
6
use Cocotte\Console\DocumentedCommand;
7
use Cocotte\Console\Style;
8
use Cocotte\DigitalOcean\ApiToken;
9
use Cocotte\DigitalOcean\ApiTokenOptionProvider;
10
use Cocotte\DigitalOcean\NetworkingConfigurator;
11
use Cocotte\Environment\LazyEnvironment;
12
use Cocotte\Help\DefaultExamples;
13
use Cocotte\Help\FromEnvExamples;
14
use Cocotte\Host\HostMount;
15
use Cocotte\Host\HostMountRequired;
16
use Cocotte\Machine\MachineCreator;
17
use Cocotte\Machine\MachineIp;
18
use Cocotte\Machine\MachineName;
19
use Cocotte\Machine\MachineNameOptionProvider;
20
use Cocotte\Machine\MachineStoragePath;
21
use Cocotte\Shell\ProcessRunner;
22
use Cocotte\Template\Traefik\TraefikCreator;
23
use Cocotte\Template\Traefik\TraefikDeploymentValidator;
24
use Cocotte\Template\Traefik\TraefikHostname;
25
use Cocotte\Template\Traefik\TraefikHostnameOptionProvider;
26
use Cocotte\Template\Traefik\TraefikPassword;
27
use Cocotte\Template\Traefik\TraefikPasswordOptionProvider;
28
use Cocotte\Template\Traefik\TraefikUsername;
29
use Cocotte\Template\Traefik\TraefikUsernameOptionProvider;
30
use Symfony\Component\Console\Input\InputInterface;
31
use Symfony\Component\Console\Input\InputOption;
32
use Symfony\Component\Console\Output\OutputInterface;
33
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
34
use Symfony\Component\Process\Process;
35
36
final class InstallCommand extends AbstractCommand implements LazyEnvironment, HostMountRequired, DocumentedCommand
37
{
38
    /**
39
     * @var MachineCreator
40
     */
41
    private $machineCreator;
42
43
    /**
44
     * @var TraefikCreator
45
     */
46
    private $traefikCreator;
47
48
    /**
49
     * @var Style
50
     */
51
    private $style;
52
53
    /**
54
     * @var MachineName
55
     */
56
    private $machineName;
57
58
    /**
59
     * @var TraefikHostname
60
     */
61
    private $traefikHostname;
62
63
    /**
64
     * @var EventDispatcherInterface
65
     */
66
    private $eventDispatcher;
67
68
    /**
69
     * @var NetworkingConfigurator
70
     */
71
    private $networkingConfigurator;
72
73
    /**
74
     * @var ProcessRunner
75
     */
76
    private $processRunner;
77
78
    /**
79
     * @var HostMount
80
     */
81
    private $hostMount;
82
83
    /**
84
     * @var TraefikDeploymentValidator
85
     */
86
    private $traefikDeploymentValidator;
87
88
    /**
89
     * @var MachineIp
90
     */
91
    private $machineIp;
92
    /**
93
     * @var FromEnvExamples
94
     */
95
    private $fromEnvExamples;
96
97
    public function __construct(
98
        MachineCreator $machineCreator,
99
        TraefikCreator $traefikCreator,
100
        Style $style,
101
        MachineName $machineName,
102
        TraefikHostname $traefikHostname,
103
        EventDispatcherInterface $eventDispatcher,
104
        NetworkingConfigurator $networkingConfigurator,
105
        ProcessRunner $processRunner,
106
        HostMount $hostMount,
107
        TraefikDeploymentValidator $traefikDeploymentValidator,
108
        MachineIp $machineIp,
109
        FromEnvExamples $fromEnvExamples
110
    ) {
111
        $this->machineCreator = $machineCreator;
112
        $this->traefikCreator = $traefikCreator;
113
        $this->style = $style;
114
        $this->machineName = $machineName;
115
        $this->traefikHostname = $traefikHostname;
116
        $this->eventDispatcher = $eventDispatcher;
117
        $this->networkingConfigurator = $networkingConfigurator;
118
        $this->processRunner = $processRunner;
119
        $this->hostMount = $hostMount;
120
        $this->traefikDeploymentValidator = $traefikDeploymentValidator;
121
        $this->machineIp = $machineIp;
122
        $this->fromEnvExamples = $fromEnvExamples;
123
        parent::__construct();
124
    }
125
126
    public function lazyEnvironmentValues(): array
127
    {
128
        return [
129
            ApiToken::class,
130
            MachineName::class,
131
            MachineStoragePath::class,
132
            TraefikHostname::class,
133
            TraefikPassword::class,
134
            TraefikUsername::class,
135
        ];
136
    }
137
138
    public function optionProviders(): array
139
    {
140
        return [
141
            ApiTokenOptionProvider::class,
142
            MachineNameOptionProvider::class,
143
            TraefikHostnameOptionProvider::class,
144
            TraefikUsernameOptionProvider::class,
145
            TraefikPasswordOptionProvider::class,
146
        ];
147
    }
148
149
    protected function eventDispatcher(): EventDispatcherInterface
150
    {
151
        return $this->eventDispatcher;
152
    }
153
154
    protected function doConfigure(): void
155
    {
156
        $this->setName('install')
157
            ->addOption('dry-run',
158
                null,
159
                InputOption::VALUE_NONE,
160
                'Validate all options but do not proceed with installation.')
161
            ->setDescription($this->description())
162
            ->setHelp(
163
                $this->formatHelp($this->description(),
164
                    (new DefaultExamples)->install(),
165
                    (new DefaultExamples)->installInteractive()
166
                )
167
            );
168
    }
169
170
    protected function doExecute(InputInterface $input, OutputInterface $output)
171
    {
172
        if ($input->getOption('dry-run')) {
173
            $this->style->writeln(
174
                "Would have created a Docker machine named '{$this->machineName}' on Digital Ocean."
175
            );
176
177
            return;
178
        }
179
180
        $this->confirm();
181
        $this->createMachine();
182
        $this->createTraefikTemplate();
183
        $this->configureNetworking();
184
        $this->deployTraefik();
185
        $this->waitForTraefikReady();
186
187
        $this->processRunner->mustRun(new Process('./bin/logs -t', $this->traefikCreator->hostAppPath()));
188
189
        $this->style->complete($this->completeMessage());
190
        $this->style->writeln($this->command());
191
    }
192
193
    private function confirm(): void
194
    {
195
        if (!$this->style->confirm(
196
            "You are about to create a Docker machine named '<options=bold>{$this->machineName->toString()}</>' on Digital Ocean \n".
197
            " and install the Traefik reverse proxy on it with hostname '<options=bold>{$this->traefikHostname->toString()}</>'.\n".
198
            " This action may take a few minutes."
199
        )) {
200
            throw new \Exception('Cancelled');
201
        };
202
    }
203
204
    private function description(): string
205
    {
206
        return 'Create a <options=bold>Docker</> machine on <options=bold>Digital Ocean</> and '.
207
            'install the <options=bold>Traefik</> reverse proxy on it.';
208
    }
209
210
    private function completeMessage(): array
211
    {
212
        return [
213
            "Installation successful.",
214
            "You can now:\n".
215
            "- Visit your Traefik UI at <options=bold>https://{$this->traefikHostname->toString()}</>\n".
216
            "- Use docker-machine commands (e.g. <options=bold>docker-machine -s machine ssh {$this->machineName}</>)\n".
217
            "- Deploy a static website to your cloud machine with the command below.",
218
        ];
219
    }
220
221
    private function command(): string
222
    {
223
        $command = $this->fromEnvExamples->staticSite(
224
            null,
225
            'site1',
226
            'site1.'.$this->traefikHostname->domainName()
227
        );
228
229
        return <<<EOF
230
<options=bold,underscore>Run this command to create a static site:</>
231
{$command}
232
233
EOF;
234
    }
235
236
    private function createMachine(): void
237
    {
238
        $this->style->writeln("Creating a Docker machine named '{$this->machineName}' on Digital Ocean.");
239
        $this->machineCreator->create();
240
    }
241
242
    private function createTraefikTemplate(): void
243
    {
244
        $this->style->writeln("Creating Traefik template in {$this->hostMount->sourcePath()}/traefik");
245
        $this->traefikCreator->create();
246
    }
247
248
    private function configureNetworking(): void
249
    {
250
        $this->style->writeln("Configuring networking for {$this->traefikHostname->toString()}");
251
        $this->networkingConfigurator->configure(
252
            $this->traefikHostname->toHostnameCollection(),
253
            $this->machineIp->toIP()
254
        );
255
    }
256
257
    private function deployTraefik(): void
258
    {
259
        $this->style->writeln('Deploying Traefik to cloud machine');
260
//        $this->processRunner->run(new Process('./bin/reset-prod 2>/dev/stdout', $this->traefikCreator->hostAppPath()));
261
        $this->processRunner->mustRun(new Process('./bin/prod 2>/dev/stdout', $this->traefikCreator->hostAppPath()));
262
    }
263
264
    private function waitForTraefikReady(): void
265
    {
266
        $this->style->writeln('Waiting for Traefik to start');
267
        $this->traefikDeploymentValidator->validate();
268
    }
269
}