Skip to content

Commit

Permalink
feat: add service resources
Browse files Browse the repository at this point in the history
Signed-off-by: warjiang <[email protected]>
  • Loading branch information
warjiang committed Sep 9, 2024
1 parent 9bf7340 commit f4cca3d
Show file tree
Hide file tree
Showing 4 changed files with 226 additions and 0 deletions.
42 changes: 42 additions & 0 deletions pkg/resource/service/common.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package service

import (
"github.com/karmada-io/dashboard/pkg/dataselect"
v1 "k8s.io/api/core/v1"
)

// The code below allows to perform complex data section on []api.Service

type ServiceCell v1.Service

func (self ServiceCell) GetProperty(name dataselect.PropertyName) dataselect.ComparableValue {
switch name {
case dataselect.NameProperty:
return dataselect.StdComparableString(self.ObjectMeta.Name)
case dataselect.CreationTimestampProperty:
return dataselect.StdComparableTime(self.ObjectMeta.CreationTimestamp.Time)
case dataselect.NamespaceProperty:
return dataselect.StdComparableString(self.ObjectMeta.Namespace)
case dataselect.TypeProperty:
return dataselect.StdComparableString(self.Spec.Type)
default:
// if name is not supported then just return a constant dummy value, sort will have no effect.
return nil
}
}

func toCells(std []v1.Service) []dataselect.DataCell {
cells := make([]dataselect.DataCell, len(std))
for i := range std {
cells[i] = ServiceCell(std[i])
}
return cells
}

func fromCells(cells []dataselect.DataCell) []v1.Service {
std := make([]v1.Service, len(cells))
for i := range std {
std[i] = v1.Service(cells[i].(ServiceCell))
}
return std
}
53 changes: 53 additions & 0 deletions pkg/resource/service/detail.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package service

import (
"context"
"github.com/karmada-io/dashboard/pkg/common/errors"
"github.com/karmada-io/dashboard/pkg/resource/endpoint"
v1 "k8s.io/api/core/v1"
metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
k8sClient "k8s.io/client-go/kubernetes"
"log"
)

// Service is a representation of a service.
type ServiceDetail struct {
// Extends list item structure.
Service `json:",inline"`

// List of Endpoint obj. that are endpoints of this Service.
EndpointList endpoint.EndpointList `json:"endpointList"`

// Show the value of the SessionAffinity of the Service.
SessionAffinity v1.ServiceAffinity `json:"sessionAffinity"`

// List of non-critical errors, that occurred during resource retrieval.
Errors []error `json:"errors"`
}

// GetServiceDetail gets service details.
func GetServiceDetail(client k8sClient.Interface, namespace, name string) (*ServiceDetail, error) {
log.Printf("Getting details of %s service in %s namespace", name, namespace)
serviceData, err := client.CoreV1().Services(namespace).Get(context.TODO(), name, metaV1.GetOptions{})
if err != nil {
return nil, err
}

endpointList, err := endpoint.GetServiceEndpoints(client, namespace, name)
nonCriticalErrors, criticalError := errors.ExtractErrors(err)
if criticalError != nil {
return nil, criticalError
}

service := toServiceDetail(serviceData, *endpointList, nonCriticalErrors)
return &service, nil
}

func toServiceDetail(service *v1.Service, endpointList endpoint.EndpointList, nonCriticalErrors []error) ServiceDetail {
return ServiceDetail{
Service: toService(service),
EndpointList: endpointList,
SessionAffinity: service.Spec.SessionAffinity,
Errors: nonCriticalErrors,
}
}
29 changes: 29 additions & 0 deletions pkg/resource/service/events.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package service

import (
"github.com/karmada-io/dashboard/pkg/common/types"
"github.com/karmada-io/dashboard/pkg/dataselect"
"github.com/karmada-io/dashboard/pkg/resource/common"
"github.com/karmada-io/dashboard/pkg/resource/event"
"log"

client "k8s.io/client-go/kubernetes"
)

// GetServiceEvents returns model events for a service with the given name in the given namespace.
func GetServiceEvents(client client.Interface, dsQuery *dataselect.DataSelectQuery, namespace, name string) (
*common.EventList, error) {
eventList := common.EventList{
Events: make([]common.Event, 0),
ListMeta: types.ListMeta{TotalItems: 0},
}

serviceEvents, err := event.GetEvents(client, namespace, name)
if err != nil {
return &eventList, err
}

eventList = event.CreateEventList(event.FillEventsType(serviceEvents), dsQuery)
log.Printf("Found %d events related to %s service in %s namespace", len(eventList.Events), name, namespace)
return &eventList, nil
}
102 changes: 102 additions & 0 deletions pkg/resource/service/list.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package service

