factory.go 50 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312
  1. /*
  2. Copyright 2014 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package util
  14. import (
  15. "bytes"
  16. "errors"
  17. "flag"
  18. "fmt"
  19. "io"
  20. "io/ioutil"
  21. "os"
  22. "os/user"
  23. "path"
  24. "path/filepath"
  25. "sort"
  26. "strconv"
  27. "strings"
  28. "time"
  29. "github.com/emicklei/go-restful/swagger"
  30. "github.com/imdario/mergo"
  31. "github.com/spf13/cobra"
  32. "github.com/spf13/pflag"
  33. "k8s.io/kubernetes/federation/apis/federation"
  34. "k8s.io/kubernetes/pkg/api"
  35. apierrors "k8s.io/kubernetes/pkg/api/errors"
  36. "k8s.io/kubernetes/pkg/api/meta"
  37. "k8s.io/kubernetes/pkg/api/service"
  38. "k8s.io/kubernetes/pkg/api/unversioned"
  39. "k8s.io/kubernetes/pkg/api/validation"
  40. "k8s.io/kubernetes/pkg/apimachinery"
  41. "k8s.io/kubernetes/pkg/apimachinery/registered"
  42. "k8s.io/kubernetes/pkg/apis/apps"
  43. "k8s.io/kubernetes/pkg/apis/autoscaling"
  44. "k8s.io/kubernetes/pkg/apis/batch"
  45. "k8s.io/kubernetes/pkg/apis/certificates"
  46. "k8s.io/kubernetes/pkg/apis/extensions"
  47. "k8s.io/kubernetes/pkg/apis/policy"
  48. "k8s.io/kubernetes/pkg/apis/rbac"
  49. "k8s.io/kubernetes/pkg/client/restclient"
  50. "k8s.io/kubernetes/pkg/client/typed/discovery"
  51. "k8s.io/kubernetes/pkg/client/typed/dynamic"
  52. client "k8s.io/kubernetes/pkg/client/unversioned"
  53. clientset "k8s.io/kubernetes/pkg/client/unversioned/adapters/internalclientset"
  54. "k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
  55. "k8s.io/kubernetes/pkg/controller"
  56. "k8s.io/kubernetes/pkg/kubectl"
  57. "k8s.io/kubernetes/pkg/kubectl/resource"
  58. "k8s.io/kubernetes/pkg/labels"
  59. "k8s.io/kubernetes/pkg/registry/thirdpartyresourcedata"
  60. "k8s.io/kubernetes/pkg/runtime"
  61. "k8s.io/kubernetes/pkg/runtime/serializer/json"
  62. utilflag "k8s.io/kubernetes/pkg/util/flag"
  63. "k8s.io/kubernetes/pkg/watch"
  64. )
  65. const (
  66. FlagMatchBinaryVersion = "match-server-version"
  67. )
  68. // Factory provides abstractions that allow the Kubectl command to be extended across multiple types
  69. // of resources and different API sets.
  70. // TODO: make the functions interfaces
  71. // TODO: pass the various interfaces on the factory directly into the command constructors (so the
  72. // commands are decoupled from the factory).
  73. type Factory struct {
  74. clients *ClientCache
  75. flags *pflag.FlagSet
  76. // Returns interfaces for dealing with arbitrary runtime.Objects. If thirdPartyDiscovery is true, performs API calls
  77. // to discovery dynamic API objects registered by third parties.
  78. Object func(thirdPartyDiscovery bool) (meta.RESTMapper, runtime.ObjectTyper)
  79. // Returns interfaces for dealing with arbitrary
  80. // runtime.Unstructured. This performs API calls to discover types.
  81. UnstructuredObject func() (meta.RESTMapper, runtime.ObjectTyper, error)
  82. // Returns interfaces for decoding objects - if toInternal is set, decoded objects will be converted
  83. // into their internal form (if possible). Eventually the internal form will be removed as an option,
  84. // and only versioned objects will be returned.
  85. Decoder func(toInternal bool) runtime.Decoder
  86. // Returns an encoder capable of encoding a provided object into JSON in the default desired version.
  87. JSONEncoder func() runtime.Encoder
  88. // Returns a client for accessing Kubernetes resources or an error.
  89. Client func() (*client.Client, error)
  90. // Returns a client.Config for accessing the Kubernetes server.
  91. ClientConfig func() (*restclient.Config, error)
  92. // Returns a RESTClient for working with the specified RESTMapping or an error. This is intended
  93. // for working with arbitrary resources and is not guaranteed to point to a Kubernetes APIServer.
  94. ClientForMapping func(mapping *meta.RESTMapping) (resource.RESTClient, error)
  95. // Returns a RESTClient for working with Unstructured objects.
  96. UnstructuredClientForMapping func(mapping *meta.RESTMapping) (resource.RESTClient, error)
  97. // Returns a Describer for displaying the specified RESTMapping type or an error.
  98. Describer func(mapping *meta.RESTMapping) (kubectl.Describer, error)
  99. // Returns a Printer for formatting objects of the given type or an error.
  100. Printer func(mapping *meta.RESTMapping, options kubectl.PrintOptions) (kubectl.ResourcePrinter, error)
  101. // Returns a Scaler for changing the size of the specified RESTMapping type or an error
  102. Scaler func(mapping *meta.RESTMapping) (kubectl.Scaler, error)
  103. // Returns a Reaper for gracefully shutting down resources.
  104. Reaper func(mapping *meta.RESTMapping) (kubectl.Reaper, error)
  105. // Returns a HistoryViewer for viewing change history
  106. HistoryViewer func(mapping *meta.RESTMapping) (kubectl.HistoryViewer, error)
  107. // Returns a Rollbacker for changing the rollback version of the specified RESTMapping type or an error
  108. Rollbacker func(mapping *meta.RESTMapping) (kubectl.Rollbacker, error)
  109. // Returns a StatusViewer for printing rollout status.
  110. StatusViewer func(mapping *meta.RESTMapping) (kubectl.StatusViewer, error)
  111. // MapBasedSelectorForObject returns the map-based selector associated with the provided object. If a
  112. // new set-based selector is provided, an error is returned if the selector cannot be converted to a
  113. // map-based selector
  114. MapBasedSelectorForObject func(object runtime.Object) (string, error)
  115. // PortsForObject returns the ports associated with the provided object
  116. PortsForObject func(object runtime.Object) ([]string, error)
  117. // ProtocolsForObject returns the <port, protocol> mapping associated with the provided object
  118. ProtocolsForObject func(object runtime.Object) (map[string]string, error)
  119. // LabelsForObject returns the labels associated with the provided object
  120. LabelsForObject func(object runtime.Object) (map[string]string, error)
  121. // LogsForObject returns a request for the logs associated with the provided object
  122. LogsForObject func(object, options runtime.Object) (*restclient.Request, error)
  123. // PauseObject marks the provided object as paused ie. it will not be reconciled by its controller.
  124. PauseObject func(object runtime.Object) (bool, error)
  125. // ResumeObject resumes a paused object ie. it will be reconciled by its controller.
  126. ResumeObject func(object runtime.Object) (bool, error)
  127. // Returns a schema that can validate objects stored on disk.
  128. Validator func(validate bool, cacheDir string) (validation.Schema, error)
  129. // SwaggerSchema returns the schema declaration for the provided group version kind.
  130. SwaggerSchema func(unversioned.GroupVersionKind) (*swagger.ApiDeclaration, error)
  131. // Returns the default namespace to use in cases where no
  132. // other namespace is specified and whether the namespace was
  133. // overridden.
  134. DefaultNamespace func() (string, bool, error)
  135. // Generators returns the generators for the provided command
  136. Generators func(cmdName string) map[string]kubectl.Generator
  137. // Check whether the kind of resources could be exposed
  138. CanBeExposed func(kind unversioned.GroupKind) error
  139. // Check whether the kind of resources could be autoscaled
  140. CanBeAutoscaled func(kind unversioned.GroupKind) error
  141. // AttachablePodForObject returns the pod to which to attach given an object.
  142. AttachablePodForObject func(object runtime.Object) (*api.Pod, error)
  143. // UpdatePodSpecForObject will call the provided function on the pod spec this object supports,
  144. // return false if no pod spec is supported, or return an error.
  145. UpdatePodSpecForObject func(obj runtime.Object, fn func(*api.PodSpec) error) (bool, error)
  146. // EditorEnvs returns a group of environment variables that the edit command
  147. // can range over in order to determine if the user has specified an editor
  148. // of their choice.
  149. EditorEnvs func() []string
  150. // PrintObjectSpecificMessage prints object-specific messages on the provided writer
  151. PrintObjectSpecificMessage func(obj runtime.Object, out io.Writer)
  152. }
  153. const (
  154. RunV1GeneratorName = "run/v1"
  155. RunPodV1GeneratorName = "run-pod/v1"
  156. ServiceV1GeneratorName = "service/v1"
  157. ServiceV2GeneratorName = "service/v2"
  158. ServiceNodePortGeneratorV1Name = "service-nodeport/v1"
  159. ServiceClusterIPGeneratorV1Name = "service-clusterip/v1"
  160. ServiceLoadBalancerGeneratorV1Name = "service-loadbalancer/v1"
  161. ServiceAccountV1GeneratorName = "serviceaccount/v1"
  162. HorizontalPodAutoscalerV1Beta1GeneratorName = "horizontalpodautoscaler/v1beta1"
  163. HorizontalPodAutoscalerV1GeneratorName = "horizontalpodautoscaler/v1"
  164. DeploymentV1Beta1GeneratorName = "deployment/v1beta1"
  165. DeploymentBasicV1Beta1GeneratorName = "deployment-basic/v1beta1"
  166. JobV1Beta1GeneratorName = "job/v1beta1"
  167. JobV1GeneratorName = "job/v1"
  168. ScheduledJobV2Alpha1GeneratorName = "scheduledjob/v2alpha1"
  169. NamespaceV1GeneratorName = "namespace/v1"
  170. ResourceQuotaV1GeneratorName = "resourcequotas/v1"
  171. SecretV1GeneratorName = "secret/v1"
  172. SecretForDockerRegistryV1GeneratorName = "secret-for-docker-registry/v1"
  173. SecretForTLSV1GeneratorName = "secret-for-tls/v1"
  174. ConfigMapV1GeneratorName = "configmap/v1"
  175. )
  176. // DefaultGenerators returns the set of default generators for use in Factory instances
  177. func DefaultGenerators(cmdName string) map[string]kubectl.Generator {
  178. generators := map[string]map[string]kubectl.Generator{}
  179. generators["expose"] = map[string]kubectl.Generator{
  180. ServiceV1GeneratorName: kubectl.ServiceGeneratorV1{},
  181. ServiceV2GeneratorName: kubectl.ServiceGeneratorV2{},
  182. }
  183. generators["service-clusterip"] = map[string]kubectl.Generator{
  184. ServiceClusterIPGeneratorV1Name: kubectl.ServiceClusterIPGeneratorV1{},
  185. }
  186. generators["service-nodeport"] = map[string]kubectl.Generator{
  187. ServiceNodePortGeneratorV1Name: kubectl.ServiceNodePortGeneratorV1{},
  188. }
  189. generators["service-loadbalancer"] = map[string]kubectl.Generator{
  190. ServiceLoadBalancerGeneratorV1Name: kubectl.ServiceLoadBalancerGeneratorV1{},
  191. }
  192. generators["deployment"] = map[string]kubectl.Generator{
  193. DeploymentBasicV1Beta1GeneratorName: kubectl.DeploymentBasicGeneratorV1{},
  194. }
  195. generators["run"] = map[string]kubectl.Generator{
  196. RunV1GeneratorName: kubectl.BasicReplicationController{},
  197. RunPodV1GeneratorName: kubectl.BasicPod{},
  198. DeploymentV1Beta1GeneratorName: kubectl.DeploymentV1Beta1{},
  199. JobV1Beta1GeneratorName: kubectl.JobV1Beta1{},
  200. JobV1GeneratorName: kubectl.JobV1{},
  201. ScheduledJobV2Alpha1GeneratorName: kubectl.ScheduledJobV2Alpha1{},
  202. }
  203. generators["autoscale"] = map[string]kubectl.Generator{
  204. HorizontalPodAutoscalerV1Beta1GeneratorName: kubectl.HorizontalPodAutoscalerV1Beta1{},
  205. HorizontalPodAutoscalerV1GeneratorName: kubectl.HorizontalPodAutoscalerV1{},
  206. }
  207. generators["namespace"] = map[string]kubectl.Generator{
  208. NamespaceV1GeneratorName: kubectl.NamespaceGeneratorV1{},
  209. }
  210. generators["quota"] = map[string]kubectl.Generator{
  211. ResourceQuotaV1GeneratorName: kubectl.ResourceQuotaGeneratorV1{},
  212. }
  213. generators["secret"] = map[string]kubectl.Generator{
  214. SecretV1GeneratorName: kubectl.SecretGeneratorV1{},
  215. }
  216. generators["secret-for-docker-registry"] = map[string]kubectl.Generator{
  217. SecretForDockerRegistryV1GeneratorName: kubectl.SecretForDockerRegistryGeneratorV1{},
  218. }
  219. generators["secret-for-tls"] = map[string]kubectl.Generator{
  220. SecretForTLSV1GeneratorName: kubectl.SecretForTLSGeneratorV1{},
  221. }
  222. return generators[cmdName]
  223. }
  224. func getGroupVersionKinds(gvks []unversioned.GroupVersionKind, group string) []unversioned.GroupVersionKind {
  225. result := []unversioned.GroupVersionKind{}
  226. for ix := range gvks {
  227. if gvks[ix].Group == group {
  228. result = append(result, gvks[ix])
  229. }
  230. }
  231. return result
  232. }
  233. func makeInterfacesFor(versionList []unversioned.GroupVersion) func(version unversioned.GroupVersion) (*meta.VersionInterfaces, error) {
  234. accessor := meta.NewAccessor()
  235. return func(version unversioned.GroupVersion) (*meta.VersionInterfaces, error) {
  236. for ix := range versionList {
  237. if versionList[ix].String() == version.String() {
  238. return &meta.VersionInterfaces{
  239. ObjectConvertor: thirdpartyresourcedata.NewThirdPartyObjectConverter(api.Scheme),
  240. MetadataAccessor: accessor,
  241. }, nil
  242. }
  243. }
  244. return nil, fmt.Errorf("unsupported storage version: %s (valid: %v)", version, versionList)
  245. }
  246. }
  247. // NewFactory creates a factory with the default Kubernetes resources defined
  248. // if optionalClientConfig is nil, then flags will be bound to a new clientcmd.ClientConfig.
  249. // if optionalClientConfig is not nil, then this factory will make use of it.
  250. func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory {
  251. mapper := kubectl.ShortcutExpander{RESTMapper: registered.RESTMapper()}
  252. flags := pflag.NewFlagSet("", pflag.ContinueOnError)
  253. flags.SetNormalizeFunc(utilflag.WarnWordSepNormalizeFunc) // Warn for "_" flags
  254. clientConfig := optionalClientConfig
  255. if optionalClientConfig == nil {
  256. clientConfig = DefaultClientConfig(flags)
  257. }
  258. clients := NewClientCache(clientConfig)
  259. return &Factory{
  260. clients: clients,
  261. flags: flags,
  262. // If discoverDynamicAPIs is true, make API calls to the discovery service to find APIs that
  263. // have been dynamically added to the apiserver
  264. Object: func(discoverDynamicAPIs bool) (meta.RESTMapper, runtime.ObjectTyper) {
  265. cfg, err := clientConfig.ClientConfig()
  266. checkErrWithPrefix("failed to get client config: ", err)
  267. cmdApiVersion := unversioned.GroupVersion{}
  268. if cfg.GroupVersion != nil {
  269. cmdApiVersion = *cfg.GroupVersion
  270. }
  271. if discoverDynamicAPIs {
  272. client, err := clients.ClientForVersion(&unversioned.GroupVersion{Version: "v1"})
  273. checkErrWithPrefix("failed to find client for version v1: ", err)
  274. var versions []unversioned.GroupVersion
  275. var gvks []unversioned.GroupVersionKind
  276. retries := 3
  277. for i := 0; i < retries; i++ {
  278. versions, gvks, err = GetThirdPartyGroupVersions(client.Discovery())
  279. // Retry if we got a NotFound error, because user may delete
  280. // a thirdparty group when the GetThirdPartyGroupVersions is
  281. // running.
  282. if err == nil || !apierrors.IsNotFound(err) {
  283. break
  284. }
  285. }
  286. checkErrWithPrefix("failed to get third-party group versions: ", err)
  287. if len(versions) > 0 {
  288. priorityMapper, ok := mapper.RESTMapper.(meta.PriorityRESTMapper)
  289. if !ok {
  290. CheckErr(fmt.Errorf("expected PriorityMapper, saw: %v", mapper.RESTMapper))
  291. return nil, nil
  292. }
  293. multiMapper, ok := priorityMapper.Delegate.(meta.MultiRESTMapper)
  294. if !ok {
  295. CheckErr(fmt.Errorf("unexpected type: %v", mapper.RESTMapper))
  296. return nil, nil
  297. }
  298. groupsMap := map[string][]unversioned.GroupVersion{}
  299. for _, version := range versions {
  300. groupsMap[version.Group] = append(groupsMap[version.Group], version)
  301. }
  302. for group, versionList := range groupsMap {
  303. preferredExternalVersion := versionList[0]
  304. thirdPartyMapper, err := kubectl.NewThirdPartyResourceMapper(versionList, getGroupVersionKinds(gvks, group))
  305. checkErrWithPrefix("failed to create third party resource mapper: ", err)
  306. accessor := meta.NewAccessor()
  307. groupMeta := apimachinery.GroupMeta{
  308. GroupVersion: preferredExternalVersion,
  309. GroupVersions: versionList,
  310. RESTMapper: thirdPartyMapper,
  311. SelfLinker: runtime.SelfLinker(accessor),
  312. InterfacesFor: makeInterfacesFor(versionList),
  313. }
  314. checkErrWithPrefix("failed to register group: ", registered.RegisterGroup(groupMeta))
  315. registered.AddThirdPartyAPIGroupVersions(versionList...)
  316. multiMapper = append(meta.MultiRESTMapper{thirdPartyMapper}, multiMapper...)
  317. }
  318. priorityMapper.Delegate = multiMapper
  319. // Reassign to the RESTMapper here because priorityMapper is actually a copy, so if we
  320. // don't reassign, the above assignement won't actually update mapper.RESTMapper
  321. mapper.RESTMapper = priorityMapper
  322. }
  323. }
  324. outputRESTMapper := kubectl.OutputVersionMapper{RESTMapper: mapper, OutputVersions: []unversioned.GroupVersion{cmdApiVersion}}
  325. priorityRESTMapper := meta.PriorityRESTMapper{
  326. Delegate: outputRESTMapper,
  327. }
  328. // TODO: this should come from registered versions
  329. groups := []string{api.GroupName, autoscaling.GroupName, extensions.GroupName, federation.GroupName, batch.GroupName}
  330. // set a preferred version
  331. for _, group := range groups {
  332. gvs := registered.EnabledVersionsForGroup(group)
  333. if len(gvs) == 0 {
  334. continue
  335. }
  336. priorityRESTMapper.ResourcePriority = append(priorityRESTMapper.ResourcePriority, unversioned.GroupVersionResource{Group: group, Version: gvs[0].Version, Resource: meta.AnyResource})
  337. priorityRESTMapper.KindPriority = append(priorityRESTMapper.KindPriority, unversioned.GroupVersionKind{Group: group, Version: gvs[0].Version, Kind: meta.AnyKind})
  338. }
  339. for _, group := range groups {
  340. priorityRESTMapper.ResourcePriority = append(priorityRESTMapper.ResourcePriority, unversioned.GroupVersionResource{Group: group, Version: meta.AnyVersion, Resource: meta.AnyResource})
  341. priorityRESTMapper.KindPriority = append(priorityRESTMapper.KindPriority, unversioned.GroupVersionKind{Group: group, Version: meta.AnyVersion, Kind: meta.AnyKind})
  342. }
  343. return priorityRESTMapper, api.Scheme
  344. },
  345. UnstructuredObject: func() (meta.RESTMapper, runtime.ObjectTyper, error) {
  346. cfg, err := clients.ClientConfigForVersion(nil)
  347. if err != nil {
  348. return nil, nil, err
  349. }
  350. dc, err := discovery.NewDiscoveryClientForConfig(cfg)
  351. if err != nil {
  352. return nil, nil, err
  353. }
  354. groupResources, err := discovery.GetAPIGroupResources(dc)
  355. if err != nil {
  356. return nil, nil, err
  357. }
  358. // Register unknown APIs as third party for now to make
  359. // validation happy. TODO perhaps make a dynamic schema
  360. // validator to avoid this.
  361. for _, group := range groupResources {
  362. for _, version := range group.Group.Versions {
  363. gv := unversioned.GroupVersion{Group: group.Group.Name, Version: version.Version}
  364. if !registered.IsRegisteredVersion(gv) {
  365. registered.AddThirdPartyAPIGroupVersions(gv)
  366. }
  367. }
  368. }
  369. mapper := discovery.NewRESTMapper(groupResources, meta.InterfacesForUnstructured)
  370. typer := discovery.NewUnstructuredObjectTyper(groupResources)
  371. return kubectl.ShortcutExpander{RESTMapper: mapper}, typer, nil
  372. },
  373. Client: func() (*client.Client, error) {
  374. return clients.ClientForVersion(nil)
  375. },
  376. ClientConfig: func() (*restclient.Config, error) {
  377. return clients.ClientConfigForVersion(nil)
  378. },
  379. ClientForMapping: func(mapping *meta.RESTMapping) (resource.RESTClient, error) {
  380. cfg, err := clientConfig.ClientConfig()
  381. if err != nil {
  382. return nil, err
  383. }
  384. if err := client.SetKubernetesDefaults(cfg); err != nil {
  385. return nil, err
  386. }
  387. gvk := mapping.GroupVersionKind
  388. switch gvk.Group {
  389. case federation.GroupName:
  390. mappingVersion := mapping.GroupVersionKind.GroupVersion()
  391. return clients.FederationClientForVersion(&mappingVersion)
  392. case api.GroupName:
  393. cfg.APIPath = "/api"
  394. default:
  395. cfg.APIPath = "/apis"
  396. }
  397. gv := gvk.GroupVersion()
  398. cfg.GroupVersion = &gv
  399. if registered.IsThirdPartyAPIGroupVersion(gvk.GroupVersion()) {
  400. cfg.NegotiatedSerializer = thirdpartyresourcedata.NewNegotiatedSerializer(api.Codecs, gvk.Kind, gv, gv)
  401. }
  402. return restclient.RESTClientFor(cfg)
  403. },
  404. UnstructuredClientForMapping: func(mapping *meta.RESTMapping) (resource.RESTClient, error) {
  405. cfg, err := clientConfig.ClientConfig()
  406. if err != nil {
  407. return nil, err
  408. }
  409. if err := restclient.SetKubernetesDefaults(cfg); err != nil {
  410. return nil, err
  411. }
  412. cfg.APIPath = "/apis"
  413. if mapping.GroupVersionKind.Group == api.GroupName {
  414. cfg.APIPath = "/api"
  415. }
  416. gv := mapping.GroupVersionKind.GroupVersion()
  417. cfg.ContentConfig = dynamic.ContentConfig()
  418. cfg.GroupVersion = &gv
  419. return restclient.RESTClientFor(cfg)
  420. },
  421. Describer: func(mapping *meta.RESTMapping) (kubectl.Describer, error) {
  422. mappingVersion := mapping.GroupVersionKind.GroupVersion()
  423. if mapping.GroupVersionKind.Group == federation.GroupName {
  424. fedClientSet, err := clients.FederationClientSetForVersion(&mappingVersion)
  425. if err != nil {
  426. return nil, err
  427. }
  428. if mapping.GroupVersionKind.Kind == "Cluster" {
  429. return &kubectl.ClusterDescriber{Interface: fedClientSet}, nil
  430. }
  431. }
  432. client, err := clients.ClientForVersion(&mappingVersion)
  433. if err != nil {
  434. return nil, err
  435. }
  436. if describer, ok := kubectl.DescriberFor(mapping.GroupVersionKind.GroupKind(), client); ok {
  437. return describer, nil
  438. }
  439. return nil, fmt.Errorf("no description has been implemented for %q", mapping.GroupVersionKind.Kind)
  440. },
  441. Decoder: func(toInternal bool) runtime.Decoder {
  442. var decoder runtime.Decoder
  443. if toInternal {
  444. decoder = api.Codecs.UniversalDecoder()
  445. } else {
  446. decoder = api.Codecs.UniversalDeserializer()
  447. }
  448. return thirdpartyresourcedata.NewDecoder(decoder, "")
  449. },
  450. JSONEncoder: func() runtime.Encoder {
  451. return api.Codecs.LegacyCodec(registered.EnabledVersions()...)
  452. },
  453. Printer: func(mapping *meta.RESTMapping, options kubectl.PrintOptions) (kubectl.ResourcePrinter, error) {
  454. return kubectl.NewHumanReadablePrinter(options), nil
  455. },
  456. MapBasedSelectorForObject: func(object runtime.Object) (string, error) {
  457. // TODO: replace with a swagger schema based approach (identify pod selector via schema introspection)
  458. switch t := object.(type) {
  459. case *api.ReplicationController:
  460. return kubectl.MakeLabels(t.Spec.Selector), nil
  461. case *api.Pod:
  462. if len(t.Labels) == 0 {
  463. return "", fmt.Errorf("the pod has no labels and cannot be exposed")
  464. }
  465. return kubectl.MakeLabels(t.Labels), nil
  466. case *api.Service:
  467. if t.Spec.Selector == nil {
  468. return "", fmt.Errorf("the service has no pod selector set")
  469. }
  470. return kubectl.MakeLabels(t.Spec.Selector), nil
  471. case *extensions.Deployment:
  472. // TODO(madhusudancs): Make this smarter by admitting MatchExpressions with Equals
  473. // operator, DoubleEquals operator and In operator with only one element in the set.
  474. if len(t.Spec.Selector.MatchExpressions) > 0 {
  475. return "", fmt.Errorf("couldn't convert expressions - \"%+v\" to map-based selector format", t.Spec.Selector.MatchExpressions)
  476. }
  477. return kubectl.MakeLabels(t.Spec.Selector.MatchLabels), nil
  478. case *extensions.ReplicaSet:
  479. // TODO(madhusudancs): Make this smarter by admitting MatchExpressions with Equals
  480. // operator, DoubleEquals operator and In operator with only one element in the set.
  481. if len(t.Spec.Selector.MatchExpressions) > 0 {
  482. return "", fmt.Errorf("couldn't convert expressions - \"%+v\" to map-based selector format", t.Spec.Selector.MatchExpressions)
  483. }
  484. return kubectl.MakeLabels(t.Spec.Selector.MatchLabels), nil
  485. default:
  486. gvks, _, err := api.Scheme.ObjectKinds(object)
  487. if err != nil {
  488. return "", err
  489. }
  490. return "", fmt.Errorf("cannot extract pod selector from %v", gvks[0])
  491. }
  492. },
  493. PortsForObject: func(object runtime.Object) ([]string, error) {
  494. // TODO: replace with a swagger schema based approach (identify pod selector via schema introspection)
  495. switch t := object.(type) {
  496. case *api.ReplicationController:
  497. return getPorts(t.Spec.Template.Spec), nil
  498. case *api.Pod:
  499. return getPorts(t.Spec), nil
  500. case *api.Service:
  501. return getServicePorts(t.Spec), nil
  502. case *extensions.Deployment:
  503. return getPorts(t.Spec.Template.Spec), nil
  504. case *extensions.ReplicaSet:
  505. return getPorts(t.Spec.Template.Spec), nil
  506. default:
  507. gvks, _, err := api.Scheme.ObjectKinds(object)
  508. if err != nil {
  509. return nil, err
  510. }
  511. return nil, fmt.Errorf("cannot extract ports from %v", gvks[0])
  512. }
  513. },
  514. ProtocolsForObject: func(object runtime.Object) (map[string]string, error) {
  515. // TODO: replace with a swagger schema based approach (identify pod selector via schema introspection)
  516. switch t := object.(type) {
  517. case *api.ReplicationController:
  518. return getProtocols(t.Spec.Template.Spec), nil
  519. case *api.Pod:
  520. return getProtocols(t.Spec), nil
  521. case *api.Service:
  522. return getServiceProtocols(t.Spec), nil
  523. case *extensions.Deployment:
  524. return getProtocols(t.Spec.Template.Spec), nil
  525. case *extensions.ReplicaSet:
  526. return getProtocols(t.Spec.Template.Spec), nil
  527. default:
  528. gvks, _, err := api.Scheme.ObjectKinds(object)
  529. if err != nil {
  530. return nil, err
  531. }
  532. return nil, fmt.Errorf("cannot extract protocols from %v", gvks[0])
  533. }
  534. },
  535. LabelsForObject: func(object runtime.Object) (map[string]string, error) {
  536. return meta.NewAccessor().Labels(object)
  537. },
  538. LogsForObject: func(object, options runtime.Object) (*restclient.Request, error) {
  539. c, err := clients.ClientForVersion(nil)
  540. if err != nil {
  541. return nil, err
  542. }
  543. switch t := object.(type) {
  544. case *api.Pod:
  545. opts, ok := options.(*api.PodLogOptions)
  546. if !ok {
  547. return nil, errors.New("provided options object is not a PodLogOptions")
  548. }
  549. return c.Pods(t.Namespace).GetLogs(t.Name, opts), nil
  550. case *api.ReplicationController:
  551. opts, ok := options.(*api.PodLogOptions)
  552. if !ok {
  553. return nil, errors.New("provided options object is not a PodLogOptions")
  554. }
  555. selector := labels.SelectorFromSet(t.Spec.Selector)
  556. sortBy := func(pods []*api.Pod) sort.Interface { return controller.ByLogging(pods) }
  557. pod, numPods, err := GetFirstPod(c, t.Namespace, selector, 20*time.Second, sortBy)
  558. if err != nil {
  559. return nil, err
  560. }
  561. if numPods > 1 {
  562. fmt.Fprintf(os.Stderr, "Found %v pods, using pod/%v\n", numPods, pod.Name)
  563. }
  564. return c.Pods(pod.Namespace).GetLogs(pod.Name, opts), nil
  565. case *extensions.ReplicaSet:
  566. opts, ok := options.(*api.PodLogOptions)
  567. if !ok {
  568. return nil, errors.New("provided options object is not a PodLogOptions")
  569. }
  570. selector, err := unversioned.LabelSelectorAsSelector(t.Spec.Selector)
  571. if err != nil {
  572. return nil, fmt.Errorf("invalid label selector: %v", err)
  573. }
  574. sortBy := func(pods []*api.Pod) sort.Interface { return controller.ByLogging(pods) }
  575. pod, numPods, err := GetFirstPod(c, t.Namespace, selector, 20*time.Second, sortBy)
  576. if err != nil {
  577. return nil, err
  578. }
  579. if numPods > 1 {
  580. fmt.Fprintf(os.Stderr, "Found %v pods, using pod/%v\n", numPods, pod.Name)
  581. }
  582. return c.Pods(pod.Namespace).GetLogs(pod.Name, opts), nil
  583. default:
  584. gvks, _, err := api.Scheme.ObjectKinds(object)
  585. if err != nil {
  586. return nil, err
  587. }
  588. return nil, fmt.Errorf("cannot get the logs from %v", gvks[0])
  589. }
  590. },
  591. PauseObject: func(object runtime.Object) (bool, error) {
  592. c, err := clients.ClientForVersion(nil)
  593. if err != nil {
  594. return false, err
  595. }
  596. switch t := object.(type) {
  597. case *extensions.Deployment:
  598. if t.Spec.Paused {
  599. return true, nil
  600. }
  601. t.Spec.Paused = true
  602. _, err := c.Extensions().Deployments(t.Namespace).Update(t)
  603. return false, err
  604. default:
  605. gvks, _, err := api.Scheme.ObjectKinds(object)
  606. if err != nil {
  607. return false, err
  608. }
  609. return false, fmt.Errorf("cannot pause %v", gvks[0])
  610. }
  611. },
  612. ResumeObject: func(object runtime.Object) (bool, error) {
  613. c, err := clients.ClientForVersion(nil)
  614. if err != nil {
  615. return false, err
  616. }
  617. switch t := object.(type) {
  618. case *extensions.Deployment:
  619. if !t.Spec.Paused {
  620. return true, nil
  621. }
  622. t.Spec.Paused = false
  623. _, err := c.Extensions().Deployments(t.Namespace).Update(t)
  624. return false, err
  625. default:
  626. gvks, _, err := api.Scheme.ObjectKinds(object)
  627. if err != nil {
  628. return false, err
  629. }
  630. return false, fmt.Errorf("cannot resume %v", gvks[0])
  631. }
  632. },
  633. Scaler: func(mapping *meta.RESTMapping) (kubectl.Scaler, error) {
  634. mappingVersion := mapping.GroupVersionKind.GroupVersion()
  635. client, err := clients.ClientForVersion(&mappingVersion)
  636. if err != nil {
  637. return nil, err
  638. }
  639. return kubectl.ScalerFor(mapping.GroupVersionKind.GroupKind(), client)
  640. },
  641. Reaper: func(mapping *meta.RESTMapping) (kubectl.Reaper, error) {
  642. mappingVersion := mapping.GroupVersionKind.GroupVersion()
  643. client, err := clients.ClientForVersion(&mappingVersion)
  644. if err != nil {
  645. return nil, err
  646. }
  647. return kubectl.ReaperFor(mapping.GroupVersionKind.GroupKind(), client)
  648. },
  649. HistoryViewer: func(mapping *meta.RESTMapping) (kubectl.HistoryViewer, error) {
  650. mappingVersion := mapping.GroupVersionKind.GroupVersion()
  651. client, err := clients.ClientForVersion(&mappingVersion)
  652. clientset := clientset.FromUnversionedClient(client)
  653. if err != nil {
  654. return nil, err
  655. }
  656. return kubectl.HistoryViewerFor(mapping.GroupVersionKind.GroupKind(), clientset)
  657. },
  658. Rollbacker: func(mapping *meta.RESTMapping) (kubectl.Rollbacker, error) {
  659. mappingVersion := mapping.GroupVersionKind.GroupVersion()
  660. client, err := clients.ClientForVersion(&mappingVersion)
  661. if err != nil {
  662. return nil, err
  663. }
  664. return kubectl.RollbackerFor(mapping.GroupVersionKind.GroupKind(), client)
  665. },
  666. StatusViewer: func(mapping *meta.RESTMapping) (kubectl.StatusViewer, error) {
  667. mappingVersion := mapping.GroupVersionKind.GroupVersion()
  668. client, err := clients.ClientForVersion(&mappingVersion)
  669. if err != nil {
  670. return nil, err
  671. }
  672. return kubectl.StatusViewerFor(mapping.GroupVersionKind.GroupKind(), client)
  673. },
  674. Validator: func(validate bool, cacheDir string) (validation.Schema, error) {
  675. if validate {
  676. client, err := clients.ClientForVersion(nil)
  677. if err != nil {
  678. return nil, err
  679. }
  680. dir := cacheDir
  681. if len(dir) > 0 {
  682. version, err := client.ServerVersion()
  683. if err != nil {
  684. return nil, err
  685. }
  686. dir = path.Join(cacheDir, version.String())
  687. }
  688. fedClient, err := clients.FederationClientForVersion(nil)
  689. if err != nil {
  690. return nil, err
  691. }
  692. return &clientSwaggerSchema{
  693. c: client,
  694. fedc: fedClient,
  695. cacheDir: dir,
  696. mapper: api.RESTMapper,
  697. }, nil
  698. }
  699. return validation.NullSchema{}, nil
  700. },
  701. SwaggerSchema: func(gvk unversioned.GroupVersionKind) (*swagger.ApiDeclaration, error) {
  702. version := gvk.GroupVersion()
  703. client, err := clients.ClientForVersion(&version)
  704. if err != nil {
  705. return nil, err
  706. }
  707. return client.Discovery().SwaggerSchema(version)
  708. },
  709. DefaultNamespace: func() (string, bool, error) {
  710. return clientConfig.Namespace()
  711. },
  712. Generators: func(cmdName string) map[string]kubectl.Generator {
  713. return DefaultGenerators(cmdName)
  714. },
  715. CanBeExposed: func(kind unversioned.GroupKind) error {
  716. switch kind {
  717. case api.Kind("ReplicationController"), api.Kind("Service"), api.Kind("Pod"), extensions.Kind("Deployment"), extensions.Kind("ReplicaSet"):
  718. // nothing to do here
  719. default:
  720. return fmt.Errorf("cannot expose a %s", kind)
  721. }
  722. return nil
  723. },
  724. CanBeAutoscaled: func(kind unversioned.GroupKind) error {
  725. switch kind {
  726. case api.Kind("ReplicationController"), extensions.Kind("Deployment"), extensions.Kind("ReplicaSet"):
  727. // nothing to do here
  728. default:
  729. return fmt.Errorf("cannot autoscale a %v", kind)
  730. }
  731. return nil
  732. },
  733. AttachablePodForObject: func(object runtime.Object) (*api.Pod, error) {
  734. client, err := clients.ClientForVersion(nil)
  735. if err != nil {
  736. return nil, err
  737. }
  738. switch t := object.(type) {
  739. case *api.ReplicationController:
  740. selector := labels.SelectorFromSet(t.Spec.Selector)
  741. sortBy := func(pods []*api.Pod) sort.Interface { return sort.Reverse(controller.ActivePods(pods)) }
  742. pod, _, err := GetFirstPod(client, t.Namespace, selector, 1*time.Minute, sortBy)
  743. return pod, err
  744. case *extensions.Deployment:
  745. selector, err := unversioned.LabelSelectorAsSelector(t.Spec.Selector)
  746. if err != nil {
  747. return nil, fmt.Errorf("invalid label selector: %v", err)
  748. }
  749. sortBy := func(pods []*api.Pod) sort.Interface { return sort.Reverse(controller.ActivePods(pods)) }
  750. pod, _, err := GetFirstPod(client, t.Namespace, selector, 1*time.Minute, sortBy)
  751. return pod, err
  752. case *batch.Job:
  753. selector, err := unversioned.LabelSelectorAsSelector(t.Spec.Selector)
  754. if err != nil {
  755. return nil, fmt.Errorf("invalid label selector: %v", err)
  756. }
  757. sortBy := func(pods []*api.Pod) sort.Interface { return sort.Reverse(controller.ActivePods(pods)) }
  758. pod, _, err := GetFirstPod(client, t.Namespace, selector, 1*time.Minute, sortBy)
  759. return pod, err
  760. case *api.Pod:
  761. return t, nil
  762. default:
  763. gvks, _, err := api.Scheme.ObjectKinds(object)
  764. if err != nil {
  765. return nil, err
  766. }
  767. return nil, fmt.Errorf("cannot attach to %v: not implemented", gvks[0])
  768. }
  769. },
  770. // UpdatePodSpecForObject update the pod specification for the provided object
  771. UpdatePodSpecForObject: func(obj runtime.Object, fn func(*api.PodSpec) error) (bool, error) {
  772. // TODO: replace with a swagger schema based approach (identify pod template via schema introspection)
  773. switch t := obj.(type) {
  774. case *api.Pod:
  775. return true, fn(&t.Spec)
  776. case *api.ReplicationController:
  777. if t.Spec.Template == nil {
  778. t.Spec.Template = &api.PodTemplateSpec{}
  779. }
  780. return true, fn(&t.Spec.Template.Spec)
  781. case *extensions.Deployment:
  782. return true, fn(&t.Spec.Template.Spec)
  783. case *extensions.DaemonSet:
  784. return true, fn(&t.Spec.Template.Spec)
  785. case *extensions.ReplicaSet:
  786. return true, fn(&t.Spec.Template.Spec)
  787. case *apps.PetSet:
  788. return true, fn(&t.Spec.Template.Spec)
  789. case *batch.Job:
  790. return true, fn(&t.Spec.Template.Spec)
  791. default:
  792. return false, fmt.Errorf("the object is not a pod or does not have a pod template")
  793. }
  794. },
  795. EditorEnvs: func() []string {
  796. return []string{"KUBE_EDITOR", "EDITOR"}
  797. },
  798. PrintObjectSpecificMessage: func(obj runtime.Object, out io.Writer) {
  799. switch obj := obj.(type) {
  800. case *api.Service:
  801. if obj.Spec.Type == api.ServiceTypeNodePort {
  802. msg := fmt.Sprintf(
  803. `You have exposed your service on an external port on all nodes in your
  804. cluster. If you want to expose this service to the external internet, you may
  805. need to set up firewall rules for the service port(s) (%s) to serve traffic.
  806. See http://releases.k8s.io/release-1.4/docs/user-guide/services-firewalls.md for more details.
  807. `,
  808. makePortsString(obj.Spec.Ports, true))
  809. out.Write([]byte(msg))
  810. }
  811. if _, ok := obj.Annotations[service.AnnotationLoadBalancerSourceRangesKey]; ok {
  812. msg := fmt.Sprintf(
  813. `You are using service annotation [service.beta.kubernetes.io/load-balancer-source-ranges].
  814. It has been promoted to field [loadBalancerSourceRanges] in service spec. This annotation will be deprecated in the future.
  815. Please use the loadBalancerSourceRanges field instead.
  816. See http://releases.k8s.io/release-1.4/docs/user-guide/services-firewalls.md for more details.
  817. `)
  818. out.Write([]byte(msg))
  819. }
  820. }
  821. },
  822. }
  823. }
  824. // GetFirstPod returns a pod matching the namespace and label selector
  825. // and the number of all pods that match the label selector.
  826. func GetFirstPod(client client.PodsNamespacer, namespace string, selector labels.Selector, timeout time.Duration, sortBy func([]*api.Pod) sort.Interface) (*api.Pod, int, error) {
  827. options := api.ListOptions{LabelSelector: selector}
  828. podList, err := client.Pods(namespace).List(options)
  829. if err != nil {
  830. return nil, 0, err
  831. }
  832. pods := []*api.Pod{}
  833. for i := range podList.Items {
  834. pod := podList.Items[i]
  835. pods = append(pods, &pod)
  836. }
  837. if len(pods) > 0 {
  838. sort.Sort(sortBy(pods))
  839. return pods[0], len(podList.Items), nil
  840. }
  841. // Watch until we observe a pod
  842. options.ResourceVersion = podList.ResourceVersion
  843. w, err := client.Pods(namespace).Watch(options)
  844. if err != nil {
  845. return nil, 0, err
  846. }
  847. defer w.Stop()
  848. condition := func(event watch.Event) (bool, error) {
  849. return event.Type == watch.Added || event.Type == watch.Modified, nil
  850. }
  851. event, err := watch.Until(timeout, w, condition)
  852. if err != nil {
  853. return nil, 0, err
  854. }
  855. pod, ok := event.Object.(*api.Pod)
  856. if !ok {
  857. return nil, 0, fmt.Errorf("%#v is not a pod event", event)
  858. }
  859. return pod, 1, nil
  860. }
  861. // Command will stringify and return all environment arguments ie. a command run by a client
  862. // using the factory.
  863. // TODO: We need to filter out stuff like secrets.
  864. func (f *Factory) Command() string {
  865. if len(os.Args) == 0 {
  866. return ""
  867. }
  868. base := filepath.Base(os.Args[0])
  869. args := append([]string{base}, os.Args[1:]...)
  870. return strings.Join(args, " ")
  871. }
  872. // BindFlags adds any flags that are common to all kubectl sub commands.
  873. func (f *Factory) BindFlags(flags *pflag.FlagSet) {
  874. // Merge factory's flags
  875. flags.AddFlagSet(f.flags)
  876. // Globally persistent flags across all subcommands.
  877. // TODO Change flag names to consts to allow safer lookup from subcommands.
  878. // TODO Add a verbose flag that turns on glog logging. Probably need a way
  879. // to do that automatically for every subcommand.
  880. flags.BoolVar(&f.clients.matchVersion, FlagMatchBinaryVersion, false, "Require server version to match client version")
  881. // Normalize all flags that are coming from other packages or pre-configurations
  882. // a.k.a. change all "_" to "-". e.g. glog package
  883. flags.SetNormalizeFunc(utilflag.WordSepNormalizeFunc)
  884. }
  885. // BindCommonFlags adds any flags defined by external projects (not part of pflags)
  886. func (f *Factory) BindExternalFlags(flags *pflag.FlagSet) {
  887. // any flags defined by external projects (not part of pflags)
  888. flags.AddGoFlagSet(flag.CommandLine)
  889. }
  890. func makePortsString(ports []api.ServicePort, useNodePort bool) string {
  891. pieces := make([]string, len(ports))
  892. for ix := range ports {
  893. var port int32
  894. if useNodePort {
  895. port = ports[ix].NodePort
  896. } else {
  897. port = ports[ix].Port
  898. }
  899. pieces[ix] = fmt.Sprintf("%s:%d", strings.ToLower(string(ports[ix].Protocol)), port)
  900. }
  901. return strings.Join(pieces, ",")
  902. }
  903. func getPorts(spec api.PodSpec) []string {
  904. result := []string{}
  905. for _, container := range spec.Containers {
  906. for _, port := range container.Ports {
  907. result = append(result, strconv.Itoa(int(port.ContainerPort)))
  908. }
  909. }
  910. return result
  911. }
  912. func getProtocols(spec api.PodSpec) map[string]string {
  913. result := make(map[string]string)
  914. for _, container := range spec.Containers {
  915. for _, port := range container.Ports {
  916. result[strconv.Itoa(int(port.ContainerPort))] = string(port.Protocol)
  917. }
  918. }
  919. return result
  920. }
  921. // Extracts the ports exposed by a service from the given service spec.
  922. func getServicePorts(spec api.ServiceSpec) []string {
  923. result := []string{}
  924. for _, servicePort := range spec.Ports {
  925. result = append(result, strconv.Itoa(int(servicePort.Port)))
  926. }
  927. return result
  928. }
  929. // Extracts the protocols exposed by a service from the given service spec.
  930. func getServiceProtocols(spec api.ServiceSpec) map[string]string {
  931. result := make(map[string]string)
  932. for _, servicePort := range spec.Ports {
  933. result[strconv.Itoa(int(servicePort.Port))] = string(servicePort.Protocol)
  934. }
  935. return result
  936. }
  937. type clientSwaggerSchema struct {
  938. c *client.Client
  939. fedc *restclient.RESTClient
  940. cacheDir string
  941. mapper meta.RESTMapper
  942. }
  943. const schemaFileName = "schema.json"
  944. type schemaClient interface {
  945. Get() *restclient.Request
  946. }
  947. func recursiveSplit(dir string) []string {
  948. parent, file := path.Split(dir)
  949. if len(parent) == 0 {
  950. return []string{file}
  951. }
  952. return append(recursiveSplit(parent[:len(parent)-1]), file)
  953. }
  954. func substituteUserHome(dir string) (string, error) {
  955. if len(dir) == 0 || dir[0] != '~' {
  956. return dir, nil
  957. }
  958. parts := recursiveSplit(dir)
  959. if len(parts[0]) == 1 {
  960. parts[0] = os.Getenv("HOME")
  961. } else {
  962. usr, err := user.Lookup(parts[0][1:])
  963. if err != nil {
  964. return "", err
  965. }
  966. parts[0] = usr.HomeDir
  967. }
  968. return path.Join(parts...), nil
  969. }
  970. func writeSchemaFile(schemaData []byte, cacheDir, cacheFile, prefix, groupVersion string) error {
  971. if err := os.MkdirAll(path.Join(cacheDir, prefix, groupVersion), 0755); err != nil {
  972. return err
  973. }
  974. tmpFile, err := ioutil.TempFile(cacheDir, "schema")
  975. if err != nil {
  976. // If we can't write, keep going.
  977. if os.IsPermission(err) {
  978. return nil
  979. }
  980. return err
  981. }
  982. if _, err := io.Copy(tmpFile, bytes.NewBuffer(schemaData)); err != nil {
  983. return err
  984. }
  985. if err := os.Link(tmpFile.Name(), cacheFile); err != nil {
  986. // If we can't write due to file existing, or permission problems, keep going.
  987. if os.IsExist(err) || os.IsPermission(err) {
  988. return nil
  989. }
  990. return err
  991. }
  992. return nil
  993. }
  994. func getSchemaAndValidate(c schemaClient, data []byte, prefix, groupVersion, cacheDir string, delegate validation.Schema) (err error) {
  995. var schemaData []byte
  996. var firstSeen bool
  997. fullDir, err := substituteUserHome(cacheDir)
  998. if err != nil {
  999. return err
  1000. }
  1001. cacheFile := path.Join(fullDir, prefix, groupVersion, schemaFileName)
  1002. if len(cacheDir) != 0 {
  1003. if schemaData, err = ioutil.ReadFile(cacheFile); err != nil && !os.IsNotExist(err) {
  1004. return err
  1005. }
  1006. }
  1007. if schemaData == nil {
  1008. firstSeen = true
  1009. schemaData, err = downloadSchemaAndStore(c, cacheDir, fullDir, cacheFile, prefix, groupVersion)
  1010. if err != nil {
  1011. return err
  1012. }
  1013. }
  1014. schema, err := validation.NewSwaggerSchemaFromBytes(schemaData, delegate)
  1015. if err != nil {
  1016. return err
  1017. }
  1018. err = schema.ValidateBytes(data)
  1019. if _, ok := err.(validation.TypeNotFoundError); ok && !firstSeen {
  1020. // As a temporary hack, kubectl would re-get the schema if validation
  1021. // fails for type not found reason.
  1022. // TODO: runtime-config settings needs to make into the file's name
  1023. schemaData, err = downloadSchemaAndStore(c, cacheDir, fullDir, cacheFile, prefix, groupVersion)
  1024. if err != nil {
  1025. return err
  1026. }
  1027. schema, err := validation.NewSwaggerSchemaFromBytes(schemaData, delegate)
  1028. if err != nil {
  1029. return err
  1030. }
  1031. return schema.ValidateBytes(data)
  1032. }
  1033. return err
  1034. }
  1035. // Download swagger schema from apiserver and store it to file.
  1036. func downloadSchemaAndStore(c schemaClient, cacheDir, fullDir, cacheFile, prefix, groupVersion string) (schemaData []byte, err error) {
  1037. schemaData, err = c.Get().
  1038. AbsPath("/swaggerapi", prefix, groupVersion).
  1039. Do().
  1040. Raw()
  1041. if err != nil {
  1042. return
  1043. }
  1044. if len(cacheDir) != 0 {
  1045. if err = writeSchemaFile(schemaData, fullDir, cacheFile, prefix, groupVersion); err != nil {
  1046. return
  1047. }
  1048. }
  1049. return
  1050. }
  1051. func (c *clientSwaggerSchema) ValidateBytes(data []byte) error {
  1052. gvk, err := json.DefaultMetaFactory.Interpret(data)
  1053. if err != nil {
  1054. return err
  1055. }
  1056. if ok := registered.IsEnabledVersion(gvk.GroupVersion()); !ok {
  1057. return fmt.Errorf("API version %q isn't supported, only supports API versions %q", gvk.GroupVersion().String(), registered.EnabledVersions())
  1058. }
  1059. switch gvk.Group {
  1060. case autoscaling.GroupName:
  1061. if c.c.AutoscalingClient == nil {
  1062. return errors.New("unable to validate: no autoscaling client")
  1063. }
  1064. return getSchemaAndValidate(c.c.AutoscalingClient.RESTClient, data, "apis/", gvk.GroupVersion().String(), c.cacheDir, c)
  1065. case policy.GroupName:
  1066. if c.c.PolicyClient == nil {
  1067. return errors.New("unable to validate: no policy client")
  1068. }
  1069. return getSchemaAndValidate(c.c.PolicyClient.RESTClient, data, "apis/", gvk.GroupVersion().String(), c.cacheDir, c)
  1070. case apps.GroupName:
  1071. if c.c.AppsClient == nil {
  1072. return errors.New("unable to validate: no apps client")
  1073. }
  1074. return getSchemaAndValidate(c.c.AppsClient.RESTClient, data, "apis/", gvk.GroupVersion().String(), c.cacheDir, c)
  1075. case batch.GroupName:
  1076. if c.c.BatchClient == nil {
  1077. return errors.New("unable to validate: no batch client")
  1078. }
  1079. return getSchemaAndValidate(c.c.BatchClient.RESTClient, data, "apis/", gvk.GroupVersion().String(), c.cacheDir, c)
  1080. case rbac.GroupName:
  1081. if c.c.RbacClient == nil {
  1082. return errors.New("unable to validate: no rbac client")
  1083. }
  1084. return getSchemaAndValidate(c.c.RbacClient.RESTClient, data, "apis/", gvk.GroupVersion().String(), c.cacheDir, c)
  1085. }
  1086. if registered.IsThirdPartyAPIGroupVersion(gvk.GroupVersion()) {
  1087. // Don't attempt to validate third party objects
  1088. return nil
  1089. }
  1090. switch gvk.Group {
  1091. case extensions.GroupName:
  1092. if c.c.ExtensionsClient == nil {
  1093. return errors.New("unable to validate: no experimental client")
  1094. }
  1095. return getSchemaAndValidate(c.c.ExtensionsClient.RESTClient, data, "apis/", gvk.GroupVersion().String(), c.cacheDir, c)
  1096. case federation.GroupName:
  1097. if c.fedc == nil {
  1098. return errors.New("unable to validate: no federation client")
  1099. }
  1100. return getSchemaAndValidate(c.fedc, data, "apis/", gvk.GroupVersion().String(), c.cacheDir, c)
  1101. case certificates.GroupName:
  1102. if c.c.CertificatesClient == nil {
  1103. return errors.New("unable to validate: no certificates client")
  1104. }
  1105. return getSchemaAndValidate(c.c.CertificatesClient.RESTClient, data, "apis/", gvk.GroupVersion().String(), c.cacheDir, c)
  1106. }
  1107. return getSchemaAndValidate(c.c.RESTClient, data, "api", gvk.GroupVersion().String(), c.cacheDir, c)
  1108. }
  1109. // DefaultClientConfig creates a clientcmd.ClientConfig with the following hierarchy:
  1110. // 1. Use the kubeconfig builder. The number of merges and overrides here gets a little crazy. Stay with me.
  1111. // 1. Merge the kubeconfig itself. This is done with the following hierarchy rules:
  1112. // 1. CommandLineLocation - this parsed from the command line, so it must be late bound. If you specify this,
  1113. // then no other kubeconfig files are merged. This file must exist.
  1114. // 2. If $KUBECONFIG is set, then it is treated as a list of files that should be merged.
  1115. // 3. HomeDirectoryLocation
  1116. // Empty filenames are ignored. Files with non-deserializable content produced errors.
  1117. // The first file to set a particular value or map key wins and the value or map key is never changed.
  1118. // This means that the first file to set CurrentContext will have its context preserved. It also means
  1119. // that if two files specify a "red-user", only values from the first file's red-user are used. Even
  1120. // non-conflicting entries from the second file's "red-user" are discarded.
  1121. // 2. Determine the context to use based on the first hit in this chain
  1122. // 1. command line argument - again, parsed from the command line, so it must be late bound
  1123. // 2. CurrentContext from the merged kubeconfig file
  1124. // 3. Empty is allowed at this stage
  1125. // 3. Determine the cluster info and auth info to use. At this point, we may or may not have a context. They
  1126. // are built based on the first hit in this chain. (run it twice, once for auth, once for cluster)
  1127. // 1. command line argument
  1128. // 2. If context is present, then use the context value
  1129. // 3. Empty is allowed
  1130. // 4. Determine the actual cluster info to use. At this point, we may or may not have a cluster info. Build
  1131. // each piece of the cluster info based on the chain:
  1132. // 1. command line argument
  1133. // 2. If cluster info is present and a value for the attribute is present, use it.
  1134. // 3. If you don't have a server location, bail.
  1135. // 5. Auth info is build using the same rules as cluster info, EXCEPT that you can only have one authentication
  1136. // technique per auth info. The following conditions result in an error:
  1137. // 1. If there are two conflicting techniques specified from the command line, fail.
  1138. // 2. If the command line does not specify one, and the auth info has conflicting techniques, fail.
  1139. // 3. If the command line specifies one and the auth info specifies another, honor the command line technique.
  1140. // 2. Use default values and potentially prompt for auth information
  1141. //
  1142. // However, if it appears that we're running in a kubernetes cluster
  1143. // container environment, then run with the auth info kubernetes mounted for
  1144. // us. Specifically:
  1145. // The env vars KUBERNETES_SERVICE_HOST and KUBERNETES_SERVICE_PORT are
  1146. // set, and the file /var/run/secrets/kubernetes.io/serviceaccount/token
  1147. // exists and is not a directory.
  1148. func DefaultClientConfig(flags *pflag.FlagSet) clientcmd.ClientConfig {
  1149. loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
  1150. flags.StringVar(&loadingRules.ExplicitPath, "kubeconfig", "", "Path to the kubeconfig file to use for CLI requests.")
  1151. overrides := &clientcmd.ConfigOverrides{}
  1152. // use the standard defaults for this client config
  1153. mergo.Merge(&overrides.ClusterDefaults, clientcmd.DefaultCluster)
  1154. flagNames := clientcmd.RecommendedConfigOverrideFlags("")
  1155. // short flagnames are disabled by default. These are here for compatibility with existing scripts
  1156. flagNames.ClusterOverrideFlags.APIServer.ShortName = "s"
  1157. clientcmd.BindOverrideFlags(overrides, flags, flagNames)
  1158. clientConfig := clientcmd.NewInteractiveDeferredLoadingClientConfig(loadingRules, overrides, os.Stdin)
  1159. return clientConfig
  1160. }
  1161. // PrintObject prints an api object given command line flags to modify the output format
  1162. func (f *Factory) PrintObject(cmd *cobra.Command, mapper meta.RESTMapper, obj runtime.Object, out io.Writer) error {
  1163. gvks, _, err := api.Scheme.ObjectKinds(obj)
  1164. if err != nil {
  1165. return err
  1166. }
  1167. mapping, err := mapper.RESTMapping(gvks[0].GroupKind())
  1168. if err != nil {
  1169. return err
  1170. }
  1171. printer, err := f.PrinterForMapping(cmd, mapping, false)
  1172. if err != nil {
  1173. return err
  1174. }
  1175. return printer.PrintObj(obj, out)
  1176. }
  1177. // PrinterForMapping returns a printer suitable for displaying the provided resource type.
  1178. // Requires that printer flags have been added to cmd (see AddPrinterFlags).
  1179. func (f *Factory) PrinterForMapping(cmd *cobra.Command, mapping *meta.RESTMapping, withNamespace bool) (kubectl.ResourcePrinter, error) {
  1180. printer, ok, err := PrinterForCommand(cmd)
  1181. if err != nil {
  1182. return nil, err
  1183. }
  1184. if ok {
  1185. clientConfig, err := f.ClientConfig()
  1186. if err != nil {
  1187. return nil, err
  1188. }
  1189. version, err := OutputVersion(cmd, clientConfig.GroupVersion)
  1190. if err != nil {
  1191. return nil, err
  1192. }
  1193. if version.Empty() && mapping != nil {
  1194. version = mapping.GroupVersionKind.GroupVersion()
  1195. }
  1196. if version.Empty() {
  1197. return nil, fmt.Errorf("you must specify an output-version when using this output format")
  1198. }
  1199. if mapping != nil {
  1200. printer = kubectl.NewVersionedPrinter(printer, mapping.ObjectConvertor, version, mapping.GroupVersionKind.GroupVersion())
  1201. }
  1202. } else {
  1203. // Some callers do not have "label-columns" so we can't use the GetFlagStringSlice() helper
  1204. columnLabel, err := cmd.Flags().GetStringSlice("label-columns")
  1205. if err != nil {
  1206. columnLabel = []string{}
  1207. }
  1208. printer, err = f.Printer(mapping, kubectl.PrintOptions{
  1209. NoHeaders: GetFlagBool(cmd, "no-headers"),
  1210. WithNamespace: withNamespace,
  1211. Wide: GetWideFlag(cmd),
  1212. ShowAll: GetFlagBool(cmd, "show-all"),
  1213. ShowLabels: GetFlagBool(cmd, "show-labels"),
  1214. AbsoluteTimestamps: isWatch(cmd),
  1215. ColumnLabels: columnLabel,
  1216. })
  1217. if err != nil {
  1218. return nil, err
  1219. }
  1220. printer = maybeWrapSortingPrinter(cmd, printer)
  1221. }
  1222. return printer, nil
  1223. }
  1224. // One stop shopping for a Builder
  1225. func (f *Factory) NewBuilder(thirdPartyDiscovery bool) *resource.Builder {
  1226. mapper, typer := f.Object(thirdPartyDiscovery)
  1227. return resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true))
  1228. }