mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-11-03 19:58:17 +00:00 
			
		
		
		
	make storage enablement, serialization, and location orthogonal
This commit is contained in:
		@@ -23,6 +23,7 @@ import (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/admission"
 | 
						"k8s.io/kubernetes/pkg/admission"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/api"
 | 
						"k8s.io/kubernetes/pkg/api"
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/api/unversioned"
 | 
				
			||||||
	apiutil "k8s.io/kubernetes/pkg/api/util"
 | 
						apiutil "k8s.io/kubernetes/pkg/api/util"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/api/validation"
 | 
						"k8s.io/kubernetes/pkg/api/validation"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/apimachinery/registered"
 | 
						"k8s.io/kubernetes/pkg/apimachinery/registered"
 | 
				
			||||||
@@ -98,39 +99,53 @@ func NewAPIServer() *APIServer {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// dest must be a map of group to groupVersion.
 | 
					// dest must be a map of group to groupVersion.
 | 
				
			||||||
func gvToMap(gvList string, dest map[string]string) {
 | 
					func mergeGroupVersionIntoMap(gvList string, dest map[string]unversioned.GroupVersion) error {
 | 
				
			||||||
	for _, gv := range strings.Split(gvList, ",") {
 | 
						for _, gvString := range strings.Split(gvList, ",") {
 | 
				
			||||||
		if gv == "" {
 | 
							if gvString == "" {
 | 
				
			||||||
			continue
 | 
								continue
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		// We accept two formats. "group/version" OR
 | 
							// We accept two formats. "group/version" OR
 | 
				
			||||||
		// "group=group/version". The latter is used when types
 | 
							// "group=group/version". The latter is used when types
 | 
				
			||||||
		// move between groups.
 | 
							// move between groups.
 | 
				
			||||||
		if !strings.Contains(gv, "=") {
 | 
							if !strings.Contains(gvString, "=") {
 | 
				
			||||||
			dest[apiutil.GetGroup(gv)] = gv
 | 
								gv, err := unversioned.ParseGroupVersion(gvString)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								dest[gv.Group] = gv
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			parts := strings.SplitN(gv, "=", 2)
 | 
								parts := strings.SplitN(gvString, "=", 2)
 | 
				
			||||||
			// TODO: error checking.
 | 
								gv, err := unversioned.ParseGroupVersion(parts[1])
 | 
				
			||||||
			dest[parts[0]] = parts[1]
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								dest[parts[0]] = gv
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// StorageGroupsToGroupVersions returns a map from group name to group version,
 | 
					// StorageGroupsToEncodingVersion returns a map from group name to group version,
 | 
				
			||||||
// computed from the s.DeprecatedStorageVersion and s.StorageVersions flags.
 | 
					// computed from the s.DeprecatedStorageVersion and s.StorageVersions flags.
 | 
				
			||||||
// TODO: can we move the whole storage version concept to the generic apiserver?
 | 
					// TODO: can we move the whole storage version concept to the generic apiserver?
 | 
				
			||||||
func (s *APIServer) StorageGroupsToGroupVersions() map[string]string {
 | 
					func (s *APIServer) StorageGroupsToEncodingVersion() (map[string]unversioned.GroupVersion, error) {
 | 
				
			||||||
	storageVersionMap := map[string]string{}
 | 
						storageVersionMap := map[string]unversioned.GroupVersion{}
 | 
				
			||||||
	if s.DeprecatedStorageVersion != "" {
 | 
						if s.DeprecatedStorageVersion != "" {
 | 
				
			||||||
		storageVersionMap[""] = s.DeprecatedStorageVersion
 | 
							storageVersionMap[""] = unversioned.GroupVersion{Group: apiutil.GetGroup(s.DeprecatedStorageVersion), Version: apiutil.GetVersion(s.DeprecatedStorageVersion)}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// First, get the defaults.
 | 
						// First, get the defaults.
 | 
				
			||||||
	gvToMap(s.DefaultStorageVersions, storageVersionMap)
 | 
						if err := mergeGroupVersionIntoMap(s.DefaultStorageVersions, storageVersionMap); err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	// Override any defaults with the user settings.
 | 
						// Override any defaults with the user settings.
 | 
				
			||||||
	gvToMap(s.StorageVersions, storageVersionMap)
 | 
						if err := mergeGroupVersionIntoMap(s.StorageVersions, storageVersionMap); err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return storageVersionMap
 | 
						return storageVersionMap, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// AddFlags adds flags for a specific APIServer to the specified FlagSet
 | 
					// AddFlags adds flags for a specific APIServer to the specified FlagSet
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -23,6 +23,7 @@ import (
 | 
				
			|||||||
	"github.com/spf13/pflag"
 | 
						"github.com/spf13/pflag"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/api"
 | 
						"k8s.io/kubernetes/pkg/api"
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/api/unversioned"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/apis/autoscaling"
 | 
						"k8s.io/kubernetes/pkg/apis/autoscaling"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/apis/extensions"
 | 
						"k8s.io/kubernetes/pkg/apis/extensions"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
@@ -32,38 +33,38 @@ func TestGenerateStorageVersionMap(t *testing.T) {
 | 
				
			|||||||
		legacyVersion   string
 | 
							legacyVersion   string
 | 
				
			||||||
		storageVersions string
 | 
							storageVersions string
 | 
				
			||||||
		defaultVersions string
 | 
							defaultVersions string
 | 
				
			||||||
		expectedMap     map[string]string
 | 
							expectedMap     map[string]unversioned.GroupVersion
 | 
				
			||||||
	}{
 | 
						}{
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			legacyVersion:   "v1",
 | 
								legacyVersion:   "v1",
 | 
				
			||||||
			storageVersions: "v1,extensions/v1beta1",
 | 
								storageVersions: "v1,extensions/v1beta1",
 | 
				
			||||||
			expectedMap: map[string]string{
 | 
								expectedMap: map[string]unversioned.GroupVersion{
 | 
				
			||||||
				api.GroupName:        "v1",
 | 
									api.GroupName:        {Version: "v1"},
 | 
				
			||||||
				extensions.GroupName: "extensions/v1beta1",
 | 
									extensions.GroupName: {Group: "extensions", Version: "v1beta1"},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			legacyVersion:   "",
 | 
								legacyVersion:   "",
 | 
				
			||||||
			storageVersions: "extensions/v1beta1,v1",
 | 
								storageVersions: "extensions/v1beta1,v1",
 | 
				
			||||||
			expectedMap: map[string]string{
 | 
								expectedMap: map[string]unversioned.GroupVersion{
 | 
				
			||||||
				api.GroupName:        "v1",
 | 
									api.GroupName:        {Version: "v1"},
 | 
				
			||||||
				extensions.GroupName: "extensions/v1beta1",
 | 
									extensions.GroupName: {Group: "extensions", Version: "v1beta1"},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			legacyVersion:   "",
 | 
								legacyVersion:   "",
 | 
				
			||||||
			storageVersions: "autoscaling=extensions/v1beta1,v1",
 | 
								storageVersions: "autoscaling=extensions/v1beta1,v1",
 | 
				
			||||||
			defaultVersions: "extensions/v1beta1,v1,autoscaling/v1",
 | 
								defaultVersions: "extensions/v1beta1,v1,autoscaling/v1",
 | 
				
			||||||
			expectedMap: map[string]string{
 | 
								expectedMap: map[string]unversioned.GroupVersion{
 | 
				
			||||||
				api.GroupName:         "v1",
 | 
									api.GroupName:         {Version: "v1"},
 | 
				
			||||||
				autoscaling.GroupName: "extensions/v1beta1",
 | 
									autoscaling.GroupName: {Group: "extensions", Version: "v1beta1"},
 | 
				
			||||||
				extensions.GroupName:  "extensions/v1beta1",
 | 
									extensions.GroupName:  {Group: "extensions", Version: "v1beta1"},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			legacyVersion:   "",
 | 
								legacyVersion:   "",
 | 
				
			||||||
			storageVersions: "",
 | 
								storageVersions: "",
 | 
				
			||||||
			expectedMap:     map[string]string{},
 | 
								expectedMap:     map[string]unversioned.GroupVersion{},
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	for i, test := range testCases {
 | 
						for i, test := range testCases {
 | 
				
			||||||
@@ -72,7 +73,10 @@ func TestGenerateStorageVersionMap(t *testing.T) {
 | 
				
			|||||||
			StorageVersions:          test.storageVersions,
 | 
								StorageVersions:          test.storageVersions,
 | 
				
			||||||
			DefaultStorageVersions:   test.defaultVersions,
 | 
								DefaultStorageVersions:   test.defaultVersions,
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		output := s.StorageGroupsToGroupVersions()
 | 
							output, err := s.StorageGroupsToEncodingVersion()
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								t.Errorf("%v: unexpected error: %v", i, err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		if !reflect.DeepEqual(test.expectedMap, output) {
 | 
							if !reflect.DeepEqual(test.expectedMap, output) {
 | 
				
			||||||
			t.Errorf("%v: unexpected error. expect: %v, got: %v", i, test.expectedMap, output)
 | 
								t.Errorf("%v: unexpected error. expect: %v, got: %v", i, test.expectedMap, output)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -37,8 +37,6 @@ import (
 | 
				
			|||||||
	"k8s.io/kubernetes/pkg/api"
 | 
						"k8s.io/kubernetes/pkg/api"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/api/unversioned"
 | 
						"k8s.io/kubernetes/pkg/api/unversioned"
 | 
				
			||||||
	apiv1 "k8s.io/kubernetes/pkg/api/v1"
 | 
						apiv1 "k8s.io/kubernetes/pkg/api/v1"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/apimachinery/registered"
 | 
					 | 
				
			||||||
	"k8s.io/kubernetes/pkg/apis/apps"
 | 
					 | 
				
			||||||
	appsapi "k8s.io/kubernetes/pkg/apis/apps/v1alpha1"
 | 
						appsapi "k8s.io/kubernetes/pkg/apis/apps/v1alpha1"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/apis/autoscaling"
 | 
						"k8s.io/kubernetes/pkg/apis/autoscaling"
 | 
				
			||||||
	autoscalingapiv1 "k8s.io/kubernetes/pkg/apis/autoscaling/v1"
 | 
						autoscalingapiv1 "k8s.io/kubernetes/pkg/apis/autoscaling/v1"
 | 
				
			||||||
@@ -58,10 +56,7 @@ import (
 | 
				
			|||||||
	"k8s.io/kubernetes/pkg/master"
 | 
						"k8s.io/kubernetes/pkg/master"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/registry/cachesize"
 | 
						"k8s.io/kubernetes/pkg/registry/cachesize"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/runtime"
 | 
						"k8s.io/kubernetes/pkg/runtime"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/runtime/serializer/versioning"
 | 
					 | 
				
			||||||
	"k8s.io/kubernetes/pkg/serviceaccount"
 | 
						"k8s.io/kubernetes/pkg/serviceaccount"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/storage"
 | 
					 | 
				
			||||||
	etcdstorage "k8s.io/kubernetes/pkg/storage/etcd"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewAPIServerCommand creates a *cobra.Command object with default parameters
 | 
					// NewAPIServerCommand creates a *cobra.Command object with default parameters
 | 
				
			||||||
@@ -81,89 +76,6 @@ cluster's shared state through which all other components interact.`,
 | 
				
			|||||||
	return cmd
 | 
						return cmd
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// For testing.
 | 
					 | 
				
			||||||
type newEtcdFunc func(runtime.NegotiatedSerializer, string, string, etcdstorage.EtcdConfig) (storage.Interface, error)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func newEtcd(ns runtime.NegotiatedSerializer, storageGroupVersionString, memoryGroupVersionString string, etcdConfig etcdstorage.EtcdConfig) (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, 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.EtcdStorageConfig
 | 
					 | 
				
			||||||
	storageConfig.Config = etcdConfig
 | 
					 | 
				
			||||||
	s, ok := ns.SerializerForMediaType("application/json", nil)
 | 
					 | 
				
			||||||
	if !ok {
 | 
					 | 
				
			||||||
		return nil, fmt.Errorf("unable to find serializer for JSON")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	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)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	storageConfig.Codec = runtime.NewCodec(encoder, decoder)
 | 
					 | 
				
			||||||
	return storageConfig.NewStorage()
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// parse the value of --etcd-servers-overrides and update given storageDestinations.
 | 
					 | 
				
			||||||
func updateEtcdOverrides(overrides []string, storageVersions map[string]string, etcdConfig etcdstorage.EtcdConfig, storageDestinations *genericapiserver.StorageDestinations, newEtcdFn newEtcdFunc) {
 | 
					 | 
				
			||||||
	if len(overrides) == 0 {
 | 
					 | 
				
			||||||
		return
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	for _, override := range overrides {
 | 
					 | 
				
			||||||
		tokens := strings.Split(override, "#")
 | 
					 | 
				
			||||||
		if len(tokens) != 2 {
 | 
					 | 
				
			||||||
			glog.Errorf("invalid value of etcd server overrides: %s", override)
 | 
					 | 
				
			||||||
			continue
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		apiresource := strings.Split(tokens[0], "/")
 | 
					 | 
				
			||||||
		if len(apiresource) != 2 {
 | 
					 | 
				
			||||||
			glog.Errorf("invalid resource definition: %s", tokens[0])
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		group := apiresource[0]
 | 
					 | 
				
			||||||
		resource := apiresource[1]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		apigroup, err := registered.Group(group)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			glog.Errorf("invalid api group %s: %v", group, err)
 | 
					 | 
				
			||||||
			continue
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if _, found := storageVersions[apigroup.GroupVersion.Group]; !found {
 | 
					 | 
				
			||||||
			glog.Errorf("Couldn't find the storage version for group %s", apigroup.GroupVersion.Group)
 | 
					 | 
				
			||||||
			continue
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		servers := strings.Split(tokens[1], ";")
 | 
					 | 
				
			||||||
		overrideEtcdConfig := etcdConfig
 | 
					 | 
				
			||||||
		overrideEtcdConfig.ServerList = servers
 | 
					 | 
				
			||||||
		// 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(api.Codecs, storageVersions[apigroup.GroupVersion.Group], internalGV, overrideEtcdConfig)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			glog.Fatalf("Invalid storage version or misconfigured etcd for %s: %v", tokens[0], err)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		storageDestinations.AddStorageOverride(group, resource, etcdOverrideStorage)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Run runs the specified APIServer.  This should never exit.
 | 
					// Run runs the specified APIServer.  This should never exit.
 | 
				
			||||||
func Run(s *options.APIServer) error {
 | 
					func Run(s *options.APIServer) error {
 | 
				
			||||||
	genericapiserver.DefaultAndValidateRunOptions(s.ServerRunOptions)
 | 
						genericapiserver.DefaultAndValidateRunOptions(s.ServerRunOptions)
 | 
				
			||||||
@@ -252,126 +164,37 @@ func Run(s *options.APIServer) error {
 | 
				
			|||||||
		glog.Errorf("Failed to create clientset: %v", err)
 | 
							glog.Errorf("Failed to create clientset: %v", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	legacyV1Group, err := registered.Group(api.GroupName)
 | 
						resourceEncoding := genericapiserver.NewDefaultResourceEncodingConfig()
 | 
				
			||||||
 | 
						groupToEncoding, err := s.StorageGroupsToEncodingVersion()
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							glog.Fatalf("error getting group encoding: %s", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for group, storageEncodingVersion := range groupToEncoding {
 | 
				
			||||||
 | 
							resourceEncoding.SetVersionEncoding(group, storageEncodingVersion, unversioned.GroupVersion{Group: group, Version: runtime.APIVersionInternal})
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	storageDestinations := genericapiserver.NewStorageDestinations()
 | 
						storageFactory := genericapiserver.NewDefaultStorageFactory(s.EtcdConfig, api.Codecs, resourceEncoding, apiResourceConfigSource)
 | 
				
			||||||
 | 
						storageFactory.AddCohabitatingResources(batch.Resource("jobs"), extensions.Resource("jobs"))
 | 
				
			||||||
 | 
						storageFactory.AddCohabitatingResources(autoscaling.Resource("horizontalpodautoscalers"), extensions.Resource("horizontalpodautoscalers"))
 | 
				
			||||||
 | 
						for _, override := range s.EtcdServersOverrides {
 | 
				
			||||||
 | 
							tokens := strings.Split(override, "#")
 | 
				
			||||||
 | 
							if len(tokens) != 2 {
 | 
				
			||||||
 | 
								glog.Errorf("invalid value of etcd server overrides: %s", override)
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	storageVersions := s.StorageGroupsToGroupVersions()
 | 
							apiresource := strings.Split(tokens[0], "/")
 | 
				
			||||||
	if _, found := storageVersions[legacyV1Group.GroupVersion.Group]; !found {
 | 
							if len(apiresource) != 2 {
 | 
				
			||||||
		glog.Fatalf("Couldn't find the storage version for group: %q in storageVersions: %v", legacyV1Group.GroupVersion.Group, storageVersions)
 | 
								glog.Errorf("invalid resource definition: %s", tokens[0])
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							group := apiresource[0]
 | 
				
			||||||
 | 
							resource := apiresource[1]
 | 
				
			||||||
 | 
							groupResource := unversioned.GroupResource{Group: group, Resource: resource}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							servers := strings.Split(tokens[1], ";")
 | 
				
			||||||
 | 
							storageFactory.SetEtcdLocation(groupResource, servers)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	etcdStorage, err := newEtcd(api.Codecs, storageVersions[legacyV1Group.GroupVersion.Group], "/__internal", s.EtcdConfig)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		glog.Fatalf("Invalid storage version or misconfigured etcd: %v", err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	storageDestinations.AddAPIGroup("", etcdStorage)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if apiResourceConfigSource.AnyResourcesForVersionEnabled(extensionsapiv1beta1.SchemeGroupVersion) {
 | 
					 | 
				
			||||||
		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)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		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(api.Codecs, storageVersions[expGroup.GroupVersion.Group], "extensions/__internal", s.EtcdConfig)
 | 
					 | 
				
			||||||
		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)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// Since Job has been moved to the batch group, we need to make
 | 
					 | 
				
			||||||
		// sure batch has a storage destination. If the batch group
 | 
					 | 
				
			||||||
		// itself is on, it will overwrite this decision below.
 | 
					 | 
				
			||||||
		storageDestinations.AddAPIGroup(batch.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 apiResourceConfigSource.AnyResourcesForVersionEnabled(autoscalingapiv1.SchemeGroupVersion) {
 | 
					 | 
				
			||||||
		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(api.Codecs, storageGroupVersion, "extensions/__internal", s.EtcdConfig)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			glog.Fatalf("Invalid extensions storage version or misconfigured etcd: %v", err)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		storageDestinations.AddAPIGroup(autoscaling.GroupName, autoscalingEtcdStorage)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// batch/v1/job is a move from extensions/v1beta1/job. The storage
 | 
					 | 
				
			||||||
	// version needs to be either extensions/v1beta1 or batch/v1. Users
 | 
					 | 
				
			||||||
	// must roll forward while using 1.2, because we will require the
 | 
					 | 
				
			||||||
	// latter for 1.3.
 | 
					 | 
				
			||||||
	if apiResourceConfigSource.AnyResourcesForVersionEnabled(batchapiv1.SchemeGroupVersion) {
 | 
					 | 
				
			||||||
		glog.Infof("Configuring batch/v1 storage destination")
 | 
					 | 
				
			||||||
		batchGroup, err := registered.Group(batch.GroupName)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			glog.Fatalf("Batch 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[batchGroup.GroupVersion.Group]
 | 
					 | 
				
			||||||
		if !found {
 | 
					 | 
				
			||||||
			glog.Fatalf("Couldn't find the storage version for group: %q in storageVersions: %v", batchGroup.GroupVersion.Group, storageVersions)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if storageGroupVersion != "batch/v1" && storageGroupVersion != "extensions/v1beta1" {
 | 
					 | 
				
			||||||
			glog.Fatalf("The storage version for batch must be either 'batch/v1' or 'extensions/v1beta1'")
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		glog.Infof("Using %v for batch group storage version", storageGroupVersion)
 | 
					 | 
				
			||||||
		batchEtcdStorage, err := newEtcd(api.Codecs, storageGroupVersion, "extensions/__internal", s.EtcdConfig)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			glog.Fatalf("Invalid extensions storage version or misconfigured etcd: %v", err)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		storageDestinations.AddAPIGroup(batch.GroupName, batchEtcdStorage)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if apiResourceConfigSource.AnyResourcesForVersionEnabled(appsapi.SchemeGroupVersion) {
 | 
					 | 
				
			||||||
		glog.Infof("Configuring apps/v1alpha1 storage destination")
 | 
					 | 
				
			||||||
		appsGroup, err := registered.Group(apps.GroupName)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			glog.Fatalf("Apps 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[appsGroup.GroupVersion.Group]
 | 
					 | 
				
			||||||
		if !found {
 | 
					 | 
				
			||||||
			glog.Fatalf("Couldn't find the storage version for group: %q in storageVersions: %v", appsGroup.GroupVersion.Group, storageVersions)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if storageGroupVersion != "apps/v1alpha1" {
 | 
					 | 
				
			||||||
			glog.Fatalf("The storage version for apps must be apps/v1alpha1")
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		glog.Infof("Using %v for petset group storage version", storageGroupVersion)
 | 
					 | 
				
			||||||
		appsEtcdStorage, err := newEtcd(api.Codecs, storageGroupVersion, "apps/__internal", s.EtcdConfig)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			glog.Fatalf("Invalid extensions storage version or misconfigured etcd: %v", err)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		storageDestinations.AddAPIGroup(apps.GroupName, appsEtcdStorage)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	updateEtcdOverrides(s.EtcdServersOverrides, storageVersions, s.EtcdConfig, &storageDestinations, newEtcd)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Default to the private server key for service account token signing
 | 
						// Default to the private server key for service account token signing
 | 
				
			||||||
	if s.ServiceAccountKeyFile == "" && s.TLSPrivateKeyFile != "" {
 | 
						if s.ServiceAccountKeyFile == "" && s.TLSPrivateKeyFile != "" {
 | 
				
			||||||
@@ -386,7 +209,11 @@ func Run(s *options.APIServer) error {
 | 
				
			|||||||
	if s.ServiceAccountLookup {
 | 
						if s.ServiceAccountLookup {
 | 
				
			||||||
		// If we need to look up service accounts and tokens,
 | 
							// If we need to look up service accounts and tokens,
 | 
				
			||||||
		// go directly to etcd to avoid recursive auth insanity
 | 
							// go directly to etcd to avoid recursive auth insanity
 | 
				
			||||||
		serviceAccountGetter = serviceaccountcontroller.NewGetterFromStorageInterface(etcdStorage)
 | 
							storage, err := storageFactory.New(api.Resource("serviceaccounts"))
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								glog.Fatalf("Unable to get serviceaccounts storage: %v", err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							serviceAccountGetter = serviceaccountcontroller.NewGetterFromStorageInterface(storage)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	authenticator, err := authenticator.New(authenticator.AuthenticatorConfig{
 | 
						authenticator, err := authenticator.New(authenticator.AuthenticatorConfig{
 | 
				
			||||||
@@ -443,8 +270,7 @@ func Run(s *options.APIServer) error {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	genericConfig := genericapiserver.NewConfig(s.ServerRunOptions)
 | 
						genericConfig := genericapiserver.NewConfig(s.ServerRunOptions)
 | 
				
			||||||
	// TODO: Move the following to generic api server as well.
 | 
						// TODO: Move the following to generic api server as well.
 | 
				
			||||||
	genericConfig.StorageDestinations = storageDestinations
 | 
						genericConfig.StorageFactory = storageFactory
 | 
				
			||||||
	genericConfig.StorageVersions = storageVersions
 | 
					 | 
				
			||||||
	genericConfig.Authenticator = authenticator
 | 
						genericConfig.Authenticator = authenticator
 | 
				
			||||||
	genericConfig.SupportsBasicAuth = len(s.BasicAuthFile) > 0
 | 
						genericConfig.SupportsBasicAuth = len(s.BasicAuthFile) > 0
 | 
				
			||||||
	genericConfig.Authorizer = authorizer
 | 
						genericConfig.Authorizer = authorizer
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,18 +19,12 @@ package app
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	"reflect"
 | 
						"reflect"
 | 
				
			||||||
	"regexp"
 | 
						"regexp"
 | 
				
			||||||
	"strings"
 | 
					 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"k8s.io/kubernetes/cmd/kube-apiserver/app/options"
 | 
						"k8s.io/kubernetes/cmd/kube-apiserver/app/options"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/api"
 | 
					 | 
				
			||||||
	"k8s.io/kubernetes/pkg/api/unversioned"
 | 
						"k8s.io/kubernetes/pkg/api/unversioned"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/apis/extensions"
 | 
					 | 
				
			||||||
	"k8s.io/kubernetes/pkg/genericapiserver"
 | 
						"k8s.io/kubernetes/pkg/genericapiserver"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/master"
 | 
						"k8s.io/kubernetes/pkg/master"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/runtime"
 | 
					 | 
				
			||||||
	"k8s.io/kubernetes/pkg/storage"
 | 
					 | 
				
			||||||
	etcdstorage "k8s.io/kubernetes/pkg/storage/etcd"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestLongRunningRequestRegexp(t *testing.T) {
 | 
					func TestLongRunningRequestRegexp(t *testing.T) {
 | 
				
			||||||
@@ -74,64 +68,6 @@ func TestLongRunningRequestRegexp(t *testing.T) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestUpdateEtcdOverrides(t *testing.T) {
 | 
					 | 
				
			||||||
	storageVersions := map[string]string{
 | 
					 | 
				
			||||||
		"":           "v1",
 | 
					 | 
				
			||||||
		"extensions": "extensions/v1beta1",
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	testCases := []struct {
 | 
					 | 
				
			||||||
		apigroup string
 | 
					 | 
				
			||||||
		resource string
 | 
					 | 
				
			||||||
		servers  []string
 | 
					 | 
				
			||||||
	}{
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			apigroup: api.GroupName,
 | 
					 | 
				
			||||||
			resource: "resource",
 | 
					 | 
				
			||||||
			servers:  []string{"http://127.0.0.1:10000"},
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			apigroup: api.GroupName,
 | 
					 | 
				
			||||||
			resource: "resource",
 | 
					 | 
				
			||||||
			servers:  []string{"http://127.0.0.1:10000", "http://127.0.0.1:20000"},
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			apigroup: extensions.GroupName,
 | 
					 | 
				
			||||||
			resource: "resource",
 | 
					 | 
				
			||||||
			servers:  []string{"http://127.0.0.1:10000"},
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for _, test := range testCases {
 | 
					 | 
				
			||||||
		newEtcd := func(_ runtime.NegotiatedSerializer, _, _ string, etcdConfig etcdstorage.EtcdConfig) (storage.Interface, error) {
 | 
					 | 
				
			||||||
			if !reflect.DeepEqual(test.servers, etcdConfig.ServerList) {
 | 
					 | 
				
			||||||
				t.Errorf("unexpected server list, expected: %#v, got: %#v", test.servers, etcdConfig.ServerList)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			return nil, nil
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		storageDestinations := genericapiserver.NewStorageDestinations()
 | 
					 | 
				
			||||||
		override := test.apigroup + "/" + test.resource + "#" + strings.Join(test.servers, ";")
 | 
					 | 
				
			||||||
		defaultEtcdConfig := etcdstorage.EtcdConfig{
 | 
					 | 
				
			||||||
			Prefix:     genericapiserver.DefaultEtcdPathPrefix,
 | 
					 | 
				
			||||||
			ServerList: []string{"http://127.0.0.1"},
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		updateEtcdOverrides([]string{override}, storageVersions, defaultEtcdConfig, &storageDestinations, newEtcd)
 | 
					 | 
				
			||||||
		apigroup, ok := storageDestinations.APIGroups[test.apigroup]
 | 
					 | 
				
			||||||
		if !ok {
 | 
					 | 
				
			||||||
			t.Errorf("apigroup: %s not created", test.apigroup)
 | 
					 | 
				
			||||||
			continue
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if apigroup.Overrides == nil {
 | 
					 | 
				
			||||||
			t.Errorf("Overrides not created for: %s", test.apigroup)
 | 
					 | 
				
			||||||
			continue
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if _, ok := apigroup.Overrides[test.resource]; !ok {
 | 
					 | 
				
			||||||
			t.Errorf("override not created for: %s", test.resource)
 | 
					 | 
				
			||||||
			continue
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func TestParseRuntimeConfig(t *testing.T) {
 | 
					func TestParseRuntimeConfig(t *testing.T) {
 | 
				
			||||||
	testCases := []struct {
 | 
						testCases := []struct {
 | 
				
			||||||
		runtimeConfig     map[string]string
 | 
							runtimeConfig     map[string]string
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -24,7 +24,7 @@ import (
 | 
				
			|||||||
	testgroupetcd "k8s.io/kubernetes/examples/apiserver/rest"
 | 
						testgroupetcd "k8s.io/kubernetes/examples/apiserver/rest"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/api"
 | 
						"k8s.io/kubernetes/pkg/api"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/api/rest"
 | 
						"k8s.io/kubernetes/pkg/api/rest"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/apimachinery"
 | 
						"k8s.io/kubernetes/pkg/api/unversioned"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/apimachinery/registered"
 | 
						"k8s.io/kubernetes/pkg/apimachinery/registered"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/genericapiserver"
 | 
						"k8s.io/kubernetes/pkg/genericapiserver"
 | 
				
			||||||
	etcdstorage "k8s.io/kubernetes/pkg/storage/etcd"
 | 
						etcdstorage "k8s.io/kubernetes/pkg/storage/etcd"
 | 
				
			||||||
@@ -40,20 +40,14 @@ const (
 | 
				
			|||||||
	SecurePort   = 6444
 | 
						SecurePort   = 6444
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func newStorageDestinations(groupName string, groupMeta *apimachinery.GroupMeta) (*genericapiserver.StorageDestinations, error) {
 | 
					func newStorageFactory() genericapiserver.StorageFactory {
 | 
				
			||||||
	storageDestinations := genericapiserver.NewStorageDestinations()
 | 
						etcdConfig := etcdstorage.EtcdConfig{
 | 
				
			||||||
	var storageConfig etcdstorage.EtcdStorageConfig
 | 
					 | 
				
			||||||
	storageConfig.Config = etcdstorage.EtcdConfig{
 | 
					 | 
				
			||||||
		Prefix:     genericapiserver.DefaultEtcdPathPrefix,
 | 
							Prefix:     genericapiserver.DefaultEtcdPathPrefix,
 | 
				
			||||||
		ServerList: []string{"http://127.0.0.1:4001"},
 | 
							ServerList: []string{"http://127.0.0.1:4001"},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	storageConfig.Codec = groupMeta.Codec
 | 
						storageFactory := genericapiserver.NewDefaultStorageFactory(etcdConfig, api.Codecs, genericapiserver.NewDefaultResourceEncodingConfig(), genericapiserver.NewResourceConfig())
 | 
				
			||||||
	storageInterface, err := storageConfig.NewStorage()
 | 
					
 | 
				
			||||||
	if err != nil {
 | 
						return storageFactory
 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	storageDestinations.AddAPIGroup(groupName, storageInterface)
 | 
					 | 
				
			||||||
	return &storageDestinations, nil
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func NewServerRunOptions() *genericapiserver.ServerRunOptions {
 | 
					func NewServerRunOptions() *genericapiserver.ServerRunOptions {
 | 
				
			||||||
@@ -86,12 +80,14 @@ func Run(serverOptions *genericapiserver.ServerRunOptions) error {
 | 
				
			|||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return fmt.Errorf("%v", err)
 | 
							return fmt.Errorf("%v", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	storageDestinations, err := newStorageDestinations(groupName, groupMeta)
 | 
						storageFactory := newStorageFactory()
 | 
				
			||||||
 | 
						storage, err := storageFactory.New(unversioned.GroupResource{Group: groupName, Resource: "testtype"})
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return fmt.Errorf("Unable to init etcd: %v", err)
 | 
							return fmt.Errorf("Unable to get storage: %v", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	restStorageMap := map[string]rest.Storage{
 | 
						restStorageMap := map[string]rest.Storage{
 | 
				
			||||||
		"testtypes": testgroupetcd.NewREST(storageDestinations.Get(groupName, "testtype"), s.StorageDecorator()),
 | 
							"testtypes": testgroupetcd.NewREST(storage, s.StorageDecorator()),
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	apiGroupInfo := genericapiserver.APIGroupInfo{
 | 
						apiGroupInfo := genericapiserver.APIGroupInfo{
 | 
				
			||||||
		GroupMeta: *groupMeta,
 | 
							GroupMeta: *groupMeta,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -24,6 +24,7 @@ import (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/admission"
 | 
						"k8s.io/kubernetes/pkg/admission"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/api"
 | 
						"k8s.io/kubernetes/pkg/api"
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/api/unversioned"
 | 
				
			||||||
	apiutil "k8s.io/kubernetes/pkg/api/util"
 | 
						apiutil "k8s.io/kubernetes/pkg/api/util"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/api/validation"
 | 
						"k8s.io/kubernetes/pkg/api/validation"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/apimachinery/registered"
 | 
						"k8s.io/kubernetes/pkg/apimachinery/registered"
 | 
				
			||||||
@@ -119,39 +120,53 @@ func NewAPIServer() *APIServer {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// dest must be a map of group to groupVersion.
 | 
					// dest must be a map of group to groupVersion.
 | 
				
			||||||
func gvToMap(gvList string, dest map[string]string) {
 | 
					func mergeGroupVersionIntoMap(gvList string, dest map[string]unversioned.GroupVersion) error {
 | 
				
			||||||
	for _, gv := range strings.Split(gvList, ",") {
 | 
						for _, gvString := range strings.Split(gvList, ",") {
 | 
				
			||||||
		if gv == "" {
 | 
							if gvString == "" {
 | 
				
			||||||
			continue
 | 
								continue
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		// We accept two formats. "group/version" OR
 | 
							// We accept two formats. "group/version" OR
 | 
				
			||||||
		// "group=group/version". The latter is used when types
 | 
							// "group=group/version". The latter is used when types
 | 
				
			||||||
		// move between groups.
 | 
							// move between groups.
 | 
				
			||||||
		if !strings.Contains(gv, "=") {
 | 
							if !strings.Contains(gvString, "=") {
 | 
				
			||||||
			dest[apiutil.GetGroup(gv)] = gv
 | 
								gv, err := unversioned.ParseGroupVersion(gvString)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								dest[gv.Group] = gv
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			parts := strings.SplitN(gv, "=", 2)
 | 
								parts := strings.SplitN(gvString, "=", 2)
 | 
				
			||||||
			// TODO: error checking.
 | 
								gv, err := unversioned.ParseGroupVersion(parts[1])
 | 
				
			||||||
			dest[parts[0]] = parts[1]
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								dest[parts[0]] = gv
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// StorageGroupsToGroupVersions returns a map from group name to group version,
 | 
					// StorageGroupsToEncodingVersion returns a map from group name to group version,
 | 
				
			||||||
// computed from the s.DeprecatedStorageVersion and s.StorageVersions flags.
 | 
					// computed from the s.DeprecatedStorageVersion and s.StorageVersions flags.
 | 
				
			||||||
// TODO: can we move the whole storage version concept to the generic apiserver?
 | 
					// TODO: can we move the whole storage version concept to the generic apiserver?
 | 
				
			||||||
func (s *APIServer) StorageGroupsToGroupVersions() map[string]string {
 | 
					func (s *APIServer) StorageGroupsToEncodingVersion() (map[string]unversioned.GroupVersion, error) {
 | 
				
			||||||
	storageVersionMap := map[string]string{}
 | 
						storageVersionMap := map[string]unversioned.GroupVersion{}
 | 
				
			||||||
	if s.DeprecatedStorageVersion != "" {
 | 
						if s.DeprecatedStorageVersion != "" {
 | 
				
			||||||
		storageVersionMap[""] = s.DeprecatedStorageVersion
 | 
							storageVersionMap[""] = unversioned.GroupVersion{Group: apiutil.GetGroup(s.DeprecatedStorageVersion), Version: apiutil.GetVersion(s.DeprecatedStorageVersion)}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// First, get the defaults.
 | 
						// First, get the defaults.
 | 
				
			||||||
	gvToMap(s.DefaultStorageVersions, storageVersionMap)
 | 
						if err := mergeGroupVersionIntoMap(s.DefaultStorageVersions, storageVersionMap); err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	// Override any defaults with the user settings.
 | 
						// Override any defaults with the user settings.
 | 
				
			||||||
	gvToMap(s.StorageVersions, storageVersionMap)
 | 
						if err := mergeGroupVersionIntoMap(s.StorageVersions, storageVersionMap); err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return storageVersionMap
 | 
						return storageVersionMap, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// AddFlags adds flags for a specific APIServer to the specified FlagSet
 | 
					// AddFlags adds flags for a specific APIServer to the specified FlagSet
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -23,6 +23,7 @@ import (
 | 
				
			|||||||
	"github.com/spf13/pflag"
 | 
						"github.com/spf13/pflag"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/api"
 | 
						"k8s.io/kubernetes/pkg/api"
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/api/unversioned"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/apis/autoscaling"
 | 
						"k8s.io/kubernetes/pkg/apis/autoscaling"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/apis/extensions"
 | 
						"k8s.io/kubernetes/pkg/apis/extensions"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
@@ -32,38 +33,38 @@ func TestGenerateStorageVersionMap(t *testing.T) {
 | 
				
			|||||||
		legacyVersion   string
 | 
							legacyVersion   string
 | 
				
			||||||
		storageVersions string
 | 
							storageVersions string
 | 
				
			||||||
		defaultVersions string
 | 
							defaultVersions string
 | 
				
			||||||
		expectedMap     map[string]string
 | 
							expectedMap     map[string]unversioned.GroupVersion
 | 
				
			||||||
	}{
 | 
						}{
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			legacyVersion:   "v1",
 | 
								legacyVersion:   "v1",
 | 
				
			||||||
			storageVersions: "v1,extensions/v1beta1",
 | 
								storageVersions: "v1,extensions/v1beta1",
 | 
				
			||||||
			expectedMap: map[string]string{
 | 
								expectedMap: map[string]unversioned.GroupVersion{
 | 
				
			||||||
				api.GroupName:        "v1",
 | 
									api.GroupName:        {Version: "v1"},
 | 
				
			||||||
				extensions.GroupName: "extensions/v1beta1",
 | 
									extensions.GroupName: {Group: "extensions", Version: "v1beta1"},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			legacyVersion:   "",
 | 
								legacyVersion:   "",
 | 
				
			||||||
			storageVersions: "extensions/v1beta1,v1",
 | 
								storageVersions: "extensions/v1beta1,v1",
 | 
				
			||||||
			expectedMap: map[string]string{
 | 
								expectedMap: map[string]unversioned.GroupVersion{
 | 
				
			||||||
				api.GroupName:        "v1",
 | 
									api.GroupName:        {Version: "v1"},
 | 
				
			||||||
				extensions.GroupName: "extensions/v1beta1",
 | 
									extensions.GroupName: {Group: "extensions", Version: "v1beta1"},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			legacyVersion:   "",
 | 
								legacyVersion:   "",
 | 
				
			||||||
			storageVersions: "autoscaling=extensions/v1beta1,v1",
 | 
								storageVersions: "autoscaling=extensions/v1beta1,v1",
 | 
				
			||||||
			defaultVersions: "extensions/v1beta1,v1,autoscaling/v1",
 | 
								defaultVersions: "extensions/v1beta1,v1,autoscaling/v1",
 | 
				
			||||||
			expectedMap: map[string]string{
 | 
								expectedMap: map[string]unversioned.GroupVersion{
 | 
				
			||||||
				api.GroupName:         "v1",
 | 
									api.GroupName:         {Version: "v1"},
 | 
				
			||||||
				autoscaling.GroupName: "extensions/v1beta1",
 | 
									autoscaling.GroupName: {Group: "extensions", Version: "v1beta1"},
 | 
				
			||||||
				extensions.GroupName:  "extensions/v1beta1",
 | 
									extensions.GroupName:  {Group: "extensions", Version: "v1beta1"},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			legacyVersion:   "",
 | 
								legacyVersion:   "",
 | 
				
			||||||
			storageVersions: "",
 | 
								storageVersions: "",
 | 
				
			||||||
			expectedMap:     map[string]string{},
 | 
								expectedMap:     map[string]unversioned.GroupVersion{},
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	for i, test := range testCases {
 | 
						for i, test := range testCases {
 | 
				
			||||||
@@ -72,7 +73,10 @@ func TestGenerateStorageVersionMap(t *testing.T) {
 | 
				
			|||||||
			StorageVersions:          test.storageVersions,
 | 
								StorageVersions:          test.storageVersions,
 | 
				
			||||||
			DefaultStorageVersions:   test.defaultVersions,
 | 
								DefaultStorageVersions:   test.defaultVersions,
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		output := s.StorageGroupsToGroupVersions()
 | 
							output, err := s.StorageGroupsToEncodingVersion()
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								t.Errorf("%v: unexpected error: %v", i, err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		if !reflect.DeepEqual(test.expectedMap, output) {
 | 
							if !reflect.DeepEqual(test.expectedMap, output) {
 | 
				
			||||||
			t.Errorf("%v: unexpected error. expect: %v, got: %v", i, test.expectedMap, output)
 | 
								t.Errorf("%v: unexpected error. expect: %v, got: %v", i, test.expectedMap, output)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,7 +21,6 @@ package app
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"crypto/tls"
 | 
						"crypto/tls"
 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
	"net"
 | 
						"net"
 | 
				
			||||||
	"net/url"
 | 
						"net/url"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
@@ -47,9 +46,6 @@ import (
 | 
				
			|||||||
	"k8s.io/kubernetes/pkg/master"
 | 
						"k8s.io/kubernetes/pkg/master"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/registry/cachesize"
 | 
						"k8s.io/kubernetes/pkg/registry/cachesize"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/runtime"
 | 
						"k8s.io/kubernetes/pkg/runtime"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/runtime/serializer/versioning"
 | 
					 | 
				
			||||||
	"k8s.io/kubernetes/pkg/storage"
 | 
					 | 
				
			||||||
	etcdstorage "k8s.io/kubernetes/pkg/storage/etcd"
 | 
					 | 
				
			||||||
	utilnet "k8s.io/kubernetes/pkg/util/net"
 | 
						utilnet "k8s.io/kubernetes/pkg/util/net"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -81,44 +77,6 @@ func verifyClusterIPFlags(s *options.APIServer) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// For testing.
 | 
					 | 
				
			||||||
type newEtcdFunc func(runtime.NegotiatedSerializer, string, string, etcdstorage.EtcdConfig) (storage.Interface, error)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func newEtcd(ns runtime.NegotiatedSerializer, storageGroupVersionString, memoryGroupVersionString string, etcdConfig etcdstorage.EtcdConfig) (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, 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.EtcdStorageConfig
 | 
					 | 
				
			||||||
	storageConfig.Config = etcdConfig
 | 
					 | 
				
			||||||
	s, ok := ns.SerializerForMediaType("application/json", nil)
 | 
					 | 
				
			||||||
	if !ok {
 | 
					 | 
				
			||||||
		return nil, fmt.Errorf("unable to find serializer for JSON")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	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)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	storageConfig.Codec = runtime.NewCodec(encoder, decoder)
 | 
					 | 
				
			||||||
	return storageConfig.NewStorage()
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Run runs the specified APIServer.  This should never exit.
 | 
					// Run runs the specified APIServer.  This should never exit.
 | 
				
			||||||
func Run(s *options.APIServer) error {
 | 
					func Run(s *options.APIServer) error {
 | 
				
			||||||
	verifyClusterIPFlags(s)
 | 
						verifyClusterIPFlags(s)
 | 
				
			||||||
@@ -228,6 +186,36 @@ func Run(s *options.APIServer) error {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	n := s.ServiceClusterIPRange
 | 
						n := s.ServiceClusterIPRange
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						resourceEncoding := genericapiserver.NewDefaultResourceEncodingConfig()
 | 
				
			||||||
 | 
						groupToEncoding, err := s.StorageGroupsToEncodingVersion()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							glog.Fatalf("error getting group encoding: %s", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for group, storageEncodingVersion := range groupToEncoding {
 | 
				
			||||||
 | 
							resourceEncoding.SetVersionEncoding(group, storageEncodingVersion, unversioned.GroupVersion{Group: group, Version: runtime.APIVersionInternal})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						storageFactory := genericapiserver.NewDefaultStorageFactory(s.EtcdConfig, api.Codecs, resourceEncoding, apiResourceConfigSource)
 | 
				
			||||||
 | 
						for _, override := range s.EtcdServersOverrides {
 | 
				
			||||||
 | 
							tokens := strings.Split(override, "#")
 | 
				
			||||||
 | 
							if len(tokens) != 2 {
 | 
				
			||||||
 | 
								glog.Errorf("invalid value of etcd server overrides: %s", override)
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							apiresource := strings.Split(tokens[0], "/")
 | 
				
			||||||
 | 
							if len(apiresource) != 2 {
 | 
				
			||||||
 | 
								glog.Errorf("invalid resource definition: %s", tokens[0])
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							group := apiresource[0]
 | 
				
			||||||
 | 
							resource := apiresource[1]
 | 
				
			||||||
 | 
							groupResource := unversioned.GroupResource{Group: group, Resource: resource}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							servers := strings.Split(tokens[1], ";")
 | 
				
			||||||
 | 
							storageFactory.SetEtcdLocation(groupResource, servers)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	authenticator, err := authenticator.New(authenticator.AuthenticatorConfig{
 | 
						authenticator, err := authenticator.New(authenticator.AuthenticatorConfig{
 | 
				
			||||||
		BasicAuthFile:     s.BasicAuthFile,
 | 
							BasicAuthFile:     s.BasicAuthFile,
 | 
				
			||||||
		ClientCAFile:      s.ClientCAFile,
 | 
							ClientCAFile:      s.ClientCAFile,
 | 
				
			||||||
@@ -276,13 +264,9 @@ func Run(s *options.APIServer) error {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	storageDestinations := genericapiserver.NewStorageDestinations()
 | 
					 | 
				
			||||||
	storageVersions := s.StorageGroupsToGroupVersions()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	config := &master.Config{
 | 
						config := &master.Config{
 | 
				
			||||||
		Config: &genericapiserver.Config{
 | 
							Config: &genericapiserver.Config{
 | 
				
			||||||
			StorageDestinations:       storageDestinations,
 | 
								StorageFactory:            storageFactory,
 | 
				
			||||||
			StorageVersions:           storageVersions,
 | 
					 | 
				
			||||||
			ServiceClusterIPRange:     &n,
 | 
								ServiceClusterIPRange:     &n,
 | 
				
			||||||
			EnableLogsSupport:         s.EnableLogsSupport,
 | 
								EnableLogsSupport:         s.EnableLogsSupport,
 | 
				
			||||||
			EnableUISupport:           true,
 | 
								EnableUISupport:           true,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -43,7 +43,6 @@ import (
 | 
				
			|||||||
	genericetcd "k8s.io/kubernetes/pkg/registry/generic/etcd"
 | 
						genericetcd "k8s.io/kubernetes/pkg/registry/generic/etcd"
 | 
				
			||||||
	ipallocator "k8s.io/kubernetes/pkg/registry/service/ipallocator"
 | 
						ipallocator "k8s.io/kubernetes/pkg/registry/service/ipallocator"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/runtime"
 | 
						"k8s.io/kubernetes/pkg/runtime"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/storage"
 | 
					 | 
				
			||||||
	"k8s.io/kubernetes/pkg/ui"
 | 
						"k8s.io/kubernetes/pkg/ui"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/util"
 | 
						"k8s.io/kubernetes/pkg/util"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/util/crypto"
 | 
						"k8s.io/kubernetes/pkg/util/crypto"
 | 
				
			||||||
@@ -55,7 +54,6 @@ import (
 | 
				
			|||||||
	"github.com/emicklei/go-restful"
 | 
						"github.com/emicklei/go-restful"
 | 
				
			||||||
	"github.com/emicklei/go-restful/swagger"
 | 
						"github.com/emicklei/go-restful/swagger"
 | 
				
			||||||
	"github.com/golang/glog"
 | 
						"github.com/golang/glog"
 | 
				
			||||||
	"golang.org/x/net/context"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
@@ -64,107 +62,6 @@ const (
 | 
				
			|||||||
	globalTimeout                   = time.Minute
 | 
						globalTimeout                   = time.Minute
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// StorageDestinations is a mapping from API group & resource to
 | 
					 | 
				
			||||||
// the underlying storage interfaces.
 | 
					 | 
				
			||||||
type StorageDestinations struct {
 | 
					 | 
				
			||||||
	APIGroups map[string]*StorageDestinationsForAPIGroup
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type StorageDestinationsForAPIGroup struct {
 | 
					 | 
				
			||||||
	Default   storage.Interface
 | 
					 | 
				
			||||||
	Overrides map[string]storage.Interface
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func NewStorageDestinations() StorageDestinations {
 | 
					 | 
				
			||||||
	return StorageDestinations{
 | 
					 | 
				
			||||||
		APIGroups: map[string]*StorageDestinationsForAPIGroup{},
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// 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{},
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (s *StorageDestinations) AddStorageOverride(group, resource string, override storage.Interface) {
 | 
					 | 
				
			||||||
	if _, ok := s.APIGroups[group]; !ok {
 | 
					 | 
				
			||||||
		s.AddAPIGroup(group, nil)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if s.APIGroups[group].Overrides == nil {
 | 
					 | 
				
			||||||
		s.APIGroups[group].Overrides = map[string]storage.Interface{}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	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 {
 | 
					 | 
				
			||||||
		// 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 {
 | 
					 | 
				
			||||||
		if client, exists := apigroup.Overrides[resource]; exists {
 | 
					 | 
				
			||||||
			return client
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	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 {
 | 
					 | 
				
			||||||
	backends := sets.String{}
 | 
					 | 
				
			||||||
	for _, group := range s.APIGroups {
 | 
					 | 
				
			||||||
		if group.Default != nil {
 | 
					 | 
				
			||||||
			for _, backend := range group.Default.Backends(context.TODO()) {
 | 
					 | 
				
			||||||
				backends.Insert(backend)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if group.Overrides != nil {
 | 
					 | 
				
			||||||
			for _, storage := range group.Overrides {
 | 
					 | 
				
			||||||
				for _, backend := range storage.Backends(context.TODO()) {
 | 
					 | 
				
			||||||
					backends.Insert(backend)
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return backends.List()
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Info about an API group.
 | 
					// Info about an API group.
 | 
				
			||||||
type APIGroupInfo struct {
 | 
					type APIGroupInfo struct {
 | 
				
			||||||
	GroupMeta apimachinery.GroupMeta
 | 
						GroupMeta apimachinery.GroupMeta
 | 
				
			||||||
@@ -199,9 +96,7 @@ type APIGroupInfo struct {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// Config is a structure used to configure a GenericAPIServer.
 | 
					// Config is a structure used to configure a GenericAPIServer.
 | 
				
			||||||
type Config struct {
 | 
					type Config struct {
 | 
				
			||||||
	StorageDestinations StorageDestinations
 | 
						StorageFactory StorageFactory
 | 
				
			||||||
	// StorageVersions is a map between groups and their storage versions
 | 
					 | 
				
			||||||
	StorageVersions map[string]string
 | 
					 | 
				
			||||||
	// allow downstream consumers to disable the core controller loops
 | 
						// allow downstream consumers to disable the core controller loops
 | 
				
			||||||
	EnableLogsSupport bool
 | 
						EnableLogsSupport bool
 | 
				
			||||||
	EnableUISupport   bool
 | 
						EnableUISupport   bool
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -32,7 +32,6 @@ import (
 | 
				
			|||||||
	"k8s.io/kubernetes/pkg/api/rest"
 | 
						"k8s.io/kubernetes/pkg/api/rest"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/api/testapi"
 | 
						"k8s.io/kubernetes/pkg/api/testapi"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/api/unversioned"
 | 
						"k8s.io/kubernetes/pkg/api/unversioned"
 | 
				
			||||||
	apiutil "k8s.io/kubernetes/pkg/api/util"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/apimachinery/registered"
 | 
						"k8s.io/kubernetes/pkg/apimachinery/registered"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/apis/extensions"
 | 
						"k8s.io/kubernetes/pkg/apis/extensions"
 | 
				
			||||||
@@ -266,7 +265,7 @@ func getGroupList(server *httptest.Server) (*unversioned.APIGroupList, error) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestDiscoveryAtAPIS(t *testing.T) {
 | 
					func TestDiscoveryAtAPIS(t *testing.T) {
 | 
				
			||||||
	master, etcdserver, config, assert := newMaster(t)
 | 
						master, etcdserver, _, assert := newMaster(t)
 | 
				
			||||||
	defer etcdserver.Terminate(t)
 | 
						defer etcdserver.Terminate(t)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	server := httptest.NewServer(master.HandlerContainer.ServeMux)
 | 
						server := httptest.NewServer(master.HandlerContainer.ServeMux)
 | 
				
			||||||
@@ -277,7 +276,6 @@ func TestDiscoveryAtAPIS(t *testing.T) {
 | 
				
			|||||||
	assert.Equal(0, len(groupList.Groups))
 | 
						assert.Equal(0, len(groupList.Groups))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Add a Group.
 | 
						// Add a Group.
 | 
				
			||||||
	extensionsGroupName := extensions.GroupName
 | 
					 | 
				
			||||||
	extensionsVersions := []unversioned.GroupVersionForDiscovery{
 | 
						extensionsVersions := []unversioned.GroupVersionForDiscovery{
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			GroupVersion: testapi.Extensions.GroupVersion().String(),
 | 
								GroupVersion: testapi.Extensions.GroupVersion().String(),
 | 
				
			||||||
@@ -285,11 +283,11 @@ func TestDiscoveryAtAPIS(t *testing.T) {
 | 
				
			|||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	extensionsPreferredVersion := unversioned.GroupVersionForDiscovery{
 | 
						extensionsPreferredVersion := unversioned.GroupVersionForDiscovery{
 | 
				
			||||||
		GroupVersion: config.StorageVersions[extensions.GroupName],
 | 
							GroupVersion: extensions.GroupName + "/preferred",
 | 
				
			||||||
		Version:      apiutil.GetVersion(config.StorageVersions[extensions.GroupName]),
 | 
							Version:      "preferred",
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	master.AddAPIGroupForDiscovery(unversioned.APIGroup{
 | 
						master.AddAPIGroupForDiscovery(unversioned.APIGroup{
 | 
				
			||||||
		Name:             extensionsGroupName,
 | 
							Name:             extensions.GroupName,
 | 
				
			||||||
		Versions:         extensionsVersions,
 | 
							Versions:         extensionsVersions,
 | 
				
			||||||
		PreferredVersion: extensionsPreferredVersion,
 | 
							PreferredVersion: extensionsPreferredVersion,
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
@@ -301,13 +299,13 @@ func TestDiscoveryAtAPIS(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	assert.Equal(1, len(groupList.Groups))
 | 
						assert.Equal(1, len(groupList.Groups))
 | 
				
			||||||
	groupListGroup := groupList.Groups[0]
 | 
						groupListGroup := groupList.Groups[0]
 | 
				
			||||||
	assert.Equal(extensionsGroupName, groupListGroup.Name)
 | 
						assert.Equal(extensions.GroupName, groupListGroup.Name)
 | 
				
			||||||
	assert.Equal(extensionsVersions, groupListGroup.Versions)
 | 
						assert.Equal(extensionsVersions, groupListGroup.Versions)
 | 
				
			||||||
	assert.Equal(extensionsPreferredVersion, groupListGroup.PreferredVersion)
 | 
						assert.Equal(extensionsPreferredVersion, groupListGroup.PreferredVersion)
 | 
				
			||||||
	assert.Equal(master.getServerAddressByClientCIDRs(&http.Request{}), groupListGroup.ServerAddressByClientCIDRs)
 | 
						assert.Equal(master.getServerAddressByClientCIDRs(&http.Request{}), groupListGroup.ServerAddressByClientCIDRs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Remove the group.
 | 
						// Remove the group.
 | 
				
			||||||
	master.RemoveAPIGroupForDiscovery(extensionsGroupName)
 | 
						master.RemoveAPIGroupForDiscovery(extensions.GroupName)
 | 
				
			||||||
	groupList, err = getGroupList(server)
 | 
						groupList, err = getGroupList(server)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		t.Fatalf("unexpected error: %v", err)
 | 
							t.Fatalf("unexpected error: %v", err)
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										118
									
								
								pkg/genericapiserver/resource_encoding_config.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										118
									
								
								pkg/genericapiserver/resource_encoding_config.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,118 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					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 genericapiserver
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/api/unversioned"
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/apimachinery/registered"
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/runtime"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ResourceEncodingConfig interface {
 | 
				
			||||||
 | 
						// StorageEncoding returns the serialization format for the resource.
 | 
				
			||||||
 | 
						// TODO this should actually return a GroupVersionKind since you can logically have multiple "matching" Kinds
 | 
				
			||||||
 | 
						// For now, it returns just the GroupVersion for consistency with old behavior
 | 
				
			||||||
 | 
						StoragageEncodingFor(unversioned.GroupResource) (unversioned.GroupVersion, error)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// InMemoryEncodingFor returns the groupVersion for the in memory representation the storage should convert to.
 | 
				
			||||||
 | 
						InMemoryEncodingFor(unversioned.GroupResource) (unversioned.GroupVersion, error)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type DefaultResourceEncodingConfig struct {
 | 
				
			||||||
 | 
						Groups map[string]*GroupResourceEncodingConfig
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type GroupResourceEncodingConfig struct {
 | 
				
			||||||
 | 
						DefaultExternalEncoding   unversioned.GroupVersion
 | 
				
			||||||
 | 
						ExternalResourceEncodings map[string]unversioned.GroupVersion
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						DefaultInternalEncoding   unversioned.GroupVersion
 | 
				
			||||||
 | 
						InternalResourceEncodings map[string]unversioned.GroupVersion
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var _ ResourceEncodingConfig = &DefaultResourceEncodingConfig{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func NewDefaultResourceEncodingConfig() *DefaultResourceEncodingConfig {
 | 
				
			||||||
 | 
						return &DefaultResourceEncodingConfig{Groups: map[string]*GroupResourceEncodingConfig{}}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func newGroupResourceEncodingConfig(defaultEncoding, defaultInternalVersion unversioned.GroupVersion) *GroupResourceEncodingConfig {
 | 
				
			||||||
 | 
						return &GroupResourceEncodingConfig{
 | 
				
			||||||
 | 
							DefaultExternalEncoding: defaultEncoding, ExternalResourceEncodings: map[string]unversioned.GroupVersion{},
 | 
				
			||||||
 | 
							DefaultInternalEncoding: defaultInternalVersion, InternalResourceEncodings: map[string]unversioned.GroupVersion{},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (o *DefaultResourceEncodingConfig) SetVersionEncoding(group string, externalEncodingVersion, internalVersion unversioned.GroupVersion) {
 | 
				
			||||||
 | 
						_, groupExists := o.Groups[group]
 | 
				
			||||||
 | 
						if !groupExists {
 | 
				
			||||||
 | 
							o.Groups[group] = newGroupResourceEncodingConfig(externalEncodingVersion, internalVersion)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						o.Groups[group].DefaultExternalEncoding = externalEncodingVersion
 | 
				
			||||||
 | 
						o.Groups[group].DefaultInternalEncoding = internalVersion
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (o *DefaultResourceEncodingConfig) SetResourceEncoding(resourceBeingStored unversioned.GroupResource, externalEncodingVersion, internalVersion unversioned.GroupVersion) {
 | 
				
			||||||
 | 
						group := resourceBeingStored.Group
 | 
				
			||||||
 | 
						_, groupExists := o.Groups[group]
 | 
				
			||||||
 | 
						if !groupExists {
 | 
				
			||||||
 | 
							o.Groups[group] = newGroupResourceEncodingConfig(externalEncodingVersion, internalVersion)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						o.Groups[group].ExternalResourceEncodings[resourceBeingStored.Resource] = externalEncodingVersion
 | 
				
			||||||
 | 
						o.Groups[group].InternalResourceEncodings[resourceBeingStored.Resource] = internalVersion
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (o *DefaultResourceEncodingConfig) StoragageEncodingFor(resource unversioned.GroupResource) (unversioned.GroupVersion, error) {
 | 
				
			||||||
 | 
						groupMeta, err := registered.Group(resource.Group)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return unversioned.GroupVersion{}, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						groupEncoding, groupExists := o.Groups[resource.Group]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if !groupExists {
 | 
				
			||||||
 | 
							// return the most preferred external version for the group
 | 
				
			||||||
 | 
							return groupMeta.GroupVersion, nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						resourceOverride, resourceExists := groupEncoding.ExternalResourceEncodings[resource.Resource]
 | 
				
			||||||
 | 
						if !resourceExists {
 | 
				
			||||||
 | 
							return groupEncoding.DefaultExternalEncoding, nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return resourceOverride, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (o *DefaultResourceEncodingConfig) InMemoryEncodingFor(resource unversioned.GroupResource) (unversioned.GroupVersion, error) {
 | 
				
			||||||
 | 
						if _, err := registered.Group(resource.Group); err != nil {
 | 
				
			||||||
 | 
							return unversioned.GroupVersion{}, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						groupEncoding, groupExists := o.Groups[resource.Group]
 | 
				
			||||||
 | 
						if !groupExists {
 | 
				
			||||||
 | 
							return unversioned.GroupVersion{Group: resource.Group, Version: runtime.APIVersionInternal}, nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						resourceOverride, resourceExists := groupEncoding.InternalResourceEncodings[resource.Resource]
 | 
				
			||||||
 | 
						if !resourceExists {
 | 
				
			||||||
 | 
							return groupEncoding.DefaultInternalEncoding, nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return resourceOverride, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										221
									
								
								pkg/genericapiserver/storage_factory.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										221
									
								
								pkg/genericapiserver/storage_factory.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,221 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					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 genericapiserver
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/api/unversioned"
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/runtime"
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/runtime/serializer/versioning"
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/storage"
 | 
				
			||||||
 | 
						etcdstorage "k8s.io/kubernetes/pkg/storage/etcd"
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/util/sets"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/golang/glog"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// StorageFactory is the interface to locate the storage for a given GroupResource
 | 
				
			||||||
 | 
					type StorageFactory interface {
 | 
				
			||||||
 | 
						// New finds the storage destination for the given group and resource. It will
 | 
				
			||||||
 | 
						// return an error if the group has no storage destination configured.
 | 
				
			||||||
 | 
						New(groupResource unversioned.GroupResource) (storage.Interface, error)
 | 
				
			||||||
 | 
						// Backends gets all backends for all registered storage destinations.
 | 
				
			||||||
 | 
						// Used for getting all instances for health validations.
 | 
				
			||||||
 | 
						Backends() []string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// DefaultStorageFactory takes a GroupResource and returns back its storage interface.  This result includes:
 | 
				
			||||||
 | 
					// 1. Merged etcd config, including: auth, server locations, prefixes
 | 
				
			||||||
 | 
					// 2. Resource encodings for storage: group,version,kind to store as
 | 
				
			||||||
 | 
					// 3. Cohabitating default: some resources like hpa are exposed through multiple APIs.  They must agree on 1 and 2
 | 
				
			||||||
 | 
					type DefaultStorageFactory struct {
 | 
				
			||||||
 | 
						// DefaultEtcdConfig describes how to connect to etcd in general.  It's authentication information will be used for
 | 
				
			||||||
 | 
						// every storage.Interface returned.
 | 
				
			||||||
 | 
						DefaultEtcdConfig etcdstorage.EtcdConfig
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Overrides map[unversioned.GroupResource]groupResourceOverrides
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// DefaultSerializer is used to create encoders and decoders for the storage.Interface.
 | 
				
			||||||
 | 
						DefaultSerializer runtime.NegotiatedSerializer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// ResourceEncodingConfig describes how to encode a particular GroupVersionResource
 | 
				
			||||||
 | 
						ResourceEncodingConfig ResourceEncodingConfig
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// APIResourceConfigSource indicates whether the *storage* is enabled, NOT the API
 | 
				
			||||||
 | 
						// This is discrete from resource enablement because those are separate concerns.  How it is surfaced to the user via flags
 | 
				
			||||||
 | 
						// or config is up to whoever is building this.
 | 
				
			||||||
 | 
						APIResourceConfigSource APIResourceConfigSource
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// newEtcdFn exists to be overwritten for unit testing.  You should never set this in a normal world.
 | 
				
			||||||
 | 
						newEtcdFn func(ns runtime.NegotiatedSerializer, storageVersion, memoryVersion unversioned.GroupVersion, etcdConfig etcdstorage.EtcdConfig) (etcdStorage storage.Interface, err error)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type groupResourceOverrides struct {
 | 
				
			||||||
 | 
						// etcdLocation contains the list of "special" locations that are used for particular GroupResources
 | 
				
			||||||
 | 
						// These are merged on top of the default DefaultEtcdConfig when requesting the storage.Interface for a given GroupResource
 | 
				
			||||||
 | 
						etcdLocation []string
 | 
				
			||||||
 | 
						// etcdPrefix contains the list of "special" prefixes for a GroupResource.  Resource=* means for the entire group
 | 
				
			||||||
 | 
						etcdPrefix string
 | 
				
			||||||
 | 
						// serializer contains the list of "special" serializers for a GroupResource.  Resource=* means for the entire group
 | 
				
			||||||
 | 
						serializer runtime.NegotiatedSerializer
 | 
				
			||||||
 | 
						// cohabitatingResources keeps track of which resources must be stored together.  This happens when we have multiple ways
 | 
				
			||||||
 | 
						// of exposing one set of concepts.  autoscaling.HPA and extensions.HPA as a for instance
 | 
				
			||||||
 | 
						// The order of the slice matters!  It is the priority order of lookup for finding a storage location
 | 
				
			||||||
 | 
						cohabitatingResources []unversioned.GroupResource
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var _ StorageFactory = &DefaultStorageFactory{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const AllResources = "*"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func NewDefaultStorageFactory(defaultEtcdConfig etcdstorage.EtcdConfig, defaultSerializer runtime.NegotiatedSerializer, resourceEncodingConfig ResourceEncodingConfig, resourceConfig APIResourceConfigSource) *DefaultStorageFactory {
 | 
				
			||||||
 | 
						return &DefaultStorageFactory{
 | 
				
			||||||
 | 
							DefaultEtcdConfig:       defaultEtcdConfig,
 | 
				
			||||||
 | 
							Overrides:               map[unversioned.GroupResource]groupResourceOverrides{},
 | 
				
			||||||
 | 
							DefaultSerializer:       defaultSerializer,
 | 
				
			||||||
 | 
							ResourceEncodingConfig:  resourceEncodingConfig,
 | 
				
			||||||
 | 
							APIResourceConfigSource: resourceConfig,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							newEtcdFn: newEtcd,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *DefaultStorageFactory) SetEtcdLocation(groupResource unversioned.GroupResource, location []string) {
 | 
				
			||||||
 | 
						overrides := s.Overrides[groupResource]
 | 
				
			||||||
 | 
						overrides.etcdLocation = location
 | 
				
			||||||
 | 
						s.Overrides[groupResource] = overrides
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *DefaultStorageFactory) SetEtcdPrefix(groupResource unversioned.GroupResource, prefix string) {
 | 
				
			||||||
 | 
						overrides := s.Overrides[groupResource]
 | 
				
			||||||
 | 
						overrides.etcdPrefix = prefix
 | 
				
			||||||
 | 
						s.Overrides[groupResource] = overrides
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *DefaultStorageFactory) SetSerializer(groupResource unversioned.GroupResource, serializer runtime.NegotiatedSerializer) {
 | 
				
			||||||
 | 
						overrides := s.Overrides[groupResource]
 | 
				
			||||||
 | 
						overrides.serializer = serializer
 | 
				
			||||||
 | 
						s.Overrides[groupResource] = overrides
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// AddCohabitatingResources links resources together the order of the slice matters!  its the priority order of lookup for finding a storage location
 | 
				
			||||||
 | 
					func (s *DefaultStorageFactory) AddCohabitatingResources(groupResources ...unversioned.GroupResource) {
 | 
				
			||||||
 | 
						for _, groupResource := range groupResources {
 | 
				
			||||||
 | 
							overrides := s.Overrides[groupResource]
 | 
				
			||||||
 | 
							overrides.cohabitatingResources = groupResources
 | 
				
			||||||
 | 
							s.Overrides[groupResource] = overrides
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func getAllResourcesAlias(resource unversioned.GroupResource) unversioned.GroupResource {
 | 
				
			||||||
 | 
						return unversioned.GroupResource{Group: resource.Group, Resource: AllResources}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *DefaultStorageFactory) getStorageGroupResource(groupResource unversioned.GroupResource) unversioned.GroupResource {
 | 
				
			||||||
 | 
						for _, potentialStorageResource := range s.Overrides[groupResource].cohabitatingResources {
 | 
				
			||||||
 | 
							if s.APIResourceConfigSource.AnyVersionOfResourceEnabled(potentialStorageResource) {
 | 
				
			||||||
 | 
								return potentialStorageResource
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return groupResource
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// New finds the storage destination for the given group and resource. It will
 | 
				
			||||||
 | 
					// return an error if the group has no storage destination configured.
 | 
				
			||||||
 | 
					func (s *DefaultStorageFactory) New(groupResource unversioned.GroupResource) (storage.Interface, error) {
 | 
				
			||||||
 | 
						chosenStorageResource := s.getStorageGroupResource(groupResource)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						groupOverride := s.Overrides[getAllResourcesAlias(chosenStorageResource)]
 | 
				
			||||||
 | 
						exactResourceOverride := s.Overrides[chosenStorageResource]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						overriddenEtcdLocations := []string{}
 | 
				
			||||||
 | 
						if len(groupOverride.etcdLocation) > 0 {
 | 
				
			||||||
 | 
							overriddenEtcdLocations = groupOverride.etcdLocation
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if len(exactResourceOverride.etcdLocation) > 0 {
 | 
				
			||||||
 | 
							overriddenEtcdLocations = exactResourceOverride.etcdLocation
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						etcdPrefix := s.DefaultEtcdConfig.Prefix
 | 
				
			||||||
 | 
						if len(groupOverride.etcdPrefix) > 0 {
 | 
				
			||||||
 | 
							etcdPrefix = groupOverride.etcdPrefix
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if len(exactResourceOverride.etcdPrefix) > 0 {
 | 
				
			||||||
 | 
							etcdPrefix = exactResourceOverride.etcdPrefix
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						etcdSerializer := s.DefaultSerializer
 | 
				
			||||||
 | 
						if groupOverride.serializer != nil {
 | 
				
			||||||
 | 
							etcdSerializer = groupOverride.serializer
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if exactResourceOverride.serializer != nil {
 | 
				
			||||||
 | 
							etcdSerializer = exactResourceOverride.serializer
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// operate on copy
 | 
				
			||||||
 | 
						etcdConfig := s.DefaultEtcdConfig
 | 
				
			||||||
 | 
						etcdConfig.Prefix = etcdPrefix
 | 
				
			||||||
 | 
						if len(overriddenEtcdLocations) > 0 {
 | 
				
			||||||
 | 
							etcdConfig.ServerList = overriddenEtcdLocations
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						storageEncodingVersion, err := s.ResourceEncodingConfig.StoragageEncodingFor(chosenStorageResource)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						internalVersion, err := s.ResourceEncodingConfig.InMemoryEncodingFor(groupResource)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						glog.V(3).Infof("storing %v in %v, reading as %v from %v", groupResource, storageEncodingVersion, internalVersion, etcdConfig)
 | 
				
			||||||
 | 
						return s.newEtcdFn(etcdSerializer, storageEncodingVersion, internalVersion, etcdConfig)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func newEtcd(ns runtime.NegotiatedSerializer, storageVersion, memoryVersion unversioned.GroupVersion, etcdConfig etcdstorage.EtcdConfig) (etcdStorage storage.Interface, err error) {
 | 
				
			||||||
 | 
						var storageConfig etcdstorage.EtcdStorageConfig
 | 
				
			||||||
 | 
						storageConfig.Config = etcdConfig
 | 
				
			||||||
 | 
						s, ok := ns.SerializerForMediaType("application/json", nil)
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							return nil, fmt.Errorf("unable to find serializer for JSON")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						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 from %v to %v: %v", memoryVersion, storageVersion, err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if err = versioning.EnableCrossGroupDecoding(decoder, storageVersion.Group, memoryVersion.Group); err != nil {
 | 
				
			||||||
 | 
								return nil, fmt.Errorf("error setting up decoder from %v to %v: %v", storageVersion, memoryVersion, err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						storageConfig.Codec = runtime.NewCodec(encoder, decoder)
 | 
				
			||||||
 | 
						return storageConfig.NewStorage()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Get all backends for all registered storage destinations.
 | 
				
			||||||
 | 
					// Used for getting all instances for health validations.
 | 
				
			||||||
 | 
					func (s *DefaultStorageFactory) Backends() []string {
 | 
				
			||||||
 | 
						backends := sets.NewString(s.DefaultEtcdConfig.ServerList...)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, overrides := range s.Overrides {
 | 
				
			||||||
 | 
							backends.Insert(overrides.etcdLocation...)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return backends.List()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										88
									
								
								pkg/genericapiserver/storage_factory_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								pkg/genericapiserver/storage_factory_test.go
									
									
									
									
									
										Normal 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 genericapiserver
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"reflect"
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/api"
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/api/unversioned"
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/apis/extensions"
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/runtime"
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/storage"
 | 
				
			||||||
 | 
						etcdstorage "k8s.io/kubernetes/pkg/storage/etcd"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestUpdateEtcdOverrides(t *testing.T) {
 | 
				
			||||||
 | 
						testCases := []struct {
 | 
				
			||||||
 | 
							resource unversioned.GroupResource
 | 
				
			||||||
 | 
							servers  []string
 | 
				
			||||||
 | 
						}{
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								resource: unversioned.GroupResource{Group: api.GroupName, Resource: "resource"},
 | 
				
			||||||
 | 
								servers:  []string{"http://127.0.0.1:10000"},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								resource: unversioned.GroupResource{Group: api.GroupName, Resource: "resource"},
 | 
				
			||||||
 | 
								servers:  []string{"http://127.0.0.1:10000", "http://127.0.0.1:20000"},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								resource: unversioned.GroupResource{Group: extensions.GroupName, Resource: "resource"},
 | 
				
			||||||
 | 
								servers:  []string{"http://127.0.0.1:10000"},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						defaultEtcdLocation := []string{"http://127.0.0.1"}
 | 
				
			||||||
 | 
						for i, test := range testCases {
 | 
				
			||||||
 | 
							actualEtcdConfig := etcdstorage.EtcdConfig{}
 | 
				
			||||||
 | 
							newEtcdFn := func(ns runtime.NegotiatedSerializer, storageVersion, memoryVersion unversioned.GroupVersion, etcdConfig etcdstorage.EtcdConfig) (etcdStorage storage.Interface, err error) {
 | 
				
			||||||
 | 
								actualEtcdConfig = etcdConfig
 | 
				
			||||||
 | 
								return nil, nil
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							defaultEtcdConfig := etcdstorage.EtcdConfig{
 | 
				
			||||||
 | 
								Prefix:     DefaultEtcdPathPrefix,
 | 
				
			||||||
 | 
								ServerList: defaultEtcdLocation,
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							storageFactory := NewDefaultStorageFactory(defaultEtcdConfig, api.Codecs, NewDefaultResourceEncodingConfig(), NewResourceConfig())
 | 
				
			||||||
 | 
							storageFactory.newEtcdFn = newEtcdFn
 | 
				
			||||||
 | 
							storageFactory.SetEtcdLocation(test.resource, test.servers)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							var err error
 | 
				
			||||||
 | 
							_, err = storageFactory.New(test.resource)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								t.Errorf("%d: unexpected error %v", i, err)
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if !reflect.DeepEqual(actualEtcdConfig.ServerList, test.servers) {
 | 
				
			||||||
 | 
								t.Errorf("%d: expected %v, got %v", i, test.servers, actualEtcdConfig.ServerList)
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							_, err = storageFactory.New(unversioned.GroupResource{Group: api.GroupName, Resource: "unlikely"})
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								t.Errorf("%d: unexpected error %v", i, err)
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if !reflect.DeepEqual(actualEtcdConfig.ServerList, defaultEtcdLocation) {
 | 
				
			||||||
 | 
								t.Errorf("%d: expected %v, got %v", i, defaultEtcdLocation, actualEtcdConfig.ServerList)
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -245,21 +245,15 @@ func (m *Master) InstallAPIs(c *Config) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	// Install extensions unless disabled.
 | 
						// Install extensions unless disabled.
 | 
				
			||||||
	if c.APIResourceConfigSource.AnyResourcesForVersionEnabled(extensionsapiv1beta1.SchemeGroupVersion) {
 | 
						if c.APIResourceConfigSource.AnyResourcesForVersionEnabled(extensionsapiv1beta1.SchemeGroupVersion) {
 | 
				
			||||||
		m.thirdPartyStorage = c.StorageDestinations.APIGroups[extensions.GroupName].Default
 | 
							var err error
 | 
				
			||||||
 | 
							m.thirdPartyStorage, err = c.StorageFactory.New(extensions.Resource("thirdpartyresources"))
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								glog.Fatalf("Error getting third party storage: %v", err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		m.thirdPartyResources = map[string]thirdPartyEntry{}
 | 
							m.thirdPartyResources = map[string]thirdPartyEntry{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		extensionResources := m.getExtensionResources(c)
 | 
							extensionResources := m.getExtensionResources(c)
 | 
				
			||||||
		extensionsGroupMeta := registered.GroupOrDie(extensions.GroupName)
 | 
							extensionsGroupMeta := registered.GroupOrDie(extensions.GroupName)
 | 
				
			||||||
		// Update the preferred version as per StorageVersions in the config.
 | 
					 | 
				
			||||||
		storageVersion, found := c.StorageVersions[extensionsGroupMeta.GroupVersion.Group]
 | 
					 | 
				
			||||||
		if !found {
 | 
					 | 
				
			||||||
			glog.Fatalf("Couldn't find storage version of group %v", extensionsGroupMeta.GroupVersion.Group)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		preferedGroupVersion, err := unversioned.ParseGroupVersion(storageVersion)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			glog.Fatalf("Error in parsing group version %s: %v", storageVersion, err)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		extensionsGroupMeta.GroupVersion = preferedGroupVersion
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		apiGroupInfo := genericapiserver.APIGroupInfo{
 | 
							apiGroupInfo := genericapiserver.APIGroupInfo{
 | 
				
			||||||
			GroupMeta: *extensionsGroupMeta,
 | 
								GroupMeta: *extensionsGroupMeta,
 | 
				
			||||||
@@ -390,13 +384,8 @@ func (m *Master) InstallAPIs(c *Config) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (m *Master) initV1ResourcesStorage(c *Config) {
 | 
					func (m *Master) initV1ResourcesStorage(c *Config) {
 | 
				
			||||||
	dbClient := func(resource string) storage.Interface { return c.StorageDestinations.Get("", resource) }
 | 
					 | 
				
			||||||
	restOptions := func(resource string) generic.RESTOptions {
 | 
						restOptions := func(resource string) generic.RESTOptions {
 | 
				
			||||||
		return generic.RESTOptions{
 | 
							return m.GetRESTOptionsOrDie(c, api.Resource(resource))
 | 
				
			||||||
			Storage:                 dbClient(resource),
 | 
					 | 
				
			||||||
			Decorator:               m.StorageDecorator(),
 | 
					 | 
				
			||||||
			DeleteCollectionWorkers: m.deleteCollectionWorkers,
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	podTemplateStorage := podtemplateetcd.NewREST(restOptions("podTemplates"))
 | 
						podTemplateStorage := podtemplateetcd.NewREST(restOptions("podTemplates"))
 | 
				
			||||||
@@ -426,8 +415,8 @@ func (m *Master) initV1ResourcesStorage(c *Config) {
 | 
				
			|||||||
		m.ProxyTransport,
 | 
							m.ProxyTransport,
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	serviceStorage, serviceStatusStorage := serviceetcd.NewREST(restOptions("services"))
 | 
						serviceRESTStorage, serviceStatusStorage := serviceetcd.NewREST(restOptions("services"))
 | 
				
			||||||
	m.serviceRegistry = service.NewRegistry(serviceStorage)
 | 
						m.serviceRegistry = service.NewRegistry(serviceRESTStorage)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var serviceClusterIPRegistry service.RangeRegistry
 | 
						var serviceClusterIPRegistry service.RangeRegistry
 | 
				
			||||||
	serviceClusterIPRange := m.ServiceClusterIPRange
 | 
						serviceClusterIPRange := m.ServiceClusterIPRange
 | 
				
			||||||
@@ -435,9 +424,16 @@ func (m *Master) initV1ResourcesStorage(c *Config) {
 | 
				
			|||||||
		glog.Fatalf("service clusterIPRange is nil")
 | 
							glog.Fatalf("service clusterIPRange is nil")
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						serviceStorage, err := c.StorageFactory.New(api.Resource("services"))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							glog.Fatal(err.Error())
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	serviceClusterIPAllocator := ipallocator.NewAllocatorCIDRRange(serviceClusterIPRange, func(max int, rangeSpec string) allocator.Interface {
 | 
						serviceClusterIPAllocator := ipallocator.NewAllocatorCIDRRange(serviceClusterIPRange, func(max int, rangeSpec string) allocator.Interface {
 | 
				
			||||||
		mem := allocator.NewAllocationMap(max, rangeSpec)
 | 
							mem := allocator.NewAllocationMap(max, rangeSpec)
 | 
				
			||||||
		etcd := etcdallocator.NewEtcd(mem, "/ranges/serviceips", api.Resource("serviceipallocations"), dbClient("services"))
 | 
							// TODO etcdallocator package to return a storage interface via the storageFactory
 | 
				
			||||||
 | 
							etcd := etcdallocator.NewEtcd(mem, "/ranges/serviceips", api.Resource("serviceipallocations"), serviceStorage)
 | 
				
			||||||
		serviceClusterIPRegistry = etcd
 | 
							serviceClusterIPRegistry = etcd
 | 
				
			||||||
		return etcd
 | 
							return etcd
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
@@ -446,7 +442,8 @@ func (m *Master) initV1ResourcesStorage(c *Config) {
 | 
				
			|||||||
	var serviceNodePortRegistry service.RangeRegistry
 | 
						var serviceNodePortRegistry service.RangeRegistry
 | 
				
			||||||
	serviceNodePortAllocator := portallocator.NewPortAllocatorCustom(m.ServiceNodePortRange, func(max int, rangeSpec string) allocator.Interface {
 | 
						serviceNodePortAllocator := portallocator.NewPortAllocatorCustom(m.ServiceNodePortRange, func(max int, rangeSpec string) allocator.Interface {
 | 
				
			||||||
		mem := allocator.NewAllocationMap(max, rangeSpec)
 | 
							mem := allocator.NewAllocationMap(max, rangeSpec)
 | 
				
			||||||
		etcd := etcdallocator.NewEtcd(mem, "/ranges/servicenodeports", api.Resource("servicenodeportallocations"), dbClient("services"))
 | 
							// TODO etcdallocator package to return a storage interface via the storageFactory
 | 
				
			||||||
 | 
							etcd := etcdallocator.NewEtcd(mem, "/ranges/servicenodeports", api.Resource("servicenodeportallocations"), serviceStorage)
 | 
				
			||||||
		serviceNodePortRegistry = etcd
 | 
							serviceNodePortRegistry = etcd
 | 
				
			||||||
		return etcd
 | 
							return etcd
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
@@ -541,7 +538,7 @@ func (m *Master) getServersToValidate(c *Config) map[string]apiserver.Server {
 | 
				
			|||||||
		"scheduler":          {Addr: "127.0.0.1", Port: ports.SchedulerPort, Path: "/healthz"},
 | 
							"scheduler":          {Addr: "127.0.0.1", Port: ports.SchedulerPort, Path: "/healthz"},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for ix, machine := range c.StorageDestinations.Backends() {
 | 
						for ix, machine := range c.StorageFactory.Backends() {
 | 
				
			||||||
		etcdUrl, err := url.Parse(machine)
 | 
							etcdUrl, err := url.Parse(machine)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			glog.Errorf("Failed to parse etcd url for validation: %v", err)
 | 
								glog.Errorf("Failed to parse etcd url for validation: %v", err)
 | 
				
			||||||
@@ -726,24 +723,36 @@ func (m *Master) thirdpartyapi(group, kind, version string) *apiserver.APIGroupV
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (m *Master) GetRESTOptionsOrDie(c *Config, resource unversioned.GroupResource) generic.RESTOptions {
 | 
				
			||||||
 | 
						storage, err := c.StorageFactory.New(resource)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							glog.Fatalf("Unable to find storage destination for %v, due to %v", resource, err.Error())
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return generic.RESTOptions{
 | 
				
			||||||
 | 
							Storage:                 storage,
 | 
				
			||||||
 | 
							Decorator:               m.StorageDecorator(),
 | 
				
			||||||
 | 
							DeleteCollectionWorkers: m.deleteCollectionWorkers,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// getExperimentalResources returns the resources for extensions api
 | 
					// getExperimentalResources returns the resources for extensions api
 | 
				
			||||||
func (m *Master) getExtensionResources(c *Config) map[string]rest.Storage {
 | 
					func (m *Master) getExtensionResources(c *Config) map[string]rest.Storage {
 | 
				
			||||||
	restOptions := func(resource string) generic.RESTOptions {
 | 
						restOptions := func(resource string) generic.RESTOptions {
 | 
				
			||||||
		return generic.RESTOptions{
 | 
							return m.GetRESTOptionsOrDie(c, extensions.Resource(resource))
 | 
				
			||||||
			Storage:                 c.StorageDestinations.Get(extensions.GroupName, resource),
 | 
					 | 
				
			||||||
			Decorator:               m.StorageDecorator(),
 | 
					 | 
				
			||||||
			DeleteCollectionWorkers: m.deleteCollectionWorkers,
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// TODO update when we support more than one version of this group
 | 
						// TODO update when we support more than one version of this group
 | 
				
			||||||
	version := extensionsapiv1beta1.SchemeGroupVersion
 | 
						version := extensionsapiv1beta1.SchemeGroupVersion
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	storage := map[string]rest.Storage{}
 | 
						storage := map[string]rest.Storage{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if c.APIResourceConfigSource.ResourceEnabled(version.WithResource("horizontalpodautoscalers")) {
 | 
						if c.APIResourceConfigSource.ResourceEnabled(version.WithResource("horizontalpodautoscalers")) {
 | 
				
			||||||
		m.constructHPAResources(c, storage)
 | 
							hpaStorage, hpaStatusStorage := horizontalpodautoscaleretcd.NewREST(restOptions("horizontalpodautoscalers"))
 | 
				
			||||||
		controllerStorage := expcontrolleretcd.NewStorage(
 | 
							storage["horizontalpodautoscalers"] = hpaStorage
 | 
				
			||||||
			generic.RESTOptions{Storage: c.StorageDestinations.Get("", "replicationControllers"), Decorator: m.StorageDecorator(), DeleteCollectionWorkers: m.deleteCollectionWorkers})
 | 
							storage["horizontalpodautoscalers/status"] = hpaStatusStorage
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							controllerStorage := expcontrolleretcd.NewStorage(m.GetRESTOptionsOrDie(c, api.Resource("replicationControllers")))
 | 
				
			||||||
		storage["replicationcontrollers"] = controllerStorage.ReplicationController
 | 
							storage["replicationcontrollers"] = controllerStorage.ReplicationController
 | 
				
			||||||
		storage["replicationcontrollers/scale"] = controllerStorage.Scale
 | 
							storage["replicationcontrollers/scale"] = controllerStorage.Scale
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -776,7 +785,9 @@ func (m *Master) getExtensionResources(c *Config) map[string]rest.Storage {
 | 
				
			|||||||
		storage["deployments/scale"] = deploymentStorage.Scale
 | 
							storage["deployments/scale"] = deploymentStorage.Scale
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if c.APIResourceConfigSource.ResourceEnabled(version.WithResource("jobs")) {
 | 
						if c.APIResourceConfigSource.ResourceEnabled(version.WithResource("jobs")) {
 | 
				
			||||||
		m.constructJobResources(c, storage)
 | 
							jobsStorage, jobsStatusStorage := jobetcd.NewREST(restOptions("jobs"))
 | 
				
			||||||
 | 
							storage["jobs"] = jobsStorage
 | 
				
			||||||
 | 
							storage["jobs/status"] = jobsStatusStorage
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	ingressStorage, ingressStatusStorage := ingressetcd.NewREST(restOptions("ingresses"))
 | 
						ingressStorage, ingressStatusStorage := ingressetcd.NewREST(restOptions("ingresses"))
 | 
				
			||||||
	if c.APIResourceConfigSource.ResourceEnabled(version.WithResource("ingresses")) {
 | 
						if c.APIResourceConfigSource.ResourceEnabled(version.WithResource("ingresses")) {
 | 
				
			||||||
@@ -797,25 +808,6 @@ func (m *Master) getExtensionResources(c *Config) map[string]rest.Storage {
 | 
				
			|||||||
	return 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.
 | 
					 | 
				
			||||||
	restOptions := func(resource string) generic.RESTOptions {
 | 
					 | 
				
			||||||
		return generic.RESTOptions{
 | 
					 | 
				
			||||||
			Storage:                 c.StorageDestinations.Search([]string{autoscaling.GroupName, extensions.GroupName}, resource),
 | 
					 | 
				
			||||||
			Decorator:               m.StorageDecorator(),
 | 
					 | 
				
			||||||
			DeleteCollectionWorkers: m.deleteCollectionWorkers,
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	autoscalerStorage, autoscalerStatusStorage := horizontalpodautoscaleretcd.NewREST(restOptions("horizontalpodautoscalers"))
 | 
					 | 
				
			||||||
	restStorage["horizontalpodautoscalers"] = autoscalerStorage
 | 
					 | 
				
			||||||
	restStorage["horizontalpodautoscalers/status"] = autoscalerStatusStorage
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// getAutoscalingResources returns the resources for autoscaling api
 | 
					// getAutoscalingResources returns the resources for autoscaling api
 | 
				
			||||||
func (m *Master) getAutoscalingResources(c *Config) map[string]rest.Storage {
 | 
					func (m *Master) getAutoscalingResources(c *Config) map[string]rest.Storage {
 | 
				
			||||||
	// TODO update when we support more than one version of this group
 | 
						// TODO update when we support more than one version of this group
 | 
				
			||||||
@@ -823,30 +815,13 @@ func (m *Master) getAutoscalingResources(c *Config) map[string]rest.Storage {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	storage := map[string]rest.Storage{}
 | 
						storage := map[string]rest.Storage{}
 | 
				
			||||||
	if c.APIResourceConfigSource.ResourceEnabled(version.WithResource("horizontalpodautoscalers")) {
 | 
						if c.APIResourceConfigSource.ResourceEnabled(version.WithResource("horizontalpodautoscalers")) {
 | 
				
			||||||
		m.constructHPAResources(c, storage)
 | 
							hpaStorage, hpaStatusStorage := horizontalpodautoscaleretcd.NewREST(m.GetRESTOptionsOrDie(c, autoscaling.Resource("horizontalpodautoscalers")))
 | 
				
			||||||
 | 
							storage["horizontalpodautoscalers"] = hpaStorage
 | 
				
			||||||
 | 
							storage["horizontalpodautoscalers/status"] = hpaStatusStorage
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return storage
 | 
						return storage
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// constructJobResources makes Job resources and adds them to the storage map.
 | 
					 | 
				
			||||||
// They're installed in both batch and extensions. It's assumed that you've
 | 
					 | 
				
			||||||
// already done the check that they should be on.
 | 
					 | 
				
			||||||
func (m *Master) constructJobResources(c *Config, restStorage map[string]rest.Storage) {
 | 
					 | 
				
			||||||
	// Note that job's storage settings are changed by changing the batch
 | 
					 | 
				
			||||||
	// group. Clearly we want all jobs to be stored in the same place no
 | 
					 | 
				
			||||||
	// matter where they're accessed from.
 | 
					 | 
				
			||||||
	restOptions := func(resource string) generic.RESTOptions {
 | 
					 | 
				
			||||||
		return generic.RESTOptions{
 | 
					 | 
				
			||||||
			Storage:                 c.StorageDestinations.Search([]string{batch.GroupName, extensions.GroupName}, resource),
 | 
					 | 
				
			||||||
			Decorator:               m.StorageDecorator(),
 | 
					 | 
				
			||||||
			DeleteCollectionWorkers: m.deleteCollectionWorkers,
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	jobStorage, jobStatusStorage := jobetcd.NewREST(restOptions("jobs"))
 | 
					 | 
				
			||||||
	restStorage["jobs"] = jobStorage
 | 
					 | 
				
			||||||
	restStorage["jobs/status"] = jobStatusStorage
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// getBatchResources returns the resources for batch api
 | 
					// getBatchResources returns the resources for batch api
 | 
				
			||||||
func (m *Master) getBatchResources(c *Config) map[string]rest.Storage {
 | 
					func (m *Master) getBatchResources(c *Config) map[string]rest.Storage {
 | 
				
			||||||
	// TODO update when we support more than one version of this group
 | 
						// TODO update when we support more than one version of this group
 | 
				
			||||||
@@ -854,26 +829,21 @@ func (m *Master) getBatchResources(c *Config) map[string]rest.Storage {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	storage := map[string]rest.Storage{}
 | 
						storage := map[string]rest.Storage{}
 | 
				
			||||||
	if c.APIResourceConfigSource.ResourceEnabled(version.WithResource("jobs")) {
 | 
						if c.APIResourceConfigSource.ResourceEnabled(version.WithResource("jobs")) {
 | 
				
			||||||
		m.constructJobResources(c, storage)
 | 
							jobsStorage, jobsStatusStorage := jobetcd.NewREST(m.GetRESTOptionsOrDie(c, batch.Resource("jobs")))
 | 
				
			||||||
 | 
							storage["jobs"] = jobsStorage
 | 
				
			||||||
 | 
							storage["jobs/status"] = jobsStatusStorage
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return storage
 | 
						return storage
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// getPetSetResources returns the resources for batch api
 | 
					// getPetSetResources returns the resources for apps api
 | 
				
			||||||
func (m *Master) getAppsResources(c *Config) map[string]rest.Storage {
 | 
					func (m *Master) getAppsResources(c *Config) map[string]rest.Storage {
 | 
				
			||||||
	// TODO update when we support more than one version of this group
 | 
						// TODO update when we support more than one version of this group
 | 
				
			||||||
	version := appsapi.SchemeGroupVersion
 | 
						version := appsapi.SchemeGroupVersion
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	storage := map[string]rest.Storage{}
 | 
						storage := map[string]rest.Storage{}
 | 
				
			||||||
	if c.APIResourceConfigSource.ResourceEnabled(version.WithResource("petsets")) {
 | 
						if c.APIResourceConfigSource.ResourceEnabled(version.WithResource("petsets")) {
 | 
				
			||||||
		restOptions := func(resource string) generic.RESTOptions {
 | 
							petsetStorage, petsetStatusStorage := petsetetcd.NewREST(m.GetRESTOptionsOrDie(c, apps.Resource("petsets")))
 | 
				
			||||||
			return generic.RESTOptions{
 | 
					 | 
				
			||||||
				Storage:                 c.StorageDestinations.Get(apps.GroupName, resource),
 | 
					 | 
				
			||||||
				Decorator:               m.StorageDecorator(),
 | 
					 | 
				
			||||||
				DeleteCollectionWorkers: m.deleteCollectionWorkers,
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		petsetStorage, petsetStatusStorage := petsetetcd.NewREST(restOptions("petsets"))
 | 
					 | 
				
			||||||
		storage["petsets"] = petsetStorage
 | 
							storage["petsets"] = petsetStorage
 | 
				
			||||||
		storage["petsets/status"] = petsetStatusStorage
 | 
							storage["petsets/status"] = petsetStatusStorage
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -36,8 +36,8 @@ import (
 | 
				
			|||||||
	utilnet "k8s.io/kubernetes/pkg/util/net"
 | 
						utilnet "k8s.io/kubernetes/pkg/util/net"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/util/sets"
 | 
						"k8s.io/kubernetes/pkg/util/sets"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	apiutil "k8s.io/kubernetes/pkg/api/util"
 | 
					 | 
				
			||||||
	apiv1 "k8s.io/kubernetes/pkg/api/v1"
 | 
						apiv1 "k8s.io/kubernetes/pkg/api/v1"
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/apimachinery/registered"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/apis/apps"
 | 
						"k8s.io/kubernetes/pkg/apis/apps"
 | 
				
			||||||
	appsapi "k8s.io/kubernetes/pkg/apis/apps"
 | 
						appsapi "k8s.io/kubernetes/pkg/apis/apps"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/apis/autoscaling"
 | 
						"k8s.io/kubernetes/pkg/apis/autoscaling"
 | 
				
			||||||
@@ -72,26 +72,27 @@ func setUp(t *testing.T) (*Master, *etcdtesting.EtcdTestServer, Config, *assert.
 | 
				
			|||||||
	config := Config{
 | 
						config := Config{
 | 
				
			||||||
		Config: &genericapiserver.Config{},
 | 
							Config: &genericapiserver.Config{},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	storageVersions := make(map[string]string)
 | 
					 | 
				
			||||||
	storageDestinations := genericapiserver.NewStorageDestinations()
 | 
					 | 
				
			||||||
	storageDestinations.AddAPIGroup(
 | 
					 | 
				
			||||||
		api.GroupName, etcdstorage.NewEtcdStorage(server.Client, testapi.Default.Codec(), etcdtest.PathPrefix(), false, etcdtest.DeserializationCacheSize))
 | 
					 | 
				
			||||||
	storageDestinations.AddAPIGroup(
 | 
					 | 
				
			||||||
		autoscaling.GroupName, etcdstorage.NewEtcdStorage(server.Client, testapi.Autoscaling.Codec(), etcdtest.PathPrefix(), false, etcdtest.DeserializationCacheSize))
 | 
					 | 
				
			||||||
	storageDestinations.AddAPIGroup(
 | 
					 | 
				
			||||||
		batch.GroupName, etcdstorage.NewEtcdStorage(server.Client, testapi.Batch.Codec(), etcdtest.PathPrefix(), false, etcdtest.DeserializationCacheSize))
 | 
					 | 
				
			||||||
	storageDestinations.AddAPIGroup(
 | 
					 | 
				
			||||||
		apps.GroupName, etcdstorage.NewEtcdStorage(server.Client, testapi.Apps.Codec(), etcdtest.PathPrefix(), false, etcdtest.DeserializationCacheSize))
 | 
					 | 
				
			||||||
	storageDestinations.AddAPIGroup(
 | 
					 | 
				
			||||||
		extensions.GroupName, etcdstorage.NewEtcdStorage(server.Client, testapi.Extensions.Codec(), etcdtest.PathPrefix(), false, etcdtest.DeserializationCacheSize))
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	config.StorageDestinations = storageDestinations
 | 
						etcdConfig := etcdstorage.EtcdConfig{
 | 
				
			||||||
	storageVersions[api.GroupName] = testapi.Default.GroupVersion().String()
 | 
							Prefix:   etcdtest.PathPrefix(),
 | 
				
			||||||
	storageVersions[autoscaling.GroupName] = testapi.Autoscaling.GroupVersion().String()
 | 
							CAFile:   server.CAFile,
 | 
				
			||||||
	storageVersions[batch.GroupName] = testapi.Batch.GroupVersion().String()
 | 
							KeyFile:  server.KeyFile,
 | 
				
			||||||
	storageVersions[apps.GroupName] = testapi.Apps.GroupVersion().String()
 | 
							CertFile: server.CertFile,
 | 
				
			||||||
	storageVersions[extensions.GroupName] = testapi.Extensions.GroupVersion().String()
 | 
						}
 | 
				
			||||||
	config.StorageVersions = storageVersions
 | 
						for _, url := range server.ClientURLs {
 | 
				
			||||||
 | 
							etcdConfig.ServerList = append(etcdConfig.ServerList, url.String())
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						resourceEncoding := genericapiserver.NewDefaultResourceEncodingConfig()
 | 
				
			||||||
 | 
						resourceEncoding.SetVersionEncoding(api.GroupName, *testapi.Default.GroupVersion(), unversioned.GroupVersion{Group: api.GroupName, Version: runtime.APIVersionInternal})
 | 
				
			||||||
 | 
						resourceEncoding.SetVersionEncoding(autoscaling.GroupName, *testapi.Autoscaling.GroupVersion(), unversioned.GroupVersion{Group: autoscaling.GroupName, Version: runtime.APIVersionInternal})
 | 
				
			||||||
 | 
						resourceEncoding.SetVersionEncoding(batch.GroupName, *testapi.Batch.GroupVersion(), unversioned.GroupVersion{Group: batch.GroupName, Version: runtime.APIVersionInternal})
 | 
				
			||||||
 | 
						resourceEncoding.SetVersionEncoding(apps.GroupName, *testapi.Apps.GroupVersion(), unversioned.GroupVersion{Group: apps.GroupName, Version: runtime.APIVersionInternal})
 | 
				
			||||||
 | 
						resourceEncoding.SetVersionEncoding(extensions.GroupName, *testapi.Extensions.GroupVersion(), unversioned.GroupVersion{Group: extensions.GroupName, Version: runtime.APIVersionInternal})
 | 
				
			||||||
 | 
						storageFactory := genericapiserver.NewDefaultStorageFactory(etcdConfig, api.Codecs, resourceEncoding, DefaultAPIResourceConfigSource())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						config.StorageFactory = storageFactory
 | 
				
			||||||
 | 
						config.APIResourceConfigSource = DefaultAPIResourceConfigSource()
 | 
				
			||||||
	config.PublicAddress = net.ParseIP("192.168.10.4")
 | 
						config.PublicAddress = net.ParseIP("192.168.10.4")
 | 
				
			||||||
	config.Serializer = api.Codecs
 | 
						config.Serializer = api.Codecs
 | 
				
			||||||
	config.KubeletClient = client.FakeKubeletClient{}
 | 
						config.KubeletClient = client.FakeKubeletClient{}
 | 
				
			||||||
@@ -398,7 +399,7 @@ func TestAPIVersionOfDiscoveryEndpoints(t *testing.T) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestDiscoveryAtAPIS(t *testing.T) {
 | 
					func TestDiscoveryAtAPIS(t *testing.T) {
 | 
				
			||||||
	master, etcdserver, config, assert := newLimitedMaster(t)
 | 
						master, etcdserver, _, assert := newLimitedMaster(t)
 | 
				
			||||||
	defer etcdserver.Terminate(t)
 | 
						defer etcdserver.Terminate(t)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	server := httptest.NewServer(master.HandlerContainer.ServeMux)
 | 
						server := httptest.NewServer(master.HandlerContainer.ServeMux)
 | 
				
			||||||
@@ -444,20 +445,20 @@ func TestDiscoveryAtAPIS(t *testing.T) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	expectPreferredVersion := map[string]unversioned.GroupVersionForDiscovery{
 | 
						expectPreferredVersion := map[string]unversioned.GroupVersionForDiscovery{
 | 
				
			||||||
		autoscaling.GroupName: {
 | 
							autoscaling.GroupName: {
 | 
				
			||||||
			GroupVersion: config.StorageVersions[autoscaling.GroupName],
 | 
								GroupVersion: registered.GroupOrDie(autoscaling.GroupName).GroupVersion.String(),
 | 
				
			||||||
			Version:      apiutil.GetVersion(config.StorageVersions[autoscaling.GroupName]),
 | 
								Version:      registered.GroupOrDie(autoscaling.GroupName).GroupVersion.Version,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		batch.GroupName: {
 | 
							batch.GroupName: {
 | 
				
			||||||
			GroupVersion: config.StorageVersions[batch.GroupName],
 | 
								GroupVersion: registered.GroupOrDie(batch.GroupName).GroupVersion.String(),
 | 
				
			||||||
			Version:      apiutil.GetVersion(config.StorageVersions[batch.GroupName]),
 | 
								Version:      registered.GroupOrDie(batch.GroupName).GroupVersion.Version,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		apps.GroupName: {
 | 
							apps.GroupName: {
 | 
				
			||||||
			GroupVersion: config.StorageVersions[apps.GroupName],
 | 
								GroupVersion: registered.GroupOrDie(apps.GroupName).GroupVersion.String(),
 | 
				
			||||||
			Version:      apiutil.GetVersion(config.StorageVersions[apps.GroupName]),
 | 
								Version:      registered.GroupOrDie(apps.GroupName).GroupVersion.Version,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		extensions.GroupName: {
 | 
							extensions.GroupName: {
 | 
				
			||||||
			GroupVersion: config.StorageVersions[extensions.GroupName],
 | 
								GroupVersion: registered.GroupOrDie(extensions.GroupName).GroupVersion.String(),
 | 
				
			||||||
			Version:      apiutil.GetVersion(config.StorageVersions[extensions.GroupName]),
 | 
								Version:      registered.GroupOrDie(extensions.GroupName).GroupVersion.Version,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -231,6 +231,7 @@ func NewEtcdTestClientServer(t *testing.T) *EtcdTestServer {
 | 
				
			|||||||
		t.Fatalf("Failed to start etcd server error=%v", err)
 | 
							t.Fatalf("Failed to start etcd server error=%v", err)
 | 
				
			||||||
		return nil
 | 
							return nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	cfg := etcd.Config{
 | 
						cfg := etcd.Config{
 | 
				
			||||||
		Endpoints: server.ClientURLs.StringSlice(),
 | 
							Endpoints: server.ClientURLs.StringSlice(),
 | 
				
			||||||
		Transport: newHttpTransport(t, server.CertFile, server.KeyFile, server.CAFile),
 | 
							Transport: newHttpTransport(t, server.CertFile, server.KeyFile, server.CAFile),
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -29,6 +29,7 @@ import (
 | 
				
			|||||||
	"github.com/golang/glog"
 | 
						"github.com/golang/glog"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/api"
 | 
						"k8s.io/kubernetes/pkg/api"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/api/testapi"
 | 
						"k8s.io/kubernetes/pkg/api/testapi"
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/api/unversioned"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/apis/apps"
 | 
						"k8s.io/kubernetes/pkg/apis/apps"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/apis/autoscaling"
 | 
						"k8s.io/kubernetes/pkg/apis/autoscaling"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/apis/batch"
 | 
						"k8s.io/kubernetes/pkg/apis/batch"
 | 
				
			||||||
@@ -149,31 +150,33 @@ func startMasterOrDie(masterConfig *master.Config) (*master.Master, *httptest.Se
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// Returns a basic master config.
 | 
					// Returns a basic master config.
 | 
				
			||||||
func NewMasterConfig() *master.Config {
 | 
					func NewMasterConfig() *master.Config {
 | 
				
			||||||
	etcdClient := NewEtcdClient()
 | 
						etcdConfig := etcdstorage.EtcdConfig{
 | 
				
			||||||
	storageVersions := make(map[string]string)
 | 
							ServerList: []string{"http://127.0.0.1:4001"},
 | 
				
			||||||
 | 
							Prefix:     etcdtest.PathPrefix(),
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	etcdStorage := etcdstorage.NewEtcdStorage(etcdClient, testapi.Default.Codec(), etcdtest.PathPrefix(), false, etcdtest.DeserializationCacheSize)
 | 
						negotiatedSerializer := NewSingleContentTypeSerializer(api.Scheme, testapi.Default.Codec(), "application/json")
 | 
				
			||||||
	storageVersions[api.GroupName] = testapi.Default.GroupVersion().String()
 | 
					 | 
				
			||||||
	autoscalingEtcdStorage := NewAutoscalingEtcdStorage(etcdClient)
 | 
					 | 
				
			||||||
	storageVersions[autoscaling.GroupName] = testapi.Autoscaling.GroupVersion().String()
 | 
					 | 
				
			||||||
	batchEtcdStorage := NewBatchEtcdStorage(etcdClient)
 | 
					 | 
				
			||||||
	storageVersions[batch.GroupName] = testapi.Batch.GroupVersion().String()
 | 
					 | 
				
			||||||
	appsEtcdStorage := NewAppsEtcdStorage(etcdClient)
 | 
					 | 
				
			||||||
	storageVersions[apps.GroupName] = testapi.Apps.GroupVersion().String()
 | 
					 | 
				
			||||||
	expEtcdStorage := NewExtensionsEtcdStorage(etcdClient)
 | 
					 | 
				
			||||||
	storageVersions[extensions.GroupName] = testapi.Extensions.GroupVersion().String()
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	storageDestinations := genericapiserver.NewStorageDestinations()
 | 
						storageFactory := genericapiserver.NewDefaultStorageFactory(etcdConfig, negotiatedSerializer, genericapiserver.NewDefaultResourceEncodingConfig(), master.DefaultAPIResourceConfigSource())
 | 
				
			||||||
	storageDestinations.AddAPIGroup(api.GroupName, etcdStorage)
 | 
						storageFactory.SetSerializer(
 | 
				
			||||||
	storageDestinations.AddAPIGroup(autoscaling.GroupName, autoscalingEtcdStorage)
 | 
							unversioned.GroupResource{Group: api.GroupName, Resource: genericapiserver.AllResources},
 | 
				
			||||||
	storageDestinations.AddAPIGroup(batch.GroupName, batchEtcdStorage)
 | 
							NewSingleContentTypeSerializer(api.Scheme, testapi.Default.Codec(), "application/json"))
 | 
				
			||||||
	storageDestinations.AddAPIGroup(apps.GroupName, appsEtcdStorage)
 | 
						storageFactory.SetSerializer(
 | 
				
			||||||
	storageDestinations.AddAPIGroup(extensions.GroupName, expEtcdStorage)
 | 
							unversioned.GroupResource{Group: autoscaling.GroupName, Resource: genericapiserver.AllResources},
 | 
				
			||||||
 | 
							NewSingleContentTypeSerializer(api.Scheme, testapi.Autoscaling.Codec(), "application/json"))
 | 
				
			||||||
 | 
						storageFactory.SetSerializer(
 | 
				
			||||||
 | 
							unversioned.GroupResource{Group: batch.GroupName, Resource: genericapiserver.AllResources},
 | 
				
			||||||
 | 
							NewSingleContentTypeSerializer(api.Scheme, testapi.Batch.Codec(), "application/json"))
 | 
				
			||||||
 | 
						storageFactory.SetSerializer(
 | 
				
			||||||
 | 
							unversioned.GroupResource{Group: apps.GroupName, Resource: genericapiserver.AllResources},
 | 
				
			||||||
 | 
							NewSingleContentTypeSerializer(api.Scheme, testapi.Apps.Codec(), "application/json"))
 | 
				
			||||||
 | 
						storageFactory.SetSerializer(
 | 
				
			||||||
 | 
							unversioned.GroupResource{Group: extensions.GroupName, Resource: genericapiserver.AllResources},
 | 
				
			||||||
 | 
							NewSingleContentTypeSerializer(api.Scheme, testapi.Extensions.Codec(), "application/json"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return &master.Config{
 | 
						return &master.Config{
 | 
				
			||||||
		Config: &genericapiserver.Config{
 | 
							Config: &genericapiserver.Config{
 | 
				
			||||||
			StorageDestinations:     storageDestinations,
 | 
								StorageFactory:          storageFactory,
 | 
				
			||||||
			StorageVersions:         storageVersions,
 | 
					 | 
				
			||||||
			APIResourceConfigSource: master.DefaultAPIResourceConfigSource(),
 | 
								APIResourceConfigSource: master.DefaultAPIResourceConfigSource(),
 | 
				
			||||||
			APIPrefix:               "/api",
 | 
								APIPrefix:               "/api",
 | 
				
			||||||
			APIGroupPrefix:          "/apis",
 | 
								APIGroupPrefix:          "/apis",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -37,6 +37,8 @@ type wrappedSerializer struct {
 | 
				
			|||||||
	contentType string
 | 
						contentType string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var _ runtime.NegotiatedSerializer = &wrappedSerializer{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (s *wrappedSerializer) SupportedMediaTypes() []string {
 | 
					func (s *wrappedSerializer) SupportedMediaTypes() []string {
 | 
				
			||||||
	return []string{s.contentType}
 | 
						return []string{s.contentType}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -49,9 +51,9 @@ func (s *wrappedSerializer) SerializerForMediaType(mediaType string, options map
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (s *wrappedSerializer) EncoderForVersion(serializer runtime.Serializer, gv unversioned.GroupVersion) runtime.Encoder {
 | 
					func (s *wrappedSerializer) EncoderForVersion(serializer runtime.Serializer, gv unversioned.GroupVersion) runtime.Encoder {
 | 
				
			||||||
	return versioning.NewCodec(s.serializer, s.scheme, s.scheme, s.scheme, runtime.ObjectTyperToTyper(s.scheme), []unversioned.GroupVersion{gv}, nil)
 | 
						return versioning.NewCodec(s.serializer, s.serializer, s.scheme, s.scheme, s.scheme, runtime.ObjectTyperToTyper(s.scheme), []unversioned.GroupVersion{gv}, nil)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (s *wrappedSerializer) DecoderToVersion(serializer runtime.Serializer, gv unversioned.GroupVersion) runtime.Decoder {
 | 
					func (s *wrappedSerializer) DecoderToVersion(serializer runtime.Serializer, gv unversioned.GroupVersion) runtime.Decoder {
 | 
				
			||||||
	return versioning.NewCodec(s.serializer, s.scheme, s.scheme, s.scheme, runtime.ObjectTyperToTyper(s.scheme), nil, []unversioned.GroupVersion{gv})
 | 
						return versioning.NewCodec(s.serializer, s.serializer, s.scheme, s.scheme, s.scheme, runtime.ObjectTyperToTyper(s.scheme), nil, []unversioned.GroupVersion{gv})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user