Merge pull request #20501 from piosz/hpa-ga

Auto commit by PR queue bot
This commit is contained in:
k8s-merge-robot 2016-02-18 06:52:39 -08:00
commit 17325ef6ef
55 changed files with 5268 additions and 172 deletions

View File

@ -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": {}
}

File diff suppressed because it is too large Load Diff

View File

@ -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": "",

View File

@ -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"

View File

@ -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"

View File

@ -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.")

View File

@ -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)
}
}
}

View File

@ -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")

View File

@ -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)
}

View File

@ -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
<!-- BEGIN MUNGE: GENERATED_ANALYTICS -->

View File

@ -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

View File

@ -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"

View File

@ -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}

View File

@ -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() {

View File

@ -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() {

View File

@ -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}" \
@ -1581,7 +1581,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"

View File

@ -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

View File

@ -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

View File

@ -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"

View File

@ -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()))
}
}

View File

@ -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]
}

View File

@ -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())
}

View File

@ -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}

View File

@ -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)
}
}

View File

@ -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)
}
}
}

View File

@ -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{},
)
}

View File

@ -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
}

View File

@ -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)
}
}

View File

@ -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)
}
}

View File

@ -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
}
},
)
}

View File

@ -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 }

File diff suppressed because it is too large Load Diff

View File

@ -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"`
}

View File

@ -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

View File

@ -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 = &copyGroupVersion
//}
config.Codec = api.Codecs.LegacyCodec(*config.GroupVersion)
if config.QPS == 0 {
config.QPS = 5
}
if config.Burst == 0 {
config.Burst = 10
}
return nil
}

View File

@ -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
}

View File

@ -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
}
experimentalConfig := *c
experimentalClient, err := NewExtensions(&experimentalConfig)
if err != nil {
return nil, err
var autoscalingClient *AutoscalingClient
if registered.IsRegistered(autoscaling.GroupName) {
autoscalingConfig := *c
autoscalingClient, err = NewAutoscaling(&autoscalingConfig)
if err != nil {
return nil, err
}
}
return &Client{RESTClient: client, ExtensionsClient: experimentalClient, DiscoveryClient: discoveryClient}, nil
var extensionsClient *ExtensionsClient
if registered.IsRegistered(extensions.GroupName) {
extensionsConfig := *c
extensionsClient, err = NewExtensions(&extensionsConfig)
if err != nil {
return nil, err
}
}
return &Client{RESTClient: client, AutoscalingClient: autoscalingClient, ExtensionsClient: extensionsClient, DiscoveryClient: discoveryClient}, nil
}
// MatchesServerVersion queries the server to compares the build version

View File

@ -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()
}

View File

