-
Notifications
You must be signed in to change notification settings - Fork 4
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
Conversation
Codecov Report
@@ 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
|
There was a problem hiding this 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)) { |
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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(); |
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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
… fulfiller of a capability
… the privateAddress of the compute to reference the database. We can do the same if the mysql is the only node hosted on a compute
…o-TOSCAna/TOSCAna into feature/aws/evaluate-relationships
|
||
@Override | ||
public void visit(MysqlDatabase node) { | ||
logger.info("Check MysqlDatabase node {}.", node.getEntityName()); |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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? |
There was a problem hiding this comment.
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()), |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
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)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.
There was a problem hiding this comment.
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;- store the initial value of
compute#privateAddress
- apply the AWS
Fn
fix (compute.setPrivateAddress(Fn....)
) - write all mysql dbms related configuration to aws template file
- restore value of
compute#privateAddress
- store the initial value of
Could this work?
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
@Override | ||
public void visit(Compute node) { | ||
logger.info("Check Compute node '{}'.", node.getEntityName()); | ||
List<OsCapability.Type> supportedTypes = new ArrayList<>(); |
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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)) { |
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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)); |
There was a problem hiding this comment.
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.
There was a problem hiding this 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<>(); |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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)) { |
There was a problem hiding this comment.
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
.
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
to -> too
@@ -10,6 +10,19 @@ | |||
} | |||
|
|||
|
|||
/** |
There was a problem hiding this comment.
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)
Implement: