diff --git a/api/swagger-spec/autoscaling.json b/api/swagger-spec/autoscaling.json new file mode 100644 index 000000000000..5127cefcfda8 --- /dev/null +++ b/api/swagger-spec/autoscaling.json @@ -0,0 +1,30 @@ +{ + "swaggerVersion": "1.2", + "apiVersion": "", + "basePath": "https://10.10.10.10:6443", + "resourcePath": "/apis/autoscaling", + "apis": [ + { + "path": "/apis/autoscaling", + "description": "get information of a group", + "operations": [ + { + "type": "void", + "method": "GET", + "summary": "get information of a group", + "nickname": "getAPIGroup", + "parameters": [], + "produces": [ + "application/json", + "application/yaml" + ], + "consumes": [ + "application/json", + "application/yaml" + ] + } + ] + } + ], + "models": {} + } \ No newline at end of file diff --git a/api/swagger-spec/autoscaling_v1.json b/api/swagger-spec/autoscaling_v1.json new file mode 100644 index 000000000000..0704257ce344 --- /dev/null +++ b/api/swagger-spec/autoscaling_v1.json @@ -0,0 +1,1192 @@ +{ + "swaggerVersion": "1.2", + "apiVersion": "autoscaling/v1", + "basePath": "https://10.10.10.10:6443", + "resourcePath": "/apis/autoscaling/v1", + "apis": [ + { + "path": "/apis/autoscaling/v1/namespaces/{namespace}/horizontalpodautoscalers", + "description": "API at /apis/autoscaling/v1", + "operations": [ + { + "type": "v1.HorizontalPodAutoscalerList", + "method": "GET", + "summary": "list or watch objects of kind HorizontalPodAutoscaler", + "nickname": "listNamespacedHorizontalPodAutoscaler", + "parameters": [ + { + "type": "string", + "paramType": "query", + "name": "pretty", + "description": "If 'true', then the output is pretty printed.", + "required": false, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "labelSelector", + "description": "A selector to restrict the list of returned objects by their labels. Defaults to everything.", + "required": false, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "fieldSelector", + "description": "A selector to restrict the list of returned objects by their fields. Defaults to everything.", + "required": false, + "allowMultiple": false + }, + { + "type": "boolean", + "paramType": "query", + "name": "watch", + "description": "Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion.", + "required": false, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "resourceVersion", + "description": "When specified with a watch call, shows changes that occur after that particular version of a resource. Defaults to changes from the beginning of history.", + "required": false, + "allowMultiple": false + }, + { + "type": "integer", + "paramType": "query", + "name": "timeoutSeconds", + "description": "Timeout for the list/watch call.", + "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 + } + ], + "responseMessages": [ + { + "code": 200, + "message": "OK", + "responseModel": "v1.HorizontalPodAutoscalerList" + } + ], + "produces": [ + "application/json", + "application/yaml" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "v1.HorizontalPodAutoscaler", + "method": "POST", + "summary": "create a HorizontalPodAutoscaler", + "nickname": "createNamespacedHorizontalPodAutoscaler", + "parameters": [ + { + "type": "string", + "paramType": "query", + "name": "pretty", + "description": "If 'true', then the output is pretty printed.", + "required": false, + "allowMultiple": false + }, + { + "type": "v1.HorizontalPodAutoscaler", + "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 + } + ], + "responseMessages": [ + { + "code": 200, + "message": "OK", + "responseModel": "v1.HorizontalPodAutoscaler" + } + ], + "produces": [ + "application/json", + "application/yaml" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "unversioned.Status", + "method": "DELETE", + "summary": "delete collection of HorizontalPodAutoscaler", + "nickname": "deletecollectionNamespacedHorizontalPodAutoscaler", + "parameters": [ + { + "type": "string", + "paramType": "query", + "name": "pretty", + "description": "If 'true', then the output is pretty printed.", + "required": false, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "labelSelector", + "description": "A selector to restrict the list of returned objects by their labels. Defaults to everything.", + "required": false, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "fieldSelector", + "description": "A selector to restrict the list of returned objects by their fields. Defaults to everything.", + "required": false, + "allowMultiple": false + }, + { + "type": "boolean", + "paramType": "query", + "name": "watch", + "description": "Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion.", + "required": false, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "resourceVersion", + "description": "When specified with a watch call, shows changes that occur after that particular version of a resource. Defaults to changes from the beginning of history.", + "required": false, + "allowMultiple": false + }, + { + "type": "integer", + "paramType": "query", + "name": "timeoutSeconds", + "description": "Timeout for the list/watch call.", + "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 + } + ], + "responseMessages": [ + { + "code": 200, + "message": "OK", + "responseModel": "unversioned.Status" + } + ], + "produces": [ + "application/json", + "application/yaml" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/apis/autoscaling/v1/watch/namespaces/{namespace}/horizontalpodautoscalers", + "description": "API at /apis/autoscaling/v1", + "operations": [ + { + "type": "json.WatchEvent", + "method": "GET", + "summary": "watch individual changes to a list of HorizontalPodAutoscaler", + "nickname": "watchNamespacedHorizontalPodAutoscalerList", + "parameters": [ + { + "type": "string", + "paramType": "query", + "name": "pretty", + "description": "If 'true', then the output is pretty printed.", + "required": false, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "labelSelector", + "description": "A selector to restrict the list of returned objects by their labels. Defaults to everything.", + "required": false, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "fieldSelector", + "description": "A selector to restrict the list of returned objects by their fields. Defaults to everything.", + "required": false, + "allowMultiple": false + }, + { + "type": "boolean", + "paramType": "query", + "name": "watch", + "description": "Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion.", + "required": false, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "resourceVersion", + "description": "When specified with a watch call, shows changes that occur after that particular version of a resource. Defaults to changes from the beginning of history.", + "required": false, + "allowMultiple": false + }, + { + "type": "integer", + "paramType": "query", + "name": "timeoutSeconds", + "description": "Timeout for the list/watch call.", + "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 + } + ], + "responseMessages": [ + { + "code": 200, + "message": "OK", + "responseModel": "json.WatchEvent" + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/apis/autoscaling/v1/namespaces/{namespace}/horizontalpodautoscalers/{name}", + "description": "API at /apis/autoscaling/v1", + "operations": [ + { + "type": "v1.HorizontalPodAutoscaler", + "method": "GET", + "summary": "read the specified HorizontalPodAutoscaler", + "nickname": "readNamespacedHorizontalPodAutoscaler", + "parameters": [ + { + "type": "string", + "paramType": "query", + "name": "pretty", + "description": "If 'true', then the output is pretty printed.", + "required": false, + "allowMultiple": false + }, + { + "type": "boolean", + "paramType": "query", + "name": "export", + "description": "Should this value be exported. Export strips fields that a user can not specify.", + "required": false, + "allowMultiple": false + }, + { + "type": "boolean", + "paramType": "query", + "name": "exact", + "description": "Should the export be exact. Exact export maintains cluster-specific fields like 'Namespace'", + "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 HorizontalPodAutoscaler", + "required": true, + "allowMultiple": false + } + ], + "responseMessages": [ + { + "code": 200, + "message": "OK", + "responseModel": "v1.HorizontalPodAutoscaler" + } + ], + "produces": [ + "application/json", + "application/yaml" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "v1.HorizontalPodAutoscaler", + "method": "PUT", + "summary": "replace the specified HorizontalPodAutoscaler", + "nickname": "replaceNamespacedHorizontalPodAutoscaler", + "parameters": [ + { + "type": "string", + "paramType": "query", + "name": "pretty", + "description": "If 'true', then the output is pretty printed.", + "required": false, + "allowMultiple": false + }, + { + "type": "v1.HorizontalPodAutoscaler", + "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 HorizontalPodAutoscaler", + "required": true, + "allowMultiple": false + } + ], + "responseMessages": [ + { + "code": 200, + "message": "OK", + "responseModel": "v1.HorizontalPodAutoscaler" + } + ], + "produces": [ + "application/json", + "application/yaml" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "v1.HorizontalPodAutoscaler", + "method": "PATCH", + "summary": "partially update the specified HorizontalPodAutoscaler", + "nickname": "patchNamespacedHorizontalPodAutoscaler", + "parameters": [ + { + "type": "string", + "paramType": "query", + "name": "pretty", + "description": "If 'true', then the output is pretty printed.", + "required": false, + "allowMultiple": false + }, + { + "type": "unversioned.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 HorizontalPodAutoscaler", + "required": true, + "allowMultiple": false + } + ], + "responseMessages": [ + { + "code": 200, + "message": "OK", + "responseModel": "v1.HorizontalPodAutoscaler" + } + ], + "produces": [ + "application/json", + "application/yaml" + ], + "consumes": [ + "application/json-patch+json", + "application/merge-patch+json", + "application/strategic-merge-patch+json" + ] + }, + { + "type": "unversioned.Status", + "method": "DELETE", + "summary": "delete a HorizontalPodAutoscaler", + "nickname": "deleteNamespacedHorizontalPodAutoscaler", + "parameters": [ + { + "type": "string", + "paramType": "query", + "name": "pretty", + "description": "If 'true', then the output is pretty printed.", + "required": false, + "allowMultiple": false + }, + { + "type": "v1.DeleteOptions", + "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 HorizontalPodAutoscaler", + "required": true, + "allowMultiple": false + } + ], + "responseMessages": [ + { + "code": 200, + "message": "OK", + "responseModel": "unversioned.Status" + } + ], + "produces": [ + "application/json", + "application/yaml" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/apis/autoscaling/v1/watch/namespaces/{namespace}/horizontalpodautoscalers/{name}", + "description": "API at /apis/autoscaling/v1", + "operations": [ + { + "type": "json.WatchEvent", + "method": "GET", + "summary": "watch changes to an object of kind HorizontalPodAutoscaler", + "nickname": "watchNamespacedHorizontalPodAutoscaler", + "parameters": [ + { + "type": "string", + "paramType": "query", + "name": "pretty", + "description": "If 'true', then the output is pretty printed.", + "required": false, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "labelSelector", + "description": "A selector to restrict the list of returned objects by their labels. Defaults to everything.", + "required": false, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "fieldSelector", + "description": "A selector to restrict the list of returned objects by their fields. Defaults to everything.", + "required": false, + "allowMultiple": false + }, + { + "type": "boolean", + "paramType": "query", + "name": "watch", + "description": "Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion.", + "required": false, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "resourceVersion", + "description": "When specified with a watch call, shows changes that occur after that particular version of a resource. Defaults to changes from the beginning of history.", + "required": false, + "allowMultiple": false + }, + { + "type": "integer", + "paramType": "query", + "name": "timeoutSeconds", + "description": "Timeout for the list/watch call.", + "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 HorizontalPodAutoscaler", + "required": true, + "allowMultiple": false + } + ], + "responseMessages": [ + { + "code": 200, + "message": "OK", + "responseModel": "json.WatchEvent" + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/apis/autoscaling/v1/horizontalpodautoscalers", + "description": "API at /apis/autoscaling/v1", + "operations": [ + { + "type": "v1.HorizontalPodAutoscalerList", + "method": "GET", + "summary": "list or watch objects of kind HorizontalPodAutoscaler", + "nickname": "listHorizontalPodAutoscaler", + "parameters": [ + { + "type": "string", + "paramType": "query", + "name": "pretty", + "description": "If 'true', then the output is pretty printed.", + "required": false, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "labelSelector", + "description": "A selector to restrict the list of returned objects by their labels. Defaults to everything.", + "required": false, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "fieldSelector", + "description": "A selector to restrict the list of returned objects by their fields. Defaults to everything.", + "required": false, + "allowMultiple": false + }, + { + "type": "boolean", + "paramType": "query", + "name": "watch", + "description": "Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion.", + "required": false, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "resourceVersion", + "description": "When specified with a watch call, shows changes that occur after that particular version of a resource. Defaults to changes from the beginning of history.", + "required": false, + "allowMultiple": false + }, + { + "type": "integer", + "paramType": "query", + "name": "timeoutSeconds", + "description": "Timeout for the list/watch call.", + "required": false, + "allowMultiple": false + } + ], + "responseMessages": [ + { + "code": 200, + "message": "OK", + "responseModel": "v1.HorizontalPodAutoscalerList" + } + ], + "produces": [ + "application/json", + "application/yaml" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/apis/autoscaling/v1/watch/horizontalpodautoscalers", + "description": "API at /apis/autoscaling/v1", + "operations": [ + { + "type": "json.WatchEvent", + "method": "GET", + "summary": "watch individual changes to a list of HorizontalPodAutoscaler", + "nickname": "watchHorizontalPodAutoscalerList", + "parameters": [ + { + "type": "string", + "paramType": "query", + "name": "pretty", + "description": "If 'true', then the output is pretty printed.", + "required": false, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "labelSelector", + "description": "A selector to restrict the list of returned objects by their labels. Defaults to everything.", + "required": false, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "fieldSelector", + "description": "A selector to restrict the list of returned objects by their fields. Defaults to everything.", + "required": false, + "allowMultiple": false + }, + { + "type": "boolean", + "paramType": "query", + "name": "watch", + "description": "Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion.", + "required": false, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "resourceVersion", + "description": "When specified with a watch call, shows changes that occur after that particular version of a resource. Defaults to changes from the beginning of history.", + "required": false, + "allowMultiple": false + }, + { + "type": "integer", + "paramType": "query", + "name": "timeoutSeconds", + "description": "Timeout for the list/watch call.", + "required": false, + "allowMultiple": false + } + ], + "responseMessages": [ + { + "code": 200, + "message": "OK", + "responseModel": "json.WatchEvent" + } + ], + "produces": [ + "application/json" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/apis/autoscaling/v1/namespaces/{namespace}/horizontalpodautoscalers/{name}/status", + "description": "API at /apis/autoscaling/v1", + "operations": [ + { + "type": "v1.HorizontalPodAutoscaler", + "method": "PUT", + "summary": "replace status of the specified HorizontalPodAutoscaler", + "nickname": "replaceNamespacedHorizontalPodAutoscalerStatus", + "parameters": [ + { + "type": "string", + "paramType": "query", + "name": "pretty", + "description": "If 'true', then the output is pretty printed.", + "required": false, + "allowMultiple": false + }, + { + "type": "v1.HorizontalPodAutoscaler", + "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 HorizontalPodAutoscaler", + "required": true, + "allowMultiple": false + } + ], + "responseMessages": [ + { + "code": 200, + "message": "OK", + "responseModel": "v1.HorizontalPodAutoscaler" + } + ], + "produces": [ + "application/json", + "application/yaml" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/apis/autoscaling/v1", + "description": "API at /apis/autoscaling/v1", + "operations": [ + { + "type": "void", + "method": "GET", + "summary": "get available resources", + "nickname": "getAPIResources", + "parameters": [], + "produces": [ + "application/json", + "application/yaml" + ], + "consumes": [ + "application/json", + "application/yaml" + ] + } + ] + } + ], + "models": { + "v1.HorizontalPodAutoscalerList": { + "id": "v1.HorizontalPodAutoscalerList", + "description": "list of horizontal pod autoscaler objects.", + "required": [ + "items" + ], + "properties": { + "kind": { + "type": "string", + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds" + }, + "apiVersion": { + "type": "string", + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#resources" + }, + "metadata": { + "$ref": "unversioned.ListMeta", + "description": "Standard list metadata." + }, + "items": { + "type": "array", + "items": { + "$ref": "v1.HorizontalPodAutoscaler" + }, + "description": "list of horizontal pod autoscaler objects." + } + } + }, + "unversioned.ListMeta": { + "id": "unversioned.ListMeta", + "description": "ListMeta describes metadata that synthetic resources must have, including lists and various status objects. A resource may have only one of {ObjectMeta, ListMeta}.", + "properties": { + "selfLink": { + "type": "string", + "description": "SelfLink is a URL representing this object. Populated by the system. Read-only." + }, + "resourceVersion": { + "type": "string", + "description": "String that identifies the server's internal version of this object that can be used by clients to determine when objects have changed. Value must be treated as opaque by clients and passed unmodified back to the server. Populated by the system. Read-only. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#concurrency-control-and-consistency" + } + } + }, + "v1.HorizontalPodAutoscaler": { + "id": "v1.HorizontalPodAutoscaler", + "description": "configuration of a horizontal pod autoscaler.", + "properties": { + "kind": { + "type": "string", + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds" + }, + "apiVersion": { + "type": "string", + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#resources" + }, + "metadata": { + "$ref": "v1.ObjectMeta", + "description": "Standard object metadata. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata" + }, + "spec": { + "$ref": "v1.HorizontalPodAutoscalerSpec", + "description": "behaviour of autoscaler. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status." + }, + "status": { + "$ref": "v1.HorizontalPodAutoscalerStatus", + "description": "current information about the autoscaler." + } + } + }, + "v1.ObjectMeta": { + "id": "v1.ObjectMeta", + "description": "ObjectMeta is metadata that all persisted resources must have, which includes all objects users must create.", + "properties": { + "name": { + "type": "string", + "description": "Name must be unique within a namespace. Is required when creating resources, although some resources may allow a client to request the generation of an appropriate name automatically. Name is primarily intended for creation idempotence and configuration definition. Cannot be updated. More info: http://releases.k8s.io/HEAD/docs/user-guide/identifiers.md#names" + }, + "generateName": { + "type": "string", + "description": "GenerateName is an optional prefix, used by the server, to generate a unique name ONLY IF the Name field has not been provided. If this field is used, the name returned to the client will be different than the name passed. This value will also be combined with a unique suffix. The provided value has the same validation rules as the Name field, and may be truncated by the length of the suffix required to make the value unique on the server.\n\nIf this field is specified and the generated name exists, the server will NOT return a 409 - instead, it will either return 201 Created or 500 with Reason ServerTimeout indicating a unique name could not be found in the time allotted, and the client should retry (optionally after the time indicated in the Retry-After header).\n\nApplied only if Name is not specified. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#idempotency" + }, + "namespace": { + "type": "string", + "description": "Namespace defines the space within each name must be unique. An empty namespace is equivalent to the \"default\" namespace, but \"default\" is the canonical representation. Not all objects are required to be scoped to a namespace - the value of this field for those objects will be empty.\n\nMust be a DNS_LABEL. Cannot be updated. More info: http://releases.k8s.io/HEAD/docs/user-guide/namespaces.md" + }, + "selfLink": { + "type": "string", + "description": "SelfLink is a URL representing this object. Populated by the system. Read-only." + }, + "uid": { + "type": "string", + "description": "UID is the unique in time and space value for this object. It is typically generated by the server on successful creation of a resource and is not allowed to change on PUT operations.\n\nPopulated by the system. Read-only. More info: http://releases.k8s.io/HEAD/docs/user-guide/identifiers.md#uids" + }, + "resourceVersion": { + "type": "string", + "description": "An opaque value that represents the internal version of this object that can be used by clients to determine when objects have changed. May be used for optimistic concurrency, change detection, and the watch operation on a resource or set of resources. Clients must treat these values as opaque and passed unmodified back to the server. They may only be valid for a particular resource or set of resources.\n\nPopulated by the system. Read-only. Value must be treated as opaque by clients and . More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#concurrency-control-and-consistency" + }, + "generation": { + "type": "integer", + "format": "int64", + "description": "A sequence number representing a specific generation of the desired state. Currently only implemented by replication controllers. Populated by the system. Read-only." + }, + "creationTimestamp": { + "type": "string", + "description": "CreationTimestamp is a timestamp representing the server time when this object was created. It is not guaranteed to be set in happens-before order across separate operations. Clients may not set this value. It is represented in RFC3339 form and is in UTC.\n\nPopulated by the system. Read-only. Null for lists. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata" + }, + "deletionTimestamp": { + "type": "string", + "description": "DeletionTimestamp is RFC 3339 date and time at which this resource will be deleted. This field is set by the server when a graceful deletion is requested by the user, and is not directly settable by a client. The resource will be deleted (no longer visible from resource lists, and not reachable by name) after the time in this field. Once set, this value may not be unset or be set further into the future, although it may be shortened or the resource may be deleted prior to this time. For example, a user may request that a pod is deleted in 30 seconds. The Kubelet will react by sending a graceful termination signal to the containers in the pod. Once the resource is deleted in the API, the Kubelet will send a hard termination signal to the container. If not set, graceful deletion of the object has not been requested.\n\nPopulated by the system when a graceful deletion is requested. Read-only. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata" + }, + "deletionGracePeriodSeconds": { + "type": "integer", + "format": "int64", + "description": "Number of seconds allowed for this object to gracefully terminate before it will be removed from the system. Only set when deletionTimestamp is also set. May only be shortened. Read-only." + }, + "labels": { + "type": "any", + "description": "Map of string keys and values that can be used to organize and categorize (scope and select) objects. May match selectors of replication controllers and services. More info: http://releases.k8s.io/HEAD/docs/user-guide/labels.md" + }, + "annotations": { + "type": "any", + "description": "Annotations is an unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. They are not queryable and should be preserved when modifying objects. More info: http://releases.k8s.io/HEAD/docs/user-guide/annotations.md" + } + } + }, + "v1.HorizontalPodAutoscalerSpec": { + "id": "v1.HorizontalPodAutoscalerSpec", + "description": "specification of a horizontal pod autoscaler.", + "required": [ + "scaleTargetRef", + "maxReplicas" + ], + "properties": { + "scaleTargetRef": { + "$ref": "v1.CrossVersionObjectReference", + "description": "reference to scaled resource; horizontal pod autoscaler will learn the current resource consumption and will set the desired number of pods by using its Scale subresource." + }, + "minReplicas": { + "type": "integer", + "format": "int32", + "description": "lower limit for the number of pods that can be set by the autoscaler, default 1." + }, + "maxReplicas": { + "type": "integer", + "format": "int32", + "description": "upper limit for the number of pods that can be set by the autoscaler; cannot be smaller than MinReplicas." + }, + "targetCPUUtilizationPercentage": { + "type": "integer", + "format": "int32", + "description": "target average CPU utilization (represented as a percentage of requested CPU) over all the pods;" + } + } + }, + "v1.CrossVersionObjectReference": { + "id": "v1.CrossVersionObjectReference", + "description": "CrossVersionObjectReference contains enough information to let you identify the referred resource.", + "required": [ + "kind", + "name" + ], + "properties": { + "kind": { + "type": "string", + "description": "Kind of the referent; More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds\"" + }, + "name": { + "type": "string", + "description": "Name of the referent; More info: http://releases.k8s.io/HEAD/docs/user-guide/identifiers.md#names" + }, + "apiVersion": { + "type": "string", + "description": "API version of the referent" + } + } + }, + "v1.HorizontalPodAutoscalerStatus": { + "id": "v1.HorizontalPodAutoscalerStatus", + "description": "current status of a horizontal pod autoscaler", + "required": [ + "currentReplicas", + "desiredReplicas" + ], + "properties": { + "observedGeneration": { + "type": "integer", + "format": "int64", + "description": "most recent generation observed by this autoscaler." + }, + "lastScaleTime": { + "type": "string", + "description": "last time the HorizontalPodAutoscaler scaled the number of pods; used by the autoscaler to control how often the number of pods is changed." + }, + "currentReplicas": { + "type": "integer", + "format": "int32", + "description": "current number of replicas of pods managed by this autoscaler." + }, + "desiredReplicas": { + "type": "integer", + "format": "int32", + "description": "desired number of replicas of pods managed by this autoscaler." + }, + "currentCPUUtilizationPercentage": { + "type": "integer", + "format": "int32", + "description": "current average CPU utilization over all pods, represented as a percentage of requested CPU, e.g. 70 means that an average pod is using now 70% of its requested CPU." + } + } + }, + "unversioned.Status": { + "id": "unversioned.Status", + "description": "Status is a return value for calls that don't return other objects.", + "properties": { + "kind": { + "type": "string", + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds" + }, + "apiVersion": { + "type": "string", + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#resources" + }, + "metadata": { + "$ref": "unversioned.ListMeta", + "description": "Standard list metadata. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds" + }, + "status": { + "type": "string", + "description": "Status of the operation. One of: \"Success\" or \"Failure\". More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status" + }, + "message": { + "type": "string", + "description": "A human-readable description of the status of this operation." + }, + "reason": { + "type": "string", + "description": "A machine-readable description of why this operation is in the \"Failure\" status. If this value is empty there is no information available. A Reason clarifies an HTTP status code but does not override it." + }, + "details": { + "$ref": "unversioned.StatusDetails", + "description": "Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type." + }, + "code": { + "type": "integer", + "format": "int32", + "description": "Suggested HTTP return code for this status, 0 if not set." + } + } + }, + "unversioned.StatusDetails": { + "id": "unversioned.StatusDetails", + "description": "StatusDetails is a set of additional properties that MAY be set by the server to provide additional information about a response. The Reason field of a Status object defines what attributes will be set. Clients must ignore fields that do not match the defined type of each attribute, and should assume that any attribute may be empty, invalid, or under defined.", + "properties": { + "name": { + "type": "string", + "description": "The name attribute of the resource associated with the status StatusReason (when there is a single name which can be described)." + }, + "group": { + "type": "string", + "description": "The group attribute of the resource associated with the status StatusReason." + }, + "kind": { + "type": "string", + "description": "The kind attribute of the resource associated with the status StatusReason. On some operations may differ from the requested resource Kind. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds" + }, + "causes": { + "type": "array", + "items": { + "$ref": "unversioned.StatusCause" + }, + "description": "The Causes array includes more details associated with the StatusReason failure. Not all StatusReasons may provide detailed causes." + }, + "retryAfterSeconds": { + "type": "integer", + "format": "int32", + "description": "If specified, the time in seconds before the operation should be retried." + } + } + }, + "unversioned.StatusCause": { + "id": "unversioned.StatusCause", + "description": "StatusCause provides more information about an api.Status failure, including cases when multiple errors are encountered.", + "properties": { + "reason": { + "type": "string", + "description": "A machine-readable description of the cause of the error. If this value is empty there is no information available." + }, + "message": { + "type": "string", + "description": "A human-readable description of the cause of the error. This field may be presented as-is to a reader." + }, + "field": { + "type": "string", + "description": "The field of the resource that has caused this error, as named by its JSON serialization. May include dot and postfix notation for nested attributes. Arrays are zero-indexed. Fields may appear more than once in an array of causes due to fields having multiple errors. Optional.\n\nExamples:\n \"name\" - the field \"name\" on the current resource\n \"items[0].name\" - the field \"name\" on the first array entry in \"items\"" + } + } + }, + "json.WatchEvent": { + "id": "json.WatchEvent", + "properties": { + "type": { + "type": "string", + "description": "the type of watch event; may be ADDED, MODIFIED, DELETED, or ERROR" + }, + "object": { + "type": "string", + "description": "the object being watched; will match the type of the resource endpoint or be a Status object if the type is ERROR" + } + } + }, + "unversioned.Patch": { + "id": "unversioned.Patch", + "description": "Patch is provided to give a concrete name and type to the Kubernetes PATCH request body.", + "properties": {} + }, + "v1.DeleteOptions": { + "id": "v1.DeleteOptions", + "description": "DeleteOptions may be provided when deleting an API object", + "required": [ + "gracePeriodSeconds" + ], + "properties": { + "kind": { + "type": "string", + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds" + }, + "apiVersion": { + "type": "string", + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#resources" + }, + "gracePeriodSeconds": { + "type": "integer", + "format": "int64", + "description": "The duration in seconds before the object should be deleted. Value must be non-negative integer. The value zero indicates delete immediately. If this value is nil, the default grace period for the specified type will be used. Defaults to a per object value if not specified. zero means delete immediately." + } + } + } + } + } \ No newline at end of file diff --git a/api/swagger-spec/resourceListing.json b/api/swagger-spec/resourceListing.json index cb3854c2b03e..a19823625dfc 100644 --- a/api/swagger-spec/resourceListing.json +++ b/api/swagger-spec/resourceListing.json @@ -24,6 +24,14 @@ { "path": "/apis/extensions", "description": "get information of a group" + }, + { + "path": "/apis/autoscaling/v1", + "description": "API at /apis/autoscaling/v1" + }, + { + "path": "/apis/autoscaling", + "description": "get information of a group" } ], "apiVersion": "", diff --git a/cmd/genconversion/conversion.go b/cmd/genconversion/conversion.go index 76321f57f3a7..b3abc37c1b2a 100644 --- a/cmd/genconversion/conversion.go +++ b/cmd/genconversion/conversion.go @@ -27,6 +27,7 @@ import ( "k8s.io/kubernetes/pkg/api" _ "k8s.io/kubernetes/pkg/api/install" "k8s.io/kubernetes/pkg/api/unversioned" + _ "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" diff --git a/cmd/gendeepcopy/deep_copy.go b/cmd/gendeepcopy/deep_copy.go index 42c887f82bc4..b569212608a5 100644 --- a/cmd/gendeepcopy/deep_copy.go +++ b/cmd/gendeepcopy/deep_copy.go @@ -28,6 +28,7 @@ import ( "k8s.io/kubernetes/pkg/api" _ "k8s.io/kubernetes/pkg/api/install" "k8s.io/kubernetes/pkg/api/unversioned" + _ "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" diff --git a/cmd/kube-apiserver/app/options/options.go b/cmd/kube-apiserver/app/options/options.go index 31bd626d68e4..5b4b0e9dede0 100644 --- a/cmd/kube-apiserver/app/options/options.go +++ b/cmd/kube-apiserver/app/options/options.go @@ -24,6 +24,7 @@ import ( "k8s.io/kubernetes/pkg/admission" "k8s.io/kubernetes/pkg/api" + apiutil "k8s.io/kubernetes/pkg/api/util" "k8s.io/kubernetes/pkg/api/validation" "k8s.io/kubernetes/pkg/apimachinery/registered" "k8s.io/kubernetes/pkg/apiserver" @@ -80,8 +81,12 @@ type APIServer struct { ServiceClusterIPRange net.IPNet // TODO: make this a list ServiceNodePortRange utilnet.PortRange StorageVersions string - TokenAuthFile string - WatchCacheSizes []string + // The default values for StorageVersions. StorageVersions overrides + // these; you can change this if you want to change the defaults (e.g., + // for testing). This is not actually exposed as a flag. + DefaultStorageVersions string + TokenAuthFile string + WatchCacheSizes []string } // NewAPIServer creates a new APIServer object with default parameters @@ -99,6 +104,7 @@ func NewAPIServer() *APIServer { MasterServiceNamespace: api.NamespaceDefault, RuntimeConfig: make(util.ConfigurationMap), StorageVersions: registered.AllPreferredGroupVersions(), + DefaultStorageVersions: registered.AllPreferredGroupVersions(), KubeletConfig: kubeletclient.KubeletClientConfig{ Port: ports.KubeletPort, EnableHttps: true, @@ -109,6 +115,42 @@ func NewAPIServer() *APIServer { return &s } +// dest must be a map of group to groupVersion. +func gvToMap(gvList string, dest map[string]string) { + for _, gv := range strings.Split(gvList, ",") { + if gv == "" { + continue + } + // We accept two formats. "group/version" OR + // "group=group/version". The latter is used when types + // move between groups. + if !strings.Contains(gv, "=") { + dest[apiutil.GetGroup(gv)] = gv + } else { + parts := strings.SplitN(gv, "=", 2) + // TODO: error checking. + dest[parts[0]] = parts[1] + } + } +} + +// StorageGroupsToGroupVersions returns a map from group name to group version, +// computed from the s.DeprecatedStorageVersion and s.StorageVersions flags. +// TODO: can we move the whole storage version concept to the generic apiserver? +func (s *APIServer) StorageGroupsToGroupVersions() map[string]string { + storageVersionMap := map[string]string{} + if s.DeprecatedStorageVersion != "" { + storageVersionMap[""] = s.DeprecatedStorageVersion + } + + // First, get the defaults. + gvToMap(s.DefaultStorageVersions, storageVersionMap) + // Override any defaults with the user settings. + gvToMap(s.StorageVersions, storageVersionMap) + + return storageVersionMap +} + // AddFlags adds flags for a specific APIServer to the specified FlagSet func (s *APIServer) AddFlags(fs *pflag.FlagSet) { // Note: the weird ""+ in below lines seems to be the only way to get gofmt to @@ -150,9 +192,10 @@ func (s *APIServer) AddFlags(fs *pflag.FlagSet) { fs.MarkDeprecated("api-prefix", "--api-prefix is deprecated and will be removed when the v1 API is retired.") fs.StringVar(&s.DeprecatedStorageVersion, "storage-version", s.DeprecatedStorageVersion, "The version to store the legacy v1 resources with. Defaults to server preferred") fs.MarkDeprecated("storage-version", "--storage-version is deprecated and will be removed when the v1 API is retired. See --storage-versions instead.") - fs.StringVar(&s.StorageVersions, "storage-versions", s.StorageVersions, "The versions to store resources with. "+ - "Different groups may be stored in different versions. Specified in the format \"group1/version1,group2/version2...\". "+ - "This flag expects a complete list of storage versions of ALL groups registered in the server. "+ + fs.StringVar(&s.StorageVersions, "storage-versions", s.StorageVersions, "The per-group version to store resources in. "+ + "Specified in the format \"group1/version1,group2/version2,...\". "+ + "In the case where objects are moved from one group to the other, you may specify the format \"group1=group2/v1beta1,group3/v1beta1,...\". "+ + "You only need to pass the groups you wish to change from the defaults. "+ "It defaults to a list of preferred versions of all registered groups, which is derived from the KUBE_API_VERSIONS environment variable.") fs.StringVar(&s.CloudProvider, "cloud-provider", s.CloudProvider, "The provider for cloud services. Empty string for no provider.") fs.StringVar(&s.CloudConfigFile, "cloud-config", s.CloudConfigFile, "The path to the cloud provider configuration file. Empty string for no configuration file.") diff --git a/cmd/kube-apiserver/app/options/options_test.go b/cmd/kube-apiserver/app/options/options_test.go new file mode 100644 index 000000000000..5cab354b0716 --- /dev/null +++ b/cmd/kube-apiserver/app/options/options_test.go @@ -0,0 +1,78 @@ +/* +Copyright 2014 The Kubernetes Authors All rights reserved. + +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 options + +import ( + "reflect" + "testing" + + "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/apis/autoscaling" + "k8s.io/kubernetes/pkg/apis/extensions" +) + +func TestGenerateStorageVersionMap(t *testing.T) { + testCases := []struct { + legacyVersion string + storageVersions string + defaultVersions string + expectedMap map[string]string + }{ + { + legacyVersion: "v1", + storageVersions: "v1,extensions/v1beta1", + expectedMap: map[string]string{ + api.GroupName: "v1", + extensions.GroupName: "extensions/v1beta1", + }, + }, + { + legacyVersion: "", + storageVersions: "extensions/v1beta1,v1", + expectedMap: map[string]string{ + api.GroupName: "v1", + extensions.GroupName: "extensions/v1beta1", + }, + }, + { + legacyVersion: "", + storageVersions: "autoscaling=extensions/v1beta1,v1", + defaultVersions: "extensions/v1beta1,v1,autoscaling/v1", + expectedMap: map[string]string{ + api.GroupName: "v1", + autoscaling.GroupName: "extensions/v1beta1", + extensions.GroupName: "extensions/v1beta1", + }, + }, + { + legacyVersion: "", + storageVersions: "", + expectedMap: map[string]string{}, + }, + } + for i, test := range testCases { + s := APIServer{ + DeprecatedStorageVersion: test.legacyVersion, + StorageVersions: test.storageVersions, + DefaultStorageVersions: test.defaultVersions, + } + output := s.StorageGroupsToGroupVersions() + if !reflect.DeepEqual(test.expectedMap, output) { + t.Errorf("%v: unexpected error. expect: %v, got: %v", i, test.expectedMap, output) + } + } +} diff --git a/cmd/kube-apiserver/app/server.go b/cmd/kube-apiserver/app/server.go index 6a157d37e881..69604b3b8f6e 100644 --- a/cmd/kube-apiserver/app/server.go +++ b/cmd/kube-apiserver/app/server.go @@ -36,8 +36,8 @@ import ( "k8s.io/kubernetes/pkg/admission" "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/unversioned" - apiutil "k8s.io/kubernetes/pkg/api/util" "k8s.io/kubernetes/pkg/apimachinery/registered" + "k8s.io/kubernetes/pkg/apis/autoscaling" "k8s.io/kubernetes/pkg/apis/extensions" "k8s.io/kubernetes/pkg/apiserver" "k8s.io/kubernetes/pkg/apiserver/authenticator" @@ -51,6 +51,7 @@ import ( "k8s.io/kubernetes/pkg/master" "k8s.io/kubernetes/pkg/registry/cachesize" "k8s.io/kubernetes/pkg/runtime" + "k8s.io/kubernetes/pkg/runtime/serializer/versioning" "k8s.io/kubernetes/pkg/serviceaccount" "k8s.io/kubernetes/pkg/storage" etcdstorage "k8s.io/kubernetes/pkg/storage/etcd" @@ -85,15 +86,20 @@ func verifyClusterIPFlags(s *options.APIServer) { } } -type newEtcdFunc func([]string, runtime.NegotiatedSerializer, string, string, bool) (storage.Interface, error) +// For testing. +type newEtcdFunc func([]string, runtime.NegotiatedSerializer, string, string, string, bool) (storage.Interface, error) -func newEtcd(etcdServerList []string, ns runtime.NegotiatedSerializer, storageGroupVersionString, pathPrefix string, quorum bool) (etcdStorage storage.Interface, err error) { +func newEtcd(etcdServerList []string, ns runtime.NegotiatedSerializer, storageGroupVersionString, memoryGroupVersionString, pathPrefix string, quorum bool) (etcdStorage storage.Interface, err error) { if storageGroupVersionString == "" { return etcdStorage, fmt.Errorf("storageVersion is required to create a etcd storage") } storageVersion, err := unversioned.ParseGroupVersion(storageGroupVersionString) if err != nil { - return nil, err + return nil, fmt.Errorf("couldn't understand storage version %v: %v", storageGroupVersionString, err) + } + memoryVersion, err := unversioned.ParseGroupVersion(memoryGroupVersionString) + if err != nil { + return nil, fmt.Errorf("couldn't understand memory version %v: %v", memoryGroupVersionString, err) } var storageConfig etcdstorage.EtcdConfig @@ -104,23 +110,20 @@ func newEtcd(etcdServerList []string, ns runtime.NegotiatedSerializer, storageGr if !ok { return nil, fmt.Errorf("unable to find serializer for JSON") } - storageConfig.Codec = runtime.NewCodec(ns.EncoderForVersion(s, storageVersion), ns.DecoderToVersion(s, unversioned.GroupVersion{Group: storageVersion.Group, Version: runtime.APIVersionInternal})) - return storageConfig.NewStorage() -} - -// convert to a map between group and groupVersions. -func generateStorageVersionMap(legacyVersion string, storageVersions string) map[string]string { - storageVersionMap := map[string]string{} - if legacyVersion != "" { - storageVersionMap[""] = legacyVersion - } - if storageVersions != "" { - groupVersions := strings.Split(storageVersions, ",") - for _, gv := range groupVersions { - storageVersionMap[apiutil.GetGroup(gv)] = gv + glog.Infof("constructing etcd storage interface.\n sv: %v\n mv: %v\n", storageVersion, memoryVersion) + encoder := ns.EncoderForVersion(s, storageVersion) + decoder := ns.DecoderToVersion(s, memoryVersion) + if memoryVersion.Group != storageVersion.Group { + // Allow this codec to translate between groups. + if err = versioning.EnableCrossGroupEncoding(encoder, memoryVersion.Group, storageVersion.Group); err != nil { + return nil, fmt.Errorf("error setting up encoder for %v: %v", storageGroupVersionString, err) + } + if err = versioning.EnableCrossGroupDecoding(decoder, storageVersion.Group, memoryVersion.Group); err != nil { + return nil, fmt.Errorf("error setting up decoder for %v: %v", storageGroupVersionString, err) } } - return storageVersionMap + storageConfig.Codec = runtime.NewCodec(encoder, decoder) + return storageConfig.NewStorage() } // parse the value of --etcd-servers-overrides and update given storageDestinations. @@ -153,7 +156,11 @@ func updateEtcdOverrides(overrides []string, storageVersions map[string]string, } servers := strings.Split(tokens[1], ";") - etcdOverrideStorage, err := newEtcdFn(servers, api.Codecs, storageVersions[apigroup.GroupVersion.Group], prefix, quorum) + // Note, internalGV will be wrong for things like batch or + // autoscalers, but they shouldn't be using the override + // storage. + internalGV := apigroup.GroupVersion.Group + "/__internal" + etcdOverrideStorage, err := newEtcdFn(servers, api.Codecs, storageVersions[apigroup.GroupVersion.Group], internalGV, prefix, quorum) if err != nil { glog.Fatalf("Invalid storage version or misconfigured etcd for %s: %v", tokens[0], err) } @@ -269,17 +276,18 @@ func Run(s *options.APIServer) error { storageDestinations := genericapiserver.NewStorageDestinations() - storageVersions := generateStorageVersionMap(s.DeprecatedStorageVersion, s.StorageVersions) + storageVersions := s.StorageGroupsToGroupVersions() if _, found := storageVersions[legacyV1Group.GroupVersion.Group]; !found { glog.Fatalf("Couldn't find the storage version for group: %q in storageVersions: %v", legacyV1Group.GroupVersion.Group, storageVersions) } - etcdStorage, err := newEtcd(s.EtcdServerList, api.Codecs, storageVersions[legacyV1Group.GroupVersion.Group], s.EtcdPathPrefix, s.EtcdQuorumRead) + etcdStorage, err := newEtcd(s.EtcdServerList, api.Codecs, storageVersions[legacyV1Group.GroupVersion.Group], "/__internal", s.EtcdPathPrefix, s.EtcdQuorumRead) if err != nil { glog.Fatalf("Invalid storage version or misconfigured etcd: %v", err) } storageDestinations.AddAPIGroup("", etcdStorage) if !apiGroupVersionOverrides["extensions/v1beta1"].Disable { + glog.Infof("Configuring extensions/v1beta1 storage destination") expGroup, err := registered.Group(extensions.GroupName) if err != nil { glog.Fatalf("Extensions API is enabled in runtime config, but not enabled in the environment variable KUBE_API_VERSIONS. Error: %v", err) @@ -287,11 +295,42 @@ func Run(s *options.APIServer) error { if _, found := storageVersions[expGroup.GroupVersion.Group]; !found { glog.Fatalf("Couldn't find the storage version for group: %q in storageVersions: %v", expGroup.GroupVersion.Group, storageVersions) } - expEtcdStorage, err := newEtcd(s.EtcdServerList, api.Codecs, storageVersions[expGroup.GroupVersion.Group], s.EtcdPathPrefix, s.EtcdQuorumRead) + expEtcdStorage, err := newEtcd(s.EtcdServerList, api.Codecs, storageVersions[expGroup.GroupVersion.Group], "extensions/__internal", s.EtcdPathPrefix, s.EtcdQuorumRead) if err != nil { glog.Fatalf("Invalid extensions storage version or misconfigured etcd: %v", err) } storageDestinations.AddAPIGroup(extensions.GroupName, expEtcdStorage) + + // Since HPA has been moved to the autoscaling group, we need to make + // sure autoscaling has a storage destination. If the autoscaling group + // itself is on, it will overwrite this decision below. + storageDestinations.AddAPIGroup(autoscaling.GroupName, expEtcdStorage) + } + + // autoscaling/v1/horizontalpodautoscalers is a move from extensions/v1beta1/horizontalpodautoscalers. + // The storage version needs to be either extensions/v1beta1 or autoscaling/v1. + // Users must roll forward while using 1.2, because we will require the latter for 1.3. + if !apiGroupVersionOverrides["autoscaling/v1"].Disable { + glog.Infof("Configuring autoscaling/v1 storage destination") + autoscalingGroup, err := registered.Group(autoscaling.GroupName) + if err != nil { + glog.Fatalf("Autoscaling API is enabled in runtime config, but not enabled in the environment variable KUBE_API_VERSIONS. Error: %v", err) + } + // Figure out what storage group/version we should use. + storageGroupVersion, found := storageVersions[autoscalingGroup.GroupVersion.Group] + if !found { + glog.Fatalf("Couldn't find the storage version for group: %q in storageVersions: %v", autoscalingGroup.GroupVersion.Group, storageVersions) + } + + if storageGroupVersion != "autoscaling/v1" && storageGroupVersion != "extensions/v1beta1" { + glog.Fatalf("The storage version for autoscaling must be either 'autoscaling/v1' or 'extensions/v1beta1'") + } + glog.Infof("Using %v for autoscaling group storage version", storageGroupVersion) + autoscalingEtcdStorage, err := newEtcd(s.EtcdServerList, api.Codecs, storageGroupVersion, "extensions/__internal", s.EtcdPathPrefix, s.EtcdQuorumRead) + if err != nil { + glog.Fatalf("Invalid extensions storage version or misconfigured etcd: %v", err) + } + storageDestinations.AddAPIGroup(autoscaling.GroupName, autoscalingEtcdStorage) } updateEtcdOverrides(s.EtcdServersOverrides, storageVersions, s.EtcdPathPrefix, s.EtcdQuorumRead, &storageDestinations, newEtcd) @@ -473,6 +512,15 @@ func parseRuntimeConfig(s *options.APIServer) (map[string]genericapiserver.APIGr } } + disableAutoscaling := disableAllAPIs + autoscalingGroupVersion := "autoscaling/v1" + disableAutoscaling = !getRuntimeConfigValue(s, autoscalingGroupVersion, !disableAutoscaling) + if disableAutoscaling { + apiGroupVersionOverrides[autoscalingGroupVersion] = genericapiserver.APIGroupVersionOverride{ + Disable: true, + } + } + for key := range s.RuntimeConfig { if strings.HasPrefix(key, v1GroupVersion+"/") { return nil, fmt.Errorf("api/v1 resources cannot be enabled/disabled individually") diff --git a/cmd/kube-apiserver/app/server_test.go b/cmd/kube-apiserver/app/server_test.go index 468f2a814bf8..0d6b6fe06ab4 100644 --- a/cmd/kube-apiserver/app/server_test.go +++ b/cmd/kube-apiserver/app/server_test.go @@ -71,44 +71,11 @@ func TestLongRunningRequestRegexp(t *testing.T) { } } -func TestGenerateStorageVersionMap(t *testing.T) { - testCases := []struct { - legacyVersion string - storageVersions string - expectedMap map[string]string - }{ - { - legacyVersion: "v1", - storageVersions: "v1,extensions/v1beta1", - expectedMap: map[string]string{ - api.GroupName: "v1", - extensions.GroupName: "extensions/v1beta1", - }, - }, - { - legacyVersion: "", - storageVersions: "extensions/v1beta1,v1", - expectedMap: map[string]string{ - api.GroupName: "v1", - extensions.GroupName: "extensions/v1beta1", - }, - }, - { - legacyVersion: "", - storageVersions: "", - expectedMap: map[string]string{}, - }, - } - for _, test := range testCases { - output := generateStorageVersionMap(test.legacyVersion, test.storageVersions) - if !reflect.DeepEqual(test.expectedMap, output) { - t.Errorf("unexpected error. expect: %v, got: %v", test.expectedMap, output) - } - } -} - func TestUpdateEtcdOverrides(t *testing.T) { - storageVersions := generateStorageVersionMap("", "v1,extensions/v1beta1") + storageVersions := map[string]string{ + "": "v1", + "extensions": "extensions/v1beta1", + } testCases := []struct { apigroup string @@ -133,7 +100,7 @@ func TestUpdateEtcdOverrides(t *testing.T) { } for _, test := range testCases { - newEtcd := func(serverList []string, _ runtime.NegotiatedSerializer, _, _ string, _ bool) (storage.Interface, error) { + newEtcd := func(serverList []string, _ runtime.NegotiatedSerializer, _, _, _ string, _ bool) (storage.Interface, error) { if !reflect.DeepEqual(test.servers, serverList) { t.Errorf("unexpected server list, expected: %#v, got: %#v", test.servers, serverList) } diff --git a/docs/admin/kube-apiserver.md b/docs/admin/kube-apiserver.md index 907f10b6725d..10db003f7f43 100644 --- a/docs/admin/kube-apiserver.md +++ b/docs/admin/kube-apiserver.md @@ -102,7 +102,7 @@ kube-apiserver --service-node-port-range=: A port range to reserve for services with NodePort visibility. Example: '30000-32767'. Inclusive at both ends of the range. --ssh-keyfile="": If non-empty, use secure SSH proxy to the nodes, using this user keyfile --ssh-user="": If non-empty, use secure SSH proxy to the nodes, using this user name - --storage-versions="authorization.k8s.io/v1beta1,componentconfig/v1alpha1,extensions/v1beta1,metrics/v1alpha1,v1": The versions to store resources with. Different groups may be stored in different versions. Specified in the format "group1/version1,group2/version2...". This flag expects a complete list of storage versions of ALL groups registered in the server. It defaults to a list of preferred versions of all registered groups, which is derived from the KUBE_API_VERSIONS environment variable. + --storage-versions="authorization.k8s.io/v1beta1,autoscaling/v1,componentconfig/v1alpha1,extensions/v1beta1,metrics/v1alpha1,v1": The per-group version to store resources in. Specified in the format "group1/version1,group2/version2,...". In the case where objects are moved from one group to the other, you may specify the format "group1=group2/v1beta1,group3/v1beta1,...". You only need to pass the groups you wish to change from the defaults. It defaults to a list of preferred versions of all registered groups, which is derived from the KUBE_API_VERSIONS environment variable. --tls-cert-file="": File containing x509 Certificate for HTTPS. (CA cert, if any, concatenated after server cert). If HTTPS serving is enabled, and --tls-cert-file and --tls-private-key-file are not provided, a self-signed certificate and key are generated for the public address and saved to /var/run/kubernetes. --tls-private-key-file="": File containing x509 private key matching --tls-cert-file. --token-auth-file="": If set, the file that will be used to secure the secure port of the API server via token authentication. @@ -110,7 +110,7 @@ kube-apiserver --watch-cache-sizes=[]: List of watch cache sizes for every resource (pods, nodes, etc.), comma separated. The individual override format: resource#size, where size is a number. It takes effect when watch-cache is enabled. ``` -###### Auto generated by spf13/cobra on 10-Feb-2016 +###### Auto generated by spf13/cobra on 15-Feb-2016 diff --git a/hack/after-build/update-generated-conversions.sh b/hack/after-build/update-generated-conversions.sh index 4258ff6f824e..03ddc21a8f19 100755 --- a/hack/after-build/update-generated-conversions.sh +++ b/hack/after-build/update-generated-conversions.sh @@ -43,7 +43,7 @@ EOF } # TODO(lavalamp): get this list by listing the pkg/apis/ directory? -DEFAULT_GROUP_VERSIONS="v1 authorization/v1beta1 extensions/v1beta1 componentconfig/v1alpha1 metrics/v1alpha1" +DEFAULT_GROUP_VERSIONS="v1 authorization/v1beta1 autoscaling/v1 extensions/v1beta1 componentconfig/v1alpha1 metrics/v1alpha1" VERSIONS=${VERSIONS:-$DEFAULT_GROUP_VERSIONS} for ver in $VERSIONS; do # Ensure that the version being processed is registered by setting diff --git a/hack/after-build/update-generated-deep-copies.sh b/hack/after-build/update-generated-deep-copies.sh index c3089be5e711..4fa21360c146 100755 --- a/hack/after-build/update-generated-deep-copies.sh +++ b/hack/after-build/update-generated-deep-copies.sh @@ -62,6 +62,6 @@ function generate_deep_copies() { # Currently pkg/api/deep_copy_generated.go is generated by the new go2idl generator. # All others (mentioned above) are still generated by the old reflection-based generator. # TODO: Migrate these to the new generator. -DEFAULT_VERSIONS="v1 authorization/__internal authorization/v1beta1 extensions/__internal extensions/v1beta1 componentconfig/__internal componentconfig/v1alpha1 metrics/__internal metrics/v1alpha1" +DEFAULT_VERSIONS="v1 authorization/__internal authorization/v1beta1 autoscaling/__internal autoscaling/v1 extensions/__internal extensions/v1beta1 componentconfig/__internal componentconfig/v1alpha1 metrics/__internal metrics/v1alpha1" VERSIONS=${VERSIONS:-$DEFAULT_VERSIONS} generate_deep_copies "$VERSIONS" diff --git a/hack/after-build/update-swagger-spec.sh b/hack/after-build/update-swagger-spec.sh index 18c306d98e3b..f2c9194be01f 100755 --- a/hack/after-build/update-swagger-spec.sh +++ b/hack/after-build/update-swagger-spec.sh @@ -50,7 +50,7 @@ kube::etcd::start # Start kube-apiserver kube::log::status "Starting kube-apiserver" -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}" \ @@ -64,7 +64,7 @@ APISERVER_PID=$! kube::util::wait_for_url "http://127.0.0.1:${API_PORT}/healthz" "apiserver: " SWAGGER_API_PATH="http://127.0.0.1:${API_PORT}/swaggerapi/" -DEFAULT_GROUP_VERSIONS="v1 extensions/v1beta1" +DEFAULT_GROUP_VERSIONS="v1 autoscaling/v1 extensions/v1beta1" VERSIONS=${VERSIONS:-$DEFAULT_GROUP_VERSIONS} kube::log::status "Updating " ${SWAGGER_ROOT_DIR} diff --git a/hack/after-build/verify-generated-conversions.sh b/hack/after-build/verify-generated-conversions.sh index 56bd73f8c830..e05197485d95 100755 --- a/hack/after-build/verify-generated-conversions.sh +++ b/hack/after-build/verify-generated-conversions.sh @@ -23,7 +23,7 @@ source "${KUBE_ROOT}/hack/lib/init.sh" kube::golang::setup_env -APIROOTS=${APIROOTS:-pkg/api pkg/apis/authorization pkg/apis/extensions pkg/apis/metrics} +APIROOTS=${APIROOTS:-pkg/api pkg/apis/authorization pkg/apis/autoscaling pkg/apis/extensions pkg/apis/metrics} _tmp="${KUBE_ROOT}/_tmp" cleanup() { diff --git a/hack/after-build/verify-generated-deep-copies.sh b/hack/after-build/verify-generated-deep-copies.sh index b8e594027b38..0ba2b2764d7c 100755 --- a/hack/after-build/verify-generated-deep-copies.sh +++ b/hack/after-build/verify-generated-deep-copies.sh @@ -25,7 +25,7 @@ kube::golang::setup_env gendeepcopy=$(kube::util::find-binary "gendeepcopy") -APIROOTS=${APIROOTS:-pkg/api pkg/apis/authorization pkg/apis/extensions pkg/apis/metrics} +APIROOTS=${APIROOTS:-pkg/api pkg/apis/authorization pkg/apis/autoscaling pkg/apis/extensions pkg/apis/metrics} _tmp="${KUBE_ROOT}/_tmp" cleanup() { diff --git a/hack/test-cmd.sh b/hack/test-cmd.sh index c396f8df00f9..c276be950c63 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 0d0148ba30c9..7f55d71b6be1 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 10f5785f0170..75266a344d99 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/hack/update-generated-swagger-docs.sh b/hack/update-generated-swagger-docs.sh index 5bdcfedbc23c..837b5c59886b 100755 --- a/hack/update-generated-swagger-docs.sh +++ b/hack/update-generated-swagger-docs.sh @@ -56,7 +56,7 @@ EOF mv "$TMPFILE" "pkg/$(kube::util::group-version-to-pkg-path "${group_version}")/types_swagger_doc_generated.go" } -GROUP_VERSIONS=(unversioned v1 authorization/v1beta1 extensions/v1beta1) +GROUP_VERSIONS=(unversioned v1 authorization/v1beta1 autoscaling/v1 extensions/v1beta1) # To avoid compile errors, remove the currently existing files. for group_version in "${GROUP_VERSIONS[@]}"; do rm -f "pkg/$(kube::util::group-version-to-pkg-path "${group_version}")/types_swagger_doc_generated.go" diff --git a/pkg/api/serialization_test.go b/pkg/api/serialization_test.go index f95e9a277541..7b1f17f818e1 100644 --- a/pkg/api/serialization_test.go +++ b/pkg/api/serialization_test.go @@ -180,7 +180,7 @@ func doRoundTripTest(group testapi.TestGroup, kind string, t *testing.T) { if api.Scheme.Recognizes(group.GroupVersion().WithKind(kind)) { roundTripSame(t, group, item, nonRoundTrippableTypesByVersion[kind]...) } - if !nonInternalRoundTrippableTypes.Has(kind) { + if !nonInternalRoundTrippableTypes.Has(kind) && api.Scheme.Recognizes(group.GroupVersion().WithKind(kind)) { roundTrip(t, group.Codec(), fuzzInternalObject(t, group.InternalGroupVersion(), item, rand.Int63())) } } diff --git a/pkg/api/testapi/testapi.go b/pkg/api/testapi/testapi.go index d3fc91aae276..f6f75e87972e 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: extensions.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 7b8bb02ba36f..7d9ad0ec0d93 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 4375d26619db..092802d111f8 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/apis/autoscaling/deep_copy_generated.go b/pkg/apis/autoscaling/deep_copy_generated.go new file mode 100644 index 000000000000..3ad4f2799695 --- /dev/null +++ b/pkg/apis/autoscaling/deep_copy_generated.go @@ -0,0 +1,29 @@ +/* +Copyright 2015 The Kubernetes Authors All rights reserved. + +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. +*/ + +// DO NOT EDIT. THIS FILE IS AUTO-GENERATED BY $KUBEROOT/hack/update-generated-deep-copies.sh. + +package autoscaling + +import api "k8s.io/kubernetes/pkg/api" + +func init() { + err := api.Scheme.AddGeneratedDeepCopyFuncs() + if err != nil { + // if one of the deep copy functions is malformed, detect it immediately. + panic(err) + } +} diff --git a/pkg/apis/autoscaling/install/install.go b/pkg/apis/autoscaling/install/install.go new file mode 100644 index 000000000000..6e226a06644e --- /dev/null +++ b/pkg/apis/autoscaling/install/install.go @@ -0,0 +1,129 @@ +/* +Copyright 2016 The Kubernetes Authors All rights reserved. + +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 install installs the experimental API group, making it available as +// an option to all of the API encoding/decoding machinery. +package install + +import ( + "fmt" + + "github.com/golang/glog" + + "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/api/meta" + "k8s.io/kubernetes/pkg/api/unversioned" + "k8s.io/kubernetes/pkg/apimachinery" + "k8s.io/kubernetes/pkg/apimachinery/registered" + "k8s.io/kubernetes/pkg/apis/autoscaling" + "k8s.io/kubernetes/pkg/apis/autoscaling/v1" + "k8s.io/kubernetes/pkg/runtime" + "k8s.io/kubernetes/pkg/util/sets" +) + +const importPrefix = "k8s.io/kubernetes/pkg/apis/autoscaling" + +var accessor = meta.NewAccessor() + +// availableVersions lists all known external versions for this group from most preferred to least preferred +var availableVersions = []unversioned.GroupVersion{v1.SchemeGroupVersion} + +func init() { + registered.RegisterVersions(availableVersions) + externalVersions := []unversioned.GroupVersion{} + for _, v := range availableVersions { + if registered.IsAllowedVersion(v) { + externalVersions = append(externalVersions, v) + } + } + if len(externalVersions) == 0 { + glog.V(4).Infof("No version is registered for group %v", autoscaling.GroupName) + return + } + + if err := registered.EnableVersions(externalVersions...); err != nil { + glog.V(4).Infof("%v", err) + return + } + if err := enableVersions(externalVersions); err != nil { + glog.V(4).Infof("%v", err) + return + } +} + +// TODO: enableVersions should be centralized rather than spread in each API +// group. +// We can combine registered.RegisterVersions, registered.EnableVersions and +// registered.RegisterGroup once we have moved enableVersions there. +func enableVersions(externalVersions []unversioned.GroupVersion) error { + addVersionsToScheme(externalVersions...) + preferredExternalVersion := externalVersions[0] + + groupMeta := apimachinery.GroupMeta{ + GroupVersion: preferredExternalVersion, + GroupVersions: externalVersions, + RESTMapper: newRESTMapper(externalVersions), + SelfLinker: runtime.SelfLinker(accessor), + InterfacesFor: interfacesFor, + } + + if err := registered.RegisterGroup(groupMeta); err != nil { + return err + } + api.RegisterRESTMapper(groupMeta.RESTMapper) + return nil +} + +func newRESTMapper(externalVersions []unversioned.GroupVersion) meta.RESTMapper { + // the list of kinds that are scoped at the root of the api hierarchy + // if a kind is not enumerated here, it is assumed to have a namespace scope + rootScoped := sets.NewString() + + ignoredKinds := sets.NewString() + + return api.NewDefaultRESTMapper(externalVersions, interfacesFor, importPrefix, ignoredKinds, rootScoped) +} + +// interfacesFor returns the default Codec and ResourceVersioner for a given version +// string, or an error if the version is not known. +func interfacesFor(version unversioned.GroupVersion) (*meta.VersionInterfaces, error) { + switch version { + case v1.SchemeGroupVersion: + return &meta.VersionInterfaces{ + ObjectConvertor: api.Scheme, + MetadataAccessor: accessor, + }, nil + default: + g, _ := registered.Group(autoscaling.GroupName) + return nil, fmt.Errorf("unsupported storage version: %s (valid: %v)", version, g.GroupVersions) + } +} + +func addVersionsToScheme(externalVersions ...unversioned.GroupVersion) { + // add the internal version to Scheme + autoscaling.AddToScheme(api.Scheme) + // add the enabled external versions to Scheme + for _, v := range externalVersions { + if !registered.IsEnabledVersion(v) { + glog.Errorf("Version %s is not enabled, so it will not be added to the Scheme.", v) + continue + } + switch v { + case v1.SchemeGroupVersion: + v1.AddToScheme(api.Scheme) + } + } +} diff --git a/pkg/apis/autoscaling/register.go b/pkg/apis/autoscaling/register.go new file mode 100644 index 000000000000..519e8f6be0b4 --- /dev/null +++ b/pkg/apis/autoscaling/register.go @@ -0,0 +1,54 @@ +/* +Copyright 2016 The Kubernetes Authors All rights reserved. + +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 autoscaling + +import ( + "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/api/unversioned" + "k8s.io/kubernetes/pkg/apis/extensions" + "k8s.io/kubernetes/pkg/runtime" +) + +// GroupName is the group name use in this package +const GroupName = "autoscaling" + +// SchemeGroupVersion is group version used to register these objects +var SchemeGroupVersion = unversioned.GroupVersion{Group: GroupName, Version: runtime.APIVersionInternal} + +// Kind takes an unqualified kind and returns back a Group qualified GroupKind +func Kind(kind string) unversioned.GroupKind { + return SchemeGroupVersion.WithKind(kind).GroupKind() +} + +// Resource takes an unqualified resource and returns back a Group qualified GroupResource +func Resource(resource string) unversioned.GroupResource { + return SchemeGroupVersion.WithResource(resource).GroupResource() +} + +func AddToScheme(scheme *runtime.Scheme) { + // Add the API to Scheme. + addKnownTypes(scheme) +} + +// Adds the list of known types to api.Scheme. +func addKnownTypes(scheme *runtime.Scheme) { + scheme.AddKnownTypes(SchemeGroupVersion, + &extensions.HorizontalPodAutoscaler{}, + &extensions.HorizontalPodAutoscalerList{}, + &api.ListOptions{}, + ) +} diff --git a/pkg/apis/autoscaling/v1/conversion.go b/pkg/apis/autoscaling/v1/conversion.go new file mode 100644 index 000000000000..286ce3fe8a4d --- /dev/null +++ b/pkg/apis/autoscaling/v1/conversion.go @@ -0,0 +1,101 @@ +/* +Copyright 2016 The Kubernetes Authors All rights reserved. + +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 v1 + +import ( + "reflect" + + "k8s.io/kubernetes/pkg/apis/extensions" + "k8s.io/kubernetes/pkg/conversion" + "k8s.io/kubernetes/pkg/runtime" +) + +func addConversionFuncs(scheme *runtime.Scheme) { + // Add non-generated conversion functions + err := scheme.AddConversionFuncs( + Convert_extensions_SubresourceReference_To_v1_CrossVersionObjectReference, + Convert_v1_CrossVersionObjectReference_To_extensions_SubresourceReference, + Convert_extensions_HorizontalPodAutoscalerSpec_To_v1_HorizontalPodAutoscalerSpec, + Convert_v1_HorizontalPodAutoscalerSpec_To_extensions_HorizontalPodAutoscalerSpec, + ) + if err != nil { + // If one of the conversion functions is malformed, detect it immediately. + panic(err) + } +} + +func Convert_extensions_SubresourceReference_To_v1_CrossVersionObjectReference(in *extensions.SubresourceReference, out *CrossVersionObjectReference, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*extensions.SubresourceReference))(in) + } + out.Kind = in.Kind + out.Name = in.Name + out.APIVersion = in.APIVersion + return nil +} + +func Convert_v1_CrossVersionObjectReference_To_extensions_SubresourceReference(in *CrossVersionObjectReference, out *extensions.SubresourceReference, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*CrossVersionObjectReference))(in) + } + out.Kind = in.Kind + out.Name = in.Name + out.APIVersion = in.APIVersion + out.Subresource = "scale" + return nil +} + +func Convert_extensions_HorizontalPodAutoscalerSpec_To_v1_HorizontalPodAutoscalerSpec(in *extensions.HorizontalPodAutoscalerSpec, out *HorizontalPodAutoscalerSpec, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*extensions.HorizontalPodAutoscalerSpec))(in) + } + if err := Convert_extensions_SubresourceReference_To_v1_CrossVersionObjectReference(&in.ScaleRef, &out.ScaleTargetRef, s); err != nil { + return err + } + if in.MinReplicas != nil { + out.MinReplicas = new(int32) + *out.MinReplicas = int32(*in.MinReplicas) + } else { + out.MinReplicas = nil + } + out.MaxReplicas = int32(in.MaxReplicas) + if in.CPUUtilization != nil { + out.TargetCPUUtilizationPercentage = new(int32) + *out.TargetCPUUtilizationPercentage = int32(in.CPUUtilization.TargetPercentage) + } + return nil +} + +func Convert_v1_HorizontalPodAutoscalerSpec_To_extensions_HorizontalPodAutoscalerSpec(in *HorizontalPodAutoscalerSpec, out *extensions.HorizontalPodAutoscalerSpec, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*HorizontalPodAutoscalerSpec))(in) + } + if err := Convert_v1_CrossVersionObjectReference_To_extensions_SubresourceReference(&in.ScaleTargetRef, &out.ScaleRef, s); err != nil { + return err + } + if in.MinReplicas != nil { + out.MinReplicas = new(int) + *out.MinReplicas = int(*in.MinReplicas) + } else { + out.MinReplicas = nil + } + out.MaxReplicas = int(in.MaxReplicas) + if in.TargetCPUUtilizationPercentage != nil { + out.CPUUtilization = &extensions.CPUTargetUtilization{TargetPercentage: int(*in.TargetCPUUtilizationPercentage)} + } + return nil +} diff --git a/pkg/apis/autoscaling/v1/conversion_generated.go b/pkg/apis/autoscaling/v1/conversion_generated.go new file mode 100644 index 000000000000..40eed35253dc --- /dev/null +++ b/pkg/apis/autoscaling/v1/conversion_generated.go @@ -0,0 +1,352 @@ +/* +Copyright 2015 The Kubernetes Authors All rights reserved. + +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. +*/ + +// DO NOT EDIT. THIS FILE IS AUTO-GENERATED BY $KUBEROOT/hack/update-generated-conversions.sh + +package v1 + +import ( + reflect "reflect" + + api "k8s.io/kubernetes/pkg/api" + unversioned "k8s.io/kubernetes/pkg/api/unversioned" + v1 "k8s.io/kubernetes/pkg/api/v1" + extensions "k8s.io/kubernetes/pkg/apis/extensions" + conversion "k8s.io/kubernetes/pkg/conversion" +) + +func autoConvert_api_ObjectMeta_To_v1_ObjectMeta(in *api.ObjectMeta, out *v1.ObjectMeta, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*api.ObjectMeta))(in) + } + out.Name = in.Name + out.GenerateName = in.GenerateName + out.Namespace = in.Namespace + out.SelfLink = in.SelfLink + out.UID = in.UID + out.ResourceVersion = in.ResourceVersion + out.Generation = in.Generation + if err := api.Convert_unversioned_Time_To_unversioned_Time(&in.CreationTimestamp, &out.CreationTimestamp, s); err != nil { + return err + } + // unable to generate simple pointer conversion for unversioned.Time -> unversioned.Time + if in.DeletionTimestamp != nil { + out.DeletionTimestamp = new(unversioned.Time) + if err := api.Convert_unversioned_Time_To_unversioned_Time(in.DeletionTimestamp, out.DeletionTimestamp, s); err != nil { + return err + } + } else { + out.DeletionTimestamp = nil + } + if in.DeletionGracePeriodSeconds != nil { + out.DeletionGracePeriodSeconds = new(int64) + *out.DeletionGracePeriodSeconds = *in.DeletionGracePeriodSeconds + } else { + out.DeletionGracePeriodSeconds = nil + } + if in.Labels != nil { + out.Labels = make(map[string]string) + for key, val := range in.Labels { + out.Labels[key] = val + } + } else { + out.Labels = nil + } + if in.Annotations != nil { + out.Annotations = make(map[string]string) + for key, val := range in.Annotations { + out.Annotations[key] = val + } + } else { + out.Annotations = nil + } + return nil +} + +func Convert_api_ObjectMeta_To_v1_ObjectMeta(in *api.ObjectMeta, out *v1.ObjectMeta, s conversion.Scope) error { + return autoConvert_api_ObjectMeta_To_v1_ObjectMeta(in, out, s) +} + +func autoConvert_v1_ObjectMeta_To_api_ObjectMeta(in *v1.ObjectMeta, out *api.ObjectMeta, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*v1.ObjectMeta))(in) + } + out.Name = in.Name + out.GenerateName = in.GenerateName + out.Namespace = in.Namespace + out.SelfLink = in.SelfLink + out.UID = in.UID + out.ResourceVersion = in.ResourceVersion + out.Generation = in.Generation + if err := api.Convert_unversioned_Time_To_unversioned_Time(&in.CreationTimestamp, &out.CreationTimestamp, s); err != nil { + return err + } + // unable to generate simple pointer conversion for unversioned.Time -> unversioned.Time + if in.DeletionTimestamp != nil { + out.DeletionTimestamp = new(unversioned.Time) + if err := api.Convert_unversioned_Time_To_unversioned_Time(in.DeletionTimestamp, out.DeletionTimestamp, s); err != nil { + return err + } + } else { + out.DeletionTimestamp = nil + } + if in.DeletionGracePeriodSeconds != nil { + out.DeletionGracePeriodSeconds = new(int64) + *out.DeletionGracePeriodSeconds = *in.DeletionGracePeriodSeconds + } else { + out.DeletionGracePeriodSeconds = nil + } + if in.Labels != nil { + out.Labels = make(map[string]string) + for key, val := range in.Labels { + out.Labels[key] = val + } + } else { + out.Labels = nil + } + if in.Annotations != nil { + out.Annotations = make(map[string]string) + for key, val := range in.Annotations { + out.Annotations[key] = val + } + } else { + out.Annotations = nil + } + return nil +} + +func Convert_v1_ObjectMeta_To_api_ObjectMeta(in *v1.ObjectMeta, out *api.ObjectMeta, s conversion.Scope) error { + return autoConvert_v1_ObjectMeta_To_api_ObjectMeta(in, out, s) +} + +func autoConvert_v1_HorizontalPodAutoscaler_To_extensions_HorizontalPodAutoscaler(in *HorizontalPodAutoscaler, out *extensions.HorizontalPodAutoscaler, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*HorizontalPodAutoscaler))(in) + } + if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { + return err + } + if err := Convert_v1_ObjectMeta_To_api_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { + return err + } + if err := Convert_v1_HorizontalPodAutoscalerSpec_To_extensions_HorizontalPodAutoscalerSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + if err := Convert_v1_HorizontalPodAutoscalerStatus_To_extensions_HorizontalPodAutoscalerStatus(&in.Status, &out.Status, s); err != nil { + return err + } + return nil +} + +func Convert_v1_HorizontalPodAutoscaler_To_extensions_HorizontalPodAutoscaler(in *HorizontalPodAutoscaler, out *extensions.HorizontalPodAutoscaler, s conversion.Scope) error { + return autoConvert_v1_HorizontalPodAutoscaler_To_extensions_HorizontalPodAutoscaler(in, out, s) +} + +func autoConvert_v1_HorizontalPodAutoscalerList_To_extensions_HorizontalPodAutoscalerList(in *HorizontalPodAutoscalerList, out *extensions.HorizontalPodAutoscalerList, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*HorizontalPodAutoscalerList))(in) + } + if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { + return err + } + if err := api.Convert_unversioned_ListMeta_To_unversioned_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil { + return err + } + if in.Items != nil { + out.Items = make([]extensions.HorizontalPodAutoscaler, len(in.Items)) + for i := range in.Items { + if err := Convert_v1_HorizontalPodAutoscaler_To_extensions_HorizontalPodAutoscaler(&in.Items[i], &out.Items[i], s); err != nil { + return err + } + } + } else { + out.Items = nil + } + return nil +} + +func Convert_v1_HorizontalPodAutoscalerList_To_extensions_HorizontalPodAutoscalerList(in *HorizontalPodAutoscalerList, out *extensions.HorizontalPodAutoscalerList, s conversion.Scope) error { + return autoConvert_v1_HorizontalPodAutoscalerList_To_extensions_HorizontalPodAutoscalerList(in, out, s) +} + +func autoConvert_v1_HorizontalPodAutoscalerSpec_To_extensions_HorizontalPodAutoscalerSpec(in *HorizontalPodAutoscalerSpec, out *extensions.HorizontalPodAutoscalerSpec, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*HorizontalPodAutoscalerSpec))(in) + } + // in.ScaleTargetRef has no peer in out + if in.MinReplicas != nil { + out.MinReplicas = new(int) + *out.MinReplicas = int(*in.MinReplicas) + } else { + out.MinReplicas = nil + } + out.MaxReplicas = int(in.MaxReplicas) + // in.TargetCPUUtilizationPercentage has no peer in out + return nil +} + +func autoConvert_v1_HorizontalPodAutoscalerStatus_To_extensions_HorizontalPodAutoscalerStatus(in *HorizontalPodAutoscalerStatus, out *extensions.HorizontalPodAutoscalerStatus, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*HorizontalPodAutoscalerStatus))(in) + } + if in.ObservedGeneration != nil { + out.ObservedGeneration = new(int64) + *out.ObservedGeneration = *in.ObservedGeneration + } else { + out.ObservedGeneration = nil + } + // unable to generate simple pointer conversion for unversioned.Time -> unversioned.Time + if in.LastScaleTime != nil { + out.LastScaleTime = new(unversioned.Time) + if err := api.Convert_unversioned_Time_To_unversioned_Time(in.LastScaleTime, out.LastScaleTime, s); err != nil { + return err + } + } else { + out.LastScaleTime = nil + } + out.CurrentReplicas = int(in.CurrentReplicas) + out.DesiredReplicas = int(in.DesiredReplicas) + if in.CurrentCPUUtilizationPercentage != nil { + out.CurrentCPUUtilizationPercentage = new(int) + *out.CurrentCPUUtilizationPercentage = int(*in.CurrentCPUUtilizationPercentage) + } else { + out.CurrentCPUUtilizationPercentage = nil + } + return nil +} + +func Convert_v1_HorizontalPodAutoscalerStatus_To_extensions_HorizontalPodAutoscalerStatus(in *HorizontalPodAutoscalerStatus, out *extensions.HorizontalPodAutoscalerStatus, s conversion.Scope) error { + return autoConvert_v1_HorizontalPodAutoscalerStatus_To_extensions_HorizontalPodAutoscalerStatus(in, out, s) +} + +func autoConvert_extensions_HorizontalPodAutoscaler_To_v1_HorizontalPodAutoscaler(in *extensions.HorizontalPodAutoscaler, out *HorizontalPodAutoscaler, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*extensions.HorizontalPodAutoscaler))(in) + } + if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { + return err + } + if err := Convert_api_ObjectMeta_To_v1_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { + return err + } + if err := Convert_extensions_HorizontalPodAutoscalerSpec_To_v1_HorizontalPodAutoscalerSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + if err := Convert_extensions_HorizontalPodAutoscalerStatus_To_v1_HorizontalPodAutoscalerStatus(&in.Status, &out.Status, s); err != nil { + return err + } + return nil +} + +func Convert_extensions_HorizontalPodAutoscaler_To_v1_HorizontalPodAutoscaler(in *extensions.HorizontalPodAutoscaler, out *HorizontalPodAutoscaler, s conversion.Scope) error { + return autoConvert_extensions_HorizontalPodAutoscaler_To_v1_HorizontalPodAutoscaler(in, out, s) +} + +func autoConvert_extensions_HorizontalPodAutoscalerList_To_v1_HorizontalPodAutoscalerList(in *extensions.HorizontalPodAutoscalerList, out *HorizontalPodAutoscalerList, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*extensions.HorizontalPodAutoscalerList))(in) + } + if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { + return err + } + if err := api.Convert_unversioned_ListMeta_To_unversioned_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil { + return err + } + if in.Items != nil { + out.Items = make([]HorizontalPodAutoscaler, len(in.Items)) + for i := range in.Items { + if err := Convert_extensions_HorizontalPodAutoscaler_To_v1_HorizontalPodAutoscaler(&in.Items[i], &out.Items[i], s); err != nil { + return err + } + } + } else { + out.Items = nil + } + return nil +} + +func Convert_extensions_HorizontalPodAutoscalerList_To_v1_HorizontalPodAutoscalerList(in *extensions.HorizontalPodAutoscalerList, out *HorizontalPodAutoscalerList, s conversion.Scope) error { + return autoConvert_extensions_HorizontalPodAutoscalerList_To_v1_HorizontalPodAutoscalerList(in, out, s) +} + +func autoConvert_extensions_HorizontalPodAutoscalerSpec_To_v1_HorizontalPodAutoscalerSpec(in *extensions.HorizontalPodAutoscalerSpec, out *HorizontalPodAutoscalerSpec, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*extensions.HorizontalPodAutoscalerSpec))(in) + } + // in.ScaleRef has no peer in out + if in.MinReplicas != nil { + out.MinReplicas = new(int32) + *out.MinReplicas = int32(*in.MinReplicas) + } else { + out.MinReplicas = nil + } + out.MaxReplicas = int32(in.MaxReplicas) + // in.CPUUtilization has no peer in out + return nil +} + +func autoConvert_extensions_HorizontalPodAutoscalerStatus_To_v1_HorizontalPodAutoscalerStatus(in *extensions.HorizontalPodAutoscalerStatus, out *HorizontalPodAutoscalerStatus, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*extensions.HorizontalPodAutoscalerStatus))(in) + } + if in.ObservedGeneration != nil { + out.ObservedGeneration = new(int64) + *out.ObservedGeneration = *in.ObservedGeneration + } else { + out.ObservedGeneration = nil + } + // unable to generate simple pointer conversion for unversioned.Time -> unversioned.Time + if in.LastScaleTime != nil { + out.LastScaleTime = new(unversioned.Time) + if err := api.Convert_unversioned_Time_To_unversioned_Time(in.LastScaleTime, out.LastScaleTime, s); err != nil { + return err + } + } else { + out.LastScaleTime = nil + } + out.CurrentReplicas = int32(in.CurrentReplicas) + out.DesiredReplicas = int32(in.DesiredReplicas) + if in.CurrentCPUUtilizationPercentage != nil { + out.CurrentCPUUtilizationPercentage = new(int32) + *out.CurrentCPUUtilizationPercentage = int32(*in.CurrentCPUUtilizationPercentage) + } else { + out.CurrentCPUUtilizationPercentage = nil + } + return nil +} + +func Convert_extensions_HorizontalPodAutoscalerStatus_To_v1_HorizontalPodAutoscalerStatus(in *extensions.HorizontalPodAutoscalerStatus, out *HorizontalPodAutoscalerStatus, s conversion.Scope) error { + return autoConvert_extensions_HorizontalPodAutoscalerStatus_To_v1_HorizontalPodAutoscalerStatus(in, out, s) +} + +func init() { + err := api.Scheme.AddGeneratedConversionFuncs( + autoConvert_api_ObjectMeta_To_v1_ObjectMeta, + autoConvert_extensions_HorizontalPodAutoscalerList_To_v1_HorizontalPodAutoscalerList, + autoConvert_extensions_HorizontalPodAutoscalerSpec_To_v1_HorizontalPodAutoscalerSpec, + autoConvert_extensions_HorizontalPodAutoscalerStatus_To_v1_HorizontalPodAutoscalerStatus, + autoConvert_extensions_HorizontalPodAutoscaler_To_v1_HorizontalPodAutoscaler, + autoConvert_v1_HorizontalPodAutoscalerList_To_extensions_HorizontalPodAutoscalerList, + autoConvert_v1_HorizontalPodAutoscalerSpec_To_extensions_HorizontalPodAutoscalerSpec, + autoConvert_v1_HorizontalPodAutoscalerStatus_To_extensions_HorizontalPodAutoscalerStatus, + autoConvert_v1_HorizontalPodAutoscaler_To_extensions_HorizontalPodAutoscaler, + autoConvert_v1_ObjectMeta_To_api_ObjectMeta, + ) + if err != nil { + // If one of the conversion functions is malformed, detect it immediately. + panic(err) + } +} diff --git a/pkg/apis/autoscaling/v1/deep_copy_generated.go b/pkg/apis/autoscaling/v1/deep_copy_generated.go new file mode 100644 index 000000000000..e2c8d6fb4f89 --- /dev/null +++ b/pkg/apis/autoscaling/v1/deep_copy_generated.go @@ -0,0 +1,200 @@ +/* +Copyright 2015 The Kubernetes Authors All rights reserved. + +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. +*/ + +// DO NOT EDIT. THIS FILE IS AUTO-GENERATED BY $KUBEROOT/hack/update-generated-deep-copies.sh. + +package v1 + +import ( + time "time" + + api "k8s.io/kubernetes/pkg/api" + unversioned "k8s.io/kubernetes/pkg/api/unversioned" + v1 "k8s.io/kubernetes/pkg/api/v1" + conversion "k8s.io/kubernetes/pkg/conversion" +) + +func deepCopy_unversioned_ListMeta(in unversioned.ListMeta, out *unversioned.ListMeta, c *conversion.Cloner) error { + out.SelfLink = in.SelfLink + out.ResourceVersion = in.ResourceVersion + return nil +} + +func deepCopy_unversioned_Time(in unversioned.Time, out *unversioned.Time, c *conversion.Cloner) error { + if newVal, err := c.DeepCopy(in.Time); err != nil { + return err + } else { + out.Time = newVal.(time.Time) + } + return nil +} + +func deepCopy_unversioned_TypeMeta(in unversioned.TypeMeta, out *unversioned.TypeMeta, c *conversion.Cloner) error { + out.Kind = in.Kind + out.APIVersion = in.APIVersion + return nil +} + +func deepCopy_v1_ObjectMeta(in v1.ObjectMeta, out *v1.ObjectMeta, c *conversion.Cloner) error { + out.Name = in.Name + out.GenerateName = in.GenerateName + out.Namespace = in.Namespace + out.SelfLink = in.SelfLink + out.UID = in.UID + out.ResourceVersion = in.ResourceVersion + out.Generation = in.Generation + if err := deepCopy_unversioned_Time(in.CreationTimestamp, &out.CreationTimestamp, c); err != nil { + return err + } + if in.DeletionTimestamp != nil { + out.DeletionTimestamp = new(unversioned.Time) + if err := deepCopy_unversioned_Time(*in.DeletionTimestamp, out.DeletionTimestamp, c); err != nil { + return err + } + } else { + out.DeletionTimestamp = nil + } + if in.DeletionGracePeriodSeconds != nil { + out.DeletionGracePeriodSeconds = new(int64) + *out.DeletionGracePeriodSeconds = *in.DeletionGracePeriodSeconds + } else { + out.DeletionGracePeriodSeconds = nil + } + if in.Labels != nil { + out.Labels = make(map[string]string) + for key, val := range in.Labels { + out.Labels[key] = val + } + } else { + out.Labels = nil + } + if in.Annotations != nil { + out.Annotations = make(map[string]string) + for key, val := range in.Annotations { + out.Annotations[key] = val + } + } else { + out.Annotations = nil + } + return nil +} + +func deepCopy_v1_CrossVersionObjectReference(in CrossVersionObjectReference, out *CrossVersionObjectReference, c *conversion.Cloner) error { + out.Kind = in.Kind + out.Name = in.Name + out.APIVersion = in.APIVersion + return nil +} + +func deepCopy_v1_HorizontalPodAutoscaler(in HorizontalPodAutoscaler, out *HorizontalPodAutoscaler, c *conversion.Cloner) error { + if err := deepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil { + return err + } + if err := deepCopy_v1_ObjectMeta(in.ObjectMeta, &out.ObjectMeta, c); err != nil { + return err + } + if err := deepCopy_v1_HorizontalPodAutoscalerSpec(in.Spec, &out.Spec, c); err != nil { + return err + } + if err := deepCopy_v1_HorizontalPodAutoscalerStatus(in.Status, &out.Status, c); err != nil { + return err + } + return nil +} + +func deepCopy_v1_HorizontalPodAutoscalerList(in HorizontalPodAutoscalerList, out *HorizontalPodAutoscalerList, c *conversion.Cloner) error { + if err := deepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil { + return err + } + if err := deepCopy_unversioned_ListMeta(in.ListMeta, &out.ListMeta, c); err != nil { + return err + } + if in.Items != nil { + out.Items = make([]HorizontalPodAutoscaler, len(in.Items)) + for i := range in.Items { + if err := deepCopy_v1_HorizontalPodAutoscaler(in.Items[i], &out.Items[i], c); err != nil { + return err + } + } + } else { + out.Items = nil + } + return nil +} + +func deepCopy_v1_HorizontalPodAutoscalerSpec(in HorizontalPodAutoscalerSpec, out *HorizontalPodAutoscalerSpec, c *conversion.Cloner) error { + if err := deepCopy_v1_CrossVersionObjectReference(in.ScaleTargetRef, &out.ScaleTargetRef, c); err != nil { + return err + } + if in.MinReplicas != nil { + out.MinReplicas = new(int32) + *out.MinReplicas = *in.MinReplicas + } else { + out.MinReplicas = nil + } + out.MaxReplicas = in.MaxReplicas + if in.TargetCPUUtilizationPercentage != nil { + out.TargetCPUUtilizationPercentage = new(int32) + *out.TargetCPUUtilizationPercentage = *in.TargetCPUUtilizationPercentage + } else { + out.TargetCPUUtilizationPercentage = nil + } + return nil +} + +func deepCopy_v1_HorizontalPodAutoscalerStatus(in HorizontalPodAutoscalerStatus, out *HorizontalPodAutoscalerStatus, c *conversion.Cloner) error { + if in.ObservedGeneration != nil { + out.ObservedGeneration = new(int64) + *out.ObservedGeneration = *in.ObservedGeneration + } else { + out.ObservedGeneration = nil + } + if in.LastScaleTime != nil { + out.LastScaleTime = new(unversioned.Time) + if err := deepCopy_unversioned_Time(*in.LastScaleTime, out.LastScaleTime, c); err != nil { + return err + } + } else { + out.LastScaleTime = nil + } + out.CurrentReplicas = in.CurrentReplicas + out.DesiredReplicas = in.DesiredReplicas + if in.CurrentCPUUtilizationPercentage != nil { + out.CurrentCPUUtilizationPercentage = new(int32) + *out.CurrentCPUUtilizationPercentage = *in.CurrentCPUUtilizationPercentage + } else { + out.CurrentCPUUtilizationPercentage = nil + } + return nil +} + +func init() { + err := api.Scheme.AddGeneratedDeepCopyFuncs( + deepCopy_unversioned_ListMeta, + deepCopy_unversioned_Time, + deepCopy_unversioned_TypeMeta, + deepCopy_v1_ObjectMeta, + deepCopy_v1_CrossVersionObjectReference, + deepCopy_v1_HorizontalPodAutoscaler, + deepCopy_v1_HorizontalPodAutoscalerList, + deepCopy_v1_HorizontalPodAutoscalerSpec, + deepCopy_v1_HorizontalPodAutoscalerStatus, + ) + if err != nil { + // if one of the deep copy functions is malformed, detect it immediately. + panic(err) + } +} diff --git a/pkg/apis/autoscaling/v1/defaults.go b/pkg/apis/autoscaling/v1/defaults.go new file mode 100644 index 000000000000..2b5173dcd2d0 --- /dev/null +++ b/pkg/apis/autoscaling/v1/defaults.go @@ -0,0 +1,32 @@ +/* +Copyright 2016 The Kubernetes Authors All rights reserved. + +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 v1 + +import ( + "k8s.io/kubernetes/pkg/runtime" +) + +func addDefaultingFuncs(scheme *runtime.Scheme) { + scheme.AddDefaultingFuncs( + func(obj *HorizontalPodAutoscaler) { + if obj.Spec.MinReplicas == nil { + minReplicas := int32(1) + obj.Spec.MinReplicas = &minReplicas + } + }, + ) +} diff --git a/pkg/apis/autoscaling/v1/register.go b/pkg/apis/autoscaling/v1/register.go new file mode 100644 index 000000000000..1209a4f9b578 --- /dev/null +++ b/pkg/apis/autoscaling/v1/register.go @@ -0,0 +1,47 @@ +/* +Copyright 2016 The Kubernetes Authors All rights reserved. + +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 v1 + +import ( + "k8s.io/kubernetes/pkg/api/unversioned" + "k8s.io/kubernetes/pkg/api/v1" + "k8s.io/kubernetes/pkg/runtime" +) + +// GroupName is the group name use in this package +const GroupName = "autoscaling" + +// SchemeGroupVersion is group version used to register these objects +var SchemeGroupVersion = unversioned.GroupVersion{Group: GroupName, Version: "v1"} + +func AddToScheme(scheme *runtime.Scheme) { + addKnownTypes(scheme) + addDefaultingFuncs(scheme) + addConversionFuncs(scheme) +} + +// Adds the list of known types to api.Scheme. +func addKnownTypes(scheme *runtime.Scheme) { + scheme.AddKnownTypes(SchemeGroupVersion, + &HorizontalPodAutoscaler{}, + &HorizontalPodAutoscalerList{}, + &v1.ListOptions{}, + ) +} + +func (obj *HorizontalPodAutoscaler) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } +func (obj *HorizontalPodAutoscalerList) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } diff --git a/pkg/apis/autoscaling/v1/types.generated.go b/pkg/apis/autoscaling/v1/types.generated.go new file mode 100644 index 000000000000..43d2dbc5a164 --- /dev/null +++ b/pkg/apis/autoscaling/v1/types.generated.go @@ -0,0 +1,1937 @@ +/* +Copyright 2015 The Kubernetes Authors All rights reserved. + +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. +*/ + +// ************************************************************ +// DO NOT EDIT. +// THIS FILE IS AUTO-GENERATED BY codecgen. +// ************************************************************ + +package v1 + +import ( + "errors" + "fmt" + codec1978 "github.com/ugorji/go/codec" + pkg1_unversioned "k8s.io/kubernetes/pkg/api/unversioned" + pkg2_v1 "k8s.io/kubernetes/pkg/api/v1" + pkg3_types "k8s.io/kubernetes/pkg/types" + "reflect" + "runtime" + time "time" +) + +const ( + // ----- content types ---- + codecSelferC_UTF81234 = 1 + codecSelferC_RAW1234 = 0 + // ----- value types used ---- + codecSelferValueTypeArray1234 = 10 + codecSelferValueTypeMap1234 = 9 + // ----- containerStateValues ---- + codecSelfer_containerMapKey1234 = 2 + codecSelfer_containerMapValue1234 = 3 + codecSelfer_containerMapEnd1234 = 4 + codecSelfer_containerArrayElem1234 = 6 + codecSelfer_containerArrayEnd1234 = 7 +) + +var ( + codecSelferBitsize1234 = uint8(reflect.TypeOf(uint(0)).Bits()) + codecSelferOnlyMapOrArrayEncodeToStructErr1234 = errors.New(`only encoded map or array can be decoded into a struct`) +) + +type codecSelfer1234 struct{} + +func init() { + if codec1978.GenVersion != 5 { + _, file, _, _ := runtime.Caller(0) + err := fmt.Errorf("codecgen version mismatch: current: %v, need %v. Re-generate file: %v", + 5, codec1978.GenVersion, file) + panic(err) + } + if false { // reference the types, but skip this branch at build/run time + var v0 pkg1_unversioned.Time + var v1 pkg2_v1.ObjectMeta + var v2 pkg3_types.UID + var v3 time.Time + _, _, _, _ = v0, v1, v2, v3 + } +} + +func (x *CrossVersionObjectReference) CodecEncodeSelf(e *codec1978.Encoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperEncoder(e) + _, _, _ = h, z, r + if x == nil { + r.EncodeNil() + } else { + yym1 := z.EncBinary() + _ = yym1 + if false { + } else if z.HasExtensions() && z.EncExt(x) { + } else { + yysep2 := !z.EncBinary() + yy2arr2 := z.EncBasicHandle().StructToArray + var yyq2 [3]bool + _, _, _ = yysep2, yyq2, yy2arr2 + const yyr2 bool = false + yyq2[2] = x.APIVersion != "" + var yynn2 int + if yyr2 || yy2arr2 { + r.EncodeArrayStart(3) + } else { + yynn2 = 2 + for _, b := range yyq2 { + if b { + yynn2++ + } + } + r.EncodeMapStart(yynn2) + yynn2 = 0 + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + yym4 := z.EncBinary() + _ = yym4 + if false { + } else { + r.EncodeString(codecSelferC_UTF81234, string(x.Kind)) + } + } else { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("kind")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + yym5 := z.EncBinary() + _ = yym5 + if false { + } else { + r.EncodeString(codecSelferC_UTF81234, string(x.Kind)) + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + yym7 := z.EncBinary() + _ = yym7 + if false { + } else { + r.EncodeString(codecSelferC_UTF81234, string(x.Name)) + } + } else { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("name")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + yym8 := z.EncBinary() + _ = yym8 + if false { + } else { + r.EncodeString(codecSelferC_UTF81234, string(x.Name)) + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[2] { + yym10 := z.EncBinary() + _ = yym10 + if false { + } else { + r.EncodeString(codecSelferC_UTF81234, string(x.APIVersion)) + } + } else { + r.EncodeString(codecSelferC_UTF81234, "") + } + } else { + if yyq2[2] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("apiVersion")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + yym11 := z.EncBinary() + _ = yym11 + if false { + } else { + r.EncodeString(codecSelferC_UTF81234, string(x.APIVersion)) + } + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayEnd1234) + } else { + z.EncSendContainerState(codecSelfer_containerMapEnd1234) + } + } + } +} + +func (x *CrossVersionObjectReference) CodecDecodeSelf(d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + yym1 := z.DecBinary() + _ = yym1 + if false { + } else if z.HasExtensions() && z.DecExt(x) { + } else { + yyct2 := r.ContainerType() + if yyct2 == codecSelferValueTypeMap1234 { + yyl2 := r.ReadMapStart() + if yyl2 == 0 { + z.DecSendContainerState(codecSelfer_containerMapEnd1234) + } else { + x.codecDecodeSelfFromMap(yyl2, d) + } + } else if yyct2 == codecSelferValueTypeArray1234 { + yyl2 := r.ReadArrayStart() + if yyl2 == 0 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + } else { + x.codecDecodeSelfFromArray(yyl2, d) + } + } else { + panic(codecSelferOnlyMapOrArrayEncodeToStructErr1234) + } + } +} + +func (x *CrossVersionObjectReference) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + var yys3Slc = z.DecScratchBuffer() // default slice to decode into + _ = yys3Slc + var yyhl3 bool = l >= 0 + for yyj3 := 0; ; yyj3++ { + if yyhl3 { + if yyj3 >= l { + break + } + } else { + if r.CheckBreak() { + break + } + } + z.DecSendContainerState(codecSelfer_containerMapKey1234) + yys3Slc = r.DecodeBytes(yys3Slc, true, true) + yys3 := string(yys3Slc) + z.DecSendContainerState(codecSelfer_containerMapValue1234) + switch yys3 { + case "kind": + if r.TryDecodeAsNil() { + x.Kind = "" + } else { + x.Kind = string(r.DecodeString()) + } + case "name": + if r.TryDecodeAsNil() { + x.Name = "" + } else { + x.Name = string(r.DecodeString()) + } + case "apiVersion": + if r.TryDecodeAsNil() { + x.APIVersion = "" + } else { + x.APIVersion = string(r.DecodeString()) + } + default: + z.DecStructFieldNotFound(-1, yys3) + } // end switch yys3 + } // end for yyj3 + z.DecSendContainerState(codecSelfer_containerMapEnd1234) +} + +func (x *CrossVersionObjectReference) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + var yyj7 int + var yyb7 bool + var yyhl7 bool = l >= 0 + yyj7++ + if yyhl7 { + yyb7 = yyj7 > l + } else { + yyb7 = r.CheckBreak() + } + if yyb7 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.Kind = "" + } else { + x.Kind = string(r.DecodeString()) + } + yyj7++ + if yyhl7 { + yyb7 = yyj7 > l + } else { + yyb7 = r.CheckBreak() + } + if yyb7 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.Name = "" + } else { + x.Name = string(r.DecodeString()) + } + yyj7++ + if yyhl7 { + yyb7 = yyj7 > l + } else { + yyb7 = r.CheckBreak() + } + if yyb7 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.APIVersion = "" + } else { + x.APIVersion = string(r.DecodeString()) + } + for { + yyj7++ + if yyhl7 { + yyb7 = yyj7 > l + } else { + yyb7 = r.CheckBreak() + } + if yyb7 { + break + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + z.DecStructFieldNotFound(yyj7-1, "") + } + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) +} + +func (x *HorizontalPodAutoscalerSpec) CodecEncodeSelf(e *codec1978.Encoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperEncoder(e) + _, _, _ = h, z, r + if x == nil { + r.EncodeNil() + } else { + yym1 := z.EncBinary() + _ = yym1 + if false { + } else if z.HasExtensions() && z.EncExt(x) { + } else { + yysep2 := !z.EncBinary() + yy2arr2 := z.EncBasicHandle().StructToArray + var yyq2 [4]bool + _, _, _ = yysep2, yyq2, yy2arr2 + const yyr2 bool = false + yyq2[1] = x.MinReplicas != nil + yyq2[3] = x.TargetCPUUtilizationPercentage != nil + var yynn2 int + if yyr2 || yy2arr2 { + r.EncodeArrayStart(4) + } else { + yynn2 = 2 + for _, b := range yyq2 { + if b { + yynn2++ + } + } + r.EncodeMapStart(yynn2) + yynn2 = 0 + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + yy4 := &x.ScaleTargetRef + yy4.CodecEncodeSelf(e) + } else { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("scaleTargetRef")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + yy6 := &x.ScaleTargetRef + yy6.CodecEncodeSelf(e) + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[1] { + if x.MinReplicas == nil { + r.EncodeNil() + } else { + yy9 := *x.MinReplicas + yym10 := z.EncBinary() + _ = yym10 + if false { + } else { + r.EncodeInt(int64(yy9)) + } + } + } else { + r.EncodeNil() + } + } else { + if yyq2[1] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("minReplicas")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + if x.MinReplicas == nil { + r.EncodeNil() + } else { + yy11 := *x.MinReplicas + yym12 := z.EncBinary() + _ = yym12 + if false { + } else { + r.EncodeInt(int64(yy11)) + } + } + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + yym14 := z.EncBinary() + _ = yym14 + if false { + } else { + r.EncodeInt(int64(x.MaxReplicas)) + } + } else { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("maxReplicas")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + yym15 := z.EncBinary() + _ = yym15 + if false { + } else { + r.EncodeInt(int64(x.MaxReplicas)) + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[3] { + if x.TargetCPUUtilizationPercentage == nil { + r.EncodeNil() + } else { + yy17 := *x.TargetCPUUtilizationPercentage + yym18 := z.EncBinary() + _ = yym18 + if false { + } else { + r.EncodeInt(int64(yy17)) + } + } + } else { + r.EncodeNil() + } + } else { + if yyq2[3] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("targetCPUUtilizationPercentage")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + if x.TargetCPUUtilizationPercentage == nil { + r.EncodeNil() + } else { + yy19 := *x.TargetCPUUtilizationPercentage + yym20 := z.EncBinary() + _ = yym20 + if false { + } else { + r.EncodeInt(int64(yy19)) + } + } + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayEnd1234) + } else { + z.EncSendContainerState(codecSelfer_containerMapEnd1234) + } + } + } +} + +func (x *HorizontalPodAutoscalerSpec) CodecDecodeSelf(d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + yym1 := z.DecBinary() + _ = yym1 + if false { + } else if z.HasExtensions() && z.DecExt(x) { + } else { + yyct2 := r.ContainerType() + if yyct2 == codecSelferValueTypeMap1234 { + yyl2 := r.ReadMapStart() + if yyl2 == 0 { + z.DecSendContainerState(codecSelfer_containerMapEnd1234) + } else { + x.codecDecodeSelfFromMap(yyl2, d) + } + } else if yyct2 == codecSelferValueTypeArray1234 { + yyl2 := r.ReadArrayStart() + if yyl2 == 0 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + } else { + x.codecDecodeSelfFromArray(yyl2, d) + } + } else { + panic(codecSelferOnlyMapOrArrayEncodeToStructErr1234) + } + } +} + +func (x *HorizontalPodAutoscalerSpec) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + var yys3Slc = z.DecScratchBuffer() // default slice to decode into + _ = yys3Slc + var yyhl3 bool = l >= 0 + for yyj3 := 0; ; yyj3++ { + if yyhl3 { + if yyj3 >= l { + break + } + } else { + if r.CheckBreak() { + break + } + } + z.DecSendContainerState(codecSelfer_containerMapKey1234) + yys3Slc = r.DecodeBytes(yys3Slc, true, true) + yys3 := string(yys3Slc) + z.DecSendContainerState(codecSelfer_containerMapValue1234) + switch yys3 { + case "scaleTargetRef": + if r.TryDecodeAsNil() { + x.ScaleTargetRef = CrossVersionObjectReference{} + } else { + yyv4 := &x.ScaleTargetRef + yyv4.CodecDecodeSelf(d) + } + case "minReplicas": + if r.TryDecodeAsNil() { + if x.MinReplicas != nil { + x.MinReplicas = nil + } + } else { + if x.MinReplicas == nil { + x.MinReplicas = new(int32) + } + yym6 := z.DecBinary() + _ = yym6 + if false { + } else { + *((*int32)(x.MinReplicas)) = int32(r.DecodeInt(32)) + } + } + case "maxReplicas": + if r.TryDecodeAsNil() { + x.MaxReplicas = 0 + } else { + x.MaxReplicas = int32(r.DecodeInt(32)) + } + case "targetCPUUtilizationPercentage": + if r.TryDecodeAsNil() { + if x.TargetCPUUtilizationPercentage != nil { + x.TargetCPUUtilizationPercentage = nil + } + } else { + if x.TargetCPUUtilizationPercentage == nil { + x.TargetCPUUtilizationPercentage = new(int32) + } + yym9 := z.DecBinary() + _ = yym9 + if false { + } else { + *((*int32)(x.TargetCPUUtilizationPercentage)) = int32(r.DecodeInt(32)) + } + } + default: + z.DecStructFieldNotFound(-1, yys3) + } // end switch yys3 + } // end for yyj3 + z.DecSendContainerState(codecSelfer_containerMapEnd1234) +} + +func (x *HorizontalPodAutoscalerSpec) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + var yyj10 int + var yyb10 bool + var yyhl10 bool = l >= 0 + yyj10++ + if yyhl10 { + yyb10 = yyj10 > l + } else { + yyb10 = r.CheckBreak() + } + if yyb10 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.ScaleTargetRef = CrossVersionObjectReference{} + } else { + yyv11 := &x.ScaleTargetRef + yyv11.CodecDecodeSelf(d) + } + yyj10++ + if yyhl10 { + yyb10 = yyj10 > l + } else { + yyb10 = r.CheckBreak() + } + if yyb10 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + if x.MinReplicas != nil { + x.MinReplicas = nil + } + } else { + if x.MinReplicas == nil { + x.MinReplicas = new(int32) + } + yym13 := z.DecBinary() + _ = yym13 + if false { + } else { + *((*int32)(x.MinReplicas)) = int32(r.DecodeInt(32)) + } + } + yyj10++ + if yyhl10 { + yyb10 = yyj10 > l + } else { + yyb10 = r.CheckBreak() + } + if yyb10 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.MaxReplicas = 0 + } else { + x.MaxReplicas = int32(r.DecodeInt(32)) + } + yyj10++ + if yyhl10 { + yyb10 = yyj10 > l + } else { + yyb10 = r.CheckBreak() + } + if yyb10 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + if x.TargetCPUUtilizationPercentage != nil { + x.TargetCPUUtilizationPercentage = nil + } + } else { + if x.TargetCPUUtilizationPercentage == nil { + x.TargetCPUUtilizationPercentage = new(int32) + } + yym16 := z.DecBinary() + _ = yym16 + if false { + } else { + *((*int32)(x.TargetCPUUtilizationPercentage)) = int32(r.DecodeInt(32)) + } + } + for { + yyj10++ + if yyhl10 { + yyb10 = yyj10 > l + } else { + yyb10 = r.CheckBreak() + } + if yyb10 { + break + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + z.DecStructFieldNotFound(yyj10-1, "") + } + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) +} + +func (x *HorizontalPodAutoscalerStatus) CodecEncodeSelf(e *codec1978.Encoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperEncoder(e) + _, _, _ = h, z, r + if x == nil { + r.EncodeNil() + } else { + yym1 := z.EncBinary() + _ = yym1 + if false { + } else if z.HasExtensions() && z.EncExt(x) { + } else { + yysep2 := !z.EncBinary() + yy2arr2 := z.EncBasicHandle().StructToArray + var yyq2 [5]bool + _, _, _ = yysep2, yyq2, yy2arr2 + const yyr2 bool = false + yyq2[0] = x.ObservedGeneration != nil + yyq2[1] = x.LastScaleTime != nil + yyq2[4] = x.CurrentCPUUtilizationPercentage != nil + var yynn2 int + if yyr2 || yy2arr2 { + r.EncodeArrayStart(5) + } else { + yynn2 = 2 + for _, b := range yyq2 { + if b { + yynn2++ + } + } + r.EncodeMapStart(yynn2) + yynn2 = 0 + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[0] { + if x.ObservedGeneration == nil { + r.EncodeNil() + } else { + yy4 := *x.ObservedGeneration + yym5 := z.EncBinary() + _ = yym5 + if false { + } else { + r.EncodeInt(int64(yy4)) + } + } + } else { + r.EncodeNil() + } + } else { + if yyq2[0] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("observedGeneration")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + if x.ObservedGeneration == nil { + r.EncodeNil() + } else { + yy6 := *x.ObservedGeneration + yym7 := z.EncBinary() + _ = yym7 + if false { + } else { + r.EncodeInt(int64(yy6)) + } + } + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[1] { + if x.LastScaleTime == nil { + r.EncodeNil() + } else { + yym9 := z.EncBinary() + _ = yym9 + if false { + } else if z.HasExtensions() && z.EncExt(x.LastScaleTime) { + } else if yym9 { + z.EncBinaryMarshal(x.LastScaleTime) + } else if !yym9 && z.IsJSONHandle() { + z.EncJSONMarshal(x.LastScaleTime) + } else { + z.EncFallback(x.LastScaleTime) + } + } + } else { + r.EncodeNil() + } + } else { + if yyq2[1] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("lastScaleTime")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + if x.LastScaleTime == nil { + r.EncodeNil() + } else { + yym10 := z.EncBinary() + _ = yym10 + if false { + } else if z.HasExtensions() && z.EncExt(x.LastScaleTime) { + } else if yym10 { + z.EncBinaryMarshal(x.LastScaleTime) + } else if !yym10 && z.IsJSONHandle() { + z.EncJSONMarshal(x.LastScaleTime) + } else { + z.EncFallback(x.LastScaleTime) + } + } + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + yym12 := z.EncBinary() + _ = yym12 + if false { + } else { + r.EncodeInt(int64(x.CurrentReplicas)) + } + } else { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("currentReplicas")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + yym13 := z.EncBinary() + _ = yym13 + if false { + } else { + r.EncodeInt(int64(x.CurrentReplicas)) + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + yym15 := z.EncBinary() + _ = yym15 + if false { + } else { + r.EncodeInt(int64(x.DesiredReplicas)) + } + } else { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("desiredReplicas")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + yym16 := z.EncBinary() + _ = yym16 + if false { + } else { + r.EncodeInt(int64(x.DesiredReplicas)) + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[4] { + if x.CurrentCPUUtilizationPercentage == nil { + r.EncodeNil() + } else { + yy18 := *x.CurrentCPUUtilizationPercentage + yym19 := z.EncBinary() + _ = yym19 + if false { + } else { + r.EncodeInt(int64(yy18)) + } + } + } else { + r.EncodeNil() + } + } else { + if yyq2[4] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("currentCPUUtilizationPercentage")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + if x.CurrentCPUUtilizationPercentage == nil { + r.EncodeNil() + } else { + yy20 := *x.CurrentCPUUtilizationPercentage + yym21 := z.EncBinary() + _ = yym21 + if false { + } else { + r.EncodeInt(int64(yy20)) + } + } + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayEnd1234) + } else { + z.EncSendContainerState(codecSelfer_containerMapEnd1234) + } + } + } +} + +func (x *HorizontalPodAutoscalerStatus) CodecDecodeSelf(d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + yym1 := z.DecBinary() + _ = yym1 + if false { + } else if z.HasExtensions() && z.DecExt(x) { + } else { + yyct2 := r.ContainerType() + if yyct2 == codecSelferValueTypeMap1234 { + yyl2 := r.ReadMapStart() + if yyl2 == 0 { + z.DecSendContainerState(codecSelfer_containerMapEnd1234) + } else { + x.codecDecodeSelfFromMap(yyl2, d) + } + } else if yyct2 == codecSelferValueTypeArray1234 { + yyl2 := r.ReadArrayStart() + if yyl2 == 0 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + } else { + x.codecDecodeSelfFromArray(yyl2, d) + } + } else { + panic(codecSelferOnlyMapOrArrayEncodeToStructErr1234) + } + } +} + +func (x *HorizontalPodAutoscalerStatus) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + var yys3Slc = z.DecScratchBuffer() // default slice to decode into + _ = yys3Slc + var yyhl3 bool = l >= 0 + for yyj3 := 0; ; yyj3++ { + if yyhl3 { + if yyj3 >= l { + break + } + } else { + if r.CheckBreak() { + break + } + } + z.DecSendContainerState(codecSelfer_containerMapKey1234) + yys3Slc = r.DecodeBytes(yys3Slc, true, true) + yys3 := string(yys3Slc) + z.DecSendContainerState(codecSelfer_containerMapValue1234) + switch yys3 { + case "observedGeneration": + if r.TryDecodeAsNil() { + if x.ObservedGeneration != nil { + x.ObservedGeneration = nil + } + } else { + if x.ObservedGeneration == nil { + x.ObservedGeneration = new(int64) + } + yym5 := z.DecBinary() + _ = yym5 + if false { + } else { + *((*int64)(x.ObservedGeneration)) = int64(r.DecodeInt(64)) + } + } + case "lastScaleTime": + if r.TryDecodeAsNil() { + if x.LastScaleTime != nil { + x.LastScaleTime = nil + } + } else { + if x.LastScaleTime == nil { + x.LastScaleTime = new(pkg1_unversioned.Time) + } + yym7 := z.DecBinary() + _ = yym7 + if false { + } else if z.HasExtensions() && z.DecExt(x.LastScaleTime) { + } else if yym7 { + z.DecBinaryUnmarshal(x.LastScaleTime) + } else if !yym7 && z.IsJSONHandle() { + z.DecJSONUnmarshal(x.LastScaleTime) + } else { + z.DecFallback(x.LastScaleTime, false) + } + } + case "currentReplicas": + if r.TryDecodeAsNil() { + x.CurrentReplicas = 0 + } else { + x.CurrentReplicas = int32(r.DecodeInt(32)) + } + case "desiredReplicas": + if r.TryDecodeAsNil() { + x.DesiredReplicas = 0 + } else { + x.DesiredReplicas = int32(r.DecodeInt(32)) + } + case "currentCPUUtilizationPercentage": + if r.TryDecodeAsNil() { + if x.CurrentCPUUtilizationPercentage != nil { + x.CurrentCPUUtilizationPercentage = nil + } + } else { + if x.CurrentCPUUtilizationPercentage == nil { + x.CurrentCPUUtilizationPercentage = new(int32) + } + yym11 := z.DecBinary() + _ = yym11 + if false { + } else { + *((*int32)(x.CurrentCPUUtilizationPercentage)) = int32(r.DecodeInt(32)) + } + } + default: + z.DecStructFieldNotFound(-1, yys3) + } // end switch yys3 + } // end for yyj3 + z.DecSendContainerState(codecSelfer_containerMapEnd1234) +} + +func (x *HorizontalPodAutoscalerStatus) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + var yyj12 int + var yyb12 bool + var yyhl12 bool = l >= 0 + yyj12++ + if yyhl12 { + yyb12 = yyj12 > l + } else { + yyb12 = r.CheckBreak() + } + if yyb12 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + if x.ObservedGeneration != nil { + x.ObservedGeneration = nil + } + } else { + if x.ObservedGeneration == nil { + x.ObservedGeneration = new(int64) + } + yym14 := z.DecBinary() + _ = yym14 + if false { + } else { + *((*int64)(x.ObservedGeneration)) = int64(r.DecodeInt(64)) + } + } + yyj12++ + if yyhl12 { + yyb12 = yyj12 > l + } else { + yyb12 = r.CheckBreak() + } + if yyb12 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + if x.LastScaleTime != nil { + x.LastScaleTime = nil + } + } else { + if x.LastScaleTime == nil { + x.LastScaleTime = new(pkg1_unversioned.Time) + } + yym16 := z.DecBinary() + _ = yym16 + if false { + } else if z.HasExtensions() && z.DecExt(x.LastScaleTime) { + } else if yym16 { + z.DecBinaryUnmarshal(x.LastScaleTime) + } else if !yym16 && z.IsJSONHandle() { + z.DecJSONUnmarshal(x.LastScaleTime) + } else { + z.DecFallback(x.LastScaleTime, false) + } + } + yyj12++ + if yyhl12 { + yyb12 = yyj12 > l + } else { + yyb12 = r.CheckBreak() + } + if yyb12 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.CurrentReplicas = 0 + } else { + x.CurrentReplicas = int32(r.DecodeInt(32)) + } + yyj12++ + if yyhl12 { + yyb12 = yyj12 > l + } else { + yyb12 = r.CheckBreak() + } + if yyb12 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.DesiredReplicas = 0 + } else { + x.DesiredReplicas = int32(r.DecodeInt(32)) + } + yyj12++ + if yyhl12 { + yyb12 = yyj12 > l + } else { + yyb12 = r.CheckBreak() + } + if yyb12 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + if x.CurrentCPUUtilizationPercentage != nil { + x.CurrentCPUUtilizationPercentage = nil + } + } else { + if x.CurrentCPUUtilizationPercentage == nil { + x.CurrentCPUUtilizationPercentage = new(int32) + } + yym20 := z.DecBinary() + _ = yym20 + if false { + } else { + *((*int32)(x.CurrentCPUUtilizationPercentage)) = int32(r.DecodeInt(32)) + } + } + for { + yyj12++ + if yyhl12 { + yyb12 = yyj12 > l + } else { + yyb12 = r.CheckBreak() + } + if yyb12 { + break + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + z.DecStructFieldNotFound(yyj12-1, "") + } + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) +} + +func (x *HorizontalPodAutoscaler) CodecEncodeSelf(e *codec1978.Encoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperEncoder(e) + _, _, _ = h, z, r + if x == nil { + r.EncodeNil() + } else { + yym1 := z.EncBinary() + _ = yym1 + if false { + } else if z.HasExtensions() && z.EncExt(x) { + } else { + yysep2 := !z.EncBinary() + yy2arr2 := z.EncBasicHandle().StructToArray + var yyq2 [5]bool + _, _, _ = yysep2, yyq2, yy2arr2 + const yyr2 bool = false + yyq2[0] = true + yyq2[1] = true + yyq2[2] = true + yyq2[3] = x.Kind != "" + yyq2[4] = x.APIVersion != "" + var yynn2 int + if yyr2 || yy2arr2 { + r.EncodeArrayStart(5) + } else { + yynn2 = 0 + for _, b := range yyq2 { + if b { + yynn2++ + } + } + r.EncodeMapStart(yynn2) + yynn2 = 0 + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[0] { + yy4 := &x.ObjectMeta + yy4.CodecEncodeSelf(e) + } else { + r.EncodeNil() + } + } else { + if yyq2[0] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("metadata")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + yy6 := &x.ObjectMeta + yy6.CodecEncodeSelf(e) + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[1] { + yy9 := &x.Spec + yy9.CodecEncodeSelf(e) + } else { + r.EncodeNil() + } + } else { + if yyq2[1] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("spec")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + yy11 := &x.Spec + yy11.CodecEncodeSelf(e) + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[2] { + yy14 := &x.Status + yy14.CodecEncodeSelf(e) + } else { + r.EncodeNil() + } + } else { + if yyq2[2] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("status")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + yy16 := &x.Status + yy16.CodecEncodeSelf(e) + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[3] { + yym19 := z.EncBinary() + _ = yym19 + if false { + } else { + r.EncodeString(codecSelferC_UTF81234, string(x.Kind)) + } + } else { + r.EncodeString(codecSelferC_UTF81234, "") + } + } else { + if yyq2[3] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("kind")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + yym20 := z.EncBinary() + _ = yym20 + if false { + } else { + r.EncodeString(codecSelferC_UTF81234, string(x.Kind)) + } + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[4] { + yym22 := z.EncBinary() + _ = yym22 + if false { + } else { + r.EncodeString(codecSelferC_UTF81234, string(x.APIVersion)) + } + } else { + r.EncodeString(codecSelferC_UTF81234, "") + } + } else { + if yyq2[4] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("apiVersion")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + yym23 := z.EncBinary() + _ = yym23 + if false { + } else { + r.EncodeString(codecSelferC_UTF81234, string(x.APIVersion)) + } + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayEnd1234) + } else { + z.EncSendContainerState(codecSelfer_containerMapEnd1234) + } + } + } +} + +func (x *HorizontalPodAutoscaler) CodecDecodeSelf(d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + yym1 := z.DecBinary() + _ = yym1 + if false { + } else if z.HasExtensions() && z.DecExt(x) { + } else { + yyct2 := r.ContainerType() + if yyct2 == codecSelferValueTypeMap1234 { + yyl2 := r.ReadMapStart() + if yyl2 == 0 { + z.DecSendContainerState(codecSelfer_containerMapEnd1234) + } else { + x.codecDecodeSelfFromMap(yyl2, d) + } + } else if yyct2 == codecSelferValueTypeArray1234 { + yyl2 := r.ReadArrayStart() + if yyl2 == 0 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + } else { + x.codecDecodeSelfFromArray(yyl2, d) + } + } else { + panic(codecSelferOnlyMapOrArrayEncodeToStructErr1234) + } + } +} + +func (x *HorizontalPodAutoscaler) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + var yys3Slc = z.DecScratchBuffer() // default slice to decode into + _ = yys3Slc + var yyhl3 bool = l >= 0 + for yyj3 := 0; ; yyj3++ { + if yyhl3 { + if yyj3 >= l { + break + } + } else { + if r.CheckBreak() { + break + } + } + z.DecSendContainerState(codecSelfer_containerMapKey1234) + yys3Slc = r.DecodeBytes(yys3Slc, true, true) + yys3 := string(yys3Slc) + z.DecSendContainerState(codecSelfer_containerMapValue1234) + switch yys3 { + case "metadata": + if r.TryDecodeAsNil() { + x.ObjectMeta = pkg2_v1.ObjectMeta{} + } else { + yyv4 := &x.ObjectMeta + yyv4.CodecDecodeSelf(d) + } + case "spec": + if r.TryDecodeAsNil() { + x.Spec = HorizontalPodAutoscalerSpec{} + } else { + yyv5 := &x.Spec + yyv5.CodecDecodeSelf(d) + } + case "status": + if r.TryDecodeAsNil() { + x.Status = HorizontalPodAutoscalerStatus{} + } else { + yyv6 := &x.Status + yyv6.CodecDecodeSelf(d) + } + case "kind": + if r.TryDecodeAsNil() { + x.Kind = "" + } else { + x.Kind = string(r.DecodeString()) + } + case "apiVersion": + if r.TryDecodeAsNil() { + x.APIVersion = "" + } else { + x.APIVersion = string(r.DecodeString()) + } + default: + z.DecStructFieldNotFound(-1, yys3) + } // end switch yys3 + } // end for yyj3 + z.DecSendContainerState(codecSelfer_containerMapEnd1234) +} + +func (x *HorizontalPodAutoscaler) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + var yyj9 int + var yyb9 bool + var yyhl9 bool = l >= 0 + yyj9++ + if yyhl9 { + yyb9 = yyj9 > l + } else { + yyb9 = r.CheckBreak() + } + if yyb9 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.ObjectMeta = pkg2_v1.ObjectMeta{} + } else { + yyv10 := &x.ObjectMeta + yyv10.CodecDecodeSelf(d) + } + yyj9++ + if yyhl9 { + yyb9 = yyj9 > l + } else { + yyb9 = r.CheckBreak() + } + if yyb9 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.Spec = HorizontalPodAutoscalerSpec{} + } else { + yyv11 := &x.Spec + yyv11.CodecDecodeSelf(d) + } + yyj9++ + if yyhl9 { + yyb9 = yyj9 > l + } else { + yyb9 = r.CheckBreak() + } + if yyb9 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.Status = HorizontalPodAutoscalerStatus{} + } else { + yyv12 := &x.Status + yyv12.CodecDecodeSelf(d) + } + yyj9++ + if yyhl9 { + yyb9 = yyj9 > l + } else { + yyb9 = r.CheckBreak() + } + if yyb9 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.Kind = "" + } else { + x.Kind = string(r.DecodeString()) + } + yyj9++ + if yyhl9 { + yyb9 = yyj9 > l + } else { + yyb9 = r.CheckBreak() + } + if yyb9 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.APIVersion = "" + } else { + x.APIVersion = string(r.DecodeString()) + } + for { + yyj9++ + if yyhl9 { + yyb9 = yyj9 > l + } else { + yyb9 = r.CheckBreak() + } + if yyb9 { + break + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + z.DecStructFieldNotFound(yyj9-1, "") + } + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) +} + +func (x *HorizontalPodAutoscalerList) CodecEncodeSelf(e *codec1978.Encoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperEncoder(e) + _, _, _ = h, z, r + if x == nil { + r.EncodeNil() + } else { + yym1 := z.EncBinary() + _ = yym1 + if false { + } else if z.HasExtensions() && z.EncExt(x) { + } else { + yysep2 := !z.EncBinary() + yy2arr2 := z.EncBasicHandle().StructToArray + var yyq2 [4]bool + _, _, _ = yysep2, yyq2, yy2arr2 + const yyr2 bool = false + yyq2[0] = true + yyq2[2] = x.Kind != "" + yyq2[3] = x.APIVersion != "" + var yynn2 int + if yyr2 || yy2arr2 { + r.EncodeArrayStart(4) + } else { + yynn2 = 1 + for _, b := range yyq2 { + if b { + yynn2++ + } + } + r.EncodeMapStart(yynn2) + yynn2 = 0 + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[0] { + yy4 := &x.ListMeta + yym5 := z.EncBinary() + _ = yym5 + if false { + } else if z.HasExtensions() && z.EncExt(yy4) { + } else { + z.EncFallback(yy4) + } + } else { + r.EncodeNil() + } + } else { + if yyq2[0] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("metadata")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + yy6 := &x.ListMeta + yym7 := z.EncBinary() + _ = yym7 + if false { + } else if z.HasExtensions() && z.EncExt(yy6) { + } else { + z.EncFallback(yy6) + } + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if x.Items == nil { + r.EncodeNil() + } else { + yym9 := z.EncBinary() + _ = yym9 + if false { + } else { + h.encSliceHorizontalPodAutoscaler(([]HorizontalPodAutoscaler)(x.Items), e) + } + } + } else { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("items")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + if x.Items == nil { + r.EncodeNil() + } else { + yym10 := z.EncBinary() + _ = yym10 + if false { + } else { + h.encSliceHorizontalPodAutoscaler(([]HorizontalPodAutoscaler)(x.Items), e) + } + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[2] { + yym12 := z.EncBinary() + _ = yym12 + if false { + } else { + r.EncodeString(codecSelferC_UTF81234, string(x.Kind)) + } + } else { + r.EncodeString(codecSelferC_UTF81234, "") + } + } else { + if yyq2[2] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("kind")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + yym13 := z.EncBinary() + _ = yym13 + if false { + } else { + r.EncodeString(codecSelferC_UTF81234, string(x.Kind)) + } + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[3] { + yym15 := z.EncBinary() + _ = yym15 + if false { + } else { + r.EncodeString(codecSelferC_UTF81234, string(x.APIVersion)) + } + } else { + r.EncodeString(codecSelferC_UTF81234, "") + } + } else { + if yyq2[3] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("apiVersion")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + yym16 := z.EncBinary() + _ = yym16 + if false { + } else { + r.EncodeString(codecSelferC_UTF81234, string(x.APIVersion)) + } + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayEnd1234) + } else { + z.EncSendContainerState(codecSelfer_containerMapEnd1234) + } + } + } +} + +func (x *HorizontalPodAutoscalerList) CodecDecodeSelf(d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + yym1 := z.DecBinary() + _ = yym1 + if false { + } else if z.HasExtensions() && z.DecExt(x) { + } else { + yyct2 := r.ContainerType() + if yyct2 == codecSelferValueTypeMap1234 { + yyl2 := r.ReadMapStart() + if yyl2 == 0 { + z.DecSendContainerState(codecSelfer_containerMapEnd1234) + } else { + x.codecDecodeSelfFromMap(yyl2, d) + } + } else if yyct2 == codecSelferValueTypeArray1234 { + yyl2 := r.ReadArrayStart() + if yyl2 == 0 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + } else { + x.codecDecodeSelfFromArray(yyl2, d) + } + } else { + panic(codecSelferOnlyMapOrArrayEncodeToStructErr1234) + } + } +} + +func (x *HorizontalPodAutoscalerList) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + var yys3Slc = z.DecScratchBuffer() // default slice to decode into + _ = yys3Slc + var yyhl3 bool = l >= 0 + for yyj3 := 0; ; yyj3++ { + if yyhl3 { + if yyj3 >= l { + break + } + } else { + if r.CheckBreak() { + break + } + } + z.DecSendContainerState(codecSelfer_containerMapKey1234) + yys3Slc = r.DecodeBytes(yys3Slc, true, true) + yys3 := string(yys3Slc) + z.DecSendContainerState(codecSelfer_containerMapValue1234) + switch yys3 { + case "metadata": + if r.TryDecodeAsNil() { + x.ListMeta = pkg1_unversioned.ListMeta{} + } else { + yyv4 := &x.ListMeta + yym5 := z.DecBinary() + _ = yym5 + if false { + } else if z.HasExtensions() && z.DecExt(yyv4) { + } else { + z.DecFallback(yyv4, false) + } + } + case "items": + if r.TryDecodeAsNil() { + x.Items = nil + } else { + yyv6 := &x.Items + yym7 := z.DecBinary() + _ = yym7 + if false { + } else { + h.decSliceHorizontalPodAutoscaler((*[]HorizontalPodAutoscaler)(yyv6), d) + } + } + case "kind": + if r.TryDecodeAsNil() { + x.Kind = "" + } else { + x.Kind = string(r.DecodeString()) + } + case "apiVersion": + if r.TryDecodeAsNil() { + x.APIVersion = "" + } else { + x.APIVersion = string(r.DecodeString()) + } + default: + z.DecStructFieldNotFound(-1, yys3) + } // end switch yys3 + } // end for yyj3 + z.DecSendContainerState(codecSelfer_containerMapEnd1234) +} + +func (x *HorizontalPodAutoscalerList) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + var yyj10 int + var yyb10 bool + var yyhl10 bool = l >= 0 + yyj10++ + if yyhl10 { + yyb10 = yyj10 > l + } else { + yyb10 = r.CheckBreak() + } + if yyb10 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.ListMeta = pkg1_unversioned.ListMeta{} + } else { + yyv11 := &x.ListMeta + yym12 := z.DecBinary() + _ = yym12 + if false { + } else if z.HasExtensions() && z.DecExt(yyv11) { + } else { + z.DecFallback(yyv11, false) + } + } + yyj10++ + if yyhl10 { + yyb10 = yyj10 > l + } else { + yyb10 = r.CheckBreak() + } + if yyb10 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.Items = nil + } else { + yyv13 := &x.Items + yym14 := z.DecBinary() + _ = yym14 + if false { + } else { + h.decSliceHorizontalPodAutoscaler((*[]HorizontalPodAutoscaler)(yyv13), d) + } + } + yyj10++ + if yyhl10 { + yyb10 = yyj10 > l + } else { + yyb10 = r.CheckBreak() + } + if yyb10 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.Kind = "" + } else { + x.Kind = string(r.DecodeString()) + } + yyj10++ + if yyhl10 { + yyb10 = yyj10 > l + } else { + yyb10 = r.CheckBreak() + } + if yyb10 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.APIVersion = "" + } else { + x.APIVersion = string(r.DecodeString()) + } + for { + yyj10++ + if yyhl10 { + yyb10 = yyj10 > l + } else { + yyb10 = r.CheckBreak() + } + if yyb10 { + break + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + z.DecStructFieldNotFound(yyj10-1, "") + } + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) +} + +func (x codecSelfer1234) encSliceHorizontalPodAutoscaler(v []HorizontalPodAutoscaler, e *codec1978.Encoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperEncoder(e) + _, _, _ = h, z, r + r.EncodeArrayStart(len(v)) + for _, yyv1 := range v { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + yy2 := &yyv1 + yy2.CodecEncodeSelf(e) + } + z.EncSendContainerState(codecSelfer_containerArrayEnd1234) +} + +func (x codecSelfer1234) decSliceHorizontalPodAutoscaler(v *[]HorizontalPodAutoscaler, d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + + yyv1 := *v + yyh1, yyl1 := z.DecSliceHelperStart() + var yyc1 bool + _ = yyc1 + if yyl1 == 0 { + if yyv1 == nil { + yyv1 = []HorizontalPodAutoscaler{} + yyc1 = true + } else if len(yyv1) != 0 { + yyv1 = yyv1[:0] + yyc1 = true + } + } else if yyl1 > 0 { + var yyrr1, yyrl1 int + var yyrt1 bool + _, _ = yyrl1, yyrt1 + yyrr1 = yyl1 // len(yyv1) + if yyl1 > cap(yyv1) { + + yyrg1 := len(yyv1) > 0 + yyv21 := yyv1 + yyrl1, yyrt1 = z.DecInferLen(yyl1, z.DecBasicHandle().MaxInitLen, 296) + if yyrt1 { + if yyrl1 <= cap(yyv1) { + yyv1 = yyv1[:yyrl1] + } else { + yyv1 = make([]HorizontalPodAutoscaler, yyrl1) + } + } else { + yyv1 = make([]HorizontalPodAutoscaler, yyrl1) + } + yyc1 = true + yyrr1 = len(yyv1) + if yyrg1 { + copy(yyv1, yyv21) + } + } else if yyl1 != len(yyv1) { + yyv1 = yyv1[:yyl1] + yyc1 = true + } + yyj1 := 0 + for ; yyj1 < yyrr1; yyj1++ { + yyh1.ElemContainerState(yyj1) + if r.TryDecodeAsNil() { + yyv1[yyj1] = HorizontalPodAutoscaler{} + } else { + yyv2 := &yyv1[yyj1] + yyv2.CodecDecodeSelf(d) + } + + } + if yyrt1 { + for ; yyj1 < yyl1; yyj1++ { + yyv1 = append(yyv1, HorizontalPodAutoscaler{}) + yyh1.ElemContainerState(yyj1) + if r.TryDecodeAsNil() { + yyv1[yyj1] = HorizontalPodAutoscaler{} + } else { + yyv3 := &yyv1[yyj1] + yyv3.CodecDecodeSelf(d) + } + + } + } + + } else { + yyj1 := 0 + for ; !r.CheckBreak(); yyj1++ { + + if yyj1 >= len(yyv1) { + yyv1 = append(yyv1, HorizontalPodAutoscaler{}) // var yyz1 HorizontalPodAutoscaler + yyc1 = true + } + yyh1.ElemContainerState(yyj1) + if yyj1 < len(yyv1) { + if r.TryDecodeAsNil() { + yyv1[yyj1] = HorizontalPodAutoscaler{} + } else { + yyv4 := &yyv1[yyj1] + yyv4.CodecDecodeSelf(d) + } + + } else { + z.DecSwallow() + } + + } + if yyj1 < len(yyv1) { + yyv1 = yyv1[:yyj1] + yyc1 = true + } else if yyj1 == 0 && yyv1 == nil { + yyv1 = []HorizontalPodAutoscaler{} + yyc1 = true + } + } + yyh1.End() + if yyc1 { + *v = yyv1 + } +} diff --git a/pkg/apis/autoscaling/v1/types.go b/pkg/apis/autoscaling/v1/types.go new file mode 100644 index 000000000000..6ad1d61af042 --- /dev/null +++ b/pkg/apis/autoscaling/v1/types.go @@ -0,0 +1,88 @@ +/* +Copyright 2016 The Kubernetes Authors All rights reserved. + +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 v1 + +import ( + "k8s.io/kubernetes/pkg/api/unversioned" + "k8s.io/kubernetes/pkg/api/v1" +) + +// CrossVersionObjectReference contains enough information to let you identify the referred resource. +type CrossVersionObjectReference struct { + // Kind of the referent; More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds" + Kind string `json:"kind"` + // Name of the referent; More info: http://releases.k8s.io/HEAD/docs/user-guide/identifiers.md#names + Name string `json:"name"` + // API version of the referent + APIVersion string `json:"apiVersion,omitempty"` +} + +// specification of a horizontal pod autoscaler. +type HorizontalPodAutoscalerSpec struct { + // reference to scaled resource; horizontal pod autoscaler will learn the current resource consumption + // and will set the desired number of pods by using its Scale subresource. + ScaleTargetRef CrossVersionObjectReference `json:"scaleTargetRef"` + // lower limit for the number of pods that can be set by the autoscaler, default 1. + MinReplicas *int32 `json:"minReplicas,omitempty"` + // upper limit for the number of pods that can be set by the autoscaler; cannot be smaller than MinReplicas. + MaxReplicas int32 `json:"maxReplicas"` + // target average CPU utilization (represented as a percentage of requested CPU) over all the pods; + TargetCPUUtilizationPercentage *int32 `json:"targetCPUUtilizationPercentage,omitempty"` +} + +// current status of a horizontal pod autoscaler +type HorizontalPodAutoscalerStatus struct { + // most recent generation observed by this autoscaler. + ObservedGeneration *int64 `json:"observedGeneration,omitempty"` + + // last time the HorizontalPodAutoscaler scaled the number of pods; + // used by the autoscaler to control how often the number of pods is changed. + LastScaleTime *unversioned.Time `json:"lastScaleTime,omitempty"` + + // current number of replicas of pods managed by this autoscaler. + CurrentReplicas int32 `json:"currentReplicas"` + + // desired number of replicas of pods managed by this autoscaler. + DesiredReplicas int32 `json:"desiredReplicas"` + + // current average CPU utilization over all pods, represented as a percentage of requested CPU, + // e.g. 70 means that an average pod is using now 70% of its requested CPU. + CurrentCPUUtilizationPercentage *int32 `json:"currentCPUUtilizationPercentage,omitempty"` +} + +// configuration of a horizontal pod autoscaler. +type HorizontalPodAutoscaler struct { + unversioned.TypeMeta `json:",inline"` + // Standard object metadata. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata + v1.ObjectMeta `json:"metadata,omitempty"` + + // behaviour of autoscaler. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status. + Spec HorizontalPodAutoscalerSpec `json:"spec,omitempty"` + + // current information about the autoscaler. + Status HorizontalPodAutoscalerStatus `json:"status,omitempty"` +} + +// list of horizontal pod autoscaler objects. +type HorizontalPodAutoscalerList struct { + unversioned.TypeMeta `json:",inline"` + // Standard list metadata. + unversioned.ListMeta `json:"metadata,omitempty"` + + // list of horizontal pod autoscaler objects. + Items []HorizontalPodAutoscaler `json:"items"` +} diff --git a/pkg/apis/autoscaling/v1/types_swagger_doc_generated.go b/pkg/apis/autoscaling/v1/types_swagger_doc_generated.go new file mode 100644 index 000000000000..ccec779f2af9 --- /dev/null +++ b/pkg/apis/autoscaling/v1/types_swagger_doc_generated.go @@ -0,0 +1,87 @@ +/* +Copyright 2015 The Kubernetes Authors All rights reserved. + +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 v1 + +// This file contains a collection of methods that can be used from go-resful to +// generate Swagger API documentation for its models. Please read this PR for more +// information on the implementation: https://github.com/emicklei/go-restful/pull/215 +// +// TODOs are ignored from the parser (e.g. TODO(andronat):... || TODO:...) if and only if +// they are on one line! For multiple line or blocks that you want to ignore use ---. +// Any context after a --- is ignored. +// +// Those methods can be generated by using hack/update-generated-swagger-docs.sh + +// AUTO-GENERATED FUNCTIONS START HERE +var map_CrossVersionObjectReference = map[string]string{ + "": "CrossVersionObjectReference contains enough information to let you identify the referred resource.", + "kind": "Kind of the referent; More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds\"", + "name": "Name of the referent; More info: http://releases.k8s.io/HEAD/docs/user-guide/identifiers.md#names", + "apiVersion": "API version of the referent", +} + +func (CrossVersionObjectReference) SwaggerDoc() map[string]string { + return map_CrossVersionObjectReference +} + +var map_HorizontalPodAutoscaler = map[string]string{ + "": "configuration of a horizontal pod autoscaler.", + "metadata": "Standard object metadata. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata", + "spec": "behaviour of autoscaler. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status.", + "status": "current information about the autoscaler.", +} + +func (HorizontalPodAutoscaler) SwaggerDoc() map[string]string { + return map_HorizontalPodAutoscaler +} + +var map_HorizontalPodAutoscalerList = map[string]string{ + "": "list of horizontal pod autoscaler objects.", + "metadata": "Standard list metadata.", + "items": "list of horizontal pod autoscaler objects.", +} + +func (HorizontalPodAutoscalerList) SwaggerDoc() map[string]string { + return map_HorizontalPodAutoscalerList +} + +var map_HorizontalPodAutoscalerSpec = map[string]string{ + "": "specification of a horizontal pod autoscaler.", + "scaleTargetRef": "reference to scaled resource; horizontal pod autoscaler will learn the current resource consumption and will set the desired number of pods by using its Scale subresource.", + "minReplicas": "lower limit for the number of pods that can be set by the autoscaler, default 1.", + "maxReplicas": "upper limit for the number of pods that can be set by the autoscaler; cannot be smaller than MinReplicas.", + "targetCPUUtilizationPercentage": "target average CPU utilization (represented as a percentage of requested CPU) over all the pods;", +} + +func (HorizontalPodAutoscalerSpec) SwaggerDoc() map[string]string { + return map_HorizontalPodAutoscalerSpec +} + +var map_HorizontalPodAutoscalerStatus = map[string]string{ + "": "current status of a horizontal pod autoscaler", + "observedGeneration": "most recent generation observed by this autoscaler.", + "lastScaleTime": "last time the HorizontalPodAutoscaler scaled the number of pods; used by the autoscaler to control how often the number of pods is changed.", + "currentReplicas": "current number of replicas of pods managed by this autoscaler.", + "desiredReplicas": "desired number of replicas of pods managed by this autoscaler.", + "currentCPUUtilizationPercentage": "current average CPU utilization over all pods, represented as a percentage of requested CPU, e.g. 70 means that an average pod is using now 70% of its requested CPU.", +} + +func (HorizontalPodAutoscalerStatus) SwaggerDoc() map[string]string { + return map_HorizontalPodAutoscalerStatus +} + +// AUTO-GENERATED FUNCTIONS END HERE diff --git a/pkg/client/unversioned/autoscaling.go b/pkg/client/unversioned/autoscaling.go new file mode 100644 index 000000000000..9445c0c6c930 --- /dev/null +++ b/pkg/client/unversioned/autoscaling.go @@ -0,0 +1,82 @@ +/* +Copyright 2016 The Kubernetes Authors All rights reserved. + +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 unversioned + +import ( + "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/apimachinery/registered" + "k8s.io/kubernetes/pkg/apis/autoscaling" +) + +type AutoscalingInterface interface { + HorizontalPodAutoscalersNamespacer +} + +// AutoscalingClient is used to interact with Kubernetes autoscaling features. +type AutoscalingClient struct { + *RESTClient +} + +func (c *AutoscalingClient) HorizontalPodAutoscalers(namespace string) HorizontalPodAutoscalerInterface { + return newHorizontalPodAutoscalersV1(c, namespace) +} + +func NewAutoscaling(c *Config) (*AutoscalingClient, error) { + config := *c + if err := setAutoscalingDefaults(&config); err != nil { + return nil, err + } + client, err := RESTClientFor(&config) + if err != nil { + return nil, err + } + return &AutoscalingClient{client}, nil +} + +func NewAutoscalingOrDie(c *Config) *AutoscalingClient { + client, err := NewAutoscaling(c) + if err != nil { + panic(err) + } + return client +} + +func setAutoscalingDefaults(config *Config) error { + // if autoscaling group is not registered, return an error + g, err := registered.Group(autoscaling.GroupName) + if err != nil { + return err + } + config.APIPath = defaultAPIPath + if config.UserAgent == "" { + config.UserAgent = DefaultKubernetesUserAgent() + } + // TODO: Unconditionally set the config.Version, until we fix the config. + //if config.Version == "" { + copyGroupVersion := g.GroupVersion + config.GroupVersion = ©GroupVersion + //} + + config.Codec = api.Codecs.LegacyCodec(*config.GroupVersion) + if config.QPS == 0 { + config.QPS = 5 + } + if config.Burst == 0 { + config.Burst = 10 + } + return nil +} diff --git a/pkg/client/unversioned/client.go b/pkg/client/unversioned/client.go index 24bc54c9376c..8a891c44a9aa 100644 --- a/pkg/client/unversioned/client.go +++ b/pkg/client/unversioned/client.go @@ -41,6 +41,7 @@ type Interface interface { PersistentVolumeClaimsNamespacer ComponentStatusesInterface ConfigMapsNamespacer + Autoscaling() AutoscalingInterface Extensions() ExtensionsInterface Discovery() DiscoveryInterface } @@ -111,6 +112,7 @@ func (c *Client) ConfigMaps(namespace string) ConfigMapsInterface { // Client is the implementation of a Kubernetes client. type Client struct { *RESTClient + *AutoscalingClient *ExtensionsClient *DiscoveryClient } @@ -146,6 +148,10 @@ func IsTimeout(err error) bool { return false } +func (c *Client) Autoscaling() AutoscalingInterface { + return c.AutoscalingClient +} + func (c *Client) Extensions() ExtensionsInterface { return c.ExtensionsClient } diff --git a/pkg/client/unversioned/helper.go b/pkg/client/unversioned/helper.go index 507f9a30a10e..346b3fdc478f 100644 --- a/pkg/client/unversioned/helper.go +++ b/pkg/client/unversioned/helper.go @@ -33,6 +33,7 @@ import ( "k8s.io/kubernetes/pkg/api" "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/util" @@ -155,16 +156,25 @@ func New(c *Config) (*Client, error) { return nil, err } - if _, err := registered.Group(extensions.GroupName); err != nil { - return &Client{RESTClient: client, ExtensionsClient: nil, DiscoveryClient: discoveryClient}, nil + var autoscalingClient *AutoscalingClient + if registered.IsRegistered(autoscaling.GroupName) { + autoscalingConfig := *c + autoscalingClient, err = NewAutoscaling(&autoscalingConfig) + if err != nil { + return nil, err + } } - experimentalConfig := *c - experimentalClient, err := NewExtensions(&experimentalConfig) - if err != nil { - return nil, err + + var extensionsClient *ExtensionsClient + if registered.IsRegistered(extensions.GroupName) { + extensionsConfig := *c + extensionsClient, err = NewExtensions(&extensionsConfig) + if err != nil { + return nil, err + } } - return &Client{RESTClient: client, ExtensionsClient: experimentalClient, DiscoveryClient: discoveryClient}, nil + return &Client{RESTClient: client, AutoscalingClient: autoscalingClient, ExtensionsClient: extensionsClient, DiscoveryClient: discoveryClient}, nil } // MatchesServerVersion queries the server to compares the build version diff --git a/pkg/client/unversioned/horizontalpodautoscaler.go b/pkg/client/unversioned/horizontalpodautoscaler.go index efdf82b70ed6..a4efc232a41a 100644 --- a/pkg/client/unversioned/horizontalpodautoscaler.go +++ b/pkg/client/unversioned/horizontalpodautoscaler.go @@ -101,3 +101,68 @@ func (c *horizontalPodAutoscalers) Watch(opts api.ListOptions) (watch.Interface, VersionedParams(&opts, api.ParameterCodec). Watch() } + +// horizontalPodAutoscalersV1 implements HorizontalPodAutoscalersNamespacer interface using AutoscalingClient internally +// TODO(piosz): get back to one client implementation once HPA will be graduated to GA completely +type horizontalPodAutoscalersV1 struct { + client *AutoscalingClient + ns string +} + +// newHorizontalPodAutoscalers returns a horizontalPodAutoscalers +func newHorizontalPodAutoscalersV1(c *AutoscalingClient, namespace string) *horizontalPodAutoscalersV1 { + return &horizontalPodAutoscalersV1{ + client: c, + ns: namespace, + } +} + +// List takes label and field selectors, and returns the list of horizontalPodAutoscalers that match those selectors. +func (c *horizontalPodAutoscalersV1) List(opts api.ListOptions) (result *extensions.HorizontalPodAutoscalerList, err error) { + result = &extensions.HorizontalPodAutoscalerList{} + err = c.client.Get().Namespace(c.ns).Resource("horizontalPodAutoscalers").VersionedParams(&opts, api.ParameterCodec).Do().Into(result) + return +} + +// Get takes the name of the horizontalPodAutoscaler, and returns the corresponding HorizontalPodAutoscaler object, and an error if it occurs +func (c *horizontalPodAutoscalersV1) Get(name string) (result *extensions.HorizontalPodAutoscaler, err error) { + result = &extensions.HorizontalPodAutoscaler{} + err = c.client.Get().Namespace(c.ns).Resource("horizontalPodAutoscalers").Name(name).Do().Into(result) + return +} + +// Delete takes the name of the horizontalPodAutoscaler and deletes it. Returns an error if one occurs. +func (c *horizontalPodAutoscalersV1) Delete(name string, options *api.DeleteOptions) error { + return c.client.Delete().Namespace(c.ns).Resource("horizontalPodAutoscalers").Name(name).Body(options).Do().Error() +} + +// Create takes the representation of a horizontalPodAutoscaler and creates it. Returns the server's representation of the horizontalPodAutoscaler, and an error, if it occurs. +func (c *horizontalPodAutoscalersV1) Create(horizontalPodAutoscaler *extensions.HorizontalPodAutoscaler) (result *extensions.HorizontalPodAutoscaler, err error) { + result = &extensions.HorizontalPodAutoscaler{} + err = c.client.Post().Namespace(c.ns).Resource("horizontalPodAutoscalers").Body(horizontalPodAutoscaler).Do().Into(result) + return +} + +// Update takes the representation of a horizontalPodAutoscaler and updates it. Returns the server's representation of the horizontalPodAutoscaler, and an error, if it occurs. +func (c *horizontalPodAutoscalersV1) Update(horizontalPodAutoscaler *extensions.HorizontalPodAutoscaler) (result *extensions.HorizontalPodAutoscaler, err error) { + result = &extensions.HorizontalPodAutoscaler{} + err = c.client.Put().Namespace(c.ns).Resource("horizontalPodAutoscalers").Name(horizontalPodAutoscaler.Name).Body(horizontalPodAutoscaler).Do().Into(result) + return +} + +// UpdateStatus takes the representation of a horizontalPodAutoscaler and updates it. Returns the server's representation of the horizontalPodAutoscaler, and an error, if it occurs. +func (c *horizontalPodAutoscalersV1) UpdateStatus(horizontalPodAutoscaler *extensions.HorizontalPodAutoscaler) (result *extensions.HorizontalPodAutoscaler, err error) { + result = &extensions.HorizontalPodAutoscaler{} + err = c.client.Put().Namespace(c.ns).Resource("horizontalPodAutoscalers").Name(horizontalPodAutoscaler.Name).SubResource("status").Body(horizontalPodAutoscaler).Do().Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested horizontalPodAutoscalers. +func (c *horizontalPodAutoscalersV1) Watch(opts api.ListOptions) (watch.Interface, error) { + return c.client.Get(). + Prefix("watch"). + Namespace(c.ns). + Resource("horizontalPodAutoscalers"). + VersionedParams(&opts, api.ParameterCodec). + Watch() +} diff --git a/pkg/client/unversioned/horizontalpodautoscaler_test.go b/pkg/client/unversioned/horizontalpodautoscaler_test.go index bd4c06c82596..b35d258af3e5 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,24 @@ 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 TestHorizontalPodAutoscalerUpdate(t *testing.T) { +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{ @@ -119,15 +150,21 @@ 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"), 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).Update(horizontalPodAutoscaler) defer c.Close() c.Validate(t, response, err) } -func TestHorizontalPodAutoscalerUpdateStatus(t *testing.T) { +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{ @@ -137,34 +174,52 @@ func TestHorizontalPodAutoscalerUpdateStatus(t *testing.T) { }, } 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: "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).UpdateStatus(horizontalPodAutoscaler) + response, err := getClient(t, c, ns, resourceGroup).UpdateStatus(horizontalPodAutoscaler) defer c.Close() c.Validate(t, response, err) } -func TestHorizontalPodAutoscalerDelete(t *testing.T) { +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 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: "DELETE", Path: group.ResourcePath(getHorizontalPodAutoscalersResoureName(), ns, "foo"), Query: simple.BuildQueryValues(nil)}, + Response: simple.Response{StatusCode: 200}, + ResourceGroup: resourceGroup, } - err := c.Setup(t).Extensions().HorizontalPodAutoscalers(ns).Delete("foo", nil) + err := getClient(t, c, ns, resourceGroup).Delete("foo", nil) defer c.Close() c.Validate(t, nil, err) } -func TestHorizontalPodAutoscalerWatch(t *testing.T) { +func TestHorizontalPodAutoscalerDelete(t *testing.T) { + 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: "GET", - Path: testapi.Extensions.ResourcePathWithPrefix("watch", getHorizontalPodAutoscalersResoureName(), "", ""), + Path: group.ResourcePathWithPrefix("watch", getHorizontalPodAutoscalersResoureName(), "", ""), Query: url.Values{"resourceVersion": []string{}}}, - Response: simple.Response{StatusCode: 200}, + Response: simple.Response{StatusCode: 200}, + ResourceGroup: resourceGroup, } - _, err := c.Setup(t).Extensions().HorizontalPodAutoscalers(api.NamespaceAll).Watch(api.ListOptions{}) + _, err := getClient(t, c, api.NamespaceAll, resourceGroup).Watch(api.ListOptions{}) defer c.Close() c.Validate(t, nil, err) } + +func TestHorizontalPodAutoscalerWatch(t *testing.T) { + testHorizontalPodAutoscalerWatch(t, testapi.Extensions, extensions.GroupName) + testHorizontalPodAutoscalerWatch(t, testapi.Autoscaling, autoscaling.GroupName) +} diff --git a/pkg/client/unversioned/import_known_versions.go b/pkg/client/unversioned/import_known_versions.go index 95f6644301cb..a86afb6dda0a 100644 --- a/pkg/client/unversioned/import_known_versions.go +++ b/pkg/client/unversioned/import_known_versions.go @@ -23,6 +23,7 @@ import ( _ "k8s.io/kubernetes/pkg/api/install" "k8s.io/kubernetes/pkg/apimachinery/registered" _ "k8s.io/kubernetes/pkg/apis/authorization/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" diff --git a/pkg/client/unversioned/testclient/fake_horizontal_pod_autoscalers.go b/pkg/client/unversioned/testclient/fake_horizontal_pod_autoscalers.go index a70a1af99dbf..e50b326d9142 100644 --- a/pkg/client/unversioned/testclient/fake_horizontal_pod_autoscalers.go +++ b/pkg/client/unversioned/testclient/fake_horizontal_pod_autoscalers.go @@ -91,3 +91,74 @@ func (c *FakeHorizontalPodAutoscalers) Delete(name string, options *api.DeleteOp func (c *FakeHorizontalPodAutoscalers) Watch(opts api.ListOptions) (watch.Interface, error) { return c.Fake.InvokesWatch(NewWatchAction("horizontalpodautoscalers", c.Namespace, opts)) } + +// FakeHorizontalPodAutoscalers implements HorizontalPodAutoscalerInterface. Meant to be embedded into a struct to get a default +// implementation. This makes faking out just the methods you want to test easier. +// This is a test implementation of HorizontalPodAutoscalersV1 +// TODO(piosz): get back to one client implementation once HPA will be graduated to GA completely +type FakeHorizontalPodAutoscalersV1 struct { + Fake *FakeAutoscaling + Namespace string +} + +func (c *FakeHorizontalPodAutoscalersV1) Get(name string) (*extensions.HorizontalPodAutoscaler, error) { + obj, err := c.Fake.Invokes(NewGetAction("horizontalpodautoscalers", c.Namespace, name), &extensions.HorizontalPodAutoscaler{}) + if obj == nil { + return nil, err + } + + return obj.(*extensions.HorizontalPodAutoscaler), err +} + +func (c *FakeHorizontalPodAutoscalersV1) List(opts api.ListOptions) (*extensions.HorizontalPodAutoscalerList, error) { + obj, err := c.Fake.Invokes(NewListAction("horizontalpodautoscalers", c.Namespace, opts), &extensions.HorizontalPodAutoscalerList{}) + if obj == nil { + return nil, err + } + label := opts.LabelSelector + if label == nil { + label = labels.Everything() + } + list := &extensions.HorizontalPodAutoscalerList{} + for _, a := range obj.(*extensions.HorizontalPodAutoscalerList).Items { + if label.Matches(labels.Set(a.Labels)) { + list.Items = append(list.Items, a) + } + } + return list, err +} + +func (c *FakeHorizontalPodAutoscalersV1) Create(a *extensions.HorizontalPodAutoscaler) (*extensions.HorizontalPodAutoscaler, error) { + obj, err := c.Fake.Invokes(NewCreateAction("horizontalpodautoscalers", c.Namespace, a), a) + if obj == nil { + return nil, err + } + + return obj.(*extensions.HorizontalPodAutoscaler), err +} + +func (c *FakeHorizontalPodAutoscalersV1) Update(a *extensions.HorizontalPodAutoscaler) (*extensions.HorizontalPodAutoscaler, error) { + obj, err := c.Fake.Invokes(NewUpdateAction("horizontalpodautoscalers", c.Namespace, a), a) + if obj == nil { + return nil, err + } + + return obj.(*extensions.HorizontalPodAutoscaler), err +} + +func (c *FakeHorizontalPodAutoscalersV1) UpdateStatus(a *extensions.HorizontalPodAutoscaler) (*extensions.HorizontalPodAutoscaler, error) { + obj, err := c.Fake.Invokes(NewUpdateSubresourceAction("horizontalpodautoscalers", "status", c.Namespace, a), &extensions.HorizontalPodAutoscaler{}) + if obj == nil { + return nil, err + } + return obj.(*extensions.HorizontalPodAutoscaler), err +} + +func (c *FakeHorizontalPodAutoscalersV1) Delete(name string, options *api.DeleteOptions) error { + _, err := c.Fake.Invokes(NewDeleteAction("horizontalpodautoscalers", c.Namespace, name), &extensions.HorizontalPodAutoscaler{}) + return err +} + +func (c *FakeHorizontalPodAutoscalersV1) Watch(opts api.ListOptions) (watch.Interface, error) { + return c.Fake.InvokesWatch(NewWatchAction("horizontalpodautoscalers", c.Namespace, opts)) +} diff --git a/pkg/client/unversioned/testclient/simple/simple_testclient.go b/pkg/client/unversioned/testclient/simple/simple_testclient.go index 7325f16aed1c..ab32f5295eeb 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) @@ -84,6 +88,10 @@ func (c *Client) Setup(t *testing.T) *Client { // TODO: caesarxuchao: hacky way to specify version of Experimental client. // We will fix this by supporting multiple group versions in Config + c.AutoscalingClient = client.NewAutoscalingOrDie(&client.Config{ + Host: c.server.URL, + ContentConfig: client.ContentConfig{GroupVersion: testapi.Autoscaling.GroupVersion()}, + }) c.ExtensionsClient = client.NewExtensionsOrDie(&client.Config{ Host: c.server.URL, ContentConfig: client.ContentConfig{GroupVersion: testapi.Extensions.GroupVersion()}, @@ -138,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) @@ -206,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/client/unversioned/testclient/testclient.go b/pkg/client/unversioned/testclient/testclient.go index 0da887984189..ef962e417435 100644 --- a/pkg/client/unversioned/testclient/testclient.go +++ b/pkg/client/unversioned/testclient/testclient.go @@ -274,6 +274,10 @@ func (c *Fake) Namespaces() client.NamespaceInterface { return &FakeNamespaces{Fake: c} } +func (c *Fake) Autoscaling() client.AutoscalingInterface { + return &FakeAutoscaling{c} +} + func (c *Fake) Extensions() client.ExtensionsInterface { return &FakeExperimental{c} } @@ -304,6 +308,19 @@ func (c *Fake) SwaggerSchema(version unversioned.GroupVersion) (*swagger.ApiDecl return &swagger.ApiDeclaration{}, nil } +// NewSimpleFakeAutoscaling returns a client that will respond with the provided objects +func NewSimpleFakeAutoscaling(objects ...runtime.Object) *FakeAutoscaling { + return &FakeAutoscaling{Fake: NewSimpleFake(objects...)} +} + +type FakeAutoscaling struct { + *Fake +} + +func (c *FakeAutoscaling) HorizontalPodAutoscalers(namespace string) client.HorizontalPodAutoscalerInterface { + return &FakeHorizontalPodAutoscalersV1{Fake: c, Namespace: namespace} +} + // NewSimpleFakeExp returns a client that will respond with the provided objects func NewSimpleFakeExp(objects ...runtime.Object) *FakeExperimental { return &FakeExperimental{Fake: NewSimpleFake(objects...)} diff --git a/pkg/genericapiserver/genericapiserver.go b/pkg/genericapiserver/genericapiserver.go index 2c3560c20190..0277d946d2c3 100644 --- a/pkg/genericapiserver/genericapiserver.go +++ b/pkg/genericapiserver/genericapiserver.go @@ -77,7 +77,9 @@ func NewStorageDestinations() StorageDestinations { } } +// AddAPIGroup replaces 'group' if it's already registered. func (s *StorageDestinations) AddAPIGroup(group string, defaultStorage storage.Interface) { + glog.Infof("Adding storage destination for group %v", group) s.APIGroups[group] = &StorageDestinationsForAPIGroup{ Default: defaultStorage, Overrides: map[string]storage.Interface{}, @@ -94,10 +96,16 @@ func (s *StorageDestinations) AddStorageOverride(group, resource string, overrid s.APIGroups[group].Overrides[resource] = override } +// Get finds the storage destination for the given group and resource. It will +// Fatalf if the group has no storage destination configured. func (s *StorageDestinations) Get(group, resource string) storage.Interface { apigroup, ok := s.APIGroups[group] if !ok { - glog.Errorf("No storage defined for API group: '%s'", apigroup) + // TODO: return an error like a normal function. For now, + // Fatalf is better than just logging an error, because this + // condition guarantees future problems and this is a less + // mysterious failure point. + glog.Fatalf("No storage defined for API group: '%s'. Defined groups: %#v", group, s.APIGroups) return nil } if apigroup.Overrides != nil { @@ -108,6 +116,30 @@ func (s *StorageDestinations) Get(group, resource string) storage.Interface { return apigroup.Default } +// Search is like Get, but can be used to search a list of groups. It tries the +// groups in order (and Fatalf's if none of them exist). The intention is for +// this to be used for resources that move between groups. +func (s *StorageDestinations) Search(groups []string, resource string) storage.Interface { + for _, group := range groups { + apigroup, ok := s.APIGroups[group] + if !ok { + continue + } + if apigroup.Overrides != nil { + if client, exists := apigroup.Overrides[resource]; exists { + return client + } + } + return apigroup.Default + } + // TODO: return an error like a normal function. For now, + // Fatalf is better than just logging an error, because this + // condition guarantees future problems and this is a less + // mysterious failure point. + glog.Fatalf("No storage defined for any of the groups: %v. Defined groups: %#v", groups, s.APIGroups) + return nil +} + // Get all backends for all registered storage destinations. // Used for getting all instances for health validations. func (s *StorageDestinations) Backends() []string { diff --git a/pkg/kubectl/cmd/util/factory.go b/pkg/kubectl/cmd/util/factory.go index ce75c476bdcc..9fbd31a4a3f9 100644 --- a/pkg/kubectl/cmd/util/factory.go +++ b/pkg/kubectl/cmd/util/factory.go @@ -39,6 +39,7 @@ import ( "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/api/validation" "k8s.io/kubernetes/pkg/apimachinery/registered" + "k8s.io/kubernetes/pkg/apis/autoscaling" "k8s.io/kubernetes/pkg/apis/extensions" clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" client "k8s.io/kubernetes/pkg/client/unversioned" @@ -217,6 +218,8 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory { switch mapping.GroupVersionKind.Group { case api.GroupName: return client.RESTClient, nil + case autoscaling.GroupName: + return client.AutoscalingClient.RESTClient, nil case extensions.GroupName: return client.ExtensionsClient.RESTClient, nil } @@ -700,6 +703,12 @@ func (c *clientSwaggerSchema) ValidateBytes(data []byte) error { if ok := registered.IsEnabledVersion(gvk.GroupVersion()); !ok { return fmt.Errorf("API version %q isn't supported, only supports API versions %q", gvk.GroupVersion().String(), registered.EnabledVersions()) } + if gvk.Group == autoscaling.GroupName { + if c.c.AutoscalingClient == nil { + return errors.New("unable to validate: no autoscaling client") + } + return getSchemaAndValidate(c.c.AutoscalingClient.RESTClient, data, "apis/", gvk.GroupVersion().String(), c.cacheDir) + } if gvk.Group == extensions.GroupName { if c.c.ExtensionsClient == nil { return errors.New("unable to validate: no experimental client") diff --git a/pkg/master/import_known_versions.go b/pkg/master/import_known_versions.go index 8a6f71919aa7..b07ebdbe6638 100644 --- a/pkg/master/import_known_versions.go +++ b/pkg/master/import_known_versions.go @@ -23,6 +23,7 @@ import ( _ "k8s.io/kubernetes/pkg/api/install" "k8s.io/kubernetes/pkg/apimachinery/registered" _ "k8s.io/kubernetes/pkg/apis/authorization/install" + _ "k8s.io/kubernetes/pkg/apis/autoscaling/install" _ "k8s.io/kubernetes/pkg/apis/componentconfig/install" _ "k8s.io/kubernetes/pkg/apis/extensions/install" ) diff --git a/pkg/master/master.go b/pkg/master/master.go index 40d6e50b32de..ddd968fc88f7 100644 --- a/pkg/master/master.go +++ b/pkg/master/master.go @@ -31,6 +31,7 @@ import ( "k8s.io/kubernetes/pkg/api/rest" "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/apiserver" apiservermetrics "k8s.io/kubernetes/pkg/apiserver/metrics" @@ -209,6 +210,7 @@ func (m *Master) InstallAPIs(c *Config) { // allGroups records all supported groups at /apis allGroups := []unversioned.APIGroup{} + // Install extensions unless disabled. if !m.ApiGroupVersionOverrides["extensions/v1beta1"].Disable { m.thirdPartyStorage = c.StorageDestinations.APIGroups[extensions.GroupName].Default @@ -250,6 +252,39 @@ func (m *Master) InstallAPIs(c *Config) { } allGroups = append(allGroups, group) } + + // Install autoscaling unless disabled. + if !m.ApiGroupVersionOverrides["autoscaling/v1"].Disable { + autoscalingResources := m.getAutoscalingResources(c) + autoscalingGroupMeta := registered.GroupOrDie(autoscaling.GroupName) + + // Hard code preferred group version to autoscaling/v1 + autoscalingGroupMeta.GroupVersion = unversioned.GroupVersion{Group: "autoscaling", Version: "v1"} + + apiGroupInfo := genericapiserver.APIGroupInfo{ + GroupMeta: *autoscalingGroupMeta, + VersionedResourcesStorageMap: map[string]map[string]rest.Storage{ + "v1": autoscalingResources, + }, + OptionsExternalVersion: ®istered.GroupOrDie(api.GroupName).GroupVersion, + Scheme: api.Scheme, + ParameterCodec: api.ParameterCodec, + NegotiatedSerializer: api.Codecs, + } + apiGroupsInfo = append(apiGroupsInfo, apiGroupInfo) + + autoscalingGVForDiscovery := unversioned.GroupVersionForDiscovery{ + GroupVersion: autoscalingGroupMeta.GroupVersion.String(), + Version: autoscalingGroupMeta.GroupVersion.Version, + } + group := unversioned.APIGroup{ + Name: autoscalingGroupMeta.GroupVersion.Group, + Versions: []unversioned.GroupVersionForDiscovery{autoscalingGVForDiscovery}, + PreferredVersion: autoscalingGVForDiscovery, + } + allGroups = append(allGroups, group) + } + if err := m.InstallAPIGroups(apiGroupsInfo); err != nil { glog.Fatalf("Error in registering group versions: %v", err) } @@ -586,9 +621,7 @@ func (m *Master) getExtensionResources(c *Config) map[string]rest.Storage { storage := map[string]rest.Storage{} if isEnabled("horizontalpodautoscalers") { - autoscalerStorage, autoscalerStatusStorage := horizontalpodautoscaleretcd.NewREST(dbClient("horizontalpodautoscalers"), storageDecorator) - storage["horizontalpodautoscalers"] = autoscalerStorage - storage["horizontalpodautoscalers/status"] = autoscalerStatusStorage + m.constructHPAResources(c, storage) controllerStorage := expcontrolleretcd.NewStorage(c.StorageDestinations.Get("", "replicationControllers"), storageDecorator) storage["replicationcontrollers"] = controllerStorage.ReplicationController storage["replicationcontrollers/scale"] = controllerStorage.Scale @@ -646,6 +679,40 @@ func (m *Master) getExtensionResources(c *Config) map[string]rest.Storage { return storage } +// constructHPAResources makes HPA resources and adds them to the storage map. +// They're installed in both autoscaling and extensions. It's assumed that +// you've already done the check that they should be on. +func (m *Master) constructHPAResources(c *Config, restStorage map[string]rest.Storage) { + // Note that hpa's storage settings are changed by changing the autoscaling + // group. Clearly we want all hpas to be stored in the same place no + // matter where they're accessed from. + storageDecorator := m.StorageDecorator() + dbClient := func(resource string) storage.Interface { + return c.StorageDestinations.Search([]string{autoscaling.GroupName, extensions.GroupName}, resource) + } + autoscalerStorage, autoscalerStatusStorage := horizontalpodautoscaleretcd.NewREST(dbClient("horizontalpodautoscalers"), storageDecorator) + restStorage["horizontalpodautoscalers"] = autoscalerStorage + restStorage["horizontalpodautoscalers/status"] = autoscalerStatusStorage +} + +// getAutoscalingResources returns the resources for autoscaling api +func (m *Master) getAutoscalingResources(c *Config) map[string]rest.Storage { + resourceOverrides := m.ApiGroupVersionOverrides["autoscaling/v1"].ResourceOverrides + isEnabled := func(resource string) bool { + // Check if the resource has been overriden. + if enabled, ok := resourceOverrides[resource]; ok { + return enabled + } + return !m.ApiGroupVersionOverrides["autoscaling/v1"].Disable + } + + storage := map[string]rest.Storage{} + if isEnabled("horizontalpodautoscalers") { + m.constructHPAResources(c, storage) + } + return storage +} + // findExternalAddress returns ExternalIP of provided node with fallback to LegacyHostIP. func findExternalAddress(node *api.Node) (string, error) { var fallback string diff --git a/pkg/master/master_test.go b/pkg/master/master_test.go index 30e18b5f775a..03f44567e995 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/pkg/runtime/codec_check.go b/pkg/runtime/codec_check.go new file mode 100644 index 000000000000..09e7d51ad9f9 --- /dev/null +++ b/pkg/runtime/codec_check.go @@ -0,0 +1,50 @@ +/* +Copyright 2016 The Kubernetes Authors All rights reserved. + +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 runtime + +import ( + "fmt" + "reflect" + + "k8s.io/kubernetes/pkg/api/unversioned" +) + +// CheckCodec makes sure that the codec can encode objects like internalType, +// decode all of the external types listed, and also decode them into the given +// object. (Will modify internalObject.) (Assumes JSON serialization.) +// TODO: verify that the correct external version is chosen on encode... +func CheckCodec(c Codec, internalType Object, externalTypes ...unversioned.GroupVersionKind) error { + _, err := Encode(c, internalType) + if err != nil { + return fmt.Errorf("Internal type not encodable: %v", err) + } + for _, et := range externalTypes { + exBytes := []byte(fmt.Sprintf(`{"kind":"%v","apiVersion":"%v"}`, et.Kind, et.GroupVersion().String())) + obj, err := Decode(c, exBytes) + if err != nil { + return fmt.Errorf("external type %s not interpretable: %v", et, err) + } + if reflect.TypeOf(obj) != reflect.TypeOf(internalType) { + return fmt.Errorf("decode of external type %s produced: %#v", et, obj) + } + err = DecodeInto(c, exBytes, internalType) + if err != nil { + return fmt.Errorf("external type %s not convertable to internal type: %v", et, err) + } + } + return nil +} diff --git a/pkg/runtime/serializer/versioning/versioning.go b/pkg/runtime/serializer/versioning/versioning.go index ea255a90c772..0ded5365ddc3 100644 --- a/pkg/runtime/serializer/versioning/versioning.go +++ b/pkg/runtime/serializer/versioning/versioning.go @@ -24,6 +24,42 @@ import ( "k8s.io/kubernetes/pkg/runtime" ) +// EnableCrossGroupDecoding modifies the given decoder in place, if it is a codec +// from this package. It allows objects from one group to be auto-decoded into +// another group. 'destGroup' must already exist in the codec. +func EnableCrossGroupDecoding(d runtime.Decoder, sourceGroup, destGroup string) error { + internal, ok := d.(*codec) + if !ok { + return fmt.Errorf("unsupported decoder type") + } + + dest, ok := internal.decodeVersion[destGroup] + if !ok { + return fmt.Errorf("group %q is not a possible destination group in the given codec", destGroup) + } + internal.decodeVersion[sourceGroup] = dest + + return nil +} + +// EnableCrossGroupEncoding modifies the given encoder in place, if it is a codec +// from this package. It allows objects from one group to be auto-decoded into +// another group. 'destGroup' must already exist in the codec. +func EnableCrossGroupEncoding(e runtime.Encoder, sourceGroup, destGroup string) error { + internal, ok := e.(*codec) + if !ok { + return fmt.Errorf("unsupported encoder type") + } + + dest, ok := internal.encodeVersion[destGroup] + if !ok { + return fmt.Errorf("group %q is not a possible destination group in the given codec", destGroup) + } + internal.encodeVersion[sourceGroup] = dest + + return nil +} + // NewCodecForScheme is a convenience method for callers that are using a scheme. func NewCodecForScheme( // TODO: I should be a scheme interface? diff --git a/pkg/storage/cacher.go b/pkg/storage/cacher.go index c5bcf2448c67..e2ef0b79a446 100644 --- a/pkg/storage/cacher.go +++ b/pkg/storage/cacher.go @@ -145,6 +145,14 @@ func NewCacherFromConfig(config CacherConfig) *Cacher { watchCache := newWatchCache(config.CacheCapacity) listerWatcher := newCacherListerWatcher(config.Storage, config.ResourcePrefix, config.NewListFunc) + // Give this error when it is constructed rather than when you get the + // first watch item, because it's much easier to track down that way. + if obj, ok := config.Type.(runtime.Object); ok { + if err := runtime.CheckCodec(config.Storage.Codec(), obj); err != nil { + panic("storage codec doesn't seem to match given type: " + err.Error()) + } + } + cacher := &Cacher{ usable: sync.RWMutex{}, storage: config.Storage, diff --git a/test/integration/auth_test.go b/test/integration/auth_test.go index ba7e15258389..32b1cf793f78 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 d02b97d80822..8270e4232688 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 eb945bdf79c5..2fb7a6e80f29 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 91d9685e69c0..15b6a43fde88 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