Connecting to CosmosDB from Docker container - docker

I am trying to learn Docker and am building up a solution slowly as I go. I created an Azure Function App that has one Http endpoint exposed. I created the Docker file to build and run the solution in a linux container(image:mcr.microsoft.com/azure-functions/dotnet:3.0, Debian image). I'm on a Windows machine.
I installed the Azure CosmosDB Emulator on my Windows machine and wanted to connect to it from the function app running in the linux container.
I am passing in the connection string for cosmos as an environment variable.
ARG COSMOS_CONNECTION_STRING="AccountEndpoint=https://host.docker.internal:8081/;AccountKey=C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw=="
I'm trying to connect to the emulator through the following code:
await new CosmosClient(Environment.GetEnvironmentVariable("AzureCosmosConnectionString", EnvironmentVariableTarget.Process)
.GetContainer("db_name", "container_name")
.UpsertItemAsync<Dto>(dto)
.ConfigureAwait(false);
When I do this, I'm getting the following error(I assume the first few lines are the most relevant, but am including the rest in case I'm wrong):
---> System.Net.Http.HttpRequestException: The SSL connection could not be established, see inner exception.
---> System.Security.Authentication.AuthenticationException: The remote certificate is invalid according to the validation procedure.
at System.Net.Security.SslStream.StartSendAuthResetSignal(ProtocolToken message, AsyncProtocolRequest asyncRequest, ExceptionDispatchInfo exception)
at System.Net.Security.SslStream.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslStream.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslStream.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslStream.StartReadFrame(Byte[] buffer, Int32 readBytes, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslStream.StartReceiveBlob(Byte[] buffer, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslStream.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslStream.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslStream.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslStream.StartReadFrame(Byte[] buffer, Int32 readBytes, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslStream.PartialFrameCallback(AsyncProtocolRequest asyncRequest)
--- End of stack trace from previous location where exception was thrown ---
at System.Net.Security.SslStream.ThrowIfExceptional()
at System.Net.Security.SslStream.InternalEndProcessAuthentication(LazyAsyncResult lazyResult)
at System.Net.Security.SslStream.EndProcessAuthentication(IAsyncResult result)
at System.Net.Security.SslStream.EndAuthenticateAsClient(IAsyncResult asyncResult)
at System.Net.Security.SslStream.<>c.<AuthenticateAsClientAsync>b__65_1(IAsyncResult iar)
at System.Threading.Tasks.TaskFactory`1.FromAsyncCoreLogic(IAsyncResult iar, Func`2 endFunction, Action`1 endAction, Task`1 promise, Boolean requiresSynchronization)
--- End of stack trace from previous location where exception was thrown ---
at System.Net.Http.ConnectHelper.EstablishSslConnectionAsyncCore(Stream stream, SslClientAuthenticationOptions sslOptions, CancellationToken cancellationToken)
--- End of inner exception stack trace ---
at System.Net.Http.ConnectHelper.EstablishSslConnectionAsyncCore(Stream stream, SslClientAuthenticationOptions sslOptions, CancellationToken cancellationToken)
at System.Net.Http.HttpConnectionPool.ConnectAsync(HttpRequestMessage request, Boolean allowHttp2, CancellationToken cancellationToken)
at System.Net.Http.HttpConnectionPool.CreateHttp11ConnectionAsync(HttpRequestMessage request, CancellationToken cancellationToken)
at System.Net.Http.HttpConnectionPool.GetHttpConnectionAsync(HttpRequestMessage request, CancellationToken cancellationToken)
at System.Net.Http.HttpConnectionPool.SendWithRetryAsync(HttpRequestMessage request, Boolean doRequestAuth, CancellationToken cancellationToken)
at System.Net.Http.RedirectHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
at Microsoft.Azure.Cosmos.DocumentClient.HttpRequestMessageHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
at System.Net.Http.HttpClient.FinishSendAsyncBuffered(Task`1 sendTask, HttpRequestMessage request, CancellationTokenSource cts, Boolean disposeCts)
at Microsoft.Azure.Cosmos.GatewayAccountReader.GetDatabaseAccountAsync(Uri serviceEndpoint)
at Microsoft.Azure.Cosmos.Routing.GlobalEndpointManager.GetDatabaseAccountFromAnyLocationsAsync(Uri defaultEndpoint, IList`1 locations, Func`2 getDatabaseAccountFn)
at Microsoft.Azure.Cosmos.GatewayAccountReader.InitializeReaderAsync()
at Microsoft.Azure.Cosmos.CosmosAccountServiceConfiguration.InitializeAsync()
at Microsoft.Azure.Cosmos.DocumentClient.InitializeGatewayConfigurationReaderAsync()
at Microsoft.Azure.Cosmos.DocumentClient.GetInitializationTaskAsync(IStoreClientFactory storeClientFactory)
at Microsoft.Azure.Cosmos.DocumentClient.EnsureValidClientAsync()
at Microsoft.Azure.Cosmos.Handlers.RequestInvokerHandler.EnsureValidClientAsync(RequestMessage request)
at Microsoft.Azure.Cosmos.Handlers.RequestInvokerHandler.SendAsync(RequestMessage request, CancellationToken cancellationToken)
at Microsoft.Azure.Cosmos.Handlers.RequestInvokerHandler.SendAsync(Uri resourceUri, ResourceType resourceType, OperationType operationType, RequestOptions requestOptions, ContainerInternal cosmosContainerble`1 partitionKey, Stream streamPayload, Action`1 requestEnricher, CosmosDiagnosticsContext diagnosticsContext, CancellationToken cancellationToken)
at Microsoft.Azure.Cosmos.ContainerCore.ProcessItemStreamAsync(Nullable`1 partitionKey, String itemId, Stream streamPayload, OperationType operationType, ItemRequestOptions requestOptions, CosmosDiagnostiiagnosticsContext, CancellationToken cancellationToken)
at Microsoft.Azure.Cosmos.ContainerCore.ExtractPartitionKeyAndProcessItemStreamAsync[T](Nullable`1 partitionKey, String itemId, T item, OperationType operationType, ItemRequestOptions requestOptions, CosmcsContext diagnosticsContext, CancellationToken cancellationToken)
at Microsoft.Azure.Cosmos.ContainerCore.UpsertItemAsync[T](T item, Nullable`1 partitionKey, ItemRequestOptions requestOptions, CancellationToken cancellationToken)
When I look at the emulator locally in my browser, it is using a localhost certificate(I assume one created with the dotnet cli).
Attempt 1
Export the localhost cert as a .pfx file and then get the linux container to trust that through the following commands(in my Dockerfile)
ARG CERTIFICATE_PASSWORD="Test|234"
RUN openssl pkcs12 \
-in "/src/localhost.pfx" \
-clcerts \
-nokeys \
-out "/src/localhost.crt" \
-passin pass:${CERTIFICATE_PASSWORD}
RUN cp "/src/localhost.crt" "/usr/local/share/ca-certificates/"
RUN update-ca-certificates
I assume this attempt doesn't work, at least in part, due to the fact that the certificate on the Windows machine is created for localhost, whereas to connect to it from docker, the address needs to be host.docker.internal.
Attempt 2
Add the exported certificate to the Kestrel process running the function app in hopes that it would then honor it by adding the following to my dockerfile
ENV ASPNETCORE_Kestrel__Certificates__Default__Path=/src/localhost.pfx
Attempt 3
Update the CosmosClient instantiation to include options overriding the HttpClientFactory as follows:
CosmosClient = new CosmosClient(
Environment.GetEnvironmentVariable("AzureCosmosConnectionString", EnvironmentVariableTarget.Process),
new CosmosClientOptions
{
HttpClientFactory = () =>
{
using (var httpClientHandler = new HttpClientHandler())
{
httpClientHandler.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;
return new HttpClient(httpClientHandler);
}
}
});
With every attempt above, I'm still seeing the same error. Not sure what else to try to get this to work...
Just found this issue on GitHub. Seems as though I'm not alone.

Update (2/10/2021)
The SDK now allows overriding SSL validation in an easy fashion (reference https://learn.microsoft.com/azure/cosmos-db/local-emulator?tabs=cli%2Cssl-netstd21#disable-ssl-validation):
CosmosClientOptions cosmosClientOptions = new CosmosClientOptions()
{
HttpClientFactory = () =>
{
HttpMessageHandler httpMessageHandler = new HttpClientHandler()
{
ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator
};
return new HttpClient(httpMessageHandler);
},
ConnectionMode = ConnectionMode.Gateway
};
CosmosClient client = new CosmosClient(endpoint, authKey, cosmosClientOptions);
Older information
Reference:
https://github.com/Azure/azure-cosmos-dotnet-v3/issues/1551#issuecomment-634365001
https://learn.microsoft.com/en-us/azure/cosmos-db/local-emulator#running-on-mac-or-linux
Step 1 - Export the Certificate
Following this guide, but the certificate needs to be a PFX, not CRT. It will ask you to set some password.
Step 2 - Place the cert somewhere you can copy or access from docker
For example, I put it on a folder I can map to when booting Docker along with the code I wanted to run:
Step 3 - Get your machine's IP address
As per https://learn.microsoft.com/en-us/azure/cosmos-db/local-emulator#running-on-mac-or-linux, I used ipconfig and got my Windows IP.
Step 4 - Start a docker image
In my case, I use one with the NET Core 3.1 SDK, the official one from https://learn.microsoft.com/en-us/dotnet/architecture/microservices/net-core-net-framework-containers/official-net-docker-images
I started the container with an interactive shell, and mapping localhost to the IP I got on Step 3. This let's you use localhost in the connection string.
docker run -v /c/DockerSample:/DockerSample --add-host="localhost:192.168.1.15" -it mcr.microsoft.com/dotnet/core/sdk:3.1 /bin/bash
And I'm also mounting the folder where I saved the project and the certificate I want to import. This is not required, but I'm not fluent on Docker to know if there is a better way to pass the certificate.
After the shell starts, I basically run the commands described in the Emulator doc and the certificate gets added.
Step 5
The Docker container should now have the required Cert to connect to localhost and you should not need the HttpClientFactory.
NOTE: Also there is a bug tracking HttpClientFactory not being used everywhere that is the source of your error https://github.com/Azure/azure-cosmos-dotnet-v3/pull/1548

First of all, thank you to Matias Quaranta for all of the useful documentation. I had already pored over most of it before receiving his answer, but really appreciate all the time it took to compile it.
I have been struggling with this for around two weeks now and finally got something that I think will work. This work is largely based on the script found in this GitHub issue. I found that there is a PowerShell module that gets put on your computer when you install the Cosmos DB Emulator, so I tried to leverage those functions as much as possible to do the work.
The script's entry point is the function Start-CosmosDbEmulatorForDocker and it
Makes sure the Emulator is stopped.
Generates a new certificate to be used with the Docker image and replaces the one that was created upon install of the emulator.
Generates a .pfx certificate from the one created for the emulator.
Restarts the emulator once the new certificate is ready.
The password taken by the function is the one used for the .pfx file generated.
azureCosmosDbEmulator.ps1
using namespace System.ServiceProcess
Function Start-CosmosDbEmulatorForDocker(
[Parameter()]
[securestring]
$password
) {
$cosmosDbInstallLocation = Get-CosmosDbInstallLocation
If (!$cosmosDbInstallLocation) {
Install-AzureCosmosDBEmulator
}
Write-Host "Importing Microsoft.Azure.CosmosDB.Emulator powershell module."
Import-Module -Name "$($cosmosDbInstallLocation.InstallLocation)\PSModules\Microsoft.Azure.CosmosDB.Emulator"
Install-CosmosDBDockerCertificate -cosmosDbInstallLocation $cosmosDbInstallLocation.InstallLocation -password $password
Start-CosmosDbEmulator -AllowNetworkAccess -Key "C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw=="
}
Function Get-CosmosDbInstallLocation() {
Get-ChildItem HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall | ForEach-Object { Get-ItemProperty $_.PsPath } | Where-Object { $_.DisplayName -eq "Azure Cosmos DB Emulator" } | Select-Object InstallLocation
}
Function Install-AzureCosmosDBEmulator() {
Write-Host "Installing Azure Cosmos Db Emulator."
$installer = "$PSScriptRoot\cosmosEmulatorInstaller.msi"
curl "https://aka.ms/cosmosdb-emulator" -O $installer
Start-Process -Wait -FilePath msiexec -ArgumentList /i, $installer
Remove-Item $installer
}
Function Install-CosmosDBDockerCertificate(
[Parameter()]
[string]
$cosmosDbInstallLocation,
[Parameter()]
[securestring]
$password
) {
If ((Get-CosmosDbEmulatorStatus) -ne [ServiceControllerStatus]::Stopped) {
Write-Host "Stopping Cosmos DB emulator."
Stop-CosmosDbEmulator
}
$dockerCertificatesPath = Join-Path (Split-Path -Path $PSScriptRoot -Parent) "certificates"
$cosmosDbPfxCertificatePath = "$($dockerCertificatesPath)\cosmosdbemulator.pfx"
Uninstall-Certificate -dockerCertificatePath $cosmosDbPfxCertificatePath
Write-Host "Generating new Cosmos DB certificate to work with Docker."
New-CosmosDbEmulatorCertificate "host.docker.internal"
Start-Sleep -s 5
New-DockerCertificate -dockerCertificatePath $cosmosDbPfxCertificatePath -password $password
Set-Location (Split-Path -Path $PSScriptRoot -Parent)
}
Function Uninstall-Certificate(
[Parameter()]
[string]
$dockerCertificatePath
) {
Write-Host "Removing existing DocumentDbEmulatorCertificate certificate."
if (Test-Path $dockerCertificatePath) {
Remove-Item -Path $dockerCertificatePath
}
}
Function New-DockerCertificate(
[Parameter()]
[string]
$dockerCertificatePath,
[Parameter()]
[securestring]
$password
) {
Write-Host "Generating new pfx version of DocumentDbEmulatorCertificate certificate for use in Docker image."
Get-CosmosDbEmulatorCertificate | Export-PfxCertificate -Filepath $dockerCertificatePath -Password $password
}
I then have a shell script to be run inside the Docker image that will install the .pfx cert into the Docker container. The COSMOS_DB_EMULATOR_PFX_PASSWORD value must match the one used by the PowerShell script.
trust_cosmos_db_emulator_crt.sh
#!/bin/bash
# Save current working directory
PWD=`pwd`
pushd $PWD
# Find and move to the location of this script
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
cd $DIR
if [ -n "$1" ]; then
COSMOS_DB_EMULATOR_PFX=$1
else
COSMOS_DB_EMULATOR_PFX="/certificates/cosmosdbemulator.pfx"
fi
COSMOS_DB_EMULATOR_PFX_PASSWORD="SUPER_SECRET_PASSWORD"
CERT_TO_TRUST="cosmosdbemulator.crt"
# Generate .crt file if pfx exists
if [ -f "$COSMOS_DB_EMULATOR_PFX" ]; then
openssl pkcs12 -in $COSMOS_DB_EMULATOR_PFX -clcerts -nokeys -out cosmosdbemulator.crt -passin pass:$COSMOS_DB_EMULATOR_PFX_PASSWORD;
fi
# # Trust Cert (will end located in /etc/ssl/certs/ based on *.crt name as a *.pem, e.g. /etc/ssl/certs/cosmosdbemulator.pem for cosmosdbemulator.crt)
if [ -f "$CERT_TO_TRUST" ]; then
cp $CERT_TO_TRUST /usr/local/share/ca-certificates/
update-ca-certificates
rm $CERT_TO_TRUST;
fi
# Restore working directory
popd
The project structure I have is as follows:
src/
scripts/
azureCosmosDbEmulator.ps1
trust_cosmos_db_emulator_crt.sh
certificates/
DockerFile
The Dockerfile contains the following lines:
COPY ["/scripts/", "/scripts/"]
COPY ["/certificates/", "/certificates/"]
RUN /scripts/trust_cosmos_db_emulator_crt.sh
With all of that in place, I can build the docker image with docker build -t temp . and then run it with docker run -it -p 80:80 temp and the code running inside of the docker container will talk to my local machine's installed version of the Azure Cosmos DB Emulator.
As this was a HUGE pain in the neck, if you are experiencing this pain as well, vote for better support from Microsoft on this here.

Related

Coverage report not published using Docker and pytest-azurepipelines

In the Azure DevOps pipeline, running pytest in the docker container is not publishing the test coverage report.
Warnings: Coverage XML was not created, skipping upload.
Dockerfile
..
...
RUN pip install pytest pytest-azurepipelines pytest-cov
...
..
azure-pipelines.yml
- task: Bash#3
displayName: Run Python Tests
inputs:
targetType: "inline"
script: |
docker run --user "$(id -u):$(id -g)" -v "$(System.DefaultWorkingDirectory)/data-science/app:/usr/src/app" "$(azure.acr.name).azurecr.io/project-name:$(branchName)" pytest tests/ --cov-report xml
Pipeline Logs
============================= test session starts ==============================
platform linux -- Python 3.7.13, pytest-7.1.2, pluggy-1.0.0
rootdir: /usr/src/app, configfile: pytest.ini
plugins: anyio-3.6.2, nunit-1.0.3, azurepipelines-1.0.4, cov-4.0.0
collected 13 items
tests/test_main.py .. [ 15%]
tests/abc****_tests/test_api.py .... [ 46%]
tests/abc****_tests/test_abc.py . [ 53%]
Result Attachments will be stored in LogStore
Run Attachments will be stored in LogStore
Failed to parse result files: System.IO.FileNotFoundException: Could not find file '/usr/src/app/test-output.xml'.
File name: '/usr/src/app/test-output.xml'
at Interop.ThrowExceptionForIoErrno(ErrorInfo errorInfo, String path, Boolean isDirectory, Func`2 errorRewriter)
at Microsoft.Win32.SafeHandles.SafeFileHandle.Open(String path, OpenFlags flags, Int32 mode)
at System.IO.FileStream.OpenHandle(FileMode mode, FileShare share, FileOptions options)
at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize, FileOptions options)
at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize)
at System.Xml.XmlDownloadManager.GetStream(Uri uri, ICredentials credentials, IWebProxy proxy, RequestCachePolicy cachePolicy)
at System.Xml.XmlUrlResolver.GetEntity(Uri absoluteUri, String role, Type ofObjectToReturn)
at System.Xml.XmlTextReaderImpl.FinishInitUriString()
at System.Xml.XmlTextReaderImpl..ctor(String uriStr, XmlReaderSettings settings, XmlParserContext context, XmlResolver uriResolver)
at System.Xml.XmlReaderSettings.CreateReader(String inputUri, XmlParserContext inputContext)
at System.Xml.XmlReader.Create(String inputUri, XmlReaderSettings settings)
at Microsoft.TeamFoundation.TestClient.PublishTestResults.NUnitResultParser.ParseTestResultFile(TestRunContext runContext, String filePath)
at Microsoft.TeamFoundation.TestClient.PublishTestResults.NUnitResultParser.<>c__DisplayClass1_0.<ParseTestResultFiles>b__0(String file)
at System.Linq.Enumerable.SelectListIterator`2.MoveNext()
at System.Linq.Enumerable.WhereEnumerableIterator`1.ToList()
at Microsoft.TeamFoundation.TestClient.PublishTestResults.NUnitResultParser.ParseTestResultFiles(TestRunContext runContext, IList`1 resultFilePaths)
at Microsoft.VisualStudio.Services.Agent.Worker.TestResults.Parser.ParseFiles(IExecutionContext executionContext, TestRunContext testRunContext, List`1 testResultsFiles, ITestResultParser testResultParser)
##[warning]Coverage XML was not created, skipping upload.
------------ generated Nunit xml file: /usr/src/app/test-output.xml ------------
--------- generated xml file: /usr/src/app/tests/junit/test-output.xml ---------
============================= 13 passed in 10.16s ==============================
Async Command Start: Publish test results
Async Command End: Publish test results
Finishing: Run Python Tests
If you notice the XML file - /usr/src/app/test-output.xml is created later but the exception is raised before it was generated.
Update
The docker cp solution will get your coverage published in the pipeline but you won't be able to see the line-wise coverage in the UI. Here is what you get when you click on the module:
References:
https://github.com/Azure/pytest-azurepipelines#running-in-docker
You could use the docker cp command to copy the .xml file out like && docker cp mycontainer:/coverage.xml ~/Development/coverage.xml
Another option is to use a volume, so something like docker run -it -v ~/Development:/output testimage python -m pytest tests -v --cov=app --cov-report xml:/output/coverage.xml
=========================================================
Updated on 11/30
I suppose that you need to generate your test result report into the format that azure devops pipeline could recognize first.
Check this doc for generate python test report in to junit.xml
- script: |
pip install pytest pytest-azurepipelines
pip install pytest-cov
pytest --doctest-modules --junitxml=junit/test-results.xml --cov=. --cov-report=xml
displayName: 'pytest'
And then add the publish test result task.
- task: PublishTestResults#2
condition: succeededOrFailed()
inputs:
testResultsFormat: 'JUnit'
testResultsFiles: '**/test-*.xml'
testRunTitle: 'Publish test results for Python $(python.version)'

Windows Docker IIS App Pool not able to read certificates from local cert store

I've asp.net MVC website hosted within docker container. The site needs to read the certificates stored on cert:\currentuser\my and present it to the Azure AD for app authentication.
I've loaded the pfx certs on to docker as part of image build per below:
# Install cert, located at certs folder in the host machine, relative to the path of the Solution Dockerfile
SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"]
WORKDIR C:\certs
COPY ./certs .\
RUN Get-ChildItem -File | Foreach { Import-PfxCertificate -Password (ConvertTo-SecureString -String "xyz1234" -AsPlainText -Force) -CertStoreLocation Cert:\CurrentUser\My -FilePath $_.fullname }
Then have this simple test aspx to read the cert by thumprint:
public X509Certificate2 FindCertificateByThumbprint(string findValue, bool validateCertificate)
{
X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
try
{
store.Open(OpenFlags.ReadOnly);
X509Certificate2Collection col = store.Certificates.Find(X509FindType.FindByThumbprint,
findValue, validateCertificate);
if (col == null || col.Count == 0)
return null;
return col[0];
}
finally
{
store.Close();
}
}
Note:
On non docker (local laptop), This works perfectly OK for web.
On docker container, console app can find the certificate but not web app.
I tried all these but no luck:
https://newbedev.com/how-to-grant-permission-to-user-on-certificate-private-key-using-powershell
How to Grant permission to user on Certificate private key using powershell?
https://www.codyhosterman.com/2019/06/assigning-read-access-to-windows-private-key/
Feels like I'm missing a step here but not sure what. Has anyone got

Error running PowerCLI from Docker image on Gitlab CI/CD

I have a Gitlab CI/CD setup that pulls the vmware/powerclicore image from Dockerhub and runs a ps1 script. This script runs correctly on a Windows host, or interactively from the Docker image, but fails if ran through the Gitlab CI/CD with the error:
Set-NetworkAdapter : 02/04/2020 20:28:38 Set-NetworkAdapter Length cannot be less than zero.
Parameter name: length
The gitlab-ci.yml is:
veeam-map-rep-networks:
image:
name: vmware/powerclicore
stage: deploy
script:
- pwsh powershell/ps_dr_replication_remapping.ps1 -pass $vCenterPassword
ps_dr_replication_remapping.ps1 is:
# Define passed in variables
param (
[Parameter(Mandatory=$True,Position=1)]
[string]$pass
)
Set-PowerCLIConfiguration -InvalidCertificateAction Ignore -Scope AllUsers -Confirm:$false
Set-PowerCLIConfiguration -ParticipateInCeip $false -Confirm:$false
$ConfirmPreference = "None"
Connect-VIServer -Server server.example.com -User administrator#example.com -Password $pass -Force
Write-Host (Get-VM -Name "REDACTED-VM-Name")
Write-Host (Get-VM -Name "REDACTED-VM-Name" | Get-NetworkAdapter -Name "Network adapter 1")
Get-VM -Name "REDACTED-VM-Name" | Get-NetworkAdapter -Name "Network adapter 1" | Set-NetworkAdapter -NetworkName "Target Network"
Write-Host ($error[0].Exception | select *)
Write-Host ($error[0].Exception.InnerException | select *)
Output of script in Gitlab CI/CD Pipeline:
Running with gitlab-runner 12.7.0 (58272c27)
on REDACTED
Using Docker executor with image vmware/powerclicore ...
00:00
Pulling docker image vmware/powerclicore ...
Using docker image sha256:ad74fa86c83bc47805e3db4b40b7baeb847d1c1924f7bb99d9cbdcecd7a55a6a for vmware/powerclicore ...
Running on runner-REDACTED via REDACTED...
00:02
Fetching changes with git depth set to 50...
00:01
Reinitialized existing Git repository in REDACTED
* [new ref] refs/pipelines/114987846 -> refs/pipelines/114987846
104dcbf..5978962 REDACTED
Skipping Git submodules setup
$ TZ=":US/Eastern" date
00:07
Tue Feb 4 20:28:31 2020
$ pwsh powershell/ps_dr_replication_remapping.ps1 -pass $pass
WARNING: Please consider joining the VMware Customer Experience Improvement Program, so you can help us make PowerCLI a better product. You can join using the following command:
Set-PowerCLIConfiguration -Scope User -ParticipateInCEIP $true
VMware's Customer Experience Improvement Program ("CEIP") provides VMware with information that enables VMware to improve its products and services, to fix problems, and to advise you on how best to deploy and use our products. As part of the CEIP, VMware collects technical information about your organizations use of VMware products and services on a regular basis in association with your organizations VMware license key(s). This information does not personally identify any individual.
For more details: type "help about_ceip" to see the related help article.
To disable this warning and set your preference use the following command and restart PowerShell:
Set-PowerCLIConfiguration -Scope User -ParticipateInCEIP $true or $false.
WARNING: The cmdlet "Get-PowerCLIVersion" is deprecated. Please use the 'Get-Module' cmdlet instead.
VMware PowerCLI 11.5.0 build 14912921
REDACTED-VM-Name <--This confirms connection to vCenter and VM
Network adapter 1 <--This confirms I can see the portgroup
Set-NetworkAdapter : 02/04/2020 20:28:38 Set-NetworkAdapter Length cannot be less than zero.
Parameter name: length
At REDACTED/powershell/ps_dr_replication_remapping.ps1:32 char:87
+ ... etwork adapter 1" | Set-NetworkAdapter -NetworkName "Target Network"
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Set-NetworkAdapter], VimException
+ FullyQualifiedErrorId : Core_BaseCmdlet_UnknownError,VMware.VimAutomation.ViCore.Cmdlets.Commands.VirtualDevice.SetNetworkAdapter
#{ErrorId=Core_BaseCmdlet_UnknownError; ErrorCategory=NotSpecified; TargetObject=; RecommendedAction=Error occured while executing cmdlet: Set-NetworkAdapter. Check inner exception for more details.; SessionId=; ConnectionId=; Severity=Error; Message=02/04/2020 20:28:38 Set-NetworkAdapter Length cannot be less than zero.
Parameter name: length ; Data=System.Collections.ListDictionaryInternal; InnerException=System.ArgumentOutOfRangeException: Length cannot be less than zero.
Parameter name: length
at System.String.Substring(Int32 startIndex, Int32 length)
at System.Management.Automation.Internal.StringUtil.TruncateToBufferCellWidth(PSHostRawUserInterface rawUI, String toTruncate, Int32 maxWidthInBufferCells) in /usr/src/photon/BUILD/PowerShell-6.2.3/src/System.Management.Automation/utils/StringUtil.cs:line 46
at Microsoft.PowerShell.ProgressNode.RenderFullDescription(String description, String indent, Int32 maxWidth, PSHostRawUserInterface rawUi, ArrayList strCollection, Boolean isFullPlus) in /usr/src/photon/BUILD/PowerShell-6.2.3/src/Microsoft.PowerShell.ConsoleHost/host/msh/ProgressNode.cs:line 214
at Microsoft.PowerShell.ProgressNode.LinesRequiredInFullStyleMethod(PSHostRawUserInterface rawUi, Int32 maxWidth, Boolean isFullPlus) in /usr/src/photon/BUILD/PowerShell-6.2.3/src/Microsoft.PowerShell.ConsoleHost/host/msh/ProgressNode.cs:line 441
at Microsoft.PowerShell.ProgressNode.LinesRequiredMethod(PSHostRawUserInterface rawUi, Int32 maxWidth) in /usr/src/photon/BUILD/PowerShell-6.2.3/src/Microsoft.PowerShell.ConsoleHost/host/msh/ProgressNode.cs:line 398
at Microsoft.PowerShell.PendingProgress.HeightTallyer.Visit(ProgressNode node, ArrayList unused, Int32 unusedToo) in /usr/src/photon/BUILD/PowerShell-6.2.3/src/Microsoft.PowerShell.ConsoleHost/host/msh/PendingProgress.cs:line 678
at Microsoft.PowerShell.PendingProgress.NodeVisitor.VisitNodes(ArrayList nodes, NodeVisitor v) in /usr/src/photon/BUILD/PowerShell-6.2.3/src/Microsoft.PowerShell.ConsoleHost/host/msh/PendingProgress.cs:line 1005
at Microsoft.PowerShell.PendingProgress.TallyHeight(PSHostRawUserInterface rawUi, Int32 maxHeight, Int32 maxWidth) in /usr/src/photon/BUILD/PowerShell-6.2.3/src/Microsoft.PowerShell.ConsoleHost/host/msh/PendingProgress.cs:line 715
at Microsoft.PowerShell.PendingProgress.Render(Int32 maxWidth, Int32 maxHeight, PSHostRawUserInterface rawUI) in /usr/src/photon/BUILD/PowerShell-6.2.3/src/Microsoft.PowerShell.ConsoleHost/host/msh/PendingProgress.cs:line 581
at Microsoft.PowerShell.ProgressPane.Show(PendingProgress pendingProgress) in /usr/src/photon/BUILD/PowerShell-6.2.3/src/Microsoft.PowerShell.ConsoleHost/host/msh/ProgressPane.cs:line 182
at Microsoft.PowerShell.ConsoleHostUserInterface.HandleIncomingProgressRecord(Int64 sourceId, ProgressRecord record) in /usr/src/photon/BUILD/PowerShell-6.2.3/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterfaceProgress.cs:line 96
at Microsoft.PowerShell.ConsoleHostUserInterface.WriteProgress(Int64 sourceId, ProgressRecord record) in /usr/src/photon/BUILD/PowerShell-6.2.3/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterface.cs:line 1219
at System.Management.Automation.Internal.Host.InternalHostUserInterface.WriteProgress(Int64 sourceId, ProgressRecord record) in /usr/src/photon/BUILD/PowerShell-6.2.3/src/System.Management.Automation/engine/hostifaces/InternalHostUserInterface.cs:line 562
at System.Management.Automation.MshCommandRuntime.WriteProgress(Int64 sourceId, ProgressRecord progressRecord, Boolean overrideInquire) in /usr/src/photon/BUILD/PowerShell-6.2.3/src/System.Management.Automation/engine/MshCommandRuntime.cs:line 414
at System.Management.Automation.MshCommandRuntime.WriteProgress(ProgressRecord progressRecord, Boolean overrideInquire) in /usr/src/photon/BUILD/PowerShell-6.2.3/src/System.Management.Automation/engine/MshCommandRuntime.cs:line 355
at System.Management.Automation.MshCommandRuntime.WriteProgress(ProgressRecord progressRecord) in /usr/src/photon/BUILD/PowerShell-6.2.3/src/System.Management.Automation/engine/MshCommandRuntime.cs:line 325
at System.Management.Automation.Cmdlet.WriteProgress(ProgressRecord progressRecord) in /usr/src/photon/BUILD/PowerShell-6.2.3/src/System.Management.Automation/engine/cmdlet.cs:line 563
at VMware.VimAutomation.Sdk.Util10Ps.BaseCmdlet.BaseCmdlet.ProgressCallback(Task task, Object result, Boolean writeResultToScreen)
at VMware.VimAutomation.Sdk.Util10Ps.BaseCmdlet.BaseCmdlet.EndProcessingErrorHandled()
at VMware.VimAutomation.ViCore.Util10Ps.BaseCmdlet.BaseCmdlet.EndProcessingErrorHandled(); TargetSite=Void ThrowTerminatingError(System.Management.Automation.ErrorRecord); StackTrace= at System.Management.Automation.MshCommandRuntime.ThrowTerminatingError(ErrorRecord errorRecord) in /usr/src/photon/BUILD/PowerShell-6.2.3/src/System.Management.Automation/engine/MshCommandRuntime.cs:line 2055; HelpLink=; Source=System.Management.Automation; HResult=-2146232832}
#{Message=Length cannot be less than zero.
Parameter name: length; ActualValue=; ParamName=length; Data=System.Collections.ListDictionaryInternal; InnerException=; TargetSite=System.String Substring(Int32, Int32); StackTrace= at System.String.Substring(Int32 startIndex, Int32 length)
at System.Management.Automation.Internal.StringUtil.TruncateToBufferCellWidth(PSHostRawUserInterface rawUI, String toTruncate, Int32 maxWidthInBufferCells) in /usr/src/photon/BUILD/PowerShell-6.2.3/src/System.Management.Automation/utils/StringUtil.cs:line 46
at Microsoft.PowerShell.ProgressNode.RenderFullDescription(String description, String indent, Int32 maxWidth, PSHostRawUserInterface rawUi, ArrayList strCollection, Boolean isFullPlus) in /usr/src/photon/BUILD/PowerShell-6.2.3/src/Microsoft.PowerShell.ConsoleHost/host/msh/ProgressNode.cs:line 214
at Microsoft.PowerShell.ProgressNode.LinesRequiredInFullStyleMethod(PSHostRawUserInterface rawUi, Int32 maxWidth, Boolean isFullPlus) in /usr/src/photon/BUILD/PowerShell-6.2.3/src/Microsoft.PowerShell.ConsoleHost/host/msh/ProgressNode.cs:line 441
at Microsoft.PowerShell.ProgressNode.LinesRequiredMethod(PSHostRawUserInterface rawUi, Int32 maxWidth) in /usr/src/photon/BUILD/PowerShell-6.2.3/src/Microsoft.PowerShell.ConsoleHost/host/msh/ProgressNode.cs:line 398
at Microsoft.PowerShell.PendingProgress.HeightTallyer.Visit(ProgressNode node, ArrayList unused, Int32 unusedToo) in /usr/src/photon/BUILD/PowerShell-6.2.3/src/Microsoft.PowerShell.ConsoleHost/host/msh/PendingProgress.cs:line 678
at Microsoft.PowerShell.PendingProgress.NodeVisitor.VisitNodes(ArrayList nodes, NodeVisitor v) in /usr/src/photon/BUILD/PowerShell-6.2.3/src/Microsoft.PowerShell.ConsoleHost/host/msh/PendingProgress.cs:line 1005
at Microsoft.PowerShell.PendingProgress.TallyHeight(PSHostRawUserInterface rawUi, Int32 maxHeight, Int32 maxWidth) in /usr/src/photon/BUILD/PowerShell-6.2.3/src/Microsoft.PowerShell.ConsoleHost/host/msh/PendingProgress.cs:line 715
at Microsoft.PowerShell.PendingProgress.Render(Int32 maxWidth, Int32 maxHeight, PSHostRawUserInterface rawUI) in /usr/src/photon/BUILD/PowerShell-6.2.3/src/Microsoft.PowerShell.ConsoleHost/host/msh/PendingProgress.cs:line 581
at Microsoft.PowerShell.ProgressPane.Show(PendingProgress pendingProgress) in /usr/src/photon/BUILD/PowerShell-6.2.3/src/Microsoft.PowerShell.ConsoleHost/host/msh/ProgressPane.cs:line 182
at Microsoft.PowerShell.ConsoleHostUserInterface.HandleIncomingProgressRecord(Int64 sourceId, ProgressRecord record) in /usr/src/photon/BUILD/PowerShell-6.2.3/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterfaceProgress.cs:line 96
at Microsoft.PowerShell.ConsoleHostUserInterface.WriteProgress(Int64 sourceId, ProgressRecord record) in /usr/src/photon/BUILD/PowerShell-6.2.3/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterface.cs:line 1219
at System.Management.Automation.Internal.Host.InternalHostUserInterface.WriteProgress(Int64 sourceId, ProgressRecord record) in /usr/src/photon/BUILD/PowerShell-6.2.3/src/System.Management.Automation/engine/hostifaces/InternalHostUserInterface.cs:line 562
at System.Management.Automation.MshCommandRuntime.WriteProgress(Int64 sourceId, ProgressRecord progressRecord, Boolean overrideInquire) in /usr/src/photon/BUILD/PowerShell-6.2.3/src/System.Management.Automation/engine/MshCommandRuntime.cs:line 414
at System.Management.Automation.MshCommandRuntime.WriteProgress(ProgressRecord progressRecord, Boolean overrideInquire) in /usr/src/photon/BUILD/PowerShell-6.2.3/src/System.Management.Automation/engine/MshCommandRuntime.cs:line 355
at System.Management.Automation.MshCommandRuntime.WriteProgress(ProgressRecord progressRecord) in /usr/src/photon/BUILD/PowerShell-6.2.3/src/System.Management.Automation/engine/MshCommandRuntime.cs:line 325
at System.Management.Automation.Cmdlet.WriteProgress(ProgressRecord progressRecord) in /usr/src/photon/BUILD/PowerShell-6.2.3/src/System.Management.Automation/engine/cmdlet.cs:line 563
at VMware.VimAutomation.Sdk.Util10Ps.BaseCmdlet.BaseCmdlet.ProgressCallback(Task task, Object result, Boolean writeResultToScreen)
at VMware.VimAutomation.Sdk.Util10Ps.BaseCmdlet.BaseCmdlet.EndProcessingErrorHandled()
at VMware.VimAutomation.ViCore.Util10Ps.BaseCmdlet.BaseCmdlet.EndProcessingErrorHandled(); HelpLink=; Source=System.Private.CoreLib; HResult=-2146233086}
Running after script...
00:02
$ TZ=":US/Eastern" date
Tue Feb 4 20:28:39 2020
Job succeeded
Why would Gitlab's runner cause this to fail on the same Docker image I can manually make it run as? The Gitlab output confirms I'm connecting to vCenter and can see the VM's and Portgroups
This issue is caused by the PowerShell progress bar on Photon OS based image. In order to avoid it set
$ProgressPreference = 'SilentlyContinue'
on top of the script.
We updated the powerclicore image with a fix. The progress bar is disabled by default to avoid this exception.
The images with the workaround are vmware/powercli:latestand vmware/powercli:12.2.1
I had similar issue. It looked like a bug in the vmware/powerclicore docker image.
So I used the docker image: mcr.microsoft.com/powershell instead of vmware/powerclicore and ran the following commands to install powercli module to its powershell instance. All other powerCLI commands should follow after them.
Set-PSRepository -Name PSGallery -InstallationPolicy Trusted
Install-Module VMware.PowerCLI -Confirm:$false
Had a similar issue using the powerclicore container. I found I was able to workaround the issue by passing the object directly into the set command rather than through the pipeline.
Try this instead:
$netadapter = Get-VM -Name "REDACTED-VM-Name" | Get-NetworkAdapter -Name "Network adapter 1"
Set-NetworkAdapter -NetworkAdapater $netadapter -NetworkName "Target Network"

How to debug Oracle 11g packages/procedures in docker with SQL Developer?

I'm running an Oracle 11g image (https://hub.docker.com/r/oracleinanutshell/oracle-xe-11g) on a docker container.
I'm creating the container with the debug option as explained:
docker run --name oracle-xe-11g -idt -p 1521:1521 -p 49161:8080 -e ORACLE_ALLOW_REMOTE=true oracleinanutshell/oracle-xe-11g /bin/bash
After that I logged in the container as sudo, configured the listener.ora with the correct hostname, everything following this guide (it's in pt-Br, but the commands are easy to understand)
http://loredata.com.br/2017/08/31/rodando-o-oracle-no-docker/
I can connect with SQL Developer and with my main application running in a Wildfly server, but for support purposes I need to debug some package and stored procedures.
I compiled all my packages and procedures to allow debugging, gave the debug permissions to the user, but when I try to debug a procedure in a package using the SQL Developer default debug options I get the following error:
Conectando ao banco de dados SFW_DOCKER.
Executando PL/SQL: ALTER SESSION SET PLSQL_DEBUG=TRUE
Executando PL/SQL: CALL DBMS_DEBUG_JDWP.CONNECT_TCP( '127.0.0.1', '20587' )
ORA-30683: falha ao estabelecer conexão com o depurador
ORA-12541: TNS:não há listener
ORA-06512: em "SYS.DBMS_DEBUG_JDWP", line 68
ORA-06512: em line 1
Processo encerrado.
Desconectando do banco de dados SFW_DOCKER.
It says there's no listener, but I'm sure everything is running fine.
I also tried to run in ports 4000-4999 exposing them in the create container command and forcing SQL Developer to use them, but I get the same error.
Anyone can help me with this question?
To solve try:
Use IPv4 from your local machine
Set 'Debugging Port Range' from 4000 to 4000
Check the option 'Prompt for Debugger Host for Database Debugging'
SQL Developer -> Tools -> Preferences -> Debugger
Debugger configuration
I solved it by setting DatabaseDebuggerDisableJDWP=true in ide.properties. On linux this can be done with this:
find ~/.sqldeveloper/ -name ide.properties -type f -exec sh -c "echo 'DatabaseDebuggerDisableJDWP=true' >> {}" \;

How to execute a CMD file in remote computer

I am looking to execute a command in remote machine using invoke but the .cmd file will call for additional .vbs script. So i guess i may have to mention CScript if so how do i mention both cmd/c and cscript in the below command
Invoke-Command -computername blrscrv01 -ScriptBlock { param($path, $command ) cmd /c $path $command } -args '"C:\windows\system32\cscript.exe"','"/?"'
Your example worked for me when I removed the extra level of quoting.
Invoke-Command -computername blrscrv01 -ScriptBlock { param($path, $command ) cmd /c $path $command } -args 'C:\windows\system32\cscript.exe','/?'
Troubleshooting
Enter a remote session and poke around.
Enter-PSSession -computername blrscrv01
Verify that the target script exists and is accessible.
dir \\lcsap027\deploy\c2.cmd
dir \\lcsap027\deploy
type \\lcsap027\deploy\c2.cmd
Attempt to run the script interactively.
\\lcsap027\deploy\c2.cmd
or
cmd /c \\lcsap027\deploy\c2.cmd
Alternative
Another thing you might try is not invoking a cmd script remotely, but issuing the commands remotely. New-PSSession will return a handle you can use to deal interactively with the remote machine. You can repeatedly issue commands with Invoke-Command and get the results (as primitive data types and generic objects, though, not the actual objects themselves).
Altered Script
Here's an altered version of the script you put in your comment. I've removed the nested Invoke-Command (I don't know why it was necessary, you're already running commands on the remote machine). Since the line breaks got lost in the comment, I don't know if there were any statement separator problems (I'll just assume there weren't, though in its "formatted" form as a one-liner, it would have died horribly because PoSH wouldn't have known where one statement ended and the next began).
param(
[string]$ComputerName,
[string]$User,
[string]$pass
)
Get-PSSEssion | Remove-PSSession
$session = New-PSSession -ComputerName $ComputerName
Invoke-Command -Session $session -ScriptBlock {
param(
[string]$ComputerName,
[string]$Username,
[string]$Password
)
$net = new-object -ComObject WScript.Network
$net.MapNetworkDrive("x:", "\\machinename\sharename", $false, $Username, $Password)
cmd.exe /c "x:\c2.cmd"
$net.RemoveNetworkDrive("x:")
} -args $ComputerName, $User, $pass
This at least got the remote script to run and produced the expected output. (I emitted the computer name, user name, and file location.)
Bear in mind that this method doesn't employ any transport-/application-layer encryption, so the password is sent cleartext over the network.

Resources