Skip to content

Commit

Permalink
Orchestrator - Single Action NetApps visibility (#171)
Browse files Browse the repository at this point in the history
* WIP: refactoring of teh DeploymentService

* DONE: finished extracting creational functionality to a new class

* Extract linq to extension method

* Change deployment order; First collect deployments and then deploy

* Add env variable to connect netapps between services

* Correct tests and messages when not in k8s

* Rename methods, add comments

* WIP: tests

* Finished tests

* Documentation of the network application communication enablement

* Added info about future state of teh ROS based communication

* Correct compilation erors after merge, remove old comments
  • Loading branch information
Artonus committed Jun 23, 2023
1 parent b1bdb76 commit 73739fc
Show file tree
Hide file tree
Showing 24 changed files with 867 additions and 525 deletions.
2 changes: 2 additions & 0 deletions Middleware.sln.DotSettings
Original file line number Diff line number Diff line change
Expand Up @@ -210,9 +210,11 @@
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpPlaceEmbeddedOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpUseContinuousIndentInsideBracesMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=depl/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Distro/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Embb/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=imsi/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=kube/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Multus/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Replan/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Urllc/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
17 changes: 17 additions & 0 deletions docs/Administrator/NetworkApplicationCommunication.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Network Application Communication

This document explains how Middleware enables communication between multiple network applications that have to cooperate.

## Deployment-based communication enablement

By default, when Middleware deploys Network Applications, it enables basic communication between them. During the deployment, each `Task` is constructed using `Actions`. An `Action` is a singular piece of service, that allows the robot to conduct a specific function, for example, navigation and mapping.

When Middleware deploys an `Action` that consists of multiple `Instances` each of the instances is informed about the existence of others. By setting the environment variables, the Middleware gives the addresses by which other Network Applications can be accessed.

The environment variables are named exactly as the Network Applications they reference, but in uppercase and `_` instead of `-`. Each environment variable points to the Kubernetes Service that exposes the deployment.

When the Network Application is configured by the user not to be exposed by a service, Middleware will create a default Kubernetes Service of type `ClusterIP` to enable communication using default `http` and `https` ports.

## Communication of ROS-based Network Applications

To be done with [#158](https://github.com/5G-ERA/middleware/issues/158)
54 changes: 34 additions & 20 deletions src/Common/Config/AppConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,57 +7,71 @@ namespace Middleware.Common.Config;
public static class AppConfig
{
/// <summary>
/// Name of the system
/// Name of the system
/// </summary>
public const string SystemName = "Middleware";

/// <summary>
/// Name of the <see cref="HttpClient"/> used to connect to RedisInterface API
/// Name of the <see cref="HttpClient" /> used to connect to RedisInterface API
/// </summary>
public const string RedisApiClientName = "redisApiClient";

/// <summary>
/// Name of the <see cref="HttpClient"/> used to connect to RedisInterface API
/// Name of the <see cref="HttpClient" /> used to connect to RedisInterface API
/// </summary>
public const string OsmApiClientName = "osmApiClient";

/// <summary>
/// Name of the <see cref="HttpClient"/> used to connect to Orchestrator API
/// Name of the <see cref="HttpClient" /> used to connect to Orchestrator API
/// </summary>
public const string OrchestratorApiClientName = "orchestratorApiClient";

/// <summary>
/// Namespace in which the middleware pods will be deployed
/// Namespace in which the middleware pods will be deployed
/// </summary>
public static string K8SNamespaceName { get; set; } = "middleware";
public const string K8SNamespaceName = "middleware";

/// <summary>
/// Mapping of the services for the conversion of the YAML files
/// Mapping of the services for the conversion of the YAML files
/// </summary>
public static readonly Dictionary<string, Type> K8STypeMappings = new()
{{"v1/Pod", typeof(V1Pod)}, {"v1/Service", typeof(V1Service)}, {"apps/v1", typeof(V1Deployment)}};
{ { "v1/Pod", typeof(V1Pod) }, { "v1/Service", typeof(V1Service) }, { "apps/v1", typeof(V1Deployment) } };

/// <summary>
/// Configuration of the application Development / Release
/// Represents the Address under which the Middleware is accessible
/// </summary>
public static string AppConfiguration { get; set; }
public static string MiddlewareAddress = string.Empty;

/// <summary>
/// Is the application running in the Development environment
/// Interval in which the status check has to be performed. Expressed in seconds.
/// </summary>
/// <returns></returns>
public static bool IsDevEnvironment() => AppConfiguration == AppVersionEnum.Dev.GetStringValue();
public static readonly int StatusCheckInterval = 10;

/// <summary>
/// Represents the Address under which the Middleware is accessible
/// Name of the network created by MultusCNI
/// </summary>
public static string MiddlewareAddress = string.Empty;
public static string MultusNetworkName = "ros-network-1";

/// <summary>
/// Interval in which the status check has to be performed. Expressed in seconds.
/// Configuration of the application Development / Release
/// </summary>
public static readonly int StatusCheckInterval = 10;
public static string AppConfiguration { get; set; }

public static string MiddlewareDeploymentLocationName { get; set; }

/// <summary>
/// Name of the network created by MultusCNI
/// Is the application running in the Development environment
/// </summary>
public static string MultusNetworkName = "ros-network-1";
/// <returns></returns>
public static bool IsDevEnvironment()
{
return AppConfiguration == AppVersionEnum.Dev.GetStringValue();
}

public static string GetMiddlewareAddress()
{
var builder = new UriBuilder(MiddlewareAddress);
builder.Path = "/status/netapp";
return builder.ToString();
}
public static string MiddlewareDeploymentLocationName { get; set; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,17 @@

namespace Middleware.Common.Enums;

public enum K8SServiceKindEnum
public enum K8SServiceKind
{
[StringValue("ClusterIP")]
ClusterIp,

[StringValue("NodePort")]
NodePort,

[StringValue("LoadBalancer")]
LoadBalancer,

[StringValue("ExternalName")]
ExternalName
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,31 +3,27 @@

namespace Middleware.Common.ExtensionMethods;

public static class V1ObjectExtensions
public static class KubernetesObjectExtensions
{
private const string NetAppIdSelector = "serviceId";

/// <summary>
/// Sets the label for the object metadata with the specified serviceId
/// Sets the label for the object metadata with the specified serviceId
/// </summary>
/// <param name="meta"></param>
/// <param name="serviceId"></param>
public static void SetServiceLabel(this V1ObjectMeta meta, Guid serviceId)
{
if (meta.Labels is null)
{
meta.Labels = new Dictionary<string, string>();
}
if (meta.Labels is null) meta.Labels = new Dictionary<string, string>();
meta.Labels.Add(NetAppIdSelector, serviceId.ToString());
}

[Obsolete(
"The Middleware does not use Multus for multiple Network Attachments to pods. Setting this annotation will not have any effect.")]
public static void AddNetAppMultusAnnotations(this V1ObjectMeta meta, string networkName)
{
const string annotationKey = "k8s.v1.cni.cncf.io/networks";
if (meta.Annotations is null)
{
meta.Annotations = new Dictionary<string, string>();
}
if (meta.Annotations is null) meta.Annotations = new Dictionary<string, string>();

meta.Annotations[annotationKey] = networkName;
}
Expand All @@ -36,25 +32,28 @@ public static string GetExternalAddress(this V1Service service, ILogger logger =
{
var ingress = service.Status?.LoadBalancer?.Ingress?.FirstOrDefault();

if (ingress is null)
{
return string.Empty;
}
if (ingress is null) return string.Empty;

logger?.LogInformation("Available ExternalIP: {externalIp}, ExternalName: {externalName}, IngressIP: {ingressIP}, " +
"IngressName: {ingressName}",
logger?.LogInformation(
"Available ExternalIP: {externalIp}, ExternalName: {externalName}, IngressIP: {ingressIP}, " +
"IngressName: {ingressName}",
service.Spec.ExternalIPs?.FirstOrDefault(), service.Spec?.ExternalName, ingress.Ip, ingress.Hostname);

return ingress.Hostname ?? ingress.Ip;
}

/// <summary>
/// Returns the selector definition for the NetApp
/// Returns the selector definition for the NetApp
/// </summary>
/// <param name="serviceId"></param>
/// <returns></returns>
public static string GetNetAppLabelSelector(Guid serviceId)
{
return $"{NetAppIdSelector}={serviceId}";
}

public static IReadOnlyList<string> GetDeploymentNames(this V1DeploymentList deployments)
{
return deployments.Items.Select(d => d.Metadata.Name).OrderBy(d => d).ToList();
}
}
25 changes: 18 additions & 7 deletions src/Common/ExtensionMethods/StringExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,23 @@
public static class StringExtensions
{
/// <summary>
/// Sanitizes the yaml string saved in Redis to the proper yaml format
/// Removes the not allowed characters so the name matches the correct k8s name format
/// </summary>
/// <param name="s"></param>
/// <returns></returns>
// ReSharper disable once InconsistentNaming
public static string SanitizeAsK8sObjectName(this string s)
{
return s.Replace(" ", "-")
.Replace('_', '-')
.Replace(':', '-')
.Replace('.', '-')
.Replace('/', '-')
.ToLower().Trim();
}

/// <summary>
/// Sanitizes the yaml string saved in Redis to the proper yaml format
/// </summary>
/// <param name="str"></param>
/// <returns></returns>
Expand All @@ -14,11 +30,6 @@ public static string SanitizeAsK8SYaml(this string str)

public static string TrimSuffix(this string s, string suffix)
{
if (s.EndsWith(suffix))
{
return s.Substring(0, s.Length - suffix.Length);
}

return s;
return !s.EndsWith(suffix) ? s : s.Substring(0, s.Length - suffix.Length);
}
}
47 changes: 28 additions & 19 deletions src/Models/Domain/ActionPlanModel.cs
Original file line number Diff line number Diff line change
@@ -1,45 +1,53 @@
using Middleware.Models.Dto;
using System.Text.Json.Serialization;

namespace Middleware.Models.Domain;

public sealed class ActionPlanModel : BaseModel
{
[JsonPropertyName("Id")] //atuomatically generated plan id by middleware
/// <summary>
/// Automatically generated plan id by middleware
/// </summary>
public override Guid Id { get; set; }

[JsonPropertyName("TaskId")] //TaskID
/// <summary>
/// Identifier of the Task that is executed
/// </summary>
public Guid TaskId { get; set; }

[JsonPropertyName("Name")] // Name of task
public override string Name { get; set; }
/// <summary>
/// Name of the executed task
/// </summary>
public override string Name { get; set; } = null!;

/// <summary>
/// Status of the whole plan
/// Status of the whole plan
/// </summary>
[JsonPropertyName("Status")]
public string Status { get; set; }

[JsonPropertyName("IsReplan")] //Status of whole plan
public string? Status { get; set; }

/// <summary>
/// Status of whole plan
/// </summary>
public bool IsReplan { get; set; }

[JsonPropertyName("LastStatusChange")]
public DateTime LastStatusChange { get; set; } // AL 2022-05-10: Not sure we need this one or how to use it.
/// <summary>
/// Last time the status of the Task has been changed
/// AL 2022-05-10: Not sure we need this one or how to use it.
/// </summary>
public DateTime LastStatusChange { get; set; }

public List<ActionModel>? ActionSequence { get; set; }

[JsonPropertyName("ActionSequence")]
public List<ActionModel> ActionSequence { get; set; }

[JsonPropertyName("RobotId")]
public Guid RobotId { get; set; }

[JsonPropertyName("TaskStartedAt")]
public DateTime TaskStartedAt { get; set; }

public ActionPlanModel()
{
}

public ActionPlanModel(Guid id, Guid taskId, string name, List<ActionModel> actionSequence, Guid robotId)
public ActionPlanModel(Guid id, Guid taskId, string name, List<ActionModel>? actionSequence, Guid robotId)
{
Id = id;
TaskId = taskId;
Expand All @@ -60,7 +68,7 @@ public void SetStatus(string status)
public override Dto.Dto ToDto()
{
var domain = this;
return new ActionPlanDto()
return new ActionPlanDto
{
Id = domain.Id.ToString(),
TaskId = domain.TaskId.ToString(),
Expand All @@ -70,7 +78,8 @@ public override Dto.Dto ToDto()
LastStatusChange = domain.LastStatusChange == default ? DateTimeOffset.Now : domain.LastStatusChange,
ActionSequence = domain.ActionSequence,
RobotId = domain.RobotId.ToString(),
TaskStartedAt = domain.TaskStartedAt == default ? DateTimeOffset.Now : domain.TaskStartedAt,
}; ;
TaskStartedAt = domain.TaskStartedAt == default ? DateTimeOffset.Now : domain.TaskStartedAt
};
;
}
}
Loading

0 comments on commit 73739fc

Please sign in to comment.