cDscDocker.psm1


enum Ensure
{
    Present
    Absent
}

enum Swarm
{
    Active
    Inactive
}


[DscResource()]
class cDscDocker 
{
    [DscProperty(Key)] 
    [string]$docker
    
    [DscProperty(Mandatory)] 
    [Ensure]$Ensure

    [DscProperty()]
    [string]$swarmToken

    [DscProperty()]
    [string]$swarmURI

    [DscProperty(Mandatory)]
    [Swarm]$swarm

    [DscProperty()]
    [string]$daemonInterface #must be in format of tcp://0.0.0.0:2375

    [DscProperty()]
    [bool]$exposeApi

    
    
    #Equivalent of Set-TargetResource
    #Executes the current operation necessary to get the system to the desired state
    [void] Set()
    {
        $dockerProviderAvailable = $this.CheckDockerProvider()
        $dockerInstalled = $this.CheckDocker()
        $dockerInSwarm = $this.CheckDockerSwarm()
        $apiExposed = $this.CheckDaemonAPI()

        Write-Verbose "Settings"
        Write-Verbose "Docker $($this.Ensure)"
        Write-Verbose "Swarm $($this.swarm)"

        switch($this.Ensure)
        {
            Present{
                if(-not $dockerProviderAvailable)
                {
                    Write-Verbose "Docker Provider not Available; Installing Provider"
                    $this.InstallDockerProvider()
                }

                if(-not $dockerInstalled)
                {
                    Write-Verbose "Docker not installed; installing docker"
                    $this.InstallDocker()
                }
                
                if($global:DSCMachineStatus -ne 1)
                {
                    Write-Verbose "Not pending reboot"
                    Write-Verbose "Entering Switch"
                    switch($this.swarm)
                    {
                        
                        Active{
                            Write-Verbose "Setting Swarm to Active"
                            if(-not $dockerInSwarm){
                                Write-Verbose "Not in docker swarm; executing join method"
                                $this.JoinDockerSwarm()
                            }
                            
                        }
                        Inactive{
                            Write-Verbose "Setting swarm to inactive"
                            if($dockerInSwarm){
                                $this.LeaveDockerSwarm()
                            }

                        }
                    }
                    if($apiExposed -eq $false){
                        $this.ConfigureDaemonAPI()
                    }
                }


            }
            Absent{
                if($dockerInSwarm){
                    $this.LeaveDockerSwarm()
                }
                if($dockerInstalled){
                    $this.UninstallDocker()
                }

                    
                
            }
        }

        
    }
    

    #Equivalent of Test-TargetResource
    #Returns true if system is in the state specified; false if a modification needs to take place
    [bool] Test()
    {
        $objectValidState = $false


        $dockerPresent = $this.CheckDocker()
        if($dockerPresent)
        {
            $dockerSwarm = $this.CheckDockerSwarm()
            $dockerApiStatus = $this.CheckDaemonAPI()
        }
        else
        {
            $dockerSwarm = $false
            $dockerApiStatus = $false
        }
        
        
        if ($this.Ensure -eq [Ensure]::Present)
        {
            
            if ($dockerPresent)
            {
                
                if($this.swarm -eq [Swarm]::Active)
                {
                    if($dockerSwarm -eq $true)
                    {
                        Write-Verbose "Docker is present (Expect: Present). Swarm is Active (Expect: Active). VALID STATE"
                        $objectValidState = $true;
                    }

                    if($dockerSwarm -eq $false)
                    {
                        Write-Verbose "Docker is present (Expect: Present). Swarm is Inactive (Expect: Active). INVALID STATE"
                        return $false;
                    }
                }
                elseif($this.swarm -eq [Swarm]::Inactive)
                {
                    if($dockerSwarm -eq $true)
                    {
                        Write-Verbose "Docker is present (Expect: Present). Swarm is Active (Expect: Inactive). INVALID STATE"
                        return $false;
                    }
                    elseif($dockerSwarm -eq $false)
                    {
                        Write-Verbose "Docker is present (Expect: Present). Swarm is Inactive (Expect: Inactive). VALID STATE"
                        $objectValidState = $true
                    }
                }
                


                if($this.exposeApi -eq $true){
                    if($dockerApiStatus -eq $false){
                        return $false
                    }
                    elseif($dockerApiStatus -eq $true){
                        $objectValidState = $true
                    }
                }

                elseif($this.exposeApi -eq $false){
                    if($dockerApiStatus -eq $true){
                        return $false
                    }
                    elseif($dockerApiStatus -eq $false){
                        $objectValidState = $true
                    }
                }            
                

                #Fallback; setting this item in case return route is missed
                $objectValidState = $true

            }

            if (-not $dockerPresent)
            {
                Write-Verbose "Docker is Absent (Expect: Present). INVALID STATE"
                return $false
            }
        }
        Else
        {
            if ($dockerPresent)
            {
                Write-Verbose "Docker is Present (Expect: Absent). INVALID STATE"
                return $false
            }
            if (-not $dockerPresent)
            {
                Write-Verbose "Docker is Absent (Expect: Absent). VALID STATE"
                $objectValidState = $true
            }
            
            
        }

        
        return $objectValidState
            
    }
    
