Skip to content
Snippets Groups Projects
Commit 2d37cc87 authored by Lidia Matei's avatar Lidia Matei Committed by Yas Naoi
Browse files

Issue #3219063 by XLD, yas: Validate K8s Launch Template manifest file with key-value pairs

parent fe1542c1
No related branches found
No related tags found
No related merge requests found
......@@ -457,7 +457,7 @@ class K8sCloudServerTemplatePlugin extends CloudPluginBase implements CloudServe
$result = $this->fileSystem->mkdir($dir_name);
if (!$result) {
$this->messenger->addError($this->t("Unable to create the working directory."));
$this->messenger->addError($this->t('Unable to create the working directory.'));
$this->handleError($form, $fieldsets_def);
return;
}
......@@ -477,7 +477,7 @@ class K8sCloudServerTemplatePlugin extends CloudPluginBase implements CloudServe
exec("git clone $git_url $dir_name", $output, $return_var);
}
if ($return_var !== 0) {
$this->messenger->addError($this->t("Unable to clone the git repository."));
$this->messenger->addError($this->t('Unable to clone the git repository.'));
$this->handleError($form, $fieldsets_def);
return;
}
......@@ -489,7 +489,7 @@ class K8sCloudServerTemplatePlugin extends CloudPluginBase implements CloudServe
}
}
catch (NotRegularDirectoryException $e) {
$this->messenger->addError($this->t("Git Resource Path might not be correct."));
$this->messenger->addError($this->t('Git Resource Path might not be correct.'));
$this->handleError($form, $fieldsets_def);
return;
}
......@@ -515,7 +515,7 @@ class K8sCloudServerTemplatePlugin extends CloudPluginBase implements CloudServe
$validated = FALSE;
$file_contents = file_get_contents($file->uri);
if (!$file_contents) {
$message = $this->t("Unable to get contents from the file [%file].", ["%file" => $file->filename]);
$message = $this->t('Unable to get contents from the file %file.', ['%file' => $file->filename]);
continue;
}
if (!preg_match($regex, $file->filename)) {
......@@ -524,28 +524,46 @@ class K8sCloudServerTemplatePlugin extends CloudPluginBase implements CloudServe
try {
$yamls = $this->k8sService->decodeMultipleDocYaml(
// Render file contents or not.
!empty($this->getLaunchTemplateParameters($entity))
? $this->renderYaml($file_contents, $this->getLaunchTemplateParameters($entity))
: $file_contents
// We can render the Yaml even if we
// do not have launch_template_parameters.
// This way, we bypass errors thrown by decoding
// and the below validation will show a more helpful error.
$this->renderYaml($file_contents, $this->getLaunchTemplateParameters($entity))
);
// Validate YAML files.
foreach ($yamls ?: [] as $yaml) {
if (!is_array($yaml)) {
$message = $this->t("The file [%file] isn't YAML format.", ['%file' => $file->filename]);
$message = $this->t("The file %file isn't YAML format.", ['%file' => $file->filename]);
break;
}
// Check if there have been any placeholders,
// replaced by NULL during rendering.
$undefined_placeholders = $this->checkUndefinedPlaceholders($file_contents, $this->getLaunchTemplateParameters($entity));
if (!empty($undefined_placeholders)) {
$missing_parameters = '';
foreach ($undefined_placeholders as $missing_parameter) {
$missing_parameters .= "<li>{$missing_parameter}</li>";
}
$message = $this->t('The file <strong><a href=":url">%file</a></strong> contains undefined parameters:<ul>@missing_parameters</ul>',
[
':url' => $entity->toUrl('edit-form', ['absolute' => TRUE])->toString(),
'%file' => $file->filename,
'@missing_parameters' => Markup::create($missing_parameters),
]);
break;
}
if (!isset($yaml['kind'])) {
$message = $this->t("No 'Kind' element found in the file [%file].", ['%file' => $file->filename]);
$message = $this->t("No 'Kind' element found in the file %file.", ['%file' => $file->filename]);
break;
}
$kind = $yaml['kind'];
$object = array_search($kind, $templates);
if ($object === FALSE) {
$message = $this->t("Unsupported 'Kind' element in the file [%file].", ['%file' => $file->filename]);
$message = $this->t("Unsupported 'Kind' element in the file %file.", ['%file' => $file->filename]);
break;
}
......@@ -553,7 +571,7 @@ class K8sCloudServerTemplatePlugin extends CloudPluginBase implements CloudServe
}
}
catch (InvalidDataTypeException $e) {
$message = $this->t("The file [%file] fails to decode: @exception", [
$message = $this->t('The file %file fails to decode: @exception', [
'%file' => $file->filename,
'@exception' => $e->getMessage(),
]);
......@@ -735,8 +753,7 @@ class K8sCloudServerTemplatePlugin extends CloudPluginBase implements CloudServe
$this->getLaunchTemplateParameters($cloud_server_template))
: '';
$yamls = !empty($this->getLaunchTemplateParameters($cloud_server_template)) ? $this->k8sService->decodeMultipleDocYaml($rendered_file_contents) :
$this->k8sService->decodeMultipleDocYaml($cloud_server_template->get('field_detail')->value);
$yamls = $this->k8sService->decodeMultipleDocYaml($rendered_file_contents);
foreach ($yamls ?: [] as $yaml) {
// Add owner uid to annotations.
......@@ -969,43 +986,58 @@ class K8sCloudServerTemplatePlugin extends CloudPluginBase implements CloudServe
}
try {
// If we have twig values within this template, we replace.
$detailValue = !empty($this->getLaunchTemplateParameters($cloud_server_template))
? $this->renderYaml(
$file_contents,
$this->getLaunchTemplateParameters($cloud_server_template)
)
: $file_contents;
$yamls = $this->k8sService->decodeMultipleDocYaml($detailValue);
// Render YAML.
// Even if there is nothing to render,
// this will not affect the functionality.
$rendered_yaml_contents = $this->renderYaml($file_contents, $this->getLaunchTemplateParameters($cloud_server_template));
$yamls = $this->k8sService->decodeMultipleDocYaml($rendered_yaml_contents);
$validated = FALSE;
$object = NULL;
// Validate yamls.
foreach ($yamls ?: [] as $yaml) {
if (!is_array($yaml)) {
$this->messenger->addError($this->t("The file [%file] isn't YAML format.", ['%file' => $yaml_file_names[$idx]]));
$this->messenger->addError($this->t("The file %file isn't YAML format.", ['%file' => $yaml_file_names[$idx]]));
break;
}
// Check if there have been any placeholders
// replaced by NULL during rendering.
$undefined_placeholders = $this->checkUndefinedPlaceholders($file_contents, $this->getLaunchTemplateParameters($cloud_server_template));
if (!empty($undefined_placeholders)) {
$missing_parameters = '';
foreach ($undefined_placeholders as $missing_parameter) {
$missing_parameters .= "<li>{$missing_parameter}</li>";
}
$this->messenger->addError($this->t('The file <strong><a href=":url">%file</a></strong> contains undefined parameters:<ul>@missing_parameters</ul>',
[
':url' => $cloud_server_template->toUrl('edit-form', ['absolute' => TRUE])->toString(),
'%file' => $yaml_file_names[$idx],
'@missing_parameters' => Markup::create($missing_parameters),
]));
break;
}
if (!isset($yaml['kind'])) {
$this->messenger->addError($this->t("No 'Kind' element found in the file [%file].", ['%file' => $yaml_file_names[$idx]]));
$this->messenger->addError($this->t("No 'Kind' element found in the file %file.", ['%file' => $yaml_file_names[$idx]]));
break;
}
$kind = $yaml['kind'];
$object = array_search($kind, $templates);
if ($object === FALSE) {
$this->messenger->addError($this->t("Unsupported 'Kind' element in the file [%file].", ['%file' => $yaml_file_names[$idx]]));
$this->messenger->addError($this->t("Unsupported 'Kind' element in the file %file.", ['%file' => $yaml_file_names[$idx]]));
break;
}
$validated = TRUE;
}
// Whenever we need to check something,
// we should use the rendered $yaml string.
$result = !empty($validated)
? $this->checkExistence(
$file_contents,
$rendered_yaml_contents,
$cloud_server_template->getCloudContext(),
$cloud_server_template->get('field_namespace')->value
) : [];
......@@ -1015,7 +1047,7 @@ class K8sCloudServerTemplatePlugin extends CloudPluginBase implements CloudServe
continue;
}
if ($validated) {
$cloud_server_template->get('field_detail')->setValue($detailValue);
$cloud_server_template->get('field_detail')->setValue($rendered_yaml_contents);
$errors_before = count($this->messenger->messagesByType($this->messenger::TYPE_ERROR));
// Put an extra 2nd param to identify called by this function.
$this->launchK8sResources($cloud_server_template, $launch_uid, TRUE);
......@@ -1031,7 +1063,7 @@ class K8sCloudServerTemplatePlugin extends CloudPluginBase implements CloudServe
}
}
catch (\Exception $e) {
$this->messenger->addError($this->t("Invalid Yaml format in the file [%file]:%message", [
$this->messenger->addError($this->t('Invalid Yaml format in the file %file: %message', [
'%file' => $yaml_file_names[$idx],
'%message' => $e->getMessage(),
]));
......@@ -1105,10 +1137,10 @@ class K8sCloudServerTemplatePlugin extends CloudPluginBase implements CloudServe
* The set of KVP's.
*/
private function getLaunchTemplateParameters(CloudServerTemplateInterface $cloud_server_template): array {
$result = [];
if (!$cloud_server_template->hasField('field_launch_template_parameters')) {
return $result;
return [];
}
$result = [];
$parameters = $cloud_server_template->get('field_launch_template_parameters')->getValue();
foreach ($parameters ?: [] as $parameter) {
if (!empty($parameter['item_key']) && !empty($parameter['item_value'])) {
......@@ -1122,7 +1154,7 @@ class K8sCloudServerTemplatePlugin extends CloudPluginBase implements CloudServe
* Check the existence of resources.
*
* @param string $yaml_string
* The YAML string.
* The rendered YAML string.
* @param string $cloud_context
* The cloud context.
* @param string $namespace
......@@ -1245,4 +1277,36 @@ class K8sCloudServerTemplatePlugin extends CloudPluginBase implements CloudServe
return $messages;
}
/**
* Helper function to check for undefined placeholders.
*
* @param string $yaml
* Unrendered yaml representation as string.
* @param array $placeholders
* Array of KVP's to check against,
* can be obtained from field_launch_template_parameters.
*
* @return array
* Empty array if all placeholders have values
* or array of undefined placeholders to display.
*/
private function checkUndefinedPlaceholders(string $yaml, array $placeholders): array {
// Extract all twig placeholders from the $yaml string.
$twig_pattern = '/\{{[^}]*\}}/';
preg_match_all($twig_pattern, $yaml, $matches);
$leftovers = [];
// If there are placeholders found
// check to see if a KVP has been provided.
foreach (array_unique($matches[0]) ?: [] as $match) {
// Check to see if a kvp has been provided for it.
preg_match('~{{(.*?)}}~', $match, $token);
// If not, return placeholder.
if (empty($placeholders[str_replace(' ', '', $token[1])])) {
$leftovers[] = $token[1];
}
}
return $leftovers;
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment