Skip to content

Commit

Permalink
feat(client): add create, update, delete methods for enumerable entities
Browse files Browse the repository at this point in the history
  • Loading branch information
buehler committed Oct 3, 2023
1 parent fc8f725 commit f56ec3a
Show file tree
Hide file tree
Showing 5 changed files with 123 additions and 91 deletions.
3 changes: 0 additions & 3 deletions examples/Operator/todos.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,3 @@
- docs
- cache?
- try .net 8 AOT?
- client:
// TODO: test list / get call.
// TODO: update list call
129 changes: 114 additions & 15 deletions src/KubeOps.KubernetesClient/IKubernetesClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,8 @@ Task<IList<TEntity>> ListAsync(
/// <returns>A list of Kubernetes entities.</returns>
Task<IList<TEntity>> ListAsync(
string? @namespace = null,
params LabelSelector[] labelSelectors);
params LabelSelector[] labelSelectors)
=> ListAsync(@namespace, labelSelectors.ToExpression());

/// <inheritdoc cref="ListAsync(string?,string?)"/>
IList<TEntity> List(
Expand All @@ -94,7 +95,8 @@ IList<TEntity> List(
/// <inheritdoc cref="ListAsync(string?,LabelSelector[])"/>
IList<TEntity> List(
string? @namespace = null,
params LabelSelector[] labelSelectors);
params LabelSelector[] labelSelectors)
=> List(@namespace, labelSelectors.ToExpression());

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

/// <inheritdoc cref="SaveAsync"/>
/// <summary>
/// Create or Update a list of entities. This first fetches each entity from the Kubernetes API
/// and if it does exist, updates the entity. Otherwise, the entity is created.
/// </summary>
/// <param name="entities">The entity list.</param>
/// <returns>The saved instances of the entities.</returns>
async Task<IEnumerable<TEntity>> SaveAsync(IEnumerable<TEntity> entities) =>
await Task.WhenAll(entities.Select(SaveAsync));

/// <summary>
/// Create or Update a list of entities. This first fetches each entity from the Kubernetes API
/// and if it does exist, updates the entity. Otherwise, the entity is created.
/// </summary>
/// <param name="entities">The entity list.</param>
/// <returns>The saved instances of the entities.</returns>
async Task<IEnumerable<TEntity>> SaveAsync(params TEntity[] entities) =>
await Task.WhenAll(entities.Select(SaveAsync));

/// <inheritdoc cref="SaveAsync(TEntity)"/>
TEntity Save(TEntity entity) => Get(entity.Name(), entity.Namespace()) switch
{
{ } e => Update(entity.WithResourceVersion(e)),
_ => Create(entity),
};

/// <inheritdoc cref="SaveAsync(IEnumerable{TEntity})"/>
IEnumerable<TEntity> Save(IEnumerable<TEntity> entities) => entities.Select(Save);

/// <inheritdoc cref="SaveAsync(IEnumerable{TEntity})"/>
IEnumerable<TEntity> Save(params TEntity[] entities) => entities.Select(Save);

/// <summary>
/// Create the given entity on the Kubernetes API.
/// </summary>
/// <param name="entity">The entity instance.</param>
/// <returns>The created instance of the entity.</returns>
Task<TEntity> CreateAsync(TEntity entity);

/// <inheritdoc cref="CreateAsync"/>
TEntity Create(TEntity entity);
/// <summary>
/// Create a list of entities on the Kubernetes API.
/// </summary>
/// <param name="entities">The entity list.</param>
/// <returns>The created instances of the entities.</returns>
async Task<IEnumerable<TEntity>> CreateAsync(IEnumerable<TEntity> entities)
=> await Task.WhenAll(entities.Select(CreateAsync));

/// <summary>
/// Create a list of entities on the Kubernetes API.
/// </summary>
/// <param name="entities">The entity list.</param>
/// <returns>The created instances of the entities.</returns>
async Task<IEnumerable<TEntity>> CreateAsync(params TEntity[] entities)
=> await Task.WhenAll(entities.Select(CreateAsync));

/// <inheritdoc cref="CreateAsync(TEntity)"/>
TEntity Create(TEntity entity)
=> CreateAsync(entity).GetAwaiter().GetResult();

/// <inheritdoc cref="CreateAsync(IEnumerable{TEntity})"/>
IEnumerable<TEntity> Create(IEnumerable<TEntity> entities)
=> entities.Select(Create);

/// <inheritdoc cref="CreateAsync(TEntity[])"/>
IEnumerable<TEntity> Create(params TEntity[] entities)
=> entities.Select(Create);

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

/// <inheritdoc cref="UpdateAsync"/>
TEntity Update(TEntity entity);
/// <summary>
/// Update a list of entities on the Kubernetes API.
/// </summary>
/// <param name="entities">An enumerable of entities.</param>
/// <returns>The updated instances of the entities.</returns>
async Task<IEnumerable<TEntity>> UpdateAsync(IEnumerable<TEntity> entities)
=> await Task.WhenAll(entities.Select(UpdateAsync));

/// <summary>
/// Update a list of entities on the Kubernetes API.
/// </summary>
/// <param name="entities">An enumerable of entities.</param>
/// <returns>The updated instances of the entities.</returns>
async Task<IEnumerable<TEntity>> UpdateAsync(params TEntity[] entities)
=> await Task.WhenAll(entities.Select(UpdateAsync));

/// <inheritdoc cref="UpdateAsync(TEntity)"/>
TEntity Update(TEntity entity)
=> UpdateAsync(entity).GetAwaiter().GetResult();

/// <inheritdoc cref="UpdateAsync(IEnumerable{TEntity})"/>
IEnumerable<TEntity> Update(IEnumerable<TEntity> entities)
=> entities.Select(Update);

/// <inheritdoc cref="UpdateAsync(TEntity[])"/>
IEnumerable<TEntity> Update(params TEntity[] entities)
=> entities.Select(Update);

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

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

/// <inheritdoc cref="Delete(IEnumerable{TEntity})"/>
/// <returns>A task that completes when the call was made.</returns>
Task DeleteAsync(IEnumerable<TEntity> entities);
Task DeleteAsync(IEnumerable<TEntity> entities)
=> Task.WhenAll(entities.Select(DeleteAsync));

/// <inheritdoc cref="Delete(TEntity[])"/>
/// <returns>A task that completes when the call was made.</returns>
Task DeleteAsync(params TEntity[] entities);
Task DeleteAsync(params TEntity[] entities)
=> Task.WhenAll(entities.Select(DeleteAsync));

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

/// <summary>
/// Delete a given list of entities from the Kubernetes API.
/// </summary>
/// <param name="entities">The entities in question.</param>
void Delete(IEnumerable<TEntity> entities);
void Delete(IEnumerable<TEntity> entities)
{
foreach (var entity in entities)
{
Delete(entity);
}
}

/// <summary>
/// Delete a given list of entities from the Kubernetes API.
/// </summary>
/// <param name="entities">The entities in question.</param>
void Delete(params TEntity[] entities);
void Delete(params TEntity[] entities)
{
foreach (var entity in entities)
{
Delete(entity);
}
}

/// <summary>
/// Delete a given entity by name from the Kubernetes API.
/// </summary>
/// <param name="name">The name of the entity.</param>
/// <param name="namespace">The optional namespace of the entity.</param>
void Delete(string name, string? @namespace = null);
void Delete(string name, string? @namespace = null)
=> DeleteAsync(name, @namespace).GetAwaiter().GetResult();

/// <summary>
/// Create a entity watcher on the Kubernetes API.
Expand All @@ -209,7 +300,15 @@ Watcher<TEntity> Watch(
string? @namespace = null,
TimeSpan? timeout = null,
CancellationToken cancellationToken = default,
params LabelSelector[] labelSelectors);
params LabelSelector[] labelSelectors)
=> Watch(
onEvent,
onError,
onClose,
@namespace,
timeout,
labelSelectors.ToExpression(),
cancellationToken);

