Skip to content

Commit

Permalink
providers: ignore "constraint providers"
Browse files Browse the repository at this point in the history
Certain providers of Terraform are meant to specify constraint rather
than locations/regions, etc. In cases where a provider doesn't contain a
valid location, it will not be added to the list of valid providers, if
no providers at all exist an error will be returned mentioning it.
  • Loading branch information
Hugo Rosnet committed Sep 2, 2021
1 parent ab95584 commit b650e5c
Show file tree
Hide file tree
Showing 8 changed files with 242 additions and 9 deletions.
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
## [Unreleased]

### Fixed

- Ignore providers related to constraints
([Pull #58](https://github.com/cycloidio/terracost/pull/58))
- Unexpected error when using supported/unsupported providers
([Pull #56](https://github.com/cycloidio/terracost/pull/56))

## [0.4.3] _2021-08-30_

### Changed

- Improved error returned when using unknown providers, empty terraform
([Pull #55](https://github.com/cycloidio/terracost/pull/55))

## [0.4.2] _2021-07-22_

### Fixed
Expand Down
6 changes: 5 additions & 1 deletion aws/terraform_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@ const RegistryName = "registry.terraform.io/hashicorp/aws"
var TerraformProviderInitializer = terraform.ProviderInitializer{
MatchNames: []string{ProviderName, RegistryName},
Provider: func(values map[string]string) (terraform.Provider, error) {
regCode := region.Code(values["region"])
r, ok := values["region"]
if !ok {
return nil, nil
}
regCode := region.Code(r)
return awstf.NewProvider(ProviderName, regCode)
},
}
21 changes: 19 additions & 2 deletions e2e/aws_estimation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,11 @@ import (
var terraformAWSTestProviderInitializer = terraform.ProviderInitializer{
MatchNames: []string{"aws", "aws-test"},
Provider: func(config map[string]string) (terraform.Provider, error) {
regCode := region.Code(config["region"])
r, ok := config["region"]
if !ok {
return nil, nil
}
regCode := region.Code(r)
return awstf.NewProvider("aws-test", regCode)
},
}
Expand All @@ -41,7 +45,11 @@ var terraformAWSTestProviderInitializer = terraform.ProviderInitializer{
var terraformAWSProviderInitializer = terraform.ProviderInitializer{
MatchNames: []string{"aws"},
Provider: func(config map[string]string) (terraform.Provider, error) {
regCode := region.Code(config["region"])
r, ok := config["region"]
if !ok {
return nil, nil
}
regCode := region.Code(r)
return awstf.NewProvider("aws", regCode)
},
}
Expand Down Expand Up @@ -227,6 +235,15 @@ func TestAWSEstimation(t *testing.T) {
}
assert.Equal(t, expected, rd.Errors())
})
t.Run("NoProvider", func(t *testing.T) {
f, err := os.Open("../testdata/aws/terraform-plan-noprovider.json")
require.NoError(t, err)
defer f.Close()

plan, err := costestimation.EstimateTerraformPlan(ctx, backend, f, terraformAWSTestProviderInitializer)
require.Error(t, err, terraform.ErrNoProviders)
require.Nil(t, plan)
})
})
t.Run("HCL", func(t *testing.T) {
t.Run("Success", func(t *testing.T) {
Expand Down
1 change: 1 addition & 0 deletions terraform/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ import "errors"
var (
ErrNoQueries = errors.New("no terraform entities found, looks empty")
ErrNoKnownProvider = errors.New("terraform providers are not yet supported")
ErrNoProviders = errors.New("no valid providers found")
)
13 changes: 7 additions & 6 deletions terraform/plan.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,7 @@ func (p *Plan) ExtractPlannedQueries() ([]query.Resource, error) {
if err != nil {
return nil, fmt.Errorf("unable to extract planned queries: %w", err)
}
queries, err := p.extractQueries(p.PlannedValues, providers), nil
if err != nil {
return nil, err
}
return queries, nil
return p.extractQueries(p.PlannedValues, providers), nil
}

// ExtractPriorQueries extracts a query.Resource slice from the `prior_state` part of the Plan.
Expand Down Expand Up @@ -77,9 +73,14 @@ func (p *Plan) extractProviders() (map[string]Provider, error) {
if err != nil {
return nil, err
}
providers[name] = prov
if prov != nil {
providers[name] = prov
}
}
}
if len(providers) == 0 {
return nil, ErrNoProviders
}
return providers, nil
}

Expand Down
1 change: 1 addition & 0 deletions terraform/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ type ProviderInitializer struct {
MatchNames []string

// Provider initializes a Provider instance given the values defined in the config and returns it.
// If a provider must be ignored (related to version constraints, etc), please return nil to avoid using it.
Provider func(values map[string]string) (Provider, error)
}

Expand Down
190 changes: 190 additions & 0 deletions testdata/aws/terraform-plan-noprovider.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
{
"format_version": "0.1",
"terraform_version": "0.14.9",
"planned_values": {
"root_module": {
"child_modules": [
{
"resources": [
{
"address": "module.instance.aws_instance.example",
"mode": "managed",
"type": "aws_instance",
"name": "example",
"provider_name": "registry.terraform.io/hashicorp/aws",
"schema_version": 1,
"values": {
"ami": "ami-2757f631",
"availability_zone": "us-east-1e",
"instance_type": "t2.xlarge",
"root_block_device": [
{
"iops": 100,
"volume_size": 8,
"volume_type": "gp2"
}
],
"tenancy": "default"
}
}
],
"address": "module.instance"
}
],
"resources": [
{
"address": "aws_lb.example",
"mode": "managed",
"type": "aws_lb",
"name": "example",
"provider_name": "registry.terraform.io/hashicorp/aws",
"schema_version": 0,
"values": {
"load_balancer_type": "application"
}
}
]
}
},
"resource_changes": [
{
"address": "module.instance.aws_instance.example",
"module_address": "module.instance",
"mode": "managed",
"type": "aws_instance",
"name": "example",
"provider_name": "aws",
"change": {
"actions": [
"update"
],
"before": {
"ami": "ami-2757f631",
"availability_zone": "us-east-1e",
"instance_type": "t2.micro",
"root_block_device": [
{
"iops": 100,
"volume_size": 8,
"volume_type": "gp2"
}
],
"tenancy": "default"
},
"after": {
"ami": "ami-2757f631",
"availability_zone": "us-east-1e",
"instance_type": "t2.xlarge",
"root_block_device": [
{
"iops": 100,
"volume_size": 8,
"volume_type": "gp2"
}
],
"tenancy": "default"
},
"after_unknown": {}
}
},
{
"address": "aws_lb.example",
"mode": "managed",
"type": "aws_lb",
"name": "example",
"provider_name": "registry.terraform.io/hashicorp/aws",
"change": {
"actions": [
"create"
],
"before": null,
"after": {
"load_balancer_type": "application"
},
"after_unknown": {}
}
}
],
"prior_state": {
"format_version": "0.1",
"terraform_version": "0.12.28",
"values": {
"root_module": {
"child_modules": [
{
"resources": [
{
"address": "module.instance.aws_instance.example",
"mode": "managed",
"type": "aws_instance",
"name": "example",
"provider_name": "aws",
"schema_version": 1,
"values": {
"ami": "ami-2757f631",
"availability_zone": "us-east-1e",
"instance_type": "t2.micro",
"root_block_device": [
{
"iops": 100,
"volume_size": 8,
"volume_type": "gp2"
}
],
"tenancy": "default"
}
}
],
"address": "module.instance"
}
]
}
}
},
"configuration": {
"provider_config": {}
},
"root_module": {
"module_calls": {
"instance": {
"source": "./instance",
"module": {
"resources": [
{
"address": "aws_instance.example",
"mode": "managed",
"type": "aws_instance",
"name": "example",
"provider_config_key": "instance:aws",
"expressions": {
"ami": {
"constant_value": "ami-2757f631"
},
"instance_type": {
"constant_value": "t2.xlarge"
}
},
"schema_version": 1
}
]
}
}
},
"resources": [
{
"address": "aws_lb.example",
"mode": "managed",
"type": "aws_lb",
"name": "example",
"provider_config_key": "aws.paris",
"expressions": {
"load_balancer_type": {
"constant_value": "application"
}
},
"schema_version": 1
}
]
}
}
}
5 changes: 5 additions & 0 deletions testdata/aws/terraform-plan.json
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,11 @@
"constant_value": "eu-west-3"
}
}
},
"ignored": {
"name": "aws-constrain",
"version_constraint": ">= 1.1.1",
"module_address": "some-module"
}
},
"root_module": {
Expand Down

0 comments on commit b650e5c

Please sign in to comment.