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

Refactor stack monitoring templating #8327

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 8 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
2 changes: 1 addition & 1 deletion pkg/controller/beat/common/pod.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ func buildPodTemplate(
}

if monitoring.IsMetricsDefined(&params.Beat) {
sideCar, err := beat_stackmon.MetricBeat(params.Context, params.Client, &params.Beat, params.Beat.Spec.Version)
sideCar, err := beat_stackmon.MetricBeat(params.Context, params.Client, &params.Beat)
if err != nil {
return podTemplate, err
}
Expand Down
36 changes: 10 additions & 26 deletions pkg/controller/beat/common/stackmon/stackmon.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,19 @@
package stackmon

import (
"bytes"
"context"
_ "embed" // for the beats config files
"errors"
"fmt"
"text/template"

corev1 "k8s.io/api/core/v1"

"github.com/elastic/cloud-on-k8s/v2/pkg/apis/beat/v1beta1"
commonv1 "github.com/elastic/cloud-on-k8s/v2/pkg/apis/common/v1"
esv1 "github.com/elastic/cloud-on-k8s/v2/pkg/apis/elasticsearch/v1"
"github.com/elastic/cloud-on-k8s/v2/pkg/controller/association"
common_name "github.com/elastic/cloud-on-k8s/v2/pkg/controller/common/name"
"github.com/elastic/cloud-on-k8s/v2/pkg/controller/common/stackmon"
"github.com/elastic/cloud-on-k8s/v2/pkg/controller/common/stackmon/monitoring"
"github.com/elastic/cloud-on-k8s/v2/pkg/controller/common/version"
"github.com/elastic/cloud-on-k8s/v2/pkg/controller/elasticsearch/bootstrap"
"github.com/elastic/cloud-on-k8s/v2/pkg/utils/k8s"
)
Expand Down Expand Up @@ -60,7 +57,7 @@ func Filebeat(ctx context.Context, client k8s.Client, resource monitoring.HasMon
return sidecar, nil
}

func MetricBeat(ctx context.Context, client k8s.Client, beat *v1beta1.Beat, version string) (stackmon.BeatSidecar, error) {
func MetricBeat(ctx context.Context, client k8s.Client, beat *v1beta1.Beat) (stackmon.BeatSidecar, error) {
if err := beat.ElasticsearchRef().IsValid(); err != nil {
return stackmon.BeatSidecar{}, err
}
Expand All @@ -70,11 +67,6 @@ func MetricBeat(ctx context.Context, client k8s.Client, beat *v1beta1.Beat, vers
return stackmon.BeatSidecar{}, err
}

beatTemplate, err := template.New("beat_stack_monitoring").Parse(metricbeatConfigTemplate)
if err != nil {
return stackmon.BeatSidecar{}, fmt.Errorf("while parsing template for beats stack monitoring configuration: %w", err)
}
var byteBuffer bytes.Buffer
data := struct {
ClusterUUID string
URL string
Expand All @@ -84,24 +76,16 @@ func MetricBeat(ctx context.Context, client k8s.Client, beat *v1beta1.Beat, vers
// Beat module http options require "http+" to be appended to unix sockets.
URL: fmt.Sprintf("http+%s", GetStackMonitoringSocketURL(beat)),
}
if err := beatTemplate.Execute(&byteBuffer, data); err != nil {
return stackmon.BeatSidecar{}, fmt.Errorf("while templating beats stack monitoring configuration: %w", err)
v, err := version.Parse(beat.Spec.Version)
if err != nil {
return stackmon.BeatSidecar{}, err
}
cfg, err := stackmon.RenderTemplate(v, metricbeatConfigTemplate, data)
if err != nil {
return stackmon.BeatSidecar{}, err
}

sidecar, err := stackmon.NewMetricBeatSidecar(
ctx,
client,
commonv1.BeatMonitoringAssociationType,
beat,
version,
byteBuffer.String(),
common_name.NewNamer("beat"),
GetStackMonitoringSocketURL(beat),
"",
"",
"",
false,
)
sidecar, err := stackmon.NewMetricBeatSidecar(ctx, client, beat, v, nil, cfg)
if err != nil {
return stackmon.BeatSidecar{}, err
}
Expand Down
11 changes: 3 additions & 8 deletions pkg/controller/beat/common/stackmon/stackmon_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -250,9 +250,8 @@ output:
}

type args struct {
client k8s.Client
beat func() *v1beta1.Beat
version string
client k8s.Client
beat func() *v1beta1.Beat
}
tests := []struct {
name string
Expand All @@ -270,7 +269,6 @@ output:
beat: func() *v1beta1.Beat {
return &beatFixture
},
version: "8.2.3",
},
want: beatSidecarFixture(fmt.Sprintf(beatYml, "abcd1234", "es-metrics-monitoring-url")),
wantErr: false,
Expand All @@ -287,7 +285,6 @@ output:
beat.Spec.ElasticsearchRef = commonv1.ObjectSelector{}
return beat
},
version: "8.2.3",
},
want: beatSidecarFixture(standaloneBeatYML),
wantErr: false,
Expand Down Expand Up @@ -317,7 +314,6 @@ output:
}
return beat
},
version: "8.2.3",
},
want: beatSidecarFixture(fmt.Sprintf(beatYml, "QGq3wcU7Sd6bC31wh37eKQ", esAPIFixture.URL)),
wantErr: false,
Expand All @@ -331,15 +327,14 @@ output:
beat.Spec.Config.Data = map[string]interface{}{"http.port": "invalid"}
return beat
},
version: "8.2.3",
},
want: stackmon.BeatSidecar{},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := MetricBeat(context.Background(), tt.args.client, tt.args.beat(), tt.args.version)
got, err := MetricBeat(context.Background(), tt.args.client, tt.args.beat())
if (err != nil) != tt.wantErr {
t.Errorf("MetricBeat() error = %v, wantErr %v", err, tt.wantErr)
return
Expand Down
91 changes: 17 additions & 74 deletions pkg/controller/common/stackmon/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,10 @@ import (
"github.com/pkg/errors"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"

commonv1 "github.com/elastic/cloud-on-k8s/v2/pkg/apis/common/v1"
"github.com/elastic/cloud-on-k8s/v2/pkg/controller/association"
"github.com/elastic/cloud-on-k8s/v2/pkg/controller/common/certificates"
"github.com/elastic/cloud-on-k8s/v2/pkg/controller/common/name"
"github.com/elastic/cloud-on-k8s/v2/pkg/controller/common/settings"
"github.com/elastic/cloud-on-k8s/v2/pkg/controller/common/stackmon/monitoring"
"github.com/elastic/cloud-on-k8s/v2/pkg/controller/common/volume"
Expand Down Expand Up @@ -165,87 +163,32 @@ func mergeConfig(rawConfig string, config map[string]interface{}) ([]byte, error
return cfgBytes, nil
}

// inputConfigData holds data to configure the Metricbeat Elasticsearch and Kibana modules used
// to collect metrics for Stack Monitoring
type inputConfigData struct {
BasePath string
URL string
Username string
Password string
IsSSL bool
HasCA bool
CAPath string
Version semver.Version
TotalFieldsLimit int
func RenderTemplate(v semver.Version, configTemplate string, params any) (string, error) {
// render the config template with the config data
var beatConfig bytes.Buffer
err := template.Must(template.New("").Funcs(TemplateFuncs(v)).Parse(configTemplate)).Execute(&beatConfig, params)
if err != nil {
return "", err
}
return beatConfig.String(), nil
}

// buildMetricbeatBaseConfig builds the base configuration for Metricbeat with the Elasticsearch or Kibana modules used
// to collect metrics for Stack Monitoring
func buildMetricbeatBaseConfig(
client k8s.Client,
associationType commonv1.AssociationType,
nsn types.NamespacedName,
namer name.Namer,
url string,
basePath string,
username string,
password string,
isTLS bool,
configTemplate string,
func TemplateFuncs(
version semver.Version,
) (string, volume.VolumeLike, error) {
hasCA := false
if isTLS {
var err error
hasCA, err = certificates.PublicCertsHasCACert(client, namer, nsn.Namespace, nsn.Name)
if err != nil {
return "", nil, err
}
}

configData := inputConfigData{
Username: username,
Password: password,
URL: url, // Metricbeat in the sidecar connects to the monitored resource using `localhost`
BasePath: basePath, // for the Metricbeat Kibana module
IsSSL: isTLS, // enable SSL configuration based on whether the monitored resource has TLS enabled
HasCA: hasCA, // the CA is optional to support custom certificate issued by a well-known CA, so without provided CA to configure
Version: version, // Version of the monitored resource
}

// See https://github.com/elastic/cloud-on-k8s/pull/8284
// The default index template for metricbeat exceeds the default
// index mapping total fields limit.
if version.GTE(semver.MustParse("8.15.0")) {
configData.TotalFieldsLimit = 12500
}

var caVolume volume.VolumeLike
if configData.HasCA {
caVolume = volume.NewSecretVolumeWithMountPath(
certificates.PublicCertsSecretName(namer, nsn.Name),
fmt.Sprintf("%s-local-ca", string(associationType)),
fmt.Sprintf("/mnt/elastic-internal/%s/%s/%s/certs", string(associationType), nsn.Namespace, nsn.Name),
)

configData.CAPath = filepath.Join(caVolume.VolumeMount().MountPath, certificates.CAFileName)
}

templateFuncMap := template.FuncMap{
) template.FuncMap {
return template.FuncMap{
"isVersionGTE": func(minAllowedVersion string) (bool, error) {
minAllowedSemver, err := semver.Parse(minAllowedVersion)
if err != nil {
return false, err
}
return version.GTE(minAllowedSemver), nil
},
"CAPath": func(caVolume volume.VolumeLike) string {
if caVolume == nil {
return ""
}
return filepath.Join(caVolume.VolumeMount().MountPath, certificates.CAFileName)
},
}
// render the config template with the config data
var metricbeatConfig bytes.Buffer
err := template.Must(template.New("").Funcs(templateFuncMap).Parse(configTemplate)).Execute(&metricbeatConfig, configData)
if err != nil {
return "", nil, err
}

return metricbeatConfig.String(), caVolume, nil
}
Loading