tapis_job-1.4.1-alpha1/src/Entity/TapisJob.php

src/Entity/TapisJob.php
<?php

namespace Drupal\tapis_job\Entity;

use Drupal\Core\Entity\ContentEntityBase;
use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
use Drupal\Core\Entity\EntityChangedTrait;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\Core\Link;
use Drupal\Core\Url;
use Drupal\tapis_app\DrupalIds as AppDrupalIds;
use Drupal\tapis_job\TapisJobInterface;
use Drupal\tapis_system\DrupalIds as SystemDrupalIds;
use Drupal\tapis_tenant\DrupalIds as TenantDrupalIds;
use Drupal\user\EntityOwnerTrait;

/**
 * Defines the job entity class.
 *
 * @ContentEntityType(
 *   id = "tapis_job",
 *   label = @Translation("Job"),
 *   label_collection = @Translation("Jobs"),
 *   label_singular = @Translation("job"),
 *   label_plural = @Translation("jobs"),
 *   label_count = @PluralTranslation(
 *     singular = "@count jobs",
 *     plural = "@count jobs",
 *   ),
 *   fieldable = TRUE,
 *   field_ui_base_route = "tapis_job.structure_settings",
 *   handlers = {
 *     "view_builder" = "Drupal\Core\Entity\EntityViewBuilder",
 *     "list_builder" = "Drupal\tapis_job\TapisJobListBuilder",
 *     "views_data" = "Drupal\views\EntityViewsData",
 *     "access" = "Drupal\tapis_job\TapisJobAccessControlHandler",
 *     "form" = {
 *       "delete" = "Drupal\Core\Entity\ContentEntityDeleteForm",
 *     },
 *     "route_provider" = {
 *       "html" = "Drupal\tapis_job\Entity\Routing\TapisJobRouteProvider",
 *     }
 *   },
 *   base_table = "tapis_job",
 *   admin_permission = "administer tapis job",
 *   entity_keys = {
 *     "id" = "id",
 *     "label" = "label",
 *     "uuid" = "uuid",
 *     "owner" = "uid",
 *   },
 *   links = {
 *     "canonical" = "/tapis/job/{tapis_job}",
 *     "delete-form" = "/tapis/job/{tapis_job}/delete",
 *   },
 * )
 */
class TapisJob extends ContentEntityBase implements TapisJobInterface {

  use EntityChangedTrait;
  use EntityOwnerTrait;

  /**
   * The Tapis job definition.
   *
   * @var array
   */
  protected array $tapisDefinition;

  /**
   * The custom job resources.
   *
   * @var array
   */
  protected array $customJobResources = [];

  /**
   * The custom properties.
   *
   * @var array
   */
  protected array $customProperties = [];

  /**
   * The allocation ID.
   *
   * @var string|null
   */
  protected ?string $allocationId = NULL;

  /**
   * The original job for restart.
   *
   * @var mixed|null
   */
  protected $originalJobForRestart = NULL;

  /**
   * {@inheritdoc}
   */
  public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
    $fields = parent::baseFieldDefinitions($entity_type);

    $fields['tapisId'] = BaseFieldDefinition::create('string')->setRequired(TRUE)->setLabel(t('Tapis ID'));
    $fields['tapisUUID'] = BaseFieldDefinition::create('string')
      ->setRequired(TRUE)
      ->setLabel(t('Tapis UUID'))
      ->setDisplayOptions('view',
        [
          'label' => 'inline',
          'type' => 'textfield',
          'weight' => 0,
        ]
      )->setDisplayConfigurable('view', TRUE);

