Skip to content
Snippets Groups Projects
Commit eed996ac authored by GitLab Bot's avatar GitLab Bot
Browse files

Add latest changes from gitlab-org/gitlab@master

parent b42f312d
No related branches found
No related tags found
No related merge requests found
Showing
with 472 additions and 220 deletions
Loading
Loading
@@ -2,14 +2,19 @@
import DropdownSearchInput from '~/vue_shared/components/dropdown/dropdown_search_input.vue';
import DropdownHiddenInput from '~/vue_shared/components/dropdown/dropdown_hidden_input.vue';
import DropdownButton from '~/vue_shared/components/dropdown/dropdown_button.vue';
import { GlIcon } from '@gitlab/ui';
 
const findItem = (items, valueProp, value) => items.find(item => item[valueProp] === value);
const toArray = value => [].concat(value);
const itemsProp = (items, prop) => items.map(item => item[prop]);
const defaultSearchFn = (searchQuery, labelProp) => item =>
item[labelProp].toLowerCase().indexOf(searchQuery) > -1;
 
export default {
components: {
DropdownButton,
DropdownSearchInput,
DropdownHiddenInput,
GlIcon,
},
props: {
fieldName: {
Loading
Loading
@@ -28,7 +33,7 @@ export default {
default: '',
},
value: {
type: [Object, String],
type: [Object, Array, String],
required: false,
default: () => null,
},
Loading
Loading
@@ -72,6 +77,11 @@ export default {
required: false,
default: false,
},
multiple: {
type: Boolean,
required: false,
default: false,
},
errorMessage: {
type: String,
required: false,
Loading
Loading
@@ -90,12 +100,11 @@ export default {
searchFn: {
type: Function,
required: false,
default: searchQuery => item => item.name.toLowerCase().indexOf(searchQuery) > -1,
default: defaultSearchFn,
},
},
data() {
return {
selectedItem: findItem(this.items, this.value),
searchQuery: '',
};
},
Loading
Loading
@@ -109,36 +118,52 @@ export default {
return this.disabledText;
}
 
if (!this.selectedItem) {
if (!this.selectedItems.length) {
return this.placeholder;
}
 
return this.selectedItemLabel;
return this.selectedItemsLabels;
},
results() {
if (!this.items) {
return [];
}
return this.items.filter(this.searchFn(this.searchQuery));
return this.getItemsOrEmptyList().filter(this.searchFn(this.searchQuery, this.labelProperty));
},
selectedItemLabel() {
return this.selectedItem && this.selectedItem[this.labelProperty];
selectedItems() {
const valueProp = this.valueProperty;
const valueList = toArray(this.value);
const items = this.getItemsOrEmptyList();
return items.filter(item => valueList.some(value => item[valueProp] === value));
},
selectedItemValue() {
return (this.selectedItem && this.selectedItem[this.valueProperty]) || '';
selectedItemsLabels() {
return itemsProp(this.selectedItems, this.labelProperty).join(', ');
},
},
watch: {
value(value) {
this.selectedItem = findItem(this.items, this.valueProperty, value);
selectedItemsValues() {
return itemsProp(this.selectedItems, this.valueProperty).join(', ');
},
},
methods: {
select(item) {
this.selectedItem = item;
getItemsOrEmptyList() {
return this.items || [];
},
selectSingle(item) {
this.$emit('input', item[this.valueProperty]);
},
selectMultiple(item) {
const value = toArray(this.value);
const itemValue = item[this.valueProperty];
const itemValueIndex = value.indexOf(itemValue);
if (itemValueIndex > -1) {
value.splice(itemValueIndex, 1);
} else {
value.push(itemValue);
}
this.$emit('input', value);
},
isSelected(item) {
return this.selectedItems.includes(item);
},
},
};
</script>
Loading
Loading
@@ -146,7 +171,7 @@ export default {
<template>
<div>
<div class="js-gcp-machine-type-dropdown dropdown">
<dropdown-hidden-input :name="fieldName" :value="selectedItemValue" />
<dropdown-hidden-input :name="fieldName" :value="selectedItemsValues" />
<dropdown-button
:class="{ 'border-danger': hasErrors }"
:is-disabled="disabled"
Loading
Loading
@@ -158,15 +183,28 @@ export default {
<div class="dropdown-content">
<ul>
<li v-if="!results.length">
<span class="js-empty-text menu-item">
{{ emptyText }}
</span>
<span class="js-empty-text menu-item">{{ emptyText }}</span>
</li>
<li v-for="item in results" :key="item.id">
<button class="js-dropdown-item" type="button" @click.prevent="select(item)">
<slot name="item" :item="item">
{{ item.name }}
</slot>
<button
v-if="multiple"
class="js-dropdown-item d-flex align-items-center"
type="button"
@click.stop.prevent="selectMultiple(item)"
>
<gl-icon
:class="[{ invisible: !isSelected(item) }, 'mr-1']"
name="mobile-issue-close"
/>
<slot name="item" :item="item">{{ item.name }}</slot>
</button>
<button
v-else
class="js-dropdown-item"
type="button"
@click.prevent="selectSingle(item)"
>
<slot name="item" :item="item">{{ item.name }}</slot>
</button>
</li>
</ul>
Loading
Loading
@@ -182,8 +220,7 @@ export default {
'text-muted': !hasErrors,
},
]"
>{{ errorMessage }}</span
>
{{ errorMessage }}
</span>
</div>
</template>
Loading
Loading
@@ -41,6 +41,7 @@ export default {
v-if="hasCredentials"
:gitlab-managed-cluster-help-path="gitlabManagedClusterHelpPath"
:kubernetes-integration-help-path="kubernetesIntegrationHelpPath"
:external-link-icon="externalLinkIcon"
/>
<service-credentials-form
v-else
Loading
Loading
Loading
Loading
@@ -4,8 +4,8 @@ import { sprintf, s__ } from '~/locale';
import _ from 'underscore';
import { GlFormInput, GlFormCheckbox } from '@gitlab/ui';
import ClusterFormDropdown from './cluster_form_dropdown.vue';
import RegionDropdown from './region_dropdown.vue';
import { KUBERNETES_VERSIONS } from '../constants';
import LoadingButton from '~/vue_shared/components/loading_button.vue';
 
const { mapState: mapRolesState, mapActions: mapRolesActions } = createNamespacedHelpers('roles');
const { mapState: mapRegionsState, mapActions: mapRegionsActions } = createNamespacedHelpers(
Loading
Loading
@@ -22,13 +22,17 @@ const {
mapState: mapSecurityGroupsState,
mapActions: mapSecurityGroupsActions,
} = createNamespacedHelpers('securityGroups');
const {
mapState: mapInstanceTypesState,
mapActions: mapInstanceTypesActions,
} = createNamespacedHelpers('instanceTypes');
 
export default {
components: {
ClusterFormDropdown,
RegionDropdown,
GlFormInput,
GlFormCheckbox,
LoadingButton,
},
props: {
gitlabManagedClusterHelpPath: {
Loading
Loading
@@ -39,6 +43,10 @@ export default {
type: String,
required: true,
},
externalLinkIcon: {
type: String,
required: true,
},
},
computed: {
...mapState([
Loading
Loading
@@ -51,7 +59,10 @@ export default {
'selectedSubnet',
'selectedRole',
'selectedSecurityGroup',
'selectedInstanceType',
'nodeCount',
'gitlabManagedCluster',
'isCreatingCluster',
]),
...mapRolesState({
roles: 'items',
Loading
Loading
@@ -83,6 +94,11 @@ export default {
isLoadingSecurityGroups: 'isLoadingItems',
loadingSecurityGroupsError: 'loadingItemsError',
}),
...mapInstanceTypesState({
instanceTypes: 'items',
isLoadingInstanceTypes: 'isLoadingItems',
loadingInstanceTypesError: 'loadingItemsError',
}),
kubernetesVersions() {
return KUBERNETES_VERSIONS;
},
Loading
Loading
@@ -98,6 +114,27 @@ export default {
securityGroupDropdownDisabled() {
return !this.selectedVpc;
},
createClusterButtonDisabled() {
return (
!this.clusterName ||
!this.environmentScope ||
!this.kubernetesVersion ||
!this.selectedRegion ||
!this.selectedKeyPair ||
!this.selectedVpc ||
!this.selectedSubnet ||
!this.selectedRole ||
!this.selectedSecurityGroup ||
!this.selectedInstanceType ||
!this.nodeCount ||
this.isCreatingCluster
);
},
createClusterButtonLabel() {
return this.isCreatingCluster
? s__('ClusterIntegration|Creating Kubernetes cluster')
: s__('ClusterIntegration|Create Kubernetes cluster');
},
kubernetesIntegrationHelpText() {
const escapedUrl = _.escape(this.kubernetesIntegrationHelpPath);
 
Loading
Loading
@@ -115,11 +152,26 @@ export default {
roleDropdownHelpText() {
return sprintf(
s__(
'ClusterIntegration|Select the IAM Role to allow Amazon EKS and the Kubernetes control plane to manage AWS resources on your behalf. To use a new role name, first create one on %{startLink}Amazon Web Services%{endLink}.',
'ClusterIntegration|Select the IAM Role to allow Amazon EKS and the Kubernetes control plane to manage AWS resources on your behalf. To use a new role name, first create one on %{startLink}Amazon Web Services %{externalLinkIcon} %{endLink}.',
),
{
startLink:
'<a href="https://docs.aws.amazon.com/eks/latest/userguide/getting-started-console.html#role-create" target="_blank" rel="noopener noreferrer">',
externalLinkIcon: this.externalLinkIcon,
endLink: '</a>',
},
false,
);
},
regionsDropdownHelpText() {
return sprintf(
s__(
'ClusterIntegration|Learn more about %{startLink}Regions %{externalLinkIcon}%{endLink}.',
),
{
startLink:
'<a href="https://console.aws.amazon.com/iam/home?#roles" target="_blank" rel="noopener noreferrer">',
'<a href="https://aws.amazon.com/about-aws/global-infrastructure/regional-product-services/" target="_blank" rel="noopener noreferrer">',
externalLinkIcon: this.externalLinkIcon,
endLink: '</a>',
},
false,
Loading
Loading
@@ -128,11 +180,12 @@ export default {
keyPairDropdownHelpText() {
return sprintf(
s__(
'ClusterIntegration|Select the key pair name that will be used to create EC2 nodes. To use a new key pair name, first create one on %{startLink}Amazon Web Services%{endLink}.',
'ClusterIntegration|Select the key pair name that will be used to create EC2 nodes. To use a new key pair name, first create one on %{startLink}Amazon Web Services %{externalLinkIcon} %{endLink}.',
),
{
startLink:
'<a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-key-pairs.html#having-ec2-create-your-key-pair" target="_blank" rel="noopener noreferrer">',
externalLinkIcon: this.externalLinkIcon,
endLink: '</a>',
},
false,
Loading
Loading
@@ -141,11 +194,12 @@ export default {
vpcDropdownHelpText() {
return sprintf(
s__(
'ClusterIntegration|Select a VPC to use for your EKS Cluster resources. To use a new VPC, first create one on %{startLink}Amazon Web Services%{endLink}.',
'ClusterIntegration|Select a VPC to use for your EKS Cluster resources. To use a new VPC, first create one on %{startLink}Amazon Web Services %{externalLinkIcon} %{endLink}.',
),
{
startLink:
'<a href="https://console.aws.amazon.com/vpc/home?#vpc" target="_blank" rel="noopener noreferrer">',
'<a href="https://docs.aws.amazon.com/eks/latest/userguide/getting-started-console.html#vpc-create" target="_blank" rel="noopener noreferrer">',
externalLinkIcon: this.externalLinkIcon,
endLink: '</a>',
},
false,
Loading
Loading
@@ -154,11 +208,12 @@ export default {
subnetDropdownHelpText() {
return sprintf(
s__(
'ClusterIntegration|Choose the %{startLink}subnets%{endLink} in your VPC where your worker nodes will run.',
'ClusterIntegration|Choose the %{startLink}subnets %{externalLinkIcon} %{endLink} in your VPC where your worker nodes will run.',
),
{
startLink:
'<a href="https://console.aws.amazon.com/vpc/home?#subnets" target="_blank" rel="noopener noreferrer">',
externalLinkIcon: this.externalLinkIcon,
endLink: '</a>',
},
false,
Loading
Loading
@@ -167,11 +222,26 @@ export default {
securityGroupDropdownHelpText() {
return sprintf(
s__(
'ClusterIntegration|Choose the %{startLink}security groups%{endLink} to apply to the EKS-managed Elastic Network Interfaces that are created in your worker node subnets.',
'ClusterIntegration|Choose the %{startLink}security group %{externalLinkIcon} %{endLink} to apply to the EKS-managed Elastic Network Interfaces that are created in your worker node subnets.',
),
{
startLink:
'<a href="https://console.aws.amazon.com/vpc/home?#securityGroups" target="_blank" rel="noopener noreferrer">',
externalLinkIcon: this.externalLinkIcon,
endLink: '</a>',
},
false,
);
},
instanceTypesDropdownHelpText() {
return sprintf(
s__(
'ClusterIntegration|Choose the worker node %{startLink}instance type %{externalLinkIcon} %{endLink}.',
),
{
startLink:
'<a href="https://aws.amazon.com/ec2/instance-types" target="_blank" rel="noopener noreferrer">',
externalLinkIcon: this.externalLinkIcon,
endLink: '</a>',
},
false,
Loading
Loading
@@ -195,9 +265,12 @@ export default {
mounted() {
this.fetchRegions();
this.fetchRoles();
this.fetchInstanceTypes();
},
methods: {
...mapActions([
'createCluster',
'signOut',
'setClusterName',
'setEnvironmentScope',
'setKubernetesVersion',
Loading
Loading
@@ -207,6 +280,8 @@ export default {
'setRole',
'setKeyPair',
'setSecurityGroup',
'setInstanceType',
'setNodeCount',
'setGitlabManagedCluster',
]),
...mapRegionsActions({ fetchRegions: 'fetchItems' }),
Loading
Loading
@@ -215,15 +290,22 @@ export default {
...mapRolesActions({ fetchRoles: 'fetchItems' }),
...mapKeyPairsActions({ fetchKeyPairs: 'fetchItems' }),
...mapSecurityGroupsActions({ fetchSecurityGroups: 'fetchItems' }),
...mapInstanceTypesActions({ fetchInstanceTypes: 'fetchItems' }),
setRegionAndFetchVpcsAndKeyPairs(region) {
this.setRegion({ region });
this.setVpc({ vpc: null });
this.setKeyPair({ keyPair: null });
this.setSubnet({ subnet: null });
this.setSecurityGroup({ securityGroup: null });
this.fetchVpcs({ region });
this.fetchKeyPairs({ region });
},
setVpcAndFetchSubnets(vpc) {
this.setVpc({ vpc });
this.fetchSubnets({ vpc });
this.fetchSecurityGroups({ vpc });
this.setSubnet({ subnet: null });
this.setSecurityGroup({ securityGroup: null });
this.fetchSubnets({ vpc, region: this.selectedRegion });
this.fetchSecurityGroups({ vpc, region: this.selectedRegion });
},
},
};
Loading
Loading
@@ -233,7 +315,12 @@ export default {
<h2>
{{ s__('ClusterIntegration|Enter the details for your Amazon EKS Kubernetes cluster') }}
</h2>
<p v-html="kubernetesIntegrationHelpText"></p>
<div class="mb-3" v-html="kubernetesIntegrationHelpText"></div>
<div class="mb-3">
<button class="btn btn-link js-sign-out" @click.prevent="signOut()">
{{ s__('ClusterIntegration|Select a different AWS role') }}
</button>
</div>
<div class="form-group">
<label class="label-bold" for="eks-cluster-name">{{
s__('ClusterIntegration|Kubernetes cluster name')
Loading
Loading
@@ -273,7 +360,7 @@ export default {
<cluster-form-dropdown
field-id="eks-role"
field-name="eks-role"
:input="selectedRole"
:value="selectedRole"
:items="roles"
:loading="isLoadingRoles"
:loading-text="s__('ClusterIntegration|Loading IAM Roles')"
Loading
Loading
@@ -288,13 +375,21 @@ export default {
</div>
<div class="form-group">
<label class="label-bold" for="eks-role">{{ s__('ClusterIntegration|Region') }}</label>
<region-dropdown
<cluster-form-dropdown
field-id="eks-region"
field-name="eks-region"
:value="selectedRegion"
:regions="regions"
:error="loadingRegionsError"
:items="regions"
:loading="isLoadingRegions"
:loading-text="s__('ClusterIntegration|Loading Regions')"
:placeholder="s__('ClusterIntergation|Select a region')"
:search-field-placeholder="s__('ClusterIntegration|Search regions')"
:empty-text="s__('ClusterIntegration|No region found')"
:has-errors="Boolean(loadingRegionsError)"
:error-message="s__('ClusterIntegration|Could not load regions from your AWS account')"
@input="setRegionAndFetchVpcsAndKeyPairs($event)"
/>
<p class="form-text text-muted" v-html="regionsDropdownHelpText"></p>
</div>
<div class="form-group">
<label class="label-bold" for="eks-key-pair">{{
Loading
Loading
@@ -303,7 +398,7 @@ export default {
<cluster-form-dropdown
field-id="eks-key-pair"
field-name="eks-key-pair"
:input="selectedKeyPair"
:value="selectedKeyPair"
:items="keyPairs"
:disabled="keyPairDropdownDisabled"
:disabled-text="s__('ClusterIntegration|Select a region to choose a Key Pair')"
Loading
Loading
@@ -323,7 +418,7 @@ export default {
<cluster-form-dropdown
field-id="eks-vpc"
field-name="eks-vpc"
:input="selectedVpc"
:value="selectedVpc"
:items="vpcs"
:loading="isLoadingVpcs"
:disabled="vpcDropdownDisabled"
Loading
Loading
@@ -339,11 +434,12 @@ export default {
<p class="form-text text-muted" v-html="vpcDropdownHelpText"></p>
</div>
<div class="form-group">
<label class="label-bold" for="eks-role">{{ s__('ClusterIntegration|Subnet') }}</label>
<label class="label-bold" for="eks-role">{{ s__('ClusterIntegration|Subnets') }}</label>
<cluster-form-dropdown
field-id="eks-subnet"
field-name="eks-subnet"
:input="selectedSubnet"
multiple
:value="selectedSubnet"
:items="subnets"
:loading="isLoadingSubnets"
:disabled="subnetDropdownDisabled"
Loading
Loading
@@ -360,12 +456,12 @@ export default {
</div>
<div class="form-group">
<label class="label-bold" for="eks-security-group">{{
s__('ClusterIntegration|Security groups')
s__('ClusterIntegration|Security group')
}}</label>
<cluster-form-dropdown
field-id="eks-security-group"
field-name="eks-security-group"
:input="selectedSecurityGroup"
:value="selectedSecurityGroup"
:items="securityGroups"
:loading="isLoadingSecurityGroups"
:disabled="securityGroupDropdownDisabled"
Loading
Loading
@@ -382,6 +478,39 @@ export default {
/>
<p class="form-text text-muted" v-html="securityGroupDropdownHelpText"></p>
</div>
<div class="form-group">
<label class="label-bold" for="eks-instance-type">{{
s__('ClusterIntegration|Instance type')
}}</label>
<cluster-form-dropdown
field-id="eks-instance-type"
field-name="eks-instance-type"
:value="selectedInstanceType"
:items="instanceTypes"
:loading="isLoadingInstanceTypes"
:loading-text="s__('ClusterIntegration|Loading instance types')"
:placeholder="s__('ClusterIntergation|Select an instance type')"
:search-field-placeholder="s__('ClusterIntegration|Search instance types')"
:empty-text="s__('ClusterIntegration|No instance type found')"
:has-errors="Boolean(loadingInstanceTypesError)"
:error-message="s__('ClusterIntegration|Could not load instance types')"
@input="setInstanceType({ instanceType: $event })"
/>
<p class="form-text text-muted" v-html="instanceTypesDropdownHelpText"></p>
</div>
<div class="form-group">
<label class="label-bold" for="eks-node-count">{{
s__('ClusterIntegration|Number of nodes')
}}</label>
<gl-form-input
id="eks-node-count"
type="number"
min="1"
step="1"
:value="nodeCount"
@input="setNodeCount({ nodeCount: $event })"
/>
</div>
<div class="form-group">
<gl-form-checkbox
:checked="gitlabManagedCluster"
Loading
Loading
@@ -390,5 +519,14 @@ export default {
>
<p class="form-text text-muted" v-html="gitlabManagedHelpText"></p>
</div>
<div class="form-group">
<loading-button
class="js-create-cluster btn-success"
:disabled="createClusterButtonDisabled"
:loading="isCreatingCluster"
:label="createClusterButtonLabel"
@click="createCluster()"
/>
</div>
</form>
</template>
<script>
import { sprintf, s__ } from '~/locale';
import ClusterFormDropdown from './cluster_form_dropdown.vue';
export default {
components: {
ClusterFormDropdown,
},
props: {
regions: {
type: Array,
required: false,
default: () => [],
},
loading: {
type: Boolean,
required: false,
default: false,
},
error: {
type: Object,
required: false,
default: null,
},
},
computed: {
hasErrors() {
return Boolean(this.error);
},
helpText() {
return sprintf(
s__('ClusterIntegration|Learn more about %{startLink}Regions%{endLink}.'),
{
startLink:
'<a href="https://aws.amazon.com/about-aws/global-infrastructure/regional-product-services/" target="_blank" rel="noopener noreferrer">',
endLink: '</a>',
},
false,
);
},
},
};
</script>
<template>
<div>
<cluster-form-dropdown
field-id="eks-region"
field-name="eks-region"
:items="regions"
:loading="loading"
:loading-text="s__('ClusterIntegration|Loading Regions')"
:placeholder="s__('ClusterIntergation|Select a region')"
:search-field-placeholder="s__('ClusterIntegration|Search regions')"
:empty-text="s__('ClusterIntegration|No region found')"
:has-errors="hasErrors"
:error-message="s__('ClusterIntegration|Could not load regions from your AWS account')"
v-bind="$attrs"
v-on="$listeners"
/>
<p class="form-text text-muted" v-html="helpText"></p>
</div>
</template>
Loading
Loading
@@ -131,7 +131,7 @@ export default {
<p class="form-text text-muted" v-html="provisionRoleArnHelpText"></p>
</div>
<loading-button
class="js-submit-service-credentials"
class="js-submit-service-credentials btn-success"
type="submit"
:disabled="submitButtonDisabled"
:loading="isCreatingRole"
Loading
Loading
// eslint-disable-next-line import/prefer-default-export
export const KUBERNETES_VERSIONS = [
{ name: '1.14', value: '1.14' },
{ name: '1.13', value: '1.13' },
{ name: '1.12', value: '1.12' },
{ name: '1.11', value: '1.11' },
];
export const KUBERNETES_VERSIONS = [{ name: '1.14', value: '1.14' }];
Loading
Loading
@@ -12,10 +12,19 @@ export default el => {
kubernetesIntegrationHelpPath,
accountAndExternalIdsHelpPath,
createRoleArnHelpPath,
getRolesPath,
getRegionsPath,
getKeyPairsPath,
getVpcsPath,
getSubnetsPath,
getSecurityGroupsPath,
getInstanceTypesPath,
externalId,
accountId,
hasCredentials,
createRolePath,
createClusterPath,
signOutPath,
externalLinkIcon,
} = el.dataset;
 
Loading
Loading
@@ -27,6 +36,17 @@ export default el => {
externalId,
accountId,
createRolePath,
createClusterPath,
signOutPath,
},
apiPaths: {
getRolesPath,
getRegionsPath,
getKeyPairsPath,
getVpcsPath,
getSubnetsPath,
getSecurityGroupsPath,
getInstanceTypesPath,
},
}),
components: {
Loading
Loading
import EC2 from 'aws-sdk/clients/ec2';
import IAM from 'aws-sdk/clients/iam';
export const fetchRoles = () => {
const iam = new IAM();
return iam
.listRoles()
.promise()
.then(({ Roles: roles }) => roles.map(({ RoleName: name }) => ({ name })));
};
export const fetchKeyPairs = () => {
const ec2 = new EC2();
return ec2
.describeKeyPairs()
.promise()
.then(({ KeyPairs: keyPairs }) => keyPairs.map(({ RegionName: name }) => ({ name })));
};
export const fetchRegions = () => {
const ec2 = new EC2();
return ec2
.describeRegions()
.promise()
.then(({ Regions: regions }) =>
regions.map(({ RegionName: name }) => ({
name,
value: name,
import axios from '~/lib/utils/axios_utils';
export default apiPaths => ({
fetchRoles() {
return axios
.get(apiPaths.getRolesPath)
.then(({ data: { roles } }) =>
roles.map(({ role_name: name, arn: value }) => ({ name, value })),
);
},
fetchKeyPairs({ region }) {
return axios
.get(apiPaths.getKeyPairsPath, { params: { region } })
.then(({ data: { key_pairs: keyPairs } }) =>
keyPairs.map(({ key_name }) => ({ name: key_name, value: key_name })),
);
},
fetchRegions() {
return axios.get(apiPaths.getRegionsPath).then(({ data: { regions } }) =>
regions.map(({ region_name }) => ({
name: region_name,
value: region_name,
})),
);
};
export const fetchVpcs = () => {
const ec2 = new EC2();
return ec2
.describeVpcs()
.promise()
.then(({ Vpcs: vpcs }) =>
vpcs.map(({ VpcId: id }) => ({
value: id,
name: id,
},
fetchVpcs({ region }) {
return axios.get(apiPaths.getVpcsPath, { params: { region } }).then(({ data: { vpcs } }) =>
vpcs.map(({ vpc_id }) => ({
value: vpc_id,
name: vpc_id,
})),
);
};
export const fetchSubnets = ({ vpc }) => {
const ec2 = new EC2();
return ec2
.describeSubnets({
Filters: [
{
Name: 'vpc-id',
Values: [vpc],
},
],
})
.promise()
.then(({ Subnets: subnets }) => subnets.map(({ SubnetId: id }) => ({ id, name: id })));
};
export const fetchSecurityGroups = ({ vpc }) => {
const ec2 = new EC2();
return ec2
.describeSecurityGroups({
Filters: [
{
Name: 'vpc-id',
Values: [vpc],
},
],
})
.promise()
.then(({ SecurityGroups: securityGroups }) =>
securityGroups.map(({ GroupName: name, GroupId: value }) => ({ name, value })),
);
};
export default () => {};
},
fetchSubnets({ vpc, region }) {
return axios
.get(apiPaths.getSubnetsPath, { params: { vpc_id: vpc, region } })
.then(({ data: { subnets } }) =>
subnets.map(({ subnet_id }) => ({ name: subnet_id, value: subnet_id })),
);
},
fetchSecurityGroups({ vpc, region }) {
return axios
.get(apiPaths.getSecurityGroupsPath, { params: { vpc_id: vpc, region } })
.then(({ data: { security_groups: securityGroups } }) =>
securityGroups.map(({ group_name: name, group_id: value }) => ({ name, value })),
);
},
fetchInstanceTypes() {
return axios
.get(apiPaths.getInstanceTypesPath)
.then(({ data: { instance_types: instanceTypes } }) =>
instanceTypes.map(({ instance_type_name }) => ({
name: instance_type_name,
value: instance_type_name,
})),
);
},
});
import * as types from './mutation_types';
import axios from '~/lib/utils/axios_utils';
import createFlash from '~/flash';
const getErrorMessage = data => {
const errorKey = Object.keys(data)[0];
return data[errorKey][0];
};
 
export const setClusterName = ({ commit }, payload) => {
commit(types.SET_CLUSTER_NAME, payload);
Loading
Loading
@@ -37,6 +44,44 @@ export const createRoleError = ({ commit }, payload) => {
commit(types.CREATE_ROLE_ERROR, payload);
};
 
export const createCluster = ({ dispatch, state }) => {
dispatch('requestCreateCluster');
return axios
.post(state.createClusterPath, {
name: state.clusterName,
environment_scope: state.environmentScope,
managed: state.gitlabManagedCluster,
provider_aws_attributes: {
region: state.selectedRegion,
vpc_id: state.selectedVpc,
subnet_ids: state.selectedSubnet,
role_arn: state.selectedRole,
key_name: state.selectedKeyPair,
security_group_id: state.selectedSecurityGroup,
instance_type: state.selectedInstanceType,
num_nodes: state.nodeCount,
},
})
.then(({ headers: { location } }) => dispatch('createClusterSuccess', location))
.catch(({ response: { data } }) => {
dispatch('createClusterError', data);
});
};
export const requestCreateCluster = ({ commit }) => {
commit(types.REQUEST_CREATE_CLUSTER);
};
export const createClusterSuccess = (_, location) => {
window.location.assign(location);
};
export const createClusterError = ({ commit }, error) => {
commit(types.CREATE_CLUSTER_ERROR, error);
createFlash(getErrorMessage(error));
};
export const setRegion = ({ commit }, payload) => {
commit(types.SET_REGION, payload);
};
Loading
Loading
@@ -64,3 +109,17 @@ export const setSecurityGroup = ({ commit }, payload) => {
export const setGitlabManagedCluster = ({ commit }, payload) => {
commit(types.SET_GITLAB_MANAGED_CLUSTER, payload);
};
export const setInstanceType = ({ commit }, payload) => {
commit(types.SET_INSTANCE_TYPE, payload);
};
export const setNodeCount = ({ commit }, payload) => {
commit(types.SET_NODE_COUNT, payload);
};
export const signOut = ({ commit, state: { signOutPath } }) =>
axios
.delete(signOutPath)
.then(() => commit(types.SIGN_OUT))
.catch(({ response: { data } }) => createFlash(getErrorMessage(data)));
Loading
Loading
@@ -6,10 +6,12 @@ import state from './state';
 
import clusterDropdownStore from './cluster_dropdown';
 
import * as awsServices from '../services/aws_services_facade';
import awsServicesFactory from '../services/aws_services_facade';
 
const createStore = ({ initialState }) =>
new Vuex.Store({
const createStore = ({ initialState, apiPaths }) => {
const awsServices = awsServicesFactory(apiPaths);
return new Vuex.Store({
actions,
getters,
mutations,
Loading
Loading
@@ -39,7 +41,12 @@ const createStore = ({ initialState }) =>
namespaced: true,
...clusterDropdownStore(awsServices.fetchSecurityGroups),
},
instanceTypes: {
namespaced: true,
...clusterDropdownStore(awsServices.fetchInstanceTypes),
},
},
});
};
 
export default createStore;
Loading
Loading
@@ -7,7 +7,13 @@ export const SET_KEY_PAIR = 'SET_KEY_PAIR';
export const SET_SUBNET = 'SET_SUBNET';
export const SET_ROLE = 'SET_ROLE';
export const SET_SECURITY_GROUP = 'SET_SECURITY_GROUP';
export const SET_INSTANCE_TYPE = 'SET_INSTANCE_TYPE';
export const SET_NODE_COUNT = 'SET_NODE_COUNT';
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';
export const SIGN_OUT = 'SIGN_OUT';
export const REQUEST_CREATE_CLUSTER = 'REQUEST_CREATE_CLUSTER';
export const CREATE_CLUSTER_SUCCESS = 'CREATE_CLUSTER_SUCCESS';
export const CREATE_CLUSTER_ERROR = 'CREATE_CLUSTER_ERROR';
Loading
Loading
@@ -28,6 +28,12 @@ export default {
[types.SET_SECURITY_GROUP](state, { securityGroup }) {
state.selectedSecurityGroup = securityGroup;
},
[types.SET_INSTANCE_TYPE](state, { instanceType }) {
state.selectedInstanceType = instanceType;
},
[types.SET_NODE_COUNT](state, { nodeCount }) {
state.nodeCount = nodeCount;
},
[types.SET_GITLAB_MANAGED_CLUSTER](state, { gitlabManagedCluster }) {
state.gitlabManagedCluster = gitlabManagedCluster;
},
Loading
Loading
@@ -46,4 +52,15 @@ export default {
state.createRoleError = error;
state.hasCredentials = false;
},
[types.REQUEST_CREATE_CLUSTER](state) {
state.isCreatingCluster = true;
state.createClusterError = null;
},
[types.CREATE_CLUSTER_ERROR](state, { error }) {
state.isCreatingCluster = false;
state.createClusterError = error;
},
[types.SIGN_OUT](state) {
state.hasCredentials = false;
},
};
import { KUBERNETES_VERSIONS } from '../constants';
 
const [{ value: kubernetesVersion }] = KUBERNETES_VERSIONS;
export default () => ({
createRolePath: null,
 
Loading
Loading
@@ -12,13 +14,18 @@ export default () => ({
 
clusterName: '',
environmentScope: '*',
kubernetesVersion: [KUBERNETES_VERSIONS].value,
kubernetesVersion,
selectedRegion: '',
selectedRole: '',
selectedKeyPair: '',
selectedVpc: '',
selectedSubnet: '',
selectedSecurityGroup: '',
selectedInstanceType: 'm5.large',
nodeCount: '3',
isCreatingCluster: false,
createClusterError: false,
 
gitlabManagedCluster: true,
});
Loading
Loading
@@ -94,8 +94,7 @@
- else
= f.text_field :name, label: s_('Profiles|Full name'), required: true, title: s_("Profiles|Using emojis in names seems fun, but please try to set a status message instead"), wrapper: { class: 'col-md-9 qa-full-name rspec-full-name' }, help: s_("Profiles|Enter your name, so people you know can recognize you")
= f.text_field :id, readonly: true, label: s_('Profiles|User ID'), wrapper: { class: 'col-md-3' }
- if experiment_enabled?(:signup_flow)
= f.select :role, ::User.roles.keys.map { |role| [role.titleize, role] }, { prompt: _('Select your role') }, class: 'input-md'
= f.select :role, ::User.roles.keys.map { |role| [role.titleize, role] }, { prompt: _('Select your role') }, required: true, class: 'input-md'
 
= render_if_exists 'profiles/email_settings', form: f
= f.text_field :skype, class: 'input-md', placeholder: s_("Profiles|username")
Loading
Loading
---
title: Build CI cache key from commit SHAs that changed given files
merge_request: 19392
author:
type: added
---
title: Create AWS EKS cluster
merge_request: 19578
author:
type: added
---
title: Make role required when editing profile
merge_request: 19636
author:
type: changed
Loading
Loading
@@ -61,7 +61,7 @@ GET /groups/:id/packages
| `exclude_subgroups` | boolean | false | If the param is included as true, packages from projects from subgroups are not listed. Default is `false`. |
 
```bash
curl --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/group/:id/packages?exclude_subgroups=true
curl --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/groups/:id/packages?exclude_subgroups=true
```
 
Example response:
Loading
Loading
Loading
Loading
@@ -1535,6 +1535,50 @@ cache:
- binaries/
```
 
##### `cache:key:files`
> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/18986) in GitLab v12.5.
If `cache:key:files` is added, the cache `key` will use the SHA of the most recent commit
that changed either of the given files. If neither file was changed in any commits, the key will be `default`.
A maximum of two files are allowed.
```yaml
cache:
key:
files:
- Gemfile.lock
- package.json
paths:
- vendor/ruby
- node_modules
```
##### `cache:key:prefix`
> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/18986) in GitLab v12.5.
The `prefix` parameter adds extra functionality to `key:files` by allowing the key to
be composed of the given `prefix` combined with the SHA of the most recent commit
that changed either of the files. For example, adding a `prefix` of `rspec`, will
cause keys to look like: `rspec-feef9576d21ee9b6a32e30c5c79d0a0ceb68d1e5`. If neither
file was changed in any commits, the prefix is added to `default`, so the key in the
example would be `rspec-default`.
`prefix` follows the same restrictions as `key`, so it can use any of the
[predefined variables](../variables/README.md). Similarly, the `/` character or the
equivalent URI-encoded `%2F`, or a value made only of `.` or `%2E`, is not allowed.
```yaml
cache:
key:
files:
- Gemfile.lock
prefix: ${CI_JOB_NAME}
paths:
- vendor/ruby
```
#### `cache:untracked`
 
Set `untracked: true` to cache all files that are untracked in your Git
Loading
Loading
Loading
Loading
@@ -52,20 +52,16 @@ isn't gated by a License or Plan.
[namespace-fa]: https://gitlab.com/gitlab-org/gitlab/blob/4cc1c62918aa4c31750cb21dfb1a6c3492d71080/ee/app/models/ee/namespace.rb#L71-85
[license-fa]: https://gitlab.com/gitlab-org/gitlab/blob/4cc1c62918aa4c31750cb21dfb1a6c3492d71080/ee/app/models/license.rb#L293-300
 
An important side-effect of the implicit feature flags mentioned above is that
**An important side-effect of the implicit feature flags mentioned above is that
unless the feature is explicitly disabled or limited to a percentage of users,
the feature flag check will default to `true`.
the feature flag check will default to `true`.**
 
As an example, if you were to ship the backend half of a feature behind a flag,
you'd want to explicitly disable that flag until the frontend half is also ready
to be shipped. [You can do this via Chatops](controls.md):
```
/chatops run feature set some_feature 0
```
Note that you can do this at any time, even before the merge request using the
flag has been merged!
to be shipped. To make sure this feature is disabled for both GitLab.com and
self-managed instances you'd need to explicitly call `Feature.enabled?` method
before the `feature_available` method. This ensures the feature_flag is defaulting
to `true`.
 
## Feature groups
 
Loading
Loading
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment