@@ -12,13 +12,15 @@ import (
1212 "encoding/base64"
1313 "errors"
1414 "fmt"
15+ "time"
1516
1617 configModel "github.com/DataDog/datadog-agent/pkg/config/model"
1718 "github.com/DataDog/datadog-agent/pkg/config/setup"
1819 log "github.com/DataDog/datadog-agent/pkg/privateactionrunner/adapters/logging"
1920 "github.com/DataDog/datadog-agent/pkg/privateactionrunner/util"
2021 "github.com/DataDog/datadog-agent/pkg/util/kubernetes/apiserver"
2122 "github.com/DataDog/datadog-agent/pkg/util/kubernetes/apiserver/common/namespace"
23+ "github.com/DataDog/datadog-agent/pkg/util/kubernetes/apiserver/leaderelection"
2224
2325 corev1 "k8s.io/api/core/v1"
2426 k8serrors "k8s.io/apimachinery/pkg/api/errors"
@@ -27,9 +29,11 @@ import (
2729)
2830
2931const (
30- defaultSecretName = "private-action-runner-identity"
31- privateKeyField = "private_key"
32- urnField = "urn"
32+ defaultSecretName = "private-action-runner-identity"
33+ privateKeyField = "private_key"
34+ urnField = "urn"
35+ secretWaitTimeout = 5 * time .Minute
36+ secretWaitPollInterval = 5 * time .Second
3337)
3438
3539// getIdentityFromK8sSecret retrieves PAR identity from a Kubernetes secret
@@ -42,12 +46,28 @@ func getIdentityFromK8sSecret(ctx context.Context, cfg configModel.Reader) (*Per
4246 ns := namespace .GetResourcesNamespace ()
4347 secretName := getSecretName (cfg )
4448
49+ // Check if we're a follower - if so, we may need to wait for leader to create the secret
50+ le , err := leaderelection .GetLeaderEngine ()
51+ isFollower := err == nil && ! le .IsLeader ()
52+
4553 secret , err := client .CoreV1 ().Secrets (ns ).Get (ctx , secretName , metav1.GetOptions {})
4654 if err != nil {
4755 if k8serrors .IsNotFound (err ) {
48- return nil , nil
56+ // If we're a follower and secret doesn't exist, wait for leader to create it
57+ if isFollower {
58+ log .Info ("Follower replica: waiting for leader to create PAR identity secret" )
59+ secret , err = waitForSecretCreation (ctx , client , ns , secretName )
60+ if err != nil {
61+ return nil , err
62+ }
63+ // Continue to parse the secret below
64+ } else {
65+ // Leader or no leader election - return nil to trigger self-enrollment
66+ return nil , nil
67+ }
68+ } else {
69+ return nil , fmt .Errorf ("failed to get identity secret: %w" , err )
4970 }
50- return nil , fmt .Errorf ("failed to get identity secret: %w" , err )
5171 }
5272
5373 privateKey , ok := secret .Data [privateKeyField ]
@@ -70,6 +90,18 @@ func getIdentityFromK8sSecret(ctx context.Context, cfg configModel.Reader) (*Per
7090
7191// persistIdentityToK8sSecret saves the enrollment result to a Kubernetes secret
7292func persistIdentityToK8sSecret (ctx context.Context , cfg configModel.Reader , result * Result ) error {
93+ // Check if this replica is the leader before creating the secret
94+ le , err := leaderelection .GetLeaderEngine ()
95+ if err != nil {
96+ log .Warnf ("Failed to get leader engine, proceeding without leader check: %v" , err )
97+ // Fall through to create secret anyway if leader election is not available
98+ } else if ! le .IsLeader () {
99+ log .Info ("Not leader, skipping PAR identity secret creation (leader will handle it)" )
100+ return nil
101+ }
102+
103+ log .Info ("Leader replica: persisting PAR identity to K8s secret" )
104+
73105 client , err := getKubeClient ()
74106 if err != nil {
75107 return err
@@ -139,3 +171,32 @@ func getSecretName(cfg configModel.Reader) string {
139171 }
140172 return defaultSecretName
141173}
174+
175+ // waitForSecretCreation waits for the leader to create the PAR identity secret
176+ func waitForSecretCreation (ctx context.Context , client kubernetes.Interface , ns , secretName string ) (* corev1.Secret , error ) {
177+ deadline := time .Now ().Add (secretWaitTimeout )
178+ ticker := time .NewTicker (secretWaitPollInterval )
179+ defer ticker .Stop ()
180+
181+ for {
182+ select {
183+ case <- ctx .Done ():
184+ return nil , fmt .Errorf ("context cancelled while waiting for secret: %w" , ctx .Err ())
185+ case <- ticker .C :
186+ if time .Now ().After (deadline ) {
187+ return nil , fmt .Errorf ("timeout waiting for leader to create secret %s/%s after %v" , ns , secretName , secretWaitTimeout )
188+ }
189+
190+ secret , err := client .CoreV1 ().Secrets (ns ).Get (ctx , secretName , metav1.GetOptions {})
191+ if err == nil {
192+ log .Infof ("Follower replica: found PAR identity secret created by leader: %s/%s" , ns , secretName )
193+ return secret , nil
194+ }
195+
196+ if ! k8serrors .IsNotFound (err ) {
197+ return nil , fmt .Errorf ("error checking for secret: %w" , err )
198+ }
199+ // Secret still not found, continue waiting
200+ }
201+ }
202+ }
0 commit comments