Skip to content

Commit

Permalink
Merge pull request #35 from env0/fix-support-gcp-container-cluster-re…
Browse files Browse the repository at this point in the history
…source-type

Fix: support gcp container cluster resource type
  • Loading branch information
yaronya authored Jul 7, 2020
2 parents 35dd40c + 61adbf1 commit b4c97d0
Show file tree
Hide file tree
Showing 14 changed files with 513 additions and 72 deletions.
15 changes: 6 additions & 9 deletions convert/convert.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package convert
import (
"encoding/json"
"github.com/env0/terratag/errors"
"github.com/env0/terratag/providers"
"github.com/env0/terratag/tag_keys"
"github.com/env0/terratag/utils"
"github.com/hashicorp/hcl/v2/hclsyntax"
Expand Down Expand Up @@ -127,34 +126,32 @@ func UnquoteTagsAttribute(swappedTagsStrings []string, text string) string {
return text
}

func MoveExistingTags(filename string, terratag TerratagLocal, resource *hclwrite.Block) bool {
func MoveExistingTags(filename string, terratag TerratagLocal, block *hclwrite.Block, tagId string) bool {
var existingTags hclwrite.Tokens

tagBlockId := providers.GetTagBlockIdByResource(*resource)

// First we try to find tags as attribute
tagsAttribute := resource.Body().GetAttribute(tagBlockId)
tagsAttribute := block.Body().GetAttribute(tagId)

if tagsAttribute != nil {
// If attribute found, get its value
log.Print("Preexisting tags ATTRIBUTE found on resource. Merging.")
log.Print("Pre-existing " + tagId + " ATTRIBUTE found on resource. Merging.")
existingTags = quoteAttributeKeys(tagsAttribute)
} else {
// Otherwise, we try to get tags as block
tagsBlock := resource.Body().FirstMatchingBlock(tagBlockId, nil)
tagsBlock := block.Body().FirstMatchingBlock(tagId, nil)
if tagsBlock != nil {
quotedTagBlock := quoteBlockKeys(tagsBlock)
existingTags = funk.Tail(quotedTagBlock.BuildTokens(hclwrite.Tokens{})).(hclwrite.Tokens)
// If we did get tags from block, we will now remove that block, as we're going to add a merged tags ATTRIBUTE
removeBlockResult := resource.Body().RemoveBlock(tagsBlock)
removeBlockResult := block.Body().RemoveBlock(tagsBlock)
if removeBlockResult == false {
log.Fatal("Failed to remove found tags block!")
}
}
}

if existingTags != nil {
terratag.Found[tag_keys.GetResourceExistingTagsKey(filename, resource)] = existingTags
terratag.Found[tag_keys.GetResourceExistingTagsKey(filename, block)] = existingTags
return true
}
return false
Expand Down
59 changes: 17 additions & 42 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,11 @@ import (
. "github.com/env0/terratag/errors"
"github.com/env0/terratag/file"
. "github.com/env0/terratag/providers"
"github.com/env0/terratag/tag_keys"
"github.com/env0/terratag/tagging"
. "github.com/env0/terratag/terraform"
. "github.com/env0/terratag/tfschema"
"github.com/env0/terratag/utils"
"github.com/hashicorp/hcl/v2/hclwrite"
"github.com/zclconf/go-cty/cty"
"log"
"strings"
)
Expand Down Expand Up @@ -47,41 +46,38 @@ func tagDirectoryResources(dir string, matches []string, tags string, isSkipTerr

func tagFileResources(path string, dir string, tags string, tfVersion int) {
log.Print("Processing file ", path)
hcl := file.ReadHCLFile(path)
var swappedTagsStrings []string

hcl := file.ReadHCLFile(path)
filename := file.GetFilename(path)
terratag := convert.TerratagLocal{
Found: map[string]hclwrite.Tokens{},
Added: jsonToHclMap(tags),
}

filename := file.GetFilename(path)

anyTagged := false
var swappedTagsStrings []string

for _, resource := range hcl.Body().Blocks() {
if resource.Type() == "resource" {
log.Print("Processing resource ", resource.Labels())

isTaggable, isTaggableViaSpecialTagBlock := IsTaggable(dir, *resource)

if isTaggable {
if !isTaggableViaSpecialTagBlock {
// for now, we count on it that if there's a single "tag" in the schema (unlike "tags" block),
// then no "tags" interpolation is used, but rather multiple instances of a "tag" block
// https://www.terraform.io/docs/providers/aws/r/autoscaling_group.html
swappedTagsStrings = append(swappedTagsStrings, tagResource(filename, terratag, resource, tfVersion))
} else {
convert.AppendTagBlocks(resource, tags)
}
anyTagged = true
if IsTaggable(dir, *resource) {
log.Print("Resource taggable, processing...")
result := tagging.TagResource(tagging.TagBlockArgs{
Filename: filename,
Block: resource,
Tags: tags,
Terratag: terratag,
TagId: GetTagIdByResource(GetResourceType(*resource)),
TfVersion: tfVersion,
})

swappedTagsStrings = append(swappedTagsStrings, result.SwappedTagsStrings...)
} else {
log.Print("Resource not taggable, skipping. ")
}
}
}

if anyTagged {
if len(swappedTagsStrings) > 0 {
convert.AppendLocalsBlock(hcl, filename, terratag)

text := string(hcl.Bytes())
Expand All @@ -95,27 +91,6 @@ func tagFileResources(path string, dir string, tags string, tfVersion int) {
}
}

func tagResource(filename string, terratag convert.TerratagLocal, resource *hclwrite.Block, tfVersion int) string {
log.Print("Resource taggable, processing...")

hasExistingTags := convert.MoveExistingTags(filename, terratag, resource)

tagsValue := ""
if hasExistingTags {
tagsValue = "merge( " + convert.GetExistingTagsExpression(terratag.Found[tag_keys.GetResourceExistingTagsKey(filename, resource)]) + ", local." + tag_keys.GetTerratagAddedKey(filename) + ")"
} else {
tagsValue = "local." + tag_keys.GetTerratagAddedKey(filename)
}

if tfVersion == 11 {
tagsValue = "${" + tagsValue + "}"
}

resource.Body().SetAttributeValue(GetTagBlockIdByResource(*resource), cty.StringVal(tagsValue))

return tagsValue
}

func jsonToHclMap(tags string) string {
var tagsMap map[string]string
err := json.Unmarshal([]byte(tags), &tagsMap)
Expand Down
19 changes: 8 additions & 11 deletions providers/providers.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
package providers

import (
"github.com/hashicorp/hcl/v2/hclwrite"
"strings"
)

func getProviderByResource(resource hclwrite.Block) Provider {
resourceType := resource.Labels()[0]

func getProviderByResource(resourceType string) Provider {
if strings.HasPrefix(resourceType, "aws_") {
return "aws"
} else if strings.HasPrefix(resourceType, "google_") {
Expand All @@ -19,18 +16,18 @@ func getProviderByResource(resource hclwrite.Block) Provider {
return ""
}

func IsTaggableByAttribute(resource hclwrite.Block, attribute string) bool {
provider := getProviderByResource(resource)
tagBlockId := GetTagBlockIdByResource(resource)
func IsTaggableByAttribute(resourceType string, attribute string) bool {
provider := getProviderByResource(resourceType)
tagBlockId := GetTagIdByResource(resourceType)

if (provider != "") && attribute == tagBlockId {
return true
}
return false
}

func GetTagBlockIdByResource(resource hclwrite.Block) string {
provider := getProviderByResource(resource)
func GetTagIdByResource(resourceType string) string {
provider := getProviderByResource(resourceType)

if provider == "aws" || provider == "azure" {
return "tags"
Expand All @@ -41,8 +38,8 @@ func GetTagBlockIdByResource(resource hclwrite.Block) string {
return ""
}

func IsTaggableResource(resource hclwrite.Block) bool {
return isSupportedProvider(getProviderByResource(resource))
func IsSupportedResource(resourceType string) bool {
return isSupportedProvider(getProviderByResource(resourceType))
}

func isSupportedProvider(provider Provider) bool {
Expand Down
14 changes: 14 additions & 0 deletions tagging/aws.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package tagging

import (
"github.com/env0/terratag/convert"
)

func tagAutoscalingGroup(args TagBlockArgs) Result {
// for now, we count on it that if there's a single "tag" in the schema (unlike "tags" block),
// then no "tags" interpolation is used, but rather multiple instances of a "tag" block
// https://www.terraform.io/docs/providers/aws/r/autoscaling_group.html
convert.AppendTagBlocks(args.Block, args.Tags)

return Result{}
}
30 changes: 30 additions & 0 deletions tagging/gcp.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package tagging

func tagNodeConfigBlocks(args TagBlockArgs) []string {
var swappedTagsStrings []string

for _, block := range args.Block.Body().Blocks() {
blockArgs := args
blockArgs.Block = block

if block.Type() == "node_config" {
swappedTagsStrings = append(swappedTagsStrings, TagBlock(blockArgs))
} else {
swappedTagsStrings = append(swappedTagsStrings, tagNodeConfigBlocks(blockArgs)...)
}
}

return swappedTagsStrings
}

func tagContainerCluster(args TagBlockArgs) Result {
rootBlockArgs := args
rootBlockArgs.TagId = "resource_labels"
rootBlockSwappedTagsStrings := []string{TagBlock(rootBlockArgs)}

return Result{SwappedTagsStrings: append(rootBlockSwappedTagsStrings, tagNodeConfigBlocks(args)...)}
}

func tagContainerNodePool(args TagBlockArgs) Result {
return Result{SwappedTagsStrings: tagNodeConfigBlocks(args)}
}
74 changes: 74 additions & 0 deletions tagging/tagging.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package tagging

import (
"github.com/env0/terratag/convert"
"github.com/env0/terratag/tag_keys"
"github.com/env0/terratag/terraform"
"github.com/hashicorp/hcl/v2/hclwrite"
"github.com/zclconf/go-cty/cty"
)

func defaultTaggingFn(args TagBlockArgs) Result {
return Result{
SwappedTagsStrings: []string{TagBlock(args)},
}
}

func TagBlock(args TagBlockArgs) string {
hasExistingTags := convert.MoveExistingTags(args.Filename, args.Terratag, args.Block, args.TagId)

tagsValue := ""
if hasExistingTags {
tagsValue = "merge( " + convert.GetExistingTagsExpression(args.Terratag.Found[tag_keys.GetResourceExistingTagsKey(args.Filename, args.Block)]) + ", local." + tag_keys.GetTerratagAddedKey(args.Filename) + ")"
} else {
tagsValue = "local." + tag_keys.GetTerratagAddedKey(args.Filename)
}

if args.TfVersion == 11 {
tagsValue = "${" + tagsValue + "}"
}

args.Block.Body().SetAttributeValue(args.TagId, cty.StringVal(tagsValue))

return tagsValue
}

func HasResourceTagFn(resourceType string) bool {
return resourceTypeToFnMap[resourceType] != nil
}

func TagResource(args TagBlockArgs) Result {
var result Result
resourceType := terraform.GetResourceType(*args.Block)

customTaggingFn := resourceTypeToFnMap[resourceType]

if customTaggingFn != nil {
result = customTaggingFn(args)
} else {
result = defaultTaggingFn(args)
}

return result
}

var resourceTypeToFnMap = map[string]TagResourceFn{
"aws_autoscaling_group": tagAutoscalingGroup,
"google_container_cluster": tagContainerCluster,
"google_container_node_pool": tagContainerNodePool,
}

type TagBlockArgs struct {
Filename string
Block *hclwrite.Block
Tags string
Terratag convert.TerratagLocal
TagId string
TfVersion int
}

type TagResourceFn func(args TagBlockArgs) Result

type Result struct {
SwappedTagsStrings []string
}
5 changes: 5 additions & 0 deletions terraform/terraform.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"encoding/json"
"github.com/bmatcuk/doublestar"
"github.com/env0/terratag/errors"
"github.com/hashicorp/hcl/v2/hclwrite"
"github.com/thoas/go-funk"
"io/ioutil"
"log"
Expand All @@ -28,6 +29,10 @@ func GetTerraformVersion() int {
return -1
}

func GetResourceType(resource hclwrite.Block) string {
return resource.Labels()[0]
}

func IsTerraformInitRun(dir string) bool {
_, err := os.Stat(dir + "/.terraform")

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
resource "google_container_cluster" "no-labels-cluster" {
name = "cluster"
location = "us-central1"

node_config {
machine_type = "n1-standard-1"
labels = "${local.terratag_added_main}"
}

node_pool {
node_config {
machine_type = "n1-standard-1"
labels = "${local.terratag_added_main}"
}
}
resource_labels = "${local.terratag_added_main}"
}

resource "google_container_node_pool" "no-labels-pool" {
cluster = "${google_container_cluster.no-labels-cluster.name}"

node_config {
machine_type = "n1-standard-1"
labels = "${local.terratag_added_main}"
}
}

resource "google_container_cluster" "existing-labels-cluster" {
name = "cluster2"
location = "us-central1"

resource_labels = "${merge( map("foo" , "bar"), local.terratag_added_main)}"

node_config {
machine_type = "n1-standard-1"
labels = "${merge( map("foo" , "bar"), local.terratag_added_main)}"
}

node_pool {
node_config {
machine_type = "n1-standard-1"
labels = "${merge( map("foo" , "bar"), local.terratag_added_main)}"
}
}
}

resource "google_container_node_pool" "existing-labels-pool" {
cluster = "${google_container_cluster.existing-labels-cluster.name}"

node_config {
machine_type = "n1-standard-1"
labels = "${merge( map("foo" , "bar"), local.terratag_added_main)}"
}
}
locals {
terratag_added_main = {"env0_environment_id"="40907eff-cf7c-419a-8694-e1c6bf1d1168","env0_project_id"="43fd4ff1-8d37-4d9d-ac97-295bd850bf94"}
}

Loading

0 comments on commit b4c97d0

Please sign in to comment.