Skip to content

[CP-21861 CP-21958]: Modify Generate Command to Update Pod ConfigMap with Service Endpoints #33

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

Merged
merged 4 commits into from
Sep 20, 2024
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
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