Skip to content

Commit 88cc35f

Browse files
committed
feat(client): add create, update, delete methods for enumerable entities
1 parent 44df7e3 commit 88cc35f

File tree

5 files changed

+123
-91
lines changed

5 files changed

+123
-91
lines changed

examples/Operator/todos.txt

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,3 @@
99
- docs
1010
- cache?
1111
- try .net 8 AOT?
12-
- client:
13-
// TODO: test list / get call.
14-
// TODO: update list call

src/KubeOps.KubernetesClient/IKubernetesClient.cs

Lines changed: 114 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,8 @@ Task<IList<TEntity>> ListAsync(
8484
/// <returns>A list of Kubernetes entities.</returns>
8585
Task<IList<TEntity>> ListAsync(
8686
string? @namespace = null,
87-
params LabelSelector[] labelSelectors);
87+
params LabelSelector[] labelSelectors)
88+
=> ListAsync(@namespace, labelSelectors.ToExpression());
8889

8990
/// <inheritdoc cref="ListAsync(string?,string?)"/>
9091
IList<TEntity> List(
@@ -94,7 +95,8 @@ IList<TEntity> List(
9495
/// <inheritdoc cref="ListAsync(string?,LabelSelector[])"/>
9596
IList<TEntity> List(
9697
string? @namespace = null,
97-
params LabelSelector[] labelSelectors);
98+
params LabelSelector[] labelSelectors)
99+
=> List(@namespace, labelSelectors.ToExpression());
98100

99101
/// <summary>
100102
/// Create or Update a entity. This first fetches the entity from the Kubernetes API
@@ -108,22 +110,71 @@ IList<TEntity> List(
108110
_ => await CreateAsync(entity),
109111
};
110112

111-
/// <inheritdoc cref="SaveAsync"/>
113+
/// <summary>
114+
/// Create or Update a list of entities. This first fetches each entity from the Kubernetes API
115+
/// and if it does exist, updates the entity. Otherwise, the entity is created.
116+
/// </summary>
117+
/// <param name="entities">The entity list.</param>
118+
/// <returns>The saved instances of the entities.</returns>
119+
async Task<IEnumerable<TEntity>> SaveAsync(IEnumerable<TEntity> entities) =>
120+
await Task.WhenAll(entities.Select(SaveAsync));
121+
122+
/// <summary>
123+
/// Create or Update a list of entities. This first fetches each entity from the Kubernetes API
124+
/// and if it does exist, updates the entity. Otherwise, the entity is created.
125+
/// </summary>
126+
/// <param name="entities">The entity list.</param>
127+
/// <returns>The saved instances of the entities.</returns>
128+
async Task<IEnumerable<TEntity>> SaveAsync(params TEntity[] entities) =>
129+
await Task.WhenAll(entities.Select(SaveAsync));
130+
131+
/// <inheritdoc cref="SaveAsync(TEntity)"/>
112132
TEntity Save(TEntity entity) => Get(entity.Name(), entity.Namespace()) switch
113133
{
114134
{ } e => Update(entity.WithResourceVersion(e)),
115135
_ => Create(entity),
116136
};
117137

138+
/// <inheritdoc cref="SaveAsync(IEnumerable{TEntity})"/>
139+
IEnumerable<TEntity> Save(IEnumerable<TEntity> entities) => entities.Select(Save);
140+
141+
/// <inheritdoc cref="SaveAsync(IEnumerable{TEntity})"/>
142+
IEnumerable<TEntity> Save(params TEntity[] entities) => entities.Select(Save);
143+
118144
/// <summary>
119145
/// Create the given entity on the Kubernetes API.
120146
/// </summary>
121147
/// <param name="entity">The entity instance.</param>
122148
/// <returns>The created instance of the entity.</returns>
123149
Task<TEntity> CreateAsync(TEntity entity);
124150

125-
/// <inheritdoc cref="CreateAsync"/>
126-
TEntity Create(TEntity entity);
151+
/// <summary>
152+
/// Create a list of entities on the Kubernetes API.
153+
/// </summary>
154+
/// <param name="entities">The entity list.</param>
155+
/// <returns>The created instances of the entities.</returns>
156+
async Task<IEnumerable<TEntity>> CreateAsync(IEnumerable<TEntity> entities)
157+
=> await Task.WhenAll(entities.Select(CreateAsync));
158+
159+
/// <summary>
160+
/// Create a list of entities on the Kubernetes API.
161+
/// </summary>
162+
/// <param name="entities">The entity list.</param>
163+
/// <returns>The created instances of the entities.</returns>
164+
async Task<IEnumerable<TEntity>> CreateAsync(params TEntity[] entities)
165+
=> await Task.WhenAll(entities.Select(CreateAsync));
166+
167+
/// <inheritdoc cref="CreateAsync(TEntity)"/>
168+
TEntity Create(TEntity entity)
169+
=> CreateAsync(entity).GetAwaiter().GetResult();
170+
171+
/// <inheritdoc cref="CreateAsync(IEnumerable{TEntity})"/>
172+
IEnumerable<TEntity> Create(IEnumerable<TEntity> entities)
173+
=> entities.Select(Create);
174+
175+
/// <inheritdoc cref="CreateAsync(TEntity[])"/>
176+
IEnumerable<TEntity> Create(params TEntity[] entities)
177+
=> entities.Select(Create);
127178

128179
/// <summary>
129180
/// Update the given entity on the Kubernetes API.
@@ -132,8 +183,33 @@ IList<TEntity> List(
132183
/// <returns>The updated instance of the entity.</returns>
133184
Task<TEntity> UpdateAsync(TEntity entity);
134185

135-
/// <inheritdoc cref="UpdateAsync"/>
136-
TEntity Update(TEntity entity);
186+
/// <summary>
187+
/// Update a list of entities on the Kubernetes API.
188+
/// </summary>
189+
/// <param name="entities">An enumerable of entities.</param>
190+
/// <returns>The updated instances of the entities.</returns>
191+
async Task<IEnumerable<TEntity>> UpdateAsync(IEnumerable<TEntity> entities)
192+
=> await Task.WhenAll(entities.Select(UpdateAsync));
193+
194+
/// <summary>
195+
/// Update a list of entities on the Kubernetes API.
196+
/// </summary>
197+
/// <param name="entities">An enumerable of entities.</param>
198+
/// <returns>The updated instances of the entities.</returns>
199+
async Task<IEnumerable<TEntity>> UpdateAsync(params TEntity[] entities)
200+
=> await Task.WhenAll(entities.Select(UpdateAsync));
201+
202+
/// <inheritdoc cref="UpdateAsync(TEntity)"/>
203+
TEntity Update(TEntity entity)
204+
=> UpdateAsync(entity).GetAwaiter().GetResult();
205+
206+
/// <inheritdoc cref="UpdateAsync(IEnumerable{TEntity})"/>
207+
IEnumerable<TEntity> Update(IEnumerable<TEntity> entities)
208+
=> entities.Select(Update);
209+
210+
/// <inheritdoc cref="UpdateAsync(TEntity[])"/>
211+
IEnumerable<TEntity> Update(params TEntity[] entities)
212+
=> entities.Select(Update);
137213

138214
/// <summary>
139215
/// Update the status object of a given entity on the Kubernetes API.
@@ -147,15 +223,17 @@ IList<TEntity> List(
147223

148224
/// <inheritdoc cref="Delete(TEntity)"/>
149225
/// <returns>A task that completes when the call was made.</returns>
150-
Task DeleteAsync(TEntity entity);
226+
Task DeleteAsync(TEntity entity) => DeleteAsync(entity.Name(), entity.Namespace());
151227

152228
/// <inheritdoc cref="Delete(IEnumerable{TEntity})"/>
153229
/// <returns>A task that completes when the call was made.</returns>
154-
Task DeleteAsync(IEnumerable<TEntity> entities);
230+
Task DeleteAsync(IEnumerable<TEntity> entities)
231+
=> Task.WhenAll(entities.Select(DeleteAsync));
155232

156233
/// <inheritdoc cref="Delete(TEntity[])"/>
157234
/// <returns>A task that completes when the call was made.</returns>
158-
Task DeleteAsync(params TEntity[] entities);
235+
Task DeleteAsync(params TEntity[] entities)
236+
=> Task.WhenAll(entities.Select(DeleteAsync));
159237

160238
/// <inheritdoc cref="Delete(string,string?)"/>
161239
/// <returns>A task that completes when the call was made.</returns>
@@ -165,26 +243,39 @@ IList<TEntity> List(
165243
/// Delete a given entity from the Kubernetes API.
166244
/// </summary>
167245
/// <param name="entity">The entity in question.</param>
168-
void Delete(TEntity entity);
246+
void Delete(TEntity entity) => Delete(entity.Name(), entity.Namespace());
169247

170248
/// <summary>
171249
/// Delete a given list of entities from the Kubernetes API.
172250
/// </summary>
173251
/// <param name="entities">The entities in question.</param>
174-
void Delete(IEnumerable<TEntity> entities);
252+
void Delete(IEnumerable<TEntity> entities)
253+
{
254+
foreach (var entity in entities)
255+
{
256+
Delete(entity);
257+
}
258+
}
175259

176260
/// <summary>
177261
/// Delete a given list of entities from the Kubernetes API.
178262
/// </summary>
179263
/// <param name="entities">The entities in question.</param>
180-
void Delete(params TEntity[] entities);
264+
void Delete(params TEntity[] entities)
265+
{
266+
foreach (var entity in entities)
267+
{
268+
Delete(entity);
269+
}
270+
}
181271

182272
/// <summary>
183273
/// Delete a given entity by name from the Kubernetes API.
184274
/// </summary>
185275
/// <param name="name">The name of the entity.</param>
186276
/// <param name="namespace">The optional namespace of the entity.</param>
187-
void Delete(string name, string? @namespace = null);
277+
void Delete(string name, string? @namespace = null)
278+
=> DeleteAsync(name, @namespace).GetAwaiter().GetResult();
188279

189280
/// <summary>
190281
/// Create a entity watcher on the Kubernetes API.
@@ -209,7 +300,15 @@ Watcher<TEntity> Watch(
209300
string? @namespace = null,
210301
TimeSpan? timeout = null,
211302
CancellationToken cancellationToken = default,
212-
params LabelSelector[] labelSelectors);
303+
params LabelSelector[] labelSelectors)
304+
=> Watch(
305+
onEvent,
306+
onError,
307+
onClose,
308+
@namespace,
309+
timeout,
310+
labelSelectors.ToExpression(),
311+
cancellationToken);
213312

214313
/// <summary>
215314
/// Create a entity watcher on the Kubernetes API.

src/KubeOps.KubernetesClient/KubernetesClient.cs

Lines changed: 0 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
using k8s.Models;
66

77
using KubeOps.Abstractions.Entities;
8-
using KubeOps.KubernetesClient.LabelSelectors;
98

109
namespace KubeOps.KubernetesClient;
1110

@@ -182,10 +181,6 @@ public async Task<IList<TEntity>> ListAsync(string? @namespace = null, string? l
182181
labelSelector: labelSelector),
183182
}).Items;
184183

185-
/// <inheritdoc />
186-
public Task<IList<TEntity>> ListAsync(string? @namespace = null, params LabelSelector[] labelSelectors)
187-
=> ListAsync(@namespace, labelSelectors.ToExpression());
188-
189184
/// <inheritdoc />
190185
public IList<TEntity> List(string? @namespace = null, string? labelSelector = null)
191186
=> (@namespace switch
@@ -203,10 +198,6 @@ public IList<TEntity> List(string? @namespace = null, string? labelSelector = nu
203198
labelSelector: labelSelector),
204199
}).Items;
205200