    $fields['tenant'] = BaseFieldDefinition::create('entity_reference')
      ->setLabel(t('Tenant'))
      ->setRequired(TRUE)
      ->setSetting('target_type', 'node')
      ->setSetting('handler', 'default')
      ->setSetting('handler_settings',
        [
          'target_bundles' => [TenantDrupalIds::NODE_BUNDLE_TENANT => TenantDrupalIds::NODE_BUNDLE_TENANT],
        ]
      )
      ->setDisplayOptions('form',
        [
          'type' => 'entity_reference_autocomplete',
          'settings' => [
            'match_operator' => 'CONTAINS',
            'size' => 60,
            'placeholder' => '',
          ],
          'weight' => 0,
        ]
      )
      ->setDisplayConfigurable('form', TRUE)
      ->setDisplayOptions('view',
        [
          'label' => 'inline',
          'type' => 'entity_reference_label',
          'weight' => 0,
        ]
      )
      ->setDisplayConfigurable('view', TRUE);
    $fields['app'] = BaseFieldDefinition::create('entity_reference')
      ->setLabel(t('App'))
      ->setRequired(TRUE)
      ->setSetting('target_type', 'node')
      ->setSetting('handler', 'default')
      ->setSetting('handler_settings',
        [
          'target_bundles' => [AppDrupalIds::NODE_BUNDLE_APP => AppDrupalIds::NODE_BUNDLE_APP],
        ]
      )
      ->setDisplayOptions('form',
        [
          'type' => 'entity_reference_autocomplete',
          'settings' => [
            'match_operator' => 'CONTAINS',
            'size' => 60,
            'placeholder' => '',
          ],
          'weight' => 0,
        ]
      )
      ->setDisplayConfigurable('form', TRUE)
      ->setDisplayOptions('view',
        [
          'label' => 'inline',
          'type' => 'entity_reference_label',
          'weight' => 0,
        ]
      )
      ->setDisplayConfigurable('view', TRUE);
    $fields['system'] = BaseFieldDefinition::create('entity_reference')
      ->setLabel(t('System'))
      ->setRequired(TRUE)
      ->setSetting('target_type', 'node')
      ->setSetting('handler', 'default')
      ->setSetting('handler_settings',
        [
          'target_bundles' => [SystemDrupalIds::NODE_BUNDLE_SYSTEM => SystemDrupalIds::NODE_BUNDLE_SYSTEM],
        ]
      )
      ->setDisplayOptions('form',
        [
          'type' => 'options_select',
          'weight' => 0,
        ]
      )
      ->setDisplayConfigurable('form', TRUE)
      ->setDisplayOptions('view',
        [
          'label' => 'inline',
          'type' => 'entity_reference_label',
          'weight' => 0,
        ]
      )
      ->setDisplayConfigurable('view', TRUE);
    $fields['webform_submission'] = BaseFieldDefinition::create('entity_reference')
      ->setLabel(t('Webform submission'))
      ->setRequired(FALSE)
      ->setSetting('target_type', 'webform_submission')
      ->setSetting('handler', 'default')
      ->setDisplayConfigurable('form', FALSE)
      ->setDisplayConfigurable('view', FALSE);

    $fields['proxyId'] = BaseFieldDefinition::create('string')
      ->setLabel(t('Proxy ID'))
      ->setRequired(FALSE)
      ->setSetting('max_length', 255);

    $fields['remoteStarted'] = BaseFieldDefinition::create('datetime')
      ->setLabel(t('Job start timestamp'))
      ->setRequired(FALSE)
      ->setDisplayConfigurable('form', FALSE)
      ->setDisplayOptions('view',
        [
          'label' => 'inline',
          'type' => 'string',
        ]
      )
      ->setDisplayConfigurable('view', TRUE);
    $fields['remoteEnded'] = BaseFieldDefinition::create('datetime')
      ->setLabel(t('Job end timestamp'))
      ->setRequired(FALSE)
      ->setDisplayConfigurable('form', FALSE)
      ->setDisplayOptions('view',
        [
          'label' => 'inline',
          'type' => 'string',
        ]
      )
      ->setDisplayConfigurable('view', TRUE);
    $fields['tapisStatus'] = BaseFieldDefinition::create('string')
      ->setLabel(t('Status'))
      ->setRequired(FALSE)
      ->setSetting('max_length', 255)
      ->setDisplayConfigurable('form', FALSE)
      ->setDisplayOptions('view',
        [
          'label' => 'inline',
          'type' => 'string',
        ]
      )
      ->setDisplayConfigurable('view', TRUE);

    $fields['tapisCondition'] = BaseFieldDefinition::create('string')
      ->setLabel(t('Condition'))
      ->setRequired(FALSE)
      ->setSetting('max_length', 255)
      ->setDisplayConfigurable('form', FALSE)
      ->setDisplayOptions('view',
        [
          'label' => 'inline',
          'type' => 'string',
        ]
      )
      ->setDisplayConfigurable('view', TRUE);

    $fields['label'] = BaseFieldDefinition::create('string')
      ->setLabel(t('Label'))
      ->setRequired(TRUE)
      ->setSetting('max_length', 255)
      ->setDisplayOptions('form',
        [
          'type' => 'string_textfield',
          'weight' => -5,
        ]
      )
      ->setDisplayConfigurable('form', TRUE)
      ->setDisplayOptions('view',
        [
          'label' => 'hidden',
          'type' => 'string',
          'weight' => -5,
        ]
      )
      ->setDisplayConfigurable('view', TRUE);
    $fields['description'] = BaseFieldDefinition::create('text_long')
      ->setLabel(t('Description'))
      ->setDisplayOptions('form',
        [
          'type' => 'text_textarea',
          'weight' => 10,
        ]
      )
      ->setDisplayConfigurable('form', TRUE)
      ->setDisplayOptions('view',
        [
          'type' => 'text_default',
          'label' => 'above',
          'weight' => 10,
        ]
      )
      ->setDisplayConfigurable('view', TRUE);

