portainer/portainer

View on GitHub
app/kubernetes/views/applications/create/createApplication.html

Summary

Maintainability
Test Coverage
<page-header
  ng-if="!ctrl.state.isEdit && !ctrl.stack.IsComposeFormat && ctrl.state.viewReady"
  title="'Create application'"
  breadcrumbs="[
    { label:'Applications', link:'kubernetes.applications' },
     'Create an application'
     ]"
  reload="true"
>
</page-header>

<page-header
  ng-if="ctrl.state.isEdit && !ctrl.stack.IsComposeFormat && ctrl.state.viewReady"
  title="'Edit application'"
  breadcrumbs="[
    { label:'Namespaces', link:'kubernetes.resourcePools' },
    {
      label:ctrl.application.ResourcePool,
      link: 'kubernetes.resourcePools.resourcePool',
      linkParams:{ id: ctrl.application.ResourcePool }
    },
    { label:'Applications', link:'kubernetes.applications' },
    {
      label:ctrl.application.Name,
      link: 'kubernetes.applications.application',
      linkParams:{ name: ctrl.application.Name, namespace: ctrl.application.ResourcePool }
    },
     'Edit',
     ]"
  reload="true"
>
</page-header>

<page-header
  ng-if="ctrl.stack.IsComposeFormat"
  title="'View application'"
  breadcrumbs="[
    { label:'Namespaces', link:'kubernetes.resourcePools' },
    {
      label:ctrl.application.ResourcePool,
      link: 'kubernetes.resourcePools.resourcePool',
      linkParams:{ id: ctrl.application.ResourcePool }
    },
    { label:'Applications', link:'kubernetes.applications' },
    {
      label:ctrl.application.Name,
      link: 'kubernetes.applications.application',
      linkParams:{ name: ctrl.application.Name, namespace: ctrl.application.ResourcePool }
    },
     'View',
     ]"
  reload="true"
>
</page-header>

