mirror of
https://github.com/kyverno/kyverno.git
synced 2025-03-31 03:45:17 +00:00
[Bug] [CLI] Restore warn-exit-code functionality for apply command (#9828)
* Restore warn-exite-code functionality for apply command Signed-off-by: Matt Veitas <mveitas@gmail.com> * Nove error handling Signed-off-by: Matt Veitas <mveitas@gmail.com> * Uncomment println statement Signed-off-by: Matt Veitas <mveitas@gmail.com> * Fixing linting Signed-off-by: Matt Veitas <mveitas@gmail.com> * Adding conformance tets for cli apply command with warn-exit-code Signed-off-by: Matt Veitas <mveitas@gmail.com> * Update path to kubectl-kyverno binary Signed-off-by: Matt Veitas <mveitas@gmail.com> * Add prepare-cli as needed dependency Signed-off-by: Matt Veitas <mveitas@gmail.com> * feat: install kubectl-kyverno in standard conformance tests Signed-off-by: ShutingZhao <shuting@nirmata.com> * fix: update chainsaw config Signed-off-by: ShutingZhao <shuting@nirmata.com> * fix: move CLI chainsaw tests to a separate action Signed-off-by: ShutingZhao <shuting@nirmata.com> * fix: CLI path Signed-off-by: ShutingZhao <shuting@nirmata.com> * fix: name Signed-off-by: ShutingZhao <shuting@nirmata.com> * fix: add chainsaw flag '--no-cluster' Signed-off-by: ShutingZhao <shuting@nirmata.com> * fix: CLI name Signed-off-by: ShutingZhao <shuting@nirmata.com> --------- Signed-off-by: Matt Veitas <mveitas@gmail.com> Signed-off-by: ShutingZhao <shuting@nirmata.com> Signed-off-by: shuting <shuting@nirmata.com> Co-authored-by: ShutingZhao <shuting@nirmata.com>
This commit is contained in:
parent
34c99c5126
commit
5ef7581c5c
9 changed files with 179 additions and 7 deletions
23
.github/workflows/conformance.yaml
vendored
23
.github/workflows/conformance.yaml
vendored
|
@ -114,7 +114,8 @@ jobs:
|
||||||
- ^verify-manifests$
|
- ^verify-manifests$
|
||||||
- ^verifyImages$
|
- ^verifyImages$
|
||||||
- ^webhooks$
|
- ^webhooks$
|
||||||
needs: prepare-images
|
needs:
|
||||||
|
- prepare-images
|
||||||
name: ${{ matrix.k8s-version.name }} - ${{ matrix.config.name }} - ${{ matrix.tests }}
|
name: ${{ matrix.k8s-version.name }} - ${{ matrix.config.name }} - ${{ matrix.tests }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
|
@ -946,20 +947,38 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
permissions:
|
||||||
packages: read
|
packages: read
|
||||||
needs: prepare-cli
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
tests:
|
||||||
|
- ^cli$
|
||||||
|
needs:
|
||||||
|
- prepare-cli
|
||||||
|
name: ${{ matrix.tests }} - chainsaw
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||||
|
# install tools
|
||||||
- name: Download kyverno CLI archive
|
- name: Download kyverno CLI archive
|
||||||
uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2
|
uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2
|
||||||
with:
|
with:
|
||||||
name: kubectl-kyverno
|
name: kubectl-kyverno
|
||||||
|
- name: Install chainsaw
|
||||||
|
uses: kyverno/action-install-chainsaw@3bf0752f44d348d859fefa022f113bda6a24a1ae # v0.1.7
|
||||||
- name: Install Kyverno CLI
|
- name: Install Kyverno CLI
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
set -e
|
set -e
|
||||||
chmod +x kubectl-kyverno && mv kubectl-kyverno ./cmd/cli/kubectl-kyverno/kyverno
|
chmod +x kubectl-kyverno && mv kubectl-kyverno ./cmd/cli/kubectl-kyverno/kyverno
|
||||||
echo "$PWD/cmd/cli/kubectl-kyverno" >> $GITHUB_PATH
|
echo "$PWD/cmd/cli/kubectl-kyverno" >> $GITHUB_PATH
|
||||||
|
# run tests
|
||||||
|
- name: Test with Chainsaw
|
||||||
|
shell: bash
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: |
|
||||||
|
set -e
|
||||||
|
cd ./test/conformance/chainsaw && chainsaw test --include-test-regex '^chainsaw$/${{ matrix.tests }}' --config ../../../.chainsaw.yaml --no-cluster
|
||||||
- name: Fix test files
|
- name: Fix test files
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
|
|
28
cmd/cli/kubectl-kyverno/_testdata/apply/test-2/policy.yaml
Normal file
28
cmd/cli/kubectl-kyverno/_testdata/apply/test-2/policy.yaml
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
apiVersion: kyverno.io/v1
|
||||||
|
kind: ClusterPolicy
|
||||||
|
metadata:
|
||||||
|
name: test-policy
|
||||||
|
annotations:
|
||||||
|
policies.kyverno.io/title: "Lol Security Standards"
|
||||||
|
policies.kyverno.io/category: "Lol Security Standards"
|
||||||
|
policies.kyverno.io/severity: "high"
|
||||||
|
policies.kyverno.io/subject: "Pod"
|
||||||
|
spec:
|
||||||
|
background: true
|
||||||
|
failurePolicy: Fail
|
||||||
|
rules:
|
||||||
|
- name: restrict-lol-annotation
|
||||||
|
match:
|
||||||
|
any:
|
||||||
|
- resources:
|
||||||
|
kinds:
|
||||||
|
- Pod
|
||||||
|
validate:
|
||||||
|
message: >-
|
||||||
|
If "lol" annotation is present then
|
||||||
|
its value can be only one of "such lol", "much annotation".
|
||||||
|
pattern:
|
||||||
|
# syntax refdoc: https://kyverno.io/docs/writing-policies/validate/#anchors
|
||||||
|
=(metadata):
|
||||||
|
=(annotations):
|
||||||
|
=(lol): such lol | much annotation
|
|
@ -0,0 +1,20 @@
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: lol
|
||||||
|
name: i-will-fail-the-policy-check
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: lol
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: lol
|
||||||
|
annotations:
|
||||||
|
lol: not much
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- image: woot
|
||||||
|
name: woot
|
|
@ -95,7 +95,7 @@ func Command() *cobra.Command {
|
||||||
} else {
|
} else {
|
||||||
printViolations(out, rc)
|
printViolations(out, rc)
|
||||||
}
|
}
|
||||||
return exit(rc, applyCommandConfig.warnExitCode, applyCommandConfig.warnNoPassed)
|
return exit(out, rc, applyCommandConfig.warnExitCode, applyCommandConfig.warnNoPassed)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
cmd.Flags().StringSliceVarP(&applyCommandConfig.ResourcePaths, "resource", "r", []string{}, "Path to resource files")
|
cmd.Flags().StringSliceVarP(&applyCommandConfig.ResourcePaths, "resource", "r", []string{}, "Path to resource files")
|
||||||
|
@ -465,15 +465,29 @@ func printViolations(out io.Writer, rc *processor.ResultCounts) {
|
||||||
fmt.Fprintf(out, "\npass: %d, fail: %d, warn: %d, error: %d, skip: %d \n", rc.Pass(), rc.Fail(), rc.Warn(), rc.Error(), rc.Skip())
|
fmt.Fprintf(out, "\npass: %d, fail: %d, warn: %d, error: %d, skip: %d \n", rc.Pass(), rc.Fail(), rc.Warn(), rc.Error(), rc.Skip())
|
||||||
}
|
}
|
||||||
|
|
||||||
func exit(rc *processor.ResultCounts, warnExitCode int, warnNoPassed bool) error {
|
type WarnExitCodeError struct {
|
||||||
|
ExitCode int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w WarnExitCodeError) Error() string {
|
||||||
|
return fmt.Sprintf("exit as warnExitCode is %d", w.ExitCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
func exit(out io.Writer, rc *processor.ResultCounts, warnExitCode int, warnNoPassed bool) error {
|
||||||
if rc.Fail() > 0 {
|
if rc.Fail() > 0 {
|
||||||
return fmt.Errorf("exit as there are policy violations")
|
return fmt.Errorf("exit as there are policy violations")
|
||||||
} else if rc.Error() > 0 {
|
} else if rc.Error() > 0 {
|
||||||
return fmt.Errorf("exit as there are policy errors")
|
return fmt.Errorf("exit as there are policy errors")
|
||||||
} else if rc.Warn() > 0 && warnExitCode != 0 {
|
} else if rc.Warn() > 0 && warnExitCode != 0 {
|
||||||
return fmt.Errorf("exit as warnExitCode is %d", warnExitCode)
|
fmt.Printf("exit as warnExitCode is %d", warnExitCode)
|
||||||
|
return WarnExitCodeError{
|
||||||
|
ExitCode: warnExitCode,
|
||||||
|
}
|
||||||
} else if rc.Pass() == 0 && warnNoPassed {
|
} else if rc.Pass() == 0 && warnNoPassed {
|
||||||
return fmt.Errorf("exit as no objects satisfied policy")
|
fmt.Println(out, "exit as no objects satisfied policy")
|
||||||
|
return WarnExitCodeError{
|
||||||
|
ExitCode: warnExitCode,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
@ -114,6 +115,7 @@ func Test_Apply(t *testing.T) {
|
||||||
ResourcePaths: []string{"../../../../../test/resources/pod_with_latest_tag.yaml"},
|
ResourcePaths: []string{"../../../../../test/resources/pod_with_latest_tag.yaml"},
|
||||||
PolicyReport: true,
|
PolicyReport: true,
|
||||||
AuditWarn: true,
|
AuditWarn: true,
|
||||||
|
warnExitCode: 3,
|
||||||
},
|
},
|
||||||
stdinFile: "../../../../../test/best_practices/disallow_latest_tag.yaml",
|
stdinFile: "../../../../../test/best_practices/disallow_latest_tag.yaml",
|
||||||
expectedPolicyReports: []policyreportv1alpha2.PolicyReport{{
|
expectedPolicyReports: []policyreportv1alpha2.PolicyReport{{
|
||||||
|
@ -480,6 +482,29 @@ func TestCommandWithInvalidFlag(t *testing.T) {
|
||||||
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(string(out)))
|
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(string(out)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCommandWarnExitCode(t *testing.T) {
|
||||||
|
var warnExitCode = 3
|
||||||
|
|
||||||
|
cmd := Command()
|
||||||
|
cmd.SetArgs([]string{
|
||||||
|
"../../_testdata/apply/test-2/policy.yaml",
|
||||||
|
"--resource",
|
||||||
|
"../../_testdata/apply/test-2/resources.yaml",
|
||||||
|
"--audit-warn",
|
||||||
|
"--warn-exit-code",
|
||||||
|
strconv.Itoa(warnExitCode),
|
||||||
|
})
|
||||||
|
err := cmd.Execute()
|
||||||
|
if err != nil {
|
||||||
|
switch e := err.(type) {
|
||||||
|
case WarnExitCodeError:
|
||||||
|
assert.Equal(t, warnExitCode, e.ExitCode)
|
||||||
|
default:
|
||||||
|
assert.Fail(t, "Expecting WarnExitCodeError")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestCommandHelp(t *testing.T) {
|
func TestCommandHelp(t *testing.T) {
|
||||||
cmd := Command()
|
cmd := Command()
|
||||||
assert.NotNil(t, cmd)
|
assert.NotNil(t, cmd)
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/commands"
|
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/commands"
|
||||||
|
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/commands/apply"
|
||||||
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/experimental"
|
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/experimental"
|
||||||
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/log"
|
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/log"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
@ -18,7 +19,12 @@ func main() {
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
if err := cmd.Execute(); err != nil {
|
if err := cmd.Execute(); err != nil {
|
||||||
os.Exit(1)
|
switch e := err.(type) {
|
||||||
|
case apply.WarnExitCodeError:
|
||||||
|
os.Exit(e.ExitCode)
|
||||||
|
default:
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
apiVersion: chainsaw.kyverno.io/v1alpha1
|
||||||
|
kind: Test
|
||||||
|
metadata:
|
||||||
|
name: test-name
|
||||||
|
spec:
|
||||||
|
steps:
|
||||||
|
- try:
|
||||||
|
- script:
|
||||||
|
content: |
|
||||||
|
kyverno apply policy.yaml --resource resource.yaml --audit-warn --warn-exit-code 3
|
||||||
|
check:
|
||||||
|
($error): "exit status 3"
|
|
@ -0,0 +1,28 @@
|
||||||
|
apiVersion: kyverno.io/v1
|
||||||
|
kind: ClusterPolicy
|
||||||
|
metadata:
|
||||||
|
name: test-policy
|
||||||
|
annotations:
|
||||||
|
policies.kyverno.io/title: "Lol Security Standards"
|
||||||
|
policies.kyverno.io/category: "Lol Security Standards"
|
||||||
|
policies.kyverno.io/severity: "high"
|
||||||
|
policies.kyverno.io/subject: "Pod"
|
||||||
|
spec:
|
||||||
|
background: true
|
||||||
|
failurePolicy: Fail
|
||||||
|
rules:
|
||||||
|
- name: restrict-lol-annotation
|
||||||
|
match:
|
||||||
|
any:
|
||||||
|
- resources:
|
||||||
|
kinds:
|
||||||
|
- Pod
|
||||||
|
validate:
|
||||||
|
message: >-
|
||||||
|
If "lol" annotation is present then
|
||||||
|
its value can be only one of "such lol", "much annotation".
|
||||||
|
pattern:
|
||||||
|
# syntax refdoc: https://kyverno.io/docs/writing-policies/validate/#anchors
|
||||||
|
=(metadata):
|
||||||
|
=(annotations):
|
||||||
|
=(lol): such lol | much annotation
|
|
@ -0,0 +1,20 @@
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: lol
|
||||||
|
name: i-will-fail-the-policy-check
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: lol
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: lol
|
||||||
|
annotations:
|
||||||
|
lol: not much
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- image: woot
|
||||||
|
name: woot
|
Loading…
Add table
Reference in a new issue