-
Notifications
You must be signed in to change notification settings - Fork 95
refactor(credential): carry wire key identifiers through the accepted set #729
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
Merged
aaron-zeisler
merged 3 commits into
feat/concurrent-keys
from
aaronz/SDK-2534/credential-key-identifiers
Jun 30, 2026
Merged
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
a094998
refactor(credential): carry wire key identifiers through the accepted…
aaron-zeisler 09075d5
refactor(credential): reconcile key metadata in a single pass
aaron-zeisler 5eceeb4
refactor(credential): store accepted-key metadata by value, trust the…
aaron-zeisler File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -11,6 +11,7 @@ import ( | |
| // acceptedKeyInfo holds per-key metadata for the accepted-set maps. | ||
| type acceptedKeyInfo struct { | ||
| expiry *time.Time // nil = permanent | ||
| key *string // wire "key" identifier — non-secret human-readable name; nil when absent | ||
| } | ||
|
|
||
| type Rotator struct { | ||
|
|
@@ -28,11 +29,11 @@ type Rotator struct { | |
|
|
||
| // acceptedSDKKeys is the full set of accepted SDK keys with optional per-key expiry. | ||
| // A nil expiry means the key is permanent. The anchor is always present with a nil expiry. | ||
| acceptedSDKKeys map[config.SDKKey]*acceptedKeyInfo | ||
| acceptedSDKKeys map[config.SDKKey]acceptedKeyInfo | ||
|
|
||
| // acceptedMobileKeys is the full set of accepted mobile keys with optional per-key expiry. | ||
| // A nil expiry means the key is permanent. | ||
| acceptedMobileKeys map[config.MobileKey]*acceptedKeyInfo | ||
| acceptedMobileKeys map[config.MobileKey]acceptedKeyInfo | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note for reviewers: Here I decided to go with values instead of pointers. This caused a handful of random-looking changes throughout the pull request. |
||
|
|
||
| expirations []SDKCredential | ||
| additions []SDKCredential | ||
|
|
@@ -51,8 +52,8 @@ type InitialCredentials struct { | |
| func NewRotator(loggers ldlog.Loggers) *Rotator { | ||
| r := &Rotator{ | ||
| loggers: loggers, | ||
| acceptedSDKKeys: make(map[config.SDKKey]*acceptedKeyInfo), | ||
| acceptedMobileKeys: make(map[config.MobileKey]*acceptedKeyInfo), | ||
| acceptedSDKKeys: make(map[config.SDKKey]acceptedKeyInfo), | ||
| acceptedMobileKeys: make(map[config.MobileKey]acceptedKeyInfo), | ||
| } | ||
| return r | ||
| } | ||
|
|
@@ -70,10 +71,10 @@ func (r *Rotator) Initialize(credentials []SDKCredential) { | |
| switch cred := cred.(type) { | ||
| case config.SDKKey: | ||
| r.anchorKey = cred | ||
| r.acceptedSDKKeys[cred] = &acceptedKeyInfo{} | ||
| r.acceptedSDKKeys[cred] = acceptedKeyInfo{} | ||
| case config.MobileKey: | ||
| r.primaryMobileKey = cred | ||
| r.acceptedMobileKeys[cred] = &acceptedKeyInfo{} | ||
| r.acceptedMobileKeys[cred] = acceptedKeyInfo{} | ||
| case config.EnvironmentID: | ||
| r.primaryEnvironmentID = cred | ||
| } | ||
|
|
@@ -222,23 +223,23 @@ type reconcilableKey interface { | |
| // key no longer desired is dropped and queued as an expiration. Per-key expiry is stored as data on | ||
| // the accepted entry; the cleanup ticker is what later acts on it. The caller must hold the write lock. | ||
| func reconcileAcceptedKeys[K reconcilableKey]( | ||
| desired map[K]*time.Time, | ||
| accepted map[K]*acceptedKeyInfo, | ||
| desired map[K]acceptedKeyInfo, | ||
| accepted map[K]acceptedKeyInfo, | ||
| additions *[]SDKCredential, | ||
| expirations *[]SDKCredential, | ||
| loggers ldlog.Loggers, | ||
| kind string, | ||
| ) { | ||
| // First pass: walk every key the set wants us to accept. If we already accept it, just refresh | ||
| // its expiry; if it's new, start accepting it and queue it as an addition. | ||
| for key, expiry := range desired { | ||
| if info, ok := accepted[key]; ok { | ||
| info.expiry = expiry | ||
| } else { | ||
| accepted[key] = &acceptedKeyInfo{expiry: expiry} | ||
| // First pass: walk every key the set wants us to accept. Writing the desired entry over the | ||
| // accepted one refreshes its metadata — both the expiry and the wire "key" identifier, clearing | ||
| // the identifier when the new payload carries none so a stale name never lingers in /status. A key | ||
| // we don't yet accept is also queued as an addition. | ||
| for key, want := range desired { | ||
| if _, ok := accepted[key]; !ok { | ||
| *additions = append(*additions, key) | ||
| loggers.Infof("%s %s is now accepted", kind, key.Masked()) | ||
| } | ||
| accepted[key] = want | ||
| } | ||
| // Second pass: walk every key we currently accept and drop the ones the set no longer wants. | ||
| // Keys still desired were handled above, so skip them; the rest are revoked outright (removed | ||
|
|
@@ -254,34 +255,32 @@ func reconcileAcceptedKeys[K reconcilableKey]( | |
| } | ||
|
|
||
| // reconcileSDKKeys diffs the desired SDK keys against the accepted set and applies the result via | ||
| // reconcileAcceptedKeys. The anchor is always accepted and permanent, regardless of any expiry the | ||
| // payload may carry for it. The caller must hold the write lock. | ||
| // reconcileAcceptedKeys. The set is trusted as well-formed: BuildAcceptedSet / the builder guarantee | ||
| // the anchor is present and permanent (WithAnchor forces a nil expiry), so no special handling is | ||
| // needed here. The caller must hold the write lock. | ||
| func (r *Rotator) reconcileSDKKeys(set AcceptedSet, anchor config.SDKKey, now time.Time) { | ||
| desired := make(map[config.SDKKey]*time.Time, len(set.sdkKeys)) | ||
| for key, expiry := range set.sdkKeys { | ||
| if expiry != nil && !now.Before(*expiry) { | ||
| desired := make(map[config.SDKKey]acceptedKeyInfo, len(set.sdkKeys)) | ||
| for key, info := range set.sdkKeys { | ||
|
aaron-zeisler marked this conversation as resolved.
|
||
| if info.expiry != nil && !now.Before(*info.expiry) { | ||
| continue // already expired; treat as absent | ||
| } | ||
| desired[key] = expiry | ||
| desired[key] = info | ||
| } | ||
| desired[anchor] = nil | ||
| reconcileAcceptedKeys(desired, r.acceptedSDKKeys, &r.additions, &r.expirations, r.loggers, "SDK key") | ||
| r.anchorKey = anchor | ||
| } | ||
|
|
||
| // reconcileMobileKeys mirrors reconcileSDKKeys for mobile keys. The primary mobile key — the wire's | ||
| // singular mobKey, used where one mobile key is required (e.g. event forwarding) — is always accepted | ||
| // and permanent; an empty value means the set declared no mobile key. The caller must hold the lock. | ||
| // reconcileMobileKeys mirrors reconcileSDKKeys for mobile keys. The set is trusted as well-formed: | ||
| // when a primary mobile key is designated, the builder guarantees it is present and permanent | ||
| // (WithPrimaryMobileKey forces a nil expiry). An empty primary means the set declared no mobile key. | ||
| // The caller must hold the lock. | ||
| func (r *Rotator) reconcileMobileKeys(set AcceptedSet, now time.Time) { | ||
| desired := make(map[config.MobileKey]*time.Time, len(set.mobileKeys)) | ||
| for key, expiry := range set.mobileKeys { | ||
| if expiry != nil && !now.Before(*expiry) { | ||
| desired := make(map[config.MobileKey]acceptedKeyInfo, len(set.mobileKeys)) | ||
| for key, info := range set.mobileKeys { | ||
|
aaron-zeisler marked this conversation as resolved.
|
||
| if info.expiry != nil && !now.Before(*info.expiry) { | ||
| continue // already expired; treat as absent | ||
| } | ||
| desired[key] = expiry | ||
| } | ||
| if set.primaryMobileKey.Defined() { | ||
| desired[set.primaryMobileKey] = nil | ||
| desired[key] = info | ||
| } | ||
| reconcileAcceptedKeys(desired, r.acceptedMobileKeys, &r.additions, &r.expirations, r.loggers, "Mobile key") | ||
| r.primaryMobileKey = set.primaryMobileKey | ||
|
|
||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.