Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2019-11-08 09:06:24 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2019-11-08 09:06:24 +0300
commit1ef4b65f55f4fc6524a47050b4f6d686beb81d3a (patch)
tree3efc2710e564b86e5e2420d65457f656454006bb /app
parent18a102a5b95198b6bc8db2589de6353997a33543 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/create_cluster/eks_cluster/components/create_eks_cluster.vue23
-rw-r--r--app/assets/javascripts/create_cluster/eks_cluster/components/service_credentials_form.vue140
-rw-r--r--app/assets/javascripts/create_cluster/eks_cluster/index.js25
-rw-r--r--app/assets/javascripts/create_cluster/eks_cluster/store/actions.js27
-rw-r--r--app/assets/javascripts/create_cluster/eks_cluster/store/index.js4
-rw-r--r--app/assets/javascripts/create_cluster/eks_cluster/store/mutation_types.js3
-rw-r--r--app/assets/javascripts/create_cluster/eks_cluster/store/mutations.js15
-rw-r--r--app/assets/javascripts/create_cluster/eks_cluster/store/state.js10
-rw-r--r--app/assets/javascripts/vue_shared/components/slot_switch.vue35
-rw-r--r--app/assets/stylesheets/utilities.scss3
-rw-r--r--app/controllers/application_controller.rb4
-rw-r--r--app/controllers/clusters/clusters_controller.rb23
-rw-r--r--app/helpers/application_settings_helper.rb4
-rw-r--r--app/helpers/clusters_helper.rb22
-rw-r--r--app/models/application_setting.rb22
-rw-r--r--app/models/application_setting_implementation.rb4
-rw-r--r--app/models/aws/role.rb6
-rw-r--r--app/presenters/clusterable_presenter.rb4
-rw-r--r--app/presenters/instance_clusterable_presenter.rb5
-rw-r--r--app/services/clusters/aws/fetch_credentials_service.rb12
-rw-r--r--app/views/admin/application_settings/_eks.html.haml31
-rw-r--r--app/views/admin/application_settings/integrations.html.haml2
-rw-r--r--app/views/clusters/clusters/aws/_new.html.haml11
-rw-r--r--app/views/clusters/clusters/cloud_providers/_cloud_provider_button.html.haml6
-rw-r--r--app/views/clusters/clusters/cloud_providers/_cloud_provider_selector.html.haml6
-rw-r--r--app/views/clusters/clusters/eks/_index.html.haml2
-rw-r--r--app/views/clusters/clusters/gcp/_new.html.haml7
-rw-r--r--app/views/clusters/clusters/new.html.haml33
28 files changed, 428 insertions, 61 deletions
diff --git a/app/assets/javascripts/create_cluster/eks_cluster/components/create_eks_cluster.vue b/app/assets/javascripts/create_cluster/eks_cluster/components/create_eks_cluster.vue
index 22ee368b8e0..6bcae6ab536 100644
--- a/app/assets/javascripts/create_cluster/eks_cluster/components/create_eks_cluster.vue
+++ b/app/assets/javascripts/create_cluster/eks_cluster/components/create_eks_cluster.vue
@@ -1,4 +1,5 @@
<script>
+import { mapState } from 'vuex';
import ServiceCredentialsForm from './service_credentials_form.vue';
import EksClusterConfigurationForm from './eks_cluster_configuration_form.vue';
@@ -16,14 +17,36 @@ export default {
type: String,
required: true,
},
+ accountAndExternalIdsHelpPath: {
+ type: String,
+ required: true,
+ },
+ createRoleArnHelpPath: {
+ type: String,
+ required: true,
+ },
+ externalLinkIcon: {
+ type: String,
+ required: true,
+ },
+ },
+ computed: {
+ ...mapState(['hasCredentials']),
},
};
</script>
<template>
<div class="js-create-eks-cluster">
<eks-cluster-configuration-form
+ v-if="hasCredentials"
:gitlab-managed-cluster-help-path="gitlabManagedClusterHelpPath"
:kubernetes-integration-help-path="kubernetesIntegrationHelpPath"
/>
+ <service-credentials-form
+ v-else
+ :create-role-arn-help-path="createRoleArnHelpPath"
+ :account-and-external-ids-help-path="accountAndExternalIdsHelpPath"
+ :external-link-icon="externalLinkIcon"
+ />
</div>
</template>
diff --git a/app/assets/javascripts/create_cluster/eks_cluster/components/service_credentials_form.vue b/app/assets/javascripts/create_cluster/eks_cluster/components/service_credentials_form.vue
index 79029b8cfa8..185fecba2d8 100644
--- a/app/assets/javascripts/create_cluster/eks_cluster/components/service_credentials_form.vue
+++ b/app/assets/javascripts/create_cluster/eks_cluster/components/service_credentials_form.vue
@@ -1,3 +1,141 @@
+<script>
+import { GlFormInput } from '@gitlab/ui';
+import { sprintf, s__, __ } from '~/locale';
+import _ from 'underscore';
+import { mapState, mapActions } from 'vuex';
+import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
+import LoadingButton from '~/vue_shared/components/loading_button.vue';
+
+export default {
+ components: {
+ GlFormInput,
+ LoadingButton,
+ ClipboardButton,
+ },
+ props: {
+ accountAndExternalIdsHelpPath: {
+ type: String,
+ required: true,
+ },
+ createRoleArnHelpPath: {
+ type: String,
+ required: true,
+ },
+ externalLinkIcon: {
+ type: String,
+ required: true,
+ },
+ },
+ data() {
+ return {
+ roleArn: '',
+ };
+ },
+ computed: {
+ ...mapState(['accountId', 'externalId', 'isCreatingRole', 'createRoleError']),
+ submitButtonDisabled() {
+ return this.isCreatingRole || !this.roleArn;
+ },
+ submitButtonLabel() {
+ return this.isCreatingRole
+ ? __('Authenticating')
+ : s__('ClusterIntegration|Authenticate with AWS');
+ },
+ accountAndExternalIdsHelpText() {
+ const escapedUrl = _.escape(this.accountAndExternalIdsHelpPath);
+
+ return sprintf(
+ s__(
+ 'ClusterIntegration|Create a provision role on %{startAwsLink}Amazon Web Services %{externalLinkIcon}%{endLink} using the account and external ID above. %{startMoreInfoLink}More information%{endLink}',
+ ),
+ {
+ startAwsLink:
+ '<a href="https://console.aws.amazon.com/iam/home?#roles" target="_blank" rel="noopener noreferrer">',
+ startMoreInfoLink: `<a href="${escapedUrl}" target="_blank" rel="noopener noreferrer">`,
+ externalLinkIcon: this.externalLinkIcon,
+ endLink: '</a>',
+ },
+ false,
+ );
+ },
+ provisionRoleArnHelpText() {
+ const escapedUrl = _.escape(this.createRoleArnHelpPath);
+
+ return sprintf(
+ s__(
+ 'ClusterIntegration|The Amazon Resource Name (ARN) associated with your role. If you do not have a provision role, first create one on %{startAwsLink}Amazon Web Services %{externalLinkIcon}%{endLink} using the above account and external IDs. %{startMoreInfoLink}More information%{endLink}',
+ ),
+ {
+ startAwsLink:
+ '<a href="https://console.aws.amazon.com/iam/home?#roles" target="_blank" rel="noopener noreferrer">',
+ startMoreInfoLink: `<a href="${escapedUrl}" target="_blank" rel="noopener noreferrer">`,
+ externalLinkIcon: this.externalLinkIcon,
+ endLink: '</a>',
+ },
+ false,
+ );
+ },
+ },
+ methods: {
+ ...mapActions(['createRole']),
+ },
+};
+</script>
<template>
- <form name="service-credentials-form"></form>
+ <form name="service-credentials-form" @submit.prevent="createRole({ roleArn, externalId })">
+ <h2>{{ s__('ClusterIntegration|Authenticate with Amazon Web Services') }}</h2>
+ <p>
+ {{
+ s__(
+ 'ClusterIntegration|You must grant access to your organization’s AWS resources in order to create a new EKS cluster. To grant access, create a provision role using the account and external ID below and provide us the ARN.',
+ )
+ }}
+ </p>
+ <div v-if="createRoleError" class="js-invalid-credentials bs-callout bs-callout-danger">
+ {{ createRoleError }}
+ </div>
+ <div class="form-row">
+ <div class="form-group col-md-6">
+ <label for="gitlab-account-id">{{ __('Account ID') }}</label>
+ <div class="input-group">
+ <gl-form-input id="gitlab-account-id" type="text" readonly :value="accountId" />
+ <div class="input-group-append">
+ <clipboard-button
+ :text="accountId"
+ :title="__('Copy Account ID to clipboard')"
+ class="input-group-text js-copy-account-id-button"
+ />
+ </div>
+ </div>
+ </div>
+ <div class="form-group col-md-6">
+ <label for="eks-external-id">{{ __('External ID') }}</label>
+ <div class="input-group">
+ <gl-form-input id="eks-external-id" type="text" readonly :value="externalId" />
+ <div class="input-group-append">
+ <clipboard-button
+ :text="externalId"
+ :title="__('Copy External ID to clipboard')"
+ class="input-group-text js-copy-external-id-button"
+ />
+ </div>
+ </div>
+ </div>
+ <div class="col-12 mb-3 mt-n3">
+ <p class="form-text text-muted" v-html="accountAndExternalIdsHelpText"></p>
+ </div>
+ </div>
+ <div class="form-group">
+ <label for="eks-provision-role-arn">{{ s__('ClusterIntegration|Provision Role ARN') }}</label>
+ <gl-form-input id="eks-provision-role-arn" v-model="roleArn" />
+ <p class="form-text text-muted" v-html="provisionRoleArnHelpText"></p>
+ </div>
+ <loading-button
+ class="js-submit-service-credentials"
+ type="submit"
+ :disabled="submitButtonDisabled"
+ :loading="isCreatingRole"
+ :label="submitButtonLabel"
+ />
+ </form>
</template>
diff --git a/app/assets/javascripts/create_cluster/eks_cluster/index.js b/app/assets/javascripts/create_cluster/eks_cluster/index.js
index 1f595e9b2df..e634a743d1d 100644
--- a/app/assets/javascripts/create_cluster/eks_cluster/index.js
+++ b/app/assets/javascripts/create_cluster/eks_cluster/index.js
@@ -1,16 +1,34 @@
import Vue from 'vue';
import Vuex from 'vuex';
+import { parseBoolean } from '~/lib/utils/common_utils';
import CreateEksCluster from './components/create_eks_cluster.vue';
import createStore from './store';
Vue.use(Vuex);
export default el => {
- const { gitlabManagedClusterHelpPath, kubernetesIntegrationHelpPath } = el.dataset;
+ const {
+ gitlabManagedClusterHelpPath,
+ kubernetesIntegrationHelpPath,
+ accountAndExternalIdsHelpPath,
+ createRoleArnHelpPath,
+ externalId,
+ accountId,
+ hasCredentials,
+ createRolePath,
+ externalLinkIcon,
+ } = el.dataset;
return new Vue({
el,
- store: createStore(),
+ store: createStore({
+ initialState: {
+ hasCredentials: parseBoolean(hasCredentials),
+ externalId,
+ accountId,
+ createRolePath,
+ },
+ }),
components: {
CreateEksCluster,
},
@@ -19,6 +37,9 @@ export default el => {
props: {
gitlabManagedClusterHelpPath,
kubernetesIntegrationHelpPath,
+ accountAndExternalIdsHelpPath,
+ createRoleArnHelpPath,
+ externalLinkIcon,
},
});
},
diff --git a/app/assets/javascripts/create_cluster/eks_cluster/store/actions.js b/app/assets/javascripts/create_cluster/eks_cluster/store/actions.js
index 917c8da6c3e..16a7547957e 100644
--- a/app/assets/javascripts/create_cluster/eks_cluster/store/actions.js
+++ b/app/assets/javascripts/create_cluster/eks_cluster/store/actions.js
@@ -1,4 +1,5 @@
import * as types from './mutation_types';
+import axios from '~/lib/utils/axios_utils';
export const setClusterName = ({ commit }, payload) => {
commit(types.SET_CLUSTER_NAME, payload);
@@ -12,6 +13,30 @@ export const setKubernetesVersion = ({ commit }, payload) => {
commit(types.SET_KUBERNETES_VERSION, payload);
};
+export const createRole = ({ dispatch, state: { createRolePath } }, payload) => {
+ dispatch('requestCreateRole');
+
+ return axios
+ .post(createRolePath, {
+ role_arn: payload.roleArn,
+ role_external_id: payload.externalId,
+ })
+ .then(() => dispatch('createRoleSuccess'))
+ .catch(error => dispatch('createRoleError', { error }));
+};
+
+export const requestCreateRole = ({ commit }) => {
+ commit(types.REQUEST_CREATE_ROLE);
+};
+
+export const createRoleSuccess = ({ commit }) => {
+ commit(types.CREATE_ROLE_SUCCESS);
+};
+
+export const createRoleError = ({ commit }, payload) => {
+ commit(types.CREATE_ROLE_ERROR, payload);
+};
+
export const setRegion = ({ commit }, payload) => {
commit(types.SET_REGION, payload);
};
@@ -39,5 +64,3 @@ export const setSecurityGroup = ({ commit }, payload) => {
export const setGitlabManagedCluster = ({ commit }, payload) => {
commit(types.SET_GITLAB_MANAGED_CLUSTER, payload);
};
-
-export default () => {};
diff --git a/app/assets/javascripts/create_cluster/eks_cluster/store/index.js b/app/assets/javascripts/create_cluster/eks_cluster/store/index.js
index d575deafd19..22cca5b816e 100644
--- a/app/assets/javascripts/create_cluster/eks_cluster/store/index.js
+++ b/app/assets/javascripts/create_cluster/eks_cluster/store/index.js
@@ -8,12 +8,12 @@ import clusterDropdownStore from './cluster_dropdown';
import * as awsServices from '../services/aws_services_facade';
-const createStore = () =>
+const createStore = ({ initialState }) =>
new Vuex.Store({
actions,
getters,
mutations,
- state: state(),
+ state: Object.assign(state(), initialState),
modules: {
roles: {
namespaced: true,
diff --git a/app/assets/javascripts/create_cluster/eks_cluster/store/mutation_types.js b/app/assets/javascripts/create_cluster/eks_cluster/store/mutation_types.js
index 82eb512ac07..398b48d725f 100644
--- a/app/assets/javascripts/create_cluster/eks_cluster/store/mutation_types.js
+++ b/app/assets/javascripts/create_cluster/eks_cluster/store/mutation_types.js
@@ -8,3 +8,6 @@ export const SET_SUBNET = 'SET_SUBNET';
export const SET_ROLE = 'SET_ROLE';
export const SET_SECURITY_GROUP = 'SET_SECURITY_GROUP';
export const SET_GITLAB_MANAGED_CLUSTER = 'SET_GITLAB_MANAGED_CLUSTER';
+export const REQUEST_CREATE_ROLE = 'REQUEST_CREATE_ROLE';
+export const CREATE_ROLE_SUCCESS = 'CREATE_ROLE_SUCCESS';
+export const CREATE_ROLE_ERROR = 'CREATE_ROLE_ERROR';
diff --git a/app/assets/javascripts/create_cluster/eks_cluster/store/mutations.js b/app/assets/javascripts/create_cluster/eks_cluster/store/mutations.js
index 79950ac7dce..f7752a23574 100644
--- a/app/assets/javascripts/create_cluster/eks_cluster/store/mutations.js
+++ b/app/assets/javascripts/create_cluster/eks_cluster/store/mutations.js
@@ -31,4 +31,19 @@ export default {
[types.SET_GITLAB_MANAGED_CLUSTER](state, { gitlabManagedCluster }) {
state.gitlabManagedCluster = gitlabManagedCluster;
},
+ [types.REQUEST_CREATE_ROLE](state) {
+ state.isCreatingRole = true;
+ state.createRoleError = null;
+ state.hasCredentials = false;
+ },
+ [types.CREATE_ROLE_SUCCESS](state) {
+ state.isCreatingRole = false;
+ state.createRoleError = null;
+ state.hasCredentials = true;
+ },
+ [types.CREATE_ROLE_ERROR](state, { error }) {
+ state.isCreatingRole = false;
+ state.createRoleError = error;
+ state.hasCredentials = false;
+ },
};
diff --git a/app/assets/javascripts/create_cluster/eks_cluster/store/state.js b/app/assets/javascripts/create_cluster/eks_cluster/store/state.js
index bf74213bdce..b69ae5b51e5 100644
--- a/app/assets/javascripts/create_cluster/eks_cluster/store/state.js
+++ b/app/assets/javascripts/create_cluster/eks_cluster/store/state.js
@@ -1,8 +1,14 @@
import { KUBERNETES_VERSIONS } from '../constants';
export default () => ({
- isValidatingCredentials: false,
- validCredentials: false,
+ createRolePath: null,
+
+ isCreatingRole: false,
+ roleCreated: false,
+ createRoleError: false,
+
+ accountId: '',
+ externalId: '',
clusterName: '',
environmentScope: '*',
diff --git a/app/assets/javascripts/vue_shared/components/slot_switch.vue b/app/assets/javascripts/vue_shared/components/slot_switch.vue
new file mode 100644
index 00000000000..67726f01744
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/slot_switch.vue
@@ -0,0 +1,35 @@
+<script>
+/**
+ * Allows to toggle slots based on an array of slot names.
+ */
+export default {
+ name: 'SlotSwitch',
+
+ props: {
+ activeSlotNames: {
+ type: Array,
+ required: true,
+ },
+
+ tagName: {
+ type: String,
+ required: false,
+ default: 'div',
+ },
+ },
+
+ computed: {
+ allSlotNames() {
+ return Object.keys(this.$slots);
+ },
+ },
+};
+</script>
+
+<template>
+ <component :is="tagName">
+ <template v-for="slotName in allSlotNames">
+ <slot v-if="activeSlotNames.includes(slotName)" :name="slotName"></slot>
+ </template>
+ </component>
+</template>
diff --git a/app/assets/stylesheets/utilities.scss b/app/assets/stylesheets/utilities.scss
index f53b6fbb1e7..3b3a2778b23 100644
--- a/app/assets/stylesheets/utilities.scss
+++ b/app/assets/stylesheets/utilities.scss
@@ -28,3 +28,6 @@
.border-color-blue-300 { border-color: $blue-300; }
.border-color-default { border-color: $border-color; }
.box-shadow-default { box-shadow: 0 2px 4px 0 $black-transparent; }
+
+.gl-w-64 { width: px-to-rem($grid-size * 8); }
+.gl-h-64 { height: px-to-rem($grid-size * 8); }
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 30882f5a9f7..7329753ac54 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -214,6 +214,10 @@ class ApplicationController < ActionController::Base
end
end
+ def respond_201
+ head :created
+ end
+
def respond_422
head :unprocessable_entity
end
diff --git a/app/controllers/clusters/clusters_controller.rb b/app/controllers/clusters/clusters_controller.rb
index abec237dd1d..7c5c4bb8e80 100644
--- a/app/controllers/clusters/clusters_controller.rb
+++ b/app/controllers/clusters/clusters_controller.rb
@@ -3,12 +3,12 @@
class Clusters::ClustersController < Clusters::BaseController
include RoutableActions
- before_action :cluster, except: [:index, :new, :create_gcp, :create_user]
+ before_action :cluster, except: [:index, :new, :create_gcp, :create_user, :authorize_aws_role]
before_action :generate_gcp_authorize_url, only: [:new]
before_action :validate_gcp_token, only: [:new]
before_action :gcp_cluster, only: [:new]
before_action :user_cluster, only: [:new]
- before_action :authorize_create_cluster!, only: [:new]
+ before_action :authorize_create_cluster!, only: [:new, :authorize_aws_role]
before_action :authorize_update_cluster!, only: [:update]
before_action :authorize_admin_cluster!, only: [:destroy]
before_action :update_applications_status, only: [:cluster_status]
@@ -43,10 +43,13 @@ class Clusters::ClustersController < Clusters::BaseController
def new
return unless Feature.enabled?(:create_eks_clusters)
- @gke_selected = params[:provider] == 'gke'
- @eks_selected = params[:provider] == 'eks'
+ if params[:provider] == 'aws'
+ @aws_role = current_user.aws_role || Aws::Role.new
+ @aws_role.ensure_role_external_id!
- return redirect_to @authorize_url if @gke_selected && @authorize_url && !@valid_gcp_token
+ elsif params[:provider] == 'gcp'
+ redirect_to @authorize_url if @authorize_url && !@valid_gcp_token
+ end
end
# Overridding ActionController::Metal#status is NOT a good idea
@@ -132,6 +135,12 @@ class Clusters::ClustersController < Clusters::BaseController
end
end
+ def authorize_aws_role
+ role = current_user.build_aws_role(create_role_params)
+
+ role.save ? respond_201 : respond_422
+ end
+
private
def update_params
@@ -203,6 +212,10 @@ class Clusters::ClustersController < Clusters::BaseController
)
end
+ def create_role_params
+ params.require(:cluster).permit(:role_arn, :role_external_id)
+ end
+
def generate_gcp_authorize_url
params = Feature.enabled?(:create_eks_clusters) ? { provider: :gke } : {}
state = generate_session_key_redirect(clusterable.new_path(params).to_s)
diff --git a/app/helpers/application_settings_helper.rb b/app/helpers/application_settings_helper.rb
index 02226cc1651..ec2e1648904 100644
--- a/app/helpers/application_settings_helper.rb
+++ b/app/helpers/application_settings_helper.rb
@@ -193,6 +193,10 @@ module ApplicationSettingsHelper
:dsa_key_restriction,
:ecdsa_key_restriction,
:ed25519_key_restriction,
+ :eks_integration_enabled,
+ :eks_account_id,
+ :eks_access_key_id,
+ :eks_secret_access_key,
:email_author_in_body,
:enabled_git_access_protocol,
:enforce_terms,
diff --git a/app/helpers/clusters_helper.rb b/app/helpers/clusters_helper.rb
index 7ca509873cc..0cfb45a12e5 100644
--- a/app/helpers/clusters_helper.rb
+++ b/app/helpers/clusters_helper.rb
@@ -6,6 +6,28 @@ module ClustersHelper
false
end
+ def create_new_cluster_label(provider: nil)
+ case provider
+ when 'aws'
+ s_('ClusterIntegration|Create new Cluster on EKS')
+ when 'gcp'
+ s_('ClusterIntegration|Create new Cluster on GKE')
+ else
+ s_('ClusterIntegration|Create new Cluster')
+ end
+ end
+
+ def new_cluster_partial(provider: nil)
+ case provider
+ when 'aws'
+ 'clusters/clusters/aws/new'
+ when 'gcp'
+ 'clusters/clusters/gcp/new'
+ else
+ 'clusters/clusters/cloud_providers/cloud_provider_selector'
+ end
+ end
+
def render_gcp_signup_offer
return if Gitlab::CurrentSettings.current_application_settings.hide_third_party_offers?
return unless show_gcp_signup_offer?
diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb
index 07335b6a883..6a34f293a4a 100644
--- a/app/models/application_setting.rb
+++ b/app/models/application_setting.rb
@@ -274,6 +274,22 @@ class ApplicationSetting < ApplicationRecord
presence: true,
if: :lets_encrypt_terms_of_service_accepted?
+ validates :eks_integration_enabled,
+ inclusion: { in: [true, false] }
+
+ validates :eks_account_id,
+ format: { with: Gitlab::Regex.aws_account_id_regex,
+ message: Gitlab::Regex.aws_account_id_message },
+ if: :eks_integration_enabled?
+
+ validates :eks_access_key_id,
+ length: { in: 16..128 },
+ if: :eks_integration_enabled?
+
+ validates :eks_secret_access_key,
+ presence: true,
+ if: :eks_integration_enabled?
+
validates_with X509CertificateCredentialsValidator,
certificate: :external_auth_client_cert,
pkey: :external_auth_client_key,
@@ -304,6 +320,12 @@ class ApplicationSetting < ApplicationRecord
algorithm: 'aes-256-gcm',
encode: true
+ attr_encrypted :eks_secret_access_key,
+ mode: :per_attribute_iv,
+ key: Settings.attr_encrypted_db_key_base_truncated,
+ algorithm: 'aes-256-gcm',
+ encode: true
+
before_validation :ensure_uuid!
before_save :ensure_runners_registration_token
diff --git a/app/models/application_setting_implementation.rb b/app/models/application_setting_implementation.rb
index 6cc77cca8a3..9119f8766eb 100644
--- a/app/models/application_setting_implementation.rb
+++ b/app/models/application_setting_implementation.rb
@@ -54,6 +54,10 @@ module ApplicationSettingImplementation
dsa_key_restriction: 0,
ecdsa_key_restriction: 0,
ed25519_key_restriction: 0,
+ eks_integration_enabled: false,
+ eks_account_id: nil,
+ eks_access_key_id: nil,
+ eks_secret_access_key: nil,
first_day_of_week: 0,
gitaly_timeout_default: 55,
gitaly_timeout_fast: 10,
diff --git a/app/models/aws/role.rb b/app/models/aws/role.rb
index 836107435ad..54132be749d 100644
--- a/app/models/aws/role.rb
+++ b/app/models/aws/role.rb
@@ -13,5 +13,11 @@ module Aws
with: Gitlab::Regex.aws_arn_regex,
message: Gitlab::Regex.aws_arn_regex_message
}
+
+ before_validation :ensure_role_external_id!, on: :create
+
+ def ensure_role_external_id!
+ self.role_external_id ||= SecureRandom.hex(20)
+ end
end
end
diff --git a/app/presenters/clusterable_presenter.rb b/app/presenters/clusterable_presenter.rb
index 34dffbf40fd..d6f67c1f2e5 100644
--- a/app/presenters/clusterable_presenter.rb
+++ b/app/presenters/clusterable_presenter.rb
@@ -29,6 +29,10 @@ class ClusterablePresenter < Gitlab::View::Presenter::Delegated
new_polymorphic_path([clusterable, :cluster], options)
end
+ def authorize_aws_role_path
+ polymorphic_path([clusterable, :clusters], action: :authorize_aws_role)
+ end
+
def create_user_clusters_path
polymorphic_path([clusterable, :clusters], action: :create_user)
end
diff --git a/app/presenters/instance_clusterable_presenter.rb b/app/presenters/instance_clusterable_presenter.rb
index 908cd17678d..f820c0f6b42 100644
--- a/app/presenters/instance_clusterable_presenter.rb
+++ b/app/presenters/instance_clusterable_presenter.rb
@@ -52,6 +52,11 @@ class InstanceClusterablePresenter < ClusterablePresenter
create_gcp_admin_clusters_path
end
+ override :authorize_aws_role_path
+ def authorize_aws_role_path
+ authorize_aws_role_admin_clusters_path
+ end
+
override :empty_state_help_text
def empty_state_help_text
s_('ClusterIntegration|Adding an integration will share the cluster across all projects.')
diff --git a/app/services/clusters/aws/fetch_credentials_service.rb b/app/services/clusters/aws/fetch_credentials_service.rb
index e1d04fac976..29442208c62 100644
--- a/app/services/clusters/aws/fetch_credentials_service.rb
+++ b/app/services/clusters/aws/fetch_credentials_service.rb
@@ -36,20 +36,12 @@ module Clusters
::Aws::Credentials.new(access_key_id, secret_access_key)
end
- ##
- # This setting is not yet configurable or documented as these
- # services are not currently used. This will be addressed in
- # https://gitlab.com/gitlab-org/gitlab/merge_requests/18307
def access_key_id
- Gitlab.config.kubernetes.provisioners.aws.access_key_id
+ Gitlab::CurrentSettings.eks_access_key_id
end
- ##
- # This setting is not yet configurable or documented as these
- # services are not currently used. This will be addressed in
- # https://gitlab.com/gitlab-org/gitlab/merge_requests/18307
def secret_access_key
- Gitlab.config.kubernetes.provisioners.aws.secret_access_key
+ Gitlab::CurrentSettings.eks_secret_access_key
end
def session_name
diff --git a/app/views/admin/application_settings/_eks.html.haml b/app/views/admin/application_settings/_eks.html.haml
new file mode 100644
index 00000000000..b1f7ed76281
--- /dev/null
+++ b/app/views/admin/application_settings/_eks.html.haml
@@ -0,0 +1,31 @@
+- expanded = integration_expanded?('eks_')
+%section.settings.as-eks.no-animate#js-eks-settings{ class: ('expanded' if expanded) }
+ .settings-header
+ %h4
+ = _('Amazon EKS')
+ %button.btn.js-settings-toggle{ type: 'button' }
+ = expanded ? 'Collapse' : 'Expand'
+ %p
+ = _('Amazon EKS integration allows you to provision EKS clusters from GitLab.')
+
+ .settings-content
+ = form_for @application_setting, url: integrations_admin_application_settings_path(anchor: 'js-eks-settings'), html: { class: 'fieldset-form' } do |f|
+ = form_errors(@application_setting)
+
+ %fieldset
+ .form-group
+ .form-check
+ = f.check_box :eks_integration_enabled, class: 'form-check-input'
+ = f.label :eks_integration_enabled, class: 'form-check-label' do
+ Enable Amazon EKS integration
+ .form-group
+ = f.label :eks_account_id, 'Account ID', class: 'label-bold'
+ = f.text_field :eks_account_id, class: 'form-control'
+ .form-group
+ = f.label :eks_access_key_id, 'Access key ID', class: 'label-bold'
+ = f.text_field :eks_access_key_id, class: 'form-control'
+ .form-group
+ = f.label :eks_secret_access_key, 'Secret access key', class: 'label-bold'
+ = f.password_field :eks_secret_access_key, value: @application_setting.eks_secret_access_key, class: 'form-control'
+
+ = f.submit 'Save changes', class: "btn btn-success"
diff --git a/app/views/admin/application_settings/integrations.html.haml b/app/views/admin/application_settings/integrations.html.haml
index 0045a149c97..519d2bf9bbc 100644
--- a/app/views/admin/application_settings/integrations.html.haml
+++ b/app/views/admin/application_settings/integrations.html.haml
@@ -8,4 +8,4 @@
= render 'admin/application_settings/third_party_offers'
= render 'admin/application_settings/snowplow'
= render_if_exists 'admin/application_settings/pendo'
-
+= render 'admin/application_settings/eks' if Feature.enabled?(:create_eks_clusters)
diff --git a/app/views/clusters/clusters/aws/_new.html.haml b/app/views/clusters/clusters/aws/_new.html.haml
new file mode 100644
index 00000000000..fe8b606af70
--- /dev/null
+++ b/app/views/clusters/clusters/aws/_new.html.haml
@@ -0,0 +1,11 @@
+- if !Gitlab::CurrentSettings.eks_integration_enabled?
+ - documentation_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: help_page_path("integration/amazon") }
+ = s_('Amazon authentication is not %{link_start}correctly configured%{link_end}. Ask your GitLab administrator if you want to use this service.').html_safe % { link_start: documentation_link_start, link_end: '<a/>'.html_safe }
+- else
+ .js-create-eks-cluster-form-container{ data: { 'gitlab-managed-cluster-help-path' => help_page_path('user/project/clusters/index.md', anchor: 'gitlab-managed-clusters'),
+ 'create-role-path' => clusterable.authorize_aws_role_path,
+ 'account-id' => Gitlab::CurrentSettings.eks_account_id,
+ 'external-id' => @aws_role.role_external_id,
+ 'kubernetes-integration-help-path' => help_page_path('user/project/clusters/index'),
+ 'external-link-icon' => icon('external-link'),
+ 'has-credentials' => @aws_role.role_arn.present?.to_s } }
diff --git a/app/views/clusters/clusters/cloud_providers/_cloud_provider_button.html.haml b/app/views/clusters/clusters/cloud_providers/_cloud_provider_button.html.haml
index d4999798c19..56d46580b9e 100644
--- a/app/views/clusters/clusters/cloud_providers/_cloud_provider_button.html.haml
+++ b/app/views/clusters/clusters/cloud_providers/_cloud_provider_button.html.haml
@@ -1,8 +1,10 @@
- provider = local_assigns.fetch(:provider)
- logo_path = local_assigns.fetch(:logo_path)
- label = local_assigns.fetch(:label)
+- last = local_assigns.fetch(:last, false)
+- classes = ['btn btn-light btn-outline flex-fill d-inline-flex flex-column justify-content-center align-items-center', ('mr-3' unless last)]
-= link_to clusterable.new_path(provider: provider), class: 'btn gl-button btn-outline flex-fill d-inline-flex flex-column mr-3 justify-content-center align-items-center' do
- .svg-content= image_tag logo_path, alt: label, class: 'gl-w-13 gl-h-13'
+= link_to clusterable.new_path(provider: provider), class: classes do
+ .svg-content.p-2= image_tag logo_path, alt: label, class: 'gl-w-64 gl-h-64'
%span
= label
diff --git a/app/views/clusters/clusters/cloud_providers/_cloud_provider_selector.html.haml b/app/views/clusters/clusters/cloud_providers/_cloud_provider_selector.html.haml
index 7a93a7604f5..91925f5f96f 100644
--- a/app/views/clusters/clusters/cloud_providers/_cloud_provider_selector.html.haml
+++ b/app/views/clusters/clusters/cloud_providers/_cloud_provider_selector.html.haml
@@ -2,10 +2,10 @@
- eks_label = s_('ClusterIntegration|Amazon EKS')
- create_cluster_label = s_('ClusterIntegration|Create cluster on')
.d-flex.flex-column
- %h5
+ %h5.mb-3
= create_cluster_label
.d-flex
= render partial: 'clusters/clusters/cloud_providers/cloud_provider_button',
- locals: { provider: 'eks', label: eks_label, logo_path: 'illustrations/logos/amazon_eks.svg' }
+ locals: { provider: 'aws', label: eks_label, logo_path: 'illustrations/logos/amazon_eks.svg' }
= render partial: 'clusters/clusters/cloud_providers/cloud_provider_button',
- locals: { provider: 'gke', label: gke_label, logo_path: 'illustrations/logos/google_gke.svg' }
+ locals: { provider: 'gcp', label: gke_label, logo_path: 'illustrations/logos/google_gke.svg', last: true }
diff --git a/app/views/clusters/clusters/eks/_index.html.haml b/app/views/clusters/clusters/eks/_index.html.haml
deleted file mode 100644
index db64698a7f2..00000000000
--- a/app/views/clusters/clusters/eks/_index.html.haml
+++ /dev/null
@@ -1,2 +0,0 @@
-.js-create-eks-cluster-form-container{ data: { 'gitlab-managed-cluster-help-path' => help_page_path('user/project/clusters/index.md', anchor: 'gitlab-managed-clusters'),
-'kubernetes-integration-help-path' => help_page_path('user/project/clusters/index') } }
diff --git a/app/views/clusters/clusters/gcp/_new.html.haml b/app/views/clusters/clusters/gcp/_new.html.haml
new file mode 100644
index 00000000000..3d47f4bf2c3
--- /dev/null
+++ b/app/views/clusters/clusters/gcp/_new.html.haml
@@ -0,0 +1,7 @@
+= render 'clusters/clusters/gcp/header'
+- if @valid_gcp_token
+ = render 'clusters/clusters/gcp/form'
+- elsif @authorize_url
+ = render 'clusters/clusters/gcp/signin_with_google_button'
+- else
+ = render 'clusters/clusters/gcp/gcp_not_configured'
diff --git a/app/views/clusters/clusters/new.html.haml b/app/views/clusters/clusters/new.html.haml
index 2c23426aaf9..cb8cbe4e6f2 100644
--- a/app/views/clusters/clusters/new.html.haml
+++ b/app/views/clusters/clusters/new.html.haml
@@ -2,9 +2,6 @@
- page_title _('Kubernetes Cluster')
- create_eks_enabled = Feature.enabled?(:create_eks_clusters)
- active_tab = local_assigns.fetch(:active_tab, 'create')
-- create_on_gke_tab_label = s_('ClusterIntegration|Create new Cluster on GKE')
-- create_on_eks_tab_label = s_('ClusterIntegration|Create new Cluster on EKS')
-- create_new_cluster_label = s_('ClusterIntegration|Create new Cluster')
= javascript_include_tag 'https://apis.google.com/js/api.js'
= render_gcp_signup_offer
@@ -18,14 +15,9 @@
%a.nav-link{ href: '#create-cluster-pane', id: 'create-cluster-tab', class: active_when(active_tab == 'create'), data: { toggle: 'tab' }, role: 'tab' }
%span
- if create_eks_enabled
- - if @gke_selected
- = create_on_gke_tab_label
- - elsif @eks_selected
- = create_on_eks_tab_label
- - else
- = create_new_cluster_label
+ = create_new_cluster_label(provider: params[:provider])
- else
- = create_on_gke_tab_label
+ = create_new_cluster_label(provider: 'gcp')
%li.nav-item{ role: 'presentation' }
%a.nav-link{ href: '#add-cluster-pane', id: 'add-cluster-tab', class: active_when(active_tab == 'add'), data: { toggle: 'tab' }, role: 'tab' }
%span Add existing cluster
@@ -33,27 +25,10 @@
.tab-content.gitlab-tab-content
- if create_eks_enabled
.tab-pane{ id: 'create-cluster-pane', class: active_when(active_tab == 'create'), role: 'tabpanel' }
- - if @gke_selected
- = render 'clusters/clusters/gcp/header'
- - if @valid_gcp_token
- = render 'clusters/clusters/gcp/form'
- - elsif @authorize_url
- = render 'clusters/clusters/gcp/signin_with_google_button'
- - else
- = render 'clusters/clusters/gcp/gcp_not_configured'
- - elsif @eks_selected
- = render 'clusters/clusters/eks/index'
- - else
- = render 'clusters/clusters/cloud_providers/cloud_provider_selector'
+ = render new_cluster_partial(provider: params[:provider])
- else
.tab-pane{ id: 'create-cluster-pane', class: active_when(active_tab == 'create'), role: 'tabpanel' }
- = render 'clusters/clusters/gcp/header'
- - if @valid_gcp_token
- = render 'clusters/clusters/gcp/form'
- - elsif @authorize_url
- = render 'clusters/clusters/gcp/signin_with_google_button'
- - else
- = render 'clusters/clusters/gcp/gcp_not_configured'
+ = render new_cluster_partial(provider: 'gcp')
.tab-pane{ id: 'add-cluster-pane', class: active_when(active_tab == 'add'), role: 'tabpanel' }
= render 'clusters/clusters/user/header'