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

Rename Configuration Environment Variables #4

Merged
merged 1 commit into from
Jul 24, 2023
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
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,10 @@ helm upgrade --install parca-operator ricoberger/parca-operator

Make sure that you set the following environment variables for the Parca Operator:

- `PARCA_OPERATOR_CONFIG`: The path to the configration file for Parca. This file is used as base for the generated Parca configration and should contain you `object_storage` configuration.
- `PARCA_OPERATOR_CONFIG_NAME`: The name of secret which should be generated. The secret contains a `parca.yaml` key with the generated configuration for Parca.
- `PARCA_OPERATOR_CONFIG_NAMESPACE`: The namespace of secret which should be generated. The secret contains a `parca.yaml` key with the generated configuration for Parca.
- `PARCA_SCRAPECONFIG_RECONCILIATION_INTERVAL`: The reconciliation interval for the `ParcaScrapeConfig` controller. If this value is not set the `ParcaScrapeConfig` CRs will be reconciled every 5 minutes. This must be a value which can be parsed via the [`ParseDuration`](https://pkg.go.dev/time#ParseDuration) function.
- `PARCA_SCRAPECONFIG_BASE_CONFIG`: The path to the configration file for Parca. This file is used as base for the generated Parca configration and should contain you `object_storage` configuration.
- `PARCA_SCRAPECONFIG_FINAL_CONFIG_NAME`: The name of secret which should be generated. The secret contains a `parca.yaml` key with the generated configuration for Parca.
- `PARCA_SCRAPECONFIG_FINAL_CONFIG_NAMESPACE`: The namespace of secret which should be generated. The secret contains a `parca.yaml` key with the generated configuration for Parca.

## API Reference

Expand Down
4 changes: 2 additions & 2 deletions charts/parca-operator/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ type: application
# This is the chart version. This version number should be incremented each time you make changes
# to the chart and its templates, including the app version.
# Versions are expected to follow Semantic Versioning (https://semver.org/)
version: 0.1.0
version: 0.2.0

# This is the version number of the application being deployed. This version number should be
# incremented each time you make changes to the application. Versions are not expected to
# follow Semantic Versioning. They should reflect the version the application is using.
# It is recommended to use it with quotes.
appVersion: "v0.1.0"
appVersion: "v0.2.0"
8 changes: 5 additions & 3 deletions charts/parca-operator/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -112,11 +112,13 @@ env:
## The following environment variables are required for the Parca Operator to read the base configration and to create
## the final configration for Parca:
##
- name: PARCA_OPERATOR_CONFIG
- name: PARCA_SCRAPECONFIG_RECONCILIATION_INTERVAL
value: 5m
- name: PARCA_SCRAPECONFIG_BASE_CONFIG
value: /etc/parca/parca.yaml
- name: PARCA_OPERATOR_CONFIG_NAME
- name: PARCA_SCRAPECONFIG_FINAL_CONFIG_NAME
value: parca-generated
- name: PARCA_OPERATOR_CONFIG_NAMESPACE
- name: PARCA_SCRAPECONFIG_FINAL_CONFIG_NAMESPACE
value: parca

## Specify additional labels and annotations for the created Pods.
Expand Down
33 changes: 17 additions & 16 deletions controllers/parcascrapeconfig_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ type ParcaScrapeConfigReconciler struct {
// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.14.1/pkg/reconcile
func (r *ParcaScrapeConfigReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
reqLogger := log.FromContext(ctx)
reconciliationInterval := scrapeconfigs.GetReconciliationInterval()

parcaScrapeConfig := &parcav1alpha1.ParcaScrapeConfig{}
err := r.Get(ctx, req.NamespacedName, parcaScrapeConfig)
Expand All @@ -62,7 +63,7 @@ func (r *ParcaScrapeConfigReconciler) Reconcile(ctx context.Context, req ctrl.Re
}
// Error reading the object - requeue the request.
reqLogger.Error(err, "Failed to get ParcaScrapeConfig.")
return ctrl.Result{RequeueAfter: 5 * time.Minute}, err
return ctrl.Result{RequeueAfter: reconciliationInterval}, err
}

// When the ParcaScrapeConfig is marked for deletion, we remove it from the Parca configuration, before we remove
Expand All @@ -76,20 +77,20 @@ func (r *ParcaScrapeConfigReconciler) Reconcile(ctx context.Context, req ctrl.Re
if err != nil {
reqLogger.Error(err, "Failed to delete ParcaScrapeConfig.")
r.updateConditions(ctx, parcaScrapeConfig, parcaScrapeConfigDeleteFailed, err.Error(), metav1.ConditionFalse, nil)
return ctrl.Result{RequeueAfter: 5 * time.Minute}, err
return ctrl.Result{RequeueAfter: reconciliationInterval}, err
}

finalConfig, err := scrapeconfigs.GetConfig()
if err != nil {
reqLogger.Error(err, "Failed to get Parca configuration.")
r.updateConditions(ctx, parcaScrapeConfig, parcaScrapeConfigUpdateFailed, err.Error(), metav1.ConditionFalse, nil)
return ctrl.Result{RequeueAfter: 5 * time.Minute}, err
return ctrl.Result{RequeueAfter: reconciliationInterval}, err
}

err = r.Update(ctx, &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: os.Getenv("PARCA_OPERATOR_CONFIG_NAME"),
Namespace: os.Getenv("PARCA_OPERATOR_CONFIG_NAMESPACE"),
Name: os.Getenv("PARCA_SCRAPECONFIG_FINAL_CONFIG_NAME"),
Namespace: os.Getenv("PARCA_SCRAPECONFIG_FINAL_CONFIG_NAMESPACE"),
},
Data: map[string][]byte{
"parca.yaml": finalConfig,
Expand All @@ -98,7 +99,7 @@ func (r *ParcaScrapeConfigReconciler) Reconcile(ctx context.Context, req ctrl.Re
if err != nil {
reqLogger.Error(err, "Failed to update Parca configuration.")
r.updateConditions(ctx, parcaScrapeConfig, parcaScrapeConfigUpdateFailed, err.Error(), metav1.ConditionFalse, nil)
return ctrl.Result{RequeueAfter: 5 * time.Minute}, err
return ctrl.Result{RequeueAfter: reconciliationInterval}, err
}

// Remove the parcaScrapeConfigFinalizer. Once the finalizer is removed the object will be deleted.
Expand All @@ -107,7 +108,7 @@ func (r *ParcaScrapeConfigReconciler) Reconcile(ctx context.Context, req ctrl.Re
if err != nil {
reqLogger.Error(err, "Failed to remove finalizer.")
r.updateConditions(ctx, parcaScrapeConfig, parcaScrapeConfigDeleteFailed, err.Error(), metav1.ConditionFalse, nil)
return ctrl.Result{RequeueAfter: 5 * time.Minute}, err
return ctrl.Result{RequeueAfter: reconciliationInterval}, err
}
}

Expand All @@ -120,7 +121,7 @@ func (r *ParcaScrapeConfigReconciler) Reconcile(ctx context.Context, req ctrl.Re
if err != nil {
reqLogger.Error(err, "Failed to build labelSelector for ParcaScrapeConfig.")
r.updateConditions(ctx, parcaScrapeConfig, parcaScrapeConfigUpdateFailed, err.Error(), metav1.ConditionFalse, nil)
return ctrl.Result{RequeueAfter: 5 * time.Minute}, err
return ctrl.Result{RequeueAfter: reconciliationInterval}, err
}

pods := &corev1.PodList{}
Expand All @@ -131,14 +132,14 @@ func (r *ParcaScrapeConfigReconciler) Reconcile(ctx context.Context, req ctrl.Re
if err != nil {
reqLogger.Error(err, "Failed to get Pods for ParcaScrapeConfig.")
r.updateConditions(ctx, parcaScrapeConfig, parcaScrapeConfigUpdateFailed, err.Error(), metav1.ConditionFalse, nil)
return ctrl.Result{RequeueAfter: 5 * time.Minute}, err
return ctrl.Result{RequeueAfter: reconciliationInterval}, err
}

podIPs, err := scrapeconfigs.SetScrapeConfig(*parcaScrapeConfig, pods.Items)
if err != nil {
reqLogger.Error(err, "Failed to update internal Parca configuration.")
r.updateConditions(ctx, parcaScrapeConfig, parcaScrapeConfigUpdateFailed, err.Error(), metav1.ConditionFalse, nil)
return ctrl.Result{RequeueAfter: 5 * time.Minute}, err
return ctrl.Result{RequeueAfter: reconciliationInterval}, err
}

// When the Parca configuration was updated, we can update the Parca configuration secret to include the new scrape
Expand All @@ -147,13 +148,13 @@ func (r *ParcaScrapeConfigReconciler) Reconcile(ctx context.Context, req ctrl.Re
if err != nil {
reqLogger.Error(err, "Failed to get Parca configuration.")
r.updateConditions(ctx, parcaScrapeConfig, parcaScrapeConfigUpdateFailed, err.Error(), metav1.ConditionFalse, nil)
return ctrl.Result{RequeueAfter: 5 * time.Minute}, err
return ctrl.Result{RequeueAfter: reconciliationInterval}, err
}

err = r.Update(ctx, &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: os.Getenv("PARCA_OPERATOR_CONFIG_NAME"),
Namespace: os.Getenv("PARCA_OPERATOR_CONFIG_NAMESPACE"),
Name: os.Getenv("PARCA_SCRAPECONFIG_FINAL_CONFIG_NAME"),
Namespace: os.Getenv("PARCA_SCRAPECONFIG_FINAL_CONFIG_NAMESPACE"),
},
Data: map[string][]byte{
"parca.yaml": finalConfig,
Expand All @@ -162,7 +163,7 @@ func (r *ParcaScrapeConfigReconciler) Reconcile(ctx context.Context, req ctrl.Re
if err != nil {
reqLogger.Error(err, "Failed to update Parca configuration.")
r.updateConditions(ctx, parcaScrapeConfig, parcaScrapeConfigUpdateFailed, err.Error(), metav1.ConditionFalse, nil)
return ctrl.Result{RequeueAfter: 5 * time.Minute}, err
return ctrl.Result{RequeueAfter: reconciliationInterval}, err
}

// Finally we add the parcaScrapeConfigFinalizer to the ParcaScrapeConfig. The finilizer is needed so that we can
Expand All @@ -173,13 +174,13 @@ func (r *ParcaScrapeConfigReconciler) Reconcile(ctx context.Context, req ctrl.Re
if err != nil {
reqLogger.Error(err, "Failed to add finalizer.")
r.updateConditions(ctx, parcaScrapeConfig, parcaScrapeConfigUpdateFailed, err.Error(), metav1.ConditionFalse, nil)
return ctrl.Result{RequeueAfter: 5 * time.Minute}, err
return ctrl.Result{RequeueAfter: reconciliationInterval}, err
}
}

reqLogger.Info("ParcaScrapeConfig updated.")
r.updateConditions(ctx, parcaScrapeConfig, parcaScrapeConfigUpdated, "Parca Configuration was updated", metav1.ConditionTrue, podIPs)
return ctrl.Result{RequeueAfter: 5 * time.Minute}, nil
return ctrl.Result{RequeueAfter: reconciliationInterval}, nil
}

func (r *ParcaScrapeConfigReconciler) updateConditions(ctx context.Context, parcaScrapeConfig *parcav1alpha1.ParcaScrapeConfig, reason, message string, status metav1.ConditionStatus, podIPs []string) {
Expand Down
58 changes: 44 additions & 14 deletions controllers/scrapeconfigs/scrapeconfigs.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"os"
"strings"
"time"

parcav1alpha1 "github.com/ricoberger/parca-operator/api/v1alpha1"

Expand All @@ -31,13 +32,29 @@ type ParcaObjectStorage struct {
} `json:"bucket,omitempty"`
}

var baseConfig ParcaConfig
var finalConfig ParcaConfig
var (
reconciliationInterval time.Duration
baseConfig ParcaConfig
finalConfig ParcaConfig
)

// Init reads the base configuration for Parca and the final configuration. It then merges the base configuration with
// the final configuration and applies the final configuration.
// Init is responsible to initialise the scrapeconfigs package. For that it reads all environment variables starting
// with "PARCA_SCRAPECONFIG_" and uses them to configure the package.
func Init() error {
baseConfigContent, err := os.ReadFile(os.Getenv("PARCA_OPERATOR_CONFIG"))
// Read the "PARCA_SCRAPECONFIG_RECONCILIATION_INTERVAL" environment variable if set. If not set the default value
// of 5 minutes is used for the reconciliation interval. If the value is seet and can be parsed as a time.Duration
// the parsed value is used.
reconciliationInterval = 5 * time.Minute
if os.Getenv("PARCA_SCRAPECONFIG_RECONCILIATION_INTERVAL") != "" {
if parsedReconciliationInterval, err := time.ParseDuration(os.Getenv("PARCA_SCRAPECONFIG_RECONCILIATION_INTERVAL")); err == nil {
reconciliationInterval = parsedReconciliationInterval
}
}

// Read the "PARCA_SCRAPECONFIG_BASE_CONFIG" environment variable. This variable must point to a file which contains
// the base configuration for Parca. After the file was read we expand all environment variables in the file. Then
// we unmarshal the YAML content into the "baseConfig" variable.
baseConfigContent, err := os.ReadFile(os.Getenv("PARCA_SCRAPECONFIG_BASE_CONFIG"))
if err != nil {
return err
}
Expand All @@ -47,25 +64,30 @@ func Init() error {
return err
}

cfg, err := config.GetConfig()
// Create a new Kubernetes client "c" which is used to interact with the Kubernetes API. This is needed because, we
// might have an existing final configuration which we need to update.
restConfig, err := config.GetConfig()
if err != nil {
return err
}

c, err := client.New(cfg, client.Options{})
c, err := client.New(restConfig, client.Options{})
if err != nil {
return err
}

// Read the existing final configuration from the Kubernetes API. If there is no existing final configuration we use
// the base configuration as the final configuration and create a new Kubernetes Secret with the content of the base
// configuration.
existingConfigSecret := &corev1.Secret{}
err = c.Get(context.Background(), types.NamespacedName{Name: os.Getenv("PARCA_OPERATOR_CONFIG_NAME"), Namespace: os.Getenv("PARCA_OPERATOR_CONFIG_NAMESPACE")}, existingConfigSecret)
err = c.Get(context.Background(), types.NamespacedName{Name: os.Getenv("PARCA_SCRAPECONFIG_FINAL_CONFIG_NAME"), Namespace: os.Getenv("PARCA_SCRAPECONFIG_FINAL_CONFIG_NAMESPACE")}, existingConfigSecret)
if err != nil {
if errors.IsNotFound(err) {
finalConfig = baseConfig
err := c.Create(context.Background(), &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: os.Getenv("PARCA_OPERATOR_CONFIG_NAME"),
Namespace: os.Getenv("PARCA_OPERATOR_CONFIG_NAMESPACE"),
Name: os.Getenv("PARCA_SCRAPECONFIG_FINAL_CONFIG_NAME"),
Namespace: os.Getenv("PARCA_SCRAPECONFIG_FINAL_CONFIG_NAMESPACE"),
},
Data: map[string][]byte{
"parca.yaml": baseConfigContent,
Expand All @@ -75,11 +97,14 @@ func Init() error {
return err
}
return nil
} else {
return err
}

return err
}

// If there is an existing final configuration we unmarshal the content of the "parca.yaml" key into the
// "finalConfig" variable. Then we apply all changes from the base configuration to the final configuration and
// update the existing Kubernetes Secret with the new content.
if err := yaml.Unmarshal(existingConfigSecret.Data["parca.yaml"], &finalConfig); err != nil {
return err
}
Expand All @@ -95,8 +120,8 @@ func Init() error {

err = c.Update(context.Background(), &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: os.Getenv("PARCA_OPERATOR_CONFIG_NAME"),
Namespace: os.Getenv("PARCA_OPERATOR_CONFIG_NAMESPACE"),
Name: os.Getenv("PARCA_SCRAPECONFIG_FINAL_CONFIG_NAME"),
Namespace: os.Getenv("PARCA_SCRAPECONFIG_FINAL_CONFIG_NAMESPACE"),
},
Data: map[string][]byte{
"parca.yaml": finalbaseConfigContent,
Expand Down Expand Up @@ -192,3 +217,8 @@ func DeleteScrapeConfig(scrapeConfig parcav1alpha1.ParcaScrapeConfig) error {
func GetConfig() ([]byte, error) {
return yaml.Marshal(finalConfig)
}

// GetReconciliationInterval returns the configured reconciliation interval.
func GetReconciliationInterval() time.Duration {
return reconciliationInterval
}
2 changes: 1 addition & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ func init() {

func main() {
var metricsAddr string
var enableLeaderElection bool
var probeAddr string
var enableLeaderElection bool

flag.StringVar(&metricsAddr, "metrics-bind-address", ":8080", "The address the metric endpoint binds to.")
flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.")
Expand Down