From 1eaebadab833db7c3723703c1ffdf18388d8bccf Mon Sep 17 00:00:00 2001 From: Kumbirai Tanekha Date: Wed, 9 Sep 2020 14:28:13 +0100 Subject: [PATCH 1/4] Add ability to integration tests against Windows environment Integration testing against puppet agents running on Windows is possible and configurable. Export the WINDOWS_DOCKER_HOST envvar to enable the Windows tests. The configurable options are available as envvars WINDOWS_DOCKER_HOST, WINDOWS_DOCKER_CERT_PATH, WINDOWS_DOCKER_TLS_VERIFY, MAIN_HOST_IP. MAIN_HOST_IP is the IP address of the host where the tests are running that should be accessible from containers running in the Windows Docker Daemon. --- .../environments/production/manifests/site.pp | 47 +++-- examples/puppetmaster/test.sh | 179 +++++++++++++++++- .../puppetmaster/windows-agent.Dockerfile | 12 ++ 3 files changed, 222 insertions(+), 16 deletions(-) create mode 100644 examples/puppetmaster/windows-agent.Dockerfile diff --git a/examples/puppetmaster/code/environments/production/manifests/site.pp b/examples/puppetmaster/code/environments/production/manifests/site.pp index 60d883b..4e5d2fe 100644 --- a/examples/puppetmaster/code/environments/production/manifests/site.pp +++ b/examples/puppetmaster/code/environments/production/manifests/site.pp @@ -2,13 +2,18 @@ node default { if $facts['os']['family'] == 'Windows' { - $cred_file_prefix = 'c:/tmp' + # There's a double backslash at the end of $cred_file_prefix because: + # When a backslash occurs at the very end of a single-quoted string, a double + # backslash must be used instead of a single backslash. For example: + # path => 'C:\Program Files(x86)\\' + $cred_file_prefix = 'c:\tmp\\' + $win_cmd_exe = 'C:\Windows\System32\cmd.exe' } else { - $cred_file_prefix = '/tmp' + $cred_file_prefix = '/tmp/' } - $output_file1 = "${cred_file_prefix}/creds1.txt" - $output_file2 = "${cred_file_prefix}/creds2.txt" + $output_file1 = "${cred_file_prefix}creds1.txt" + $output_file2 = "${cred_file_prefix}creds2.txt" # If using server-supplied identity for the agent's Conjur / DAP connection, # you would use the optional parameters to the `conjur::secret` function as @@ -24,28 +29,42 @@ notify { "Writing regular secret to ${output_file1}...": } file { $output_file1: - ensure => file, + ensure => file, content => Sensitive(Deferred(conjur::secret, ['inventory/db-password'])), } notify { "Writing funky secret to ${output_file2}...": } file { $output_file2: - ensure => file, + ensure => file, content => Sensitive(Deferred(conjur::secret, [ 'inventory/funky/special @#$%^&*(){}[].,+/variable' ])), } - exec { "cat ${output_file1}": - path => '/usr/bin:/usr/sbin:/bin', - provider => shell, - logoutput => true, + if $facts['os']['family'] == 'Windows' { + exec { "Read secret from ${output_file1}...": + command => "${win_cmd_exe} /c type ${output_file1}", + logoutput => true, + } + } else { + exec { "cat ${output_file1}": + path => '/usr/bin:/usr/sbin:/bin', + provider => shell, + logoutput => true, + } } - exec { "cat ${output_file2}": - path => '/usr/bin:/usr/sbin:/bin', - provider => shell, - logoutput => true, + if $facts['os']['family'] == 'Windows' { + exec { "Read secret from ${output_file2}...": + command => "${win_cmd_exe} /c type ${output_file2}", + logoutput => true, + } + } else { + exec { "cat ${output_file2}": + path => '/usr/bin:/usr/sbin:/bin', + provider => shell, + logoutput => true, + } } notify { 'Done!': } diff --git a/examples/puppetmaster/test.sh b/examples/puppetmaster/test.sh index 9f66563..b944c33 100755 --- a/examples/puppetmaster/test.sh +++ b/examples/puppetmaster/test.sh @@ -6,6 +6,15 @@ set -euo pipefail source vagrant/utils.sh +# MAIN_HOST_IP is the IP address of the host where the tests are running that should be +# accessible from containers running in the Windows Docker Daemon. +export MAIN_HOST_IP=${MAIN_HOST_IP:-} +# Configuration for the Windows Docker Daemon. If WINDOWS_DOCKER_HOST is not set then the +# Windows tests are skipped. +export WINDOWS_DOCKER_HOST=${WINDOWS_DOCKER_HOST:-} +export WINDOWS_DOCKER_CERT_PATH=${WINDOWS_DOCKER_CERT_PATH:-} +export WINDOWS_DOCKER_TLS_VERIFY=${WINDOWS_DOCKER_TLS_VERIFY:-0} + CLEAN_UP_ON_EXIT=${CLEAN_UP_ON_EXIT:-true} INSTALL_PACKAGED_MODULE=${INSTALL_PACKAGED_MODULE:-true} COMPOSE_PROJECT_NAME=${COMPOSE_PROJECT_NAME:-puppetmaster_$(openssl rand -hex 3)} @@ -31,12 +40,39 @@ cleanup() { docker-compose down -v || true } +build_windows_puppet_image() { + cat ./windows-agent.Dockerfile | run_with_docker_windows docker build -t puppet-agent - +} + +prepare_windows() { + if [[ -z "${WINDOWS_DOCKER_HOST}" ]]; then + echo "---"; + echo "Windows Daemon not available. Skipping tests for 'Windows'"; + + return + fi + + echo "---" + echo "Windows Daemon available. Preparing test environment for 'Windows'" + + if [[ -z "${MAIN_HOST_IP}" ]]; then + echo "MAIN_HOST_IP envvar must be set to accompany WINDOWS_DOCKER_HOST"; + exit 1; + fi + + echo + echo "=> Building puppet agent image for 'Windows' <=" + build_windows_puppet_image +} + main() { cleanup if [[ "${CLEAN_UP_ON_EXIT}" = true ]]; then trap cleanup EXIT fi + prepare_windows + start_services setup_conjur wait_for_puppetmaster @@ -78,10 +114,46 @@ main() { done done + run_windows_tests + echo "===" echo "ALL TESTS COMPLETED" } +run_windows_tests() { + # Run tests on Windows if there's a Windows Daemon + if [[ -z "${WINDOWS_DOCKER_HOST}" ]]; then + echo "---" + + echo "Tests for 'Windows': Skipped" + return; + fi + + echo "---" + echo "Running tests for 'Windows'..." + + echo + echo "=> Agent config, API Key <=" + converge_windows_node_agent_apikey + + echo + echo "=> Hiera manifest config, API Key <=" + converge_windows_node_hiera_manifest_apikey + + echo "Tests for 'Windows': OK" +} + +run_with_docker_windows() { + local DOCKER_HOST=${WINDOWS_DOCKER_HOST} + local DOCKER_CERT_PATH=${WINDOWS_DOCKER_CERT_PATH} + local DOCKER_TLS_VERIFY=${WINDOWS_DOCKER_TLS_VERIFY} + export DOCKER_HOST; + export DOCKER_CERT_PATH; + export DOCKER_TLS_VERIFY; + + "$@" +} + run_in_conjur() { docker-compose exec -T conjur "$@" } @@ -210,7 +282,7 @@ revoke_cert_for() { converge_node_agent_apikey() { local agent_image="$1" local node_name="agent-apikey-node" - local hostname="${node_name}_$(openssl rand -hex 3)" + local hostname="${node_name}-$(openssl rand -hex 3)" local login="host/$node_name" local api_key=$(get_host_key $node_name) @@ -262,7 +334,7 @@ converge_node_agent_apikey() { converge_node_hiera_manifest_apikey() { local agent_image="$1" local node_name="hiera-manifest-apikey-node" - local hostname="${node_name}_$(openssl rand -hex 3)" + local hostname="${node_name}-$(openssl rand -hex 3)" local login="host/$node_name" local api_key=$(get_host_key $node_name) @@ -349,4 +421,107 @@ $ssl_certificate rm -rf "$manifest_config_file" "$hiera_config_file" } +converge_windows_node_agent_apikey() { + local node_name="agent-apikey-node" + local hostname="${node_name}-$(openssl rand -hex 3)" + + local login="host/$node_name" + local api_key=$(get_host_key $node_name) + echo "API key for $node_name: $api_key" + + revoke_cert_for "$hostname" + + set -x + run_with_docker_windows docker run --rm -t \ + --hostname "${hostname}" \ + puppet-agent \ + powershell -Command " + # Allow resolution of the hostnames for conjur and puppet + Add-Content -Path 'c:\Windows\System32\Drivers\etc\hosts' -Value '${MAIN_HOST_IP} conjur.cyberark.com' + Add-Content -Path 'c:\Windows\System32\Drivers\etc\hosts' -Value '${MAIN_HOST_IP} puppet' + Add-Content -Path 'c:\conjur-ca.crt' -Value '$(cat $PWD/https_config/ca.crt)' + + + # Set conjur.conf equivalent with connection details + reg ADD HKLM\Software\CyberArk\Conjur /v ApplianceUrl /t REG_SZ /d https://conjur.cyberark.com:$(conjur_host_port)/ + reg ADD HKLM\Software\CyberArk\Conjur /v Version /t REG_DWORD /d 5 + reg ADD HKLM\Software\CyberArk\Conjur /v Account /t REG_SZ /d cucumber + reg ADD HKLM\Software\CyberArk\Conjur /v CertFile /t REG_SZ /d c:\conjur-ca.crt + + # Set conjur.identity equivalent with auth details + cmdkey /generic:conjur.cyberark.com /user:${login} /pass:${api_key} + + puppet agent --verbose --onetime --no-daemonize --summarize --masterport $(puppet_host_port) --certname \$(hostname) + " + set +x +} + +converge_windows_node_hiera_manifest_apikey() { + local node_name="hiera-manifest-apikey-node" + local hostname="${node_name}-$(openssl rand -hex 3)" + + local login="host/$node_name" + local api_key=$(get_host_key $node_name) + echo "API key for $node_name: $api_key" + + local hiera_config_file="./code/data/nodes/$hostname.yaml" + local manifest_config_file="./code/environments/production/manifests/00_$hostname.pp" + + local ssl_certificate="$(cat https_config/ca.crt | sed 's/^/ /')" + + echo "--- +lookup_options: + '^conjur::authn_api_key': + convert_to: 'Sensitive' + +conjur::account: 'cucumber' +conjur::appliance_url: 'https://conjur.cyberark.com:$(conjur_host_port)' +conjur::authn_login: 'host/$node_name' +conjur::authn_api_key: '$api_key' +conjur::ssl_certificate: | +$ssl_certificate + " > $hiera_config_file + + echo " + node '$hostname' { + \$pem_file = 'c:\tmp\test.pem' + \$secret = Sensitive(Deferred(conjur::secret, ['inventory/db-password', { + appliance_url => lookup('conjur::appliance_url'), + account => lookup('conjur::account'), + authn_login => lookup('conjur::authn_login'), + authn_api_key => lookup('conjur::authn_api_key'), + ssl_certificate => lookup('conjur::ssl_certificate') + }])) + + notify { \"Writing secret to \${pem_file}...\": } + file { \$pem_file: + ensure => file, + content => \$secret, + } + + exec { \"Read secret from \${pem_file}...\": + command => \"C:\Windows\System32\cmd.exe /c type \${pem_file}\", + logoutput => true, + } + }" > $manifest_config_file + + revoke_cert_for "$hostname" + + set -x + run_with_docker_windows \ + docker run --rm -t \ + --hostname "${hostname}" \ + puppet-agent \ + powershell -Command " + # Allow resolution of the hostnames for conjur and puppet + Add-Content -Path 'c:\Windows\System32\Drivers\etc\hosts' -Value '${MAIN_HOST_IP} conjur.cyberark.com' + Add-Content -Path 'c:\Windows\System32\Drivers\etc\hosts' -Value '${MAIN_HOST_IP} puppet' + + puppet agent --verbose --onetime --no-daemonize --summarize --masterport $(puppet_host_port) --certname \$(hostname) + " + set +x + + rm -rf "$manifest_config_file" "$hiera_config_file" +} + main "$@" diff --git a/examples/puppetmaster/windows-agent.Dockerfile b/examples/puppetmaster/windows-agent.Dockerfile new file mode 100644 index 0000000..6bf92ac --- /dev/null +++ b/examples/puppetmaster/windows-agent.Dockerfile @@ -0,0 +1,12 @@ +FROM mcr.microsoft.com/windows/servercore:1607 + +RUN powershell mkdir "c:\tmp" + +RUN powershell \ + wget https://downloads.puppetlabs.com/windows/puppet6/puppet-agent-x64-latest.msi \ + -outfile puppet6.msi + +RUN powershell Start-Process msiexec.exe \ + -Wait \ + -ArgumentList \ + '/qn /quiet /norestart /L*v C:\puppet_install_log.txt /i C:\puppet6.msi PUPPET_AGENT_STARTUP_MODE=Manual' From 50e53be76db2e9f36b455f55c5aecade85f6600d Mon Sep 17 00:00:00 2001 From: Kumbirai Tanekha Date: Wed, 9 Sep 2020 14:32:28 +0100 Subject: [PATCH 2/4] Add Windows node to integration tests in Jenkins Adds a Windows 2016, with containers, node to the pipeline using the parallel stages approach for cooperation between Jenkins nodes. This provides a Windows Docker daemon for the integration tests to use --- Jenkinsfile | 82 ++++++++++++++++++++++--- expose-daemon.ps1 | 149 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 223 insertions(+), 8 deletions(-) create mode 100644 expose-daemon.ps1 diff --git a/Jenkinsfile b/Jenkinsfile index fceb184..3a10b12 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -5,6 +5,7 @@ pipeline { options { timestamps() + parallelsAlwaysFailFast() } triggers { @@ -52,6 +53,79 @@ pipeline { stage('Tests') { parallel { + stage('Prepare Windows 2016 with containers') { + agent { label 'executor-windows-2016-containers' } + stages { + stage('Configure Windows Node'){ + steps { + powershell '.\\expose-daemon.ps1' + script { + env.WINDOWS_IP = powershell(returnStdout: true, script: '(curl http://169.254.169.254/latest/meta-data/local-ipv4).Content').trim() + env.WINDOWS_DOCKER_CERT_CA = powershell(returnStdout: true, script: 'cat $env:USERPROFILE\\.docker\\ca.pem') + env.WINDOWS_DOCKER_CERT_CERT = powershell(returnStdout: true, script: 'cat $env:USERPROFILE\\.docker\\cert.pem') + env.WINDOWS_DOCKER_CERT_KEY = powershell(returnStdout: true, script: 'cat $env:USERPROFILE\\.docker\\key.pem') + env.WINDOWS_READY = true + } + } + } + stage('Wait for Main Node') { + steps { + waitUntil { + script { + return (env.MAIN_NODE_DONE == "true"); + } + } + } + } + } + } + + stage('With Windows 2016 containers') { + stages { + stage("Wait for Windows node") { + steps { + waitUntil { + script { + return (env.WINDOWS_READY == "true"); + } + } + script { + env.WINDOWS_DOCKER_CERT_DIR = "${pwd()}/tmp-docker" + } + + sh "mkdir ${env.WINDOWS_DOCKER_CERT_DIR}" + writeFile file: "${env.WINDOWS_DOCKER_CERT_DIR}/ca.pem", text: env.WINDOWS_DOCKER_CERT_CA + writeFile file: "${env.WINDOWS_DOCKER_CERT_DIR}/cert.pem", text: env.WINDOWS_DOCKER_CERT_CERT + writeFile file: "${env.WINDOWS_DOCKER_CERT_DIR}/key.pem", text: env.WINDOWS_DOCKER_CERT_KEY + } + } + +// Integration tests + stage('E2E - Puppet 6 - Conjur 5') { + steps { + dir('examples/puppetmaster') { + sh ''' + MAIN_HOST_IP="$(curl http://169.254.169.254/latest/meta-data/local-ipv4)" \ + WINDOWS_DOCKER_HOST="tcp://${WINDOWS_IP}:2376" \ + WINDOWS_DOCKER_CERT_PATH="${WINDOWS_DOCKER_CERT_DIR}" \ + WINDOWS_DOCKER_TLS_VERIFY=1 \ + ./test.sh + ''' + } + } + } + } + + post { + always { + script { + env.MAIN_NODE_DONE = true + } + } + } + } + +// Linting and unit tests stage('Linting and unit tests') { steps { sh './test.sh' @@ -69,14 +143,6 @@ pipeline { } } } - - stage('E2E - Puppet 6 - Conjur 5') { - steps { - dir('examples/puppetmaster') { - sh './test.sh' - } - } - } } } diff --git a/expose-daemon.ps1 b/expose-daemon.ps1 new file mode 100644 index 0000000..9fc091c --- /dev/null +++ b/expose-daemon.ps1 @@ -0,0 +1,149 @@ +$ErrorActionPreference = 'Stop'; + +if (!(Test-Path $env:USERPROFILE\.docker)) { + mkdir $env:USERPROFILE\.docker +} + +$ipAddresses = ((Get-NetIPAddress -AddressFamily IPv4).IPAddress) -Join ',' + +function ensureDirs($dirs) { + foreach ($dir in $dirs) { + if (!(Test-Path $dir)) { + mkdir $dir + } + } +} + +# https://docs.docker.com/engine/security/https/ +# Thanks to @artisticcheese! https://artisticcheese.wordpress.com/2017/06/10/using-pure-powershell-to-generate-tls-certificates-for-docker-daemon-running-on-windows/ +function createCA($serverCertsPath) { + Write-Host "`n=== Generating CA" + $parms = @{ + type = "Custom" ; + KeyExportPolicy = "Exportable"; + Subject = "CN=Docker TLS Root"; + CertStoreLocation = "Cert:\LocalMachine\My"; + HashAlgorithm = "sha256"; + KeyLength = 4096; + KeyUsage = @("CertSign", "CRLSign"); + TextExtension = @("2.5.29.19 ={critical} {text}ca=1") + } + $rootCert = New-SelfSignedCertificate @parms + + Write-Host "`n=== Generating CA public key" + $parms = @{ + Path = "$serverCertsPath\ca.pem"; + Value = "-----BEGIN CERTIFICATE-----`n" ` + + [System.Convert]::ToBase64String($rootCert.RawData, [System.Base64FormattingOptions]::InsertLineBreaks) ` + + "`n-----END CERTIFICATE-----"; + Encoding = "ASCII"; + } + Set-Content @parms + return $rootCert +} + +# https://docs.docker.com/engine/security/https/ +function createCerts($rootCert, $serverCertsPath, $serverName, $ipAddresses, $clientCertsPath) { + Write-Host "`n=== Generating Server certificate" + $parms = @{ + CertStoreLocation = "Cert:\LocalMachine\My"; + Signer = $rootCert; + Subject = "CN=serverCert"; + KeyExportPolicy = "Exportable"; + Provider = "Microsoft Enhanced Cryptographic Provider v1.0"; + Type = "SSLServerAuthentication"; + HashAlgorithm = "sha256"; + TextExtension = @("2.5.29.37= {text}1.3.6.1.5.5.7.3.1", "2.5.29.17={text}DNS=$serverName&DNS=localhost&IPAddress=$($ipAddresses.Split(',') -Join '&IPAddress=')"); + KeyLength = 4096; + } + $serverCert = New-SelfSignedCertificate @parms + + $parms = @{ + Path = "$serverCertsPath\server-cert.pem"; + Value = "-----BEGIN CERTIFICATE-----`n" ` + + [System.Convert]::ToBase64String($serverCert.RawData, [System.Base64FormattingOptions]::InsertLineBreaks) ` + + "`n-----END CERTIFICATE-----"; + Encoding = "Ascii" + } + Set-Content @parms + + Write-Host "`n=== Generating Server private key" + $privateKeyFromCert = [System.Security.Cryptography.X509Certificates.RSACertificateExtensions]::GetRSAPrivateKey($serverCert) + $parms = @{ + Path = "$serverCertsPath\server-key.pem"; + Value = ("-----BEGIN PRIVATE KEY-----`n" ` + + [System.Convert]::ToBase64String($privateKeyFromCert.Key.Export([System.Security.Cryptography.CngKeyBlobFormat]::Pkcs8PrivateBlob), [System.Base64FormattingOptions]::InsertLineBreaks) ` + + "`n-----END PRIVATE KEY-----"); + Encoding = "Ascii"; + } + Set-Content @parms + + Write-Host "`n=== Generating Client certificate" + $parms = @{ + CertStoreLocation = "Cert:\LocalMachine\My"; + Subject = "CN=clientCert"; + Signer = $rootCert ; + KeyExportPolicy = "Exportable"; + Provider = "Microsoft Enhanced Cryptographic Provider v1.0"; + TextExtension = @("2.5.29.37= {text}1.3.6.1.5.5.7.3.2") ; + HashAlgorithm = "sha256"; + KeyLength = 4096; + } + $clientCert = New-SelfSignedCertificate @parms + + $parms = @{ + Path = "$clientCertsPath\cert.pem" ; + Value = ("-----BEGIN CERTIFICATE-----`n" + [System.Convert]::ToBase64String($clientCert.RawData, [System.Base64FormattingOptions]::InsertLineBreaks) + "`n-----END CERTIFICATE-----"); + Encoding = "Ascii"; + } + Set-Content @parms + + Write-Host "`n=== Generating Client key" + $clientprivateKeyFromCert = [System.Security.Cryptography.X509Certificates.RSACertificateExtensions]::GetRSAPrivateKey($clientCert) + $parms = @{ + Path = "$clientCertsPath\key.pem"; + Value = ("-----BEGIN PRIVATE KEY-----`n" ` + + [System.Convert]::ToBase64String($clientprivateKeyFromCert.Key.Export([System.Security.Cryptography.CngKeyBlobFormat]::Pkcs8PrivateBlob), [System.Base64FormattingOptions]::InsertLineBreaks) ` + + "`n-----END PRIVATE KEY-----"); + Encoding = "Ascii"; + } + Set-Content @parms + + copy $serverCertsPath\ca.pem $clientCertsPath\ca.pem +} + +function updateConfig($daemonJson, $serverCertsPath) { + $config = @{} + if (Test-Path $daemonJson) { + $config = (Get-Content $daemonJson) -join "`n" | ConvertFrom-Json + } + + $config = $config | Add-Member(@{ ` + hosts = @("tcp://0.0.0.0:2376", "npipe://"); ` + tlsverify = $true; ` + tlscacert = "$serverCertsPath\ca.pem"; ` + tlscert = "$serverCertsPath\server-cert.pem"; ` + tlskey = "$serverCertsPath\server-key.pem" ` + }) -Force -PassThru + + Write-Host "`n=== Creating / Updating $daemonJson" + $config | ConvertTo-Json | Set-Content $daemonJson -Encoding Ascii +} + +$dockerData = "$env:ProgramData\docker" +$userPath = "$env:USERPROFILE\.docker" + +ensureDirs @("$dockerData\certs.d", "$dockerData\config", "$userPath") + +$serverCertsPath = "$dockerData\certs.d" +$clientCertsPath = "$userPath" +$rootCert = createCA "$dockerData\certs.d" + +createCerts $rootCert $serverCertsPath $serverName $ipAddresses $clientCertsPath +updateConfig "$dockerData\config\daemon.json" $serverCertsPath $enableLCOW $experimental + +Write-Host "Restarting Docker" +Restart-Service docker + +Write-Host "Opening Docker TLS port" +& netsh advfirewall firewall add rule name="Docker TLS" dir=in action=allow protocol=TCP localport=2376 From bbcb3b410e8d1603f514da63c1f1589f3edea89c Mon Sep 17 00:00:00 2001 From: Bradley Boutcher Date: Wed, 16 Sep 2020 13:04:28 -0400 Subject: [PATCH 3/4] Clean up Jenkinsfile --- Jenkinsfile | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 3a10b12..0fb8cac 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -29,7 +29,7 @@ pipeline { } } - // workaround for Jenkins not fetching tags + // Workaround for Jenkins not fetching tags stage('Fetch tags') { steps { withCredentials( @@ -50,10 +50,9 @@ pipeline { } } - stage('Tests') { parallel { - stage('Prepare Windows 2016 with containers') { + stage('Setup Win2016') { agent { label 'executor-windows-2016-containers' } stages { stage('Configure Windows Node'){ @@ -80,7 +79,7 @@ pipeline { } } - stage('With Windows 2016 containers') { + stage('Test Win2016') { stages { stage("Wait for Windows node") { steps { @@ -100,8 +99,7 @@ pipeline { } } -// Integration tests - stage('E2E - Puppet 6 - Conjur 5') { + stage('Puppet 6 & Conjur 5 Integration Tests') { steps { dir('examples/puppetmaster') { sh ''' @@ -125,8 +123,7 @@ pipeline { } } -// Linting and unit tests - stage('Linting and unit tests') { + stage('Linting & Unit Tests') { steps { sh './test.sh' } From dda8626961875600da426496b21f60e9748c61d1 Mon Sep 17 00:00:00 2001 From: Hugh Saunders Date: Thu, 17 Sep 2020 09:31:09 +0100 Subject: [PATCH 4/4] Use node instead of agent to avoid auto git checkout. We have had reliability problems with the automatic git checkout performed by the agent step. So instead we use the node step to allocate a node, then fetch the required script via http. Also removes explicit git tag fetch as Jenkins now always fetches tags Related: conjurinc/ops#682 --- Jenkinsfile | 52 +++++++++++++++++++--------------------------------- 1 file changed, 19 insertions(+), 33 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 0fb8cac..1237760 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -29,20 +29,6 @@ pipeline { } } - // Workaround for Jenkins not fetching tags - stage('Fetch tags') { - steps { - withCredentials( - [usernameColonPassword(credentialsId: 'conjur-jenkins-api', variable: 'GITCREDS')] - ) { - sh ''' - git fetch --tags `git remote get-url origin | sed -e "s|https://|https://$GITCREDS@|"` - git tag # just print them out to make sure, can remove when this is robust - ''' - } - } - } - stage('Build') { steps { sh './build.sh' @@ -52,27 +38,27 @@ pipeline { stage('Tests') { parallel { - stage('Setup Win2016') { - agent { label 'executor-windows-2016-containers' } - stages { - stage('Configure Windows Node'){ - steps { + stage('Setup & Hold Win2016 Node') { + steps { + script { + // Node used instead of agent to avoid the automatic git checkout that agent provides. + // This is because git checkout is unreliable on windows agents + node('executor-windows-2016-containers'){ + // because the repo is not auto checked out, fetch the configure script via http + powershell """ + Invoke-WebRequest -Uri "https://raw.githubusercontent.com/cyberark/conjur-puppet/${BRANCH_NAME}/expose-daemon.ps1" -OutFile "expose-daemon.ps1" + """ powershell '.\\expose-daemon.ps1' - script { - env.WINDOWS_IP = powershell(returnStdout: true, script: '(curl http://169.254.169.254/latest/meta-data/local-ipv4).Content').trim() - env.WINDOWS_DOCKER_CERT_CA = powershell(returnStdout: true, script: 'cat $env:USERPROFILE\\.docker\\ca.pem') - env.WINDOWS_DOCKER_CERT_CERT = powershell(returnStdout: true, script: 'cat $env:USERPROFILE\\.docker\\cert.pem') - env.WINDOWS_DOCKER_CERT_KEY = powershell(returnStdout: true, script: 'cat $env:USERPROFILE\\.docker\\key.pem') - env.WINDOWS_READY = true - } - } - } - stage('Wait for Main Node') { - steps { + env.WINDOWS_IP = powershell(returnStdout: true, script: '(curl http://169.254.169.254/latest/meta-data/local-ipv4).Content').trim() + env.WINDOWS_DOCKER_CERT_CA = powershell(returnStdout: true, script: 'cat $env:USERPROFILE\\.docker\\ca.pem') + env.WINDOWS_DOCKER_CERT_CERT = powershell(returnStdout: true, script: 'cat $env:USERPROFILE\\.docker\\cert.pem') + env.WINDOWS_DOCKER_CERT_KEY = powershell(returnStdout: true, script: 'cat $env:USERPROFILE\\.docker\\key.pem') + env.WINDOWS_READY = true + + // The windows node is terminated when the containing node block ends, so we wait until the tests are finished + // before letting this block complete. waitUntil { - script { - return (env.MAIN_NODE_DONE == "true"); - } + return (env.MAIN_NODE_DONE == "true"); } } }