diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline_container.vue b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline_container.vue
index ffc3e0967d413283a620649f0c7c333942a74b4f..a297156ab107aa35fcaaa7a9a1e63c6b99ac0e81 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline_container.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline_container.vue
@@ -4,6 +4,7 @@ import ArtifactsApp from './artifacts_list_app.vue';
 import Deployment from './deployment.vue';
 import MrWidgetContainer from './mr_widget_container.vue';
 import MrWidgetPipeline from './mr_widget_pipeline.vue';
+import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
 
 /**
  * Renders the pipeline and related deployments from the store.
@@ -23,6 +24,7 @@ export default {
     MergeTrainPositionIndicator: () =>
       import('ee_component/vue_merge_request_widget/components/merge_train_position_indicator.vue'),
   },
+  mixins: [glFeatureFlagsMixin()],
   props: {
     mr: {
       type: Object,
@@ -62,7 +64,7 @@ export default {
       return this.isPostMerge ? this.mr.mergePipeline : this.mr.pipeline;
     },
     showVisualReviewAppLink() {
-      return this.mr.visualReviewAppAvailable;
+      return this.mr.visualReviewAppAvailable && this.glFeatures.anonymousVisualReviewFeedback;
     },
     showMergeTrainPositionIndicator() {
       return _.isNumber(this.mr.mergeTrainIndex);
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/review_app_link.vue b/app/assets/javascripts/vue_merge_request_widget/components/review_app_link.vue
index 75f557d05dd47310eb304f4533e8157c6377bed6..d2d32492e6ca4b5795b881886ce97099c7885169 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/review_app_link.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/review_app_link.vue
@@ -18,7 +18,14 @@ export default {
 };
 </script>
 <template>
-  <a :href="link" target="_blank" rel="noopener noreferrer nofollow" :class="cssClass">
+  <a
+    :href="link"
+    target="_blank"
+    rel="noopener noreferrer nofollow"
+    :class="cssClass"
+    data-track-event="open_review_app"
+    data-track-label="review_app"
+  >
     {{ __('View app') }} <icon class="fgray" name="external-link" />
   </a>
 </template>
diff --git a/app/models/project.rb b/app/models/project.rb
index 9a208b0058b04dcbad8a1078074afac33ba268c2..0887e742d96559787e0009892a9cf338e544de74 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -1773,7 +1773,6 @@ class Project < ApplicationRecord
     InternalId.flush_records!(project: self)
 
     import_state.finish
-    import_state.remove_jid
     update_project_counter_caches
     after_create_default_branch
     join_pool_repository
diff --git a/app/models/project_import_state.rb b/app/models/project_import_state.rb
index bff00816e15915fc61de1e24bf65fa92622ed00b..b79e355492663e4e7b25e579c2122d217e889436 100644
--- a/app/models/project_import_state.rb
+++ b/app/models/project_import_state.rb
@@ -42,6 +42,14 @@ class ProjectImportState < ApplicationRecord
       end
     end
 
+    after_transition any => :finished do |state, _|
+      if state.jid.present?
+        Gitlab::SidekiqStatus.unset(state.jid)
+
+        state.update_column(:jid, nil)
+      end
+    end
+
     after_transition started: :finished do |state, _|
       project = state.project
 
@@ -81,14 +89,6 @@ class ProjectImportState < ApplicationRecord
     status == 'started' && project.import?
   end
 
-  def remove_jid
-    return unless jid
-
-    Gitlab::SidekiqStatus.unset(jid)
-
-    update_column(:jid, nil)
-  end
-
   # Refreshes the expiration time of the associated import job ID.
   #
   # This method can be used by asynchronous importers to refresh the status,
diff --git a/app/presenters/clusters/cluster_presenter.rb b/app/presenters/clusters/cluster_presenter.rb
index 1634d2479a03a2d0fbda3cec0bd6223f39e3fdf8..97771d84031f93bd67481b8e0ae65139850fd6a3 100644
--- a/app/presenters/clusters/cluster_presenter.rb
+++ b/app/presenters/clusters/cluster_presenter.rb
@@ -18,8 +18,20 @@ module Clusters
       end
     end
 
-    def gke_cluster_url
-      "https://console.cloud.google.com/kubernetes/clusters/details/#{provider.zone}/#{name}" if gcp?
+    def provider_label
+      if aws?
+        s_('ClusterIntegration|Elastic Kubernetes Service')
+      elsif gcp?
+        s_('ClusterIntegration|Google Kubernetes Engine')
+      end
+    end
+
+    def provider_management_url
+      if aws?
+        "https://console.aws.amazon.com/eks/home?region=#{provider.region}\#/clusters/#{name}"
+      elsif gcp?
+        "https://console.cloud.google.com/kubernetes/clusters/details/#{provider.zone}/#{name}"
+      end
     end
 
     def can_read_cluster?
diff --git a/app/views/clusters/clusters/_advanced_settings.html.haml b/app/views/clusters/clusters/_advanced_settings.html.haml
index 493d7a0085495341b1d4daaff717c56eb62105a7..59cdf2016fb521f75787c08e697945eeaeaee625 100644
--- a/app/views/clusters/clusters/_advanced_settings.html.haml
+++ b/app/views/clusters/clusters/_advanced_settings.html.haml
@@ -8,10 +8,10 @@
   - unless @cluster.provided_by_user?
     .append-bottom-20
       %label.append-bottom-10
-        = s_('ClusterIntegration|Google Kubernetes Engine')
+        = @cluster.provider_label
       %p
-        - link_gke = link_to(s_('ClusterIntegration|Google Kubernetes Engine'), @cluster.gke_cluster_url, target: '_blank', rel: 'noopener noreferrer')
-        = s_('ClusterIntegration|Manage your Kubernetes cluster by visiting %{link_gke}').html_safe % { link_gke: link_gke }
+        - provider_link = link_to(@cluster.provider_label, @cluster.provider_management_url, target: '_blank', rel: 'noopener noreferrer')
+        = s_('ClusterIntegration|Manage your Kubernetes cluster by visiting %{provider_link}').html_safe % { provider_link: provider_link }
 
   = form_for @cluster, url: clusterable.cluster_path(@cluster), as: :cluster, html: { class: 'cluster_management_form' } do |field|
 
diff --git a/changelogs/unreleased/22392-update-cluster-settings-provider-link.yml b/changelogs/unreleased/22392-update-cluster-settings-provider-link.yml
new file mode 100644
index 0000000000000000000000000000000000000000..cd041ea491b93788fec2ea48b522a83f674fc71f
--- /dev/null
+++ b/changelogs/unreleased/22392-update-cluster-settings-provider-link.yml
@@ -0,0 +1,5 @@
+---
+title: Update external link to provider in cluster settings
+merge_request: 20425
+author:
+type: fixed
diff --git a/changelogs/unreleased/29067-vrt-no-auth.yml b/changelogs/unreleased/29067-vrt-no-auth.yml
new file mode 100644
index 0000000000000000000000000000000000000000..5e3a81740c76a2c835e8e53a0cf908a8ed64bfba
--- /dev/null
+++ b/changelogs/unreleased/29067-vrt-no-auth.yml
@@ -0,0 +1,5 @@
+---
+title: Remove authentication step from visual review tools instructions
+merge_request:
+author:
+type: changed
diff --git a/changelogs/unreleased/33596-package-ci-metadata.yml b/changelogs/unreleased/33596-package-ci-metadata.yml
new file mode 100644
index 0000000000000000000000000000000000000000..24fecc077c874cb0cdbc9d410cb44eb82656918d
--- /dev/null
+++ b/changelogs/unreleased/33596-package-ci-metadata.yml
@@ -0,0 +1,5 @@
+---
+title: Improve job tokens and provide access helper
+merge_request: 19793
+author:
+type: other
diff --git a/doc/administration/integration/plantuml.md b/doc/administration/integration/plantuml.md
index 23803b8254301d28a19a0db369945c4df6b61d2b..33ac925748fcea160a75803c057ba1b8cfa99e02 100644
--- a/doc/administration/integration/plantuml.md
+++ b/doc/administration/integration/plantuml.md
@@ -60,11 +60,11 @@ mvn package
 The above sequence of commands will generate a WAR file that can be deployed
 using Tomcat:
 
-```sh
-sudo apt-get install tomcat7
-sudo cp target/plantuml.war /var/lib/tomcat7/webapps/plantuml.war
-sudo chown tomcat7:tomcat7 /var/lib/tomcat7/webapps/plantuml.war
-sudo service tomcat7 restart
+```shell
+sudo apt-get install tomcat8
+sudo cp target/plantuml.war /var/lib/tomcat8/webapps/plantuml.war
+sudo chown tomcat8:tomcat8 /var/lib/tomcat8/webapps/plantuml.war
+sudo service tomcat8 restart
 ```
 
 Once the Tomcat service restarts the PlantUML service will be ready and
@@ -74,7 +74,7 @@ listening for requests on port 8080:
 http://localhost:8080/plantuml
 ```
 
-you can change these defaults by editing the `/etc/tomcat7/server.xml` file.
+you can change these defaults by editing the `/etc/tomcat8/server.xml` file.
 
 Note that the default URL is different than when using the Docker-based image,
 where the service is available at the root of URL with no relative path. Adjust
diff --git a/doc/ci/review_apps/index.md b/doc/ci/review_apps/index.md
index b69562faad77d05cc01d8a6a1ef94d7a5e291331..0da1228aa537b6014ab2814d14032515a022a88d 100644
--- a/doc/ci/review_apps/index.md
+++ b/doc/ci/review_apps/index.md
@@ -163,6 +163,13 @@ that spawned the Review App.
 
 ### Configuring Visual Reviews
 
+Ensure that the `anonymous_visual_review_feedback` feature flag is enabled.
+Administrators can enable with a Rails console as follows:
+
+```ruby
+Feature.enabled(:anonymous_visual_review_feedback)
+```
+
 The feedback form is served through a script you add to pages in your Review App.
 If you have [Developer permissions](../../user/permissions.md) to the project,
 you can access it by clicking the **Review** button in the **Pipeline** section
@@ -221,6 +228,19 @@ NOTE: **Note:**
 Future enhancements [are planned](https://gitlab.com/gitlab-org/gitlab/issues/11322)
 to make this process even easier.
 
+### Determining merge request ID
+
+The visual review tools retrieve the merge request ID from the `data-merge-request-id`
+data attribute included in the `script` HTML tag used to add the visual review tools
+to your review app.
+
+​After determining the ID for the merge request to link to a visual review app, you
+can supply the ID by either:​​
+
+- Hardcoding it in the script tag via the data attribute `data-merge-request-id` of the app.
+- Dynamically adding the `data-merge-request-id` value during the build of the app.
+- Supplying it manually through the visual review form in the app.
+
 ### Using Visual Reviews
 
 After Visual Reviews has been [enabled](#configuring-visual-reviews) for the
@@ -231,25 +251,15 @@ the bottom-right corner.
 
 To use the feedback form:
 
-1. Create a [personal access token](../../user/profile/personal_access_tokens.md)
-   with the API scope selected.
-1. Paste the token into the feedback box when prompted. If you select **Remember me**,
-   your browser stores the token so that future visits to Review Apps at the same URL
-   will not require you to re-enter the token. To clear the token, click **Log out**.
 1. Make a comment on the visual review. You can make use of all the
    [Markdown annotations](../../user/markdown.md) that are also available in
    merge request comments.
+1. Submit your feedback anonymously or add your name.
 1. Finally, click **Send feedback**.
 
 After you make and submit a comment in the visual review box, it will appear
 automatically in the respective merge request.
 
-TIP: **Tip:**
-Because tokens must be entered on a per-domain basis and they can only be accessed
-once, different review apps will not remember your token. You can save the token
-to your password manager specifically for the purpose of Visual Reviews. This way,
-you will not need to create additional tokens for each merge request.
-
 ## Limitations
 
 Review App limitations are the same as [environments limitations](../environments.md#limitations).
diff --git a/doc/user/clusters/management_project.md b/doc/user/clusters/management_project.md
index 83b6f6fe300304953bfb04df7cdf5c4ca19d04ac..57a1f46ac6e265857ac27c02602f359f3815e6bc 100644
--- a/doc/user/clusters/management_project.md
+++ b/doc/user/clusters/management_project.md
@@ -55,7 +55,7 @@ To select a cluster management project to use:
 ### Configuring your pipeline
 
 After designating a project as the management project for the cluster,
-write a [`.gitlab-ci,yml`](../../ci/yaml/README.md) in that project. For example:
+write a [`.gitlab-ci.yml`](../../ci/yaml/README.md) in that project. For example:
 
 ```yaml
 configure cluster:
diff --git a/doc/user/project/labels.md b/doc/user/project/labels.md
index d8356abdd1cbb985bff66bd1ad722241f9707c4c..e4264615488403bb88ddbf2d05a6553916c9ae6b 100644
--- a/doc/user/project/labels.md
+++ b/doc/user/project/labels.md
@@ -12,7 +12,7 @@ requests are located.
 
 In GitLab, you can create project and group labels:
 
-- **Project labels** can be assigned to epics, issues and merge requests in that project only.
+- **Project labels** can be assigned to issues and merge requests in that project only.
 - **Group labels** can be assigned to any epics, issue and merge request in any project in
   that group, or any subgroups of the group.
 
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index a6b2c315effc81383c69f831b68f6c944673e6e9..0ef14bf8ed129ebc5b14eaa701a05c866a900576 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -3673,6 +3673,9 @@ msgstr ""
 msgid "ClusterIntegration|Did you know?"
 msgstr ""
 
+msgid "ClusterIntegration|Elastic Kubernetes Service"
+msgstr ""
+
 msgid "ClusterIntegration|Elastic Stack"
 msgstr ""
 
@@ -3904,7 +3907,7 @@ msgstr ""
 msgid "ClusterIntegration|Make sure your account %{link_to_requirements} to create Kubernetes clusters"
 msgstr ""
 
-msgid "ClusterIntegration|Manage your Kubernetes cluster by visiting %{link_gke}"
+msgid "ClusterIntegration|Manage your Kubernetes cluster by visiting %{provider_link}"
 msgstr ""
 
 msgid "ClusterIntegration|No IAM Roles found"
@@ -19327,13 +19330,10 @@ msgstr ""
 msgid "VisualReviewApp|%{stepStart}Step 2%{stepEnd}. Add it to the %{headTags} tags of every page of your application, ensuring the merge request ID is set or not set as required. "
 msgstr ""
 
-msgid "VisualReviewApp|%{stepStart}Step 3%{stepEnd}. Open the Review App and provide a %{linkStart}personal access token%{linkEnd}."
-msgstr ""
-
-msgid "VisualReviewApp|%{stepStart}Step 4%{stepEnd}. If not previously %{linkStart}configured%{linkEnd} by a developer, enter the merge request ID for the review when prompted. The ID of this merge request is %{stepStart}%{mrId}%{stepStart}."
+msgid "VisualReviewApp|%{stepStart}Step 3%{stepEnd}. If not previously %{linkStart}configured%{linkEnd} by a developer, enter the merge request ID for the review when prompted. The ID of this merge request is %{stepStart}%{mrId}%{stepStart}."
 msgstr ""
 
-msgid "VisualReviewApp|%{stepStart}Step 5%{stepEnd}. Leave feedback in the Review App."
+msgid "VisualReviewApp|%{stepStart}Step 4%{stepEnd}. Leave feedback in the Review App."
 msgstr ""
 
 msgid "VisualReviewApp|Copy merge request ID"
diff --git a/spec/javascripts/vue_mr_widget/components/review_app_link_spec.js b/spec/javascripts/vue_mr_widget/components/review_app_link_spec.js
index 68a65bd21c648dd3c8fb1ca451ddae3f0daa87ee..069bc14e01e3aa46445fa64710089bda4eef4bc8 100644
--- a/spec/javascripts/vue_mr_widget/components/review_app_link_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/review_app_link_spec.js
@@ -1,5 +1,6 @@
 import Vue from 'vue';
 import component from '~/vue_merge_request_widget/components/review_app_link.vue';
+import { mockTracking, triggerEvent } from 'spec/helpers/tracking_helper';
 import mountComponent from '../../helpers/vue_mount_component_helper';
 
 describe('review app link', () => {
@@ -35,4 +36,13 @@ describe('review app link', () => {
   it('renders svg icon', () => {
     expect(el.querySelector('svg')).not.toBeNull();
   });
+
+  it('tracks an event when clicked', () => {
+    const spy = mockTracking('_category_', el, spyOn);
+    triggerEvent(el);
+
+    expect(spy).toHaveBeenCalledWith('_category_', 'open_review_app', {
+      label: 'review_app',
+    });
+  });
 });
diff --git a/spec/javascripts/vue_mr_widget/mock_data.js b/spec/javascripts/vue_mr_widget/mock_data.js
index 089ec08fbf968409a544b3f074da4a3dbdcc006a..40bec001ffe8cfd5d080924c6540a3aaf9151eeb 100644
--- a/spec/javascripts/vue_mr_widget/mock_data.js
+++ b/spec/javascripts/vue_mr_widget/mock_data.js
@@ -284,7 +284,20 @@ export const mockStore = {
   targetBranch: 'target-branch',
   sourceBranch: 'source-branch',
   sourceBranchLink: 'source-branch-link',
-  deployments: [{ id: 0, name: 'bogus' }, { id: 1, name: 'bogus-docs' }],
+  deployments: [
+    {
+      id: 0,
+      name: 'bogus',
+      external_url: 'https://fake.com',
+      external_url_formatted: 'https://fake.com',
+    },
+    {
+      id: 1,
+      name: 'bogus-docs',
+      external_url: 'https://fake.com',
+      external_url_formatted: 'https://fake.com',
+    },
+  ],
   postMergeDeployments: [{ id: 0, name: 'prod' }, { id: 1, name: 'prod-docs' }],
   troubleshootingDocsPath: 'troubleshooting-docs-path',
   ciStatus: 'ci-status',
diff --git a/spec/models/project_import_state_spec.rb b/spec/models/project_import_state_spec.rb
index 0b4dcc62ff6c17bdbc6b819c49ada103c32c30b3..babde7b0670c11bf363f0d57941e3afc0e41e10d 100644
--- a/spec/models/project_import_state_spec.rb
+++ b/spec/models/project_import_state_spec.rb
@@ -95,30 +95,28 @@ describe ProjectImportState, type: :model do
     end
   end
 
-  describe '#remove_jid', :clean_gitlab_redis_cache do
-    let(:project) {  }
-
+  describe 'clearing `jid` after finish', :clean_gitlab_redis_cache do
     context 'without an JID' do
       it 'does nothing' do
-        import_state = create(:import_state)
+        import_state = create(:import_state, :started)
 
         expect(Gitlab::SidekiqStatus)
           .not_to receive(:unset)
 
-        import_state.remove_jid
+        import_state.finish!
       end
     end
 
     context 'with an JID' do
       it 'unsets the JID' do
-        import_state = create(:import_state, jid: '123')
+        import_state = create(:import_state, :started, jid: '123')
 
         expect(Gitlab::SidekiqStatus)
           .to receive(:unset)
           .with('123')
           .and_call_original
 
-        import_state.remove_jid
+        import_state.finish!
 
         expect(import_state.jid).to be_nil
       end
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 815ab7aa166d0f2effd36c52da34a61343c8d1e0..4e6a4bd6b51e519ecd8b3ccf0fe7e7dbfb9f495d 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -4335,7 +4335,6 @@ describe Project do
       expect(project.wiki.repository).to receive(:after_import)
       expect(import_state).to receive(:finish)
       expect(project).to receive(:update_project_counter_caches)
-      expect(import_state).to receive(:remove_jid)
       expect(project).to receive(:after_create_default_branch)
       expect(project).to receive(:refresh_markdown_cache!)
       expect(InternalId).to receive(:flush_records!).with(project: project)
diff --git a/spec/presenters/clusters/cluster_presenter_spec.rb b/spec/presenters/clusters/cluster_presenter_spec.rb
index 8bc5374f2db89d86a6d509d02500fa536133a4e8..6a1360807b76e96905b46672d5fc9bf1c181fa17 100644
--- a/spec/presenters/clusters/cluster_presenter_spec.rb
+++ b/spec/presenters/clusters/cluster_presenter_spec.rb
@@ -153,11 +153,42 @@ describe Clusters::ClusterPresenter do
     end
   end
 
-  describe '#gke_cluster_url' do
-    subject { described_class.new(cluster).gke_cluster_url }
+  describe '#provider_label' do
+    let(:cluster) { create(:cluster, provider_type: provider_type) }
 
-    it { is_expected.to include(cluster.provider.zone) }
-    it { is_expected.to include(cluster.name) }
+    subject { described_class.new(cluster).provider_label }
+
+    context 'AWS provider' do
+      let(:provider_type) { :aws }
+
+      it { is_expected.to eq('Elastic Kubernetes Service') }
+    end
+
+    context 'GCP provider' do
+      let(:provider_type) { :gcp }
+
+      it { is_expected.to eq('Google Kubernetes Engine') }
+    end
+  end
+
+  describe '#provider_management_url' do
+    let(:cluster) { provider.cluster }
+
+    subject { described_class.new(cluster).provider_management_url }
+
+    context 'AWS provider' do
+      let(:provider) { create(:cluster_provider_aws) }
+
+      it { is_expected.to include(provider.region) }
+      it { is_expected.to include(cluster.name) }
+    end
+
+    context 'GCP provider' do
+      let(:provider) { create(:cluster_provider_gcp) }
+
+      it { is_expected.to include(provider.zone) }
+      it { is_expected.to include(cluster.name) }
+    end
   end
 
   describe '#cluster_type_description' do