Skip to content

Commit

Permalink
Merge pull request #33 from Cloudzero/cp-21861
Browse files Browse the repository at this point in the history
CP-21861: Modify Generate Command to Update Pod ConfigMap with Service Endpoints
  • Loading branch information
bdrennz authored Sep 20, 2024
2 parents b2304d9 + 111fb71 commit be18bf5
Show file tree
Hide file tree
Showing 5 changed files with 330 additions and 74 deletions.
81 changes: 50 additions & 31 deletions pkg/cmd/config/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ package config
import (
"context"
_ "embed"
"html/template"
"os"
"strings"
"text/template"

"github.com/pkg/errors"
"github.com/urfave/cli/v2"
Expand All @@ -15,13 +16,20 @@ import (
"github.com/cloudzero/cloudzero-agent-validator/pkg/util/gh"
)

//go:embed internal/template.yml
var templateString string
//go:embed internal/scrape_config.tmpl
var scrapeConfigTemplate string

var (
configAlias = []string{"f"}
)

type ScrapeConfigData struct {
Targets []string
ClusterName string
CloudAccountID string
Region string
}

func NewCommand(ctx context.Context) *cli.Command {
cmd := &cli.Command{
Name: "config",
Expand All @@ -34,11 +42,16 @@ func NewCommand(ctx context.Context) *cli.Command {
&cli.StringFlag{Name: config.FlagAccountID, Aliases: []string{"a"}, Usage: config.FlagDescAccountID, Required: true},
&cli.StringFlag{Name: config.FlagClusterName, Aliases: []string{"c"}, Usage: config.FlagDescClusterName, Required: true},
&cli.StringFlag{Name: config.FlagRegion, Aliases: []string{"r"}, Usage: config.FlagDescRegion, Required: true},
&cli.StringFlag{Name: config.FlagConfigFile, Aliases: configAlias, Usage: "output configuration file. if omitted output will print to standard out", Required: false},
&cli.StringFlag{Name: "kubeconfig", Usage: "absolute path to the kubeconfig file", Required: false},
&cli.StringFlag{Name: "namespace", Usage: "namespace of the cloudzero-agent pod", Required: true},
&cli.StringFlag{Name: "configmap", Usage: "name of the ConfigMap", Required: true},
&cli.StringFlag{Name: "pod", Usage: "name of the cloudzero-agent pod", Required: true},
},
Action: func(c *cli.Context) error {
kubeconfigPath := c.String("kubeconfig")
namespace := c.String("namespace")
configMapName := c.String("configmap")

clientset, err := k8s.BuildKubeClient(kubeconfigPath)
if err != nil {
return err
Expand All @@ -49,16 +62,29 @@ func NewCommand(ctx context.Context) *cli.Command {
return err
}

return Generate(map[string]interface{}{ //nolint: gofmt
"ChartVerson": getCurrentChartVersion(),
"AgentVersion": getCurrentAgentVersion(),
"AccountID": c.String(config.FlagAccountID),
"ClusterName": c.String(config.FlagClusterName),
"Region": c.String(config.FlagRegion),
"CloudzeroHost": build.PlatformEndpoint,
"KubeStateMetricsURL": kubeStateMetricsURL,
"PromNodeExporterURL": nodeExporterURL,
}, c.String(config.FlagConfigFile))
targets := []string{kubeStateMetricsURL, nodeExporterURL}
scrapeConfigData := ScrapeConfigData{
Targets: targets,
ClusterName: c.String(config.FlagClusterName),
CloudAccountID: c.String(config.FlagAccountID),
Region: c.String(config.FlagRegion),
}

configContent, err := Generate(scrapeConfigData)
if err != nil {
return err
}

configMapData := map[string]string{
"prometheus.yml": configContent,
}

err = k8s.UpdateConfigMap(ctx, clientset, namespace, configMapName, configMapData)
if err != nil {
return err
}

return nil
},
},
{
Expand Down Expand Up @@ -92,26 +118,19 @@ func NewCommand(ctx context.Context) *cli.Command {
return cmd
}

func Generate(values map[string]interface{}, outputFile string) error { //nolint: gofmt
t := template.New("template")
t, err := t.Parse(templateString)
func Generate(data ScrapeConfigData) (string, error) {
t, err := template.New("scrape_config").Parse(scrapeConfigTemplate)
if err != nil {
return errors.Wrap(err, "template parser")
return "", errors.Wrap(err, "template parser")
}
var cleanup = func() {}
out := os.Stdout
if outputFile != "" {
output, err := os.Create(outputFile)
if err != nil {
return errors.Wrap(err, "creating output file")
}
cleanup = func() {
output.Close()
}
out = output

var result strings.Builder
err = t.Execute(&result, data)
if err != nil {
return "", errors.Wrap(err, "executing template")
}
defer cleanup()
return t.Execute(out, values)

return result.String(), nil
}

func getCurrentChartVersion() string {
Expand Down
54 changes: 32 additions & 22 deletions pkg/cmd/config/command_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,34 +47,44 @@ func TestGenerate(t *testing.T) {
kubeStateMetricsURL, nodeExporterURL, err := k8s.GetServiceURLs(ctx, clientset)
assert.NoError(t, err)

values := map[string]interface{}{
"ChartVerson": "1.0.0",
"AgentVersion": "1.0.0",
"AccountID": "123456789",
"ClusterName": "test-cluster",
"Region": "us-west-2",
"CloudzeroHost": "https://cloudzero.com",
"KubeStateMetricsURL": kubeStateMetricsURL,
"PromNodeExporterURL": nodeExporterURL,
// Define the scrape config data
scrapeConfigData := config.ScrapeConfigData{
Targets: []string{kubeStateMetricsURL, nodeExporterURL},
ClusterName: "test-cluster",
CloudAccountID: "123456789",
Region: "us-west-2",
}

outputFile := "test_output.yml"

err = config.Generate(values, outputFile)
// Generate the configuration content
configContent, err := config.Generate(scrapeConfigData)
assert.NoError(t, err)
assert.NotEmpty(t, configContent)

// Verify that the output file exists
_, err = os.Stat(outputFile)
assert.NoError(t, err)
// Validate the dynamically populated values
assert.Contains(t, configContent, kubeStateMetricsURL)
assert.Contains(t, configContent, nodeExporterURL)
assert.Contains(t, configContent, "cluster_name=test-cluster")
assert.Contains(t, configContent, "cloud_account_id=123456789")
assert.Contains(t, configContent, "region=us-west-2")

// Read the contents of the output file
content, err := os.ReadFile(outputFile)
assert.NoError(t, err)
assert.NotEmpty(t, content)
// Define the ConfigMap data
configMapData := map[string]string{
"prometheus.yml": configContent,
}

// TODO: Add assertions to validate the content of the output file
// Update the ConfigMap
err = k8s.UpdateConfigMap(ctx, clientset, "default", "test-configmap", configMapData)
assert.NoError(t, err)

// Clean up the output file
err = os.Remove(outputFile)
// Verify the ConfigMap was updated
updatedConfigMap, err := clientset.CoreV1().ConfigMaps("default").Get(ctx, "test-configmap", metav1.GetOptions{})
assert.NoError(t, err)
assert.Equal(t, configContent, updatedConfigMap.Data["prometheus.yml"])

// Clean up the output file if it exists
outputFile := "test_output.yml"
if _, err := os.Stat(outputFile); err == nil {
err = os.Remove(outputFile)
assert.NoError(t, err)
}
}
144 changes: 144 additions & 0 deletions pkg/cmd/config/internal/scrape_config.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
global:
scrape_interval: 1m
scrape_timeout: 10s
scrape_protocols:
- OpenMetricsText1.0.0
- OpenMetricsText0.0.1
- PrometheusText0.0.4
evaluation_interval: 1m
scrape_configs:
- job_name: cloudzero-nodes-cadvisor
honor_timestamps: true
track_timestamps_staleness: false
scrape_interval: 1m
scrape_timeout: 10s
scrape_protocols:
- OpenMetricsText1.0.0
- OpenMetricsText0.0.1
- PrometheusText0.0.4
metrics_path: /metrics
scheme: https
enable_compression: true
authorization:
type: Bearer
credentials_file: /var/run/secrets/kubernetes.io/serviceaccount/token
tls_config:
ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
insecure_skip_verify: true
follow_redirects: true
enable_http2: true
relabel_configs:
- separator: ;
regex: __meta_kubernetes_node_label_(.+)
replacement: $1
action: labelmap
- separator: ;
regex: (.*)
target_label: __address__
replacement: kubernetes.default.svc:443
action: replace
- source_labels: [__meta_kubernetes_node_name]
separator: ;
regex: (.+)
target_label: __metrics_path__
replacement: /api/v1/nodes/$1/proxy/metrics/cadvisor
action: replace
- source_labels: [__meta_kubernetes_node_name]
separator: ;
regex: (.*)
target_label: node
replacement: $1
action: replace
metric_relabel_configs:
- separator: ;
regex: ^(board_asset_tag|container|created_by_kind|created_by_name|image|instance|name|namespace|node|node_kubernetes_io_instance_type|pod|product_name|provider_id|resource|unit|uid|_.*|label_.*|app.kubernetes.io/*|k8s.*)$
replacement: $1
action: labelkeep
- source_labels: [__name__]
separator: ;
regex: ^(container_cpu_usage_seconds_total|container_memory_working_set_bytes|container_network_receive_bytes_total|container_network_transmit_bytes_total)$
replacement: $1
action: keep
kubernetes_sd_configs:
- role: node
kubeconfig_file: ""
follow_redirects: true
enable_http2: true
- job_name: static-kube-state-metrics
honor_timestamps: true
track_timestamps_staleness: false
scrape_interval: 1m
scrape_timeout: 10s
scrape_protocols:
- OpenMetricsText1.0.0
- OpenMetricsText0.0.1
- PrometheusText0.0.4
metrics_path: /metrics
scheme: http
enable_compression: true
follow_redirects: true
enable_http2: true
relabel_configs:
- separator: ;
regex: __meta_kubernetes_service_label_(.+)
replacement: $1
action: labelmap
- source_labels: [__meta_kubernetes_namespace]
separator: ;
regex: (.*)
target_label: namespace
replacement: $1
action: replace
- source_labels: [__meta_kubernetes_service_name]
separator: ;
regex: (.*)
target_label: service
replacement: $1
action: replace
- source_labels: [__meta_kubernetes_pod_node_name]
separator: ;
regex: (.*)
target_label: node
replacement: $1
action: replace
metric_relabel_configs:
- source_labels: [__name__]
separator: ;
regex: ^(kube_node_info|kube_node_status_capacity|kube_pod_container_resource_limits|kube_pod_container_resource_requests|kube_pod_labels|kube_pod_info|node_dmi_info)$
replacement: $1
action: keep
- separator: ;
regex: ^(board_asset_tag|container|created_by_kind|created_by_name|image|instance|name|namespace|node|node_kubernetes_io_instance_type|pod|product_name|provider_id|resource|unit|uid|_.*|label_.*|app.kubernetes.io/*|k8s.*)$
replacement: $1
action: labelkeep
static_configs:
- targets:
{{- range .Targets }}
- {{ . }}
{{- end }}
remote_write:
- url: https://api.cloudzero.com/v1/container-metrics?cluster_name={{ .ClusterName }}&cloud_account_id={{ .CloudAccountID }}&region={{ .Region }}
remote_timeout: 30s
write_relabel_configs:
- source_labels: [__name__]
separator: ;
regex: ^(kube_node_info|kube_node_status_capacity|kube_pod_container_resource_limits|kube_pod_container_resource_requests|kube_pod_labels|kube_pod_info|node_dmi_info|container_cpu_usage_seconds_total|container_memory_working_set_bytes|container_network_receive_bytes_total|container_network_transmit_bytes_total)$
replacement: $1
action: keep
authorization:
type: Bearer
credentials_file: /etc/config/prometheus/secrets/value
follow_redirects: true
enable_http2: true
queue_config:
capacity: 10000
max_shards: 50
min_shards: 1
max_samples_per_send: 2000
batch_send_deadline: 5s
min_backoff: 30ms
max_backoff: 5s
metadata_config:
send: false
send_interval: 1m
max_samples_per_send: 2000
Loading

0 comments on commit be18bf5

Please sign in to comment.