Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: show a 100% cost increase with prior empty projects #2899

Merged
merged 1 commit into from
Feb 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
14 changes: 14 additions & 0 deletions cmd/infracost/diff_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -320,3 +320,17 @@ func TestDiffWithPolicyDataUpload(t *testing.T) {
},
)
}

func TestDiffPriorEmptyProject(t *testing.T) {
testName := testutil.CalcGoldenFileTestdataDirName()
dir := path.Join("./testdata", testName)
GoldenFileCommandTest(
t,
testutil.CalcGoldenFileTestdataDirName(), []string{
"diff",
"--compare-to",
path.Join(dir, "base.json"),
"--path",
dir,
}, nil)
}
5 changes: 2 additions & 3 deletions cmd/infracost/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -245,10 +245,9 @@ func (r *parallelRunner) run() ([]projectResult, error) {

if err != nil {
m := fmt.Sprintf("%s\n\n", err)
m += fmt.Sprintf("Try adding a config-file to configure how Infracost should run. See %s for details and examples.", ui.LinkString("https://infracost.io/config-file"))
m += fmt.Sprintf(" Try adding a config-file to configure how Infracost should run. See %s for details and examples.", ui.LinkString("https://infracost.io/config-file"))

err = clierror.NewCLIError(errors.New(m), "Could not detect path type")
queue = append(queue, projectJob{index: i, err: err, ctx: ctx})
queue = append(queue, projectJob{index: i, err: schema.NewEmptyPathTypeError(errors.New(m)), ctx: ctx})
continue
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ Project: infracost/infracost/cmd/infracost/invalid
Errors:
No such file or directory invalid

Try adding a config-file to configure how Infracost should run. See https://infracost.io/config-file for details and examples.
Try adding a config-file to configure how Infracost should run. See https://infracost.io/config-file for details and examples.

OVERALL TOTAL $0.00
──────────────────────────────────
Expand Down
72 changes: 72 additions & 0 deletions cmd/infracost/testdata/diff_prior_empty_project/base.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
{
"version": "0.2",
"metadata": {
"infracostCommand": "breakdown",
"vcsBranch": "master",
"vcsCommitSha": "71785af96bce822bd54f359d0883cfa4ca805432",
"vcsCommitAuthorName": "Hugo",
"vcsCommitAuthorEmail": "",
"vcsCommitTimestamp": "2024-02-23T11:04:26Z",
"vcsCommitMessage": "wip",
"vcsRepositoryUrl": "https://github.com/infracost/infracost.git"
},
"currency": "USD",
"projects": [
{
"name": "infracost/infracost/cmd/infracost/testdata/diff_prior_empty_project",
"metadata": {
"path": ".",
"type": "error",
"vcsSubPath": "cmd/infracost/testdata/diff_prior_empty_project",
"errors": [
{
"code": 106,
"message": "could not detect path type for '.'\n\nTry adding a config-file to configure how Infracost should run. See \u001b[4;1mhttps://infracost.io/config-file\u001b[0m for details and examples.",
"data": null,
"isError": true
}
]
},
"pastBreakdown": {
"resources": [],
"totalHourlyCost": "0",
"totalMonthlyCost": "0"
},
"breakdown": {
"resources": [],
"totalHourlyCost": "0",
"totalMonthlyCost": "0"
},
"diff": {
"resources": [],
"totalHourlyCost": "0",
"totalMonthlyCost": "0"
},
"summary": {
"totalDetectedResources": 0,
"totalSupportedResources": 0,
"totalUnsupportedResources": 0,
"totalUsageBasedResources": 0,
"totalNoPriceResources": 0,
"unsupportedResourceCounts": {},
"noPriceResourceCounts": {}
}
}
],
"totalHourlyCost": "0",
"totalMonthlyCost": "0",
"pastTotalHourlyCost": "0",
"pastTotalMonthlyCost": "0",
"diffTotalHourlyCost": "0",
"diffTotalMonthlyCost": "0",
"timeGenerated": "2024-02-23T12:58:15.71255Z",
"summary": {
"totalDetectedResources": 0,
"totalSupportedResources": 0,
"totalUnsupportedResources": 0,
"totalUsageBasedResources": 0,
"totalNoPriceResources": 0,
"unsupportedResourceCounts": {},
"noPriceResourceCounts": {}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
──────────────────────────────────
Project: infracost/infracost/cmd/infracost/testdata/diff_prior_empty_project

+ aws_instance.web_app
+$743

+ Instance usage (Linux/UNIX, on-demand, m5.4xlarge)
+$561

+ root_block_device

+ Storage (general purpose SSD, gp2)
+$5

+ ebs_block_device[0]

+ Storage (provisioned IOPS SSD, io1)
+$125

+ Provisioned IOPS
+$52

Monthly cost change for infracost/infracost/cmd/infracost/testdata/diff_prior_empty_project
Amount: +$743 ($0.00 → $743)

──────────────────────────────────
Key: ~ changed, + added, - removed

1 cloud resource was detected:
∙ 1 was estimated, it includes usage-based costs, see https://infracost.io/usage-file

Infracost estimate: Monthly cost will increase by $743 ↑
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━┓
┃ Project ┃ Cost change ┃ New monthly cost ┃
┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╋━━━━━━━━━━━━━╋━━━━━━━━━━━━━━━━━━┫
┃ infracost/infracost/cmd/infraco...tdata/diff_prior_empty_project ┃ +$743 ┃ $743 ┃
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┻━━━━━━━━━━━━━┻━━━━━━━━━━━━━━━━━━┛

Err:

23 changes: 23 additions & 0 deletions cmd/infracost/testdata/diff_prior_empty_project/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
provider "aws" {
region = "us-east-1" # <<<<< Try changing this to eu-west-1 to compare the costs
skip_credentials_validation = true
skip_requesting_account_id = true
access_key = "mock_access_key"
secret_key = "mock_secret_key"
}

resource "aws_instance" "web_app" {
ami = "ami-674cbc1e"
instance_type = "m5.4xlarge"

root_block_device {
volume_size = 50
}

ebs_block_device {
device_name = "my_data"
volume_type = "io1"
volume_size = 1000
iops = 800
}
}
2 changes: 1 addition & 1 deletion internal/output/combined.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ func CompareTo(c *config.Config, current, prior Root) (Root, error) {
scp.Diff = schema.CalculateDiff(scp.PastResources, scp.Resources)
}

if !p.Metadata.HasErrors() && v.Metadata.HasErrors() {
if !p.Metadata.HasErrors() && !v.Metadata.IsEmptyProjectError() && v.Metadata.HasErrors() {
// the prior project has errors, but the current one does not
// The prior errors will be copied over to the current, but we
// also need to remove the current project costs
Expand Down
2 changes: 1 addition & 1 deletion internal/output/diff.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ func ToDiff(out Root, opts Options) ([]byte, error) {

s += "──────────────────────────────────\n"
for _, project := range out.Projects {
if project.Metadata.HasErrors() {
if project.Metadata.HasErrors() && !project.Metadata.IsEmptyProjectError() {
erroredProjects = append(erroredProjects, project)
continue
}
Expand Down
15 changes: 15 additions & 0 deletions internal/schema/project.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const (
diagTerragruntEvaluationFailure = 103
diagTerragruntModuleEvaluationFailure = 104
diagMissingVars = 105
diagEmptyPathType = 106

// Diags for git module issues
diagPrivateModuleDownloadFailure = 201
Expand All @@ -44,6 +45,12 @@ type ProjectDiag struct {
FriendlyMessage string `json:"-"`
}

// NewEmptyPathTypeError returns a project diag to indicate that a path type
// cannot be detected.
func NewEmptyPathTypeError(err error) *ProjectDiag {
return newDiag(diagEmptyPathType, err.Error(), true, nil, err)
}

// NewDiagRunQuotaExceeded returns a project diag for a run quota exceeded error.
func NewDiagRunQuotaExceeded(err error) *ProjectDiag {
return &ProjectDiag{
Expand Down Expand Up @@ -208,6 +215,14 @@ func (m *ProjectMetadata) HasErrors() bool {
return len(m.Errors) > 0
}

func (m *ProjectMetadata) IsEmptyProjectError() bool {
if len(m.Errors) == 0 || len(m.Errors) > 1 {
return false
}

return m.Errors[0].Code == diagEmptyPathType
}

// IsRunQuotaExceeded checks if any of the project diags are of type "run quota
// exceeded". If found it returns the associated message with this diag. This
// should be used when in any output that notifies the user.
Expand Down