From d01b95e1c6faef595177b210befb875556bdcf23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20M=C3=BCller?= Date: Tue, 23 Mar 2021 14:46:23 +0100 Subject: [PATCH 01/21] Add registration form --- .../config/http/services/siteacc/_index.md | 8 + examples/siteacc/siteacc.toml | 2 + .../http/services/siteacc/config/config.go | 2 + .../http/services/siteacc/config/endpoints.go | 2 + internal/http/services/siteacc/manager.go | 18 +- .../services/siteacc/registration/form.go | 76 ++++++ .../services/siteacc/registration/template.go | 216 ++++++++++++++++++ internal/http/services/siteacc/siteacc.go | 23 +- 8 files changed, 343 insertions(+), 4 deletions(-) create mode 100644 internal/http/services/siteacc/registration/form.go create mode 100644 internal/http/services/siteacc/registration/template.go diff --git a/docs/content/en/docs/config/http/services/siteacc/_index.md b/docs/content/en/docs/config/http/services/siteacc/_index.md index 425d3f0c7c..cd6955caf9 100644 --- a/docs/content/en/docs/config/http/services/siteacc/_index.md +++ b/docs/content/en/docs/config/http/services/siteacc/_index.md @@ -19,6 +19,14 @@ prefix = "/siteacc" {{< /highlight >}} {{% /dir %}} +{{% dir name="enable_registration_form" type="string" default="false" %}} +If set to true, the service will expose a simple form for account registration. +{{< highlight toml >}} +[http.services.siteacc] +enable_registration_form = true +{{< /highlight >}} +{{% /dir %}} + {{% dir name="notifications_mail" type="string" default="" %}} An email address where all notifications are sent to. {{< highlight toml >}} diff --git a/examples/siteacc/siteacc.toml b/examples/siteacc/siteacc.toml index a03c393065..093fe58f86 100644 --- a/examples/siteacc/siteacc.toml +++ b/examples/siteacc/siteacc.toml @@ -2,6 +2,8 @@ address = "0.0.0.0:9600" [http.services.siteacc] +# If this is set to true, the service will expose a simple form for account registration +enable_registration_form = true # All notification emails are sent to this email notifications_mail = "science.mesh@example.com" diff --git a/internal/http/services/siteacc/config/config.go b/internal/http/services/siteacc/config/config.go index 6a4a282183..95734e820e 100644 --- a/internal/http/services/siteacc/config/config.go +++ b/internal/http/services/siteacc/config/config.go @@ -32,6 +32,8 @@ type Configuration struct { } `mapstructure:"file"` } `mapstructure:"storage"` + EnableRegistrationForm bool `mapstructure:"enable_registration_form"` + SMTP *smtpclient.SMTPCredentials `mapstructure:"smtp"` NotificationsMail string `mapstructure:"notifications_mail"` } diff --git a/internal/http/services/siteacc/config/endpoints.go b/internal/http/services/siteacc/config/endpoints.go index 80541bf803..11663a6ca6 100644 --- a/internal/http/services/siteacc/config/endpoints.go +++ b/internal/http/services/siteacc/config/endpoints.go @@ -21,6 +21,8 @@ package config const ( // EndpointPanel is the endpoint path of the web interface panel. EndpointPanel = "/panel" + // EndpointRegistration is the endpoint path of the web interface registration form. + EndpointRegistration = "/register" // EndpointGenerateAPIKey is the endpoint path of the API key generator. EndpointGenerateAPIKey = "/generate-api-key" diff --git a/internal/http/services/siteacc/manager.go b/internal/http/services/siteacc/manager.go index f625dd96a3..4f992185aa 100644 --- a/internal/http/services/siteacc/manager.go +++ b/internal/http/services/siteacc/manager.go @@ -33,6 +33,7 @@ import ( "github.com/cs3org/reva/internal/http/services/siteacc/data" "github.com/cs3org/reva/internal/http/services/siteacc/email" "github.com/cs3org/reva/internal/http/services/siteacc/panel" + "github.com/cs3org/reva/internal/http/services/siteacc/registration" "github.com/cs3org/reva/pkg/mentix/key" "github.com/cs3org/reva/pkg/smtpclient" ) @@ -54,8 +55,9 @@ type Manager struct { accounts data.Accounts storage data.Storage - panel *panel.Panel - smtp *smtpclient.SMTPCredentials + panel *panel.Panel + registrationForm *registration.Form + smtp *smtpclient.SMTPCredentials mutex sync.RWMutex } @@ -88,6 +90,13 @@ func (mngr *Manager) initialize(conf *config.Configuration, log *zerolog.Logger) return errors.Wrap(err, "unable to create panel") } + // Create the web interface registrationForm + if frm, err := registration.NewForm(conf, log); err == nil { + mngr.registrationForm = frm + } else { + return errors.Wrap(err, "unable to create registrationForm") + } + // Create the SMTP client if conf.SMTP != nil { mngr.smtp = smtpclient.NewSMTPCredentials(conf.SMTP) @@ -163,6 +172,11 @@ func (mngr *Manager) ShowPanel(w http.ResponseWriter) error { return mngr.panel.Execute(w, &accounts) } +// ShowRegistrationForm writes the registration registrationForm HTTP output directly to the response writer. +func (mngr *Manager) ShowRegistrationForm(w http.ResponseWriter) error { + return mngr.registrationForm.Execute(w) +} + // CreateAccount creates a new account; if an account with the same email address already exists, an error is returned. func (mngr *Manager) CreateAccount(accountData *data.Account) error { mngr.mutex.Lock() diff --git a/internal/http/services/siteacc/registration/form.go b/internal/http/services/siteacc/registration/form.go new file mode 100644 index 0000000000..e7649e891d --- /dev/null +++ b/internal/http/services/siteacc/registration/form.go @@ -0,0 +1,76 @@ +// 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 registration + +import ( + "html/template" + "net/http" + + "github.com/pkg/errors" + "github.com/rs/zerolog" + + "github.com/cs3org/reva/internal/http/services/siteacc/config" +) + +// Form represents the web interface form for user account registration. +type Form struct { + conf *config.Configuration + log *zerolog.Logger + + tpl *template.Template +} + +func (form *Form) initialize(conf *config.Configuration, log *zerolog.Logger) error { + if conf == nil { + return errors.Errorf("no configuration provided") + } + form.conf = conf + + if log == nil { + return errors.Errorf("no logger provided") + } + form.log = log + + // Create the form template + form.tpl = template.New("form") + if _, err := form.tpl.Parse(formTemplate); err != nil { + return errors.Wrap(err, "error while parsing form template") + } + + return nil +} + +// Execute generates the HTTP output of the form and writes it to the response writer. +func (form *Form) Execute(w http.ResponseWriter) error { + type TemplateData struct { + } + + tplData := TemplateData{} + + return form.tpl.Execute(w, tplData) +} + +// NewForm creates a new web interface form. +func NewForm(conf *config.Configuration, log *zerolog.Logger) (*Form, error) { + form := &Form{} + if err := form.initialize(conf, log); err != nil { + return nil, errors.Wrapf(err, "unable to initialize the form") + } + return form, nil +} diff --git a/internal/http/services/siteacc/registration/template.go b/internal/http/services/siteacc/registration/template.go new file mode 100644 index 0000000000..8da584177d --- /dev/null +++ b/internal/http/services/siteacc/registration/template.go @@ -0,0 +1,216 @@ +// 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 registration + +const formTemplate = ` + + + + + + ScienceMesh Account Registration + + + +
+

Welcome to the ScienceMesh Account Registration!

+
+

Fill out the form below to register for a ScienceMesh account. A confirmation email will be sent to you shortly after registration.

+

+ After inspection by a ScienceMesh administrator, you will also receive an API key which can then be used in the + ownCloud or + Nextcloud web application. +

+
+
 
