diff options
49 files changed, 551 insertions, 390 deletions
diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION index 3eefcb9dd5b..9084fa2f716 100644 --- a/GITALY_SERVER_VERSION +++ b/GITALY_SERVER_VERSION @@ -1 +1 @@ -1.0.0 +1.1.0 @@ -235,7 +235,7 @@ gem 'asana', '~> 0.8.1' gem 'ruby-fogbugz', '~> 0.2.1' # Kubernetes integration -gem 'kubeclient', '~> 3.1.0' +gem 'kubeclient', '~> 4.0.0' # Sanitize user input gem 'sanitize', '~> 4.6' @@ -425,7 +425,7 @@ group :ed25519 do end # Gitaly GRPC client -gem 'gitaly-proto', '~> 0.123.0', require: 'gitaly' +gem 'gitaly-proto', '~> 1.1.0', require: 'gitaly' gem 'grpc', '~> 1.15.0' gem 'google-protobuf', '~> 3.6' diff --git a/Gemfile.lock b/Gemfile.lock index 20231696795..f622c6292b2 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -273,7 +273,7 @@ GEM gettext_i18n_rails (>= 0.7.1) po_to_json (>= 1.0.0) rails (>= 3.2.0) - gitaly-proto (0.123.0) + gitaly-proto (1.1.0) grpc (~> 1.0) github-markup (1.7.0) gitlab-default_value_for (3.1.1) @@ -367,14 +367,14 @@ GEM html2text (0.2.0) nokogiri (~> 1.6) htmlentities (4.3.4) - http (2.2.2) + http (3.3.0) addressable (~> 2.3) http-cookie (~> 1.0) - http-form_data (~> 1.0.1) + http-form_data (~> 2.0) http_parser.rb (~> 0.6.0) http-cookie (1.0.3) domain_name (~> 0.5) - http-form_data (1.0.3) + http-form_data (2.1.1) http_parser.rb (0.6.0) httparty (0.13.7) json (~> 1.8) @@ -418,8 +418,8 @@ GEM kgio (2.10.0) knapsack (1.17.0) rake - kubeclient (3.1.0) - http (~> 2.2.2) + kubeclient (4.0.0) + http (~> 3.0) recursive-open-struct (~> 1.0, >= 1.0.4) rest-client (~> 2.0) launchy (2.4.3) @@ -1006,7 +1006,7 @@ DEPENDENCIES gettext (~> 3.2.2) gettext_i18n_rails (~> 1.8.0) gettext_i18n_rails_js (~> 1.3) - gitaly-proto (~> 0.123.0) + gitaly-proto (~> 1.1.0) github-markup (~> 1.7.0) gitlab-default_value_for (~> 3.1.1) gitlab-markup (~> 1.6.5) @@ -1042,7 +1042,7 @@ DEPENDENCIES jwt (~> 1.5.6) kaminari (~> 1.0) knapsack (~> 1.17) - kubeclient (~> 3.1.0) + kubeclient (~> 4.0.0) letter_opener_web (~> 1.3.0) license_finder (~> 5.4) licensee (~> 8.9) diff --git a/Gemfile.rails4.lock b/Gemfile.rails4.lock index caaa886987b..2542e085815 100644 --- a/Gemfile.rails4.lock +++ b/Gemfile.rails4.lock @@ -272,7 +272,7 @@ GEM gettext_i18n_rails (>= 0.7.1) po_to_json (>= 1.0.0) rails (>= 3.2.0) - gitaly-proto (0.123.0) + gitaly-proto (1.1.0) grpc (~> 1.0) github-markup (1.7.0) gitlab-markup (1.6.5) @@ -364,14 +364,14 @@ GEM html2text (0.2.0) nokogiri (~> 1.6) htmlentities (4.3.4) - http (2.2.2) + http (3.3.0) addressable (~> 2.3) http-cookie (~> 1.0) - http-form_data (~> 1.0.1) + http-form_data (~> 2.0) http_parser.rb (~> 0.6.0) http-cookie (1.0.3) domain_name (~> 0.5) - http-form_data (1.0.3) + http-form_data (2.1.1) http_parser.rb (0.6.0) httparty (0.13.7) json (~> 1.8) @@ -415,8 +415,8 @@ GEM kgio (2.10.0) knapsack (1.17.0) rake - kubeclient (3.1.0) - http (~> 2.2.2) + kubeclient (4.0.0) + http (~> 3.0) recursive-open-struct (~> 1.0, >= 1.0.4) rest-client (~> 2.0) launchy (2.4.3) @@ -998,7 +998,7 @@ DEPENDENCIES gettext (~> 3.2.2) gettext_i18n_rails (~> 1.8.0) gettext_i18n_rails_js (~> 1.3) - gitaly-proto (~> 0.123.0) + gitaly-proto (~> 1.1.0) github-markup (~> 1.7.0) gitlab-markup (~> 1.6.5) gitlab-sidekiq-fetcher @@ -1033,7 +1033,7 @@ DEPENDENCIES jwt (~> 1.5.6) kaminari (~> 1.0) knapsack (~> 1.17) - kubeclient (~> 3.1.0) + kubeclient (~> 4.0.0) letter_opener_web (~> 1.3.0) license_finder (~> 5.4) licensee (~> 8.9) diff --git a/app/assets/javascripts/blob_edit/blob_bundle.js b/app/assets/javascripts/blob_edit/blob_bundle.js index ec27ae8c291..9f547471170 100644 --- a/app/assets/javascripts/blob_edit/blob_bundle.js +++ b/app/assets/javascripts/blob_edit/blob_bundle.js @@ -16,9 +16,17 @@ export default () => { const filePath = editBlobForm.data('blobFilename'); const currentAction = $('.js-file-title').data('currentAction'); const projectId = editBlobForm.data('project-id'); + const commitButton = $('.js-commit-button'); + + commitButton.on('click', () => { + window.onbeforeunload = null; + }); new EditBlob(`${urlRoot}${assetsPath}`, filePath, currentAction, projectId); new NewCommitForm(editBlobForm); + + // returning here blocks page navigation + window.onbeforeunload = () => ''; } if (uploadBlobForm.length) { diff --git a/app/assets/javascripts/clusters/components/applications.vue b/app/assets/javascripts/clusters/components/applications.vue index 16acb4c3153..a37cb4def28 100644 --- a/app/assets/javascripts/clusters/components/applications.vue +++ b/app/assets/javascripts/clusters/components/applications.vue @@ -168,6 +168,9 @@ export default { knativeInstalled() { return this.applications.knative.status === APPLICATION_STATUS.INSTALLED; }, + knativeExternalIp() { + return this.applications.knative.externalIp; + }, }, created() { this.helmInstallIllustration = helmInstallIllustration; @@ -443,6 +446,49 @@ export default { /> </div> </template> + <template v-if="knativeInstalled"> + <div class="form-group"> + <label for="knative-ip-address"> + {{ s__('ClusterIntegration|Knative IP Address:') }} + </label> + <div v-if="knativeExternalIp" class="input-group"> + <input + id="knative-ip-address" + :value="knativeExternalIp" + type="text" + class="form-control js-ip-address" + readonly + /> + <span class="input-group-append"> + <clipboard-button + :text="knativeExternalIp" + :title="s__('ClusterIntegration|Copy Knative IP Address to clipboard')" + class="input-group-text js-clipboard-btn" + /> + </span> + </div> + <input v-else type="text" class="form-control js-ip-address" readonly value="?" /> + </div> + + <p v-if="!knativeExternalIp" class="settings-message js-no-ip-message"> + {{ + s__(`ClusterIntegration|The IP address is in + the process of being assigned. Please check your Kubernetes + cluster or Quotas on Google Kubernetes Engine if it takes a long time.`) + }} + </p> + + <p> + {{ + s__(`ClusterIntegration|Point a wildcard DNS to this + generated IP address in order to access + your application after it has been deployed.`) + }} + <a :href="ingressDnsHelpPath" target="_blank" rel="noopener noreferrer"> + {{ __('More information') }} + </a> + </p> + </template> </div> </application-row> </div> diff --git a/app/assets/javascripts/clusters/stores/clusters_store.js b/app/assets/javascripts/clusters/stores/clusters_store.js index 3678be59d24..2d69da8eaec 100644 --- a/app/assets/javascripts/clusters/stores/clusters_store.js +++ b/app/assets/javascripts/clusters/stores/clusters_store.js @@ -60,6 +60,7 @@ export default class ClusterStore { requestStatus: null, requestReason: null, hostname: null, + externalIp: null, }, }, }; @@ -111,6 +112,8 @@ export default class ClusterStore { } else if (appId === KNATIVE) { this.state.applications.knative.hostname = serverAppEntry.hostname || this.state.applications.knative.hostname; + this.state.applications.knative.externalIp = + serverAppEntry.external_ip || this.state.applications.knative.externalIp; } }); } diff --git a/app/assets/javascripts/monitoring/components/empty_state.vue b/app/assets/javascripts/monitoring/components/empty_state.vue index 23e1e734b37..0e141d02ead 100644 --- a/app/assets/javascripts/monitoring/components/empty_state.vue +++ b/app/assets/javascripts/monitoring/components/empty_state.vue @@ -44,9 +44,9 @@ export default { title: 'Get started with performance monitoring', description: `Stay updated about the performance and health of your environment by configuring Prometheus to monitor your deployments.`, - buttonText: 'Install Prometheus on clusters', + buttonText: 'Install on clusters', buttonPath: this.clustersPath, - secondaryButtonText: 'Configure existing Prometheus', + secondaryButtonText: 'Configure existing installation', secondaryButtonPath: this.settingsPath, }, loading: { @@ -88,26 +88,32 @@ export default { </script> <template> - <div class="prometheus-state"> - <div class="state-svg svg-content"><img :src="currentState.svgUrl" /></div> - <h4 class="state-title">{{ currentState.title }}</h4> - <p class="state-description"> - {{ currentState.description }} - <a v-if="showButtonDescription" :href="settingsPath"> Prometheus server </a> - </p> - <div class="state-button"> - <a v-if="currentState.buttonPath" :href="currentState.buttonPath" class="btn btn-success"> - {{ currentState.buttonText }} - </a> + <div class="row empty-state js-empty-state"> + <div class="col-12"> + <div class="state-svg svg-content"><img :src="currentState.svgUrl" /></div> </div> - <div class="state-button"> - <a - v-if="currentState.secondaryButtonPath" - :href="currentState.secondaryButtonPath" - class="btn" - > - {{ currentState.secondaryButtonText }} - </a> + + <div class="col-12"> + <div class="text-content"> + <h4 class="state-title text-center">{{ currentState.title }}</h4> + <p class="state-description"> + {{ currentState.description }} + <a v-if="showButtonDescription" :href="settingsPath"> Prometheus server </a> + </p> + + <div class="text-center"> + <a v-if="currentState.buttonPath" :href="currentState.buttonPath" class="btn btn-success"> + {{ currentState.buttonText }} + </a> + <a + v-if="currentState.secondaryButtonPath" + :href="currentState.secondaryButtonPath" + class="btn" + > + {{ currentState.secondaryButtonText }} + </a> + </div> + </div> </div> </div> </template> diff --git a/app/assets/stylesheets/pages/environments.scss b/app/assets/stylesheets/pages/environments.scss index 347fcad771a..75166ffcada 100644 --- a/app/assets/stylesheets/pages/environments.scss +++ b/app/assets/stylesheets/pages/environments.scss @@ -203,21 +203,6 @@ stroke: $gray-darkest; } -.prometheus-state { - max-width: 460px; - margin: 10px auto; - text-align: center; - - .state-svg { - max-width: 80vw; - margin: 0 auto; - } - - .state-button { - padding: $gl-padding / 2; - } -} - .prometheus-graphs { .environments { .dropdown-menu-toggle { diff --git a/app/models/clusters/applications/ingress.rb b/app/models/clusters/applications/ingress.rb index bd0286ee3f9..8f8790585a3 100644 --- a/app/models/clusters/applications/ingress.rb +++ b/app/models/clusters/applications/ingress.rb @@ -51,6 +51,10 @@ module Clusters ClusterWaitForIngressIpAddressWorker.perform_async(name, id) end + + def ingress_service + cluster.kubeclient.get_service('ingress-nginx-ingress-controller', Gitlab::Kubernetes::Helm::NAMESPACE) + end end end end diff --git a/app/models/clusters/applications/knative.rb b/app/models/clusters/applications/knative.rb index c66d5ce54db..c0aaa8dce20 100644 --- a/app/models/clusters/applications/knative.rb +++ b/app/models/clusters/applications/knative.rb @@ -6,9 +6,7 @@ module Clusters VERSION = '0.1.3'.freeze REPOSITORY = 'https://storage.googleapis.com/triggermesh-charts'.freeze - # This is required for helm version <= 2.10.x in order to support - # Setting up CRDs - ISTIO_CRDS = 'https://storage.googleapis.com/triggermesh-charts/istio-crds.yaml'.freeze + FETCH_IP_ADDRESS_DELAY = 30.seconds self.table_name = 'clusters_applications_knative' @@ -16,6 +14,16 @@ module Clusters include ::Clusters::Concerns::ApplicationStatus include ::Clusters::Concerns::ApplicationVersion include ::Clusters::Concerns::ApplicationData + include AfterCommitQueue + + state_machine :status do + before_transition any => [:installed] do |application| + application.run_after_commit do + ClusterWaitForIngressIpAddressWorker.perform_in( + FETCH_IP_ADDRESS_DELAY, application.name, application.id) + end + end + end default_value_for :version, VERSION @@ -36,19 +44,23 @@ module Clusters rbac: cluster.platform_kubernetes_rbac?, chart: chart, files: files, - repository: REPOSITORY, - preinstall: install_script + repository: REPOSITORY ) end - def client - cluster.platform_kubernetes.kubeclient.knative_client + def schedule_status_update + return unless installed? + return if external_ip + + ClusterWaitForIngressIpAddressWorker.perform_async(name, id) end - private + def ingress_service + cluster.kubeclient.get_service('knative-ingressgateway', 'istio-system') + end - def install_script - ["/usr/bin/kubectl apply -f #{ISTIO_CRDS}"] + def client + cluster.platform_kubernetes.kubeclient.knative_client end end end diff --git a/app/models/project.rb b/app/models/project.rb index 4d1917b9ab2..b85ec90f3ca 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -1394,7 +1394,7 @@ class Project < ActiveRecord::Base def change_head(branch) if repository.branch_exists?(branch) repository.before_change_head - repository.raw_repository.write_ref('HEAD', "refs/heads/#{branch}", shell: false) + repository.raw_repository.write_ref('HEAD', "refs/heads/#{branch}") repository.copy_gitattributes(branch) repository.after_change_head reload_default_branch diff --git a/app/models/repository.rb b/app/models/repository.rb index a77fa8f2ce7..427dac99b79 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -259,7 +259,7 @@ class Repository next if kept_around?(sha) # This will still fail if the file is corrupted (e.g. 0 bytes) - raw_repository.write_ref(keep_around_ref_name(sha), sha, shell: false) + raw_repository.write_ref(keep_around_ref_name(sha), sha) rescue Gitlab::Git::CommandError => ex Rails.logger.error "Unable to create keep-around reference for repository #{disk_path}: #{ex}" end diff --git a/app/services/clusters/applications/check_ingress_ip_address_service.rb b/app/services/clusters/applications/check_ingress_ip_address_service.rb index f32e73e8b1c..0ec06e776a7 100644 --- a/app/services/clusters/applications/check_ingress_ip_address_service.rb +++ b/app/services/clusters/applications/check_ingress_ip_address_service.rb @@ -30,7 +30,7 @@ module Clusters def service strong_memoize(:ingress_service) do - kubeclient.get_service('ingress-nginx-ingress-controller', Gitlab::Kubernetes::Helm::NAMESPACE) + app.ingress_service end end end diff --git a/app/views/layouts/header/_new_dropdown.haml b/app/views/layouts/header/_new_dropdown.haml index e134f416c70..5cb8aebadb3 100644 --- a/app/views/layouts/header/_new_dropdown.haml +++ b/app/views/layouts/header/_new_dropdown.haml @@ -7,15 +7,14 @@ - if @group&.persisted? - create_group_project = can?(current_user, :create_projects, @group) - create_group_subgroup = can?(current_user, :create_subgroup, @group) + - if create_group_project || create_group_subgroup %li.dropdown-bold-header = _('This group') - if create_group_project - %li.header-new-group-project - = link_to _('New project'), new_project_path(namespace_id: @group.id) + %li= link_to _('New project'), new_project_path(namespace_id: @group.id) - if create_group_subgroup - %li - = link_to _('New subgroup'), new_group_path(parent_id: @group.id) + %li= link_to _('New subgroup'), new_group_path(parent_id: @group.id) %li.divider %li.dropdown-bold-header GitLab @@ -23,25 +22,20 @@ - create_project_issue = show_new_issue_link?(@project) - merge_project = merge_request_source_project_for_project(@project) - create_project_snippet = can?(current_user, :create_project_snippet, @project) + - if create_project_issue || merge_project || create_project_snippet %li.dropdown-bold-header = _('This project') - if create_project_issue - %li - = link_to _('New issue'), new_project_issue_path(@project) + %li= link_to _('New issue'), new_project_issue_path(@project) - if merge_project - %li - = link_to _('New merge request'), project_new_merge_request_path(merge_project) + %li= link_to _('New merge request'), project_new_merge_request_path(merge_project) - if create_project_snippet - %li.header-new-project-snippet - = link_to _('New snippet'), new_project_snippet_path(@project) + %li= link_to _('New snippet'), new_project_snippet_path(@project) %li.divider %li.dropdown-bold-header GitLab - if current_user.can_create_project? - %li - = link_to _('New project'), new_project_path, class: 'qa-global-new-project-link' + %li= link_to _('New project'), new_project_path, class: 'qa-global-new-project-link' - if current_user.can_create_group? - %li - = link_to _('New group'), new_group_path - %li - = link_to _('New snippet'), new_snippet_path + %li= link_to _('New group'), new_group_path + %li= link_to _('New snippet'), new_snippet_path diff --git a/app/views/projects/branches/new.html.haml b/app/views/projects/branches/new.html.haml index 8181ee9eea1..af8887b0c39 100644 --- a/app/views/projects/branches/new.html.haml +++ b/app/views/projects/branches/new.html.haml @@ -13,7 +13,7 @@ .form-group.row = label_tag :branch_name, nil, class: 'col-form-label col-sm-2' .col-sm-10 - = text_field_tag :branch_name, params[:branch_name], required: true, autofocus: true, class: 'form-control js-branch-name' + = text_field_tag :branch_name, params[:branch_name], required: true, autofocus: true, class: 'form-control js-branch-name monospace' .form-text.text-muted.text-danger.js-branch-name-error .form-group.row = label_tag :ref, 'Create from', class: 'col-form-label col-sm-2' diff --git a/changelogs/unreleased/52828-inconsistency-in-fonts-used-for-branch-name-and-create-from-fields-when-creating-new-branch-from-ui.yml b/changelogs/unreleased/52828-inconsistency-in-fonts-used-for-branch-name-and-create-from-fields-when-creating-new-branch-from-ui.yml new file mode 100644 index 00000000000..8132dde8636 --- /dev/null +++ b/changelogs/unreleased/52828-inconsistency-in-fonts-used-for-branch-name-and-create-from-fields-when-creating-new-branch-from-ui.yml @@ -0,0 +1,5 @@ +--- +title: Make new branch form fields' fonts consistent +merge_request: +author: +type: fixed diff --git a/changelogs/unreleased/53728-warn-in-web-editor-when-user-navigates-away.yml b/changelogs/unreleased/53728-warn-in-web-editor-when-user-navigates-away.yml new file mode 100644 index 00000000000..8377fdc6133 --- /dev/null +++ b/changelogs/unreleased/53728-warn-in-web-editor-when-user-navigates-away.yml @@ -0,0 +1,5 @@ +--- +title: Prevent user from navigating away from file edit without commit +merge_request: +author: +type: fixed diff --git a/changelogs/unreleased/53988-remove-notes-index-on-updated-at.yml b/changelogs/unreleased/53988-remove-notes-index-on-updated-at.yml new file mode 100644 index 00000000000..f0bbf69736d --- /dev/null +++ b/changelogs/unreleased/53988-remove-notes-index-on-updated-at.yml @@ -0,0 +1,5 @@ +--- +title: Remove index for notes on updated_at +merge_request: 23356 +author: +type: performance diff --git a/changelogs/unreleased/bvl-use-shell-writeref.yml b/changelogs/unreleased/bvl-use-shell-writeref.yml new file mode 100644 index 00000000000..682d428e8c5 --- /dev/null +++ b/changelogs/unreleased/bvl-use-shell-writeref.yml @@ -0,0 +1,5 @@ +--- +title: Avoid creating invalid refs using rugged, shelling out for writing refs +merge_request: 23286 +author: +type: fixed diff --git a/changelogs/unreleased/gt-update-env-metrics-empty-state.yml b/changelogs/unreleased/gt-update-env-metrics-empty-state.yml new file mode 100644 index 00000000000..a05dc07e65c --- /dev/null +++ b/changelogs/unreleased/gt-update-env-metrics-empty-state.yml @@ -0,0 +1,5 @@ +--- +title: Update environments metrics empty state +merge_request: 23074 +author: George Tsiolis +type: changed diff --git a/changelogs/unreleased/sh-handle-string-null-bytes.yml b/changelogs/unreleased/sh-handle-string-null-bytes.yml new file mode 100644 index 00000000000..edc045274e3 --- /dev/null +++ b/changelogs/unreleased/sh-handle-string-null-bytes.yml @@ -0,0 +1,5 @@ +--- +title: Gracefully handle references with null bytes +merge_request: 23365 +author: +type: fixed diff --git a/changelogs/unreleased/triggermesh-phase2-external-ip.yml b/changelogs/unreleased/triggermesh-phase2-external-ip.yml new file mode 100644 index 00000000000..582c8f6df2e --- /dev/null +++ b/changelogs/unreleased/triggermesh-phase2-external-ip.yml @@ -0,0 +1,5 @@ +--- +title: Add an external IP address to the knative cluster application page +merge_request: +author: Chris Baumbauer +type: fixed diff --git a/changelogs/unreleased/upgrade_kubeclient_400.yml b/changelogs/unreleased/upgrade_kubeclient_400.yml new file mode 100644 index 00000000000..edb38710e6a --- /dev/null +++ b/changelogs/unreleased/upgrade_kubeclient_400.yml @@ -0,0 +1,5 @@ +--- +title: Upgrade kubeclient to 4.0.0 +merge_request: 23261 +author: Praveen Arimbrathodiyil @pravi +type: other diff --git a/config/initializers/kubeclient.rb b/config/initializers/kubeclient.rb index 2d9f439fdc0..f8fe1156aaa 100644 --- a/config/initializers/kubeclient.rb +++ b/config/initializers/kubeclient.rb @@ -1,19 +1,4 @@ class Kubeclient::Client - # We need to monkey patch this method until - # https://github.com/abonas/kubeclient/pull/323 is merged - def proxy_url(kind, name, port, namespace = '') - discover unless @discovered - entity_name_plural = - if %w[services pods nodes].include?(kind.to_s) - kind.to_s - else - @entities[kind.to_s].resource_name - end - - ns_prefix = build_namespace_prefix(namespace) - rest_client["#{ns_prefix}#{entity_name_plural}/#{name}:#{port}/proxy"].url - end - # Monkey patch to set `max_redirects: 0`, so that kubeclient # does not follow redirects and expose internal services. # See https://gitlab.com/gitlab-org/gitlab-ce/issues/53158 diff --git a/db/migrate/20181116050532_knative_external_ip.rb b/db/migrate/20181116050532_knative_external_ip.rb new file mode 100644 index 00000000000..f1f903fb692 --- /dev/null +++ b/db/migrate/20181116050532_knative_external_ip.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +# See http://doc.gitlab.com/ce/development/migration_style_guide.html +# for more information on how to write migrations for GitLab. + +class KnativeExternalIp < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + def change + add_column :clusters_applications_knative, :external_ip, :string + end +end diff --git a/db/migrate/20181126153547_remove_notes_index_on_updated_at.rb b/db/migrate/20181126153547_remove_notes_index_on_updated_at.rb new file mode 100644 index 00000000000..d7ca46b50e4 --- /dev/null +++ b/db/migrate/20181126153547_remove_notes_index_on_updated_at.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +# See http://doc.gitlab.com/ce/development/migration_style_guide.html +# for more information on how to write migrations for GitLab. + +class RemoveNotesIndexOnUpdatedAt < ActiveRecord::Migration[5.0] + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + disable_ddl_transaction! + + def up + remove_concurrent_index(*index_arguments) + end + + def down + add_concurrent_index(*index_arguments) + end + + private + + def index_arguments + [ + :notes, + [:updated_at], + { + name: 'index_notes_on_updated_at' + } + ] + end +end diff --git a/db/schema.rb b/db/schema.rb index acabd7b442b..e0ca571df5a 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20181126150622) do +ActiveRecord::Schema.define(version: 20181126153547) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -698,6 +698,7 @@ ActiveRecord::Schema.define(version: 20181126150622) do t.string "version", null: false t.string "hostname" t.text "status_reason" + t.string "external_ip" t.index ["cluster_id"], name: "index_clusters_applications_knative_on_cluster_id", unique: true, using: :btree end @@ -1392,7 +1393,6 @@ ActiveRecord::Schema.define(version: 20181126150622) do t.index ["noteable_id", "noteable_type"], name: "index_notes_on_noteable_id_and_noteable_type", using: :btree t.index ["noteable_type"], name: "index_notes_on_noteable_type", using: :btree t.index ["project_id", "noteable_type"], name: "index_notes_on_project_id_and_noteable_type", using: :btree - t.index ["updated_at"], name: "index_notes_on_updated_at", using: :btree end create_table "notification_settings", force: :cascade do |t| diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb index 9b48031b2d3..0a541031884 100644 --- a/lib/gitlab/git/repository.rb +++ b/lib/gitlab/git/repository.rb @@ -723,11 +723,11 @@ module Gitlab delete_refs(tmp_ref) end - def write_ref(ref_path, ref, old_ref: nil, shell: true) + def write_ref(ref_path, ref, old_ref: nil) ref_path = "#{Gitlab::Git::BRANCH_REF_PREFIX}#{ref_path}" unless ref_path.start_with?("refs/") || ref_path == "HEAD" wrapped_gitaly_errors do - gitaly_repository_client.write_ref(ref_path, ref, old_ref, shell) + gitaly_repository_client.write_ref(ref_path, ref, old_ref) end end diff --git a/lib/gitlab/git_ref_validator.rb b/lib/gitlab/git_ref_validator.rb index a90b69ff42b..3f13ebeb9d0 100644 --- a/lib/gitlab/git_ref_validator.rb +++ b/lib/gitlab/git_ref_validator.rb @@ -13,7 +13,11 @@ module Gitlab return false if ref_name.start_with?(*not_allowed_prefixes) return false if ref_name == 'HEAD' - Rugged::Reference.valid_name? "refs/heads/#{ref_name}" + begin + Rugged::Reference.valid_name?("refs/heads/#{ref_name}") + rescue ArgumentError + return false + end end end end diff --git a/lib/gitlab/gitaly_client/repository_service.rb b/lib/gitlab/gitaly_client/repository_service.rb index 12a0ee16649..8a1abfbf874 100644 --- a/lib/gitlab/gitaly_client/repository_service.rb +++ b/lib/gitlab/gitaly_client/repository_service.rb @@ -251,20 +251,15 @@ module Gitlab ) end - def write_ref(ref_path, ref, old_ref, shell) + def write_ref(ref_path, ref, old_ref) request = Gitaly::WriteRefRequest.new( repository: @gitaly_repo, ref: ref_path.b, - revision: ref.b, - shell: shell + revision: ref.b ) request.old_revision = old_ref.b unless old_ref.nil? - response = GitalyClient.call(@storage, :repository_service, :write_ref, request, timeout: GitalyClient.fast_timeout) - - raise Gitlab::Git::CommandError, encode!(response.error) if response.error.present? - - true + GitalyClient.call(@storage, :repository_service, :write_ref, request, timeout: GitalyClient.fast_timeout) end def set_config(entries) diff --git a/locale/gitlab.pot b/locale/gitlab.pot index f76b61a2258..619450fa0fe 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -1451,6 +1451,9 @@ msgstr "" msgid "ClusterIntegration|Copy Jupyter Hostname to clipboard" msgstr "" +msgid "ClusterIntegration|Copy Knative IP Address to clipboard" +msgstr "" + msgid "ClusterIntegration|Copy Kubernetes cluster name" msgstr "" @@ -1559,6 +1562,9 @@ msgstr "" msgid "ClusterIntegration|Knative Domain Name:" msgstr "" +msgid "ClusterIntegration|Knative IP Address:" +msgstr "" + msgid "ClusterIntegration|Kubernetes cluster" msgstr "" diff --git a/spec/factories/clusters/applications/helm.rb b/spec/factories/clusters/applications/helm.rb index 7fc3d16e864..fe56ac5b71d 100644 --- a/spec/factories/clusters/applications/helm.rb +++ b/spec/factories/clusters/applications/helm.rb @@ -59,6 +59,7 @@ FactoryBot.define do end factory :clusters_applications_runner, class: Clusters::Applications::Runner do + runner factory: %i(ci_runner) cluster factory: %i(cluster with_installed_helm provided_by_gcp) end diff --git a/spec/features/explore/new_menu_spec.rb b/spec/features/explore/new_menu_spec.rb deleted file mode 100644 index 259f22139ef..00000000000 --- a/spec/features/explore/new_menu_spec.rb +++ /dev/null @@ -1,167 +0,0 @@ -require 'spec_helper' - -describe 'Top Plus Menu', :js do - let(:user) { create(:user) } - let(:group) { create(:group) } - let(:project) { create(:project, :repository, creator: user, namespace: user.namespace) } - let(:public_project) { create(:project, :public) } - - before do - group.add_owner(user) - end - - context 'used by full user' do - before do - sign_in(user) - end - - it 'click on New project shows new project page' do - visit root_dashboard_path - - click_topmenuitem("New project") - - expect(page).to have_content('Project URL') - expect(page).to have_content('Project name') - end - - it 'click on New group shows new group page' do - visit root_dashboard_path - - click_topmenuitem("New group") - - expect(page).to have_content('Group URL') - expect(page).to have_content('Group name') - end - - it 'click on New snippet shows new snippet page' do - visit root_dashboard_path - - click_topmenuitem("New snippet") - - expect(page).to have_content('New Snippet') - expect(page).to have_content('Title') - end - - it 'click on New issue shows new issue page' do - visit project_path(project) - - click_topmenuitem("New issue") - - expect(page).to have_content('New Issue') - expect(page).to have_content('Title') - end - - it 'click on New merge request shows new merge request page' do - visit project_path(project) - - click_topmenuitem("New merge request") - - expect(page).to have_content('New Merge Request') - expect(page).to have_content('Source branch') - expect(page).to have_content('Target branch') - end - - it 'click on New project snippet shows new snippet page' do - visit project_path(project) - - page.within '.header-content' do - find('.header-new-dropdown-toggle').click - expect(page).to have_selector('.header-new.dropdown.show', count: 1) - find('.header-new-project-snippet a').click - end - - expect(page).to have_content('New Snippet') - expect(page).to have_content('Title') - end - - it 'Click on New subgroup shows new group page', :nested_groups do - visit group_path(group) - - click_topmenuitem("New subgroup") - - expect(page).to have_content('Group URL') - expect(page).to have_content('Group name') - end - - it 'Click on New project in group shows new project page' do - visit group_path(group) - - page.within '.header-content' do - find('.header-new-dropdown-toggle').click - expect(page).to have_selector('.header-new.dropdown.show', count: 1) - find('.header-new-group-project a').click - end - - expect(page).to have_content('Project URL') - expect(page).to have_content('Project name') - end - end - - context 'used by guest user' do - let(:guest_user) { create(:user) } - - before do - group.add_guest(guest_user) - project.add_guest(guest_user) - - sign_in(guest_user) - end - - it 'click on New issue shows new issue page' do - visit project_path(project) - - click_topmenuitem("New issue") - - expect(page).to have_content('New Issue') - expect(page).to have_content('Title') - end - - it 'has no New merge request menu item' do - visit project_path(project) - - hasnot_topmenuitem("New merge request") - end - - it 'has no New project snippet menu item' do - visit project_path(project) - - expect(find('.header-new.dropdown')).not_to have_selector('.header-new-project-snippet') - end - - it 'public project has no New merge request menu item' do - visit project_path(public_project) - - hasnot_topmenuitem("New merge request") - end - - it 'public project has no New project snippet menu item' do - visit project_path(public_project) - - expect(find('.header-new.dropdown')).not_to have_selector('.header-new-project-snippet') - end - - it 'has no New subgroup menu item' do - visit group_path(group) - - hasnot_topmenuitem("New subgroup") - end - - it 'has no New project for group menu item' do - visit group_path(group) - - expect(find('.header-new.dropdown')).not_to have_selector('.header-new-group-project') - end - end - - def click_topmenuitem(item_name) - page.within '.header-content' do - find('.header-new-dropdown-toggle').click - expect(page).to have_selector('.header-new.dropdown.show', count: 1) - click_link item_name - end - end - - def hasnot_topmenuitem(item_name) - expect(find('.header-new.dropdown')).not_to have_content(item_name) - end -end diff --git a/spec/features/signed_commits_spec.rb b/spec/features/signed_commits_spec.rb index ef0e55a1468..e2b3444272e 100644 --- a/spec/features/signed_commits_spec.rb +++ b/spec/features/signed_commits_spec.rb @@ -1,25 +1,22 @@ +# frozen_string_literal: true + require 'spec_helper' -describe 'GPG signed commits', :js do - set(:ref) { :'2d1096e3a0ecf1d2baf6dee036cc80775d4940ba' } - let(:project) { create(:project, :repository) } +describe 'GPG signed commits' do + let(:project) { create(:project, :public, :repository) } it 'changes from unverified to verified when the user changes his email to match the gpg key' do - user = create :user, email: 'unrelated.user@example.org' - project.add_maintainer(user) + ref = GpgHelpers::SIGNED_AND_AUTHORED_SHA + user = create(:user, email: 'unrelated.user@example.org') perform_enqueued_jobs do create :gpg_key, key: GpgHelpers::User1.public_key, user: user end - sign_in(user) - - visit project_commits_path(project, ref) + visit project_commit_path(project, ref) - within '#commits-list' do - expect(page).to have_content 'Unverified' - expect(page).not_to have_content 'Verified' - end + expect(page).to have_link 'Unverified' + expect(page).not_to have_link 'Verified' # user changes his email which makes the gpg key verified perform_enqueued_jobs do @@ -27,41 +24,33 @@ describe 'GPG signed commits', :js do user.update!(email: GpgHelpers::User1.emails.first) end - visit project_commits_path(project, ref) + visit project_commit_path(project, ref) - within '#commits-list' do - expect(page).to have_content 'Unverified' - expect(page).to have_content 'Verified' - end + expect(page).not_to have_link 'Unverified' + expect(page).to have_link 'Verified' end it 'changes from unverified to verified when the user adds the missing gpg key' do - user = create :user, email: GpgHelpers::User1.emails.first - project.add_maintainer(user) + ref = GpgHelpers::SIGNED_AND_AUTHORED_SHA + user = create(:user, email: GpgHelpers::User1.emails.first) - sign_in(user) + visit project_commit_path(project, ref) - visit project_commits_path(project, ref) - - within '#commits-list' do - expect(page).to have_content 'Unverified' - expect(page).not_to have_content 'Verified' - end + expect(page).to have_link 'Unverified' + expect(page).not_to have_link 'Verified' # user adds the gpg key which makes the signature valid perform_enqueued_jobs do create :gpg_key, key: GpgHelpers::User1.public_key, user: user end - visit project_commits_path(project, ref) + visit project_commit_path(project, ref) - within '#commits-list' do - expect(page).to have_content 'Unverified' - expect(page).to have_content 'Verified' - end + expect(page).not_to have_link 'Unverified' + expect(page).to have_link 'Verified' end - context 'shows popover badges' do + context 'shows popover badges', :js do let(:user_1) do create :user, email: GpgHelpers::User1.emails.first, username: 'nannie.bernhard', name: 'Nannie Bernhard' end @@ -85,19 +74,10 @@ describe 'GPG signed commits', :js do end end - before do - user = create :user - project.add_maintainer(user) - - sign_in(user) - end - it 'unverified signature' do - visit project_commits_path(project, ref) + visit project_commit_path(project, GpgHelpers::SIGNED_COMMIT_SHA) - within(find('.commit', text: 'signed commit by bette cartwright')) do - click_on 'Unverified' - end + click_on 'Unverified' within '.popover' do expect(page).to have_content 'This commit was signed with an unverified signature.' @@ -108,11 +88,9 @@ describe 'GPG signed commits', :js do it 'unverified signature: user email does not match the committer email, but is the same user' do user_2_key - visit project_commits_path(project, ref) + visit project_commit_path(project, GpgHelpers::DIFFERING_EMAIL_SHA) - within(find('.commit', text: 'signed and authored commit by bette cartwright, different email')) do - click_on 'Unverified' - end + click_on 'Unverified' within '.popover' do expect(page).to have_content 'This commit was signed with a verified signature, but the committer email is not verified to belong to the same user.' @@ -125,11 +103,9 @@ describe 'GPG signed commits', :js do it 'unverified signature: user email does not match the committer email' do user_2_key - visit project_commits_path(project, ref) + visit project_commit_path(project, GpgHelpers::SIGNED_COMMIT_SHA) - within(find('.commit', text: 'signed commit by bette cartwright')) do - click_on 'Unverified' - end + click_on 'Unverified' within '.popover' do expect(page).to have_content "This commit was signed with a different user's verified signature." @@ -142,11 +118,9 @@ describe 'GPG signed commits', :js do it 'verified and the gpg user has a gitlab profile' do user_1_key - visit project_commits_path(project, ref) + visit project_commit_path(project, GpgHelpers::SIGNED_AND_AUTHORED_SHA) - within(find('.commit', text: 'signed and authored commit by nannie bernhard')) do - click_on 'Verified' - end + click_on 'Verified' within '.popover' do expect(page).to have_content 'This commit was signed with a verified signature and the committer email is verified to belong to the same user.' @@ -159,20 +133,16 @@ describe 'GPG signed commits', :js do it "verified and the gpg user's profile doesn't exist anymore" do user_1_key - visit project_commits_path(project, ref) + visit project_commit_path(project, GpgHelpers::SIGNED_AND_AUTHORED_SHA) # wait for the signature to get generated - within(find('.commit', text: 'signed and authored commit by nannie bernhard')) do - expect(page).to have_content 'Verified' - end + expect(page).to have_link 'Verified' user_1.destroy! refresh - within(find('.commit', text: 'signed and authored commit by nannie bernhard')) do - click_on 'Verified' - end + click_on 'Verified' within '.popover' do expect(page).to have_content 'This commit was signed with a verified signature and the committer email is verified to belong to the same user.' diff --git a/spec/javascripts/blob_edit/blob_bundle_spec.js b/spec/javascripts/blob_edit/blob_bundle_spec.js new file mode 100644 index 00000000000..759d170af77 --- /dev/null +++ b/spec/javascripts/blob_edit/blob_bundle_spec.js @@ -0,0 +1,30 @@ +import blobBundle from '~/blob_edit/blob_bundle'; +import $ from 'jquery'; + +window.ace = { + config: { + set: () => {}, + loadModule: () => {}, + }, + edit: () => ({ focus: () => {} }), +}; + +describe('EditBlob', () => { + beforeEach(() => { + setFixtures(` + <div class="js-edit-blob-form"> + <button class="js-commit-button"></button> + </div>`); + blobBundle(); + }); + + it('sets the window beforeunload listener to a function returning a string', () => { + expect(window.onbeforeunload()).toBe(''); + }); + + it('removes beforeunload listener if commit button is clicked', () => { + $('.js-commit-button').click(); + + expect(window.onbeforeunload).toBeNull(); + }); +}); diff --git a/spec/javascripts/clusters/stores/clusters_store_spec.js b/spec/javascripts/clusters/stores/clusters_store_spec.js index 6a08d08f33e..7ea0878ad45 100644 --- a/spec/javascripts/clusters/stores/clusters_store_spec.js +++ b/spec/javascripts/clusters/stores/clusters_store_spec.js @@ -107,6 +107,7 @@ describe('Clusters Store', () => { requestStatus: null, requestReason: null, hostname: null, + externalIp: null, }, cert_manager: { title: 'Cert-Manager', diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb index 1fe73c12fc0..852ee9c96af 100644 --- a/spec/lib/gitlab/git/repository_spec.rb +++ b/spec/lib/gitlab/git/repository_spec.rb @@ -1469,6 +1469,19 @@ describe Gitlab::Git::Repository, :seed_helper do end end end + + it 'writes the HEAD' do + repository.write_ref('HEAD', 'refs/heads/feature') + + expect(repository.commit('HEAD')).to eq(repository.commit('feature')) + expect(repository.root_ref).to eq('feature') + end + + it 'writes other refs' do + repository.write_ref('refs/heads/feature', SeedRepo::Commit::ID) + + expect(repository.commit('feature').sha).to eq(SeedRepo::Commit::ID) + end end describe '#write_config' do diff --git a/spec/lib/gitlab/git_ref_validator_spec.rb b/spec/lib/gitlab/git_ref_validator_spec.rb index ba7fb168a3b..3ab04a1c46d 100644 --- a/spec/lib/gitlab/git_ref_validator_spec.rb +++ b/spec/lib/gitlab/git_ref_validator_spec.rb @@ -27,4 +27,5 @@ describe Gitlab::GitRefValidator do it { expect(described_class.validate('-branch')).to be_falsey } it { expect(described_class.validate('.tag')).to be_falsey } it { expect(described_class.validate('my branch')).to be_falsey } + it { expect(described_class.validate("\xA0\u0000\xB0")).to be_falsey } end diff --git a/spec/models/clusters/applications/ingress_spec.rb b/spec/models/clusters/applications/ingress_spec.rb index 6b0b23eeab3..cfe0e216c78 100644 --- a/spec/models/clusters/applications/ingress_spec.rb +++ b/spec/models/clusters/applications/ingress_spec.rb @@ -5,7 +5,7 @@ describe Clusters::Applications::Ingress do include_examples 'cluster application core specs', :clusters_applications_ingress include_examples 'cluster application status specs', :clusters_applications_ingress - include_examples 'cluster application helm specs', :clusters_applications_knative + include_examples 'cluster application helm specs', :clusters_applications_ingress before do allow(ClusterWaitForIngressIpAddressWorker).to receive(:perform_in) diff --git a/spec/models/clusters/applications/jupyter_spec.rb b/spec/models/clusters/applications/jupyter_spec.rb index faaabafddb7..a40edbf267b 100644 --- a/spec/models/clusters/applications/jupyter_spec.rb +++ b/spec/models/clusters/applications/jupyter_spec.rb @@ -2,7 +2,7 @@ require 'rails_helper' describe Clusters::Applications::Jupyter do include_examples 'cluster application core specs', :clusters_applications_jupyter - include_examples 'cluster application helm specs', :clusters_applications_knative + include_examples 'cluster application helm specs', :clusters_applications_jupyter it { is_expected.to belong_to(:oauth_application) } diff --git a/spec/models/clusters/applications/knative_spec.rb b/spec/models/clusters/applications/knative_spec.rb index be2a91d566b..d43d88c2924 100644 --- a/spec/models/clusters/applications/knative_spec.rb +++ b/spec/models/clusters/applications/knative_spec.rb @@ -7,6 +7,11 @@ describe Clusters::Applications::Knative do include_examples 'cluster application status specs', :clusters_applications_knative include_examples 'cluster application helm specs', :clusters_applications_knative + before do + allow(ClusterWaitForIngressIpAddressWorker).to receive(:perform_in) + allow(ClusterWaitForIngressIpAddressWorker).to receive(:perform_async) + end + describe '.installed' do subject { described_class.installed } @@ -45,6 +50,48 @@ describe Clusters::Applications::Knative do it { is_expected.to contain_exactly(cluster) } end + describe 'make_installed with external_ip' do + before do + application.make_installed! + end + + let(:application) { create(:clusters_applications_knative, :installing) } + + it 'schedules a ClusterWaitForIngressIpAddressWorker' do + expect(ClusterWaitForIngressIpAddressWorker).to have_received(:perform_in) + .with(Clusters::Applications::Knative::FETCH_IP_ADDRESS_DELAY, 'knative', application.id) + end + end + + describe '#schedule_status_update with external_ip' do + let(:application) { create(:clusters_applications_knative, :installed) } + + before do + application.schedule_status_update + end + + it 'schedules a ClusterWaitForIngressIpAddressWorker' do + expect(ClusterWaitForIngressIpAddressWorker).to have_received(:perform_async) + .with('knative', application.id) + end + + context 'when the application is not installed' do + let(:application) { create(:clusters_applications_knative, :installing) } + + it 'does not schedule a ClusterWaitForIngressIpAddressWorker' do + expect(ClusterWaitForIngressIpAddressWorker).not_to have_received(:perform_async) + end + end + + context 'when there is already an external_ip' do + let(:application) { create(:clusters_applications_knative, :installed, external_ip: '111.222.222.111') } + + it 'does not schedule a ClusterWaitForIngressIpAddressWorker' do + expect(ClusterWaitForIngressIpAddressWorker).not_to have_received(:perform_in) + end + end + end + describe '#install_command' do subject { knative.install_command } diff --git a/spec/models/clusters/applications/prometheus_spec.rb b/spec/models/clusters/applications/prometheus_spec.rb index b5aa1dcece5..893ed3e3f64 100644 --- a/spec/models/clusters/applications/prometheus_spec.rb +++ b/spec/models/clusters/applications/prometheus_spec.rb @@ -5,7 +5,7 @@ describe Clusters::Applications::Prometheus do include_examples 'cluster application core specs', :clusters_applications_prometheus include_examples 'cluster application status specs', :clusters_applications_prometheus - include_examples 'cluster application helm specs', :clusters_applications_knative + include_examples 'cluster application helm specs', :clusters_applications_prometheus describe '.installed' do subject { described_class.installed } diff --git a/spec/models/clusters/applications/runner_spec.rb b/spec/models/clusters/applications/runner_spec.rb index a8b28a335d6..97e50809647 100644 --- a/spec/models/clusters/applications/runner_spec.rb +++ b/spec/models/clusters/applications/runner_spec.rb @@ -5,7 +5,7 @@ describe Clusters::Applications::Runner do include_examples 'cluster application core specs', :clusters_applications_runner include_examples 'cluster application status specs', :clusters_applications_runner - include_examples 'cluster application helm specs', :clusters_applications_knative + include_examples 'cluster application helm specs', :clusters_applications_runner it { is_expected.to belong_to(:runner) } @@ -90,7 +90,7 @@ describe Clusters::Applications::Runner do context 'without a runner' do let(:project) { create(:project) } let(:cluster) { create(:cluster, :with_installed_helm, projects: [project]) } - let(:application) { create(:clusters_applications_runner, cluster: cluster) } + let(:application) { create(:clusters_applications_runner, runner: nil, cluster: cluster) } it 'creates a runner' do expect do diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 51278836604..d23bdbc2c30 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -2203,12 +2203,6 @@ describe Project do project.change_head(project.default_branch) end - it 'creates the new reference with rugged' do - expect(project.repository.raw_repository).to receive(:write_ref).with('HEAD', "refs/heads/#{project.default_branch}", shell: false) - - project.change_head(project.default_branch) - end - it 'copies the gitattributes' do expect(project.repository).to receive(:copy_gitattributes).with(project.default_branch) project.change_head(project.default_branch) diff --git a/spec/services/clusters/applications/check_ingress_ip_address_service_spec.rb b/spec/services/clusters/applications/check_ingress_ip_address_service_spec.rb index eb0bdb61ee3..f3036fbcb0e 100644 --- a/spec/services/clusters/applications/check_ingress_ip_address_service_spec.rb +++ b/spec/services/clusters/applications/check_ingress_ip_address_service_spec.rb @@ -28,41 +28,7 @@ describe Clusters::Applications::CheckIngressIpAddressService do allow(application.cluster).to receive(:kubeclient).and_return(kubeclient) end - describe '#execute' do - context 'when the ingress ip address is available' do - it 'updates the external_ip for the app' do - subject + include_examples 'check ingress ip executions', :clusters_applications_ingress - expect(application.external_ip).to eq('111.222.111.222') - end - end - - context 'when the ingress ip address is not available' do - let(:ingress) { nil } - - it 'does not error' do - subject - end - end - - context 'when the exclusive lease cannot be obtained' do - it 'does not call kubeclient' do - stub_exclusive_lease_taken(lease_key, timeout: 15.seconds.to_i) - - subject - - expect(kubeclient).not_to have_received(:get_service) - end - end - - context 'when there is already an external_ip' do - let(:application) { create(:clusters_applications_ingress, :installed, external_ip: '001.111.002.111') } - - it 'does not call kubeclient' do - subject - - expect(kubeclient).not_to have_received(:get_service) - end - end - end + include_examples 'check ingress ip executions', :clusters_applications_knative end diff --git a/spec/support/helpers/gpg_helpers.rb b/spec/support/helpers/gpg_helpers.rb index 3f7279a50e0..8d1637228d0 100644 --- a/spec/support/helpers/gpg_helpers.rb +++ b/spec/support/helpers/gpg_helpers.rb @@ -1,5 +1,9 @@ +# frozen_string_literal: true + module GpgHelpers - SIGNED_COMMIT_SHA = '8a852d50dda17cc8fd1408d2fd0c5b0f24c76ca4'.freeze + SIGNED_COMMIT_SHA = '8a852d50dda17cc8fd1408d2fd0c5b0f24c76ca4' + SIGNED_AND_AUTHORED_SHA = '3c1d9a0266cb0c62d926f4a6c649beed561846f5' + DIFFERING_EMAIL_SHA = 'a17a9f66543673edf0a3d1c6b93bdda3fe600f32' module User1 extend self diff --git a/spec/support/shared_examples/services/check_ingress_ip_address_service_shared_examples.rb b/spec/support/shared_examples/services/check_ingress_ip_address_service_shared_examples.rb new file mode 100644 index 00000000000..14638a574a5 --- /dev/null +++ b/spec/support/shared_examples/services/check_ingress_ip_address_service_shared_examples.rb @@ -0,0 +1,33 @@ +shared_examples 'check ingress ip executions' do |app_name| + describe '#execute' do + let(:application) { create(app_name, :installed) } + let(:service) { described_class.new(application) } + let(:kubeclient) { double(::Kubeclient::Client, get_service: kube_service) } + + context 'when the ingress ip address is available' do + it 'updates the external_ip for the app' do + subject + + expect(application.external_ip).to eq('111.222.111.222') + end + end + + context 'when the ingress ip address is not available' do + let(:ingress) { nil } + + it 'does not error' do + subject + end + end + + context 'when the exclusive lease cannot be obtained' do + it 'does not call kubeclient' do + stub_exclusive_lease_taken(lease_key, timeout: 15.seconds.to_i) + + subject + + expect(kubeclient).not_to have_received(:get_service) + end + end + end +end diff --git a/spec/views/layouts/header/_new_dropdown.haml_spec.rb b/spec/views/layouts/header/_new_dropdown.haml_spec.rb new file mode 100644 index 00000000000..2e19d0cec26 --- /dev/null +++ b/spec/views/layouts/header/_new_dropdown.haml_spec.rb @@ -0,0 +1,134 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'layouts/header/_new_dropdown' do + let(:user) { create(:user) } + + context 'group-specific links' do + let(:group) { create(:group) } + + before do + stub_current_user(user) + + assign(:group, group) + end + + context 'as a Group owner' do + before do + group.add_owner(user) + end + + it 'has a "New project" link' do + render + + expect(rendered).to have_link( + 'New project', + href: new_project_path(namespace_id: group.id) + ) + end + + it 'has a "New subgroup" link', :nested_groups do + render + + expect(rendered).to have_link( + 'New subgroup', + href: new_group_path(parent_id: group.id) + ) + end + end + end + + context 'project-specific links' do + let(:project) { create(:project, creator: user, namespace: user.namespace) } + + before do + assign(:project, project) + end + + context 'as a Project owner' do + before do + stub_current_user(user) + end + + it 'has a "New issue" link' do + render + + expect(rendered).to have_link( + 'New issue', + href: new_project_issue_path(project) + ) + end + + it 'has a "New merge request" link' do + render + + expect(rendered).to have_link( + 'New merge request', + href: project_new_merge_request_path(project) + ) + end + + it 'has a "New snippet" link' do + render + + expect(rendered).to have_link( + 'New snippet', + href: new_project_snippet_path(project) + ) + end + end + + context 'as a Project guest' do + let(:guest) { create(:user) } + + before do + stub_current_user(guest) + project.add_guest(guest) + end + + it 'has no "New merge request" link' do + render + + expect(rendered).not_to have_link('New merge request') + end + + it 'has no "New snippet" link' do + render + + expect(rendered).not_to have_link( + 'New snippet', + href: new_project_snippet_path(project) + ) + end + end + end + + context 'global links' do + before do + stub_current_user(user) + end + + it 'has a "New project" link' do + render + + expect(rendered).to have_link('New project', href: new_project_path) + end + + it 'has a "New group" link' do + render + + expect(rendered).to have_link('New group', href: new_group_path) + end + + it 'has a "New snippet" link' do + render + + expect(rendered).to have_link('New snippet', href: new_snippet_path) + end + end + + def stub_current_user(current_user) + allow(view).to receive(:current_user).and_return(current_user) + end +end |