コード例 #1
0
func (f *ring1Factory) Object() (meta.RESTMapper, runtime.ObjectTyper) {
	mapper := api.Registry.RESTMapper()
	discoveryClient, err := f.clientAccessFactory.DiscoveryClient()
	if err == nil {
		mapper = meta.FirstHitRESTMapper{
			MultiRESTMapper: meta.MultiRESTMapper{
				discovery.NewDeferredDiscoveryRESTMapper(discoveryClient, api.Registry.InterfacesFor),
				api.Registry.RESTMapper(), // hardcoded fall back
			},
		}
	}

	// wrap with shortcuts
	mapper = NewShortcutExpander(mapper, discoveryClient)

	// wrap with output preferences
	cfg, err := f.clientAccessFactory.ClientConfigForVersion(nil)
	checkErrWithPrefix("failed to get client config: ", err)
	cmdApiVersion := schema.GroupVersion{}
	if cfg.GroupVersion != nil {
		cmdApiVersion = *cfg.GroupVersion
	}
	mapper = kubectl.OutputVersionMapper{RESTMapper: mapper, OutputVersions: []schema.GroupVersion{cmdApiVersion}}
	return mapper, api.Scheme
}
コード例 #2
0
func (f *factory) UnstructuredObject() (meta.RESTMapper, runtime.ObjectTyper, error) {
	discoveryClient, err := f.DiscoveryClient()
	if err != nil {
		return nil, nil, err
	}
	groupResources, err := discovery.GetAPIGroupResources(discoveryClient)
	if err != nil && !discoveryClient.Fresh() {
		discoveryClient.Invalidate()
		groupResources, err = discovery.GetAPIGroupResources(discoveryClient)
	}
	if err != nil {
		return nil, nil, err
	}

	// Register unknown APIs as third party for now to make
	// validation happy. TODO perhaps make a dynamic schema
	// validator to avoid this.
	for _, group := range groupResources {
		for _, version := range group.Group.Versions {
			gv := unversioned.GroupVersion{Group: group.Group.Name, Version: version.Version}
			if !registered.IsRegisteredVersion(gv) {
				registered.AddThirdPartyAPIGroupVersions(gv)
			}
		}
	}

	mapper := discovery.NewDeferredDiscoveryRESTMapper(discoveryClient, meta.InterfacesForUnstructured)
	typer := discovery.NewUnstructuredObjectTyper(groupResources)
	return NewShortcutExpander(mapper, discoveryClient), typer, nil
}
コード例 #3
0
ファイル: factory.go プロジェクト: oszi/kubernetes
func (f *factory) Object() (meta.RESTMapper, runtime.ObjectTyper) {
	cfg, err := f.clientConfig.ClientConfig()
	checkErrWithPrefix("failed to get client config: ", err)
	cmdApiVersion := unversioned.GroupVersion{}
	if cfg.GroupVersion != nil {
		cmdApiVersion = *cfg.GroupVersion
	}

	mapper := registered.RESTMapper()
	discoveryClient, err := discovery.NewDiscoveryClientForConfig(cfg)
	if err == nil {
		mapper = meta.FirstHitRESTMapper{
			MultiRESTMapper: meta.MultiRESTMapper{
				discovery.NewDeferredDiscoveryRESTMapper(discoveryClient, registered.InterfacesFor),
				registered.RESTMapper(), // hardcoded fall back
			},
		}
	}

	// wrap with shortcuts
	mapper = NewShortcutExpander(mapper, discoveryClient)
	// wrap with output preferences
	mapper = kubectl.OutputVersionMapper{RESTMapper: mapper, OutputVersions: []unversioned.GroupVersion{cmdApiVersion}}
	return mapper, api.Scheme
}
コード例 #4
0
func (f *ring1Factory) UnstructuredObject() (meta.RESTMapper, runtime.ObjectTyper, error) {
	discoveryClient, err := f.clientAccessFactory.DiscoveryClient()
	if err != nil {
		return nil, nil, err
	}
	groupResources, err := discovery.GetAPIGroupResources(discoveryClient)
	if err != nil && !discoveryClient.Fresh() {
		discoveryClient.Invalidate()
		groupResources, err = discovery.GetAPIGroupResources(discoveryClient)
	}
	if err != nil {
		return nil, nil, err
	}

	mapper := discovery.NewDeferredDiscoveryRESTMapper(discoveryClient, meta.InterfacesForUnstructured)
	typer := discovery.NewUnstructuredObjectTyper(groupResources)
	return NewShortcutExpander(mapper, discoveryClient), typer, nil
}
コード例 #5
0
ファイル: factory.go プロジェクト: Random-Liu/kubernetes
func (f *factory) Object() (meta.RESTMapper, runtime.ObjectTyper) {
	cfg, err := f.clientConfig.ClientConfig()
	checkErrWithPrefix("failed to get client config: ", err)
	cmdApiVersion := unversioned.GroupVersion{}
	if cfg.GroupVersion != nil {
		cmdApiVersion = *cfg.GroupVersion
	}

	mapper := registered.RESTMapper()
	discoveryClient, err := discovery.NewDiscoveryClientForConfig(cfg)
	if err == nil {
		// register third party resources with the api machinery groups.  This probably should be done, but
		// its consistent with old code, so we'll start with it.
		if err := registerThirdPartyResources(discoveryClient); err != nil {
			fmt.Fprintf(os.Stderr, "Unable to register third party resources: %v\n", err)
		}
		// ThirdPartyResourceData is special.  It's not discoverable, but needed for thirdparty resource listing
		// TODO eliminate this once we're truly generic.
		thirdPartyResourceDataMapper := meta.NewDefaultRESTMapper([]unversioned.GroupVersion{extensionsv1beta1.SchemeGroupVersion}, registered.InterfacesFor)
		thirdPartyResourceDataMapper.Add(extensionsv1beta1.SchemeGroupVersion.WithKind("ThirdPartyResourceData"), meta.RESTScopeNamespace)

		mapper = meta.FirstHitRESTMapper{
			MultiRESTMapper: meta.MultiRESTMapper{
				discovery.NewDeferredDiscoveryRESTMapper(discoveryClient, registered.InterfacesFor),
				thirdPartyResourceDataMapper, // needed for TPR printing
				registered.RESTMapper(),      // hardcoded fall back
			},
		}
	}

	// wrap with shortcuts
	mapper = NewShortcutExpander(mapper, discoveryClient)
	// wrap with output preferences
	mapper = kubectl.OutputVersionMapper{RESTMapper: mapper, OutputVersions: []unversioned.GroupVersion{cmdApiVersion}}
	return mapper, api.Scheme
}
コード例 #6
0
ファイル: factory.go プロジェクト: PeterLamar/kubernetes
// NewFactory creates a factory with the default Kubernetes resources defined
// if optionalClientConfig is nil, then flags will be bound to a new clientcmd.ClientConfig.
// if optionalClientConfig is not nil, then this factory will make use of it.
func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory {
	flags := pflag.NewFlagSet("", pflag.ContinueOnError)
	flags.SetNormalizeFunc(utilflag.WarnWordSepNormalizeFunc) // Warn for "_" flags

	clientConfig := optionalClientConfig
	if optionalClientConfig == nil {
		clientConfig = DefaultClientConfig(flags)
	}

	clients := NewClientCache(clientConfig)

	return &Factory{
		clients: clients,
		flags:   flags,

		Object: func() (meta.RESTMapper, runtime.ObjectTyper) {
			cfg, err := clientConfig.ClientConfig()
			checkErrWithPrefix("failed to get client config: ", err)
			cmdApiVersion := unversioned.GroupVersion{}
			if cfg.GroupVersion != nil {
				cmdApiVersion = *cfg.GroupVersion
			}

			mapper := registered.RESTMapper()
			discoveryClient, err := discovery.NewDiscoveryClientForConfig(cfg)
			// if we can find the server version and it's current enough to have discovery information, use it.  Otherwise,
			// fallback to our hardcoded list
			if err == nil {
				if serverVersion, err := discoveryClient.ServerVersion(); err == nil && useDiscoveryRESTMapper(serverVersion.GitVersion) {
					// register third party resources with the api machinery groups.  This probably should be done, but
					// its consistent with old code, so we'll start with it.
					if err := registerThirdPartyResources(discoveryClient); err != nil {
						fmt.Fprintf(os.Stderr, "Unable to register third party resources: %v\n", err)
					}
					// ThirdPartyResourceData is special.  It's not discoverable, but needed for thirdparty resource listing
					// TODO eliminate this once we're truly generic.
					thirdPartyResourceDataMapper := meta.NewDefaultRESTMapper([]unversioned.GroupVersion{extensionsv1beta1.SchemeGroupVersion}, registered.InterfacesFor)
					thirdPartyResourceDataMapper.Add(extensionsv1beta1.SchemeGroupVersion.WithKind("ThirdPartyResourceData"), meta.RESTScopeNamespace)

					mapper = meta.FirstHitRESTMapper{
						MultiRESTMapper: meta.MultiRESTMapper{
							discovery.NewDeferredDiscoveryRESTMapper(discoveryClient, registered.InterfacesFor),
							thirdPartyResourceDataMapper,
						},
					}
				}
			}

			// wrap with shortcuts
			mapper = NewShortcutExpander(mapper, discoveryClient)
			// wrap with output preferences
			mapper = kubectl.OutputVersionMapper{RESTMapper: mapper, OutputVersions: []unversioned.GroupVersion{cmdApiVersion}}
			return mapper, api.Scheme
		},
		UnstructuredObject: func() (meta.RESTMapper, runtime.ObjectTyper, error) {
			cfg, err := clients.ClientConfigForVersion(nil)
			if err != nil {
				return nil, nil, err
			}

			dc, err := discovery.NewDiscoveryClientForConfig(cfg)
			if err != nil {
				return nil, nil, err
			}

			groupResources, err := discovery.GetAPIGroupResources(dc)
			if err != nil {
				return nil, nil, err
			}

			// Register unknown APIs as third party for now to make
			// validation happy. TODO perhaps make a dynamic schema
			// validator to avoid this.
			for _, group := range groupResources {
				for _, version := range group.Group.Versions {
					gv := unversioned.GroupVersion{Group: group.Group.Name, Version: version.Version}
					if !registered.IsRegisteredVersion(gv) {
						registered.AddThirdPartyAPIGroupVersions(gv)
					}
				}
			}

			mapper := discovery.NewRESTMapper(groupResources, meta.InterfacesForUnstructured)

			typer := discovery.NewUnstructuredObjectTyper(groupResources)

			return NewShortcutExpander(mapper, dc), typer, nil
		},
		RESTClient: func() (*restclient.RESTClient, error) {
			clientConfig, err := clients.ClientConfigForVersion(nil)
			if err != nil {
				return nil, err
			}
			return restclient.RESTClientFor(clientConfig)
		},
		ClientSet: func() (*internalclientset.Clientset, error) {
			return clients.ClientSetForVersion(nil)
		},
		ClientConfig: func() (*restclient.Config, error) {
			return clients.ClientConfigForVersion(nil)
		},
		ClientForMapping: func(mapping *meta.RESTMapping) (resource.RESTClient, error) {
			cfg, err := clientConfig.ClientConfig()
			if err != nil {
				return nil, err
			}
			if err := client.SetKubernetesDefaults(cfg); err != nil {
				return nil, err
			}
			gvk := mapping.GroupVersionKind
			switch gvk.Group {
			case federation.GroupName:
				mappingVersion := mapping.GroupVersionKind.GroupVersion()
				return clients.FederationClientForVersion(&mappingVersion)
			case api.GroupName:
				cfg.APIPath = "/api"
			default:
				cfg.APIPath = "/apis"
			}
			gv := gvk.GroupVersion()
			cfg.GroupVersion = &gv
			if registered.IsThirdPartyAPIGroupVersion(gvk.GroupVersion()) {
				cfg.NegotiatedSerializer = thirdpartyresourcedata.NewNegotiatedSerializer(api.Codecs, gvk.Kind, gv, gv)
			}
			return restclient.RESTClientFor(cfg)
		},
		UnstructuredClientForMapping: func(mapping *meta.RESTMapping) (resource.RESTClient, error) {
			cfg, err := clientConfig.ClientConfig()
			if err != nil {
				return nil, err
			}
			if err := restclient.SetKubernetesDefaults(cfg); err != nil {
				return nil, err
			}
			cfg.APIPath = "/apis"
			if mapping.GroupVersionKind.Group == api.GroupName {
				cfg.APIPath = "/api"
			}
			gv := mapping.GroupVersionKind.GroupVersion()
			cfg.ContentConfig = dynamic.ContentConfig()
			cfg.GroupVersion = &gv
			return restclient.RESTClientFor(cfg)
		},
		Describer: func(mapping *meta.RESTMapping) (kubectl.Describer, error) {
			mappingVersion := mapping.GroupVersionKind.GroupVersion()
			if mapping.GroupVersionKind.Group == federation.GroupName {
				fedClientSet, err := clients.FederationClientSetForVersion(&mappingVersion)
				if err != nil {
					return nil, err
				}
				if mapping.GroupVersionKind.Kind == "Cluster" {
					return &kubectl.ClusterDescriber{Interface: fedClientSet}, nil
				}
			}
			clientset, err := clients.ClientSetForVersion(&mappingVersion)
			if err != nil {
				return nil, err
			}
			if describer, ok := kubectl.DescriberFor(mapping.GroupVersionKind.GroupKind(), clientset); ok {
				return describer, nil
			}
			return nil, fmt.Errorf("no description has been implemented for %q", mapping.GroupVersionKind.Kind)
		},
		Decoder: func(toInternal bool) runtime.Decoder {
			var decoder runtime.Decoder
			if toInternal {
				decoder = api.Codecs.UniversalDecoder()
			} else {
				decoder = api.Codecs.UniversalDeserializer()
			}
			return thirdpartyresourcedata.NewDecoder(decoder, "")

		},
		JSONEncoder: func() runtime.Encoder {
			return api.Codecs.LegacyCodec(registered.EnabledVersions()...)
		},
		Printer: func(mapping *meta.RESTMapping, options kubectl.PrintOptions) (kubectl.ResourcePrinter, error) {
			return kubectl.NewHumanReadablePrinter(options), nil
		},
		MapBasedSelectorForObject: func(object runtime.Object) (string, error) {
			// TODO: replace with a swagger schema based approach (identify pod selector via schema introspection)
			switch t := object.(type) {
			case *api.ReplicationController:
				return kubectl.MakeLabels(t.Spec.Selector), nil
			case *api.Pod:
				if len(t.Labels) == 0 {
					return "", fmt.Errorf("the pod has no labels and cannot be exposed")
				}
				return kubectl.MakeLabels(t.Labels), nil
			case *api.Service:
				if t.Spec.Selector == nil {
					return "", fmt.Errorf("the service has no pod selector set")
				}
				return kubectl.MakeLabels(t.Spec.Selector), nil
			case *extensions.Deployment:
				// TODO(madhusudancs): Make this smarter by admitting MatchExpressions with Equals
				// operator, DoubleEquals operator and In operator with only one element in the set.
				if len(t.Spec.Selector.MatchExpressions) > 0 {
					return "", fmt.Errorf("couldn't convert expressions - \"%+v\" to map-based selector format", t.Spec.Selector.MatchExpressions)
				}
				return kubectl.MakeLabels(t.Spec.Selector.MatchLabels), nil
			case *extensions.ReplicaSet:
				// TODO(madhusudancs): Make this smarter by admitting MatchExpressions with Equals
				// operator, DoubleEquals operator and In operator with only one element in the set.
				if len(t.Spec.Selector.MatchExpressions) > 0 {
					return "", fmt.Errorf("couldn't convert expressions - \"%+v\" to map-based selector format", t.Spec.Selector.MatchExpressions)
				}
				return kubectl.MakeLabels(t.Spec.Selector.MatchLabels), nil
			default:
				gvks, _, err := api.Scheme.ObjectKinds(object)
				if err != nil {
					return "", err
				}
				return "", fmt.Errorf("cannot extract pod selector from %v", gvks[0])
			}
		},
		PortsForObject: func(object runtime.Object) ([]string, error) {
			// TODO: replace with a swagger schema based approach (identify pod selector via schema introspection)
			switch t := object.(type) {
			case *api.ReplicationController:
				return getPorts(t.Spec.Template.Spec), nil
			case *api.Pod:
				return getPorts(t.Spec), nil
			case *api.Service:
				return getServicePorts(t.Spec), nil
			case *extensions.Deployment:
				return getPorts(t.Spec.Template.Spec), nil
			case *extensions.ReplicaSet:
				return getPorts(t.Spec.Template.Spec), nil
			default:
				gvks, _, err := api.Scheme.ObjectKinds(object)
				if err != nil {
					return nil, err
				}
				return nil, fmt.Errorf("cannot extract ports from %v", gvks[0])
			}
		},
		ProtocolsForObject: func(object runtime.Object) (map[string]string, error) {
			// TODO: replace with a swagger schema based approach (identify pod selector via schema introspection)
			switch t := object.(type) {
			case *api.ReplicationController:
				return getProtocols(t.Spec.Template.Spec), nil
			case *api.Pod:
				return getProtocols(t.Spec), nil
			case *api.Service:
				return getServiceProtocols(t.Spec), nil
			case *extensions.Deployment:
				return getProtocols(t.Spec.Template.Spec), nil
			case *extensions.ReplicaSet:
				return getProtocols(t.Spec.Template.Spec), nil
			default:
				gvks, _, err := api.Scheme.ObjectKinds(object)
				if err != nil {
					return nil, err
				}
				return nil, fmt.Errorf("cannot extract protocols from %v", gvks[0])
			}
		},
		LabelsForObject: func(object runtime.Object) (map[string]string, error) {
			return meta.NewAccessor().Labels(object)
		},
		LogsForObject: func(object, options runtime.Object) (*restclient.Request, error) {
			clientset, err := clients.ClientSetForVersion(nil)
			if err != nil {
				return nil, err
			}

			switch t := object.(type) {
			case *api.Pod:
				opts, ok := options.(*api.PodLogOptions)
				if !ok {
					return nil, errors.New("provided options object is not a PodLogOptions")
				}
				return clientset.Core().Pods(t.Namespace).GetLogs(t.Name, opts), nil

			case *api.ReplicationController:
				opts, ok := options.(*api.PodLogOptions)
				if !ok {
					return nil, errors.New("provided options object is not a PodLogOptions")
				}
				selector := labels.SelectorFromSet(t.Spec.Selector)
				sortBy := func(pods []*api.Pod) sort.Interface { return controller.ByLogging(pods) }
				pod, numPods, err := GetFirstPod(clientset.Core(), t.Namespace, selector, 20*time.Second, sortBy)
				if err != nil {
					return nil, err
				}
				if numPods > 1 {
					fmt.Fprintf(os.Stderr, "Found %v pods, using pod/%v\n", numPods, pod.Name)
				}

				return clientset.Core().Pods(pod.Namespace).GetLogs(pod.Name, opts), nil

			case *extensions.ReplicaSet:
				opts, ok := options.(*api.PodLogOptions)
				if !ok {
					return nil, errors.New("provided options object is not a PodLogOptions")
				}
				selector, err := unversioned.LabelSelectorAsSelector(t.Spec.Selector)
				if err != nil {
					return nil, fmt.Errorf("invalid label selector: %v", err)
				}
				sortBy := func(pods []*api.Pod) sort.Interface { return controller.ByLogging(pods) }
				pod, numPods, err := GetFirstPod(clientset.Core(), t.Namespace, selector, 20*time.Second, sortBy)
				if err != nil {
					return nil, err
				}
				if numPods > 1 {
					fmt.Fprintf(os.Stderr, "Found %v pods, using pod/%v\n", numPods, pod.Name)
				}

				return clientset.Core().Pods(pod.Namespace).GetLogs(pod.Name, opts), nil

			default:
				gvks, _, err := api.Scheme.ObjectKinds(object)
				if err != nil {
					return nil, err
				}
				return nil, fmt.Errorf("cannot get the logs from %v", gvks[0])
			}
		},
		PauseObject: func(object runtime.Object) (bool, error) {
			clientset, err := clients.ClientSetForVersion(nil)
			if err != nil {
				return false, err
			}

			switch t := object.(type) {
			case *extensions.Deployment:
				if t.Spec.Paused {
					return true, nil
				}
				t.Spec.Paused = true
				_, err := clientset.Extensions().Deployments(t.Namespace).Update(t)
				return false, err
			default:
				gvks, _, err := api.Scheme.ObjectKinds(object)
				if err != nil {
					return false, err
				}
				return false, fmt.Errorf("cannot pause %v", gvks[0])
			}
		},
		ResumeObject: func(object runtime.Object) (bool, error) {
			clientset, err := clients.ClientSetForVersion(nil)
			if err != nil {
				return false, err
			}

			switch t := object.(type) {
			case *extensions.Deployment:
				if !t.Spec.Paused {
					return true, nil
				}
				t.Spec.Paused = false
				_, err := clientset.Extensions().Deployments(t.Namespace).Update(t)
				return false, err
			default:
				gvks, _, err := api.Scheme.ObjectKinds(object)
				if err != nil {
					return false, err
				}
				return false, fmt.Errorf("cannot resume %v", gvks[0])
			}
		},
		Scaler: func(mapping *meta.RESTMapping) (kubectl.Scaler, error) {
			mappingVersion := mapping.GroupVersionKind.GroupVersion()
			clientset, err := clients.ClientSetForVersion(&mappingVersion)
			if err != nil {
				return nil, err
			}
			return kubectl.ScalerFor(mapping.GroupVersionKind.GroupKind(), clientset)
		},
		Reaper: func(mapping *meta.RESTMapping) (kubectl.Reaper, error) {
			mappingVersion := mapping.GroupVersionKind.GroupVersion()
			clientset, err := clients.ClientSetForVersion(&mappingVersion)
			if err != nil {
				return nil, err
			}
			return kubectl.ReaperFor(mapping.GroupVersionKind.GroupKind(), clientset)
		},
		HistoryViewer: func(mapping *meta.RESTMapping) (kubectl.HistoryViewer, error) {
			mappingVersion := mapping.GroupVersionKind.GroupVersion()
			clientset, err := clients.ClientSetForVersion(&mappingVersion)
			if err != nil {
				return nil, err
			}
			return kubectl.HistoryViewerFor(mapping.GroupVersionKind.GroupKind(), clientset)
		},
		Rollbacker: func(mapping *meta.RESTMapping) (kubectl.Rollbacker, error) {
			mappingVersion := mapping.GroupVersionKind.GroupVersion()
			clientset, err := clients.ClientSetForVersion(&mappingVersion)
			if err != nil {
				return nil, err
			}
			return kubectl.RollbackerFor(mapping.GroupVersionKind.GroupKind(), clientset)
		},
		StatusViewer: func(mapping *meta.RESTMapping) (kubectl.StatusViewer, error) {
			mappingVersion := mapping.GroupVersionKind.GroupVersion()
			clientset, err := clients.ClientSetForVersion(&mappingVersion)
			if err != nil {
				return nil, err
			}
			return kubectl.StatusViewerFor(mapping.GroupVersionKind.GroupKind(), clientset)
		},
		Validator: func(validate bool, cacheDir string) (validation.Schema, error) {
			if validate {
				clientConfig, err := clients.ClientConfigForVersion(nil)
				if err != nil {
					return nil, err
				}
				restclient, err := restclient.RESTClientFor(clientConfig)
				if err != nil {
					return nil, err
				}
				clientset, err := clients.ClientSetForVersion(nil)
				if err != nil {
					return nil, err
				}
				dir := cacheDir
				if len(dir) > 0 {
					version, err := clientset.Discovery().ServerVersion()
					if err != nil {
						return nil, err
					}
					dir = path.Join(cacheDir, version.String())
				}
				fedClient, err := clients.FederationClientForVersion(nil)
				if err != nil {
					return nil, err
				}
				return &clientSwaggerSchema{
					c:        restclient,
					fedc:     fedClient,
					cacheDir: dir,
				}, nil
			}
			return validation.NullSchema{}, nil
		},
		SwaggerSchema: func(gvk unversioned.GroupVersionKind) (*swagger.ApiDeclaration, error) {
			version := gvk.GroupVersion()
			clientset, err := clients.ClientSetForVersion(&version)
			if err != nil {
				return nil, err
			}
			return clientset.Discovery().SwaggerSchema(version)
		},
		DefaultNamespace: func() (string, bool, error) {
			return clientConfig.Namespace()
		},
		Generators: func(cmdName string) map[string]kubectl.Generator {
			return DefaultGenerators(cmdName)
		},
		CanBeExposed: func(kind unversioned.GroupKind) error {
			switch kind {
			case api.Kind("ReplicationController"), api.Kind("Service"), api.Kind("Pod"), extensions.Kind("Deployment"), extensions.Kind("ReplicaSet"):
				// nothing to do here
			default:
				return fmt.Errorf("cannot expose a %s", kind)
			}
			return nil
		},
		CanBeAutoscaled: func(kind unversioned.GroupKind) error {
			switch kind {
			case api.Kind("ReplicationController"), extensions.Kind("Deployment"), extensions.Kind("ReplicaSet"):
				// nothing to do here
			default:
				return fmt.Errorf("cannot autoscale a %v", kind)
			}
			return nil
		},
		AttachablePodForObject: func(object runtime.Object) (*api.Pod, error) {
			clientset, err := clients.ClientSetForVersion(nil)
			if err != nil {
				return nil, err
			}
			switch t := object.(type) {
			case *api.ReplicationController:
				selector := labels.SelectorFromSet(t.Spec.Selector)
				sortBy := func(pods []*api.Pod) sort.Interface { return sort.Reverse(controller.ActivePods(pods)) }
				pod, _, err := GetFirstPod(clientset.Core(), t.Namespace, selector, 1*time.Minute, sortBy)
				return pod, err
			case *extensions.Deployment:
				selector, err := unversioned.LabelSelectorAsSelector(t.Spec.Selector)
				if err != nil {
					return nil, fmt.Errorf("invalid label selector: %v", err)
				}
				sortBy := func(pods []*api.Pod) sort.Interface { return sort.Reverse(controller.ActivePods(pods)) }
				pod, _, err := GetFirstPod(clientset.Core(), t.Namespace, selector, 1*time.Minute, sortBy)
				return pod, err
			case *batch.Job:
				selector, err := unversioned.LabelSelectorAsSelector(t.Spec.Selector)
				if err != nil {
					return nil, fmt.Errorf("invalid label selector: %v", err)
				}
				sortBy := func(pods []*api.Pod) sort.Interface { return sort.Reverse(controller.ActivePods(pods)) }
				pod, _, err := GetFirstPod(clientset.Core(), t.Namespace, selector, 1*time.Minute, sortBy)
				return pod, err
			case *api.Pod:
				return t, nil
			default:
				gvks, _, err := api.Scheme.ObjectKinds(object)
				if err != nil {
					return nil, err
				}
				return nil, fmt.Errorf("cannot attach to %v: not implemented", gvks[0])
			}
		},
		// UpdatePodSpecForObject update the pod specification for the provided object
		UpdatePodSpecForObject: func(obj runtime.Object, fn func(*api.PodSpec) error) (bool, error) {
			// TODO: replace with a swagger schema based approach (identify pod template via schema introspection)
			switch t := obj.(type) {
			case *api.Pod:
				return true, fn(&t.Spec)
			case *api.ReplicationController:
				if t.Spec.Template == nil {
					t.Spec.Template = &api.PodTemplateSpec{}
				}
				return true, fn(&t.Spec.Template.Spec)
			case *extensions.Deployment:
				return true, fn(&t.Spec.Template.Spec)
			case *extensions.DaemonSet:
				return true, fn(&t.Spec.Template.Spec)
			case *extensions.ReplicaSet:
				return true, fn(&t.Spec.Template.Spec)
			case *apps.PetSet:
				return true, fn(&t.Spec.Template.Spec)
			case *batch.Job:
				return true, fn(&t.Spec.Template.Spec)
			default:
				return false, fmt.Errorf("the object is not a pod or does not have a pod template")
			}
		},
		EditorEnvs: func() []string {
			return []string{"KUBE_EDITOR", "EDITOR"}
		},
		PrintObjectSpecificMessage: func(obj runtime.Object, out io.Writer) {
			switch obj := obj.(type) {
			case *api.Service:
				if obj.Spec.Type == api.ServiceTypeNodePort {
					msg := fmt.Sprintf(
						`You have exposed your service on an external port on all nodes in your
cluster.  If you want to expose this service to the external internet, you may
need to set up firewall rules for the service port(s) (%s) to serve traffic.

See http://releases.k8s.io/HEAD/docs/user-guide/services-firewalls.md for more details.
`,
						makePortsString(obj.Spec.Ports, true))
					out.Write([]byte(msg))
				}

				if _, ok := obj.Annotations[service.AnnotationLoadBalancerSourceRangesKey]; ok {
					msg := fmt.Sprintf(
						`You are using service annotation [service.beta.kubernetes.io/load-balancer-source-ranges].
It has been promoted to field [loadBalancerSourceRanges] in service spec. This annotation will be deprecated in the future.
Please use the loadBalancerSourceRanges field instead.

See http://releases.k8s.io/HEAD/docs/user-guide/services-firewalls.md for more details.
`)
					out.Write([]byte(msg))
				}
			}
		},
	}
}