diff --git a/CHANGELOG.md b/CHANGELOG.md index b71aa38..562d1fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,15 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [Unreleased] +## [0.4.0] - 2025-01-21 + +### Added + +* `--indent` option + +### Changed + +* Bump all dependencies to the latest versions (Kubernetes v0.32.1) ## [0.3.0] - 2022-06-07 ### Changed diff --git a/README.md b/README.md index 861b2c7..cee3c07 100644 --- a/README.md +++ b/README.md @@ -166,6 +166,7 @@ $ kubectl get service gatekeeper-webhook-service -o yaml | kube-review create -- | `--action` | string | create | Type of operation to apply in admission review (create, update, delete, connect) | | `--as` | string | kube-review | Name of user or service account for userInfo attributes | | `--as-group` | string | none | Name of group this user or service account belongs to. May be repeated for multiple groups | +| `--indent` | int | 2 | Number of spaces to indent JSON output | The `action` provided has the following effects on the produced `AdmissionReview` object: diff --git a/cmd/main.go b/cmd/main.go index 629e985..1a907e0 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -14,6 +14,7 @@ type parameters struct { action string as string groups []string + indent uint8 } //nolint:gochecknoglobals @@ -59,7 +60,7 @@ webhooks`, } } - req, err := admission.CreateAdmissionReviewRequest(input, params.action, params.as, params.groups) + req, err := admission.CreateAdmissionReviewRequest(input, params.action, params.as, params.groups, params.indent) if err != nil { log.Fatal(err) } @@ -97,6 +98,12 @@ func Execute() { []string{}, "Group(s) of user (may be repeated) (default: empty)", ) + rootCmd.PersistentFlags().Uint8Var( + ¶ms.indent, + "indent", + 2, + "Number of spaces to indent JSON output (default: 2)", + ) rootCmd.AddCommand(createCmd) rootCmd.AddCommand(versionCmd) diff --git a/pkg/admission/review.go b/pkg/admission/review.go index 9c0be73..4e120f8 100644 --- a/pkg/admission/review.go +++ b/pkg/admission/review.go @@ -4,6 +4,7 @@ import ( "bytes" "encoding/json" "fmt" + "strings" admissionv1 "k8s.io/api/admission/v1" v1 "k8s.io/api/authentication/v1" @@ -16,7 +17,13 @@ import ( "k8s.io/client-go/kubernetes/scheme" ) -func CreateAdmissionReviewRequest(input []byte, action string, username string, groups []string) ([]byte, error) { +func CreateAdmissionReviewRequest( + input []byte, + action string, + username string, + groups []string, + indent uint8, +) ([]byte, error) { operation, err := actionToOperation(action) if err != nil { return nil, err @@ -27,7 +34,7 @@ func CreateAdmissionReviewRequest(input []byte, action string, username string, object, kind, err := decode(input, nil, nil) if err != nil { // Failure to decode, likely due to unrecognized type, try unstructured - return fromUnstructured(input, operation, username, groups) + return fromUnstructured(input, operation, username, groups, indent) } unstructured, err := runtime.DefaultUnstructuredConverter.ToUnstructured(object) @@ -42,6 +49,7 @@ func CreateAdmissionReviewRequest(input []byte, action string, username string, getUserInfo(username, groups), getNewObject(object, *operation), getOldObject(object, *operation), + indent, ) } @@ -50,6 +58,7 @@ func fromUnstructured( operation *admissionv1.Operation, username string, groups []string, + indent uint8, ) ([]byte, error) { var object any @@ -87,7 +96,7 @@ func fromUnstructured( newObject := getUnknownRaw(&unknown, *operation) oldObject := getOldUnknownRaw(&unknown, *operation) - return createAdmissionRequest(unstructured, *kind, operation, userInfo, newObject, oldObject) + return createAdmissionRequest(unstructured, *kind, operation, userInfo, newObject, oldObject, indent) } func createAdmissionRequest( @@ -96,6 +105,7 @@ func createAdmissionRequest( operation *admissionv1.Operation, user v1.UserInfo, object, oldObject runtime.RawExtension, + indent uint8, ) ([]byte, error) { dryRun := true @@ -130,9 +140,20 @@ func createAdmissionRequest( Request: admissionRequest, } - requestJSON, err := json.MarshalIndent(&admissionReview, "", " ") - if err != nil { - return nil, fmt.Errorf("failed encoding object to JSON %w", err) + var requestJSON []byte + + var err error + + if indent == 0 { + requestJSON, err = json.Marshal(&admissionReview) + if err != nil { + return nil, fmt.Errorf("failed encoding object to JSON %w", err) + } + } else { + requestJSON, err = json.MarshalIndent(&admissionReview, "", strings.Repeat(" ", int(indent))) + if err != nil { + return nil, fmt.Errorf("failed encoding object to JSON %w", err) + } } return requestJSON, nil diff --git a/pkg/admission/review_test.go b/pkg/admission/review_test.go index c3b9919..27cedeb 100644 --- a/pkg/admission/review_test.go +++ b/pkg/admission/review_test.go @@ -2,6 +2,7 @@ package admission import ( "encoding/json" + "os" "testing" v1 "k8s.io/api/admission/v1" @@ -10,29 +11,14 @@ import ( func TestBasicReview(t *testing.T) { t.Parallel() - manifest := `apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx - labels: - app: nginx -spec: - selector: - matchLabels: - app: nginx - template: - metadata: - labels: - app: nginx - spec: - containers: - - image: nginx - name: nginx - ports: - - containerPort: 8080` + manifest := mustReadFileString(t, "testdata/in.yaml") reviewBytes, err := CreateAdmissionReviewRequest( - []byte(manifest), "create", "kube-review", []string{"system:masters"}, + []byte(manifest), + "create", + "kube-review", + []string{"system:masters"}, + 2, ) if err != nil { t.Fatal(err) @@ -45,3 +31,14 @@ spec: t.Fatal(err) } } + +func mustReadFileString(t *testing.T, path string) string { + t.Helper() + + data, err := os.ReadFile(path) + if err != nil { + t.Fatal(err) + } + + return string(data) +} diff --git a/pkg/admission/testdata/in.yaml b/pkg/admission/testdata/in.yaml new file mode 100644 index 0000000..035a4b0 --- /dev/null +++ b/pkg/admission/testdata/in.yaml @@ -0,0 +1,20 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: nginx + labels: + app: nginx +spec: + selector: + matchLabels: + app: nginx + template: + metadata: + labels: + app: nginx + spec: + containers: + - image: nginx + name: nginx + ports: + - containerPort: 8080