    $fields['uid'] = BaseFieldDefinition::create('entity_reference')
      ->setLabel(t('Owner'))
      ->setSetting('target_type', 'user')
      ->setDefaultValueCallback(static::class . '::getDefaultEntityOwner')
      ->setDisplayOptions('form',
        [
          'type' => 'entity_reference_autocomplete',
          'settings' => [
            'match_operator' => 'CONTAINS',
            'size' => 60,
            'placeholder' => '',
          ],
          'weight' => 15,
        ]
      )
      ->setDisplayConfigurable('form', TRUE)
      ->setDisplayOptions('view',
        [
          'label' => 'inline',
          'type' => 'author',
          'weight' => 15,
        ]
      )
      ->setDisplayConfigurable('view', TRUE);
    $fields['created'] = BaseFieldDefinition::create('created')
      ->setLabel(t('Created'))
      ->setDescription(t('The time that the job was created.'))
      ->setDisplayOptions('view',
        [
          'label' => 'inline',
          'type' => 'timestamp',
          'weight' => 20,
        ]
      )
      ->setDisplayConfigurable('form', TRUE)
      ->setDisplayOptions('form',
        [
          'type' => 'datetime_timestamp',
          'weight' => 20,
        ]
      )
      ->setDisplayConfigurable('view', TRUE);
    $fields['changed'] = BaseFieldDefinition::create('changed')->setLabel(t('Changed'))->setDescription(t('The time that the job was last edited.'));

    $fields['remoteSubmitted'] = BaseFieldDefinition::create('datetime')
      ->setLabel(t('Job submit timestamp'))
      ->setRequired(FALSE)
      ->setDisplayConfigurable('form', FALSE)
      ->setDisplayOptions('view',
        [
          'label' => 'inline',
          'type' => 'string',
        ]
      )
      ->setDisplayConfigurable('view', TRUE);

    $fields['jobUsage'] = BaseFieldDefinition::create('string_long')
      ->setLabel(t('Job Usage'))
      ->setDescription(t('Provide a summary of the job usage.'))
      ->setDefaultValue('')
      ->setRequired(FALSE)
      ->setDisplayOptions('view', [
        'label' => 'inline',
        'type' => 'basic_string',
      ])
      ->setDisplayConfigurable('view', TRUE)
      ->setDisplayConfigurable('form', FALSE);

