Repository: chanzuckerberg/fogg
Pull request #440 (2020-03-27 22:02:14)

[feature] allow setting provider versions for datadog and heroku
This allows pinning versions of the datadog and heroku providers.


### Test Plan
* CI

### References
*


Patch:
diff --git a/config/v2/config.go b/config/v2/config.go
index d1e7e6eb..90a6a7af 100644
--- a/config/v2/config.go
+++ b/config/v2/config.go
@@ -146,9 +146,13 @@ type SnowflakeProvider struct {
 	Version *string `json:"version,omitempty" yaml:"version,omitempty"`
 }
 
-type HerokuProvider struct{}
+type HerokuProvider struct {
+	Version *string `yaml:"version,omitempty"`
+}
 
-type DatadogProvider struct{}
+type DatadogProvider struct {
+	Version *string `yaml:"version,omitempty"`
+}
 
 //Backend is used to configure a terraform backend
 type Backend struct {
diff --git a/config/v2/resolvers.go b/config/v2/resolvers.go
index 0e949394..64a09608 100644
--- a/config/v2/resolvers.go
+++ b/config/v2/resolvers.go
@@ -220,7 +220,15 @@ func ResolveHerokuProvider(commons ...Common) *HerokuProvider {
 		p = c.Providers.Heroku
 	}
 
-	return p
+	version := lastNonNil(HerokuProviderVersionGetter, commons...)
+
+	if version != nil {
+		return &HerokuProvider{
+			Version: version,
+		}
+	} else {
+		return p
+	}
 }
 
 func ResolveDatadogProvider(commons ...Common) *DatadogProvider {
@@ -232,7 +240,15 @@ func ResolveDatadogProvider(commons ...Common) *DatadogProvider {
 		p = c.Providers.Datadog
 	}
 
-	return p
+	version := lastNonNil(DatadogProviderVersionGetter, commons...)
+
+	if version != nil {
+		return &DatadogProvider{
+			Version: version,
+		}
+	} else {
+		return p
+	}
 }
 
 func ResolveTfLint(commons ...Common) TfLint {
@@ -543,6 +559,20 @@ func BlessProviderAdditionalRegionsGetter(comm Common) []string {
 	return comm.Providers.Bless.AdditionalRegions
 }
 
+func HerokuProviderVersionGetter(comm Common) *string {
+	if comm.Providers == nil || comm.Providers.Heroku == nil {
+		return nil
+	}
+	return comm.Providers.Heroku.Version
+}
+
+func DatadogProviderVersionGetter(comm Common) *string {
+	if comm.Providers == nil || comm.Providers.Datadog == nil {
+		return nil
+	}
+	return comm.Providers.Datadog.Version
+}
+
 func OktaProviderVersionGetter(comm Common) *string {
 	if comm.Providers == nil || comm.Providers.Okta == nil {
 		return nil
diff --git a/plan/plan.go b/plan/plan.go
index 7ac0cffc..2b223216 100644
--- a/plan/plan.go
+++ b/plan/plan.go
@@ -158,9 +158,13 @@ type BlessProvider struct {
 	Version           *string  `json:"version,omitempty" yaml:"version,omitempty"`
 }
 
-type HerokuProvider struct{}
+type HerokuProvider struct {
+	Version *string `yaml:"version,omitempty"`
+}
 
-type DatadogProvider struct{}
+type DatadogProvider struct {
+	Version *string `yaml:"version,omitempty"`
+}
 
 // BackendKind is a enum of backends we support
 type BackendKind string
@@ -479,13 +483,17 @@ func resolveComponentCommon(commons ...v2.Common) ComponentCommon {
 	var herokuPlan *HerokuProvider
 	herokuConfig := v2.ResolveHerokuProvider(commons...)
 	if herokuConfig != nil {
-		herokuPlan = &HerokuProvider{}
+		herokuPlan = &HerokuProvider{
+			Version: herokuConfig.Version,
+		}
 	}
 
 	var datadogPlan *DatadogProvider
 	datadogConfig := v2.ResolveDatadogProvider(commons...)
 	if datadogConfig != nil {
-		datadogPlan = &DatadogProvider{}
+		datadogPlan = &DatadogProvider{
+			Version: datadogConfig.Version,
+		}
 	}
 
 	tflintConfig := v2.ResolveTfLint(commons...)
diff --git a/templates/common/datadog_provider.tmpl b/templates/common/datadog_provider.tmpl
index f12d2826..47230b95 100644
--- a/templates/common/datadog_provider.tmpl
+++ b/templates/common/datadog_provider.tmpl
@@ -1,4 +1,7 @@
 {{define "datadog_provider"}}
 provider datadog {
+{{ if .Version -}}
+  version = "~>{{ .Version }}"
+{{ end -}}
 }
 {{ end }}
diff --git a/templates/common/heroku_provider.tmpl b/templates/common/heroku_provider.tmpl
index ebae2273..f9625a1b 100644
--- a/templates/common/heroku_provider.tmpl
+++ b/templates/common/heroku_provider.tmpl
@@ -1,4 +1,6 @@
 {{define "heroku_provider"}}
 provider heroku {
-}
+{{ if .Version -}}
+  version = "~>{{ .Version }}"
+{{ end -}}
 {{ end }}


Test patch:
diff --git a/testdata/v2_full_yaml/terraform/envs/prod/hero/fogg.tf b/testdata/v2_full_yaml/terraform/envs/prod/hero/fogg.tf
index 4f756824..f5dfd8dc 100644
--- a/testdata/v2_full_yaml/terraform/envs/prod/hero/fogg.tf
+++ b/testdata/v2_full_yaml/terraform/envs/prod/hero/fogg.tf
@@ -26,192 +26,191 @@ provider aws {
 
 
 provider heroku {
-}
 
 
 
 
 
-terraform {
-  required_version = "~>0.100.0"
+  terraform {
+    required_version = "~>0.100.0"
 
 
-  backend s3 {
+    backend s3 {
 
-    bucket = "buck"
+      bucket = "buck"
 
-    key     = "terraform/proj/envs/prod/components/hero.tfstate"
-    encrypt = true
-    region  = "us-west-2"
-    profile = "profile"
+      key     = "terraform/proj/envs/prod/components/hero.tfstate"
+      encrypt = true
+      region  = "us-west-2"
+      profile = "profile"
 
-  }
+    }
 
-}
+  }
 
-variable env {
-  type    = string
-  default = "prod"
-}
+  variable env {
+    type    = string
+    default = "prod"
+  }
 
-variable project {
-  type    = string
-  default = "proj"
-}
+  variable project {
+    type    = string
+    default = "proj"
+  }
 
 
-variable region {
-  type    = string
-  default = "us-west-2"
-}
+  variable region {
+    type    = string
+    default = "us-west-2"
+  }
 
 
-variable component {
-  type    = string
-  default = "hero"
-}
+  variable component {
+    type    = string
+    default = "hero"
+  }
 
 
-variable aws_profile {
-  type    = string
-  default = "profile"
-}
+  variable aws_profile {
+    type    = string
+    default = "profile"
+  }
 
 
 
-variable owner {
-  type    = string
-  default = "foo@example.com"
-}
+  variable owner {
+    type    = string
+    default = "foo@example.com"
+  }
 
-variable tags {
-  type = map(string)
-  default = {
-    project   = "proj"
-    env       = "prod"
-    service   = "hero"
-    owner     = "foo@example.com"
-    managedBy = "terraform"
+  variable tags {
+    type = map(string)
+    default = {
+      project   = "proj"
+      env       = "prod"
+      service   = "hero"
+      owner     = "foo@example.com"
+      managedBy = "terraform"
+    }
   }
-}
 
 
-variable foo {
-  type    = string
-  default = "bar1"
-}
+  variable foo {
+    type    = string
+    default = "bar1"
+  }
 
 
-data terraform_remote_state global {
-  backend = "s3"
+  data terraform_remote_state global {
+    backend = "s3"
 
-  config = {
+    config = {
 
 
-    bucket = "buck"
+      bucket = "buck"
 
-    key     = "terraform/proj/global.tfstate"
-    region  = "us-west-2"
-    profile = "profile"
+      key     = "terraform/proj/global.tfstate"
+      region  = "us-west-2"
+      profile = "profile"
 
 
+    }
   }
-}
 
 
 
 
-data terraform_remote_state datadog {
-  backend = "s3"
+  data terraform_remote_state datadog {
+    backend = "s3"
 
-  config = {
+    config = {
 
 
-    bucket = "buck"
+      bucket = "buck"
 
-    key     = "terraform/proj/envs/prod/components/datadog.tfstate"
-    region  = "us-west-2"
-    profile = "profile"
+      key     = "terraform/proj/envs/prod/components/datadog.tfstate"
+      region  = "us-west-2"
+      profile = "profile"
 
 
+    }
   }
-}
 
 
 
 
 
-# remote state for accounts
+  # remote state for accounts
 
-data terraform_remote_state bar {
-  backend = "s3"
+  data terraform_remote_state bar {
+    backend = "s3"
 
-  config = {
+    config = {
 
 
-    bucket = "buck"
+      bucket = "buck"
 
-    key     = "terraform/proj/accounts/bar.tfstate"
-    region  = "us-west-2"
-    profile = "profile"
+      key     = "terraform/proj/accounts/bar.tfstate"
+      region  = "us-west-2"
+      profile = "profile"
 
 
+    }
   }
-}
 
-data terraform_remote_state foo {
-  backend = "s3"
+  data terraform_remote_state foo {
+    backend = "s3"
 
-  config = {
+    config = {
 
 
-    bucket = "buck"
+      bucket = "buck"
 
-    key     = "terraform/proj/accounts/foo.tfstate"
-    region  = "us-west-2"
-    profile = "profile"
+      key     = "terraform/proj/accounts/foo.tfstate"
+      region  = "us-west-2"
+      profile = "profile"
 
 
+    }
   }
-}
 
 
-# map of aws_accounts
-variable aws_accounts {
-  type = map
-  default = {
+  # map of aws_accounts
+  variable aws_accounts {
+    type = map
+    default = {
 
 
-    bar = 456
+      bar = 456
 
 
 
-    foo = 123
+      foo = 123
 
 
+    }
   }
-}
 
-provider random {
-  version = "~> 2.2"
-}
+  provider random {
+    version = "~> 2.2"
+  }
 
-provider template {
-  version = "~> 2.1"
-}
+  provider template {
+    version = "~> 2.1"
+  }
 
-provider archive {
-  version = "~> 1.3"
-}
+  provider archive {
+    version = "~> 1.3"
+  }
 
-provider null {
-  version = "~> 2.1"
-}
+  provider null {
+    version = "~> 2.1"
+  }
 
-provider local {
-  version = "~> 1.4"
-}
+  provider local {
+    version = "~> 1.4"
+  }
 
-provider tls {
-  version = "~> 2.1"
-}
+  provider tls {
+    version = "~> 2.1"
+  }