/// <summary>
/// Create a entity watcher on the Kubernetes API.
Expand Down
64 changes: 0 additions & 64 deletions src/KubeOps.KubernetesClient/KubernetesClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
using k8s.Models;

using KubeOps.Abstractions.Entities;
using KubeOps.KubernetesClient.LabelSelectors;

namespace KubeOps.KubernetesClient;

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

/// <inheritdoc />
public Task<IList<TEntity>> ListAsync(string? @namespace = null, params LabelSelector[] labelSelectors)
=> ListAsync(@namespace, labelSelectors.ToExpression());

/// <inheritdoc />
public IList<TEntity> List(string? @namespace = null, string? labelSelector = null)
=> (@namespace switch
Expand All @@ -203,10 +198,6 @@ public IList<TEntity> List(string? @namespace = null, string? labelSelector = nu
labelSelector: labelSelector),
}).Items;

/// <inheritdoc />
public IList<TEntity> List(string? @namespace = null, params LabelSelector[] labelSelectors)
=> List(@namespace, labelSelectors.ToExpression());

/// <inheritdoc />
public Task<TEntity> CreateAsync(TEntity entity)
=> entity.Namespace() switch
Expand All @@ -215,10 +206,6 @@ public Task<TEntity> CreateAsync(TEntity entity)
null => _genericClient.CreateAsync(entity),
};

