Skip to content
This repository has been archived by the owner on Nov 13, 2024. It is now read-only.

Commit

Permalink
feature: avoid updating when it's not needed (reduce resource consump…
Browse files Browse the repository at this point in the history
…tion) (#74)

* feature: avoid updating when it's not needed

* bump chart to 0.14.0
  • Loading branch information
BlowaXD authored Aug 28, 2023
1 parent 1096b3d commit a7e4b6e
Show file tree
Hide file tree
Showing 9 changed files with 164 additions and 10 deletions.
6 changes: 6 additions & 0 deletions Bitwarden.SecretOperator.sln
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
Microsoft Visual Studio Solution File, Format Version 12.00
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bitwarden.SecretOperator", "src\Bitwarden.SecretOperator\Bitwarden.SecretOperator.csproj", "{9155C74A-D237-42AA-8246-6EAFD9102F0D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bitwarden.SecretOperator.Tests", "tests\Bitwarden.SecretOperator.Tests\Bitwarden.SecretOperator.Tests.csproj", "{BE7101CF-323D-4C17-86BC-6AF01C840AE3}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -12,5 +14,9 @@ Global
{9155C74A-D237-42AA-8246-6EAFD9102F0D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9155C74A-D237-42AA-8246-6EAFD9102F0D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9155C74A-D237-42AA-8246-6EAFD9102F0D}.Release|Any CPU.Build.0 = Release|Any CPU
{BE7101CF-323D-4C17-86BC-6AF01C840AE3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BE7101CF-323D-4C17-86BC-6AF01C840AE3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BE7101CF-323D-4C17-86BC-6AF01C840AE3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BE7101CF-323D-4C17-86BC-6AF01C840AE3}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
4 changes: 2 additions & 2 deletions charts/bitwarden-secret-operator/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ description: Deploy the Bitwarden Secret Operator

type: application

version: "0.13.2"
version: "0.14.0"

appVersion: "0.13.2"
appVersion: "0.14.0"

keywords:
- operator
Expand Down
4 changes: 2 additions & 2 deletions src/Bitwarden.SecretOperator/Bitwarden.SecretOperator.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@

<ItemGroup>
<PackageReference Include="CliWrap" Version="3.6.4" />
<PackageReference Include="KubeOps" Version="7.4.0" />
<PackageReference Include="KubeOps.KubernetesClient" Version="7.4.0" />
<PackageReference Include="KubeOps" Version="7.4.4" />
<PackageReference Include="KubeOps.KubernetesClient" Version="7.4.4" />
<PackageReference Include="Polly" Version="7.2.4" />
<PackageReference Include="Serilog" Version="3.0.1" />
<PackageReference Include="Serilog.Extensions.Logging" Version="7.0.0" />
Expand Down
7 changes: 6 additions & 1 deletion src/Bitwarden.SecretOperator/CRDs/Secret/BitWardenHelper.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
using System.Data;
using System.Text;
using Bitwarden.SecretOperator.CliWrapping;
using Bitwarden.SecretOperator.Helpers;
using k8s.Models;

namespace Bitwarden.SecretOperator.CRDs.Secret;

public static class BitWardenHelper
{
public const string HashAnnotation = "bitwarden-operator/hash";
public static async Task<V1Secret> GetSecretAsync(this BitwardenSecretCrd entity, BitwardenCliWrapper wrapper)
{
BitwardenSecretSpec spec = entity.Spec;
Expand Down Expand Up @@ -60,6 +62,9 @@ public static async Task<V1Secret> GetSecretAsync(this BitwardenSecretCrd entity
secrets[rawValues.KubernetesSecretKey] = Encoding.UTF8.GetBytes(rawValues.KubernetesSecretValue!);
}

spec.Labels ??= new Dictionary<string, string>();
spec.Labels[HashAnnotation] = secrets.ComputeHash();

string? destinationName = spec.Name ?? entity.Name();
string? destinationNamespace = spec.Namespace ?? entity.Namespace();
return new V1Secret
Expand All @@ -71,7 +76,7 @@ public static async Task<V1Secret> GetSecretAsync(this BitwardenSecretCrd entity
{
Name = destinationName,
NamespaceProperty = destinationNamespace,
Labels = spec.Labels
Labels = spec.Labels,
},
Data = secrets,
StringData = spec.StringData
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using Bitwarden.SecretOperator.CliWrapping;
using System.Security.Cryptography;
using System.Text;
using Bitwarden.SecretOperator.CliWrapping;
using k8s.Models;
using KubeOps.KubernetesClient;
using KubeOps.Operator.Controller;
Expand Down Expand Up @@ -39,7 +41,6 @@ public BitwardenSecretController(ILogger<BitwardenSecretController> logger, Kube
string? destinationNamespace = spec.Namespace ?? entity.Namespace();
try
{
await _eventManager.PublishAsync(entity, "bitwarden-secret-operator", "Received reconcile");
var secret = await _kubernetesClient.Get<V1Secret>(destinationName, destinationNamespace);
if (secret == null)
{
Expand All @@ -48,7 +49,7 @@ public BitwardenSecretController(ILogger<BitwardenSecretController> logger, Kube
// create
secret = await entity.GetSecretAsync(_cliWrapper);
secret.WithOwnerReference(entity);

secret = await _kubernetesClient.Create<V1Secret>(secret);

// created events
Expand All @@ -61,9 +62,25 @@ public BitwardenSecretController(ILogger<BitwardenSecretController> logger, Kube
_logger.LogInformation("Secret: {SecretName} in namespace: {Namespace} exists, updating it", destinationName, destinationNamespace);

// update
secret = await entity.GetSecretAsync(_cliWrapper);
V1Secret newSecret = await entity.GetSecretAsync(_cliWrapper);

// avoid updating if not needed
string? expectedHash = newSecret.GetLabel(BitWardenHelper.HashAnnotation);
string? hash = secret.GetLabel(BitWardenHelper.HashAnnotation);
if (hash is not null && expectedHash is not null && hash == expectedHash)
{
return null;
}

secret.WithOwnerReference(entity);
if (secret.FindOwnerReference(s => s.Name == entity.Name() && s.Uid == entity.Uid()) < 1)
{
secret.AddOwnerReference(entity.MakeOwnerReference());
}

// update data
secret.Data = newSecret.Data;
secret.StringData = newSecret.StringData;

await _kubernetesClient.Update<V1Secret>(secret);

// updated events
Expand All @@ -88,6 +105,7 @@ public BitwardenSecretController(ILogger<BitwardenSecretController> logger, Kube
return ResourceControllerResult.RequeueEvent(_operatorOptions.DelayAfterFailedWebhook.Value);
}
}


public Task StatusModifiedAsync(BitwardenSecretCrd entity)
{
Expand Down
40 changes: 40 additions & 0 deletions src/Bitwarden.SecretOperator/Helpers/DictionaryHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using System.Security.Cryptography;
using System.Text;

namespace Bitwarden.SecretOperator.Helpers;

public static class DictionaryHelper
{

public static string ComputeHash(this Dictionary<string, string> dict)
{
// Sorting dictionary by keys to ensure consistent ordering
IEnumerable<string> combinedPairs = dict
.OrderBy(kvp => kvp.Key)
.Select(kvp => $"{kvp.Key}={kvp.Value}");

string concatenatedString = string.Join("|", combinedPairs);

byte[] inputBytes = Encoding.UTF8.GetBytes(concatenatedString);
byte[] hashedBytes = SHA256.HashData(inputBytes);

return BitConverter.ToString(hashedBytes).Replace("-", "").ToLower();
}

public static string ComputeHash(this Dictionary<string, byte[]> dict)
{
// Sorting dictionary by keys to ensure consistent ordering
List<KeyValuePair<string, byte[]>> sortedDict = dict.OrderBy(kvp => kvp.Key).ToList();

using var stream = new MemoryStream();
foreach (KeyValuePair<string, byte[]> pair in sortedDict)
{
byte[] keyBytes = Encoding.UTF8.GetBytes(pair.Key);
stream.Write(keyBytes, 0, keyBytes.Length);
stream.WriteByte((byte)'|');
stream.Write(pair.Value, 0, pair.Value.Length);
}

return BitConverter.ToString(SHA256.HashData(stream.ToArray())).Replace("-", "").ToLower();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>

<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.1" />
<PackageReference Include="NFluent" Version="3.0.1.352" />
<PackageReference Include="xunit" Version="2.5.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.0">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="6.0.0">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\Bitwarden.SecretOperator\Bitwarden.SecretOperator.csproj" />
</ItemGroup>

</Project>
54 changes: 54 additions & 0 deletions tests/Bitwarden.SecretOperator.Tests/DictionaryHashComputation.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
using System.Text;
using Bitwarden.SecretOperator.Helpers;
using NFluent;

namespace Bitwarden.SecretOperator.Tests;

public class DictionaryHashComputation
{
[Fact]
public void ComputeHashDictionaryStringString()
{
var dico1 = new Dictionary<string, string>()
{
{"test1", "test2"},
{"test2", "test3"}
};
var dico2 = new Dictionary<string, string>()
{
{"test1", "test3"},
{"test2", "test2"}
};

string hash1 = dico1.ComputeHash();
string hash1Bis = dico1.ComputeHash();
Check.That(hash1).IsEqualTo(hash1Bis);

string hash2 = dico2.ComputeHash();

Check.That(hash1).IsNotEqualTo(hash2);
}

[Fact]
public void ComputeHashDictionaryStringByteArray()
{
var dico1 = new Dictionary<string, byte[]>()
{
{"test1", "test2"u8.ToArray()},
{"test2", "test3"u8.ToArray()}
};
var dico2 = new Dictionary<string, byte[]>()
{
{"test1", "test3"u8.ToArray()},
{"test2", "test2"u8.ToArray()}
};

string hash1 = dico1.ComputeHash();
string hash1Bis = dico1.ComputeHash();
Check.That(hash1).IsEqualTo(hash1Bis);

string hash2 = dico2.ComputeHash();

Check.That(hash1).IsNotEqualTo(hash2);
}
}
1 change: 1 addition & 0 deletions tests/Bitwarden.SecretOperator.Tests/Usings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
global using Xunit;

0 comments on commit a7e4b6e

Please sign in to comment.