Skip to content

Commit 5694e50

Browse files
authored
MAGETWO-67537: [WIP] Varnish Vcl generator command #9286
2 parents 25c0a12 + 118932b commit 5694e50

File tree

9 files changed

+748
-7
lines changed

9 files changed

+748
-7
lines changed
Lines changed: 278 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,278 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
namespace Magento\PageCache\Console\Command;
8+
9+
use Magento\Framework\App\Config\ScopeConfigInterface;
10+
use Magento\Framework\Console\Cli;
11+
use Magento\Framework\Filesystem\DriverPool;
12+
use Magento\Framework\Filesystem\File\WriteFactory;
13+
use Magento\Framework\HTTP\PhpEnvironment\Request;
14+
use Magento\Framework\Serialize\Serializer\Json;
15+
use Magento\PageCache\Model\VclGeneratorInterfaceFactory;
16+
use Magento\PageCache\Model\Config;
17+
use Magento\PageCache\Model\Varnish\VclTemplateLocator;
18+
use Magento\Store\Model\ScopeInterface;
19+
use Symfony\Component\Console\Command\Command;
20+
use Symfony\Component\Console\Input\InputInterface;
21+
use Symfony\Component\Console\Input\InputOption;
22+
use Symfony\Component\Console\Output\OutputInterface;
23+
24+
/**
25+
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
26+
*/
27+
class GenerateVclCommand extends Command
28+
{
29+
/**
30+
* Access list option name
31+
*/
32+
const ACCESS_LIST_OPTION = 'access-list';
33+
34+
/**
35+
* Backend host option name
36+
*/
37+
const BACKEND_HOST_OPTION = 'backend-host';
38+
39+
/**
40+
* Backend port option name
41+
*/
42+
const BACKEND_PORT_OPTION = 'backend-port';
43+
44+
/**
45+
* Varnish version option name
46+
*/
47+
const EXPORT_VERSION_OPTION = 'export-version';
48+
49+
/**
50+
* Grace period option name
51+
*/
52+
const GRACE_PERIOD_OPTION = 'grace-period';
53+
54+
/**
55+
* Output file option name
56+
*/
57+
const OUTPUT_FILE_OPTION = 'output-file';
58+
59+
/**
60+
* @var \Magento\Framework\Filesystem\Directory\WriteFactory
61+
*/
62+
private $writeFactory;
63+
64+
/**
65+
* @var VclGeneratorInterfaceFactory
66+
*/
67+
private $vclGeneratorFactory;
68+
69+
/**
70+
* @var array
71+
*/
72+
private $inputToVclMap = [
73+
self::ACCESS_LIST_OPTION => 'accessList',
74+
self::BACKEND_PORT_OPTION => 'backendPort',
75+
self::BACKEND_HOST_OPTION => 'backendHost',
76+
self::GRACE_PERIOD_OPTION => 'gracePeriod',
77+
];
78+
79+
/**
80+
* @var ScopeConfigInterface
81+
*/
82+
private $scopeConfig;
83+
84+
/**
85+
* @var Json
86+
*/
87+
private $serializer;
88+
89+
/**
90+
* @inheritdoc
91+
*/
92+
protected function configure()
93+
{
94+
$this->setName('varnish:vcl:generate')
95+
->setDescription('Generates Varnish VCL and echos it to the command line')
96+
->setDefinition($this->getOptionList());
97+
}
98+
99+
/**
100+
* @param VclGeneratorInterfaceFactory $vclGeneratorFactory
101+
* @param WriteFactory $writeFactory
102+
* @param ScopeConfigInterface $scopeConfig
103+
* @param Json $serializer
104+
*/
105+
public function __construct(
106+
VclGeneratorInterfaceFactory $vclGeneratorFactory,
107+
WriteFactory $writeFactory,
108+
ScopeConfigInterface $scopeConfig,
109+
Json $serializer
110+
) {
111+
parent::__construct();
112+
$this->writeFactory = $writeFactory;
113+
$this->vclGeneratorFactory = $vclGeneratorFactory;
114+
$this->scopeConfig = $scopeConfig;
115+
$this->serializer = $serializer;
116+
}
117+
118+
/**
119+
* @inheritdoc
120+
*/
121+
protected function execute(InputInterface $input, OutputInterface $output)
122+
{
123+
$errors = $this->validate($input);
124+
if ($errors) {
125+
foreach ($errors as $error) {
126+
$output->writeln('<error>'.$error.'</error>');
127+
128+
return Cli::RETURN_FAILURE;
129+
}
130+
}
131+
132+
try {
133+
$outputFile = $input->getOption(self::OUTPUT_FILE_OPTION);
134+
$varnishVersion = $input->getOption(self::EXPORT_VERSION_OPTION);
135+
$vclParameters = array_merge($this->inputToVclParameters($input), [
136+
'sslOffloadedHeader' => $this->getSslOffloadedHeader(),
137+
'designExceptions' => $this->getDesignExceptions(),
138+
]);
139+
$vclGenerator = $this->vclGeneratorFactory->create($vclParameters);
140+
$vcl = $vclGenerator->generateVcl($varnishVersion);
141+
142+
if ($outputFile) {
143+
$writer = $this->writeFactory->create($outputFile, DriverPool::FILE, 'w+');
144+
$writer->write($vcl);
145+
$writer->close();
146+
} else {
147+
$output->writeln($vcl);
148+
}
149+
150+
return Cli::RETURN_SUCCESS;
151+
} catch (\Exception $e) {
152+
$output->writeln('<error>'.$e->getMessage().'</error>');
153+
if ($output->getVerbosity() >= OutputInterface::VERBOSITY_VERBOSE) {
154+
$output->writeln($e->getTraceAsString());
155+
}
156+
157+
return Cli::RETURN_FAILURE;
158+
}
159+
}
160+
161+
/**
162+
* Get list of options for the command
163+
*
164+
* @return InputOption[]
165+
*/
166+
private function getOptionList()
167+
{
168+
return [
169+
new InputOption(
170+
self::ACCESS_LIST_OPTION,
171+
null,
172+
InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY,
173+
'IPs access list that can purge Varnish',
174+
['localhost']
175+
),
176+
new InputOption(
177+
self::BACKEND_HOST_OPTION,
178+
null,
179+
InputOption::VALUE_REQUIRED,
180+
'Host of the web backend',
181+
'localhost'
182+
),
183+
new InputOption(
184+
self::BACKEND_PORT_OPTION,
185+
null,
186+
InputOption::VALUE_REQUIRED,
187+
'Port of the web backend',
188+
8080
189+
),
190+
new InputOption(
191+
self::EXPORT_VERSION_OPTION,
192+
null,
193+
InputOption::VALUE_REQUIRED,
194+
'The version of Varnish file',
195+
VclTemplateLocator::VARNISH_SUPPORTED_VERSION_4
196+
),
197+
new InputOption(
198+
self::GRACE_PERIOD_OPTION,
199+
null,
200+
InputOption::VALUE_REQUIRED,
201+
'Grace period in seconds',
202+
300
203+
),
204+
new InputOption(
205+
self::OUTPUT_FILE_OPTION,
206+
null,
207+
InputOption::VALUE_REQUIRED,
208+
'Path to the file to write vcl'
209+
),
210+
];
211+
}
212+
213+
/**
214+
* @param InputInterface $input
215+
* @return array
216+
*/
217+
private function inputToVclParameters(InputInterface $input)
218+
{
219+
$parameters = [];
220+
221+
foreach ($this->inputToVclMap as $inputKey => $vclKey) {
222+
$parameters[$vclKey] = $input->getOption($inputKey);
223+
}
224+
225+
return $parameters;
226+
}
227+
228+
/**
229+
* Input validation
230+
*
231+
* @param InputInterface $input
232+
* @return array
233+
*/
234+
private function validate(InputInterface $input)
235+
{
236+
$errors = [];
237+
238+
if ($input->hasOption(self::BACKEND_PORT_OPTION)
239+
&& ($input->getOption(self::BACKEND_PORT_OPTION) < 0
240+
|| $input->getOption(self::BACKEND_PORT_OPTION) > 65535)
241+
) {
242+
$errors[] = 'Invalid backend port value';
243+
}
244+
245+
if ($input->hasOption(self::GRACE_PERIOD_OPTION)
246+
&& $input->getOption(self::GRACE_PERIOD_OPTION) < 0
247+
) {
248+
$errors[] = 'Grace period can\'t be lower than 0';
249+
}
250+
251+
return $errors;
252+
}
253+
254+
/**
255+
* Get ssl Offloaded header
256+
*
257+
* @return mixed
258+
*/
259+
private function getSslOffloadedHeader()
260+
{
261+
return $this->scopeConfig->getValue(Request::XML_PATH_OFFLOADER_HEADER);
262+
}
263+
264+
/**
265+
* Get design exceptions
266+
*
267+
* @return array
268+
*/
269+
private function getDesignExceptions()
270+
{
271+
$expressions = $this->scopeConfig->getValue(
272+
Config::XML_VARNISH_PAGECACHE_DESIGN_THEME_REGEX,
273+
ScopeInterface::SCOPE_STORE
274+
);
275+
276+
return $expressions ? $this->serializer->unserialize($expressions) : [];
277+
}
278+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
namespace Magento\PageCache\Exception;
8+
9+
use Magento\Framework\Exception\LocalizedException;
10+
11+
class UnsupportedVarnishVersion extends LocalizedException
12+
{
13+
14+
}

0 commit comments

Comments
 (0)