Skip to content

Commit 762b670

Browse files
author
Jonathan Henrique Medeiros
committed
feature(lint): Add new linter to restart policy for all deployment like objects
1 parent 9d11bc6 commit 762b670

File tree

10 files changed

+338
-0
lines changed

10 files changed

+338
-0
lines changed

docs/generated/checks.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -531,6 +531,15 @@ value: '[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+'
531531
```yaml
532532
key: owner
533533
```
534+
## restart-policy
535+
536+
**Enabled by default**: No
537+
538+
**Description**: Indicates when a object deployment's like doesn't use a restart policy
539+
540+
**Remediation**: Set up the restart policy for your object to 'Always' to increase the fault tolerance.
541+
542+
**Template**: [restart-policy](templates.md#restart-policy)
534543
## run-as-non-root
535544
536545
**Enabled by default**: Yes

docs/generated/templates.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -712,6 +712,15 @@ KubeLinter supports the following templates:
712712
type: string
713713
```
714714

715+
## Restart policy
716+
717+
**Key**: `restart-policy`
718+
719+
**Description**: Flag applications running without the restart policy set to Always.
720+
721+
**Supported Objects**: DeploymentLike
722+
723+
715724
## Run as non-root user
716725

717726
**Key**: `run-as-non-root`

e2etests/bats-tests.sh

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -802,6 +802,29 @@ get_value_from() {
802802
[[ "${count}" == "2" ]]
803803
}
804804

805+
@test "restart-policy" {
806+
tmp="tests/checks/restart-policy.yaml"
807+
cmd="${KUBE_LINTER_BIN} lint --include restart-policy --do-not-auto-add-defaults --format json ${tmp}"
808+
run ${cmd}
809+
810+
print_info "${status}" "${output}" "${cmd}" "${tmp}"
811+
[ "$status" -eq 1 ]
812+
813+
message1=$(get_value_from "${lines[0]}" '.Reports[0].Object.K8sObject.GroupVersionKind.Kind + ": " + .Reports[0].Diagnostic.Message')
814+
message2=$(get_value_from "${lines[0]}" '.Reports[1].Object.K8sObject.GroupVersionKind.Kind + ": " + .Reports[1].Diagnostic.Message')
815+
message3=$(get_value_from "${lines[0]}" '.Reports[2].Object.K8sObject.GroupVersionKind.Kind + ": " + .Reports[2].Diagnostic.Message')
816+
message4=$(get_value_from "${lines[0]}" '.Reports[3].Object.K8sObject.GroupVersionKind.Kind + ": " + .Reports[3].Diagnostic.Message')
817+
message5=$(get_value_from "${lines[0]}" '.Reports[4].Object.K8sObject.GroupVersionKind.Kind + ": " + .Reports[4].Diagnostic.Message')
818+
count=$(get_value_from "${lines[0]}" '.Reports | length')
819+
820+
[[ "${message1}" == "Deployment: object has a restart policy defined with 'Never' but the only accepted restart policy is 'Always'" ]]
821+
[[ "${message2}" == "Pod: object has a restart policy defined with 'OnFailure' but the only accepted restart policy is 'Always'" ]]
822+
[[ "${message3}" == "DaemonSet: object has a restart policy defined with 'Never' but the only accepted restart policy is 'Always'" ]]
823+
[[ "${message4}" == "ReplicaSet: object has a restart policy defined with 'OnFailure' but the only accepted restart policy is 'Always'" ]]
824+
[[ "${message5}" == "ReplicationController: object has a restart policy defined with 'Never' but the only accepted restart policy is 'Always'" ]]
825+
[[ "${count}" == "5" ]]
826+
}
827+
805828
@test "scc-deny-privileged-container" {
806829
tmp="tests/checks/scc-deny-privileged-container.yml"
807830
cmd="${KUBE_LINTER_BIN} lint --include scc-deny-privileged-container --do-not-auto-add-defaults --format json ${tmp}"
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
name: "restart-policy"
2+
description: "Indicates when a object deployment's like doesn't use a restart policy"
3+
remediation: >-
4+
Set up the restart policy for your object to 'Always' to increase the fault tolerance.
5+
scope:
6+
objectKinds:
7+
- DeploymentLike
8+
template: "restart-policy"

pkg/templates/all/all.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ import (
4848
_ "golang.stackrox.io/kube-linter/pkg/templates/replicas"
4949
_ "golang.stackrox.io/kube-linter/pkg/templates/requiredannotation"
5050
_ "golang.stackrox.io/kube-linter/pkg/templates/requiredlabel"
51+
_ "golang.stackrox.io/kube-linter/pkg/templates/restartpolicy"
5152
_ "golang.stackrox.io/kube-linter/pkg/templates/runasnonroot"
5253
_ "golang.stackrox.io/kube-linter/pkg/templates/sccdenypriv"
5354
_ "golang.stackrox.io/kube-linter/pkg/templates/serviceaccount"

pkg/templates/restartpolicy/internal/params/gen-params.go

Lines changed: 52 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package params
2+
3+
// Params represents the params accepted by this template.
4+
type Params struct {
5+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package restartpolicy
2+
3+
import (
4+
"fmt"
5+
6+
"golang.stackrox.io/kube-linter/pkg/check"
7+
"golang.stackrox.io/kube-linter/pkg/config"
8+
"golang.stackrox.io/kube-linter/pkg/diagnostic"
9+
"golang.stackrox.io/kube-linter/pkg/extract"
10+
"golang.stackrox.io/kube-linter/pkg/lintcontext"
11+
"golang.stackrox.io/kube-linter/pkg/objectkinds"
12+
"golang.stackrox.io/kube-linter/pkg/templates"
13+
"golang.stackrox.io/kube-linter/pkg/templates/restartpolicy/internal/params"
14+
coreV1 "k8s.io/api/core/v1"
15+
)
16+
17+
const (
18+
templateKey = "restart-policy"
19+
acceptedRestartPolicy = coreV1.RestartPolicyAlways
20+
)
21+
22+
func init() {
23+
templates.Register(check.Template{
24+
HumanName: "Restart policy",
25+
Key: templateKey,
26+
Description: "Flag applications running without the restart policy set to Always.",
27+
SupportedObjectKinds: config.ObjectKindsDesc{
28+
ObjectKinds: []string{objectkinds.DeploymentLike},
29+
},
30+
Parameters: params.ParamDescs,
31+
ParseAndValidateParams: params.ParseAndValidate,
32+
Instantiate: params.WrapInstantiateFunc(func(p params.Params) (check.Func, error) {
33+
return func(_ lintcontext.LintContext, object lintcontext.Object) []diagnostic.Diagnostic {
34+
spec, found := extract.PodSpec(object.K8sObject)
35+
if !found {
36+
return nil
37+
}
38+
if spec.RestartPolicy == acceptedRestartPolicy {
39+
return nil
40+
}
41+
return []diagnostic.Diagnostic{
42+
{Message: fmt.Sprintf("object has a restart policy defined with '%s' but the only accepted restart policy is '%s'", spec.RestartPolicy, acceptedRestartPolicy)},
43+
}
44+
}, nil
45+
}),
46+
})
47+
}
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
package restartpolicy
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/suite"
7+
"golang.stackrox.io/kube-linter/pkg/diagnostic"
8+
"golang.stackrox.io/kube-linter/pkg/lintcontext/mocks"
9+
"golang.stackrox.io/kube-linter/pkg/templates"
10+
"golang.stackrox.io/kube-linter/pkg/templates/restartpolicy/internal/params"
11+
appsV1 "k8s.io/api/apps/v1"
12+
coreV1 "k8s.io/api/core/v1"
13+
)
14+
15+
func TestRestartPolicy(t *testing.T) {
16+
suite.Run(t, new(RestartPolicyTestSuite))
17+
}
18+
19+
type RestartPolicyTestSuite struct {
20+
templates.TemplateTestSuite
21+
22+
ctx *mocks.MockLintContext
23+
}
24+
25+
func (s *RestartPolicyTestSuite) SetupTest() {
26+
s.Init(templateKey)
27+
s.ctx = mocks.NewMockContext()
28+
}
29+
30+
func (s *RestartPolicyTestSuite) addDeploymentWithRestartPolicy(name string, policy coreV1.RestartPolicy) {
31+
s.ctx.AddMockDeployment(s.T(), name)
32+
s.ctx.ModifyDeployment(s.T(), name, func(deployment *appsV1.Deployment) {
33+
deployment.Spec.Template.Spec.RestartPolicy = policy
34+
})
35+
}
36+
37+
func (s *RestartPolicyTestSuite) addDeploymentWithEmptyRestartPolicy(name string) {
38+
s.ctx.AddMockDeployment(s.T(), name)
39+
s.ctx.ModifyDeployment(s.T(), name, func(deployment *appsV1.Deployment) {
40+
deployment.Spec.Template.Spec.RestartPolicy = ""
41+
})
42+
}
43+
44+
func (s *RestartPolicyTestSuite) addDeploymentWithoutRestartPolicy(name string) {
45+
s.ctx.AddMockDeployment(s.T(), name)
46+
}
47+
48+
func (s *RestartPolicyTestSuite) TestInvalidRestartPolicies() {
49+
const (
50+
withoutRestartPolicy = "without-restart-policy"
51+
emptyRestartPolicy = "empty-restart-policy"
52+
restartPolicyNever = "restart-policy-never"
53+
restartPolicyOnFailure = "restart-policy-on-failure"
54+
)
55+
56+
s.addDeploymentWithoutRestartPolicy(withoutRestartPolicy)
57+
s.addDeploymentWithEmptyRestartPolicy(emptyRestartPolicy)
58+
s.addDeploymentWithRestartPolicy(restartPolicyNever, coreV1.RestartPolicyNever)
59+
s.addDeploymentWithRestartPolicy(restartPolicyOnFailure, coreV1.RestartPolicyOnFailure)
60+
61+
s.Validate(s.ctx, []templates.TestCase{
62+
{
63+
Param: params.Params{},
64+
Diagnostics: map[string][]diagnostic.Diagnostic{
65+
withoutRestartPolicy: {
66+
{Message: "object has a restart policy defined with '' but the only accepted restart policy is 'Always'"},
67+
},
68+
emptyRestartPolicy: {
69+
{Message: "object has a restart policy defined with '' but the only accepted restart policy is 'Always'"},
70+
},
71+
restartPolicyNever: {
72+
{Message: "object has a restart policy defined with 'Never' but the only accepted restart policy is 'Always'"},
73+
},
74+
restartPolicyOnFailure: {
75+
{Message: "object has a restart policy defined with 'OnFailure' but the only accepted restart policy is 'Always'"},
76+
},
77+
},
78+
ExpectInstantiationError: false,
79+
},
80+
})
81+
}
82+
83+
func (s *RestartPolicyTestSuite) TestAcceptableRestartPolicy() {
84+
const (
85+
acceptableRestartPolicy = "acceptable-restart-policy"
86+
)
87+
s.addDeploymentWithRestartPolicy(acceptableRestartPolicy, coreV1.RestartPolicyAlways)
88+
89+
s.Validate(s.ctx, []templates.TestCase{
90+
{
91+
Param: params.Params{},
92+
Diagnostics: map[string][]diagnostic.Diagnostic{
93+
acceptableRestartPolicy: nil,
94+
},
95+
ExpectInstantiationError: false,
96+
},
97+
})
98+
}

tests/checks/restart-policy.yaml

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
---
2+
apiVersion: apps/v1
3+
kind: Deployment
4+
metadata:
5+
name: dont-fire-deployment
6+
spec:
7+
template:
8+
spec:
9+
restartPolicy: Always
10+
---
11+
apiVersion: v1
12+
kind: Pod
13+
metadata:
14+
name: dont-fire-pod
15+
spec:
16+
restartPolicy: Always
17+
---
18+
apiVersion: apps/v1
19+
kind: DaemonSet
20+
metadata:
21+
name: dont-fire-daemonset
22+
spec:
23+
template:
24+
spec:
25+
restartPolicy: Always
26+
---
27+
apiVersion: apps/v1
28+
kind: ReplicaSet
29+
metadata:
30+
name: dont-fire-replicaset
31+
spec:
32+
template:
33+
spec:
34+
restartPolicy: Always
35+
---
36+
apiVersion: v1
37+
kind: ReplicationController
38+
metadata:
39+
name: dont-fire-replicationcontroller
40+
spec:
41+
template:
42+
spec:
43+
restartPolicy: Always
44+
---
45+
apiVersion: apps/v1
46+
kind: Deployment
47+
metadata:
48+
name: fire-deployment
49+
spec:
50+
template:
51+
spec:
52+
restartPolicy: Never
53+
---
54+
apiVersion: v1
55+
kind: Pod
56+
metadata:
57+
name: fire-pod
58+
spec:
59+
restartPolicy: OnFailure
60+
---
61+
apiVersion: apps/v1
62+
kind: DaemonSet
63+
metadata:
64+
name: fire-daemonset
65+
spec:
66+
template:
67+
spec:
68+
restartPolicy: Never
69+
---
70+
apiVersion: apps/v1
71+
kind: ReplicaSet
72+
metadata:
73+
name: fire-replicaset
74+
spec:
75+
template:
76+
spec:
77+
restartPolicy: OnFailure
78+
---
79+
apiVersion: v1
80+
kind: ReplicationController
81+
metadata:
82+
name: fire-replicationcontroller
83+
spec:
84+
template:
85+
spec:
86+
restartPolicy: Never

0 commit comments

Comments
 (0)