Skip to content

Commit

Permalink
Fix Config Generation when Pod has no IP Address (#6)
Browse files Browse the repository at this point in the history
When a Pod was already created, but doesn't had an IP address assigned
yet, the configuration only contained the port number instead of the
full address of the Pod. We are no checking that the Pod has an IP
address assigned and if this is not the case we skip the Pod.

We also improved the update logic of the generated secret with the Parca
configuration. Therefor we are checking the the list of Pod IPs is not
empty and not equal to the list of Pod IPs saved in the status field of
the ParcaScrapeConfig CR. Only when the Pod IPs are not equal to the
saved list of Pod IPs we will update the configuration. For all other
cases we skip the update.
  • Loading branch information
ricoberger committed Jul 24, 2023
1 parent 1ea7d6d commit 630d87a
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 14 deletions.
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,9 +114,10 @@ This Makefile target will invoke controller-gen to generate the CRD manifests at
Deploy the CRD and run the operator locally with the default Kubernetes config file present at `$HOME/.kube/config`:

```sh
export PARCA_OPERATOR_CONFIG=parca.yaml
export PARCA_OPERATOR_CONFIG_NAME=parca-generated
export PARCA_OPERATOR_CONFIG_NAMESPACE=parca
export PARCA_SCRAPECONFIG_RECONCILIATION_INTERVAL=1m
export PARCA_SCRAPECONFIG_BASE_CONFIG=parca.yaml
export PARCA_SCRAPECONFIG_FINAL_CONFIG_NAME=parca-generated
export PARCA_SCRAPECONFIG_FINAL_CONFIG_NAMESPACE=parca

make run
```
47 changes: 36 additions & 11 deletions controllers/parcascrapeconfig/parcascrapeconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,18 +152,26 @@ func SetScrapeConfig(scrapeConfig parcav1alpha1.ParcaScrapeConfig, pods []corev1
container := getContainerName(scrapeConfig.Spec.Port, pods)

for _, pod := range pods {
scrapeConfig.Spec.ScrapeConfig.StaticConfigs = append(scrapeConfig.Spec.ScrapeConfig.StaticConfigs, parcav1alpha1.StaticConfig{
Targets: []string{fmt.Sprintf("%s:%d", pod.Status.PodIP, scrapeConfig.Spec.Port)},
Labels: map[string]string{
"name": scrapeConfig.Name,
"namespace": scrapeConfig.Namespace,
"pod": pod.Name,
"container": container,
"node": pod.Spec.NodeName,
},
})
if pod.Status.PodIP != "" {
scrapeConfig.Spec.ScrapeConfig.StaticConfigs = append(scrapeConfig.Spec.ScrapeConfig.StaticConfigs, parcav1alpha1.StaticConfig{
Targets: []string{fmt.Sprintf("%s:%d", pod.Status.PodIP, scrapeConfig.Spec.Port)},
Labels: map[string]string{
"name": scrapeConfig.Name,
"namespace": scrapeConfig.Namespace,
"pod": pod.Name,
"container": container,
"node": pod.Spec.NodeName,
},
})

podIPs = append(podIPs, fmt.Sprintf("%s:%d", pod.Status.PodIP, scrapeConfig.Spec.Port))
}
}

podIPs = append(podIPs, fmt.Sprintf("%s:%d", pod.Status.PodIP, scrapeConfig.Spec.Port))
// When there are not Pod IPs or the Pod IPs are equal to the existing Pod IPs saved in the status of the CR we can
// return nil and don't need to update the final configuration.
if len(podIPs) == 0 || testPodIPsEq(podIPs, scrapeConfig.Status.PodIPs) {
return nil, nil
}

for i := 0; i < len(finalConfig.ScrapeConfigs); i++ {
Expand Down Expand Up @@ -192,6 +200,23 @@ func getContainerName(port int32, pods []corev1.Pod) string {
return "unknown"
}

// testPodIPsEq tests if the provided slices of Pod IPs are equal. We can simply check the length of the slices and
// then iterate over the slices and compare the values, because the Kubernetes API will return the Pods always in the
// same order.
func testPodIPsEq(a, b []string) bool {
if len(a) != len(b) {
return false
}

for i := range a {
if a[i] != b[i] {
return false
}
}

return true
}

// DeleteScrapeConfig deletes the scrape configuration for the provided ParcaScrapeConfig.
func DeleteScrapeConfig(scrapeConfig parcav1alpha1.ParcaScrapeConfig) error {
if scrapeConfig.Spec.ScrapeConfig.JobName == "" {
Expand Down
8 changes: 8 additions & 0 deletions controllers/parcascrapeconfig_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,14 @@ func (r *ParcaScrapeConfigReconciler) Reconcile(ctx context.Context, req ctrl.Re
return ctrl.Result{RequeueAfter: reconciliationInterval}, err
}

// If the SetScrapeConfig function returns nil, we don't need to update the Parca configuration secret, because no
// Pods with an IP where found or the Pods where not changed since the last reconciliation loop.
if podIPs == nil {
reqLogger.Info("ParcaScrapeConfig must not be updated.")
r.updateConditions(ctx, parcaScrapeConfig, parcaScrapeConfigUpdated, "Parca Configuration must not be updated", metav1.ConditionTrue, parcaScrapeConfig.Status.PodIPs)
return ctrl.Result{RequeueAfter: reconciliationInterval}, nil
}

// When the Parca configuration was updated, we can update the Parca configuration secret to include the new scrape
// configuration.
finalConfig, err := parcascrapeconfig.GetConfig()
Expand Down

0 comments on commit 630d87a

Please sign in to comment.