|
3 | 3 | using System; |
4 | 4 | using System.Collections.Generic; |
5 | 5 | using System.Linq; |
| 6 | + using System.Net.WebSockets; |
6 | 7 | using System.Threading.Tasks; |
7 | 8 | using Google.Api.Gax; |
8 | 9 | using Google.Api.Gax.ResourceNames; |
|
17 | 18 | public class GoogleSecretsProvider : ConfigurationProvider |
18 | 19 | { |
19 | 20 | private readonly ILogger<GoogleSecretsProvider> logger; |
| 21 | + private readonly IConfigurationRoot existingConfiguration; |
| 22 | + private readonly Dictionary<string, string> secretCache; |
20 | 23 |
|
21 | 24 | /// <summary> |
22 | 25 | /// Initializes a new instance of the <see cref="GoogleSecretsProvider"/> class. |
23 | 26 | /// </summary> |
24 | 27 | /// <param name="source">The source.</param> |
25 | | - public GoogleSecretsProvider(GoogleSecretsSource source) |
| 28 | + /// <param name="existingConfiguration">The configuration builder.</param> |
| 29 | + public GoogleSecretsProvider(GoogleSecretsSource source, IConfigurationRoot existingConfiguration) |
26 | 30 | { |
27 | 31 | this.Source = source; |
28 | 32 | this.logger = LoggerFactory.Create(b => b.AddConsole()).CreateLogger<GoogleSecretsProvider>(); |
| 33 | + this.existingConfiguration = existingConfiguration; |
| 34 | + this.secretCache = new Dictionary<string, string>(); |
29 | 35 | } |
30 | 36 |
|
31 | 37 | /// <summary> |
@@ -56,41 +62,84 @@ public override void Load() |
56 | 62 | { |
57 | 63 | foreach (Secret secret in response) |
58 | 64 | { |
59 | | - if (!this.Source.FilterFn(secret)) |
60 | | - { |
61 | | - continue; |
62 | | - } |
63 | | - |
64 | | - string version = "latest"; |
65 | | - var secretId = secret.SecretName.SecretId; |
66 | | - |
67 | | - if (this.Source.VersionDictionary?.ContainsKey(secretId) == true) |
68 | | - { |
69 | | - version = this.Source.VersionDictionary[secretId]; |
70 | | - } |
71 | | - |
72 | 65 | try |
73 | 66 | { |
74 | | - string versionName = $"{secret.Name}/versions/{version}"; |
75 | | - var value = secretManagerServiceClient.AccessSecretVersion(versionName); |
76 | | - var secretValue = value.Payload.Data.ToStringUtf8(); |
| 67 | + if (!this.Source.FilterFn(secret)) |
| 68 | + { |
| 69 | + continue; |
| 70 | + } |
77 | 71 |
|
78 | | - this.Set(this.Source.MapFn(secret), secretValue); |
79 | | - this.logger.LogInformation($"Successfully loaded secret {secret.SecretName.SecretId}"); |
| 72 | + ScanExistingConfiguration(secretManagerServiceClient, secret); |
| 73 | + ApplyMapFn(secretManagerServiceClient, secret); |
80 | 74 | } |
81 | 75 | catch (Exception e) |
82 | 76 | { |
83 | 77 | this.logger.LogWarning(e, $"Skipping secret {secret.SecretName.SecretId}"); |
84 | 78 | } |
85 | | - |
86 | | - |
87 | 79 | } |
88 | 80 | } |
89 | 81 | } |
90 | 82 | catch (Exception e) |
91 | 83 | { |
92 | 84 | this.logger.LogError(e, "Unhandeled Exception"); |
93 | 85 | } |
| 86 | + |
| 87 | + void SetSecretValue(SecretManagerServiceClient secretManagerServiceClient, Secret secret, string key, string version) |
| 88 | + { |
| 89 | + string versionName = $"{secret.Name}/versions/{version}"; |
| 90 | + |
| 91 | + if (secretCache.TryGetValue(versionName, out var cachedValue)) |
| 92 | + { |
| 93 | + this.Set(key, cachedValue); |
| 94 | + this.logger.LogDebug($"Using cached value for secret {secret.SecretName.SecretId} Key: {key} Version: {version}"); |
| 95 | + } |
| 96 | + else |
| 97 | + { |
| 98 | + var value = secretManagerServiceClient.AccessSecretVersion(versionName); |
| 99 | + var secretValue = value.Payload.Data.ToStringUtf8(); |
| 100 | + secretCache[versionName] = secretValue; |
| 101 | + this.Set(key, secretValue); |
| 102 | + } |
| 103 | + |
| 104 | + this.logger.LogInformation($"Successfully loaded secret {secret.SecretName.SecretId} into configuration. Key: {key} Version: {version}"); |
| 105 | + } |
| 106 | + |
| 107 | + void ScanExistingConfiguration(SecretManagerServiceClient secretManagerServiceClient, Secret secret) |
| 108 | + { |
| 109 | + var existingKeyValues = this.existingConfiguration.AsEnumerable(); |
| 110 | + // value will be in the format of {GoogleSecret:SecretName} or {GoogleSecret:SecretName:Version} |
| 111 | + var keyValuesToReplace = existingKeyValues.Where(x => x.Value?.StartsWith($"{{GoogleSecret:{secret.SecretName.SecretId}") == true); |
| 112 | + |
| 113 | + foreach (var keyValue in keyValuesToReplace) |
| 114 | + { |
| 115 | + var replaceParams = keyValue.Value.Split(':'); |
| 116 | + string version = "latest"; |
| 117 | + if (replaceParams.Length >= 3) |
| 118 | + { |
| 119 | + version = replaceParams[2]; |
| 120 | + } |
| 121 | + |
| 122 | + SetSecretValue(secretManagerServiceClient, secret, keyValue.Key, version); |
| 123 | + } |
| 124 | + } |
| 125 | + |
| 126 | + void ApplyMapFn(SecretManagerServiceClient secretManagerServiceClient, Secret secret) |
| 127 | + { |
| 128 | + if (this.Source.MapFn == null) |
| 129 | + { |
| 130 | + return; |
| 131 | + } |
| 132 | + |
| 133 | + string version = "latest"; |
| 134 | + var secretId = secret.SecretName.SecretId; |
| 135 | + |
| 136 | + if (this.Source.VersionDictionary?.ContainsKey(secretId) == true) |
| 137 | + { |
| 138 | + version = this.Source.VersionDictionary[secretId]; |
| 139 | + } |
| 140 | + |
| 141 | + SetSecretValue(secretManagerServiceClient, secret, this.Source.MapFn(secret), version); |
| 142 | + } |
94 | 143 | } |
95 | 144 | } |
96 | 145 | } |
0 commit comments