Skip to content

Commit 20a1464

Browse files
authored
aws_lambda_end_of_life (terraform-linters#154)
* aws_lambda_end_of_life - Added lambda End of Life rules * aws_lambda_end_of_life - Added end of support * Updated to have one rule called deprecated_runtime
1 parent 8047cf5 commit 20a1464

4 files changed

+228
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# aws_lambda_function_deprecated_runtime
2+
3+
Checks to see if a lambda function has been set with a runtime that is deprecated. This can show up as either "end of support" or "end of life" depending on the phase of deprecation it is currently in.
4+
5+
## Example
6+
7+
```hcl
8+
resource "aws_lambda_function" "function" {
9+
function_name = "test_function"
10+
role = "test_role"
11+
runtime = "python2.7"
12+
}
13+
```
14+
15+
16+
```
17+
$ tflint
18+
1 issue(s) found:
19+
20+
Error: The "python2.7" runtime has reached the end of support. (aws_lambda_function_deprecated_runtime)
21+
22+
on template.tf line 4:
23+
4: runtime = "python2.7" // end of support reached!
24+
25+
```
26+
27+
## Why
28+
29+
AWS no longer supports these runtimes.
30+
31+
## How To Fix
32+
33+
Update to a newer runtime. Supported runtimes can be found [here](https://docs.aws.amazon.com/lambda/latest/dg/runtime-support-policy.html)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
package rules
2+
3+
import (
4+
"fmt"
5+
hcl "github.com/hashicorp/hcl/v2"
6+
"github.com/terraform-linters/tflint-plugin-sdk/tflint"
7+
"github.com/terraform-linters/tflint-ruleset-aws/project"
8+
"time"
9+
)
10+
11+
// AwsLambdaFunctionDeprecatedRuntimeRule checks to see if the lambda runtime has reached End Of Support
12+
type AwsLambdaFunctionDeprecatedRuntimeRule struct {
13+
resourceType string
14+
attributeName string
15+
eosRuntimes map[string]time.Time
16+
eolRuntimes map[string]time.Time
17+
}
18+
19+
// NewAwsLambdaFunctionDeprecatedRuntimeRule returns new rule with default attributes
20+
func NewAwsLambdaFunctionDeprecatedRuntimeRule() *AwsLambdaFunctionDeprecatedRuntimeRule {
21+
return &AwsLambdaFunctionDeprecatedRuntimeRule{
22+
resourceType: "aws_lambda_function",
23+
attributeName: "runtime",
24+
eosRuntimes: map[string]time.Time{
25+
"nodejs10.x": time.Date(2021, time.July, 30, 0, 0, 0, 0, time.UTC),
26+
"ruby2.5": time.Date(2021, time.July, 30, 0, 0, 0, 0, time.UTC),
27+
"python2.7": time.Date(2021, time.July, 15, 0, 0, 0, 0, time.UTC),
28+
"dotnetcore2.1": time.Date(2021, time.September, 20, 0, 0, 0, 0, time.UTC),
29+
},
30+
eolRuntimes: map[string]time.Time{
31+
"dotnetcore1.0": time.Date(2019, time.July, 30, 0, 0, 0, 0, time.UTC),
32+
"dotnetcore2.0": time.Date(2019, time.May, 30, 0, 0, 0, 0, time.UTC),
33+
"nodejs": time.Date(2016, time.October, 31, 0, 0, 0, 0, time.UTC),
34+
"nodejs4.3": time.Date(2020, time.March, 06, 0, 0, 0, 0, time.UTC),
35+
"nodejs4.3-edge": time.Date(2019, time.April, 30, 0, 0, 0, 0, time.UTC),
36+
"nodejs6.10": time.Date(2019, time.August, 12, 0, 0, 0, 0, time.UTC),
37+
"nodejs8.10": time.Date(2020, time.March, 06, 0, 0, 0, 0, time.UTC),
38+
"nodejs10.x": time.Date(2021, time.August, 30, 0, 0, 0, 0, time.UTC),
39+
"ruby2.5": time.Date(2021, time.August, 30, 0, 0, 0, 0, time.UTC),
40+
"python2.7": time.Date(2021, time.September, 30, 0, 0, 0, 0, time.UTC),
41+
"dotnetcore2.1": time.Date(2021, time.October, 30, 0, 0, 0, 0, time.UTC),
42+
},
43+
}
44+
}
45+
46+
// Name returns the rule name
47+
func (r *AwsLambdaFunctionDeprecatedRuntimeRule) Name() string {
48+
return "aws_lambda_function_deprecated_runtime"
49+
}
50+
51+
// Enabled returns whether the rule is enabled by default
52+
func (r *AwsLambdaFunctionDeprecatedRuntimeRule) Enabled() bool {
53+
return true
54+
}
55+
56+
// Severity returns the rule severity
57+
func (r *AwsLambdaFunctionDeprecatedRuntimeRule) Severity() string {
58+
return tflint.WARNING
59+
}
60+
61+
// Link returns the rule reference link
62+
func (r *AwsLambdaFunctionDeprecatedRuntimeRule) Link() string {
63+
return project.ReferenceLink(r.Name())
64+
}
65+
66+
// Check checks if the chosen runtime has reached EOS. Date check allows future values to be created as well.
67+
func (r *AwsLambdaFunctionDeprecatedRuntimeRule) Check(runner tflint.Runner) error {
68+
69+
return runner.WalkResourceAttributes(r.resourceType, r.attributeName, func(attribute *hcl.Attribute) error {
70+
var val string
71+
err := runner.EvaluateExpr(attribute.Expr, &val, nil)
72+
now := time.Now().UTC()
73+
74+
return runner.EnsureNoError(err, func() error {
75+
if _, ok := r.eolRuntimes[val]; ok && now.After(r.eolRuntimes[val]) {
76+
runner.EmitIssueOnExpr(
77+
r,
78+
fmt.Sprintf("The \"%s\" runtime has reached the end of life", val),
79+
attribute.Expr,
80+
)
81+
} else if _, ok := r.eosRuntimes[val]; ok && now.After(r.eosRuntimes[val]) {
82+
runner.EmitIssueOnExpr(
83+
r,
84+
fmt.Sprintf("The \"%s\" runtime has reached the end of support", val),
85+
attribute.Expr,
86+
)
87+
}
88+
return nil
89+
})
90+
})
91+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
package rules
2+
3+
import (
4+
hcl "github.com/hashicorp/hcl/v2"
5+
"github.com/terraform-linters/tflint-plugin-sdk/helper"
6+
"testing"
7+
"time"
8+
)
9+
10+
func Test_AwsLambdaFunctionEndOfSupport(t *testing.T) {
11+
type caseStudy struct {
12+
Name string
13+
Content string
14+
Expected helper.Issues
15+
}
16+
eosRuntimes := map[string]time.Time{
17+
"nodejs10.x": time.Date(2021, time.July, 30, 0, 0, 0, 0, time.UTC),
18+
"ruby2.5": time.Date(2021, time.July, 30, 0, 0, 0, 0, time.UTC),
19+
"python2.7": time.Date(2021, time.July, 15, 0, 0, 0, 0, time.UTC),
20+
"dotnetcore2.1": time.Date(2021, time.September, 20, 0, 0, 0, 0, time.UTC),
21+
}
22+
var cases []caseStudy
23+
now := time.Now().UTC()
24+
for runtime, eosDate := range eosRuntimes {
25+
if now.Before(eosDate) {
26+
continue
27+
}
28+
study := caseStudy{
29+
Name: runtime + " end of support",
30+
Content: `
31+
resource "aws_lambda_function" "function" {
32+
function_name = "test_function"
33+
role = "test_role"
34+
runtime = "` + runtime + `"
35+
}
36+
`,
37+
Expected: helper.Issues{
38+
{
39+
Rule: NewAwsLambdaFunctionDeprecatedRuntimeRule(),
40+
Message: "The \"" + runtime + "\" runtime has reached the end of support",
41+
Range: hcl.Range{
42+
Filename: "resource.tf",
43+
Start: hcl.Pos{Line: 5, Column: 12},
44+
End: hcl.Pos{Line: 5, Column: len(runtime) + 14},
45+
},
46+
},
47+
},
48+
}
49+
cases = append(cases, study)
50+
}
51+
eolRuntimes := map[string]time.Time{
52+
"dotnetcore1.0": time.Date(2019, time.July, 30, 0, 0, 0, 0, time.UTC),
53+
"dotnetcore2.0": time.Date(2019, time.May, 30, 0, 0, 0, 0, time.UTC),
54+
"nodejs": time.Date(2016, time.October, 31, 0, 0, 0, 0, time.UTC),
55+
"nodejs4.3": time.Date(2020, time.March, 06, 0, 0, 0, 0, time.UTC),
56+
"nodejs4.3-edge": time.Date(2019, time.April, 30, 0, 0, 0, 0, time.UTC),
57+
"nodejs6.10": time.Date(2019, time.August, 12, 0, 0, 0, 0, time.UTC),
58+
"nodejs8.10": time.Date(2020, time.March, 06, 0, 0, 0, 0, time.UTC),
59+
"nodejs10.x": time.Date(2021, time.August, 30, 0, 0, 0, 0, time.UTC),
60+
"ruby2.5": time.Date(2021, time.August, 30, 0, 0, 0, 0, time.UTC),
61+
"python2.7": time.Date(2021, time.September, 30, 0, 0, 0, 0, time.UTC),
62+
"dotnetcore2.1": time.Date(2021, time.October, 30, 0, 0, 0, 0, time.UTC),
63+
}
64+
for runtime, eolDate := range eolRuntimes {
65+
if now.Before(eolDate) {
66+
continue
67+
}
68+
study := caseStudy{
69+
Name: runtime + " end of life",
70+
Content: `
71+
resource "aws_lambda_function" "function" {
72+
function_name = "test_function"
73+
role = "test_role"
74+
runtime = "` + runtime + `"
75+
}
76+
`,
77+
Expected: helper.Issues{
78+
{
79+
Rule: NewAwsLambdaFunctionDeprecatedRuntimeRule(),
80+
Message: "The \"" + runtime + "\" runtime has reached the end of life",
81+
Range: hcl.Range{
82+
Filename: "resource.tf",
83+
Start: hcl.Pos{Line: 5, Column: 12},
84+
End: hcl.Pos{Line: 5, Column: len(runtime) + 14},
85+
},
86+
},
87+
},
88+
}
89+
cases = append(cases, study)
90+
}
91+
92+
rule := NewAwsLambdaFunctionDeprecatedRuntimeRule()
93+
94+
for _, tc := range cases {
95+
runner := helper.TestRunner(t, map[string]string{"resource.tf": tc.Content})
96+
97+
if err := rule.Check(runner); err != nil {
98+
t.Fatalf("Unexpected error occurred: %s", err)
99+
}
100+
101+
helper.AssertIssues(t, tc.Expected, runner.Issues)
102+
}
103+
}

rules/provider.go

+1
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,5 @@ var Rules = append([]tflint.Rule{
3434
NewAwsElastiCacheReplicationGroupPreviousTypeRule(),
3535
NewAwsIAMPolicySidInvalidCharactersRule(),
3636
NewAwsIAMPolicyTooLongPolicyRule(),
37+
NewAwsLambdaFunctionDeprecatedRuntimeRule(),
3738
}, models.Rules...)

0 commit comments

Comments
 (0)