Skip to content
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
40 changes: 40 additions & 0 deletions api/v1alpha1/utils/vaultobject.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,46 @@ func (ve *VaultEndpoint) Create(context context.Context) error {
return write(context, ve.vaultObject.GetPath(), ve.vaultObject.GetPayload())
}

// CreateOrMergeKV reads existing KV secret data and merges the new payload keys into it.
// This allows multiple resources to contribute different keys to the same Vault secret path.
// For KVv2, it expects the payload in {"data": {...}} format.
func (ve *VaultEndpoint) CreateOrMergeKV(context context.Context, isKVv2 bool) error {
log := log.FromContext(context)
currentPayload, found, err := read(context, ve.vaultObject.GetPath())
if err != nil {
log.Error(err, "unable to read object at", "path", ve.vaultObject.GetPath())
return err
}
if !found {
return write(context, ve.vaultObject.GetPath(), ve.vaultObject.GetPayload())
}
// Merge new keys into existing data
newPayload := ve.vaultObject.GetPayload()
if isKVv2 {
// For KVv2, data is nested under "data" key
existingData, ok := currentPayload["data"].(map[string]interface{})
if !ok {
existingData = make(map[string]interface{})
}
newData, ok := newPayload["data"].(map[string]interface{})
if !ok {
return write(context, ve.vaultObject.GetPath(), newPayload)
}
for k, v := range newData {
existingData[k] = v
}
mergedPayload := map[string]interface{}{
"data": existingData,
}
return write(context, ve.vaultObject.GetPath(), mergedPayload)
}
// For KVv1, merge directly
for k, v := range newPayload {
currentPayload[k] = v
}
return write(context, ve.vaultObject.GetPath(), currentPayload)
}

func (ve *VaultEndpoint) CreateOrUpdate(context context.Context) error {
log := log.FromContext(context)
currentPayload, found, err := read(context, ve.vaultObject.GetPath())
Expand Down
14 changes: 1 addition & 13 deletions controllers/randomsecret_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,24 +152,12 @@ func (r *RandomSecretReconciler) manageReconcileLogic(context context.Context, i
return nil
}
vaultEndpoint := vaultutils.NewVaultEndpoint(instance)
// When this is a newly created RandomSecret and no refresh period is defined (= one-off random password)
// check that the Vault KV secret does not exist already to avoid overwriting its existing value.
if instance.Status.LastVaultSecretUpdate == nil && instance.Spec.RefreshPeriod == nil {
found, err := vaultEndpoint.Exists(context)
if err != nil {
r.Log.Info("unable to verify secret existence", "instance", instance, "error", err)
}
if found {
r.Log.Info("no refresh period is defined and Vault secret already exists - nothing to do", "name", instance.Name)
return nil
}
}
err := instance.PrepareInternalValues(context, instance)
if err != nil {
r.Log.Error(err, "unable to generate new secret", "instance", instance)
return err
}
err = vaultEndpoint.Create(context)
err = vaultEndpoint.CreateOrMergeKV(context, instance.IsKVSecretsEngineV2())
if err != nil {
r.Log.Error(err, "unable to create/update Vault Secret", "instance", instance)
return err
Expand Down
Loading