Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added a site locations exporter to Mentix #972

Merged
merged 18 commits into from
Jul 16, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions changelog/unreleased/mentix-site-locations.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Enhancement: Added a site locations exporter to Mentix.

Mentix now offers an endpoint that exposes location information of all sites in the mesh. This can be used in Grafana's world map view to show the exact location of every site.

https://github.com/cs3org/reva/pull/972
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ Supported values are:

- **webapi**
Mentix exposes its data via an HTTP endpoint using the `webapi` exporter. Data can be retrieved at the configured relative endpoint (see [here](webapi)). The web API currently doesn't support any parameters but will most likely be extended in the future.
- **cs3api** Similar to the WebAPI exporter, the `cs3api` exporter exposes its data via an HTTP endpoint. Data can be retrieved at the configured relative endpoint (see [here](cs3api)). The data is compliant with the CS3API `ProviderInfo` structure.
- **cs3api** Similar to the WebAPI exporter, the `cs3api` exporter exposes its data via an HTTP endpoint. Data can be retrieved at the configured relative endpoint (see [here](cs3api)). The data is compliant with the CS3API `ProviderInfo` structure.
- **siteloc** The Site Locations exporter `siteloc` exposes location information of all sites to be consumed by Grafana at the configured relative endpoint (see [here](siteloc)).
- **prom_filesd**
[Prometheus](https://prometheus.io/) supports discovering new services it should monitor via external configuration files (hence, this is called _file-based service discovery_). Mentix can create such files using the `prom_filesd` exporter. To use this exporter, you have to specify the target output file in the configuration (see [here](prom_filesd)). You also have to set up the discovery service in Prometheus by adding a scrape configuration like the following example to the Prometheus configuration (for more information, visit the official [Prometheus documentation](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#file_sd_config)):
``` scrape_configs:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
---
title: "siteloc"
linkTitle: "siteloc"
weight: 10
description: >
Configuration for the Site Locations exporter of the Mentix service
---

{{% pageinfo %}}
The Site Locations exporter exposes location information of all sites to be consumed by Grafana via an HTTP endpoint.
{{% /pageinfo %}}

{{% dir name="endpoint" type="string" default="/" %}}
The endpoint where the locations data can be queried.
{{< highlight toml >}}
[http.services.mentix.siteloc]
endpoint = "/loc"
{{< /highlight >}}
{{% /dir %}}
7 changes: 5 additions & 2 deletions examples/mentix/mentix.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ enabled_services = ["mentix"]

[http.services.mentix]
connector = "gocdb"
exporters = ["webapi", "cs3api"]
exporters = ["webapi", "cs3api", "siteloc"]
# Enable the Prometheus File Service Discovery:
# exporters = ["webapi", "cs3api", "prom_filesd"]
# exporters = ["webapi", "cs3api", "siteloc", "prom_filesd"]
update_interval = "15m"

[http.services.mentix.gocdb]
Expand All @@ -21,6 +21,9 @@ endpoint = "/"
[http.services.mentix.cs3api]
endpoint = "/cs3"

[http.services.mentix.siteloc]
endpoint = "/loc"

# Configure the Prometheus File Service Discovery:
# [http.services.mentix.prom_filesd]
# Prometheus must be configured to read the following file:
Expand Down
4 changes: 4 additions & 0 deletions internal/http/services/mentix/mentix.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,10 @@ func applyDefaultConfig(conf *config.Configuration) {
if conf.CS3API.Endpoint == "" {
conf.CS3API.Endpoint = "/cs3"
}

if conf.SiteLocations.Endpoint == "" {
conf.SiteLocations.Endpoint = "/loc"
}
}

// New returns a new Mentix service.
Expand Down
4 changes: 4 additions & 0 deletions pkg/mentix/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ type Configuration struct {
Endpoint string `mapstructure:"endpoint"`
} `yaml:"cs3api"`

SiteLocations struct {
Endpoint string `mapstructure:"endpoint"`
} `yaml:"siteloc"`

PrometheusFileSD struct {
OutputFile string `mapstructure:"output_file"`
} `mapstructure:"prom_filesd"`
Expand Down
2 changes: 2 additions & 0 deletions pkg/mentix/config/ids.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ const (
ExporterIDWebAPI = "webapi"
// ExporterIDCS3API is the identifier for the CS3API exporter.
ExporterIDCS3API = "cs3api"
// ExporterIDSiteLocations is the identifier for the Site Locations exporter.
ExporterIDSiteLocations = "siteloc"
// ExporterIDPrometheusFileSD is the identifier for the Prometheus File SD exporter.
ExporterIDPrometheusFileSD = "prom_filesd"
)
2 changes: 2 additions & 0 deletions pkg/mentix/connectors/gocdb.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,8 @@ func (connector *GOCDBConnector) querySites(meshData *meshdata.MeshData) error {
Description: site.Description,
Country: site.Country,
CountryCode: site.CountryCode,
Longitude: site.Longitude,
Latitude: site.Latitude,
Services: nil,
Properties: properties,
}
Expand Down
2 changes: 2 additions & 0 deletions pkg/mentix/connectors/gocdb/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ type Site struct {
Domain string `xml:"DOMAIN>DOMAIN_NAME"`
Country string `xml:"COUNTRY"`
CountryCode string `xml:"COUNTRY_CODE"`
Latitude float32 `xml:"LATITUDE"`
Longitude float32 `xml:"LONGITUDE"`
Extensions Extensions `xml:"EXTENSIONS"`
}

Expand Down
24 changes: 2 additions & 22 deletions pkg/mentix/exporters/cs3api.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,6 @@
package exporters

import (
"fmt"
"net/http"

"github.com/rs/zerolog"

"github.com/cs3org/reva/pkg/mentix/config"
Expand All @@ -39,26 +36,9 @@ func (exporter *CS3APIExporter) Activate(conf *config.Configuration, log *zerolo
return err
}

// Store CS3API specific settings
// Store CS3API specifics
exporter.endpoint = conf.CS3API.Endpoint

return nil
}

// HandleRequest handles the actual HTTP request.
func (exporter *CS3APIExporter) HandleRequest(resp http.ResponseWriter, req *http.Request) error {
// Data is read, so acquire a read lock
exporter.locker.RLock()
defer exporter.locker.RUnlock()

data, err := cs3api.HandleQuery(exporter.meshData, req.URL.Query())
if err == nil {
if _, err := resp.Write(data); err != nil {
return fmt.Errorf("error writing the API request response: %v", err)
}
} else {
return fmt.Errorf("error while serving API request: %v", err)
}
exporter.defaultMethodHandler = cs3api.HandleDefaultQuery

return nil
}
Expand Down
22 changes: 3 additions & 19 deletions pkg/mentix/exporters/cs3api/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,30 +22,14 @@ import (
"encoding/json"
"fmt"
"net/url"
"strings"

ocmprovider "github.com/cs3org/go-cs3apis/cs3/ocm/provider/v1beta1"

"github.com/cs3org/reva/pkg/mentix/meshdata"
)

const (
queryMethodDefault = ""
)

// HandleQuery handles an HTTP request based on the provided 'method' parameter.
func HandleQuery(meshData *meshdata.MeshData, params url.Values) ([]byte, error) {
method := params.Get("method")
switch strings.ToLower(method) {
case queryMethodDefault:
return handleDefaultQuery(meshData, params)

default:
return []byte{}, fmt.Errorf("unknown API method '%v'", method)
}
}

func handleDefaultQuery(meshData *meshdata.MeshData, params url.Values) ([]byte, error) {
// HandleDefaultQuery processes a basic query.
func HandleDefaultQuery(meshData *meshdata.MeshData, params url.Values) ([]byte, error) {
// Convert the mesh data
ocmData, err := convertMeshDataToOCMData(meshData)
if err != nil {
Expand All @@ -55,7 +39,7 @@ func handleDefaultQuery(meshData *meshdata.MeshData, params url.Values) ([]byte,
// Marshal the OCM data as JSON
data, err := json.MarshalIndent(ocmData, "", "\t")
if err != nil {
return []byte{}, fmt.Errorf("unable to marshal the mesh data: %v", err)
return []byte{}, fmt.Errorf("unable to marshal the OCM data: %v", err)
}

return data, nil
Expand Down
45 changes: 45 additions & 0 deletions pkg/mentix/exporters/reqexporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,20 @@
package exporters

import (
"fmt"
"net/http"
"net/url"
"strings"

"github.com/cs3org/reva/pkg/mentix/meshdata"
)

const (
queryMethodDefault = ""
)

type queryCallback func(*meshdata.MeshData, url.Values) ([]byte, error)

// RequestExporter is the interface implemented by exporters that offer an HTTP endpoint.
type RequestExporter interface {
Exporter
Expand All @@ -40,6 +50,8 @@ type BaseRequestExporter struct {
BaseExporter

endpoint string

defaultMethodHandler queryCallback
}

// Endpoint returns the (relative) endpoint of the exporter.
Expand All @@ -56,3 +68,36 @@ func (exporter *BaseRequestExporter) Endpoint() string {
func (exporter *BaseRequestExporter) WantsRequest(r *http.Request) bool {
return r.URL.Path == exporter.Endpoint()
}

// HandleRequest handles the actual HTTP request.
func (exporter *BaseRequestExporter) HandleRequest(resp http.ResponseWriter, req *http.Request) error {
// Data is read, so acquire a read lock
exporter.locker.RLock()
defer exporter.locker.RUnlock()

data, err := exporter.handleQuery(exporter.meshData, req.URL.Query())
if err == nil {
if _, err := resp.Write(data); err != nil {
return fmt.Errorf("error writing the API request response: %v", err)
}
} else {
return fmt.Errorf("error while serving API request: %v", err)
}

return nil
}

func (exporter *BaseRequestExporter) handleQuery(meshData *meshdata.MeshData, params url.Values) ([]byte, error) {
method := params.Get("method")
switch strings.ToLower(method) {
case queryMethodDefault:
if exporter.defaultMethodHandler != nil {
return exporter.defaultMethodHandler(meshData, params)
}

default:
return []byte{}, fmt.Errorf("unknown API method '%v'", method)
}

return []byte{}, fmt.Errorf("unhandled query for method '%v'", method)
}
59 changes: 59 additions & 0 deletions pkg/mentix/exporters/siteloc/query.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Copyright 2018-2020 CERN
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// In applying this license, CERN does not waive the privileges and immunities
// granted to it by virtue of its status as an Intergovernmental Organization
// or submit itself to any jurisdiction.

package siteloc

import (
"encoding/json"
"fmt"
"net/url"

"github.com/cs3org/reva/pkg/mentix/meshdata"
)

// HandleDefaultQuery processes a basic query.
func HandleDefaultQuery(meshData *meshdata.MeshData, params url.Values) ([]byte, error) {
// Convert the mesh data
locData, err := convertMeshDataToLocationData(meshData)
if err != nil {
return []byte{}, fmt.Errorf("unable to convert the mesh data to location data: %v", err)
}

// Marshal the location data as JSON
data, err := json.MarshalIndent(locData, "", "\t")
if err != nil {
return []byte{}, fmt.Errorf("unable to marshal the location data: %v", err)
}

return data, nil
}

func convertMeshDataToLocationData(meshData *meshdata.MeshData) ([]*SiteLocation, error) {
// Gather the locations of all sites
locations := make([]*SiteLocation, 0, len(meshData.Sites))
for _, site := range meshData.Sites {
locations = append(locations, &SiteLocation{
Site: site.Name,
FullName: site.FullName,
Longitude: site.Longitude,
Latitude: site.Latitude,
})
}

return locations, nil
}
27 changes: 27 additions & 0 deletions pkg/mentix/exporters/siteloc/types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright 2018-2020 CERN
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// In applying this license, CERN does not waive the privileges and immunities
// granted to it by virtue of its status as an Intergovernmental Organization
// or submit itself to any jurisdiction.

package siteloc

// SiteLocation represents the location information of a site.
type SiteLocation struct {
Site string `json:"key"`
FullName string `json:"name"`
Longitude float32 `json:"longitude"`
Latitude float32 `json:"latitude"`
}
53 changes: 53 additions & 0 deletions pkg/mentix/exporters/sitelocations.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// Copyright 2018-2020 CERN
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// In applying this license, CERN does not waive the privileges and immunities
// granted to it by virtue of its status as an Intergovernmental Organization
// or submit itself to any jurisdiction.

package exporters

import (
"github.com/rs/zerolog"

"github.com/cs3org/reva/pkg/mentix/config"
"github.com/cs3org/reva/pkg/mentix/exporters/siteloc"
)

// SiteLocationsExporter implements the Site Locations exporter to use with Grafana.
type SiteLocationsExporter struct {
BaseRequestExporter
}

// Activate activates the exporter.
func (exporter *SiteLocationsExporter) Activate(conf *config.Configuration, log *zerolog.Logger) error {
if err := exporter.BaseExporter.Activate(conf, log); err != nil {
return err
}

// Store SiteLocations specifics
exporter.endpoint = conf.SiteLocations.Endpoint
exporter.defaultMethodHandler = siteloc.HandleDefaultQuery

return nil
}

// GetName returns the display name of the exporter.
func (exporter *SiteLocationsExporter) GetName() string {
return "Site Locations"
}

func init() {
registerExporter(config.ExporterIDSiteLocations, &SiteLocationsExporter{})
}
Loading