diff --git a/site/content/docs/user/CHANGELOG.md b/site/content/docs/user/CHANGELOG.md index 24f6ebbe1..c1e8f2a8d 100644 --- a/site/content/docs/user/CHANGELOG.md +++ b/site/content/docs/user/CHANGELOG.md @@ -8,6 +8,11 @@ menu: --- # Changelog +## kamus-0.9.0.7 (18/03/2021) + +#### feature : +- The controller now reconcile all KamusSecrets every 60 seconds (make sure to recreate if any secret is missing) + ## kamus-0.9.0.6 (15/02/2021) #### feature : diff --git a/src/crd-controller/HostedServices/V1Alpha2Controller.cs b/src/crd-controller/HostedServices/V1Alpha2Controller.cs index e66532b15..3107217dc 100644 --- a/src/crd-controller/HostedServices/V1Alpha2Controller.cs +++ b/src/crd-controller/HostedServices/V1Alpha2Controller.cs @@ -24,17 +24,19 @@ public class V1Alpha2Controller : IHostedService private readonly IKubernetes mKubernetes; private readonly IKeyManagement mKeyManagement; private readonly bool mSetOwnerReference; + private readonly double mReconciliationIntervalInSeconds; private IDisposable mSubscription; private readonly ILogger mAuditLogger = Log.ForContext().AsAudit(); private readonly ILogger mLogger = Log.ForContext(); private readonly IMetrics mMetrics; private const string ApiVersion = "v1alpha2"; - public V1Alpha2Controller(IKubernetes kubernetes, IKeyManagement keyManagement, bool setOwnerReference, IMetrics metrics) + public V1Alpha2Controller(IKubernetes kubernetes, IKeyManagement keyManagement, bool setOwnerReference, double reconciliationIntervalInSeconds, IMetrics metrics) { mKubernetes = kubernetes; mKeyManagement = keyManagement; mSetOwnerReference = setOwnerReference; + mReconciliationIntervalInSeconds = reconciliationIntervalInSeconds; mMetrics = metrics; } @@ -44,13 +46,13 @@ public Task StopAsync(CancellationToken cancellationToken) return Task.CompletedTask; } - public Task StartAsync(CancellationToken token) + private IDisposable ObserveKamusSecret(CancellationToken token) { - mSubscription = mKubernetes.ObserveClusterCustomObject( + return mKubernetes.ObserveClusterCustomObject( "soluto.com", - ApiVersion, - "kamussecrets", - token) + ApiVersion, + "kamussecrets", + token) .SelectMany(x => Observable.FromAsync(async () => await HandleEvent(x.Item1, x.Item2)) ) @@ -66,7 +68,16 @@ public Task StartAsync(CancellationToken token) mLogger.Information("Watching KamusSecret events completed, terminating process"); Environment.Exit(0); }); - + } + public Task StartAsync(CancellationToken token) + { + mSubscription = ObserveKamusSecret(token); + Observable.Interval(TimeSpan.FromSeconds(mReconciliationIntervalInSeconds)).Subscribe((s) => + { + mSubscription.Dispose(); + mSubscription = ObserveKamusSecret(token); + }); + mLogger.Information("Starting watch for KamusSecret V1Alpha2 events"); return Task.CompletedTask; diff --git a/src/crd-controller/Startup.cs b/src/crd-controller/Startup.cs index 79c0263e7..7223f6e67 100644 --- a/src/crd-controller/Startup.cs +++ b/src/crd-controller/Startup.cs @@ -63,10 +63,11 @@ public void ConfigureServices (IServiceCollection services) { services.AddHostedService(serviceProvider => { var setOwnerReference = Configuration.GetValue("Controller:SetOwnerReference", true); + var reconciliationIntervalInSeconds = Configuration.GetValue("Controller:ReconciliationIntervalInSeconds", 60); var kubernetes = serviceProvider.GetService(); var kms = serviceProvider.GetService(); var metrics = serviceProvider.GetService(); - return new V1Alpha2Controller(kubernetes, kms, setOwnerReference, metrics); + return new V1Alpha2Controller(kubernetes, kms, setOwnerReference, reconciliationIntervalInSeconds, metrics); }); services.AddHealthChecks() diff --git a/src/crd-controller/crd-controller.csproj b/src/crd-controller/crd-controller.csproj index d24ac5531..df1944904 100644 --- a/src/crd-controller/crd-controller.csproj +++ b/src/crd-controller/crd-controller.csproj @@ -7,7 +7,7 @@ - 0.9.0.6 + 0.9.0.7 diff --git a/src/decrypt-api/decrypt-api.csproj b/src/decrypt-api/decrypt-api.csproj index e2fc59de6..9d81760ab 100644 --- a/src/decrypt-api/decrypt-api.csproj +++ b/src/decrypt-api/decrypt-api.csproj @@ -3,7 +3,7 @@ netcoreapp3.1 - 0.9.0.6 + 0.9.0.7 diff --git a/src/encrypt-api/encrypt-api.csproj b/src/encrypt-api/encrypt-api.csproj index 1cabeaf7e..8e1eba6f8 100644 --- a/src/encrypt-api/encrypt-api.csproj +++ b/src/encrypt-api/encrypt-api.csproj @@ -3,7 +3,7 @@ netcoreapp3.1 - 0.9.0.6 + 0.9.0.7 diff --git a/tests/crd-controller/FlowTest.cs b/tests/crd-controller/FlowTest.cs index a22a1f12b..c565a1789 100644 --- a/tests/crd-controller/FlowTest.cs +++ b/tests/crd-controller/FlowTest.cs @@ -116,6 +116,64 @@ public async Task CreateKamusSecret_LabelsAndAnnotationsCopied() Assert.Equal("value", v1Secret.Metadata.Annotations.First(x => x.Key == "key").Value); } + [Fact] + public async Task CreateKamusSecret_DeleteSecret_ReconciliationRecreateIt() + { + Cleanup(); + await DeployController(); + var kubernetes = new Kubernetes(KubernetesClientConfiguration.BuildDefaultConfig()); + + var watcher = await kubernetes.ListNamespacedSecretWithHttpMessagesAsync( + "default", + watch: true + ); + + var subject = new ReplaySubject<(WatchEventType, V1Secret)>(); + + watcher.Watch( + onEvent: (@type, @event) => subject.OnNext((@type, @event)), + onError: e => subject.OnError(e), + onClosed: () => subject.OnCompleted()); + + RunKubectlCommand("apply -f tls-KamusSecretV1Alpha2-with-annotations.yaml"); + mTestOutputHelper.WriteLine("Waiting for secret creation"); + var (_, v1Secret) = await subject + .Where(t => t.Item1 == WatchEventType.Added && t.Item2.Metadata.Name == "my-tls-secret").Timeout(TimeSpan.FromSeconds(30)).FirstAsync(); + + watcher.Dispose(); + + Assert.Equal(1, v1Secret.Metadata.Labels.Count); + Assert.True(v1Secret.Metadata.Labels.Keys.Contains("key")); + Assert.Equal("value", v1Secret.Metadata.Labels.First(x => x.Key == "key").Value); + Assert.Equal(1, v1Secret.Metadata.Annotations.Count); + Assert.True(v1Secret.Metadata.Annotations.Keys.Contains("key")); + Assert.Equal("value", v1Secret.Metadata.Annotations.First(x => x.Key == "key").Value); + + var newWatcher = await kubernetes.ListNamespacedSecretWithHttpMessagesAsync( + "default", + watch: true + ); + + var newSubject = new ReplaySubject<(WatchEventType, V1Secret)>(); + + newWatcher.Watch( + onEvent: (@type, @event) => newSubject.OnNext((@type, @event)), + onError: e => newSubject.OnError(e), + onClosed: () => newSubject.OnCompleted()); + + RunKubectlCommand($"delete secret {v1Secret.Metadata.Name}"); + + var (_, v1SecretRecreation) = await newSubject + .Where(t => t.Item1 == WatchEventType.Added && t.Item2.Metadata.Name == "my-tls-secret").Timeout(TimeSpan.FromSeconds(15)).FirstAsync(); + + Assert.Equal(1, v1SecretRecreation.Metadata.Labels.Count); + Assert.True(v1SecretRecreation.Metadata.Labels.Keys.Contains("key")); + Assert.Equal("value", v1SecretRecreation.Metadata.Labels.First(x => x.Key == "key").Value); + Assert.Equal(1, v1SecretRecreation.Metadata.Annotations.Count); + Assert.True(v1SecretRecreation.Metadata.Annotations.Keys.Contains("key")); + Assert.Equal("value", v1SecretRecreation.Metadata.Annotations.First(x => x.Key == "key").Value); + } + [Theory] [InlineData("updated-tls-KamusSecretV1Alpha2.yaml")] public async Task UpdateKamusSecret_SecretUpdated(string fileName) diff --git a/tests/crd-controller/deployment.yaml b/tests/crd-controller/deployment.yaml index 20e3added..7b72cadbd 100644 --- a/tests/crd-controller/deployment.yaml +++ b/tests/crd-controller/deployment.yaml @@ -73,6 +73,8 @@ spec: image: crd-controller imagePullPolicy: IfNotPresent env: + - name: Controller__ReconciliationIntervalInSeconds + value: "10" livenessProbe: httpGet: path: /healthz