Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds new configloader package #40

Merged
merged 1 commit into from
Aug 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,24 @@ run:
# vendor$, third_party$, testdata$, examples$, Godeps$, builtin$
skip-dirs-use-default: true
linters-settings:
funlen:
lines: 100
statements: 50
goimports:
local-prefixes: github.com/calyptia/cloud
revive:
min-confidence: 0.8
gocyclo:
min-complexity: 15
min-complexity: 25
govet:
check-shadowing: true
misspell:
locale: US
gomnd:
ignored-numbers:
- 2 #used for word splitting.
gocognit:
min-complexity: 54
nolintlint:
allow-leading-space: false # require machine-readable nolint directives (with no leading space)
allow-unused: false # report any unused nolint directives
Expand All @@ -40,7 +45,6 @@ linters:
- unused
- varcheck
- bodyclose
- depguard
- dupl
- exportloopref
- forcetypeassert
Expand Down
22 changes: 22 additions & 0 deletions configloader/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Package configloader provides functionality to load Calyptia configurations
// from a set of specified configuration files. The main purpose of this package
// is to provide a simplified and streamlined way to fetch configurations without
// relying on environment variables.
//
// The package introduces a `Loader` type, which is responsible for loading and
// parsing the configuration files. It leverages the FileReader interface to read
// file contents, which allows for easy mocking in unit tests.
//
// Usage:
//
// loader := configloader.NewLoader(nil) // uses the real file reader by default
// config := loader.LoadFromFiles("path/to/config1.yaml", "path/to/config2.conf")
//
// if config != nil {
// fmt.Println("Loaded config:", config)
// }
//
// The package also supports multiple file formats, including `.conf`, `.ini`, `.yaml`,
// and `.yml`. The first valid Calyptia configuration found among the provided files
// will be returned. If none is found, the function returns nil.
package configloader
20 changes: 20 additions & 0 deletions configloader/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
github.com/alecthomas/assert/v2 v2.3.0 h1:mAsH2wmvjsuvyBvAmCtm7zFsBlb8mIHx5ySLVdDZXL0=
github.com/alecthomas/assert/v2 v2.3.0/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ=
github.com/alecthomas/repr v0.2.0 h1:HAzS41CIzNW5syS8Mf9UwXhNH1J9aix/BvDRf1Ml2Yk=
github.com/alecthomas/repr v0.2.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
github.com/calyptia/go-fluentbit-config/v2 v2.1.0 h1:DWV0N4DO9vJX9I1Y3tdYi0P7A1xa+rqGsqLG06tUmEc=
github.com/calyptia/go-fluentbit-config/v2 v2.1.0/go.mod h1:bmPxFuwb7//+pLPSvLbLxS/fYesxysQtiSksElDVszQ=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df h1:UA2aFVmmsIlefxMk29Dp2juaUSth8Pyn3Tq5Y5mJGME=
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
154 changes: 154 additions & 0 deletions configloader/loader.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
package configloader

import (
"fmt"
"net/url"
"os"
"path/filepath"
"strings"

fluentbitconfig "github.com/calyptia/go-fluentbit-config/v2"
)

// CalyptiaConfig contains configurations related to Calyptia.
type CalyptiaConfig struct {
Token string
URL string
TLS bool
}

// FileReader is an interface for reading files.
type FileReader interface {
ReadFile(filename string) ([]byte, error)
}

// LoaderInterface is an interface for the configuration loader.
type LoaderInterface interface {
LoadFromFiles(configFiles ...string) *CalyptiaConfig
}

// RealFileReader is a concrete implementation of FileReader.
type RealFileReader struct{}

// ReadFile reads the content of the given filename.
func (r RealFileReader) ReadFile(filename string) ([]byte, error) {
//nolint:gosec //required filesystem access to read fixture data.
return os.ReadFile(filename)
}

// Loader is responsible for loading configurations.
type Loader struct {
FileReader
}