206-
/// <inheritdoc />
207-
public IList<TEntity> List(string? @namespace = null, params LabelSelector[] labelSelectors)
208-
=> List(@namespace, labelSelectors.ToExpression());
209-
210201
/// <inheritdoc />
211202
public Task<TEntity> CreateAsync(TEntity entity)
212203
=> entity.Namespace() switch
@@ -215,10 +206,6 @@ public Task<TEntity> CreateAsync(TEntity entity)
215206
null => _genericClient.CreateAsync(entity),
216207
};
217208

218-
/// <inheritdoc />
219-
public TEntity Create(TEntity entity)
220-
=> CreateAsync(entity).GetAwaiter().GetResult();
221-
222209
/// <inheritdoc />
223210
public Task<TEntity> UpdateAsync(TEntity entity)
224211
=> entity.Namespace() switch
@@ -227,10 +214,6 @@ public Task<TEntity> UpdateAsync(TEntity entity)
227214
null => _genericClient.ReplaceAsync(entity, entity.Name()),
228215
};
229216

230-
/// <inheritdoc />
231-
public TEntity Update(TEntity entity)
232-
=> UpdateAsync(entity).GetAwaiter().GetResult();
233-
234217
/// <inheritdoc />
235218
public Task<TEntity> UpdateStatusAsync(TEntity entity)
236219
=> entity.Namespace() switch
@@ -269,19 +252,6 @@ public TEntity UpdateStatus(TEntity entity)
269252
entity.Name()),
270253
};
271254

