Class/UpdateContainersContainerNetworkDefinition.Class.psm1

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

## Custom Modules.
using module '..\Enum\UpdateContainersIpAddressMethod.Enum.psm1';
using module '.\UpdateContainersContainerDefinition.Class.psm1';
using module '.\UpdateContainersContainerNetworkRouteDefinition.Class.psm1';

## Container Network Definition Class.
class UpdateContainersContainerNetworkDefinition
{
    <#
    .NAME
        UpdateContainersContainerNetworkDefinition

    .SYNOPSIS
        Class defining a network configuration for container engines.

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

    .PROPERTIES
        DefaultRoute
            A boolean indicating whether this network should be the default route for the container.

        IPv4Method
            The method for assigning an IPv4 address (Auto or Manual).

        IPv4Node
            The last octet of the IPv4 address to assign if using Manual method.

        IPv6Method
            The method for assigning an IPv6 address (Auto or Manual).

        IPv6Node
            The last segment of the IPv6 address to assign if using Manual method.

        Name
            The name of the network to connect the container to.
    #>


    ## IPv4 Property.
    [AllowNull()]
    [JsonPropertyName('method')]
    [UpdateContainersIpAddressMethodEnum] $IPv4Method = [UpdateContainersIpAddressMethodEnum]::Auto;

    ## IPv4 Node Property.
    [AllowNull()]
    [JsonPropertyName('node')]
    [Range(2, 254)]
    [Int32] $IPv4Node = $null;

    ## IPv6 Property.
    [JsonPropertyName('method6')]
    [UpdateContainersIpAddressMethodEnum] $IPv6Method = [UpdateContainersIpAddressMethodEnum]::Auto;

    ## IPv6 Node Property.
    [AllowNull()]
    [JsonPropertyName('node6')]
    [Range(2, 254)]
    [Int32] $IPv6Node = $null;

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

    ## Routes Property.
    [AllowNull()]
    [JsonPropertyName('route')]
    [List[UpdateContainersContainerNetworkRouteDefinition]] $Routes = [List[UpdateContainersContainerNetworkRouteDefinition]]::new();

