chore: Move private packages to internal (#1664)

This commit is contained in:
Jan-Otto Kröpke
2024-10-03 20:23:56 +02:00
committed by GitHub
parent bcfe6df24d
commit 5d95610c84
181 changed files with 1680 additions and 1867 deletions

148
internal/config/config.go Normal file
View File

@@ -0,0 +1,148 @@
// Copyright 2018 Prometheus Team
// 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 config
import (
"context"
"crypto/tls"
"fmt"
"io"
"log/slog"
"net/http"
"os"
"strings"
"github.com/alecthomas/kingpin/v2"
"gopkg.in/yaml.v3"
)
type getFlagger interface {
GetFlag(name string) *kingpin.FlagClause
}
// Resolver represents a configuration file resolver for kingpin.
type Resolver struct {
flags map[string]string
}
// NewResolver returns a Resolver structure.
func NewResolver(file string, logger *slog.Logger, insecureSkipVerify bool) (*Resolver, error) {
flags := map[string]string{}
var fileBytes []byte
var err error
if strings.HasPrefix(file, "http://") || strings.HasPrefix(file, "https://") {
fileBytes, err = readFromURL(file, logger, insecureSkipVerify)
if err != nil {
return nil, err
}
} else {
fileBytes, err = readFromFile(file, logger)
if err != nil {
return nil, err
}
}
var rawValues map[string]interface{}
err = yaml.Unmarshal(fileBytes, &rawValues)
if err != nil {
return nil, fmt.Errorf("failed to unmarshal configuration file: %w", err)
}
// Flatten nested YAML values
flattenedValues := flatten(rawValues)
for k, v := range flattenedValues {
if _, ok := flags[k]; !ok {
flags[k] = v
}
}
return &Resolver{flags: flags}, nil
}
func readFromFile(file string, logger *slog.Logger) ([]byte, error) {
logger.Info("Loading configuration file: " + file)
if _, err := os.Stat(file); err != nil {
return nil, fmt.Errorf("failed to read configuration file: %w", err)
}
fileBytes, err := os.ReadFile(file)
if err != nil {
return nil, fmt.Errorf("failed to read configuration file: %w", err)
}
return fileBytes, nil
}
func readFromURL(file string, logger *slog.Logger, insecureSkipVerify bool) ([]byte, error) {
logger.Info("Loading configuration file from URL: " + file)
tr := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: insecureSkipVerify}, //nolint:gosec
}
if insecureSkipVerify {
logger.Warn("Loading configuration file with TLS verification disabled")
}
client := &http.Client{Transport: tr}
req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, file, nil)
if err != nil {
return nil, fmt.Errorf("failed to create HTTP request: %w", err)
}
resp, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("failed to read configuration file from URL: %w", err)
}
defer resp.Body.Close()
fileBytes, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
return fileBytes, nil
}
func (c *Resolver) setDefault(v getFlagger) {
for name, value := range c.flags {
f := v.GetFlag(name)
if f != nil {
f.Default(value)
}
}
}
// Bind sets active flags with their default values from the configuration file(s).
func (c *Resolver) Bind(app *kingpin.Application, args []string) error {
// Parse the command line arguments to get the selected command.
pc, err := app.ParseContext(args)
if err != nil {
return err
}
c.setDefault(app)
if pc.SelectedCommand != nil {
c.setDefault(pc.SelectedCommand)
}
return nil
}

View File

@@ -0,0 +1,73 @@
package config
import (
"fmt"
"strconv"
)
// flatten flattens the nested struct.
//
// All keys will be joined by dot
// e.g. {"a": {"b":"c"}} => {"a.b":"c"}
// or {"a": {"b":[1,2]}} => {"a.b.0":1, "a.b.1": 2}.
func flatten(data map[string]interface{}) map[string]string {
ret := make(map[string]string)
for k, v := range data {
switch typed := v.(type) {
case map[interface{}]interface{}:
for fk, fv := range flatten(convertMap(typed)) {
ret[fmt.Sprintf("%s.%s", k, fk)] = fv
}
case map[string]interface{}:
for fk, fv := range flatten(typed) {
ret[fmt.Sprintf("%s.%s", k, fk)] = fv
}
case []interface{}:
for fk, fv := range flattenSlice(typed) {
ret[fmt.Sprintf("%s.%s", k, fk)] = fv
}
default:
ret[k] = fmt.Sprint(typed)
}
}
return ret
}
func flattenSlice(data []interface{}) map[string]string {
ret := make(map[string]string)
for idx, v := range data {
switch typed := v.(type) {
case map[interface{}]interface{}:
for fk, fv := range flatten(convertMap(typed)) {
ret[fmt.Sprintf("%d,%s", idx, fk)] = fv
}
case map[string]interface{}:
for fk, fv := range flatten(typed) {
ret[fmt.Sprintf("%d,%s", idx, fk)] = fv
}
case []interface{}:
for fk, fv := range flattenSlice(typed) {
ret[fmt.Sprintf("%d,%s", idx, fk)] = fv
}
default:
ret[strconv.Itoa(idx)] = fmt.Sprint(typed)
}
}
return ret
}
func convertMap(originalMap map[interface{}]interface{}) map[string]interface{} {
convertedMap := map[string]interface{}{}
for key, value := range originalMap {
if keyString, ok := key.(string); ok {
convertedMap[keyString] = value
}
}
return convertedMap
}

View File

@@ -0,0 +1,38 @@
package config
import (
"reflect"
"testing"
"gopkg.in/yaml.v3"
)
// Unmarshal good configuration file and confirm data is flattened correctly.
func TestConfigFlattening(t *testing.T) {
t.Parallel()
goodYamlConfig := []byte(`---
collectors:
enabled: cpu,net,service
log:
level: debug`)
var data map[string]interface{}
err := yaml.Unmarshal(goodYamlConfig, &data)
if err != nil {
t.Error(err)
}
expectedResult := map[string]string{
"collectors.enabled": "cpu,net,service",
"log.level": "debug",
}
flattenedValues := flatten(data)
if !reflect.DeepEqual(expectedResult, flattenedValues) {
t.Errorf("Flattened values do not match!\nExpected result: %s\nActual result: %s", expectedResult, flattenedValues)
}
}