    return $fields;
  }

  /**
   * {@inheritdoc}
   */
  public static function alterJobView(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display, $view_mode) {
    // Get details from Tapis API for this job.
    /** @var \Drupal\tapis_job\TapisProvider\TapisJobProviderInterface */
    $tapisJobProvider = \Drupal::service("tapis_job.tapis_job_provider");

    /** @var \Drupal\tapis_job\TapisJobInterface $entity */
    $jobOwnerId = $entity->getOwnerId();
    $tenantId = $entity->getTenantId();

    $tapisJob = $tapisJobProvider->getJob($tenantId, $entity->getTapisUUID(), $jobOwnerId);

    $jobStatus = $tapisJob['status'];

    $jobHistory = $tapisJobProvider->getJobHistory($tenantId, $entity->getTapisUUID(), $jobOwnerId);

    $remoteStarted = NULL;
    $remoteEnded = NULL;

    // Iterate over the job history array.
    foreach ($jobHistory as $jobHistoryItem) {
      if ($jobHistoryItem['eventDetail'] == 'RUNNING') {
        $remoteStarted = $jobHistoryItem['created'];
      }
      if ($jobHistoryItem['eventDetail'] == 'FINISHED' || $jobHistoryItem['eventDetail'] == 'CANCELLED' || $jobHistoryItem['eventDetail'] == 'FAILED') {
        $remoteEnded = $jobHistoryItem['created'];
        break;
      }
    }

    if ($remoteEnded && !$remoteStarted) {
      $remoteStarted = $remoteEnded;
    }

    // If ($remoteStarted) {
    // $remoteStarted =
    // substr($remoteStarted, 0, strpos($remoteStarted, '.')) . 'Z';
    // dpm("Remote started: $remoteStarted");
    // $remoteStarted = DrupalDateTime::createFromFormat(DateTime::RFC3339,
    // $remoteStarted, 'UTC')->format(DATETIME_DATETIME_STORAGE_FORMAT);
    // $entity->setRemoteStarted($remoteStarted);
    // }
    // If ($remoteEnded) {
    // dpm("Remote ended: $remoteEnded");
    // $remoteEnded = substr($remoteEnded, 0, strpos($remoteEnded, '.')) . 'Z';
    // dpm("Remote started: $remoteStarted");
    // $remoteEnded = DrupalDateTime::createFromFormat(DateTime::RFC3339,
    // $remoteEnded, 'UTC')->format(DATETIME_DATETIME_STORAGE_FORMAT);;
    // $entity->setRemoteEnded($remoteEnded);
    // }
    // Whenever we view the job, we update the status locally.
    $entity->setStatus($jobStatus);
    $entity->save();

    $lowercaseJobStatus = strtolower($jobStatus);
    $capitalizedJobStatus = str_replace('_', ' ', ucfirst($lowercaseJobStatus));
    // Edit the build array's existing job status element value
    // to be the new job status.
    $build['tapisStatus'][0]['#context']['value'] = $capitalizedJobStatus;

    $lastMessage = $tapisJob['lastMessage'];
    $revisedLastMessage = str_replace('_', ' ', str_replace($jobStatus, $lowercaseJobStatus, $lastMessage));

    $anAccessLinkForThisJob = $tapisJobProvider->getAJobAccessLinkForJob($entity);

    if ($anAccessLinkForThisJob && $jobStatus === "RUNNING") {
      // Only display a job access link for the job
      // if it has a job access link and the job is running.
      $jobProxyURLString = $anAccessLinkForThisJob->getProxyURL();

      // Get a job.
      $jobProxyURL = Url::fromUri($jobProxyURLString);
      $link = new Link('Open app session', $jobProxyURL);
      $build['tapis_job_proxyURL'] = $link->toRenderable();
      $build['tapis_job_proxyURL']['#attributes'] = [
        'class' => [
          'button',
          'button--primary',
        ],
        'target' => '_blank',
      ];
      $build[] = ['#type' => 'markup', '#markup' => '<br/>'];
    }

    $refreshJobStatuses =
      [
        "PENDING",
        "PROCESSING_INPUTS",
        "STAGING_INPUTS",
        "STAGING_JOB",
        "SUBMITTING_JOB",
        "QUEUED",
        "ARCHIVING",
      ];
    if (in_array($jobStatus, $refreshJobStatuses)) {
      $build['loader'] = [
        '#type' => 'markup',
        '#markup' => '<span class="loader"></span>',
      ];
    }

    // $build[] = ['#type' => 'markup', '#markup' => '<br/>'];
    // $build['tapis_job_status'] = ['#type' => 'markup',
    // '#markup' => "<div><b>Status</b> $jobStatus</div>",];
    $build['tapis_job_lastMessage'] = [
      '#type' => 'markup',
      '#markup' => "<div><b>Last message</b> $revisedLastMessage</div>",
    ];
    $build[] = ['#type' => 'markup', '#markup' => '<br/>'];

    // Add the js library.
    $build['#attached']['library'][] = 'tapis_job/tapis_job.jobpage';
    $build['#attached']['drupalSettings']['tapis_job']['status'] = $jobStatus;
  }

  /**
   * {@inheritdoc}
   */
  public function getTenantId() {
    return $this->get('tenant')->first()->getValue()['target_id'];
  }

  /**
   * {@inheritdoc}
   */
  public function getTapisUUID() {
    return $this->get("tapisUUID")->value;
  }

  /**
   * {@inheritdoc}
   */
  public function setRemoteStarted(?string $remoteStarted): static {
    $this->set('remoteStarted', $remoteStarted);
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function setRemoteEnded(?string $remoteEnded): static {
    $this->set('remoteEnded', $remoteEnded);
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function setStatus(?string $status): static {
    $this->set('tapisStatus', $status);
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function setCondition(?string $condition): static {
    $this->set('tapisCondition', $condition);
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function setJobUsage(?string $jobUsage): static {
    $this->set('jobUsage', $jobUsage);
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function setRemoteSubmitted(?string $remoteSubmitted): static {
    $this->set('remoteSubmitted', $remoteSubmitted);
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function getRemoteStarted() {
    return $this->get('remoteStarted')->value;
  }

  /**
   * {@inheritdoc}
   */
  public function getRemoteEnded() {
    return $this->get('remoteEnded')->value;
  }

  /**
   * {@inheritdoc}
   */
  public function getStatus() {
    return $this->get('tapisStatus')->value;
  }

  /**
   * {@inheritdoc}
   */
  public function getCondition() {
    return $this->get('tapisCondition')->value;
  }

  /**
   * {@inheritdoc}
   */
  public function getJobUsage() {
    return $this->get('jobUsage')->value;
  }

  /**
   * {@inheritdoc}
   */
  public function getRemoteSubmitted() {
    return $this->get('remoteSubmitted')->value;
  }

  /**
   * {@inheritdoc}
   */
  public function getTapisId() {
    return $this->get("tapisId")->value;
  }

  /**
   * {@inheritdoc}
   */
  public function setTapisId(?string $jobId) {
    $this->set('tapisId', $jobId);
  }

  /**
   * {@inheritdoc}
   */
  public function setTapisUUID(?string $jobUuid): void {
    $this->set('tapisUUID', $jobUuid);
  }

  /**
   * {@inheritdoc}
   */
  public function setProxyId($proxyId): void {
    $this->set('proxyId', $proxyId);
  }

  /**
   * {@inheritdoc}
   */
  public function getTapisDefinition(): array {
    return $this->tapisDefinition;
  }

  /**
   * {@inheritdoc}
   */
  public function setTapisDefinition(?array $definition): void {
    $this->tapisDefinition = $definition;
  }

  /**
   * {@inheritdoc}
   */
  public function preSave(EntityStorageInterface $storage): void {
    parent::preSave($storage);
    if (!$this->getOwnerId()) {
      // If no owner has been set explicitly, make the anonymous user the owner.
      $this->setOwnerId(0);
    }
  }

  /**
   * {@inheritdoc}
   */
  public function getWebformSubmissionId() {
    $webformSubmission = $this->get('webform_submission')->first();
    if ($webformSubmission !== null) {
      return $this->get('webform_submission')->first()->getValue()['target_id'];
    } else {
      return null;
    }
  }

  /**
   * {@inheritdoc}
   */
  public function getSystemId() {
    return $this->get('system')->first()->getValue()['target_id'];
  }

  /**
   * {@inheritdoc}
   */
  public function getAppId() {
    return $this->get('app')->first()->getValue()['target_id'];
  }

  /**
   * {@inheritdoc}
   */
  public function setCustomJobResources($customJobResources): void {
    $this->customJobResources = $customJobResources;
  }

  /**
   * {@inheritdoc}
   */
  public function setCustomProperties(array $properties): void {
    $this->customProperties = $properties;
  }

  /**
   * {@inheritdoc}
   */
  public function setAllocationId($allocation_id): void {
    $this->allocationId = $allocation_id;
  }

  /**
   * {@inheritdoc}
   */
  public function setOriginalJobForRestart($originalJob): void {
    $this->originalJobForRestart = $originalJob;
  }

  /**
   * {@inheritdoc}
   */
  public function toJSON($appArgsString = "", $schedulerOptions = [], $envVars = [], $jobFileInputArrays = []) {
    \Drupal::logger('tapis_job')->debug("Creating JSON for job");
    $tenant = $this->getTenant();
    $app = $this->getApp();

    $system = $this->get("system")->first();
    if ($system) {
      /** @var \Drupal\Core\Entity\Plugin\DataType\EntityReference $systemEntity */
      $systemEntity = $system->get("entity");
      $system = $systemEntity->getTarget()->getValue();
    }

    $tapisSystemProvider = \Drupal::service("tapis_system.tapis_system_provider");
    $tenantId = $system->get(SystemDrupalIds::SYSTEM_TENANT)->first()->getValue()['target_id'];
    $tapisSystemId = $system->get(SystemDrupalIds::SYSTEM_TAPIS_ID)->first()->getValue()['value'];
    $doesSystemUseStaticSystemUser = boolval($system->get(SystemDrupalIds::SYSTEM_USE_STATIC_SYSTEM_USER)->getValue()[0]['value']);

    \Drupal::logger('tapis_job')->debug("System uses static system user: $doesSystemUseStaticSystemUser");

    $systemOwnerUid = $system->getOwnerId();
    $tapisSystemJson = $tapisSystemProvider->getSystem($tenantId, $tapisSystemId, $systemOwnerUid);

    $tapisSystemJobWorkingDir = $tapisSystemJson['jobWorkingDir'];
    \Drupal::logger('tapis_job')->debug("Job working dir: $tapisSystemJobWorkingDir");

    $json = [];
    $modifiedJobName = str_replace(' ', '_', $this->label());
    $json['name'] = $modifiedJobName;

    if ($this->originalJobForRestart) {
      // Copy the execSystemExecDir, execSystemInputDir,
      // and execSystemOutputDir from the original job.
      $tapisJobProvider = \Drupal::service("tapis_job.tapis_job_provider");
      $originalTapisJob = $tapisJobProvider->getJob($this->originalJobForRestart->getTenantId(), $this->originalJobForRestart->getTapisUUID(), $this->originalJobForRestart->getOwnerId());
      $json['execSystemExecDir'] = $originalTapisJob['execSystemExecDir'];
      $json['execSystemInputDir'] = $originalTapisJob['execSystemInputDir'];
      $json['execSystemOutputDir'] = $originalTapisJob['execSystemOutputDir'];
    }

    elseif (strpos($tapisSystemJobWorkingDir, '${JobOwner}') !== FALSE) {
      $tapisTokenProvider = \Drupal::service("tapis_auth.tapis_token_provider");
      $tapisUsername = $tapisTokenProvider->getTapisUsername($tenantId, $this->getOwnerId());

      // Replace ${JobOwner} with the current user's tapis username
      // due to validation we run in the system node form,
      // we know that tapisSystemJobWorkingDir
      // must end with ${JobOwner} or ${JobOwner}/
      // for all systems that use a static system user.
      $tapisSystemJobWorkingDir = str_replace('${JobOwner}', $tapisUsername, $tapisSystemJobWorkingDir);

      // Also check if the job working directory has
      // the ${EffectiveUserId} token in it,
      // then replace it with the current user's system credential account name
      // to do that, we first check if this system uses a static system user.
      if ($doesSystemUseStaticSystemUser) {
        // System has a static effective user,
        // so we use the static username to replace ${EffectiveUserId} with.
        $system_static_username = $system->get(SystemDrupalIds::SYSTEM_EFFECTIVE_USER)->getValue()[0]['value'];
        $tapisSystemJobWorkingDir = str_replace('${EffectiveUserId}', $system_static_username, $tapisSystemJobWorkingDir);
      }
      else {
        // System has a dynamic effective user,
        // so the current user must have their own credential,
        // and that's the value we use to replace ${EffectiveUserId} with.
        if (strpos($tapisSystemJobWorkingDir, '${EffectiveUserId}') !== FALSE) {
          // Get the system credential to use for this user and system,
          // get its loginUser value, and replace [loginUser] with it.
          $systemCredentialIds = \Drupal::entityQuery("tapis_system_credential")
            ->condition("uid", \Drupal::currentUser()->id())
            ->condition("system", $system->id())
            ->accessCheck(FALSE)
            ->execute();

          // Check if $tapisSystemJobWorkingDir has [loginUser] in it.
          $systemCredentialId = reset($systemCredentialIds);
          // Load the tapis_system_credential entity.
          /** @var \Drupal\tapis_system\Entity\TapisSystemCredential $systemCredential */
          $systemCredential = \Drupal::entityTypeManager()->getStorage('tapis_system_credential')->load($systemCredentialId);
          // Get the login user field.
          $loginUser = $systemCredential->get("loginUser")->getValue()[0]['value'];

          // Replace ${JobOwner} with the current user's tapis username
          // due to validation we run in the system node form,
          // we know that tapisSystemJobWorkingDir
          // must end with ${JobOwner} or ${JobOwner}/.
          $tapisSystemJobWorkingDir = str_replace('${EffectiveUserId}', $loginUser, $tapisSystemJobWorkingDir);
        }
      }

      $tapisSystemJobWorkingDir = '/' . trim($tapisSystemJobWorkingDir, '/') . '/';

      \Drupal::logger('tapis_job')->debug("Granting MODIFY permission to $tapisUsername on $tapisSystemJobWorkingDir");

      // Grant the current user modify permissions on the user-specific path.
      $tapisSystemProvider->grantSystemPermission($tenantId, $tapisSystemId, $tapisUsername, "MODIFY", $tapisSystemJobWorkingDir, $systemOwnerUid);
    }

    elseif (!$doesSystemUseStaticSystemUser) {
      $tapisTokenProvider = \Drupal::service("tapis_auth.tapis_token_provider");
      $tapisUsername = $tapisTokenProvider->getTapisUsername($tenantId, $this->getOwnerId());

      // If the job working directory has the ${EffectiveUserId} token in it,
      // then replace it with the current user's system credential account name.
      if (strpos($tapisSystemJobWorkingDir, '${EffectiveUserId}') !== FALSE) {
        // Get the system credential to use for this user and system,
        // get its loginUser value, and replace [loginUser] with it.
        $systemCredentialIds = \Drupal::entityQuery("tapis_system_credential")
          ->condition("uid", \Drupal::currentUser()->id())
          ->condition("system", $system->id())
          ->accessCheck(FALSE)
          ->execute();

        // Check if $tapisSystemJobWorkingDir has ${EffectiveUserId} in it.
        $systemCredentialId = reset($systemCredentialIds);
        // Load the tapis_system_credential entity.
        /** @var \Drupal\tapis_system\Entity\TapisSystemCredential $systemCredential */
        $systemCredential = \Drupal::entityTypeManager()->getStorage('tapis_system_credential')->load($systemCredentialId);
        // Get the login user field.
        $loginUser = $systemCredential->get("loginUser")->getValue()[0]['value'];

        // Replace ${JobOwner} with the current user's tapis username
        // due to validation we run in the system node form,
        // we know that tapisSystemJobWorkingDir
        // must end with ${JobOwner} or ${JobOwner}/.
        $tapisSystemJobWorkingDir = str_replace('${EffectiveUserId}', $loginUser, $tapisSystemJobWorkingDir);
      }

      // also, check if the job working directory
      // has the ${JobOwner} token in it,
      // then replace it with the current user's tapis username.
      if (strpos($tapisSystemJobWorkingDir, '${JobOwner}') !== FALSE) {
        // Replace ${JobOwner} with the current user's tapis username
        // due to validation we run in the system node form,
        // we know that tapisSystemJobWorkingDir must end
        // with ${JobOwner} or ${JobOwner}/.
        $tapisSystemJobWorkingDir = str_replace('${JobOwner}', $tapisUsername, $tapisSystemJobWorkingDir);
      }

      $tapisSystemJobWorkingDir = '/' . trim($tapisSystemJobWorkingDir, '/') . '/';

      \Drupal::logger('tapis_job')->debug("Granting MODIFY permission to $tapisUsername on $tapisSystemJobWorkingDir");

      // Grant the current user modify permissions on the user-specific path.
      $tapisSystemProvider->grantSystemPermission($tenantId, $tapisSystemId, $tapisUsername, "MODIFY", $tapisSystemJobWorkingDir, $systemOwnerUid);
    }

    if (!$this->originalJobForRestart) {
      // If this is not a restart, then we need to set the execSystemExecDir,
      // execSystemInputDir, and execSystemOutputDir
      // set the execSystemExecDir, execSystemInputDir, and execSystemOutputDir.
      // ExecDir is where the job will be executed
      // InputDir is where the job will read input files from
      // OutputDir is where the job will write output files to
      // Later, when you want to use Job name
      // as the folder name, instead of Job UUID,
      // yo would replace ${JobUUID} with ${JobName}
      // (or better yet, the macro that Tapis will hopefully soon have
      // that will be job name + an auto-incrementing number
      // at the end to avoid name collisions)
      // $json['execSystemExecDir'] = '${JobWorkingDir}/jobs/${JobUUID}';
      // $json['execSystemInputDir'] = '${JobWorkingDir}/jobs/${JobUUID}';
      // $json['execSystemOutputDir'] =
      // '${JobWorkingDir}/jobs/${JobUUID}/outputs';.
      $json['execSystemExecDir'] = '${JobWorkingDir}/jobs/${JobName}';
      $json['execSystemInputDir'] = '${JobWorkingDir}/jobs/${JobName}';
      $json['execSystemOutputDir'] = '${JobWorkingDir}/jobs/${JobName}/outputs';

      if (!$app->get(AppDrupalIds::APP_JOB_WORKING_DIR_PREFIX)->isEmpty()) {
        $appJobWoringDirPrefix = $app->get(AppDrupalIds::APP_JOB_WORKING_DIR_PREFIX)
          ->getValue()[0]['value'];
        if ($appJobWoringDirPrefix) {
          $appJobWoringDirPrefix = trim($appJobWoringDirPrefix);
          \Drupal::logger("tapis_job")
            ->debug("Application specific job working directory prefix = $appJobWoringDirPrefix");
          // Check if the last character is not a hyphen
          if (substr($appJobWoringDirPrefix, -1) !== '-') {
            // Add a hyphen to the end of the string
            $appJobWoringDirPrefix .= '-';
          }
          $appJobWoringDirPrefix .= '${JobUUID}';
          $json['execSystemExecDir'] = '${JobWorkingDir}/jobs/' . $appJobWoringDirPrefix;
          $json['execSystemInputDir'] = '${JobWorkingDir}/jobs/' . $appJobWoringDirPrefix;
          $json['execSystemOutputDir'] = '${JobWorkingDir}/jobs/' . $appJobWoringDirPrefix . '/outputs';
        }
      }
    }

    $appId = $app->get(AppDrupalIds::APP_TAPIS_ID)->getValue()[0]['value'];
    $appVersion = $app->get(AppDrupalIds::APP_VERSION)->getValue()[0]['value'];

    $json['appId'] = $appId;
    $json['appVersion'] = $appVersion;
    $json['execSystemId'] = $system->get(SystemDrupalIds::SYSTEM_TAPIS_ID)->getValue()[0]['value'];
    $json['parameterSet'] = [
      'envVariables' => [],
      'containerArgs' => [],
      'appArgs' => [],
      'schedulerOptions' => [],
    ];

    // Add all the environment variables.
    foreach ($envVars as $envVar) {
      // Split the envVar into name and value.
      $envVar = explode("=", $envVar);
      $json['parameterSet']['envVariables'][] = [
        'key' => $envVar[0],
        'value' => $envVar[1] ?? "",
      ];
    }

    $appInputType = $app->get(AppDrupalIds::APP_INPUT_TYPE)->getValue()[0]['value'];
    if ($appInputType === "flexible_parameters" && !empty($appArgsString)) {
      $json['parameterSet']['appArgs'][] = [
        'name' => 'fixed_command',
        'arg' => $appArgsString,
      ];
    }

    if (!$app->get(AppDrupalIds::APP_CONTAINER_ARGS)->isEmpty()) {
      $containerArgsString = $app->get(AppDrupalIds::APP_CONTAINER_ARGS)->getValue()[0]['value'];
      if ($containerArgsString) {
        $containerArgsString = trim($containerArgsString);

        /*
         * containerArgsString will be a string of the form:
         * key1 value1
         * key2 value2
         * ...
         * where key is the name of the container argument and value
         * is the value of the container argument.
         * Each line will be a separate container argument,
         * and the value can be empty.
         * When a value is present, it will be separated from the key
         * by the first space character (' ').
         * Otherwise, the line will just be the key
         * (and if a space character is present at the end, it will be ignored).
         *
         * Note: the value may contain a space character, but the key cannot.
         */
        // Parse the containerArgsString into an array of container arguments.
        $containerArgLineIndex = 0;
        foreach (explode("\n", $containerArgsString) as $containerArgLine) {
          $containerArgLine = trim($containerArgLine);

          // Use the Drupal token service to replace tokens
          // in the container argument (include current user in context)
          $containerArgLine = \Drupal::token()->replace($containerArgLine,
            [
              'tapis_job' => $this,
              'user' => \Drupal::currentUser(),
            ]
          );
          $json['parameterSet']['containerArgs'][] = [
            'name' => "container_arg_$containerArgLineIndex",
            "arg" => $containerArgLine,
          ];
          $containerArgLineIndex++;
        }
      }
    }

    for ($i = 0; $i < count($schedulerOptions); $i++) {
      $json['parameterSet']['schedulerOptions'][] = ["arg" => $schedulerOptions[$i]];
    }

    if ($this->allocationId) {
      $json['parameterSet']['schedulerOptions'][] = ["arg" => "--account " . trim($this->allocationId)];
    }

    $appType = $app->get(AppDrupalIds::APP_TYPE)->getValue()[0]['value'];

    $app_runtime = $app->get(AppDrupalIds::APP_RUNTIME)->getValue()[0]['value'];

    if ($appType === "web" || $appType === "vnc" || $app_runtime === "EXECUTABLE") {
      $is_executable_app = ($app_runtime === "EXECUTABLE") ? "1" : "0";
      $setup_app_proxying = ($appType === "web" || $appType === "vnc") ? "1" : "0";

      $satellite_proxy_url = $tenant->get(TenantDrupalIds::TENANT_SATELLITE_PROXY_URL)->getValue()[0]['uri'];
      // Remove the http:// and https:// schemes from the url.
      $satellite_proxy_url = preg_replace('/^https?:\/\//', '', $satellite_proxy_url);

      $proxyId = "0";
      if ($appType === "web" || $appType === "vnc") {
        $proxyId = $this->getProxyId();
      }

      $launcher_path = $system->get(SystemDrupalIds::SYSTEM_LAUNCHER_PATH)->getValue()[0]['value'];
      if ($launcher_path) {
        // Trim.
        $launcher_path = trim($launcher_path);
      }
      $json['cmdPrefix'] = "$launcher_path $is_executable_app $setup_app_proxying $satellite_proxy_url $proxyId ";
    }

    // Handle custom job resources (if allowed by this app)
    $user_can_override_num_nodes = boolval($app->get(AppDrupalIds::OVERRIDE_NUM_NODES)->getValue()[0]['value']);
    $user_can_override_cores_per_node = boolval($app->get(AppDrupalIds::OVERRIDE_CORES_PER_NODE)->getValue()[0]['value']);
    $user_can_override_memory = boolval($app->get(AppDrupalIds::OVERRIDE_MEMORY)->getValue()[0]['value']);
    $user_can_override_max_runtime = boolval($app->get(AppDrupalIds::OVERRIDE_MAX_RUNTIME)->getValue()[0]['value']);

    if ($user_can_override_num_nodes && array_key_exists(AppDrupalIds::APP_NUM_NODES, $this->customJobResources)) {
      $json['nodeCount'] = intval($this->customJobResources[AppDrupalIds::APP_NUM_NODES]);
    }
    if ($user_can_override_cores_per_node && array_key_exists(AppDrupalIds::APP_CORES_PER_NODE, $this->customJobResources)) {
      $json['coresPerNode'] = intval($this->customJobResources[AppDrupalIds::APP_CORES_PER_NODE]);
    }
    if ($user_can_override_memory && array_key_exists(AppDrupalIds::APP_MEMORY_MB, $this->customJobResources)) {
      $json['memoryMB'] = intval($this->customJobResources[AppDrupalIds::APP_MEMORY_MB]);
    }
    if ($user_can_override_max_runtime && array_key_exists(AppDrupalIds::APP_MAX_MINUTES, $this->customJobResources)) {
      $json['maxMinutes'] = intval($this->customJobResources[AppDrupalIds::APP_MAX_MINUTES]);
    }

    // Check if the $jobFileInputArrays is not empty.
    if (!empty($jobFileInputArrays)) {
      $json['fileInputArrays'] = $jobFileInputArrays;
    }

    // Merge in custom properties.
    if ($this->customProperties) {
      $json = array_merge($json, $this->customProperties);
    }

    return $json;
  }

  /**
   * {@inheritdoc}
   */
  public function getTenant() {
    $tenant = $this->get("tenant")->first();
    // Return $tenant?->get("entity")->getTarget()->getValue();
    if (!empty($tenant)) {
      /** @var \Drupal\Core\Entity\Plugin\DataType\EntityReference $tenantEntity */
      $tenantEntity = $tenant->get("entity");
      return $tenantEntity->getTarget()->getValue();
    }
    else {
      return NULL;
    }
  }

  /**
   * {@inheritdoc}
   */
  public function getApp() {
    $app = $this->get("app")->first();
    // $app_target = $app?->get("entity")->getTarget();
    if (!empty($app)) {
      /** @var \Drupal\Core\Entity\Plugin\DataType\EntityReference $appEntity */
      $appEntity = $app->get("entity");
      $app_target = $appEntity->getTarget();
    }
    else {
      $app_target = NULL;
    }
    if (!$app_target) {
      \Drupal::messenger()->addError("The app for this job id could not be found: " . $this->id());
      return NULL;
    }
    return $app_target->getValue();
  }

  /**
   * {@inheritdoc}
   */
  public function getProxyId() {
    return $this->get("proxyId")->value;
  }

  /**
   * {@inheritdoc}
   */
  public function getSystem() {
    $system = $this->get("system")->first();
    // Return $system?->get("entity")->getTarget()->getValue();
    if (!empty($system)) {
      /** @var \Drupal\Core\Entity\Plugin\DataType\EntityReference $systemEntity */
      $systemEntity = $system->get("entity");
      if ($systemEntity->getTarget()) {
        return $systemEntity->getTarget()->getValue();
      }
      else {
        return NULL;
      }
    }
    else {
      return NULL;
    }
  }

}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc