Skip to content

Commit 071c837

Browse files
authored
[Feature] Exporter - network mesh check (#823)
1 parent 1f7d272 commit 071c837

File tree

10 files changed

+259
-11
lines changed

10 files changed

+259
-11
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -391,7 +391,7 @@ init: tools update-generated $(BIN) vendor
391391
.PHONY: tools
392392
tools: update-vendor
393393
@echo ">> Fetching golangci-lint linter"
394-
@GOBIN=$(GOPATH)/bin go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.40.0
394+
@go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.42.1
395395
@echo ">> Fetching goimports"
396396
@GOBIN=$(GOPATH)/bin go get golang.org/x/tools/cmd/goimports@0bb7e5c47b1a31f85d4f173edc878a8e049764a5
397397
@echo ">> Fetching license check"

exporter.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,15 @@
2323
package main
2424

2525
import (
26+
"context"
2627
"io/ioutil"
2728
"os"
2829
"os/signal"
2930
"syscall"
3031
"time"
3132

3233
"github.com/arangodb/kube-arangodb/pkg/exporter"
34+
"github.com/arangodb/kube-arangodb/pkg/util"
3335

3436
"github.com/rs/zerolog/log"
3537
"github.com/spf13/cobra"
@@ -100,6 +102,21 @@ func cmdExporterCheckE() error {
100102
return err
101103
}
102104

105+
mon := exporter.NewMonitor(exporterInput.endpoint, func() (string, error) {
106+
if exporterInput.jwtFile == "" {
107+
return "", nil
108+
}
109+
110+
data, err := ioutil.ReadFile(exporterInput.jwtFile)
111+
if err != nil {
112+
return "", err
113+
}
114+
115+
return string(data), nil
116+
}, false, 15*time.Second)
117+
118+
go mon.UpdateMonitorStatus(util.CreateSignalContext(context.Background()))
119+
103120
exporter := exporter.NewExporter(exporterInput.listenAddress, "/metrics", p)
104121
if exporterInput.keyfile != "" {
105122
if e, err := exporter.WithKeyfile(exporterInput.keyfile); err != nil {

pkg/apis/deployment/v1/license_spec.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ func (s LicenseSpec) Validate() error {
5252
}
5353

5454
// SetDefaultsFrom fills all values not set in s with values from other
55-
func (s LicenseSpec) SetDefaultsFrom(other LicenseSpec) {
55+
func (s *LicenseSpec) SetDefaultsFrom(other LicenseSpec) {
5656
if !s.HasSecretName() {
5757
s.SecretName = util.NewStringOrNil(other.SecretName)
5858
}

pkg/apis/deployment/v2alpha1/license_spec.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ func (s LicenseSpec) Validate() error {
5252
}
5353

5454
// SetDefaultsFrom fills all values not set in s with values from other
55-
func (s LicenseSpec) SetDefaultsFrom(other LicenseSpec) {
55+
func (s *LicenseSpec) SetDefaultsFrom(other LicenseSpec) {
5656
if !s.HasSecretName() {
5757
s.SecretName = util.NewStringOrNil(other.SecretName)
5858
}

pkg/deployment/resources/secrets.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -431,9 +431,11 @@ func AppendKeyfileToKeyfolder(ctx context.Context, cachedStatus inspectorInterfa
431431

432432
var (
433433
exporterTokenClaims = jg.MapClaims{
434-
"iss": "arangodb",
435-
"server_id": "exporter",
436-
"allowed_paths": []interface{}{"/_admin/statistics", "/_admin/statistics-description", k8sutil.ArangoExporterInternalEndpoint, k8sutil.ArangoExporterInternalEndpointV2},
434+
"iss": "arangodb",
435+
"server_id": "exporter",
436+
"allowed_paths": []interface{}{"/_admin/statistics", "/_admin/statistics-description",
437+
k8sutil.ArangoExporterInternalEndpoint, k8sutil.ArangoExporterInternalEndpointV2,
438+
k8sutil.ArangoExporterStatusEndpoint, k8sutil.ArangoExporterClusterHealthEndpoint},
437439
}
438440
)
439441

pkg/exporter/monitor.go

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
//
2+
// DISCLAIMER
3+
//
4+
// Copyright 2016-2021 ArangoDB GmbH, Cologne, Germany
5+
//
6+
// Licensed under the Apache License, Version 2.0 (the "License");
7+
// you may not use this file except in compliance with the License.
8+
// You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing, software
13+
// distributed under the License is distributed on an "AS IS" BASIS,
14+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
// See the License for the specific language governing permissions and
16+
// limitations under the License.
17+
//
18+
// Copyright holder is ArangoDB GmbH, Cologne, Germany
19+
//
20+
// Author Jakub Wierzbowski
21+
//
22+
23+
package exporter
24+
25+
import (
26+
"context"
27+
"encoding/json"
28+
"errors"
29+
"fmt"
30+
"io/ioutil"
31+
"net/url"
32+
"os"
33+
"path"
34+
"strings"
35+
"sync/atomic"
36+
"time"
37+
38+
"github.com/arangodb/go-driver"
39+
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil"
40+
41+
"github.com/rs/zerolog/log"
42+
)
43+
44+
const (
45+
monitorMetricTemplate = "arangodb_member_health{role=\"%s\",id=\"%s\"} %d \n"
46+
successRefreshInterval = time.Second * 120
47+
failRefreshInterval = time.Second * 15
48+
)
49+
50+
var currentMembersStatus atomic.Value
51+
52+
func NewMonitor(arangodbEndpoint string, auth Authentication, sslVerify bool, timeout time.Duration) *monitor {
53+
uri, err := setPath(arangodbEndpoint, k8sutil.ArangoExporterClusterHealthEndpoint)
54+
if err != nil {
55+
log.Error().Err(err).Msgf("Fatal")
56+
os.Exit(1)
57+
}
58+
59+
return &monitor{
60+
factory: newHttpClientFactory(arangodbEndpoint, auth, sslVerify, timeout),
61+
healthURI: uri,
62+
}
63+
}
64+
65+
type monitor struct {
66+
factory httpClientFactory
67+
healthURI *url.URL
68+
}
69+
70+
// UpdateMonitorStatus load monitor metrics for current cluster into currentMembersStatus
71+
func (m monitor) UpdateMonitorStatus(ctx context.Context) {
72+
for {
73+
sleep := successRefreshInterval
74+
75+
health, err := m.GetClusterHealth()
76+
if err != nil {
77+
log.Error().Err(err).Msg("GetClusterHealth error")
78+
sleep = failRefreshInterval
79+
} else {
80+
var output strings.Builder
81+
for key, value := range health.Health {
82+
entry, err := m.GetMemberStatus(key, value)
83+
if err != nil {
84+
log.Error().Err(err).Msg("GetMemberStatus error")
85+
sleep = failRefreshInterval
86+
}
87+
output.WriteString(entry)
88+
}
89+
currentMembersStatus.Store(output.String())
90+
}
91+
92+
select {
93+
case <-ctx.Done():
94+
return
95+
case <-time.After(sleep):
96+
continue
97+
}
98+
}
99+
}
100+
101+
// GetClusterHealth returns current ArangoDeployment cluster health status
102+
func (m monitor) GetClusterHealth() (*driver.ClusterHealth, error) {
103+
c, req, err := m.factory()
104+
if err != nil {
105+
return nil, err
106+
}
107+
req.URL = m.healthURI
108+
resp, err := c.Do(req)
109+
if err != nil {
110+
return nil, err
111+
}
112+
113+
defer resp.Body.Close()
114+
body, err := ioutil.ReadAll(resp.Body)
115+
if err != nil {
116+
return nil, err
117+
}
118+
119+
var result driver.ClusterHealth
120+
if err := json.Unmarshal(body, &result); err != nil {
121+
return nil, err
122+
}
123+
124+
return &result, err
125+
}
126+
127+
// GetMemberStatus returns Prometheus monitor metric for specific member
128+
func (m monitor) GetMemberStatus(id driver.ServerID, member driver.ServerHealth) (string, error) {
129+
result := fmt.Sprintf(monitorMetricTemplate, member.Role, id, 0)
130+
131+
c, req, err := m.factory()
132+
if err != nil {
133+
return result, err
134+
}
135+
136+
req.URL, err = setPath(member.Endpoint, k8sutil.ArangoExporterStatusEndpoint)
137+
if err != nil {
138+
return result, err
139+
}
140+
141+
resp, err := c.Do(req)
142+
if err != nil {
143+
return result, err
144+
}
145+
146+
if resp.StatusCode != 200 {
147+
defer resp.Body.Close()
148+
body, err := ioutil.ReadAll(resp.Body)
149+
if err != nil {
150+
return result, err
151+
}
152+
return result, errors.New(string(body))
153+
}
154+
return fmt.Sprintf(monitorMetricTemplate, member.Role, id, 1), nil
155+
}
156+
157+
func setPath(uri, uriPath string) (*url.URL, error) {
158+
u, err := url.Parse(uri)
159+
if err != nil {
160+
return u, err
161+
}
162+
u.Path = path.Join(uriPath)
163+
u.Scheme = "https"
164+
return u, nil
165+
}

pkg/exporter/passthru.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,12 @@ func (p passthru) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
123123
// Fix Header response
124124
responseStr = strings.ReplaceAll(responseStr, "guage", "gauge")
125125

126+
// Attach monitor data
127+
monitorData := currentMembersStatus.Load()
128+
if monitorData != nil {
129+
responseStr = responseStr + monitorData.(string)
130+
}
131+
126132
resp.WriteHeader(data.StatusCode)
127133
_, err = resp.Write([]byte(responseStr))
128134
if err != nil {

pkg/storage/provisioner/service/provisioner.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,10 +84,10 @@ func (p *Provisioner) GetInfo(ctx context.Context, localPath string) (provisione
8484
}
8585

8686
// Available is blocks available * fragment size
87-
available := int64(statfs.Bavail) * statfs.Bsize
87+
available := int64(statfs.Bavail) * statfs.Bsize // nolint:typecheck
8888

8989
// Capacity is total block count * fragment size
90-
capacity := int64(statfs.Blocks) * statfs.Bsize
90+
capacity := int64(statfs.Blocks) * statfs.Bsize // nolint:typecheck
9191

9292
log.Debug().
9393
Str("node-name", p.NodeName).

pkg/util/k8sutil/constants.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,11 @@ const (
2929
ArangoSyncWorkerPort = 8729
3030
ArangoExporterPort = 9101
3131

32-
ArangoExporterInternalEndpoint = "/_admin/metrics"
33-
ArangoExporterInternalEndpointV2 = "/_admin/metrics/v2"
34-
ArangoExporterDefaultEndpoint = "/metrics"
32+
ArangoExporterStatusEndpoint = "/_api/version"
33+
ArangoExporterClusterHealthEndpoint = "/_admin/cluster/health"
34+
ArangoExporterInternalEndpoint = "/_admin/metrics"
35+
ArangoExporterInternalEndpointV2 = "/_admin/metrics/v2"
36+
ArangoExporterDefaultEndpoint = "/metrics"
3537

3638
// K8s constants
3739
ClusterIPNone = "None"

pkg/util/signal.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
//
2+
// DISCLAIMER
3+
//
4+
// Copyright 2016-2021 ArangoDB GmbH, Cologne, Germany
5+
//
6+
// Licensed under the Apache License, Version 2.0 (the "License");
7+
// you may not use this file except in compliance with the License.
8+
// You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing, software
13+
// distributed under the License is distributed on an "AS IS" BASIS,
14+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
// See the License for the specific language governing permissions and
16+
// limitations under the License.
17+
//
18+
// Copyright holder is ArangoDB GmbH, Cologne, Germany
19+
//
20+
// Author Jakub Wierzbowski
21+
//
22+
23+
package util
24+
25+
import (
26+
"context"
27+
"os"
28+
"os/signal"
29+
"syscall"
30+
)
31+
32+
// CreateSignalContext creates and returns the context which is closed when one of the provided signal occurs.
33+
// If the provided list of signals is empty, then SIGINT and SIGTERM is used by default.
34+
func CreateSignalContext(ctx context.Context, signals ...os.Signal) context.Context {
35+
if ctx == nil {
36+
ctx = context.Background()
37+
}
38+
ctxSignal, cancelSignal := context.WithCancel(ctx)
39+
sigChannel := make(chan os.Signal, 2)
40+
41+
if len(signals) > 0 {
42+
signal.Notify(sigChannel, signals...)
43+
} else {
44+
signal.Notify(sigChannel, os.Interrupt, syscall.SIGTERM)
45+
}
46+
47+
go func() {
48+
// Wait until signal occurs.
49+
<-sigChannel
50+
close(sigChannel)
51+
// Close the context which is used by the caller.
52+
cancelSignal()
53+
}()
54+
55+
return ctxSignal
56+
}

0 commit comments

Comments
 (0)