UpdateContainersWithDefinitionFile.psm1
## Namespaces. using namespace System; using namespace System.Collections.Generic; using namespace System.IO; using namespace System.Text.Json; using namespace System.Text.Json.Serialization; using namespace System.ComponentModel.DataAnnotations; ## Module Functions using module '.\Function\Get-ContainerDefinitionFilePath.Function.psm1'; using module '.\Function\Get-ContainerDefinitionJsonSerializerOptions.Function.psm1'; using module '.\Function\Update-ContainersWithDefinitionFile.Function.psm1'; ## Engine Enum. enum UpdateContainersEngineEnum { <# .NAME UpdateContainersEngineEnum .SYNOPSIS Enum defining supported container engines. .DESCRIPTION This enum specifies the container engines that the script can manage, allowing for differentiation between Docker and Podman commands. #> ## Docker Command Enum Value. Docker ## Podman Command Enum Value. Podman }; ## IP Address Method Enum. enum UpdateContainersIpAddressMethodEnum { <# .NAME UpdateContainersIpAddressingMethodEnum .SYNOPSIS Enum defining IP addressing methods for container networks. .DESCRIPTION This enum specifies the methods available for assigning IP addresses to containers within a network, either automatically or manually. #> ## Auto Addressing Enum Value. Auto ## Manual Addressing Enum Value. Manual }; ## Container Definition Class. [NoRunspaceAffinity()] 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())'"; } }; ## Container Device Definition Class. [NoRunspaceAffinity()] class UpdateContainersContainerDeviceDefinition { <# .NAME UpdateContainersContainerDeviceDefinition .SYNOPSIS Class defining a device mapping for container engines. .DESCRIPTION This class represents a device mapping that can be applied to containers managed by container engines like Docker and Podman. .PROPERTIES Destination The path inside the container where the device will be mapped. Source The path on the host system of the device to be mapped. #> ## Destination Property. [AllowNull()] [JsonPropertyName('destination')] [String] $Destination = $null; ## Source Property. [JsonPropertyName('source')] [JsonRequired()] [String] $Source; ## ToCommandLine Method. [String] ToCommandLine() { <# .NAME ToCommandLine .SYNOPSIS Generates the command line string for the device mapping. .DESCRIPTION This method constructs the appropriate command line string to map a device into a container using the specified source and destination paths defined in the ContainerDeviceDefinition class. .OUTPUTS String The command line string for the device mapping. #> ## Define our command line string. [String] $command = "--device '$($this.Source.Trim())"; ## Check for a provided source. if ($null -ne $this.Destination -and "" -ne $this.Destination.Trim()) { ## Append the source to the command line string. $command = "${command}:$($this.Source.Trim())"; } ## We're done, return the command line string. return "${command}'".Trim(); } }; ## Container Health Check Definition Class. [NoRunspaceAffinity()] class UpdateContainersContainerHealthCheckDefinition { <# .NAME UpdateContainersContainerHealthCheckDefinition .SYNOPSIS Class defining a health check configuration for container engines. .DESCRIPTION This class represents the configuration details for a health check that can be applied to containers managed by container engines like Docker and Podman. .PROPERTIES Command The command to run to check the health of the container. Interval The time interval between health checks (e.g., "30s" for 30 seconds). Retries The number of consecutive failures required before marking the container as unhealthy. StartPeriod The initial delay before starting health checks after the container starts (e.g., "3s" for 3 seconds). Timeout The maximum time to wait for the health check command to complete (e.g., "30s" for 30 seconds). #> ## Command Property. [JsonPropertyName('command')] [JsonRequired()] [String] $Command; ## Interval Property. [AllowNull()] [JsonPropertyName('interval')] [String] $Interval = "30s"; ## Retries Property. [AllowNull()] [JsonPropertyName('retries')] [Range(1, 10)] [Int32] $Retries = 3; ## Start Period Property. [AllowNull()] [JsonPropertyName('start')] [String] $StartPeriod = "3s"; ## Timeout Property. [AllowNull()] [JsonPropertyName('timeout')] [String] $Timeout = "30s"; ## ToCommandLine Method. [String] ToCommandLine() { <# .NAME ToCommandLine .SYNOPSIS Generates the command line string for the health check configuration. .DESCRIPTION This method constructs the appropriate command line string to define a health check for a container using the properties defined in the ContainerHealthCheckDefinition class. .OUTPUTS String The command line string for the health check configuration. #> ## Define our command line string. [String] $commandLine = "--health-cmd='$($this.Command.Trim())'"; ## Check for a provided interval. if ($null -ne $this.Interval -and "" -ne $this.Interval.Trim()) { ## Append the interval to the command line string. $commandLine = "${commandLine} ```n`t--health-interval='$($this.Interval.Trim())'"; } ## Check for a provided retries. if ($null -ne $this.Retries) { ## Append the retries to the command line string. $commandLine = "${commandLine} ```n`t--health-retries=$($this.Retries)"; } ## Check for a provided start period. if ($null -ne $this.StartPeriod -and "" -ne $this.StartPeriod.Trim()) { ## Append the start period to the command line string. $commandLine = "${commandLine} ```n`t--health-start-period='$($this.StartPeriod.Trim())'"; } ## Check for a provided timeout. if ($null -ne $this.Timeout -and "" -ne $this.Timeout.Trim()) { ## Append the timeout to the command line string. $commandLine = "${commandLine} ```n`t--health-timeout='$($this.Timeout.Trim())'"; } ## We're done, return the command line string. return $commandLine.Trim(); } }; ## Container Mount Definition Class. [NoRunspaceAffinity()] class UpdateContainersContainerMountDefinition { <# .NAME UpdateContainersContainerMountDefinition .SYNOPSIS Class defining a mount configuration for container engines. .DESCRIPTION This class represents the configuration details for a mount that can be applied to containers managed by container engines like Docker and Podman. .PROPERTIES Destination The path inside the container where the mount will be located. Source The path on the host system to be mounted into the container. ReadOnly A boolean indicating whether the mount should be read-only. Type The type of mount (e.g., bind, volume, tmpfs). #> ## Destination Property. [JsonPropertyName('destination')] [String] $Destination = $null; ## Source Property. [JsonPropertyName('source')] [JsonRequired()] [String] $Source; ## Read-Only Property. [JsonPropertyName('ro')] [Boolean] $ReadOnly = $false; ## Type Property. [JsonPropertyName('type')] [String] $Type = "bind"; ## ToCommandLine Method. [String] ToCommandLine([List[UpdateContainersReplacementDefinition]] $replacements = [List[UpdateContainersReplacementDefinition]]::new()) { <# .NAME ToCommandLine .SYNOPSIS Generates the command line string for the mount configuration. .DESCRIPTION This method constructs the appropriate command line string to define a mount for a container using the properties defined in the ContainerMountDefinition class. It also applies any provided path replacements to the source and destination paths. .PARAMETERS replacements A list of DefinitionReplacement objects to apply to the source and destination paths. Required? false Default @() .OUTPUTS String The command line string for the mount configuration. #> ## Ensure we have a destination. if ($null -eq $this.Destination -or "" -eq $this.Destination.Trim()) { ## Reset the destination to the source path. $this.Destination = $this.Source.Trim(); } ## Iterate over the replacements. foreach ($replacement in $replacements) { ## Make the replacement in the destination path. $this.Destination = $replacement.Replace($this.Destination); ## Make the replacement in the source path. $this.Source = $replacement.Replace($this.Source); } ## Define our command line string. [String] $command = "--mount 'destination=$($this.Destination.Trim())"; ## Check the read-only flag. if ($this.ReadOnly) { ## Append the read-only flag to the command line string. $command = "${command},ro=true"; } ## Add the source to the command line string. $command = "${command},source=$($this.Source.Trim())"; ## Check for a provided type. if ($null -ne $this.Type -and "" -ne $this.Type.Trim()) { ## Append the type to the command line string. $command = "${command},type=$($this.Type.ToLower().Trim())"; } else { ## Default the type to bind in the command line string. $command = "${command},type=bind"; } ## We're done, return the command line string. return "${command}'".Trim(); } }; ## Container Network Definition Class. [NoRunspaceAffinity()] 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 our command results. $commandResults = (& "$($engine.ToString().ToLower())" network ls ` --filter "Name=$($this.Name.Trim())" ` --format "{{.Name}}"); ## Localize the existence of the network. [Int32] $exists = if ($null -ne $commandResults -and "" -ne $commandResults.Trim()) { ($commandResults.Trim() | Where-Object { $_.Trim() -eq $this.Name.Trim() }).Length } else { 0 }; ## 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 our command results. $commandResults = (& "$($engine.ToString().ToLower())" network inspect "$($this.Name.Trim())" ` --format "{{(index .IPAM.Config 0).Gateway}},{{(index .IPAM.Config 1).Gateway}}"); ## Localize the gateway addresses for the network. [String[]] $gateways = if ($null -ne $commandResults -and "" -ne $commandResults.Trim()) { ($commandResults.Trim() | Where-Object { $null -ne $_ -and "" -ne $_.Trim() -and $_ -ne "," }).Split(",") ` | Where-Object { $null -ne $_ -and "" -ne $_.Trim() } ` | ForEach-Object { $_.Trim() } ` | Select-Object -Unique } else { @() }; ## 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())'"; } }; ## Container Network Route Definition Class. [NoRunspaceAffinity()] class UpdateContainersContainerNetworkRouteDefinition { <# .NAME UpdateContainersContainerNetworkRouteDefinition .SYNOPSIS Class defining a network route configuration for container engines. .DESCRIPTION This class represents the configuration details for a network route that can be applied to containers managed by container engines like Docker and Podman. .PROPERTIES Destination The destination CIDR for the route (e.g., "192.168.3.0/24"). Gateway The gateway IP address for the route (e.g., "192.168.3.1"). #> ## Destination Property. [JsonPropertyName('destination')] [JsonRequired()] [String] $Destination; ## Gateway Property. [JsonPropertyName('gateway')] [JsonRequired()] [String] $Gateway; ## ToCommandLine Method. [String] ToCommandLine([String] $interface) { <# .NAME ToCommandLine .SYNOPSIS Generates the command line string for the network route configuration. .DESCRIPTION This method constructs the appropriate command line string to define a network route for a container using the properties defined in the ContainerNetworkRouteDefinition class. .PARAMETERS interface The network interface to which the route applies. Required? true .OUTPUTS String The command line string for the network route configuration. #> ## Define and return our command line string. return "ip route add '$($this.Destination.Trim())' via '$($this.Gateway.Trim())' dev '$($interface.Trim())'"; } }; ## Container Publish Definition Class. [NoRunspaceAffinity()] class UpdateContainersContainerPublishDefinition { <# .NAME UpdateContainersContainerPublishDefinition .SYNOPSIS Class defining a port publishing configuration for container engines. .DESCRIPTION This class represents the configuration details for publishing a port from a container to the host system, which can be applied to containers managed by container engines like Docker and Podman. .PROPERTIES Container The port inside the container to be published. Host The port on the host system to which the container port will be mapped. Protocol The protocol to use for the port mapping (e.g., tcp, udp). #> ## Container Property. [JsonPropertyName('container')] [JsonRequired()] [String] $Container; ## Host Property. [AllowNull()] [JsonPropertyName('host')] [String] $Host = $null; ## Protocol Property. [AllowNull()] [JsonPropertyName('protocol')] [String] $Protocol = $null; ## ToCommandLine Method. [String] ToCommandLine() { <# .NAME ToCommandLine .SYNOPSIS Generates the command line string for the port publishing configuration. .DESCRIPTION This method constructs the appropriate command line string to publish a port from a container to the host system using the properties defined in the ContainerPublishDefinition class. .OUTPUTS String The command line string for the port publishing configuration. #> ## Reconcile the host port. $this.Host = ($null -eq $this.Host -or "" -eq $this.Host.Trim() ? $this.Container : $this.Host.Trim()); ## Define our command line string. [String] $command = "--publish '$($this.Host.Trim()):$($this.Container.Trim())"; ## Check for a provided protocol. if ($null -ne $this.Protocol -and "" -ne $this.Protocol.Trim()) { ## Append the protocol to the command line string. $command = "${command}/$($this.Protocol.ToLower().Trim())"; } ## We're done, return the command line string. return "${command}'".Trim(); } }; ## Definition Class. [NoRunspaceAffinity()] class UpdateContainersDefinition { <# .NAME UpdateContainersDefinition .SYNOPSIS Class defining a container engine definition. .DESCRIPTION This class represents a container engine definition that includes configurations for containers, networks, and other related settings. It supports both Docker and Podman as container engines. .PROPERTIES AnyRemoved A boolean indicating whether any containers were removed during the operation. Command The container engine command to use (Docker or Podman). Containers A list of container definitions to be created and managed. DebugEnabled A boolean indicating whether debug mode is enabled. DnsServers A list of DNS server IP addresses to use for the containers. DryRunEnabled A boolean indicating whether dry-run mode is enabled. Networks A list of network definitions to be created and managed. NoStartContainers A boolean indicating whether to create containers without starting them. OnlyCreateContainers An array of container names to create; if empty, all containers are created. PurgeImagesEnabled A boolean indicating whether to purge unused images after operations. Replacements A list of definition replacements to apply to paths and strings. #> ## AnyRemoved Property. [JsonIgnore()] [Boolean] $AnyRemoved = $false; ## Command Property. [JsonPropertyName('command')] [JsonRequired()] [UpdateContainersEngineEnum] $Command; ## Containers Property. [JsonPropertyName('container')] [JsonRequired()] [List[UpdateContainersContainerDefinition]] $Containers = [List[UpdateContainersContainerDefinition]]::new(); ## Debug Property. [JsonPropertyName('debug')] [Boolean] $DebugEnabled = $false; ## DNS Servers Property. [JsonPropertyName('dns')] [List[String]] $DnsServers = [List[String]]::new(); ## Dry-Run Property. [JsonPropertyName('dryRun')] [Boolean] $DryRunEnabled = $false; ## Networks Property. [JsonPropertyName('network')] [List[UpdateContainersNetworkDefinition]] $Networks = [List[UpdateContainersNetworkDefinition]]::new(); ## No Start Containers Property. [JsonPropertyName('noStart')] [Boolean] $NoStartContainers = $false; ## Only Create Containers Property. [JsonPropertyName('only')] [String[]] $OnlyCreateContainers = @(); ## Purge Images Property. [JsonPropertyName('purgeImages')] [Boolean] $PurgeImagesEnabled = $false; ## Replacements Property [JsonPropertyName('replacement')] [List[UpdateContainersReplacementDefinition]] $Replacements = [List[UpdateContainersReplacementDefinition]]::new(); ## Create Containers Method. [Void] CreateContainers() { <# .NAME CreateContainers .SYNOPSIS Creates containers based on the definitions provided. .DESCRIPTION This method iterates over the container definitions and creates each container if it does not already exist. It uses the specified container engine command (Docker or Podman) to perform the creation. If dry-run mode is enabled, it will only simulate the creation without actually executing the commands. #> ## Iterate over the containers. foreach ($container in $this.Containers) { ## Reconcile the engine command between the definition and the container. [String] $engine = $($null -ne $container.Command -and "" -ne $container.Command.Trim() ? ` $container.Command.ToString() : $this.Command.ToString()).ToLower().Trim(); ## Execute our existence command results. $commandResults = (& "${engine}" ps -a --filter "Name=$($container.Name.Trim())" --format "{{.Names}}"); ## Localize the existence of the container. [Int32] $exists = if ($null -ne $commandResults -and "" -ne $commandResults.Trim()) { ($commandResults.Trim() | Where-Object { $_.Trim() -eq $container.Name.Trim() }).Length } else { 0 }; ## Check if the container exists. if (0 -eq $exists -or $true -eq $this.DryRunEnabled) { ## Create the container. Write-Host "Creating container '$($container.Name.Trim())'." -ForegroundColor Green; ## Create the container in the local registry. $this.ExecuteCommand("${engine} $($container.ToCommandLine($this.Command, $this.DnsServers, ` $this.Replacements, $this.DryRunEnabled))"); } } } ## Create Networks Method. [Void] CreateNetworks() { <# .NAME CreateNetworks .SYNOPSIS Creates networks based on the definitions provided. .DESCRIPTION This method iterates over the network definitions and creates each network if it does not already exist. It uses the specified container engine command (Docker or Podman) to perform the creation. If dry-run mode is enabled, it will only simulate the creation without actually executing the commands. #> ## Iterate over the distinct networks. foreach ($network in ($this.Networks | Group-Object -Property 'Name' | ForEach-Object { $_.Group | Select-Object -First 1 })) { ## Reconcile the engine command between the definition and the network. [String] $engine = $($null -ne $network.Command -and "" -ne $network.Command.Trim() ? ` $network.Command : $this.Command).ToString().ToLower().Trim(); ## Localize our command results. $commandResults = (& "${engine}" network ls --filter "Name=$($network.Name.Trim())" --format "{{.Name}}"); ## Localize the existence of the network. [Int32] $exists = if ($null -ne $commandResults -and "" -ne $commandResults.Trim()) { ($commandResults.Trim() | Where-Object { $_.Trim() -eq $network.Name.Trim() }).Length } else { 0 }; ## Check if the network exists. if (0 -eq $exists -or $true -eq $this.DryRunEnabled) { ## Create the network. Write-Host "Creating network '$($network.Name.Trim())'." -ForegroundColor Green; ## Create the network in the local registry. $this.ExecuteCommand("${engine} $($network.ToCommandLine())"); } } } ## Execute Command Method. [Void] ExecuteCommand([String] $command) { <# .NAME ExecuteCommand .SYNOPSIS Executes a command line string using Invoke-Expression. .DESCRIPTION This method takes a command line string as input and executes it using the Invoke-Expression cmdlet. It supports dry-run and debug modes, where the command is only displayed without execution. .PARAMETERS command The command line string to be executed. #> ## Check for a dry-run. if ($this.DebugEnabled -or $this.DryRunEnabled) { ## Output the command that would be executed. Write-Host "Invoke-Expression -Command `"$($command.Trim());`" | Out-Null;" -ForegroundColor Yellow; ## Write our buffer line. Write-Host "" -ForegroundColor Yellow; } ## Check to see if we should execute the command then execute it. if ($false -eq $this.DryRunEnabled) { Invoke-Expression -Command "${command};" | Out-Null; } } ## Link Containers to Routes Method. [Void] LinkContainersToRoutes() { <# .NAME LinkContainersToRoutes .SYNOPSIS Links containers to their defined networks based on the definitions provided. .DESCRIPTION This method iterates over the network definitions and links each container to its defined networks if it is not already linked. It uses the specified container engine command (Docker or Podman) to perform the linking. If dry-run mode is enabled, it will only simulate the linking without actually executing the commands. #> ## Iterate over the containers whose network(s) contain routes. foreach ($container in ($this.Containers | Where-Object { $null -ne $_.Networks -and $_.Networks.Count -gt 0 -and ($_.Networks | Where-Object { $null -ne $_.Routes -and $_.Routes.Count -gt 0 }).Count -gt 0 })) { ## Reconcile the engine command between the definition and the container. [String] $engine = ($null -ne $container.Command ? ` $container.Command : $this.Command).ToString().ToLower().Trim(); ## Localize our command results. $commandResults = (& "${engine}" ps -a --filter "Name=$($container.Name.Trim())" --format "{{.Names}}"); ## Check for the existence of the container. [Int32] $exists = if ($null -ne $commandResults -and "" -ne $commandResults.Trim()) { ($commandResults.Trim() | Where-Object { $_.Trim() -eq $container.Name.Trim() }).Length } else { 0 }; ## Ensure the container exists. if (0 -eq $exists) { ## Write the message to the host, letting them know we're skipping it. Write-Host "Container '$($container.Name.Trim())' does not exist, skipping route link(s)." -ForegroundColor Gray; ## We're done with this iteration. continue; } ## Define our interface iterator. [Int32] $interface = 0; ## Iterate over the networks for the container that contain routes. foreach ($network in ($container.Networks | Where-Object { $null -ne $_.Routes -and $_.Routes.Count -gt 0 })) { ## Localize our command results. $commandResults = (& "${engine}" network ls --filter "Name=$($network.Name.Trim())" --format "{{.Name}}"); ## Check for the existence of the network. $exists = if ($null -ne $commandResults -and "" -ne $commandResults.Trim()) { ($commandResults.Trim() | Where-Object { $_.Trim() -eq $network.Name.Trim() }).Length } else { 0 }; ## Ensure the network exists. if (0 -eq $exists) { ## Write the message to the host, letting them know we're skipping it. Write-Host "Network '$($network.Name.Trim())' does not exist, skipping route link(s) for container '$($container.Name.Trim())'." -ForegroundColor Gray; ## Increment the interface iterator. ++$interface; ## We're done with this iteration. if (-not $this.DebugEnabled) { continue; }; } ## Iterate over the routes for the network. foreach ($route in $network.Routes) { ## Write the message to the host, letting them know we're linking it. Write-Host -ForegroundColor Green ` "Routing container '$($container.Name.Trim())' to '$($route.Destination.Trim())' via '$($route.Gateway.Trim())' on 'eth$($interface)'."; ## Execute the command to link the container to the physical network if necessary. $this.ExecuteCommand("${engine} exec --privileged '$($container.Name.Trim())' ```n`t$($route.ToCommandLine("eth${interface}"))"); } } } } ## Link Containers to Physical Network Method. [Void] LinkContainersToSecondaryNetworks() { <# .NAME LinkContainersToSecondaryNetworks .SYNOPSIS Links containers to their secondary networks based on the definitions provided. .DESCRIPTION This method iterates over the container definitions and links each container to its secondary networks if it is not already linked. It uses the specified container engine command (Docker or Podman) to perform the linking. If dry-run mode is enabled, it will only simulate the linking without actually executing the commands. #> ## Iterate over the containers. foreach ($container in $this.Containers) { ## Reconcile the engine command between the definition and the container. [String] $engine = ($null -ne $container.Command ? ` $container.Command : $this.Command).ToString().ToLower().Trim(); ## Iterate over the networks for the container, skipping the first one as it's the primary network. foreach ($network in ($container.Networks | Select-Object -Skip 1)) { ## Localize our command results. $commandResults = (& "${engine}" ps -a --filter "Name=$($container.Name.Trim())" --format "{{.Names}}"); ## Check to see if the container is already linked to the network. [Int32] $exists = if ($null -ne $commandResults -and "" -ne $commandResults.Trim()) { ($commandResults.Trim() | Where-Object { $_.Trim() -eq $container.Name.Trim() }).Length } else { 0 }; ## Check if the container is already linked to the network. if (0 -ne $exists) { ## Write the message to the host, letting them know we're skipping it. Write-Host "Container '$($container.Name.Trim())' is already linked to network '$($network.Name.Trim())', skipping." -ForegroundColor Gray; ## We're done with this iteration. if ($false -eq $this.DryRunEnabled) { continue; }; } ## Write the message to the host, letting them know we're linking it. Write-Host "Linking container '$($container.Name.Trim())' to network '$($network.Name.Trim())'." -ForegroundColor Green; ## Generate and execute the command to link the container to the physical network if necessary. $this.ExecuteCommand("${engine} $($network.ToCommandLine($engine, $true, $container, $this.DryRunEnabled))"); } } } ## Prune Method. [Void] Prune() { <# .NAME Prune .SYNOPSIS Prunes unused containers, images, networks, and volumes. .DESCRIPTION This method prunes unused containers, images, networks, and volumes from the container engine's local registry. It uses the specified container engine command (Docker or Podman) to perform the pruning. If dry-run mode is enabled, it will only simulate the pruning without actually executing the commands. #> ## Write the message to the host, letting them know we're pruning containers. Write-Host "Pruning containers." -ForegroundColor Gray; ## Prune any stopped containers. $this.ExecuteCommand("$($this.Command.ToString().ToLower()) container prune -f"); ## Write the message to the host, letting them know we're pruning images. Write-Host "Pruning images." -ForegroundColor Gray; ## Prune any dangling images. $this.ExecuteCommand("$($this.Command.ToString().ToLower()) image prune -a -f"); ## Write the message to the host, letting them know we're pruning networks. Write-Host "Pruning networks." -ForegroundColor Gray; ## Prune any unused networks. $this.ExecuteCommand("$($this.Command.ToString().ToLower()) network prune -f"); ## Write the message to the host, letting them know we're pruning volumes. Write-Host "Pruning volumes." -ForegroundColor Gray; ## Prune any unused volumes. $this.ExecuteCommand("$($this.Command.ToString().ToLower()) volume prune -f"); } ## Pull Images Method. [Void] PullImages() { <# .NAME PullImages .SYNOPSIS Pulls the latest images for the defined containers. .DESCRIPTION This method iterates over the unique container images defined in the container definitions and pulls the latest version of each image from the remote registry. It uses the specified container engine command (Docker or Podman) to perform the pull operation. If dry-run mode is enabled, it will only simulate the pull without actually executing the commands. #> ## Iterate over the unique container images. foreach ($container in ($this.Containers | Select-Object -Property Image -Unique)) { ## Reconcile the engine command between the definition and the container. [String] $engine = $($null -ne $container.Command -and "" -ne $container.Command.Trim() ? ` $container.Command.ToString() : $this.Command.ToString()).ToLower().Trim(); ## Pull the image from the remote registry. Write-Host "Pulling image '$($container.Image.Trim())'." -ForegroundColor Cyan; ## Pull the latest image into the local registry. $this.ExecuteCommand("${engine} pull --quiet '$($container.Image.Trim())'"); } } ## Purge Containers Method. [Void] PurgeContainers() { <# .NAME PurgeContainers .SYNOPSIS Purges containers based on the definitions provided. .DESCRIPTION This method iterates over the container definitions and removes each container if it exists. It uses the specified container engine command (Docker or Podman) to perform the removal. If dry-run mode is enabled, it will only simulate the removal without actually executing the commands. #> ## Iterate over the containers. foreach ($container in $this.Containers) { ## Reconcile the engine command between the definition and the container. [String] $engine = $($null -ne $container.Command -and "" -ne $container.Command.Trim() ? ` $container.Command.ToString() : $this.Command.ToString()).ToLower().Trim(); ## Localize our command results. $commandResults = (& "${engine}" ps -a --filter "Name=$($container.Name.Trim())" --format "{{.Names}}"); ## Localize the existence of the container. [Int32] $exists = if ($null -ne $commandResults -and "" -ne $commandResults.Trim()) { ($commandResults.Trim() | Where-Object { $_.Trim() -eq $container.Name.Trim() }).Length } else { 0 }; ## Check if the container exists. if (0 -ne $exists -or $true -eq $this.DryRunEnabled) { ## Stop the container. Write-Host "Stopping container '$($container.Name.Trim())'." -ForegroundColor Gray; ## Stop the container if it's running. $this.ExecuteCommand("${engine} container stop '$($container.Name.Trim())'"); ## Remove the container. Write-Host "Removing container '$($container.Name.Trim())'." -ForegroundColor Gray; ## Remove the container from the local registry. $this.ExecuteCommand("${engine} container rm -f '$($container.Name.Trim())'"); ## Reset the AnyRemoved flag. $this.AnyRemoved = $true; } } } ## Purge Images Method. [Void] PurgeImages() { <# .NAME PurgeImages .SYNOPSIS Purges images based on the definitions provided. .DESCRIPTION This method iterates over the unique container images defined in the container definitions and removes each image if it exists. It uses the specified container engine command (Docker or Podman) to perform the removal. #> ## Iterate over the distinct container images. foreach ($container in ($this.Containers | Select-Object -Property Image -Unique)) { ## Reconcile the engine command between the definition and the container. [String] $engine = $($null -ne $container.Command -and "" -ne $container.Command.Trim() ? ` $container.Command.ToString() : $this.Command.ToString()).ToLower().Trim(); ## Localize our command results. $commandResults = (& "${engine}" image ls --filter "Reference=$($container.Image.Trim())" ` --format "{{.Repository}}:{{.Tag}}"); ## Localize the existence of the image. [Int32] $exists = if ($null -ne $commandResults -and "" -ne $commandResults.Trim()) { ($commandResults.Trim() | Where-Object { $_.Trim() -eq $container.Image.Trim() }).Length } else { 0 }; ## Check if the image exists. if (0 -ne $exists -or $true -eq $this.DryRunEnabled) { ## Remove the image. Write-Host "Removing image '$($container.Image.Trim())'." -ForegroundColor Gray; ## Remove the image from the local registry. $this.ExecuteCommand("${engine} image rm -f '$($container.Image.Trim())'"); ## Reset the AnyRemoved flag. $this.AnyRemoved = $true; } } } ## Purge Networks Method. [Void] PurgeNetworks() { <# .NAME PurgeNetworks .SYNOPSIS Purges networks based on the definitions provided. .DESCRIPTION This method iterates over the network definitions and removes each network if it exists. It uses the specified container engine command (Docker or Podman) to perform the removal. If dry-run mode is enabled, it will only simulate the removal without actually executing the commands. #> ## Iterate over the networks. foreach ($network in $this.Networks | Group-Object -Property 'Name' | ForEach-Object { $_.Group | Select-Object -First 1 }) { ## Reconcile the engine command between the definition and the network. [String] $engine = $($null -ne $network.Command -and "" -ne $network.Command.Trim() ? ` $network.Command.ToString() : $this.Command.ToString()).ToLower().Trim(); ## Localize our command results. $commandResults = (& "${engine}" network ls --filter "Name=$($network.Name.Trim())" --format "{{.Name}}"); ## Localize the existence of the network. [Int32] $exists = if ($null -ne $commandResults -and "" -ne $commandResults.Trim()) { ($commandResults.Trim() | Where-Object { $_.Trim() -eq $network.Name.Trim() }).Length } else { 0 }; ## Check if the network exists. if (0 -ne $exists -or $true -eq $this.DryRunEnabled) { ## Remove the network. Write-Host "Removing network '$($network.Name.Trim())'." -ForegroundColor Gray; ## Remove the network from the local registry. $this.ExecuteCommand("${engine} network rm '$($network.Name.Trim())'"); ## Reset the AnyRemoved flag. $this.AnyRemoved = $true; } } } ## Run Method. [Void] Run([Boolean] $dryRun, [Boolean] $noStart, [String[]] $only, [Boolean] $printCommands, [Boolean] $purgeImages, [HashTable] $replacements) { <# .NAME Run .SYNOPSIS Executes the container engine definition to manage containers and networks. .DESCRIPTION This method orchestrates the entire process of managing containers and networks based on the definitions provided. It handles purging existing resources, pulling images, creating networks and containers, linking containers to networks, and starting containers. It also supports dry-run and debug modes, as well as path replacements. .PARAMETERS dryRun A boolean indicating whether to perform a dry-run (no actual changes). noStart A boolean indicating whether to skip starting the containers after creation. only An array of container names to limit the operation to specific containers. printCommands A boolean indicating whether to print the commands that would be executed. purgeImages A boolean indicating whether to purge unused images after operations. replacements A hashtable of key-value pairs for path and string replacements. #> ## Reconcile the debug flag between the definition file and the command line parameter. $this.DebugEnabled = ($printCommands -or $this.DebugEnabled); ## Reconcile the dry-run flag between the definition file and the command line parameter. $this.DryRunEnabled = ($dryRun -or $this.DryRunEnabled); ## Reconcile the no-start flag between the definition file and the command line parameter. $this.NoStartContainers = ($noStart -or $this.NoStartContainers); ## Reconcile the only-create flag between the definition file and the command line parameter. $this.OnlyCreateContainers += $only; ## Reconcile the purge-images flag between the definition file and the command line parameter. $this.PurgeImagesEnabled = ($purgeImages -or $this.PurgeImagesEnabled); ## Sort and trim the only list and ensure uniqueness. if ($this.OnlyCreateContainers.Count -gt 0) { ## Reset the containers in the instance to the subset of only containers. $this.Containers = $this.Containers | Where-Object { $this.OnlyCreateContainers -contains $_.Name.Trim() }; } ## Check for dry-run or debug mode. if ($this.DryRunEnabled -or ($this.OnlyCreateContainers.Count -gt 0) -or $this.DebugEnabled -or $this.PurgeImagesEnabled) { ## Write the buffer space. Write-Host "`n" -ForegroundColor Magenta; } ## Check for a dry run then write the message to the host. if ($this.DryRunEnabled) { Write-Host "[DRY-RUN]: No changes will be made." -ForegroundColor Magenta; } ## Check for no container start then write the message to the host. if ($this.NoStartContainers) { Write-Host "[NO-START]: Containers will not be started after creation nor will routes be linked to them." ` -ForegroundColor Magenta; } ## Check for only creating specific containers then write the message to the host. if ($this.OnlyCreateContainers.Count -gt 0) { Write-Host "[ONLY]: Only the following containers will be processed: `n`t`t`t - $(($this.OnlyCreateContainers | Sort-Object -Unique) -join "`n`t`t`t - ")`n" ` -ForegroundColor Magenta; } ## Check for debug mode then write the message to the host. if ($this.DebugEnabled) { Write-Host "[PRINT-CMD]: Commands will be printed before execution." -ForegroundColor Magenta; } ## Check for image purging then write the message to the host. if ($this.PurgeImagesEnabled) { Write-Host "[PURGE-IMG]: All images will be removed before pulling new ones." -ForegroundColor Magenta; } ## Check for dry-run or debug mode. if ($this.DryRunEnabled -or ($this.OnlyCreateContainers.Count -gt 0) -or $this.DebugEnabled -or $this.PurgeImagesEnabled) { ## Write the buffer space. Write-Host "`n" -ForegroundColor Magenta; } ## Iterate over the replacements from the command line parameter. foreach ($key in $Replacements.Keys) { ## Add the replacement to the definition replacements. $this.Replacements[$key.Trim()] = $replacements[$key].Trim(); } ## Ensure the containers are unique by name. $this.Containers = $this.Containers | Group-Object -Property 'Name' | ForEach-Object { $_.Group | Select-Object -First 1 }; ## Purge the containers. $this.PurgeContainers(); ## Purge the networks. $this.PurgeNetworks(); ## Check for image purging. if ($this.PurgeImagesEnabled -or $this.DryRunEnabled) { ## Purge the images. $this.PurgeImages(); } ## Pull the images. $this.PullImages(); ## Create the networks. $this.CreateNetworks(); ## Create the containers. $this.CreateContainers(); ## Link the containers to their secondary networks. $this.LinkContainersToSecondaryNetworks(); ## Check to see if we should start the containers. if (-not $this.NoStartContainers -or $this.DryRunEnabled) { ## Start the containers. $this.StartContainers(); ## Link the containers to their routes. $this.LinkContainersToRoutes(); } ## Cleanup any dangling resources. $this.Prune(); } ## StartContainers Method. [Void] StartContainers() { <# .NAME StartContainers .SYNOPSIS Starts containers based on the definitions provided. .DESCRIPTION This method iterates over the container definitions and starts each container if it exists. It uses the specified container engine command (Docker or Podman) to perform the start operation. If dry-run mode is enabled, it will only simulate the start without actually executing the commands. #> ## Iterate over the containers. foreach ($container in $this.Containers) { ## Reconcile the engine command between the definition and the container. [String] $engine = $($null -ne $container.Command -and "" -ne $container.Command.Trim() ? ` $container.Command.ToString() : $this.Command.ToString()).ToLower().Trim(); ## Localize our command results. $commandResults = (& "${engine}" ps -a --filter "Name=$($container.Name.Trim())" --format "{{.Names}}"); ## Localize the existence of the container. [Int32] $exists = if ($null -ne $commandResults -and "" -ne $commandResults.Trim()) { ($commandResults.Trim() | Where-Object { $_.Trim() -eq $container.Name.Trim() }).Length } else { 0 }; ## Check if the container exists. if (0 -ne $exists -or $true -eq $this.DryRunEnabled) { ## Start the container. Write-Host "Starting container '$($container.Name.Trim())'." -ForegroundColor Green; ## Start the container in the local registry. $this.ExecuteCommand("${engine} container start '$($container.Name.Trim())'"); } } } }; ## Definition Replacement Class. [NoRunspaceAffinity()] class UpdateContainersNetworkDefinition { <# .NAME UpdateContainersNetworkDefinition .SYNOPSIS Class defining a network configuration for container engines. .DESCRIPTION This class represents the configuration details for a network that can be created and managed by container engines like Docker and Podman. .PROPERTIES Bridge The name of the bridge interface to use for the network. Command The container engine command to use (Docker or Podman). Driver The network driver to use (e.g., bridge, macvlan). Interface The name of the network interface to bind to (if applicable). IPv4Prefix The IPv4 subnet prefix for the network (e.g., 192.168.1). IPv6Prefix The IPv6 subnet prefix for the network (e.g., fd00:abcd:1234:1). Name The name of the network. #> ## Bridge Property. [AllowNull()] [JsonPropertyName('bridge')] [String] $Bridge = $null; ## Command Property. [AllowNull()] [JsonPropertyName('command')] [Nullable[UpdateContainersEngineEnum]] $Command = $null; ## Driver Property. [AllowNull()] [JsonPropertyName('driver')] [String] $Driver = "bridge"; ## Interface Name Property. [AllowNull()] [JsonPropertyName('interface')] [String] $Interface = $null; ## IPv4Prefix Property. [JsonPropertyName('prefix')] [JsonRequired()] [String] $IPv4Prefix; ## IPv6 Property. [AllowNull()] [JsonPropertyName('prefix6')] [String] $IPv6Prefix; ## Name Property. [JsonPropertyName('name')] [JsonRequired()] [String] $Name; ## ToCommandLine Method. [String] ToCommandLine() { <# .NAME ToCommandLine .SYNOPSIS Generates the command line string to create the network. .DESCRIPTION This method constructs the appropriate command line string to create a network using the specified container engine, based on the properties defined in the NetworkDefinition class. .OUTPUTS String The command line string to create the network. #> ## Define our list of non-namable interface drivers. [String[]] $unnamedDrivers = @("macvlan", "ipvlan"); ## Define our command line string. [String] $commandLine = "network create ```n`t--driver '$($this.Driver.ToLower().Trim())'"; ## Check for an IPv4 prefix. if ($null -ne $this.IPv4Prefix -and "" -ne $this.IPv4Prefix.Trim()) { ## Add the IPv6 gateway and subnet to the command line string. $commandLine = "${commandLine} ```n`t--gateway '$($this.IPv4Prefix.Trim()).1' ```n`t--ipv4 ```n`t--subnet '$($this.IPv4Prefix.Trim()).0/24'"; } ## Check for an IPv6 prefix if ($null -ne $this.IPv6Prefix -and "" -ne $this.IPv6Prefix.Trim()) { ## Append the IPv6 to the command line string. $commandLine = "${commandLine} ```n`t--gateway '$($this.IPv6Prefix.Trim())::1' ```n`t--ipv6 ```n`t--subnet '$($this.IPv6Prefix.Trim())::/64'"; } ## Check for a bridge name. if ($null -ne $this.Bridge -and "" -ne $this.Bridge.Trim() -and ($unnamedDrivers -Contains $this.Driver.ToLower().Trim())) { ## Append the bridge name to the command line string. $commandLine = "${commandLine} ```n`t--opt 'parent=$($this.Bridge.Trim() )'"; } ## Check for an interface name. if ($null -ne $this.Interface -and "" -ne $this.Interface.Trim() -and -not ($unnamedDrivers -Contains $this.Driver.ToLower().Trim())) { ## Append the interface name to the command line string. $commandLine = "${commandLine} ```n`t--opt 'com.docker.network.bridge.name=$($this.Interface.Trim() )'"; } ## We're done, return the command line string. return "${commandLine} ```n`t'$($this.Name.Trim())'".Trim(); } }; ## Definition Replacement Class. [NoRunspaceAffinity()] class UpdateContainersReplacementDefinition { <# .NAME UpdateContainersReplacementDefinition .SYNOPSIS Class defining a key-value replacement for paths and strings. .DESCRIPTION This class represents a key-value pair used for replacing placeholders in strings or paths, with an option to treat the value as a file system path. .PROPERTIES Key The placeholder key to be replaced in the target string. Path A boolean indicating whether the value should be treated as a file system path. Value The value to replace the key with, which can be a string or a path. #> ## Key Property. [JsonPropertyName('key')] [JsonRequired()] [String] $Key; ## The Path Property. [JsonPropertyName('path')] [Boolean] $Path = $false; ## Value Property. [JsonPropertyName('value')] [JsonRequired()] [String] $Value; ## Replace Method. [String] Replace([String] $value) { <# .NAME Replace .SYNOPSIS Replaces occurrences of the key in the provided value with the defined value. .DESCRIPTION This method searches for occurrences of the key, formatted as `%{key}%`, in the provided string and replaces them with the defined value. If the Path property is set to true, it treats the value as a file system path and resolves it accordingly. .PARAMETERS value The string in which to perform the replacement. #> ## Ensure we have a value to replace. if ($null -eq $value -or "" -eq $value.Trim()) { return $value; } ## Check for a path replacement. if ($this.Path) { ## Check check the operation system. if ([Environment]::OSVersion.Platform -eq [PlatformID]::Unix) { ## Replace any drive letters with empty in the value. $this.Value = $($this.Value.Trim() -replace "^[a-zA-Z]:", "").Trim(); ## Replace any Windows-style backslashes with slashes in the value. $this.Value = $($this.Value.Trim() -replace "\\", "/").Trim(); ## Replace any double slashes with a single slash in the value. $this.Value = $($this.Value.Trim() -replace "//", "/").Trim(); } else { ## Replace any Unix-style slashes with backslashes in the value. $this.Value = $($this.Value.Trim() -replace "/", "\\").Trim(); ## Replace any double backslashes with a single backslash in the value. $this.Value = $($this.Value.Trim() -replace "\\\\", "\\").Trim(); ## Check for a leading backslash in the value. if ($this.Value.StartsWith("\")) { ## Prepend the value with the current drive letter. $this.Value = "$($([Path]::GetPathRoot((Get-Location).Path).Trim()))$($this.Value.Trim())".Trim(); } } ## Resolve the path to ensure it exists. $resolvedPath = (Resolve-Path -Path $this.Value.Trim() -ErrorAction SilentlyContinue); ## Resolve the full path from the value. $this.Value = ($null -ne $resolvedPath ? $resolvedPath.Path.Trim() : $this.Value.Trim()); } ## Return the replaced value. return ($value.Trim() -replace "%{$($this.Key.Trim())}%", $this.Value.Trim()); } }; ## Define the types we want to export. [Type[]] $exportableTypes = @( [UpdateContainersEngineEnum] [UpdateContainersIpAddressMethodEnum] [UpdateContainersContainerDefinition] [UpdateContainersContainerDeviceDefinition] [UpdateContainersContainerHealthCheckDefinition] [UpdateContainersContainerMountDefinition] [UpdateContainersContainerNetworkDefinition] [UpdateContainersContainerNetworkRouteDefinition] [UpdateContainersContainerPublishDefinition] [UpdateContainersDefinition] [UpdateContainersNetworkDefinition] [UpdateContainersReplacementDefinition] ); ## Get the non-public TypeAccelerators class for defining new accelerators. $typeAcceleratorsClass = [PSObject].Assembly.GetType('System.Management.Automation.TypeAccelerators') ## Add type accelerators for every exportable type. $existingTypeAccelerators = $typeAcceleratorsClass::Get ## Iterate over the types we want to export. foreach ($type in $exportableTypes) { # !! $TypeAcceleratorsClass::Add() quietly ignores attempts to redefine existing # !! accelerators with different target types, so we check explicitly. $existing = $existingTypeAccelerators[$type.FullName] ## Check for an existing type accelerator with a different type. if ($null -ne $existing -and $existing -ne $type) { ## We're done, throw an error. throw "Unable to register type accelerator [$($type.FullName)], because it is already defined with a different type ([$existing])." } ## Add the type accelerator for the type. $typeAcceleratorsClass::Add($type.FullName, $type) } ## Export our module functions. Export-ModuleMember -Function 'Get-ContainerDefinitionFilePath'; Export-ModuleMember -Function 'Get-ContainerDefinitionJsonSerializerOptions'; Export-ModuleMember -Function 'Update-ContainersWithDefinitionFile'; |