Skip to content

Commit

Permalink
Documented DockerfileLinter.
Browse files Browse the repository at this point in the history
  • Loading branch information
devanshu911 committed Jul 9, 2015
1 parent 1f88210 commit 131ffa1
Showing 1 changed file with 123 additions and 29 deletions.
152 changes: 123 additions & 29 deletions src/main/java/org/jboss/forge/addon/docker/linter/DockerfileLinter.java
Expand Up @@ -19,9 +19,6 @@

public class DockerfileLinter
{
// TODO add support for JavaScript regexes.
// TODO add support for case insensitive regexes.

private ResourceFactory resourceFactory;

private FileResource<?> baseRuleFile = null;
Expand All @@ -31,36 +28,24 @@ public DockerfileLinter(ResourceFactory resourceFactory)
this.resourceFactory = resourceFactory;
}

@SuppressWarnings("unchecked")
public FileResource<?> getBaseRules()
{
InputStream ist = getClass().getResourceAsStream("base_rules.yaml");
File file = null;
try
{
file = File.createTempFile("fileresourcetest", ".yaml");
}
catch (IOException e1)
{
}
file.deleteOnExit();
FileResource<?> fileResource = resourceFactory.create(FileResource.class, file);
fileResource.setContents(ist);
try
{
ist.close();
}
catch (IOException e)
{
}
return fileResource;
}

/**
* Return the {@link DockerfileLintResult} ,lint the underlying Dockerfile against a set of preset base rules.
*
* @param dockerfile The {@link DockerFileResource} to be linted.
* @return The result of validation containing errors, warnings and info.
*/
public DockerfileLintResult lint(DockerFileResource dockerfile)
{
return lint(dockerfile, null);
}

/**
* Return the {@link DockerfileLintResult} ,lint the underlying Dockerfile against the given rule file.
*
* @param dockerfile The {@link DockerFileResource} to be linted.
* @param ruleFile The {@link Resource} which is the abstraction for YAML rule file used to lint against.
* @return The result of validation containing errors, warnings and info.
*/
public DockerfileLintResult lint(DockerFileResource dockerfile, Resource<?> ruleFile)

