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 | $this->echoTraefikLogs(); |
||
187 | $this->style->complete($this->completeMessage()); |
||
188 | $this->style->writeln($this->command()); |
||
189 | } |
||
190 | |||
191 | private function confirm(): void |
||
192 | { |
||
193 | if (!$this->style->confirm( |
||
194 | "You are about to create a Docker machine named '<options=bold>{$this->machineName->toString()}</>' on Digital Ocean \n". |
||
195 | " and install the Traefik reverse proxy on it with hostname '<options=bold>{$this->traefikHostname->toString()}</>'.\n". |
||
196 | " This action may take a few minutes." |
||
197 | )) { |
||
198 | throw new \Exception('Cancelled'); |
||
199 | }; |
||
200 | } |
||
201 | |||
202 | private function description(): string |
||
203 | { |
||
204 | return 'Create a <options=bold>Docker</> machine on <options=bold>Digital Ocean</> and '. |
||
205 | 'install the <options=bold>Traefik</> reverse proxy on it.'; |
||
206 | } |
||
207 | |||
208 | private function completeMessage(): array |
||
209 | { |
||
210 | return [ |
||
211 | "Installation successful.", |
||
212 | "You can now:\n". |
||
213 | "- Visit your Traefik UI at <options=bold>https://{$this->traefikHostname->toString()}</>\n". |
||
214 | "- Use docker-machine commands (e.g. <options=bold>docker-machine -s machine ssh {$this->machineName}</>)\n". |
||
215 | "- Deploy a static website to your cloud machine with the command below.", |
||
216 | ]; |
||
217 | } |
||
218 | |||
219 | private function command(): string |
||
220 | { |
||
221 | $command = $this->fromEnvExamples->staticSite( |
||
222 | null, |
||
223 | 'site1', |
||
224 | 'site1.'.$this->traefikHostname->domainName() |
||
225 | ); |
||
226 | |||
227 | return <<<EOF |
||
228 | <options=bold,underscore>Run this command to create a static site:</> |
||
229 | {$command} |
||
230 | |||
231 | EOF; |
||
232 | } |
||
233 | |||
234 | private function createMachine(): void |
||
235 | { |
||
236 | $this->style->writeln("Creating a Docker machine named '{$this->machineName}' on Digital Ocean."); |
||
237 | $this->machineCreator->create(); |
||
238 | } |
||
239 | |||
240 | private function createTraefikTemplate(): void |
||
241 | { |
||
242 | $this->style->writeln("Creating Traefik template in {$this->hostMount->sourcePath()}/traefik"); |
||
243 | $this->traefikCreator->create(); |
||
244 | } |
||
245 | |||
246 | private function configureNetworking(): void |
||
247 | { |
||
248 | $this->style->writeln("Configuring networking for {$this->traefikHostname->toString()}"); |
||
249 | $this->networkingConfigurator->configure( |
||
250 | $this->traefikHostname->toHostnameCollection(), |
||
251 | $this->machineIp->toIP() |
||
252 | ); |
||
253 | } |
||
254 | |||
255 | private function deployTraefik(): void |
||
256 | { |
||
257 | $this->style->writeln('Deploying Traefik to cloud machine'); |
||
258 | // $this->processRunner->run(new Process('./bin/reset-prod 2>/dev/stdout', $this->traefikCreator->hostAppPath())); |
||
0 ignored issues
–
show
|
|||
259 | $this->processRunner->mustRun(Process::fromShellCommandline('./bin/prod 2>/dev/stdout', $this->traefikCreator->hostAppPath())); |
||
260 | } |
||
261 | |||
262 | private function waitForTraefikReady(): void |
||
263 | { |
||
264 | $this->style->writeln('Waiting for Traefik to start'); |
||
265 | $this->traefikDeploymentValidator->validate(); |
||
266 | } |
||
267 | |||
268 | private function echoTraefikLogs(): void |
||
269 | { |
||
270 | $this->processRunner->mustRun(Process::fromShellCommandline('./bin/logs -t', $this->traefikCreator->hostAppPath())); |
||
271 | } |
||
272 | } |
||
273 |
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.
The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.
This check looks for comments that seem to be mostly valid code and reports them.