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

Add a configurable delay before writing a dynamic secret #477

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
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
5 changes: 5 additions & 0 deletions api/v1beta1/vaultdynamicsecret_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ type VaultDynamicSecretSpec struct {
RolloutRestartTargets []RolloutRestartTarget `json:"rolloutRestartTargets,omitempty"`
// Destination provides configuration necessary for syncing the Vault secret to Kubernetes.
Destination Destination `json:"destination"`
// Delay adds an artifical delay between fetching the secret and writing it to Kubernetes.
// This is to cater for services that are eventually consistent
// +kubebuilder:default=0
// +kubebuilder:validation:Minimum=0
Delay int `json:"delay,omitempty"`
}

// VaultDynamicSecretStatus defines the observed state of VaultDynamicSecret
Expand Down
7 changes: 7 additions & 0 deletions chart/crds/secrets.hashicorp.com_vaultdynamicsecrets.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,13 @@ spec:
roles", or "static credentials", with a request path that contains
"static-creds".
type: boolean
delay:
default: 0
description: Delay adds an artifical delay between fetching the secret
and writing it to Kubernetes. This is to cater for services that
are eventually consistent
minimum: 0
type: integer
destination:
description: Destination provides configuration necessary for syncing
the Vault secret to Kubernetes.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,13 @@ spec:
roles", or "static credentials", with a request path that contains
"static-creds".
type: boolean
delay:
default: 0
description: Delay adds an artifical delay between fetching the secret
and writing it to Kubernetes. This is to cater for services that
are eventually consistent
minimum: 0
type: integer
destination:
description: Destination provides configuration necessary for syncing
the Vault secret to Kubernetes.
Expand Down
4 changes: 4 additions & 0 deletions controllers/vaultdynamicsecret_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,10 @@ func (r *VaultDynamicSecretReconciler) syncSecret(ctx context.Context, c vault.C
}
}

if o.Spec.Delay > 0 {
sleepTime := time.Duration(o.Spec.Delay) * time.Second
time.Sleep(sleepTime)
}
if err := helpers.SyncSecret(ctx, r.Client, o, data); err != nil {
logger.Error(err, "Destination sync failed")
return nil, false, err
Expand Down
99 changes: 99 additions & 0 deletions controllers/vaultdynamicsecret_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -686,6 +686,105 @@ func Test_computeRotationTime(t *testing.T) {
}
}

func Test_artificialDelay(t *testing.T) {
type fields struct {
Client client.Client
runtimePodUID types.UID
}
type args struct {
ctx context.Context
vClient *vault.MockRecordingVaultClient
o *secretsv1beta1.VaultDynamicSecret
}
tests := []struct {
name string
fields fields
args args
want int
wantErr assert.ErrorAssertionFunc
}{
{
name: "no-delay",
fields: fields{
Client: fake.NewClientBuilder().Build(),
runtimePodUID: "",
},
args: args{
ctx: nil,
vClient: &vault.MockRecordingVaultClient{},
o: &secretsv1beta1.VaultDynamicSecret{
ObjectMeta: metav1.ObjectMeta{
Name: "baz",
Namespace: "default",
},
Spec: secretsv1beta1.VaultDynamicSecretSpec{
Mount: "baz",
Path: "foo",
Destination: secretsv1beta1.Destination{
Name: "baz",
Create: true,
},
Delay: 0,
},
Status: secretsv1beta1.VaultDynamicSecretStatus{},
},
},
wantErr: assert.NoError,
want: 0,
},
{
name: "with-ten-second-delay",
fields: fields{
Client: fake.NewClientBuilder().Build(),
runtimePodUID: "",
},
args: args{
ctx: nil,
vClient: &vault.MockRecordingVaultClient{},
o: &secretsv1beta1.VaultDynamicSecret{
ObjectMeta: metav1.ObjectMeta{
Name: "baz",
Namespace: "default",
},
Spec: secretsv1beta1.VaultDynamicSecretSpec{
Mount: "baz",
Path: "foo",
Destination: secretsv1beta1.Destination{
Name: "baz",
Create: true,
},
Delay: 10,
},

Status: secretsv1beta1.VaultDynamicSecretStatus{},
},
},
want: 10,
wantErr: assert.NoError,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := &VaultDynamicSecretReconciler{
Client: tt.fields.Client,
}
start := time.Now()
_, _, err := r.syncSecret(tt.args.ctx, tt.args.vClient, tt.args.o)
if !tt.wantErr(t, err, fmt.Sprintf("syncSecret(%v, %v, %v)", tt.args.ctx, tt.args.vClient, tt.args.o)) {
return
}

duration := time.Since(start).Round(time.Second).Seconds()

fmt.Println(duration)
if duration != float64(tt.want) {
fmt.Println(duration != float64(tt.want))
t.Error("Sleep length not correct")
}
})
}
}

func Test_computeRelativeHorizonWithJitter(t *testing.T) {
staticNow := time.Unix(nowFunc().Unix(), 0)
defaultNowFunc := func() time.Time { return staticNow }
Expand Down
1 change: 1 addition & 0 deletions docs/api/api-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,7 @@ _Appears in:_
| `allowStaticCreds` _boolean_ | AllowStaticCreds should be set when syncing credentials that are periodically rotated by the Vault server, rather than created upon request. These secrets are sometimes referred to as "static roles", or "static credentials", with a request path that contains "static-creds". |
| `rolloutRestartTargets` _[RolloutRestartTarget](#rolloutrestarttarget) array_ | RolloutRestartTargets should be configured whenever the application(s) consuming the Vault secret does not support dynamically reloading a rotated secret. In that case one, or more RolloutRestartTarget(s) can be configured here. The Operator will trigger a "rollout-restart" for each target whenever the Vault secret changes between reconciliation events. See RolloutRestartTarget for more details. |
| `destination` _[Destination](#destination)_ | Destination provides configuration necessary for syncing the Vault secret to Kubernetes. |
| `delay` _integer_ | Delay adds an artifical delay between fetching the secret and writing it to Kubernetes. This is to cater for services that are eventually consistent |



Expand Down