github.com/cs3org/reva/v2@v2.27.7/pkg/mentix/exchangers/exporters/promsd.go (about) 1 // Copyright 2018-2021 CERN 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 // 15 // In applying this license, CERN does not waive the privileges and immunities 16 // granted to it by virtue of its status as an Intergovernmental Organization 17 // or submit itself to any jurisdiction. 18 19 package exporters 20 21 import ( 22 "encoding/json" 23 "fmt" 24 "net/url" 25 "os" 26 "path" 27 "path/filepath" 28 "strings" 29 30 "github.com/cs3org/reva/v2/pkg/mentix/utils" 31 "github.com/rs/zerolog" 32 33 "github.com/cs3org/reva/v2/pkg/mentix/config" 34 "github.com/cs3org/reva/v2/pkg/mentix/exchangers/exporters/prometheus" 35 "github.com/cs3org/reva/v2/pkg/mentix/meshdata" 36 ) 37 38 type prometheusSDScrapeCreatorCallback = func(site *meshdata.Site, service *meshdata.Service, endpoint *meshdata.ServiceEndpoint) *prometheus.ScrapeConfig 39 type prometheusSDScrapeCreator struct { 40 outputFilename string 41 creatorCallback prometheusSDScrapeCreatorCallback 42 serviceFilter []string 43 } 44 45 // PrometheusSDExporter implements various Prometheus Service Discovery scrape config exporters. 46 type PrometheusSDExporter struct { 47 BaseExporter 48 49 scrapeCreators map[string]prometheusSDScrapeCreator 50 } 51 52 const ( 53 labelSiteName = "__meta_mentix_site" 54 labelSiteID = "__meta_mentix_site_id" 55 labelSiteCountry = "__meta_mentix_site_country" 56 labelType = "__meta_mentix_type" 57 labelURL = "__meta_mentix_url" 58 labelScheme = "__meta_mentix_scheme" 59 labelHost = "__meta_mentix_host" 60 labelPort = "__meta_mentix_port" 61 labelPath = "__meta_mentix_path" 62 labelServiceHost = "__meta_mentix_service_host" 63 labelServiceURL = "__meta_mentix_service_url" 64 ) 65 66 func createGenericScrapeConfig(site *meshdata.Site, service *meshdata.Service, endpoint *meshdata.ServiceEndpoint) *prometheus.ScrapeConfig { 67 endpointURL, _ := url.Parse(endpoint.URL) 68 labels := getScrapeTargetLabels(site, service, endpoint) 69 return &prometheus.ScrapeConfig{ 70 Targets: []string{endpointURL.Host}, 71 Labels: labels, 72 } 73 } 74 func getScrapeTargetLabels(site *meshdata.Site, service *meshdata.Service, endpoint *meshdata.ServiceEndpoint) map[string]string { 75 endpointURL, _ := url.Parse(endpoint.URL) 76 labels := map[string]string{ 77 labelSiteName: site.Name, 78 labelSiteID: site.ID, 79 labelSiteCountry: site.CountryCode, 80 labelType: endpoint.Type.Name, 81 labelURL: endpoint.URL, 82 labelScheme: endpointURL.Scheme, 83 labelHost: endpointURL.Hostname(), 84 labelPort: endpointURL.Port(), 85 labelPath: endpointURL.Path, 86 labelServiceHost: service.Host, 87 labelServiceURL: service.URL, 88 } 89 90 return labels 91 } 92 93 func (exporter *PrometheusSDExporter) registerScrapeCreators(conf *config.Configuration) error { 94 exporter.scrapeCreators = make(map[string]prometheusSDScrapeCreator) 95 96 registerCreator := func(name string, outputFilename string, creator prometheusSDScrapeCreatorCallback, serviceFilter []string) error { 97 if len(outputFilename) > 0 { // Only register the creator if an output filename was configured 98 exporter.scrapeCreators[name] = prometheusSDScrapeCreator{ 99 outputFilename: outputFilename, 100 creatorCallback: creator, 101 serviceFilter: serviceFilter, 102 } 103 104 // Create the output directory for the target file so it exists when exporting 105 if err := os.MkdirAll(filepath.Dir(outputFilename), 0755); err != nil { 106 return fmt.Errorf("unable to create output directory tree: %v", err) 107 } 108 } 109 110 return nil 111 } 112 113 // Register all scrape creators 114 for _, endpoint := range meshdata.GetServiceEndpoints() { 115 epName := strings.ToLower(endpoint) 116 filename := path.Join(conf.Exporters.PrometheusSD.OutputPath, "svc_"+epName+".json") 117 118 if err := registerCreator(epName, filename, createGenericScrapeConfig, []string{endpoint}); err != nil { 119 return fmt.Errorf("unable to register the '%v' scrape config creator: %v", epName, err) 120 } 121 } 122 123 return nil 124 } 125 126 // Activate activates the exporter. 127 func (exporter *PrometheusSDExporter) Activate(conf *config.Configuration, log *zerolog.Logger) error { 128 if err := exporter.BaseExporter.Activate(conf, log); err != nil { 129 return err 130 } 131 132 if err := exporter.registerScrapeCreators(conf); err != nil { 133 return fmt.Errorf("unable to register the scrape creators: %v", err) 134 } 135 136 // Create all output directories 137 for _, creator := range exporter.scrapeCreators { 138 if err := os.MkdirAll(filepath.Dir(creator.outputFilename), 0755); err != nil { 139 return fmt.Errorf("unable to create directory tree: %v", err) 140 } 141 } 142 143 // Store PrometheusSD specifics 144 exporter.SetEnabledConnectors(conf.Exporters.PrometheusSD.EnabledConnectors) 145 146 return nil 147 } 148 149 // Update is called whenever the mesh data set has changed to reflect these changes. 150 func (exporter *PrometheusSDExporter) Update(meshDataSet meshdata.Map) error { 151 if err := exporter.BaseExporter.Update(meshDataSet); err != nil { 152 return err 153 } 154 155 // Perform exporting the data asynchronously 156 go exporter.exportMeshData() 157 return nil 158 } 159 160 func (exporter *PrometheusSDExporter) exportMeshData() { 161 // Data is read, so acquire a read lock 162 exporter.Locker().RLock() 163 defer exporter.Locker().RUnlock() 164 165 for name, creator := range exporter.scrapeCreators { 166 scrapes := exporter.createScrapeConfigs(creator.creatorCallback, creator.serviceFilter) 167 if err := exporter.exportScrapeConfig(creator.outputFilename, scrapes); err != nil { 168 exporter.Log().Err(err).Str("kind", name).Str("file", creator.outputFilename).Msg("error exporting Prometheus SD scrape config") 169 } else { 170 exporter.Log().Debug().Str("kind", name).Str("file", creator.outputFilename).Msg("exported Prometheus SD scrape config") 171 } 172 } 173 } 174 175 func (exporter *PrometheusSDExporter) createScrapeConfigs(creatorCallback prometheusSDScrapeCreatorCallback, serviceFilter []string) []*prometheus.ScrapeConfig { 176 var scrapes []*prometheus.ScrapeConfig 177 var addScrape = func(site *meshdata.Site, service *meshdata.Service, endpoint *meshdata.ServiceEndpoint) { 178 if len(serviceFilter) == 0 || utils.FindInStringArray(endpoint.Type.Name, serviceFilter, false) != -1 { 179 if scrape := creatorCallback(site, service, endpoint); scrape != nil { 180 scrapes = append(scrapes, scrape) 181 } 182 } 183 } 184 185 // Create a scrape config for each service alongside any additional endpoints 186 for _, site := range exporter.MeshData().Sites { 187 for _, service := range site.Services { 188 if !service.IsMonitored { 189 continue 190 } 191 192 // Add the "main" service to the scrapes 193 addScrape(site, service, service.ServiceEndpoint) 194 195 // Add all additional endpoints as well 196 for _, endpoint := range service.AdditionalEndpoints { 197 if endpoint.IsMonitored { 198 addScrape(site, service, endpoint) 199 } 200 } 201 } 202 } 203 204 if scrapes == nil { 205 scrapes = []*prometheus.ScrapeConfig{} 206 } 207 208 return scrapes 209 } 210 211 func (exporter *PrometheusSDExporter) exportScrapeConfig(outputFilename string, v interface{}) error { 212 // Encode scrape config as JSON 213 data, err := json.MarshalIndent(v, "", "\t") 214 if err != nil { 215 return fmt.Errorf("unable to marshal scrape config: %v", err) 216 } 217 218 // Write the data to disk 219 if err := os.WriteFile(outputFilename, data, 0755); err != nil { 220 return fmt.Errorf("unable to write scrape config '%v': %v", outputFilename, err) 221 } 222 223 return nil 224 } 225 226 // GetID returns the ID of the exporter. 227 func (exporter *PrometheusSDExporter) GetID() string { 228 return config.ExporterIDPrometheusSD 229 } 230 231 // GetName returns the display name of the exporter. 232 func (exporter *PrometheusSDExporter) GetName() string { 233 return "Prometheus SD" 234 } 235 236 func init() { 237 registerExporter(&PrometheusSDExporter{}) 238 }