+
+
+
+
+
+
+
+
+ +
+ Fields marked with * are mandatory. +
+
+ + +
+
+
+ + + +
+ + +` diff --git a/internal/http/services/siteacc/siteacc.go b/internal/http/services/siteacc/siteacc.go index 1d49e73570..960148dc02 100644 --- a/internal/http/services/siteacc/siteacc.go +++ b/internal/http/services/siteacc/siteacc.go @@ -63,8 +63,15 @@ func (s *svc) Prefix() string { // Unprotected returns all endpoints that can be queried without prior authorization. func (s *svc) Unprotected() []string { - // This service currently only has one public endpoint used for account registration - return []string{config.EndpointCreate} + // The account creation endpoint is always unprotected + endpoints := []string{config.EndpointCreate} + + // If enabled, the registration registrationForm endpoint is also unprotected + if s.conf.EnableRegistrationForm { + endpoints = append(endpoints, config.EndpointRegistration) + } + + return endpoints } // Handler serves all HTTP requests. @@ -76,6 +83,11 @@ func (s *svc) Handler() http.Handler { case config.EndpointPanel: s.handlePanelEndpoint(w, r) + case config.EndpointRegistration: + if s.conf.EnableRegistrationForm { + s.handleRegistrationEndpoint(w, r) + } + default: s.handleRequestEndpoints(w, r) } @@ -89,6 +101,13 @@ func (s *svc) handlePanelEndpoint(w http.ResponseWriter, r *http.Request) { } } +func (s *svc) handleRegistrationEndpoint(w http.ResponseWriter, r *http.Request) { + if err := s.manager.ShowRegistrationForm(w); err != nil { + w.WriteHeader(http.StatusInternalServerError) + _, _ = w.Write([]byte(fmt.Sprintf("Unable to show the web interface registration registrationForm: %v", err))) + } +} + func (s *svc) handleRequestEndpoints(w http.ResponseWriter, r *http.Request) { // Allow definition of endpoints in a flexible and easy way type Endpoint struct { From 5e795b6732ab0b04f249d19322f940ad69405dd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20M=C3=BCller?= Date: Thu, 25 Mar 2021 16:22:31 +0100 Subject: [PATCH 02/21] Check accounts service response status --- pkg/mentix/accservice/accservice.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/mentix/accservice/accservice.go b/pkg/mentix/accservice/accservice.go index 8d53bbba50..5d1ae1e973 100644 --- a/pkg/mentix/accservice/accservice.go +++ b/pkg/mentix/accservice/accservice.go @@ -53,7 +53,7 @@ func Query(endpoint string, params network.URLParams) (*RequestResponse, error) return nil, errors.Wrap(err, "error while building the service accounts query URL") } - data, err := network.ReadEndpoint(fullURL, &network.BasicAuth{User: settings.User, Password: settings.Password}, false) + data, err := network.ReadEndpoint(fullURL, &network.BasicAuth{User: settings.User, Password: settings.Password}, true) if err != nil { return nil, errors.Wrap(err, "unable to query the service accounts endpoint") } From 355e4e98e36ed94ff5f4aa3535117710bdaf9e15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20M=C3=BCller?= Date: Fri, 26 Mar 2021 10:34:42 +0100 Subject: [PATCH 03/21] Remove site registration from xcloud metrics driver --- examples/metrics/xcloud.toml | 3 +- pkg/metrics/config/config.go | 5 +- pkg/metrics/driver/xcloud/xcloud.go | 94 +---------------------------- 3 files changed, 8 insertions(+), 94 deletions(-) diff --git a/examples/metrics/xcloud.toml b/examples/metrics/xcloud.toml index 95b0d1b4d8..8896d40ea1 100644 --- a/examples/metrics/xcloud.toml +++ b/examples/metrics/xcloud.toml @@ -5,7 +5,6 @@ address = "0.0.0.0:5550" metrics_data_driver_type = "xcloud" metrics_record_interval = 5000 xcloud_instance = "http://localhost" -xcloud_interval = 5 -xcloud_catalog = 'https://sciencemesh-test.uni-muenster.de/api/mentix/sites?action=register' +xcloud_pull_interval = 60 [http.services.prometheus] diff --git a/pkg/metrics/config/config.go b/pkg/metrics/config/config.go index 424e5e1659..91fe59c8ac 100644 --- a/pkg/metrics/config/config.go +++ b/pkg/metrics/config/config.go @@ -24,7 +24,6 @@ type Config struct { MetricsDataLocation string `mapstructure:"metrics_data_location"` MetricsRecordInterval int `mapstructure:"metrics_record_interval"` XcloudInstance string `mapstructure:"xcloud_instance"` - XcloudCatalog string `mapstructure:"xcloud_catalog"` XcloudPullInterval int `mapstructure:"xcloud_pull_interval"` } @@ -40,4 +39,8 @@ func (c *Config) Init() { if c.MetricsRecordInterval == 0 { c.MetricsRecordInterval = 5000 } + + if c.XcloudPullInterval == 0 { + c.XcloudPullInterval = 5 + } } diff --git a/pkg/metrics/driver/xcloud/xcloud.go b/pkg/metrics/driver/xcloud/xcloud.go index 9855b3f88d..3c8ec3d42e 100644 --- a/pkg/metrics/driver/xcloud/xcloud.go +++ b/pkg/metrics/driver/xcloud/xcloud.go @@ -19,7 +19,6 @@ package json import ( - "bytes" "crypto/tls" "encoding/json" "errors" @@ -32,9 +31,10 @@ import ( "github.com/cs3org/reva/pkg/metrics/driver/registry" + "github.com/rs/zerolog" + "github.com/cs3org/reva/pkg/logger" "github.com/cs3org/reva/pkg/metrics/config" - "github.com/rs/zerolog" ) var log zerolog.Logger @@ -43,7 +43,6 @@ func init() { log = logger.New().With().Int("pid", os.Getpid()).Logger() driver := &CloudDriver{CloudData: &CloudData{}} registry.Register(driverName(), driver) - } func driverName() string { @@ -53,7 +52,6 @@ func driverName() string { // CloudDriver is the driver to use for Sciencemesh apps type CloudDriver struct { instance string - catalog string pullInterval int CloudData *CloudData sync.Mutex @@ -61,7 +59,6 @@ type CloudDriver struct { } func (d *CloudDriver) refresh() error { - // endpoint example: https://mybox.com or https://mybox.com/owncloud endpoint := fmt.Sprintf("%s/index.php/apps/sciencemesh/internal_metrics", d.instance) @@ -102,59 +99,6 @@ func (d *CloudDriver) refresh() error { d.CloudData = cd log.Info().Msgf("xcloud: received internal metrics from cloud provider: %+v", cd) - mc := &MentixCatalog{ - Name: cd.Settings.Sitename, - FullName: cd.Settings.Sitename, - Homepage: cd.Settings.Hostname, - Description: "ScienceMesh App from " + cd.Settings.Sitename, - CountryCode: cd.Settings.Country, - Services: []*MentixService{ - &MentixService{ - Host: cd.Settings.Hostname, - IsMonitored: true, - Name: cd.Settings.Hostname + " - REVAD", - URL: cd.Settings.Siteurl, - Properties: &MentixServiceProperties{ - MetricsPath: "/index.php/apps/sciencemesh/metrics", - }, - Type: &MentixServiceType{ - Name: "REVAD", - }, - }, - }, - } - - j, err := json.Marshal(mc) - if err != nil { - log.Err(err).Msgf("xcloud: error marhsaling mentix calalog info") - return err - } - - log.Info().Msgf("xcloud: info to send to register: %s", string(j)) - - // send to register if catalog is set - req, err = http.NewRequest("POST", d.catalog, bytes.NewBuffer(j)) - if err != nil { - log.Err(err).Msgf("xcloud: error creating POST request to: %s", d.catalog) - return err - } - - resp, err = d.client.Do(req) - if err != nil { - log.Err(err).Msgf("xcloud: error registering catalog info to: %s with info: %s", d.catalog, string(j)) - return err - } - - defer resp.Body.Close() - body, _ := ioutil.ReadAll(resp.Body) - - if resp.StatusCode != http.StatusOK { - err := fmt.Errorf("xcloud: error registering site: status code(%d) body(%s)", resp.StatusCode, string(body)) - log.Err(err).Msg("xcloud: error registering site") - return err - } - - log.Info().Msgf("xcloud: site registered: %s", string(body)) return nil } @@ -166,12 +110,11 @@ func (d *CloudDriver) Configure(c *config.Config) error { } if c.XcloudPullInterval == 0 { - c.XcloudPullInterval = 10 // seconds + c.XcloudPullInterval = 5 // seconds } d.instance = c.XcloudInstance d.pullInterval = c.XcloudPullInterval - d.catalog = c.XcloudCatalog // TODO(labkode): make it configurable once site adopted are prod-ready tr := &http.Transport{ @@ -234,36 +177,5 @@ type CloudDataSettings struct { IOPUrl string `json:"iopurl"` Sitename string `json:"sitename"` Siteurl string `json:"siteurl"` - Hostname string `json:"hostname"` Country string `json:"country"` } - -// MentixCatalog represents the information needed to register a site into the mesh -type MentixCatalog struct { - CountryCode string `json:"CountryCode"` - Description string `json:"Description"` - FullName string `json:"FullName"` - Homepage string `json:"Homepage"` - Name string `json:"Name"` - Services []*MentixService `json:"Services"` -} - -// MentixService represents the service running in a site -type MentixService struct { - Host string `json:"Host"` - IsMonitored bool `json:"IsMonitored"` - Name string `json:"Name"` - Properties *MentixServiceProperties `json:"Properties"` - Type *MentixServiceType `json:"Type"` - URL string `json:"URL"` -} - -// MentixServiceProperties represents the properties to expose the metrics endpoint -type MentixServiceProperties struct { - MetricsPath string `json:"METRICS_PATH"` -} - -// MentixServiceType represents the type of service running -type MentixServiceType struct { - Name string `json:"Name"` -} From 54b6e21a5e912a414d947fbd97b3f7c50e3759f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20M=C3=BCller?= Date: Mon, 29 Mar 2021 13:13:53 +0200 Subject: [PATCH 04/21] Add ability to unregister an account's site to the panel --- .../config/http/services/siteacc/_index.md | 10 ++++ .../http/services/siteacc/config/config.go | 4 ++ .../http/services/siteacc/config/endpoints.go | 4 +- .../http/services/siteacc/data/account.go | 3 +- internal/http/services/siteacc/manager.go | 26 ++++++++- .../http/services/siteacc/panel/template.go | 7 ++- internal/http/services/siteacc/siteacc.go | 21 ++++++- .../http/services/siteacc/sitereg/sitereg.go | 57 +++++++++++++++++++ pkg/mentix/key/apikey.go | 6 ++ 9 files changed, 129 insertions(+), 9 deletions(-) create mode 100644 internal/http/services/siteacc/sitereg/sitereg.go diff --git a/docs/content/en/docs/config/http/services/siteacc/_index.md b/docs/content/en/docs/config/http/services/siteacc/_index.md index cd6955caf9..d56f901c0f 100644 --- a/docs/content/en/docs/config/http/services/siteacc/_index.md +++ b/docs/content/en/docs/config/http/services/siteacc/_index.md @@ -21,6 +21,7 @@ prefix = "/siteacc" {{% dir name="enable_registration_form" type="string" default="false" %}} If set to true, the service will expose a simple form for account registration. + {{< highlight toml >}} [http.services.siteacc] enable_registration_form = true @@ -101,3 +102,12 @@ The file location. file = "/var/reva/accounts.json" {{< /highlight >}} {{% /dir %}} + +## Site registration settings +{{% dir name="url" type="string" default="" %}} +The registration service URL. +{{< highlight toml >}} +[http.services.siteacc.sitereg] +url = "https://iop.example.com/sitereg" +{{< /highlight >}} +{{% /dir %}} diff --git a/internal/http/services/siteacc/config/config.go b/internal/http/services/siteacc/config/config.go index 95734e820e..60eeec1902 100644 --- a/internal/http/services/siteacc/config/config.go +++ b/internal/http/services/siteacc/config/config.go @@ -36,4 +36,8 @@ type Configuration struct { SMTP *smtpclient.SMTPCredentials `mapstructure:"smtp"` NotificationsMail string `mapstructure:"notifications_mail"` + + SiteRegistration struct { + URL string `mapstructure:"url"` + } `mapstructure:"sitereg"` } diff --git a/internal/http/services/siteacc/config/endpoints.go b/internal/http/services/siteacc/config/endpoints.go index 11663a6ca6..8042085e61 100644 --- a/internal/http/services/siteacc/config/endpoints.go +++ b/internal/http/services/siteacc/config/endpoints.go @@ -43,8 +43,10 @@ const ( // EndpointRemove is the endpoint path for account removal. EndpointRemove = "/remove" - // EndpointAuthorize is the endpoint path for account authorization + // EndpointAuthorize is the endpoint path for account authorization. EndpointAuthorize = "/authorize" // EndpointIsAuthorized is the endpoint path used to check the authorization status of an account. EndpointIsAuthorized = "/is-authorized" + // EndpointUnregisterSite is the endpoint path for site unregistration. + EndpointUnregisterSite = "/unregister-site" ) diff --git a/internal/http/services/siteacc/data/account.go b/internal/http/services/siteacc/data/account.go index dce4ea7b1a..914d5b7e12 100644 --- a/internal/http/services/siteacc/data/account.go +++ b/internal/http/services/siteacc/data/account.go @@ -19,7 +19,6 @@ package data import ( - "strings" "time" "github.com/pkg/errors" @@ -51,7 +50,7 @@ type Accounts = []*Account // GetSiteID returns the site ID (generated from the API key) for the given account. func (acc *Account) GetSiteID() key.SiteIdentifier { - if id, err := key.CalculateSiteID(acc.Data.APIKey, strings.ToLower(acc.Email)); err == nil { + if id, err := key.CalculateSiteID(acc.Data.APIKey, key.SaltFromEmail(acc.Email)); err == nil { return id } diff --git a/internal/http/services/siteacc/manager.go b/internal/http/services/siteacc/manager.go index 4f992185aa..6d7426ec61 100644 --- a/internal/http/services/siteacc/manager.go +++ b/internal/http/services/siteacc/manager.go @@ -34,6 +34,7 @@ import ( "github.com/cs3org/reva/internal/http/services/siteacc/email" "github.com/cs3org/reva/internal/http/services/siteacc/panel" "github.com/cs3org/reva/internal/http/services/siteacc/registration" + "github.com/cs3org/reva/internal/http/services/siteacc/sitereg" "github.com/cs3org/reva/pkg/mentix/key" "github.com/cs3org/reva/pkg/smtpclient" ) @@ -275,7 +276,7 @@ func (mngr *Manager) AssignAPIKeyToAccount(accountData *data.Account, flags int) } for { - apiKey, err := key.GenerateAPIKey(strings.ToLower(account.Email), flags) // Use the (lowered) email address as the key's salt value + apiKey, err := key.GenerateAPIKey(key.SaltFromEmail(account.Email), flags) if err != nil { return errors.Wrap(err, "error while generating API key") } @@ -297,6 +298,29 @@ func (mngr *Manager) AssignAPIKeyToAccount(accountData *data.Account, flags int) return nil } +// UnregisterAccountSite unregisters the site associated with the given account. +func (mngr *Manager) UnregisterAccountSite(accountData *data.Account) error { + mngr.mutex.RLock() + defer mngr.mutex.RUnlock() + + account, err := mngr.findAccount(FindByEmail, accountData.Email) + if err != nil { + return errors.Wrap(err, "no account with the specified email exists") + } + + salt := key.SaltFromEmail(account.Email) + siteId, err := key.CalculateSiteID(account.Data.APIKey, salt) + if err != nil { + return errors.Wrap(err, "unable to get site ID") + } + + if err := sitereg.UnregisterSite(mngr.conf.SiteRegistration.URL, account.Data.APIKey, siteId, salt); err != nil { + return errors.Wrap(err, "error while unregistering the site") + } + + return nil +} + // RemoveAccount removes the account identified by the account email; if no such account exists, an error is returned. func (mngr *Manager) RemoveAccount(accountData *data.Account) error { mngr.mutex.Lock() diff --git a/internal/http/services/siteacc/panel/template.go b/internal/http/services/siteacc/panel/template.go index 7493d15287..df3fa7636f 100644 --- a/internal/http/services/siteacc/panel/template.go +++ b/internal/http/services/siteacc/panel/template.go @@ -89,11 +89,14 @@ const panelTemplate = ` {{if .Data.Authorized}} - + {{else}} - + {{end}} +   + +   diff --git a/internal/http/services/siteacc/siteacc.go b/internal/http/services/siteacc/siteacc.go index 960148dc02..e3f5765d39 100644 --- a/internal/http/services/siteacc/siteacc.go +++ b/internal/http/services/siteacc/siteacc.go @@ -134,6 +134,7 @@ func (s *svc) handleRequestEndpoints(w http.ResponseWriter, r *http.Request) { {config.EndpointRemove, http.MethodPost, s.handleRemove}, {config.EndpointAuthorize, http.MethodPost, s.handleAuthorize}, {config.EndpointIsAuthorized, http.MethodGet, s.handleIsAuthorized}, + {config.EndpointUnregisterSite, http.MethodPost, s.handleUnregisterSite}, } // The default response is an unknown endpoint (for the specified method) @@ -183,7 +184,7 @@ func (s *svc) handleGenerateAPIKey(values url.Values, body []byte) (interface{}, return nil, errors.Errorf("no email provided") } - apiKey, err := key.GenerateAPIKey(strings.ToLower(email), flags) + apiKey, err := key.GenerateAPIKey(key.SaltFromEmail(email), flags) if err != nil { return nil, errors.Wrap(err, "unable to generate API key") } @@ -202,7 +203,7 @@ func (s *svc) handleVerifyAPIKey(values url.Values, body []byte) (interface{}, e return nil, errors.Errorf("no email provided") } - err := key.VerifyAPIKey(apiKey, strings.ToLower(email)) + err := key.VerifyAPIKey(apiKey, key.SaltFromEmail(email)) if err != nil { return nil, errors.Wrap(err, "invalid API key") } @@ -290,6 +291,20 @@ func (s *svc) handleIsAuthorized(values url.Values, body []byte) (interface{}, e return account.Data.Authorized, nil } +func (s *svc) handleUnregisterSite(values url.Values, body []byte) (interface{}, error) { + account, err := s.unmarshalRequestData(body) + if err != nil { + return nil, err + } + + // Unregister the account's site through the account manager + if err := s.manager.UnregisterAccountSite(account); err != nil { + return nil, errors.Wrap(err, "unable to unregister the site of the given account") + } + + return nil, nil +} + func (s *svc) handleAuthorize(values url.Values, body []byte) (interface{}, error) { account, err := s.unmarshalRequestData(body) if err != nil { @@ -311,7 +326,7 @@ func (s *svc) handleAuthorize(values url.Values, body []byte) (interface{}, erro // Authorize the account through the account manager if err := s.manager.AuthorizeAccount(account, authorize); err != nil { - return nil, errors.Wrap(err, "unable to remove account") + return nil, errors.Wrap(err, "unable to (un)authorize account") } } else { return nil, errors.Errorf("no authorization status provided") diff --git a/internal/http/services/siteacc/sitereg/sitereg.go b/internal/http/services/siteacc/sitereg/sitereg.go new file mode 100644 index 0000000000..f9bef2ee87 --- /dev/null +++ b/internal/http/services/siteacc/sitereg/sitereg.go @@ -0,0 +1,57 @@ +// 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 sitereg + +import ( + "fmt" + "net/url" + + "github.com/pkg/errors" + + "github.com/cs3org/reva/pkg/mentix/key" + "github.com/cs3org/reva/pkg/mentix/utils/network" +) + +// UnregisterSite unregister a site using the given site registration endpoint. +func UnregisterSite(serviceUrl string, apiKey key.APIKey, siteId key.SiteIdentifier, salt string) error { + if len(serviceUrl) == 0 { + return errors.Errorf("no site registration service URL provided") + } + + if err := key.VerifyAPIKey(apiKey, salt); err != nil { + return err + } + + fullURL, err := url.Parse(serviceUrl) + if err != nil { + return errors.Wrap(err, "unable to parse the site registration service URL") + } + fullURL.Query().Add("action", "unregister") + fullURL.Query().Add("apiKey", apiKey) + fullURL.Query().Add("siteId", siteId) + + fmt.Println(fullURL.String()) + + _, err = network.ReadEndpoint(fullURL, nil, true) + if err != nil { + return errors.Wrap(err, "unable to query the service registration endpoint") + } + + return nil +} diff --git a/pkg/mentix/key/apikey.go b/pkg/mentix/key/apikey.go index 4157e08ff0..8da609a016 100644 --- a/pkg/mentix/key/apikey.go +++ b/pkg/mentix/key/apikey.go @@ -24,6 +24,7 @@ import ( "fmt" hashpkg "hash" "strconv" + "strings" "github.com/pkg/errors" ) @@ -94,6 +95,11 @@ func SplitAPIKey(apiKey APIKey) (string, int, string, error) { return randomString, flags, hash, nil } +// SaltFromEmail generates a salt-value from an email address. +func SaltFromEmail(email string) string { + return strings.ToLower(email) +} + func calculateHash(randomString string, flags int, salt string) hashpkg.Hash { hash := md5.New() _, _ = hash.Write([]byte(randomString)) From 4f63189f74a30db55732c0c697fa690f8341088a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20M=C3=BCller?= Date: Mon, 29 Mar 2021 13:49:05 +0200 Subject: [PATCH 05/21] Fix error reporting --- pkg/mentix/accservice/accservice.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/mentix/accservice/accservice.go b/pkg/mentix/accservice/accservice.go index 5d1ae1e973..8d53bbba50 100644 --- a/pkg/mentix/accservice/accservice.go +++ b/pkg/mentix/accservice/accservice.go @@ -53,7 +53,7 @@ func Query(endpoint string, params network.URLParams) (*RequestResponse, error) return nil, errors.Wrap(err, "error while building the service accounts query URL") } - data, err := network.ReadEndpoint(fullURL, &network.BasicAuth{User: settings.User, Password: settings.Password}, true) + data, err := network.ReadEndpoint(fullURL, &network.BasicAuth{User: settings.User, Password: settings.Password}, false) if err != nil { return nil, errors.Wrap(err, "unable to query the service accounts endpoint") } From 66f5355891c1501b2dad9b98d64caa321737ad1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20M=C3=BCller?= Date: Mon, 29 Mar 2021 14:03:22 +0200 Subject: [PATCH 06/21] Fix site unregister endpoint --- internal/http/services/siteacc/sitereg/sitereg.go | 2 +- pkg/mentix/utils/network/network.go | 15 ++++++++++++--- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/internal/http/services/siteacc/sitereg/sitereg.go b/internal/http/services/siteacc/sitereg/sitereg.go index f9bef2ee87..cd7372ba19 100644 --- a/internal/http/services/siteacc/sitereg/sitereg.go +++ b/internal/http/services/siteacc/sitereg/sitereg.go @@ -48,7 +48,7 @@ func UnregisterSite(serviceUrl string, apiKey key.APIKey, siteId key.SiteIdentif fmt.Println(fullURL.String()) - _, err = network.ReadEndpoint(fullURL, nil, true) + _, err = network.WriteEndpoint(fullURL, nil, true) if err != nil { return errors.Wrap(err, "unable to query the service registration endpoint") } diff --git a/pkg/mentix/utils/network/network.go b/pkg/mentix/utils/network/network.go index da6fa65bec..6185ce8fde 100644 --- a/pkg/mentix/utils/network/network.go +++ b/pkg/mentix/utils/network/network.go @@ -63,10 +63,9 @@ func GenerateURL(host string, path string, params URLParams) (*url.URL, error) { return fullURL, nil } -// ReadEndpoint reads data from an HTTP endpoint. -func ReadEndpoint(endpointURL *url.URL, auth *BasicAuth, checkStatus bool) ([]byte, error) { +func queryEndpoint(method string, endpointURL *url.URL, auth *BasicAuth, checkStatus bool) ([]byte, error) { // Prepare the request - req, err := http.NewRequest("GET", endpointURL.String(), nil) + req, err := http.NewRequest(method, endpointURL.String(), nil) if err != nil { return nil, fmt.Errorf("unable to create HTTP request: %v", err) } @@ -89,6 +88,16 @@ func ReadEndpoint(endpointURL *url.URL, auth *BasicAuth, checkStatus bool) ([]by return body, nil } +// ReadEndpoint reads data from an HTTP endpoint via GET. +func ReadEndpoint(endpointURL *url.URL, auth *BasicAuth, checkStatus bool) ([]byte, error) { + return queryEndpoint(http.MethodGet, endpointURL, auth, checkStatus) +} + +// WriteEndpoint sends data to an HTTP endpoint via POST. +func WriteEndpoint(endpointURL *url.URL, auth *BasicAuth, checkStatus bool) ([]byte, error) { + return queryEndpoint(http.MethodPost, endpointURL, auth, checkStatus) +} + // CreateResponse creates a generic HTTP response in JSON format. func CreateResponse(msg string, params ResponseParams) []byte { if params == nil { From 32fccafac100f7df0f140a2e824208f84242884e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20M=C3=BCller?= Date: Mon, 29 Mar 2021 14:27:02 +0200 Subject: [PATCH 07/21] Fix site unregister endpoint --- internal/http/services/siteacc/siteacc.go | 2 ++ internal/http/services/siteacc/sitereg/sitereg.go | 9 +++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/internal/http/services/siteacc/siteacc.go b/internal/http/services/siteacc/siteacc.go index e3f5765d39..e36eee8af0 100644 --- a/internal/http/services/siteacc/siteacc.go +++ b/internal/http/services/siteacc/siteacc.go @@ -63,6 +63,8 @@ func (s *svc) Prefix() string { // Unprotected returns all endpoints that can be queried without prior authorization. func (s *svc) Unprotected() []string { + return []string{"/"} + // The account creation endpoint is always unprotected endpoints := []string{config.EndpointCreate} diff --git a/internal/http/services/siteacc/sitereg/sitereg.go b/internal/http/services/siteacc/sitereg/sitereg.go index cd7372ba19..b73e051372 100644 --- a/internal/http/services/siteacc/sitereg/sitereg.go +++ b/internal/http/services/siteacc/sitereg/sitereg.go @@ -42,11 +42,12 @@ func UnregisterSite(serviceUrl string, apiKey key.APIKey, siteId key.SiteIdentif if err != nil { return errors.Wrap(err, "unable to parse the site registration service URL") } - fullURL.Query().Add("action", "unregister") - fullURL.Query().Add("apiKey", apiKey) - fullURL.Query().Add("siteId", siteId) - fmt.Println(fullURL.String()) + query := make(url.Values) + query.Set("action", "unregister") + query.Set("apiKey", apiKey) + query.Set("siteId", siteId) + fullURL.RawQuery = query.Encode() _, err = network.WriteEndpoint(fullURL, nil, true) if err != nil { From adbf7ee2ed28d405685a4efa9f98b5e362ae732a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20M=C3=BCller?= Date: Mon, 29 Mar 2021 14:38:31 +0200 Subject: [PATCH 08/21] Remove debug stuff --- internal/http/services/siteacc/manager.go | 4 ++-- internal/http/services/siteacc/siteacc.go | 2 -- internal/http/services/siteacc/sitereg/sitereg.go | 9 ++++----- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/internal/http/services/siteacc/manager.go b/internal/http/services/siteacc/manager.go index 6d7426ec61..23916bd117 100644 --- a/internal/http/services/siteacc/manager.go +++ b/internal/http/services/siteacc/manager.go @@ -309,12 +309,12 @@ func (mngr *Manager) UnregisterAccountSite(accountData *data.Account) error { } salt := key.SaltFromEmail(account.Email) - siteId, err := key.CalculateSiteID(account.Data.APIKey, salt) + siteID, err := key.CalculateSiteID(account.Data.APIKey, salt) if err != nil { return errors.Wrap(err, "unable to get site ID") } - if err := sitereg.UnregisterSite(mngr.conf.SiteRegistration.URL, account.Data.APIKey, siteId, salt); err != nil { + if err := sitereg.UnregisterSite(mngr.conf.SiteRegistration.URL, account.Data.APIKey, siteID, salt); err != nil { return errors.Wrap(err, "error while unregistering the site") } diff --git a/internal/http/services/siteacc/siteacc.go b/internal/http/services/siteacc/siteacc.go index e36eee8af0..e3f5765d39 100644 --- a/internal/http/services/siteacc/siteacc.go +++ b/internal/http/services/siteacc/siteacc.go @@ -63,8 +63,6 @@ func (s *svc) Prefix() string { // Unprotected returns all endpoints that can be queried without prior authorization. func (s *svc) Unprotected() []string { - return []string{"/"} - // The account creation endpoint is always unprotected endpoints := []string{config.EndpointCreate} diff --git a/internal/http/services/siteacc/sitereg/sitereg.go b/internal/http/services/siteacc/sitereg/sitereg.go index b73e051372..685011f18b 100644 --- a/internal/http/services/siteacc/sitereg/sitereg.go +++ b/internal/http/services/siteacc/sitereg/sitereg.go @@ -19,7 +19,6 @@ package sitereg import ( - "fmt" "net/url" "github.com/pkg/errors" @@ -29,8 +28,8 @@ import ( ) // UnregisterSite unregister a site using the given site registration endpoint. -func UnregisterSite(serviceUrl string, apiKey key.APIKey, siteId key.SiteIdentifier, salt string) error { - if len(serviceUrl) == 0 { +func UnregisterSite(serviceURL string, apiKey key.APIKey, siteID key.SiteIdentifier, salt string) error { + if len(serviceURL) == 0 { return errors.Errorf("no site registration service URL provided") } @@ -38,7 +37,7 @@ func UnregisterSite(serviceUrl string, apiKey key.APIKey, siteId key.SiteIdentif return err } - fullURL, err := url.Parse(serviceUrl) + fullURL, err := url.Parse(serviceURL) if err != nil { return errors.Wrap(err, "unable to parse the site registration service URL") } @@ -46,7 +45,7 @@ func UnregisterSite(serviceUrl string, apiKey key.APIKey, siteId key.SiteIdentif query := make(url.Values) query.Set("action", "unregister") query.Set("apiKey", apiKey) - query.Set("siteId", siteId) + query.Set("siteId", siteID) fullURL.RawQuery = query.Encode() _, err = network.WriteEndpoint(fullURL, nil, true) From c6c7b81759833244be9ad46ea2ca0bd9351ae00e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20M=C3=BCller?= Date: Mon, 29 Mar 2021 15:02:19 +0200 Subject: [PATCH 09/21] Add changelog --- changelog/unreleased/siteacc-reg-panel.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 changelog/unreleased/siteacc-reg-panel.md diff --git a/changelog/unreleased/siteacc-reg-panel.md b/changelog/unreleased/siteacc-reg-panel.md new file mode 100644 index 0000000000..1f89dd683e --- /dev/null +++ b/changelog/unreleased/siteacc-reg-panel.md @@ -0,0 +1,5 @@ +Enhancement: Add site account registration panel + +This PR adds a site account registration panel to the site accounts service. It also removes site registration from the xcloud metrics driver. + +https://github.com/cs3org/reva/pull/1595 From 13b2820f0941b39053c4c790fb4a924c1f201ff4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20M=C3=BCller?= Date: Thu, 8 Apr 2021 11:36:05 +0200 Subject: [PATCH 10/21] Make local file connector more error tolerant --- pkg/mentix/connectors/localfile.go | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/pkg/mentix/connectors/localfile.go b/pkg/mentix/connectors/localfile.go index 6b83aab568..ed521353fc 100755 --- a/pkg/mentix/connectors/localfile.go +++ b/pkg/mentix/connectors/localfile.go @@ -64,22 +64,21 @@ func (connector *LocalFileConnector) Activate(conf *config.Configuration, log *z // RetrieveMeshData fetches new mesh data. func (connector *LocalFileConnector) RetrieveMeshData() (*meshdata.MeshData, error) { - meshData := &meshdata.MeshData{} - jsonData, err := ioutil.ReadFile(connector.filePath) if err != nil { - return nil, fmt.Errorf("unable to read file '%v': %v", connector.filePath, err) + connector.log.Warn().Err(err).Msgf("unable to read file '%v'", connector.filePath) + return &meshdata.MeshData{}, nil } - if err := json.Unmarshal(jsonData, &meshData.Sites); err != nil { - return nil, fmt.Errorf("invalid file '%v': %v", connector.filePath, err) + meshData := &meshdata.MeshData{} + if err := json.Unmarshal(jsonData, &meshData.Sites); err == nil { + // Enforce site types + connector.setSiteTypes(meshData) + meshData.InferMissingData() + } else { + connector.log.Warn().Err(err).Msgf("invalid file '%v'", connector.filePath) } - // Enforce site types - connector.setSiteTypes(meshData) - - meshData.InferMissingData() - return meshData, nil } From 463f4b62e693afede34174f39f1a3adbd758b0a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20M=C3=BCller?= Date: Thu, 8 Apr 2021 11:40:34 +0200 Subject: [PATCH 11/21] Add changelog --- changelog/unreleased/fix-mentix-local-file.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 changelog/unreleased/fix-mentix-local-file.md diff --git a/changelog/unreleased/fix-mentix-local-file.md b/changelog/unreleased/fix-mentix-local-file.md new file mode 100644 index 0000000000..1c37953958 --- /dev/null +++ b/changelog/unreleased/fix-mentix-local-file.md @@ -0,0 +1,5 @@ +Bugfix: Make local file connector more error tolerant + +The local file connector caused Reva to throw an exception if the local file for storing site data couldn't be loaded. This PR changes this behavior so that only a warning is logged. + +https://github.com/cs3org/reva/pull/1625 From cd9369a1d63b9bc7715a81e953d79b65073bb34b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20M=C3=BCller?= Date: Mon, 12 Apr 2021 13:55:26 +0200 Subject: [PATCH 12/21] Site authorization status fixes --- changelog/1.6.0_2021-02-16/mentix-auth-fix.md | 5 +++++ pkg/mentix/connectors/localfile.go | 15 +++++++-------- pkg/mentix/exchangers/exchanger.go | 2 +- pkg/mentix/mentix.go | 10 +++++----- pkg/mentix/meshdata/site.go | 10 ++++++---- 5 files changed, 24 insertions(+), 18 deletions(-) create mode 100644 changelog/1.6.0_2021-02-16/mentix-auth-fix.md diff --git a/changelog/1.6.0_2021-02-16/mentix-auth-fix.md b/changelog/1.6.0_2021-02-16/mentix-auth-fix.md new file mode 100644 index 0000000000..d482278794 --- /dev/null +++ b/changelog/1.6.0_2021-02-16/mentix-auth-fix.md @@ -0,0 +1,5 @@ +Bugfix: Site authorization status changes + +If a site changes its authorization status, Mentix did not update its internal data to reflect this change. This PR fixes this issue. + +https://github.com/cs3org/reva/pull/1634 diff --git a/pkg/mentix/connectors/localfile.go b/pkg/mentix/connectors/localfile.go index ed521353fc..8ed918ec31 100755 --- a/pkg/mentix/connectors/localfile.go +++ b/pkg/mentix/connectors/localfile.go @@ -25,6 +25,7 @@ import ( "os" "path/filepath" + "github.com/pkg/errors" "github.com/rs/zerolog" "github.com/cs3org/reva/pkg/mentix/config" @@ -66,19 +67,17 @@ func (connector *LocalFileConnector) Activate(conf *config.Configuration, log *z func (connector *LocalFileConnector) RetrieveMeshData() (*meshdata.MeshData, error) { jsonData, err := ioutil.ReadFile(connector.filePath) if err != nil { - connector.log.Warn().Err(err).Msgf("unable to read file '%v'", connector.filePath) - return &meshdata.MeshData{}, nil + return nil, errors.Wrapf(err, "unable to read file '%v'", connector.filePath) } meshData := &meshdata.MeshData{} - if err := json.Unmarshal(jsonData, &meshData.Sites); err == nil { - // Enforce site types - connector.setSiteTypes(meshData) - meshData.InferMissingData() - } else { - connector.log.Warn().Err(err).Msgf("invalid file '%v'", connector.filePath) + if err := json.Unmarshal(jsonData, &meshData.Sites); err != nil { + return nil, errors.Wrapf(err, "invalid file '%v'", connector.filePath) } + connector.setSiteTypes(meshData) + meshData.InferMissingData() + return meshData, nil } diff --git a/pkg/mentix/exchangers/exchanger.go b/pkg/mentix/exchangers/exchanger.go index 908b2ba27d..341442b979 100644 --- a/pkg/mentix/exchangers/exchanger.go +++ b/pkg/mentix/exchangers/exchanger.go @@ -134,7 +134,7 @@ func (exchanger *BaseExchanger) cloneMeshData(clean bool) *meshdata.MeshData { cleanedSites := make([]*meshdata.Site, 0, len(meshDataClone.Sites)) for _, site := range meshDataClone.Sites { // Only keep authorized sites - if site.IsAuthorized() { + if site.IsAuthorized { cleanedSites = append(cleanedSites, site) } } diff --git a/pkg/mentix/mentix.go b/pkg/mentix/mentix.go index cb499ae870..856d857af2 100644 --- a/pkg/mentix/mentix.go +++ b/pkg/mentix/mentix.go @@ -208,8 +208,7 @@ func (mntx *Mentix) tick(updateTimestamp *time.Time) { if meshDataUpdated || time.Since(*updateTimestamp) >= mntx.updateInterval { // Retrieve and update the mesh data; if the importers modified any data, these changes will // be reflected automatically here - meshDataSet, err := mntx.retrieveMeshDataSet() - if err == nil { + if meshDataSet, err := mntx.retrieveMeshDataSet(); err == nil { if err := mntx.applyMeshDataSet(meshDataSet); err != nil { mntx.log.Err(err).Msg("failed to apply mesh data") } @@ -244,10 +243,11 @@ func (mntx *Mentix) retrieveMeshDataSet() (meshdata.Map, error) { for _, connector := range mntx.connectors.Connectors { meshData, err := connector.RetrieveMeshData() - if err != nil { - return nil, fmt.Errorf("retrieving mesh data from connector '%v' failed: %v", connector.GetName(), err) + if err == nil { + meshDataSet[connector.GetID()] = meshData + } else { + mntx.log.Err(err).Msgf("retrieving mesh data from connector '%v' failed", connector.GetName()) } - meshDataSet[connector.GetID()] = meshData } return meshDataSet, nil diff --git a/pkg/mentix/meshdata/site.go b/pkg/mentix/meshdata/site.go index e48ab6c363..a6fdae8f42 100644 --- a/pkg/mentix/meshdata/site.go +++ b/pkg/mentix/meshdata/site.go @@ -39,7 +39,9 @@ type SiteType int // Site represents a single site managed by Mentix. type Site struct { - Type SiteType `json:"-"` + // Internal settings + Type SiteType `json:"-"` + IsAuthorized bool `json:"-"` ID string Name string @@ -116,6 +118,8 @@ func (site *Site) Verify() error { // InferMissingData infers missing data from other data where possible. func (site *Site) InferMissingData() { // Infer missing data + site.IsAuthorized = site.getAuthorizationStatus() + if site.Homepage == "" { site.Homepage = fmt.Sprintf("http://www.%v", site.Domain) } else if site.Domain == "" { @@ -130,9 +134,7 @@ func (site *Site) InferMissingData() { } } -// IsAuthorized checks whether the site is authorized. ScienceMesh are always authorized, while for community sites, -// the accounts service is queried. -func (site *Site) IsAuthorized() bool { +func (site *Site) getAuthorizationStatus() bool { // ScienceMesh sites are always authorized if site.Type == SiteTypeScienceMesh { return true From e1fad2b427f99976edc7f11a264c307cfb8e6c07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20M=C3=BCller?= Date: Mon, 12 Apr 2021 14:06:18 +0200 Subject: [PATCH 13/21] Add changelog --- changelog/unreleased/fix-mentix-auth-status.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 changelog/unreleased/fix-mentix-auth-status.md diff --git a/changelog/unreleased/fix-mentix-auth-status.md b/changelog/unreleased/fix-mentix-auth-status.md new file mode 100644 index 0000000000..84918adda3 --- /dev/null +++ b/changelog/unreleased/fix-mentix-auth-status.md @@ -0,0 +1,5 @@ +Bugfix: Mentix site authorization status changes + +If a site changes its authorization status, Mentix did not update its internal data to reflect this change. This PR fixes this issue. + +https://github.com/cs3org/reva/pull/1634 From 4a1d5ac549f2f3cffd576d808c9080532d5ecec4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20M=C3=BCller?= Date: Tue, 13 Apr 2021 14:54:01 +0200 Subject: [PATCH 14/21] Gather downtimes from GOCDB --- pkg/mentix/config/config.go | 2 + pkg/mentix/connectors/gocdb.go | 36 ++++++++ pkg/mentix/connectors/gocdb/types.go | 24 ++++++ pkg/mentix/meshdata/downtime.go | 124 +++++++++++++++++++++++++++ pkg/mentix/meshdata/site.go | 2 + 5 files changed, 188 insertions(+) create mode 100644 pkg/mentix/meshdata/downtime.go diff --git a/pkg/mentix/config/config.go b/pkg/mentix/config/config.go index 75d44780b2..70c9ccc166 100644 --- a/pkg/mentix/config/config.go +++ b/pkg/mentix/config/config.go @@ -35,6 +35,8 @@ type Configuration struct { UpdateInterval string `mapstructure:"update_interval"` + CriticalServiceTypes []string `mapstructure:"critical_services"` + Importers struct { SiteRegistration struct { Endpoint string `mapstructure:"endpoint"` diff --git a/pkg/mentix/connectors/gocdb.go b/pkg/mentix/connectors/gocdb.go index ca24e85e71..a9d6db6eff 100755 --- a/pkg/mentix/connectors/gocdb.go +++ b/pkg/mentix/connectors/gocdb.go @@ -24,6 +24,7 @@ import ( "net/url" "path" "strings" + "time" "github.com/rs/zerolog" @@ -73,6 +74,11 @@ func (connector *GOCDBConnector) RetrieveMeshData() (*meshdata.MeshData, error) if err := connector.queryServices(meshData, site); err != nil { return nil, fmt.Errorf("could not query services of site '%v': %v", site.Name, err) } + + // Get downtimes scheduled for the current site + if err := connector.queryDowntimes(meshData, site); err != nil { + return nil, fmt.Errorf("could not query downtimes of site '%v': %v", site.Name, err) + } } meshData.InferMissingData() @@ -152,6 +158,7 @@ func (connector *GOCDBConnector) querySites(meshData *meshdata.MeshData) error { Latitude: site.Latitude, Services: nil, Properties: properties, + Downtimes: meshdata.Downtimes{}, } meshData.Sites = append(meshData.Sites, meshsite) } @@ -216,6 +223,35 @@ func (connector *GOCDBConnector) queryServices(meshData *meshdata.MeshData, site return nil } +func (connector *GOCDBConnector) queryDowntimes(meshData *meshdata.MeshData, site *meshdata.Site) error { + var downtimes gocdb.Downtimes + if err := connector.query(&downtimes, "get_downtime_nested_services", false, true, network.URLParams{"topentity": site.Name, "ongoing_only": "yes"}); err != nil { + return err + } + + // Copy retrieved data into the mesh data + site.Downtimes.Clear() + for _, dt := range downtimes.Downtimes { + if !strings.EqualFold(dt.Severity, "outage") { // Only take real outages into account + continue + } + + services := make([]string, 0, len(dt.AffectedServices.Services)) + for _, service := range dt.AffectedServices.Services { + // Only add critical services to the list of affected services + for _, svcType := range connector.conf.CriticalServiceTypes { + if strings.EqualFold(svcType, service.Type) { + services = append(services, service.Type) + } + } + } + + _, _ = site.Downtimes.ScheduleDowntime(time.Unix(dt.StartDate, 0), time.Unix(dt.EndDate, 0), services) + } + + return nil +} + func (connector *GOCDBConnector) findServiceType(meshData *meshdata.MeshData, name string) *meshdata.ServiceType { for _, serviceType := range meshData.ServiceTypes { if strings.EqualFold(serviceType.Name, name) { diff --git a/pkg/mentix/connectors/gocdb/types.go b/pkg/mentix/connectors/gocdb/types.go index 8e5da543ae..d00d98254a 100755 --- a/pkg/mentix/connectors/gocdb/types.go +++ b/pkg/mentix/connectors/gocdb/types.go @@ -88,3 +88,27 @@ type Service struct { type Services struct { Services []*Service `xml:"SERVICE_ENDPOINT"` } + +// DowntimeService represents a service scheduled for downtime. +type DowntimeService struct { + Type string `xml:"SERVICE_TYPE"` +} + +// DowntimeServices represents a list of DowntimeService objects. +type DowntimeServices struct { + Services []*DowntimeService `xml:"SERVICE"` +} + +// Downtime is a scheduled downtime for a site. +type Downtime struct { + Severity string `xml:"SEVERITY"` + StartDate int64 `xml:"START_DATE"` + EndDate int64 `xml:"END_DATE"` + + AffectedServices DowntimeServices `xml:"SERVICES"` +} + +// Downtimes represents a list of Downtime objects. +type Downtimes struct { + Downtimes []*Downtime `xml:"DOWNTIME"` +} diff --git a/pkg/mentix/meshdata/downtime.go b/pkg/mentix/meshdata/downtime.go new file mode 100644 index 0000000000..ae7051a682 --- /dev/null +++ b/pkg/mentix/meshdata/downtime.go @@ -0,0 +1,124 @@ +// Copyright 2018-2021 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 meshdata + +import ( + "time" + + "github.com/pkg/errors" +) + +// Downtimes represents all scheduled downtimes of a site. +type Downtimes struct { + Downtimes []*Downtime +} + +// ScheduleDowntime schedules a new downtime. +func (dts *Downtimes) ScheduleDowntime(start time.Time, end time.Time, affectedServices []string) (*Downtime, error) { + // Create a new downtime and verify it + dt := &Downtime{ + StartDate: start, + EndDate: end, + AffectedServices: affectedServices, + } + dt.InferMissingData() + if err := dt.Verify(); err != nil { + return nil, err + } + + // Only schedule the downtime if it hasn't expired yet + if dt.IsExpired() { + return nil, nil + } + + dts.Downtimes = append(dts.Downtimes, dt) + return dt, nil +} + +// Clear clears all downtimes. +func (dts *Downtimes) Clear() { + dts.Downtimes = make([]*Downtime, 0, 10) +} + +// IsAnyActive returns true if any downtime is currently active. +func (dts *Downtimes) IsAnyActive() bool { + for _, dt := range dts.Downtimes { + if dt.IsActive() { + return true + } + } + return false +} + +// InferMissingData infers missing data from other data where possible. +func (dts *Downtimes) InferMissingData() { + for _, dt := range dts.Downtimes { + dt.InferMissingData() + } +} + +// Verify checks if the downtimes data is valid. +func (dts *Downtimes) Verify() error { + for _, dt := range dts.Downtimes { + if err := dt.Verify(); err != nil { + return err + } + } + + return nil +} + +// Downtime represents a single scheduled downtime. +type Downtime struct { + StartDate time.Time + EndDate time.Time + AffectedServices []string +} + +// IsActive returns true if the downtime is currently active. +func (dt *Downtime) IsActive() bool { + now := time.Now() + return dt.StartDate.Before(now) && dt.EndDate.After(now) +} + +// IsPending returns true if the downtime is yet to come. +func (dt *Downtime) IsPending() bool { + return dt.StartDate.After(time.Now()) +} + +// IsExpired returns true of the downtime has expired (i.e., lies in the past). +func (dt *Downtime) IsExpired() bool { + return dt.EndDate.Before(time.Now()) +} + +// InferMissingData infers missing data from other data where possible. +func (dt *Downtime) InferMissingData() { +} + +// Verify checks if the downtime data is valid. +func (dt *Downtime) Verify() error { + if dt.EndDate.Before(dt.StartDate) { + return errors.Errorf("downtime end is before its start") + } + if len(dt.AffectedServices) == 0 { + return errors.Errorf("no services affected by downtime") + } + + return nil +} diff --git a/pkg/mentix/meshdata/site.go b/pkg/mentix/meshdata/site.go index a6fdae8f42..a6866a15d1 100644 --- a/pkg/mentix/meshdata/site.go +++ b/pkg/mentix/meshdata/site.go @@ -59,6 +59,8 @@ type Site struct { Services []*Service Properties map[string]string + + Downtimes Downtimes `json:"-"` } // AddService adds a new service; if a service with the same name already exists, the existing one is overwritten. From 776fbdb1605ef756bad0241c0e8baf7b8cd435ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20M=C3=BCller?= Date: Wed, 14 Apr 2021 11:46:15 +0200 Subject: [PATCH 15/21] Remove wrong changelog --- changelog/1.6.0_2021-02-16/mentix-auth-fix.md | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 changelog/1.6.0_2021-02-16/mentix-auth-fix.md diff --git a/changelog/1.6.0_2021-02-16/mentix-auth-fix.md b/changelog/1.6.0_2021-02-16/mentix-auth-fix.md deleted file mode 100644 index d482278794..0000000000 --- a/changelog/1.6.0_2021-02-16/mentix-auth-fix.md +++ /dev/null @@ -1,5 +0,0 @@ -Bugfix: Site authorization status changes - -If a site changes its authorization status, Mentix did not update its internal data to reflect this change. This PR fixes this issue. - -https://github.com/cs3org/reva/pull/1634 From ed84136d152a237a90fb33d2bfedd243debbbca2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20M=C3=BCller?= Date: Wed, 14 Apr 2021 13:10:10 +0200 Subject: [PATCH 16/21] Add metrics exporter --- internal/http/services/mentix/mentix.go | 1 + pkg/mentix/config/config.go | 4 + pkg/mentix/config/ids.go | 4 +- pkg/mentix/exchangers/exporters/metrics.go | 84 +++++++++++++ .../exchangers/exporters/metrics/metrics.go | 117 ++++++++++++++++++ 5 files changed, 209 insertions(+), 1 deletion(-) create mode 100644 pkg/mentix/exchangers/exporters/metrics.go create mode 100644 pkg/mentix/exchangers/exporters/metrics/metrics.go diff --git a/internal/http/services/mentix/mentix.go b/internal/http/services/mentix/mentix.go index 2d82bdb88e..c3c5acc92e 100644 --- a/internal/http/services/mentix/mentix.go +++ b/internal/http/services/mentix/mentix.go @@ -164,6 +164,7 @@ func applyDefaultConfig(conf *config.Configuration) { addDefaultConnector(&conf.Exporters.SiteLocations.EnabledConnectors) addDefaultConnector(&conf.Exporters.PrometheusSD.EnabledConnectors) + addDefaultConnector(&conf.Exporters.Metrics.EnabledConnectors) } // New returns a new Mentix service. diff --git a/pkg/mentix/config/config.go b/pkg/mentix/config/config.go index 70c9ccc166..fdf89d6772 100644 --- a/pkg/mentix/config/config.go +++ b/pkg/mentix/config/config.go @@ -70,6 +70,10 @@ type Configuration struct { BlackboxOutputFile string `mapstructure:"blackbox_output_file"` EnabledConnectors []string `mapstructure:"enabled_connectors"` } `mapstructure:"promsd"` + + Metrics struct { + EnabledConnectors []string `mapstructure:"enabled_connectors"` + } `mapstructure:"metrics"` } `mapstructure:"exporters"` AccountsService struct { diff --git a/pkg/mentix/config/ids.go b/pkg/mentix/config/ids.go index 74ccedbe3c..348b22b45e 100644 --- a/pkg/mentix/config/ids.go +++ b/pkg/mentix/config/ids.go @@ -37,6 +37,8 @@ const ( ExporterIDCS3API = "cs3api" // ExporterIDSiteLocations is the identifier for the Site Locations exporter. ExporterIDSiteLocations = "siteloc" - // ExporterIDPrometheusSD is the identifier for the PrometheusSD SD exporter. + // ExporterIDPrometheusSD is the identifier for the PrometheusSD exporter. ExporterIDPrometheusSD = "promsd" + // ExporterIDMetrics is the identifier for the Metrics exporter. + ExporterIDMetrics = "metrics" ) diff --git a/pkg/mentix/exchangers/exporters/metrics.go b/pkg/mentix/exchangers/exporters/metrics.go new file mode 100644 index 0000000000..21635b0b6f --- /dev/null +++ b/pkg/mentix/exchangers/exporters/metrics.go @@ -0,0 +1,84 @@ +// 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/cs3org/reva/pkg/mentix/config" + "github.com/cs3org/reva/pkg/mentix/exchangers/exporters/metrics" + "github.com/cs3org/reva/pkg/mentix/meshdata" + "github.com/pkg/errors" + "github.com/rs/zerolog" +) + +// MetricsExporter exposes various Prometheus metrics. +type MetricsExporter struct { + BaseExporter + + metrics *metrics.Metrics +} + +// Activate activates the exporter. +func (exporter *MetricsExporter) Activate(conf *config.Configuration, log *zerolog.Logger) error { + if err := exporter.BaseExporter.Activate(conf, log); err != nil { + return err + } + + // Create the metrics handler + m, err := metrics.New(conf, log) + if err != nil { + return errors.Wrap(err, "unable to create metrics") + } + exporter.metrics = m + + // Store Metrics specifics + exporter.SetEnabledConnectors(conf.Exporters.Metrics.EnabledConnectors) + + return nil +} + +// Update is called whenever the mesh data set has changed to reflect these changes. +func (exporter *MetricsExporter) Update(meshDataSet meshdata.Map) error { + if err := exporter.BaseExporter.Update(meshDataSet); err != nil { + return err + } + + // Data is read, so acquire a read lock + exporter.Locker().RLock() + defer exporter.Locker().RUnlock() + + if err := exporter.metrics.Update(exporter.MeshData()); err != nil { + return errors.Wrap(err, "error while updating the metrics") + } + + return nil +} + +// GetID returns the ID of the exporter. +func (exporter *MetricsExporter) GetID() string { + return config.ExporterIDMetrics +} + +// GetName returns the display name of the exporter. +func (exporter *MetricsExporter) GetName() string { + return "Metrics" +} + +func init() { + registerExporter(&MetricsExporter{}) +} diff --git a/pkg/mentix/exchangers/exporters/metrics/metrics.go b/pkg/mentix/exchangers/exporters/metrics/metrics.go new file mode 100644 index 0000000000..91345c4981 --- /dev/null +++ b/pkg/mentix/exchangers/exporters/metrics/metrics.go @@ -0,0 +1,117 @@ +// 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 metrics + +import ( + "context" + + "github.com/cs3org/reva/pkg/mentix/config" + "github.com/cs3org/reva/pkg/mentix/meshdata" + "github.com/pkg/errors" + "github.com/rs/zerolog" + "go.opencensus.io/stats" + "go.opencensus.io/stats/view" + "go.opencensus.io/tag" +) + +// Metrics exposes various Mentix related metrics via Prometheus. +type Metrics struct { + conf *config.Configuration + log *zerolog.Logger + + isScheduledStats *stats.Int64Measure +} + +const ( + keySite = "site" +) + +func (m *Metrics) initialize(conf *config.Configuration, log *zerolog.Logger) error { + if conf == nil { + return errors.Errorf("no configuration provided") + } + m.conf = conf + + if log == nil { + return errors.Errorf("no logger provided") + } + m.log = log + + if err := m.registerMetrics(); err != nil { + return errors.Wrap(err, "error while registering metrics") + } + + return nil +} + +func (m *Metrics) registerMetrics() error { + // Create the OpenCensus statistics and a corresponding view + m.isScheduledStats = stats.Int64("site_is_scheduled", "A boolean metric which shows whether the given site is currently scheduled or not", stats.UnitDimensionless) + isScheduledView := &view.View{ + Name: m.isScheduledStats.Name(), + Description: m.isScheduledStats.Description(), + Measure: m.isScheduledStats, + TagKeys: []tag.Key{tag.MustNewKey(keySite)}, + Aggregation: view.LastValue(), + } + + if err := view.Register(isScheduledView); err != nil { + return errors.Wrap(err, "unable to register the site schedule status metrics view") + } + + return nil +} + +// Update is used to update/expose all metrics. +func (m *Metrics) Update(meshData *meshdata.MeshData) error { + for _, site := range meshData.Sites { + if err := m.exportSiteMetrics(site); err != nil { + return errors.Wrapf(err, "error while exporting metrics for site '%v'", site.Name) + } + } + + return nil +} + +func (m *Metrics) exportSiteMetrics(site *meshdata.Site) error { + mutators := make([]tag.Mutator, 0) + mutators = append(mutators, tag.Insert(tag.MustNewKey(keySite), site.Name)) + + // Create a new context to serve the metrics + if ctx, err := tag.New(context.Background(), mutators...); err == nil { + isScheduled := int64(1) + if site.Downtimes.IsAnyActive() { + isScheduled = 0 + } + stats.Record(ctx, m.isScheduledStats.M(isScheduled)) + } else { + return errors.Wrap(err, "unable to create a context for the site schedule status metrics") + } + + return nil +} + +// New creates a new Metrics instance. +func New(conf *config.Configuration, log *zerolog.Logger) (*Metrics, error) { + m := &Metrics{} + if err := m.initialize(conf, log); err != nil { + return nil, errors.Wrap(err, "unable to create new metrics object") + } + return m, nil +} From 29a6f4c0ed124384731b0c91f1c463477239bdfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20M=C3=BCller?= Date: Wed, 14 Apr 2021 13:13:34 +0200 Subject: [PATCH 17/21] Update docs and example --- .../config/http/services/mentix/_index.md | 3 +++ .../http/services/mentix/metrics/_index.md | 19 +++++++++++++++++++ examples/mentix/mentix.toml | 2 ++ 3 files changed, 24 insertions(+) create mode 100644 docs/content/en/docs/config/http/services/mentix/metrics/_index.md diff --git a/docs/content/en/docs/config/http/services/mentix/_index.md b/docs/content/en/docs/config/http/services/mentix/_index.md index 62a614e6e3..65c1e0bd1f 100644 --- a/docs/content/en/docs/config/http/services/mentix/_index.md +++ b/docs/content/en/docs/config/http/services/mentix/_index.md @@ -63,6 +63,9 @@ Mentix exposes its data via an HTTP endpoint using the `webapi` exporter. Data c - files: - '/usr/share/prom/sciencemesh_services.json' ``` + +- **metrics** +The [Metrics](metrics) exporter exposes various site-specific metrics through Prometheus. ## Site Accounts service Mentix uses the Reva site accounts service to query information about site accounts. The following settings must be configured properly: diff --git a/docs/content/en/docs/config/http/services/mentix/metrics/_index.md b/docs/content/en/docs/config/http/services/mentix/metrics/_index.md new file mode 100644 index 0000000000..5df157a285 --- /dev/null +++ b/docs/content/en/docs/config/http/services/mentix/metrics/_index.md @@ -0,0 +1,19 @@ +--- +title: "metrics" +linkTitle: "metrics" +weight: 10 +description: > + Configuration for the Metrics exporter of the Mentix service +--- + +{{% pageinfo %}} +The Metrics exporter exposes site-specific metrics through Prometheus. +{{% /pageinfo %}} + +{{% dir name="enabled_connectors" type="[]string" default="*" %}} +A list of all enabled connectors for the exporter. +{{< highlight toml >}} +[http.services.mentix.exporters.metrics] +enabled_connectors = ["gocdb"] +{{< /highlight >}} +{{% /dir %}} diff --git a/examples/mentix/mentix.toml b/examples/mentix/mentix.toml index 8cdaa11d38..6a841b488d 100644 --- a/examples/mentix/mentix.toml +++ b/examples/mentix/mentix.toml @@ -20,6 +20,8 @@ endpoint = "/" [http.services.mentix.exporters.siteloc] # If this setting is omitted, all connectors will be used as data sources enabled_connectors = ["gocdb"] +# Enable the Metrics exporter +[http.services.mentix.exporters.metrics] # Enable the site registration importer [http.services.mentix.importers.sitereg] From c411aab43564c212b7240c79d1394d5bcd88e3a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20M=C3=BCller?= Date: Thu, 15 Apr 2021 14:24:39 +0200 Subject: [PATCH 18/21] Refactoring --- .../en/docs/config/http/services/mentix/_index.md | 9 +++++++++ examples/mentix/mentix.toml | 4 ++++ pkg/mentix/config/config.go | 4 +++- pkg/mentix/connectors/gocdb.go | 2 +- 4 files changed, 17 insertions(+), 2 deletions(-) diff --git a/docs/content/en/docs/config/http/services/mentix/_index.md b/docs/content/en/docs/config/http/services/mentix/_index.md index 65c1e0bd1f..dd73b4f51a 100644 --- a/docs/content/en/docs/config/http/services/mentix/_index.md +++ b/docs/content/en/docs/config/http/services/mentix/_index.md @@ -27,6 +27,15 @@ update_interval = "15m" {{< /highlight >}} {{% /dir %}} +## Services +{{% dir name="critical_types" type="[]string" default="[]" %}} +The service types that are considered as critical/essential. +{{< highlight toml >}} +[http.services.mentix.services] +critical_types = ["REVAD] +{{< /highlight >}} +{{% /dir %}} + ## Connectors Mentix is decoupled from the actual sources of the mesh data by using so-called _connectors_. A connector is used to gather the data from a certain source, which are then converted into Mentix' own internal format. diff --git a/examples/mentix/mentix.toml b/examples/mentix/mentix.toml index 6a841b488d..6e3e149ea6 100644 --- a/examples/mentix/mentix.toml +++ b/examples/mentix/mentix.toml @@ -11,6 +11,10 @@ address = "http://sciencemesh-test.uni-muenster.de" [http.services.mentix.connectors.localfile] file = "/usr/share/revad/sites.json" +# Configure the service types that are considered as critical/essential +[http.services.mentix.services] +critical_types = ["REVAD"] + # Enable the WebAPI exporter [http.services.mentix.exporters.webapi] endpoint = "/" diff --git a/pkg/mentix/config/config.go b/pkg/mentix/config/config.go index fdf89d6772..6e90c8d8db 100644 --- a/pkg/mentix/config/config.go +++ b/pkg/mentix/config/config.go @@ -35,7 +35,9 @@ type Configuration struct { UpdateInterval string `mapstructure:"update_interval"` - CriticalServiceTypes []string `mapstructure:"critical_services"` + Services struct { + CriticalTypes []string `mapstructure:"critical_types"` + } `mapstructure:"services"` Importers struct { SiteRegistration struct { diff --git a/pkg/mentix/connectors/gocdb.go b/pkg/mentix/connectors/gocdb.go index a9d6db6eff..81fc73479b 100755 --- a/pkg/mentix/connectors/gocdb.go +++ b/pkg/mentix/connectors/gocdb.go @@ -239,7 +239,7 @@ func (connector *GOCDBConnector) queryDowntimes(meshData *meshdata.MeshData, sit services := make([]string, 0, len(dt.AffectedServices.Services)) for _, service := range dt.AffectedServices.Services { // Only add critical services to the list of affected services - for _, svcType := range connector.conf.CriticalServiceTypes { + for _, svcType := range connector.conf.Services.CriticalTypes { if strings.EqualFold(svcType, service.Type) { services = append(services, service.Type) } From 382bcced5b82127fa6b50f6cadfa1f9d45b6859c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20M=C3=BCller?= Date: Mon, 19 Apr 2021 13:09:08 +0200 Subject: [PATCH 19/21] Add further labels to site scheduled metric --- pkg/mentix/exchangers/exporters/metrics/metrics.go | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/pkg/mentix/exchangers/exporters/metrics/metrics.go b/pkg/mentix/exchangers/exporters/metrics/metrics.go index 91345c4981..4208cfd342 100644 --- a/pkg/mentix/exchangers/exporters/metrics/metrics.go +++ b/pkg/mentix/exchangers/exporters/metrics/metrics.go @@ -39,7 +39,9 @@ type Metrics struct { } const ( - keySite = "site" + keySiteID = "site_id" + keySiteName = "site" + keySiteType = "site_type" ) func (m *Metrics) initialize(conf *config.Configuration, log *zerolog.Logger) error { @@ -67,7 +69,7 @@ func (m *Metrics) registerMetrics() error { Name: m.isScheduledStats.Name(), Description: m.isScheduledStats.Description(), Measure: m.isScheduledStats, - TagKeys: []tag.Key{tag.MustNewKey(keySite)}, + TagKeys: []tag.Key{tag.MustNewKey(keySiteID), tag.MustNewKey(keySiteName), tag.MustNewKey(keySiteType)}, Aggregation: view.LastValue(), } @@ -91,7 +93,9 @@ func (m *Metrics) Update(meshData *meshdata.MeshData) error { func (m *Metrics) exportSiteMetrics(site *meshdata.Site) error { mutators := make([]tag.Mutator, 0) - mutators = append(mutators, tag.Insert(tag.MustNewKey(keySite), site.Name)) + mutators = append(mutators, tag.Insert(tag.MustNewKey(keySiteID), site.ID)) + mutators = append(mutators, tag.Insert(tag.MustNewKey(keySiteName), site.Name)) + mutators = append(mutators, tag.Insert(tag.MustNewKey(keySiteType), meshdata.GetSiteTypeName(site.Type))) // Create a new context to serve the metrics if ctx, err := tag.New(context.Background(), mutators...); err == nil { From d3cab83ae22260ea442f889da221e1d425528bff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20M=C3=BCller?= Date: Mon, 19 Apr 2021 13:12:21 +0200 Subject: [PATCH 20/21] Add further labels to site scheduled metric --- pkg/mentix/exchangers/exporters/metrics/metrics.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/pkg/mentix/exchangers/exporters/metrics/metrics.go b/pkg/mentix/exchangers/exporters/metrics/metrics.go index 4208cfd342..812435b7da 100644 --- a/pkg/mentix/exchangers/exporters/metrics/metrics.go +++ b/pkg/mentix/exchangers/exporters/metrics/metrics.go @@ -39,9 +39,10 @@ type Metrics struct { } const ( - keySiteID = "site_id" - keySiteName = "site" - keySiteType = "site_type" + keySiteID = "site_id" + keySiteName = "site" + keySiteType = "site_type" + keyServiceType = "service_type" ) func (m *Metrics) initialize(conf *config.Configuration, log *zerolog.Logger) error { @@ -69,7 +70,7 @@ func (m *Metrics) registerMetrics() error { Name: m.isScheduledStats.Name(), Description: m.isScheduledStats.Description(), Measure: m.isScheduledStats, - TagKeys: []tag.Key{tag.MustNewKey(keySiteID), tag.MustNewKey(keySiteName), tag.MustNewKey(keySiteType)}, + TagKeys: []tag.Key{tag.MustNewKey(keySiteID), tag.MustNewKey(keySiteName), tag.MustNewKey(keySiteType), tag.MustNewKey(keyServiceType)}, Aggregation: view.LastValue(), } @@ -96,6 +97,7 @@ func (m *Metrics) exportSiteMetrics(site *meshdata.Site) error { mutators = append(mutators, tag.Insert(tag.MustNewKey(keySiteID), site.ID)) mutators = append(mutators, tag.Insert(tag.MustNewKey(keySiteName), site.Name)) mutators = append(mutators, tag.Insert(tag.MustNewKey(keySiteType), meshdata.GetSiteTypeName(site.Type))) + mutators = append(mutators, tag.Insert(tag.MustNewKey(keyServiceType), "SCIENCEMESH_HCHECK")) // Create a new context to serve the metrics if ctx, err := tag.New(context.Background(), mutators...); err == nil { From 5993610f58150f32aa883b60ae897d0f7dec2b54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20M=C3=BCller?= Date: Tue, 20 Apr 2021 14:41:01 +0200 Subject: [PATCH 21/21] Add changelog --- changelog/unreleased/mentix-add-reliab.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 changelog/unreleased/mentix-add-reliab.md diff --git a/changelog/unreleased/mentix-add-reliab.md b/changelog/unreleased/mentix-add-reliab.md new file mode 100644 index 0000000000..c57faae135 --- /dev/null +++ b/changelog/unreleased/mentix-add-reliab.md @@ -0,0 +1,5 @@ +Enhancement: Add reliability calculations support to Mentix + +To make reliability calculations possible, a new exporter has been added to Mentix that reads scheduled downtimes from the GOCDB and exposes it through Prometheus metrics. + +https://github.com/cs3org/reva/pull/1649