public void Base36Uuid_Crypto() { // Generate 20K base-36 cyoptographically secure random UUIDs and verify that // the're all 13-characters, consist of lowercase letters and digits and also // that we never generate duplicates. var existing = new HashSet <string>(); for (int i = 0; i < 20000; i++) { var v = NeonHelper.CreateBase36Uuid(secure: true); Assert.NotNull(v); Assert.Equal(13, v.Length); foreach (var ch in v) { if (!char.IsLetterOrDigit(ch)) { Assert.False(false, $"Invalid character [{ch}] found in [{v}]."); } else if (char.IsUpper(ch)) { Assert.False(false, $"Uppercase character [{ch}] found in [{v}]."); } else if (ch > (char)127) { Assert.False(false, $"Non-ASCII character [{ch}] found in [{v}]."); } } Assert.DoesNotContain(v, existing); existing.Add(v); } }
public async Task Single_WithoutActionsOrCounters() { // Verify that the elector works without callback actions. var leaseName = $"test-{NeonHelper.CreateBase36Uuid()}"; var config = new LeaderElectionConfig(fixture.K8s, @namespace: KubeNamespace.Default, leaseName: leaseName, identity: "instance-0"); var elector = new LeaderElector(config); Task electorTask; try { using (elector) { electorTask = elector.RunAsync(); NeonHelper.WaitFor(() => elector.IsLeader, timeout: MaxWaitTime); Assert.True(elector.IsLeader); Assert.Equal("instance-0", elector.Leader); } // Ensure that the elector task completes. await electorTask.WaitAsync(timeout : MaxWaitTime); } finally { await config.K8s.DeleteNamespacedLeaseWithHttpMessagesAsync(leaseName, config.Namespace); } }
/// <summary> /// <para> /// Transforms responses before sending them back to the client. In this case it intercepts the initial Bla /// </para> /// </summary> /// <param name="httpContext">The HTTP Context.</param> /// <param name="proxyResponse">The Proxied Response.</param> /// <returns></returns> public override async ValueTask <bool> TransformResponseAsync(HttpContext httpContext, HttpResponseMessage proxyResponse) { await SyncContext.Clear; await base.TransformResponseAsync(httpContext, proxyResponse); var session = new Session() { Id = NeonHelper.CreateBase36Uuid(), UpstreamHost = proxyResponse.RequestMessage.RequestUri.Authority }; var headers = proxyResponse.Content.Headers; var mediaType = headers.ContentType?.MediaType ?? ""; if (!httpContext.Request.Cookies.ContainsKey(Service.SessionCookieName) || (mediaType == "text/html" && httpContext.Response.StatusCode == 200)) { httpContext.Response.Cookies.Append(Service.SessionCookieName, cipher.EncryptToBase64($"{session.Id}")); } await cache.SetAsync(session.Id, NeonHelper.JsonSerializeToBytes(session)); return(true); }
public async Task Single_WithCounters() { // Verify that the elector can increment performance counters. var leaseName = $"test-{NeonHelper.CreateBase36Uuid()}"; var promotionCounter = Metrics.CreateCounter("test_promotions", string.Empty); var demotionCounter = Metrics.CreateCounter("test_demotions", string.Empty); var newLeaderCounter = Metrics.CreateCounter("test_newleaders", string.Empty); var config = new LeaderElectionConfig( k8s: fixture.K8s, @namespace: KubeNamespace.Default, leaseName: leaseName, identity: "instance-0", promotionCounter: promotionCounter, demotionCounter: demotionCounter, newLeaderCounter: newLeaderCounter); var elector = new LeaderElector(config); Task electorTask; try { using (elector) { electorTask = elector.RunAsync(); NeonHelper.WaitFor(() => elector.IsLeader, timeout: MaxWaitTime); Assert.True(elector.IsLeader); Assert.Equal("instance-0", elector.Leader); } // Ensure that the elector task completes. await electorTask.WaitAsync(timeout : MaxWaitTime); // Ensure that the counters are correct. Assert.Equal(1, promotionCounter.Value); Assert.Equal(0, demotionCounter.Value); // We don't see demotions when the elector is disposed Assert.Equal(2, newLeaderCounter.Value); // We do see a leadership change when the elector is disposed } finally { await config.K8s.DeleteNamespacedLeaseWithHttpMessagesAsync(leaseName, config.Namespace); } }
public async Task Single_WithoutCounters() { // Verify that we can create a single [LeaderElector] instance and that: // // 1. The [OnNewLeader] action is called // 2. The [OnStartedLeader] action is called // 3. The new leader matches the current instance // 4. IsLeader and GetLeader() work var leaseName = $"test-{NeonHelper.CreateBase36Uuid()}"; bool isLeading = false; string leader = null; var config = new LeaderElectionConfig(fixture.K8s, @namespace: KubeNamespace.Default, leaseName: leaseName, identity: "instance-0"); Task electorTask; try { var elector = new LeaderElector( config: config, onStartedLeading: () => isLeading = true, onStoppedLeading: () => isLeading = false, onNewLeader: identity => leader = identity); using (elector) { electorTask = elector.RunAsync(); NeonHelper.WaitFor(() => isLeading, timeout: MaxWaitTime); Assert.True(isLeading); Assert.True(elector.IsLeader); Assert.Equal("instance-0", leader); Assert.Equal("instance-0", elector.Leader); } // Ensure that the elector task completes. await electorTask.WaitAsync(timeout : MaxWaitTime); } finally { await config.K8s.DeleteNamespacedLeaseWithHttpMessagesAsync(leaseName, config.Namespace); } }
/// <summary> /// Returns a 5-digit base-36 UUID string. /// </summary> /// <returns>The UUID.</returns> private string CreateUuidString() { return(NeonHelper.CreateBase36Uuid().Substring(0, 5)); }
/// <summary> /// Starts the controller. /// </summary> /// <param name="k8s">The <see cref="IKubernetes"/> client to use.</param> /// <returns>The tracking <see cref="Task"/>.</returns> public static async Task StartAsync(IKubernetes k8s) { Covenant.Requires <ArgumentNullException>(k8s != null, nameof(k8s)); if (NeonHelper.IsLinux) { // Ensure that the [/var/run/neonkube/node-tasks] folder exists on the node. var scriptPath = Path.Combine(Node.HostMount, $"tmp/node-agent-folder-{NeonHelper.CreateBase36Uuid()}.sh"); var script = $@"#!/bin/bash set -euo pipefail # Ensure that the nodetask runtime folders exist and have the correct permissions. if [ ! -d {hostNeonRunFolder} ]; then mkdir -p {hostNeonRunFolder} chmod 700 {hostNeonRunFolder} fi if [ ! -d {hostNeonTasksFolder} ]; then mkdir -p {hostNeonTasksFolder} chmod 700 {hostNeonTasksFolder} fi # Remove this script. rm $0 "; File.WriteAllText(scriptPath, NeonHelper.ToLinuxLineEndings(script)); try { (await Node.BashExecuteCaptureAsync(scriptPath)).EnsureSuccess(); } finally { NeonHelper.DeleteFile(scriptPath); } } // Load the configuration settings. var leaderConfig = new LeaderElectionConfig( k8s, @namespace: KubeNamespace.NeonSystem, leaseName: $"{Program.Service.Name}.nodetask-{Node.Name}", identity: Pod.Name, promotionCounter: Metrics.CreateCounter($"{Program.Service.MetricsPrefix}nodetask_promoted", "Leader promotions"), demotionCounter: Metrics.CreateCounter($"{Program.Service.MetricsPrefix}nodetask_demoted", "Leader demotions"), newLeaderCounter: Metrics.CreateCounter($"{Program.Service.MetricsPrefix}nodetask_newLeader", "Leadership changes")); var options = new ResourceManagerOptions() { IdleInterval = Program.Service.Environment.Get("NODETASK_IDLE_INTERVAL", TimeSpan.FromSeconds(60)), ErrorMinRequeueInterval = Program.Service.Environment.Get("NODETASK_ERROR_MIN_REQUEUE_INTERVAL", TimeSpan.FromSeconds(15)), ErrorMaxRetryInterval = Program.Service.Environment.Get("NODETASK_ERROR_MAX_REQUEUE_INTERVAL", TimeSpan.FromSeconds(60)), IdleCounter = Metrics.CreateCounter($"{Program.Service.MetricsPrefix}nodetask_idle", "IDLE events processed."), ReconcileCounter = Metrics.CreateCounter($"{Program.Service.MetricsPrefix}nodetask_reconcile", "RECONCILE events processed."), DeleteCounter = Metrics.CreateCounter($"{Program.Service.MetricsPrefix}nodetask_delete", "DELETED events processed."), IdleErrorCounter = Metrics.CreateCounter($"{Program.Service.MetricsPrefix}nodetask_idle_error", "Failed NodeTask IDLE event processing."), ReconcileErrorCounter = Metrics.CreateCounter($"{Program.Service.MetricsPrefix}nodetask_reconcile_error", "Failed NodeTask RECONCILE event processing."), DeleteErrorCounter = Metrics.CreateCounter($"{Program.Service.MetricsPrefix}nodetask_delete_error", "Failed NodeTask DELETE event processing."), StatusModifyErrorCounter = Metrics.CreateCounter($"{Program.Service.MetricsPrefix}nodetask_statusmodify_error", "Failed NodeTask STATUS-MODIFY events processing.") }; resourceManager = new ResourceManager <V1NeonNodeTask, NodeTaskController>( k8s, options: options, filter: NodeTaskFilter, leaderConfig: leaderConfig); await resourceManager.StartAsync(); }