import (
"github.com/karmada-io/dashboard/pkg/common/errors"
"github.com/karmada-io/dashboard/pkg/common/types"
"github.com/karmada-io/dashboard/pkg/dataselect"
"github.com/karmada-io/dashboard/pkg/resource/common"
v1 "k8s.io/api/core/v1"
client "k8s.io/client-go/kubernetes"
"log"
)

// Service is a representation of a service.
type Service struct {
ObjectMeta types.ObjectMeta `json:"objectMeta"`
TypeMeta types.TypeMeta `json:"typeMeta"`

// InternalEndpoint of all Kubernetes services that have the same label selector as connected Replication
// Controller. Endpoint is DNS name merged with ports.
InternalEndpoint common.Endpoint `json:"internalEndpoint"`

// ExternalEndpoints of all Kubernetes services that have the same label selector as connected Replication
// Controller. Endpoint is external IP address name merged with ports.
ExternalEndpoints []common.Endpoint `json:"externalEndpoints"`

// Label selector of the service.
Selector map[string]string `json:"selector"`

// Type determines how the service will be exposed. Valid options: ClusterIP, NodePort, LoadBalancer, ExternalName
Type v1.ServiceType `json:"type"`

// ClusterIP is usually assigned by the control plane. Valid values are None, empty string (""), or
// a valid IP address. None can be specified for headless services when proxying is not required
ClusterIP string `json:"clusterIP"`
}

// ServiceList contains a list of services in the cluster.
type ServiceList struct {
ListMeta types.ListMeta `json:"listMeta"`

// Unordered list of services.
Services []Service `json:"services"`

// List of non-critical errors, that occurred during resource retrieval.
Errors []error `json:"errors"`
}

// GetServiceList returns a list of all services in the cluster.
func GetServiceList(client client.Interface, nsQuery *common.NamespaceQuery,
dsQuery *dataselect.DataSelectQuery) (*ServiceList, error) {
log.Print("Getting list of all services in the cluster")

channels := &common.ResourceChannels{
ServiceList: common.GetServiceListChannel(client, nsQuery, 1),
}

return GetServiceListFromChannels(channels, dsQuery)
}

// GetServiceListFromChannels returns a list of all services in the cluster.
func GetServiceListFromChannels(channels *common.ResourceChannels,
dsQuery *dataselect.DataSelectQuery) (*ServiceList, error) {
services := <-channels.ServiceList.List
err := <-channels.ServiceList.Error
nonCriticalErrors, criticalError := errors.ExtractErrors(err)
if criticalError != nil {
return nil, criticalError
}

return CreateServiceList(services.Items, nonCriticalErrors, dsQuery), nil
}

func toService(service *v1.Service) Service {
return Service{
ObjectMeta: types.NewObjectMeta(service.ObjectMeta),
TypeMeta: types.NewTypeMeta(types.ResourceKindService),
InternalEndpoint: common.GetInternalEndpoint(service.Name, service.Namespace, service.Spec.Ports),
ExternalEndpoints: common.GetExternalEndpoints(service),
Selector: service.Spec.Selector,
ClusterIP: service.Spec.ClusterIP,
Type: service.Spec.Type,
}
}

// CreateServiceList returns paginated service list based on given service array and pagination query.
func CreateServiceList(services []v1.Service, nonCriticalErrors []error, dsQuery *dataselect.DataSelectQuery) *ServiceList {
serviceList := &ServiceList{
Services: make([]Service, 0),
ListMeta: types.ListMeta{TotalItems: len(services)},
Errors: nonCriticalErrors,
}

serviceCells, filteredTotal := dataselect.GenericDataSelectWithFilter(toCells(services), dsQuery)
services = fromCells(serviceCells)
serviceList.ListMeta = types.ListMeta{TotalItems: filteredTotal}

for _, service := range services {
serviceList.Services = append(serviceList.Services, toService(&service))
}

return serviceList
}

0 comments on commit f4cca3d

Please sign in to comment.