mirror of
https://github.com/portainer/portainer.git
synced 2025-08-05 13:55:21 +02:00
feat(kubernetes): list all kube services screen [EE-1571] (#8524)
* port services from ee * fix external link * post review improvements * remove applications-ports-datatable * minor post review updates * add services help url * post review update * more post review updates * post review updates * rename index to component * fix external ip display and sorting * fix external apps tag * fix ingress screen time format * use uid for row id. Prevent blank link * fix some missing bits ported from EE * match ee * fix display of show system resources * remove icon next to service type
This commit is contained in:
parent
8d6797dc9f
commit
ac47649631
43 changed files with 1121 additions and 456 deletions
|
@ -37,7 +37,15 @@ func (handler *Handler) getKubernetesServices(w http.ResponseWriter, r *http.Req
|
|||
)
|
||||
}
|
||||
|
||||
services, err := cli.GetServices(namespace)
|
||||
lookup, err := request.RetrieveBooleanQueryParameter(r, "lookupapplications", true)
|
||||
if err != nil {
|
||||
return httperror.BadRequest(
|
||||
"Invalid lookupapplications query parameter",
|
||||
err,
|
||||
)
|
||||
}
|
||||
|
||||
services, err := cli.GetServices(namespace, lookup)
|
||||
if err != nil {
|
||||
return httperror.InternalServerError(
|
||||
"Unable to retrieve services",
|
||||
|
|
11
api/http/models/kubernetes/application.go
Normal file
11
api/http/models/kubernetes/application.go
Normal file
|
@ -0,0 +1,11 @@
|
|||
package kubernetes
|
||||
|
||||
type (
|
||||
K8sApplication struct {
|
||||
UID string `json:",omitempty"`
|
||||
Name string `json:""`
|
||||
Namespace string `json:",omitempty"`
|
||||
Kind string `json:",omitempty"`
|
||||
Labels map[string]string `json:",omitempty"`
|
||||
}
|
||||
)
|
|
@ -3,6 +3,7 @@ package kubernetes
|
|||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
type (
|
||||
|
@ -18,15 +19,17 @@ type (
|
|||
K8sIngressControllers []K8sIngressController
|
||||
|
||||
K8sIngressInfo struct {
|
||||
Name string `json:"Name"`
|
||||
UID string `json:"UID"`
|
||||
Type string `json:"Type"`
|
||||
Namespace string `json:"Namespace"`
|
||||
ClassName string `json:"ClassName"`
|
||||
Annotations map[string]string `json:"Annotations"`
|
||||
Hosts []string `json:"Hosts"`
|
||||
Paths []K8sIngressPath `json:"Paths"`
|
||||
TLS []K8sIngressTLS `json:"TLS"`
|
||||
Name string `json:"Name"`
|
||||
UID string `json:"UID"`
|
||||
Type string `json:"Type"`
|
||||
Namespace string `json:"Namespace"`
|
||||
ClassName string `json:"ClassName"`
|
||||
Annotations map[string]string `json:"Annotations"`
|
||||
Hosts []string `json:"Hosts"`
|
||||
Paths []K8sIngressPath `json:"Paths"`
|
||||
TLS []K8sIngressTLS `json:"TLS"`
|
||||
Labels map[string]string `json:"Labels,omitempty"`
|
||||
CreationDate time.Time `json:"CreationDate"`
|
||||
}
|
||||
|
||||
K8sIngressTLS struct {
|
||||
|
|
|
@ -7,17 +7,23 @@ import (
|
|||
|
||||
type (
|
||||
K8sServiceInfo struct {
|
||||
Name string `json:"Name"`
|
||||
UID string `json:"UID"`
|
||||
Type string `json:"Type"`
|
||||
Namespace string `json:"Namespace"`
|
||||
Annotations map[string]string `json:"Annotations"`
|
||||
CreationTimestamp string `json:"CreationTimestamp"`
|
||||
Labels map[string]string `json:"Labels"`
|
||||
AllocateLoadBalancerNodePorts *bool `json:"AllocateLoadBalancerNodePorts,omitempty"`
|
||||
Ports []K8sServicePort `json:"Ports"`
|
||||
Selector map[string]string `json:"Selector"`
|
||||
IngressStatus []K8sServiceIngress `json:"IngressStatus"`
|
||||
Name string
|
||||
UID string
|
||||
Type string
|
||||
Namespace string
|
||||
Annotations map[string]string
|
||||
CreationTimestamp string
|
||||
Labels map[string]string
|
||||
AllocateLoadBalancerNodePorts *bool `json:",omitempty"`
|
||||
Ports []K8sServicePort
|
||||
Selector map[string]string
|
||||
IngressStatus []K8sServiceIngress `json:",omitempty"`
|
||||
|
||||
// serviceList screen
|
||||
Applications []K8sApplication `json:",omitempty"`
|
||||
ClusterIPs []string `json:",omitempty"`
|
||||
ExternalName string `json:",omitempty"`
|
||||
ExternalIPs []string `json:",omitempty"`
|
||||
}
|
||||
|
||||
K8sServicePort struct {
|
||||
|
@ -25,7 +31,7 @@ type (
|
|||
NodePort int `json:"NodePort"`
|
||||
Port int `json:"Port"`
|
||||
Protocol string `json:"Protocol"`
|
||||
TargetPort int `json:"TargetPort"`
|
||||
TargetPort string `json:"TargetPort"`
|
||||
}
|
||||
|
||||
K8sServiceIngress struct {
|
||||
|
|
|
@ -102,6 +102,8 @@ func (kcl *KubeClient) GetIngresses(namespace string) ([]models.K8sIngressInfo,
|
|||
}
|
||||
info.Type = classes[info.ClassName]
|
||||
info.Annotations = ingress.Annotations
|
||||
info.Labels = ingress.Labels
|
||||
info.CreationDate = ingress.CreationTimestamp.Time
|
||||
|
||||
// Gather TLS information.
|
||||
for _, v := range ingress.Spec.TLS {
|
||||
|
|
|
@ -6,11 +6,12 @@ import (
|
|||
models "github.com/portainer/portainer/api/http/models/kubernetes"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
labels "k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
)
|
||||
|
||||
// GetServices gets all the services for a given namespace in a k8s endpoint.
|
||||
func (kcl *KubeClient) GetServices(namespace string) ([]models.K8sServiceInfo, error) {
|
||||
func (kcl *KubeClient) GetServices(namespace string, lookupApplications bool) ([]models.K8sServiceInfo, error) {
|
||||
client := kcl.cli.CoreV1().Services(namespace)
|
||||
|
||||
services, err := client.List(context.Background(), metav1.ListOptions{})
|
||||
|
@ -28,7 +29,7 @@ func (kcl *KubeClient) GetServices(namespace string) ([]models.K8sServiceInfo, e
|
|||
NodePort: int(port.NodePort),
|
||||
Port: int(port.Port),
|
||||
Protocol: string(port.Protocol),
|
||||
TargetPort: port.TargetPort.IntValue(),
|
||||
TargetPort: port.TargetPort.String(),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -40,6 +41,11 @@ func (kcl *KubeClient) GetServices(namespace string) ([]models.K8sServiceInfo, e
|
|||
})
|
||||
}
|
||||
|
||||
var applications []models.K8sApplication
|
||||
if lookupApplications {
|
||||
applications, _ = kcl.getOwningApplication(namespace, service.Spec.Selector)
|
||||
}
|
||||
|
||||
result = append(result, models.K8sServiceInfo{
|
||||
Name: service.Name,
|
||||
UID: string(service.GetUID()),
|
||||
|
@ -51,6 +57,10 @@ func (kcl *KubeClient) GetServices(namespace string) ([]models.K8sServiceInfo, e
|
|||
IngressStatus: ingressStatus,
|
||||
Labels: service.GetLabels(),
|
||||
Annotations: service.GetAnnotations(),
|
||||
ClusterIPs: service.Spec.ClusterIPs,
|
||||
ExternalName: service.Spec.ExternalName,
|
||||
ExternalIPs: service.Spec.ExternalIPs,
|
||||
Applications: applications,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -77,7 +87,7 @@ func (kcl *KubeClient) CreateService(namespace string, info models.K8sServiceInf
|
|||
port.NodePort = int32(p.NodePort)
|
||||
port.Port = int32(p.Port)
|
||||
port.Protocol = v1.Protocol(p.Protocol)
|
||||
port.TargetPort = intstr.FromInt(p.TargetPort)
|
||||
port.TargetPort = intstr.FromString(p.TargetPort)
|
||||
service.Spec.Ports = append(service.Spec.Ports, port)
|
||||
}
|
||||
|
||||
|
@ -133,7 +143,7 @@ func (kcl *KubeClient) UpdateService(namespace string, info models.K8sServiceInf
|
|||
port.NodePort = int32(p.NodePort)
|
||||
port.Port = int32(p.Port)
|
||||
port.Protocol = v1.Protocol(p.Protocol)
|
||||
port.TargetPort = intstr.FromInt(p.TargetPort)
|
||||
port.TargetPort = intstr.FromString(p.TargetPort)
|
||||
service.Spec.Ports = append(service.Spec.Ports, port)
|
||||
}
|
||||
|
||||
|
@ -151,3 +161,54 @@ func (kcl *KubeClient) UpdateService(namespace string, info models.K8sServiceInf
|
|||
_, err := ServiceClient.Update(context.Background(), &service, metav1.UpdateOptions{})
|
||||
return err
|
||||
}
|
||||
|
||||
// getOwningApplication gets the application that owns the given service selector.
|
||||
func (kcl *KubeClient) getOwningApplication(namespace string, selector map[string]string) ([]models.K8sApplication, error) {
|
||||
if len(selector) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
selectorLabels := labels.SelectorFromSet(selector).String()
|
||||
|
||||
// look for replicasets first, limit 1 (we only support one owner)
|
||||
replicasets, err := kcl.cli.AppsV1().ReplicaSets(namespace).List(context.TODO(), metav1.ListOptions{LabelSelector: selectorLabels, Limit: 1})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var meta metav1.Object
|
||||
if replicasets != nil && len(replicasets.Items) > 0 {
|
||||
meta = replicasets.Items[0].GetObjectMeta()
|
||||
} else {
|
||||
// otherwise look for matching pods, limit 1 (we only support one owner)
|
||||
pods, err := kcl.cli.CoreV1().Pods(namespace).List(context.TODO(), metav1.ListOptions{LabelSelector: selectorLabels, Limit: 1})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if pods == nil || len(pods.Items) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
meta = pods.Items[0].GetObjectMeta()
|
||||
}
|
||||
|
||||
return makeApplication(meta), nil
|
||||
}
|
||||
|
||||
func makeApplication(meta metav1.Object) []models.K8sApplication {
|
||||
ownerReferences := meta.GetOwnerReferences()
|
||||
if len(ownerReferences) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Currently, we only support one owner reference
|
||||
ownerReference := ownerReferences[0]
|
||||
return []models.K8sApplication{
|
||||
{
|
||||
// Only the name is used right now, but we can add more fields in the future
|
||||
Name: ownerReference.Name,
|
||||
},
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1429,7 +1429,7 @@ type (
|
|||
DeleteIngresses(reqs models.K8sIngressDeleteRequests) error
|
||||
CreateService(namespace string, service models.K8sServiceInfo) error
|
||||
UpdateService(namespace string, service models.K8sServiceInfo) error
|
||||
GetServices(namespace string) ([]models.K8sServiceInfo, error)
|
||||
GetServices(namespace string, lookupApplications bool) ([]models.K8sServiceInfo, error)
|
||||
DeleteServices(reqs models.K8sServiceDeleteRequests) error
|
||||
GetNodesLimits() (K8sNodesLimits, error)
|
||||
GetNamespaceAccessPolicies() (map[string]K8sNamespaceAccessPolicy, error)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue