Commit 79770908 authored by Takumaru Sekine's avatar Takumaru Sekine Committed by Yas Naoi
Browse files

Issue #3266848 by sekinet, yas, Xiaohua Guan: Add the schedule (date / time)...

Issue #3266848 by sekinet, yas, Xiaohua Guan: Add the schedule (date / time) in K8s Launch Template (Pod)
parent 33295c48
Loading
Loading
Loading
Loading
+22 −0
Original line number Diff line number Diff line
@@ -68,6 +68,20 @@ class K8sEntityBase extends CloudContentEntityBase implements K8sEntityInterface
    return $this->set('detail', $detail);
  }

  /**
   * {@inheritdoc}
   */
  public function getYamlUrl(): string {
    return $this->get('yaml_url')->value;
  }

  /**
   * {@inheritdoc}
   */
  public function setYamlUrl($detail): K8sEntityInterface {
    return $this->set('yaml_url', $detail);
  }

  /**
   * {@inheritdoc}
   */
@@ -161,6 +175,14 @@ class K8sEntityBase extends CloudContentEntityBase implements K8sEntityInterface
      ])
      ->addConstraint('yaml_array_data');

    $fields['yaml_url'] = BaseFieldDefinition::create('string')
      ->setLabel(t('YAML URL'))
      ->setDisplayOptions('view', [
        'label' => 'inline',
        'type' => 'string',
        'weight' => -5,
      ]);

    $fields['created'] = BaseFieldDefinition::create('created')
      ->setLabel(t('Created'))
      ->setDescription(t('The time that the entity was created.'));