{
Expand All @@ -75,6 +60,7 @@ public DockerfileLintResult lint(DockerFileResource dockerfile, Resource<?> rule
Map<String, Object> baseRuleFileObject = (Map<String, Object>) baseYaml.load(bis);
Map<String, Object> object = baseRuleFileObject;

// Load the rule file YAML object.
if (ruleFile != null)
{
InputStream is = ruleFile.getResourceInputStream();
Expand All @@ -95,12 +81,15 @@ public DockerfileLintResult lint(DockerFileResource dockerfile, Resource<?> rule
// @SuppressWarnings("unchecked")
// Map<String, String> profile = (Map<String, String>) object.get("profile");

// Map representing the "line_rules" section of the rule file.
@SuppressWarnings("unchecked")
Map<String, Object> linerules = (Map<String, Object>) object.get("line_rules");

// Map representing the "required_instructions" section of the rule file.
@SuppressWarnings("unchecked")
List<Object> reqinst = (List<Object>) object.get("required_instructions");

// Map representing the "general" section of the rule file.
@SuppressWarnings("unchecked")
Map<String, Object> general = (Map<String, Object>) object.get("general");

Expand Down Expand Up @@ -128,21 +117,24 @@ public DockerfileLintResult lint(DockerFileResource dockerfile, Resource<?> rule
String currentLine = i.next();
currentLineIndex++;

// ignore blank lines.
if (currentLine == null || currentLine.length() == 0)
{
continue;
}

// ignore comments.
if (currentLine.charAt(0) == '#')
{
continue;
}

// ignore if "ignoreRegex" matches the current line.
if (ignoreRegex != null && testRegex(ignoreRegex, currentLine))
{
continue;
}

// join multiple lines into one using the "multiLineRegex" match on the current line.
while (multiLineRegex != null && testRegex(multiLineRegex, currentLine))
{
currentLine = currentLine.replaceFirst(multiLineRegex, " ");
Expand All @@ -151,6 +143,7 @@ public DockerfileLintResult lint(DockerFileResource dockerfile, Resource<?> rule
currentLineIndex++;
}

// Check if the presence the required "FROM" instruction has been checked.
if (!fromCheck)
{
fromCheck = true;
Expand All @@ -163,12 +156,14 @@ public DockerfileLintResult lint(DockerFileResource dockerfile, Resource<?> rule

}

// Add error if the current line does not contain the "instructionRegex" defined in the rule file.
if (instructionRegex != null && !testRegex(instructionRegex, currentLine))
{
dockerfileLintResult.addError("Not a Instruction", currentLine, currentLineIndex);
continue;
}

// Add error if the current line does not contain a Valid Instruction.
if (validInstructionRegex != null && !testRegex(validInstructionRegex, currentLine))
{
dockerfileLintResult.addError("Not a Valid Instruction", currentLine, currentLineIndex);
Expand All @@ -190,6 +185,7 @@ public DockerfileLintResult lint(DockerFileResource dockerfile, Resource<?> rule
continue;
}

// Reduce the number of occurrences required of the current instruction by one because one has been just found.
if (requiredInstructions.get(instruction) != null)
{
requiredInstructions.put(instruction, requiredInstructions.get(instruction) - 1);
Expand All @@ -198,31 +194,51 @@ public DockerfileLintResult lint(DockerFileResource dockerfile, Resource<?> rule
@SuppressWarnings("unchecked")
List<Object> instructionRules = (List<Object>) ((Map<String, Object>) linerules.get(instruction)).get("rules");

// check the line rules for the instruction found.
checkLineRules(instruction, currentLine, instructionRules, dockerfileLintResult, currentLineIndex);

@SuppressWarnings("unchecked")
String parameterSyntaxRegex = (String) ((Map<String, String>) linerules.get(instruction))
.get("paramSyntaxRegex");

// Extract the command parameters.
String parameters = currentLine.substring(matcher.end());

// check the parameter rules for the instruction found by matching the "paramSyntaxRegex" to the parameter.
if (parameterSyntaxRegex != null && !testRegex(parameterSyntaxRegex, parameters))
{
dockerfileLintResult.addError("Bad Parameters", currentLine, currentLineIndex);
continue;
}
}

// Check whether all instructions defined in the "required_instructions" section of the rule file exist.
checkRequiredInstructions(requiredInstructions, dockerfileLintResult, reqinst);

return dockerfileLintResult;
}

/**
* Return the {@link boolean} ,returns true if the source matches the regex.
*
* @param regex The {@link String} representing the Regular Expression.
* @param source The {@link String} representing the source text.
* @return Boolean result of the pattern match.
*/
private boolean testRegex(String regex, String source)

This comment has been minimized.

Copy link
@gastaldi

gastaldi Jul 9, 2015

Member

It looks like this method could be removed and used source.matches(regex) instead?

{
return (Pattern.compile(regex).matcher(source).find());

}

/**
* Return the {@link Map} ,representing the Dockerfile Command name as the key and the number of command occurrences
* required by the lint file as values.
*
* @param requiredInstructions The {@link List},representing "required_instructions" section of the YAML rule file.
* @return Map result of parsing the "required_instructions" section of the YAML rule file as Dockerfile Command name
* as the key and the number of command occurrences required by the lint file as values.
*/
private Map<String, Integer> setupReqInstExists(List<Object> requiredInstructions)
{

Expand All @@ -237,6 +253,15 @@ private Map<String, Integer> setupReqInstExists(List<Object> requiredInstruction
return reqInst;
}

/**
* Validate and add messages defined in YAML rule file to the {@link DockerfileLintResult} based on the problem found
* in the linting process. Checks that all the required instructions exist in the Dockerfile.
*
* @param dockerfileLintResult The {@link DockerfileLintResult} representing the Lint result being constructed.
* @param reqinst The {@link List} representing the "required_instructions" section of rule file.
* @param requiredInstructions, representing the Dockerfile Command name as the key and the number of command
* occurrences required by the lint file as values.
*/
private void checkRequiredInstructions(Map<String, Integer> requiredInstructions,
DockerfileLintResult dockerfileLintResult,
List<Object> reqinst)
Expand Down Expand Up @@ -269,6 +294,16 @@ private void checkRequiredInstructions(Map<String, Integer> requiredInstructions

}

/**
* Validate and add messages defined in YAML rule file to the {@link DockerfileLintResult} based on the problem found
* in the linting process for individual Dockerfile instruction.
*
* @param dockerfileLintResult The {@link DockerfileLintResult} representing the Lint result being constructed.
* @param instructionRules The {@link List} representing the "rules" section of the YAML file.
* @param instruction The {@link String} representing the current Dockerfile instruction.
* @param currentLine The {@link String} representing the current Dockerfile line.
* @param currentLineIndex The {@link Integer} representing the index of the current Dockerfile line.
*/
private void checkLineRules(String instruction, String currentLine, List<Object> instructionRules,
DockerfileLintResult dockerfileLintResult,
int currentLineIndex)
Expand Down Expand Up @@ -309,6 +344,13 @@ private void checkLineRules(String instruction, String currentLine, List<Object>

}

/**
* Return the {@link Map} ,which represents the merged rules of the rule file and the based rule file.
*
* @param ruleFileObject The {@link Map} representing the rules in YAML rule file used to lint against.
* @param baseFileObject The {@link Map} representing the rules in YAML base rule file used to lint against.
* @return Map containing rule names as keys and the corresponding regexes as values.
*/
@SuppressWarnings("unchecked")
private Map<String, Object> getCombinedRules(Map<String, Object> ruleFileObject,
Map<String, Object> baseRuleFileObject)
Expand Down Expand Up @@ -383,6 +425,49 @@ public void setBaseRuleFile(FileResource<?> baseRuleFile)
this.baseRuleFile = baseRuleFile;
}

/**
* Return the {@link FileResource} ,for the base rule file.
*
* @return The Base Rule file FileResource containing basic set of lint rules for Dockerfiles.
*/
@SuppressWarnings("unchecked")
public FileResource<?> getBaseRules()

This comment has been minimized.

Copy link
@gastaldi

gastaldi Jul 9, 2015

Member

Return a Resource here and return a URLResource instead (getClass().getResource). There is no need to create a temp file for this

{
InputStream ist = getClass().getResourceAsStream("base_rules.yaml");
File file = null;
try
{
file = File.createTempFile("fileresourcetest", ".yaml");
}
catch (IOException e1)
{
}
file.deleteOnExit();
FileResource<?> fileResource = resourceFactory.create(FileResource.class, file);
fileResource.setContents(ist);
try
{
ist.close();
}
catch (IOException e)
{
}
return fileResource;
}

/**
* Add messages defined in YAML rule file to the {@link DockerfileLintResult} based on the problem found in the
* linting process.
*
* @param dockerfileLintResult The {@link DockerfileLintResult} representing the Lint result being constructed.
* @param type The {@link String} representing the type of problem found in the linting process.
* @param message The {@link String} representing the message corresponding to type of problem found in the linting
* process.
* @param line The {@link String} representing the statement in the Dockerfile where the problem was found in the
* linting process.
* @param lineNumber The {@link Integer} representing the line number of the statement in the Dockerfile where the
* problem was found in the linting process.
*/
private void addLintResult(DockerfileLintResult dockerfileLintResult, String type, String message,
String line, Integer lineNumber)
{
Expand All @@ -404,6 +489,15 @@ else if (type.toUpperCase().equals("INFO"))
}
}

/**
* Add messages defined in YAML rule file to the {@link DockerfileLintResult} based on the problem found in the
* linting process.
*
* @param dockerfileLintResult The {@link DockerfileLintResult} representing the Lint result being constructed.
* @param type The {@link String} representing the type of problem found in the linting process.
* @param message The {@link String} representing the message corresponding to type of problem found in the linting
* process.
*/
private void addLintResult(DockerfileLintResult dockerfileLintResult, String type, String message)
{
addLintResult(dockerfileLintResult, type, message, "", -1);
Expand Down

0 comments on commit 131ffa1

Please sign in to comment.