summaryrefslogtreecommitdiffstats
path: root/src/go/plugin/go.d/modules/couchbase
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-08-26 08:15:24 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-08-26 08:15:35 +0000
commitf09848204fa5283d21ea43e262ee41aa578e1808 (patch)
treec62385d7adf209fa6a798635954d887f718fb3fb /src/go/plugin/go.d/modules/couchbase
parentReleasing debian version 1.46.3-2. (diff)
downloadnetdata-f09848204fa5283d21ea43e262ee41aa578e1808.tar.xz
netdata-f09848204fa5283d21ea43e262ee41aa578e1808.zip
Merging upstream version 1.47.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/go/plugin/go.d/modules/couchbase')
l---------src/go/plugin/go.d/modules/couchbase/README.md1
-rw-r--r--src/go/plugin/go.d/modules/couchbase/charts.go84
-rw-r--r--src/go/plugin/go.d/modules/couchbase/collect.go154
-rw-r--r--src/go/plugin/go.d/modules/couchbase/config_schema.json183
-rw-r--r--src/go/plugin/go.d/modules/couchbase/couchbase.go122
-rw-r--r--src/go/plugin/go.d/modules/couchbase/couchbase_test.go240
-rw-r--r--src/go/plugin/go.d/modules/couchbase/init.go39
-rw-r--r--src/go/plugin/go.d/modules/couchbase/integrations/couchbase.md247
-rw-r--r--src/go/plugin/go.d/modules/couchbase/metadata.yaml214
-rw-r--r--src/go/plugin/go.d/modules/couchbase/metrics.go33
-rw-r--r--src/go/plugin/go.d/modules/couchbase/testdata/6.6.0/buckets_basic_stats.json422
-rw-r--r--src/go/plugin/go.d/modules/couchbase/testdata/config.json20
-rw-r--r--src/go/plugin/go.d/modules/couchbase/testdata/config.yaml17
13 files changed, 1776 insertions, 0 deletions
diff --git a/src/go/plugin/go.d/modules/couchbase/README.md b/src/go/plugin/go.d/modules/couchbase/README.md
new file mode 120000
index 00000000..fa8d05e1
--- /dev/null
+++ b/src/go/plugin/go.d/modules/couchbase/README.md
@@ -0,0 +1 @@
+integrations/couchbase.md \ No newline at end of file
diff --git a/src/go/plugin/go.d/modules/couchbase/charts.go b/src/go/plugin/go.d/modules/couchbase/charts.go
new file mode 100644
index 00000000..277b814a
--- /dev/null
+++ b/src/go/plugin/go.d/modules/couchbase/charts.go
@@ -0,0 +1,84 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package couchbase
+
+import (
+ "github.com/netdata/netdata/go/plugins/plugin/go.d/agent/module"
+)
+
+type (
+ Charts = module.Charts
+ Chart = module.Chart
+ Dim = module.Dim
+)
+
+var bucketQuotaPercentUsedChart = Chart{
+ ID: "bucket_quota_percent_used",
+ Title: "Quota Percent Used Per Bucket",
+ Units: "%",
+ Fam: "buckets basic stats",
+ Ctx: "couchbase.bucket_quota_percent_used",
+}
+
+var bucketOpsPerSecChart = Chart{
+ ID: "bucket_ops_per_sec",
+ Title: "Operations Per Second Per Bucket",
+ Units: "ops/s",
+ Fam: "buckets basic stats",
+ Ctx: "couchbase.bucket_ops_per_sec",
+ Type: module.Stacked,
+}
+
+var bucketDiskFetchesChart = Chart{
+ ID: "bucket_disk_fetches",
+ Title: "Disk Fetches Per Bucket",
+ Units: "fetches",
+ Fam: "buckets basic stats",
+ Ctx: "couchbase.bucket_disk_fetches",
+ Type: module.Stacked,
+}
+
+var bucketItemCountChart = Chart{
+ ID: "bucket_item_count",
+ Title: "Item Count Per Bucket",
+ Units: "items",
+ Fam: "buckets basic stats",
+ Ctx: "couchbase.bucket_item_count",
+ Type: module.Stacked,
+}
+
+var bucketDiskUsedChart = Chart{
+ ID: "bucket_disk_used_stats",
+ Title: "Disk Used Per Bucket",
+ Units: "bytes",
+ Fam: "buckets basic stats",
+ Ctx: "couchbase.bucket_disk_used_stats",
+ Type: module.Stacked,
+}
+
+var bucketDataUsedChart = Chart{
+ ID: "bucket_data_used",
+ Title: "Data Used Per Bucket",
+ Units: "bytes",
+ Fam: "buckets basic stats",
+ Ctx: "couchbase.bucket_data_used",
+ Type: module.Stacked,
+}
+
+var bucketMemUsedChart = Chart{
+ ID: "bucket_mem_used",
+ Title: "Memory Used Per Bucket",
+ Units: "bytes",
+ Fam: "buckets basic stats",
+ Ctx: "couchbase.bucket_mem_used",
+ Type: module.Stacked,
+}
+
+var bucketVBActiveNumNonResidentChart = Chart{
+ ID: "bucket_vb_active_num_non_resident_stats",
+ Title: "Number Of Non-Resident Items Per Bucket",
+ Units: "items",
+ Fam: "buckets basic stats",
+ Ctx: "couchbase.bucket_vb_active_num_non_resident",
+ Type: module.Stacked,
+}
diff --git a/src/go/plugin/go.d/modules/couchbase/collect.go b/src/go/plugin/go.d/modules/couchbase/collect.go
new file mode 100644
index 00000000..6027ac91
--- /dev/null
+++ b/src/go/plugin/go.d/modules/couchbase/collect.go
@@ -0,0 +1,154 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package couchbase
+
+import (
+ "encoding/json"
+ "fmt"
+ "io"
+ "net/http"
+ "net/url"
+
+ "github.com/netdata/netdata/go/plugins/plugin/go.d/agent/module"
+ "github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/web"
+)
+
+const (
+ urlPathBucketsStats = "/pools/default/buckets"
+
+ precision = 1000
+)
+
+func (cb *Couchbase) collect() (map[string]int64, error) {
+ ms, err := cb.scrapeCouchbase()
+ if err != nil {
+ return nil, fmt.Errorf("error on scraping couchbase: %v", err)
+ }
+ if ms.empty() {
+ return nil, nil
+ }
+
+ collected := make(map[string]int64)
+ cb.collectBasicStats(collected, ms)
+
+ return collected, nil
+}
+
+func (cb *Couchbase) collectBasicStats(collected map[string]int64, ms *cbMetrics) {
+ for _, b := range ms.BucketsBasicStats {
+
+ if !cb.collectedBuckets[b.Name] {
+ cb.collectedBuckets[b.Name] = true
+ cb.addBucketToCharts(b.Name)
+ }
+
+ bs := b.BasicStats
+ collected[indexDimID(b.Name, "quota_percent_used")] = int64(bs.QuotaPercentUsed * precision)
+ collected[indexDimID(b.Name, "ops_per_sec")] = int64(bs.OpsPerSec * precision)
+ collected[indexDimID(b.Name, "disk_fetches")] = int64(bs.DiskFetches)
+ collected[indexDimID(b.Name, "item_count")] = int64(bs.ItemCount)
+ collected[indexDimID(b.Name, "disk_used")] = int64(bs.DiskUsed)
+ collected[indexDimID(b.Name, "data_used")] = int64(bs.DataUsed)
+ collected[indexDimID(b.Name, "mem_used")] = int64(bs.MemUsed)
+ collected[indexDimID(b.Name, "vb_active_num_non_resident")] = int64(bs.VbActiveNumNonResident)
+ }
+}
+
+func (cb *Couchbase) addBucketToCharts(bucket string) {
+ cb.addDimToChart(bucketQuotaPercentUsedChart.ID, &module.Dim{
+ ID: indexDimID(bucket, "quota_percent_used"),
+ Name: bucket,
+ Div: precision,
+ })
+
+ cb.addDimToChart(bucketOpsPerSecChart.ID, &module.Dim{
+ ID: indexDimID(bucket, "ops_per_sec"),
+ Name: bucket,
+ Div: precision,
+ })
+
+ cb.addDimToChart(bucketDiskFetchesChart.ID, &module.Dim{
+ ID: indexDimID(bucket, "disk_fetches"),
+ Name: bucket,
+ })
+
+ cb.addDimToChart(bucketItemCountChart.ID, &module.Dim{
+ ID: indexDimID(bucket, "item_count"),
+ Name: bucket,
+ })
+
+ cb.addDimToChart(bucketDiskUsedChart.ID, &module.Dim{
+ ID: indexDimID(bucket, "disk_used"),
+ Name: bucket,
+ })
+
+ cb.addDimToChart(bucketDataUsedChart.ID, &module.Dim{
+ ID: indexDimID(bucket, "data_used"),
+ Name: bucket,
+ })
+
+ cb.addDimToChart(bucketMemUsedChart.ID, &module.Dim{
+ ID: indexDimID(bucket, "mem_used"),
+ Name: bucket,
+ })
+
+ cb.addDimToChart(bucketVBActiveNumNonResidentChart.ID, &module.Dim{
+ ID: indexDimID(bucket, "vb_active_num_non_resident"),
+ Name: bucket,
+ })
+}
+
+func (cb *Couchbase) addDimToChart(chartID string, dim *module.Dim) {
+ chart := cb.Charts().Get(chartID)
+ if chart == nil {
+ cb.Warningf("error on adding '%s' dimension: can not find '%s' chart", dim.ID, chartID)
+ return
+ }
+ if err := chart.AddDim(dim); err != nil {
+ cb.Warning(err)
+ return
+ }
+ chart.MarkNotCreated()
+}
+
+func (cb *Couchbase) scrapeCouchbase() (*cbMetrics, error) {
+ req, err := web.NewHTTPRequestWithPath(cb.Request, urlPathBucketsStats)
+ if err != nil {
+ return nil, err
+ }
+ req.URL.RawQuery = url.Values{"skipMap": []string{"true"}}.Encode()
+
+ ms := &cbMetrics{}
+ if err := cb.doOKDecode(req, &ms.BucketsBasicStats); err != nil {
+ return nil, err
+ }
+ return ms, nil
+}
+
+func (cb *Couchbase) doOKDecode(req *http.Request, in interface{}) error {
+ resp, err := cb.httpClient.Do(req)
+ if err != nil {
+ return fmt.Errorf("error on HTTP request '%s': %v", req.URL, err)
+ }
+ defer closeBody(resp)
+
+ if resp.StatusCode != http.StatusOK {
+ return fmt.Errorf("'%s' returned HTTP status code: %d", req.URL, resp.StatusCode)
+ }
+
+ if err := json.NewDecoder(resp.Body).Decode(in); err != nil {
+ return fmt.Errorf("error on decoding response from '%s': %v", req.URL, err)
+ }
+ return nil
+}
+
+func closeBody(resp *http.Response) {
+ if resp != nil && resp.Body != nil {
+ _, _ = io.Copy(io.Discard, resp.Body)
+ _ = resp.Body.Close()
+ }
+}
+
+func indexDimID(name, metric string) string {
+ return fmt.Sprintf("bucket_%s_%s", name, metric)
+}
diff --git a/src/go/plugin/go.d/modules/couchbase/config_schema.json b/src/go/plugin/go.d/modules/couchbase/config_schema.json
new file mode 100644
index 00000000..6ef455a9
--- /dev/null
+++ b/src/go/plugin/go.d/modules/couchbase/config_schema.json
@@ -0,0 +1,183 @@
+{
+ "jsonSchema": {
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "title": "Couchbase collector configuration.",
+ "type": "object",
+ "properties": {
+ "update_every": {
+ "title": "Update every",
+ "description": "Data collection interval, measured in seconds.",
+ "type": "integer",
+ "minimum": 1,
+ "default": 5
+ },
+ "url": {
+ "title": "URL",
+ "description": "The URL of the Couchbase REST API.",
+ "type": "string",
+ "default": "http://127.0.0.1:8091",
+ "format": "uri"
+ },
+ "timeout": {
+ "title": "Timeout",
+ "description": "The timeout in seconds for the HTTP request.",
+ "type": "number",
+ "minimum": 0.5,
+ "default": 1
+ },
+ "not_follow_redirects": {
+ "title": "Not follow redirects",
+ "description": "If set, the client will not follow HTTP redirects automatically.",
+ "type": "boolean"
+ },
+ "username": {
+ "title": "Username",
+ "description": "The username for basic authentication.",
+ "type": "string",
+ "sensitive": true
+ },
+ "password": {
+ "title": "Password",
+ "description": "The password for basic authentication.",
+ "type": "string",
+ "sensitive": true
+ },
+ "proxy_url": {
+ "title": "Proxy URL",
+ "description": "The URL of the proxy server.",
+ "type": "string"
+ },
+ "proxy_username": {
+ "title": "Proxy username",
+ "description": "The username for proxy authentication.",
+ "type": "string",
+ "sensitive": true
+ },
+ "proxy_password": {
+ "title": "Proxy password",
+ "description": "The password for proxy authentication.",
+ "type": "string",
+ "sensitive": true
+ },
+ "headers": {
+ "title": "Headers",
+ "description": "Additional HTTP headers to include in the request.",
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": {
+ "type": "string"
+ }
+ },
+ "tls_skip_verify": {
+ "title": "Skip TLS verification",
+ "description": "If set, TLS certificate verification will be skipped.",
+ "type": "boolean"
+ },
+ "tls_ca": {
+ "title": "TLS CA",
+ "description": "The path to the CA certificate file for TLS verification.",
+ "type": "string",
+ "pattern": "^$|^/"
+ },
+ "tls_cert": {
+ "title": "TLS certificate",
+ "description": "The path to the client certificate file for TLS authentication.",
+ "type": "string",
+ "pattern": "^$|^/"
+ },
+ "tls_key": {
+ "title": "TLS key",
+ "description": "The path to the client key file for TLS authentication.",
+ "type": "string",
+ "pattern": "^$|^/"
+ },
+ "body": {
+ "title": "Body",
+ "type": "string"
+ },
+ "method": {
+ "title": "Method",
+ "type": "string"
+ }
+ },
+ "required": [
+ "url"
+ ],
+ "additionalProperties": false,
+ "patternProperties": {
+ "^name$": {}
+ }
+ },
+ "uiSchema": {
+ "ui:flavour": "tabs",
+ "ui:options": {
+ "tabs": [
+ {
+ "title": "Base",
+ "fields": [
+ "update_every",
+ "url",
+ "timeout",
+ "not_follow_redirects"
+ ]
+ },
+ {
+ "title": "Auth",
+ "fields": [
+ "username",
+ "password"
+ ]
+ },
+ {
+ "title": "TLS",
+ "fields": [
+ "tls_skip_verify",
+ "tls_ca",
+ "tls_cert",
+ "tls_key"
+ ]
+ },
+ {
+ "title": "Proxy",
+ "fields": [
+ "proxy_url",
+ "proxy_username",
+ "proxy_password"
+ ]
+ },
+ {
+ "title": "Headers",
+ "fields": [
+ "headers"
+ ]
+ }
+ ]
+ },
+ "uiOptions": {
+ "fullPage": true
+ },
+ "body": {
+ "ui:widget": "hidden"
+ },
+ "method": {
+ "ui:widget": "hidden"
+ },
+ "timeout": {
+ "ui:help": "Accepts decimals for precise control (e.g., type 1.5 for 1.5 seconds)."
+ },
+ "username": {
+ "ui:widget": "password"
+ },
+ "proxy_username": {
+ "ui:widget": "password"
+ },
+ "password": {
+ "ui:widget": "password"
+ },
+ "proxy_password": {
+ "ui:widget": "password"
+ }
+ }
+}
diff --git a/src/go/plugin/go.d/modules/couchbase/couchbase.go b/src/go/plugin/go.d/modules/couchbase/couchbase.go
new file mode 100644
index 00000000..8ef880c2
--- /dev/null
+++ b/src/go/plugin/go.d/modules/couchbase/couchbase.go
@@ -0,0 +1,122 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package couchbase
+
+import (
+ _ "embed"
+ "errors"
+ "net/http"
+ "time"
+
+ "github.com/netdata/netdata/go/plugins/plugin/go.d/agent/module"
+ "github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/web"
+)
+
+//go:embed "config_schema.json"
+var configSchema string
+
+func init() {
+ module.Register("couchbase", module.Creator{
+ JobConfigSchema: configSchema,
+ Defaults: module.Defaults{
+ UpdateEvery: 5,
+ },
+ Create: func() module.Module { return New() },
+ Config: func() any { return &Config{} },
+ })
+}
+
+func New() *Couchbase {
+ return &Couchbase{
+ Config: Config{
+ HTTP: web.HTTP{
+ Request: web.Request{
+ URL: "http://127.0.0.1:8091",
+ },
+ Client: web.Client{
+ Timeout: web.Duration(time.Second),
+ },
+ },
+ },
+ collectedBuckets: make(map[string]bool),
+ }
+}
+
+type Config struct {
+ UpdateEvery int `yaml:"update_every,omitempty" json:"update_every"`
+ web.HTTP `yaml:",inline" json:""`
+}
+
+type Couchbase struct {
+ module.Base
+ Config `yaml:",inline" json:""`
+
+ httpClient *http.Client
+ charts *module.Charts
+
+ collectedBuckets map[string]bool
+}
+
+func (cb *Couchbase) Configuration() any {
+ return cb.Config
+}
+
+func (cb *Couchbase) Init() error {
+ err := cb.validateConfig()
+ if err != nil {
+ cb.Errorf("check configuration: %v", err)
+ return err
+ }
+
+ httpClient, err := cb.initHTTPClient()
+ if err != nil {
+ cb.Errorf("init HTTP client: %v", err)
+ return err
+ }
+ cb.httpClient = httpClient
+
+ charts, err := cb.initCharts()
+ if err != nil {
+ cb.Errorf("init charts: %v", err)
+ return err
+ }
+ cb.charts = charts
+
+ return nil
+}
+
+func (cb *Couchbase) Check() error {
+ mx, err := cb.collect()
+ if err != nil {
+ cb.Error(err)
+ return err
+ }
+ if len(mx) == 0 {
+ return errors.New("no metrics collected")
+
+ }
+ return nil
+}
+
+func (cb *Couchbase) Charts() *Charts {
+ return cb.charts
+}
+
+func (cb *Couchbase) Collect() map[string]int64 {
+ mx, err := cb.collect()
+ if err != nil {
+ cb.Error(err)
+ }
+
+ if len(mx) == 0 {
+ return nil
+ }
+ return mx
+}
+
+func (cb *Couchbase) Cleanup() {
+ if cb.httpClient == nil {
+ return
+ }
+ cb.httpClient.CloseIdleConnections()
+}
diff --git a/src/go/plugin/go.d/modules/couchbase/couchbase_test.go b/src/go/plugin/go.d/modules/couchbase/couchbase_test.go
new file mode 100644
index 00000000..b28c8e8f
--- /dev/null
+++ b/src/go/plugin/go.d/modules/couchbase/couchbase_test.go
@@ -0,0 +1,240 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package couchbase
+
+import (
+ "net/http"
+ "net/http/httptest"
+ "os"
+ "testing"
+
+ "github.com/netdata/netdata/go/plugins/plugin/go.d/agent/module"
+ "github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/web"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+var (
+ dataConfigJSON, _ = os.ReadFile("testdata/config.json")
+ dataConfigYAML, _ = os.ReadFile("testdata/config.yaml")
+
+ dataVer660BucketsBasicStats, _ = os.ReadFile("testdata/6.6.0/buckets_basic_stats.json")
+)
+
+func Test_testDataIsValid(t *testing.T) {
+ for name, data := range map[string][]byte{
+ "dataConfigJSON": dataConfigJSON,
+ "dataConfigYAML": dataConfigYAML,
+ "dataVer660BucketsBasicStats": dataVer660BucketsBasicStats,
+ } {
+ require.NotNil(t, data, name)
+ }
+}
+
+func TestCouchbase_ConfigurationSerialize(t *testing.T) {
+ module.TestConfigurationSerialize(t, &Couchbase{}, dataConfigJSON, dataConfigYAML)
+}
+
+func TestCouchbase_Init(t *testing.T) {
+ tests := map[string]struct {
+ config Config
+ wantFail bool
+ }{
+ "success on default config": {
+ config: New().Config,
+ },
+ "fails on unset 'URL'": {
+ wantFail: true,
+ config: Config{
+ HTTP: web.HTTP{
+ Request: web.Request{
+ URL: "",
+ },
+ },
+ },
+ },
+ "fails on invalid URL": {
+ wantFail: true,
+ config: Config{
+ HTTP: web.HTTP{
+ Request: web.Request{
+ URL: "127.0.0.1:9090",
+ },
+ },
+ },
+ },
+ }
+
+ for name, test := range tests {
+ t.Run(name, func(t *testing.T) {
+ cb := New()
+ cb.Config = test.config
+
+ if test.wantFail {
+ assert.Error(t, cb.Init())
+ } else {
+ assert.NoError(t, cb.Init())
+ }
+ })
+ }
+}
+
+func TestCouchbase_Check(t *testing.T) {
+ tests := map[string]struct {
+ prepare func(*testing.T) (cb *Couchbase, cleanup func())
+ wantFail bool
+ }{
+ "success on valid response v6.6.0": {
+ prepare: prepareCouchbaseV660,
+ },
+ "fails on response with invalid data": {
+ wantFail: true,
+ prepare: prepareCouchbaseInvalidData,
+ },
+ "fails on 404 response": {
+ wantFail: true,
+ prepare: prepareCouchbase404,
+ },
+ "fails on connection refused": {
+ wantFail: true,
+ prepare: prepareCouchbaseConnectionRefused,
+ },
+ }
+
+ for name, test := range tests {
+ t.Run(name, func(t *testing.T) {
+ cb, cleanup := test.prepare(t)
+ defer cleanup()
+
+ if test.wantFail {
+ assert.Error(t, cb.Check())
+ } else {
+ assert.NoError(t, cb.Check())
+ }
+ })
+ }
+}
+
+func TestCouchbase_Collect(t *testing.T) {
+ tests := map[string]struct {
+ prepare func(t *testing.T) (cb *Couchbase, cleanup func())
+ wantCollected map[string]int64
+ }{
+ "success on valid response v6.6.0": {
+ prepare: prepareCouchbaseV660,
+ wantCollected: map[string]int64{
+ "bucket_beer-sample_data_used": 13990431,
+ "bucket_beer-sample_disk_fetches": 1,
+ "bucket_beer-sample_disk_used": 27690472,
+ "bucket_beer-sample_item_count": 7303,
+ "bucket_beer-sample_mem_used": 34294872,
+ "bucket_beer-sample_ops_per_sec": 1100,
+ "bucket_beer-sample_quota_percent_used": 32706,
+ "bucket_beer-sample_vb_active_num_non_resident": 1,
+ "bucket_gamesim-sample_data_used": 5371804,
+ "bucket_gamesim-sample_disk_fetches": 1,
+ "bucket_gamesim-sample_disk_used": 13821793,
+ "bucket_gamesim-sample_item_count": 586,
+ "bucket_gamesim-sample_mem_used": 29586696,
+ "bucket_gamesim-sample_ops_per_sec": 1100,
+ "bucket_gamesim-sample_quota_percent_used": 28216,
+ "bucket_gamesim-sample_vb_active_num_non_resident": 1,
+ "bucket_travel-sample_data_used": 53865472,
+ "bucket_travel-sample_disk_fetches": 1,
+ "bucket_travel-sample_disk_used": 62244260,
+ "bucket_travel-sample_item_count": 31591,
+ "bucket_travel-sample_mem_used": 54318184,
+ "bucket_travel-sample_ops_per_sec": 1100,
+ "bucket_travel-sample_quota_percent_used": 51801,
+ "bucket_travel-sample_vb_active_num_non_resident": 1,
+ },
+ },
+ "fails on response with invalid data": {
+ prepare: prepareCouchbaseInvalidData,
+ },
+ "fails on 404 response": {
+ prepare: prepareCouchbase404,
+ },
+ "fails on connection refused": {
+ prepare: prepareCouchbaseConnectionRefused,
+ },
+ }
+
+ for name, test := range tests {
+ t.Run(name, func(t *testing.T) {
+ cb, cleanup := test.prepare(t)
+ defer cleanup()
+
+ collected := cb.Collect()
+
+ assert.Equal(t, test.wantCollected, collected)
+ ensureCollectedHasAllChartsDimsVarsIDs(t, cb, collected)
+ })
+ }
+}
+
+func prepareCouchbaseV660(t *testing.T) (cb *Couchbase, cleanup func()) {
+ t.Helper()
+ srv := httptest.NewServer(http.HandlerFunc(
+ func(w http.ResponseWriter, r *http.Request) {
+ _, _ = w.Write(dataVer660BucketsBasicStats)
+ }))
+
+ cb = New()
+ cb.URL = srv.URL
+ require.NoError(t, cb.Init())
+
+ return cb, srv.Close
+}
+
+func prepareCouchbaseInvalidData(t *testing.T) (*Couchbase, func()) {
+ t.Helper()
+ srv := httptest.NewServer(http.HandlerFunc(
+ func(w http.ResponseWriter, r *http.Request) {
+ _, _ = w.Write([]byte("hello and\n goodbye"))
+ }))
+ cb := New()
+ cb.URL = srv.URL
+ require.NoError(t, cb.Init())
+
+ return cb, srv.Close
+}
+
+func prepareCouchbase404(t *testing.T) (*Couchbase, func()) {
+ t.Helper()
+ srv := httptest.NewServer(http.HandlerFunc(
+ func(w http.ResponseWriter, r *http.Request) {
+ w.WriteHeader(http.StatusNotFound)
+ }))
+ cb := New()
+ cb.URL = srv.URL
+ require.NoError(t, cb.Init())
+
+ return cb, srv.Close
+}
+
+func prepareCouchbaseConnectionRefused(t *testing.T) (*Couchbase, func()) {
+ t.Helper()
+ cb := New()
+ cb.URL = "http://127.0.0.1:38001"
+ require.NoError(t, cb.Init())
+
+ return cb, func() {}
+}
+
+func ensureCollectedHasAllChartsDimsVarsIDs(t *testing.T, cb *Couchbase, collected map[string]int64) {
+ for _, chart := range *cb.Charts() {
+ if chart.Obsolete {
+ continue
+ }
+ for _, dim := range chart.Dims {
+ _, ok := collected[dim.ID]
+ assert.Truef(t, ok, "chart '%s' dim '%s': no dim in collected", dim.ID, chart.ID)
+ }
+ for _, v := range chart.Vars {
+ _, ok := collected[v.ID]
+ assert.Truef(t, ok, "chart '%s' dim '%s': no dim in collected", v.ID, chart.ID)
+ }
+ }
+}
diff --git a/src/go/plugin/go.d/modules/couchbase/init.go b/src/go/plugin/go.d/modules/couchbase/init.go
new file mode 100644
index 00000000..196e6998
--- /dev/null
+++ b/src/go/plugin/go.d/modules/couchbase/init.go
@@ -0,0 +1,39 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package couchbase
+
+import (
+ "errors"
+ "net/http"
+
+ "github.com/netdata/netdata/go/plugins/plugin/go.d/agent/module"
+ "github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/web"
+)
+
+func (cb *Couchbase) initCharts() (*Charts, error) {
+ var bucketCharts = module.Charts{
+ bucketQuotaPercentUsedChart.Copy(),
+ bucketOpsPerSecChart.Copy(),
+ bucketDiskFetchesChart.Copy(),
+ bucketItemCountChart.Copy(),
+ bucketDiskUsedChart.Copy(),
+ bucketDataUsedChart.Copy(),
+ bucketMemUsedChart.Copy(),
+ bucketVBActiveNumNonResidentChart.Copy(),
+ }
+ return bucketCharts.Copy(), nil
+}
+
+func (cb *Couchbase) initHTTPClient() (*http.Client, error) {
+ return web.NewHTTPClient(cb.Client)
+}
+
+func (cb *Couchbase) validateConfig() error {
+ if cb.URL == "" {
+ return errors.New("URL not set")
+ }
+ if _, err := web.NewHTTPRequest(cb.Request); err != nil {
+ return err
+ }
+ return nil
+}
diff --git a/src/go/plugin/go.d/modules/couchbase/integrations/couchbase.md b/src/go/plugin/go.d/modules/couchbase/integrations/couchbase.md
new file mode 100644
index 00000000..b53dc940
--- /dev/null
+++ b/src/go/plugin/go.d/modules/couchbase/integrations/couchbase.md
@@ -0,0 +1,247 @@
+<!--startmeta
+custom_edit_url: "https://github.com/netdata/netdata/edit/master/src/go/plugin/go.d/modules/couchbase/README.md"
+meta_yaml: "https://github.com/netdata/netdata/edit/master/src/go/plugin/go.d/modules/couchbase/metadata.yaml"
+sidebar_label: "Couchbase"
+learn_status: "Published"
+learn_rel_path: "Collecting Metrics/Databases"
+most_popular: False
+message: "DO NOT EDIT THIS FILE DIRECTLY, IT IS GENERATED BY THE COLLECTOR'S metadata.yaml FILE"
+endmeta-->
+
+# Couchbase
+
+
+<img src="https://netdata.cloud/img/couchbase.svg" width="150"/>
+
+
+Plugin: go.d.plugin
+Module: couchbase
+
+<img src="https://img.shields.io/badge/maintained%20by-Netdata-%2300ab44" />
+
+## Overview
+
+This collector monitors Couchbase servers.
+
+
+
+
+This collector is supported on all platforms.
+
+This collector supports collecting metrics from multiple instances of this integration, including remote instances.
+
+
+### Default Behavior
+
+#### Auto-Detection
+
+This integration doesn't support auto-detection.
+
+#### Limits
+
+The default configuration for this integration does not impose any limits on data collection.
+
+#### Performance Impact
+
+The default configuration for this integration is not expected to impose a significant performance impact on the system.
+
+
+## Metrics
+
+Metrics grouped by *scope*.
+
+The scope defines the instance that the metric belongs to. An instance is uniquely identified by a set of labels.
+
+
+
+### Per Couchbase instance
+
+These metrics refer to the entire monitored application.
+
+This scope has no labels.
+
+Metrics:
+
+| Metric | Dimensions | Unit |
+|:------|:----------|:----|
+| couchbase.bucket_quota_percent_used | a dimension per bucket | percentage |
+| couchbase.bucket_ops_per_sec | a dimension per bucket | ops/s |
+| couchbase.bucket_disk_fetches | a dimension per bucket | fetches |
+| couchbase.bucket_item_count | a dimension per bucket | items |
+| couchbase.bucket_disk_used_stats | a dimension per bucket | bytes |
+| couchbase.bucket_data_used | a dimension per bucket | bytes |
+| couchbase.bucket_mem_used | a dimension per bucket | bytes |
+| couchbase.bucket_vb_active_num_non_resident | a dimension per bucket | items |
+
+
+
+## Alerts
+
+There are no alerts configured by default for this integration.
+
+
+## Setup
+
+### Prerequisites
+
+No action required.
+
+### Configuration
+
+#### File
+
+The configuration file name for this integration is `go.d/couchbase.conf`.
+
+
+You can edit the configuration file using the `edit-config` script from the
+Netdata [config directory](/docs/netdata-agent/configuration/README.md#the-netdata-config-directory).
+
+```bash
+cd /etc/netdata 2>/dev/null || cd /opt/netdata/etc/netdata
+sudo ./edit-config go.d/couchbase.conf
+```
+#### Options
+
+The following options can be defined globally: update_every, autodetection_retry.
+
+
+<details open><summary>All options</summary>
+
+| Name | Description | Default | Required |
+|:----|:-----------|:-------|:--------:|
+| update_every | Data collection frequency. | 1 | no |
+| autodetection_retry | Recheck interval in seconds. Zero means no recheck will be scheduled. | 0 | no |
+| url | Server URL. | http://127.0.0.1:8091 | yes |
+| username | Username for basic HTTP authentication. | | no |
+| password | Password for basic HTTP authentication. | | no |
+| proxy_url | Proxy URL. | | no |
+| proxy_username | Username for proxy basic HTTP authentication. | | no |
+| proxy_password | Password for proxy basic HTTP authentication. | | no |
+| timeout | HTTP request timeout. | 2 | no |
+| method | HTTP request method. | GET | no |
+| body | HTTP request body. | | no |
+| headers | HTTP request headers. | | no |
+| not_follow_redirects | Redirect handling policy. Controls whether the client follows redirects. | no | no |
+| tls_skip_verify | Server certificate chain and hostname validation policy. Controls whether the client performs this check. | no | no |
+| tls_ca | Certification authority that the client uses when verifying the server's certificates. | | no |
+| tls_cert | Client tls certificate. | | no |
+| tls_key | Client tls key. | | no |
+
+</details>
+
+#### Examples
+
+##### Basic
+
+An example configuration.
+
+<details open><summary>Config</summary>
+
+```yaml
+jobs:
+ - name: local
+ url: http://127.0.0.1:8091
+
+```
+</details>
+
+##### Basic HTTP auth
+
+Local server with basic HTTP authentication.
+
+<details open><summary>Config</summary>
+
+```yaml
+jobs:
+ - name: local
+ url: http://127.0.0.1:8091
+ username: foo
+ password: bar
+
+```
+</details>
+
+##### Multi-instance
+
+> **Note**: When you define multiple jobs, their names must be unique.
+
+Collecting metrics from local and remote instances.
+
+
+<details open><summary>Config</summary>
+
+```yaml
+jobs:
+ - name: local
+ url: http://127.0.0.1:8091
+
+ - name: remote
+ url: http://203.0.113.0:8091
+
+```
+</details>
+
+
+
+## Troubleshooting
+
+### Debug Mode
+
+**Important**: Debug mode is not supported for data collection jobs created via the UI using the Dyncfg feature.
+
+To troubleshoot issues with the `couchbase` collector, run the `go.d.plugin` with the debug option enabled. The output
+should give you clues as to why the collector isn't working.
+
+- Navigate to the `plugins.d` directory, usually at `/usr/libexec/netdata/plugins.d/`. If that's not the case on
+ your system, open `netdata.conf` and look for the `plugins` setting under `[directories]`.
+
+ ```bash
+ cd /usr/libexec/netdata/plugins.d/
+ ```
+
+- Switch to the `netdata` user.
+
+ ```bash
+ sudo -u netdata -s
+ ```
+
+- Run the `go.d.plugin` to debug the collector:
+
+ ```bash
+ ./go.d.plugin -d -m couchbase
+ ```
+
+### Getting Logs
+
+If you're encountering problems with the `couchbase` collector, follow these steps to retrieve logs and identify potential issues:
+
+- **Run the command** specific to your system (systemd, non-systemd, or Docker container).
+- **Examine the output** for any warnings or error messages that might indicate issues. These messages should provide clues about the root cause of the problem.
+
+#### System with systemd
+
+Use the following command to view logs generated since the last Netdata service restart:
+
+```bash
+journalctl _SYSTEMD_INVOCATION_ID="$(systemctl show --value --property=InvocationID netdata)" --namespace=netdata --grep couchbase
+```
+
+#### System without systemd
+
+Locate the collector log file, typically at `/var/log/netdata/collector.log`, and use `grep` to filter for collector's name:
+
+```bash
+grep couchbase /var/log/netdata/collector.log
+```
+
+**Note**: This method shows logs from all restarts. Focus on the **latest entries** for troubleshooting current issues.
+
+#### Docker Container
+
+If your Netdata runs in a Docker container named "netdata" (replace if different), use this command:
+
+```bash
+docker logs netdata 2>&1 | grep couchbase
+```
+
+
diff --git a/src/go/plugin/go.d/modules/couchbase/metadata.yaml b/src/go/plugin/go.d/modules/couchbase/metadata.yaml
new file mode 100644
index 00000000..de21e924
--- /dev/null
+++ b/src/go/plugin/go.d/modules/couchbase/metadata.yaml
@@ -0,0 +1,214 @@
+plugin_name: go.d.plugin
+modules:
+ - meta:
+ id: collector-go.d.plugin-couchbase
+ plugin_name: go.d.plugin
+ module_name: couchbase
+ monitored_instance:
+ name: Couchbase
+ link: https://www.couchbase.com/
+ icon_filename: couchbase.svg
+ categories:
+ - data-collection.database-servers
+ keywords:
+ - couchbase
+ - databases
+ related_resources:
+ integrations:
+ list: []
+ info_provided_to_referring_integrations:
+ description: ""
+ most_popular: false
+ overview:
+ data_collection:
+ metrics_description: |
+ This collector monitors Couchbase servers.
+ method_description: ""
+ supported_platforms:
+ include: []
+ exclude: []
+ multi_instance: true
+ additional_permissions:
+ description: ""
+ default_behavior:
+ auto_detection:
+ description: ""
+ limits:
+ description: ""
+ performance_impact:
+ description: ""
+ setup:
+ prerequisites:
+ list: []
+ configuration:
+ file:
+ name: go.d/couchbase.conf
+ options:
+ description: |
+ The following options can be defined globally: update_every, autodetection_retry.
+ folding:
+ title: All options
+ enabled: true
+ list:
+ - name: update_every
+ description: Data collection frequency.
+ default_value: 1
+ required: false
+ - name: autodetection_retry
+ description: Recheck interval in seconds. Zero means no recheck will be scheduled.
+ default_value: 0
+ required: false
+ - name: url
+ description: Server URL.
+ default_value: http://127.0.0.1:8091
+ required: true
+ - name: username
+ description: Username for basic HTTP authentication.
+ default_value: ""
+ required: false
+ - name: password
+ description: Password for basic HTTP authentication.
+ default_value: ""
+ required: false
+ - name: proxy_url
+ description: Proxy URL.
+ default_value: ""
+ required: false
+ - name: proxy_username
+ description: Username for proxy basic HTTP authentication.
+ default_value: ""
+ required: false
+ - name: proxy_password
+ description: Password for proxy basic HTTP authentication.
+ default_value: ""
+ required: false
+ - name: timeout
+ description: HTTP request timeout.
+ default_value: 2
+ required: false
+ - name: method
+ description: HTTP request method.
+ default_value: GET
+ required: false
+ - name: body
+ description: HTTP request body.
+ default_value: ""
+ required: false
+ - name: headers
+ description: HTTP request headers.
+ default_value: ""
+ required: false
+ - name: not_follow_redirects
+ description: Redirect handling policy. Controls whether the client follows redirects.
+ default_value: false
+ required: false
+ - name: tls_skip_verify
+ description: Server certificate chain and hostname validation policy. Controls whether the client performs this check.
+ default_value: false
+ required: false
+ - name: tls_ca
+ description: Certification authority that the client uses when verifying the server's certificates.
+ default_value: ""
+ required: false
+ - name: tls_cert
+ description: Client tls certificate.
+ default_value: ""
+ required: false
+ - name: tls_key
+ description: Client tls key.
+ default_value: ""
+ required: false
+ examples:
+ folding:
+ title: Config
+ enabled: true
+ list:
+ - name: Basic
+ description: An example configuration.
+ config: |
+ jobs:
+ - name: local
+ url: http://127.0.0.1:8091
+ - name: Basic HTTP auth
+ description: Local server with basic HTTP authentication.
+ config: |
+ jobs:
+ - name: local
+ url: http://127.0.0.1:8091
+ username: foo
+ password: bar
+ - name: Multi-instance
+ description: |
+ > **Note**: When you define multiple jobs, their names must be unique.
+
+ Collecting metrics from local and remote instances.
+ config: |
+ jobs:
+ - name: local
+ url: http://127.0.0.1:8091
+
+ - name: remote
+ url: http://203.0.113.0:8091
+ troubleshooting:
+ problems:
+ list: []
+ alerts: []
+ metrics:
+ folding:
+ title: Metrics
+ enabled: false
+ description: ""
+ availability: []
+ scopes:
+ - name: global
+ description: These metrics refer to the entire monitored application.
+ labels: []
+ metrics:
+ - name: couchbase.bucket_quota_percent_used
+ description: Quota Percent Used Per Bucket
+ unit: percentage
+ chart_type: line
+ dimensions:
+ - name: a dimension per bucket
+ - name: couchbase.bucket_ops_per_sec
+ description: Operations Per Second Per Bucket
+ unit: ops/s
+ chart_type: stacked
+ dimensions:
+ - name: a dimension per bucket
+ - name: couchbase.bucket_disk_fetches
+ description: Disk Fetches Per Bucket
+ unit: fetches
+ chart_type: stacked
+ dimensions:
+ - name: a dimension per bucket
+ - name: couchbase.bucket_item_count
+ description: Item Count Per Bucket
+ unit: items
+ chart_type: stacked
+ dimensions:
+ - name: a dimension per bucket
+ - name: couchbase.bucket_disk_used_stats
+ description: Disk Used Per Bucket
+ unit: bytes
+ chart_type: stacked
+ dimensions:
+ - name: a dimension per bucket
+ - name: couchbase.bucket_data_used
+ description: Data Used Per Bucket
+ unit: bytes
+ chart_type: stacked
+ dimensions:
+ - name: a dimension per bucket
+ - name: couchbase.bucket_mem_used
+ description: Memory Used Per Bucket
+ unit: bytes
+ chart_type: stacked
+ dimensions:
+ - name: a dimension per bucket
+ - name: couchbase.bucket_vb_active_num_non_resident
+ description: Number Of Non-Resident Items Per Bucket
+ unit: items
+ chart_type: stacked
+ dimensions:
+ - name: a dimension per bucket
diff --git a/src/go/plugin/go.d/modules/couchbase/metrics.go b/src/go/plugin/go.d/modules/couchbase/metrics.go
new file mode 100644
index 00000000..c4f23304
--- /dev/null
+++ b/src/go/plugin/go.d/modules/couchbase/metrics.go
@@ -0,0 +1,33 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package couchbase
+
+type cbMetrics struct {
+ // https://developer.couchbase.com/resources/best-practice-guides/monitoring-guide.pdf
+ BucketsBasicStats []bucketsBasicStats
+}
+
+func (m cbMetrics) empty() bool {
+ switch {
+ case m.hasBucketsStats():
+ return false
+ }
+ return true
+}
+
+func (m cbMetrics) hasBucketsStats() bool { return len(m.BucketsBasicStats) > 0 }
+
+type bucketsBasicStats struct {
+ Name string `json:"name"`
+
+ BasicStats struct {
+ DataUsed float64 `json:"dataUsed"`
+ DiskFetches float64 `json:"diskFetches"`
+ ItemCount float64 `json:"itemCount"`
+ DiskUsed float64 `json:"diskUsed"`
+ MemUsed float64 `json:"memUsed"`
+ OpsPerSec float64 `json:"opsPerSec"`
+ QuotaPercentUsed float64 `json:"quotaPercentUsed"`
+ VbActiveNumNonResident float64 `json:"vbActiveNumNonResident"`
+ } `json:"basicStats"`
+}
diff --git a/src/go/plugin/go.d/modules/couchbase/testdata/6.6.0/buckets_basic_stats.json b/src/go/plugin/go.d/modules/couchbase/testdata/6.6.0/buckets_basic_stats.json
new file mode 100644
index 00000000..3749add7
--- /dev/null
+++ b/src/go/plugin/go.d/modules/couchbase/testdata/6.6.0/buckets_basic_stats.json
@@ -0,0 +1,422 @@
+[
+ {
+ "name": "beer-sample",
+ "uuid": "bf10ab11911f1c065db5fd58c5fbc0b6",
+ "bucketType": "membase",
+ "authType": "sasl",
+ "uri": "/pools/default/buckets/beer-sample?bucket_uuid=bf10ab11911f1c065db5fd58c5fbc0b6",
+ "streamingUri": "/pools/default/bucketsStreaming/beer-sample?bucket_uuid=bf10ab11911f1c065db5fd58c5fbc0b6",
+ "localRandomKeyUri": "/pools/default/buckets/beer-sample/localRandomKey",
+ "controllers": {
+ "compactAll": "/pools/default/buckets/beer-sample/controller/compactBucket",
+ "compactDB": "/pools/default/buckets/beer-sample/controller/compactDatabases",
+ "purgeDeletes": "/pools/default/buckets/beer-sample/controller/unsafePurgeBucket",
+ "startRecovery": "/pools/default/buckets/beer-sample/controller/startRecovery"
+ },
+ "nodes": [
+ {
+ "couchApiBaseHTTPS": "https://172.17.0.2:18092/beer-sample%2Bbf10ab11911f1c065db5fd58c5fbc0b6",
+ "couchApiBase": "http://172.17.0.2:8092/beer-sample%2Bbf10ab11911f1c065db5fd58c5fbc0b6",
+ "systemStats": {
+ "cpu_utilization_rate": 15.21035598705502,
+ "cpu_stolen_rate": 0,
+ "swap_total": 0,
+ "swap_used": 0,
+ "mem_total": 33587437568,
+ "mem_free": 30532227072,
+ "mem_limit": 33587437568,
+ "cpu_cores_available": 6,
+ "allocstall": 0
+ },
+ "interestingStats": {
+ "cmd_get": 0,
+ "couch_docs_actual_disk_size": 102960477,
+ "couch_docs_data_size": 72439963,
+ "couch_spatial_data_size": 0,
+ "couch_spatial_disk_size": 0,
+ "couch_views_actual_disk_size": 796048,
+ "couch_views_data_size": 787744,
+ "curr_items": 39480,
+ "curr_items_tot": 39480,
+ "ep_bg_fetched": 0,
+ "get_hits": 0,
+ "mem_used": 118199752,
+ "ops": 0,
+ "vb_active_num_non_resident": 0,
+ "vb_replica_curr_items": 0
+ },
+ "uptime": "638",
+ "memoryTotal": 33587437568,
+ "memoryFree": 30532227072,
+ "mcdMemoryReserved": 25625,
+ "mcdMemoryAllocated": 25625,
+ "replication": 0,
+ "clusterMembership": "active",
+ "recoveryType": "none",
+ "status": "healthy",
+ "otpNode": "ns_1@cb.local",
+ "thisNode": true,
+ "hostname": "172.17.0.2:8091",
+ "nodeUUID": "da79fcb65d6ae1f8b4fdfa3ccb2e4500",
+ "clusterCompatibility": 393222,
+ "version": "6.6.0-7909-enterprise",
+ "os": "x86_64-unknown-linux-gnu",
+ "cpuCount": 6,
+ "ports": {
+ "direct": 11210,
+ "httpsCAPI": 18092,
+ "httpsMgmt": 18091,
+ "distTCP": 21100,
+ "distTLS": 21150
+ },
+ "services": [
+ "cbas",
+ "eventing",
+ "fts",
+ "index",
+ "kv",
+ "n1ql"
+ ],
+ "nodeEncryption": false,
+ "configuredHostname": "127.0.0.1:8091",
+ "addressFamily": "inet",
+ "externalListeners": [
+ {
+ "afamily": "inet",
+ "nodeEncryption": false
+ },
+ {
+ "afamily": "inet6",
+ "nodeEncryption": false
+ }
+ ]
+ }
+ ],
+ "stats": {
+ "uri": "/pools/default/buckets/beer-sample/stats",
+ "directoryURI": "/pools/default/buckets/beer-sample/statsDirectory",
+ "nodeStatsListURI": "/pools/default/buckets/beer-sample/nodes"
+ },
+ "nodeLocator": "vbucket",
+ "saslPassword": "47809efed0156c874b91bbdfeba89912",
+ "ddocs": {
+ "uri": "/pools/default/buckets/beer-sample/ddocs"
+ },
+ "replicaIndex": true,
+ "autoCompactionSettings": false,
+ "maxTTL": 0,
+ "compressionMode": "passive",
+ "replicaNumber": 1,
+ "threadsNumber": 3,
+ "quota": {
+ "ram": 104857600,
+ "rawRAM": 104857600
+ },
+ "basicStats": {
+ "quotaPercentUsed": 32.70613861083984,
+ "opsPerSec": 1.1,
+ "diskFetches": 1,
+ "itemCount": 7303,
+ "diskUsed": 27690472,
+ "dataUsed": 13990431,
+ "memUsed": 34294872,
+ "vbActiveNumNonResident": 1
+ },
+ "evictionPolicy": "valueOnly",
+ "durabilityMinLevel": "none",
+ "conflictResolutionType": "seqno",
+ "bucketCapabilitiesVer": "",
+ "bucketCapabilities": [
+ "durableWrite",
+ "tombstonedUserXAttrs",
+ "couchapi",
+ "dcp",
+ "cbhello",
+ "touch",
+ "cccp",
+ "xdcrCheckpointing",
+ "nodesExt",
+ "xattr"
+ ]
+ },
+ {
+ "name": "gamesim-sample",
+ "uuid": "23ff61363bc4df9af4eb9c2198fc74d3",
+ "bucketType": "membase",
+ "authType": "sasl",
+ "uri": "/pools/default/buckets/gamesim-sample?bucket_uuid=23ff61363bc4df9af4eb9c2198fc74d3",
+ "streamingUri": "/pools/default/bucketsStreaming/gamesim-sample?bucket_uuid=23ff61363bc4df9af4eb9c2198fc74d3",
+ "localRandomKeyUri": "/pools/default/buckets/gamesim-sample/localRandomKey",
+ "controllers": {
+ "compactAll": "/pools/default/buckets/gamesim-sample/controller/compactBucket",
+ "compactDB": "/pools/default/buckets/gamesim-sample/controller/compactDatabases",
+ "purgeDeletes": "/pools/default/buckets/gamesim-sample/controller/unsafePurgeBucket",
+ "startRecovery": "/pools/default/buckets/gamesim-sample/controller/startRecovery"
+ },
+ "nodes": [
+ {
+ "couchApiBaseHTTPS": "https://172.17.0.2:18092/gamesim-sample%2B23ff61363bc4df9af4eb9c2198fc74d3",
+ "couchApiBase": "http://172.17.0.2:8092/gamesim-sample%2B23ff61363bc4df9af4eb9c2198fc74d3",
+ "systemStats": {
+ "cpu_utilization_rate": 15.21035598705502,
+ "cpu_stolen_rate": 0,
+ "swap_total": 0,
+ "swap_used": 0,
+ "mem_total": 33587437568,
+ "mem_free": 30532227072,
+ "mem_limit": 33587437568,
+ "cpu_cores_available": 6,
+ "allocstall": 0
+ },
+ "interestingStats": {
+ "cmd_get": 0,
+ "couch_docs_actual_disk_size": 102960477,
+ "couch_docs_data_size": 72439963,
+ "couch_spatial_data_size": 0,
+ "couch_spatial_disk_size": 0,
+ "couch_views_actual_disk_size": 796048,
+ "couch_views_data_size": 787744,
+ "curr_items": 39480,
+ "curr_items_tot": 39480,
+ "ep_bg_fetched": 0,
+ "get_hits": 0,
+ "mem_used": 118199752,
+ "ops": 0,
+ "vb_active_num_non_resident": 0,
+ "vb_replica_curr_items": 0
+ },
+ "uptime": "638",
+ "memoryTotal": 33587437568,
+ "memoryFree": 30532227072,
+ "mcdMemoryReserved": 25625,
+ "mcdMemoryAllocated": 25625,
+ "replication": 0,
+ "clusterMembership": "active",
+ "recoveryType": "none",
+ "status": "healthy",
+ "otpNode": "ns_1@cb.local",
+ "thisNode": true,
+ "hostname": "172.17.0.2:8091",
+ "nodeUUID": "da79fcb65d6ae1f8b4fdfa3ccb2e4500",
+ "clusterCompatibility": 393222,
+ "version": "6.6.0-7909-enterprise",
+ "os": "x86_64-unknown-linux-gnu",
+ "cpuCount": 6,
+ "ports": {
+ "direct": 11210,
+ "httpsCAPI": 18092,
+ "httpsMgmt": 18091,
+ "distTCP": 21100,
+ "distTLS": 21150
+ },
+ "services": [
+ "cbas",
+ "eventing",
+ "fts",
+ "index",
+ "kv",
+ "n1ql"
+ ],
+ "nodeEncryption": false,
+ "configuredHostname": "127.0.0.1:8091",
+ "addressFamily": "inet",
+ "externalListeners": [
+ {
+ "afamily": "inet",
+ "nodeEncryption": false
+ },
+ {
+ "afamily": "inet6",
+ "nodeEncryption": false
+ }
+ ]
+ }
+ ],
+ "stats": {
+ "uri": "/pools/default/buckets/gamesim-sample/stats",
+ "directoryURI": "/pools/default/buckets/gamesim-sample/statsDirectory",
+ "nodeStatsListURI": "/pools/default/buckets/gamesim-sample/nodes"
+ },
+ "nodeLocator": "vbucket",
+ "saslPassword": "39cf71a1da3f298bed52d19973dce967",
+ "ddocs": {
+ "uri": "/pools/default/buckets/gamesim-sample/ddocs"
+ },
+ "replicaIndex": true,
+ "autoCompactionSettings": false,
+ "maxTTL": 0,
+ "compressionMode": "passive",
+ "replicaNumber": 1,
+ "threadsNumber": 3,
+ "quota": {
+ "ram": 104857600,
+ "rawRAM": 104857600
+ },
+ "basicStats": {
+ "quotaPercentUsed": 28.21607208251953,
+ "opsPerSec": 1.1,
+ "diskFetches": 1,
+ "itemCount": 586,
+ "diskUsed": 13821793,
+ "dataUsed": 5371804,
+ "memUsed": 29586696,
+ "vbActiveNumNonResident": 1
+ },
+ "evictionPolicy": "valueOnly",
+ "durabilityMinLevel": "none",
+ "conflictResolutionType": "seqno",
+ "bucketCapabilitiesVer": "",
+ "bucketCapabilities": [
+ "durableWrite",
+ "tombstonedUserXAttrs",
+ "couchapi",
+ "dcp",
+ "cbhello",
+ "touch",
+ "cccp",
+ "xdcrCheckpointing",
+ "nodesExt",
+ "xattr"
+ ]
+ },
+ {
+ "name": "travel-sample",
+ "uuid": "68a336f9ec0e0d2150d56298c896d0a9",
+ "bucketType": "membase",
+ "authType": "sasl",
+ "uri": "/pools/default/buckets/travel-sample?bucket_uuid=68a336f9ec0e0d2150d56298c896d0a9",
+ "streamingUri": "/pools/default/bucketsStreaming/travel-sample?bucket_uuid=68a336f9ec0e0d2150d56298c896d0a9",
+ "localRandomKeyUri": "/pools/default/buckets/travel-sample/localRandomKey",
+ "controllers": {
+ "compactAll": "/pools/default/buckets/travel-sample/controller/compactBucket",
+ "compactDB": "/pools/default/buckets/travel-sample/controller/compactDatabases",
+ "purgeDeletes": "/pools/default/buckets/travel-sample/controller/unsafePurgeBucket",
+ "startRecovery": "/pools/default/buckets/travel-sample/controller/startRecovery"
+ },
+ "nodes": [
+ {
+ "couchApiBaseHTTPS": "https://172.17.0.2:18092/travel-sample%2B68a336f9ec0e0d2150d56298c896d0a9",
+ "couchApiBase": "http://172.17.0.2:8092/travel-sample%2B68a336f9ec0e0d2150d56298c896d0a9",
+ "systemStats": {
+ "cpu_utilization_rate": 15.21035598705502,
+ "cpu_stolen_rate": 0,
+ "swap_total": 0,
+ "swap_used": 0,
+ "mem_total": 33587437568,
+ "mem_free": 30532227072,
+ "mem_limit": 33587437568,
+ "cpu_cores_available": 6,
+ "allocstall": 0
+ },
+ "interestingStats": {
+ "cmd_get": 0,
+ "couch_docs_actual_disk_size": 102960477,
+ "couch_docs_data_size": 72439963,
+ "couch_spatial_data_size": 0,
+ "couch_spatial_disk_size": 0,
+ "couch_views_actual_disk_size": 796048,
+ "couch_views_data_size": 787744,
+ "curr_items": 39480,
+ "curr_items_tot": 39480,
+ "ep_bg_fetched": 0,
+ "get_hits": 0,
+ "mem_used": 118199752,
+ "ops": 0,
+ "vb_active_num_non_resident": 0,
+ "vb_replica_curr_items": 0
+ },
+ "uptime": "638",
+ "memoryTotal": 33587437568,
+ "memoryFree": 30532227072,
+ "mcdMemoryReserved": 25625,
+ "mcdMemoryAllocated": 25625,
+ "replication": 0,
+ "clusterMembership": "active",
+ "recoveryType": "none",
+ "status": "healthy",
+ "otpNode": "ns_1@cb.local",
+ "thisNode": true,
+ "hostname": "172.17.0.2:8091",
+ "nodeUUID": "da79fcb65d6ae1f8b4fdfa3ccb2e4500",
+ "clusterCompatibility": 393222,
+ "version": "6.6.0-7909-enterprise",
+ "os": "x86_64-unknown-linux-gnu",
+ "cpuCount": 6,
+ "ports": {
+ "direct": 11210,
+ "httpsCAPI": 18092,
+ "httpsMgmt": 18091,
+ "distTCP": 21100,
+ "distTLS": 21150
+ },
+ "services": [
+ "cbas",
+ "eventing",
+ "fts",
+ "index",
+ "kv",
+ "n1ql"
+ ],
+ "nodeEncryption": false,
+ "configuredHostname": "127.0.0.1:8091",
+ "addressFamily": "inet",
+ "externalListeners": [
+ {
+ "afamily": "inet",
+ "nodeEncryption": false
+ },
+ {
+ "afamily": "inet6",
+ "nodeEncryption": false
+ }
+ ]
+ }
+ ],
+ "stats": {
+ "uri": "/pools/default/buckets/travel-sample/stats",
+ "directoryURI": "/pools/default/buckets/travel-sample/statsDirectory",
+ "nodeStatsListURI": "/pools/default/buckets/travel-sample/nodes"
+ },
+ "nodeLocator": "vbucket",
+ "saslPassword": "c6be6d9be723b8b1f8eac4edb84a06ed",
+ "ddocs": {
+ "uri": "/pools/default/buckets/travel-sample/ddocs"
+ },
+ "replicaIndex": true,
+ "autoCompactionSettings": false,
+ "maxTTL": 0,
+ "compressionMode": "passive",
+ "replicaNumber": 1,
+ "threadsNumber": 3,
+ "quota": {
+ "ram": 104857600,
+ "rawRAM": 104857600
+ },
+ "basicStats": {
+ "quotaPercentUsed": 51.80185699462891,
+ "opsPerSec": 1.1,
+ "diskFetches": 1,
+ "itemCount": 31591,
+ "diskUsed": 62244260,
+ "dataUsed": 53865472,
+ "memUsed": 54318184,
+ "vbActiveNumNonResident": 1
+ },
+ "evictionPolicy": "valueOnly",
+ "durabilityMinLevel": "none",
+ "conflictResolutionType": "seqno",
+ "bucketCapabilitiesVer": "",
+ "bucketCapabilities": [
+ "durableWrite",
+ "tombstonedUserXAttrs",
+ "couchapi",
+ "dcp",
+ "cbhello",
+ "touch",
+ "cccp",
+ "xdcrCheckpointing",
+ "nodesExt",
+ "xattr"
+ ]
+ }
+]
diff --git a/src/go/plugin/go.d/modules/couchbase/testdata/config.json b/src/go/plugin/go.d/modules/couchbase/testdata/config.json
new file mode 100644
index 00000000..984c3ed6
--- /dev/null
+++ b/src/go/plugin/go.d/modules/couchbase/testdata/config.json
@@ -0,0 +1,20 @@
+{
+ "update_every": 123,
+ "url": "ok",
+ "body": "ok",
+ "method": "ok",
+ "headers": {
+ "ok": "ok"
+ },
+ "username": "ok",
+ "password": "ok",
+ "proxy_url": "ok",
+ "proxy_username": "ok",
+ "proxy_password": "ok",
+ "timeout": 123.123,
+ "not_follow_redirects": true,
+ "tls_ca": "ok",
+ "tls_cert": "ok",
+ "tls_key": "ok",
+ "tls_skip_verify": true
+}
diff --git a/src/go/plugin/go.d/modules/couchbase/testdata/config.yaml b/src/go/plugin/go.d/modules/couchbase/testdata/config.yaml
new file mode 100644
index 00000000..8558b61c
--- /dev/null
+++ b/src/go/plugin/go.d/modules/couchbase/testdata/config.yaml
@@ -0,0 +1,17 @@
+update_every: 123
+url: "ok"
+body: "ok"
+method: "ok"
+headers:
+ ok: "ok"
+username: "ok"
+password: "ok"
+proxy_url: "ok"
+proxy_username: "ok"
+proxy_password: "ok"
+timeout: 123.123
+not_follow_redirects: yes
+tls_ca: "ok"
+tls_cert: "ok"
+tls_key: "ok"
+tls_skip_verify: yes