Skip to content

Commit

Permalink
Merge pull request #217 from gaia-app/216-extract-provider-information
Browse files Browse the repository at this point in the history
✨ : extract provider information from module code
  • Loading branch information
cdubuisson committed Jan 29, 2020
2 parents 47a28fa + a9bca8e commit 39820a2
Show file tree
Hide file tree
Showing 19 changed files with 274 additions and 103 deletions.
12 changes: 11 additions & 1 deletion src/main/antlr4/io/codeka/gaia/hcl/antlr/hcl.g4
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,20 @@ file
;

directive
: variableDirective
: providerDirective
| resourceDirective
| variableDirective
| outputDirective
;

providerDirective
: 'provider' STRING object
;

resourceDirective
: 'resource' STRING STRING object
;

variableDirective
: 'variable' STRING variableBlock
;
Expand Down
6 changes: 6 additions & 0 deletions src/main/java/io/codeka/gaia/hcl/HclParserImpl.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ interface HclParser {
fun parseContent(content: String): HclVisitor
fun parseVariables(content: String): List<Variable>
fun parseOutputs(content: String): List<Output>
fun parseProvider(fileContent: String): String
}

@Service
Expand Down Expand Up @@ -45,4 +46,9 @@ class HclParserImpl : HclParser {
val hclVisitor = parseContent(content)
return hclVisitor.outputs
}

