diff --git a/hack/test-cmd.sh b/hack/test-cmd.sh index c396f8df00f..c276be950c6 100755 --- a/hack/test-cmd.sh +++ b/hack/test-cmd.sh @@ -178,7 +178,7 @@ kube::log::status "Starting kube-apiserver" # Admission Controllers to invoke prior to persisting objects in cluster ADMISSION_CONTROL="NamespaceLifecycle,LimitRanger,ResourceQuota" -KUBE_API_VERSIONS="v1,extensions/v1beta1" "${KUBE_OUTPUT_HOSTBIN}/kube-apiserver" \ +KUBE_API_VERSIONS="v1,autoscaling/v1,extensions/v1beta1" "${KUBE_OUTPUT_HOSTBIN}/kube-apiserver" \ --address="127.0.0.1" \ --public-address-override="127.0.0.1" \ --port="${API_PORT}" \ @@ -1548,7 +1548,7 @@ kube_api_versions=( v1 ) for version in "${kube_api_versions[@]}"; do - KUBE_API_VERSIONS="v1,extensions/v1beta1" runTests "${version}" + KUBE_API_VERSIONS="v1,autoscaling/v1,extensions/v1beta1" runTests "${version}" done kube::log::status "TEST PASSED" diff --git a/hack/test-go.sh b/hack/test-go.sh index 0d0148ba30c..7f55d71b6be 100755 --- a/hack/test-go.sh +++ b/hack/test-go.sh @@ -58,7 +58,7 @@ KUBE_GOVERALLS_BIN=${KUBE_GOVERALLS_BIN:-} # Lists of API Versions of each groups that should be tested, groups are # separated by comma, lists are separated by semicolon. e.g., # "v1,compute/v1alpha1,experimental/v1alpha2;v1,compute/v2,experimental/v1alpha3" -KUBE_TEST_API_VERSIONS=${KUBE_TEST_API_VERSIONS:-"v1,extensions/v1beta1,metrics/v1alpha1"} +KUBE_TEST_API_VERSIONS=${KUBE_TEST_API_VERSIONS:-"v1,extensions/v1beta1,metrics/v1alpha1;v1,autoscaling/v1,extensions/v1beta1,metrics/v1alpha1"} # once we have multiple group supports # Run tests with the standard (registry) and a custom etcd prefix # (kubernetes.io/registry). @@ -315,7 +315,7 @@ for (( i=0, j=0; ; )); do # KUBE_TEST_API sets the version of each group to be tested. KUBE_API_VERSIONS # register the groups/versions as supported by k8s. So KUBE_API_VERSIONS # needs to be the superset of KUBE_TEST_API. - KUBE_TEST_API="${apiVersion}" KUBE_API_VERSIONS="v1,extensions/v1beta1,componentconfig/v1alpha1,metrics/v1alpha1" ETCD_PREFIX=${etcdPrefix} runTests "$@" + KUBE_TEST_API="${apiVersion}" KUBE_API_VERSIONS="v1,autoscaling/v1,extensions/v1beta1,componentconfig/v1alpha1,metrics/v1alpha1" ETCD_PREFIX=${etcdPrefix} runTests "$@" i=${i}+1 j=${j}+1 if [[ i -eq ${apiVersionsCount} ]] && [[ j -eq ${etcdPrefixesCount} ]]; then diff --git a/hack/test-integration.sh b/hack/test-integration.sh index 10f5785f017..75266a344d9 100755 --- a/hack/test-integration.sh +++ b/hack/test-integration.sh @@ -29,7 +29,7 @@ source "${KUBE_ROOT}/hack/lib/init.sh" # "v1,compute/v1alpha1,experimental/v1alpha2;v1,compute/v2,experimental/v1alpha3" # TODO: It's going to be: # KUBE_TEST_API_VERSIONS=${KUBE_TEST_API_VERSIONS:-"v1,extensions/v1beta1"} -KUBE_TEST_API_VERSIONS=${KUBE_TEST_API_VERSIONS:-"v1,extensions/v1beta1"} +KUBE_TEST_API_VERSIONS=${KUBE_TEST_API_VERSIONS:-"v1,extensions/v1beta1;v1,autoscaling/v1,extensions/v1beta1"} # Give integration tests longer to run KUBE_TIMEOUT=${KUBE_TIMEOUT:--timeout 240s} @@ -52,22 +52,21 @@ runTests() { KUBE_RACE="" \ KUBE_TIMEOUT="${KUBE_TIMEOUT}" \ KUBE_TEST_API_VERSIONS="$1" \ - KUBE_API_VERSIONS="v1,extensions/v1beta1" \ + KUBE_API_VERSIONS="v1,autoscaling/v1,extensions/v1beta1" \ "${KUBE_ROOT}/hack/test-go.sh" test/integration kube::log::status "Running integration test scenario with watch cache on" - KUBE_API_VERSIONS="v1,extensions/v1beta1" KUBE_TEST_API_VERSIONS="$1" "${KUBE_OUTPUT_HOSTBIN}/integration" --v=${LOG_LEVEL} \ + KUBE_API_VERSIONS="v1,autoscaling/v1,extensions/v1beta1" KUBE_TEST_API_VERSIONS="$1" "${KUBE_OUTPUT_HOSTBIN}/integration" --v=${LOG_LEVEL} \ --max-concurrency="${KUBE_INTEGRATION_TEST_MAX_CONCURRENCY}" --watch-cache=true kube::log::status "Running integration test scenario with watch cache off" - KUBE_API_VERSIONS="v1,extensions/v1beta1" KUBE_TEST_API_VERSIONS="$1" "${KUBE_OUTPUT_HOSTBIN}/integration" --v=${LOG_LEVEL} \ + KUBE_API_VERSIONS="v1,autoscaling/v1,extensions/v1beta1" KUBE_TEST_API_VERSIONS="$1" "${KUBE_OUTPUT_HOSTBIN}/integration" --v=${LOG_LEVEL} \ --max-concurrency="${KUBE_INTEGRATION_TEST_MAX_CONCURRENCY}" --watch-cache=false - cleanup } -KUBE_API_VERSIONS="v1,extensions/v1beta1" "${KUBE_ROOT}/hack/build-go.sh" "$@" cmd/integration +KUBE_API_VERSIONS="v1,autoscaling/v1,extensions/v1beta1" "${KUBE_ROOT}/hack/build-go.sh" "$@" cmd/integration # Run cleanup to stop etcd on interrupt or other kill signal. trap cleanup EXIT diff --git a/pkg/api/testapi/testapi.go b/pkg/api/testapi/testapi.go index d3fc91aae27..9170052e3b6 100644 --- a/pkg/api/testapi/testapi.go +++ b/pkg/api/testapi/testapi.go @@ -26,19 +26,22 @@ import ( "k8s.io/kubernetes/pkg/api/meta" "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/apimachinery/registered" + "k8s.io/kubernetes/pkg/apis/autoscaling" "k8s.io/kubernetes/pkg/apis/extensions" "k8s.io/kubernetes/pkg/runtime" _ "k8s.io/kubernetes/pkg/api/install" + _ "k8s.io/kubernetes/pkg/apis/autoscaling/install" _ "k8s.io/kubernetes/pkg/apis/componentconfig/install" _ "k8s.io/kubernetes/pkg/apis/extensions/install" _ "k8s.io/kubernetes/pkg/apis/metrics/install" ) var ( - Groups = make(map[string]TestGroup) - Default TestGroup - Extensions TestGroup + Groups = make(map[string]TestGroup) + Default TestGroup + Autoscaling TestGroup + Extensions TestGroup ) type TestGroup struct { @@ -69,6 +72,12 @@ func init() { internalGroupVersion: api.SchemeGroupVersion, } } + if _, ok := Groups[autoscaling.GroupName]; !ok { + Groups[autoscaling.GroupName] = TestGroup{ + externalGroupVersion: unversioned.GroupVersion{Group: autoscaling.GroupName, Version: registered.GroupOrDie(autoscaling.GroupName).GroupVersion.Version}, + internalGroupVersion: autoscaling.SchemeGroupVersion, + } + } if _, ok := Groups[extensions.GroupName]; !ok { Groups[extensions.GroupName] = TestGroup{ externalGroupVersion: unversioned.GroupVersion{Group: extensions.GroupName, Version: registered.GroupOrDie(extensions.GroupName).GroupVersion.Version}, @@ -77,6 +86,7 @@ func init() { } Default = Groups[api.GroupName] + Autoscaling = Groups[autoscaling.GroupName] Extensions = Groups[extensions.GroupName] } diff --git a/pkg/api/testapi/testapi_test.go b/pkg/api/testapi/testapi_test.go index 7b8bb02ba36..7d9ad0ec0d9 100644 --- a/pkg/api/testapi/testapi_test.go +++ b/pkg/api/testapi/testapi_test.go @@ -100,9 +100,8 @@ func TestV1EncodeDecodeStatus(t *testing.T) { } } -func TestExperimentalEncodeDecodeStatus(t *testing.T) { - extensionCodec := Extensions.Codec() - encoded, err := runtime.Encode(extensionCodec, status) +func testEncodeDecodeStatus(t *testing.T, codec runtime.Codec) { + encoded, err := runtime.Encode(codec, status) if err != nil { t.Errorf("unexpected error: %v", err) } @@ -116,7 +115,7 @@ func TestExperimentalEncodeDecodeStatus(t *testing.T) { if typeMeta.APIVersion != "v1" { t.Errorf("APIVersion is not set to \"\". Got %s", encoded) } - decoded, err := runtime.Decode(extensionCodec, encoded) + decoded, err := runtime.Decode(codec, encoded) if err != nil { t.Errorf("unexpected error: %v", err) } @@ -124,3 +123,11 @@ func TestExperimentalEncodeDecodeStatus(t *testing.T) { t.Errorf("expected: %v, got: %v", status, decoded) } } + +func TestAutoscalingEncodeDecodeStatus(t *testing.T) { + testEncodeDecodeStatus(t, Autoscaling.Codec()) +} + +func TestExperimentalEncodeDecodeStatus(t *testing.T) { + testEncodeDecodeStatus(t, Extensions.Codec()) +} diff --git a/pkg/api/testing/fuzzer.go b/pkg/api/testing/fuzzer.go index 4375d26619d..092802d111f 100644 --- a/pkg/api/testing/fuzzer.go +++ b/pkg/api/testing/fuzzer.go @@ -397,6 +397,10 @@ func FuzzerFor(t *testing.T, version unversioned.GroupVersion, src rand.Source) s.MinReplicas = &minReplicas s.CPUUtilization = &extensions.CPUTargetUtilization{TargetPercentage: int(int32(c.RandUint64()))} }, + func(s *extensions.SubresourceReference, c fuzz.Continue) { + c.FuzzNoCustom(s) // fuzz self without calling this function again + s.Subresource = "scale" + }, func(psp *extensions.PodSecurityPolicySpec, c fuzz.Continue) { c.FuzzNoCustom(psp) // fuzz self without calling this function again userTypes := []extensions.RunAsUserStrategy{extensions.RunAsUserStrategyMustRunAsNonRoot, extensions.RunAsUserStrategyMustRunAs, extensions.RunAsUserStrategyRunAsAny} diff --git a/pkg/client/unversioned/horizontalpodautoscaler_test.go b/pkg/client/unversioned/horizontalpodautoscaler_test.go index bd4c06c8259..b35d258af3e 100644 --- a/pkg/client/unversioned/horizontalpodautoscaler_test.go +++ b/pkg/client/unversioned/horizontalpodautoscaler_test.go @@ -27,6 +27,7 @@ import ( "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/testapi" + "k8s.io/kubernetes/pkg/apis/autoscaling" "k8s.io/kubernetes/pkg/apis/extensions" ) @@ -34,7 +35,19 @@ func getHorizontalPodAutoscalersResoureName() string { return "horizontalpodautoscalers" } -func TestHorizontalPodAutoscalerCreate(t *testing.T) { +func getClient(t *testing.T, c *simple.Client, ns, resourceGroup string) HorizontalPodAutoscalerInterface { + switch resourceGroup { + case autoscaling.GroupName: + return c.Setup(t).Autoscaling().HorizontalPodAutoscalers(ns) + case extensions.GroupName: + return c.Setup(t).Extensions().HorizontalPodAutoscalers(ns) + default: + t.Fatalf("Unknown group %v", resourceGroup) + } + return nil +} + +func testHorizontalPodAutoscalerCreate(t *testing.T, group testapi.TestGroup, resourceGroup string) { ns := api.NamespaceDefault horizontalPodAutoscaler := extensions.HorizontalPodAutoscaler{ ObjectMeta: api.ObjectMeta{ @@ -45,14 +58,15 @@ func TestHorizontalPodAutoscalerCreate(t *testing.T) { c := &simple.Client{ Request: simple.Request{ Method: "POST", - Path: testapi.Extensions.ResourcePath(getHorizontalPodAutoscalersResoureName(), ns, ""), + Path: group.ResourcePath(getHorizontalPodAutoscalersResoureName(), ns, ""), Query: simple.BuildQueryValues(nil), Body: &horizontalPodAutoscaler, }, - Response: simple.Response{StatusCode: 200, Body: &horizontalPodAutoscaler}, + Response: simple.Response{StatusCode: 200, Body: &horizontalPodAutoscaler}, + ResourceGroup: resourceGroup, } - response, err := c.Setup(t).Extensions().HorizontalPodAutoscalers(ns).Create(&horizontalPodAutoscaler) + response, err := getClient(t, c, ns, resourceGroup).Create(&horizontalPodAutoscaler) defer c.Close() if err != nil { t.Fatalf("unexpected error: %v", err) @@ -60,7 +74,12 @@ func TestHorizontalPodAutoscalerCreate(t *testing.T) { c.Validate(t, response, err) } -func TestHorizontalPodAutoscalerGet(t *testing.T) { +func TestHorizontalPodAutoscalerCreate(t *testing.T) { + testHorizontalPodAutoscalerCreate(t, testapi.Extensions, extensions.GroupName) + testHorizontalPodAutoscalerCreate(t, testapi.Autoscaling, autoscaling.GroupName) +} + +func testHorizontalPodAutoscalerGet(t *testing.T, group testapi.TestGroup, resourceGroup string) { ns := api.NamespaceDefault horizontalPodAutoscaler := &extensions.HorizontalPodAutoscaler{ ObjectMeta: api.ObjectMeta{ @@ -71,19 +90,25 @@ func TestHorizontalPodAutoscalerGet(t *testing.T) { c := &simple.Client{ Request: simple.Request{ Method: "GET", - Path: testapi.Extensions.ResourcePath(getHorizontalPodAutoscalersResoureName(), ns, "abc"), + Path: group.ResourcePath(getHorizontalPodAutoscalersResoureName(), ns, "abc"), Query: simple.BuildQueryValues(nil), Body: nil, }, - Response: simple.Response{StatusCode: 200, Body: horizontalPodAutoscaler}, + Response: simple.Response{StatusCode: 200, Body: horizontalPodAutoscaler}, + ResourceGroup: resourceGroup, } - response, err := c.Setup(t).Extensions().HorizontalPodAutoscalers(ns).Get("abc") + response, err := getClient(t, c, ns, resourceGroup).Get("abc") defer c.Close() c.Validate(t, response, err) } -func TestHorizontalPodAutoscalerList(t *testing.T) { +func TestHorizontalPodAutoscalerGet(t *testing.T) { + testHorizontalPodAutoscalerGet(t, testapi.Extensions, extensions.GroupName) + testHorizontalPodAutoscalerGet(t, testapi.Autoscaling, autoscaling.GroupName) +} + +func testHorizontalPodAutoscalerList(t *testing.T, group testapi.TestGroup, resourceGroup string) { ns := api.NamespaceDefault horizontalPodAutoscalerList := &extensions.HorizontalPodAutoscalerList{ Items: []extensions.HorizontalPodAutoscaler{ @@ -98,18 +123,48 @@ func TestHorizontalPodAutoscalerList(t *testing.T) { c := &simple.Client{ Request: simple.Request{ Method: "GET", - Path: testapi.Extensions.ResourcePath(getHorizontalPodAutoscalersResoureName(), ns, ""), + Path: group.ResourcePath(getHorizontalPodAutoscalersResoureName(), ns, ""), Query: simple.BuildQueryValues(nil), Body: nil, }, - Response: simple.Response{StatusCode: 200, Body: horizontalPodAutoscalerList}, + Response: simple.Response{StatusCode: 200, Body: horizontalPodAutoscalerList}, + ResourceGroup: resourceGroup, } - response, err := c.Setup(t).Extensions().HorizontalPodAutoscalers(ns).List(api.ListOptions{}) + response, err := getClient(t, c, ns, resourceGroup).List(api.ListOptions{}) + defer c.Close() + c.Validate(t, response, err) +} + +func TestHorizontalPodAutoscalerList(t *testing.T) { + testHorizontalPodAutoscalerList(t, testapi.Extensions, extensions.GroupName) + testHorizontalPodAutoscalerList(t, testapi.Autoscaling, autoscaling.GroupName) +} + +func testHorizontalPodAutoscalerUpdate(t *testing.T, group testapi.TestGroup, resourceGroup string) { + ns := api.NamespaceDefault + horizontalPodAutoscaler := &extensions.HorizontalPodAutoscaler{ + ObjectMeta: api.ObjectMeta{ + Name: "abc", + Namespace: ns, + ResourceVersion: "1", + }, + } + c := &simple.Client{ + Request: simple.Request{Method: "PUT", Path: group.ResourcePath(getHorizontalPodAutoscalersResoureName(), ns, "abc"), Query: simple.BuildQueryValues(nil)}, + Response: simple.Response{StatusCode: 200, Body: horizontalPodAutoscaler}, + ResourceGroup: resourceGroup, + } + response, err := getClient(t, c, ns, resourceGroup).Update(horizontalPodAutoscaler) defer c.Close() c.Validate(t, response, err) } func TestHorizontalPodAutoscalerUpdate(t *testing.T) { + testHorizontalPodAutoscalerUpdate(t, testapi.Extensions, extensions.GroupName) + testHorizontalPodAutoscalerUpdate(t, testapi.Autoscaling, autoscaling.GroupName) +} + +func testHorizontalPodAutoscalerUpdateStatus(t *testing.T, group testapi.TestGroup, resourceGroup string) { ns := api.NamespaceDefault horizontalPodAutoscaler := &extensions.HorizontalPodAutoscaler{ ObjectMeta: api.ObjectMeta{ @@ -119,52 +174,52 @@ func TestHorizontalPodAutoscalerUpdate(t *testing.T) { }, } c := &simple.Client{ - Request: simple.Request{Method: "PUT", Path: testapi.Extensions.ResourcePath(getHorizontalPodAutoscalersResoureName(), ns, "abc"), Query: simple.BuildQueryValues(nil)}, - Response: simple.Response{StatusCode: 200, Body: horizontalPodAutoscaler}, + Request: simple.Request{Method: "PUT", Path: group.ResourcePath(getHorizontalPodAutoscalersResoureName(), ns, "abc") + "/status", Query: simple.BuildQueryValues(nil)}, + Response: simple.Response{StatusCode: 200, Body: horizontalPodAutoscaler}, + ResourceGroup: resourceGroup, } - response, err := c.Setup(t).Extensions().HorizontalPodAutoscalers(ns).Update(horizontalPodAutoscaler) + response, err := getClient(t, c, ns, resourceGroup).UpdateStatus(horizontalPodAutoscaler) defer c.Close() c.Validate(t, response, err) } func TestHorizontalPodAutoscalerUpdateStatus(t *testing.T) { + testHorizontalPodAutoscalerUpdateStatus(t, testapi.Extensions, extensions.GroupName) + testHorizontalPodAutoscalerUpdateStatus(t, testapi.Autoscaling, autoscaling.GroupName) +} + +func testHorizontalPodAutoscalerDelete(t *testing.T, group testapi.TestGroup, resourceGroup string) { ns := api.NamespaceDefault - horizontalPodAutoscaler := &extensions.HorizontalPodAutoscaler{ - ObjectMeta: api.ObjectMeta{ - Name: "abc", - Namespace: ns, - ResourceVersion: "1", - }, - } c := &simple.Client{ - Request: simple.Request{Method: "PUT", Path: testapi.Extensions.ResourcePath(getHorizontalPodAutoscalersResoureName(), ns, "abc") + "/status", Query: simple.BuildQueryValues(nil)}, - Response: simple.Response{StatusCode: 200, Body: horizontalPodAutoscaler}, + Request: simple.Request{Method: "DELETE", Path: group.ResourcePath(getHorizontalPodAutoscalersResoureName(), ns, "foo"), Query: simple.BuildQueryValues(nil)}, + Response: simple.Response{StatusCode: 200}, + ResourceGroup: resourceGroup, } - response, err := c.Setup(t).Extensions().HorizontalPodAutoscalers(ns).UpdateStatus(horizontalPodAutoscaler) + err := getClient(t, c, ns, resourceGroup).Delete("foo", nil) defer c.Close() - c.Validate(t, response, err) + c.Validate(t, nil, err) } func TestHorizontalPodAutoscalerDelete(t *testing.T) { - ns := api.NamespaceDefault + testHorizontalPodAutoscalerDelete(t, testapi.Extensions, extensions.GroupName) + testHorizontalPodAutoscalerDelete(t, testapi.Autoscaling, autoscaling.GroupName) +} + +func testHorizontalPodAutoscalerWatch(t *testing.T, group testapi.TestGroup, resourceGroup string) { c := &simple.Client{ - Request: simple.Request{Method: "DELETE", Path: testapi.Extensions.ResourcePath(getHorizontalPodAutoscalersResoureName(), ns, "foo"), Query: simple.BuildQueryValues(nil)}, - Response: simple.Response{StatusCode: 200}, + Request: simple.Request{ + Method: "GET", + Path: group.ResourcePathWithPrefix("watch", getHorizontalPodAutoscalersResoureName(), "", ""), + Query: url.Values{"resourceVersion": []string{}}}, + Response: simple.Response{StatusCode: 200}, + ResourceGroup: resourceGroup, } - err := c.Setup(t).Extensions().HorizontalPodAutoscalers(ns).Delete("foo", nil) + _, err := getClient(t, c, api.NamespaceAll, resourceGroup).Watch(api.ListOptions{}) defer c.Close() c.Validate(t, nil, err) } func TestHorizontalPodAutoscalerWatch(t *testing.T) { - c := &simple.Client{ - Request: simple.Request{ - Method: "GET", - Path: testapi.Extensions.ResourcePathWithPrefix("watch", getHorizontalPodAutoscalersResoureName(), "", ""), - Query: url.Values{"resourceVersion": []string{}}}, - Response: simple.Response{StatusCode: 200}, - } - _, err := c.Setup(t).Extensions().HorizontalPodAutoscalers(api.NamespaceAll).Watch(api.ListOptions{}) - defer c.Close() - c.Validate(t, nil, err) + testHorizontalPodAutoscalerWatch(t, testapi.Extensions, extensions.GroupName) + testHorizontalPodAutoscalerWatch(t, testapi.Autoscaling, autoscaling.GroupName) } diff --git a/pkg/client/unversioned/testclient/simple/simple_testclient.go b/pkg/client/unversioned/testclient/simple/simple_testclient.go index 08b979501d2..ab32f5295ee 100644 --- a/pkg/client/unversioned/testclient/simple/simple_testclient.go +++ b/pkg/client/unversioned/testclient/simple/simple_testclient.go @@ -66,13 +66,17 @@ type Client struct { // Maps from query arg key to validator. // If no validator is present, string equality is used. QueryValidator map[string]func(string, string) bool + + // If your object could exist in multiple groups, set this to + // correspond to the URL you're testing it with. + ResourceGroup string } func (c *Client) Setup(t *testing.T) *Client { c.handler = &utiltesting.FakeHandler{ StatusCode: c.Response.StatusCode, } - if responseBody := body(t, c.Response.Body, c.Response.RawBody); responseBody != nil { + if responseBody := c.body(t, c.Response.Body, c.Response.RawBody); responseBody != nil { c.handler.ResponseBody = *responseBody } c.server = httptest.NewServer(c.handler) @@ -142,7 +146,7 @@ func (c *Client) ValidateCommon(t *testing.T, err error) { return } - requestBody := body(t, c.Request.Body, c.Request.RawBody) + requestBody := c.body(t, c.Request.Body, c.Request.RawBody) actualQuery := c.handler.RequestReceived.URL.Query() t.Logf("got query: %v", actualQuery) t.Logf("path: %v", c.Request.Path) @@ -210,16 +214,20 @@ func validateFields(a, b string) bool { return sA.String() == sB.String() } -func body(t *testing.T, obj runtime.Object, raw *string) *string { +func (c *Client) body(t *testing.T, obj runtime.Object, raw *string) *string { if obj != nil { fqKind, err := api.Scheme.ObjectKind(obj) if err != nil { t.Errorf("unexpected encoding error: %v", err) } + groupName := fqKind.GroupVersion().Group + if c.ResourceGroup != "" { + groupName = c.ResourceGroup + } var bs []byte - g, found := testapi.Groups[fqKind.GroupVersion().Group] + g, found := testapi.Groups[groupName] if !found { - t.Errorf("Group %s is not registered in testapi", fqKind.GroupVersion().Group) + t.Errorf("Group %s is not registered in testapi", groupName) } bs, err = runtime.Encode(g.Codec(), obj) if err != nil { diff --git a/pkg/master/master_test.go b/pkg/master/master_test.go index 30e18b5f775..03f44567e99 100644 --- a/pkg/master/master_test.go +++ b/pkg/master/master_test.go @@ -35,6 +35,7 @@ import ( apiutil "k8s.io/kubernetes/pkg/api/util" utilnet "k8s.io/kubernetes/pkg/util/net" + "k8s.io/kubernetes/pkg/apis/autoscaling" "k8s.io/kubernetes/pkg/apis/extensions" "k8s.io/kubernetes/pkg/genericapiserver" "k8s.io/kubernetes/pkg/kubelet/client" @@ -66,11 +67,14 @@ func setUp(t *testing.T) (Master, *etcdtesting.EtcdTestServer, Config, *assert.A storageDestinations := genericapiserver.NewStorageDestinations() storageDestinations.AddAPIGroup( api.GroupName, etcdstorage.NewEtcdStorage(server.Client, testapi.Default.Codec(), etcdtest.PathPrefix(), false)) + storageDestinations.AddAPIGroup( + autoscaling.GroupName, etcdstorage.NewEtcdStorage(server.Client, testapi.Autoscaling.Codec(), etcdtest.PathPrefix(), false)) storageDestinations.AddAPIGroup( extensions.GroupName, etcdstorage.NewEtcdStorage(server.Client, testapi.Extensions.Codec(), etcdtest.PathPrefix(), false)) config.StorageDestinations = storageDestinations storageVersions[api.GroupName] = testapi.Default.GroupVersion().String() + storageVersions[autoscaling.GroupName] = testapi.Autoscaling.GroupVersion().String() storageVersions[extensions.GroupName] = testapi.Extensions.GroupVersion().String() config.StorageVersions = storageVersions config.PublicAddress = net.ParseIP("192.168.10.4") @@ -274,20 +278,40 @@ func TestDiscoveryAtAPIS(t *testing.T) { t.Fatalf("unexpected error: %v", err) } - extensionsGroupName := extensions.GroupName - extensionsVersions := []unversioned.GroupVersionForDiscovery{ + expectGroupNames := []string{autoscaling.GroupName, extensions.GroupName} + expectVersions := [][]unversioned.GroupVersionForDiscovery{ { - GroupVersion: testapi.Extensions.GroupVersion().String(), - Version: testapi.Extensions.GroupVersion().Version, + { + GroupVersion: testapi.Autoscaling.GroupVersion().String(), + Version: testapi.Autoscaling.GroupVersion().Version, + }, + }, + { + { + GroupVersion: testapi.Extensions.GroupVersion().String(), + Version: testapi.Extensions.GroupVersion().Version, + }, }, } - extensionsPreferredVersion := unversioned.GroupVersionForDiscovery{ - GroupVersion: config.StorageVersions[extensions.GroupName], - Version: apiutil.GetVersion(config.StorageVersions[extensions.GroupName]), + expectPreferredVersion := []unversioned.GroupVersionForDiscovery{ + { + GroupVersion: config.StorageVersions[autoscaling.GroupName], + Version: apiutil.GetVersion(config.StorageVersions[autoscaling.GroupName]), + }, + { + GroupVersion: config.StorageVersions[extensions.GroupName], + Version: apiutil.GetVersion(config.StorageVersions[extensions.GroupName]), + }, } - assert.Equal(extensionsGroupName, groupList.Groups[0].Name) - assert.Equal(extensionsVersions, groupList.Groups[0].Versions) - assert.Equal(extensionsPreferredVersion, groupList.Groups[0].PreferredVersion) + + assert.Equal(expectGroupNames[0], groupList.Groups[0].Name) + assert.Equal(expectGroupNames[1], groupList.Groups[1].Name) + + assert.Equal(expectVersions[0], groupList.Groups[0].Versions) + assert.Equal(expectVersions[1], groupList.Groups[1].Versions) + + assert.Equal(expectPreferredVersion[0], groupList.Groups[0].PreferredVersion) + assert.Equal(expectPreferredVersion[1], groupList.Groups[1].PreferredVersion) thirdPartyGV := unversioned.GroupVersionForDiscovery{GroupVersion: "company.com/v1", Version: "v1"} master.addThirdPartyResourceStorage("/apis/company.com/v1", nil, @@ -312,13 +336,19 @@ func TestDiscoveryAtAPIS(t *testing.T) { thirdPartyGroupName := "company.com" thirdPartyExpectVersions := []unversioned.GroupVersionForDiscovery{thirdPartyGV} - assert.Equal(2, len(groupList.Groups)) - assert.Equal(thirdPartyGroupName, groupList.Groups[0].Name) - assert.Equal(thirdPartyExpectVersions, groupList.Groups[0].Versions) - assert.Equal(thirdPartyGV, groupList.Groups[0].PreferredVersion) - assert.Equal(extensionsGroupName, groupList.Groups[1].Name) - assert.Equal(extensionsVersions, groupList.Groups[1].Versions) - assert.Equal(extensionsPreferredVersion, groupList.Groups[1].PreferredVersion) + assert.Equal(3, len(groupList.Groups)) + // autoscaling group + assert.Equal(expectGroupNames[0], groupList.Groups[0].Name) + assert.Equal(expectVersions[0], groupList.Groups[0].Versions) + assert.Equal(expectPreferredVersion[0], groupList.Groups[0].PreferredVersion) + // third party + assert.Equal(thirdPartyGroupName, groupList.Groups[1].Name) + assert.Equal(thirdPartyExpectVersions, groupList.Groups[1].Versions) + assert.Equal(thirdPartyGV, groupList.Groups[1].PreferredVersion) + // extensions group + assert.Equal(expectGroupNames[1], groupList.Groups[2].Name) + assert.Equal(expectVersions[1], groupList.Groups[2].Versions) + assert.Equal(expectPreferredVersion[1], groupList.Groups[2].PreferredVersion) } var versionsToTest = []string{"v1", "v3"} diff --git a/test/integration/auth_test.go b/test/integration/auth_test.go index ba7e1525838..32b1cf793f7 100644 --- a/test/integration/auth_test.go +++ b/test/integration/auth_test.go @@ -38,6 +38,7 @@ import ( "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/testapi" + "k8s.io/kubernetes/pkg/apis/autoscaling" "k8s.io/kubernetes/pkg/apis/extensions" "k8s.io/kubernetes/pkg/apiserver" "k8s.io/kubernetes/pkg/auth/authenticator" @@ -780,8 +781,9 @@ func TestAuthorizationAttributeDetermination(t *testing.T) { URL string expectedAttributes authorizer.Attributes }{ - "prefix/version/resource": {"GET", "/api/v1/pods", authorizer.AttributesRecord{APIGroup: api.GroupName, Resource: "pods"}}, - "prefix/group/version/resource": {"GET", "/apis/extensions/v1/pods", authorizer.AttributesRecord{APIGroup: extensions.GroupName, Resource: "pods"}}, + "prefix/version/resource": {"GET", "/api/v1/pods", authorizer.AttributesRecord{APIGroup: api.GroupName, Resource: "pods"}}, + "prefix/group/version/resource": {"GET", "/apis/extensions/v1/pods", authorizer.AttributesRecord{APIGroup: extensions.GroupName, Resource: "pods"}}, + "prefix/group/version/resource2": {"GET", "/apis/autoscaling/v1/horizontalpodautoscalers", authorizer.AttributesRecord{APIGroup: autoscaling.GroupName, Resource: "horizontalpodautoscalers"}}, } currentAuthorizationAttributesIndex := 0 diff --git a/test/integration/framework/etcd_utils.go b/test/integration/framework/etcd_utils.go index d02b97d8082..8270e423268 100644 --- a/test/integration/framework/etcd_utils.go +++ b/test/integration/framework/etcd_utils.go @@ -52,6 +52,13 @@ func NewEtcdStorage() storage.Interface { return etcdstorage.NewEtcdStorage(NewEtcdClient(), testapi.Default.Codec(), etcdtest.PathPrefix(), false) } +func NewAutoscalingEtcdStorage(client etcd.Client) storage.Interface { + if client == nil { + client = NewEtcdClient() + } + return etcdstorage.NewEtcdStorage(client, testapi.Autoscaling.Codec(), etcdtest.PathPrefix(), false) +} + func NewExtensionsEtcdStorage(client etcd.Client) storage.Interface { if client == nil { client = NewEtcdClient() diff --git a/test/integration/framework/master_utils.go b/test/integration/framework/master_utils.go index eb945bdf79c..2fb7a6e80f2 100644 --- a/test/integration/framework/master_utils.go +++ b/test/integration/framework/master_utils.go @@ -29,6 +29,7 @@ import ( "github.com/golang/glog" "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/testapi" + "k8s.io/kubernetes/pkg/apis/autoscaling" "k8s.io/kubernetes/pkg/apis/extensions" "k8s.io/kubernetes/pkg/apiserver" clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" @@ -150,11 +151,14 @@ func NewMasterConfig() *master.Config { etcdStorage := etcdstorage.NewEtcdStorage(etcdClient, testapi.Default.Codec(), etcdtest.PathPrefix(), false) storageVersions[api.GroupName] = testapi.Default.GroupVersion().String() + autoscalingEtcdStorage := NewAutoscalingEtcdStorage(etcdClient) + storageVersions[autoscaling.GroupName] = testapi.Autoscaling.GroupVersion().String() expEtcdStorage := NewExtensionsEtcdStorage(etcdClient) storageVersions[extensions.GroupName] = testapi.Extensions.GroupVersion().String() storageDestinations := genericapiserver.NewStorageDestinations() storageDestinations.AddAPIGroup(api.GroupName, etcdStorage) + storageDestinations.AddAPIGroup(autoscaling.GroupName, autoscalingEtcdStorage) storageDestinations.AddAPIGroup(extensions.GroupName, expEtcdStorage) return &master.Config{ diff --git a/test/integration/master_test.go b/test/integration/master_test.go index 91d9685e69c..15b6a43fde8 100644 --- a/test/integration/master_test.go +++ b/test/integration/master_test.go @@ -19,30 +19,42 @@ limitations under the License. package integration import ( + "bytes" "encoding/json" "io/ioutil" "net/http" + "strings" "testing" "github.com/ghodss/yaml" + "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/api/testapi" "k8s.io/kubernetes/test/integration/framework" ) -func TestExperimentalPrefix(t *testing.T) { +func testPrefix(t *testing.T, prefix string) { _, s := framework.RunAMaster(t) // TODO: Uncomment when fix #19254 // defer s.Close() - resp, err := http.Get(s.URL + "/apis/extensions/") + resp, err := http.Get(s.URL + prefix) if err != nil { - t.Fatalf("unexpected error getting experimental prefix: %v", err) + t.Fatalf("unexpected error getting %s prefix: %v", prefix, err) } if resp.StatusCode != http.StatusOK { t.Fatalf("got status %v instead of 200 OK", resp.StatusCode) } } +func TestAutoscalingPrefix(t *testing.T) { + testPrefix(t, "/apis/autoscaling/") +} + +func TestExtensionsPrefix(t *testing.T) { + testPrefix(t, "/apis/extensions/") +} + func TestWatchSucceedsWithoutArgs(t *testing.T) { _, s := framework.RunAMaster(t) // TODO: Uncomment when fix #19254 @@ -58,6 +70,81 @@ func TestWatchSucceedsWithoutArgs(t *testing.T) { resp.Body.Close() } +var hpaV1 string = ` +{ + "apiVersion": "autoscaling/v1", + "kind": "HorizontalPodAutoscaler", + "metadata": { + "name": "test-hpa", + "namespace": "default" + }, + "spec": { + "scaleTargetRef": { + "kind": "ReplicationController", + "name": "test-hpa", + "namespace": "default" + }, + "minReplicas": 1, + "maxReplicas": 10, + "targetCPUUtilizationPercentage": 50 + } +} +` + +func autoscalingPath(resource, namespace, name string) string { + return testapi.Autoscaling.ResourcePath(resource, namespace, name) +} + +func extensionsPath(resource, namespace, name string) string { + return testapi.Extensions.ResourcePath(resource, namespace, name) +} + +func TestAutoscalingGroupBackwardCompatibility(t *testing.T) { + _, s := framework.RunAMaster(t) + defer s.Close() + transport := http.DefaultTransport + + requests := []struct { + verb string + URL string + body string + expectedStatusCodes map[int]bool + expectedVersion string + }{ + {"POST", autoscalingPath("horizontalpodautoscalers", api.NamespaceDefault, ""), hpaV1, code201, ""}, + {"GET", autoscalingPath("horizontalpodautoscalers", api.NamespaceDefault, ""), "", code200, testapi.Autoscaling.GroupVersion().String()}, + {"GET", extensionsPath("horizontalpodautoscalers", api.NamespaceDefault, ""), "", code200, testapi.Extensions.GroupVersion().String()}, + } + + for _, r := range requests { + bodyBytes := bytes.NewReader([]byte(r.body)) + req, err := http.NewRequest(r.verb, s.URL+r.URL, bodyBytes) + if err != nil { + t.Logf("case %v", r) + t.Fatalf("unexpected error: %v", err) + } + func() { + resp, err := transport.RoundTrip(req) + defer resp.Body.Close() + if err != nil { + t.Logf("case %v", r) + t.Fatalf("unexpected error: %v", err) + } + b, _ := ioutil.ReadAll(resp.Body) + body := string(b) + if _, ok := r.expectedStatusCodes[resp.StatusCode]; !ok { + t.Logf("case %v", r) + t.Errorf("Expected status one of %v, but got %v", r.expectedStatusCodes, resp.StatusCode) + t.Errorf("Body: %v", body) + } + if !strings.Contains(body, "\"apiVersion\":\""+r.expectedVersion) { + t.Logf("case %v", r) + t.Errorf("Expected version %v, got body %v", r.expectedVersion, body) + } + }() + } +} + func TestAccept(t *testing.T) { _, s := framework.RunAMaster(t) // TODO: Uncomment when fix #19254