Repository: hashicorp/vault
File: builtin/logical/database/rotation.go (Go)

Code before:
		role, err := b.StaticRole(ctx, s, roleName)
		if err != nil {
			log.Warn("unable to read static role", "error", err, "role", roleName)
			continue
		}

		// If an account's NextVaultRotation period is nil, it means that the
		// role was created before we added the `NextVaultRotation` field. In this
		// case, we need to calculate the next rotation time based on the
		// LastVaultRotation and the RotationPeriod. However, if the role was
		// created with skip_import_rotation set, we need to use the current time
		// instead of LastVaultRotation because LastVaultRotation is 0
		if role.StaticAccount.NextVaultRotation.IsZero() {
			log.Debug("NextVaultRotation is zero", roleName)
			// Previously skipped import rotation roles had a LastVaultRotation value of zero
			if role.StaticAccount.LastVaultRotation.IsZero() {
				role.StaticAccount.SetNextVaultRotation(time.Now())
			} else {
				role.StaticAccount.SetNextVaultRotation(role.StaticAccount.LastVaultRotation)
			}

			entry, err := logical.StorageEntryJSON(databaseStaticRolePath+roleName, role)
			if err != nil {
				log.Warn("failed to build write storage entry", "error", err, "roleName", roleName)
			} else if err := s.Put(ctx, entry); err != nil {
				log.Warn("failed to write storage entry", "error", err, "roleName", roleName)
			}
		}

		item := queue.Item{
			Key:      roleName,
			Priority: role.StaticAccount.NextRotationTime().Unix(),
		}

		// Check if role name is in map
		walEntry := walMap[roleName]
		if walEntry != nil {
			// Check walEntry last vault time
			if walEntry.LastVaultRotation.IsZero() {
				// A WAL's last Vault rotation can only ever be 0 for a role that
				// was never successfully created. So we know this WAL couldn't
				// have been created for this role we just retrieved from storage.
				// i.e. it must be a hangover from a previous attempt at creating
				// a role with the same name
				log.Debug("deleting WAL with zero last rotation time", "WAL ID", walEntry.walID, "created", walEntry.walCreatedAt)
				if err := framework.DeleteWAL(ctx, s, walEntry.walID); err != nil {
					log.Warn("unable to delete zero-time WAL", "error", err, "WAL ID", walEntry.walID)
				}
			} else if walEntry.LastVaultRotation.Before(role.StaticAccount.LastVaultRotation) {
				// WAL's last vault rotation record is older than the role's data, so
				// delete and move on

Review comment (bug):
Failure to write to the Vault storage is only a warning? Do we need better error handling here?

Code after:
		role, err := b.StaticRole(ctx, s, roleName)
		if err != nil {
			log.Warn("unable to read static role", "error", err, "role", roleName)
			continue
		}

		// If an account's NextVaultRotation period is zero time (time.Time{}), it means that the
		// role was created before we added the `NextVaultRotation` field. In this
		// case, we need to calculate the next rotation time based on the
		// LastVaultRotation and the RotationPeriod. However, if the role was
		// created with skip_import_rotation set, we need to use the current time
		// instead of LastVaultRotation because LastVaultRotation is 0
		if role.StaticAccount.NextVaultRotation.IsZero() {
			log.Debug("NextVaultRotation unset (zero time). Role may predate field", roleName)
			if role.StaticAccount.LastVaultRotation.IsZero() {
				log.Debug("Setting NextVaultRotation based on current time", roleName)
				role.StaticAccount.SetNextVaultRotation(time.Now())
			} else {
				log.Debug("Setting NextVaultRotation based on LastVaultRotation", roleName)
				role.StaticAccount.SetNextVaultRotation(role.StaticAccount.LastVaultRotation)
			}

			b.StoreStaticRole(ctx, s, role)
		}

		item := queue.Item{
			Key:      roleName,
			Priority: role.StaticAccount.NextRotationTime().Unix(),
		}

		// Check if role name is in map
		walEntry := walMap[roleName]
		if walEntry != nil {
			// Check walEntry last vault time
			if walEntry.LastVaultRotation.IsZero() {
				// A WAL's last Vault rotation can only ever be 0 for a role that
				// was never successfully created. So we know this WAL couldn't
				// have been created for this role we just retrieved from storage.
				// i.e. it must be a hangover from a previous attempt at creating
				// a role with the same name
				log.Debug("deleting WAL with zero last rotation time", "WAL ID", walEntry.walID, "created", walEntry.walCreatedAt)
				if err := framework.DeleteWAL(ctx, s, walEntry.walID); err != nil {
					log.Warn("unable to delete zero-time WAL", "error", err, "WAL ID", walEntry.walID)
				}
			} else if walEntry.LastVaultRotation.Before(role.StaticAccount.LastVaultRotation) {
				// WAL's last vault rotation record is older than the role's data, so
				// delete and move on
				log.Debug("deleting outdated WAL", "WAL ID", walEntry.walID, "created", walEntry.walCreatedAt)
				if err := framework.DeleteWAL(ctx, s, walEntry.walID); err != nil {
					log.Warn("unable to delete WAL", "error", err, "WAL ID", walEntry.walID)
				}