mirror of https://github.com/k3s-io/k3s
Merge pull request #49168 from crimsonfaith91/apps-v1beta2
Automatic merge from submit-queue StatefulSet scale subresource **What this PR does / why we need it**: This PR implements scale subresource for StatefulSet. **Which issue this PR fixes** *(optional, in `fixes #<issue number>(, fixes #<issue_number>, ...)` format, will close that issue when PR gets merged)*: fixes #46005 **Special notes for your reviewer**: **Release note**: ```release-note StatefulSet uses scale subresource when scaling in accord with ReplicationController, ReplicaSet, and Deployment implementations. ``` **Feature Checklist**: - [x] Introduce Registry interface for storage purpose - [x] Introduce `ScaleREST New(), Get() and Update()` utility functions - [x] Create a `ScaleREST` object at `NewREST()` and return it - [x] Enable scale subresource by adding `/scale` field to the storage map **Testing Checklist**: - Unit testing - [x] Modify `newStorage()` to call `NewStorage()`, and change all unit tests accordingly - [x] Add unit tests for `ScaleREST Get() and Update()` utility functions - [x] Add missing unit test for `ShortNames` - Manual testing - [x] Verify existence of the subresource using `kubectl proxy` command - [x] Modify the subresource using `curl` via `POST` - e2e testing - [x] Add e2e tests using `RESTClient`pull/6/head
commit
0967f9560a
|
@ -21339,6 +21339,160 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"/apis/apps/v1beta1/namespaces/{namespace}/statefulsets/{name}/scale": {
|
||||
"get": {
|
||||
"description": "read scale of the specified StatefulSet",
|
||||
"consumes": [
|
||||
"*/*"
|
||||
],
|
||||
"produces": [
|
||||
"application/json",
|
||||
"application/yaml",
|
||||
"application/vnd.kubernetes.protobuf"
|
||||
],
|
||||
"schemes": [
|
||||
"https"
|
||||
],
|
||||
"tags": [
|
||||
"apps_v1beta1"
|
||||
],
|
||||
"operationId": "readAppsV1beta1NamespacedStatefulSetScale",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/io.k8s.api.apps.v1beta1.Scale"
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized"
|
||||
}
|
||||
},
|
||||
"x-kubernetes-action": "get",
|
||||
"x-kubernetes-group-version-kind": {
|
||||
"group": "apps",
|
||||
"kind": "Scale",
|
||||
"version": "v1beta1"
|
||||
}
|
||||
},
|
||||
"put": {
|
||||
"description": "replace scale of the specified StatefulSet",
|
||||
"consumes": [
|
||||
"*/*"
|
||||
],
|
||||
"produces": [
|
||||
"application/json",
|
||||
"application/yaml",
|
||||
"application/vnd.kubernetes.protobuf"
|
||||
],
|
||||
"schemes": [
|
||||
"https"
|
||||
],
|
||||
"tags": [
|
||||
"apps_v1beta1"
|
||||
],
|
||||
"operationId": "replaceAppsV1beta1NamespacedStatefulSetScale",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "body",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/io.k8s.api.apps.v1beta1.Scale"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/io.k8s.api.apps.v1beta1.Scale"
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized"
|
||||
}
|
||||
},
|
||||
"x-kubernetes-action": "put",
|
||||
"x-kubernetes-group-version-kind": {
|
||||
"group": "apps",
|
||||
"kind": "Scale",
|
||||
"version": "v1beta1"
|
||||
}
|
||||
},
|
||||
"patch": {
|
||||
"description": "partially update scale of the specified StatefulSet",
|
||||
"consumes": [
|
||||
"application/json-patch+json",
|
||||
"application/merge-patch+json",
|
||||
"application/strategic-merge-patch+json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json",
|
||||
"application/yaml",
|
||||
"application/vnd.kubernetes.protobuf"
|
||||
],
|
||||
"schemes": [
|
||||
"https"
|
||||
],
|
||||
"tags": [
|
||||
"apps_v1beta1"
|
||||
],
|
||||
"operationId": "patchAppsV1beta1NamespacedStatefulSetScale",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "body",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Patch"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/io.k8s.api.apps.v1beta1.Scale"
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized"
|
||||
}
|
||||
},
|
||||
"x-kubernetes-action": "patch",
|
||||
"x-kubernetes-group-version-kind": {
|
||||
"group": "apps",
|
||||
"kind": "Scale",
|
||||
"version": "v1beta1"
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"uniqueItems": true,
|
||||
"type": "string",
|
||||
"description": "name of the Scale",
|
||||
"name": "name",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"uniqueItems": true,
|
||||
"type": "string",
|
||||
"description": "object name and auth scope, such as for teams and projects",
|
||||
"name": "namespace",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"uniqueItems": true,
|
||||
"type": "string",
|
||||
"description": "If 'true', then the output is pretty printed.",
|
||||
"name": "pretty",
|
||||
"in": "query"
|
||||
}
|
||||
]
|
||||
},
|
||||
"/apis/apps/v1beta1/namespaces/{namespace}/statefulsets/{name}/status": {
|
||||
"get": {
|
||||
"description": "read status of the specified StatefulSet",
|
||||
|
@ -25360,6 +25514,160 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"/apis/apps/v1beta2/namespaces/{namespace}/statefulsets/{name}/scale": {
|
||||
"get": {
|
||||
"description": "read scale of the specified StatefulSet",
|
||||
"consumes": [
|
||||
"*/*"
|
||||
],
|
||||
"produces": [
|
||||
"application/json",
|
||||
"application/yaml",
|
||||
"application/vnd.kubernetes.protobuf"
|
||||
],
|
||||
"schemes": [
|
||||
"https"
|
||||
],
|
||||
"tags": [
|
||||
"apps_v1beta2"
|
||||
],
|
||||
"operationId": "readAppsV1beta2NamespacedStatefulSetScale",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/io.k8s.api.apps.v1beta2.Scale"
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized"
|
||||
}
|
||||
},
|
||||
"x-kubernetes-action": "get",
|
||||
"x-kubernetes-group-version-kind": {
|
||||
"group": "apps",
|
||||
"kind": "Scale",
|
||||
"version": "v1beta2"
|
||||
}
|
||||
},
|
||||
"put": {
|
||||
"description": "replace scale of the specified StatefulSet",
|
||||
"consumes": [
|
||||
"*/*"
|
||||
],
|
||||
"produces": [
|
||||
"application/json",
|
||||
"application/yaml",
|
||||
"application/vnd.kubernetes.protobuf"
|
||||
],
|
||||
"schemes": [
|
||||
"https"
|
||||
],
|
||||
"tags": [
|
||||
"apps_v1beta2"
|
||||
],
|
||||
"operationId": "replaceAppsV1beta2NamespacedStatefulSetScale",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "body",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/io.k8s.api.apps.v1beta2.Scale"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/io.k8s.api.apps.v1beta2.Scale"
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized"
|
||||
}
|
||||
},
|
||||
"x-kubernetes-action": "put",
|
||||
"x-kubernetes-group-version-kind": {
|
||||
"group": "apps",
|
||||
"kind": "Scale",
|
||||
"version": "v1beta2"
|
||||
}
|
||||
},
|
||||
"patch": {
|
||||
"description": "partially update scale of the specified StatefulSet",
|
||||
"consumes": [
|
||||
"application/json-patch+json",
|
||||
"application/merge-patch+json",
|
||||
"application/strategic-merge-patch+json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json",
|
||||
"application/yaml",
|
||||
"application/vnd.kubernetes.protobuf"
|
||||
],
|
||||
"schemes": [
|
||||
"https"
|
||||
],
|
||||
"tags": [
|
||||
"apps_v1beta2"
|
||||
],
|
||||
"operationId": "patchAppsV1beta2NamespacedStatefulSetScale",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "body",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Patch"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/io.k8s.api.apps.v1beta2.Scale"
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized"
|
||||
}
|
||||
},
|
||||
"x-kubernetes-action": "patch",
|
||||
"x-kubernetes-group-version-kind": {
|
||||
"group": "apps",
|
||||
"kind": "Scale",
|
||||
"version": "v1beta2"
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"uniqueItems": true,
|
||||
"type": "string",
|
||||
"description": "name of the Scale",
|
||||
"name": "name",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"uniqueItems": true,
|
||||
"type": "string",
|
||||
"description": "object name and auth scope, such as for teams and projects",
|
||||
"name": "namespace",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"uniqueItems": true,
|
||||
"type": "string",
|
||||
"description": "If 'true', then the output is pretty printed.",
|
||||
"name": "pretty",
|
||||
"in": "query"
|
||||
}
|
||||
]
|
||||
},
|
||||
"/apis/apps/v1beta2/namespaces/{namespace}/statefulsets/{name}/status": {
|
||||
"get": {
|
||||
"description": "read status of the specified StatefulSet",
|
||||
|
|
|
@ -2982,6 +2982,171 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/apis/apps/v1beta1/namespaces/{namespace}/statefulsets/{name}/scale",
|
||||
"description": "API at /apis/apps/v1beta1",
|
||||
"operations": [
|
||||
{
|
||||
"type": "v1beta1.Scale",
|
||||
"method": "GET",
|
||||
"summary": "read scale of the specified StatefulSet",
|
||||
"nickname": "readNamespacedStatefulSetScale",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"paramType": "query",
|
||||
"name": "pretty",
|
||||
"description": "If 'true', then the output is pretty printed.",
|
||||
"required": false,
|
||||
"allowMultiple": false
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"paramType": "path",
|
||||
"name": "namespace",
|
||||
"description": "object name and auth scope, such as for teams and projects",
|
||||
"required": true,
|
||||
"allowMultiple": false
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"paramType": "path",
|
||||
"name": "name",
|
||||
"description": "name of the Scale",
|
||||
"required": true,
|
||||
"allowMultiple": false
|
||||
}
|
||||
],
|
||||
"responseMessages": [
|
||||
{
|
||||
"code": 200,
|
||||
"message": "OK",
|
||||
"responseModel": "v1beta1.Scale"
|
||||
}
|
||||
],
|
||||
"produces": [
|
||||
"application/json",
|
||||
"application/yaml",
|
||||
"application/vnd.kubernetes.protobuf"
|
||||
],
|
||||
"consumes": [
|
||||
"*/*"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "v1beta1.Scale",
|
||||
"method": "PUT",
|
||||
"summary": "replace scale of the specified StatefulSet",
|
||||
"nickname": "replaceNamespacedStatefulSetScale",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"paramType": "query",
|
||||
"name": "pretty",
|
||||
"description": "If 'true', then the output is pretty printed.",
|
||||
"required": false,
|
||||
"allowMultiple": false
|
||||
},
|
||||
{
|
||||
"type": "v1beta1.Scale",
|
||||
"paramType": "body",
|
||||
"name": "body",
|
||||
"description": "",
|
||||
"required": true,
|
||||
"allowMultiple": false
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"paramType": "path",
|
||||
"name": "namespace",
|
||||
"description": "object name and auth scope, such as for teams and projects",
|
||||
"required": true,
|
||||
"allowMultiple": false
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"paramType": "path",
|
||||
"name": "name",
|
||||
"description": "name of the Scale",
|
||||
"required": true,
|
||||
"allowMultiple": false
|
||||
}
|
||||
],
|
||||
"responseMessages": [
|
||||
{
|
||||
"code": 200,
|
||||
"message": "OK",
|
||||
"responseModel": "v1beta1.Scale"
|
||||
}
|
||||
],
|
||||
"produces": [
|
||||
"application/json",
|
||||
"application/yaml",
|
||||
"application/vnd.kubernetes.protobuf"
|
||||
],
|
||||
"consumes": [
|
||||
"*/*"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "v1beta1.Scale",
|
||||
"method": "PATCH",
|
||||
"summary": "partially update scale of the specified StatefulSet",
|
||||
"nickname": "patchNamespacedStatefulSetScale",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"paramType": "query",
|
||||
"name": "pretty",
|
||||
"description": "If 'true', then the output is pretty printed.",
|
||||
"required": false,
|
||||
"allowMultiple": false
|
||||
},
|
||||
{
|
||||
"type": "v1.Patch",
|
||||
"paramType": "body",
|
||||
"name": "body",
|
||||
"description": "",
|
||||
"required": true,
|
||||
"allowMultiple": false
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"paramType": "path",
|
||||
"name": "namespace",
|
||||
"description": "object name and auth scope, such as for teams and projects",
|
||||
"required": true,
|
||||
"allowMultiple": false
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"paramType": "path",
|
||||
"name": "name",
|
||||
"description": "name of the Scale",
|
||||
"required": true,
|
||||
"allowMultiple": false
|
||||
}
|
||||
],
|
||||
"responseMessages": [
|
||||
{
|
||||
"code": 200,
|
||||
"message": "OK",
|
||||
"responseModel": "v1beta1.Scale"
|
||||
}
|
||||
],
|
||||
"produces": [
|
||||
"application/json",
|
||||
"application/yaml",
|
||||
"application/vnd.kubernetes.protobuf"
|
||||
],
|
||||
"consumes": [
|
||||
"application/json-patch+json",
|
||||
"application/merge-patch+json",
|
||||
"application/strategic-merge-patch+json"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/apis/apps/v1beta1/namespaces/{namespace}/statefulsets/{name}/status",
|
||||
"description": "API at /apis/apps/v1beta1",
|
||||
|
|
|
@ -4338,6 +4338,171 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/apis/apps/v1beta2/namespaces/{namespace}/statefulsets/{name}/scale",
|
||||
"description": "API at /apis/apps/v1beta2",
|
||||
"operations": [
|
||||
{
|
||||
"type": "v1beta2.Scale",
|
||||
"method": "GET",
|
||||
"summary": "read scale of the specified StatefulSet",
|
||||
"nickname": "readNamespacedStatefulSetScale",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"paramType": "query",
|
||||
"name": "pretty",
|
||||
"description": "If 'true', then the output is pretty printed.",
|
||||
"required": false,
|
||||
"allowMultiple": false
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"paramType": "path",
|
||||
"name": "namespace",
|
||||
"description": "object name and auth scope, such as for teams and projects",
|
||||
"required": true,
|
||||
"allowMultiple": false
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"paramType": "path",
|
||||
"name": "name",
|
||||
"description": "name of the Scale",
|
||||
"required": true,
|
||||
"allowMultiple": false
|
||||
}
|
||||
],
|
||||
"responseMessages": [
|
||||
{
|
||||
"code": 200,
|
||||
"message": "OK",
|
||||
"responseModel": "v1beta2.Scale"
|
||||
}
|
||||
],
|
||||
"produces": [
|
||||
"application/json",
|
||||
"application/yaml",
|
||||
"application/vnd.kubernetes.protobuf"
|
||||
],
|
||||
"consumes": [
|
||||
"*/*"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "v1beta2.Scale",
|
||||
"method": "PUT",
|
||||
"summary": "replace scale of the specified StatefulSet",
|
||||
"nickname": "replaceNamespacedStatefulSetScale",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"paramType": "query",
|
||||
"name": "pretty",
|
||||
"description": "If 'true', then the output is pretty printed.",
|
||||
"required": false,
|
||||
"allowMultiple": false
|
||||
},
|
||||
{
|
||||
"type": "v1beta2.Scale",
|
||||
"paramType": "body",
|
||||
"name": "body",
|
||||
"description": "",
|
||||
"required": true,
|
||||
"allowMultiple": false
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"paramType": "path",
|
||||
"name": "namespace",
|
||||
"description": "object name and auth scope, such as for teams and projects",
|
||||
"required": true,
|
||||
"allowMultiple": false
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"paramType": "path",
|
||||
"name": "name",
|
||||
"description": "name of the Scale",
|
||||
"required": true,
|
||||
"allowMultiple": false
|
||||
}
|
||||
],
|
||||
"responseMessages": [
|
||||
{
|
||||
"code": 200,
|
||||
"message": "OK",
|
||||
"responseModel": "v1beta2.Scale"
|
||||
}
|
||||
],
|
||||
"produces": [
|
||||
"application/json",
|
||||
"application/yaml",
|
||||
"application/vnd.kubernetes.protobuf"
|
||||
],
|
||||
"consumes": [
|
||||
"*/*"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "v1beta2.Scale",
|
||||
"method": "PATCH",
|
||||
"summary": "partially update scale of the specified StatefulSet",
|
||||
"nickname": "patchNamespacedStatefulSetScale",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"paramType": "query",
|
||||
"name": "pretty",
|
||||
"description": "If 'true', then the output is pretty printed.",
|
||||
"required": false,
|
||||
"allowMultiple": false
|
||||
},
|
||||
{
|
||||
"type": "v1.Patch",
|
||||
"paramType": "body",
|
||||
"name": "body",
|
||||
"description": "",
|
||||
"required": true,
|
||||
"allowMultiple": false
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"paramType": "path",
|
||||
"name": "namespace",
|
||||
"description": "object name and auth scope, such as for teams and projects",
|
||||
"required": true,
|
||||
"allowMultiple": false
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"paramType": "path",
|
||||
"name": "name",
|
||||
"description": "name of the Scale",
|
||||
"required": true,
|
||||
"allowMultiple": false
|
||||
}
|
||||
],
|
||||
"responseMessages": [
|
||||
{
|
||||
"code": 200,
|
||||
"message": "OK",
|
||||
"responseModel": "v1beta2.Scale"
|
||||
}
|
||||
],
|
||||
"produces": [
|
||||
"application/json",
|
||||
"application/yaml",
|
||||
"application/vnd.kubernetes.protobuf"
|
||||
],
|
||||
"consumes": [
|
||||
"application/json-patch+json",
|
||||
"application/merge-patch+json",
|
||||
"application/strategic-merge-patch+json"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/apis/apps/v1beta2/namespaces/{namespace}/statefulsets/{name}/status",
|
||||
"description": "API at /apis/apps/v1beta2",
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -63,9 +63,10 @@ func (p RESTStorageProvider) v1beta1Storage(apiResourceConfigSource serverstorag
|
|||
storage["deployments/scale"] = deploymentStorage.Scale
|
||||
}
|
||||
if apiResourceConfigSource.ResourceEnabled(version.WithResource("statefulsets")) {
|
||||
statefulsetStorage, statefulsetStatusStorage := statefulsetstore.NewREST(restOptionsGetter)
|
||||
storage["statefulsets"] = statefulsetStorage
|
||||
storage["statefulsets/status"] = statefulsetStatusStorage
|
||||
statefulSetStorage := statefulsetstore.NewStorage(restOptionsGetter)
|
||||
storage["statefulsets"] = statefulSetStorage.StatefulSet
|
||||
storage["statefulsets/status"] = statefulSetStorage.Status
|
||||
storage["statefulsets/scale"] = statefulSetStorage.Scale
|
||||
}
|
||||
if apiResourceConfigSource.ResourceEnabled(version.WithResource("controllerrevisions")) {
|
||||
historyStorage := controllerrevisionsstore.NewREST(restOptionsGetter)
|
||||
|
@ -86,9 +87,10 @@ func (p RESTStorageProvider) v1beta2Storage(apiResourceConfigSource serverstorag
|
|||
storage["deployments/scale"] = deploymentStorage.Scale
|
||||
}
|
||||
if apiResourceConfigSource.ResourceEnabled(version.WithResource("statefulsets")) {
|
||||
statefulsetStorage, statefulsetStatusStorage := statefulsetstore.NewREST(restOptionsGetter)
|
||||
storage["statefulsets"] = statefulsetStorage
|
||||
storage["statefulsets/status"] = statefulsetStatusStorage
|
||||
statefulSetStorage := statefulsetstore.NewStorage(restOptionsGetter)
|
||||
storage["statefulsets"] = statefulSetStorage.StatefulSet
|
||||
storage["statefulsets/status"] = statefulSetStorage.Status
|
||||
storage["statefulsets/scale"] = statefulSetStorage.Scale
|
||||
}
|
||||
if apiResourceConfigSource.ResourceEnabled(version.WithResource("daemonsets")) {
|
||||
daemonSetStorage, daemonSetStatusStorage := daemonsetstore.NewREST(restOptionsGetter)
|
||||
|
|
|
@ -12,6 +12,7 @@ go_library(
|
|||
name = "go_default_library",
|
||||
srcs = [
|
||||
"doc.go",
|
||||
"registry.go",
|
||||
"strategy.go",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
|
@ -20,8 +21,12 @@ go_library(
|
|||
"//pkg/apis/apps:go_default_library",
|
||||
"//pkg/apis/apps/validation:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/equality:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/internalversion:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/watch:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/storage/names:go_default_library",
|
||||
|
|
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
Copyright 2017 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.
|
||||
*/
|
||||
|
||||
package statefulset
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/watch"
|
||||
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
||||
"k8s.io/apiserver/pkg/registry/rest"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/apis/apps"
|
||||
)
|
||||
|
||||
// Registry is an interface for things that know how to store StatefulSets.
|
||||
type Registry interface {
|
||||
ListStatefulSets(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (*apps.StatefulSetList, error)
|
||||
WatchStatefulSets(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (watch.Interface, error)
|
||||
GetStatefulSet(ctx genericapirequest.Context, statefulSetID string, options *metav1.GetOptions) (*apps.StatefulSet, error)
|
||||
CreateStatefulSet(ctx genericapirequest.Context, statefulSet *apps.StatefulSet) (*apps.StatefulSet, error)
|
||||
UpdateStatefulSet(ctx genericapirequest.Context, statefulSet *apps.StatefulSet) (*apps.StatefulSet, error)
|
||||
DeleteStatefulSet(ctx genericapirequest.Context, statefulSetID string) error
|
||||
}
|
||||
|
||||
// storage puts strong typing around storage calls
|
||||
type storage struct {
|
||||
rest.StandardStorage
|
||||
}
|
||||
|
||||
// NewRegistry returns a new Registry interface for the given Storage. Any mismatched
|
||||
// types will panic.
|
||||
func NewRegistry(s rest.StandardStorage) Registry {
|
||||
return &storage{s}
|
||||
}
|
||||
|
||||
func (s *storage) ListStatefulSets(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (*apps.StatefulSetList, error) {
|
||||
if options != nil && options.FieldSelector != nil && !options.FieldSelector.Empty() {
|
||||
return nil, fmt.Errorf("field selector not supported yet")
|
||||
}
|
||||
obj, err := s.List(ctx, options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*apps.StatefulSetList), err
|
||||
}
|
||||
|
||||
func (s *storage) WatchStatefulSets(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (watch.Interface, error) {
|
||||
return s.Watch(ctx, options)
|
||||
}
|
||||
|
||||
func (s *storage) GetStatefulSet(ctx genericapirequest.Context, statefulSetID string, options *metav1.GetOptions) (*apps.StatefulSet, error) {
|
||||
obj, err := s.Get(ctx, statefulSetID, options)
|
||||
if err != nil {
|
||||
return nil, errors.NewNotFound(apps.Resource("statefulsets/scale"), statefulSetID)
|
||||
}
|
||||
return obj.(*apps.StatefulSet), nil
|
||||
}
|
||||
|
||||
func (s *storage) CreateStatefulSet(ctx genericapirequest.Context, statefulSet *apps.StatefulSet) (*apps.StatefulSet, error) {
|
||||
obj, err := s.Create(ctx, statefulSet, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*apps.StatefulSet), nil
|
||||
}
|
||||
|
||||
func (s *storage) UpdateStatefulSet(ctx genericapirequest.Context, statefulSet *apps.StatefulSet) (*apps.StatefulSet, error) {
|
||||
obj, _, err := s.Update(ctx, statefulSet.Name, rest.DefaultUpdatedObjectInfo(statefulSet, api.Scheme))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*apps.StatefulSet), nil
|
||||
}
|
||||
|
||||
func (s *storage) DeleteStatefulSet(ctx genericapirequest.Context, statefulSetID string) error {
|
||||
_, _, err := s.Delete(ctx, statefulSetID, nil)
|
||||
return err
|
||||
}
|
|
@ -16,10 +16,14 @@ go_test(
|
|||
deps = [
|
||||
"//pkg/api:go_default_library",
|
||||
"//pkg/apis/apps:go_default_library",
|
||||
"//pkg/apis/extensions:go_default_library",
|
||||
"//pkg/registry/registrytest:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/equality:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/fields:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/labels:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/diff:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/registry/generic:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library",
|
||||
|
@ -34,8 +38,11 @@ go_library(
|
|||
deps = [
|
||||
"//pkg/api:go_default_library",
|
||||
"//pkg/apis/apps:go_default_library",
|
||||
"//pkg/apis/extensions:go_default_library",
|
||||
"//pkg/apis/extensions/validation:go_default_library",
|
||||
"//pkg/registry/apps/statefulset:go_default_library",
|
||||
"//pkg/registry/cachesize:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
|
||||
|
|
|
@ -17,6 +17,9 @@ limitations under the License.
|
|||
package storage
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
||||
|
@ -24,23 +27,43 @@ import (
|
|||
genericregistry "k8s.io/apiserver/pkg/registry/generic/registry"
|
||||
"k8s.io/apiserver/pkg/registry/rest"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
appsapi "k8s.io/kubernetes/pkg/apis/apps"
|
||||
"k8s.io/kubernetes/pkg/apis/apps"
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
extvalidation "k8s.io/kubernetes/pkg/apis/extensions/validation"
|
||||
"k8s.io/kubernetes/pkg/registry/apps/statefulset"
|
||||
"k8s.io/kubernetes/pkg/registry/cachesize"
|
||||
)
|
||||
|
||||
// rest implements a RESTStorage for replication controllers against etcd
|
||||
// StatefulSetStorage includes dummy storage for StatefulSets, and their Status and Scale subresource.
|
||||
type StatefulSetStorage struct {
|
||||
StatefulSet *REST
|
||||
Status *StatusREST
|
||||
Scale *ScaleREST
|
||||
}
|
||||
|
||||
func NewStorage(optsGetter generic.RESTOptionsGetter) StatefulSetStorage {
|
||||
statefulSetRest, statefulSetStatusRest := NewREST(optsGetter)
|
||||
statefulSetRegistry := statefulset.NewRegistry(statefulSetRest)
|
||||
|
||||
return StatefulSetStorage{
|
||||
StatefulSet: statefulSetRest,
|
||||
Status: statefulSetStatusRest,
|
||||
Scale: &ScaleREST{registry: statefulSetRegistry},
|
||||
}
|
||||
}
|
||||
|
||||
// rest implements a RESTStorage for statefulsets against etcd
|
||||
type REST struct {
|
||||
*genericregistry.Store
|
||||
}
|
||||
|
||||
// NewREST returns a RESTStorage object that will work against replication controllers.
|
||||
// NewREST returns a RESTStorage object that will work against statefulsets.
|
||||
func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST) {
|
||||
store := &genericregistry.Store{
|
||||
Copier: api.Scheme,
|
||||
NewFunc: func() runtime.Object { return &appsapi.StatefulSet{} },
|
||||
NewListFunc: func() runtime.Object { return &appsapi.StatefulSetList{} },
|
||||
DefaultQualifiedResource: appsapi.Resource("statefulsets"),
|
||||
NewFunc: func() runtime.Object { return &apps.StatefulSet{} },
|
||||
NewListFunc: func() runtime.Object { return &apps.StatefulSetList{} },
|
||||
DefaultQualifiedResource: apps.Resource("statefulsets"),
|
||||
WatchCacheSize: cachesize.GetWatchCacheSizeByResource("statefulsets"),
|
||||
|
||||
CreateStrategy: statefulset.Strategy,
|
||||
|
@ -71,7 +94,7 @@ type StatusREST struct {
|
|||
}
|
||||
|
||||
func (r *StatusREST) New() runtime.Object {
|
||||
return &appsapi.StatefulSet{}
|
||||
return &apps.StatefulSet{}
|
||||
}
|
||||
|
||||
// Get retrieves the object from the storage. It is required to support Patch.
|
||||
|
@ -91,3 +114,88 @@ var _ rest.ShortNamesProvider = &REST{}
|
|||
func (r *REST) ShortNames() []string {
|
||||
return []string{"sts"}
|
||||
}
|
||||
|
||||
type ScaleREST struct {
|
||||
registry statefulset.Registry
|
||||
}
|
||||
|
||||
// ScaleREST implements Patcher
|
||||
var _ = rest.Patcher(&ScaleREST{})
|
||||
|
||||
// New creates a new Scale object
|
||||
func (r *ScaleREST) New() runtime.Object {
|
||||
return &extensions.Scale{}
|
||||
}
|
||||
|
||||
func (r *ScaleREST) Get(ctx genericapirequest.Context, name string, options *metav1.GetOptions) (runtime.Object, error) {
|
||||
ss, err := r.registry.GetStatefulSet(ctx, name, options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
scale, err := scaleFromStatefulSet(ss)
|
||||
if err != nil {
|
||||
return nil, errors.NewBadRequest(fmt.Sprintf("%v", err))
|
||||
}
|
||||
return scale, err
|
||||
}
|
||||
|
||||
func (r *ScaleREST) Update(ctx genericapirequest.Context, name string, objInfo rest.UpdatedObjectInfo) (runtime.Object, bool, error) {
|
||||
ss, err := r.registry.GetStatefulSet(ctx, name, &metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
oldScale, err := scaleFromStatefulSet(ss)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
obj, err := objInfo.UpdatedObject(ctx, oldScale)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
if obj == nil {
|
||||
return nil, false, errors.NewBadRequest(fmt.Sprintf("nil update passed to Scale"))
|
||||
}
|
||||
scale, ok := obj.(*extensions.Scale)
|
||||
if !ok {
|
||||
return nil, false, errors.NewBadRequest(fmt.Sprintf("wrong object passed to Scale update: %v", obj))
|
||||
}
|
||||
|
||||
if errs := extvalidation.ValidateScale(scale); len(errs) > 0 {
|
||||
return nil, false, errors.NewInvalid(extensions.Kind("Scale"), scale.Name, errs)
|
||||
}
|
||||
|
||||
ss.Spec.Replicas = scale.Spec.Replicas
|
||||
ss.ResourceVersion = scale.ResourceVersion
|
||||
ss, err = r.registry.UpdateStatefulSet(ctx, ss)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
newScale, err := scaleFromStatefulSet(ss)
|
||||
if err != nil {
|
||||
return nil, false, errors.NewBadRequest(fmt.Sprintf("%v", err))
|
||||
}
|
||||
return newScale, false, err
|
||||
}
|
||||
|
||||
// scaleFromStatefulSet returns a scale subresource for a statefulset.
|
||||
func scaleFromStatefulSet(ss *apps.StatefulSet) (*extensions.Scale, error) {
|
||||
return &extensions.Scale{
|
||||
// TODO: Create a variant of ObjectMeta type that only contains the fields below.
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: ss.Name,
|
||||
Namespace: ss.Namespace,
|
||||
UID: ss.UID,
|
||||
ResourceVersion: ss.ResourceVersion,
|
||||
CreationTimestamp: ss.CreationTimestamp,
|
||||
},
|
||||
Spec: extensions.ScaleSpec{
|
||||
Replicas: ss.Spec.Replicas,
|
||||
},
|
||||
Status: extensions.ScaleStatus{
|
||||
Replicas: ss.Status.Replicas,
|
||||
Selector: ss.Spec.Selector,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
|
|
@ -19,24 +19,28 @@ package storage
|
|||
import (
|
||||
"testing"
|
||||
|
||||
apiequality "k8s.io/apimachinery/pkg/api/equality"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/fields"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/util/diff"
|
||||
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
||||
"k8s.io/apiserver/pkg/registry/generic"
|
||||
"k8s.io/apiserver/pkg/registry/rest"
|
||||
etcdtesting "k8s.io/apiserver/pkg/storage/etcd/testing"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/apis/apps"
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
"k8s.io/kubernetes/pkg/registry/registrytest"
|
||||
)
|
||||
|
||||
// TODO: allow for global factory override
|
||||
func newStorage(t *testing.T) (*REST, *StatusREST, *etcdtesting.EtcdTestServer) {
|
||||
func newStorage(t *testing.T) (StatefulSetStorage, *etcdtesting.EtcdTestServer) {
|
||||
etcdStorage, server := registrytest.NewEtcdStorage(t, apps.GroupName)
|
||||
restOptions := generic.RESTOptions{StorageConfig: etcdStorage, Decorator: generic.UndecoratedStorage, DeleteCollectionWorkers: 1, ResourcePrefix: "statefulsets"}
|
||||
statefulSetStorage, statusStorage := NewREST(restOptions)
|
||||
return statefulSetStorage, statusStorage, server
|
||||
storage := NewStorage(restOptions)
|
||||
return storage, server
|
||||
}
|
||||
|
||||
// createStatefulSet is a helper function that returns a StatefulSet with the updated resource version.
|
||||
|
@ -83,11 +87,13 @@ func validNewStatefulSet() *apps.StatefulSet {
|
|||
}
|
||||
}
|
||||
|
||||
var validStatefulSet = *validNewStatefulSet()
|
||||
|
||||
func TestCreate(t *testing.T) {
|
||||
storage, _, server := newStorage(t)
|
||||
storage, server := newStorage(t)
|
||||
defer server.Terminate(t)
|
||||
defer storage.Store.DestroyFunc()
|
||||
test := registrytest.New(t, storage.Store)
|
||||
defer storage.StatefulSet.Store.DestroyFunc()
|
||||
test := registrytest.New(t, storage.StatefulSet.Store)
|
||||
ps := validNewStatefulSet()
|
||||
ps.ObjectMeta = metav1.ObjectMeta{}
|
||||
test.TestCreate(
|
||||
|
@ -100,13 +106,13 @@ func TestCreate(t *testing.T) {
|
|||
// TODO: Test updates to spec when we allow them.
|
||||
|
||||
func TestStatusUpdate(t *testing.T) {
|
||||
storage, statusStorage, server := newStorage(t)
|
||||
storage, server := newStorage(t)
|
||||
defer server.Terminate(t)
|
||||
defer storage.Store.DestroyFunc()
|
||||
defer storage.StatefulSet.Store.DestroyFunc()
|
||||
ctx := genericapirequest.WithNamespace(genericapirequest.NewContext(), metav1.NamespaceDefault)
|
||||
key := "/statefulsets/" + metav1.NamespaceDefault + "/foo"
|
||||
validStatefulSet := validNewStatefulSet()
|
||||
if err := storage.Storage.Create(ctx, key, validStatefulSet, nil, 0); err != nil {
|
||||
if err := storage.StatefulSet.Storage.Create(ctx, key, validStatefulSet, nil, 0); err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
update := apps.StatefulSet{
|
||||
|
@ -119,10 +125,10 @@ func TestStatusUpdate(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
if _, _, err := statusStorage.Update(ctx, update.Name, rest.DefaultUpdatedObjectInfo(&update, api.Scheme)); err != nil {
|
||||
if _, _, err := storage.Status.Update(ctx, update.Name, rest.DefaultUpdatedObjectInfo(&update, api.Scheme)); err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
obj, err := storage.Get(ctx, "foo", &metav1.GetOptions{})
|
||||
obj, err := storage.StatefulSet.Get(ctx, "foo", &metav1.GetOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
@ -137,34 +143,34 @@ func TestStatusUpdate(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestGet(t *testing.T) {
|
||||
storage, _, server := newStorage(t)
|
||||
storage, server := newStorage(t)
|
||||
defer server.Terminate(t)
|
||||
defer storage.Store.DestroyFunc()
|
||||
test := registrytest.New(t, storage.Store)
|
||||
defer storage.StatefulSet.Store.DestroyFunc()
|
||||
test := registrytest.New(t, storage.StatefulSet.Store)
|
||||
test.TestGet(validNewStatefulSet())
|
||||
}
|
||||
|
||||
func TestList(t *testing.T) {
|
||||
storage, _, server := newStorage(t)
|
||||
storage, server := newStorage(t)
|
||||
defer server.Terminate(t)
|
||||
defer storage.Store.DestroyFunc()
|
||||
test := registrytest.New(t, storage.Store)
|
||||
defer storage.StatefulSet.Store.DestroyFunc()
|
||||
test := registrytest.New(t, storage.StatefulSet.Store)
|
||||
test.TestList(validNewStatefulSet())
|
||||
}
|
||||
|
||||
func TestDelete(t *testing.T) {
|
||||
storage, _, server := newStorage(t)
|
||||
storage, server := newStorage(t)
|
||||
defer server.Terminate(t)
|
||||
defer storage.Store.DestroyFunc()
|
||||
test := registrytest.New(t, storage.Store)
|
||||
defer storage.StatefulSet.Store.DestroyFunc()
|
||||
test := registrytest.New(t, storage.StatefulSet.Store)
|
||||
test.TestDelete(validNewStatefulSet())
|
||||
}
|
||||
|
||||
func TestWatch(t *testing.T) {
|
||||
storage, _, server := newStorage(t)
|
||||
storage, server := newStorage(t)
|
||||
defer server.Terminate(t)
|
||||
defer storage.Store.DestroyFunc()
|
||||
test := registrytest.New(t, storage.Store)
|
||||
defer storage.StatefulSet.Store.DestroyFunc()
|
||||
test := registrytest.New(t, storage.StatefulSet.Store)
|
||||
test.TestWatch(
|
||||
validNewStatefulSet(),
|
||||
// matching labels
|
||||
|
@ -189,11 +195,104 @@ func TestWatch(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestCategories(t *testing.T) {
|
||||
storage, _, server := newStorage(t)
|
||||
storage, server := newStorage(t)
|
||||
defer server.Terminate(t)
|
||||
defer storage.Store.DestroyFunc()
|
||||
defer storage.StatefulSet.Store.DestroyFunc()
|
||||
expected := []string{"all"}
|
||||
registrytest.AssertCategories(t, storage, expected)
|
||||
registrytest.AssertCategories(t, storage.StatefulSet, expected)
|
||||
}
|
||||
|
||||
func TestShortNames(t *testing.T) {
|
||||
storage, server := newStorage(t)
|
||||
defer server.Terminate(t)
|
||||
defer storage.StatefulSet.Store.DestroyFunc()
|
||||
expected := []string{"sts"}
|
||||
registrytest.AssertShortNames(t, storage.StatefulSet, expected)
|
||||
}
|
||||
|
||||
func TestScaleGet(t *testing.T) {
|
||||
storage, server := newStorage(t)
|
||||
defer server.Terminate(t)
|
||||
defer storage.StatefulSet.Store.DestroyFunc()
|
||||
|
||||
name := "foo"
|
||||
|
||||
var sts apps.StatefulSet
|
||||
ctx := genericapirequest.WithNamespace(genericapirequest.NewContext(), metav1.NamespaceDefault)
|
||||
key := "/statefulsets/" + metav1.NamespaceDefault + "/" + name
|
||||
if err := storage.StatefulSet.Storage.Create(ctx, key, &validStatefulSet, &sts, 0); err != nil {
|
||||
t.Fatalf("error setting new statefulset (key: %s) %v: %v", key, validStatefulSet, err)
|
||||
}
|
||||
|
||||
want := &extensions.Scale{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: metav1.NamespaceDefault,
|
||||
UID: sts.UID,
|
||||
ResourceVersion: sts.ResourceVersion,
|
||||
CreationTimestamp: sts.CreationTimestamp,
|
||||
},
|
||||
Spec: extensions.ScaleSpec{
|
||||
Replicas: validStatefulSet.Spec.Replicas,
|
||||
},
|
||||
Status: extensions.ScaleStatus{
|
||||
Replicas: validStatefulSet.Status.Replicas,
|
||||
Selector: validStatefulSet.Spec.Selector,
|
||||
},
|
||||
}
|
||||
obj, err := storage.Scale.Get(ctx, name, &metav1.GetOptions{})
|
||||
got := obj.(*extensions.Scale)
|
||||
if err != nil {
|
||||
t.Fatalf("error fetching scale for %s: %v", name, err)
|
||||
}
|
||||
if !apiequality.Semantic.DeepEqual(got, want) {
|
||||
t.Errorf("unexpected scale: %s", diff.ObjectDiff(got, want))
|
||||
}
|
||||
}
|
||||
|
||||
func TestScaleUpdate(t *testing.T) {
|
||||
storage, server := newStorage(t)
|
||||
defer server.Terminate(t)
|
||||
defer storage.StatefulSet.Store.DestroyFunc()
|
||||
|
||||
name := "foo"
|
||||
|
||||
var sts apps.StatefulSet
|
||||
ctx := genericapirequest.WithNamespace(genericapirequest.NewContext(), metav1.NamespaceDefault)
|
||||
key := "/statefulsets/" + metav1.NamespaceDefault + "/" + name
|
||||
if err := storage.StatefulSet.Storage.Create(ctx, key, &validStatefulSet, &sts, 0); err != nil {
|
||||
t.Fatalf("error setting new statefulset (key: %s) %v: %v", key, validStatefulSet, err)
|
||||
}
|
||||
replicas := 12
|
||||
update := extensions.Scale{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: metav1.NamespaceDefault,
|
||||
},
|
||||
Spec: extensions.ScaleSpec{
|
||||
Replicas: int32(replicas),
|
||||
},
|
||||
}
|
||||
|
||||
if _, _, err := storage.Scale.Update(ctx, update.Name, rest.DefaultUpdatedObjectInfo(&update, api.Scheme)); err != nil {
|
||||
t.Fatalf("error updating scale %v: %v", update, err)
|
||||
}
|
||||
|
||||
obj, err := storage.Scale.Get(ctx, name, &metav1.GetOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("error fetching scale for %s: %v", name, err)
|
||||
}
|
||||
scale := obj.(*extensions.Scale)
|
||||
if scale.Spec.Replicas != int32(replicas) {
|
||||
t.Errorf("wrong replicas count expected: %d got: %d", replicas, scale.Spec.Replicas)
|
||||
}
|
||||
|
||||
update.ResourceVersion = sts.ResourceVersion
|
||||
update.Spec.Replicas = 15
|
||||
|
||||
if _, _, err = storage.Scale.Update(ctx, update.Name, rest.DefaultUpdatedObjectInfo(&update, api.Scheme)); err != nil && !errors.IsConflict(err) {
|
||||
t.Fatalf("unexpected error, expecting an update conflict but got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Test generation number.
|
||||
|
|
|
@ -843,6 +843,43 @@ var _ = SIGDescribe("StatefulSet", func() {
|
|||
return nil
|
||||
}, framework.StatefulPodTimeout, 2*time.Second).Should(BeNil())
|
||||
})
|
||||
|
||||
It("should have a working scale subresource", func() {
|
||||
By("Creating statefulset " + ssName + " in namespace " + ns)
|
||||
ss := framework.NewStatefulSet(ssName, ns, headlessSvcName, 1, nil, nil, labels)
|
||||
sst := framework.NewStatefulSetTester(c)
|
||||
sst.SetHttpProbe(ss)
|
||||
ss, err := c.AppsV1beta1().StatefulSets(ns).Create(ss)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
sst.WaitForRunningAndReady(*ss.Spec.Replicas, ss)
|
||||
ss = sst.WaitForStatus(ss)
|
||||
|
||||
By("getting scale subresource")
|
||||
scale := framework.NewStatefulSetScale(ss)
|
||||
scaleResult := &apps.Scale{}
|
||||
err = c.AppsV1beta1().RESTClient().Get().AbsPath("/apis/apps/v1beta1").Namespace(ns).Resource("statefulsets").Name(ssName).SubResource("scale").Do().Into(scale)
|
||||
if err != nil {
|
||||
framework.Failf("Failed to get scale subresource: %v", err)
|
||||
}
|
||||
Expect(scale.Spec.Replicas).To(Equal(int32(1)))
|
||||
Expect(scale.Status.Replicas).To(Equal(int32(1)))
|
||||
|
||||
By("updating a scale subresource")
|
||||
scale.ResourceVersion = "" //unconditionally update to 2 replicas
|
||||
scale.Spec.Replicas = 2
|
||||
err = c.AppsV1beta1().RESTClient().Put().AbsPath("/apis/apps/v1beta1").Namespace(ns).Resource("statefulsets").Name(ssName).SubResource("scale").Body(scale).Do().Into(scaleResult)
|
||||
if err != nil {
|
||||
framework.Failf("Failed to put scale subresource: %v", err)
|
||||
}
|
||||
Expect(scaleResult.Spec.Replicas).To(Equal(int32(2)))
|
||||
|
||||
By("verifying the statefulset Spec.Replicas was modified")
|
||||
ss, err = c.AppsV1beta1().StatefulSets(ns).Get(ssName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
framework.Failf("Failed to get statefulset resource: %v", err)
|
||||
}
|
||||
Expect(*(ss.Spec.Replicas)).To(Equal(int32(2)))
|
||||
})
|
||||
})
|
||||
|
||||
framework.KubeDescribe("Deploy clustered applications [Feature:StatefulSet] [Slow]", func() {
|
||||
|
|
|
@ -101,6 +101,7 @@ go_library(
|
|||
"//vendor/google.golang.org/api/compute/v1:go_default_library",
|
||||
"//vendor/google.golang.org/api/googleapi:go_default_library",
|
||||
"//vendor/k8s.io/api/apps/v1beta1:go_default_library",
|
||||
"//vendor/k8s.io/api/apps/v1beta2:go_default_library",
|
||||
"//vendor/k8s.io/api/authorization/v1beta1:go_default_library",
|
||||
"//vendor/k8s.io/api/batch/v1:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
|
|
|
@ -29,6 +29,7 @@ import (
|
|||
. "github.com/onsi/gomega"
|
||||
|
||||
apps "k8s.io/api/apps/v1beta1"
|
||||
appsV1beta2 "k8s.io/api/apps/v1beta2"
|
||||
"k8s.io/api/core/v1"
|
||||
apierrs "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
|
@ -224,7 +225,7 @@ func (s *StatefulSetTester) Scale(ss *apps.StatefulSet, count int32) error {
|
|||
|
||||
// UpdateReplicas updates the replicas of ss to count.
|
||||
func (s *StatefulSetTester) UpdateReplicas(ss *apps.StatefulSet, count int32) {
|
||||
s.update(ss.Namespace, ss.Name, func(ss *apps.StatefulSet) { ss.Spec.Replicas = &count })
|
||||
s.update(ss.Namespace, ss.Name, func(ss *apps.StatefulSet) { *(ss.Spec.Replicas) = count })
|
||||
}
|
||||
|
||||
// Restart scales ss to 0 and then back to its previous number of replicas.
|
||||
|
@ -812,6 +813,23 @@ func NewStatefulSet(name, ns, governingSvcName string, replicas int32, statefulP
|
|||
}
|
||||
}
|
||||
|
||||
// NewStatefulSetScale creates a new StatefulSet scale subresource and returns it
|
||||
func NewStatefulSetScale(ss *apps.StatefulSet) *appsV1beta2.Scale {
|
||||
return &appsV1beta2.Scale{
|
||||
// TODO: Create a variant of ObjectMeta type that only contains the fields below.
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: ss.Name,
|
||||
Namespace: ss.Namespace,
|
||||
},
|
||||
Spec: appsV1beta2.ScaleSpec{
|
||||
Replicas: *(ss.Spec.Replicas),
|
||||
},
|
||||
Status: appsV1beta2.ScaleStatus{
|
||||
Replicas: ss.Status.Replicas,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
var statefulPodRegex = regexp.MustCompile("(.*)-([0-9]+)$")
|
||||
|
||||
func getStatefulPodOrdinal(pod *v1.Pod) int {
|
||||
|
|
Loading…
Reference in New Issue