Skip to content

Commit

Permalink
Merge pull request #658 from ystia/feature/events_and_logs_elastic_st…
Browse files Browse the repository at this point in the history
…orage

Feature/events and logs elastic storage
  • Loading branch information
loicalbertin committed Jul 10, 2020
2 parents 0e65153 + 825c2a1 commit 2d1b508
Show file tree
Hide file tree
Showing 10 changed files with 1,292 additions and 0 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## UNRELEASED

### FEATURES

* Added an ElasticSearch store for events and logs ([GH-658](https://github.com/ystia/yorc/issues/658))

### SECURITY FIXES

* Fix [vulnerability in golang.org/x/crypto/ssh](https://snyk.io/vuln/SNYK-GOLANG-GOLANGORGXCRYPTOSSH-551923) by upgrading dependency
Expand Down
56 changes: 56 additions & 0 deletions doc/configuration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1178,6 +1178,7 @@ Yorc supports 5 store ``implementations``:
* ``cipherFile``
* ``fileCache``
* ``cipherFileCache``
* ``elastic`` (experimental)
By default, ``Log`` and ``Event`` store types use ``consul`` implementation, and ``Deployment`` store uses ``fileCache``.
Expand Down Expand Up @@ -1389,6 +1390,61 @@ If no storage configuration is set, default stores implementations are used as d
If any storage configuration is set with partial stores types, the missing store types will be added with default implementations.
elastic
^^^^^^^
This store ables you to store ``Log`` s and ``Event`` s in elasticsearch.
.. warning::
This storage is only suitable to store logs and events.
Per Yorc cluster : 1 index for logs, 1 index for events.
+-----------------------------+----------------------------------------------------+-----------+------------------+-----------------+
| Property Name | Description | Data Type | Required | Default |
+=============================+====================================================+===========+==================+=================+
| ``es_urls`` | the ES cluster urls | []string | yes | |
+-----------------------------+----------------------------------------------------+-----------+------------------+-----------------+
| ``ca_cert_path`` | path to the PEM encoded CA's certificate file when | string | no | |
| | TLS is activated for ES | | | |
+-----------------------------+----------------------------------------------------+-----------+------------------+-----------------+
| ``cert_path`` | path to a PEM encoded certificate file when TLS | string | no | |
| | is activated for ES | | | |
+-----------------------------+----------------------------------------------------+-----------+------------------+-----------------+
| ``key_path`` | path to a PEM encoded private key file when TLS | string | no | |
| | is activated for ES | | | |
+-----------------------------+----------------------------------------------------+-----------+------------------+-----------------+
| ``index_prefix`` | indexes used by yorc can be prefixed | string | no | yorc_ |
+-----------------------------+----------------------------------------------------+-----------+------------------+-----------------+
| ``es_query_period`` | when querying logs and event, we wait this timeout | duration | no | 4s |
| | before each request when it returns nothing (until | | | |
| | something is returned or the waitTimeout is | | | |
| | reached) | | | |
+-----------------------------+----------------------------------------------------+-----------+------------------+-----------------+
| ``es_refresh_wait_timeout`` | used to wait for more than refresh_interval (1s) | duration | no | 2s |
| | (until something is returned or the waitTimeout is | | | |
| | is reached) | | | |
+-----------------------------+----------------------------------------------------+-----------+------------------+-----------------+
| ``es_force_refresh`` | when querying ES, force refresh index before when | bool | no | false |
| | waiting for refresh. | | | |
+-----------------------------+----------------------------------------------------+-----------+------------------+-----------------+
| ``max_bulk_size`` | the maximum size (in kB) of bulk request sent when | int64 | no | 4000 |
| | while migrating data | | | |
+-----------------------------+----------------------------------------------------+-----------+------------------+-----------------+
| ``max_bulk_count`` | maximum size (in term of number of documents) when | int64 | no | 1000 |
| | of bulk request sent while migrating data | | | |
+-----------------------------+----------------------------------------------------+-----------+------------------+-----------------+
| ``cluster_id`` | used to distinguish logs & events in the indexes | string | no | |
| | if different yorc cluster are writing in the same | | | |
| | elastic cluster. | | | |
| | If not set, the consul.datacenter will be used. | | | |
+-----------------------------+----------------------------------------------------+-----------+------------------+-----------------+
| ``trace_requests`` | to print ES requests (for debug only) | bool | no | false |
+-----------------------------+----------------------------------------------------+-----------+------------------+-----------------+
| ``trace_events`` | to trace events & logs when sent (for debug only) | bool | no | false |
+-----------------------------+----------------------------------------------------+-----------+------------------+-----------------+
Vault configuration
-------------------
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ require (
github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 // indirect
github.com/duosecurity/duo_api_golang v0.0.0-20200206192355-a9725220d6ca // indirect
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4
github.com/elastic/go-elasticsearch/v6 v6.8.6-0.20200428134631-c5be8f8ee116
github.com/fatih/color v1.7.0
github.com/fatih/structs v1.1.0 // indirect
github.com/fsnotify/fsnotify v1.4.7
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,8 @@ github.com/duosecurity/duo_api_golang v0.0.0-20200206192355-a9725220d6ca h1:/YYq
github.com/duosecurity/duo_api_golang v0.0.0-20200206192355-a9725220d6ca/go.mod h1:jdoEJUIrTIxN7nNTwwqA3TBNcSM+W1lrWM6OXVhjbG8=
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4 h1:qk/FSDDxo05wdJH28W+p5yivv7LuLYLRXPPD8KQCtZs=
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/elastic/go-elasticsearch/v6 v6.8.6-0.20200428134631-c5be8f8ee116 h1:Cukct/JLkvYHsHgC810jQKMT6emk9v/fspxPbDKJSoo=
github.com/elastic/go-elasticsearch/v6 v6.8.6-0.20200428134631-c5be8f8ee116/go.mod h1:UwaDJsD3rWLM5rKNFzv9hgox93HoX8utj1kxD9aFUcI=
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
Expand Down
191 changes: 191 additions & 0 deletions storage/internal/elastic/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
// Copyright 2019 Bull S.A.S. Atos Technologies - Bull, Rue Jean Jaures, B.P.68, 78340, Les Clayes-sous-Bois, France.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package elastic

import (
"github.com/pkg/errors"
"github.com/spf13/cast"
"github.com/ystia/yorc/v4/config"
"github.com/ystia/yorc/v4/log"
"reflect"
"time"
)

var elasticStoreConfType = reflect.TypeOf(elasticStoreConf{})

// elasticStoreConf represents the elastic store configuration that can be set in store.properties configuration.
type elasticStoreConf struct {
// The ES cluster urls (array or CSV)
esUrls []string `json:"es_urls"`
// The path to the CACert file when TLS is activated for ES
caCertPath string `json:"ca_cert_path"`
// The path to a PEM encoded certificate file when TLS is activated for ES
certPath string `json:"cert_path"`
// The path to a PEM encoded private key file when TLS is activated for ES
keyPath string `json:"key_path"`
// All index used by yorc will be prefixed by this prefix
indicePrefix string `json:"index_prefix" default:"yorc_"`
// When querying logs and event, we wait this timeout before each request when it returns nothing
// (until something is returned or the waitTimeout is reached)
esQueryPeriod time.Duration `json:"es_query_period" default:"4s"`
// This timeout is used to wait for more than refresh_interval = 1s when querying logs and events indexes
esRefreshWaitTimeout time.Duration `json:"es_refresh_wait_timeout" default:"2s"`
// When querying ES, force refresh index before waiting for refresh
esForceRefresh bool `json:"es_force_refresh" default:"false"`
// This is the maximum size (in kB) of bulk request sent while migrating data
maxBulkSize int `json:"max_bulk_size" default:"4000"`
// This is the maximum size (in term of number of documents) of bulk request sent while migrating data
maxBulkCount int `json:"max_bulk_count" default:"1000"`
// This optional ID will be used to distinguish logs & events in the indexes. If not set, we'll use the Consul.Datacenter
clusterID string `json:"cluster_id"`
// Set to true if you want to print ES requests (for debug only)
traceRequests bool `json:"trace_requests" default:"false"`
// Set to true if you want to trace events & logs when sent (for debug only)
traceEvents bool `json:"trace_events" default:"false"`
}

// Get the tag for this field (for internal usage only: fatal if not found !).
func getElasticStorageConfigPropertyTag(fn string, tn string) (tagValue string, e error) {
f, found := elasticStoreConfType.FieldByName(fn)
if !found {
e = errors.Errorf("Not able to get field %s on elasticStoreConf struct, there is an issue with this code !", fn)
return
}
tagValue = f.Tag.Get(tn)
if tagValue == "" {
e = errors.Errorf("Not able to get field %s's tag %s value on elasticStoreConf struct, there is an issue with this code !\"", fn, tn)
return
}
return
}

// The configuration of the elastic store is defined regarding default values and store 'properties' dynamic map.
// Will fail if the required es_urls is not set in store 'properties'
func getElasticStoreConfig(yorcConfig config.Configuration, storeConfig config.Store) (cfg elasticStoreConf, e error) {
storeProperties := storeConfig.Properties
var t string
// The ES urls is required
t, e = getElasticStorageConfigPropertyTag("esUrls", "json")
if e != nil {
return
}
if storeProperties.IsSet(t) {
cfg.esUrls = storeProperties.GetStringSlice(t)
if cfg.esUrls == nil || len(cfg.esUrls) == 0 {
e = errors.Errorf("Not able to get ES configuration for elastic store, es_urls store property seems empty : %+v", storeProperties.Get(t))
return
}
} else {
log.Fatal("Not able to get ES configuration for elastic store, es_urls store property should be set !")
}
// Define the clusterID
t, e = getElasticStorageConfigPropertyTag("clusterID", "json")
if e != nil {
return
}
if storeProperties.IsSet(t) {
cfg.clusterID = storeProperties.GetString(t)
}
if len(cfg.clusterID) == 0 {
cfg.clusterID = yorcConfig.Consul.Datacenter
log.Printf("clusterID not provided or empty, using consul datacenter (%s) as clusterID", yorcConfig.Consul.Datacenter)
}
if len(cfg.clusterID) == 0 {
e = errors.Errorf("Not able to define clusterID, please check configuration !")
return
}
// Define store optional / default configuration
t, e = getElasticStorageConfigPropertyTag("caCertPath", "json")
if storeProperties.IsSet(t) {
cfg.caCertPath = storeProperties.GetString(t)
}
t, e = getElasticStorageConfigPropertyTag("certPath", "json")
if storeProperties.IsSet(t) {
cfg.certPath = storeProperties.GetString(t)
}
t, e = getElasticStorageConfigPropertyTag("keyPath", "json")
if storeProperties.IsSet(t) {
cfg.keyPath = storeProperties.GetString(t)
}
cfg.esForceRefresh, e = getBoolFromSettingsOrDefaults("esForceRefresh", storeProperties)
t, e = getElasticStorageConfigPropertyTag("indicePrefix", "json")
if storeProperties.IsSet(t) {
cfg.indicePrefix = storeProperties.GetString(t)
} else {
cfg.indicePrefix, e = getElasticStorageConfigPropertyTag("indicePrefix", "default")
}
cfg.esQueryPeriod, e = getDurationFromSettingsOrDefaults("esQueryPeriod", storeProperties)
cfg.esRefreshWaitTimeout, e = getDurationFromSettingsOrDefaults("esRefreshWaitTimeout", storeProperties)
cfg.maxBulkSize, e = getIntFromSettingsOrDefaults("maxBulkSize", storeProperties)
cfg.maxBulkCount, e = getIntFromSettingsOrDefaults("maxBulkCount", storeProperties)
cfg.traceRequests, e = getBoolFromSettingsOrDefaults("traceRequests", storeProperties)
cfg.traceEvents, e = getBoolFromSettingsOrDefaults("traceEvents", storeProperties)
// If any error have been encountered, it will be returned
return
}

// Get the duration from store config properties, fallback to required default value defined in struc.
func getDurationFromSettingsOrDefaults(fn string, dm config.DynamicMap) (v time.Duration, er error) {
t, er := getElasticStorageConfigPropertyTag(fn, "json")
if er != nil {
return
}
if dm.IsSet(t) {
v = dm.GetDuration(t)
return
}
t, er = getElasticStorageConfigPropertyTag(fn, "default")
if er != nil {
return
}
v = cast.ToDuration(t)
return
}

// Get the int from store config properties, fallback to required default value defined in struc.
func getIntFromSettingsOrDefaults(fn string, dm config.DynamicMap) (v int, err error) {
t, err := getElasticStorageConfigPropertyTag(fn, "json")
if err != nil {
return
}
if dm.IsSet(t) {
v = dm.GetInt(t)
return
}
t, err = getElasticStorageConfigPropertyTag(fn, "default")
if err != nil {
return
}
v = cast.ToInt(t)
return
}

// Get the bool from store config properties, fallback to required default value defined in struc.
func getBoolFromSettingsOrDefaults(fn string, dm config.DynamicMap) (v bool, e error) {
t, e := getElasticStorageConfigPropertyTag(fn, "json")
if e != nil {
return
}
if dm.IsSet(t) {
v = dm.GetBool(t)
return
}
t, e = getElasticStorageConfigPropertyTag(fn, "default")
if e != nil {
return
}
v = cast.ToBool(t)
return
}

0 comments on commit 2d1b508

Please sign in to comment.