mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-05 18:24:07 +00:00
Merge pull request #127573 from benluddy/dynamic-golden-response-test
Add test for unintended changes to dynamic client response handling.
This commit is contained in:
commit
56071089e2
@ -17,7 +17,9 @@ limitations under the License.
|
|||||||
package dynamic_test
|
package dynamic_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
@ -32,6 +34,7 @@ import (
|
|||||||
"github.com/google/go-cmp/cmp"
|
"github.com/google/go-cmp/cmp"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
"k8s.io/client-go/dynamic"
|
"k8s.io/client-go/dynamic"
|
||||||
@ -246,3 +249,157 @@ func TestGoldenRequest(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type RoundTripperFunc func(*http.Request) (*http.Response, error)
|
||||||
|
|
||||||
|
func (f RoundTripperFunc) RoundTrip(r *http.Request) (*http.Response, error) {
|
||||||
|
return f(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestGoldenResponse tests that the objects returned from dynamic client methods, given a fixed
|
||||||
|
// HTTP response, are not changed unintentionally by changes to the client.
|
||||||
|
func TestGoldenResponse(t *testing.T) {
|
||||||
|
for _, tc := range []struct {
|
||||||
|
name string
|
||||||
|
response string // name of fixture containing a serialized HTTP1.1 response
|
||||||
|
do func(t *testing.T, client dynamic.ResourceInterface) interface{}
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "create",
|
||||||
|
response: "nonlist",
|
||||||
|
do: func(t *testing.T, client dynamic.ResourceInterface) interface{} {
|
||||||
|
got, err := client.Create(context.Background(), &unstructured.Unstructured{}, metav1.CreateOptions{})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return got.UnstructuredContent()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "update",
|
||||||
|
response: "nonlist",
|
||||||
|
do: func(t *testing.T, client dynamic.ResourceInterface) interface{} {
|
||||||
|
got, err := client.Update(context.Background(), &unstructured.Unstructured{Object: map[string]interface{}{"metadata": map[string]interface{}{"name": "name"}}}, metav1.UpdateOptions{})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return got.UnstructuredContent()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "updatestatus",
|
||||||
|
response: "nonlist",
|
||||||
|
do: func(t *testing.T, client dynamic.ResourceInterface) interface{} {
|
||||||
|
got, err := client.UpdateStatus(context.Background(), &unstructured.Unstructured{Object: map[string]interface{}{"metadata": map[string]interface{}{"name": "name"}}}, metav1.UpdateOptions{})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return got.UnstructuredContent()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "get",
|
||||||
|
response: "nonlist",
|
||||||
|
do: func(t *testing.T, client dynamic.ResourceInterface) interface{} {
|
||||||
|
got, err := client.Get(context.Background(), "name", metav1.GetOptions{})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return got.UnstructuredContent()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "list",
|
||||||
|
response: "list",
|
||||||
|
do: func(t *testing.T, client dynamic.ResourceInterface) interface{} {
|
||||||
|
got, err := client.List(context.Background(), metav1.ListOptions{})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return got.UnstructuredContent()
|
||||||
|
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "watch",
|
||||||
|
response: "events",
|
||||||
|
do: func(t *testing.T, client dynamic.ResourceInterface) interface{} {
|
||||||
|
w, err := client.Watch(context.Background(), metav1.ListOptions{})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer w.Stop()
|
||||||
|
|
||||||
|
var got []interface{}
|
||||||
|
for e := range w.ResultChan() {
|
||||||
|
u, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&e)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to convert watch event to unstructured content: %v", err)
|
||||||
|
}
|
||||||
|
got = append(got, u)
|
||||||
|
}
|
||||||
|
|
||||||
|
return got
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
parentTestName := t.Name()
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
client, err := dynamic.NewForConfig(&rest.Config{
|
||||||
|
|
||||||
|
Transport: RoundTripperFunc(func(request *http.Request) (*http.Response, error) {
|
||||||
|
fd, err := os.Open(filepath.Join("testdata", filepath.FromSlash(parentTestName), "responses", tc.response))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if err := fd.Close(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
response, err := http.ReadResponse(bufio.NewReader(fd), request)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
return response, nil
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
got := tc.do(t, client.Resource(schema.GroupVersionResource{}))
|
||||||
|
|
||||||
|
path := filepath.Join("testdata", filepath.FromSlash(t.Name()))
|
||||||
|
if os.Getenv("UPDATE_DYNAMIC_CLIENT_FIXTURES") == "true" {
|
||||||
|
fixture, err := json.Marshal(got)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := os.WriteFile(path, fixture, os.FileMode(0644)); err != nil {
|
||||||
|
t.Fatalf("failed to update fixture: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fixture, err := os.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var want interface{}
|
||||||
|
if err := json.Unmarshal(fixture, &want); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if diff := cmp.Diff(want, got); diff != "" {
|
||||||
|
t.Errorf("unexpected diff:\n%s", diff)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
2
staging/src/k8s.io/client-go/dynamic/testdata/TestGoldenResponse/.gitattributes
vendored
Normal file
2
staging/src/k8s.io/client-go/dynamic/testdata/TestGoldenResponse/.gitattributes
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
# disable end-of-line conversion for fixtures
|
||||||
|
* -text
|
1
staging/src/k8s.io/client-go/dynamic/testdata/TestGoldenResponse/create
vendored
Normal file
1
staging/src/k8s.io/client-go/dynamic/testdata/TestGoldenResponse/create
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
{"apiVersion":"v1","kind":"ServiceAccount","metadata":{"creationTimestamp":"2024-09-20T17:16:28Z","name":"foobar","namespace":"default","resourceVersion":"207","uid":"a6bb38b5-67ca-4d0d-bd87-da746a94c8a5"}}
|
1
staging/src/k8s.io/client-go/dynamic/testdata/TestGoldenResponse/get
vendored
Normal file
1
staging/src/k8s.io/client-go/dynamic/testdata/TestGoldenResponse/get
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
{"apiVersion":"v1","kind":"ServiceAccount","metadata":{"creationTimestamp":"2024-09-20T17:16:28Z","name":"foobar","namespace":"default","resourceVersion":"207","uid":"a6bb38b5-67ca-4d0d-bd87-da746a94c8a5"}}
|
1
staging/src/k8s.io/client-go/dynamic/testdata/TestGoldenResponse/list
vendored
Normal file
1
staging/src/k8s.io/client-go/dynamic/testdata/TestGoldenResponse/list
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
{"apiVersion":"v1","items":[{"apiVersion":"v1","kind":"ServiceAccount","metadata":{"creationTimestamp":"2024-09-20T17:16:28Z","name":"foobar","namespace":"default","resourceVersion":"207","uid":"a6bb38b5-67ca-4d0d-bd87-da746a94c8a5"}}],"kind":"ServiceAccountList","metadata":{"resourceVersion":"222"}}
|
17
staging/src/k8s.io/client-go/dynamic/testdata/TestGoldenResponse/responses/events
vendored
Normal file
17
staging/src/k8s.io/client-go/dynamic/testdata/TestGoldenResponse/responses/events
vendored
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
HTTP/1.1 200 OK
|
||||||
|
Audit-Id: 1ef157ba-f535-4398-8813-80d86604fa8b
|
||||||
|
Cache-Control: no-cache, private
|
||||||
|
Content-Type: application/json
|
||||||
|
X-Kubernetes-Pf-Flowschema-Uid: 6027abd0-aa72-4d3c-a2cb-42b534068f6d
|
||||||
|
X-Kubernetes-Pf-Prioritylevel-Uid: f72ba9d7-6df9-4dd9-8720-be5485197492
|
||||||
|
Date: Wed, 18 Sep 2024 15:17:06 GMT
|
||||||
|
Transfer-Encoding: chunked
|
||||||
|
|
||||||
|
e9
|
||||||
|
{"type":"ADDED","object":{"kind":"ServiceAccount","apiVersion":"v1","metadata":{"name":"foobar","namespace":"default","uid":"a1453396-7a5c-405a-93f9-ff44af8e7689","resourceVersion":"217","creationTimestamp":"2024-09-18T14:06:40Z"}}}
|
||||||
|
|
||||||
|
ba
|
||||||
|
{"type":"BOOKMARK","object":{"kind":"ServiceAccount","apiVersion":"v1","metadata":{"resourceVersion":"964","creationTimestamp":null,"annotations":{"k8s.io/initial-events-end":"true"}}}}
|
||||||
|
|
||||||
|
0
|
||||||
|
|
10
staging/src/k8s.io/client-go/dynamic/testdata/TestGoldenResponse/responses/list
vendored
Normal file
10
staging/src/k8s.io/client-go/dynamic/testdata/TestGoldenResponse/responses/list
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
HTTP/1.1 200 OK
|
||||||
|
Audit-Id: 683f3c08-8e36-406a-ba2f-e327b7f107f1
|
||||||
|
Cache-Control: no-cache, private
|
||||||
|
Content-Type: application/json
|
||||||
|
X-Kubernetes-Pf-Flowschema-Uid: f04daf25-4159-46d9-8547-499158737500
|
||||||
|
X-Kubernetes-Pf-Prioritylevel-Uid: a8711f26-ec13-47dd-ae6c-27c345737e12
|
||||||
|
Date: Fri, 20 Sep 2024 17:17:40 GMT
|
||||||
|
Content-Length: 260
|
||||||
|
|
||||||
|
{"kind":"ServiceAccountList","apiVersion":"v1","metadata":{"resourceVersion":"222"},"items":[{"metadata":{"name":"foobar","namespace":"default","uid":"a6bb38b5-67ca-4d0d-bd87-da746a94c8a5","resourceVersion":"207","creationTimestamp":"2024-09-20T17:16:28Z"}}]}
|
10
staging/src/k8s.io/client-go/dynamic/testdata/TestGoldenResponse/responses/nonlist
vendored
Normal file
10
staging/src/k8s.io/client-go/dynamic/testdata/TestGoldenResponse/responses/nonlist
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
HTTP/1.1 200 OK
|
||||||
|
Audit-Id: a050a5c6-5cc9-40b9-bf0e-7d178e72794a
|
||||||
|
Cache-Control: no-cache, private
|
||||||
|
Content-Type: application/json
|
||||||
|
X-Kubernetes-Pf-Flowschema-Uid: f04daf25-4159-46d9-8547-499158737500
|
||||||
|
X-Kubernetes-Pf-Prioritylevel-Uid: a8711f26-ec13-47dd-ae6c-27c345737e12
|
||||||
|
Date: Fri, 20 Sep 2024 17:17:33 GMT
|
||||||
|
Content-Length: 207
|
||||||
|
|
||||||
|
{"kind":"ServiceAccount","apiVersion":"v1","metadata":{"name":"foobar","namespace":"default","uid":"a6bb38b5-67ca-4d0d-bd87-da746a94c8a5","resourceVersion":"207","creationTimestamp":"2024-09-20T17:16:28Z"}}
|
1
staging/src/k8s.io/client-go/dynamic/testdata/TestGoldenResponse/update
vendored
Normal file
1
staging/src/k8s.io/client-go/dynamic/testdata/TestGoldenResponse/update
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
{"apiVersion":"v1","kind":"ServiceAccount","metadata":{"creationTimestamp":"2024-09-20T17:16:28Z","name":"foobar","namespace":"default","resourceVersion":"207","uid":"a6bb38b5-67ca-4d0d-bd87-da746a94c8a5"}}
|
1
staging/src/k8s.io/client-go/dynamic/testdata/TestGoldenResponse/updatestatus
vendored
Normal file
1
staging/src/k8s.io/client-go/dynamic/testdata/TestGoldenResponse/updatestatus
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
{"apiVersion":"v1","kind":"ServiceAccount","metadata":{"creationTimestamp":"2024-09-20T17:16:28Z","name":"foobar","namespace":"default","resourceVersion":"207","uid":"a6bb38b5-67ca-4d0d-bd87-da746a94c8a5"}}
|
1
staging/src/k8s.io/client-go/dynamic/testdata/TestGoldenResponse/watch
vendored
Normal file
1
staging/src/k8s.io/client-go/dynamic/testdata/TestGoldenResponse/watch
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
[{"object":{"apiVersion":"v1","kind":"ServiceAccount","metadata":{"creationTimestamp":"2024-09-18T14:06:40Z","name":"foobar","namespace":"default","resourceVersion":"217","uid":"a1453396-7a5c-405a-93f9-ff44af8e7689"}},"type":"ADDED"},{"object":{"apiVersion":"v1","kind":"ServiceAccount","metadata":{"annotations":{"k8s.io/initial-events-end":"true"},"creationTimestamp":null,"resourceVersion":"964"}},"type":"BOOKMARK"}]
|
Loading…
Reference in New Issue
Block a user