/// <inheritdoc />
public TEntity Create(TEntity entity)
=> CreateAsync(entity).GetAwaiter().GetResult();

/// <inheritdoc />
public Task<TEntity> UpdateAsync(TEntity entity)
=> entity.Namespace() switch
Expand All @@ -227,10 +214,6 @@ public Task<TEntity> UpdateAsync(TEntity entity)
null => _genericClient.ReplaceAsync(entity, entity.Name()),
};

/// <inheritdoc />
public TEntity Update(TEntity entity)
=> UpdateAsync(entity).GetAwaiter().GetResult();

/// <inheritdoc />
public Task<TEntity> UpdateStatusAsync(TEntity entity)
=> entity.Namespace() switch
Expand Down Expand Up @@ -269,19 +252,6 @@ public TEntity UpdateStatus(TEntity entity)
entity.Name()),
};

/// <inheritdoc />
public Task DeleteAsync(TEntity entity) => DeleteAsync(
entity.Name(),
entity.Namespace());

/// <inheritdoc />
public Task DeleteAsync(IEnumerable<TEntity> entities) =>
Task.WhenAll(entities.Select(DeleteAsync));

/// <inheritdoc />
public Task DeleteAsync(params TEntity[] entities) =>
Task.WhenAll(entities.Select(DeleteAsync));

/// <inheritdoc />
public async Task DeleteAsync(string name, string? @namespace = null)
{
Expand All @@ -303,40 +273,6 @@ public async Task DeleteAsync(string name, string? @namespace = null)
}
}

/// <inheritdoc />
public void Delete(TEntity entity)
=> DeleteAsync(entity).GetAwaiter().GetResult();

/// <inheritdoc />
public void Delete(IEnumerable<TEntity> entities)
=> DeleteAsync(entities).GetAwaiter().GetResult();

/// <inheritdoc />
public void Delete(params TEntity[] entities)
=> DeleteAsync(entities).GetAwaiter().GetResult();

/// <inheritdoc />
public void Delete(string name, string? @namespace = null)
=> DeleteAsync(name, @namespace).GetAwaiter().GetResult();

/// <inheritdoc />
public Watcher<TEntity> Watch(
Action<WatchEventType, TEntity> onEvent,
Action<Exception>? onError = null,
Action? onClose = null,
string? @namespace = null,
TimeSpan? timeout = null,
CancellationToken cancellationToken = default,
params LabelSelector[] labelSelectors)
=> Watch(
onEvent,
onError,
onClose,
@namespace,
timeout,
labelSelectors.ToExpression(),
cancellationToken);

/// <inheritdoc />
public Watcher<TEntity> Watch(
Action<WatchEventType, TEntity> onEvent,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public async Task Should_Call_Reconcile_On_New_Entity()
{
(await _client.ListAsync("default")).Count.Should().Be(0);

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

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

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

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

Expand Down
Loading

0 comments on commit f56ec3a

Please sign in to comment.