Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for YAML files for App Engine content block #3186

Merged
merged 10 commits into from
Jun 29, 2018
Merged

Conversation

briandealwis
Copy link
Member

Adds support for app.yaml and the various App Engine .yaml configuration files. This change moves support for resolving App Engine configuration files into a utility class called AppEngineConfigurationUtil; we were previously lumping these files into WEB-INF. The idea is that this class can use the project facets and language types to determine the right place for these configuration files.

But you'll notice that this AppEngineConfigurationUtil class doesn't currently do any of the special logic. I started down the path of adding special logic for App Engine standard vs flexible for determining where these configuration files should be looked up, but giving some of @ludoch's musings about new configuration files I thought it was better to support a src/main/appengine if present, and figure out the rest later. It checks if there's an src/main/appengine and tries to resolve the file there, and otherwise looks in the WEB-INF. This works for our existing projects as AES projects don't have src/main/appengine and all files are placed in WEB-INF, and AEF projects were using src/main/appengine anyways. This has a few knock-on effects:

  • App Engine .yaml configuration files will be found and showed when they are located in WEB-INF: this is unlikely.
  • AES projects with src/main/appengine will break on run or deployment since our launch and deployment processes do not currently resolving files from there. This is an unlikely to happen in practice. And such projects can easily be made to work with by mapping src/main/appengine to WEB-INF the project's Deployment Assembly.

We may want to teach our projects to place these App Engine configuration files to an alternative well-known location via the deployment assembly (e.g., WEB-INF/appengine)?

Fixes #3091
Fixes #3099

@codecov
Copy link

codecov bot commented Jun 28, 2018

Codecov Report

Merging #3186 into master will increase coverage by 0.26%.
The diff coverage is 90.28%.

Impacted file tree graph

@@             Coverage Diff              @@
##             master    #3186      +/-   ##
============================================
+ Coverage     69.75%   70.02%   +0.26%     
- Complexity     2931     2963      +32     
============================================
  Files           372      373       +1     
  Lines         13422    13511      +89     
  Branches       1611     1622      +11     
============================================
+ Hits           9363     9461      +98     
+ Misses         3377     3373       -4     
+ Partials        682      677       -5
Impacted Files Coverage Δ Complexity Δ
...erver/ui/DatastoreIndexesUpdatedStatusHandler.java 0% <0%> (ø) 0 <0> (ø) ⬇️
.../ui/navigator/model/DenialOfServiceDescriptor.java 100% <100%> (ø) 2 <1> (ø) ⬇️
...rd/java8/AppEngineStandardFacetChangeListener.java 71.83% <100%> (+0.4%) 15 <0> (ø) ⬇️
...va8/AppEngineStandardJre8ProjectFacetDetector.java 73.17% <100%> (+0.67%) 6 <0> (ø) ⬇️
...ipse/appengine/localserver/server/ModuleUtils.java 81.81% <100%> (+0.56%) 9 <0> (ø) ⬇️
...acets/ui/navigator/model/TaskQueuesDescriptor.java 100% <100%> (ø) 2 <1> (ø) ⬇️
.../ui/navigator/model/DispatchRoutingDescriptor.java 100% <100%> (ø) 2 <1> (ø) ⬇️
...e/standard/java8/ConvertToJava8RuntimeHandler.java 87.5% <100%> (+0.54%) 4 <0> (ø) ⬇️
.../appengine/standard/java8/AppEngineWebBuilder.java 74.5% <100%> (+0.5%) 10 <0> (ø) ⬇️
...ne/facets/ui/navigator/AppEngineLabelProvider.java 96% <100%> (+11.38%) 23 <13> (+1) ⬆️
... and 14 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update aa89aa3...b061eb0. Read the comment docs.

}

