Skip to content

Commit b697e61

Browse files
authored
Release/v1.1.8 (#49)
* Fixing get services external ips and port * Update feature get endpoint & services * Update changelogs
1 parent de9d27e commit b697e61

File tree

8 files changed

+385
-136
lines changed

8 files changed

+385
-136
lines changed

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,16 @@ Customize Kubernetes Change Context (KUBECONFIG)
4646

4747
---
4848

49+
### version 1.1.8
50+
51+
- Added features `get endpoints` or `get ep`, show kubernetes endpoints
52+
```
53+
./k8s-context get endpoints -n [namespace]
54+
./k8s-context get ep -n [namespace]
55+
```
56+
57+
---
58+
4959
### version 1.1.7
5060

5161
- Added flag "-n" to make shortcut from "--namespace"

README.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ Customize Kubernetes Change Context (KUBECONFIG)
77
![tags](https://img.shields.io/github/v/tag/devopscorner/k8s-context?sort=semver)
88
[![docker pulls](https://img.shields.io/docker/pulls/devopscorner/k8s-context.svg)](https://hub.docker.com/r/devopscorner/k8s-context/)
99
![download all](https://img.shields.io/github/downloads/devopscorner/k8s-context/total.svg)
10-
![download latest](https://img.shields.io/github/downloads/devopscorner/k8s-context/1.1.7/total)
10+
![download latest](https://img.shields.io/github/downloads/devopscorner/k8s-context/1.1.8/total)
1111
![view](https://views.whatilearened.today/views/github/devopscorner/k8s-context.svg)
1212
![clone](https://img.shields.io/badge/dynamic/json?color=success&label=clone&query=count&url=https://github.com/devopscorner/k8s-context/blob/master/clone.json?raw=True&logo=github)
1313
![issues](https://img.shields.io/github/issues/devopscorner/k8s-context)
@@ -25,7 +25,7 @@ Customize Kubernetes Change Context (KUBECONFIG)
2525
| Image name | Size |
2626
|------------|------|
2727
| `devopscorner/k8s-context:latest` | [![docker image size](https://img.shields.io/docker/image-size/devopscorner/k8s-context/latest.svg?label=Image%20size&logo=docker)](https://hub.docker.com/repository/docker/devopscorner/k8s-context/tags?page=1&ordering=last_updated&name=latest) ![default-aws-cli](https://img.shields.io/static/v1?label=latest&message=default&color=brightgreen) ![latest-aws-cli](https://img.shields.io/static/v1?label=latest&message=alpine&color=orange) |
28-
| `devopscorner/k8s-context:1.1.7` | [![docker image size](https://img.shields.io/docker/image-size/devopscorner/k8s-context/1.1.7.svg?label=Image%20size&logo=docker)](https://hub.docker.com/repository/docker/devopscorner/k8s-context/tags?page=1&ordering=last_updated&name=1.1.7) ![latest-1.1.7](https://img.shields.io/static/v1?label=latest&message=1.1.7&color=orange) |
28+
| `devopscorner/k8s-context:1.1.8` | [![docker image size](https://img.shields.io/docker/image-size/devopscorner/k8s-context/1.1.8.svg?label=Image%20size&logo=docker)](https://hub.docker.com/repository/docker/devopscorner/k8s-context/tags?page=1&ordering=last_updated&name=1.1.8) ![latest-1.1.8](https://img.shields.io/static/v1?label=latest&message=1.1.8&color=orange) |
2929
| `devopscorner/k8s-context:alpine` | [![docker image size](https://img.shields.io/docker/image-size/devopscorner/k8s-context/alpine.svg?label=Image%20size&logo=docker)](https://hub.docker.com/repository/docker/devopscorner/k8s-context/tags?page=1&ordering=last_updated&name=alpine) |
3030
| `devopscorner/k8s-context:alpine-latest` | [![docker image size](https://img.shields.io/docker/image-size/devopscorner/k8s-context/alpine-latest.svg?label=Image%20size&logo=docker)](https://hub.docker.com/repository/docker/devopscorner/k8s-context/tags?page=1&ordering=last_updated&name=alpine-latest) |
3131
| `devopscorner/k8s-context:alpine-3.16` | [![docker image size](https://img.shields.io/docker/image-size/devopscorner/k8s-context/alpine-3.16.svg?label=Image%20size&logo=docker)](https://hub.docker.com/repository/docker/devopscorner/k8s-context/tags?page=1&ordering=last_updated&name=alpine-3.16) |
@@ -39,6 +39,7 @@ Customize Kubernetes Change Context (KUBECONFIG)
3939

4040
| Image name | Size |
4141
|------------|------|
42+
| `devopscorner/k8s-context:1.1.7` | [![docker image size](https://img.shields.io/docker/image-size/devopscorner/k8s-context/1.1.7.svg?label=Image%20size&logo=docker)](https://hub.docker.com/repository/docker/devopscorner/k8s-context/tags?page=1&ordering=last_updated&name=1.1.7) |
4243
| `devopscorner/k8s-context:1.1.6` | [![docker image size](https://img.shields.io/docker/image-size/devopscorner/k8s-context/1.1.6.svg?label=Image%20size&logo=docker)](https://hub.docker.com/repository/docker/devopscorner/k8s-context/tags?page=1&ordering=last_updated&name=1.1.6) |
4344
| `devopscorner/k8s-context:1.1.5` | [![docker image size](https://img.shields.io/docker/image-size/devopscorner/k8s-context/1.1.5.svg?label=Image%20size&logo=docker)](https://hub.docker.com/repository/docker/devopscorner/k8s-context/tags?page=1&ordering=last_updated&name=1.1.5) |
4445
| `devopscorner/k8s-context:1.1.4` | [![docker image size](https://img.shields.io/docker/image-size/devopscorner/k8s-context/1.1.4.svg?label=Image%20size&logo=docker)](https://hub.docker.com/repository/docker/devopscorner/k8s-context/tags?page=1&ordering=last_updated&name=1.1.4) |
@@ -60,8 +61,8 @@ Customize Kubernetes Change Context (KUBECONFIG)
6061
|_|\_\___/|___/ \___\___/|_| |_|\__\___/_/\_\\__|
6162
6263
63-
[[ K8S-CONTEXT ]] - v1.1.7
64-
=============================
64+
[[ K8S-CONTEXT (K8C) ]] - v1.1.8
65+
==================================
6566
Usage:
6667
k8s-context [command]
6768

src/features/menus.go

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ const (
3131
|_|\_\___/|___/ \___\___/|_| |_|\__\___/_/\_\\__|
3232
3333
`
34-
AppName = "K8S-CONTEXT"
35-
VERSION = "v1.1.7"
34+
AppName = "K8S-CONTEXT (K8C)"
35+
VERSION = "v1.1.8"
3636
)
3737

3838
func GetCommands() []*cobra.Command {
@@ -164,8 +164,8 @@ func GetCommands() []*cobra.Command {
164164

165165
getCmd := &cobra.Command{
166166
Use: "get",
167-
Short: "Get Kubernetes resources (ns, svc, deploy, po)",
168-
Long: "Get Kubernetes resources: namespace (ns), services (svc), deployments (deploy), pods (po)",
167+
Short: "Get Kubernetes resources (ns, svc, deploy, po, ep)",
168+
Long: "Get Kubernetes resources: namespace (ns), services (svc), deployments (deploy), pods (po), endpoints (ep)",
169169
RunE: func(cmd *cobra.Command, args []string) error {
170170
if len(args) < 1 {
171171
return fmt.Errorf("resource type not specified")
@@ -226,6 +226,13 @@ func GetCommands() []*cobra.Command {
226226
}
227227
ShowServiceByFilter(services)
228228

229+
case "endpoints", "ep":
230+
endpoints, err := clientset.CoreV1().Endpoints(namespace).List(ctx, metav1.ListOptions{})
231+
if err != nil {
232+
return err
233+
}
234+
ShowEndpointByFilter(endpoints)
235+
229236
case "deployment", "deploy":
230237
deployments, err := clientset.AppsV1().Deployments(namespace).List(ctx, metav1.ListOptions{})
231238
if err != nil {
@@ -273,6 +280,13 @@ func GetCommands() []*cobra.Command {
273280
}
274281
ShowServiceByFilter(services)
275282

283+
case "endpoints", "ep":
284+
endpoints, err := clientset.CoreV1().Endpoints(namespace).List(ctx, metav1.ListOptions{})
285+
if err != nil {
286+
return err
287+
}
288+
ShowEndpointByFilter(endpoints)
289+
276290
case "deployment", "deploy":
277291
deployments, err := clientset.AppsV1().Deployments(namespace).List(ctx, metav1.ListOptions{})
278292
if err != nil {

src/features/network.go

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
package features
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"os"
7+
"strconv"
8+
"strings"
9+
"time"
10+
11+
"github.com/olekukonko/tablewriter"
12+
corev1 "k8s.io/api/core/v1"
13+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
14+
)
15+
16+
func ShowServiceByFilter(services *corev1.ServiceList) {
17+
table := tablewriter.NewWriter(os.Stdout)
18+
table.SetHeader([]string{
19+
"NAME",
20+
"TYPE",
21+
"CLUSTER-IP",
22+
"EXTERNAL-IP(S)",
23+
"PORT(S)",
24+
"AGE",
25+
})
26+
27+
table.SetAutoFormatHeaders(false)
28+
table.SetAutoWrapText(false)
29+
30+
for _, service := range services.Items {
31+
var externalIPs string
32+
if service.Spec.Type == corev1.ServiceTypeLoadBalancer && len(service.Status.LoadBalancer.Ingress) > 0 {
33+
if service.Status.LoadBalancer.Ingress[0].IP != "" {
34+
externalIPs = service.Status.LoadBalancer.Ingress[0].IP
35+
} else if service.Status.LoadBalancer.Ingress[0].Hostname != "" {
36+
externalIPs = service.Status.LoadBalancer.Ingress[0].Hostname
37+
} else {
38+
externalIPs = "<pending>"
39+
}
40+
} else if len(service.Spec.ExternalIPs) > 0 {
41+
externalIPs = strings.Join(service.Spec.ExternalIPs, ", ")
42+
} else {
43+
externalIPs = "<none>"
44+
}
45+
age := HumanReadableDuration(time.Since(service.ObjectMeta.CreationTimestamp.Time))
46+
ports := make([]string, len(service.Spec.Ports))
47+
for i, port := range service.Spec.Ports {
48+
protocolName := string(port.Protocol)
49+
if port.Port != port.TargetPort.IntVal {
50+
// The port is not named, so try to find the corresponding named port
51+
for _, namedPort := range service.Spec.Ports {
52+
if namedPort.Name == port.Name {
53+
protocolName = string(namedPort.Protocol)
54+
break
55+
}
56+
}
57+
}
58+
if port.Port == 0 {
59+
ports[i] = fmt.Sprintf("%s", protocolName)
60+
} else {
61+
ports[i] = fmt.Sprintf("%d", port.Port)
62+
}
63+
if port.TargetPort.String() != "0" {
64+
ports[i] += ":" + port.TargetPort.String()
65+
}
66+
ports[i] += "/" + protocolName
67+
}
68+
69+
table.Append([]string{
70+
service.Name,
71+
string(service.Spec.Type),
72+
service.Spec.ClusterIP,
73+
externalIPs,
74+
strings.Join(ports, ", "),
75+
age,
76+
})
77+
}
78+
table.Render()
79+
}
80+
81+
func ShowEndpointByFilter(endpoints *corev1.EndpointsList) {
82+
table := tablewriter.NewWriter(os.Stdout)
83+
table.SetHeader([]string{
84+
"NAME",
85+
"ENDPOINTS TARGET",
86+
"ENDPOINTS PORT(S)",
87+
// "ENDPOINTS NAME",
88+
"AGE",
89+
})
90+
91+
table.SetAutoFormatHeaders(false)
92+
table.SetAutoWrapText(false)
93+
94+
for _, ep := range endpoints.Items {
95+
serviceName := ep.ObjectMeta.Name
96+
age := HumanReadableDuration(time.Since(ep.ObjectMeta.CreationTimestamp.Time))
97+
98+
for _, subset := range ep.Subsets {
99+
addresses := make([]string, len(subset.Addresses))
100+
ports := make([]string, len(subset.Ports))
101+
for i, addr := range subset.Addresses {
102+
target := addr.TargetRef.Name
103+
if addr.TargetRef.Kind == "Pod" {
104+
pod, err := GetPod(addr.TargetRef.Namespace, target)
105+
if err == nil {
106+
target = fmt.Sprintf("%s (%s)", target, pod.Status.PodIP)
107+
}
108+
}
109+
addresses[i] = target
110+
}
111+
for i, port := range subset.Ports {
112+
portNumber := strconv.Itoa(int(port.Port))
113+
if int(port.Port) == 0 {
114+
portNumber = port.Name
115+
}
116+
ports[i] = portNumber
117+
}
118+
table.Append([]string{
119+
serviceName,
120+
strings.Join(addresses, ", "),
121+
strings.Join(ports, ", "),
122+
age,
123+
})
124+
}
125+
}
126+
127+
table.Render()
128+
}
129+
130+
func GetPod(namespace string, name string) (*corev1.Pod, error) {
131+
clientset, err := GetClientSet(kubeconfig)
132+
if err != nil {
133+
return nil, err
134+
}
135+
136+
ctx := context.Background()
137+
pod, err := clientset.CoreV1().Pods(namespace).Get(ctx, name, metav1.GetOptions{})
138+
if err != nil {
139+
return nil, err
140+
}
141+
142+
return pod, nil
143+
}

src/features/pods.go

Lines changed: 0 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -43,28 +43,6 @@ func GetLabels(pod *corev1.Pod) []string {
4343
return labels
4444
}
4545

46-
func HumanReadableDuration(duration time.Duration) string {
47-
if duration.Seconds() < 60 {
48-
return fmt.Sprintf("%ds", int(duration.Seconds()))
49-
} else if duration.Minutes() < 60 {
50-
return fmt.Sprintf("%dm", int(duration.Minutes()))
51-
} else if duration.Hours() < 24 {
52-
return fmt.Sprintf("%dh", int(duration.Hours()))
53-
}
54-
return fmt.Sprintf("%dd", int(duration.Hours()/24))
55-
}
56-
57-
func CalculateReadiness(pod *corev1.Pod) (int, int) {
58-
var ready, total int
59-
for _, cs := range pod.Status.ContainerStatuses {
60-
if cs.Ready {
61-
ready++
62-
}
63-
total++
64-
}
65-
return ready, total
66-
}
67-
6846
func ShowPodsByFilter(pods *corev1.PodList) {
6947
table := tablewriter.NewWriter(os.Stdout)
7048
table.SetHeader([]string{
@@ -127,45 +105,6 @@ func ShowNamespaceByFilter(namespaces *corev1.NamespaceList) {
127105
table.Render()
128106
}
129107

130-
func ShowServiceByFilter(services *corev1.ServiceList) {
131-
table := tablewriter.NewWriter(os.Stdout)
132-
table.SetHeader([]string{
133-
"NAME",
134-
"TYPE",
135-
"CLUSTER-IP",
136-
"EXTERNAL-IP",
137-
"PORT(S)",
138-
"AGE",
139-
})
140-
141-
table.SetAutoFormatHeaders(false)
142-
table.SetAutoWrapText(false)
143-
144-
for _, service := range services.Items {
145-
var externalIPs string
146-
if len(service.Spec.ExternalIPs) > 0 {
147-
externalIPs = strings.Join(service.Spec.ExternalIPs, ", ")
148-
} else {
149-
externalIPs = "<none>"
150-
}
151-
age := HumanReadableDuration(time.Since(service.ObjectMeta.CreationTimestamp.Time))
152-
ports := make([]string, len(service.Spec.Ports))
153-
for i, port := range service.Spec.Ports {
154-
ports[i] = fmt.Sprintf("%d/%s", port.Port, string(port.Protocol))
155-
}
156-
157-
table.Append([]string{
158-
service.Name,
159-
string(service.Spec.Type),
160-
service.Spec.ClusterIP,
161-
externalIPs,
162-
strings.Join(ports, ", "),
163-
age,
164-
})
165-
}
166-
table.Render()
167-
}
168-
169108
func ShowDeploymentByFilter(deployments *v1.DeploymentList) {
170109
table := tablewriter.NewWriter(os.Stdout)
171110
table.SetHeader([]string{

src/features/utils.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package features
2+
3+
import (
4+
"fmt"
5+
"time"
6+
7+
corev1 "k8s.io/api/core/v1"
8+
)
9+
10+
func HumanReadableDuration(duration time.Duration) string {
11+
if duration.Seconds() < 60 {
12+
return fmt.Sprintf("%ds", int(duration.Seconds()))
13+
} else if duration.Minutes() < 60 {
14+
return fmt.Sprintf("%dm", int(duration.Minutes()))
15+
} else if duration.Hours() < 24 {
16+
return fmt.Sprintf("%dh", int(duration.Hours()))
17+
}
18+
return fmt.Sprintf("%dd", int(duration.Hours()/24))
19+
}
20+
21+
func CalculateReadiness(pod *corev1.Pod) (int, int) {
22+
var ready, total int
23+
for _, cs := range pod.Status.ContainerStatuses {
24+
if cs.Ready {
25+
ready++
26+
}
27+
total++
28+
}
29+
return ready, total
30+
}

src/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,6 @@ func main() {
1313

1414
fmt.Println(logoStyle.Styled(features.Logo))
1515
fmt.Println("[[ ", appNameStyle.Styled(features.AppName), " ]] -", features.VERSION)
16-
fmt.Println("=============================")
16+
fmt.Println("==================================")
1717
features.GetCommands()
1818
}

0 commit comments

Comments
 (0)