From 1ffe7b856ff159ef1c444aeeba0c0f53da8e9f47 Mon Sep 17 00:00:00 2001 From: zhangzhx Date: Fri, 5 Jan 2018 00:50:00 +0000 Subject: [PATCH] AWS Toolkit for Eclipse: v201801042359 Release. --- CHANGELOG.json | 4 + .../eclipse/core/AWSClientFactory.java | 37 ++++-- .../eclipse/core/maven/MavenFactory.java | 9 +- .../model/MultipleSelectionListDataModel.java | 30 +++++ .../eclipse/core/regions/LocalRegion.java | 5 + .../eclipse/core/regions/Region.java | 49 +++++++ .../eclipse/core/regions/RegionImpl.java | 9 +- .../core/regions/RegionMetadataParser.java | 4 +- .../ui/MultipleSelectionListComposite.java | 123 ++++++++++++++++++ .../webproject/JavaWebProjectWizardPage.java | 1 - .../eclipse/lambda/LambdaConstants.java | 1 + ...electOrCreateBasicLambdaRoleDataModel.java | 2 +- .../DeployServerlessProjectDataModel.java | 7 + .../DeployServerlessProjectHandler.java | 6 +- .../ui/DeployServerlessProjectPage.java | 28 ++++ .../wizard/DeployServerlessProjectWizard.java | 3 +- .../page/FunctionConfigurationPage.java | 2 +- releng/com.amazonaws.eclipse.devide/.project | 8 -- .../core/regions/AwsClientFactoryTests.java | 97 ++++++++++++++ .../META-INF/MANIFEST.MF | 6 +- 20 files changed, 400 insertions(+), 31 deletions(-) create mode 100644 bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/model/MultipleSelectionListDataModel.java create mode 100644 bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/ui/MultipleSelectionListComposite.java create mode 100644 tests/com.amazonaws.eclipse.core.tests/src/com/amazonaws/eclipse/core/regions/AwsClientFactoryTests.java diff --git a/CHANGELOG.json b/CHANGELOG.json index beaab57d..23a198c0 100644 --- a/CHANGELOG.json +++ b/CHANGELOG.json @@ -1,5 +1,9 @@ { "current": [ + "* **Merge Pull Request #93.**", + "* **Resolve Issues: #87, #94, #95, #96, #97.**" + ], + "v201712181839": [ "**Support AWS SAM Local to locally debug Lambda functions and AWS Gateway**", "", "* Debug Lambda Function", diff --git a/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/AWSClientFactory.java b/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/AWSClientFactory.java index 93d71b3d..f9f61fd4 100644 --- a/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/AWSClientFactory.java +++ b/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/AWSClientFactory.java @@ -30,6 +30,7 @@ import com.amazonaws.AmazonWebServiceClient; import com.amazonaws.ClientConfiguration; import com.amazonaws.auth.AWSCredentials; +import com.amazonaws.auth.AWSCredentialsProvider; import com.amazonaws.auth.AWSStaticCredentialsProvider; import com.amazonaws.auth.AnonymousAWSCredentials; import com.amazonaws.auth.BasicAWSCredentials; @@ -127,6 +128,8 @@ public class AWSClientFactory { */ private AccountInfo accountInfo; + private AWSCredentialsProvider credentialsProviderOverride; + /** * Constructs a client factory that uses the given account identifier to * retrieve its credentials. @@ -142,6 +145,15 @@ public AWSClientFactory(String accountId) { plugin.getAccountManager().addAccountInfoChangeListener(this::onAccountInfoChange); } + /** + * Decoupling AWS Client Factory from AwsToolkitCore for testing purpose only. + * @TestOnly + */ + public AWSClientFactory(AWSCredentialsProvider credentialsProvider) { + this.accountId = null; + this.credentialsProviderOverride = credentialsProvider; + } + private void onProxyChange(IProxyChangeEvent e) { onAccountInfoChange(); } @@ -377,12 +389,12 @@ public AWSCodeCommit getCodeCommitClientByEndpoint(String endpoint) { public AmazonIdentityManagement getIAMClientByRegion(String regionId) { return getOrCreateClientByRegion(ServiceAbbreviations.IAM, regionId, - AmazonIdentityManagementClientBuilder.standard(), AmazonIdentityManagement.class); + AmazonIdentityManagementClientBuilder.standard(), AmazonIdentityManagement.class, true); } public AmazonCloudFront getCloudFrontClientByRegion(String regionId) { return getOrCreateClientByRegion(ServiceAbbreviations.CLOUDFRONT, regionId, - AmazonCloudFrontClientBuilder.standard(), AmazonCloudFront.class); + AmazonCloudFrontClientBuilder.standard(), AmazonCloudFront.class, true); } /** @@ -505,22 +517,26 @@ private T getOrCreateClient(String endpoint, } private T getOrCreateClientByRegion(String serviceName, String regionId, - AwsSyncClientBuilder builder, Class clientClass) { + AwsSyncClientBuilder builder, Class clientClass, boolean isGlobalClient) { Region region = RegionUtils.getRegion(regionId); if (region == null) { return null; } - String endpoint = region.getServiceEndpoint(serviceName); synchronized (clientClass) { if ( cachedClients.getClient(regionId, clientClass) == null ) { - cachedClients.cacheClient(regionId, clientClass, createClientByRegion(builder, regionId, endpoint)); + cachedClients.cacheClient(regionId, clientClass, createClientByRegion(builder, serviceName, region, isGlobalClient)); } } return cachedClients.getClient(regionId, clientClass); } + private T getOrCreateClientByRegion(String serviceName, String regionId, + AwsSyncClientBuilder builder, Class clientClass) { + return getOrCreateClientByRegion(serviceName, regionId, builder, clientClass, false); + } + /** * @deprecated for {@link #createClientByRegion(AwsSyncClientBuilder, String, String)} */ @@ -569,9 +585,12 @@ private T createClient(String endpoint, Class // Low layer method for building a service client by using the client builder. @SuppressWarnings("unchecked") - private T createClientByRegion(AwsSyncClientBuilder builder, String region, String endpoint) { - builder.withEndpointConfiguration(new EndpointConfiguration(endpoint, region)); + private T createClientByRegion(AwsSyncClientBuilder builder, + String serviceName, Region region, boolean isGlobalClient) { + String endpoint = region.getServiceEndpoint(serviceName); + String signingRegion = isGlobalClient ? region.getGlobalRegionSigningRegion() : region.getId(); Object client = builder + .withEndpointConfiguration(new EndpointConfiguration(endpoint, signingRegion)) .withCredentials(new AWSStaticCredentialsProvider(getAwsCredentials())) .withClientConfiguration(createClientConfiguration(endpoint)) .build(); @@ -614,7 +633,9 @@ private Method lookupSigV4SetEndpointMethod(C private AWSCredentials getAwsCredentials() { AWSCredentials credentials = null; - if (accountInfo.isUseSessionToken()) { + if (credentialsProviderOverride != null) { + credentials = credentialsProviderOverride.getCredentials(); + } else if (accountInfo.isUseSessionToken()) { credentials = new BasicSessionCredentials( accountInfo.getAccessKey(), accountInfo.getSecretKey(), accountInfo.getSessionToken()); diff --git a/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/maven/MavenFactory.java b/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/maven/MavenFactory.java index 1bed4800..8bf53ad5 100644 --- a/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/maven/MavenFactory.java +++ b/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/maven/MavenFactory.java @@ -15,6 +15,7 @@ package com.amazonaws.eclipse.core.maven; import java.util.List; +import java.util.Optional; import java.util.Properties; import org.apache.maven.archetype.catalog.Archetype; @@ -23,6 +24,7 @@ import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jdt.annotation.NonNull; import org.eclipse.m2e.core.MavenPlugin; import org.eclipse.m2e.core.project.ProjectImportConfiguration; @@ -42,12 +44,12 @@ public class MavenFactory { private static String AWS_JAVA_SDK_GROUP_NAME = "com.amazonaws"; private static String AWS_JAVA_SDK_ARTIFACT_NAME = "aws-java-sdk"; private static String AWS_JAVA_SDK_ARTIFACT_TYPE = "jar"; - private static String DEFAULT_AWS_JAVA_SDK_VERSION = "1.11.66"; + private static String DEFAULT_AWS_JAVA_SDK_VERSION = "1.11.256"; private static String AWS_JAVA_SDK_BOM_GROUP_NAME = "com.amazonaws"; private static String AWS_JAVA_SDK_BOM_ARTIFACT_NAME = "aws-java-sdk-bom"; private static String AWS_JAVA_SDK_BOM_ARTIFACT_TYPE = "pom"; - private static String DEFAULT_AWS_JAVA_SDK_BOM_VERSION = "1.11.66"; + private static String DEFAULT_AWS_JAVA_SDK_BOM_VERSION = "1.11.256"; private static String AMAZON_KINESIS_CLIENT_GROUP_NAME = "com.amazonaws"; private static String AMAZON_KINESIS_CLIENT_ARTIFACT_NAME = "amazon-kinesis-client"; @@ -150,8 +152,9 @@ public static Dependency getJunitDependency() { return getJunitDependency(DEFAULT_JUNIT_VERSION, "test"); } + @NonNull public static String getLatestJavaSdkVersion() { - return getLatestArtifactVersion(AWS_JAVA_SDK_GROUP_NAME, AWS_JAVA_SDK_ARTIFACT_NAME); + return Optional.ofNullable(getLatestArtifactVersion(AWS_JAVA_SDK_GROUP_NAME, AWS_JAVA_SDK_ARTIFACT_NAME)).orElse(DEFAULT_AWS_JAVA_SDK_VERSION); } private static Dependency getLatestArtifactDependency(String groupId, String artifactId, String scope, String type, String defaultVersion) { diff --git a/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/model/MultipleSelectionListDataModel.java b/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/model/MultipleSelectionListDataModel.java new file mode 100644 index 00000000..e8eabe43 --- /dev/null +++ b/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/model/MultipleSelectionListDataModel.java @@ -0,0 +1,30 @@ +/* + * Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +package com.amazonaws.eclipse.core.model; + +import java.util.ArrayList; +import java.util.List; + +/** + * Data model for a widget of List with check box. The data model collects the checked items instead of + * the selected items from the List. + */ +public class MultipleSelectionListDataModel { + private final List selectedList = new ArrayList<>(); + + public List getSelectedList() { + return selectedList; + } +} diff --git a/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/regions/LocalRegion.java b/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/regions/LocalRegion.java index 2d1c5438..5672d8b0 100644 --- a/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/regions/LocalRegion.java +++ b/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/regions/LocalRegion.java @@ -135,4 +135,9 @@ public ImageDescriptor getFlagImageDescriptor() { public String toString() { return "Region: Local (localhost) (local)"; } + + @Override + public String getRegionRestriction() { + return "IsLocalAccount"; + } } diff --git a/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/regions/Region.java b/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/regions/Region.java index 0bb7d9e4..1b147b6f 100644 --- a/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/regions/Region.java +++ b/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/regions/Region.java @@ -87,4 +87,53 @@ public interface Region { * Returns the flag's image descriptor. */ ImageDescriptor getFlagImageDescriptor(); + + String getRegionRestriction(); + + default String getGlobalRegionSigningRegion() { + RegionPartition partition = RegionPartition.fromValue(getRegionRestriction()); + if (partition == null) { + return getId(); + } else { + return partition.getGlobalSigningRegion(); + } + } + + public static enum RegionPartition { + US_GOV_CLOUD("IsGovCloudAccount", "us-gov-west-1"), + CHINA_CLOUD("IsChinaAccount", "cn-north-1"), + AWS_CLOUD("IsAwsAccount", "us-east-1") + ; + + private final String restriction; + private final String globalSigningRegion; + + private RegionPartition(String restriction, String globalSigningRegion) { + this.restriction = restriction; + this.globalSigningRegion = globalSigningRegion; + } + public String getRestriction() { + return this.restriction; + } + public String getGlobalSigningRegion() { + return globalSigningRegion; + } + + /** + * Find the {@link RegionPartition} by the provided restriction. If the restriction + * is null or empty, return the default AWS_CLOUD; Otherwise, return the corresponding + * {@link RegionPartition} if found in the enum or null if not. + */ + public static RegionPartition fromValue(String restriction) { + if (restriction == null || restriction.isEmpty()) { + return AWS_CLOUD; + } + for (RegionPartition partition : RegionPartition.values()) { + if (partition.getRestriction().equals(restriction)) { + return partition; + } + } + return null; + } + } } diff --git a/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/regions/RegionImpl.java b/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/regions/RegionImpl.java index 170c7da8..5e4bc295 100644 --- a/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/regions/RegionImpl.java +++ b/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/regions/RegionImpl.java @@ -31,14 +31,16 @@ class RegionImpl implements Region { private final String name; private final String id; private final String flagIconPath; + private final String restriction; private final Map serviceEndpoints = new HashMap<>(); private final Map servicesByName = new HashMap<>(); - public RegionImpl(String name, String id, String flagIconPath) { + public RegionImpl(String name, String id, String flagIconPath, String restriction) { this.name = name; this.id = id; this.flagIconPath = flagIconPath; + this.restriction = restriction; } /** @@ -154,4 +156,9 @@ public boolean equals(Object obj) { public String toString() { return "Region: " + name + " (" + id + ")"; } + + @Override + public String getRegionRestriction() { + return restriction; + } } diff --git a/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/regions/RegionMetadataParser.java b/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/regions/RegionMetadataParser.java index dc9aee20..4e52a6bd 100644 --- a/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/regions/RegionMetadataParser.java +++ b/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/regions/RegionMetadataParser.java @@ -41,6 +41,7 @@ public class RegionMetadataParser { private static final String SERVICE_ID_ATTRIBUTE = "serviceId"; private static final String SERVICE_NAME_ATTRIBUTE = "name"; private static final String SIGNER_ATTRIBUTE = "signer"; + private static final String RESTRICTIONS = "restrictions"; /** * Parses the specified input stream and returns a list of the regions @@ -77,7 +78,8 @@ private Region parseRegionElement(Element regionElement) { String name = getTagValue(REGION_DISPLAY_NAME_TAG, regionElement); String id = getTagValue(REGION_SYSTEM_ID_TAG, regionElement); String flagIcon = getTagValue(FLAG_ICON_TAG, regionElement); - Region region = new RegionImpl(name, id, flagIcon); + String restriction = getTagValue(RESTRICTIONS, regionElement); + Region region = new RegionImpl(name, id, flagIcon, restriction); NodeList serviceNodes = regionElement.getElementsByTagName(SERVICE_TAG); for (int i = 0; i < serviceNodes.getLength(); i++) { diff --git a/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/ui/MultipleSelectionListComposite.java b/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/ui/MultipleSelectionListComposite.java new file mode 100644 index 00000000..4547afa4 --- /dev/null +++ b/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/ui/MultipleSelectionListComposite.java @@ -0,0 +1,123 @@ +/* + * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +package com.amazonaws.eclipse.core.ui; + +import static com.amazonaws.eclipse.core.ui.wizards.WizardWidgetFactory.newControlDecoration; + +import java.util.Arrays; +import java.util.List; + +import org.eclipse.core.databinding.DataBindingContext; +import org.eclipse.core.databinding.validation.MultiValidator; +import org.eclipse.core.databinding.validation.ValidationStatus; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.jface.fieldassist.ControlDecoration; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Table; +import org.eclipse.swt.widgets.TableItem; + +import com.amazonaws.eclipse.core.model.MultipleSelectionListDataModel; +import com.amazonaws.eclipse.databinding.DecorationChangeListener; + +/** + * A reusable Composite for multiple selection using check box. + */ +public class MultipleSelectionListComposite extends Composite { + private final MultipleSelectionListDataModel dataModel; + private final ListSelectionNotEmptyValidator validator; + private Table table; + + public MultipleSelectionListComposite( + Composite parent, + DataBindingContext context, + MultipleSelectionListDataModel dataModel, + List itemSet, + List defaultSelected, + String selectedItemsEmptyErrorMessage) { + super(parent, SWT.NONE); + setLayout(new GridLayout(1, false)); + setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + + this.dataModel = dataModel; + this.validator = new ListSelectionNotEmptyValidator<>( + dataModel.getSelectedList(), selectedItemsEmptyErrorMessage); + + createControl(context, itemSet, defaultSelected); + } + + private void createControl(DataBindingContext context, List itemSet, List defaultSelected) { + table = new Table(this, SWT.CHECK | SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL | SWT.READ_ONLY); + table.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false)); + + for (T item : itemSet) { + TableItem tableItem = new TableItem(table, SWT.NONE); + tableItem.setData(item); + tableItem.setText(item.toString()); + if (defaultSelected != null && defaultSelected.contains(item)) { + tableItem.setChecked(true); + } + } + + ControlDecoration controlDecoration = newControlDecoration(table, ""); + context.addValidationStatusProvider(validator); + new DecorationChangeListener(controlDecoration, validator.getValidationStatus()); + + table.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + onTableItemSelected(); + } + }); + + onTableItemSelected(); + } + + private void onTableItemSelected() { + List selectedItems = dataModel.getSelectedList(); + selectedItems.clear(); + Arrays.stream(table.getItems()) + .filter(TableItem::getChecked) + .forEach(tt -> selectedItems.add((T)tt.getData())); + validator.revalidateDelegate(); + } + + private static class ListSelectionNotEmptyValidator extends MultiValidator { + private final List model; + private final String errorMessage; + + public ListSelectionNotEmptyValidator(List selectedItems, String emptyErrorMessage) { + this.model = selectedItems; + this.errorMessage = emptyErrorMessage; + } + + @Override + protected IStatus validate() { + if (model == null || model.isEmpty()) { + return ValidationStatus.error(errorMessage); + } + return ValidationStatus.ok(); + } + + // We need to expose the protected revalidate method to explicitly refresh the validation status. + public void revalidateDelegate() { + revalidate(); + } + } +} \ No newline at end of file diff --git a/bundles/com.amazonaws.eclipse.elasticbeanstalk/src/com/amazonaws/eclipse/elasticbeanstalk/webproject/JavaWebProjectWizardPage.java b/bundles/com.amazonaws.eclipse.elasticbeanstalk/src/com/amazonaws/eclipse/elasticbeanstalk/webproject/JavaWebProjectWizardPage.java index afe33e15..cae5d3a2 100644 --- a/bundles/com.amazonaws.eclipse.elasticbeanstalk/src/com/amazonaws/eclipse/elasticbeanstalk/webproject/JavaWebProjectWizardPage.java +++ b/bundles/com.amazonaws.eclipse.elasticbeanstalk/src/com/amazonaws/eclipse/elasticbeanstalk/webproject/JavaWebProjectWizardPage.java @@ -114,7 +114,6 @@ public void createControl(Composite parent) { Group group = new Group(composite, SWT.NONE); group.setLayoutData(layoutData); -// group.setLayout(new FillLayout()); group.setLayout(new GridLayout()); accountSelectionComposite = new AccountSelectionComposite(group, SWT.None); diff --git a/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/LambdaConstants.java b/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/LambdaConstants.java index 4351d1b9..826d0cd8 100644 --- a/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/LambdaConstants.java +++ b/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/LambdaConstants.java @@ -20,6 +20,7 @@ public class LambdaConstants { public static final String LAMBDA_JAVA_HANDLER_DOC_URL = "https://docs.aws.amazon.com/lambda/latest/dg/programming-model.html"; public static final String LAMBDA_EXECUTION_ROLE_DOC_URL = "https://docs.aws.amazon.com/lambda/latest/dg/intro-permission-model.html#lambda-intro-execution-role"; + public static final String CLOUDFORMATION_CAPABILITIES = "https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-iam-template.html#capabilities"; public static final String LAMBDA_REQUEST_HANDLER_DOC_URL = "https://docs.aws.amazon.com/lambda/latest/dg/java-programming-model-req-resp.html"; public static final String LAMBDA_STREAM_REQUEST_HANDLER_DOC_URL = "https://docs.aws.amazon.com/lambda/latest/dg/java-handler-io-type-stream.html"; public static final String LAMBDA_FUNCTION_VERSIONING_AND_ALIASES_URL = "http://docs.aws.amazon.com/lambda/latest/dg/versioning-aliases.html"; diff --git a/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/model/SelectOrCreateBasicLambdaRoleDataModel.java b/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/model/SelectOrCreateBasicLambdaRoleDataModel.java index 82119727..49abc8fd 100644 --- a/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/model/SelectOrCreateBasicLambdaRoleDataModel.java +++ b/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/model/SelectOrCreateBasicLambdaRoleDataModel.java @@ -57,7 +57,7 @@ public String getDefaultResourceName() { @Override public List loadAwsResources(AwsResourceScopeParamBase param) { AmazonIdentityManagement iamClient = AwsToolkitCore.getClientFactory(param.getAccountId()) - .getIAMClient(); + .getIAMClientByRegion(param.getRegionId()); return ServiceApiUtils.getAllLambdaRoles(iamClient); } diff --git a/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/project/wizard/model/DeployServerlessProjectDataModel.java b/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/project/wizard/model/DeployServerlessProjectDataModel.java index 74346e8c..3f34ea03 100644 --- a/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/project/wizard/model/DeployServerlessProjectDataModel.java +++ b/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/project/wizard/model/DeployServerlessProjectDataModel.java @@ -21,10 +21,12 @@ import org.eclipse.core.resources.IProject; import com.amazonaws.eclipse.cloudformation.model.ParametersDataModel; +import com.amazonaws.eclipse.core.model.MultipleSelectionListDataModel; import com.amazonaws.eclipse.core.model.RegionDataModel; import com.amazonaws.eclipse.core.model.SelectOrCreateBucketDataModel; import com.amazonaws.eclipse.lambda.model.SelectOrInputStackDataModel; import com.amazonaws.eclipse.lambda.project.metadata.ServerlessProjectMetadata; +import com.amazonaws.services.cloudformation.model.Capability; public class DeployServerlessProjectDataModel { @@ -32,6 +34,7 @@ public class DeployServerlessProjectDataModel { private final SelectOrCreateBucketDataModel bucketDataModel = new SelectOrCreateBucketDataModel(); private final SelectOrInputStackDataModel stackDataModel = new SelectOrInputStackDataModel(); private final ParametersDataModel parametersDataModel = new ParametersDataModel(); + private final MultipleSelectionListDataModel capabilitiesDataModel = new MultipleSelectionListDataModel<>(); private final IProject project; private final String projectName; @@ -72,6 +75,10 @@ public SelectOrInputStackDataModel getStackDataModel() { return stackDataModel; } + public MultipleSelectionListDataModel getCapabilitiesDataModel() { + return capabilitiesDataModel; + } + public ParametersDataModel getParametersDataModel() { return parametersDataModel; } diff --git a/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/serverless/handler/DeployServerlessProjectHandler.java b/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/serverless/handler/DeployServerlessProjectHandler.java index 04b15885..ef163572 100644 --- a/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/serverless/handler/DeployServerlessProjectHandler.java +++ b/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/serverless/handler/DeployServerlessProjectHandler.java @@ -15,6 +15,7 @@ package com.amazonaws.eclipse.lambda.serverless.handler; import java.io.File; +import java.util.HashSet; import java.util.Set; import org.eclipse.core.commands.AbstractHandler; @@ -71,8 +72,9 @@ public static void doDeployServerlessTemplate(IProject project) { return; } - Set handlerClasses = UploadFunctionUtil.findValidHandlerClass(project); - if (handlerClasses == null || handlerClasses.isEmpty()) { + Set handlerClasses = new HashSet<>(UploadFunctionUtil.findValidHandlerClass(project)); + handlerClasses.addAll(UploadFunctionUtil.findValidStreamHandlerClass(project)); + if (handlerClasses.isEmpty()) { MessageDialog.openError( Display.getCurrent().getActiveShell(), "Invalid AWS Lambda Project", diff --git a/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/serverless/ui/DeployServerlessProjectPage.java b/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/serverless/ui/DeployServerlessProjectPage.java index 7669912c..98fe13c1 100644 --- a/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/serverless/ui/DeployServerlessProjectPage.java +++ b/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/serverless/ui/DeployServerlessProjectPage.java @@ -15,12 +15,14 @@ package com.amazonaws.eclipse.lambda.serverless.ui; import static com.amazonaws.eclipse.core.ui.wizards.WizardWidgetFactory.newGroup; +import static com.amazonaws.eclipse.core.ui.wizards.WizardWidgetFactory.newLink; import static com.amazonaws.eclipse.lambda.LambdaAnalytics.trackRegionComboChangeSelection; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.io.File; import java.io.FileInputStream; +import java.util.Arrays; import java.util.List; import java.util.Map.Entry; @@ -48,10 +50,12 @@ import com.amazonaws.eclipse.core.regions.Region; import com.amazonaws.eclipse.core.regions.ServiceAbbreviations; import com.amazonaws.eclipse.core.ui.CancelableThread; +import com.amazonaws.eclipse.core.ui.MultipleSelectionListComposite; import com.amazonaws.eclipse.core.ui.RegionComposite; import com.amazonaws.eclipse.core.ui.SelectOrCreateBucketComposite; import com.amazonaws.eclipse.core.util.S3BucketUtil; import com.amazonaws.eclipse.databinding.ChainValidator; +import com.amazonaws.eclipse.lambda.LambdaConstants; import com.amazonaws.eclipse.lambda.LambdaPlugin; import com.amazonaws.eclipse.lambda.model.SelectOrInputStackDataModel; import com.amazonaws.eclipse.lambda.project.wizard.model.DeployServerlessProjectDataModel; @@ -61,6 +65,7 @@ import com.amazonaws.eclipse.lambda.serverless.model.transform.ServerlessModel; import com.amazonaws.eclipse.lambda.ui.SelectOrInputStackComposite; import com.amazonaws.services.cloudformation.AmazonCloudFormation; +import com.amazonaws.services.cloudformation.model.Capability; import com.amazonaws.services.cloudformation.model.TemplateParameter; import com.amazonaws.services.cloudformation.model.ValidateTemplateRequest; @@ -76,6 +81,7 @@ public class DeployServerlessProjectPage extends WizardPage { private RegionComposite regionComposite; private SelectOrCreateBucketComposite bucketComposite; private SelectOrInputStackComposite stackComposite; + private MultipleSelectionListComposite capabilitiesSelectionComposite; private IObservableValue templateValidated = new WritableValue(); private ValidateTemplateThread validateTemplateThread; @@ -99,6 +105,8 @@ public void createControl(Composite parent) { createRegionSection(container); createS3BucketSection(container); createStackSection(container); + createCapabilities(container); + createValidationBinding(); aggregateValidationStatus.addChangeListener(new IChangeListener() { @@ -192,6 +200,26 @@ private void createStackSection(Composite parent) { group, bindingContext, dataModel.getStackDataModel()); } + private void createCapabilities(Composite parent) { + Group group = newGroup(parent, "Configure capabilities"); + group.setLayout(new GridLayout(1, false)); + + newLink(group, + LambdaConstants.webLinkListener, + "If you have IAM resources, you can specify either capability. If you " + + "have IAM resources with custom names, you must specify CAPABILITY_NAMED_IAM. " + + "You must select at least one capability. " + + "For more information, see Acknowledging IAM Resources in AWS CloudFormation Templates.", 1, 100, 50); + + capabilitiesSelectionComposite = new MultipleSelectionListComposite<>( + group, bindingContext, dataModel.getCapabilitiesDataModel(), + Arrays.asList(Capability.values()), + Arrays.asList(Capability.CAPABILITY_IAM), + "You must select at least one capability"); + } + private void createValidationBinding() { templateValidated.setValue(null); bindingContext.addValidationStatusProvider(new ChainValidator(templateValidated, new IValidator() { diff --git a/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/serverless/wizard/DeployServerlessProjectWizard.java b/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/serverless/wizard/DeployServerlessProjectWizard.java index 7303e0b9..f1ac6f7d 100644 --- a/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/serverless/wizard/DeployServerlessProjectWizard.java +++ b/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/serverless/wizard/DeployServerlessProjectWizard.java @@ -134,7 +134,6 @@ public boolean performCancel() { private boolean deployServerlessTemplate(IProgressMonitor monitor, int totalUnitOfWork) throws IOException, InterruptedException { String stackName = dataModel.getStackDataModel().getStackName(); - monitor.subTask("Exporting Lambda functions..."); File jarFile = FunctionJarExportHelper.exportProjectToJarFile(dataModel.getProject(), true); monitor.worked((int)(totalUnitOfWork * 0.1)); @@ -210,7 +209,7 @@ private boolean deployServerlessTemplate(IProgressMonitor monitor, int totalUnit .withTemplateURL(s3.getUrl(bucketName, generatedServerlessTemplateKeyName).toString()) .withChangeSetName(changeSetName).withStackName(stackName) .withChangeSetType(changeSetType) - .withCapabilities(Capability.CAPABILITY_IAM) + .withCapabilities(dataModel.getCapabilitiesDataModel().getSelectedList().toArray(new Capability[0])) .withParameters(dataModel.getParametersDataModel().getParameters()) .withTags(new Tag().withKey("ApiGateway").withValue("true"))); diff --git a/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/upload/wizard/page/FunctionConfigurationPage.java b/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/upload/wizard/page/FunctionConfigurationPage.java index 29a04f30..d10a0dc6 100644 --- a/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/upload/wizard/page/FunctionConfigurationPage.java +++ b/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/upload/wizard/page/FunctionConfigurationPage.java @@ -72,7 +72,7 @@ public class FunctionConfigurationPage extends WizardPageWithOnEnterHook { private static final int MIN_MEMORY = 128; - private static final int MAX_MEMORY = 1536; + private static final int MAX_MEMORY = 3008; private static final int DEFAULT_MEMORY = 512; private static final int MIN_TIMEOUT = 1; diff --git a/releng/com.amazonaws.eclipse.devide/.project b/releng/com.amazonaws.eclipse.devide/.project index 1c4f0d46..fd54b7ca 100644 --- a/releng/com.amazonaws.eclipse.devide/.project +++ b/releng/com.amazonaws.eclipse.devide/.project @@ -3,14 +3,6 @@ com.amazonaws.eclipse.devide - aws-toolkit-eclipse - com.amazonaws.eclipse.core - com.amazonaws.eclipse.core.feature - com.amazonaws.eclipse.elasticbeanstalk - com.amazonaws.eclipse.elasticbeanstalk.feature - com.amazonaws.eclipse.javasdk - com.amazonaws.eclipse.oxygen - com.amazonaws.eclipse.sdk.ui diff --git a/tests/com.amazonaws.eclipse.core.tests/src/com/amazonaws/eclipse/core/regions/AwsClientFactoryTests.java b/tests/com.amazonaws.eclipse.core.tests/src/com/amazonaws/eclipse/core/regions/AwsClientFactoryTests.java new file mode 100644 index 00000000..504b2bf1 --- /dev/null +++ b/tests/com.amazonaws.eclipse.core.tests/src/com/amazonaws/eclipse/core/regions/AwsClientFactoryTests.java @@ -0,0 +1,97 @@ +package com.amazonaws.eclipse.core.regions; + +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +import com.amazonaws.AmazonServiceException; +import com.amazonaws.auth.AWSStaticCredentialsProvider; +import com.amazonaws.auth.BasicAWSCredentials; +import com.amazonaws.eclipse.core.AWSClientFactory; +import com.amazonaws.services.cloudfront.AmazonCloudFront; +import com.amazonaws.services.cloudfront.model.ListDistributionsRequest; +import com.amazonaws.services.identitymanagement.AmazonIdentityManagement; +import com.amazonaws.services.s3.AmazonS3; + +public class AwsClientFactoryTests { + private static AWSClientFactory FACTORY; + + @BeforeClass + public static void setUp() { + // We are testing invalid signing region, it doesn't matter if the AWS Credentials + // is not valid. + FACTORY = new AWSClientFactory(new AWSStaticCredentialsProvider( + new BasicAWSCredentials("foo", "bar"))); + } + + /** + * IAM and CloudFront are using global endpoints. The following two tests assure the clients + * are created correctly with different regions. + */ + @Test + public void testIamClient() { + testIamClientByRegion("us-west-2"); + testIamClientByRegion("cn-north-1"); + testIamClientByRegion("cn-northwest-1"); + testIamClientByRegion("us-gov-west-1"); + } + + @Test + public void testCloudFrontClient() { + testCloudFrontClientByRegion("us-west-2"); + } + + @Test + public void testS3Client() { + testS3ClientByRegion("us-west-2"); + testS3ClientByRegion("eu-central-1"); + testS3ClientByRegion("cn-north-1"); + testS3ClientByRegion("cn-northwest-1"); + testS3ClientByRegion("us-gov-west-1"); + } + + /** + * For IAM client, if the provided signing region is not "us-east-1" for AWS partition, + * the SignatureDoesNotMatch exception would be thrown. This test case assures the IAM + * client is created correctly. + */ + private void testIamClientByRegion(String regionId) { + try { + AmazonIdentityManagement iam = FACTORY.getIAMClientByRegion(regionId); + iam.listAccessKeys(); + } catch (AmazonServiceException e) { + String errorCode = e.getErrorCode(); + Assert.assertNotEquals("SignatureDoesNotMatch", errorCode); + } + } + + /** + * For CloudFront client, if the provided signing region is not "us-east-1" for AWS partition, + * the SignatureDoesNotMatch exception would be thrown. This test case assures the CloudFront + * client is created correctly. + */ + private void testCloudFrontClientByRegion(String regionId) { + try { + AmazonCloudFront cloudFront = FACTORY.getCloudFrontClientByRegion(regionId); + cloudFront.listDistributions(new ListDistributionsRequest()); + } catch (AmazonServiceException e) { + String errorCode = e.getErrorCode(); + Assert.assertNotEquals("SignatureDoesNotMatch", errorCode); + } + } + + /** + * For S3 client, if the underlying endpoint is the global endpoint "https://s3.amazonaws.com", + * but the signing region is not "us-east-1", the AuthorizationHeaderMalformed exception would + * be thrown. This test assures the S3 client is created correctly with different regions. + */ + private void testS3ClientByRegion(String regionId) { + try { + AmazonS3 s3 = FACTORY.getS3ClientByRegion(regionId); + s3.listBuckets(); + } catch (AmazonServiceException e) { + String errorCode = e.getErrorCode(); + Assert.assertNotEquals("AuthorizationHeaderMalformed", errorCode); + } + } +} diff --git a/thirdparty/com.amazonaws.eclipse.javasdk/META-INF/MANIFEST.MF b/thirdparty/com.amazonaws.eclipse.javasdk/META-INF/MANIFEST.MF index 0c7943d7..ce13eb28 100644 --- a/thirdparty/com.amazonaws.eclipse.javasdk/META-INF/MANIFEST.MF +++ b/thirdparty/com.amazonaws.eclipse.javasdk/META-INF/MANIFEST.MF @@ -195,10 +195,10 @@ Export-Package: com.amazonaws.services.codedeploy,com.amazonaws.servic Bundle-SymbolicName: com.amazonaws.eclipse.javasdk Bundle-Name: AWS Toolkit for Eclipse Java SDK Bundle Bundle-Version: 1.11.248 -Built-By: dmainz +Built-By: zhaoxiz Bundle-ManifestVersion: 2 -Bnd-LastModified: 1514212585795 +Bnd-LastModified: 1513292086530 Created-By: Apache Maven Bundle Plugin Tool: Bnd-0.0.357 -Build-Jdk: 1.8.0_40 +Build-Jdk: 1.8.0_151