/**
* Test that creating that a newly-created {@code appengine-web.xml} "overrides" the previous
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"... creating a newly ..."

public void setUp() {
// Many of ResourceManager's methods are final; we check getImage() methods
// by verifying that create() is called
doReturn(null).when(resourceManager).create(any(DeviceResourceDescriptor.class));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAIK, a mock will return null for non-mocked methods by default, so I don't get why this is needed.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm, I'm sure there was a reason! But it doesn't seem to fail now…

@@ -105,8 +105,8 @@ public void testAppEngineStandardProjectElementCreate_initial() throws AppEngine
@Test
public void testAppEngineStandardProjectElementCreate_staggered() throws AppEngineException {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The method name contains "Standard". There are other methods having that too.

* Create an App Engine configuration file in the appropriate location for the project.
*
* @param project the hosting project
* @param relativePath the file name
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"relative path to the file"?

public static IFile createConfigurationFile(
IProject project,
IPath relativePath,
InputStream contents,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was going to say this could just be String, but then WebProjectUtil.createFileInWebInf() takes InputStream too. Looking at the callers, they could both accept String, but I don't want to stretch this PR for that. I just think this can just be String, what about you?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's modelled directly after IFile's create() / setContent() which takes an InputStream. But I certainly don't see anything wrong with helper methods.

IFolder appengineFolder = project.getFolder(DEFAULT_CONFIGURATION_FILE_LOCATION);
if (appengineFolder != null && appengineFolder.exists()) {
IFile destination = appengineFolder.getFile(relativePath);
if (destination.exists()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to call exists() twice. This could basically be

IFile destination = project.getFolder(DEFAULT_CONFIGURATION_FILE_LOCATION).getFile(relativePath);

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto the above: it can be with mocks.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getFolder() returning null.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, that's fair. I still don't get the inconsistency (checking null for getFolder() but not for getFile(), etc.), but this is not a big deal.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Point taken: I'll add a null check here too.

Copy link
Member Author

@briandealwis briandealwis Jun 29, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right the null check shouldn't be necessary and I will instead fix up any tests.

But back to your original question about combining into one chain: we need to split it up as we only create or update the file providing the src/main/appengine exists. If it doesn't exist, then we put it into WEB-INF. I think it's time for me to go home: responding to the wrong comment.

@VisibleForTesting
static String getVersionTuple(AppEngineDescriptor descriptor) throws AppEngineException {
static String getVersionTuple(String projectId, String projectVersion, String serviceId) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The order seems weird, given that the tuple will be "project:service:version".

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method was a hold-over when I maintained both a AppEngineDescriptor and AppYaml instance. I can just pass in the AppEngineProjectElement.

@@ -204,8 +209,7 @@ private void resourceChanged(IResourceChangeEvent event) {
projectMapping.invalidate(project);
toBeRefreshed.add(project);
}
} else if (Iterables.any(
projectFiles, file -> file != null && "appengine-web.xml".equals(file.getName()))) {
} else if (anyMatch(projectFiles, Sets.newHashSet("appengine-web.xml", "app.yaml"))) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we reuse AppEngineProjectElement.hasAppEngineDescriptor()?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh yes! I forgot about this.

private String projectVersion;
private String serviceId;

/** Return the App Engine environment type (e.g., standard, flexible). */
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking at the code, this would be flex for the flexible and null for the standard, wouldn't it?

/** Return the App Engine environment type (e.g., standard, flexible). */
private String environmentType;

/** Return the App Engine runtime (e.g., java7, java8). */
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment is not really correct. The values are not compatible between standard and flex, and I think we should clearly explain it. For example, for standard, the values are null and java8, and for flex, the values are java, custom, python27, etc. I haven't checked the actual runtime: value of app.yaml auto-generated from appengine-web.xml where <runtime> is not present (i.e., java7) or it is set to java8, but another point of confusion could be the possibly different values of runtime: and <runtime> after the auto-conversion.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

… and here. I'm currently returning the actual values and leaving the interpretation in getStyledText(). But it's more useful to provide the right interpretation here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verified auto-generated app.yaml from an appengine-web.xml has runtime: java7 or runtime: java8.

@@ -3,6 +3,5 @@
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="src" path="src/"/>
<classpathentry kind="src" path="tests"/>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just confirming (and for documentation), this is a cleanup?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops sorry, this shouldn't have been included. Photon now supports scoped classpath entries, and some action occasionally causes the IDE to regenerate the .classpath files.

IFolder appengineFolder = project.getFolder(DEFAULT_CONFIGURATION_FILE_LOCATION);
if (appengineFolder != null && appengineFolder.exists()) {
IFile destination = appengineFolder.getFile(relativePath);
if (destination.exists()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, that's fair. I still don't get the inconsistency (checking null for getFolder() but not for getFile(), etc.), but this is not a big deal.

@briandealwis
Copy link
Member Author

We have this resource != null && resource.exists() pattern throughout the code base. It shouldn't be necessary, but I'll fix that separately.

@briandealwis briandealwis merged commit f909706 into master Jun 29, 2018
@briandealwis briandealwis deleted the i3091 branch June 29, 2018 20:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Support flex in App Engine content block Content Block Descriptors should recognize YAML
3 participants