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

Evaluate Relationships #430

Merged
merged 41 commits into from
Feb 2, 2018
Merged

Evaluate Relationships #430

merged 41 commits into from
Feb 2, 2018

Conversation

mklopp
Copy link
Collaborator

@mklopp mklopp commented Jan 22, 2018

Implement:

  • CheckModel
  • Prepare
  • VisitRelationships

@codecov
Copy link

codecov bot commented Jan 22, 2018

Codecov Report

Merging #430 into master will increase coverage by 1.19%.
The diff coverage is 80.85%.

Impacted file tree graph

@@             Coverage Diff             @@
##             master    #430      +/-   ##
===========================================
+ Coverage        69%   70.2%   +1.19%     
  Complexity      114     114              
===========================================
  Files           299     304       +5     
  Lines          6602    6756     +154     
  Branches        452     458       +6     
===========================================
+ Hits           4556    4743     +187     
+ Misses         1859    1813      -46     
- Partials        187     200      +13
Impacted Files Coverage Δ Complexity Δ
...a/plugins/cloudformation/CloudFormationPlugin.java 80.95% <0%> (+7.03%) 0 <0> (ø) ⬇️
...gins/cloudformation/CloudFormationFileCreator.java 85.52% <100%> (-6.58%) 0 <0> (ø)
...lugins/cloudformation/mapper/CapabilityMapper.java 56.47% <61.53%> (+30%) 0 <0> (ø) ⬇️
.../cloudformation/visitor/CheckModelNodeVisitor.java 66.66% <66.66%> (ø) 0 <0> (?)
...loudformation/visitor/PrepareModelNodeVisitor.java 72.22% <72.22%> (ø) 0 <0> (?)
...rmation/visitor/CheckModelRelationshipVisitor.java 75% <75%> (ø) 0 <0> (?)
...mation/visitor/CloudFormationVisitorExtension.java 80.76% <80.76%> (ø) 0 <0> (?)
...lugins/cloudformation/CloudFormationLifecycle.java 85.71% <81.25%> (+12.38%) 0 <0> (ø) ⬇️
...udformation/visitor/TransformModelNodeVisitor.java 83.8% <83.8%> (ø) 0 <0> (?)
...ation/visitor/PrepareModelRelationshipVisitor.java 90.9% <90.9%> (ø) 0 <0> (?)
... and 21 more

Copy link
Collaborator

@c-mueller c-mueller left a comment

Choose a reason for hiding this comment

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

Just some Points i want to adress

