Class/UpdateContainersContainerDefinition.Class.psm1

## Namespaces.
using namespace System.Collections.Generic;
using namespace System.Text.Json;
using namespace System.Text.Json.Serialization;

## Custom Modules.
using module '..\Enum\UpdateContainersEngine.Enum.psm1';
using module '.\UpdateContainersContainerDeviceDefinition.Class.psm1';
using module '.\UpdateContainersContainerHealthCheckDefinition.Class.psm1';
using module '.\UpdateContainersContainerMountDefinition.Class.psm1';
using module '.\UpdateContainersContainerNetworkDefinition.Class.psm1';
using module '.\UpdateContainersContainerPublishDefinition.Class.psm1';
using module '.\UpdateContainersReplacementDefinition.Class.psm1';

## Container Definition Class.
class UpdateContainersContainerDefinition
{
    <#
    .NAME
        UpdateContainersContainerDefinition

    .SYNOPSIS
        Class defining a container configuration for container engines.

    .DESCRIPTION
        This class represents the configuration details for a container that can be
        created and managed by container engines like Docker and Podman.

    .PROPERTIES
        Aliases
            A list of aliases for the container within its networks.

        Capabilities
            A list of Linux capabilities to add to the container.

        Command
            The container engine command to use (Docker or Podman).

        Devices
            A list of device mappings for the container.

        DnsServers
            A list of DNS server IP addresses to use for the container.

        Environment
            A dictionary of environment variables to set in the container.

        HealthCheck
            The health check configuration for the container.

        Hostname
            The hostname to assign to the container.

        Image
            The container image to use.

        MacAddress
            The MAC address to assign to the container's network interface.

        Memory
            The memory limit for the container (e.g., "512m" for 512 megabytes).

        Mounts
            A list of mount configurations for the container.

        Name
            The name of the container.

        Networks
            A list of network configurations for the container.

        Privileged
            A boolean indicating whether the container should run in privileged mode.

        PublishedPorts
            A list of port publishing configurations for the container.

        RestartPolicy
            The restart policy for the container (e.g., "no", "on-failure", "always", "unless-stopped").

        Runtime
            The runtime to use for the container (e.g., "runc", "nvidia").
    #>


    ## Aliases Property.
    [JsonPropertyName('alias')]
    [List[String]] $Aliases = [List[String]]::new();

    ## Capabilities Property.
    [JsonPropertyName('capability')]
    [List[String]] $Capabilities = [List[String]]::new();

    ## Command Property.
    [AllowNull()]
    [JsonPropertyName('command')]
    [Nullable[UpdateContainersEngineEnum]] $Command = $null;

    ## Devices Property.
    [JsonPropertyName('device')]
    [List[UpdateContainersContainerDeviceDefinition]] $Devices = [List[UpdateContainersContainerDeviceDefinition]]::new();

    ## DNS Servers Property.
    [JsonPropertyName('dns')]
    [List[String]] $DnsServers = [List[String]]::new();

    ## Environment Property.
    [JsonPropertyName('environment')]
    [Dictionary[String, String]] $Environment = [Dictionary[String, String]]::new();

    ## Health Check Property.
    [JsonPropertyName('healthCheck')]
    [UpdateContainersContainerHealthCheckDefinition] $HealthCheck = $null;

    ## Hostname Property.
    [JsonPropertyName('hostname')]
    [String] $Hostname = $null;

    ## Image Property.
    [JsonPropertyName('image')]
    [JsonRequired()]
    [String] $Image;

    ## MacAddress Property.
    [AllowNull()]
    [JsonPropertyName('mac')]
    [String] $MacAddress = $null;

    ## Memory Property.
    [JsonPropertyName('memory')]
    [String] $Memory = $null;

    ## Mounts Property.
    [JsonPropertyName('mount')]
    [List[UpdateContainersContainerMountDefinition]] $Mounts = [List[UpdateContainersContainerMountDefinition]]::new();

    ## Name Property.
    [JsonPropertyName('name')]
    [JsonRequired()]
    [String] $Name;

    ## Networks Property.
    [JsonPropertyName('network')]
    [List[UpdateContainersContainerNetworkDefinition]] $Networks = [List[UpdateContainersContainerNetworkDefinition]]::new();

    ## Privileged Property.
    [JsonPropertyName('privileged')]
    [Boolean] $Privileged = $false;

    ## Published Ports Property.
    [JsonPropertyName('publish')]
    [List[UpdateContainersContainerPublishDefinition]] $PublishedPorts = [List[UpdateContainersContainerPublishDefinition]]::new();

    ## Restart Policy Property.
    [JsonPropertyName('restart')]
    [String] $RestartPolicy = "unless-stopped";

    ## Runtime Property.
    [JsonPropertyName('runtime')]
    [String] $Runtime = $null;