272-
/// <inheritdoc />
273-
public Task DeleteAsync(TEntity entity) => DeleteAsync(
274-
entity.Name(),
275-
entity.Namespace());
276-
277-
/// <inheritdoc />
278-
public Task DeleteAsync(IEnumerable<TEntity> entities) =>
279-
Task.WhenAll(entities.Select(DeleteAsync));
280-
281-
/// <inheritdoc />
282-
public Task DeleteAsync(params TEntity[] entities) =>
283-
Task.WhenAll(entities.Select(DeleteAsync));
284-
285255
/// <inheritdoc />
286256
public async Task DeleteAsync(string name, string? @namespace = null)
287257
{
@@ -303,40 +273,6 @@ public async Task DeleteAsync(string name, string? @namespace = null)
303273
}
304274
}
305275

306-
/// <inheritdoc />
307-
public void Delete(TEntity entity)
308-
=> DeleteAsync(entity).GetAwaiter().GetResult();
309-
310-
/// <inheritdoc />
311-
public void Delete(IEnumerable<TEntity> entities)
312-
=> DeleteAsync(entities).GetAwaiter().GetResult();
313-
314-
/// <inheritdoc />
315-
public void Delete(params TEntity[] entities)
316-
=> DeleteAsync(entities).GetAwaiter().GetResult();
317-
318-
/// <inheritdoc />
319-
public void Delete(string name, string? @namespace = null)
320-
=> DeleteAsync(name, @namespace).GetAwaiter().GetResult();
321-
322-
/// <inheritdoc />
323-
public Watcher<TEntity> Watch(
324-
Action<WatchEventType, TEntity> onEvent,
325-
Action<Exception>? onError = null,
326-
Action? onClose = null,
327-
string? @namespace = null,
328-
TimeSpan? timeout = null,
329-
CancellationToken cancellationToken = default,
330-
params LabelSelector[] labelSelectors)
331-
=> Watch(
332-
onEvent,
333-
onError,
334-
onClose,
335-
@namespace,
336-
timeout,
337-
labelSelectors.ToExpression(),
338-
cancellationToken);
339-
340276
/// <inheritdoc />
341277
public Watcher<TEntity> Watch(
342278
Action<WatchEventType, TEntity> onEvent,

test/KubeOps.Operator.Test/Controller/EntityController.Integration.Test.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ public async Task Should_Call_Reconcile_On_New_Entity()
2727
{
2828
(await _client.ListAsync("default")).Count.Should().Be(0);
2929

30-
await _client.CreateAsync(new("test-entity", "username", "default"));
30+
await _client.CreateAsync(new V1IntegrationTestEntity("test-entity", "username", "default"));
3131
await _mock.WaitForInvocations;
3232

3333
_mock.Invocations.Count.Should().Be(1);
@@ -44,7 +44,7 @@ public async Task Should_Call_Reconcile_On_Modification_Of_Entity()
4444
_mock.TargetInvocationCount = 2;
4545
(await _client.ListAsync("default")).Count.Should().Be(0);
4646

47-
var result = await _client.CreateAsync(new("test-entity", "username", "default"));
47+
var result = await _client.CreateAsync(new V1IntegrationTestEntity("test-entity", "username", "default"));
4848
result.Spec.Username = "changed";
4949
await _client.UpdateAsync(result);
5050
await _mock.WaitForInvocations;
@@ -70,7 +70,7 @@ public async Task Should_Call_Delete_For_Deleted_Entity()
7070
_mock.TargetInvocationCount = 2;
7171
(await _client.ListAsync("default")).Count.Should().Be(0);
7272

73-
var result = await _client.CreateAsync(new("test-entity", "username", "default"));
73+
var result = await _client.CreateAsync(new V1IntegrationTestEntity("test-entity", "username", "default"));
7474
await _client.DeleteAsync(result);
7575
await _mock.WaitForInvocations;
7676

0 commit comments

Comments
 (0)