diff --git a/go.mod b/go.mod index 6064710387..129df0c511 100644 --- a/go.mod +++ b/go.mod @@ -51,6 +51,7 @@ require ( github.com/go-test/deep v1.0.1 // indirect github.com/gocql/gocql v0.0.0-20181117210152-33c0e89ca93a // indirect github.com/gogo/protobuf v1.1.1 + github.com/golang/gddo v0.0.0-20181116215533-9bd4a3295021 github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c github.com/google/go-cmp v0.2.0 diff --git a/go.sum b/go.sum index 9c1f7a1c3d..d06d88807c 100644 --- a/go.sum +++ b/go.sum @@ -140,6 +140,8 @@ github.com/gocql/gocql v0.0.0-20181117210152-33c0e89ca93a h1:B5gyGsJJmFKS7exambl github.com/gocql/gocql v0.0.0-20181117210152-33c0e89ca93a/go.mod h1:4Fw1eo5iaEhDUs8XyuhSVCVy52Jq3L+/3GJgYkwc+/0= github.com/gogo/protobuf v1.1.1 h1:72R+M5VuhED/KujmZVcIquuo8mBgX4oVda//DQb3PXo= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/golang/gddo v0.0.0-20181116215533-9bd4a3295021 h1:HYV500jCgk+IC68L5sWrLFIWMpaUFfXXpJSAb7XOoBk= +github.com/golang/gddo v0.0.0-20181116215533-9bd4a3295021/go.mod h1:xEhNfoBDX1hzLm2Nf80qUvZ2sVwoMZ8d6IE2SrsQfh4= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= diff --git a/http/telegraf.go b/http/telegraf.go index 13757d6764..0cc2ae6581 100644 --- a/http/telegraf.go +++ b/http/telegraf.go @@ -7,6 +7,7 @@ import ( "net/http" "strings" + "github.com/golang/gddo/httputil" "github.com/influxdata/platform" pctx "github.com/influxdata/platform/context" "github.com/influxdata/platform/kit/errors" @@ -152,7 +153,9 @@ func (h *TelegrafHandler) handleGetTelegraf(w http.ResponseWriter, r *http.Reque return } - mimeType := r.Header.Get("Accept") + offers := []string{"application/toml", "application/json", "application/octet-stream"} + defaultOffer := "application/toml" + mimeType := httputil.NegotiateContentType(r, offers, defaultOffer) switch mimeType { case "application/octet-stream": w.Header().Set("Content-Type", "application/octet-stream") @@ -164,7 +167,7 @@ func (h *TelegrafHandler) handleGetTelegraf(w http.ResponseWriter, r *http.Reque logEncodingError(h.Logger, r, err) return } - default: + case "application/toml": w.Header().Set("Content-Type", "application/toml; charset=utf-8") w.WriteHeader(http.StatusOK) w.Write([]byte(tc.TOML())) diff --git a/http/telegraf_test.go b/http/telegraf_test.go new file mode 100644 index 0000000000..2cf8c6ba54 --- /dev/null +++ b/http/telegraf_test.go @@ -0,0 +1,815 @@ +package http + +import ( + "context" + "encoding/json" + "io/ioutil" + "net/http" + "net/http/httptest" + "strings" + "testing" + + "github.com/influxdata/platform" + "github.com/influxdata/platform/mock" + "github.com/influxdata/platform/telegraf/plugins/inputs" + "github.com/influxdata/platform/telegraf/plugins/outputs" + "go.uber.org/zap/zaptest" +) + +func TestTelegrafHandler_handleGetTelegrafs(t *testing.T) { + type wants struct { + statusCode int + contentType string + body string + } + tests := []struct { + name string + svc *mock.TelegrafConfigStore + r *http.Request + wants wants + }{ + { + name: "return CPU plugin for telegraf", + r: httptest.NewRequest("GET", "http://any.url/api/v2/telegrafs", nil), + svc: &mock.TelegrafConfigStore{ + FindTelegrafConfigsF: func(ctx context.Context, filter platform.UserResourceMappingFilter, opt ...platform.FindOptions) ([]*platform.TelegrafConfig, int, error) { + return []*platform.TelegrafConfig{ + &platform.TelegrafConfig{ + ID: platform.ID(1), + Name: "my config", + Agent: platform.TelegrafAgentConfig{ + Interval: 10000, + }, + Plugins: []platform.TelegrafPlugin{ + { + Comment: "my cpu stats", + Config: &inputs.CPUStats{}, + }, + { + Comment: "my influx output", + Config: &outputs.InfluxDBV2{ + URLs: []string{"http://127.0.0.1:9999"}, + Token: "no_more_secrets", + Organization: "my_org", + Bucket: "my_bucket", + }, + }, + }, + }, + }, 1, nil + }, + }, + wants: wants{ + statusCode: http.StatusOK, + contentType: "application/json; charset=utf-8", + // TODO(goller): once links are in for telegraf, this will need to change. + body: `{ + "configurations": [ + { + "id": "0000000000000001", + "name": "my config", + "agent": { + "collectionInterval": 10000 + }, + "plugins": [ + { + "name": "cpu", + "type": "input", + "comment": "my cpu stats", + "config": {} + }, + { + "name": "influxdb_v2", + "type": "output", + "comment": "my influx output", + "config": { + "urls": [ + "http://127.0.0.1:9999" + ], + "token": "no_more_secrets", + "organization": "my_org", + "bucket": "my_bucket" + } + } + ] + } + ] + }`, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + logger := zaptest.NewLogger(t) + mapping := mock.NewUserResourceMappingService() + labels := mock.NewLabelService() + users := mock.NewUserService() + w := httptest.NewRecorder() + h := NewTelegrafHandler(logger, mapping, labels, tt.svc, users) + h.ServeHTTP(w, tt.r) + + res := w.Result() + content := res.Header.Get("Content-Type") + body, _ := ioutil.ReadAll(res.Body) + + if res.StatusCode != tt.wants.statusCode { + t.Errorf("%q. handleGetTelegrafs() = %v, want %v", tt.name, res.StatusCode, tt.wants.statusCode) + t.Logf("headers: %v", res.Header) + } + if tt.wants.contentType != "" && content != tt.wants.contentType { + t.Errorf("%q. handleGetTelegrafs() = %v, want %v", tt.name, content, tt.wants.contentType) + } + if eq, _ := jsonEqual(string(body), tt.wants.body); tt.wants.body != "" && !eq { + t.Errorf("%q. handleGetTelegrafs() = \n***%v***\n,\nwant\n***%v***", tt.name, string(body), tt.wants.body) + } + }) + } +} + +func TestTelegrafHandler_handleGetTelegraf(t *testing.T) { + type wants struct { + statusCode int + contentType string + body string + } + tests := []struct { + name string + svc *mock.TelegrafConfigStore + r *http.Request + acceptHeader string + wants wants + }{ + { + name: "return JSON telegraf config", + r: httptest.NewRequest("GET", "http://any.url/api/v2/telegrafs/0000000000000001", nil), + acceptHeader: "application/json", + svc: &mock.TelegrafConfigStore{ + FindTelegrafConfigByIDF: func(ctx context.Context, id platform.ID) (*platform.TelegrafConfig, error) { + return &platform.TelegrafConfig{ + ID: platform.ID(1), + Name: "my config", + Agent: platform.TelegrafAgentConfig{ + Interval: 10000, + }, + Plugins: []platform.TelegrafPlugin{ + { + Comment: "my cpu stats", + Config: &inputs.CPUStats{}, + }, + { + Comment: "my influx output", + Config: &outputs.InfluxDBV2{ + URLs: []string{"http://127.0.0.1:9999"}, + Token: "no_more_secrets", + Organization: "my_org", + Bucket: "my_bucket", + }, + }, + }, + }, nil + }, + }, + wants: wants{ + statusCode: http.StatusOK, + contentType: "application/json; charset=utf-8", + // TODO(goller): once links are in for telegraf, this will need to change. + body: `{ + "id": "0000000000000001", + "name": "my config", + "agent": { + "collectionInterval": 10000 + }, + "plugins": [ + { + "name": "cpu", + "type": "input", + "comment": "my cpu stats", + "config": {} + }, + { + "name": "influxdb_v2", + "type": "output", + "comment": "my influx output", + "config": { + "urls": [ + "http://127.0.0.1:9999" + ], + "token": "no_more_secrets", + "organization": "my_org", + "bucket": "my_bucket" + } + } + ] + }`, + }, + }, + { + name: "return JSON telegraf config using fuzzy accept header matching", + r: httptest.NewRequest("GET", "http://any.url/api/v2/telegrafs/0000000000000001", nil), + acceptHeader: "application/json, text/plain, */*", + svc: &mock.TelegrafConfigStore{ + FindTelegrafConfigByIDF: func(ctx context.Context, id platform.ID) (*platform.TelegrafConfig, error) { + return &platform.TelegrafConfig{ + ID: platform.ID(1), + Name: "my config", + Agent: platform.TelegrafAgentConfig{ + Interval: 10000, + }, + Plugins: []platform.TelegrafPlugin{ + { + Comment: "my cpu stats", + Config: &inputs.CPUStats{}, + }, + { + Comment: "my influx output", + Config: &outputs.InfluxDBV2{ + URLs: []string{"http://127.0.0.1:9999"}, + Token: "no_more_secrets", + Organization: "my_org", + Bucket: "my_bucket", + }, + }, + }, + }, nil + }, + }, + wants: wants{ + statusCode: http.StatusOK, + contentType: "application/json; charset=utf-8", + // TODO(goller): once links are in for telegraf, this will need to change. + body: `{ + "id": "0000000000000001", + "name": "my config", + "agent": { + "collectionInterval": 10000 + }, + "plugins": [ + { + "name": "cpu", + "type": "input", + "comment": "my cpu stats", + "config": {} + }, + { + "name": "influxdb_v2", + "type": "output", + "comment": "my influx output", + "config": { + "urls": [ + "http://127.0.0.1:9999" + ], + "token": "no_more_secrets", + "organization": "my_org", + "bucket": "my_bucket" + } + } + ] + }`, + }, + }, + { + name: "return TOML telegraf config with accept header application/toml", + r: httptest.NewRequest("GET", "http://any.url/api/v2/telegrafs/0000000000000001", nil), + acceptHeader: "application/toml", + svc: &mock.TelegrafConfigStore{ + FindTelegrafConfigByIDF: func(ctx context.Context, id platform.ID) (*platform.TelegrafConfig, error) { + return &platform.TelegrafConfig{ + ID: platform.ID(1), + Name: "my config", + Agent: platform.TelegrafAgentConfig{ + Interval: 10000, + }, + Plugins: []platform.TelegrafPlugin{ + { + Comment: "my cpu stats", + Config: &inputs.CPUStats{}, + }, + { + Comment: "my influx output", + Config: &outputs.InfluxDBV2{ + URLs: []string{"http://127.0.0.1:9999"}, + Token: "no_more_secrets", + Organization: "my_org", + Bucket: "my_bucket", + }, + }, + }, + }, nil + }, + }, + wants: wants{ + statusCode: http.StatusOK, + contentType: "application/toml; charset=utf-8", + // TODO(goller): once links are in for telegraf, this will need to change. + body: `# Configuration for telegraf agent +[agent] + ## Default data collection interval for all inputs + interval = "10s" + ## Rounds collection interval to 'interval' + ## ie, if interval="10s" then always collect on :00, :10, :20, etc. + round_interval = true + + ## Telegraf will send metrics to outputs in batches of at most + ## metric_batch_size metrics. + ## This controls the size of writes that Telegraf sends to output plugins. + metric_batch_size = 1000 + + ## For failed writes, telegraf will cache metric_buffer_limit metrics for each + ## output, and will flush this buffer on a successful write. Oldest metrics + ## are dropped first when this buffer fills. + ## This buffer only fills when writes fail to output plugin(s). + metric_buffer_limit = 10000 + + ## Collection jitter is used to jitter the collection by a random amount. + ## Each plugin will sleep for a random time within jitter before collecting. + ## This can be used to avoid many plugins querying things like sysfs at the + ## same time, which can have a measurable effect on the system. + collection_jitter = "0s" + + ## Default flushing interval for all outputs. Maximum flush_interval will be + ## flush_interval + flush_jitter + flush_interval = "10s" + ## Jitter the flush interval by a random amount. This is primarily to avoid + ## large write spikes for users running a large number of telegraf instances. + ## ie, a jitter of 5s and interval 10s means flushes will happen every 10-15s + flush_jitter = "0s" + + ## By default or when set to "0s", precision will be set to the same + ## timestamp order as the collection interval, with the maximum being 1s. + ## ie, when interval = "10s", precision will be "1s" + ## when interval = "250ms", precision will be "1ms" + ## Precision will NOT be used for service inputs. It is up to each individual + ## service input to set the timestamp at the appropriate precision. + ## Valid time units are "ns", "us" (or "µs"), "ms", "s". + precision = "" + + ## Logging configuration: + ## Run telegraf with debug log messages. + debug = false + ## Run telegraf in quiet mode (error log messages only). + quiet = false + ## Specify the log file name. The empty string means to log to stderr. + logfile = "" + + ## Override default hostname, if empty use os.Hostname() + hostname = "" + ## If set to true, do no set the "host" tag in the telegraf agent. + omit_hostname = false +[[inputs.cpu]] + ## Whether to report per-cpu stats or not + percpu = true + ## Whether to report total system cpu stats or not + totalcpu = true + ## If true, collect raw CPU time metrics. + collect_cpu_time = false + ## If true, compute and report the sum of all non-idle CPU states. + report_active = false +[[outputs.influxdb_v2]] + ## The URLs of the InfluxDB cluster nodes. + ## + ## Multiple URLs can be specified for a single cluster, only ONE of the + ## urls will be written to each interval. + ## urls exp: http://127.0.0.1:9999 + urls = ["http://127.0.0.1:9999"] + + ## Token for authentication. + token = "no_more_secrets" + + ## Organization is the name of the organization you wish to write to; must exist. + organization = "my_org" + + ## Destination bucket to write into. + bucket = "my_bucket" +`, + }, + }, + { + name: "return TOML telegraf config with no accept header", + r: httptest.NewRequest("GET", "http://any.url/api/v2/telegrafs/0000000000000001", nil), + svc: &mock.TelegrafConfigStore{ + FindTelegrafConfigByIDF: func(ctx context.Context, id platform.ID) (*platform.TelegrafConfig, error) { + return &platform.TelegrafConfig{ + ID: platform.ID(1), + Name: "my config", + Agent: platform.TelegrafAgentConfig{ + Interval: 10000, + }, + Plugins: []platform.TelegrafPlugin{ + { + Comment: "my cpu stats", + Config: &inputs.CPUStats{}, + }, + { + Comment: "my influx output", + Config: &outputs.InfluxDBV2{ + URLs: []string{"http://127.0.0.1:9999"}, + Token: "no_more_secrets", + Organization: "my_org", + Bucket: "my_bucket", + }, + }, + }, + }, nil + }, + }, + wants: wants{ + statusCode: http.StatusOK, + contentType: "application/toml; charset=utf-8", + // TODO(goller): once links are in for telegraf, this will need to change. + body: `# Configuration for telegraf agent +[agent] + ## Default data collection interval for all inputs + interval = "10s" + ## Rounds collection interval to 'interval' + ## ie, if interval="10s" then always collect on :00, :10, :20, etc. + round_interval = true + + ## Telegraf will send metrics to outputs in batches of at most + ## metric_batch_size metrics. + ## This controls the size of writes that Telegraf sends to output plugins. + metric_batch_size = 1000 + + ## For failed writes, telegraf will cache metric_buffer_limit metrics for each + ## output, and will flush this buffer on a successful write. Oldest metrics + ## are dropped first when this buffer fills. + ## This buffer only fills when writes fail to output plugin(s). + metric_buffer_limit = 10000 + + ## Collection jitter is used to jitter the collection by a random amount. + ## Each plugin will sleep for a random time within jitter before collecting. + ## This can be used to avoid many plugins querying things like sysfs at the + ## same time, which can have a measurable effect on the system. + collection_jitter = "0s" + + ## Default flushing interval for all outputs. Maximum flush_interval will be + ## flush_interval + flush_jitter + flush_interval = "10s" + ## Jitter the flush interval by a random amount. This is primarily to avoid + ## large write spikes for users running a large number of telegraf instances. + ## ie, a jitter of 5s and interval 10s means flushes will happen every 10-15s + flush_jitter = "0s" + + ## By default or when set to "0s", precision will be set to the same + ## timestamp order as the collection interval, with the maximum being 1s. + ## ie, when interval = "10s", precision will be "1s" + ## when interval = "250ms", precision will be "1ms" + ## Precision will NOT be used for service inputs. It is up to each individual + ## service input to set the timestamp at the appropriate precision. + ## Valid time units are "ns", "us" (or "µs"), "ms", "s". + precision = "" + + ## Logging configuration: + ## Run telegraf with debug log messages. + debug = false + ## Run telegraf in quiet mode (error log messages only). + quiet = false + ## Specify the log file name. The empty string means to log to stderr. + logfile = "" + + ## Override default hostname, if empty use os.Hostname() + hostname = "" + ## If set to true, do no set the "host" tag in the telegraf agent. + omit_hostname = false +[[inputs.cpu]] + ## Whether to report per-cpu stats or not + percpu = true + ## Whether to report total system cpu stats or not + totalcpu = true + ## If true, collect raw CPU time metrics. + collect_cpu_time = false + ## If true, compute and report the sum of all non-idle CPU states. + report_active = false +[[outputs.influxdb_v2]] + ## The URLs of the InfluxDB cluster nodes. + ## + ## Multiple URLs can be specified for a single cluster, only ONE of the + ## urls will be written to each interval. + ## urls exp: http://127.0.0.1:9999 + urls = ["http://127.0.0.1:9999"] + + ## Token for authentication. + token = "no_more_secrets" + + ## Organization is the name of the organization you wish to write to; must exist. + organization = "my_org" + + ## Destination bucket to write into. + bucket = "my_bucket" +`, + }, + }, + { + name: "return TOML telegraf config with application/octet-stream", + r: httptest.NewRequest("GET", "http://any.url/api/v2/telegrafs/0000000000000001", nil), + acceptHeader: "application/octet-stream", + svc: &mock.TelegrafConfigStore{ + FindTelegrafConfigByIDF: func(ctx context.Context, id platform.ID) (*platform.TelegrafConfig, error) { + return &platform.TelegrafConfig{ + ID: platform.ID(1), + Name: "my config", + Agent: platform.TelegrafAgentConfig{ + Interval: 10000, + }, + Plugins: []platform.TelegrafPlugin{ + { + Comment: "my cpu stats", + Config: &inputs.CPUStats{}, + }, + { + Comment: "my influx output", + Config: &outputs.InfluxDBV2{ + URLs: []string{"http://127.0.0.1:9999"}, + Token: "no_more_secrets", + Organization: "my_org", + Bucket: "my_bucket", + }, + }, + }, + }, nil + }, + }, + wants: wants{ + statusCode: http.StatusOK, + contentType: "application/octet-stream", + // TODO(goller): once links are in for telegraf, this will need to change. + body: `# Configuration for telegraf agent +[agent] + ## Default data collection interval for all inputs + interval = "10s" + ## Rounds collection interval to 'interval' + ## ie, if interval="10s" then always collect on :00, :10, :20, etc. + round_interval = true + + ## Telegraf will send metrics to outputs in batches of at most + ## metric_batch_size metrics. + ## This controls the size of writes that Telegraf sends to output plugins. + metric_batch_size = 1000 + + ## For failed writes, telegraf will cache metric_buffer_limit metrics for each + ## output, and will flush this buffer on a successful write. Oldest metrics + ## are dropped first when this buffer fills. + ## This buffer only fills when writes fail to output plugin(s). + metric_buffer_limit = 10000 + + ## Collection jitter is used to jitter the collection by a random amount. + ## Each plugin will sleep for a random time within jitter before collecting. + ## This can be used to avoid many plugins querying things like sysfs at the + ## same time, which can have a measurable effect on the system. + collection_jitter = "0s" + + ## Default flushing interval for all outputs. Maximum flush_interval will be + ## flush_interval + flush_jitter + flush_interval = "10s" + ## Jitter the flush interval by a random amount. This is primarily to avoid + ## large write spikes for users running a large number of telegraf instances. + ## ie, a jitter of 5s and interval 10s means flushes will happen every 10-15s + flush_jitter = "0s" + + ## By default or when set to "0s", precision will be set to the same + ## timestamp order as the collection interval, with the maximum being 1s. + ## ie, when interval = "10s", precision will be "1s" + ## when interval = "250ms", precision will be "1ms" + ## Precision will NOT be used for service inputs. It is up to each individual + ## service input to set the timestamp at the appropriate precision. + ## Valid time units are "ns", "us" (or "µs"), "ms", "s". + precision = "" + + ## Logging configuration: + ## Run telegraf with debug log messages. + debug = false + ## Run telegraf in quiet mode (error log messages only). + quiet = false + ## Specify the log file name. The empty string means to log to stderr. + logfile = "" + + ## Override default hostname, if empty use os.Hostname() + hostname = "" + ## If set to true, do no set the "host" tag in the telegraf agent. + omit_hostname = false +[[inputs.cpu]] + ## Whether to report per-cpu stats or not + percpu = true + ## Whether to report total system cpu stats or not + totalcpu = true + ## If true, collect raw CPU time metrics. + collect_cpu_time = false + ## If true, compute and report the sum of all non-idle CPU states. + report_active = false +[[outputs.influxdb_v2]] + ## The URLs of the InfluxDB cluster nodes. + ## + ## Multiple URLs can be specified for a single cluster, only ONE of the + ## urls will be written to each interval. + ## urls exp: http://127.0.0.1:9999 + urls = ["http://127.0.0.1:9999"] + + ## Token for authentication. + token = "no_more_secrets" + + ## Organization is the name of the organization you wish to write to; must exist. + organization = "my_org" + + ## Destination bucket to write into. + bucket = "my_bucket" +`, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + logger := zaptest.NewLogger(t) + mapping := mock.NewUserResourceMappingService() + labels := mock.NewLabelService() + users := mock.NewUserService() + + tt.r.Header.Set("Accept", tt.acceptHeader) + w := httptest.NewRecorder() + h := NewTelegrafHandler(logger, mapping, labels, tt.svc, users) + + h.ServeHTTP(w, tt.r) + + res := w.Result() + content := res.Header.Get("Content-Type") + body, _ := ioutil.ReadAll(res.Body) + + if res.StatusCode != tt.wants.statusCode { + t.Errorf("%q. handleGetTelegraf() = %v, want %v", tt.name, res.StatusCode, tt.wants.statusCode) + t.Logf("headers: %v", res.Header) + } + if tt.wants.contentType != "" && content != tt.wants.contentType { + t.Errorf("%q. handleGetTelegraf() = %v, want %v", tt.name, content, tt.wants.contentType) + return + } + + if strings.Contains(tt.wants.contentType, "application/json") { + if eq, _ := jsonEqual(string(body), tt.wants.body); tt.wants.body != "" && !eq { + t.Errorf("%q. handleGetTelegraf() = \n***%v***\n,\nwant\n***%v***", tt.name, string(body), tt.wants.body) + } + } else if string(body) != tt.wants.body { + t.Errorf("%q. handleGetTelegraf() = \n***%v***\n,\nwant\n***%v***", tt.name, string(body), tt.wants.body) + } + }) + } +} + +func Test_newTelegrafResponses(t *testing.T) { + type args struct { + tcs []*platform.TelegrafConfig + } + tests := []struct { + name string + args args + want string + }{ + { + args: args{ + tcs: []*platform.TelegrafConfig{ + &platform.TelegrafConfig{ + ID: platform.ID(1), + Name: "my config", + Agent: platform.TelegrafAgentConfig{ + Interval: 10000, + }, + Plugins: []platform.TelegrafPlugin{ + { + Comment: "my cpu stats", + Config: &inputs.CPUStats{}, + }, + { + Comment: "my influx output", + Config: &outputs.InfluxDBV2{ + URLs: []string{"http://127.0.0.1:9999"}, + Token: "no_more_secrets", + Organization: "my_org", + Bucket: "my_bucket", + }, + }, + }, + }, + }, + }, + want: `{ + "configurations": [ + { + "id": "0000000000000001", + "name": "my config", + "agent": { + "collectionInterval": 10000 + }, + "plugins": [ + { + "name": "cpu", + "type": "input", + "comment": "my cpu stats", + "config": {} + }, + { + "name": "influxdb_v2", + "type": "output", + "comment": "my influx output", + "config": { + "urls": [ + "http://127.0.0.1:9999" + ], + "token": "no_more_secrets", + "organization": "my_org", + "bucket": "my_bucket" + } + } + ] + } + ] + }`, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + res := newTelegrafResponses(tt.args.tcs) + got, err := json.Marshal(res) + if err != nil { + t.Fatalf("newTelegrafResponses() JSON marshal %v", err) + } + if eq, _ := jsonEqual(string(got), tt.want); tt.want != "" && !eq { + t.Errorf("%q. newTelegrafResponses() = \n***%v***\n,\nwant\n***%v***", tt.name, string(got), tt.want) + } + }) + } +} + +func Test_newTelegrafResponse(t *testing.T) { + type args struct { + tc *platform.TelegrafConfig + } + tests := []struct { + name string + args args + want string + }{ + { + args: args{ + tc: &platform.TelegrafConfig{ + ID: platform.ID(1), + Name: "my config", + Agent: platform.TelegrafAgentConfig{ + Interval: 10000, + }, + Plugins: []platform.TelegrafPlugin{ + { + Comment: "my cpu stats", + Config: &inputs.CPUStats{}, + }, + { + Comment: "my influx output", + Config: &outputs.InfluxDBV2{ + URLs: []string{"http://127.0.0.1:9999"}, + Token: "no_more_secrets", + Organization: "my_org", + Bucket: "my_bucket", + }, + }, + }, + }, + }, + want: `{ + "id": "0000000000000001", + "name": "my config", + "agent": { + "collectionInterval": 10000 + }, + "plugins": [ + { + "name": "cpu", + "type": "input", + "comment": "my cpu stats", + "config": {} + }, + { + "name": "influxdb_v2", + "type": "output", + "comment": "my influx output", + "config": { + "urls": [ + "http://127.0.0.1:9999" + ], + "token": "no_more_secrets", + "organization": "my_org", + "bucket": "my_bucket" + } + } + ] + }`, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + res := newTelegrafResponse(tt.args.tc) + got, err := json.Marshal(res) + if err != nil { + t.Fatalf("newTelegrafResponse() JSON marshal %v", err) + } + if eq, _ := jsonEqual(string(got), tt.want); tt.want != "" && !eq { + t.Errorf("%q. newTelegrafResponse() = \n%v\n,\nwant\n%v", tt.name, string(got), tt.want) + } + }) + } +} diff --git a/mock/telegraf_service.go b/mock/telegraf_service.go new file mode 100644 index 0000000000..3063d9a98e --- /dev/null +++ b/mock/telegraf_service.go @@ -0,0 +1,69 @@ +package mock + +import ( + "context" + + "github.com/influxdata/platform" +) + +var _ platform.TelegrafConfigStore = &TelegrafConfigStore{} + +// TelegrafConfigStore represents a service for managing telegraf config data. +type TelegrafConfigStore struct { + FindUserResourceMappingsF func(ctx context.Context, filter platform.UserResourceMappingFilter, opt ...platform.FindOptions) ([]*platform.UserResourceMapping, int, error) + CreateUserResourceMappingF func(ctx context.Context, m *platform.UserResourceMapping) error + DeleteUserResourceMappingF func(ctx context.Context, resourceID platform.ID, userID platform.ID) error + FindTelegrafConfigByIDF func(ctx context.Context, id platform.ID) (*platform.TelegrafConfig, error) + FindTelegrafConfigF func(ctx context.Context, filter platform.UserResourceMappingFilter) (*platform.TelegrafConfig, error) + FindTelegrafConfigsF func(ctx context.Context, filter platform.UserResourceMappingFilter, opt ...platform.FindOptions) ([]*platform.TelegrafConfig, int, error) + CreateTelegrafConfigF func(ctx context.Context, tc *platform.TelegrafConfig, userID platform.ID) error + UpdateTelegrafConfigF func(ctx context.Context, id platform.ID, tc *platform.TelegrafConfig, userID platform.ID) (*platform.TelegrafConfig, error) + DeleteTelegrafConfigF func(ctx context.Context, id platform.ID) error +} + +// FindUserResourceMappings returns a list of UserResourceMappings that match filter and the total count of matching mappings. +func (s *TelegrafConfigStore) FindUserResourceMappings(ctx context.Context, filter platform.UserResourceMappingFilter, opt ...platform.FindOptions) ([]*platform.UserResourceMapping, int, error) { + return s.FindUserResourceMappingsF(ctx, filter, opt...) +} + +// CreateUserResourceMapping creates a user resource mapping +func (s *TelegrafConfigStore) CreateUserResourceMapping(ctx context.Context, m *platform.UserResourceMapping) error { + return s.CreateUserResourceMappingF(ctx, m) +} + +// DeleteUserResourceMapping deletes a user resource mapping +func (s *TelegrafConfigStore) DeleteUserResourceMapping(ctx context.Context, resourceID platform.ID, userID platform.ID) error { + return s.DeleteUserResourceMappingF(ctx, resourceID, userID) +} + +// FindTelegrafConfigByID returns a single telegraf config by ID. +func (s *TelegrafConfigStore) FindTelegrafConfigByID(ctx context.Context, id platform.ID) (*platform.TelegrafConfig, error) { + return s.FindTelegrafConfigByIDF(ctx, id) +} + +// FindTelegrafConfig returns the first telegraf config that matches filter. +func (s *TelegrafConfigStore) FindTelegrafConfig(ctx context.Context, filter platform.UserResourceMappingFilter) (*platform.TelegrafConfig, error) { + return s.FindTelegrafConfigF(ctx, filter) +} + +// FindTelegrafConfigs returns a list of telegraf configs that match filter and the total count of matching telegraf configs. +// Additional options provide pagination & sorting. +func (s *TelegrafConfigStore) FindTelegrafConfigs(ctx context.Context, filter platform.UserResourceMappingFilter, opt ...platform.FindOptions) ([]*platform.TelegrafConfig, int, error) { + return s.FindTelegrafConfigsF(ctx, filter, opt...) +} + +// CreateTelegrafConfig creates a new telegraf config and sets b.ID with the new identifier. +func (s *TelegrafConfigStore) CreateTelegrafConfig(ctx context.Context, tc *platform.TelegrafConfig, userID platform.ID) error { + return s.CreateTelegrafConfigF(ctx, tc, userID) +} + +// UpdateTelegrafConfig updates a single telegraf config. +// Returns the new telegraf config after update. +func (s *TelegrafConfigStore) UpdateTelegrafConfig(ctx context.Context, id platform.ID, tc *platform.TelegrafConfig, userID platform.ID) (*platform.TelegrafConfig, error) { + return s.UpdateTelegrafConfigF(ctx, id, tc, userID) +} + +// DeleteTelegrafConfig removes a telegraf config by ID. +func (s *TelegrafConfigStore) DeleteTelegrafConfig(ctx context.Context, id platform.ID) error { + return s.DeleteTelegrafConfigF(ctx, id) +}