    ## ToCommandLine Method.
    [String] ToCommandLine([UpdateContainersEngineEnum] $engine, [List[String]] $dns = [List[String]]::new(), [List[UpdateContainersReplacementDefinition]] $replacements = [List[UpdateContainersReplacementDefinition]]::new(), [Boolean] $dryRun = $false)
    {
        <#
        .NAME
            ToCommandLine

        .SYNOPSIS
            Generates the command line string to create the container.

        .DESCRIPTION
            This method constructs the appropriate command line string to create
            a container using the specified container engine, based on the properties
            defined in the ContainerDefinition class. It also applies any provided
            DNS servers and path replacements.

        .PARAMETERS
            engine
                The container engine to use (Docker or Podman).

                Required? true

            dns
                A list of DNS server IP addresses to use for the container.

                Required? false
                Default @()

            replacements
                A list of DefinitionReplacement objects to apply to paths in the
                container definition.

                Required? false
                Default @()

            dryRun
                A boolean indicating whether this is a dry run (no actual execution).

                Required? false
                Default $false

        .OUTPUTS
            String
                The command line string to create the container.
        #>


        ## Reconcile the engine command between the definition and the container.
        $engine = ($null -ne $this.Command -and "" -ne $this.Command.Trim() ? $this.Command.ToString() : $engine.ToString()).ToLower().Trim();

        ## Define our command line string.
        [String] $commandLine = "container create";

        ## Iterate over the capabilities if there are any.
        foreach ($capability in $this.Capabilities)
        {
            ## Append the capability to the command string.
            $commandLine = "${commandLine} ```n`t--cap-add '$($capability.Trim())'";
        }

        ## Iterate over the devices if there are any.
        foreach ($device in $this.Devices)
        {
            ## Append the device to the command string.
            $commandLine = "${commandLine} ```n`t$($device.ToCommandLine())";
        }

        ## Iterate over the provided DNS servers if there are any.
        foreach ($server in $this.DnsServers)
        {
            ## Append the DNS server to the command string.
            $commandLine = "${commandLine} ```n`t--dns '$($server.Trim())'";
        }

        ## Iterate over the provided DNS servers if there are any.
        foreach ($server in $dns)
        {
            ## Append the DNS server to the command string.
            $commandLine = "${commandLine} ```n`t--dns '$($server.Trim())'";
        }

        ## Iterate over the environment variables if there are any.
        foreach ($key in $this.Environment.Keys)
        {
            ## Localize the value.
            $value = $this.Environment[$key].Trim();

            ## Iterate over the replacements and replace them in the value.
            foreach ($replacement in $replacements) { $value = $replacement.Replace($value); }

            ## Update the command line string.
            $commandLine = "${commandLine} ```n`t--env '$($key.Trim())=$($value.Trim())'";
        }

        ## Check for a health check on the container.
        if ($null -ne $this.HealthCheck)
        {
            ## Append the health check to the command line string.
            $commandLine = "${commandLine} ```n`t$($this.HealthCheck.ToCommandLine())";
        }

        ## Check for a provided hostname in the container definition.
        if ($null -eq $this.Hostname -or "" -eq $this.Hostname.Trim())
        {
            ## Reset the hostname in the container definition to the container name.
            $this.Hostname = $this.Name.Trim();
        }

        ## Add the hostname to the command string.
        $commandLine = "${commandLine} ```n`t--hostname '$($this.Hostname.Trim())'";

        ## Check for a provided MAC address in the container definition.
        if ($null -ne $this.MacAddress -and "" -ne $this.MacAddress.Trim())
        {
            ## Append the MAC address to the command string.
            $commandLine = "${commandLine} ```n`t--mac-address '$($this.MacAddress.Trim())'";
        }

        ## Check for a memory limit on the container.
        if ($null -ne $this.Memory -and "" -ne $this.Memory.Trim())
        {
            ## Append the memory limit to the command string.
            $commandLine = "${commandLine} ```n`t--memory '$($this.Memory.Trim())'";
        }

        ## Iterate over the mounts if there are any.
        foreach ($mount in $this.Mounts)
        {
            ## Append the mount to the command string.
            $commandLine = "${commandLine} ```n`t$($mount.ToCommandLine($replacements))";
        }

        ## Add the name to the command string.
        $commandLine = "${commandLine} ```n`t--name '$($this.Name.Trim())'";

        ## Localize the first network as the primary network.
        [UpdateContainersContainerNetworkDefinition] $network = $this.Networks | Select-Object -First 1;

        ## Check for a primary network.
        if ($null -ne $network)
        {
            ## Append the network to the command string.
            $commandLine = "${commandLine} ```n`t$($network.ToCommandLine($engine, $false, $this, $dryRun))";
        }

        ## Iterate over the aliases if there are any.
        foreach ($alias in $this.Aliases)
        {
            ## Append the alias to the command string.
            $commandLine = "${commandLine} ```n`t--network-alias '$($alias.Trim())'";
        }

        ## Check for a privileged container.
        if ($this.Privileged)
        {
            ## Append the privileged flag to the command string.
            $commandLine = "${commandLine} ```n`t--privileged";
        }

        ## Iterate over the published ports if there are any.
        foreach ($port in $this.PublishedPorts)
        {
            ## Append the published port to the command string.
            $commandLine = "${commandLine} ```n`t$($port.ToCommandLine())";
        }

        ## Add the restart-policy to the command string.
        if ($null -ne $this.RestartPolicy -and "" -ne $this.RestartPolicy.Trim())
        {
            ## Append the restart policy to the command string.
            $commandLine = "${commandLine} ```n`t--restart '$($this.RestartPolicy.Trim())'";
        }
        else
        {
            ## Default the restart policy to unless-stopped in the command string.
            $commandLine = "${commandLine} ```n`t--restart 'unless-stopped'";
        }

        ## Check for a provided runtime.
        if ($null -ne $this.Runtime -and "" -ne $this.Runtime.Trim())
        {
            ## Append the runtime to the command string.
            $commandLine = "${commandLine} ```n`t--runtime '$($this.Runtime.Trim())'";
        }

        ## We're done, return the command.
        return "${commandLine} ```n`t'$($this.Image.Trim())'";
    }
};