// NewLoader initializes a new Loader with the given FileReader.
func NewLoader(fr FileReader) *Loader {
if fr == nil {
fr = RealFileReader{}
}
return &Loader{fr}
}

// parseConfig reads the content of the given config path and parses it into a fluentbit configuration object.
func (cl *Loader) parseConfig(path string) (*fluentbitconfig.Config, error) {
// Read the content from the given path using the FileReader
content, err := cl.FileReader.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("error reading file %s: %v", path, err)
}

// Determine the format based on the file extension
ext := filepath.Ext(path)
var format fluentbitconfig.Format
switch ext {
case ".conf", ".ini":
format = fluentbitconfig.FormatClassic
case ".yaml", ".yml":
format = fluentbitconfig.FormatYAML
default:
return nil, fmt.Errorf("unsupported file format for %s", path)
}

// Parse the content into a fluentbit configuration object
parsedConfig, err := fluentbitconfig.ParseAs(string(content), format)
if err != nil {
return nil, fmt.Errorf("error parsing config from file %s: %v", path, err)
}

return &parsedConfig, nil
}

// LoadFromFiles loads the Calyptia configurations from the given list of files.
// It goes through each file, parses it, and attempts to retrieve the Calyptia configuration.
// The method returns the first valid Calyptia configuration it finds.
func (cl *Loader) LoadFromFiles(configFiles ...string) *CalyptiaConfig {
calyptiaConfig := &CalyptiaConfig{
TLS: false, // default value
}

for _, path := range configFiles {
parsedConfig, err := cl.parseConfig(path)
if err != nil {
continue
}

// Attempt to find the calyptia plugin properties
plugin, found := parsedConfig.Customs.FindByID("calyptia.0")
if !found {
continue
}
// Extract calyptia_tls and determine whether it's "true" or "on"
value, exists := plugin.Properties.Get("calyptia_tls")
if exists {
tlsValue, ok := value.(string)
if ok {
tlsVal := strings.ToLower(tlsValue)
calyptiaConfig.TLS = tlsVal == "true" || tlsVal == "on"
} else {
calyptiaConfig.TLS = false
}
}

// Extract calyptia_host and construct full URL
value, exists = plugin.Properties.Get("calyptia_host")
if exists {
host, ok := value.(string)
if !ok {
host = "" // Set to empty string if type assertion fails
}
if !strings.HasPrefix(host, "http://") && !strings.HasPrefix(host, "https://") {
if calyptiaConfig.TLS {
host = "https://" + host
} else {
host = "http://" + host
}
}
parsedURL, err := url.Parse(host)
if err == nil {
fullURL := parsedURL.Scheme + "://" + parsedURL.Hostname()
if !(calyptiaConfig.TLS && parsedURL.Port() == "443" || !calyptiaConfig.TLS && parsedURL.Port() == "80") && parsedURL.Port() != "" {
fullURL += ":" + parsedURL.Port()
}
calyptiaConfig.URL = fullURL
}
}

// Extract api_key
value, exists = plugin.Properties.Get("api_key")
if exists {
token, ok := value.(string)
if !ok {
token = "" // Set to empty string if type assertion fails
}
calyptiaConfig.Token = token
}

return calyptiaConfig
}
return nil
}

// NewDefaultLoader returns a new Loader instance with the default real file reader.
func NewDefaultLoader() *Loader {
return &Loader{RealFileReader{}}
}
72 changes: 72 additions & 0 deletions configloader/loader_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package configloader

import (
"path/filepath"
"testing"

"github.com/stretchr/testify/assert"
)

