Skip to content
Draft
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
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ require (
github.com/openshift/elasticsearch-operator v0.0.0-20220613183908-e1648e67c298
github.com/openshift/hypershift/api v0.0.0-20250331115040-26fc3ceb1929
github.com/pavel-v-chernykh/keystore-go/v4 v4.1.0
github.com/pelletier/go-toml/v2 v2.2.4
github.com/pkg/errors v0.9.1
github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.55.1
github.com/prometheus/client_golang v1.20.5
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,8 @@ github.com/openshift/hypershift/api v0.0.0-20250331115040-26fc3ceb1929 h1:4VEo5A
github.com/openshift/hypershift/api v0.0.0-20250331115040-26fc3ceb1929/go.mod h1:/mwHEHti5qFebmxfBzI3UlRMOy60QpGhPlrYJDlMWL0=
github.com/pavel-v-chernykh/keystore-go/v4 v4.1.0 h1:xKxUVGoB9VJU+lgQLPN0KURjw+XCVVSpHfQEeyxk3zo=
github.com/pavel-v-chernykh/keystore-go/v4 v4.1.0/go.mod h1:2ejgys4qY+iNVW1IittZhyRYA6MNv8TgM6VHqojbB9g=
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
Expand Down
69 changes: 57 additions & 12 deletions internal/generator/framework/generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ package framework

import (
"bytes"
log "github.com/ViaQ/logerr/v2/log/static"
"sort"
"strings"
"text/template"

log "github.com/ViaQ/logerr/v2/log/static"
"github.com/pelletier/go-toml/v2"
)

// Element is a basic unit of configuration. It wraps a golang template along with the data type to hold the data template needs. A type implementing
Expand All @@ -14,6 +16,11 @@ type Element interface {
Template() string
}

type StructuredElement interface {
Element
Config() any
}

// Section is a collection of Elements at a high level division of configuration. e.g. Inputs, Outputs, Ingress etc. It is used to show a breakdown of generated configuration + adding a comment along with the declared section to document the meaning of the section.
type Section struct {
Elements []Element
Expand Down Expand Up @@ -50,6 +57,7 @@ func (g Generator) generate(es []Element) (string, error) {
if len(es) == 0 {
return "", nil
}
structMaps := map[string]interface{}{}
t := template.New("generate")
f := template.FuncMap{
"compose": g.generate,
Expand Down Expand Up @@ -83,25 +91,62 @@ func (g Generator) generate(es []Element) (string, error) {
if e == nil || e == Nil {
continue
}
var err error
t, err = t.Parse(e.Template())
if err != nil {
log.V(0).Error(err, "Error parsing template", "element", e, "name", e.Name(), "template", e.Template())
log.V(3).Error(err, "Error parsing template", "element", e)
panic(err)
}
err = t.ExecuteTemplate(b, e.Name(), e)
if err != nil {
log.V(0).Error(err, "Error in conf generation")
return "error in conf generation", err

switch el := e.(type) {
case StructuredElement:
cfg := el.Config().(map[string]interface{})
structMaps = mergeMaps(structMaps, cfg)

default:
var err error
t, err = t.Parse(e.Template())
if err != nil {
log.V(0).Error(err, "Error parsing template", "element", e, "name", e.Name(), "template", e.Template())
log.V(3).Error(err, "Error parsing template", "element", e)
panic(err)
}
err = t.ExecuteTemplate(b, e.Name(), e)
if err != nil {
log.V(0).Error(err, "Error in conf generation")
return "error in conf generation", err
}
if i < len(es)-1 {
b.Write([]byte("\n"))
}
}

if i < len(es)-1 {
b.Write([]byte("\n"))
}
}

if len(structMaps) > 0 {
b1, err := toml.Marshal(structMaps)
if err != nil {
return "", err
}
b.Write(b1)
}

return strings.TrimRight(b.String(), "\n"), nil
}

func mergeMaps(dest, src map[string]interface{}) map[string]interface{} {
for k, v := range src {
if vMap, ok := v.(map[string]interface{}); ok {
if destMap, exists := dest[k].(map[string]interface{}); exists {
// recursively merge nested maps
dest[k] = mergeMaps(destMap, vMap)
} else {
dest[k] = vMap
}
} else {
dest[k] = v
}
}
return dest
}

// MergeElements merges multiple arrays of Elements into a single array of Element
func MergeElements(els ...[]Element) []Element {
merged := make([]Element, 0)
Expand Down
14 changes: 7 additions & 7 deletions internal/generator/vector/output/azuremonitor/azm_common.toml
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@

[sinks]
[sinks.output_azure_monitor_logs]
type = "azure_monitor_logs"
inputs = ["pipelineName"]
customer_id = "6vzw6sHc-0bba-6sHc-4b6c-8bz7sr5eggRt"
log_type = "myLogType"
shared_key = "SECRET[kubernetes_secret.azure-monitor-secret/shared_key]"
type = 'azure_monitor_logs'
inputs = ['pipelineName']
customer_id = '6vzw6sHc-0bba-6sHc-4b6c-8bz7sr5eggRt'
log_type = 'myLogType'
shared_key = 'SECRET[kubernetes_secret.azure-monitor-secret/shared_key]'

[sinks.output_azure_monitor_logs.encoding]
except_fields = ["_internal"]
except_fields = ['_internal']
82 changes: 40 additions & 42 deletions internal/generator/vector/output/azuremonitor/azuremonitor.go
Original file line number Diff line number Diff line change
@@ -1,46 +1,45 @@
package azuremonitor

import (
obs "github.com/openshift/cluster-logging-operator/api/observability/v1"
"github.com/openshift/cluster-logging-operator/internal/api/observability"
"github.com/openshift/cluster-logging-operator/internal/generator/framework"
"github.com/openshift/cluster-logging-operator/internal/generator/vector/output/common"
"github.com/openshift/cluster-logging-operator/internal/generator/vector/output/common/tls"

obs "github.com/openshift/cluster-logging-operator/api/observability/v1"
genhelper "github.com/openshift/cluster-logging-operator/internal/generator/helpers"
. "github.com/openshift/cluster-logging-operator/internal/generator/vector/elements"
vectorhelpers "github.com/openshift/cluster-logging-operator/internal/generator/vector/helpers"
"github.com/openshift/cluster-logging-operator/internal/generator/vector/output/common"
)

type AzureMonitorSink struct {
Type string `toml:"type"`
Inputs []string `toml:"inputs"`
CustomerId string `toml:"customer_id"`
LogType string `toml:"log_type"`
SharedKey string `toml:"shared_key"`
AzureResourceId string `toml:"azure_resource_id,omitempty"`
Host string `toml:"host,omitempty"`
Encoding common.Encoding `toml:"encoding,omitempty"`
}

type AzureMonitor struct {
ComponentID string
Inputs string
CustomerId string
LogType string
AzureResourceId string
SharedKey string
Host string
ID string
Sink AzureMonitorSink
}

func (azm AzureMonitor) Config() any {
return map[string]interface{}{
"sinks": map[string]interface{}{
azm.ID: azm.Sink,
},
}
}

func (azm AzureMonitor) Name() string {
return "AzureMonitorVectorTemplate"
}

func (azm AzureMonitor) Template() string {
return `{{define "` + azm.Name() + `" -}}
[sinks.{{.ComponentID}}]
type = "azure_monitor_logs"
inputs = {{.Inputs}}
{{ if .AzureResourceId}}
azure_resource_id = "{{.AzureResourceId}}"
{{- end }}
customer_id = "{{.CustomerId}}"
{{ if .Host }}
host = "{{.Host}}"
{{- end }}
log_type = "{{.LogType}}"
shared_key = "{{.SharedKey}}"
{{end}}`
return ""
}

func New(id string, o obs.OutputSpec, inputs []string, secrets observability.Secrets, strategy common.ConfigStrategy, op framework.Options) []framework.Element {
Expand All @@ -50,26 +49,25 @@ func New(id string, o obs.OutputSpec, inputs []string, secrets observability.Sec
}
}
azm := o.AzureMonitor
e := AzureMonitor{
ComponentID: id,
Inputs: vectorhelpers.MakeInputs(inputs...),
CustomerId: azm.CustomerId,
LogType: azm.LogType,
AzureResourceId: azm.AzureResourceId,
Host: azm.Host,
sink := AzureMonitorSink{
Type: "azure_monitor_logs",
Inputs: inputs,
CustomerId: azm.CustomerId,
LogType: azm.LogType,
Host: azm.Host,
Encoding: common.NewEncoding(id, ""),
}
if azm.Authentication != nil && azm.Authentication.SharedKey != nil {
e.SharedKey = secrets.AsString(azm.Authentication.SharedKey)
e.SharedKey = vectorhelpers.SecretFrom(azm.Authentication.SharedKey)
sink.SharedKey = vectorhelpers.SecretFrom(azm.Authentication.SharedKey)
}
confTLS := tls.New(id, o.TLS, secrets, op)
//confTLS := tls.New(id, o.TLS, secrets, op)
return []framework.Element{
e,
common.NewEncoding(id, ""),
common.NewAcknowledgments(id, strategy),
common.NewBatch(id, strategy),
common.NewBuffer(id, strategy),
common.NewRequest(id, strategy),
confTLS,
AzureMonitor{ID: id, Sink: sink},
//common.NewEncoding(id, ""),
//common.NewAcknowledgments(id, strategy),
//common.NewBatch(id, strategy),
//common.NewBuffer(id, strategy),
//common.NewRequest(id, strategy),
//confTLS,
}
}
77 changes: 37 additions & 40 deletions internal/generator/vector/output/azuremonitor/azuremonitor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,16 @@ package azuremonitor
import (
_ "embed"
"fmt"
"time"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
obs "github.com/openshift/cluster-logging-operator/api/observability/v1"
"github.com/openshift/cluster-logging-operator/internal/constants"
"github.com/openshift/cluster-logging-operator/internal/generator/framework"
vectorhelpers "github.com/openshift/cluster-logging-operator/internal/generator/vector/helpers"
"github.com/openshift/cluster-logging-operator/internal/utils"
"github.com/openshift/cluster-logging-operator/test/helpers/outputs/adapter/fake"
. "github.com/openshift/cluster-logging-operator/test/matchers"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
)

var _ = Describe("Generating vector config for Azure Monitor Logs output:", func() {
Expand Down Expand Up @@ -43,27 +40,27 @@ var _ = Describe("Generating vector config for Azure Monitor Logs output:", func
},
}

tlsSpec = &obs.OutputTLSSpec{
InsecureSkipVerify: true,
TLSSpec: obs.TLSSpec{
CA: &obs.ValueReference{
Key: constants.TrustedCABundleKey,
SecretName: secretTlsName,
},
Certificate: &obs.ValueReference{
Key: constants.ClientCertKey,
SecretName: secretTlsName,
},
Key: &obs.SecretReference{
Key: constants.ClientPrivateKey,
SecretName: secretTlsName,
},
KeyPassphrase: &obs.SecretReference{
Key: constants.Passphrase,
SecretName: secretName,
},
},
}
//tlsSpec = &obs.OutputTLSSpec{
// InsecureSkipVerify: true,
// TLSSpec: obs.TLSSpec{
// CA: &obs.ValueReference{
// Key: constants.TrustedCABundleKey,
// SecretName: secretTlsName,
// },
// Certificate: &obs.ValueReference{
// Key: constants.ClientCertKey,
// SecretName: secretTlsName,
// },
// Key: &obs.SecretReference{
// Key: constants.ClientPrivateKey,
// SecretName: secretTlsName,
// },
// KeyPassphrase: &obs.SecretReference{
// Key: constants.Passphrase,
// SecretName: secretName,
// },
// },
//}
initOutput = func() obs.OutputSpec {
return obs.OutputSpec{
Type: obs.OutputTypeAzureMonitor,
Expand All @@ -81,12 +78,12 @@ var _ = Describe("Generating vector config for Azure Monitor Logs output:", func
}
}

baseTune = &obs.BaseOutputTuningSpec{
DeliveryMode: obs.DeliveryModeAtLeastOnce,
MaxWrite: utils.GetPtr(resource.MustParse("10M")),
MaxRetryDuration: utils.GetPtr(time.Duration(35)),
MinRetryDuration: utils.GetPtr(time.Duration(20)),
}
//baseTune = &obs.BaseOutputTuningSpec{
// DeliveryMode: obs.DeliveryModeAtLeastOnce,
// MaxWrite: utils.GetPtr(resource.MustParse("10M")),
// MaxRetryDuration: utils.GetPtr(time.Duration(35)),
// MinRetryDuration: utils.GetPtr(time.Duration(20)),
//}
)

DescribeTable("should generate valid config", func(visit func(output *obs.OutputSpec), tune bool, expFile string) {
Expand All @@ -107,15 +104,15 @@ var _ = Describe("Generating vector config for Azure Monitor Logs output:", func
Expect(string(exp)).To(EqualConfigFrom(conf))
},
Entry("for common case", nil, false, "azm_common.toml"),
Entry("for advance case", func(output *obs.OutputSpec) {
output.AzureMonitor.AzureResourceId = azureId
output.AzureMonitor.Host = hostCN
}, false, "azm_advance.toml"),
Entry("for common with tls case", func(output *obs.OutputSpec) {
output.TLS = tlsSpec
}, false, "azm_tls.toml"),
Entry("for common with tls case", func(output *obs.OutputSpec) {
output.AzureMonitor.Tuning = baseTune
}, true, "azm_tuning.toml"),
//Entry("for advance case", func(output *obs.OutputSpec) {
// output.AzureMonitor.AzureResourceId = azureId
// output.AzureMonitor.Host = hostCN
//}, false, "azm_advance.toml"),
//Entry("for common with tls case", func(output *obs.OutputSpec) {
// output.TLS = tlsSpec
//}, false, "azm_tls.toml"),
//Entry("for common with tls case", func(output *obs.OutputSpec) {
// output.AzureMonitor.Tuning = baseTune
//}, true, "azm_tuning.toml"),
)
})
Loading