override fun parseProvider(content: String): String {
val hclVisitor = parseContent(content)
return hclVisitor.provider
}
}
20 changes: 20 additions & 0 deletions src/main/java/io/codeka/gaia/hcl/HclVisitor.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,14 @@ class HclVisitor : hclBaseVisitor<Unit>() {

var variables: MutableList<Variable> = LinkedList()
var outputs: MutableList<Output> = LinkedList()
var provider: String = "unknown"

private val IGNORED_PROVIDERS = setOf("null", "random", "template", "terraform")

private var currentVariable: Variable = Variable(name = "")
private var currentOutput: Output = Output()


override fun visitVariableDirective(ctx: hclParser.VariableDirectiveContext) {
currentVariable = Variable(name = ctx.STRING().text.removeSurrounding("\""))
variables.add(currentVariable)
Expand Down Expand Up @@ -49,4 +53,20 @@ class HclVisitor : hclBaseVisitor<Unit>() {
override fun visitOutputSensitive(ctx: hclParser.OutputSensitiveContext) {
currentOutput.sensitive = ctx.BOOLEAN().text.removeSurrounding("\"")
}

override fun visitProviderDirective(ctx: hclParser.ProviderDirectiveContext) {
val parsedProvider = ctx.STRING().text.removeSurrounding("\"")
if (! IGNORED_PROVIDERS.contains(parsedProvider)) {
provider = parsedProvider
}
}

override fun visitResourceDirective(ctx: hclParser.ResourceDirectiveContext) {
// provider already found !
if (provider != "unknown") return

// check first part of the resource type
provider = ctx.STRING(0).text.removeSurrounding("\"")
.substringBefore("_")
}
}
9 changes: 9 additions & 0 deletions src/main/java/io/codeka/gaia/modules/bo/TerraformModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ public class TerraformModule {

private RegistryDetails registryDetails;

private String mainProvider;

public String getId() {
return id;
}
Expand Down Expand Up @@ -152,4 +154,11 @@ public void setModuleMetadata(ModuleMetadata moduleMetadata) {
this.moduleMetadata = moduleMetadata;
}

public String getMainProvider() {
return mainProvider;
}

public void setMainProvider(String mainProvider) {
this.mainProvider = mainProvider;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ class RegistryServiceImpl(
val variablesFile = registryApis[registryType]?.getFileContent(user, projectId, "variables.tf")!!
module.variables = hclParser.parseVariables(variablesFile)

// find main provider
val mainFile = registryApis[registryType]?.getFileContent(user, projectId, "main.tf")!!
module.mainProvider = hclParser.parseProvider(mainFile)

// saving module !
return moduleRepository.save(module)
}
Expand Down
File renamed without changes
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 1 addition & 4 deletions src/main/resources/templates/module_description.html
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
<div class="col-md-8">
<div class="row">
<div class="col-md-2">
<img :src="'/images/' + mainProvider + '.png'" :alt="mainProvider"/>
<img :src="'/images/providers/' + mainProvider + '.png'" :alt="mainProvider"/>
</div>
<div class="col-md-10">
<h1>
Expand Down Expand Up @@ -107,9 +107,6 @@ <h1>

$.get(`/api/modules/${moduleId}`)
.then(data => {
// START FIXME: link with real data once implemented in the model
data.mainProvider = 'aws';
// END FIXME
new Vue({
el: "#app",
data,
Expand Down
98 changes: 0 additions & 98 deletions src/test/java/io/codeka/gaia/hcl/HCLParserTest.java

This file was deleted.

140 changes: 140 additions & 0 deletions src/test/java/io/codeka/gaia/hcl/HCLParserTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
package io.codeka.gaia.hcl

import io.codeka.gaia.modules.bo.Output
import io.codeka.gaia.modules.bo.Variable
import org.apache.commons.io.IOUtils
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test
import org.springframework.core.io.ClassPathResource
import java.io.IOException
import java.nio.charset.Charset

class HCLParserTest {

private val hclParser = HclParserImpl()

@Test
@Throws(IOException::class)
fun parsing_variables_shouldWorkWithVisitor() {
// given
val fileContent = IOUtils.toString(ClassPathResource("hcl/variables.tf").url, Charset.defaultCharset())

// when
val variables = hclParser.parseVariables(fileContent)

// then
assertThat(variables).hasSize(3)
val stringVar = Variable("string_var", "string", "a test string var", "foo")
val numberVar = Variable("number_var", "number", "a test number var", "42")
val boolVar = Variable("bool_var", defaultValue = "false")
assertThat(variables).contains(stringVar, numberVar, boolVar)
}

@Test
@Throws(IOException::class)
fun parsing_variables_shouldWork_withComplexFile() {
// given
val fileContent = IOUtils.toString(ClassPathResource("hcl/variables_aws_eks.tf").url, Charset.defaultCharset())

// when
val variables = hclParser.parseVariables(fileContent)

// then
assertThat(variables).hasSize(49)
}

@Test
@Throws(IOException::class)
fun parsing_variables_shouldWork_withAnotherComplexFile() {
// given
val fileContent = IOUtils.toString(ClassPathResource("hcl/variables_aws_vpc.tf").url, Charset.defaultCharset())

// when
val variables = hclParser.parseVariables(fileContent)

// then
assertThat(variables).hasSize(282)
}

@Test
@Throws(IOException::class)
fun parsing_outputs_shouldWorkWithVisitor() {
// given
val fileContent = IOUtils.toString(ClassPathResource("hcl/outputs.tf").url, Charset.defaultCharset())

// when
val outputs = hclParser.parseOutputs(fileContent)

// then
assertThat(outputs).hasSize(2)
val output1 = Output("instance_ip_addr", "\${aws_instance.server.private_ip}", "The private IP address of the main server instance.", "false")
val output2 = Output("db_password", "aws_db_instance.db[1].password", "The password for logging in to the database.", "true")
assertThat(outputs).contains(output1, output2)
}

@Test
@Throws(IOException::class)
fun parsing_outputs_shouldWork_withComplexFile() {
// given
val fileContent = IOUtils.toString(ClassPathResource("hcl/outputs_aws_eks.tf").url, Charset.defaultCharset())

// when
val outputs = hclParser.parseOutputs(fileContent)

// then
assertThat(outputs).hasSize(27)
}

/**
* Tests for the provider part
*/
@Nested
inner class ProviderTest {

@Test
@Throws(IOException::class)
fun parsing_provider_shouldWork_withMainFile_includingProviderDirective() {
// given
val fileContent = IOUtils.toString(ClassPathResource("hcl/terraform_docker_mongo_main_with_provider.tf").url, Charset.defaultCharset())
// when
val provider: String = hclParser.parseProvider(fileContent)
// then
assertThat(provider).isEqualTo("docker")
}

@Test
@Throws(IOException::class)
fun parsing_provider_shouldWork_withMainFile_withoutProviderDirective() {
// given
val fileContent = IOUtils.toString(ClassPathResource("hcl/terraform_docker_mongo_main_without_provider.tf").url, Charset.defaultCharset())

// when
val provider: String = hclParser.parseProvider(fileContent)

// then
assertThat(provider).isEqualTo("docker")
}

@Test
@Throws(IOException::class)
fun parsing_provider_shouldReturn_unknown_ifNoProviderFound() {
// given
val fileContent = IOUtils.toString(ClassPathResource("hcl/variables.tf").url, Charset.defaultCharset())

// when
val provider: String = hclParser.parseProvider(fileContent)

// then
assertThat(provider).isEqualTo("unknown")
}

@Test
fun dummyProvidersShouldBeIgnored() {
assertThat(hclParser.parseProvider("""provider "null" {} """)).isEqualTo("unknown")
assertThat(hclParser.parseProvider("""provider "template" {} """)).isEqualTo("unknown")
assertThat(hclParser.parseProvider("""provider "random" {} """)).isEqualTo("unknown")
assertThat(hclParser.parseProvider("""provider "terraform" {} """)).isEqualTo("unknown")
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,10 @@ class GithubRegistryControllerIT{
.andExpect(MockRestRequestMatchers.header("Authorization", "Bearer Tok'ra"))
.andRespond(MockRestResponseCreators.withSuccess(ClassPathResource("/rest/github/selmak-terraform-docker-mongo-content-variables.json"), MediaType.APPLICATION_JSON))

server.expect(requestTo("https://api.github.com/repos/selmak/terraform-docker-mongo/contents/main.tf?ref=master"))
.andExpect(MockRestRequestMatchers.header("Authorization", "Bearer Tok'ra"))
.andRespond(MockRestResponseCreators.withSuccess(ClassPathResource("/rest/github/selmak-terraform-docker-mongo-content-main.json"), MediaType.APPLICATION_JSON))

val selmak = User("Selmak", null)
selmak.oAuth2User = OAuth2User("GITHUB", "Tok'ra", null)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,10 @@ class GitlabRegistryControllerIT{
.andExpect(MockRestRequestMatchers.header("Authorization", "Bearer Tok'ra"))
.andRespond(MockRestResponseCreators.withSuccess(ClassPathResource("/rest/gitlab/selmak-terraform-docker-mongo-content-variables.json"), MediaType.APPLICATION_JSON))

server.expect(requestTo("https://gitlab.com/api/v4/projects/16181047/repository/files/main.tf?ref=master"))
.andExpect(MockRestRequestMatchers.header("Authorization", "Bearer Tok'ra"))
.andRespond(MockRestResponseCreators.withSuccess(ClassPathResource("/rest/gitlab/selmak-terraform-docker-mongo-content-main.json"), MediaType.APPLICATION_JSON))

val selmak = User("Selmak", null)
selmak.oAuth2User = OAuth2User("GITLAB", "Tok'ra", null)

Expand Down

0 comments on commit 39820a2

Please sign in to comment.