func TestConfigLoader(t *testing.T) {
tests := []struct {
name string
fileName string
expectedConfig *CalyptiaConfig
expectError bool
}{
{
name: "Basic YAML config provided",
fileName: "basic.yaml",
expectedConfig: &CalyptiaConfig{
URL: "https://cloud-api.calyptia.com",
Token: "token",
TLS: true,
},
expectError: false,
},
{
name: "Basic YAML no TLS config provided",
fileName: "basic-no-tls.yaml",
expectedConfig: &CalyptiaConfig{
URL: "http://cloud-api.calyptia.com",
Token: "token",
TLS: false,
},
expectError: false,
},
{
name: "Basic INI config provided",
fileName: "basic.ini",
expectedConfig: &CalyptiaConfig{
URL: "https://cloud-api.calyptia.com",
Token: "token",
TLS: true,
},
expectError: false,
},
{
name: "Incorrect YAML config provided",
fileName: "incorrect.yaml",
expectedConfig: nil,
expectError: true,
},
{
name: "Incorrect INI config provided",
fileName: "incorrect.ini",
expectedConfig: nil,
expectError: true,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
loader := NewDefaultLoader()
config := loader.LoadFromFiles(filepath.Join("testdata", tt.fileName))
if tt.expectError {
assert.Nil(t, config)
} else {
assert.Equal(t, tt.expectedConfig, config)
}
})
}
}
19 changes: 19 additions & 0 deletions configloader/testdata/basic-no-tls.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
HTTP_Server: "On"
HTTP_Listen: 0.0.0.0
HTTP_PORT: 2020
Log_Level: debug
plugins_file: /opt/calyptia-fluent-bit/etc/enterprise_plugins.conf
Parsers_File: /config/parsers.conf
customs:
- name: calyptia
calyptia_tls: "off"
api_key: token
pipeline_id: 774e7aed-cce1-4460-a160-1d24be8c37dd
add_label: pipeline_id 774e7aed-cce1-4460-a160-1d24be8c37dd
calyptia_host: cloud-api.calyptia.com
pipeline:
inputs:
- name: fluentbit_metrics
tag: _calyptia_cloud
scrape_on_start: true
scrape_interval: 30
24 changes: 24 additions & 0 deletions configloader/testdata/basic.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
[SERVICE]
HTTP_Server On
HTTP_Listen 0.0.0.0
HTTP_PORT 2020
Log_Level debug

[SERVICE]
plugins_file /opt/calyptia-fluent-bit/etc/enterprise_plugins.conf
storage.path /data/storage
storage.backlog.mem_limit 5M
storage.max_chunks_up 128.000000
storage.sync normal
[CUSTOM]
name calyptia
api_key token
log_level debug
pipeline_id 9c797f01-531a-4c16-8b54-efd576eb2ce9
add_label pipeline_id 9c797f01-531a-4c16-8b54-efd576eb2ce9
add_label pod_uid ${POD_UID}
calyptia_host cloud-api.calyptia.com
calyptia_port 443
calyptia_tls on
calyptia_tls.verify on

19 changes: 19 additions & 0 deletions configloader/testdata/basic.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
HTTP_Server: "On"
HTTP_Listen: 0.0.0.0
HTTP_PORT: 2020
Log_Level: debug
plugins_file: /opt/calyptia-fluent-bit/etc/enterprise_plugins.conf
Parsers_File: /config/parsers.conf
customs:
- name: calyptia
calyptia_tls: "on"
api_key: token
pipeline_id: 774e7aed-cce1-4460-a160-1d24be8c37dd
add_label: pipeline_id 774e7aed-cce1-4460-a160-1d24be8c37dd
calyptia_host: cloud-api.calyptia.com
pipeline:
inputs:
- name: fluentbit_metrics
tag: _calyptia_cloud
scrape_on_start: true
scrape_interval: 30
2 changes: 2 additions & 0 deletions configloader/testdata/incorrect.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[SERVICE]
wrongField example
2 changes: 2 additions & 0 deletions configloader/testdata/incorrect.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
HTTP_Server: "On"
wrongField: "example"
8 changes: 5 additions & 3 deletions cshared.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,11 @@ import (
"github.com/calyptia/plugin/output"
)

var unregister func()
var cmt *cmetrics.Context
var logger Logger
var (
unregister func()
cmt *cmetrics.Context
logger Logger
)

// FLBPluginRegister registers a plugin in the context of the fluent-bit runtime, a name and description
// can be provided.
Expand Down
Loading