+17 −0
Original line number Diff line number Diff line
@@ -259,6 +259,23 @@ class K8sPod extends K8sEntityBase implements K8sPodInterface {

    $fields = K8sEntityBase::baseFieldDefinitions($entity_type);

    $fields['detail'] = BaseFieldDefinition::create('string_long')
      ->setLabel(t('Detail'))
      ->setDescription(t('Enter entity detail.'))
      ->setRequired(FALSE)
      ->setDisplayOptions('view', [
        'label' => 'inline',
        'type' => 'pre_string_formatter',
        'weight' => -5,
      ])
      ->setDisplayOptions('form', [
        'type' => 'string_textarea',
        'settings' => [
          'rows' => 20,
        ],
      ])
      ->addConstraint('yaml_array_data');

    $fields['namespace'] = BaseFieldDefinition::create('string')
      ->setLabel(t('Namespace'))
      ->setDescription(t('The namespace of pod.'))
+199 −1
Original line number Diff line number Diff line
@@ -28,7 +28,7 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
/**
 * The base form class for the k8s content form.
 */
abstract class K8sContentForm extends CloudContentForm {
abstract class K8sContentForm extends CloudContentForm implements K8sContentFormInterface {

  /**
   * The K8s Service.
@@ -255,6 +255,204 @@ abstract class K8sContentForm extends CloudContentForm {
    return TRUE;
  }

  /**
   * Create a cronjob that gets a yaml and schedules a pod.
   *
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The current state of the form.
   */
  protected function createCronJobFromYaml(FormStateInterface $form_state): bool {
    if (empty($form_state->getValue('enable_time_scheduler'))
      || empty($form_state->getValue('time_scheduler_option'))
      || $form_state->getValue('time_scheduler_option')
        !== K8sContentFormInterface::CRONJOB_SCHEDULER
    ) {
      // Scheduler is not enabled or type is empty or not a CronJob.
      return FALSE;
    }

    $yaml_url = $form_state->getValue('yaml_url');
    $context = !empty($yaml_url) ? file_get_contents($yaml_url) : '';
    if (empty($context)) {
      $this->messenger->addError($this->t('The @yaml_url is invalid.', [
        '@yaml_url' => $yaml_url,
      ]));
      $this->processOperationErrorStatus($this->entity, 'created');
      return FALSE;
    }

    $yaml = $this->k8sService->decodeMultipleDocYaml($context)[0];
    $namespace = method_exists($this->entity, 'getNamespace')
      ? $this->entity->getNamespace()
      : 'default';

    // Create Service Account.
    $service_account_yaml = $this->k8sService->createScheduleServiceAccountYaml(
      $namespace,
    );
    $this->createScheduleServiceAccount($namespace, $service_account_yaml);

    // Create Cluster Role Binding.
    $cluster_role_binding_yaml = $this->k8sService->createScheduleClusterRoleBindingYaml(
      $namespace,
    );
    $this->createScheduleClusterRoleBinding($cluster_role_binding_yaml);

    // Get CronJob parameters.
    $start_hour = $form_state->getValue('start_hour');
    $start_minute = $form_state->getValue('start_minute');
    $stop_hour = $form_state->getValue('stop_hour');
    $stop_minute = $form_state->getValue('stop_minute');
    $name = $yaml['metadata']['name'];

    // Create start CronJob.
    $start_cronjob_yaml = $this->k8sService->createScheduleCronJobYaml(
      $name,
      $namespace,
      $start_hour,
      $start_minute,
      'start',
      $yaml_url
    );
    if (empty($this->createScheduleCronJob($namespace, $start_cronjob_yaml))) {
      return FALSE;
    }

    // Create stop CronJob.
    $stop_cronjob_yaml = $this->k8sService->createScheduleCronJobYaml(
      $name,
      $namespace,
      $stop_hour,
      $stop_minute,
      'stop'
    );
    if (empty($this->createScheduleCronJob($namespace, $stop_cronjob_yaml))) {
      return FALSE;
    }

    return TRUE;
  }

  /**
   * Create a CronJob based on $yaml.
   *
   * @param string $namespace
   *   The namespace.
   * @param array $yaml
   *   The yaml array.
   *
   * @return bool
   *   Whether or not it was created.
   */
  protected function createScheduleCronJob(
    string $namespace,
    array $yaml
  ): bool {
    // Check if a CronJob has already been created.
    $result = $this->k8sService->getCronJobs([
      'metadata.name' => $yaml['metadata']['name'],
      'metadata.namespace' => $namespace,
    ]);

    if (!empty($result)) {
      $this->messenger->addError($this->t('The %name already exists.', [
        '%name' => $yaml['metadata']['name'],
      ]));
      $this->processOperationErrorStatus($this->entity, 'created');
      return FALSE;
    }

    // Create a CronJob.
    $result = $this->k8sService->createCronJob($namespace, $yaml);
    $this->k8sService->updateCronJobs([
      'metadata.name' => $result['metadata']['name'],
    ], FALSE);

    $entity = $this->k8sService->getCronJobEntity(
      $this->entity->getCloudContext(),
      $result['metadata']['name'],
      $namespace
    );

    $this->processOperationStatus($entity, 'created');

    return TRUE;
  }

  /**
   * Create a ClusterRoleBinding based on $yaml.
   *
   * @param array $yaml
   *   The yaml array.
   *
   * @return bool
   *   Whether or not it was created.
   */
  protected function createScheduleClusterRoleBinding(array $yaml): bool {
    // Check if a ClusterRoleBinding has already been created.
    $result = $this->k8sService->getClusterRoleBindings([
      'metadata.name' => $yaml['metadata']['name'],
    ]);

    if (!empty($result)) {
      return FALSE;
    }

    // Create a ClusterRoleBinding.
    $result = $this->k8sService->createClusterRoleBinding($yaml);
    $this->k8sService->updateClusterRoleBindings([
      'metadata.name' => $result['metadata']['name'],
    ], FALSE);

    $entity = $this->k8sService->getClusterRoleBindingEntity(
      $this->entity->getCloudContext(),
      $result['metadata']['name']
    );

    $this->processOperationStatus($entity, 'created');

    return TRUE;
  }

  /**
   * Create a ServiceAccount based on $yaml.
   *
   * @param string $namespace
   *   The namespace.
   * @param array $yaml
   *   The yaml array.
   *
   * @return bool
   *   Whether or not it was created.
   */
  protected function createScheduleServiceAccount(string $namespace, array $yaml): bool {
    // Check if a ServiceAccount has already been created.
    $result = $this->k8sService->getServiceAccounts([
      'metadata.name' => $yaml['metadata']['name'],
      'metadata.namespace' => $namespace,
    ]);

    if (!empty($result)) {
      return FALSE;
    }

    // Create a ServiceAccount.
    $result = $this->k8sService->createServiceAccount($namespace, $yaml);
    $this->k8sService->updateServiceAccounts([
      'metadata.name' => $result['metadata']['name'],
    ], FALSE);

    $entity = $this->k8sService->getServiceAccountEntity(
      $this->entity->getCloudContext(),
      $result['metadata']['name'],
      $namespace
    );

    $this->processOperationStatus($entity, 'created');

    return TRUE;
  }

  /**
   * Update yaml information to entity.
   *
+14 −0
Original line number Diff line number Diff line
<?php

namespace Drupal\k8s\Form;

/**
 * Provides an interface defining a K8s content form.
 */
interface K8sContentFormInterface {

  public const CLOUD_ORCHESTRATOR_SCHEDULER = 'cloud_orchestrator_scheduler';

  public const CRONJOB_SCHEDULER = 'cronjob_scheduler';

}
+27 −0
Original line number Diff line number Diff line
@@ -51,10 +51,37 @@ class K8sCreateForm extends K8sContentForm {
        '#options'       => $options,
        '#default_value' => 'default',
        '#required'      => TRUE,
        '#weight' => $weight++,
      ];
    }

    $form[$name_underscore]['yaml_url'] = [
      '#type' => 'textfield',
      '#title' => $this->t('YAML URL'),
      '#weight' => $weight++,
      '#description' => $this->t(
        'This must be an external URL such as %url',
        ['%url' => 'http://example.com/examples/hello_world.yaml']
      ),
      '#states' => [
        'visible' => [
          'input[name="enable_time_scheduler"]' => ['checked' => TRUE],
          'input[name="time_scheduler_option"]' => [
            'value' => K8sContentFormInterface::CRONJOB_SCHEDULER,
          ],
        ],
      ],
    ];

    $form[$name_underscore]['detail'] = $form['detail'];
    $form[$name_underscore]['detail']['#states'] = [
      'invisible' => [
        'input[name="time_scheduler_option"]' => [
          'value' => K8sContentFormInterface::CRONJOB_SCHEDULER,
        ],
      ],
    ];
    $form[$name_underscore]['detail']['#weight'] = $weight++;
    unset($form['detail']);

    $this->addOthersFieldset($form, $weight++, $cloud_context);
Loading