@ -27,6 +27,7 @@ import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/testapi"
"k8s.io/kubernetes/pkg/apis/autoscaling"
"k8s.io/kubernetes/pkg/apis/extensions"
)
@ -34,7 +35,19 @@ func getHorizontalPodAutoscalersResoureName() string {
return "horizontalpodautoscalers"
}
func TestHorizontalPodAutoscalerCreate(t *testing.T) {
func getClient(t *testing.T, c *simple.Client, ns, resourceGroup string) HorizontalPodAutoscalerInterface {
switch resourceGroup {
case autoscaling.GroupName:
return c.Setup(t).Autoscaling().HorizontalPodAutoscalers(ns)
case extensions.GroupName:
return c.Setup(t).Extensions().HorizontalPodAutoscalers(ns)
default:
t.Fatalf("Unknown group %v", resourceGroup)
}
return nil
}
func testHorizontalPodAutoscalerCreate(t *testing.T, group testapi.TestGroup, resourceGroup string) {
ns := api.NamespaceDefault
horizontalPodAutoscaler := extensions.HorizontalPodAutoscaler{
ObjectMeta: api.ObjectMeta{
@ -45,14 +58,15 @@ func TestHorizontalPodAutoscalerCreate(t *testing.T) {
c := &simple.Client{
Request: simple.Request{
Method: "POST",
Path: testapi.Extensions.ResourcePath(getHorizontalPodAutoscalersResoureName(), ns, ""),
Path: group.ResourcePath(getHorizontalPodAutoscalersResoureName(), ns, ""),
Query: simple.BuildQueryValues(nil),
Body: &horizontalPodAutoscaler,
},
Response: simple.Response{StatusCode: 200, Body: &horizontalPodAutoscaler},
Response: simple.Response{StatusCode: 200, Body: &horizontalPodAutoscaler},
ResourceGroup: resourceGroup,
}
response, err := c.Setup(t).Extensions().HorizontalPodAutoscalers(ns).Create(&horizontalPodAutoscaler)
response, err := getClient(t, c, ns, resourceGroup).Create(&horizontalPodAutoscaler)
defer c.Close()
if err != nil {
t.Fatalf("unexpected error: %v", err)
@ -60,7 +74,12 @@ func TestHorizontalPodAutoscalerCreate(t *testing.T) {
c.Validate(t, response, err)
}
func TestHorizontalPodAutoscalerGet(t *testing.T) {
func TestHorizontalPodAutoscalerCreate(t *testing.T) {
testHorizontalPodAutoscalerCreate(t, testapi.Extensions, extensions.GroupName)
testHorizontalPodAutoscalerCreate(t, testapi.Autoscaling, autoscaling.GroupName)
}
func testHorizontalPodAutoscalerGet(t *testing.T, group testapi.TestGroup, resourceGroup string) {
ns := api.NamespaceDefault
horizontalPodAutoscaler := &extensions.HorizontalPodAutoscaler{
ObjectMeta: api.ObjectMeta{
@ -71,19 +90,25 @@ func TestHorizontalPodAutoscalerGet(t *testing.T) {
c := &simple.Client{
Request: simple.Request{
Method: "GET",
Path: testapi.Extensions.ResourcePath(getHorizontalPodAutoscalersResoureName(), ns, "abc"),
Path: group.ResourcePath(getHorizontalPodAutoscalersResoureName(), ns, "abc"),
Query: simple.BuildQueryValues(nil),
Body: nil,
},
Response: simple.Response{StatusCode: 200, Body: horizontalPodAutoscaler},
Response: simple.Response{StatusCode: 200, Body: horizontalPodAutoscaler},
ResourceGroup: resourceGroup,
}
response, err := c.Setup(t).Extensions().HorizontalPodAutoscalers(ns).Get("abc")
response, err := getClient(t, c, ns, resourceGroup).Get("abc")
defer c.Close()
c.Validate(t, response, err)
}
func TestHorizontalPodAutoscalerList(t *testing.T) {
func TestHorizontalPodAutoscalerGet(t *testing.T) {
testHorizontalPodAutoscalerGet(t, testapi.Extensions, extensions.GroupName)
testHorizontalPodAutoscalerGet(t, testapi.Autoscaling, autoscaling.GroupName)
}
func testHorizontalPodAutoscalerList(t *testing.T, group testapi.TestGroup, resourceGroup string) {
ns := api.NamespaceDefault
horizontalPodAutoscalerList := &extensions.HorizontalPodAutoscalerList{
Items: []extensions.HorizontalPodAutoscaler{
@ -98,18 +123,48 @@ func TestHorizontalPodAutoscalerList(t *testing.T) {
c := &simple.Client{
Request: simple.Request{
Method: "GET",
Path: testapi.Extensions.ResourcePath(getHorizontalPodAutoscalersResoureName(), ns, ""),
Path: group.ResourcePath(getHorizontalPodAutoscalersResoureName(), ns, ""),
Query: simple.BuildQueryValues(nil),
Body: nil,
},
Response: simple.Response{StatusCode: 200, Body: horizontalPodAutoscalerList},
Response: simple.Response{StatusCode: 200, Body: horizontalPodAutoscalerList},
ResourceGroup: resourceGroup,
}
response, err := c.Setup(t).Extensions().HorizontalPodAutoscalers(ns).List(api.ListOptions{})
response, err := getClient(t, c, ns, resourceGroup).List(api.ListOptions{})
defer c.Close()
c.Validate(t, response, err)
}
func TestHorizontalPodAutoscalerList(t *testing.T) {
testHorizontalPodAutoscalerList(t, testapi.Extensions, extensions.GroupName)
testHorizontalPodAutoscalerList(t, testapi.Autoscaling, autoscaling.GroupName)
}
func testHorizontalPodAutoscalerUpdate(t *testing.T, group testapi.TestGroup, resourceGroup string) {
ns := api.NamespaceDefault
horizontalPodAutoscaler := &extensions.HorizontalPodAutoscaler{
ObjectMeta: api.ObjectMeta{
Name: "abc",
Namespace: ns,
ResourceVersion: "1",
},
}
c := &simple.Client{
Request: simple.Request{Method: "PUT", Path: group.ResourcePath(getHorizontalPodAutoscalersResoureName(), ns, "abc"), Query: simple.BuildQueryValues(nil)},
Response: simple.Response{StatusCode: 200, Body: horizontalPodAutoscaler},
ResourceGroup: resourceGroup,
}
response, err := getClient(t, c, ns, resourceGroup).Update(horizontalPodAutoscaler)
defer c.Close()
c.Validate(t, response, err)
}
func TestHorizontalPodAutoscalerUpdate(t *testing.T) {
testHorizontalPodAutoscalerUpdate(t, testapi.Extensions, extensions.GroupName)
testHorizontalPodAutoscalerUpdate(t, testapi.Autoscaling, autoscaling.GroupName)
}
func testHorizontalPodAutoscalerUpdateStatus(t *testing.T, group testapi.TestGroup, resourceGroup string) {
ns := api.NamespaceDefault
horizontalPodAutoscaler := &extensions.HorizontalPodAutoscaler{
ObjectMeta: api.ObjectMeta{
@ -119,52 +174,52 @@ func TestHorizontalPodAutoscalerUpdate(t *testing.T) {
},
}
c := &simple.Client{
Request: simple.Request{Method: "PUT", Path: testapi.Extensions.ResourcePath(getHorizontalPodAutoscalersResoureName(), ns, "abc"), Query: simple.BuildQueryValues(nil)},
Response: simple.Response{StatusCode: 200, Body: horizontalPodAutoscaler},
Request: simple.Request{Method: "PUT", Path: group.ResourcePath(getHorizontalPodAutoscalersResoureName(), ns, "abc") + "/status", Query: simple.BuildQueryValues(nil)},
Response: simple.Response{StatusCode: 200, Body: horizontalPodAutoscaler},
ResourceGroup: resourceGroup,
}
response, err := c.Setup(t).Extensions().HorizontalPodAutoscalers(ns).Update(horizontalPodAutoscaler)
response, err := getClient(t, c, ns, resourceGroup).UpdateStatus(horizontalPodAutoscaler)
defer c.Close()
c.Validate(t, response, err)
}
func TestHorizontalPodAutoscalerUpdateStatus(t *testing.T) {
testHorizontalPodAutoscalerUpdateStatus(t, testapi.Extensions, extensions.GroupName)
testHorizontalPodAutoscalerUpdateStatus(t, testapi.Autoscaling, autoscaling.GroupName)
}
func testHorizontalPodAutoscalerDelete(t *testing.T, group testapi.TestGroup, resourceGroup string) {
ns := api.NamespaceDefault
horizontalPodAutoscaler := &extensions.HorizontalPodAutoscaler{
ObjectMeta: api.ObjectMeta{
Name: "abc",
Namespace: ns,
ResourceVersion: "1",
},
}
c := &simple.Client{
Request: simple.Request{Method: "PUT", Path: testapi.Extensions.ResourcePath(getHorizontalPodAutoscalersResoureName(), ns, "abc") + "/status", Query: simple.BuildQueryValues(nil)},
Response: simple.Response{StatusCode: 200, Body: horizontalPodAutoscaler},
Request: simple.Request{Method: "DELETE", Path: group.ResourcePath(getHorizontalPodAutoscalersResoureName(), ns, "foo"), Query: simple.BuildQueryValues(nil)},
Response: simple.Response{StatusCode: 200},
ResourceGroup: resourceGroup,
}
response, err := c.Setup(t).Extensions().HorizontalPodAutoscalers(ns).UpdateStatus(horizontalPodAutoscaler)
err := getClient(t, c, ns, resourceGroup).Delete("foo", nil)
defer c.Close()
c.Validate(t, response, err)
c.Validate(t, nil, err)
}
func TestHorizontalPodAutoscalerDelete(t *testing.T) {
ns := api.NamespaceDefault
testHorizontalPodAutoscalerDelete(t, testapi.Extensions, extensions.GroupName)
testHorizontalPodAutoscalerDelete(t, testapi.Autoscaling, autoscaling.GroupName)
}
func testHorizontalPodAutoscalerWatch(t *testing.T, group testapi.TestGroup, resourceGroup string) {
c := &simple.Client{
Request: simple.Request{Method: "DELETE", Path: testapi.Extensions.ResourcePath(getHorizontalPodAutoscalersResoureName(), ns, "foo"), Query: simple.BuildQueryValues(nil)},
Response: simple.Response{StatusCode: 200},
Request: simple.Request{
Method: "GET",
Path: group.ResourcePathWithPrefix("watch", getHorizontalPodAutoscalersResoureName(), "", ""),
Query: url.Values{"resourceVersion": []string{}}},
Response: simple.Response{StatusCode: 200},
ResourceGroup: resourceGroup,
}
err := c.Setup(t).Extensions().HorizontalPodAutoscalers(ns).Delete("foo", nil)
_, err := getClient(t, c, api.NamespaceAll, resourceGroup).Watch(api.ListOptions{})
defer c.Close()
c.Validate(t, nil, err)
}
func TestHorizontalPodAutoscalerWatch(t *testing.T) {
c := &simple.Client{
Request: simple.Request{
Method: "GET",
Path: testapi.Extensions.ResourcePathWithPrefix("watch", getHorizontalPodAutoscalersResoureName(), "", ""),
Query: url.Values{"resourceVersion": []string{}}},
Response: simple.Response{StatusCode: 200},
}
_, err := c.Setup(t).Extensions().HorizontalPodAutoscalers(api.NamespaceAll).Watch(api.ListOptions{})
defer c.Close()
c.Validate(t, nil, err)
testHorizontalPodAutoscalerWatch(t, testapi.Extensions, extensions.GroupName)
testHorizontalPodAutoscalerWatch(t, testapi.Autoscaling, autoscaling.GroupName)
}

View File

@ -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"

View File

@ -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))
}

View File

@ -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 {

View File

@ -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...)}

View File

@ -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 {

View File

@ -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")

View File

@ -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"
)

View File

@ -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: &registered.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)
}
@ -587,9 +622,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
@ -647,6 +680,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

View File

@ -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"}

View File

@ -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
}

View File

@ -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?

View File

@ -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,

View File

@ -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

View File

@ -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()

View File

@ -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{

View File

@ -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