github.com/kuoss/venti@v0.2.20/pkg/service/discovery/kubernetes/kubernetes.go (about) 1 package kubernetes 2 3 import ( 4 "context" 5 "fmt" 6 "log" 7 8 "github.com/kuoss/venti/pkg/model" 9 v1 "k8s.io/api/core/v1" 10 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 11 "k8s.io/client-go/kubernetes" 12 "k8s.io/client-go/rest" 13 ) 14 15 type k8sService struct { 16 client kubernetes.Interface 17 } 18 19 func NewK8sService() (*k8sService, error) { 20 clusterCfg, err := rest.InClusterConfig() 21 if err != nil { 22 return nil, fmt.Errorf("cannot InClusterConfig: %w", err) 23 } 24 clientset, err := kubernetes.NewForConfig(clusterCfg) 25 if err != nil { 26 return nil, fmt.Errorf("cannot NewForConfig: %w", err) 27 } 28 return &k8sService{client: clientset}, nil 29 } 30 31 func (s *k8sService) Do(discovery model.Discovery) ([]model.Datasource, error) { 32 33 services, err := s.client.CoreV1().Services("").List(context.Background(), metav1.ListOptions{}) 34 if err != nil { 35 return nil, fmt.Errorf("cannot ListServices: %w", err) 36 } 37 return s.getDatasourcesFromServices(services.Items, discovery), nil 38 } 39 40 func (s *k8sService) getDatasourcesFromServices(services []v1.Service, discovery model.Discovery) []model.Datasource { 41 var datasources []model.Datasource 42 43 for _, service := range services { 44 datasourceType := getDatasourceTypeByConfig(service, discovery) 45 46 // the service is not a datasource 47 if datasourceType == model.DatasourceTypeNone { 48 continue 49 } 50 51 // recognize as a main datasource by namespace 52 isMain := false 53 if service.Namespace == discovery.MainNamespace { 54 isMain = true 55 } 56 57 // get port number of datasource from k8s service 58 portNumber, err := getPortNumberFromService(service) 59 if err != nil { 60 log.Printf("extract port number from service failed. %s", err) 61 continue 62 } 63 // append to datasources 64 datasources = append(datasources, model.Datasource{ 65 Name: fmt.Sprintf("%s.%s", service.Name, service.Namespace), 66 Type: datasourceType, 67 URL: fmt.Sprintf("http://%s.%s:%d", service.Name, service.Namespace, portNumber), 68 IsDiscovered: true, 69 IsMain: isMain, 70 }) 71 } 72 return datasources 73 } 74 75 // getDatasourceTypeByConfig return DatasourceType. 76 // 1. If configured within config.Discovery.ByNamePrometheus or config.Discovery.ByNameLethe return if service has matched name. 77 // 2. If configured within config.Discovery.AnnotationKey matched with service's annotation key and also value is 78 // one of promethe or lethe. 79 func getDatasourceTypeByConfig(service v1.Service, cfg model.Discovery) model.DatasourceType { 80 81 // recognize as a datasource by name 'prometheus' 82 if cfg.ByNamePrometheus && service.Name == "prometheus" { 83 return model.DatasourceTypePrometheus 84 } 85 // recognize as a datasource by name 'lethe' 86 if cfg.ByNameLethe && service.Name == "lethe" { 87 return model.DatasourceTypeLethe 88 } 89 90 // recognize as a datasource by annotation of k8s service 91 for key, value := range service.Annotations { 92 if key != cfg.AnnotationKey { 93 continue 94 } 95 if value == string(model.DatasourceTypePrometheus) { 96 return model.DatasourceTypePrometheus 97 } 98 if value == string(model.DatasourceTypeLethe) { 99 return model.DatasourceTypeLethe 100 } 101 } 102 103 return model.DatasourceTypeNone 104 } 105 106 // return port number within "http" named port. if not exist return service's first port number 107 func getPortNumberFromService(service v1.Service) (int32, error) { 108 if len(service.Spec.Ports) < 1 { 109 return 0, fmt.Errorf("service %s/%s have any port", service.Namespace, service.Name) 110 } 111 112 for _, port := range service.Spec.Ports { 113 if port.Name == "http" { 114 return port.Port, nil 115 } 116 } 117 return service.Spec.Ports[0].Port, nil 118 }