Skip to content

Commit c1f3e0f

Browse files
authored
Add support for k8s 1.23 and watcher timeout (#249)
- Updated NuGet packages - Fix breaking change in k8s client. It uses Syste.Text.Json for annotations - Added support for watcher timeout - Update workflow to only build on PR
1 parent 92c6207 commit c1f3e0f

File tree

15 files changed

+115
-23
lines changed

15 files changed

+115
-23
lines changed

.github/workflows/pipeline.yaml

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,7 @@ jobs:
2525
- name: tools - helm - install
2626
uses: azure/setup-helm@v1
2727

28-
- name: tools - docker - login
29-
uses: docker/login-action@v1
30-
with:
31-
username: ${{ secrets.ES_DOCKERHUB_USERNAME }}
32-
password: ${{ secrets.ES_DOCKERHUB_PAT }}
28+
3329

3430
- name: checkout
3531
uses: actions/checkout@v2
@@ -60,7 +56,26 @@ jobs:
6056
name: kubectl
6157
path: .artifacts/kubectl
6258

59+
- name: "docker - build PR"
60+
if: github.event_name == 'pull_request'
61+
run: |
62+
docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
63+
docker buildx create --name builder --driver docker-container --use
64+
docker buildx inspect --bootstrap
65+
docker buildx build --platform linux/amd64 -t ${{env.imageRepository}}:build-${{env.version}}-amd64 -f src/ES.Kubernetes.Reflector/Dockerfile src/
66+
docker buildx build --platform linux/arm -t ${{env.imageRepository}}:build-${{env.version}}-arm32v7 -f src/ES.Kubernetes.Reflector/Dockerfile src/
67+
docker buildx build --platform linux/arm64 -t ${{env.imageRepository}}:build-${{env.version}}-arm64v8 -f src/ES.Kubernetes.Reflector/Dockerfile src/
68+
69+
- name: tools - docker - login
70+
if: github.event_name == 'push'
71+
uses: docker/login-action@v1
72+
with:
73+
username: ${{ secrets.ES_DOCKERHUB_USERNAME }}
74+
password: ${{ secrets.ES_DOCKERHUB_PAT }}
75+
76+
6377
- name: "docker - build and publish"
78+
if: github.event_name == 'push'
6479
run: |
6580
docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
6681
docker buildx create --name builder --driver docker-container --use

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ You can customize the values of the helm deployment by using the following Value
4040
| `image.tag` | Container image tag | `Same as chart version` |
4141
| `image.pullPolicy` | Container image pull policy | `IfNotPresent` |
4242
| `configuration.logging.minimumLevel` | Logging minimum level | `Information` |
43+
| `configuration.watcher.timeout` | Maximum watcher lifetime in seconds | `` |
4344
| `rbac.enabled` | Create and use RBAC resources | `true` |
4445
| `serviceAccount.create` | Create ServiceAccount | `true` |
4546
| `serviceAccount.name` | ServiceAccount name | _release name_ |
Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,25 @@
1-
using ES.Kubernetes.Reflector.Core.Watchers;
1+
using ES.Kubernetes.Reflector.Core.Configuration;
2+
using ES.Kubernetes.Reflector.Core.Watchers;
23
using k8s;
34
using k8s.Models;
45
using MediatR;
6+
using Microsoft.Extensions.Options;
57
using Microsoft.Rest;
68

79
namespace ES.Kubernetes.Reflector.Core;
810

911
public class ConfigMapWatcher : WatcherBackgroundService<V1ConfigMap, V1ConfigMapList>
1012
{
11-
public ConfigMapWatcher(ILogger<ConfigMapWatcher> logger, IMediator mediator, IKubernetes client) : base(logger,
12-
mediator,
13-
client)
13+
public ConfigMapWatcher(ILogger<ConfigMapWatcher> logger, IMediator mediator, IKubernetes client,
14+
IOptionsMonitor<ReflectorOptions> options) :
15+
base(logger, mediator, client, options)
1416
{
1517
}
1618

1719

1820
protected override Task<HttpOperationResponse<V1ConfigMapList>> OnGetWatcher(CancellationToken cancellationToken)
1921
{
20-
return Client.ListConfigMapForAllNamespacesWithHttpMessagesAsync(watch: true,
22+
return Client.ListConfigMapForAllNamespacesWithHttpMessagesAsync(watch: true, timeoutSeconds: WatcherTimeout,
2123
cancellationToken: cancellationToken);
2224
}
2325
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
namespace ES.Kubernetes.Reflector.Core.Configuration;
2+
3+
public class ReflectorOptions
4+
{
5+
public WatcherOptions? Watcher { get; set; }
6+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
namespace ES.Kubernetes.Reflector.Core.Configuration;
2+
3+
public class WatcherOptions
4+
{
5+
public int? Timeout { get; set; }
6+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
using System.Reflection;
2+
using System.Text.Json.Serialization;
3+
using Newtonsoft.Json;
4+
using Newtonsoft.Json.Serialization;
5+
6+
namespace ES.Kubernetes.Reflector.Core.Json;
7+
8+
/// <summary>
9+
/// Used to resolve <see cref="JsonPropertyNameAttribute" /> decorated contracts (breaking change since Kubernetes
10+
/// client switched to System.Text.Json
11+
/// </summary>
12+
public class JsonPropertyNameContractResolver : DefaultContractResolver
13+
{
14+
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
15+
{
16+
var property = base.CreateProperty(member, memberSerialization);
17+
18+
if (member.GetCustomAttribute<JsonPropertyNameAttribute>() is { } propertyNameAttribute)
19+
{
20+
property.PropertyName = propertyNameAttribute.Name;
21+
return property;
22+
}
23+
24+
return property;
25+
}
26+
}

src/ES.Kubernetes.Reflector/Core/Mirroring/ResourceMirror.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System.Collections.Concurrent;
22
using System.Net;
33
using ES.Kubernetes.Reflector.Core.Extensions;
4+
using ES.Kubernetes.Reflector.Core.Json;
45
using ES.Kubernetes.Reflector.Core.Messages;
56
using ES.Kubernetes.Reflector.Core.Mirroring.Constants;
67
using ES.Kubernetes.Reflector.Core.Mirroring.Extensions;
@@ -9,6 +10,7 @@
910
using k8s.Models;
1011
using MediatR;
1112
using Microsoft.AspNetCore.JsonPatch;
13+
using Microsoft.AspNetCore.JsonPatch.Operations;
1214
using Microsoft.Rest;
1315
using Newtonsoft.Json;
1416

@@ -445,15 +447,17 @@ private async Task ResourceReflect(KubeRef sourceId, KubeRef targetId, TResource
445447
return;
446448
}
447449

448-
var patchDoc = new JsonPatchDocument<TResource>();
450+
451+
var patchDoc = new JsonPatchDocument<TResource>(new List<Operation<TResource>>(),
452+
new JsonPropertyNameContractResolver());
449453
var annotations = new Dictionary<string, string>(targetResource.Metadata.Annotations);
450454
foreach (var patchAnnotation in patchAnnotations)
451455
annotations[patchAnnotation.Key] = patchAnnotation.Value;
452456
patchDoc.Replace(e => e.Metadata.Annotations, annotations);
453457

454458
await OnResourceConfigurePatch(source, patchDoc);
455459

456-
var patch = JsonConvert.SerializeObject(patchDoc);
460+
var patch = JsonConvert.SerializeObject(patchDoc, Formatting.Indented);
457461
await OnResourceApplyPatch(new V1Patch(patch, V1Patch.PatchType.JsonPatch), targetId);
458462
Logger.LogInformation("Patched {id} as a reflection of {sourceId}", targetId, sourceId);
459463
}
Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,25 @@
1-
using ES.Kubernetes.Reflector.Core.Watchers;
1+
using ES.Kubernetes.Reflector.Core.Configuration;
2+
using ES.Kubernetes.Reflector.Core.Watchers;
23
using k8s;
34
using k8s.Models;
45
using MediatR;
6+
using Microsoft.Extensions.Options;
57
using Microsoft.Rest;
68

79
namespace ES.Kubernetes.Reflector.Core;
810

911
public class NamespaceWatcher : WatcherBackgroundService<V1Namespace, V1NamespaceList>
1012
{
11-
public NamespaceWatcher(ILogger<NamespaceWatcher> logger, IMediator mediator, IKubernetes client) : base(logger,
12-
mediator, client)
13+
public NamespaceWatcher(ILogger<NamespaceWatcher> logger, IMediator mediator, IKubernetes client,
14+
IOptionsMonitor<ReflectorOptions> options) :
15+
base(logger, mediator, client, options)
1316
{
1417
}
1518

1619

1720
protected override Task<HttpOperationResponse<V1NamespaceList>> OnGetWatcher(CancellationToken cancellationToken)
1821
{
19-
return Client.ListNamespaceWithHttpMessagesAsync(watch: true, cancellationToken: cancellationToken);
22+
return Client.ListNamespaceWithHttpMessagesAsync(watch: true, timeoutSeconds: WatcherTimeout,
23+
cancellationToken: cancellationToken);
2024
}
2125
}
Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,26 @@
1-
using ES.Kubernetes.Reflector.Core.Watchers;
1+
using ES.Kubernetes.Reflector.Core.Configuration;
2+
using ES.Kubernetes.Reflector.Core.Watchers;
23
using k8s;
34
using k8s.Models;
45
using MediatR;
6+
using Microsoft.Extensions.Options;
57
using Microsoft.Rest;
68

79
namespace ES.Kubernetes.Reflector.Core;
810

911
public class SecretWatcher : WatcherBackgroundService<V1Secret, V1SecretList>
1012
{
11-
public SecretWatcher(ILogger<SecretWatcher> logger, IMediator mediator, IKubernetes client) : base(logger, mediator,
12-
client)
13+
public SecretWatcher(ILogger<SecretWatcher> logger, IMediator mediator, IKubernetes client,
14+
IOptionsMonitor<ReflectorOptions> options) :
15+
base(logger, mediator, client, options)
1316
{
1417
}
1518

1619

1720
protected override Task<HttpOperationResponse<V1SecretList>> OnGetWatcher(CancellationToken cancellationToken)
1821
{
19-
return Client.ListSecretForAllNamespacesWithHttpMessagesAsync(watch: true,
22+
return Client.ListSecretForAllNamespacesWithHttpMessagesAsync(watch: true, timeoutSeconds: WatcherTimeout,
2023
cancellationToken: cancellationToken);
24+
;
2125
}
2226
}

src/ES.Kubernetes.Reflector/Core/Watchers/WatcherBackgroundService.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,33 @@
11
using System.Diagnostics;
2+
using ES.Kubernetes.Reflector.Core.Configuration;
23
using ES.Kubernetes.Reflector.Core.Messages;
34
using k8s;
45
using k8s.Models;
56
using MediatR;
7+
using Microsoft.Extensions.Options;
68
using Microsoft.Rest;
79

810
namespace ES.Kubernetes.Reflector.Core.Watchers;
911

1012
public abstract class WatcherBackgroundService<TResource, TResourceList> : BackgroundService
1113
where TResource : IKubernetesObject<V1ObjectMeta>
1214
{
15+
private readonly IOptionsMonitor<ReflectorOptions> _options;
1316
protected readonly IKubernetes Client;
1417
protected readonly ILogger Logger;
1518
protected readonly IMediator Mediator;
1619

17-
protected WatcherBackgroundService(ILogger logger, IMediator mediator, IKubernetes client)
20+
protected WatcherBackgroundService(ILogger logger, IMediator mediator, IKubernetes client,
21+
IOptionsMonitor<ReflectorOptions> options)
1822
{
1923
Logger = logger;
2024
Mediator = mediator;
2125
Client = client;
26+
_options = options;
2227
}
2328

29+
protected int? WatcherTimeout => _options.CurrentValue.Watcher?.Timeout;
30+
2431
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
2532
{
2633
var sessionStopwatch = new Stopwatch();

0 commit comments

Comments
 (0)