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

feature(lint): Adds a new template linter to check the restart policy for all deployments like objects #915

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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' or 'OnFailure' 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.

**Supported Objects**: DeploymentLike


## Run as non-root user

**Key**: `run-as-non-root`
Expand Down
24 changes: 24 additions & 0 deletions e2etests/bats-tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -785,6 +785,30 @@ 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}

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')
message6=$(get_value_from "${lines[0]}" '.Reports[5].Object.K8sObject.GroupVersionKind.Kind + ": " + .Reports[5].Diagnostic.Message')
message7=$(get_value_from "${lines[0]}" '.Reports[6].Object.K8sObject.GroupVersionKind.Kind + ": " + .Reports[6].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 policies are '[Always OnFailure]'" ]]
[[ "${message2}" == "Pod: object has a restart policy defined with 'Never' but the only accepted restart policies are '[Always OnFailure]'" ]]
[[ "${message3}" == "DaemonSet: object has a restart policy defined with 'Never' but the only accepted restart policies are '[Always OnFailure]'" ]]
[[ "${message4}" == "ReplicaSet: object has a restart policy defined with 'Never' but the only accepted restart policies are '[Always OnFailure]'" ]]
[[ "${message5}" == "ReplicationController: object has a restart policy defined with 'Never' but the only accepted restart policies are '[Always OnFailure]'" ]]
[[ "${message6}" == "Job: object has a restart policy defined with 'Never' but the only accepted restart policies are '[Always OnFailure]'" ]]
[[ "${message7}" == "CronJob: object has a restart policy defined with 'Never' but the only accepted restart policies are '[Always OnFailure]'" ]]
[[ "${count}" == "7" ]]
}

@test "run-as-non-root" {
tmp="tests/checks/run-as-non-root.yml"
cmd="${KUBE_LINTER_BIN} lint --include run-as-non-root --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' or 'OnFailure' 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 {
}
50 changes: 50 additions & 0 deletions pkg/templates/restartpolicy/template.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
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"
)

var acceptedRestartPolicies = []coreV1.RestartPolicy{coreV1.RestartPolicyAlways, coreV1.RestartPolicyOnFailure}

func init() {
templates.Register(check.Template{
HumanName: "Restart policy",
Key: templateKey,
Description: "Flag applications running without the restart policy.",
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
}
for _, policy := range acceptedRestartPolicies {
if spec.RestartPolicy == policy {
return nil
}
}
return []diagnostic.Diagnostic{
{Message: fmt.Sprintf("object has a restart policy defined with '%s' but the only accepted restart policies are '%s'", spec.RestartPolicy, acceptedRestartPolicies)},
}
}, nil
}),
})
}
118 changes: 118 additions & 0 deletions pkg/templates/restartpolicy/template_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
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) addObjectWithoutPodSpec(name string) {
s.ctx.AddMockService(s.T(), name)
}

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

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

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 policies are '[Always OnFailure]'"},
},
emptyRestartPolicy: {
{Message: "object has a restart policy defined with '' but the only accepted restart policies are '[Always OnFailure]'"},
},
restartPolicyNever: {
{Message: "object has a restart policy defined with 'Never' but the only accepted restart policies are '[Always OnFailure]'"},
},
},
ExpectInstantiationError: false,
},
})
}

func (s *RestartPolicyTestSuite) TestAcceptableRestartPolicy() {
const (
alwaysRestartPolicy = "restart-policy-always"
onFailureRestartPolicy = "restart-policy-on-failure"
)
s.addDeploymentWithRestartPolicy(alwaysRestartPolicy, coreV1.RestartPolicyAlways)
s.addDeploymentWithRestartPolicy(onFailureRestartPolicy, coreV1.RestartPolicyOnFailure)

s.Validate(s.ctx, []templates.TestCase{
{
Param: params.Params{},
Diagnostics: map[string][]diagnostic.Diagnostic{
alwaysRestartPolicy: nil,
onFailureRestartPolicy: nil,
},
ExpectInstantiationError: false,
},
})
}

func (s *RestartPolicyTestSuite) TestObjectWithoutPodSpec() {
const (
objectWithoutPodSpec = "object-without-pod-spec"
)

s.addObjectWithoutPodSpec(objectWithoutPodSpec)

s.Validate(s.ctx, []templates.TestCase{
{
Param: params.Params{},
Diagnostics: map[string][]diagnostic.Diagnostic{
objectWithoutPodSpec: nil,
},
ExpectInstantiationError: false,
},
})
}
Loading
Loading