<kubernetes-view-loading view-ready="ctrl.state.viewReady"></kubernetes-view-loading>
<div ng-if="ctrl.state.viewReady">
  <div class="row kubernetes-create">
    <div class="col-xs-12">
      <rd-widget>
        <rd-widget-body>
          <form class="form-horizontal mt-4" name="kubernetesApplicationCreationForm" autocomplete="off" novalidate>
            <div ng-if="ctrl.isExternalApplication()">
              <div class="col-sm-12 form-section-title" ng-if="ctrl.state.appType === ctrl.KubernetesDeploymentTypes.APPLICATION_FORM"> Namespace </div>
              <!-- #region NAMESPACE -->
              <namespace-selector
                values="ctrl.formValues.ResourcePool.Namespace.Name"
                on-change="(ctrl.onChangeNamespaceName)"
                validation-data="{hasQuota: ctrl.state.resourcePoolHasQuota, isResourceQuotaCapacityExceeded: ctrl.resourceQuotaCapacityExceeded(), namespaceOptionCount: ctrl.resourcePools.length, isEnvironmentAdmin: ctrl.isAdmin}"
                is-edit="ctrl.state.isEdit"
              ></namespace-selector>
              <!-- kubernetes services options -->
              <div ng-if="ctrl.formValues.ResourcePool">
                <kube-services-form
                  on-change="(ctrl.onServicesChange)"
                  values="ctrl.formValues.Services"
                  app-name="ctrl.formValues.Name"
                  selector="ctrl.formValues.Selector"
                  validation-data="{nodePortServices: ctrl.state.nodePortServices, formServices: ctrl.formValues.Services, ingressPaths: ctrl.ingressPaths, originalIngressPaths: ctrl.originalIngressPaths}"
                  is-edit-mode="ctrl.state.isEdit"
                  namespace="ctrl.formValues.ResourcePool.Namespace.Name"
                ></kube-services-form>
              </div>
              <!-- kubernetes services options -->

              <!-- kubernetes summary for external application -->
              <!--  below workaround is needed because we wanted react to render it again 
                by hiding/showing it, but the page was fluctuating. so we added two blocks for both the conditions -->
              <div ng-if="!ctrl.isTemporaryRefresh">
                <application-summary-section
                  application-kind="ctrl.formValues.ApplicationType"
                  form-values="ctrl.formValues"
                  old-form-values="ctrl.savedFormValues"
                  ng-if="kubernetesApplicationCreationForm.$valid && ctrl.isExternalApplication()"
                ></application-summary-section>
              </div>
              <div ng-if="ctrl.isTemporaryRefresh">
                <application-summary-section
                  application-kind="ctrl.formValues.ApplicationType"
                  form-values="ctrl.formValues"
                  old-form-values="ctrl.savedFormValues"
                  ng-if="kubernetesApplicationCreationForm.$valid && ctrl.isExternalApplication()"
                ></application-summary-section>
              </div>
              <!-- kubernetes summary for external application -->
              <div class="col-sm-12 form-section-title !mt-6" ng-if="ctrl.state.appType !== ctrl.KubernetesDeploymentTypes.GIT" ng-hide="ctrl.stack.IsComposeFormat"> Actions </div>
              <!-- #region ACTIONS -->
              <div class="form-group" ng-hide="ctrl.stack.IsComposeFormat">
                <div class="col-sm-12">
                  <button
                    ng-if="ctrl.state.appType === ctrl.KubernetesDeploymentTypes.APPLICATION_FORM"
                    type="button"
                    class="btn btn-primary btn-sm !ml-0"
                    ng-disabled="!kubernetesApplicationCreationForm.$valid || ctrl.isDeployUpdateButtonDisabled() || !ctrl.imageValidityIsValid() || ctrl.hasPortErrors() || !ctrl.formValues.ResourcePool"
                    ng-click="ctrl.deployApplication()"
                    button-spinner="ctrl.state.actionInProgress"
                    data-cy="k8sAppCreate-deployButton"
                  >
                    <span ng-show="!ctrl.state.isEdit && !ctrl.state.actionInProgress">Deploy application</span>
                    <span ng-show="!ctrl.state.isEdit && ctrl.state.actionInProgress">Deployment in progress...</span>
                    <span ng-show="ctrl.state.isEdit && !ctrl.state.actionInProgress">Update application</span>
                    <span ng-show="ctrl.state.isEdit && ctrl.state.actionInProgress">Update in progress...</span>
                  </button>
                  <button
                    ng-if="ctrl.state.isEdit && !ctrl.state.actionInProgress && ctrl.state.appType === ctrl.KubernetesDeploymentTypes.APPLICATION_FORM"
                    type="button"
                    class="btn btn-sm btn-default"
                    ui-sref="kubernetes.applications.application({ name: ctrl.application.Name, namespace: ctrl.application.ResourcePool })"
                    data-cy="k8sAppCreate-appCancelButton"
                  >
                    Cancel
                  </button>
                  <!-- #Web editor buttons -->
                  <button
                    class="btn btn-sm btn-primary"
                    ng-click="ctrl.updateApplicationViaWebEditor()"
                    ng-if="ctrl.state.appType === ctrl.KubernetesDeploymentTypes.CONTENT || ctrl.state.updateWebEditorInProgress"
                    ng-disabled="ctrl.isUpdateApplicationViaWebEditorButtonDisabled() || !kubernetesApplicationCreationForm.$valid"
                    style="margin-top: 7px; margin-left: 0"
                    button-spinner="ctrl.state.updateWebEditorInProgress"
                  >
                    <span ng-show="!ctrl.state.updateWebEditorInProgress">Update application</span>
                    <span ng-show="ctrl.state.updateWebEditorInProgress">Update in progress...</span>
                  </button>
                </div>
              </div>
              <!-- #endregion -->
            </div>
            <div ng-if="!ctrl.isExternalApplication()">
              <git-form-info-panel
                ng-if="ctrl.state.appType == ctrl.KubernetesDeploymentTypes.GIT"
                class-name="'text-muted'"
                url="ctrl.stack.GitConfig.URL"
                config-file-path="ctrl.stack.GitConfig.ConfigFilePath"
                additional-files="ctrl.stack.AdditionalFiles"
                type="'application'"
              ></git-form-info-panel>
              <!-- #region NAMESPACE -->
              <namespace-selector
                values="ctrl.formValues.ResourcePool.Namespace.Name"
                on-change="(ctrl.onChangeNamespaceName)"
                validation-data="{hasQuota: ctrl.state.resourcePoolHasQuota, isResourceQuotaCapacityExceeded: ctrl.resourceQuotaCapacityExceeded(), namespaceOptionCount: ctrl.resourcePools.length, isEnvironmentAdmin: ctrl.isAdmin}"
                is-edit="ctrl.state.isEdit"
              ></namespace-selector>
              <!-- #endregion -->

              <!-- #region STACK -->
              <kube-stack-name
                ng-if="!ctrl.deploymentOptions.hideStacksFunctionality && ctrl.state.appType !== ctrl.KubernetesDeploymentTypes.APPLICATION_FORM"
                stack-name="ctrl.formValues.StackName"
                set-stack-name="(ctrl.onChangeStackName)"
                text-tip="'Enter or select a \'stack\' name to group multiple deployments together, or else leave empty to ignore.'"
                stacks="ctrl.stacks"
                input-class-name="'col-lg-10 col-sm-9'"
              ></kube-stack-name>
              <!-- #endregion -->

              <!-- #region Git repository -->
              <kubernetes-redeploy-app-git-form
                ng-if="ctrl.state.appType === ctrl.KubernetesDeploymentTypes.GIT"
                stack="ctrl.stack"
                namespace="ctrl.formValues.ResourcePool.Namespace.Name"
                stack-name="ctrl.formValues.StackName"
              ></kubernetes-redeploy-app-git-form>
              <!-- #endregion -->

              <!-- #region web editor -->
              <edit-yaml-form-section
                ng-if="ctrl.state.appType === ctrl.KubernetesDeploymentTypes.CONTENT"
                values="ctrl.stackFileContent"
                on-change="(ctrl.onChangeFileContent)"
                is-compose-format="ctrl.stack.IsComposeFormat"
              ></edit-yaml-form-section>
              <!-- #endregion -->
              <div ng-if="ctrl.state.appType === ctrl.KubernetesDeploymentTypes.APPLICATION_FORM">
                <!-- #region NAME FIELD -->
                <name-form-section
                  values="ctrl.formValues.Name"
                  on-change="(ctrl.onChangeAppName)"
                  is-edit="ctrl.state.isEdit"
                  validation-data="{existingNames: ctrl.applicationNames, isEdit: ctrl.state.isEdit, originalName: ctrl.application.Name}"
                ></name-form-section>
                <!-- #endregion -->

                <!-- #region IMAGE FIELD -->
                <div class="form-group mb-2">
                  <div class="col-sm-12">
                    <por-image-registry
                      model="ctrl.formValues.ImageModel"
                      ng-if="ctrl.formValues.ResourcePool"
                      auto-complete="false"
                      label-class="col-sm-3 col-lg-2"
                      input-class="col-sm-9 col-lg-10"
                      namespace="ctrl.formValues.ResourcePool.Namespace.Name"
                      endpoint="ctrl.endpoint"
                      is-admin="ctrl.isAdmin"
                      check-rate-limits="true"
                      set-validity="ctrl.setPullImageValidity"
                    ></por-image-registry>
                  </div>
                </div>
                <!-- #end region IMAGE FIELD -->

                <div class="col-sm-12 mb-4 !p-0">
                  <annotations-be-teaser></annotations-be-teaser>
                </div>

                <div ng-if="ctrl.formValues.ResourcePool">
                  <!-- #region STACK -->
                  <kube-stack-name
                    ng-if="!ctrl.deploymentOptions.hideStacksFunctionality"
                    stack-name="ctrl.formValues.StackName"
                    set-stack-name="(ctrl.onChangeStackName)"
                    text-tip="'Enter or select a \'stack\' name to group multiple deployments together, or else leave empty to ignore.'"
                    stacks="ctrl.stacks"
                    input-class-name="'col-lg-10 col-sm-9'"
                  ></kube-stack-name>
                  <!-- #endregion -->

                  <!-- #region ENVIRONMENT VARIABLES -->
                  <environment-variables-form-section
                    values="ctrl.formValues.EnvironmentVariables"
                    on-change="(ctrl.onEnvironmentVariableChange)"
                  ></environment-variables-form-section>
                  <!-- #endregion -->

                  <!-- #region CONFIGMAPS -->
                  <config-maps-form-section
                    values="ctrl.formValues.ConfigMaps"
                    on-change="(ctrl.onConfigMapsChange)"
                    namespace="ctrl.formValues.ResourcePool.Namespace.Name"
                    validation-data="ctrl.formValues.ConfigMaps"
                  ></config-maps-form-section>

                  <!-- #region SECRETS -->
                  <secrets-form-section
                    values="ctrl.formValues.Secrets"
                    on-change="(ctrl.onSecretsChange)"
                    namespace="ctrl.formValues.ResourcePool.Namespace.Name"
                    validation-data="ctrl.formValues.Secrets"
                  ></secrets-form-section>
                  <!-- #endregion -->

                  <persisted-folders-form-section
                    values="ctrl.formValues.PersistedFolders"
                    initial-values="ctrl.formValues.OriginalPersistedFolders"
                    on-change="(ctrl.onChangePersistedFolder)"
                    is-edit="ctrl.state.isEdit"
                    application-values="ctrl.formValues"
                    is-add-persistent-folder-button-shown="ctrl.isAddPersistentFolderButtonShown()"
                    available-volumes="ctrl.availableVolumes"
                    validation-data="{ namespaceQuotas: ctrl.formValues.ResourcePool.Quota, persistedFolders: ctrl.formValues.PersistedFolders, storageAvailabilities: ctrl.state.storages.availabilities }"
                  ></persisted-folders-form-section>

                  <!-- #region DATA ACCESS POLICY -->
                  <access-policy-form-section
                    ng-if="ctrl.showDataAccessPolicySection()"
                    value="ctrl.formValues.DataAccessPolicy"
                    on-change="(ctrl.onDataAccessPolicyChange)"
                    is-edit="ctrl.state.isEdit"
                    persisted-folders-use-existing-volumes="ctrl.state.persistedFoldersUseExistingVolumes"
                  ></access-policy-form-section>
                  <!-- #endregion -->

                  <resource-reservation-form-section
                    values="{memoryLimit: ctrl.formValues.MemoryLimit, cpuLimit: ctrl.formValues.CpuLimit}"
                    on-change="(ctrl.onChangeResourceReservation)"
                    namespace-has-quota="ctrl.state.resourcePoolHasQuota"
                    min-memory-limit="ctrl.state.sliders.memory.min"
                    min-cpu-limit="ctrl.state.sliders.cpu.min"
                    max-memory-limit="ctrl.state.sliders.memory.max"
                    max-cpu-limit="ctrl.state.sliders.cpu.max"
                    validation-data="{isExistingCPUReservationUnchanged: ctrl.state.isExistingCPUReservationUnchanged, isExistingMemoryReservationUnchanged: ctrl.state.isExistingMemoryReservationUnchanged, maxMemoryLimit: ctrl.state.sliders.memory.max, maxCpuLimit: ctrl.state.sliders.cpu.max, isEnvironmentAdmin: ctrl.isAdmin, nodeLimits: ctrl.nodesLimits.nodesLimits}"
                    resource-quota-capacity-exceeded="ctrl.resourceQuotaCapacityExceeded()"
                  ></resource-reservation-form-section>

                  <!-- deployment options -->
                  <app-deployment-type-form-section
                    values="ctrl.formValues.DeploymentType"
                    on-change="(ctrl.onChangeDeploymentType)"
                    support-global-deployment="ctrl.supportGlobalDeployment()"
                    radio-name="'deploymentType'"
                    validation-data="{isQuotaExceeded: ctrl.resourceReservationsOverflow()}"
                  ></app-deployment-type-form-section>

                  <!-- replica count -->
                  <div ng-if="ctrl.formValues.DeploymentType === ctrl.ApplicationDeploymentTypes.Replicated">
                    <replication-form-section
                      values="{replicaCount: ctrl.formValues.ReplicaCount}"
                      on-change="(ctrl.onChangeReplicaCount)"
                      support-scalable-replica-deployment="ctrl.supportScalableReplicaDeployment()"
                      memory-limit="ctrl.formValues.MemoryLimit"
                      cpu-limit="ctrl.formValues.CpuLimit"
                      resource-reservations-overflow="ctrl.resourceReservationsOverflow()"
                      non-scalable-storage="ctrl.getNonScalableStorage()"
                      validation-data="{resourceReservationsOverflow: ctrl.resourceReservationsOverflow(), quotaExceeded: ctrl.state.storages.quotaExceeded, nonScalableStorage: ctrl.getNonScalableStorage(), supportScalableReplicaDeployment: ctrl.supportScalableReplicaDeployment()}"
                    ></replication-form-section>
                  </div>
                  <!-- #endregion -->

                  <!-- #region AUTO SCALING -->
                  <div ng-if="ctrl.formValues.DeploymentType !== ctrl.ApplicationDeploymentTypes.Global">
                    <auto-scaling-form-section
                      values="ctrl.formValues.AutoScaler"
                      on-change="(ctrl.onAutoScaleChange)"
                      validation-data="{autoScalerOverflow: ctrl.autoScalerOverflow()}"
                      is-metrics-enabled="ctrl.state.useServerMetrics"
                    ></auto-scaling-form-section>
                  </div>
                  <!-- #endregion -->
                </div>

                <div class="mb-8" ng-if="ctrl.formValues.ResourcePool">
                  <placement-form-section
                    ng-show="ctrl.formValues.DeploymentType === ctrl.ApplicationDeploymentTypes.Replicated"
                    values="{placements: ctrl.formValues.Placements, placementType: ctrl.formValues.PlacementType}"
                    on-change="(ctrl.onChangePlacements)"
                  ></placement-form-section>

                  <!-- kubernetes services options -->
                  <kube-services-form
                    on-change="(ctrl.onServicesChange)"
                    values="ctrl.formValues.Services"
                    load-balancer-enabled="ctrl.publishViaLoadBalancerEnabled()"
                    app-name="ctrl.formValues.Name"
                    selector="ctrl.formValues.Selector"
                    validation-data="{nodePortServices: ctrl.state.nodePortServices, formServices: ctrl.formValues.Services, ingressPaths: ctrl.ingressPaths, originalIngressPaths: ctrl.originalIngressPaths}"
                    is-edit-mode="ctrl.state.isEdit"
                    namespace="ctrl.formValues.ResourcePool.Namespace.Name"
                  ></kube-services-form>
                </div>
                <!-- kubernetes services options -->

                <!-- application summary start -->
                <!--  below workaround is needed because we wanted react to render it again 
                  by hiding/showing it, but the page was fluctuating. so we added two blocks for both the conditions -->
                <div ng-if="!ctrl.isTemporaryRefresh">
                  <!-- summary -->
                  <application-summary-section
                    application-kind="ctrl.formValues.ApplicationType"
                    ng-if="!(!kubernetesApplicationCreationForm.$valid || ctrl.isDeployUpdateButtonDisabled() || !ctrl.state.pullImageValidity)"
                    form-values="ctrl.formValues"
                    old-form-values="ctrl.savedFormValues"
                  ></application-summary-section>
                </div>
                <div ng-if="ctrl.isTemporaryRefresh">
                  <!-- summary -->
                  <application-summary-section
                    application-kind="ctrl.formValues.ApplicationType"
                    ng-if="!(!kubernetesApplicationCreationForm.$valid || ctrl.isDeployUpdateButtonDisabled() || !ctrl.state.pullImageValidity)"
                    form-values="ctrl.formValues"
                    old-form-values="ctrl.savedFormValues"
                  ></application-summary-section>
                </div>
                <!-- application summary end -->
              </div>
              <!-- #region ACTIONS -->
              <div class="col-sm-12 form-section-title !mt-6" ng-if="ctrl.state.appType !== ctrl.KubernetesDeploymentTypes.GIT" ng-hide="ctrl.stack.IsComposeFormat"> Actions </div>
              <div class="form-group" ng-hide="ctrl.stack.IsComposeFormat">
                <div class="col-sm-12">
                  <button
                    ng-if="ctrl.state.appType === ctrl.KubernetesDeploymentTypes.APPLICATION_FORM"
                    type="button"
                    class="btn btn-primary btn-sm !ml-0"
                    ng-disabled="!kubernetesApplicationCreationForm.$valid || ctrl.isDeployUpdateButtonDisabled() || !ctrl.imageValidityIsValid() || ctrl.hasPortErrors() || !ctrl.formValues.ResourcePool"
                    ng-click="ctrl.deployApplication()"
                    button-spinner="ctrl.state.actionInProgress"
                    data-cy="k8sAppCreate-deployButton"
                  >
                    <span ng-show="!ctrl.state.isEdit && !ctrl.state.actionInProgress">Deploy application</span>
                    <span ng-show="!ctrl.state.isEdit && ctrl.state.actionInProgress">Deployment in progress...</span>
                    <span ng-show="ctrl.state.isEdit && !ctrl.state.actionInProgress">Update application</span>
                    <span ng-show="ctrl.state.isEdit && ctrl.state.actionInProgress">Update in progress...</span>
                  </button>
                  <button
                    ng-if="ctrl.state.isEdit && !ctrl.state.actionInProgress && ctrl.state.appType === ctrl.KubernetesDeploymentTypes.APPLICATION_FORM"
                    type="button"
                    class="btn btn-sm btn-default"
                    ui-sref="kubernetes.applications.application({ name: ctrl.application.Name, namespace: ctrl.application.ResourcePool })"
                    data-cy="k8sAppCreate-appCancelButton"
                  >
                    Cancel
                  </button>
                  <!-- #Web editor buttons -->
                  <button
                    class="btn btn-sm btn-primary"
                    ng-click="ctrl.updateApplicationViaWebEditor()"
                    ng-if="ctrl.state.appType === ctrl.KubernetesDeploymentTypes.CONTENT || ctrl.state.updateWebEditorInProgress"
                    ng-disabled="ctrl.isUpdateApplicationViaWebEditorButtonDisabled() || !kubernetesApplicationCreationForm.$valid"
                    style="margin-top: 7px; margin-left: 0"
                    button-spinner="ctrl.state.updateWebEditorInProgress"
                  >
                    <span ng-show="!ctrl.state.updateWebEditorInProgress">Update application</span>
                    <span ng-show="ctrl.state.updateWebEditorInProgress">Update in progress...</span>
                  </button>
                </div>
              </div>
            </div>
          </form>
        </rd-widget-body>
      </rd-widget>
    </div>
  </div>
</div>