Skip to content

Commit

Permalink
feature(lint): Add new linter to restart policy for all deployment li…
Browse files Browse the repository at this point in the history
…ke objects
  • Loading branch information
Jonathan Henrique Medeiros committed Feb 21, 2025
1 parent 9d11bc6 commit 762b670
Show file tree
Hide file tree
Showing 10 changed files with 338 additions and 0 deletions.
9 changes: 9 additions & 0 deletions docs/generated/checks.md
Original file line number Diff line number Diff line change
Expand Up @@ -531,6 +531,15 @@ value: '[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+'
```yaml
key: owner
```
## restart-policy
**Enabled by default**: No
**Description**: Indicates when a object deployment's like doesn't use a restart policy
**Remediation**: Set up the restart policy for your object to 'Always' to increase the fault tolerance.
**Template**: [restart-policy](templates.md#restart-policy)
## run-as-non-root
**Enabled by default**: Yes
Expand Down
9 changes: 9 additions & 0 deletions docs/generated/templates.md
Original file line number Diff line number Diff line change
Expand Up @@ -712,6 +712,15 @@ KubeLinter supports the following templates:
type: string
```

## Restart policy

**Key**: `restart-policy`

**Description**: Flag applications running without the restart policy set to Always.

**Supported Objects**: DeploymentLike


## Run as non-root user

**Key**: `run-as-non-root`
Expand Down
23 changes: 23 additions & 0 deletions e2etests/bats-tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -802,6 +802,29 @@ get_value_from() {
[[ "${count}" == "2" ]]
}

@test "restart-policy" {
tmp="tests/checks/restart-policy.yaml"
cmd="${KUBE_LINTER_BIN} lint --include restart-policy --do-not-auto-add-defaults --format json ${tmp}"
run ${cmd}

print_info "${status}" "${output}" "${cmd}" "${tmp}"
[ "$status" -eq 1 ]

message1=$(get_value_from "${lines[0]}" '.Reports[0].Object.K8sObject.GroupVersionKind.Kind + ": " + .Reports[0].Diagnostic.Message')
message2=$(get_value_from "${lines[0]}" '.Reports[1].Object.K8sObject.GroupVersionKind.Kind + ": " + .Reports[1].Diagnostic.Message')
message3=$(get_value_from "${lines[0]}" '.Reports[2].Object.K8sObject.GroupVersionKind.Kind + ": " + .Reports[2].Diagnostic.Message')
message4=$(get_value_from "${lines[0]}" '.Reports[3].Object.K8sObject.GroupVersionKind.Kind + ": " + .Reports[3].Diagnostic.Message')
message5=$(get_value_from "${lines[0]}" '.Reports[4].Object.K8sObject.GroupVersionKind.Kind + ": " + .Reports[4].Diagnostic.Message')
count=$(get_value_from "${lines[0]}" '.Reports | length')

[[ "${message1}" == "Deployment: object has a restart policy defined with 'Never' but the only accepted restart policy is 'Always'" ]]
[[ "${message2}" == "Pod: object has a restart policy defined with 'OnFailure' but the only accepted restart policy is 'Always'" ]]
[[ "${message3}" == "DaemonSet: object has a restart policy defined with 'Never' but the only accepted restart policy is 'Always'" ]]
[[ "${message4}" == "ReplicaSet: object has a restart policy defined with 'OnFailure' but the only accepted restart policy is 'Always'" ]]
[[ "${message5}" == "ReplicationController: object has a restart policy defined with 'Never' but the only accepted restart policy is 'Always'" ]]
[[ "${count}" == "5" ]]
}

@test "scc-deny-privileged-container" {
tmp="tests/checks/scc-deny-privileged-container.yml"
cmd="${KUBE_LINTER_BIN} lint --include scc-deny-privileged-container --do-not-auto-add-defaults --format json ${tmp}"
Expand Down
8 changes: 8 additions & 0 deletions pkg/builtinchecks/yamls/restart-policy.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
name: "restart-policy"
description: "Indicates when a object deployment's like doesn't use a restart policy"
remediation: >-
Set up the restart policy for your object to 'Always' to increase the fault tolerance.
scope:
objectKinds:
- DeploymentLike
template: "restart-policy"
1 change: 1 addition & 0 deletions pkg/templates/all/all.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ import (
_ "golang.stackrox.io/kube-linter/pkg/templates/replicas"
_ "golang.stackrox.io/kube-linter/pkg/templates/requiredannotation"
_ "golang.stackrox.io/kube-linter/pkg/templates/requiredlabel"
_ "golang.stackrox.io/kube-linter/pkg/templates/restartpolicy"
_ "golang.stackrox.io/kube-linter/pkg/templates/runasnonroot"
_ "golang.stackrox.io/kube-linter/pkg/templates/sccdenypriv"
_ "golang.stackrox.io/kube-linter/pkg/templates/serviceaccount"
Expand Down
52 changes: 52 additions & 0 deletions pkg/templates/restartpolicy/internal/params/gen-params.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions pkg/templates/restartpolicy/internal/params/params.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package params

// Params represents the params accepted by this template.
type Params struct {
}
47 changes: 47 additions & 0 deletions pkg/templates/restartpolicy/template.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package restartpolicy

import (
"fmt"

"golang.stackrox.io/kube-linter/pkg/check"
"golang.stackrox.io/kube-linter/pkg/config"
"golang.stackrox.io/kube-linter/pkg/diagnostic"
"golang.stackrox.io/kube-linter/pkg/extract"
"golang.stackrox.io/kube-linter/pkg/lintcontext"
"golang.stackrox.io/kube-linter/pkg/objectkinds"
"golang.stackrox.io/kube-linter/pkg/templates"
"golang.stackrox.io/kube-linter/pkg/templates/restartpolicy/internal/params"
coreV1 "k8s.io/api/core/v1"
)

const (
templateKey = "restart-policy"
acceptedRestartPolicy = coreV1.RestartPolicyAlways
)

func init() {
templates.Register(check.Template{
HumanName: "Restart policy",
Key: templateKey,
Description: "Flag applications running without the restart policy set to Always.",
SupportedObjectKinds: config.ObjectKindsDesc{
ObjectKinds: []string{objectkinds.DeploymentLike},
},
Parameters: params.ParamDescs,
ParseAndValidateParams: params.ParseAndValidate,
Instantiate: params.WrapInstantiateFunc(func(p params.Params) (check.Func, error) {
return func(_ lintcontext.LintContext, object lintcontext.Object) []diagnostic.Diagnostic {
spec, found := extract.PodSpec(object.K8sObject)
if !found {
return nil
}

Check warning on line 37 in pkg/templates/restartpolicy/template.go

View check run for this annotation

Codecov / codecov/patch

pkg/templates/restartpolicy/template.go#L36-L37

Added lines #L36 - L37 were not covered by tests
if spec.RestartPolicy == acceptedRestartPolicy {
return nil
}
return []diagnostic.Diagnostic{
{Message: fmt.Sprintf("object has a restart policy defined with '%s' but the only accepted restart policy is '%s'", spec.RestartPolicy, acceptedRestartPolicy)},
}
}, nil
}),
})
}
98 changes: 98 additions & 0 deletions pkg/templates/restartpolicy/template_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package restartpolicy

import (
"testing"

"github.com/stretchr/testify/suite"
"golang.stackrox.io/kube-linter/pkg/diagnostic"
"golang.stackrox.io/kube-linter/pkg/lintcontext/mocks"
"golang.stackrox.io/kube-linter/pkg/templates"
"golang.stackrox.io/kube-linter/pkg/templates/restartpolicy/internal/params"
appsV1 "k8s.io/api/apps/v1"
coreV1 "k8s.io/api/core/v1"
)

func TestRestartPolicy(t *testing.T) {
suite.Run(t, new(RestartPolicyTestSuite))
}

type RestartPolicyTestSuite struct {
templates.TemplateTestSuite

ctx *mocks.MockLintContext
}

func (s *RestartPolicyTestSuite) SetupTest() {
s.Init(templateKey)
s.ctx = mocks.NewMockContext()
}

func (s *RestartPolicyTestSuite) addDeploymentWithRestartPolicy(name string, policy coreV1.RestartPolicy) {
s.ctx.AddMockDeployment(s.T(), name)
s.ctx.ModifyDeployment(s.T(), name, func(deployment *appsV1.Deployment) {
deployment.Spec.Template.Spec.RestartPolicy = policy
})
}

func (s *RestartPolicyTestSuite) addDeploymentWithEmptyRestartPolicy(name string) {
s.ctx.AddMockDeployment(s.T(), name)
s.ctx.ModifyDeployment(s.T(), name, func(deployment *appsV1.Deployment) {
deployment.Spec.Template.Spec.RestartPolicy = ""
})
}

func (s *RestartPolicyTestSuite) addDeploymentWithoutRestartPolicy(name string) {
s.ctx.AddMockDeployment(s.T(), name)
}

func (s *RestartPolicyTestSuite) TestInvalidRestartPolicies() {
const (
withoutRestartPolicy = "without-restart-policy"
emptyRestartPolicy = "empty-restart-policy"
restartPolicyNever = "restart-policy-never"
restartPolicyOnFailure = "restart-policy-on-failure"
)

s.addDeploymentWithoutRestartPolicy(withoutRestartPolicy)
s.addDeploymentWithEmptyRestartPolicy(emptyRestartPolicy)
s.addDeploymentWithRestartPolicy(restartPolicyNever, coreV1.RestartPolicyNever)
s.addDeploymentWithRestartPolicy(restartPolicyOnFailure, coreV1.RestartPolicyOnFailure)

s.Validate(s.ctx, []templates.TestCase{
{
Param: params.Params{},
Diagnostics: map[string][]diagnostic.Diagnostic{
withoutRestartPolicy: {
{Message: "object has a restart policy defined with '' but the only accepted restart policy is 'Always'"},
},
emptyRestartPolicy: {
{Message: "object has a restart policy defined with '' but the only accepted restart policy is 'Always'"},
},
restartPolicyNever: {
{Message: "object has a restart policy defined with 'Never' but the only accepted restart policy is 'Always'"},
},
restartPolicyOnFailure: {
{Message: "object has a restart policy defined with 'OnFailure' but the only accepted restart policy is 'Always'"},
},
},
ExpectInstantiationError: false,
},
})
}

func (s *RestartPolicyTestSuite) TestAcceptableRestartPolicy() {
const (
acceptableRestartPolicy = "acceptable-restart-policy"
)
s.addDeploymentWithRestartPolicy(acceptableRestartPolicy, coreV1.RestartPolicyAlways)

s.Validate(s.ctx, []templates.TestCase{
{
Param: params.Params{},
Diagnostics: map[string][]diagnostic.Diagnostic{
acceptableRestartPolicy: nil,
},
ExpectInstantiationError: false,
},
})
}
86 changes: 86 additions & 0 deletions tests/checks/restart-policy.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: dont-fire-deployment
spec:
template:
spec:
restartPolicy: Always
---
apiVersion: v1
kind: Pod
metadata:
name: dont-fire-pod
spec:
restartPolicy: Always
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: dont-fire-daemonset
spec:
template:
spec:
restartPolicy: Always
---
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: dont-fire-replicaset
spec:
template:
spec:
restartPolicy: Always
---
apiVersion: v1
kind: ReplicationController
metadata:
name: dont-fire-replicationcontroller
spec:
template:
spec:
restartPolicy: Always
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: fire-deployment
spec:
template:
spec:
restartPolicy: Never
---
apiVersion: v1
kind: Pod
metadata:
name: fire-pod
spec:
restartPolicy: OnFailure
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: fire-daemonset
spec:
template:
spec:
restartPolicy: Never
---
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: fire-replicaset
spec:
template:
spec:
restartPolicy: OnFailure
---
apiVersion: v1
kind: ReplicationController
metadata:
name: fire-replicationcontroller
spec:
template:
spec:
restartPolicy: Never

0 comments on commit 762b670

Please sign in to comment.