From 7a96057755142f7e431c67caaa1d9e6d972c76e8 Mon Sep 17 00:00:00 2001 From: Jordan Liggitt Date: Mon, 3 Jun 2019 10:15:57 -0400 Subject: [PATCH 1/2] Add helper script to regenerate API compatibility data --- hack/make-rules/update.sh | 1 + ...update-generated-api-compatibility-data.sh | 33 +++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100755 hack/update-generated-api-compatibility-data.sh diff --git a/hack/make-rules/update.sh b/hack/make-rules/update.sh index 5b45b2c6f69..79756a698d6 100755 --- a/hack/make-rules/update.sh +++ b/hack/make-rules/update.sh @@ -48,6 +48,7 @@ BASH_TARGETS=" update-codegen update-generated-runtime update-generated-device-plugin + update-generated-api-compatibility-data update-generated-docs update-generated-swagger-docs update-openapi-spec diff --git a/hack/update-generated-api-compatibility-data.sh b/hack/update-generated-api-compatibility-data.sh new file mode 100755 index 00000000000..94f5a7200b7 --- /dev/null +++ b/hack/update-generated-api-compatibility-data.sh @@ -0,0 +1,33 @@ +#!/usr/bin/env bash + +# Copyright 2019 The Kubernetes Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -o errexit +set -o nounset +set -o pipefail + +KUBE_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. +source "${KUBE_ROOT}/hack/lib/init.sh" + +kube::golang::setup_env + +# UPDATE_COMPATIBILITY_FIXTURE_DATA=true regenerates fixture data if needed. +# -run //HEAD only runs the test cases comparing against testdata for HEAD. +# We suppress the output because we are expecting to have changes. +# We suppress the test failure that occurs when there are changes. +UPDATE_COMPATIBILITY_FIXTURE_DATA=true go test ./vendor/k8s.io/api -run //HEAD >/dev/null 2>&1 || true + +# Now that we have regenerated data at HEAD, run the test without suppressing output or failures +go test ./vendor/k8s.io/api -run //HEAD -count=1 From 191b87c6291092b48591fad5dd30bd59ed723b69 Mon Sep 17 00:00:00 2001 From: Jordan Liggitt Date: Mon, 3 Jun 2019 12:00:23 -0400 Subject: [PATCH 2/2] Add readme for API compatibility data --- staging/src/k8s.io/api/testdata/README.md | 126 ++++++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 staging/src/k8s.io/api/testdata/README.md diff --git a/staging/src/k8s.io/api/testdata/README.md b/staging/src/k8s.io/api/testdata/README.md new file mode 100644 index 00000000000..eec9a8cd420 --- /dev/null +++ b/staging/src/k8s.io/api/testdata/README.md @@ -0,0 +1,126 @@ +# API serialization compatibility tests + +This directory tree contains serialized API objects in json, yaml, and protobuf formats. + +## Current version + +The `HEAD` subdirectory contains serialized API objects generated from the current commit: + +``` +HEAD/ + ...[json|yaml|pb] +``` + +To run serialization tests just for the current version: + +```sh +go test ./vendor/k8s.io/api -run //HEAD +``` + +All three formats of a given group/version/kind are expected to decode successfully to identical objects, +and to round-trip back to serialized form with identical bytes. +Adding new fields or API types *is* expected to modify these fixtures. To regenerate them, run: + +```sh +UPDATE_COMPATIBILITY_FIXTURE_DATA=true go test ./vendor/k8s.io/api -run //HEAD +``` + +## Previous versions + +The vX.Y.Z subdirectories contain serialized API objects from previous releases: + +``` +vX.Y.Z/ + ...[json|yaml|pb] +``` + +All three formats of a given group/version/kind are expected to decode successfully to identical objects, +and to round-trip back to serialized form with identical bytes. +Adding new fields to existing API types is *not* expected to require modifications to these fixtures. +This requires making optional scalar and struct fields pointers so that protobuf serialization omits them when not present. + +To run serialization tests just for a previous version, like `v1.14.0`: + +```sh +go test ./vendor/k8s.io/api -run //v1.14.0 +``` + +To run serialization tests for a particular group/version/kind, like `apps/v1` `Deployment`: +```sh +go test ./vendor/k8s.io/api -run /apps.v1.Deployment/ +``` + +Failures to decode, to round-trip identical bytes, or to decode identical objects from json/yaml/protobuf, +will output detailed errors about the differences encountered. Detailed errors about protobuf differences +requires `protoc` to be available on your `$PATH`. + +In exceptional cases, new non-pointer fields were added to existing API types that serialized zero values, +resulting in additional fields being output when round-tripping data from previous releases, and failing round-trip tests. + +To resolve this, a `.._after_roundtrip.[json|yaml|pb]` file containing the +expected data after roundtripping can be placed beside the serialized data file from a previous release. + +These `after_roundtrip` files are generated by running the failing round-trip tests with `UPDATE_COMPATIBILITY_FIXTURE_DATA=true` set. +The detailed diff from the test failure should be included in the commit message, along with a reference +to the change that caused the failure. Updates to these files is exceptional, and requires extremely close review +to ensure we are not breaking backwards compatibility with serialized data from previous releases. + +To see the diff between the original JSON/YAML data and the `...after_roundtrip...` files: + +```sh +cd vendor/k8s.io/api/testdata/v1.14.0/ +diff -u admission.k8s.io.v1beta1.AdmissionReview.json admission.k8s.io.v1beta1.AdmissionReview.after_roundtrip.json +diff -u admission.k8s.io.v1beta1.AdmissionReview.yaml admission.k8s.io.v1beta1.AdmissionReview.after_roundtrip.yaml +``` + +> ```diff +> --- admission.k8s.io.v1beta1.AdmissionReview.json 2019-06-02 20:21:03.000000000 -0400 +> +++ admission.k8s.io.v1beta1.AdmissionReview.after_roundtrip.json 2019-06-02 20:21:03.000000000 -0400 +> @@ -31,7 +31,8 @@ +> }, +> "object": {"apiVersion":"example.com/v1","kind":"CustomType","spec":{"replicas":1},"status":{"available":1}}, +> "oldObject": {"apiVersion":"example.com/v1","kind":"CustomType","spec":{"replicas":1},"status":{"available":1}}, +> - "dryRun": true +> + "dryRun": true, +> + "options": null +> }, +> "response": { +> "uid": "爟¼ªov鈶", +> ``` + +> ```diff +> --- admission.k8s.io.v1beta1.AdmissionReview.yaml 2019-06-02 20:21:03.000000000 -0400 +> +++ admission.k8s.io.v1beta1.AdmissionReview.after_roundtrip.yaml 2019-06-02 20:21:03.000000000 -0400 +> @@ -23,6 +23,7 @@ +> status: +> available: 1 +> operation: 祈¡ıŵDz廔ȇ{sŊƏp饏姥呄鐊 +> + options: null +> resource: +> group: "5" +> resource: "7" +> ``` + +To see the diff between the original proto data and the `...after_roundtrip...` file, you must have `protoc` available, +and strip off the leading four-byte kubernetes protobuf header to get standard protobuf that can be decoded: + +```sh +cd vendor/k8s.io/api/testdata/v1.14.0/ +diff -u \ + <(tail -c +5 admission.k8s.io.v1beta1.AdmissionReview.pb | protoc --decode_raw) \ + <(tail -c +5 admission.k8s.io.v1beta1.AdmissionReview.after_roundtrip.pb | protoc --decode_raw) +``` + +> ```diff +> --- /dev/fd/63 2019-06-03 11:56:12.000000000 -0400 +> +++ /dev/fd/62 2019-06-03 11:56:12.000000000 -0400 +> @@ -37,6 +37,8 @@ +> 1: "{\"apiVersion\":\"example.com/v1\",\"kind\":\"CustomType\",\"spec\":{\"replicas\":1},\"status\":{\"available\":1}}" +> } +> 11: 1 +> + 12: "" +> + 15: "" +> } +> 2 { +> 1: "\347\210\237\302\274\302\252ov\351\210\266" +> ``` \ No newline at end of file