    #Equivalent of the Get-TargetResource
    #Get's the current state of the system in cDscDocker object form
    [cDscDocker] Get()
    {
        if ($this.CheckDocker())
        {
            $this.Ensure = [Ensure]::Present
        }
        else
        {
            $this.Ensure = [Ensure]::Absent
        }

        return $this
        
        
    }









    #Swarm Action, join
    [void]JoinDockerSwarm()
    {
        Write-Verbose "Joining Docker Swarm"
        docker swarm join --token $($this.swarmToken) $($this.swarmURI)
        
    }

    #Swarm action, leave
    [void]LeaveDockerSwarm()
    {
        docker swarm leave --force
    }

    [bool]CheckDockerSwarm()
    {
        $returnValue = $false
        

        if($this.CheckDocker()){
            $info = docker info
            foreach($item in $info)
            {
                if($item -match "swarm")
                {
                    if($item -match "\sactive")
                    {
                        return $true
                    }
                    elseif($item -match "inactive")
                    {
                        return $false
                    }
                }
            }
        }


        return $returnValue
    }
    #Docker Action, Verify
    [bool]CheckDocker()
    {
        $installed = $false

        if($this.CheckDockerProvider())
        {

            $packages = Get-Package -ProviderName DockerMsftProvider
            foreach ($p in $packages)
            {
                if ($p.Name -eq 'docker')
                {
                    $installed = $true
                }
            }

            #In case this was installed as a service via other means; e.g. not the DockerMsftProvider
            $services = Get-Service | Where-Object {$_.Name -like "docker"}
            if ($services.Length )
            {
                $installed = $true
            }
        }


        return $installed
    }

    #Docker Action, Install
    [void]InstallDocker()
    {
        Install-Package -Name docker -ProviderName DockerMsftProvider -Force
        $global:DSCMachineStatus = 1
    }
    
    #Docker Action, Uninstall
    [void]UninstallDocker()
    {
        Uninstall-Package -ProviderName DockerMsftProvider -Name docker -Force #Uninstall docker; will move to a function if re-used elsewhere
    }

    #Docker Provider Action; verify
    [bool]CheckDockerProvider()
    {
        $available = $false
        $providers = Get-PackageProvider -ListAvailable
        
        foreach($provider in $providers)
        {
            if ($provider.Name -eq "DockerMsftProvider")
            {
                $available = $true
            }
        }
        
        return $available
    }

    #Docker Provider Action; install
    [void]InstallDockerProvider()
    {
        Install-Module -Name DockerMsftProvider -Repository psgallery -Force
    }

    #Daemon API Checks
    [bool]CheckDaemonAPI()
    {
        if(Test-Path "C:\ProgramData\docker\config\daemon.json"){
            $daemon = ((Get-Content 'C:\ProgramData\docker\config\daemon.json') | ConvertFrom-Json)
            if($daemon.PSObject.Properties['hosts']){
                #daemon has hosts key
                if(($daemon.hosts -contains $this.daemonInterface) -and ($daemon.hosts -contains "npipe://")){
                    return $true
                }
            }
        }
        else{
            return $false
        }

        return $false
    }

    #create keys and values as needed
    [void]ConfigureDaemonAPI()
    {
        
        $daemon = New-Object PSObject

        #Verify and create daemon.json if necessary
        if(-not (Test-Path "C:\ProgramData\docker\config\daemon.json")){
            New-Item -ItemType File -Path "C:\ProgramData\docker\config\daemon.json" -Force
        }
        else{
            $contents = ((Get-Content 'C:\ProgramData\docker\config\daemon.json') | ConvertFrom-Json)
            if(-not ($contents -eq $null)){
                $daemon = $contents
            }
        }

        


        #Verify if json object contains hosts key
        if($daemon.PSObject.Properties['hosts']){
            $daemon.PSObject.Properties.Remove('hosts') #remove the hosts key in order to clear it entirely of bad values / previous entries
            if($this.exposeApi -eq $true){
                $daemon | Add-Member -Name "hosts" -MemberType NoteProperty -Value @($this.daemonInterface, "npipe://")
            }
            

            
        }
        elseif($this.exposeApi -eq $true){
            #if full hosts key missing; add it with the values necessary
            $daemon | Add-Member -Name "hosts" -MemberType NoteProperty -Value @($this.daemonInterface, "npipe://")
            #create values
        }

        $daemon | ConvertTo-Json | Out-File 'C:\ProgramData\docker\config\daemon.json' -Encoding ascii -Force
        

        
    }


}