{"action":"created","comment":{"url":"https://api.github.com/repos/kubernetes/kubernetes/pulls/comments/37081907","id":37081907,"diff_hunk":"@@ -0,0 +1,551 @@\n+/*\n+Copyright 2015 The Kubernetes Authors All rights reserved.\n+\n+Licensed under the Apache License, Version 2.0 (the \"License\");\n+you may not use this file except in compliance with the License.\n+You may obtain a copy of the License at\n+\n+    http://www.apache.org/licenses/LICENSE-2.0\n+\n+Unless required by applicable law or agreed to in writing, software\n+distributed under the License is distributed on an \"AS IS\" BASIS,\n+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n+See the License for the specific language governing permissions and\n+limitations under the License.\n+*/\n+\n+package kubectl\n+\n+import (\n+\t\"fmt\"\n+\t\"reflect\"\n+\t\"strings\"\n+\t\"time\"\n+\n+\t\"k8s.io/kubernetes/pkg/api\"\n+\t\"k8s.io/kubernetes/pkg/runtime\"\n+\t\"k8s.io/kubernetes/pkg/util\"\n+\t\"k8s.io/kubernetes/pkg/volume\"\n+\t\"k8s.io/kubernetes/pkg/watch\"\n+)\n+\n+type columnDefinition struct {\n+\tlabel     string\n+\tcontent   interface{}\n+\tcondition func(printContext) bool\n+}\n+\n+type printContext struct {\n+\twithNamespace bool\n+\twithEvent     bool\n+\twide          bool\n+\tcolumnLabels  []string\n+}\n+\n+func vof(i interface{}) reflect.Value {\n+\treturn reflect.ValueOf(i)\n+}\n+\n+func getStringField(obj runtime.Object, name string) string {\n+\treturn reflect.ValueOf(obj).Elem().FieldByName(name).String()\n+}\n+\n+func getInterfaceField(obj runtime.Object, name string) interface{} {\n+\treturn reflect.ValueOf(obj).Elem().FieldByName(name).Interface()\n+}\n+\n+func makeUpdateColumn(event *watch.EventType) columnDefinition {\n+\treturn columnDefinition{\n+\t\tlabel:   \"UPDATE\",\n+\t\tcontent: func(c printContext) string { return eventToString(event) }}\n+}\n+\n+func makeNameColumn(name string) columnDefinition {\n+\treturn columnDefinition{\n+\t\tlabel:   name,\n+\t\tcontent: func(obj runtime.Object) string { return getStringField(obj, \"Name\") }}\n+}\n+\n+var namespaceColumn = columnDefinition{\n+\tlabel:     \"NAMESPACE\",\n+\tcontent:   func(obj runtime.Object) string { return getStringField(obj, \"Namespace\") },\n+\tcondition: func(c printContext) bool { return c.withNamespace }}\n+\n+var noNamespaceColumn = columnDefinition{\n+\tlabel:     \"NAMESPACE\",\n+\tcontent:   func(obj runtime.Object) error { return fmt.Errorf(\"Namespace not supported\") },\n+\tcondition: func(c printContext) bool { return c.withNamespace }}\n+\n+var ageColumn = columnDefinition{\n+\tlabel: \"AGE\",\n+\tcontent: func(obj runtime.Object) string {\n+\t\treturn translateTimestamp(getInterfaceField(obj, \"CreationTimestamp\").(util.Time))\n+\t}}\n+\n+func makeLabelColumn(labelName string) columnDefinition {\n+\tsplitLabelName := strings.Split(labelName, \"/\")\n+\tcolumnLabel := strings.ToUpper(splitLabelName[len(splitLabelName)-1])\n+\treturn columnDefinition{\n+\t\tlabel: columnLabel,\n+\t\tcontent: func(obj runtime.Object) string {\n+\t\t\tlabelsMap := reflect.ValueOf(obj).Elem().FieldByName(\"Labels\")\n+\t\t\tval := labelsMap.MapIndex(vof(labelName))\n+\t\t\tif val.IsValid() {\n+\t\t\t\treturn val.String()\n+\t\t\t} else {\n+\t\t\t\treturn \"<n/a>\"\n+\t\t\t}\n+\t\t}}\n+}\n+\n+var podColumnsFunc = []columnDefinition{\n+\tnamespaceColumn,\n+\tmakeNameColumn(\"NAME\"),\n+\t{label: \"READY\", content: func(pod *api.Pod) string {\n+\t\treadyContainers := 0\n+\t\tfor i := 0; i < len(pod.Status.ContainerStatuses); i++ {\n+\t\t\tcontainer := pod.Status.ContainerStatuses[i]\n+\t\t\tif container.Ready && container.State.Running != nil {\n+\t\t\t\treadyContainers++\n+\t\t\t}\n+\t\t}\n+\t\ttotalContainers := len(pod.Spec.Containers)\n+\t\treturn fmt.Sprintf(\"%d/%d\", readyContainers, totalContainers)\n+\t}},\n+\t{label: \"STATUS\", content: func(pod *api.Pod) string {\n+\t\treason := string(pod.Status.Phase)\n+\t\tif pod.Status.Reason != \"\" {\n+\t\t\treason = pod.Status.Reason\n+\t\t}\n+\t\tfor i := len(pod.Status.ContainerStatuses) - 1; i >= 0; i-- {\n+\t\t\tcontainer := pod.Status.ContainerStatuses[i]\n+\n+\t\t\tif container.State.Waiting != nil && container.State.Waiting.Reason != \"\" {\n+\t\t\t\treason = container.State.Waiting.Reason\n+\t\t\t} else if container.State.Terminated != nil && container.State.Terminated.Reason != \"\" {\n+\t\t\t\treason = container.State.Terminated.Reason\n+\t\t\t} else if container.State.Terminated != nil && container.State.Terminated.Reason == \"\" {\n+\t\t\t\tif container.State.Terminated.Signal != 0 {\n+\t\t\t\t\treason = fmt.Sprintf(\"Signal:%d\", container.State.Terminated.Signal)\n+\t\t\t\t} else {\n+\t\t\t\t\treason = fmt.Sprintf(\"ExitCode:%d\", container.State.Terminated.ExitCode)\n+\t\t\t\t}\n+\t\t\t}\n+\t\t}\n+\t\treturn reason\n+\t}},\n+\t{label: \"RESTARTS\", content: func(pod *api.Pod) string {\n+\t\trestarts := 0\n+\t\tfor i := 0; i < len(pod.Status.ContainerStatuses); i++ {\n+\t\t\trestarts += pod.Status.ContainerStatuses[i].RestartCount\n+\t\t}\n+\t\treturn fmt.Sprintf(\"%d\", restarts)\n+\t}},\n+\tageColumn,\n+\t{label: \"NODE\", content: func(pod *api.Pod) string { return pod.Spec.NodeName },\n+\t\tcondition: func(c printContext) bool { return c.wide }},\n+}\n+\n+var podTemplateColumnsFunc = []columnDefinition{\n+\tnamespaceColumn,\n+\tmakeNameColumn(\"TEMPLATE\"),\n+\t{label: \"CONTAINER(S)\", content: func(pod *api.PodTemplate) []string {\n+\t\tresult := []string{}\n+\t\tfor _, container := range pod.Template.Spec.Containers {\n+\t\t\tresult = append(result, container.Name)\n+\t\t}\n+\t\treturn result\n+\t}},\n+\t{label: \"IMAGE(S)\", content: func(pod *api.PodTemplate) []string {\n+\t\tresult := []string{}\n+\t\tfor _, container := range pod.Template.Spec.Containers {\n+\t\t\tresult = append(result, container.Image)\n+\t\t}\n+\t\treturn result\n+\t}},\n+\t{label: \"PODLABELS\", content: func(pod *api.PodTemplate) string { return formatLabels(pod.Template.Labels) }},\n+}\n+\n+var replicationControllerColumnsFunc = []columnDefinition{\n+\tnamespaceColumn,\n+\tmakeNameColumn(\"CONTROLLER\"),\n+\t{label: \"CONTAINER(S)\", content: func(rc *api.ReplicationController) []string {\n+\t\tresult := []string{}\n+\t\tfor _, container := range rc.Spec.Template.Spec.Containers {\n+\t\t\tresult = append(result, container.Name)\n+\t\t}\n+\t\treturn result\n+\t}},\n+\t{label: \"IMAGE(S)\", content: func(rc *api.ReplicationController) []string {\n+\t\tresult := []string{}\n+\t\tfor _, container := range rc.Spec.Template.Spec.Containers {\n+\t\t\tresult = append(result, container.Image)\n+\t\t}\n+\t\treturn result\n+\t}},\n+\t{label: \"SELECTOR\", content: func(rc *api.ReplicationController) string { return formatLabels(rc.Spec.Selector) }},\n+\t{label: \"REPLICAS\", content: func(rc *api.ReplicationController) string { return fmt.Sprintf(\"%d\", rc.Spec.Replicas) }},\n+\tageColumn,\n+}\n+\n+var serviceColumnsFunc = []columnDefinition{\n+\tnamespaceColumn,\n+\tmakeNameColumn(\"NAME\"),\n+\t{label: \"CLUSTER_IP\", content: func(s *api.Service) string {\n+\t\treturn s.Spec.ClusterIP\n+\t}},\n+\t{label: \"EXTERNAL_IP\", content: func(s *api.Service) string {\n+\t\tswitch s.Spec.Type {\n+\t\tcase api.ServiceTypeClusterIP:\n+\t\t\treturn \"<none>\"\n+\t\tcase api.ServiceTypeNodePort:\n+\t\t\treturn \"nodes\"\n+\t\tcase api.ServiceTypeLoadBalancer:\n+\t\t\tingress := s.Status.LoadBalancer.Ingress\n+\t\t\tresult := []string{}\n+\t\t\tfor i := range ingress {\n+\t\t\t\tif ingress[i].IP != \"\" {\n+\t\t\t\t\tresult = append(result, ingress[i].IP)\n+\t\t\t\t}\n+\t\t\t}\n+\t\t\treturn strings.Join(result, \",\")\n+\t\t}\n+\t\treturn \"unknown\"\n+\t}},\n+\t{label: \"PORT(S)\", content: func(s *api.Service) string {\n+\t\tports := []string{}\n+\t\tfor _, port := range s.Spec.Ports {\n+\t\t\tports = append(ports, fmt.Sprintf(\"%d/%s\", port.Port, port.Protocol))\n+\t\t}\n+\t\treturn strings.Join(ports, \",\")\n+\t}},\n+\t{label: \"SELECTOR\", content: func(s *api.Service) string { return formatLabels(s.Spec.Selector) }},\n+\tageColumn,\n+}\n+\n+var endpointsColumnsFunc = []columnDefinition{\n+\tnamespaceColumn,\n+\tmakeNameColumn(\"NAME\"),\n+\t{label: \"ENDPOINTS\", content: func(e *api.Endpoints) string { return formatEndpoints(e, nil) }},\n+\tageColumn,\n+}\n+\n+var nodeColumnsFunc = []columnDefinition{\n+\tnoNamespaceColumn,\n+\tmakeNameColumn(\"NAME\"),\n+\t{label: \"LABELS\", content: func(node *api.Node) string { return formatLabels(node.Labels) }},\n+\t{label: \"STATUS\", content: func(node *api.Node) string {\n+\t\tconditionMap := make(map[api.NodeConditionType]*api.NodeCondition)\n+\t\tNodeAllConditions := []api.NodeConditionType{api.NodeReady}\n+\t\tfor i := range node.Status.Conditions {\n+\t\t\tcond := node.Status.Conditions[i]\n+\t\t\tconditionMap[cond.Type] = &cond\n+\t\t}\n+\t\tvar status []string\n+\t\tfor _, validCondition := range NodeAllConditions {\n+\t\t\tif condition, ok := conditionMap[validCondition]; ok {\n+\t\t\t\tif condition.Status == api.ConditionTrue {\n+\t\t\t\t\tstatus = append(status, string(condition.Type))\n+\t\t\t\t} else {\n+\t\t\t\t\tstatus = append(status, \"Not\"+string(condition.Type))\n+\t\t\t\t}\n+\t\t\t}\n+\t\t}\n+\t\tif len(status) == 0 {\n+\t\t\tstatus = append(status, \"Unknown\")\n+\t\t}\n+\t\tif node.Spec.Unschedulable {\n+\t\t\tstatus = append(status, \"SchedulingDisabled\")\n+\t\t}\n+\t\treturn strings.Join(status, \",\")\n+\t}},\n+\tageColumn,\n+}\n+\n+var eventColumnsFunc = []columnDefinition{\n+\tnamespaceColumn,\n+\t{label: \"FIRSTSEEN\", content: func(event *api.Event) string { return translateTimestamp(event.FirstTimestamp) }},\n+\t{label: \"LASTSEEN\", content: func(event *api.Event) string { return translateTimestamp(event.LastTimestamp) }},\n+\t{label: \"COUNT\", content: func(event *api.Event) string { return fmt.Sprintf(\"%d\", event.Count) }},\n+\t{label: \"NAME\", content: func(event *api.Event) string { return event.InvolvedObject.Name }},\n+\t{label: \"KIND\", content: func(event *api.Event) string { return event.InvolvedObject.Kind }},\n+\t{label: \"SUBOBJECT\", content: func(event *api.Event) string { return event.InvolvedObject.FieldPath }},\n+\t{label: \"REASON\", content: func(event *api.Event) string { return event.Reason }},\n+\t{label: \"SOURCE\", content: func(event *api.Event) string { return fmt.Sprintf(\"%s\", event.Source) }},\n+\t{label: \"MESSAGE\", content: func(event *api.Event) string { return event.Message }},\n+}\n+\n+var limitRangeColumnsFunc = []columnDefinition{\n+\tnamespaceColumn,\n+\tmakeNameColumn(\"NAME\"),\n+\tageColumn,\n+}\n+\n+var resourceQuotaColumnsFunc = []columnDefinition{\n+\tnamespaceColumn,\n+\tmakeNameColumn(\"NAME\"),\n+\tageColumn,\n+}\n+\n+var namespaceColumnsFunc = []columnDefinition{\n+\tnoNamespaceColumn,\n+\tmakeNameColumn(\"NAME\"),\n+\t{label: \"LABELS\", content: func(n *api.Namespace) string { return formatLabels(n.Labels) }},\n+\t{label: \"STATUS\", content: func(n *api.Namespace) string { return fmt.Sprintf(\"%s\", n.Status.Phase) }},\n+\tageColumn,\n+}\n+\n+var secretColumnsFunc = []columnDefinition{\n+\tnamespaceColumn,\n+\tmakeNameColumn(\"NAME\"),\n+\t{label: \"TYPE\", content: func(s *api.Secret) string { return fmt.Sprintf(\"%s\", s.Type) }},\n+\t{label: \"DATA\", content: func(s *api.Secret) string { return fmt.Sprintf(\"%d\", len(s.Data)) }},\n+\tageColumn,\n+}\n+\n+var serviceAccountColumnsFunc = []columnDefinition{\n+\tnamespaceColumn,\n+\tmakeNameColumn(\"NAME\"),\n+\t{label: \"SECRETS\", content: func(s *api.ServiceAccount) string { return fmt.Sprintf(\"%d\", len(s.Secrets)) }},\n+\tageColumn,\n+}\n+\n+var persistentVolumeColumnsFunc = []columnDefinition{\n+\tnoNamespaceColumn,\n+\tmakeNameColumn(\"NAME\"),\n+\t{label: \"LABELS\", content: func(p *api.PersistentVolume) string { return formatLabels(p.Labels) }},\n+\t{label: \"CAPACITY\", content: func(p *api.PersistentVolume) string {\n+\t\taQty := p.Spec.Capacity[api.ResourceStorage]\n+\t\taSize := aQty.Value()\n+\t\treturn fmt.Sprintf(\"%d\", aSize)\n+\t}},\n+\t{label: \"ACCESSMODES\", content: func(p *api.PersistentVolume) string { return volume.GetAccessModesAsString(p.Spec.AccessModes) }},\n+\t{label: \"STATUS\", content: func(p *api.PersistentVolume) string { return fmt.Sprintf(\"%s\", p.Status.Phase) }},\n+\t{label: \"CLAIM\", content: func(p *api.PersistentVolume) string {\n+\t\tclaimRefUID := \"\"\n+\t\tif p.Spec.ClaimRef != nil {\n+\t\t\tclaimRefUID += p.Spec.ClaimRef.Namespace\n+\t\t\tclaimRefUID += \"/\"\n+\t\t\tclaimRefUID += p.Spec.ClaimRef.Name\n+\t\t}\n+\t\treturn claimRefUID\n+\t}},\n+\t{label: \"REASON\", content: func(p *api.PersistentVolume) string { return p.Status.Reason }},\n+\tageColumn,\n+}\n+\n+var persistentVolumeClaimColumnsFunc = []columnDefinition{\n+\tnamespaceColumn,\n+\tmakeNameColumn(\"NAME\"),\n+\t{label: \"STATUS\", content: func(p *api.PersistentVolumeClaim) string { return fmt.Sprintf(\"%s\", p.Status.Phase) }},\n+\t{label: \"LABELS\", content: func(p *api.PersistentVolumeClaim) string { return formatLabels(p.Labels) }},\n+\t{label: \"VOLUME\", content: func(p *api.PersistentVolumeClaim) string { return p.Spec.VolumeName }},\n+\tageColumn,\n+}\n+\n+var componentStatus = []columnDefinition{\n+\tnoNamespaceColumn,\n+\tmakeNameColumn(\"NAME\"),\n+\t{label: \"STATUS\", content: func(cs *api.ComponentStatus) string {\n+\t\tstatus := \"Unknown\"\n+\t\tfor _, condition := range cs.Conditions {\n+\t\t\tif condition.Type == api.ComponentHealthy {\n+\t\t\t\tif condition.Status == api.ConditionTrue {\n+\t\t\t\t\tstatus = \"Healthy\"\n+\t\t\t\t} else {\n+\t\t\t\t\tstatus = \"Unhealthy\"\n+\t\t\t\t}\n+\t\t\t\tbreak\n+\t\t\t}\n+\t\t}\n+\t\treturn status\n+\t}},\n+\t{label: \"MESSAGE\", content: func(cs *api.ComponentStatus) string {\n+\t\tmessage := \"\"\n+\t\tfor _, condition := range cs.Conditions {\n+\t\t\tif condition.Type == api.ComponentHealthy {\n+\t\t\t\tmessage = condition.Message\n+\t\t\t\tbreak\n+\t\t\t}\n+\t\t}\n+\t\treturn message\n+\t}},\n+\t{label: \"ERROR\", content: func(cs *api.ComponentStatus) string {\n+\t\terror := \"\"\n+\t\tfor _, condition := range cs.Conditions {\n+\t\t\tif condition.Type == api.ComponentHealthy {\n+\t\t\t\terror = condition.Error\n+\t\t\t\tbreak\n+\t\t\t}\n+\t\t}\n+\t\treturn error\n+\t}},\n+}\n+\n+func buildColumns(obj runtime.Object, c printContext, columns []columnDefinition) (result [][]string, err error) {\n+\tobjType := reflect.TypeOf(obj)\n+\tcontextType := reflect.TypeOf(c)\n+\n+\tfor _, entry := range columns {\n+\t\tif entry.condition != nil && !entry.condition(c) {\n+\t\t\tcontinue\n+\t\t}\n+\t\tcontent := vof(entry.content)\n+\n+\t\tif content.Kind() != reflect.Func {\n+\t\t\terr = fmt.Errorf(\"invalid print handler. %#v is not a function\", entry.content)\n+\t\t\treturn\n+\t\t}\n+\t\tfuncType := content.Type()\n+\n+\t\targs := []reflect.Value{}\n+\t\tfor i := 0; i < funcType.NumIn(); i++ {\n+\t\t\tif funcType.In(i) == objType {\n+\t\t\t\targs = append(args, vof(obj))\n+\t\t\t\tcontinue\n+\t\t\t}\n+\t\t\tif funcType.In(i) == reflect.TypeOf((*runtime.Object)(nil)).Elem() {\n+\t\t\t\targs = append(args, vof(obj))\n+\t\t\t\tcontinue\n+\t\t\t}\n+\t\t\tif funcType.In(i) == contextType {\n+\t\t\t\targs = append(args, vof(c))\n+\t\t\t\tcontinue\n+\t\t\t}\n+\t\t\terr = fmt.Errorf(\"Cannot populate type %v for %s\", funcType.In(i), entry.label)\n+\t\t\treturn\n+\t\t}\n+\t\tresultValue := content.Call(args)\n+\n+\t\tif funcType.Out(0) == reflect.TypeOf((*string)(nil)).Elem() {\n+\t\t\tresult = append(result, []string{resultValue[0].String()})\n+\t\t} else if funcType.Out(0) == reflect.TypeOf((*[]string)(nil)).Elem() {\n+\t\t\tallRows := []string{}\n+\t\t\tfor i := 0; i < resultValue[0].Len(); i++ {\n+\t\t\t\tallRows = append(allRows, resultValue[0].Index(i).String())\n+\t\t\t}\n+\t\t\tresult = append(result, allRows)\n+\t\t} else if funcType.Out(0) == reflect.TypeOf((*error)(nil)).Elem() {\n+\t\t\tif !resultValue[0].IsNil() {\n+\t\t\t\terr, _ = resultValue[0].Interface().(error)\n+\t\t\t\treturn\n+\t\t\t}\n+\t\t} else {\n+\t\t\terr = fmt.Errorf(\"Wrong return type %v for %s\", funcType.In(0), entry.label)\n+\t\t\treturn\n+\t\t}\n+\n+\t\tif funcType.NumOut() == 2 {\n+\t\t\tif funcType.Out(1) == reflect.TypeOf((*error)(nil)).Elem() {\n+\t\t\t\tif !resultValue[1].IsNil() {\n+\t\t\t\t\terr, _ = resultValue[1].Interface().(error)\n+\t\t\t\t\treturn\n+\t\t\t\t}\n+\t\t\t} else {\n+\t\t\t\terr = fmt.Errorf(\"Wrong return type %v for %s\", funcType.In(1), entry.label)\n+\t\t\t\treturn\n+\t\t\t}\n+\t\t}\n+\t}\n+\treturn\n+}\n+\n+var columnMap = map[reflect.Type][]columnDefinition{\n+\treflect.TypeOf((*api.Pod)(nil)):                   podColumnsFunc,\n+\treflect.TypeOf((*api.PodTemplate)(nil)):           podTemplateColumnsFunc,\n+\treflect.TypeOf((*api.ReplicationController)(nil)): replicationControllerColumnsFunc,\n+\treflect.TypeOf((*api.Service)(nil)):               serviceColumnsFunc,\n+\treflect.TypeOf((*api.Endpoints)(nil)):             endpointsColumnsFunc,\n+\treflect.TypeOf((*api.Node)(nil)):                  nodeColumnsFunc,\n+\treflect.TypeOf((*api.Event)(nil)):                 eventColumnsFunc,\n+\treflect.TypeOf((*api.LimitRange)(nil)):            limitRangeColumnsFunc,\n+\treflect.TypeOf((*api.ResourceQuota)(nil)):         resourceQuotaColumnsFunc,\n+\treflect.TypeOf((*api.Namespace)(nil)):             namespaceColumnsFunc,\n+\treflect.TypeOf((*api.Secret)(nil)):                secretColumnsFunc,\n+\treflect.TypeOf((*api.ServiceAccount)(nil)):        serviceAccountColumnsFunc,\n+\treflect.TypeOf((*api.PersistentVolume)(nil)):      persistentVolumeColumnsFunc,\n+\treflect.TypeOf((*api.PersistentVolumeClaim)(nil)): persistentVolumeClaimColumnsFunc,\n+\treflect.TypeOf((*api.ComponentStatus)(nil)):       componentStatus,\n+}\n+\n+func getColumnsFor(obj runtime.Object, c printContext) (columns []columnDefinition, err error) {","path":"pkg/kubectl/resource_columns.go","position":471,"original_position":471,"commit_id":"9ee3cb421b21efe847be5974910ea282510a9ab8","original_commit_id":"9ee3cb421b21efe847be5974910ea282510a9ab8","user":{"login":"deads2k","id":8225098,"avatar_url":"https://avatars.githubusercontent.com/u/8225098?v=3","gravatar_id":"","url":"https://api.github.com/users/deads2k","html_url":"https://github.com/deads2k","followers_url":"https://api.github.com/users/deads2k/followers","following_url":"https://api.github.com/users/deads2k/following{/other_user}","gists_url":"https://api.github.com/users/deads2k/gists{/gist_id}","starred_url":"https://api.github.com/users/deads2k/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/deads2k/subscriptions","organizations_url":"https://api.github.com/users/deads2k/orgs","repos_url":"https://api.github.com/users/deads2k/repos","events_url":"https://api.github.com/users/deads2k/events{/privacy}","received_events_url":"https://api.github.com/users/deads2k/received_events","type":"User","site_admin":false},"body":"> And now, why do we need the extra type at all?\r\n\r\nWe aren't attaching functions to our API objects.  Type safety is an overwhelming benefit as people try to extend what has already been built and react to change downstream.  Type safety allows the compiler to immediately flag errors instead of locating them runtime.  It also makes it much easier for people not knowledgeable in the art to contribute.\r\n\r\nHaving structured types with the variability where it matters may take more lines of code, but being able to do things like directly, in the type itself identify what its displaying enhances locality and for extenders makes intent more clear.  Bundling related `ColumnPrinter` via direct calls instead of registration reduces the search area as you're trying to figure out where columns come from.\r\n\r\nThere is a tradeoff between lines of code and clarity of intent, but I'm concerned that this implementation places too much emphasis on the former at the expense of the latter.","created_at":"2015-08-14T14:34:29Z","updated_at":"2015-08-14T14:34:29Z","html_url":"https://github.com/kubernetes/kubernetes/pull/11695#discussion_r37081907","pull_request_url":"https://api.github.com/repos/kubernetes/kubernetes/pulls/11695","_links":{"self":{"href":"https://api.github.com/repos/kubernetes/kubernetes/pulls/comments/37081907"},"html":{"href":"https://github.com/kubernetes/kubernetes/pull/11695#discussion_r37081907"},"pull_request":{"href":"https://api.github.com/repos/kubernetes/kubernetes/pulls/11695"}}},"pull_request":{"url":"https://api.github.com/repos/kubernetes/kubernetes/pulls/11695","id":40571209,"html_url":"https://github.com/kubernetes/kubernetes/pull/11695","diff_url":"https://github.com/kubernetes/kubernetes/pull/11695.diff","patch_url":"https://github.com/kubernetes/kubernetes/pull/11695.patch","issue_url":"https://api.github.com/repos/kubernetes/kubernetes/issues/11695","number":11695,"state":"open","locked":false,"title":"pkg/kubectl/resource_printer.go refactoring","user":{"login":"mwielgus","id":11994812,"avatar_url":"https://avatars.githubusercontent.com/u/11994812?v=3","gravatar_id":"","url":"https://api.github.com/users/mwielgus","html_url":"https://github.com/mwielgus","followers_url":"https://api.github.com/users/mwielgus/followers","following_url":"https://api.github.com/users/mwielgus/following{/other_user}","gists_url":"https://api.github.com/users/mwielgus/gists{/gist_id}","starred_url":"https://api.github.com/users/mwielgus/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mwielgus/subscriptions","organizations_url":"https://api.github.com/users/mwielgus/orgs","repos_url":"https://api.github.com/users/mwielgus/repos","events_url":"https://api.github.com/users/mwielgus/events{/privacy}","received_events_url":"https://api.github.com/users/mwielgus/received_events","type":"User","site_admin":false},"body":"Do not merge (yet)\r\nref: #11617, #11440\r\n\r\nChanges:\r\n* column labels and content defined in one place `resource_columns.go`\r\n* clear view when which column is populated\r\n* less boilerplate code (automatically print Items from api.*List)\r\n* build [][]string for each object instead of writing directly to io.Writer\r\n* pass context-like struct with all \"global\" parameters instead of 5-6 separate params\r\n* pass `*EventType` (possibly nil) to the printer so `kubectl get * -w output` is understandable ","created_at":"2015-07-22T14:31:47Z","updated_at":"2015-08-14T14:34:29Z","closed_at":null,"merged_at":null,"merge_commit_sha":"6407407ca4b1cb7287ed2176ca6f20634f225606","assignee":{"login":"smarterclayton","id":1163175,"avatar_url":"https://avatars.githubusercontent.com/u/1163175?v=3","gravatar_id":"","url":"https://api.github.com/users/smarterclayton","html_url":"https://github.com/smarterclayton","followers_url":"https://api.github.com/users/smarterclayton/followers","following_url":"https://api.github.com/users/smarterclayton/following{/other_user}","gists_url":"https://api.github.com/users/smarterclayton/gists{/gist_id}","starred_url":"https://api.github.com/users/smarterclayton/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/smarterclayton/subscriptions","organizations_url":"https://api.github.com/users/smarterclayton/orgs","repos_url":"https://api.github.com/users/smarterclayton/repos","events_url":"https://api.github.com/users/smarterclayton/events{/privacy}","received_events_url":"https://api.github.com/users/smarterclayton/received_events","type":"User","site_admin":false},"milestone":null,"commits_url":"https://api.github.com/repos/kubernetes/kubernetes/pulls/11695/commits","review_comments_url":"https://api.github.com/repos/kubernetes/kubernetes/pulls/11695/comments","review_comment_url":"https://api.github.com/repos/kubernetes/kubernetes/pulls/comments{/number}","comments_url":"https://api.github.com/repos/kubernetes/kubernetes/issues/11695/comments","statuses_url":"https://api.github.com/repos/kubernetes/kubernetes/statuses/9ee3cb421b21efe847be5974910ea282510a9ab8","head":{"label":"mwielgus:kubectl_watch_refactor","ref":"kubectl_watch_refactor","sha":"9ee3cb421b21efe847be5974910ea282510a9ab8","user":{"login":"mwielgus","id":11994812,"avatar_url":"https://avatars.githubusercontent.com/u/11994812?v=3","gravatar_id":"","url":"https://api.github.com/users/mwielgus","html_url":"https://github.com/mwielgus","followers_url":"https://api.github.com/users/mwielgus/followers","following_url":"https://api.github.com/users/mwielgus/following{/other_user}","gists_url":"https://api.github.com/users/mwielgus/gists{/gist_id}","starred_url":"https://api.github.com/users/mwielgus/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mwielgus/subscriptions","organizations_url":"https://api.github.com/users/mwielgus/orgs","repos_url":"https://api.github.com/users/mwielgus/repos","events_url":"https://api.github.com/users/mwielgus/events{/privacy}","received_events_url":"https://api.github.com/users/mwielgus/received_events","type":"User","site_admin":false},"repo":{"id":34387979,"name":"kubernetes","full_name":"mwielgus/kubernetes","owner":{"login":"mwielgus","id":11994812,"avatar_url":"https://avatars.githubusercontent.com/u/11994812?v=3","gravatar_id":"","url":"https://api.github.com/users/mwielgus","html_url":"https://github.com/mwielgus","followers_url":"https://api.github.com/users/mwielgus/followers","following_url":"https://api.github.com/users/mwielgus/following{/other_user}","gists_url":"https://api.github.com/users/mwielgus/gists{/gist_id}","starred_url":"https://api.github.com/users/mwielgus/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mwielgus/subscriptions","organizations_url":"https://api.github.com/users/mwielgus/orgs","repos_url":"https://api.github.com/users/mwielgus/repos","events_url":"https://api.github.com/users/mwielgus/events{/privacy}","received_events_url":"https://api.github.com/users/mwielgus/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/mwielgus/kubernetes","description":"Container Cluster Manager from Google","fork":true,"url":"https://api.github.com/repos/mwielgus/kubernetes","forks_url":"https://api.github.com/repos/mwielgus/kubernetes/forks","keys_url":"https://api.github.com/repos/mwielgus/kubernetes/keys{/key_id}","collaborators_url":"https://api.github.com/repos/mwielgus/kubernetes/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/mwielgus/kubernetes/teams","hooks_url":"https://api.github.com/repos/mwielgus/kubernetes/hooks","issue_events_url":"https://api.github.com/repos/mwielgus/kubernetes/issues/events{/number}","events_url":"https://api.github.com/repos/mwielgus/kubernetes/events","assignees_url":"https://api.github.com/repos/mwielgus/kubernetes/assignees{/user}","branches_url":"https://api.github.com/repos/mwielgus/kubernetes/branches{/branch}","tags_url":"https://api.github.com/repos/mwielgus/kubernetes/tags","blobs_url":"https://api.github.com/repos/mwielgus/kubernetes/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/mwielgus/kubernetes/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/mwielgus/kubernetes/git/refs{/sha}","trees_url":"https://api.github.com/repos/mwielgus/kubernetes/git/trees{/sha}","statuses_url":"https://api.github.com/repos/mwielgus/kubernetes/statuses/{sha}","languages_url":"https://api.github.com/repos/mwielgus/kubernetes/languages","stargazers_url":"https://api.github.com/repos/mwielgus/kubernetes/stargazers","contributors_url":"https://api.github.com/repos/mwielgus/kubernetes/contributors","subscribers_url":"https://api.github.com/repos/mwielgus/kubernetes/subscribers","subscription_url":"https://api.github.com/repos/mwielgus/kubernetes/subscription","commits_url":"https://api.github.com/repos/mwielgus/kubernetes/commits{/sha}","git_commits_url":"https://api.github.com/repos/mwielgus/kubernetes/git/commits{/sha}","comments_url":"https://api.github.com/repos/mwielgus/kubernetes/comments{/number}","issue_comment_url":"https://api.github.com/repos/mwielgus/kubernetes/issues/comments{/number}","contents_url":"https://api.github.com/repos/mwielgus/kubernetes/contents/{+path}","compare_url":"https://api.github.com/repos/mwielgus/kubernetes/compare/{base}...{head}","merges_url":"https://api.github.com/repos/mwielgus/kubernetes/merges","archive_url":"https://api.github.com/repos/mwielgus/kubernetes/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/mwielgus/kubernetes/downloads","issues_url":"https://api.github.com/repos/mwielgus/kubernetes/issues{/number}","pulls_url":"https://api.github.com/repos/mwielgus/kubernetes/pulls{/number}","milestones_url":"https://api.github.com/repos/mwielgus/kubernetes/milestones{/number}","notifications_url":"https://api.github.com/repos/mwielgus/kubernetes/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/mwielgus/kubernetes/labels{/name}","releases_url":"https://api.github.com/repos/mwielgus/kubernetes/releases{/id}","created_at":"2015-04-22T12:04:42Z","updated_at":"2015-04-22T12:04:46Z","pushed_at":"2015-08-13T15:23:49Z","git_url":"git://github.com/mwielgus/kubernetes.git","ssh_url":"git@github.com:mwielgus/kubernetes.git","clone_url":"https://github.com/mwielgus/kubernetes.git","svn_url":"https://github.com/mwielgus/kubernetes","homepage":"http://kubernetes.io","size":99484,"stargazers_count":0,"watchers_count":0,"language":"Go","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"kubernetes:master","ref":"master","sha":"296ffdf925e0663a81cd28b0b324cf5af049bcb9","user":{"login":"kubernetes","id":13629408,"avatar_url":"https://avatars.githubusercontent.com/u/13629408?v=3","gravatar_id":"","url":"https://api.github.com/users/kubernetes","html_url":"https://github.com/kubernetes","followers_url":"https://api.github.com/users/kubernetes/followers","following_url":"https://api.github.com/users/kubernetes/following{/other_user}","gists_url":"https://api.github.com/users/kubernetes/gists{/gist_id}","starred_url":"https://api.github.com/users/kubernetes/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/kubernetes/subscriptions","organizations_url":"https://api.github.com/users/kubernetes/orgs","repos_url":"https://api.github.com/users/kubernetes/repos","events_url":"https://api.github.com/users/kubernetes/events{/privacy}","received_events_url":"https://api.github.com/users/kubernetes/received_events","type":"Organization","site_admin":false},"repo":{"id":20580498,"name":"kubernetes","full_name":"kubernetes/kubernetes","owner":{"login":"kubernetes","id":13629408,"avatar_url":"https://avatars.githubusercontent.com/u/13629408?v=3","gravatar_id":"","url":"https://api.github.com/users/kubernetes","html_url":"https://github.com/kubernetes","followers_url":"https://api.github.com/users/kubernetes/followers","following_url":"https://api.github.com/users/kubernetes/following{/other_user}","gists_url":"https://api.github.com/users/kubernetes/gists{/gist_id}","starred_url":"https://api.github.com/users/kubernetes/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/kubernetes/subscriptions","organizations_url":"https://api.github.com/users/kubernetes/orgs","repos_url":"https://api.github.com/users/kubernetes/repos","events_url":"https://api.github.com/users/kubernetes/events{/privacy}","received_events_url":"https://api.github.com/users/kubernetes/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/kubernetes/kubernetes","description":"Container Cluster Manager from Google","fork":false,"url":"https://api.github.com/repos/kubernetes/kubernetes","forks_url":"https://api.github.com/repos/kubernetes/kubernetes/forks","keys_url":"https://api.github.com/repos/kubernetes/kubernetes/keys{/key_id}","collaborators_url":"https://api.github.com/repos/kubernetes/kubernetes/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/kubernetes/kubernetes/teams","hooks_url":"https://api.github.com/repos/kubernetes/kubernetes/hooks","issue_events_url":"https://api.github.com/repos/kubernetes/kubernetes/issues/events{/number}","events_url":"https://api.github.com/repos/kubernetes/kubernetes/events","assignees_url":"https://api.github.com/repos/kubernetes/kubernetes/assignees{/user}","branches_url":"https://api.github.com/repos/kubernetes/kubernetes/branches{/branch}","tags_url":"https://api.github.com/repos/kubernetes/kubernetes/tags","blobs_url":"https://api.github.com/repos/kubernetes/kubernetes/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/kubernetes/kubernetes/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/kubernetes/kubernetes/git/refs{/sha}","trees_url":"https://api.github.com/repos/kubernetes/kubernetes/git/trees{/sha}","statuses_url":"https://api.github.com/repos/kubernetes/kubernetes/statuses/{sha}","languages_url":"https://api.github.com/repos/kubernetes/kubernetes/languages","stargazers_url":"https://api.github.com/repos/kubernetes/kubernetes/stargazers","contributors_url":"https://api.github.com/repos/kubernetes/kubernetes/contributors","subscribers_url":"https://api.github.com/repos/kubernetes/kubernetes/subscribers","subscription_url":"https://api.github.com/repos/kubernetes/kubernetes/subscription","commits_url":"https://api.github.com/repos/kubernetes/kubernetes/commits{/sha}","git_commits_url":"https://api.github.com/repos/kubernetes/kubernetes/git/commits{/sha}","comments_url":"https://api.github.com/repos/kubernetes/kubernetes/comments{/number}","issue_comment_url":"https://api.github.com/repos/kubernetes/kubernetes/issues/comments{/number}","contents_url":"https://api.github.com/repos/kubernetes/kubernetes/contents/{+path}","compare_url":"https://api.github.com/repos/kubernetes/kubernetes/compare/{base}...{head}","merges_url":"https://api.github.com/repos/kubernetes/kubernetes/merges","archive_url":"https://api.github.com/repos/kubernetes/kubernetes/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/kubernetes/kubernetes/downloads","issues_url":"https://api.github.com/repos/kubernetes/kubernetes/issues{/number}","pulls_url":"https://api.github.com/repos/kubernetes/kubernetes/pulls{/number}","milestones_url":"https://api.github.com/repos/kubernetes/kubernetes/milestones{/number}","notifications_url":"https://api.github.com/repos/kubernetes/kubernetes/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/kubernetes/kubernetes/labels{/name}","releases_url":"https://api.github.com/repos/kubernetes/kubernetes/releases{/id}","created_at":"2014-06-06T22:56:04Z","updated_at":"2015-08-14T11:57:37Z","pushed_at":"2015-08-14T14:31:24Z","git_url":"git://github.com/kubernetes/kubernetes.git","ssh_url":"git@github.com:kubernetes/kubernetes.git","clone_url":"https://github.com/kubernetes/kubernetes.git","svn_url":"https://github.com/kubernetes/kubernetes","homepage":"http://kubernetes.io","size":348383,"stargazers_count":9692,"watchers_count":9692,"language":"Go","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":2331,"mirror_url":null,"open_issues_count":1764,"forks":2331,"open_issues":1764,"watchers":9692,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/kubernetes/kubernetes/pulls/11695"},"html":{"href":"https://github.com/kubernetes/kubernetes/pull/11695"},"issue":{"href":"https://api.github.com/repos/kubernetes/kubernetes/issues/11695"},"comments":{"href":"https://api.github.com/repos/kubernetes/kubernetes/issues/11695/comments"},"review_comments":{"href":"https://api.github.com/repos/kubernetes/kubernetes/pulls/11695/comments"},"review_comment":{"href":"https://api.github.com/repos/kubernetes/kubernetes/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/kubernetes/kubernetes/pulls/11695/commits"},"statuses":{"href":"https://api.github.com/repos/kubernetes/kubernetes/statuses/9ee3cb421b21efe847be5974910ea282510a9ab8"}}}}