From b236665cee34ef870ff7324a55cec4ad82f4a403 Mon Sep 17 00:00:00 2001 From: Suzuki Shunsuke Date: Sat, 8 Sep 2018 21:39:53 +0900 Subject: [PATCH 01/10] feat(client): support Collector Configuration APIs --- client/collector_configuration.go | 105 +++++++++++++++++++++ client/endpoint/collector_configuration.go | 54 +++++++++++ client/endpoint/endpoint.go | 54 ++++++----- collector_configuration.go | 65 +++++++++++++ 4 files changed, 254 insertions(+), 24 deletions(-) create mode 100644 client/collector_configuration.go create mode 100644 client/endpoint/collector_configuration.go create mode 100644 collector_configuration.go diff --git a/client/collector_configuration.go b/client/collector_configuration.go new file mode 100644 index 00000000..02acce9f --- /dev/null +++ b/client/collector_configuration.go @@ -0,0 +1,105 @@ +package client + +import ( + "context" + "fmt" + + "github.com/pkg/errors" + + "github.com/suzuki-shunsuke/go-graylog" +) + +// CreateCollectorConfiguration creates a collector configuration. +func (client *Client) CreateCollectorConfiguration(cfg *graylog.CollectorConfiguration) (*ErrorInfo, error) { + return client.CreateCollectorConfigurationContext(context.Background(), cfg) +} + +// CreateCollectorConfigurationContext creates a collector configuration with a context. +func (client *Client) CreateCollectorConfigurationContext( + ctx context.Context, cfg *graylog.CollectorConfiguration, +) (*ErrorInfo, error) { + // POST /plugins/org.graylog.plugins.collector/configurations Create new collector configuration + if cfg == nil { + return nil, fmt.Errorf("collector configuration is nil") + } + return client.callPost(ctx, client.Endpoints().CollectorConfigurations(), cfg, nil) +} + +// GetCollectorConfigurations returns all collector configurations. +func (client *Client) GetCollectorConfigurations() ([]graylog.CollectorConfiguration, int, *ErrorInfo, error) { + return client.GetCollectorConfigurationsContext(context.Background()) +} + +// GetCollectorConfigurationsContext returns all collector configurations with a context. +func (client *Client) GetCollectorConfigurationsContext(ctx context.Context) ([]graylog.CollectorConfiguration, int, *ErrorInfo, error) { + cfgs := &graylog.CollectorConfigurationsBody{} + ei, err := client.callGet( + ctx, client.Endpoints().CollectorConfigurations(), nil, cfgs) + return cfgs.Configurations, cfgs.Total, ei, err +} + +// GetCollectorConfiguration returns a collector configuration. +func (client *Client) GetCollectorConfiguration(id string) (*graylog.CollectorConfiguration, *ErrorInfo, error) { + return client.GetCollectorConfigurationContext(context.Background(), id) +} + +// GetCollectorConfigurationContext returns a given user with a context. +func (client *Client) GetCollectorConfigurationContext( + ctx context.Context, id string, +) (*graylog.CollectorConfiguration, *ErrorInfo, error) { + // GET /api/plugins/org.graylog.plugins.collector/configurations/:id + if id == "" { + return nil, nil, errors.New("id is empty") + } + u, err := client.Endpoints().CollectorConfiguration(id) + if err != nil { + return nil, nil, err + } + cfg := &graylog.CollectorConfiguration{} + ei, err := client.callGet(ctx, u.String(), nil, cfg) + return cfg, ei, err +} + +// RenameCollectorConfiguration renames a collector configuration. +func (client *Client) RenameCollectorConfiguration(id, name string) (*graylog.CollectorConfiguration, *ErrorInfo, error) { + return client.RenameCollectorConfigurationContext(context.Background(), id, name) +} + +// RenameCollectorConfigurationContext renames a collector configuration with a context. +func (client *Client) RenameCollectorConfigurationContext( + ctx context.Context, id, name string, +) (*graylog.CollectorConfiguration, *ErrorInfo, error) { + if id == "" { + return nil, nil, fmt.Errorf("id is nil") + } + if name == "" { + return nil, nil, fmt.Errorf("name is nil") + } + input := graylog.CollectorConfiguration{Name: name} + u, err := client.Endpoints().CollectorConfiguration(id) + if err != nil { + return nil, nil, err + } + cfg := graylog.CollectorConfiguration{Name: name} + ei, err := client.callPut(ctx, u.String(), &input, &cfg) + return &cfg, ei, err +} + +// DeleteCollectorConfiguration deletes a collector configuration. +func (client *Client) DeleteCollectorConfiguration(id string) (*ErrorInfo, error) { + return client.DeleteCollectorConfigurationContext(context.Background(), id) +} + +// DeleteCollectorConfigurationContext deletes a collector configuration with a context. +func (client *Client) DeleteCollectorConfigurationContext( + ctx context.Context, id string, +) (*ErrorInfo, error) { + if id == "" { + return nil, errors.New("id is empty") + } + u, err := client.Endpoints().CollectorConfiguration(id) + if err != nil { + return nil, err + } + return client.callDelete(ctx, u.String(), nil, nil) +} diff --git a/client/endpoint/collector_configuration.go b/client/endpoint/collector_configuration.go new file mode 100644 index 00000000..4876a06d --- /dev/null +++ b/client/endpoint/collector_configuration.go @@ -0,0 +1,54 @@ +package endpoint + +import ( + "fmt" + "net/url" +) + +// CollectorConfigurations returns an Collector Configuration API's endpoint url. +func (ep *Endpoints) CollectorConfigurations() string { + // /plugins/org.graylog.plugins.collector/configurations + return ep.collectorConfigurations.String() +} + +// CollectorConfiguration returns an Collector Configuration API's endpoint url. +func (ep *Endpoints) CollectorConfiguration(id string) (*url.URL, error) { + // /plugins/org.graylog.plugins.collector/configurations + return urlJoin(ep.collectorConfigurations, id) +} + +// CollectorConfigurationInputs returns an Collector Configuration Input API's endpoint url. +func (ep *Endpoints) CollectorConfigurationInputs(id string) (*url.URL, error) { + // /plugins/org.graylog.plugins.collector/configurations/{id}/inputs + return urlJoin(ep.collectorConfigurations, fmt.Sprintf("%s/inputs", id)) +} + +// CollectorConfigurationInput returns an Collector Configuration Input API's endpoint url. +func (ep *Endpoints) CollectorConfigurationInput(id, inputID string) (*url.URL, error) { + // /plugins/org.graylog.plugins.collector/configurations/{id}/inputs/{inputId} + return urlJoin(ep.collectorConfigurations, fmt.Sprintf("%s/inputs/%s", id, inputID)) +} + +// CollectorConfigurationOutputs returns an Collector Configuration Output API's endpoint url. +func (ep *Endpoints) CollectorConfigurationOutputs(id string) (*url.URL, error) { + // /plugins/org.graylog.plugins.collector/configurations/{id}/outputs + return urlJoin(ep.collectorConfigurations, fmt.Sprintf("%s/outputs", id)) +} + +// CollectorConfigurationOutput returns an Collector Configuration Output API's endpoint url. +func (ep *Endpoints) CollectorConfigurationOutput(id, outputID string) (*url.URL, error) { + // /plugins/org.graylog.plugins.collector/configurations/{id}/outputs/{outputId} + return urlJoin(ep.collectorConfigurations, fmt.Sprintf("%s/outputs/%s", id, outputID)) +} + +// CollectorConfigurationSnippets returns an Collector Configuration Snippet API's endpoint url. +func (ep *Endpoints) CollectorConfigurationSnippets(id string) (*url.URL, error) { + // /plugins/org.graylog.plugins.collector/configurations/{id}/snippets + return urlJoin(ep.collectorConfigurations, fmt.Sprintf("%s/snippets", id)) +} + +// CollectorConfigurationSnippet returns an Collector Configuration Snippet API's endpoint url. +func (ep *Endpoints) CollectorConfigurationSnippet(id, snippetID string) (*url.URL, error) { + // /plugins/org.graylog.plugins.collector/configurations/{id}/snippets/{snippetId} + return urlJoin(ep.collectorConfigurations, fmt.Sprintf("%s/snippets/%s", id, snippetID)) +} diff --git a/client/endpoint/endpoint.go b/client/endpoint/endpoint.go index a6b18987..2d0d0b3f 100644 --- a/client/endpoint/endpoint.go +++ b/client/endpoint/endpoint.go @@ -12,18 +12,19 @@ func urlJoin(ep *url.URL, arg string) (*url.URL, error) { // Endpoints represents each API's endpoint URLs. type Endpoints struct { - alarmCallbacks *url.URL - alerts *url.URL - alertConditions *url.URL - dashboards *url.URL - enabledStreams *url.URL - indexSets *url.URL - indexSetStats *url.URL - inputs *url.URL - roles *url.URL - streams *url.URL - users *url.URL - ldapSetting string + alarmCallbacks *url.URL + alerts *url.URL + alertConditions *url.URL + collectorConfigurations *url.URL + dashboards *url.URL + enabledStreams *url.URL + indexSets *url.URL + indexSetStats *url.URL + inputs *url.URL + roles *url.URL + streams *url.URL + users *url.URL + ldapSetting string } // NewEndpoints returns a new Endpoints. @@ -43,6 +44,10 @@ func NewEndpoints(endpoint string) (*Endpoints, error) { if err != nil { return nil, err } + collectorConfigurations, err := urlJoin(ep, "plugins/org.graylog.plugins.collector/configurations") + if err != nil { + return nil, err + } dashboards, err := urlJoin(ep, "dashboards") if err != nil { return nil, err @@ -84,17 +89,18 @@ func NewEndpoints(endpoint string) (*Endpoints, error) { return nil, err } return &Endpoints{ - alarmCallbacks: alarmCallbacks, - alerts: alerts, - alertConditions: alertConditions, - dashboards: dashboards, - enabledStreams: enabledStreams, - indexSets: indexSets, - indexSetStats: indexSetStats, - inputs: inputs, - roles: roles, - streams: streams, - users: users, - ldapSetting: ldapSetting.String(), + alarmCallbacks: alarmCallbacks, + alerts: alerts, + alertConditions: alertConditions, + collectorConfigurations: collectorConfigurations, + dashboards: dashboards, + enabledStreams: enabledStreams, + indexSets: indexSets, + indexSetStats: indexSetStats, + inputs: inputs, + roles: roles, + streams: streams, + users: users, + ldapSetting: ldapSetting.String(), }, nil } diff --git a/collector_configuration.go b/collector_configuration.go new file mode 100644 index 00000000..e4174b26 --- /dev/null +++ b/collector_configuration.go @@ -0,0 +1,65 @@ +package graylog + +type ( + // CollectorConfiguration represents a Graylog's Collector Configuration. + CollectorConfiguration struct { + ID string `json:"id,omitempty" v-create:"isdefault"` + Name string `json:"name,omitempty" v-create:"required"` + // Tags string `json:"tags,omitempty" v-create:"isdefault"` + Inputs []CollectorConfigurationInput `json:"inputs"` + Outputs []CollectorConfigurationOutput `json:"outputs"` + Snippets []CollectorConfigurationSnippet `json:"snippets"` + } + + // CollectorConfigurationInput represents a Graylog's Collector Configuration Input. + CollectorConfigurationInput struct { + Backend string `json:"backend"` + Type string `json:"type"` + Name string `json:"name"` + InputID string `json:"input_id"` + ForwardTo string `json:"forward_to"` + Properties CollectorConfigurationInputProperty `json:"properties"` + } + + // CollectorConfigurationInputProperty represents a Graylog's Collector Configuration Input properties. + CollectorConfigurationInputProperty struct { + Paths string `json:"paths"` + ExcludeFiles string `json:"exclude_files"` + ScanFrequency string `json:"scan_frequency"` + Encoding string `json:"encoding"` + IgnoreOlder string `json:"ignore_older"` + DocumentType string `json:"document_type"` + ExcludeLines string `json:"exclude_lines"` + IncludeLines string `json:"include_lines"` + TailFiles bool `json:"tail_files"` + } + + // CollectorConfigurationOutput represents a Graylog's Collector Configuration Output. + CollectorConfigurationOutput struct { + Backend string `json:"backend"` + Type string `json:"type"` + Name string `json:"name"` + OutputID string `json:"output_id"` + Properties CollectorConfigurationOutputProperty `json:"properties"` + } + + // CollectorConfigurationOutputProperty represents a Graylog's Collector Configuration Output properties. + CollectorConfigurationOutputProperty struct { + Hosts string `json:"hosts"` + } + + // CollectorConfigurationSnippet represents a Graylog's Collector Configuration Snippet. + CollectorConfigurationSnippet struct { + Backend string `json:"backend"` + Name string `json:"name"` + Snippet string `json:"snippet"` + SnippetID string `json:"snippet_id"` + } +) + +// CollectorConfigurationsBody represents Get Collector Configurations API's response body. +// Basically users don't use this struct, but this struct is public because some sub packages use this struct. +type CollectorConfigurationsBody struct { + Configurations []CollectorConfiguration `json:"configurations"` + Total int `json:"total"` +} From 41823223263ccf0cddd8d3335f7ecee880b416a3 Mon Sep 17 00:00:00 2001 From: Suzuki Shunsuke Date: Sat, 8 Sep 2018 22:16:30 +0900 Subject: [PATCH 02/10] feat(mockserver): support collector configuration APIs --- mockserver/handler/collector_configuration.go | 122 ++++++++++++++++++ mockserver/handler/router.go | 17 +++ mockserver/logic/collector_configuration.go | 96 ++++++++++++++ .../store/plain/collector_configuration.go | 77 +++++++++++ mockserver/store/plain/store.go | 84 ++++++------ mockserver/store/store.go | 7 + 6 files changed, 364 insertions(+), 39 deletions(-) create mode 100644 mockserver/handler/collector_configuration.go create mode 100644 mockserver/logic/collector_configuration.go create mode 100644 mockserver/store/plain/collector_configuration.go diff --git a/mockserver/handler/collector_configuration.go b/mockserver/handler/collector_configuration.go new file mode 100644 index 00000000..19b51dde --- /dev/null +++ b/mockserver/handler/collector_configuration.go @@ -0,0 +1,122 @@ +package handler + +import ( + "net/http" + + log "github.com/sirupsen/logrus" + "github.com/suzuki-shunsuke/go-set" + + "github.com/suzuki-shunsuke/go-graylog" + "github.com/suzuki-shunsuke/go-graylog/mockserver/logic" + "github.com/suzuki-shunsuke/go-graylog/util" +) + +// HandleGetCollectorConfiguration is the handler of Get an CollectorConfiguration API. +func HandleGetCollectorConfiguration( + user *graylog.User, lgc *logic.Logic, r *http.Request, ps Params, +) (interface{}, int, error) { + // GET /api/plugins/org.graylog.plugins.collector/configurations/:id + id := ps.PathParam("collectorConfigurationID") + // TODO authorize + return lgc.GetCollectorConfiguration(id) +} + +// HandleGetCollectorConfigurations is the handler of Get Collector Configurations API. +func HandleGetCollectorConfigurations( + user *graylog.User, lgc *logic.Logic, r *http.Request, ps Params, +) (interface{}, int, error) { + // GET /plugins/org.graylog.plugins.collector/configurations List all collector configurations + arr, total, sc, err := lgc.GetCollectorConfigurations() + if err != nil { + return arr, sc, err + } + cfgs := &graylog.CollectorConfigurationsBody{Configurations: arr, Total: total} + return cfgs, sc, nil +} + +// HandleCreateCollectorConfiguration is the handler of Create an CollectorConfiguration API. +func HandleCreateCollectorConfiguration( + user *graylog.User, lgc *logic.Logic, r *http.Request, ps Params, +) (interface{}, int, error) { + // POST /system/inputs Launch input on this node + // TODO authorize + body, sc, err := validateRequestBody( + r.Body, &validateReqBodyPrms{ + Required: set.NewStrSet("tags", "inputs", "outputs", "snippets"), + Optional: nil, + ExtForbidden: true, + }) + if err != nil { + return nil, sc, err + } + cfg := &graylog.CollectorConfiguration{} + if err := util.MSDecode(body, cfg); err != nil { + lgc.Logger().WithFields(log.Fields{ + "body": body, "error": err, + }).Info("Failed to parse request body as CollectorConfiguration") + return nil, 400, err + } + sc, err = lgc.AddCollectorConfiguration(cfg) + if err != nil { + return nil, sc, err + } + if err := lgc.Save(); err != nil { + return nil, 500, err + } + return cfg, sc, nil +} + +// HandleUpdateCollectorConfigurationName is the handler of Update a CollectorConfiguration name API. +func HandleUpdateCollectorConfigurationName( + user *graylog.User, lgc *logic.Logic, r *http.Request, ps Params, +) (interface{}, int, error) { + // PUT /plugins/org.graylog.plugins.collector/configurations/{id}/name Updates a collector configuration name + id := ps.PathParam("collectorConfigurationID") + // TODO authorize + body, sc, err := validateRequestBody( + r.Body, &validateReqBodyPrms{ + Required: set.NewStrSet("tags", "inputs", "outputs", "snippets"), + Optional: nil, + ExtForbidden: true, + }) + if err != nil { + return nil, sc, err + } + prms := &graylog.CollectorConfiguration{} + if err := util.MSDecode(body, prms); err != nil { + lgc.Logger().WithFields(log.Fields{ + "body": body, "error": err, + }).Info("Failed to parse request body as CollectorConfiguration") + return nil, 400, err + } + + lgc.Logger().WithFields(log.Fields{ + "body": body, "input": prms, "id": id, + }).Debug("request body") + + cfg, sc, err := lgc.RenameCollectorConfiguration(id, prms.Name) + if err != nil { + return nil, sc, err + } + if err := lgc.Save(); err != nil { + return nil, 500, err + } + return cfg, sc, nil +} + +// HandleDeleteCollectorConfiguration is the handler of Delete an CollectorConfiguration API. +func HandleDeleteCollectorConfiguration( + user *graylog.User, lgc *logic.Logic, r *http.Request, ps Params, +) (interface{}, int, error) { + // DELETE /plugins/org.graylog.plugins.collector/configurations/{id} Delete a collector configuration + id := ps.PathParam("collectorConfigurationID") + // TODO authorize + sc, err := lgc.DeleteCollectorConfiguration(id) + if err != nil { + return nil, sc, err + } + if err := lgc.Save(); err != nil { + return nil, 500, err + } + return nil, sc, nil +} diff --git a/mockserver/handler/router.go b/mockserver/handler/router.go index 0e8deecf..cf7521d8 100644 --- a/mockserver/handler/router.go +++ b/mockserver/handler/router.go @@ -25,6 +25,23 @@ func NewRouter(lgc *logic.Logic) http.Handler { e.PUT("/api/dashboards/:dashboardID", wrapEchoHandle(lgc, HandleUpdateDashboard)) e.DELETE("/api/dashboards/:dashboardID", wrapEchoHandle(lgc, HandleDeleteDashboard)) + // Collector Configuration + e.GET( + "/api/plugins/org.graylog.plugins.collector/configurations", + wrapEchoHandle(lgc, HandleGetCollectorConfigurations)) + e.POST( + "/api/plugins/org.graylog.plugins.collector/configurations", + wrapEchoHandle(lgc, HandleCreateCollectorConfiguration)) + e.PUT( + "/api/plugins/org.graylog.plugins.collector/configurations/:collectorConfigurationID/name", + wrapEchoHandle(lgc, HandleUpdateCollectorConfigurationName)) + e.GET( + "/api/plugins/org.graylog.plugins.collector/configurations/:collectorConfigurationID", + wrapEchoHandle(lgc, HandleGetCollectorConfiguration)) + e.DELETE( + "/api/plugins/org.graylog.plugins.collector/configurations/:collectorConfigurationID", + wrapEchoHandle(lgc, HandleDeleteCollectorConfiguration)) + // Role e.GET("/api/roles/:rolename", wrapEchoHandle(lgc, HandleGetRole)) e.GET("/api/roles", wrapEchoHandle(lgc, HandleGetRoles)) diff --git a/mockserver/logic/collector_configuration.go b/mockserver/logic/collector_configuration.go new file mode 100644 index 00000000..c8d744b6 --- /dev/null +++ b/mockserver/logic/collector_configuration.go @@ -0,0 +1,96 @@ +package logic + +import ( + "fmt" + + log "github.com/sirupsen/logrus" + + "github.com/suzuki-shunsuke/go-graylog" + "github.com/suzuki-shunsuke/go-graylog/validator" +) + +// HasCollectorConfiguration returns whether the collector configuration exists. +func (lgc *Logic) HasCollectorConfiguration(id string) (bool, error) { + return lgc.store.HasCollectorConfiguration(id) +} + +// GetCollectorConfiguration returns a collector configuration. +// If a collector configuration is not found, returns an error. +func (lgc *Logic) GetCollectorConfiguration(id string) (*graylog.CollectorConfiguration, int, error) { + if id == "" { + return nil, 400, fmt.Errorf("collector configuration id is required") + } + if err := ValidateObjectID(id); err != nil { + // unfortunately graylog returns not 400 but 404. + return nil, 404, err + } + cfg, err := lgc.store.GetCollectorConfiguration(id) + if err != nil { + return cfg, 500, err + } + if cfg == nil { + return nil, 404, fmt.Errorf("no collector configuration with id <%s> is found", id) + } + return cfg, 200, nil +} + +// AddCollectorConfiguration adds a collector configuration to the mock server. +func (lgc *Logic) AddCollectorConfiguration(cfg *graylog.CollectorConfiguration) (int, error) { + if err := validator.CreateValidator.Struct(cfg); err != nil { + return 400, err + } + if err := lgc.store.AddCollectorConfiguration(cfg); err != nil { + return 500, err + } + return 200, nil +} + +// RenameCollectorConfiguration renames a collector configuration. +func (lgc *Logic) RenameCollectorConfiguration(id, name string) (*graylog.CollectorConfiguration, int, error) { + if id == "" { + return nil, 400, fmt.Errorf("id is required") + } + if name == "" { + return nil, 400, fmt.Errorf("name is required") + } + ok, err := lgc.HasCollectorConfiguration(id) + if err != nil { + return nil, 500, err + } + if !ok { + return nil, 404, fmt.Errorf("the collector configuration <%s> is not found", id) + } + + cfg, err := lgc.store.RenameCollectorConfiguration(id, name) + if err != nil { + return nil, 500, err + } + return cfg, 200, nil +} + +// DeleteCollectorConfiguration deletes a collector configuration from the mock server. +func (lgc *Logic) DeleteCollectorConfiguration(id string) (int, error) { + ok, err := lgc.HasCollectorConfiguration(id) + if err != nil { + lgc.Logger().WithFields(log.Fields{ + "error": err, "id": id, + }).Error("lgc.HasCollectorConfiguration() is failure") + return 500, err + } + if !ok { + return 404, fmt.Errorf("the collector configuration <%s> is not found", id) + } + if err := lgc.store.DeleteCollectorConfiguration(id); err != nil { + return 500, err + } + return 200, nil +} + +// GetCollectorConfigurations returns a list of collector configurations. +func (lgc *Logic) GetCollectorConfigurations() ([]graylog.CollectorConfiguration, int, int, error) { + cfgs, total, err := lgc.store.GetCollectorConfigurations() + if err != nil { + return nil, 0, 500, err + } + return cfgs, total, 200, nil +} diff --git a/mockserver/store/plain/collector_configuration.go b/mockserver/store/plain/collector_configuration.go new file mode 100644 index 00000000..5f030bf2 --- /dev/null +++ b/mockserver/store/plain/collector_configuration.go @@ -0,0 +1,77 @@ +package plain + +import ( + "fmt" + + "github.com/suzuki-shunsuke/go-graylog" + st "github.com/suzuki-shunsuke/go-graylog/mockserver/store" +) + +// HasCollectorConfiguration returns whether the collector configuration exists. +func (store *Store) HasCollectorConfiguration(id string) (bool, error) { + store.imutex.RLock() + defer store.imutex.RUnlock() + _, ok := store.collectorConfigurations[id] + return ok, nil +} + +// GetCollectorConfiguration returns an input. +func (store *Store) GetCollectorConfiguration(id string) (*graylog.CollectorConfiguration, error) { + store.imutex.RLock() + defer store.imutex.RUnlock() + s, ok := store.collectorConfigurations[id] + if ok { + return &s, nil + } + return nil, nil +} + +// AddCollectorConfiguration adds a collector configuration to the store. +func (store *Store) AddCollectorConfiguration(cfg *graylog.CollectorConfiguration) error { + if cfg == nil { + return fmt.Errorf("collector configuration is nil") + } + if cfg.ID == "" { + cfg.ID = st.NewObjectID() + } + + store.imutex.Lock() + defer store.imutex.Unlock() + store.collectorConfigurations[cfg.ID] = *cfg + return nil +} + +// RenameCollectorConfiguration renames a collector configuration. +func (store *Store) RenameCollectorConfiguration(id, name string) (*graylog.CollectorConfiguration, error) { + store.imutex.Lock() + defer store.imutex.Unlock() + cfg, ok := store.collectorConfigurations[id] + if !ok { + return nil, fmt.Errorf("the collector configuration <%s> is not found", id) + } + cfg.Name = name + store.collectorConfigurations[id] = cfg + return &cfg, nil +} + +// DeleteCollectorConfiguration deletes a collector configuration from the store. +func (store *Store) DeleteCollectorConfiguration(id string) error { + store.imutex.Lock() + defer store.imutex.Unlock() + delete(store.collectorConfigurations, id) + return nil +} + +// GetCollectorConfigurations returns all collector configurations. +func (store *Store) GetCollectorConfigurations() ([]graylog.CollectorConfiguration, int, error) { + store.imutex.RLock() + defer store.imutex.RUnlock() + size := len(store.collectorConfigurations) + arr := make([]graylog.CollectorConfiguration, size) + i := 0 + for _, cfg := range store.collectorConfigurations { + arr[i] = cfg + i++ + } + return arr, size, nil +} diff --git a/mockserver/store/plain/store.go b/mockserver/store/plain/store.go index 53c7c57b..134a145e 100644 --- a/mockserver/store/plain/store.go +++ b/mockserver/store/plain/store.go @@ -13,16 +13,17 @@ import ( // Store is the implementation of the Store interface with pure golang. type Store struct { - alarmCallbacks map[string]graylog.AlarmCallback - alerts map[string]graylog.Alert - alertConditions map[string]graylog.AlertCondition - dashboards map[string]graylog.Dashboard - indexSets []graylog.IndexSet - inputs map[string]graylog.Input - roles map[string]graylog.Role - streams map[string]graylog.Stream - users map[string]graylog.User - ldapSetting *graylog.LDAPSetting + alarmCallbacks map[string]graylog.AlarmCallback + alerts map[string]graylog.Alert + alertConditions map[string]graylog.AlertCondition + collectorConfigurations map[string]graylog.CollectorConfiguration + dashboards map[string]graylog.Dashboard + indexSets []graylog.IndexSet + inputs map[string]graylog.Input + roles map[string]graylog.Role + streams map[string]graylog.Stream + users map[string]graylog.User + ldapSetting *graylog.LDAPSetting tokens map[string]string @@ -33,16 +34,17 @@ type Store struct { } type plainStore struct { - AlarmCallbacks map[string]graylog.AlarmCallback `json:"alarm_callbacks"` - Alerts map[string]graylog.Alert `json:"alerts"` - AlertConditions map[string]graylog.AlertCondition `json:"alert_conditions"` - Dashboards map[string]graylog.Dashboard `json:"dashboards"` - Inputs map[string]graylog.Input `json:"inputs"` - IndexSets []graylog.IndexSet `json:"index_sets"` - Roles map[string]graylog.Role `json:"roles"` - Streams map[string]graylog.Stream `json:"streams"` - Users map[string]graylog.User `json:"users"` - LDAPSetting *graylog.LDAPSetting `json:"ldap_setting"` + AlarmCallbacks map[string]graylog.AlarmCallback `json:"alarm_callbacks"` + Alerts map[string]graylog.Alert `json:"alerts"` + AlertConditions map[string]graylog.AlertCondition `json:"alert_conditions"` + CollectorConfigurations map[string]graylog.CollectorConfiguration `json:"collector_configurations"` + Dashboards map[string]graylog.Dashboard `json:"dashboards"` + Inputs map[string]graylog.Input `json:"inputs"` + IndexSets []graylog.IndexSet `json:"index_sets"` + Roles map[string]graylog.Role `json:"roles"` + Streams map[string]graylog.Stream `json:"streams"` + Users map[string]graylog.User `json:"users"` + LDAPSetting *graylog.LDAPSetting `json:"ldap_setting"` Tokens map[string]string `json:"tokens"` @@ -52,15 +54,17 @@ type plainStore struct { // MarshalJSON is the implementation of the json.Marshaler interface. func (store *Store) MarshalJSON() ([]byte, error) { data := map[string]interface{}{ - "alarm_callbacks": store.alarmCallbacks, - "alerts": store.alerts, - "alert_conditions": store.alertConditions, - "inputs": store.inputs, - "index_sets": store.indexSets, - "roles": store.roles, - "streams": store.streams, - "users": store.users, - "ldap_setting": store.ldapSetting, + "alarm_callbacks": store.alarmCallbacks, + "alerts": store.alerts, + "alert_conditions": store.alertConditions, + "collector_configurations": store.collectorConfigurations, + "dashboards": store.dashboards, + "inputs": store.inputs, + "index_sets": store.indexSets, + "roles": store.roles, + "streams": store.streams, + "users": store.users, + "ldap_setting": store.ldapSetting, "tokens": store.tokens, @@ -78,6 +82,7 @@ func (store *Store) UnmarshalJSON(b []byte) error { store.alarmCallbacks = s.AlarmCallbacks store.alerts = s.Alerts store.alertConditions = s.AlertConditions + store.collectorConfigurations = s.CollectorConfigurations store.dashboards = s.Dashboards store.inputs = s.Inputs store.indexSets = s.IndexSets @@ -97,16 +102,17 @@ func (store *Store) UnmarshalJSON(b []byte) error { // If `dataPath` is empty, the data aren't written to the file. func NewStore(dataPath string) store.Store { return &Store{ - alarmCallbacks: map[string]graylog.AlarmCallback{}, - alerts: map[string]graylog.Alert{}, - alertConditions: map[string]graylog.AlertCondition{}, - dashboards: map[string]graylog.Dashboard{}, - inputs: map[string]graylog.Input{}, - indexSets: []graylog.IndexSet{}, - roles: map[string]graylog.Role{}, - streams: map[string]graylog.Stream{}, - users: map[string]graylog.User{}, - ldapSetting: defaultLDAPSetting(), + alarmCallbacks: map[string]graylog.AlarmCallback{}, + alerts: map[string]graylog.Alert{}, + alertConditions: map[string]graylog.AlertCondition{}, + collectorConfigurations: map[string]graylog.CollectorConfiguration{}, + dashboards: map[string]graylog.Dashboard{}, + inputs: map[string]graylog.Input{}, + indexSets: []graylog.IndexSet{}, + roles: map[string]graylog.Role{}, + streams: map[string]graylog.Stream{}, + users: map[string]graylog.User{}, + ldapSetting: defaultLDAPSetting(), tokens: map[string]string{}, diff --git a/mockserver/store/store.go b/mockserver/store/store.go index 2eaf8d18..fc039bdd 100644 --- a/mockserver/store/store.go +++ b/mockserver/store/store.go @@ -35,6 +35,13 @@ type Store interface { DeleteInput(id string) error HasInput(id string) (bool, error) + HasCollectorConfiguration(id string) (bool, error) + DeleteCollectorConfiguration(id string) error + AddCollectorConfiguration(*graylog.CollectorConfiguration) error + RenameCollectorConfiguration(id, name string) (*graylog.CollectorConfiguration, error) + GetCollectorConfiguration(id string) (*graylog.CollectorConfiguration, error) + GetCollectorConfigurations() ([]graylog.CollectorConfiguration, int, error) + AddIndexSet(*graylog.IndexSet) error GetIndexSet(id string) (*graylog.IndexSet, error) GetIndexSets(skip, limit int) ([]graylog.IndexSet, int, error) From cfded83d634965defe16c472ad02e0e6bc03f8c1 Mon Sep 17 00:00:00 2001 From: Suzuki Shunsuke Date: Sun, 9 Sep 2018 11:06:50 +0900 Subject: [PATCH 03/10] feat(mockserver): support collector configuration input APIs --- client/collector_configuration_input.go | 92 ++++++++++++++++ collector_configuration.go | 10 +- .../handler/collector_configuration_input.go | 104 ++++++++++++++++++ mockserver/handler/router.go | 11 ++ .../logic/collector_configuration_input.go | 77 +++++++++++++ .../plain/collector_configuration_input.go | 99 +++++++++++++++++ mockserver/store/store.go | 5 + 7 files changed, 397 insertions(+), 1 deletion(-) create mode 100644 client/collector_configuration_input.go create mode 100644 mockserver/handler/collector_configuration_input.go create mode 100644 mockserver/logic/collector_configuration_input.go create mode 100644 mockserver/store/plain/collector_configuration_input.go diff --git a/client/collector_configuration_input.go b/client/collector_configuration_input.go new file mode 100644 index 00000000..be1a0123 --- /dev/null +++ b/client/collector_configuration_input.go @@ -0,0 +1,92 @@ +package client + +import ( + "context" + "fmt" + + // "github.com/pkg/errors" + + "github.com/suzuki-shunsuke/go-graylog" +) + +// CreateCollectorConfigurationInput creates a collector configuration input. +func (client *Client) CreateCollectorConfigurationInput( + id string, input *graylog.CollectorConfigurationInput, +) (*ErrorInfo, error) { + return client.CreateCollectorConfigurationInputContext( + context.Background(), id, input) +} + +// CreateCollectorConfigurationInputContext creates a collector configuration input with a context. +func (client *Client) CreateCollectorConfigurationInputContext( + ctx context.Context, id string, input *graylog.CollectorConfigurationInput, +) (*ErrorInfo, error) { + // POST /plugins/org.graylog.plugins.collector/configurations/{id}/inputs Create a configuration input + if id == "" { + return nil, fmt.Errorf("id is required") + } + if input == nil { + return nil, fmt.Errorf("collector configuration is nil") + } + u, err := client.Endpoints().CollectorConfigurationInputs(id) + if err != nil { + return nil, err + } + return client.callPost(ctx, u.String(), input, nil) +} + +// DeleteCollectorConfigurationInput deletes a collector configuration input. +func (client *Client) DeleteCollectorConfigurationInput(id, inputID string) (*ErrorInfo, error) { + return client.DeleteCollectorConfigurationInputContext( + context.Background(), id, inputID) +} + +// DeleteCollectorConfigurationInputContext deletes a collector configuration input with a context. +func (client *Client) DeleteCollectorConfigurationInputContext( + ctx context.Context, id, inputID string, +) (*ErrorInfo, error) { + // DELETE /plugins/org.graylog.plugins.collector/configurations/{id}/inputs/{inputId} Delete input form configuration + if id == "" { + return nil, fmt.Errorf("id is required") + } + if inputID == "" { + return nil, fmt.Errorf("input id is required") + } + u, err := client.Endpoints().CollectorConfigurationInput(id, inputID) + if err != nil { + return nil, err + } + return client.callDelete( + ctx, u.String(), nil, nil) +} + +// UpdateCollectorConfigurationInput updates a collector configuration input. +func (client *Client) UpdateCollectorConfigurationInput( + id, inputID string, input *graylog.CollectorConfigurationInput, +) (*ErrorInfo, error) { + return client.UpdateCollectorConfigurationInputContext( + context.Background(), id, inputID, input) +} + +// UpdateCollectorConfigurationInputContext updates a collector configuration input with a context. +func (client *Client) UpdateCollectorConfigurationInputContext( + ctx context.Context, id, inputID string, + input *graylog.CollectorConfigurationInput, +) (*ErrorInfo, error) { + // PUT /plugins/org.graylog.plugins.collector/configurations/{id}/inputs/{input_id} Update a configuration input + if id == "" { + return nil, fmt.Errorf("id is required") + } + if inputID == "" { + return nil, fmt.Errorf("input id is required") + } + if input == nil { + return nil, fmt.Errorf("input is nil") + } + u, err := client.Endpoints().CollectorConfigurationInput(id, inputID) + if err != nil { + return nil, err + } + return client.callPut( + ctx, u.String(), input, nil) +} diff --git a/collector_configuration.go b/collector_configuration.go index e4174b26..0dd933fa 100644 --- a/collector_configuration.go +++ b/collector_configuration.go @@ -22,7 +22,10 @@ type ( } // CollectorConfigurationInputProperty represents a Graylog's Collector Configuration Input properties. - CollectorConfigurationInputProperty struct { + CollectorConfigurationInputProperty interface{} + + // CollectorConfigurationInputFileProperty represents a Graylog's Collector Configuration file type Input properties. + CollectorConfigurationInputFileProperty struct { Paths string `json:"paths"` ExcludeFiles string `json:"exclude_files"` ScanFrequency string `json:"scan_frequency"` @@ -34,6 +37,11 @@ type ( TailFiles bool `json:"tail_files"` } + // CollectorConfigurationInputWindowsEventLogProperty represents a Graylog's Collector Configuration windows event log type Input properties. + CollectorConfigurationInputWindowsEventLogProperty struct { + Event string `json:"event"` + } + // CollectorConfigurationOutput represents a Graylog's Collector Configuration Output. CollectorConfigurationOutput struct { Backend string `json:"backend"` diff --git a/mockserver/handler/collector_configuration_input.go b/mockserver/handler/collector_configuration_input.go new file mode 100644 index 00000000..a94ceb9a --- /dev/null +++ b/mockserver/handler/collector_configuration_input.go @@ -0,0 +1,104 @@ +package handler + +import ( + "net/http" + + log "github.com/sirupsen/logrus" + "github.com/suzuki-shunsuke/go-set" + + "github.com/suzuki-shunsuke/go-graylog" + "github.com/suzuki-shunsuke/go-graylog/mockserver/logic" + "github.com/suzuki-shunsuke/go-graylog/util" +) + +// HandleCreateCollectorConfigurationInput is the handler of Create a CollectorConfiguration Input API. +func HandleCreateCollectorConfigurationInput( + user *graylog.User, lgc *logic.Logic, r *http.Request, ps Params, +) (interface{}, int, error) { + // TODO authorize + cfgID := ps.PathParam("collectorConfigurationInputID") + body, sc, err := validateRequestBody( + r.Body, &validateReqBodyPrms{ + Required: set.NewStrSet("tags", "inputs", "outputs", "snippets"), + Optional: nil, + ExtForbidden: true, + }) + if err != nil { + return nil, sc, err + } + input := &graylog.CollectorConfigurationInput{} + if err := util.MSDecode(body, input); err != nil { + lgc.Logger().WithFields(log.Fields{ + "body": body, "error": err, + }).Info("Failed to parse request body as CollectorConfigurationInput") + return nil, 400, err + } + sc, err = lgc.AddCollectorConfigurationInput(cfgID, input) + if err != nil { + return nil, sc, err + } + if err := lgc.Save(); err != nil { + return nil, 500, err + } + // 202 no content + return nil, sc, nil +} + +// HandleUpdateCollectorConfigurationInput is the handler of Update a CollectorConfiguration Input API. +func HandleUpdateCollectorConfigurationInput( + user *graylog.User, lgc *logic.Logic, r *http.Request, ps Params, +) (interface{}, int, error) { + cfgID := ps.PathParam("collectorConfigurationID") + inputID := ps.PathParam("collectorConfigurationInputID") + // TODO authorize + body, sc, err := validateRequestBody( + r.Body, &validateReqBodyPrms{ + Required: set.NewStrSet("backend", "type", "name", "forward_to"), + Optional: set.NewStrSet("properties"), + Ignored: set.NewStrSet("input_id"), + ExtForbidden: true, + }) + if err != nil { + return nil, sc, err + } + input := &graylog.CollectorConfigurationInput{} + if err := util.MSDecode(body, input); err != nil { + lgc.Logger().WithFields(log.Fields{ + "body": body, "error": err, + }).Info("Failed to parse request body as CollectorConfiguration Input") + return nil, 400, err + } + + lgc.Logger().WithFields(log.Fields{ + "body": body, "input": input, + "collector_configuration_id": cfgID, + "collector_configuration_input_id": inputID, + }).Debug("request body") + + sc, err = lgc.UpdateCollectorConfigurationInput(cfgID, inputID, input) + if err != nil { + return nil, sc, err + } + if err := lgc.Save(); err != nil { + return nil, 500, err + } + // 202 no content + return nil, sc, nil +} + +// HandleDeleteCollectorConfigurationInput is the handler of Delete an CollectorConfiguration Input API. +func HandleDeleteCollectorConfigurationInput( + user *graylog.User, lgc *logic.Logic, r *http.Request, ps Params, +) (interface{}, int, error) { + id := ps.PathParam("collectorConfigurationID") + inputID := ps.PathParam("collectorConfigurationInputID") + // TODO authorize + sc, err := lgc.DeleteCollectorConfigurationInput(id, inputID) + if err != nil { + return nil, sc, err + } + if err := lgc.Save(); err != nil { + return nil, 500, err + } + return nil, sc, nil +} diff --git a/mockserver/handler/router.go b/mockserver/handler/router.go index cf7521d8..6b7f9a5a 100644 --- a/mockserver/handler/router.go +++ b/mockserver/handler/router.go @@ -42,6 +42,17 @@ func NewRouter(lgc *logic.Logic) http.Handler { "/api/plugins/org.graylog.plugins.collector/configurations/:collectorConfigurationID", wrapEchoHandle(lgc, HandleDeleteCollectorConfiguration)) + // Collector Configuration Input + e.POST( + "/api/plugins/org.graylog.plugins.collector/configurations/:collectorConfigurationID/inputs", + wrapEchoHandle(lgc, HandleCreateCollectorConfigurationInput)) + e.PUT( + "/api/plugins/org.graylog.plugins.collector/configurations/:collectorConfigurationID/inputs/:collectorConfigurationInputID", + wrapEchoHandle(lgc, HandleUpdateCollectorConfigurationInput)) + e.DELETE( + "/api/plugins/org.graylog.plugins.collector/configurations/:collectorConfigurationID/inputs/:collectorConfigurationInputID", + wrapEchoHandle(lgc, HandleDeleteCollectorConfigurationInput)) + // Role e.GET("/api/roles/:rolename", wrapEchoHandle(lgc, HandleGetRole)) e.GET("/api/roles", wrapEchoHandle(lgc, HandleGetRoles)) diff --git a/mockserver/logic/collector_configuration_input.go b/mockserver/logic/collector_configuration_input.go new file mode 100644 index 00000000..9e9c49ff --- /dev/null +++ b/mockserver/logic/collector_configuration_input.go @@ -0,0 +1,77 @@ +package logic + +import ( + "fmt" + + log "github.com/sirupsen/logrus" + + "github.com/suzuki-shunsuke/go-graylog" + "github.com/suzuki-shunsuke/go-graylog/validator" +) + +// HasCollectorConfigurationInput returns whether the collector configuration exists. +func (lgc *Logic) HasCollectorConfigurationInput(cfgID, inputID string) (bool, error) { + return lgc.store.HasCollectorConfigurationInput(cfgID, inputID) +} + +// AddCollectorConfigurationInput adds a collector configuration input to the mock server. +func (lgc *Logic) AddCollectorConfigurationInput(id string, input *graylog.CollectorConfigurationInput) (int, error) { + if id == "" { + return 400, fmt.Errorf("id is required") + } + if err := validator.CreateValidator.Struct(input); err != nil { + return 400, err + } + if err := lgc.store.AddCollectorConfigurationInput(id, input); err != nil { + return 500, err + } + // 202 no content + return 202, nil +} + +// UpdateCollectorConfigurationInput updates a collector configuration input. +func (lgc *Logic) UpdateCollectorConfigurationInput( + cfgID, inputID string, input *graylog.CollectorConfigurationInput, +) (int, error) { + if cfgID == "" { + return 400, fmt.Errorf("collector configuration id is required") + } + if inputID == "" { + return 400, fmt.Errorf("collector configuration input id is required") + } + if err := validator.UpdateValidator.Struct(input); err != nil { + return 400, err + } + ok, err := lgc.HasCollectorConfigurationInput(cfgID, inputID) + if err != nil { + return 500, err + } + if !ok { + return 404, fmt.Errorf("the collector configuration input is not found") + } + + if err := lgc.store.UpdateCollectorConfigurationInput(cfgID, inputID, input); err != nil { + return 500, err + } + // 202 no content + return 200, nil +} + +// DeleteCollectorConfigurationInput deletes a collector configuration input from the mock server. +func (lgc *Logic) DeleteCollectorConfigurationInput(cfgID, inputID string) (int, error) { + ok, err := lgc.HasCollectorConfigurationInput(cfgID, inputID) + if err != nil { + lgc.Logger().WithFields(log.Fields{ + "error": err, "configuration_id": cfgID, + "input_id": inputID, + }).Error("failed to check whether the collector configuration exists") + return 500, err + } + if !ok { + return 404, fmt.Errorf("the collector configuration input is not found") + } + if err := lgc.store.DeleteCollectorConfigurationInput(cfgID, inputID); err != nil { + return 500, err + } + return 204, nil +} diff --git a/mockserver/store/plain/collector_configuration_input.go b/mockserver/store/plain/collector_configuration_input.go new file mode 100644 index 00000000..4266b53f --- /dev/null +++ b/mockserver/store/plain/collector_configuration_input.go @@ -0,0 +1,99 @@ +package plain + +import ( + "fmt" + + "github.com/suzuki-shunsuke/go-graylog" + st "github.com/suzuki-shunsuke/go-graylog/mockserver/store" +) + +// HasCollectorConfigurationInput returns whether the collector configuration input exists. +func (store *Store) HasCollectorConfigurationInput(cfgID, inputID string) (bool, error) { + store.imutex.RLock() + defer store.imutex.RUnlock() + cfg, ok := store.collectorConfigurations[cfgID] + if !ok { + return false, nil + } + for _, input := range cfg.Inputs { + if input.InputID == inputID { + return true, nil + } + } + return false, nil +} + +// AddCollectorConfigurationInput adds a collector configuration input to the store. +func (store *Store) AddCollectorConfigurationInput(cfgID string, input *graylog.CollectorConfigurationInput) error { + if cfgID == "" { + return fmt.Errorf("id is required") + } + if input == nil { + return fmt.Errorf("collector configuration input is nil") + } + if input.InputID == "" { + input.InputID = st.NewObjectID() + } + + store.imutex.Lock() + defer store.imutex.Unlock() + cfg, ok := store.collectorConfigurations[cfgID] + if !ok { + return fmt.Errorf("collector configuration <%s> is not found", cfgID) + } + cfg.Inputs = append(cfg.Inputs, *input) + store.collectorConfigurations[cfgID] = cfg + return nil +} + +// UpdateCollectorConfigurationInput updates a collector configuration input. +func (store *Store) UpdateCollectorConfigurationInput(cfgID, inputID string, input *graylog.CollectorConfigurationInput) error { + if cfgID == "" { + return fmt.Errorf("collector configuration id is required") + } + if inputID == "" { + return fmt.Errorf("collector configuration input id is required") + } + if input == nil { + return fmt.Errorf("collector configuration input is nil") + } + store.imutex.Lock() + defer store.imutex.Unlock() + cfg, ok := store.collectorConfigurations[cfgID] + if !ok { + return fmt.Errorf("the collector configuration <%s> is not found", cfgID) + } + for i, a := range cfg.Inputs { + if a.InputID == inputID { + cfg.Inputs[i] = *input + store.collectorConfigurations[cfgID] = cfg + return nil + } + } + return fmt.Errorf("collector configuration input is not found") +} + +// DeleteCollectorConfigurationInput deletes a collector configuration input from the store. +func (store *Store) DeleteCollectorConfigurationInput(cfgID, inputID string) error { + store.imutex.Lock() + defer store.imutex.Unlock() + cfg, ok := store.collectorConfigurations[cfgID] + if !ok { + return fmt.Errorf("the collector configuration <%s> is not found", cfgID) + } + inputs := []graylog.CollectorConfigurationInput{} + removed := false + for _, a := range cfg.Inputs { + if a.InputID == inputID { + removed = true + continue + } + inputs = append(inputs, a) + } + if !removed { + return fmt.Errorf("the collector configuration input is not found") + } + cfg.Inputs = inputs + store.collectorConfigurations[cfgID] = cfg + return nil +} diff --git a/mockserver/store/store.go b/mockserver/store/store.go index fc039bdd..f4f84424 100644 --- a/mockserver/store/store.go +++ b/mockserver/store/store.go @@ -42,6 +42,11 @@ type Store interface { GetCollectorConfiguration(id string) (*graylog.CollectorConfiguration, error) GetCollectorConfigurations() ([]graylog.CollectorConfiguration, int, error) + HasCollectorConfigurationInput(cfgID, inputID string) (bool, error) + AddCollectorConfigurationInput(cfgID string, input *graylog.CollectorConfigurationInput) error + UpdateCollectorConfigurationInput(cfgID, inputID string, input *graylog.CollectorConfigurationInput) error + DeleteCollectorConfigurationInput(cfgID, inputID string) error + AddIndexSet(*graylog.IndexSet) error GetIndexSet(id string) (*graylog.IndexSet, error) GetIndexSets(skip, limit int) ([]graylog.IndexSet, int, error) From be0d6228c2a702e21cce949f2111d49acdf82807 Mon Sep 17 00:00:00 2001 From: Suzuki Shunsuke Date: Sun, 9 Sep 2018 11:31:09 +0900 Subject: [PATCH 04/10] feat(mockserver): support collector configuration output APIs --- client/collector_configuration_output.go | 92 ++++++++++++++++ .../handler/collector_configuration_output.go | 104 ++++++++++++++++++ mockserver/handler/router.go | 11 ++ .../logic/collector_configuration_output.go | 77 +++++++++++++ .../plain/collector_configuration_output.go | 99 +++++++++++++++++ mockserver/store/store.go | 5 + 6 files changed, 388 insertions(+) create mode 100644 client/collector_configuration_output.go create mode 100644 mockserver/handler/collector_configuration_output.go create mode 100644 mockserver/logic/collector_configuration_output.go create mode 100644 mockserver/store/plain/collector_configuration_output.go diff --git a/client/collector_configuration_output.go b/client/collector_configuration_output.go new file mode 100644 index 00000000..de7b07ea --- /dev/null +++ b/client/collector_configuration_output.go @@ -0,0 +1,92 @@ +package client + +import ( + "context" + "fmt" + + // "github.com/pkg/errors" + + "github.com/suzuki-shunsuke/go-graylog" +) + +// CreateCollectorConfigurationOutput creates a collector configuration output. +func (client *Client) CreateCollectorConfigurationOutput( + id string, output *graylog.CollectorConfigurationOutput, +) (*ErrorInfo, error) { + return client.CreateCollectorConfigurationOutputContext( + context.Background(), id, output) +} + +// CreateCollectorConfigurationOutputContext creates a collector configuration output with a context. +func (client *Client) CreateCollectorConfigurationOutputContext( + ctx context.Context, id string, output *graylog.CollectorConfigurationOutput, +) (*ErrorInfo, error) { + // POST /plugins/org.graylog.plugins.collector/configurations/{id}/outputs Create a configuration output + if id == "" { + return nil, fmt.Errorf("id is required") + } + if output == nil { + return nil, fmt.Errorf("collector configuration is nil") + } + u, err := client.Endpoints().CollectorConfigurationOutputs(id) + if err != nil { + return nil, err + } + return client.callPost(ctx, u.String(), output, nil) +} + +// DeleteCollectorConfigurationOutput deletes a collector configuration output. +func (client *Client) DeleteCollectorConfigurationOutput(id, outputID string) (*ErrorInfo, error) { + return client.DeleteCollectorConfigurationOutputContext( + context.Background(), id, outputID) +} + +// DeleteCollectorConfigurationOutputContext deletes a collector configuration output with a context. +func (client *Client) DeleteCollectorConfigurationOutputContext( + ctx context.Context, id, outputID string, +) (*ErrorInfo, error) { + // DELETE /plugins/org.graylog.plugins.collector/configurations/{id}/outputs/{outputId} Delete output form configuration + if id == "" { + return nil, fmt.Errorf("id is required") + } + if outputID == "" { + return nil, fmt.Errorf("output id is required") + } + u, err := client.Endpoints().CollectorConfigurationOutput(id, outputID) + if err != nil { + return nil, err + } + return client.callDelete( + ctx, u.String(), nil, nil) +} + +// UpdateCollectorConfigurationOutput updates a collector configuration output. +func (client *Client) UpdateCollectorConfigurationOutput( + id, outputID string, output *graylog.CollectorConfigurationOutput, +) (*ErrorInfo, error) { + return client.UpdateCollectorConfigurationOutputContext( + context.Background(), id, outputID, output) +} + +// UpdateCollectorConfigurationOutputContext updates a collector configuration output with a context. +func (client *Client) UpdateCollectorConfigurationOutputContext( + ctx context.Context, id, outputID string, + output *graylog.CollectorConfigurationOutput, +) (*ErrorInfo, error) { + // PUT /plugins/org.graylog.plugins.collector/configurations/{id}/outputs/{output_id} Update a configuration output + if id == "" { + return nil, fmt.Errorf("id is required") + } + if outputID == "" { + return nil, fmt.Errorf("output id is required") + } + if output == nil { + return nil, fmt.Errorf("output is nil") + } + u, err := client.Endpoints().CollectorConfigurationOutput(id, outputID) + if err != nil { + return nil, err + } + return client.callPut( + ctx, u.String(), output, nil) +} diff --git a/mockserver/handler/collector_configuration_output.go b/mockserver/handler/collector_configuration_output.go new file mode 100644 index 00000000..27d241ad --- /dev/null +++ b/mockserver/handler/collector_configuration_output.go @@ -0,0 +1,104 @@ +package handler + +import ( + "net/http" + + log "github.com/sirupsen/logrus" + "github.com/suzuki-shunsuke/go-set" + + "github.com/suzuki-shunsuke/go-graylog" + "github.com/suzuki-shunsuke/go-graylog/mockserver/logic" + "github.com/suzuki-shunsuke/go-graylog/util" +) + +// HandleCreateCollectorConfigurationOutput is the handler of Create a CollectorConfiguration Output API. +func HandleCreateCollectorConfigurationOutput( + user *graylog.User, lgc *logic.Logic, r *http.Request, ps Params, +) (interface{}, int, error) { + // TODO authorize + cfgID := ps.PathParam("collectorConfigurationOutputID") + body, sc, err := validateRequestBody( + r.Body, &validateReqBodyPrms{ + Required: set.NewStrSet("tags", "outputs", "outputs", "snippets"), + Optional: nil, + ExtForbidden: true, + }) + if err != nil { + return nil, sc, err + } + output := &graylog.CollectorConfigurationOutput{} + if err := util.MSDecode(body, output); err != nil { + lgc.Logger().WithFields(log.Fields{ + "body": body, "error": err, + }).Info("Failed to parse request body as CollectorConfigurationOutput") + return nil, 400, err + } + sc, err = lgc.AddCollectorConfigurationOutput(cfgID, output) + if err != nil { + return nil, sc, err + } + if err := lgc.Save(); err != nil { + return nil, 500, err + } + // 202 no content + return nil, sc, nil +} + +// HandleUpdateCollectorConfigurationOutput is the handler of Update a CollectorConfiguration Output API. +func HandleUpdateCollectorConfigurationOutput( + user *graylog.User, lgc *logic.Logic, r *http.Request, ps Params, +) (interface{}, int, error) { + cfgID := ps.PathParam("collectorConfigurationID") + outputID := ps.PathParam("collectorConfigurationOutputID") + // TODO authorize + body, sc, err := validateRequestBody( + r.Body, &validateReqBodyPrms{ + Required: set.NewStrSet("backend", "type", "name"), + Optional: set.NewStrSet("properties"), + Ignored: set.NewStrSet("output_id"), + ExtForbidden: true, + }) + if err != nil { + return nil, sc, err + } + output := &graylog.CollectorConfigurationOutput{} + if err := util.MSDecode(body, output); err != nil { + lgc.Logger().WithFields(log.Fields{ + "body": body, "error": err, + }).Info("Failed to parse request body as CollectorConfiguration Output") + return nil, 400, err + } + + lgc.Logger().WithFields(log.Fields{ + "body": body, "output": output, + "collector_configuration_id": cfgID, + "collector_configuration_output_id": outputID, + }).Debug("request body") + + sc, err = lgc.UpdateCollectorConfigurationOutput(cfgID, outputID, output) + if err != nil { + return nil, sc, err + } + if err := lgc.Save(); err != nil { + return nil, 500, err + } + // 202 no content + return nil, sc, nil +} + +// HandleDeleteCollectorConfigurationOutput is the handler of Delete an CollectorConfiguration Output API. +func HandleDeleteCollectorConfigurationOutput( + user *graylog.User, lgc *logic.Logic, r *http.Request, ps Params, +) (interface{}, int, error) { + id := ps.PathParam("collectorConfigurationID") + outputID := ps.PathParam("collectorConfigurationOutputID") + // TODO authorize + sc, err := lgc.DeleteCollectorConfigurationOutput(id, outputID) + if err != nil { + return nil, sc, err + } + if err := lgc.Save(); err != nil { + return nil, 500, err + } + return nil, sc, nil +} diff --git a/mockserver/handler/router.go b/mockserver/handler/router.go index 6b7f9a5a..8ec1621f 100644 --- a/mockserver/handler/router.go +++ b/mockserver/handler/router.go @@ -53,6 +53,17 @@ func NewRouter(lgc *logic.Logic) http.Handler { "/api/plugins/org.graylog.plugins.collector/configurations/:collectorConfigurationID/inputs/:collectorConfigurationInputID", wrapEchoHandle(lgc, HandleDeleteCollectorConfigurationInput)) + // Collector Configuration Output + e.POST( + "/api/plugins/org.graylog.plugins.collector/configurations/:collectorConfigurationID/outputs", + wrapEchoHandle(lgc, HandleCreateCollectorConfigurationOutput)) + e.PUT( + "/api/plugins/org.graylog.plugins.collector/configurations/:collectorConfigurationID/outputs/:collectorConfigurationOutputID", + wrapEchoHandle(lgc, HandleUpdateCollectorConfigurationOutput)) + e.DELETE( + "/api/plugins/org.graylog.plugins.collector/configurations/:collectorConfigurationID/outputs/:collectorConfigurationOutputID", + wrapEchoHandle(lgc, HandleDeleteCollectorConfigurationOutput)) + // Role e.GET("/api/roles/:rolename", wrapEchoHandle(lgc, HandleGetRole)) e.GET("/api/roles", wrapEchoHandle(lgc, HandleGetRoles)) diff --git a/mockserver/logic/collector_configuration_output.go b/mockserver/logic/collector_configuration_output.go new file mode 100644 index 00000000..bbbf82c7 --- /dev/null +++ b/mockserver/logic/collector_configuration_output.go @@ -0,0 +1,77 @@ +package logic + +import ( + "fmt" + + log "github.com/sirupsen/logrus" + + "github.com/suzuki-shunsuke/go-graylog" + "github.com/suzuki-shunsuke/go-graylog/validator" +) + +// HasCollectorConfigurationOutput returns whether the collector configuration exists. +func (lgc *Logic) HasCollectorConfigurationOutput(cfgID, outputID string) (bool, error) { + return lgc.store.HasCollectorConfigurationOutput(cfgID, outputID) +} + +// AddCollectorConfigurationOutput adds a collector configuration output to the mock server. +func (lgc *Logic) AddCollectorConfigurationOutput(id string, output *graylog.CollectorConfigurationOutput) (int, error) { + if id == "" { + return 400, fmt.Errorf("id is required") + } + if err := validator.CreateValidator.Struct(output); err != nil { + return 400, err + } + if err := lgc.store.AddCollectorConfigurationOutput(id, output); err != nil { + return 500, err + } + // 202 no content + return 202, nil +} + +// UpdateCollectorConfigurationOutput updates a collector configuration output. +func (lgc *Logic) UpdateCollectorConfigurationOutput( + cfgID, outputID string, output *graylog.CollectorConfigurationOutput, +) (int, error) { + if cfgID == "" { + return 400, fmt.Errorf("collector configuration id is required") + } + if outputID == "" { + return 400, fmt.Errorf("collector configuration output id is required") + } + if err := validator.UpdateValidator.Struct(output); err != nil { + return 400, err + } + ok, err := lgc.HasCollectorConfigurationOutput(cfgID, outputID) + if err != nil { + return 500, err + } + if !ok { + return 404, fmt.Errorf("the collector configuration output is not found") + } + + if err := lgc.store.UpdateCollectorConfigurationOutput(cfgID, outputID, output); err != nil { + return 500, err + } + // 202 no content + return 200, nil +} + +// DeleteCollectorConfigurationOutput deletes a collector configuration output from the mock server. +func (lgc *Logic) DeleteCollectorConfigurationOutput(cfgID, outputID string) (int, error) { + ok, err := lgc.HasCollectorConfigurationOutput(cfgID, outputID) + if err != nil { + lgc.Logger().WithFields(log.Fields{ + "error": err, "configuration_id": cfgID, + "output_id": outputID, + }).Error("failed to check whether the collector configuration exists") + return 500, err + } + if !ok { + return 404, fmt.Errorf("the collector configuration output is not found") + } + if err := lgc.store.DeleteCollectorConfigurationOutput(cfgID, outputID); err != nil { + return 500, err + } + return 204, nil +} diff --git a/mockserver/store/plain/collector_configuration_output.go b/mockserver/store/plain/collector_configuration_output.go new file mode 100644 index 00000000..167e349e --- /dev/null +++ b/mockserver/store/plain/collector_configuration_output.go @@ -0,0 +1,99 @@ +package plain + +import ( + "fmt" + + "github.com/suzuki-shunsuke/go-graylog" + st "github.com/suzuki-shunsuke/go-graylog/mockserver/store" +) + +// HasCollectorConfigurationOutput returns whether the collector configuration output exists. +func (store *Store) HasCollectorConfigurationOutput(cfgID, outputID string) (bool, error) { + store.imutex.RLock() + defer store.imutex.RUnlock() + cfg, ok := store.collectorConfigurations[cfgID] + if !ok { + return false, nil + } + for _, output := range cfg.Outputs { + if output.OutputID == outputID { + return true, nil + } + } + return false, nil +} + +// AddCollectorConfigurationOutput adds a collector configuration output to the store. +func (store *Store) AddCollectorConfigurationOutput(cfgID string, output *graylog.CollectorConfigurationOutput) error { + if cfgID == "" { + return fmt.Errorf("id is required") + } + if output == nil { + return fmt.Errorf("collector configuration output is nil") + } + if output.OutputID == "" { + output.OutputID = st.NewObjectID() + } + + store.imutex.Lock() + defer store.imutex.Unlock() + cfg, ok := store.collectorConfigurations[cfgID] + if !ok { + return fmt.Errorf("collector configuration <%s> is not found", cfgID) + } + cfg.Outputs = append(cfg.Outputs, *output) + store.collectorConfigurations[cfgID] = cfg + return nil +} + +// UpdateCollectorConfigurationOutput updates a collector configuration output. +func (store *Store) UpdateCollectorConfigurationOutput(cfgID, outputID string, output *graylog.CollectorConfigurationOutput) error { + if cfgID == "" { + return fmt.Errorf("collector configuration id is required") + } + if outputID == "" { + return fmt.Errorf("collector configuration output id is required") + } + if output == nil { + return fmt.Errorf("collector configuration output is nil") + } + store.imutex.Lock() + defer store.imutex.Unlock() + cfg, ok := store.collectorConfigurations[cfgID] + if !ok { + return fmt.Errorf("the collector configuration <%s> is not found", cfgID) + } + for i, a := range cfg.Outputs { + if a.OutputID == outputID { + cfg.Outputs[i] = *output + store.collectorConfigurations[cfgID] = cfg + return nil + } + } + return fmt.Errorf("collector configuration output is not found") +} + +// DeleteCollectorConfigurationOutput deletes a collector configuration output from the store. +func (store *Store) DeleteCollectorConfigurationOutput(cfgID, outputID string) error { + store.imutex.Lock() + defer store.imutex.Unlock() + cfg, ok := store.collectorConfigurations[cfgID] + if !ok { + return fmt.Errorf("the collector configuration <%s> is not found", cfgID) + } + outputs := []graylog.CollectorConfigurationOutput{} + removed := false + for _, a := range cfg.Outputs { + if a.OutputID == outputID { + removed = true + continue + } + outputs = append(outputs, a) + } + if !removed { + return fmt.Errorf("the collector configuration output is not found") + } + cfg.Outputs = outputs + store.collectorConfigurations[cfgID] = cfg + return nil +} diff --git a/mockserver/store/store.go b/mockserver/store/store.go index f4f84424..dfd7b501 100644 --- a/mockserver/store/store.go +++ b/mockserver/store/store.go @@ -47,6 +47,11 @@ type Store interface { UpdateCollectorConfigurationInput(cfgID, inputID string, input *graylog.CollectorConfigurationInput) error DeleteCollectorConfigurationInput(cfgID, inputID string) error + HasCollectorConfigurationOutput(cfgID, outputID string) (bool, error) + AddCollectorConfigurationOutput(cfgID string, output *graylog.CollectorConfigurationOutput) error + UpdateCollectorConfigurationOutput(cfgID, outputID string, output *graylog.CollectorConfigurationOutput) error + DeleteCollectorConfigurationOutput(cfgID, outputID string) error + AddIndexSet(*graylog.IndexSet) error GetIndexSet(id string) (*graylog.IndexSet, error) GetIndexSets(skip, limit int) ([]graylog.IndexSet, int, error) From 99b86c8e36f69b013d87408b21d89a44233a2a35 Mon Sep 17 00:00:00 2001 From: Suzuki Shunsuke Date: Sun, 9 Sep 2018 11:33:06 +0900 Subject: [PATCH 05/10] refactor: rename method --- mockserver/handler/collector_configuration.go | 4 ++-- mockserver/handler/router.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/mockserver/handler/collector_configuration.go b/mockserver/handler/collector_configuration.go index 19b51dde..b76ba2cf 100644 --- a/mockserver/handler/collector_configuration.go +++ b/mockserver/handler/collector_configuration.go @@ -66,8 +66,8 @@ func HandleCreateCollectorConfiguration( return cfg, sc, nil } -// HandleUpdateCollectorConfigurationName is the handler of Update a CollectorConfiguration name API. -func HandleUpdateCollectorConfigurationName( +// HandleRenameCollectorConfiguration is the handler of Rename a CollectorConfiguration API. +func HandleRenameCollectorConfiguration( user *graylog.User, lgc *logic.Logic, r *http.Request, ps Params, ) (interface{}, int, error) { // PUT /plugins/org.graylog.plugins.collector/configurations/{id}/name Updates a collector configuration name diff --git a/mockserver/handler/router.go b/mockserver/handler/router.go index 8ec1621f..17417af6 100644 --- a/mockserver/handler/router.go +++ b/mockserver/handler/router.go @@ -34,7 +34,7 @@ func NewRouter(lgc *logic.Logic) http.Handler { wrapEchoHandle(lgc, HandleCreateCollectorConfiguration)) e.PUT( "/api/plugins/org.graylog.plugins.collector/configurations/:collectorConfigurationID/name", - wrapEchoHandle(lgc, HandleUpdateCollectorConfigurationName)) + wrapEchoHandle(lgc, HandleRenameCollectorConfiguration)) e.GET( "/api/plugins/org.graylog.plugins.collector/configurations/:collectorConfigurationID", wrapEchoHandle(lgc, HandleGetCollectorConfiguration)) From 9419321c5058319b20b11a8b07cda608e3f693db Mon Sep 17 00:00:00 2001 From: Suzuki Shunsuke Date: Sun, 9 Sep 2018 11:42:02 +0900 Subject: [PATCH 06/10] feat(mockserver): support collector configuration snippet APIs --- client/collector_configuration_snippet.go | 90 +++++++++++++++ .../collector_configuration_snippet.go | 103 ++++++++++++++++++ mockserver/handler/router.go | 11 ++ .../logic/collector_configuration_snippet.go | 77 +++++++++++++ .../plain/collector_configuration_snippet.go | 99 +++++++++++++++++ mockserver/store/store.go | 5 + 6 files changed, 385 insertions(+) create mode 100644 client/collector_configuration_snippet.go create mode 100644 mockserver/handler/collector_configuration_snippet.go create mode 100644 mockserver/logic/collector_configuration_snippet.go create mode 100644 mockserver/store/plain/collector_configuration_snippet.go diff --git a/client/collector_configuration_snippet.go b/client/collector_configuration_snippet.go new file mode 100644 index 00000000..ee953e31 --- /dev/null +++ b/client/collector_configuration_snippet.go @@ -0,0 +1,90 @@ +package client + +import ( + "context" + "fmt" + + "github.com/suzuki-shunsuke/go-graylog" +) + +// CreateCollectorConfigurationSnippet creates a collector configuration snippet. +func (client *Client) CreateCollectorConfigurationSnippet( + id string, snippet *graylog.CollectorConfigurationSnippet, +) (*ErrorInfo, error) { + return client.CreateCollectorConfigurationSnippetContext( + context.Background(), id, snippet) +} + +// CreateCollectorConfigurationSnippetContext creates a collector configuration snippet with a context. +func (client *Client) CreateCollectorConfigurationSnippetContext( + ctx context.Context, id string, snippet *graylog.CollectorConfigurationSnippet, +) (*ErrorInfo, error) { + // POST /plugins/org.graylog.plugins.collector/configurations/{id}/snippets Create a configuration snippet + if id == "" { + return nil, fmt.Errorf("id is required") + } + if snippet == nil { + return nil, fmt.Errorf("collector configuration is nil") + } + u, err := client.Endpoints().CollectorConfigurationSnippets(id) + if err != nil { + return nil, err + } + return client.callPost(ctx, u.String(), snippet, nil) +} + +// DeleteCollectorConfigurationSnippet deletes a collector configuration snippet. +func (client *Client) DeleteCollectorConfigurationSnippet(id, snippetID string) (*ErrorInfo, error) { + return client.DeleteCollectorConfigurationSnippetContext( + context.Background(), id, snippetID) +} + +// DeleteCollectorConfigurationSnippetContext deletes a collector configuration snippet with a context. +func (client *Client) DeleteCollectorConfigurationSnippetContext( + ctx context.Context, id, snippetID string, +) (*ErrorInfo, error) { + // DELETE /plugins/org.graylog.plugins.collector/configurations/{id}/snippets/{snippetId} Delete snippet form configuration + if id == "" { + return nil, fmt.Errorf("id is required") + } + if snippetID == "" { + return nil, fmt.Errorf("snippet id is required") + } + u, err := client.Endpoints().CollectorConfigurationSnippet(id, snippetID) + if err != nil { + return nil, err + } + return client.callDelete( + ctx, u.String(), nil, nil) +} + +// UpdateCollectorConfigurationSnippet updates a collector configuration snippet. +func (client *Client) UpdateCollectorConfigurationSnippet( + id, snippetID string, snippet *graylog.CollectorConfigurationSnippet, +) (*ErrorInfo, error) { + return client.UpdateCollectorConfigurationSnippetContext( + context.Background(), id, snippetID, snippet) +} + +// UpdateCollectorConfigurationSnippetContext updates a collector configuration snippet with a context. +func (client *Client) UpdateCollectorConfigurationSnippetContext( + ctx context.Context, id, snippetID string, + snippet *graylog.CollectorConfigurationSnippet, +) (*ErrorInfo, error) { + // PUT /plugins/org.graylog.plugins.collector/configurations/{id}/snippets/{snippet_id} Update a configuration snippet + if id == "" { + return nil, fmt.Errorf("id is required") + } + if snippetID == "" { + return nil, fmt.Errorf("snippet id is required") + } + if snippet == nil { + return nil, fmt.Errorf("snippet is nil") + } + u, err := client.Endpoints().CollectorConfigurationSnippet(id, snippetID) + if err != nil { + return nil, err + } + return client.callPut( + ctx, u.String(), snippet, nil) +} diff --git a/mockserver/handler/collector_configuration_snippet.go b/mockserver/handler/collector_configuration_snippet.go new file mode 100644 index 00000000..35c34422 --- /dev/null +++ b/mockserver/handler/collector_configuration_snippet.go @@ -0,0 +1,103 @@ +package handler + +import ( + "net/http" + + log "github.com/sirupsen/logrus" + "github.com/suzuki-shunsuke/go-set" + + "github.com/suzuki-shunsuke/go-graylog" + "github.com/suzuki-shunsuke/go-graylog/mockserver/logic" + "github.com/suzuki-shunsuke/go-graylog/util" +) + +// HandleCreateCollectorConfigurationSnippet is the handler of Create a CollectorConfiguration Snippet API. +func HandleCreateCollectorConfigurationSnippet( + user *graylog.User, lgc *logic.Logic, r *http.Request, ps Params, +) (interface{}, int, error) { + // TODO authorize + cfgID := ps.PathParam("collectorConfigurationSnippetID") + body, sc, err := validateRequestBody( + r.Body, &validateReqBodyPrms{ + Required: set.NewStrSet("tags", "snippets", "outputs", "snippets"), + Optional: nil, + ExtForbidden: true, + }) + if err != nil { + return nil, sc, err + } + snippet := &graylog.CollectorConfigurationSnippet{} + if err := util.MSDecode(body, snippet); err != nil { + lgc.Logger().WithFields(log.Fields{ + "body": body, "error": err, + }).Info("Failed to parse request body as CollectorConfigurationSnippet") + return nil, 400, err + } + sc, err = lgc.AddCollectorConfigurationSnippet(cfgID, snippet) + if err != nil { + return nil, sc, err + } + if err := lgc.Save(); err != nil { + return nil, 500, err + } + // 202 no content + return nil, sc, nil +} + +// HandleUpdateCollectorConfigurationSnippet is the handler of Update a CollectorConfiguration Snippet API. +func HandleUpdateCollectorConfigurationSnippet( + user *graylog.User, lgc *logic.Logic, r *http.Request, ps Params, +) (interface{}, int, error) { + cfgID := ps.PathParam("collectorConfigurationID") + snippetID := ps.PathParam("collectorConfigurationSnippetID") + // TODO authorize + body, sc, err := validateRequestBody( + r.Body, &validateReqBodyPrms{ + Required: set.NewStrSet("backend", "name", "snippet"), + Ignored: set.NewStrSet("snippet_id"), + ExtForbidden: true, + }) + if err != nil { + return nil, sc, err + } + snippet := &graylog.CollectorConfigurationSnippet{} + if err := util.MSDecode(body, snippet); err != nil { + lgc.Logger().WithFields(log.Fields{ + "body": body, "error": err, + }).Info("Failed to parse request body as CollectorConfiguration Snippet") + return nil, 400, err + } + + lgc.Logger().WithFields(log.Fields{ + "body": body, "snippet": snippet, + "collector_configuration_id": cfgID, + "collector_configuration_snippet_id": snippetID, + }).Debug("request body") + + sc, err = lgc.UpdateCollectorConfigurationSnippet(cfgID, snippetID, snippet) + if err != nil { + return nil, sc, err + } + if err := lgc.Save(); err != nil { + return nil, 500, err + } + // 202 no content + return nil, sc, nil +} + +// HandleDeleteCollectorConfigurationSnippet is the handler of Delete an CollectorConfiguration Snippet API. +func HandleDeleteCollectorConfigurationSnippet( + user *graylog.User, lgc *logic.Logic, r *http.Request, ps Params, +) (interface{}, int, error) { + id := ps.PathParam("collectorConfigurationID") + snippetID := ps.PathParam("collectorConfigurationSnippetID") + // TODO authorize + sc, err := lgc.DeleteCollectorConfigurationSnippet(id, snippetID) + if err != nil { + return nil, sc, err + } + if err := lgc.Save(); err != nil { + return nil, 500, err + } + return nil, sc, nil +} diff --git a/mockserver/handler/router.go b/mockserver/handler/router.go index 17417af6..edbeeffb 100644 --- a/mockserver/handler/router.go +++ b/mockserver/handler/router.go @@ -64,6 +64,17 @@ func NewRouter(lgc *logic.Logic) http.Handler { "/api/plugins/org.graylog.plugins.collector/configurations/:collectorConfigurationID/outputs/:collectorConfigurationOutputID", wrapEchoHandle(lgc, HandleDeleteCollectorConfigurationOutput)) + // Collector Configuration Snippet + e.POST( + "/api/plugins/org.graylog.plugins.collector/configurations/:collectorConfigurationID/snippets", + wrapEchoHandle(lgc, HandleCreateCollectorConfigurationSnippet)) + e.PUT( + "/api/plugins/org.graylog.plugins.collector/configurations/:collectorConfigurationID/snippets/:collectorConfigurationSnippetID", + wrapEchoHandle(lgc, HandleUpdateCollectorConfigurationSnippet)) + e.DELETE( + "/api/plugins/org.graylog.plugins.collector/configurations/:collectorConfigurationID/snippets/:collectorConfigurationSnippetID", + wrapEchoHandle(lgc, HandleDeleteCollectorConfigurationSnippet)) + // Role e.GET("/api/roles/:rolename", wrapEchoHandle(lgc, HandleGetRole)) e.GET("/api/roles", wrapEchoHandle(lgc, HandleGetRoles)) diff --git a/mockserver/logic/collector_configuration_snippet.go b/mockserver/logic/collector_configuration_snippet.go new file mode 100644 index 00000000..1ec4807e --- /dev/null +++ b/mockserver/logic/collector_configuration_snippet.go @@ -0,0 +1,77 @@ +package logic + +import ( + "fmt" + + log "github.com/sirupsen/logrus" + + "github.com/suzuki-shunsuke/go-graylog" + "github.com/suzuki-shunsuke/go-graylog/validator" +) + +// HasCollectorConfigurationSnippet returns whether the collector configuration exists. +func (lgc *Logic) HasCollectorConfigurationSnippet(cfgID, snippetID string) (bool, error) { + return lgc.store.HasCollectorConfigurationSnippet(cfgID, snippetID) +} + +// AddCollectorConfigurationSnippet adds a collector configuration snippet to the mock server. +func (lgc *Logic) AddCollectorConfigurationSnippet(id string, snippet *graylog.CollectorConfigurationSnippet) (int, error) { + if id == "" { + return 400, fmt.Errorf("id is required") + } + if err := validator.CreateValidator.Struct(snippet); err != nil { + return 400, err + } + if err := lgc.store.AddCollectorConfigurationSnippet(id, snippet); err != nil { + return 500, err + } + // 202 no content + return 202, nil +} + +// UpdateCollectorConfigurationSnippet updates a collector configuration snippet. +func (lgc *Logic) UpdateCollectorConfigurationSnippet( + cfgID, snippetID string, snippet *graylog.CollectorConfigurationSnippet, +) (int, error) { + if cfgID == "" { + return 400, fmt.Errorf("collector configuration id is required") + } + if snippetID == "" { + return 400, fmt.Errorf("collector configuration snippet id is required") + } + if err := validator.UpdateValidator.Struct(snippet); err != nil { + return 400, err + } + ok, err := lgc.HasCollectorConfigurationSnippet(cfgID, snippetID) + if err != nil { + return 500, err + } + if !ok { + return 404, fmt.Errorf("the collector configuration snippet is not found") + } + + if err := lgc.store.UpdateCollectorConfigurationSnippet(cfgID, snippetID, snippet); err != nil { + return 500, err + } + // 202 no content + return 200, nil +} + +// DeleteCollectorConfigurationSnippet deletes a collector configuration snippet from the mock server. +func (lgc *Logic) DeleteCollectorConfigurationSnippet(cfgID, snippetID string) (int, error) { + ok, err := lgc.HasCollectorConfigurationSnippet(cfgID, snippetID) + if err != nil { + lgc.Logger().WithFields(log.Fields{ + "error": err, "configuration_id": cfgID, + "snippet_id": snippetID, + }).Error("failed to check whether the collector configuration exists") + return 500, err + } + if !ok { + return 404, fmt.Errorf("the collector configuration snippet is not found") + } + if err := lgc.store.DeleteCollectorConfigurationSnippet(cfgID, snippetID); err != nil { + return 500, err + } + return 204, nil +} diff --git a/mockserver/store/plain/collector_configuration_snippet.go b/mockserver/store/plain/collector_configuration_snippet.go new file mode 100644 index 00000000..af8813c0 --- /dev/null +++ b/mockserver/store/plain/collector_configuration_snippet.go @@ -0,0 +1,99 @@ +package plain + +import ( + "fmt" + + "github.com/suzuki-shunsuke/go-graylog" + st "github.com/suzuki-shunsuke/go-graylog/mockserver/store" +) + +// HasCollectorConfigurationSnippet returns whether the collector configuration snippet exists. +func (store *Store) HasCollectorConfigurationSnippet(cfgID, snippetID string) (bool, error) { + store.imutex.RLock() + defer store.imutex.RUnlock() + cfg, ok := store.collectorConfigurations[cfgID] + if !ok { + return false, nil + } + for _, snippet := range cfg.Snippets { + if snippet.SnippetID == snippetID { + return true, nil + } + } + return false, nil +} + +// AddCollectorConfigurationSnippet adds a collector configuration snippet to the store. +func (store *Store) AddCollectorConfigurationSnippet(cfgID string, snippet *graylog.CollectorConfigurationSnippet) error { + if cfgID == "" { + return fmt.Errorf("id is required") + } + if snippet == nil { + return fmt.Errorf("collector configuration snippet is nil") + } + if snippet.SnippetID == "" { + snippet.SnippetID = st.NewObjectID() + } + + store.imutex.Lock() + defer store.imutex.Unlock() + cfg, ok := store.collectorConfigurations[cfgID] + if !ok { + return fmt.Errorf("collector configuration <%s> is not found", cfgID) + } + cfg.Snippets = append(cfg.Snippets, *snippet) + store.collectorConfigurations[cfgID] = cfg + return nil +} + +// UpdateCollectorConfigurationSnippet updates a collector configuration snippet. +func (store *Store) UpdateCollectorConfigurationSnippet(cfgID, snippetID string, snippet *graylog.CollectorConfigurationSnippet) error { + if cfgID == "" { + return fmt.Errorf("collector configuration id is required") + } + if snippetID == "" { + return fmt.Errorf("collector configuration snippet id is required") + } + if snippet == nil { + return fmt.Errorf("collector configuration snippet is nil") + } + store.imutex.Lock() + defer store.imutex.Unlock() + cfg, ok := store.collectorConfigurations[cfgID] + if !ok { + return fmt.Errorf("the collector configuration <%s> is not found", cfgID) + } + for i, a := range cfg.Snippets { + if a.SnippetID == snippetID { + cfg.Snippets[i] = *snippet + store.collectorConfigurations[cfgID] = cfg + return nil + } + } + return fmt.Errorf("collector configuration snippet is not found") +} + +// DeleteCollectorConfigurationSnippet deletes a collector configuration snippet from the store. +func (store *Store) DeleteCollectorConfigurationSnippet(cfgID, snippetID string) error { + store.imutex.Lock() + defer store.imutex.Unlock() + cfg, ok := store.collectorConfigurations[cfgID] + if !ok { + return fmt.Errorf("the collector configuration <%s> is not found", cfgID) + } + snippets := []graylog.CollectorConfigurationSnippet{} + removed := false + for _, a := range cfg.Snippets { + if a.SnippetID == snippetID { + removed = true + continue + } + snippets = append(snippets, a) + } + if !removed { + return fmt.Errorf("the collector configuration snippet is not found") + } + cfg.Snippets = snippets + store.collectorConfigurations[cfgID] = cfg + return nil +} diff --git a/mockserver/store/store.go b/mockserver/store/store.go index dfd7b501..b5208b77 100644 --- a/mockserver/store/store.go +++ b/mockserver/store/store.go @@ -52,6 +52,11 @@ type Store interface { UpdateCollectorConfigurationOutput(cfgID, outputID string, output *graylog.CollectorConfigurationOutput) error DeleteCollectorConfigurationOutput(cfgID, outputID string) error + HasCollectorConfigurationSnippet(cfgID, snippetID string) (bool, error) + AddCollectorConfigurationSnippet(cfgID string, snippet *graylog.CollectorConfigurationSnippet) error + UpdateCollectorConfigurationSnippet(cfgID, snippetID string, snippet *graylog.CollectorConfigurationSnippet) error + DeleteCollectorConfigurationSnippet(cfgID, snippetID string) error + AddIndexSet(*graylog.IndexSet) error GetIndexSet(id string) (*graylog.IndexSet, error) GetIndexSets(skip, limit int) ([]graylog.IndexSet, int, error) From cd7e69a2ec32a164302008eba8d468387d964fa1 Mon Sep 17 00:00:00 2001 From: Suzuki Shunsuke Date: Sun, 9 Sep 2018 11:46:26 +0900 Subject: [PATCH 07/10] fix: make collector configuration output properties interface --- collector_configuration.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/collector_configuration.go b/collector_configuration.go index 0dd933fa..60fdd061 100644 --- a/collector_configuration.go +++ b/collector_configuration.go @@ -52,9 +52,7 @@ type ( } // CollectorConfigurationOutputProperty represents a Graylog's Collector Configuration Output properties. - CollectorConfigurationOutputProperty struct { - Hosts string `json:"hosts"` - } + CollectorConfigurationOutputProperty interface{} // CollectorConfigurationSnippet represents a Graylog's Collector Configuration Snippet. CollectorConfigurationSnippet struct { From d05bd784e6c5a547b11957776dcf72e06a421e44 Mon Sep 17 00:00:00 2001 From: Suzuki Shunsuke Date: Sun, 9 Sep 2018 13:45:08 +0900 Subject: [PATCH 08/10] feat(terraform): support collector configuration --- client/collector_configuration.go | 20 +++- client/endpoint/collector_configuration.go | 22 ++-- collector_configuration.go | 10 +- mockserver/handler/collector_configuration.go | 4 +- terraform/README.md | 4 + terraform/docs/collector_configuration.md | 39 ++++++ .../docs/collector_configuration_input.md | 40 +++++++ .../docs/collector_configuration_output.md | 38 ++++++ .../docs/collector_configuration_snippet.md | 33 ++++++ terraform/graylog/provider.go | 15 +-- .../resource_collector_configuration.go | 110 +++++++++++++++++ .../resource_collector_configuration_test.go | 111 ++++++++++++++++++ 12 files changed, 423 insertions(+), 23 deletions(-) create mode 100644 terraform/docs/collector_configuration.md create mode 100644 terraform/docs/collector_configuration_input.md create mode 100644 terraform/docs/collector_configuration_output.md create mode 100644 terraform/docs/collector_configuration_snippet.md create mode 100644 terraform/graylog/resource_collector_configuration.go create mode 100644 terraform/graylog/resource_collector_configuration_test.go diff --git a/client/collector_configuration.go b/client/collector_configuration.go index 02acce9f..d1f27f84 100644 --- a/client/collector_configuration.go +++ b/client/collector_configuration.go @@ -22,7 +22,16 @@ func (client *Client) CreateCollectorConfigurationContext( if cfg == nil { return nil, fmt.Errorf("collector configuration is nil") } - return client.callPost(ctx, client.Endpoints().CollectorConfigurations(), cfg, nil) + if len(cfg.Inputs) == 0 { + cfg.Inputs = []graylog.CollectorConfigurationInput{} + } + if len(cfg.Outputs) == 0 { + cfg.Outputs = []graylog.CollectorConfigurationOutput{} + } + if len(cfg.Snippets) == 0 { + cfg.Snippets = []graylog.CollectorConfigurationSnippet{} + } + return client.callPost(ctx, client.Endpoints().CollectorConfigurations(), cfg, cfg) } // GetCollectorConfigurations returns all collector configurations. @@ -75,8 +84,13 @@ func (client *Client) RenameCollectorConfigurationContext( if name == "" { return nil, nil, fmt.Errorf("name is nil") } - input := graylog.CollectorConfiguration{Name: name} - u, err := client.Endpoints().CollectorConfiguration(id) + input := graylog.CollectorConfiguration{ + Name: name, + Inputs: []graylog.CollectorConfigurationInput{}, + Outputs: []graylog.CollectorConfigurationOutput{}, + Snippets: []graylog.CollectorConfigurationSnippet{}, + } + u, err := client.Endpoints().CollectorConfigurationName(id) if err != nil { return nil, nil, err } diff --git a/client/endpoint/collector_configuration.go b/client/endpoint/collector_configuration.go index 4876a06d..20e38a80 100644 --- a/client/endpoint/collector_configuration.go +++ b/client/endpoint/collector_configuration.go @@ -5,49 +5,55 @@ import ( "net/url" ) -// CollectorConfigurations returns an Collector Configuration API's endpoint url. +// CollectorConfigurations returns a Collector Configuration API's endpoint url. func (ep *Endpoints) CollectorConfigurations() string { // /plugins/org.graylog.plugins.collector/configurations return ep.collectorConfigurations.String() } -// CollectorConfiguration returns an Collector Configuration API's endpoint url. +// CollectorConfiguration returns a Collector Configuration API's endpoint url. func (ep *Endpoints) CollectorConfiguration(id string) (*url.URL, error) { // /plugins/org.graylog.plugins.collector/configurations return urlJoin(ep.collectorConfigurations, id) } -// CollectorConfigurationInputs returns an Collector Configuration Input API's endpoint url. +// CollectorConfigurationName returns a Collector Configuration API's endpoint url. +func (ep *Endpoints) CollectorConfigurationName(id string) (*url.URL, error) { + // /plugins/org.graylog.plugins.collector/configurations/:id/name + return urlJoin(ep.collectorConfigurations, fmt.Sprintf("%s/name", id)) +} + +// CollectorConfigurationInputs returns a Collector Configuration Input API's endpoint url. func (ep *Endpoints) CollectorConfigurationInputs(id string) (*url.URL, error) { // /plugins/org.graylog.plugins.collector/configurations/{id}/inputs return urlJoin(ep.collectorConfigurations, fmt.Sprintf("%s/inputs", id)) } -// CollectorConfigurationInput returns an Collector Configuration Input API's endpoint url. +// CollectorConfigurationInput returns a Collector Configuration Input API's endpoint url. func (ep *Endpoints) CollectorConfigurationInput(id, inputID string) (*url.URL, error) { // /plugins/org.graylog.plugins.collector/configurations/{id}/inputs/{inputId} return urlJoin(ep.collectorConfigurations, fmt.Sprintf("%s/inputs/%s", id, inputID)) } -// CollectorConfigurationOutputs returns an Collector Configuration Output API's endpoint url. +// CollectorConfigurationOutputs returns a Collector Configuration Output API's endpoint url. func (ep *Endpoints) CollectorConfigurationOutputs(id string) (*url.URL, error) { // /plugins/org.graylog.plugins.collector/configurations/{id}/outputs return urlJoin(ep.collectorConfigurations, fmt.Sprintf("%s/outputs", id)) } -// CollectorConfigurationOutput returns an Collector Configuration Output API's endpoint url. +// CollectorConfigurationOutput returns a Collector Configuration Output API's endpoint url. func (ep *Endpoints) CollectorConfigurationOutput(id, outputID string) (*url.URL, error) { // /plugins/org.graylog.plugins.collector/configurations/{id}/outputs/{outputId} return urlJoin(ep.collectorConfigurations, fmt.Sprintf("%s/outputs/%s", id, outputID)) } -// CollectorConfigurationSnippets returns an Collector Configuration Snippet API's endpoint url. +// CollectorConfigurationSnippets returns a Collector Configuration Snippet API's endpoint url. func (ep *Endpoints) CollectorConfigurationSnippets(id string) (*url.URL, error) { // /plugins/org.graylog.plugins.collector/configurations/{id}/snippets return urlJoin(ep.collectorConfigurations, fmt.Sprintf("%s/snippets", id)) } -// CollectorConfigurationSnippet returns an Collector Configuration Snippet API's endpoint url. +// CollectorConfigurationSnippet returns a Collector Configuration Snippet API's endpoint url. func (ep *Endpoints) CollectorConfigurationSnippet(id, snippetID string) (*url.URL, error) { // /plugins/org.graylog.plugins.collector/configurations/{id}/snippets/{snippetId} return urlJoin(ep.collectorConfigurations, fmt.Sprintf("%s/snippets/%s", id, snippetID)) diff --git a/collector_configuration.go b/collector_configuration.go index 60fdd061..1eeb56e7 100644 --- a/collector_configuration.go +++ b/collector_configuration.go @@ -1,11 +1,15 @@ package graylog +import ( + "github.com/suzuki-shunsuke/go-set" +) + type ( // CollectorConfiguration represents a Graylog's Collector Configuration. CollectorConfiguration struct { - ID string `json:"id,omitempty" v-create:"isdefault"` - Name string `json:"name,omitempty" v-create:"required"` - // Tags string `json:"tags,omitempty" v-create:"isdefault"` + ID string `json:"id,omitempty" v-create:"isdefault"` + Name string `json:"name,omitempty" v-create:"required"` + Tags set.StrSet `json:"tags"` Inputs []CollectorConfigurationInput `json:"inputs"` Outputs []CollectorConfigurationOutput `json:"outputs"` Snippets []CollectorConfigurationSnippet `json:"snippets"` diff --git a/mockserver/handler/collector_configuration.go b/mockserver/handler/collector_configuration.go index b76ba2cf..8f4f7593 100644 --- a/mockserver/handler/collector_configuration.go +++ b/mockserver/handler/collector_configuration.go @@ -43,7 +43,7 @@ func HandleCreateCollectorConfiguration( body, sc, err := validateRequestBody( r.Body, &validateReqBodyPrms{ Required: set.NewStrSet("tags", "inputs", "outputs", "snippets"), - Optional: nil, + Optional: set.NewStrSet("name", "id"), ExtForbidden: true, }) if err != nil { @@ -76,7 +76,7 @@ func HandleRenameCollectorConfiguration( body, sc, err := validateRequestBody( r.Body, &validateReqBodyPrms{ Required: set.NewStrSet("tags", "inputs", "outputs", "snippets"), - Optional: nil, + Optional: set.NewStrSet("name", "id"), ExtForbidden: true, }) if err != nil { diff --git a/terraform/README.md b/terraform/README.md index 691d084c..ece47aed 100644 --- a/terraform/README.md +++ b/terraform/README.md @@ -67,3 +67,7 @@ auth_password | GRAYLOG_AUTH_PASSWORD | * [stream](docs/stream.md) * [user](docs/user.md) * [ldap_setting](docs/ldap_setting.md) +* [collector configuration](docs/collector_configuration.md) +* [collector configuration input](docs/collector_configuration_input.md) +* [collector configuration output](docs/collector_configuration_output.md) +* [collector configuration snippet](docs/collector_configuration_snippet.md) diff --git a/terraform/docs/collector_configuration.md b/terraform/docs/collector_configuration.md new file mode 100644 index 00000000..e9056eec --- /dev/null +++ b/terraform/docs/collector_configuration.md @@ -0,0 +1,39 @@ +# graylog_collector_configuration + +https://github.com/suzuki-shunsuke/terraform-provider-graylog/blob/master/resource_collector_configuration.go + +``` +resource "graylog_collector_configuration" "test" { + tags = ["test"] + name = "terraform test" +} +``` + +Note that collector configuration's inputs, outputs and snippets are defined as other resources. + +* [input](collector_configuration_input.md) +* [output](collector_configuration_output.md) +* [snippet](collector_configuration_snippet.md) + +Note that graylog doesn't provide an API to update collector configuration's tags, +so it is impossible to update tags. + +## Argument Reference + +### Required Argument + +name | type | description +--- | --- | --- +name | string | + +### Optional Argument + +name | default | type | description +--- | --- | --- | --- +tags | [] | []string | + +## Attrs Reference + +name | type | etc +--- | --- | --- +id | string | diff --git a/terraform/docs/collector_configuration_input.md b/terraform/docs/collector_configuration_input.md new file mode 100644 index 00000000..16e762a9 --- /dev/null +++ b/terraform/docs/collector_configuration_input.md @@ -0,0 +1,40 @@ +# graylog_collector_configuration_input + +https://github.com/suzuki-shunsuke/terraform-provider-graylog/blob/master/resource_collector_configuration_input.go + +``` +resource "graylog_collector_configuration_input" "test" { + backend = "winlogbeat" + type = "" + name = "" + properties = { + event = "[{'name':'Application'},{'name':'System'},{'name':'Security'}]" + } + forward_to = "${graylog_collector_configuration_output.test-terraform.id}" + collector_configuration_id = "${graylog_collector_configuration.test-terraform.id}" +} +``` + +## Argument Reference + +### Required Argument + +name | type | description +--- | --- | --- +backend | string | +type | string | +name | string | +forward_to | string | +collector_configuration_id | string | + +### Optional Argument + +name | default | type | description +--- | --- | --- | --- +properties | null | object | + +## Attrs Reference + +name | type | etc +--- | --- | --- +input_id | string | diff --git a/terraform/docs/collector_configuration_output.md b/terraform/docs/collector_configuration_output.md new file mode 100644 index 00000000..d434cfd9 --- /dev/null +++ b/terraform/docs/collector_configuration_output.md @@ -0,0 +1,38 @@ +# graylog_collector_configuration_output + +https://github.com/suzuki-shunsuke/terraform-provider-graylog/blob/master/resource_collector_configuration_output.go + +``` +resource "graylog_collector_configuration_output" "test" { + backend = "filebeat" + type = "logstash" + name = "test" + properties = { + hosts = "['localhost:5044']" + } + collector_configuration_id = "${graylog_collector_configuration.test-terraform.id}" +} +``` + +## Argument Reference + +### Required Argument + +name | type | description +--- | --- | --- +backend | string | +type | string | +name | string | +collector_configuration_id | string | + +### Optional Argument + +name | default | type | description +--- | --- | --- | --- +properties | null | object | + +## Attrs Reference + +name | type | etc +--- | --- | --- +output_id | string | diff --git a/terraform/docs/collector_configuration_snippet.md b/terraform/docs/collector_configuration_snippet.md new file mode 100644 index 00000000..0b0bb85a --- /dev/null +++ b/terraform/docs/collector_configuration_snippet.md @@ -0,0 +1,33 @@ +# graylog_collector_configuration_snippet + +https://github.com/suzuki-shunsuke/terraform-provider-graylog/blob/master/resource_collector_configuration_snippet.go + +``` +resource "graylog_collector_configuration_snippet" "test" { + backend = "nxlog" + name = "test" + snippet = "{{if .Linux}}\nUser nxlog\nGroup nxlog\n{{if eq .LinuxPlatform \"debian\"}}\nModuledir /usr/lib/nxlog/modules\nCacheDir /var/spool/collector-sidecar/nxlog\nPidFile /var/run/graylog/collector-sidecar/nxlog.pid\n{{end}}\n{{if eq .LinuxPlatform \"redhat\"}}\nModuledir /usr/libexec/nxlog/modules\nCacheDir /var/spool/collector-sidecar/nxlog\nPidFile /var/run/graylog/collector-sidecar/nxlog.pid\n{{end}}\ndefine LOGFILE /var/log/graylog/collector-sidecar/nxlog.log\nLogFile %LOGFILE%\nLogLevel INFO\n\n\n Module xm_fileop\n \n When @daily\n Exec file_cycle('%LOGFILE%', 7);\n \n\n{{end}}\n{{if .Windows}}\nModuledir %ROOT%\\modules\nCacheDir %ROOT%\\data\nPidfile %ROOT%\\data\\nxlog.pid\nSpoolDir %ROOT%\\data\nLogFile %ROOT%\\data\\nxlog.log\nLogLevel INFO\n\n\n Module xm_fileop\n \n When @daily\n Exec file_cycle('%ROOT%\\data\\nxlog.log', 7);\n \n\n{{end}}" + collector_configuration_id = "${graylog_collector_configuration.test-terraform.id}" +} +``` + +## Argument Reference + +### Required Argument + +name | type | description +--- | --- | --- +backend | string | +name | string | +snippet | string | +collector_configuration_id | string | + +### Optional Argument + +Nothing. + +## Attrs Reference + +name | type | etc +--- | --- | --- +snippet_id | string | diff --git a/terraform/graylog/provider.go b/terraform/graylog/provider.go index 7e6e0265..e2ca8c48 100644 --- a/terraform/graylog/provider.go +++ b/terraform/graylog/provider.go @@ -27,13 +27,14 @@ func Provider() *schema.Provider { }, }, ResourcesMap: map[string]*schema.Resource{ - "graylog_role": resourceRole(), - "graylog_user": resourceUser(), - "graylog_input": resourceInput(), - "graylog_index_set": resourceIndexSet(), - "graylog_stream": resourceStream(), - "graylog_dashboard": resourceDashboard(), - "graylog_ldap_setting": resourceLDAPSetting(), + "graylog_role": resourceRole(), + "graylog_user": resourceUser(), + "graylog_input": resourceInput(), + "graylog_index_set": resourceIndexSet(), + "graylog_stream": resourceStream(), + "graylog_dashboard": resourceDashboard(), + "graylog_ldap_setting": resourceLDAPSetting(), + "graylog_collector_configuration": resourceCollectorConfiguration(), }, ConfigureFunc: providerConfigure, } diff --git a/terraform/graylog/resource_collector_configuration.go b/terraform/graylog/resource_collector_configuration.go new file mode 100644 index 00000000..d93b77de --- /dev/null +++ b/terraform/graylog/resource_collector_configuration.go @@ -0,0 +1,110 @@ +package graylog + +import ( + "github.com/hashicorp/terraform/helper/schema" + "github.com/suzuki-shunsuke/go-set" + + "github.com/suzuki-shunsuke/go-graylog" + "github.com/suzuki-shunsuke/go-graylog/client" +) + +func resourceCollectorConfiguration() *schema.Resource { + return &schema.Resource{ + Create: resourceCollectorConfigurationCreate, + Read: resourceCollectorConfigurationRead, + Update: resourceCollectorConfigurationUpdate, + Delete: resourceCollectorConfigurationDelete, + + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + // Required + "name": { + Type: schema.TypeString, + Required: true, + }, + + // Optional + "tags": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + }, + } +} + +func newCollectorConfiguration(d *schema.ResourceData) (*graylog.CollectorConfiguration, error) { + return &graylog.CollectorConfiguration{ + ID: d.Id(), + Name: d.Get("name").(string), + Tags: set.NewStrSet(getStringArray(d.Get("tags").(*schema.Set).List())...), + }, nil +} + +func resourceCollectorConfigurationCreate(d *schema.ResourceData, m interface{}) error { + config := m.(*Config) + cl, err := client.NewClient( + config.Endpoint, config.AuthName, config.AuthPassword) + if err != nil { + return err + } + cfg, err := newCollectorConfiguration(d) + if err != nil { + return err + } + if _, err := cl.CreateCollectorConfiguration(cfg); err != nil { + return err + } + d.SetId(cfg.ID) + return nil +} + +func resourceCollectorConfigurationRead(d *schema.ResourceData, m interface{}) error { + config := m.(*Config) + cl, err := client.NewClient( + config.Endpoint, config.AuthName, config.AuthPassword) + if err != nil { + return err + } + cfg, _, err := cl.GetCollectorConfiguration(d.Id()) + if err != nil { + return err + } + setStrToRD(d, "name", cfg.Name) + setStrListToRD(d, "tags", cfg.Tags.ToList()) + return nil +} + +func resourceCollectorConfigurationUpdate(d *schema.ResourceData, m interface{}) error { + config := m.(*Config) + cl, err := client.NewClient( + config.Endpoint, config.AuthName, config.AuthPassword) + if err != nil { + return err + } + cfg, err := newCollectorConfiguration(d) + if err != nil { + return err + } + + if _, _, err = cl.RenameCollectorConfiguration(cfg.ID, cfg.Name); err != nil { + return err + } + return nil +} + +func resourceCollectorConfigurationDelete(d *schema.ResourceData, m interface{}) error { + config := m.(*Config) + cl, err := client.NewClient( + config.Endpoint, config.AuthName, config.AuthPassword) + if err != nil { + return err + } + if _, err := cl.DeleteCollectorConfiguration(d.Id()); err != nil { + return err + } + return nil +} diff --git a/terraform/graylog/resource_collector_configuration_test.go b/terraform/graylog/resource_collector_configuration_test.go new file mode 100644 index 00000000..00268ea0 --- /dev/null +++ b/terraform/graylog/resource_collector_configuration_test.go @@ -0,0 +1,111 @@ +package graylog + +import ( + "fmt" + "os" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + + "github.com/suzuki-shunsuke/go-graylog/client" +) + +func testDeleteCollectorConfiguration( + cl *client.Client, key string, +) resource.TestCheckFunc { + return func(tfState *terraform.State) error { + id, err := getIDFromTfState(tfState, key) + if err != nil { + return err + } + if _, _, err := cl.GetCollectorConfiguration(id); err == nil { + return fmt.Errorf(`collector configuration "%s" must be deleted`, id) + } + return nil + } +} + +func testCreateCollectorConfiguration( + cl *client.Client, key string, +) resource.TestCheckFunc { + return func(tfState *terraform.State) error { + id, err := getIDFromTfState(tfState, key) + if err != nil { + return err + } + _, _, err = cl.GetCollectorConfiguration(id) + return err + } +} + +func testUpdateCollectorConfiguration( + cl *client.Client, key, name string, +) resource.TestCheckFunc { + return func(tfState *terraform.State) error { + id, err := getIDFromTfState(tfState, key) + if err != nil { + return err + } + cfg, _, err := cl.GetCollectorConfiguration(id) + if err != nil { + return err + } + if cfg.Name != name { + return fmt.Errorf("collector configuration name is not updated") + } + return nil + } +} + +func TestAccCollectorConfiguration(t *testing.T) { + cl, server, err := setEnv() + if err != nil { + t.Fatal(err) + } + if server != nil { + defer os.Unsetenv("GRAYLOG_WEB_ENDPOINT_URI") + } + + testAccProvider := Provider() + testAccProviders := map[string]terraform.ResourceProvider{ + "graylog": testAccProvider, + } + + name := "test-cfg" + updatedName := "test-cfg changed" + + dbTf := fmt.Sprintf(` +resource "graylog_collector_configuration" "zoo" { + name = "%s" + tags = ["test"] +}`, name) + updateTf := fmt.Sprintf(` +resource "graylog_collector_configuration" "zoo" { + name = "%s" + tags = ["test"] +}`, updatedName) + if server != nil { + server.Start() + defer server.Close() + } + key := "graylog_collector_configuration.zoo" + resource.Test(t, resource.TestCase{ + Providers: testAccProviders, + CheckDestroy: testDeleteCollectorConfiguration(cl, key), + Steps: []resource.TestStep{ + { + Config: dbTf, + Check: resource.ComposeTestCheckFunc( + testCreateCollectorConfiguration(cl, key), + ), + }, + { + Config: updateTf, + Check: resource.ComposeTestCheckFunc( + testUpdateCollectorConfiguration(cl, key, updatedName), + ), + }, + }, + }) +} From e70a6aeb5849c8af0fadb6bc8b2f31f9604e0110 Mon Sep 17 00:00:00 2001 From: Suzuki Shunsuke Date: Sun, 9 Sep 2018 15:57:14 +0900 Subject: [PATCH 09/10] fix: fix collector configuration client and mockserver --- client/collector_configuration.go | 10 +++++++--- client/collector_configuration_input.go | 3 +-- client/collector_configuration_output.go | 3 +-- mockserver/handler/collector_configuration_input.go | 8 +++++--- mockserver/handler/collector_configuration_output.go | 8 +++++--- 5 files changed, 19 insertions(+), 13 deletions(-) diff --git a/client/collector_configuration.go b/client/collector_configuration.go index d1f27f84..c3dfa34a 100644 --- a/client/collector_configuration.go +++ b/client/collector_configuration.go @@ -5,6 +5,7 @@ import ( "fmt" "github.com/pkg/errors" + "github.com/suzuki-shunsuke/go-set" "github.com/suzuki-shunsuke/go-graylog" ) @@ -22,15 +23,18 @@ func (client *Client) CreateCollectorConfigurationContext( if cfg == nil { return nil, fmt.Errorf("collector configuration is nil") } - if len(cfg.Inputs) == 0 { + if cfg.Inputs == nil { cfg.Inputs = []graylog.CollectorConfigurationInput{} } - if len(cfg.Outputs) == 0 { + if cfg.Outputs == nil { cfg.Outputs = []graylog.CollectorConfigurationOutput{} } - if len(cfg.Snippets) == 0 { + if cfg.Snippets == nil { cfg.Snippets = []graylog.CollectorConfigurationSnippet{} } + if cfg.Tags == nil { + cfg.Tags = set.StrSet{} + } return client.callPost(ctx, client.Endpoints().CollectorConfigurations(), cfg, cfg) } diff --git a/client/collector_configuration_input.go b/client/collector_configuration_input.go index be1a0123..51530fba 100644 --- a/client/collector_configuration_input.go +++ b/client/collector_configuration_input.go @@ -4,8 +4,6 @@ import ( "context" "fmt" - // "github.com/pkg/errors" - "github.com/suzuki-shunsuke/go-graylog" ) @@ -32,6 +30,7 @@ func (client *Client) CreateCollectorConfigurationInputContext( if err != nil { return nil, err } + // 202 no content return client.callPost(ctx, u.String(), input, nil) } diff --git a/client/collector_configuration_output.go b/client/collector_configuration_output.go index de7b07ea..146cc493 100644 --- a/client/collector_configuration_output.go +++ b/client/collector_configuration_output.go @@ -4,8 +4,6 @@ import ( "context" "fmt" - // "github.com/pkg/errors" - "github.com/suzuki-shunsuke/go-graylog" ) @@ -32,6 +30,7 @@ func (client *Client) CreateCollectorConfigurationOutputContext( if err != nil { return nil, err } + // 202 no content return client.callPost(ctx, u.String(), output, nil) } diff --git a/mockserver/handler/collector_configuration_input.go b/mockserver/handler/collector_configuration_input.go index a94ceb9a..60af5bc0 100644 --- a/mockserver/handler/collector_configuration_input.go +++ b/mockserver/handler/collector_configuration_input.go @@ -16,11 +16,13 @@ func HandleCreateCollectorConfigurationInput( user *graylog.User, lgc *logic.Logic, r *http.Request, ps Params, ) (interface{}, int, error) { // TODO authorize - cfgID := ps.PathParam("collectorConfigurationInputID") + cfgID := ps.PathParam("collectorConfigurationID") + // Known properties include: input_id, properties, backend, forward_to, type, name body, sc, err := validateRequestBody( r.Body, &validateReqBodyPrms{ - Required: set.NewStrSet("tags", "inputs", "outputs", "snippets"), - Optional: nil, + Required: set.NewStrSet("backend", "type", "name", "forward_to"), + Optional: set.NewStrSet("properties"), + Ignored: set.NewStrSet("input_id"), ExtForbidden: true, }) if err != nil { diff --git a/mockserver/handler/collector_configuration_output.go b/mockserver/handler/collector_configuration_output.go index 27d241ad..e749331c 100644 --- a/mockserver/handler/collector_configuration_output.go +++ b/mockserver/handler/collector_configuration_output.go @@ -16,11 +16,13 @@ func HandleCreateCollectorConfigurationOutput( user *graylog.User, lgc *logic.Logic, r *http.Request, ps Params, ) (interface{}, int, error) { // TODO authorize - cfgID := ps.PathParam("collectorConfigurationOutputID") + cfgID := ps.PathParam("collectorConfigurationID") + // Known properties include: output_id, type, name, backend, properties body, sc, err := validateRequestBody( r.Body, &validateReqBodyPrms{ - Required: set.NewStrSet("tags", "outputs", "outputs", "snippets"), - Optional: nil, + Required: set.NewStrSet("type", "name", "backend"), + Optional: set.NewStrSet("properties"), + Ignored: set.NewStrSet("output_id"), ExtForbidden: true, }) if err != nil { From b84d574bbaa7ccb17903e6ffa8d8c3839a5b5296 Mon Sep 17 00:00:00 2001 From: Suzuki Shunsuke Date: Sun, 9 Sep 2018 16:08:17 +0900 Subject: [PATCH 10/10] fix: give up support of some terraform resources * collector configuration * collector configuration input * collector configuration output * collector configuration snippet --- terraform/README.md | 18 ++- terraform/docs/collector_configuration.md | 39 ------ .../docs/collector_configuration_input.md | 40 ------- .../docs/collector_configuration_output.md | 38 ------ .../docs/collector_configuration_snippet.md | 33 ------ terraform/graylog/provider.go | 15 ++- .../resource_collector_configuration.go | 110 ----------------- .../resource_collector_configuration_test.go | 111 ------------------ 8 files changed, 21 insertions(+), 383 deletions(-) delete mode 100644 terraform/docs/collector_configuration.md delete mode 100644 terraform/docs/collector_configuration_input.md delete mode 100644 terraform/docs/collector_configuration_output.md delete mode 100644 terraform/docs/collector_configuration_snippet.md delete mode 100644 terraform/graylog/resource_collector_configuration.go delete mode 100644 terraform/graylog/resource_collector_configuration_test.go diff --git a/terraform/README.md b/terraform/README.md index ece47aed..01573534 100644 --- a/terraform/README.md +++ b/terraform/README.md @@ -67,7 +67,17 @@ auth_password | GRAYLOG_AUTH_PASSWORD | * [stream](docs/stream.md) * [user](docs/user.md) * [ldap_setting](docs/ldap_setting.md) -* [collector configuration](docs/collector_configuration.md) -* [collector configuration input](docs/collector_configuration_input.md) -* [collector configuration output](docs/collector_configuration_output.md) -* [collector configuration snippet](docs/collector_configuration_snippet.md) + +## Unsupported resources + +We can't support these resources for some reasons. + +### CollectorConfiguration (includes input, output snippet) + +We can't support these resources because graylog API doesn't return the created resource id (response body: no content). + +The following APIs doesn't return the created resource id (response body: no content). + +* POST /plugins/org.graylog.plugins.collector/configurations/{id}/inputs Create a configuration input +* POST /plugins/org.graylog.plugins.collector/configurations/{id}/outputs Create a configuration output +* POST /plugins/org.graylog.plugins.collector/configurations/{id}/snippets Create a configuration snippet diff --git a/terraform/docs/collector_configuration.md b/terraform/docs/collector_configuration.md deleted file mode 100644 index e9056eec..00000000 --- a/terraform/docs/collector_configuration.md +++ /dev/null @@ -1,39 +0,0 @@ -# graylog_collector_configuration - -https://github.com/suzuki-shunsuke/terraform-provider-graylog/blob/master/resource_collector_configuration.go - -``` -resource "graylog_collector_configuration" "test" { - tags = ["test"] - name = "terraform test" -} -``` - -Note that collector configuration's inputs, outputs and snippets are defined as other resources. - -* [input](collector_configuration_input.md) -* [output](collector_configuration_output.md) -* [snippet](collector_configuration_snippet.md) - -Note that graylog doesn't provide an API to update collector configuration's tags, -so it is impossible to update tags. - -## Argument Reference - -### Required Argument - -name | type | description ---- | --- | --- -name | string | - -### Optional Argument - -name | default | type | description ---- | --- | --- | --- -tags | [] | []string | - -## Attrs Reference - -name | type | etc ---- | --- | --- -id | string | diff --git a/terraform/docs/collector_configuration_input.md b/terraform/docs/collector_configuration_input.md deleted file mode 100644 index 16e762a9..00000000 --- a/terraform/docs/collector_configuration_input.md +++ /dev/null @@ -1,40 +0,0 @@ -# graylog_collector_configuration_input - -https://github.com/suzuki-shunsuke/terraform-provider-graylog/blob/master/resource_collector_configuration_input.go - -``` -resource "graylog_collector_configuration_input" "test" { - backend = "winlogbeat" - type = "" - name = "" - properties = { - event = "[{'name':'Application'},{'name':'System'},{'name':'Security'}]" - } - forward_to = "${graylog_collector_configuration_output.test-terraform.id}" - collector_configuration_id = "${graylog_collector_configuration.test-terraform.id}" -} -``` - -## Argument Reference - -### Required Argument - -name | type | description ---- | --- | --- -backend | string | -type | string | -name | string | -forward_to | string | -collector_configuration_id | string | - -### Optional Argument - -name | default | type | description ---- | --- | --- | --- -properties | null | object | - -## Attrs Reference - -name | type | etc ---- | --- | --- -input_id | string | diff --git a/terraform/docs/collector_configuration_output.md b/terraform/docs/collector_configuration_output.md deleted file mode 100644 index d434cfd9..00000000 --- a/terraform/docs/collector_configuration_output.md +++ /dev/null @@ -1,38 +0,0 @@ -# graylog_collector_configuration_output - -https://github.com/suzuki-shunsuke/terraform-provider-graylog/blob/master/resource_collector_configuration_output.go - -``` -resource "graylog_collector_configuration_output" "test" { - backend = "filebeat" - type = "logstash" - name = "test" - properties = { - hosts = "['localhost:5044']" - } - collector_configuration_id = "${graylog_collector_configuration.test-terraform.id}" -} -``` - -## Argument Reference - -### Required Argument - -name | type | description ---- | --- | --- -backend | string | -type | string | -name | string | -collector_configuration_id | string | - -### Optional Argument - -name | default | type | description ---- | --- | --- | --- -properties | null | object | - -## Attrs Reference - -name | type | etc ---- | --- | --- -output_id | string | diff --git a/terraform/docs/collector_configuration_snippet.md b/terraform/docs/collector_configuration_snippet.md deleted file mode 100644 index 0b0bb85a..00000000 --- a/terraform/docs/collector_configuration_snippet.md +++ /dev/null @@ -1,33 +0,0 @@ -# graylog_collector_configuration_snippet - -https://github.com/suzuki-shunsuke/terraform-provider-graylog/blob/master/resource_collector_configuration_snippet.go - -``` -resource "graylog_collector_configuration_snippet" "test" { - backend = "nxlog" - name = "test" - snippet = "{{if .Linux}}\nUser nxlog\nGroup nxlog\n{{if eq .LinuxPlatform \"debian\"}}\nModuledir /usr/lib/nxlog/modules\nCacheDir /var/spool/collector-sidecar/nxlog\nPidFile /var/run/graylog/collector-sidecar/nxlog.pid\n{{end}}\n{{if eq .LinuxPlatform \"redhat\"}}\nModuledir /usr/libexec/nxlog/modules\nCacheDir /var/spool/collector-sidecar/nxlog\nPidFile /var/run/graylog/collector-sidecar/nxlog.pid\n{{end}}\ndefine LOGFILE /var/log/graylog/collector-sidecar/nxlog.log\nLogFile %LOGFILE%\nLogLevel INFO\n\n\n Module xm_fileop\n \n When @daily\n Exec file_cycle('%LOGFILE%', 7);\n \n\n{{end}}\n{{if .Windows}}\nModuledir %ROOT%\\modules\nCacheDir %ROOT%\\data\nPidfile %ROOT%\\data\\nxlog.pid\nSpoolDir %ROOT%\\data\nLogFile %ROOT%\\data\\nxlog.log\nLogLevel INFO\n\n\n Module xm_fileop\n \n When @daily\n Exec file_cycle('%ROOT%\\data\\nxlog.log', 7);\n \n\n{{end}}" - collector_configuration_id = "${graylog_collector_configuration.test-terraform.id}" -} -``` - -## Argument Reference - -### Required Argument - -name | type | description ---- | --- | --- -backend | string | -name | string | -snippet | string | -collector_configuration_id | string | - -### Optional Argument - -Nothing. - -## Attrs Reference - -name | type | etc ---- | --- | --- -snippet_id | string | diff --git a/terraform/graylog/provider.go b/terraform/graylog/provider.go index e2ca8c48..7e6e0265 100644 --- a/terraform/graylog/provider.go +++ b/terraform/graylog/provider.go @@ -27,14 +27,13 @@ func Provider() *schema.Provider { }, }, ResourcesMap: map[string]*schema.Resource{ - "graylog_role": resourceRole(), - "graylog_user": resourceUser(), - "graylog_input": resourceInput(), - "graylog_index_set": resourceIndexSet(), - "graylog_stream": resourceStream(), - "graylog_dashboard": resourceDashboard(), - "graylog_ldap_setting": resourceLDAPSetting(), - "graylog_collector_configuration": resourceCollectorConfiguration(), + "graylog_role": resourceRole(), + "graylog_user": resourceUser(), + "graylog_input": resourceInput(), + "graylog_index_set": resourceIndexSet(), + "graylog_stream": resourceStream(), + "graylog_dashboard": resourceDashboard(), + "graylog_ldap_setting": resourceLDAPSetting(), }, ConfigureFunc: providerConfigure, } diff --git a/terraform/graylog/resource_collector_configuration.go b/terraform/graylog/resource_collector_configuration.go deleted file mode 100644 index d93b77de..00000000 --- a/terraform/graylog/resource_collector_configuration.go +++ /dev/null @@ -1,110 +0,0 @@ -package graylog - -import ( - "github.com/hashicorp/terraform/helper/schema" - "github.com/suzuki-shunsuke/go-set" - - "github.com/suzuki-shunsuke/go-graylog" - "github.com/suzuki-shunsuke/go-graylog/client" -) - -func resourceCollectorConfiguration() *schema.Resource { - return &schema.Resource{ - Create: resourceCollectorConfigurationCreate, - Read: resourceCollectorConfigurationRead, - Update: resourceCollectorConfigurationUpdate, - Delete: resourceCollectorConfigurationDelete, - - Importer: &schema.ResourceImporter{ - State: schema.ImportStatePassthrough, - }, - - Schema: map[string]*schema.Schema{ - // Required - "name": { - Type: schema.TypeString, - Required: true, - }, - - // Optional - "tags": { - Type: schema.TypeSet, - Optional: true, - Elem: &schema.Schema{Type: schema.TypeString}, - }, - }, - } -} - -func newCollectorConfiguration(d *schema.ResourceData) (*graylog.CollectorConfiguration, error) { - return &graylog.CollectorConfiguration{ - ID: d.Id(), - Name: d.Get("name").(string), - Tags: set.NewStrSet(getStringArray(d.Get("tags").(*schema.Set).List())...), - }, nil -} - -func resourceCollectorConfigurationCreate(d *schema.ResourceData, m interface{}) error { - config := m.(*Config) - cl, err := client.NewClient( - config.Endpoint, config.AuthName, config.AuthPassword) - if err != nil { - return err - } - cfg, err := newCollectorConfiguration(d) - if err != nil { - return err - } - if _, err := cl.CreateCollectorConfiguration(cfg); err != nil { - return err - } - d.SetId(cfg.ID) - return nil -} - -func resourceCollectorConfigurationRead(d *schema.ResourceData, m interface{}) error { - config := m.(*Config) - cl, err := client.NewClient( - config.Endpoint, config.AuthName, config.AuthPassword) - if err != nil { - return err - } - cfg, _, err := cl.GetCollectorConfiguration(d.Id()) - if err != nil { - return err - } - setStrToRD(d, "name", cfg.Name) - setStrListToRD(d, "tags", cfg.Tags.ToList()) - return nil -} - -func resourceCollectorConfigurationUpdate(d *schema.ResourceData, m interface{}) error { - config := m.(*Config) - cl, err := client.NewClient( - config.Endpoint, config.AuthName, config.AuthPassword) - if err != nil { - return err - } - cfg, err := newCollectorConfiguration(d) - if err != nil { - return err - } - - if _, _, err = cl.RenameCollectorConfiguration(cfg.ID, cfg.Name); err != nil { - return err - } - return nil -} - -func resourceCollectorConfigurationDelete(d *schema.ResourceData, m interface{}) error { - config := m.(*Config) - cl, err := client.NewClient( - config.Endpoint, config.AuthName, config.AuthPassword) - if err != nil { - return err - } - if _, err := cl.DeleteCollectorConfiguration(d.Id()); err != nil { - return err - } - return nil -} diff --git a/terraform/graylog/resource_collector_configuration_test.go b/terraform/graylog/resource_collector_configuration_test.go deleted file mode 100644 index 00268ea0..00000000 --- a/terraform/graylog/resource_collector_configuration_test.go +++ /dev/null @@ -1,111 +0,0 @@ -package graylog - -import ( - "fmt" - "os" - "testing" - - "github.com/hashicorp/terraform/helper/resource" - "github.com/hashicorp/terraform/terraform" - - "github.com/suzuki-shunsuke/go-graylog/client" -) - -func testDeleteCollectorConfiguration( - cl *client.Client, key string, -) resource.TestCheckFunc { - return func(tfState *terraform.State) error { - id, err := getIDFromTfState(tfState, key) - if err != nil { - return err - } - if _, _, err := cl.GetCollectorConfiguration(id); err == nil { - return fmt.Errorf(`collector configuration "%s" must be deleted`, id) - } - return nil - } -} - -func testCreateCollectorConfiguration( - cl *client.Client, key string, -) resource.TestCheckFunc { - return func(tfState *terraform.State) error { - id, err := getIDFromTfState(tfState, key) - if err != nil { - return err - } - _, _, err = cl.GetCollectorConfiguration(id) - return err - } -} - -func testUpdateCollectorConfiguration( - cl *client.Client, key, name string, -) resource.TestCheckFunc { - return func(tfState *terraform.State) error { - id, err := getIDFromTfState(tfState, key) - if err != nil { - return err - } - cfg, _, err := cl.GetCollectorConfiguration(id) - if err != nil { - return err - } - if cfg.Name != name { - return fmt.Errorf("collector configuration name is not updated") - } - return nil - } -} - -func TestAccCollectorConfiguration(t *testing.T) { - cl, server, err := setEnv() - if err != nil { - t.Fatal(err) - } - if server != nil { - defer os.Unsetenv("GRAYLOG_WEB_ENDPOINT_URI") - } - - testAccProvider := Provider() - testAccProviders := map[string]terraform.ResourceProvider{ - "graylog": testAccProvider, - } - - name := "test-cfg" - updatedName := "test-cfg changed" - - dbTf := fmt.Sprintf(` -resource "graylog_collector_configuration" "zoo" { - name = "%s" - tags = ["test"] -}`, name) - updateTf := fmt.Sprintf(` -resource "graylog_collector_configuration" "zoo" { - name = "%s" - tags = ["test"] -}`, updatedName) - if server != nil { - server.Start() - defer server.Close() - } - key := "graylog_collector_configuration.zoo" - resource.Test(t, resource.TestCase{ - Providers: testAccProviders, - CheckDestroy: testDeleteCollectorConfiguration(cl, key), - Steps: []resource.TestStep{ - { - Config: dbTf, - Check: resource.ComposeTestCheckFunc( - testCreateCollectorConfiguration(cl, key), - ), - }, - { - Config: updateTf, - Check: resource.ComposeTestCheckFunc( - testUpdateCollectorConfiguration(cl, key, updatedName), - ), - }, - }, - }) -}