diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 2329c9c6edc493f520d4c37ef803fc191c41ddba..26087b2cc4d8d367c09d320aa29ac804262fba4d 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -444,6 +444,17 @@ setup-test-env: - master - /(^docs[\/-].*|.*-docs$)/ +.review-schedules-only: &review-schedules-only + only: + refs: + - schedules@gitlab-org/gitlab-ce + - schedules@gitlab-org/gitlab-ee + kubernetes: active + except: + refs: + - tags + - /(^docs[\/-].*|.*-docs$)/ + .review-base: &review-base <<: *dedicated-no-docs-no-db-pull-cache-job <<: *review-only @@ -672,6 +683,35 @@ gitlab:assets:compile: - docker - gitlab-org +gitlab:ui:visual: + <<: *except-docs + tags: + - gitlab-org + before_script: [] + dependencies: + - compile-assets + script: + # Remove node modules from GitLab that may conflict with gitlab-ui + - rm -r node_modules + - git clone https://gitlab.com/gitlab-org/gitlab-ui.git + - cp public/assets/application-*.css gitlab-ui/styles/application.css + - cd gitlab-ui + - yarn install + - CSS_URL=./application.css yarn test + only: + changes: + - app/assets/stylesheets/*.scss + - app/assets/stylesheets/**/*.scss + - app/assets/stylesheets/**/**/*.scss + except: + refs: + - master + variables: + - $CI_COMMIT_MESSAGE =~ /\[skip visual\]/i + artifacts: + paths: + - tests/__image_snapshots__/ + karma: <<: *dedicated-no-docs-pull-cache-job <<: *use-pg @@ -959,8 +999,7 @@ no_ee_check: - //@gitlab-org/gitlab-ce # GitLab Review apps -review-build-cng: - <<: *review-only +.review-build-cng-base: &review-build-cng-base image: ruby:2.5-alpine stage: test before_script: [] @@ -976,7 +1015,15 @@ review-build-cng: - wait_for_job_to_be_done "gitlab:assets:compile" - BUILD_TRIGGER_TOKEN=$REVIEW_APPS_BUILD_TRIGGER_TOKEN ./scripts/trigger-build cng -review-deploy: +review-build-cng: + <<: *review-only + <<: *review-build-cng-base + +schedule:review-build-cng: + <<: *review-schedules-only + <<: *review-build-cng-base + +.review-deploy-base: &review-deploy-base <<: *review-base retry: 2 allow_failure: true @@ -998,6 +1045,8 @@ review-deploy: - source ./scripts/review_apps/review-apps.sh script: - wait_for_job_to_be_done "review-build-cng" + after_script: + - source ./scripts/review_apps/review-apps.sh - check_kube_domain - download_gitlab_chart - ensure_namespace @@ -1006,6 +1055,15 @@ review-deploy: - time deploy - add_license +review-deploy: + <<: *review-deploy-base + +schedule:review-deploy: + <<: *review-deploy-base + <<: *review-schedules-only + script: + - wait_for_job_to_be_done "schedule:review-build-cng" + .review-qa-base: &review-qa-base <<: *review-docker allow_failure: true @@ -1032,22 +1090,26 @@ review-deploy: - apk update && apk add curl jq - source ./scripts/review_apps/review-apps.sh - gem install gitlab-qa --no-document ${GITLAB_QA_VERSION:+ --version ${GITLAB_QA_VERSION}} - - wait_for_job_to_be_done "review-deploy" review-qa-smoke: <<: *review-qa-base script: + - wait_for_job_to_be_done "review-deploy" - gitlab-qa Test::Instance::Smoke "${QA_IMAGE}" "${CI_ENVIRONMENT_URL}" review-qa-all: <<: *review-qa-base script: + - wait_for_job_to_be_done "review-deploy" - gitlab-qa Test::Instance::Any "${QA_IMAGE}" "${CI_ENVIRONMENT_URL}" when: manual -review-performance: + +.review-performance-base: &review-performance-base <<: *review-qa-base script: + - wait_for_job_to_be_done "review-deploy" + after_script: - mkdir gitlab-exporter - wget -O ./gitlab-exporter/index.js https://gitlab.com/gitlab-org/gl-performance/raw/master/index.js - mkdir sitespeed-results @@ -1059,6 +1121,9 @@ review-performance: reports: performance: performance.json +review-performance: + <<: *review-performance-base + review-stop: <<: *review-base <<: *single-script-job @@ -1078,21 +1143,20 @@ review-stop: schedule:review-cleanup: <<: *review-base + <<: *review-schedules-only stage: build allow_failure: true variables: GIT_DEPTH: "1" environment: name: review/auto-cleanup - only: - refs: - - schedules@gitlab-org/gitlab-ce - - schedules@gitlab-org/gitlab-ee - kubernetes: active - except: - - tags - - /(^docs[\/-].*|.*-docs$)/ before_script: - gem install gitlab --no-document script: - ruby -rrubygems scripts/review_apps/automated_cleanup.rb + +schedule:review-performance: + <<: *review-performance-base + <<: *review-schedules-only + script: + - wait_for_job_to_be_done "schedule:review-deploy" diff --git a/.rubocop.yml b/.rubocop.yml index bcff67ded8cfa153835c0cbaaf2f684e191a567f..2985c1446e43c975e878208b0ca49e9d816a0bb9 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -181,3 +181,6 @@ Cop/InjectEnterpriseEditionModule: Exclude: - 'spec/**/*' - 'ee/spec/**/*' + +Style/ReturnNil: + Enabled: true diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION index bfbadb3a2ac0252360e21743d9f3cc0168f9750c..5ff8c4f5d2ad2fab0f675c329d99cc618ac3e74c 100644 --- a/GITALY_SERVER_VERSION +++ b/GITALY_SERVER_VERSION @@ -1 +1 @@ -1.23.0 \ No newline at end of file +1.26.0 diff --git a/GITLAB_SHELL_VERSION b/GITLAB_SHELL_VERSION index acd405b1d62e3db6545a68a7084bd5fa5dade317..d139a75408e9f9bb9a3b05aada6e3b31b19dfd3e 100644 --- a/GITLAB_SHELL_VERSION +++ b/GITLAB_SHELL_VERSION @@ -1 +1 @@ -8.6.0 +8.7.1 diff --git a/Gemfile b/Gemfile index 2d769284f91f9d0789f9b9b31dff4062f13c812c..2e465f8ced7dd5c433865e9e41fdc297caeee682 100644 --- a/Gemfile +++ b/Gemfile @@ -421,7 +421,7 @@ group :ed25519 do end # Gitaly GRPC client -gem 'gitaly-proto', '~> 1.12.0', require: 'gitaly' +gem 'gitaly-proto', '~> 1.13.0', require: 'gitaly' gem 'grpc', '~> 1.15.0' diff --git a/Gemfile.lock b/Gemfile.lock index e4791a98f2fe436c941881da381c4c906df229b3..4d37075cdfac84d3ac12692b0fcd4e54af11d08a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -279,7 +279,7 @@ GEM gettext_i18n_rails (>= 0.7.1) po_to_json (>= 1.0.0) rails (>= 3.2.0) - gitaly-proto (1.12.0) + gitaly-proto (1.13.0) grpc (~> 1.0) github-markup (1.7.0) gitlab-default_value_for (3.1.1) @@ -310,7 +310,7 @@ GEM representable (~> 3.0) retriable (>= 2.0, < 4.0) google-protobuf (3.6.1) - googleapis-common-protos-types (1.0.2) + googleapis-common-protos-types (1.0.3) google-protobuf (~> 3.0) googleauth (0.6.6) faraday (~> 0.12) @@ -1018,7 +1018,7 @@ DEPENDENCIES gettext (~> 3.2.2) gettext_i18n_rails (~> 1.8.0) gettext_i18n_rails_js (~> 1.3) - gitaly-proto (~> 1.12.0) + gitaly-proto (~> 1.13.0) github-markup (~> 1.7.0) gitlab-default_value_for (~> 3.1.1) gitlab-markup (~> 1.6.5) diff --git a/app/assets/javascripts/awards_handler.js b/app/assets/javascripts/awards_handler.js index 73ce3e760ab3c0e3c28a75cea8b182a04cd5599c..aeb88715c1111104884bccf9d4c2cf7864456f6f 100644 --- a/app/assets/javascripts/awards_handler.js +++ b/app/assets/javascripts/awards_handler.js @@ -615,10 +615,18 @@ export class AwardsHandler { let awardsHandlerPromise = null; export default function loadAwardsHandler(reload = false) { if (!awardsHandlerPromise || reload) { - awardsHandlerPromise = import(/* webpackChunkName: 'emoji' */ './emoji').then(Emoji => { - const awardsHandler = new AwardsHandler(Emoji); - awardsHandler.bindEvents(); - return awardsHandler; + awardsHandlerPromise = new Promise((resolve, reject) => { + import(/* webpackChunkName: 'emoji' */ './emoji') + .then(Emoji => { + Emoji.initEmojiMap() + .then(() => { + const awardsHandler = new AwardsHandler(Emoji); + awardsHandler.bindEvents(); + resolve(awardsHandler); + }) + .catch(() => reject); + }) + .catch(() => reject); }); } return awardsHandlerPromise; diff --git a/app/assets/javascripts/behaviors/gl_emoji.js b/app/assets/javascripts/behaviors/gl_emoji.js index d1d75658181a5df95eddbf4420b85c8de7ae5198..9034563d9b31f164d4cad042723016acd8b92fab 100644 --- a/app/assets/javascripts/behaviors/gl_emoji.js +++ b/app/assets/javascripts/behaviors/gl_emoji.js @@ -1,47 +1,74 @@ import 'document-register-element'; import isEmojiUnicodeSupported from '../emoji/support'; +import { initEmojiMap, getEmojiInfo, emojiFallbackImageSrc, emojiImageTag } from '../emoji'; class GlEmoji extends HTMLElement { constructor() { super(); - const emojiUnicode = this.textContent.trim(); - const { name, unicodeVersion, fallbackSrc, fallbackSpriteClass } = this.dataset; - - const isEmojiUnicode = - this.childNodes && - Array.prototype.every.call(this.childNodes, childNode => childNode.nodeType === 3); - const hasImageFallback = fallbackSrc && fallbackSrc.length > 0; - const hasCssSpriteFalback = fallbackSpriteClass && fallbackSpriteClass.length > 0; - - if (emojiUnicode && isEmojiUnicode && !isEmojiUnicodeSupported(emojiUnicode, unicodeVersion)) { - // CSS sprite fallback takes precedence over image fallback - if (hasCssSpriteFalback) { - if (!gon.emoji_sprites_css_added && gon.emoji_sprites_css_path) { - const emojiSpriteLinkTag = document.createElement('link'); - emojiSpriteLinkTag.setAttribute('rel', 'stylesheet'); - emojiSpriteLinkTag.setAttribute('href', gon.emoji_sprites_css_path); - document.head.appendChild(emojiSpriteLinkTag); - gon.emoji_sprites_css_added = true; - } - // IE 11 doesn't like adding multiple at once :( - this.classList.add('emoji-icon'); - this.classList.add(fallbackSpriteClass); - } else { - import(/* webpackChunkName: 'emoji' */ '../emoji') - .then(({ emojiImageTag, emojiFallbackImageSrc }) => { - if (hasImageFallback) { - this.innerHTML = emojiImageTag(name, fallbackSrc); + let emojiUnicode = this.textContent.trim(); + const { fallbackSpriteClass, fallbackSrc, forceFallback } = this.dataset; + let { name, unicodeVersion } = this.dataset; + + initEmojiMap() + .then(() => { + if (!unicodeVersion) { + const emojiInfo = getEmojiInfo(name); + + if (emojiInfo) { + if (name !== emojiInfo.name) { + ({ name } = emojiInfo); + this.dataset.name = emojiInfo.name; + } + unicodeVersion = emojiInfo.u; + this.dataset.uni = unicodeVersion; + + if (forceFallback === 'true' && !fallbackSpriteClass) { + this.innerHTML = emojiImageTag(name, emojiFallbackImageSrc(name)); } else { - const src = emojiFallbackImageSrc(name); - this.innerHTML = emojiImageTag(name, src); + emojiUnicode = emojiInfo.e; + this.innerHTML = emojiInfo.e; } - }) - .catch(() => { - // do nothing - }); - } - } + + this.title = emojiInfo.d; + } + } + + const isEmojiUnicode = + this.childNodes && + Array.prototype.every.call(this.childNodes, childNode => childNode.nodeType === 3); + const hasImageFallback = fallbackSrc && fallbackSrc.length > 0; + const hasCssSpriteFalback = fallbackSpriteClass && fallbackSpriteClass.length > 0; + + if ( + emojiUnicode && + isEmojiUnicode && + !isEmojiUnicodeSupported(emojiUnicode, unicodeVersion) + ) { + // CSS sprite fallback takes precedence over image fallback + if (hasCssSpriteFalback) { + if (!gon.emoji_sprites_css_added && gon.emoji_sprites_css_path) { + const emojiSpriteLinkTag = document.createElement('link'); + emojiSpriteLinkTag.setAttribute('rel', 'stylesheet'); + emojiSpriteLinkTag.setAttribute('href', gon.emoji_sprites_css_path); + document.head.appendChild(emojiSpriteLinkTag); + gon.emoji_sprites_css_added = true; + } + // IE 11 doesn't like adding multiple at once :( + this.classList.add('emoji-icon'); + this.classList.add(fallbackSpriteClass); + } else if (hasImageFallback) { + this.innerHTML = emojiImageTag(name, fallbackSrc); + } else { + const src = emojiFallbackImageSrc(name); + this.innerHTML = emojiImageTag(name, src); + } + } + }) + .catch(error => { + // Only reject is already handled in initEmojiMap + throw error; + }); } } diff --git a/app/assets/javascripts/blob_edit/blob_bundle.js b/app/assets/javascripts/blob_edit/blob_bundle.js index 5f64175362dfdb1d328c84b115e3c9a364c73c7d..6aaf5bf7296f5af6ef46826c2563c77b2538ac7f 100644 --- a/app/assets/javascripts/blob_edit/blob_bundle.js +++ b/app/assets/javascripts/blob_edit/blob_bundle.js @@ -13,7 +13,7 @@ export default () => { if (editBlobForm.length) { const urlRoot = editBlobForm.data('relativeUrlRoot'); const assetsPath = editBlobForm.data('assetsPrefix'); - const filePath = editBlobForm.data('blobFilename'); + const filePath = `${editBlobForm.data('blobFilename')}`; const currentAction = $('.js-file-title').data('currentAction'); const projectId = editBlobForm.data('project-id'); const isMarkdown = editBlobForm.data('is-markdown'); diff --git a/app/assets/javascripts/boards/components/issue_due_date.vue b/app/assets/javascripts/boards/components/issue_due_date.vue index 9c4c663297683c95dca4a86542e07711c7d2a117..9bc66978198e9724a94ebd3589125db200f3dee3 100644 --- a/app/assets/javascripts/boards/components/issue_due_date.vue +++ b/app/assets/javascripts/boards/components/issue_due_date.vue @@ -53,7 +53,7 @@ export default { } else if (timeDifference === -1) { return __('Yesterday'); } else if (timeDifference > 0 && timeDifference < 7) { - return dateFormat(issueDueDate, 'dddd', true); + return dateFormat(issueDueDate, 'dddd'); } return standardDateFormat; diff --git a/app/assets/javascripts/boards/index.js b/app/assets/javascripts/boards/index.js index f88e9b55988543dccfa71dbecdfd9178aeb2e426..c4c5fedc615835a397151bf981446c8bd1207875 100644 --- a/app/assets/javascripts/boards/index.js +++ b/app/assets/javascripts/boards/index.js @@ -58,6 +58,7 @@ export default () => { state: boardsStore.state, loading: true, boardsEndpoint: $boardApp.dataset.boardsEndpoint, + recentBoardsEndpoint: $boardApp.dataset.recentBoardsEndpoint, listsEndpoint: $boardApp.dataset.listsEndpoint, boardId: $boardApp.dataset.boardId, disabled: parseBoolean($boardApp.dataset.disabled), @@ -75,6 +76,7 @@ export default () => { created() { gl.boardService = new BoardService({ boardsEndpoint: this.boardsEndpoint, + recentBoardsEndpoint: this.recentBoardsEndpoint, listsEndpoint: this.listsEndpoint, bulkUpdatePath: this.bulkUpdatePath, boardId: this.boardId, diff --git a/app/assets/javascripts/boards/services/board_service.js b/app/assets/javascripts/boards/services/board_service.js index 3de6eb056c25039dcd43b645f655e31dcd2a6ec6..7d463f17ab1a1474b7c6743853579b9e8ba44ba6 100644 --- a/app/assets/javascripts/boards/services/board_service.js +++ b/app/assets/javascripts/boards/services/board_service.js @@ -2,12 +2,13 @@ import axios from '../../lib/utils/axios_utils'; import { mergeUrlParams } from '../../lib/utils/url_utility'; export default class BoardService { - constructor({ boardsEndpoint, listsEndpoint, bulkUpdatePath, boardId }) { + constructor({ boardsEndpoint, listsEndpoint, bulkUpdatePath, boardId, recentBoardsEndpoint }) { this.boardsEndpoint = boardsEndpoint; this.boardId = boardId; this.listsEndpoint = listsEndpoint; this.listsEndpointGenerate = `${listsEndpoint}/generate.json`; this.bulkUpdatePath = bulkUpdatePath; + this.recentBoardsEndpoint = `${recentBoardsEndpoint}.json`; } generateBoardsPath(id) { diff --git a/app/assets/javascripts/clusters/clusters_bundle.js b/app/assets/javascripts/clusters/clusters_bundle.js index 6ebd1ad109ea9e9e706c05d03b65f7806ab842a1..388f674f643384bb143249e5d825ae83e2d50244 100644 --- a/app/assets/javascripts/clusters/clusters_bundle.js +++ b/app/assets/javascripts/clusters/clusters_bundle.js @@ -36,6 +36,7 @@ export default class Clusters { installRunnerPath, installJupyterPath, installKnativePath, + updateKnativePath, installPrometheusPath, managePrometheusPath, hasRbac, @@ -62,6 +63,7 @@ export default class Clusters { installPrometheusEndpoint: installPrometheusPath, installJupyterEndpoint: installJupyterPath, installKnativeEndpoint: installKnativePath, + updateKnativeEndpoint: updateKnativePath, }); this.installApplication = this.installApplication.bind(this); @@ -119,8 +121,7 @@ export default class Clusters { static initDismissableCallout() { const callout = document.querySelector('.js-cluster-security-warning'); - - if (callout) new PersistentUserCallout(callout); // eslint-disable-line no-new + PersistentUserCallout.factory(callout); } addListeners() { @@ -129,6 +130,8 @@ export default class Clusters { eventHub.$on('upgradeApplication', data => this.upgradeApplication(data)); eventHub.$on('upgradeFailed', appId => this.upgradeFailed(appId)); eventHub.$on('dismissUpgradeSuccess', appId => this.dismissUpgradeSuccess(appId)); + eventHub.$on('saveKnativeDomain', data => this.saveKnativeDomain(data)); + eventHub.$on('setKnativeHostname', data => this.setKnativeHostname(data)); } removeListeners() { @@ -137,6 +140,8 @@ export default class Clusters { eventHub.$off('upgradeApplication', this.upgradeApplication); eventHub.$off('upgradeFailed', this.upgradeFailed); eventHub.$off('dismissUpgradeSuccess', this.dismissUpgradeSuccess); + eventHub.$off('saveKnativeDomain'); + eventHub.$off('setKnativeHostname'); } initPolling() { @@ -272,6 +277,18 @@ export default class Clusters { this.store.updateAppProperty(appId, 'requestStatus', null); } + saveKnativeDomain(data) { + const appId = data.id; + this.store.updateAppProperty(appId, 'status', APPLICATION_STATUS.UPDATING); + this.service.updateApplication(appId, data.params); + } + + setKnativeHostname(data) { + const appId = data.id; + this.store.updateAppProperty(appId, 'isEditingHostName', true); + this.store.updateAppProperty(appId, 'hostname', data.hostname); + } + destroy() { this.destroyed = true; diff --git a/app/assets/javascripts/clusters/components/application_row.vue b/app/assets/javascripts/clusters/components/application_row.vue index 5952e93b9a74d11dd4c90cf7a1b397d2690c5750..19e5ac1567db168661d3363f5080a8d9b416cbc7 100644 --- a/app/assets/javascripts/clusters/components/application_row.vue +++ b/app/assets/javascripts/clusters/components/application_row.vue @@ -191,14 +191,7 @@ export default { return this.status === APPLICATION_STATUS.UPDATE_ERRORED; }, upgradeFailureDescription() { - return sprintf( - s__( - 'ClusterIntegration|Something went wrong when upgrading %{title}. Please check the logs and try again.', - ), - { - title: this.title, - }, - ); + return s__('ClusterIntegration|Update failed. Please check the logs and try again.'); }, upgradeSuccessDescription() { return sprintf(s__('ClusterIntegration|%{title} upgraded successfully.'), { @@ -210,9 +203,9 @@ export default { if (this.upgradeAvailable && !this.upgradeFailed && !this.isUpgrading) { label = s__('ClusterIntegration|Upgrade'); } else if (this.isUpgrading) { - label = s__('ClusterIntegration|Upgrading'); + label = s__('ClusterIntegration|Updating'); } else if (this.upgradeFailed) { - label = s__('ClusterIntegration|Retry upgrade'); + label = s__('ClusterIntegration|Retry update'); } return label; @@ -224,6 +217,14 @@ export default { (this.upgradeRequested && !this.upgradeSuccessful) ); }, + shouldShowUpgradeDetails() { + // This method only returns true when; + // Upgrade was successful OR Upgrade failed + // AND new upgrade is unavailable AND version information is present. + return ( + (this.upgradeSuccessful || this.upgradeFailed) && !this.upgradeAvailable && this.version + ); + }, }, watch: { status() { @@ -303,7 +304,7 @@ export default {
{{ versionLabel }} diff --git a/app/assets/javascripts/clusters/components/applications.vue b/app/assets/javascripts/clusters/components/applications.vue index 0cf187d4189d2e43d7dc74fdcacdd19990ccff91..13e8617c515ab90b178e64fd8eb1e83d7840d02b 100644 --- a/app/assets/javascripts/clusters/components/applications.vue +++ b/app/assets/javascripts/clusters/components/applications.vue @@ -15,11 +15,14 @@ import { s__, sprintf } from '../../locale'; import applicationRow from './application_row.vue'; import clipboardButton from '../../vue_shared/components/clipboard_button.vue'; import { CLUSTER_TYPE, APPLICATION_STATUS, INGRESS } from '../constants'; +import LoadingButton from '~/vue_shared/components/loading_button.vue'; +import eventHub from '~/clusters/event_hub'; export default { components: { applicationRow, clipboardButton, + LoadingButton, }, props: { type: { @@ -86,53 +89,26 @@ export default { ingressInstalled() { return this.applications.ingress.status === APPLICATION_STATUS.INSTALLED; }, - ingressExternalIp() { - return this.applications.ingress.externalIp; + ingressExternalEndpoint() { + return this.applications.ingress.externalIp || this.applications.ingress.externalHostname; }, certManagerInstalled() { return this.applications.cert_manager.status === APPLICATION_STATUS.INSTALLED; }, ingressDescription() { - const extraCostParagraph = sprintf( - _.escape( - s__( - `ClusterIntegration|%{boldNotice} This will add some extra resources - like a load balancer, which may incur additional costs depending on - the hosting provider your Kubernetes cluster is installed on. If you are using - Google Kubernetes Engine, you can %{pricingLink}.`, - ), - ), - { - boldNotice: `${_.escape(s__('ClusterIntegration|Note:'))}`, - pricingLink: ` - ${_.escape(s__('ClusterIntegration|check the pricing here'))}`, - }, - false, - ); - - const externalIpParagraph = sprintf( + return sprintf( _.escape( s__( - `ClusterIntegration|After installing Ingress, you will need to point your wildcard DNS - at the generated external IP address in order to view your app after it is deployed. %{ingressHelpLink}`, + `ClusterIntegration|Installing Ingress may incur additional costs. Learn more about %{pricingLink}.`, ), ), { - ingressHelpLink: ` - ${_.escape(s__('ClusterIntegration|More information'))} - `, + pricingLink: ` + ${_.escape(s__('ClusterIntegration|pricing'))}`, }, false, ); - - return ` -

- ${extraCostParagraph} -

-

- ${externalIpParagraph} -

- `; }, certManagerDescription() { return sprintf( @@ -173,16 +149,70 @@ export default { jupyterHostname() { return this.applications.jupyter.hostname; }, + knative() { + return this.applications.knative; + }, knativeInstalled() { - return this.applications.knative.status === APPLICATION_STATUS.INSTALLED; + return ( + this.knative.status === APPLICATION_STATUS.INSTALLED || + this.knativeUpgrading || + this.knativeUpgradeFailed || + this.knative.status === APPLICATION_STATUS.UPDATED + ); + }, + knativeUpgrading() { + return ( + this.knative.status === APPLICATION_STATUS.UPDATING || + this.knative.status === APPLICATION_STATUS.SCHEDULED + ); }, - knativeExternalIp() { - return this.applications.knative.externalIp; + knativeUpgradeFailed() { + return this.knative.status === APPLICATION_STATUS.UPDATE_ERRORED; + }, + knativeExternalEndpoint() { + return this.knative.externalIp || this.knative.externalHostname; + }, + knativeDescription() { + return sprintf( + _.escape( + s__( + `ClusterIntegration|Installing Knative may incur additional costs. Learn more about %{pricingLink}.`, + ), + ), + { + pricingLink: ` + ${_.escape(s__('ClusterIntegration|pricing'))}`, + }, + false, + ); + }, + canUpdateKnativeEndpoint() { + return this.knativeExternalEndpoint && !this.knativeUpgradeFailed && !this.knativeUpgrading; + }, + knativeHostname: { + get() { + return this.knative.hostname; + }, + set(hostname) { + eventHub.$emit('setKnativeHostname', { + id: 'knative', + hostname, + }); + }, }, }, created() { this.helmInstallIllustration = helmInstallIllustration; }, + methods: { + saveKnativeDomain() { + eventHub.$emit('saveKnativeDomain', { + id: 'knative', + params: { hostname: this.knative.hostname }, + }); + }, + }, }; @@ -247,31 +277,31 @@ export default { -
+
-