    ## To Command Line Method.
    [String] ToCommandLine([String] $engine, [Boolean] $link, [UpdateContainersContainerDefinition] $container, [Boolean] $dryRun)
    {
        <#
        .NAME
            ToCommandLine

        .SYNOPSIS
            Generates the command line string to connect the container to the network.

        .DESCRIPTION
            This method constructs the appropriate command line string to connect
            a container to a specified network using the given container engine.
            It also handles IP address assignments and DNS server configurations.

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

                Required? true

            link
                A boolean indicating whether this is a network link operation.

                Required? true

            container
                The container definition to which the network will be connected.

                Required? true

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

                Required? true

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


        ## Reconcile the IPv4 addressing method based on the inputs.
        $this.IPv4Method = ($this.IPv4Method -eq [UpdateContainersIpAddressMethodEnum]::Manual -and $null -ne $this.IPv4Node `
                -and $this.IPv4Node -ge 2 -and $this.IPv4Node -le 254 ? `
                    [UpdateContainersIpAddressMethodEnum]::Manual : [UpdateContainersIpAddressMethodEnum]::Auto);

        ## Reconcile the IPv4 Node based on the IPv6 Node if necessary.
        $this.IPv4Node = ($this.IPv4Method -eq [UpdateContainersIpAddressMethodEnum]::Manual -and ($this.IPv4Node -lt 2 `
                -or $this.IPv4Node -gt 254) ? $null : $this.IPv4Node);

        ## Reconcile the IPv6 addressing method based on the inputs.
        $this.IPv6Method = ($this.IPv6Method -eq [UpdateContainersIpAddressMethodEnum]::Manual -and (($null -ne $this.IPv6Node `
            -and $this.IPv6Node -ge 2 -and $this.IPv6Node -le 254) -or ($null -ne $this.IPv4Node `
            -and $this.IPv4Node -ge 2 -and $this.IPv4Node -le 254)) ? `
                [UpdateContainersIpAddressMethodEnum]::Manual : [UpdateContainersIpAddressMethodEnum]::Auto);

        ## Reconcile the IPv6 Node based on the IPv4 Node if necessary.
        $this.IPv6Node = ($this.IPv6Method -eq [UpdateContainersIpAddressMethodEnum]::Manual -and ($null -eq $this.IPv6Node `
            -or $this.IPv6Node -le 2 -or $this.IPv6Node -ge 254) -and ($this.IPv4Method `
            -eq [UpdateContainersIpAddressMethodEnum]::Manual -and $null -ne $this.IPv4Node -and $this.IPv4Node -ge 2 `
            -and $this.IPv4Node -le 254) ? $this.IPv4Node : $this.IPv6Node);

        ## Localize the existence of the network.
        [Int32] $exists = (& "$($engine.ToString().ToLower())" network ls `
            --filter "Name=$($this.Name.Trim())" `
            --format "{{.Name}}" | Where-Object { $_ -eq "$($this.Name.Trim())" }).Length;

        ## Check if the network exists.
        if (0 -eq $exists -and -not $dryRun)
        {
            ## We don't have a valid network for this container.
            throw "Network [$($this.Name.Trim() )] Not Found";
        }

        ## Localize the gateway addresses for the network.
        [String[]] $gateways = (& "$($engine.ToString().ToLower())" network inspect "$($this.Name.Trim())" `
            --format "{{(index .IPAM.Config 0).Gateway}},{{(index .IPAM.Config 1).Gateway}}" `
            | Where-Object { $null -ne $_ -and "" -ne $_.Trim() -and $_ -ne "," }).Split(",") `
            | Where-Object { $null -ne $_ -and "" -ne $_.Trim() } `
            | ForEach-Object { $_.Trim() } `
            | Select-Object -Unique;

        ## Define our assignments list.
        [List[String]] $assignments = [List[String]]::new();

        ## Iterate over the gateways.
        foreach ($gateway in $gateways)
        {
            ## Check for an IPv4 gateway.
            if ($gateway.Contains(".") -and $this.IPv4Method -eq [UpdateContainersIpAddressMethodEnum]::Manual -and $null -ne $this.IPv4Node)
            {
                ## Add the IPv4 assignment to the list.
                $assignments.Add("--ip '$($gateway.Substring(0, $gateway.LastIndexOf(".")).Trim()).$($this.IPv4Node)'");
            }

            ## Check for an IPv6 gateway.
            if ($gateway.Contains("::") -and $this.IPv6Method -eq [UpdateContainersIpAddressMethodEnum]::Manual -and $null -ne $this.IPv6Node)
            {
                ## Add the IPv6 assignment to the list.
                $assignments.Add("--ip6 '$($gateway.Substring(0, $gateway.LastIndexOf("::")).Trim())::$($this.IPv6Node)'");
            }
        }

        ## Check for a container assignment.
        if ($false -eq $link)
        {
            ## Define our command line string.
            [String] $commandLineString = "--network '$($this.Name.Trim())'";

            ## Check for any assignments.
            if ($assignments.Count -gt 0)
            {
                ## Localize the assignments.
                [String] $assignmentsString = ($assignments -Join " ```n`t");

                ## Append the assignments to the command line string.
                $commandLineString = "${commandLineString} ```n`t${assignmentsString}";
            }

            ## We're done, generate and return our container network definition.
            return $commandLineString.Trim();
        }

        ## Localize the command line string.
        [String] $commandLine = "network connect";

        ## Check for a container name.
        if ($null -ne $container)
        {
            ## Iterate over the aliases if there are any.
            foreach ($alias in $container.Aliases)
            {
                ## Append the alias to the command line string.
                $commandLine = "${commandLine} ```n`t--alias '$($alias.Trim())'";
            }
        }

        ## Check for a default route.
        if ($this.DefaultRoute)
        {
            ## Append the default route to the command line string.
            $commandLine = "${commandLine} ```n`t--gw-priority 1";
        }

        ## Check for any assignments.
        if ($assignments.Count -ge 0)
        {
            ## Append the assignments to the command line string.
            $commandLine = "${commandLine} ```n`t$($assignments -Join " ```n`t")";
        }

        ## We're done, generate and return our container network linking definition.
        return "${commandLine} ```n`t'$($this.Name.Trim())' '$($container.Name.Trim())'";
    }
};