@@ -31,7 +33,10 @@ protected void onSuccess(File outputDir) {

@Override
protected void onFailure(File outputDir, Exception e) {
fail();
//if the cause for transformation failure is amazonEC2Exception its fine because of the credentials
if (!(e instanceof TransformationFailureException && e.getCause() instanceof AmazonEC2Exception)) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please consider using assumptions to prevnt a test from beeing executed if the credentials are not set.

you can use the check assumptions method in this test to do this.

throw tfe;
} catch (Exception e) {
logger.error("Transformation to CloudFormation unsuccessful. Random Exception should not appear here.");
throw new TransformationFailureException("Random exception", e);
Copy link
Collaborator

Choose a reason for hiding this comment

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

I dont like this wording. Unexpected error sonds much better.

Noting is really random here

logger.error("Transformation to CloudFormation unsuccessful. Please check the StackTrace for more Info.");
e.printStackTrace();
tfe.printStackTrace();
Copy link
Collaborator

Choose a reason for hiding this comment

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

there is no need to print the stacktrace, because the ExecutionTask (the part of the core that handles the excution of transformation)

}
} catch (Exception e) {
logger.error("Error while creating WebApplication");
throw new TransformationFailureException("Failed at WebApplication node", e);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Maybe condider adding the name of the node

@mklopp mklopp requested a review from mmunozba January 29, 2018 14:36
@mklopp mklopp changed the title [WIP] Evaluate Relationships Evaluate Relationships Jan 29, 2018
hnicke
hnicke previously requested changes Jan 29, 2018

@Override
public void visit(MysqlDatabase node) {
logger.info("Check MysqlDatabase node {}.", node.getEntityName());
Copy link
Contributor

Choose a reason for hiding this comment

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

I adopted the habit to always quote variables when logging.

logger.info("Check MysqlDatabase node '{}'"),

This way you can detect null variables when reading the log:

INFO   Check MysqlDatabase node ''

But taste varies.

// check if Mysql is the only node hosted on his compute node
Compute compute = getCompute(node);
if (topology.incomingEdgesOf(compute).size() == 1) {
// means our dbms is the only one hosted on this compute
Copy link
Contributor

Choose a reason for hiding this comment

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

This is incorrect. The graph's edges are a Set of all possible Relationships. HostedOn is only one of them.
A quick fix could be:

if ( topology.incomingEdgesOf(compute).stream().filter(p -> p instanceof HostedOn).collect(Collectors.toSet()).size() == 1)

Didn't try that code, though.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Actually I'm pretty sure this is correct. Sure it is a Set of all possible relationships but I already know that the mysqlDatabase node I am currently visiting is hosted on this specific compute (or mysqlDatabase is hosted on mysqlDbms is hosted on this compute node).
That means if the compute node has exactly one relationship it is that hosted on relation.

Copy link
Contributor

Choose a reason for hiding this comment

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

Imagine the tosca modeller has written a template that defines a timing dependency (DependsOn) from node 'otherNode' to 'server'.
Now,

if (topology.incomingEdgesOf(compute).size() == 1) {

..will evaluate to false, altough mysql dbms node is still the only node hosted on the server node.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

True

Compute computeWebApplication = getCompute(webApplication);
if (computeMysqlDatabase.equals(computeWebApplication)) {
// means we can set the private address as reference the database endpoint
//TODO only set privateAddress or also publicAddress?
Copy link
Contributor

Choose a reason for hiding this comment

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

Why not set the public address as well? Go for it.

if (computeMysqlDatabase.equals(computeWebApplication)) {
// means we can set the private address as reference the database endpoint
//TODO only set privateAddress or also publicAddress?
computeMysqlDatabase.setPrivateAddress(Fn.fnGetAtt(toAlphanumerical(mysqlDatabase.getEntityName()),
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is the private address set in both Visitor classes?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I try to cover two possibilities here.

  1. PrepareModelNodeVisitor.java sets the private address of the compute node to the database endpoint exactly when the database is the only node hosted on the compute. That means this compute node completely belongs to the database and represents it (by providing the endpoint)
  2. PrepareModelRelationshipVisitor.java is used in the case of our current lamp-model where both the webapplication and the database are hosted on one compute. I only feel safe to set the private address of a compute node to reference the database if there is a connects to relationship between the two nodes sitting on top of the compute node.
    Here I see the problem tackled in the previous meeting. What if someone references the webapplication as well by calling the private address of compute node it is hosted on (which is valid). He would get the database endpoint instead.

Copy link
Contributor

Choose a reason for hiding this comment

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

I just had following alternative idea:

  • when handling the dbms node;
    1. store the initial value of compute#privateAddress
    2. apply the AWS Fn fix (compute.setPrivateAddress(Fn....))
    3. write all mysql dbms related configuration to aws template file
    4. restore value of compute#privateAddress

Could this work?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I will look into this

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I don't think this will work. I actually need the privateAddress to be set to the database endpoint when I visit the webApplication node and not the dbms/database node.
I think the current solution is the best there is and will work for most cases.

@hnicke hnicke changed the title Evaluate Relationships [WIP] Evaluate Relationships Jan 29, 2018
@mklopp mklopp dismissed stale reviews from c-mueller and hnicke January 31, 2018 15:23

stale

@mklopp mklopp requested review from hnicke and mmunozba and removed request for mmunozba January 31, 2018 15:24
@Override
public void visit(Compute node) {
logger.info("Check Compute node '{}'.", node.getEntityName());
List<OsCapability.Type> supportedTypes = new ArrayList<>();
Copy link
Contributor

Choose a reason for hiding this comment

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

There's a utility class that could help for better readibility here:

List<OsCapability.Type> supportedTypes = Lists.newArrayList(OsCapability.Type.LINUX);

}

@Override
public void visit(MysqlDatabase node) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe refactor this, as its a quite big method.

CheckModelNodeVisitor checkModelNodeVisitor = new CheckModelNodeVisitor(context);
logger.debug("Check nodes");
for (VisitableNode node : nodes) {
node.accept(checkModelNodeVisitor);
Copy link
Contributor

Choose a reason for hiding this comment

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

In the Visitor classes you always log which node you are visiting when entering the method. Why don't you just write it here once for all methods?

CheckModelRelationshipVisitor checkModelRelationshipVisitor = new CheckModelRelationshipVisitor(context);
logger.debug("Check relationships");
for (VisitableRelationship relationship : relationships) {
relationship.accept(checkModelRelationshipVisitor);
Copy link
Contributor

Choose a reason for hiding this comment

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

Same for relationships.

// if they are hosted on the same compute --> we can set the compute private address to
Compute computeMysqlDatabase = getCompute(mysqlDatabase);
Compute computeWebApplication = getCompute(webApplication);
if (computeMysqlDatabase.equals(computeWebApplication)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

What happens if that is not the case?

logger.info("Prepare ConnectsTo relation '{}'.", relation.getEntityName());
RootNode source = topology.getEdgeSource(relation);
RootNode target = topology.getEdgeTarget(relation);
if (source instanceof WebApplication && target instanceof MysqlDatabase) {
Copy link
Contributor

Choose a reason for hiding this comment

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

What happens if that is not the case? Maybe log that you drop the relationship.

PluginFileAccess accessL = new PluginFileAccess(
new File(tmpdir, "sourceDir"),
new File(tmpdir, "targetDir"),
mock(Log.class));
Copy link
Contributor

Choose a reason for hiding this comment

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

you could simply use logMock() instead of mock(Log.class). The calls to getLogger() are already stubbed.

@hnicke hnicke changed the title Evaluate Relationships [WIP] Evaluate Relationships Jan 31, 2018
Copy link
Collaborator

@mmunozba mmunozba left a comment

Choose a reason for hiding this comment

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

Some questions, some suggestions.

@@ -75,6 +83,7 @@ public CloudFormationModule(PluginFileAccess fileAccess, String awsRegion, AWSCr
(KEYNAME_CONSTRAINT_DESCRIPTION);
keyNameVar = template.ref(KEYNAME);
cfnInitMap = new HashMap<>();
computeToEc2 = new HashSet<>();
Copy link
Collaborator

Choose a reason for hiding this comment

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

Could you explain the purpose of this set?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

We don't want to transform every compute node to an ec2 instance. For example if an database is the only node hosted on an compute we dont want to transform it.

//might grow but for now only linux
List<OsCapability.Distribution> supportedDistributions = new ArrayList<>();
supportedDistributions.add(OsCapability.Distribution.UBUNTU);
//might grow, but for now only ubuntu, maybe already work with others but not yet tested
Copy link
Collaborator

Choose a reason for hiding this comment

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

Added an issue for this: #461.

logger.info("Check Compute node '{}'.", node.getEntityName());
List<OsCapability.Type> supportedTypes = new ArrayList<>();
supportedTypes.add(OsCapability.Type.LINUX);
//might grow but for now only linux
Copy link
Collaborator

Choose a reason for hiding this comment

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

Added an issue for this: #462.

@param webApplication the webApplication to find the host for
@return the underlying Compute node
*/
protected static Compute getCompute(WebApplication webApplication) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I feel like CloudFormationNodeVisitor is getting pretty big.
Maybe we should extract some of the utility methods like these getCompute methods?
We might need these for other future visitors, so it might be a good idea to extract them regardless of the size of this class.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I extracted these into a parent class

.imageId(imageId)
.instanceType(instanceType);
logger.info("Visit Compute node '{}'.", node.getEntityName());
if (cfnModule.checkComputeToEc2(node)) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Maybe I'm misunderstanding this, but doesn't this mean that a Compute node only gets transformed if an Apache is hosted on it? Since addComputeToEc2 only gets called in the visit() method of Apache in PrepareModelNodeVisitor.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yeah this is wrong, I changed it

@@ -296,4 +338,18 @@ private String getFileURL(String bucketName, String objectKey) {
public CapabilityMapper createCapabilityMapper() {
return new CapabilityMapper(cfnModule.getAWSRegion(), cfnModule.getAwsCredentials(), logger);
}

private Set<Compute> getHostsOfConnectedTo(RootNode node) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Maybe extract this method? See above.

//password needs to be at least 8 characters long
String password = node.getPassword().get();
if (password.length() < minPWLength) {
logger.warn("Database password to short, creating new random password");
Copy link
Collaborator

Choose a reason for hiding this comment

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

to -> too

hnicke
hnicke previously requested changes Jan 31, 2018
@@ -10,6 +10,19 @@
}


/**
Copy link
Contributor

Choose a reason for hiding this comment

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

Please also rezip the corresponding csar (run csar-make)

@mklopp mklopp dismissed stale reviews from mmunozba and hnicke February 2, 2018 13:27

stale

@mklopp mklopp changed the title [WIP] Evaluate Relationships Evaluate Relationships Feb 2, 2018
@mklopp mklopp merged commit 84d91f7 into master Feb 2, 2018
@mklopp mklopp deleted the feature/aws/evaluate-relationships branch February 2, 2018 14:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants