Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix windows default paths #326

Merged
merged 1 commit into from
Dec 5, 2018
Merged

Conversation

florindragos
Copy link
Contributor

read windows system paths from custom facts

@davejrt
Copy link
Contributor

davejrt commented Sep 10, 2018

@glennsarti would be quite keen to get your input here

@davejrt davejrt requested a review from glennsarti September 10, 2018 23:34
Copy link

@glennsarti glennsarti left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A bunch of things to change;

  • Excessive use of PATH changes seems un-needed
  • Using top level scope variables for facts instead of $facts
  • Dangerous path changes using TEMP directories
  • Non-idempotent resources

@@ -55,26 +55,24 @@
$docker_download_command = "if (Invoke-WebRequest ${docker_compose_url} ${proxy_opt} -UseBasicParsing -OutFile \"${docker_compose_location_versioned}\") { exit 0 } else { exit 1}"

exec { 'Enable TLS 1.2 in powershell':

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This whole exec probably isn't required. IIRC this only affects the current process and you may as well just add this to the PS1 template files instead of a resource in of itself. This also will have no affect on older powershell module (1.x)

exec { "Install Docker Compose ${version}":
path => ['c:/Windows/Temp/', 'C:/Program Files/Docker/'],
command => "& ${script_path}",
path => ["${::systemroot}/Temp/", "${::program_files_path}/Docker/"],

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Path is not required here as the template file itself uses absolute paths.

side note - Also "${::systemroot}/Temp/" is not what you want. That is the TEMP environment for the SYSTEM account. If puppet runs under a different account (which is common enough) then the TEMP path is different.

provider => powershell,
creates => $docker_compose_location_versioned,
}

file { $docker_compose_location:

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Can't say I'm a fan of symlinks as it's not Windows-y but I guess it will do.

@@ -55,26 +55,24 @@
$docker_download_command = "if (Invoke-WebRequest ${docker_compose_url} ${proxy_opt} -UseBasicParsing -OutFile \"${docker_compose_location_versioned}\") { exit 0 } else { exit 1}"

exec { 'Enable TLS 1.2 in powershell':
path => ['c:/Windows/Temp/', 'C:/Program Files/Docker/'],
path => ["${::systemroot}/Temp/", "${::program_files_path}/Docker/"],
command => '[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12',
provider => powershell,
creates => $docker_compose_location_versioned,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you keep this resource in, this is not idempotent. this resource can never create the $docker_compose_location_versioned file.

$exec_timeout = 3000
$exec_path = ['c:/Windows/Temp/', 'C:/Program Files/Docker/']
$exec_path = ["${::systemroot}/Temp/", "${::program_files_path}/Docker/"]

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think any of this is required.

  • See above. Systemroot\temp is not what you want.
  • In fact there is no way you want %TEMP% to be first in the executable path. That is a security risk.
  • $docker::params::docker_command should contain the absolute path, not a relative one. As you're using absolute paths everywhere else.

@@ -38,11 +38,11 @@

if $::osfamily == 'windows' {
$update_docker_image_template = 'docker/windows/update_docker_image.ps1.erb'
$update_docker_image_path = 'C:/Windows/Temp/update_docker_image.ps1'
$exec_environment = 'PATH=C:/Program Files/Docker/'
$update_docker_image_path = "${::systemroot}/Temp/update_docker_image.ps1"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See above. Systemroot\temp is not what you want.

$tls_cacert = 'C:/ProgramData/docker/certs.d/ca.pem'
$tls_cert = 'C:/ProgramData/docker/certs.d/server-cert.pem'
$tls_key = 'C:/ProgramData/docker/certs.d/server-key.pem'
$tls_cacert = "${::program_data_path}/docker/certs.d/ca.pem"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is configurable for the Docker daemon. Should this path be parameterised?

@@ -48,9 +48,9 @@
$docker_command = $docker::params::docker_command

if $::osfamily == 'windows' {
$exec_environment = ['PATH=C:/Program Files/Docker/']
$exec_environment = ["PATH=${::program_files_path}/Docker/"]

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@@ -67,3 +67,24 @@ def interfaces
end
end
end

Facter.add(:systemroot) do

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not keen on these fact names. It's very possible they will collide with other modules that need to do same thing.

Perhaps prefix with docker_ or something?

Also it's generally considered better practice for one file per fact. Although I see prior art says otherwise.

manifests/run.pp Outdated
$exec_provider = 'powershell'
$cidfile = "c:/Windows/Temp/${service_prefix}${sanitised_title}.cid"
$cidfile = "${::systemroot}/Temp/${service_prefix}${sanitised_title}.cid"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See above. Systemroot\temp is not what you want.

$exec_timeout = 3000
$exec_path = ['c:/Windows/Temp/', 'C:/Program Files/Docker/']
$exec_path = ["${::systemroot}/Temp/", "${::program_files_path}/Docker/"]

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See above about PATH= and exec_path

(
[string]$DockerImage
)
$docker_cmd = "${Env:ProgramFiles}\docker\docker.exe"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given docker_cmd is part of the params.pp surely we should be using that?

end

context 'with ensure => present and image_tag => nanoserver' do
let(:params) { { 'ensure' => 'present', 'image_tag' => 'nanoserver' } }
it { should contain_exec('& C:/Windows/Temp/update_docker_image.ps1 base:nanoserver') }
it { should contain_exec('& C:/Windows/Temp/update_docker_image.ps1 -DockerImage base:nanoserver') }

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These tests seem very brittle for the resource name. Perhaps a better resource name would make sense?

@florindragos
Copy link
Contributor Author

@glennsarti i've fixed some of the problems.
I'm not sure if we should use full paths for the docker command everywhere. It's the same way on linux and on windows we might run into problems with path separators since the acceptance tests are run using beaker that does ssh into cygwin and then cmd /c
Getting the users temp directory from an the TMP env var was also problematic because of cygwin so I just went with LOCALAPPDATA\Temp

@glennsarti
Copy link

@florindragos So you're running your Windows tests under cygwin?

@florindragos
Copy link
Contributor Author

@glennsarti beaker uses ssh to connect to the hosts running on vmpooler. on windows, cygwin is used for the ssh server

@glennsarti
Copy link

Seems very odd given a lot of other modules use env vars in acceptance. Have you got some debug logs etc. to show the issue?

@florindragos
Copy link
Contributor Author

florindragos commented Sep 25, 2018

I'm seeing really strange behaviour using TMP env var for the custom fact.
Listing the fact on the VM (beaker does cmd.exe /c):

bash-4.3$ cmd.exe /c puppet facts | grep docker_user_temp_path
    "docker_user_temp_path": "C:\\cygwin64\\tmp",

but running the tests:
Warning: Unknown variable: '::docker_user_temp_path'.

even if we got C:\cygwin64\tmp, this is not the value we are expecting...

@glennsarti
Copy link

glennsarti commented Sep 25, 2018

On a real windows machine using PowerShell

Note - Ignore the Get http://%2F%2F.%2Fpipe%2Fdocker_engine/v1.38/version: errors. My docker VM wasn't running at the time.

Using "#{ENV['LOCALAPPDATA']}\\Temp"

C:\Source\tmp\puppetlabs-docker-pr326 [master ↑2 +0 ~1 -0 !]> & bundle exec puppet facts --modulepath .\spec\fixtures\modules\ | sls docker_user_temp_path
Failed to load feature test for cfpropertylist: cannot load such file -- CFPropertyList
error during connect: Get http://%2F%2F.%2Fpipe%2Fdocker_engine/v1.38/version: open //./pipe/docker_engine: The system cannot find the file specified. In the default daemon configuration on Windows, the docker client must be run elevated to connect. This error may also indicate that the docker daemon is not running.
error during connect: Get http://%2F%2F.%2Fpipe%2Fdocker_engine/v1.38/info: open //./pipe/docker_engine: The system cannot find the file specified. In the default daemon configuration on Windows, the docker client must be run elevated to connect. This error may also indicate that the docker daemon is not running.
Warning: Facter: Fact resolution fact='puppet_classes', resolution='<anonymous>' resolved to an invalid value: Expected all to be one of [Integer, Float, TrueClass, FalseClass, NilClass, String, Array, Hash], but was Symbol

    "docker_user_temp_path": "C:\\Users\\glenn.sarti\\AppData\\Local\\Temp",

Using ENV['TMP']

C:\Source\tmp\puppetlabs-docker-pr326 [master ↑2 +0 ~1 -0 !]> & bundle exec puppet facts --modulepath .\spec\fixtures\modules\ | sls docker_user_temp_path
Failed to load feature test for cfpropertylist: cannot load such file -- CFPropertyList
error during connect: Get http://%2F%2F.%2Fpipe%2Fdocker_engine/v1.38/version: open //./pipe/docker_engine: The system cannot find the file specified. In the default daemon configuration on Windows, the docker client must be run elevated to connect. This error may also indicate that the docker daemon is not running.
error during connect: Get http://%2F%2F.%2Fpipe%2Fdocker_engine/v1.38/info: open //./pipe/docker_engine: The system cannot find the file specified. In the default daemon configuration on Windows, the docker client must be run elevated to connect. This error may also indicate that the docker daemon is not running.
Warning: Facter: Fact resolution fact='puppet_classes', resolution='<anonymous>' resolved to an invalid value: Expected all to be one of [Integer, Float, TrueClass, FalseClass, NilClass, String, Array, Hash], but was Symbol

    "docker_user_temp_path": "C:\\Users\\GLENN~1.SAR\\AppData\\Local\\Temp",

Note that these give different values

So given that;

  • You get different behaviour between manual and beaker testing
  • 99.999999% of windows users do not use cygwin SSH shells
  • Works fine on real Windows
  • There is a difference between TMP and LOCALAPPDAT (8.3 filenames), although this is minor

Changing your code because of a flaky test or test environment is bad idea. We know that the correct env var is TMP, and the fact you're working around this is a "bad smell" in the testing framework.

@florindragos
Copy link
Contributor Author

@glennsarti you're right. making assumptions for the temp folder location in the module just so the tests are working is wrong. The temp folder could also be C:\Users\Administrator\AppData\Local\Temp\2
I changed the custom fact to ENV['TEMP'] and explicitly setting the TEMP variable in the spec_helper_acceptance.ro when running the acceptance tests seems to work just fine.

docker_path = "/cygdrive/c/Program Files/Docker"
host.add_env_var('PATH', docker_path)
host.add_env_var('TEMP', 'C:\Users\Administrator\AppData\Local\Temp')

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This still seems very wrong. The host should already have a correctly setup TEMP directory.

Even though the cygwin environment says \tmp, external processes created from within cygwin use C:\cygwin64\tmp

Windows 2012R2 Pooler VM

Administrator@adx7v3xdcx8nwfn ~
$ export
...
declare -x TEMP="/tmp"
declare -x TERM="xterm-256color"
declare -x TMP="/tmp"
...

Administrator@adx7v3xdcx8nwfn ~
$ cmd.exe /c set temp
TEMP=C:\cygwin64\tmp

Administrator@adx7v3xdcx8nwfn ~
$ powershell.exe -Command "Write-Host \$ENV:TEMP"
C:\cygwin64\tmp

Copy link
Contributor Author

@florindragos florindragos Oct 9, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is the way it should work. Even though facter shows the fact pointing to C:\cygwin64\tmp,
when running tests, the fact does not seem to be initialized:
Warning: Unknown variable: '::docker_user_temp_path'
This happens if I don't set the TEMP env var from within the spec_helper_acceptance...
It works fine for the other 2 facts that are being read from the environment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's difficult to understand how overriding a system set env variable is the right way to go, especially since it's being hard coded to a specific user's temp path that may or may not be the correct user for a particular run.

Once this variable is overwritten in this way, it's hard to tell what might be broken down the road. Doesn't it seem like this issue you are seeing might be down to misbehavior on the part of beaker or cygwin, that should be worked around more formally?

Copy link
Contributor Author

@florindragos florindragos Oct 15, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately, I have not found a way to work around this.
After doing some more digging around, it looks like the user profile is not being loaded when beaker runs the commands.
The commands are being run remote through ssh: ssh Administrator@host -i key cmd /c puppet apply /tmp/manifest

Logging in with ssh will load your user profile and give you the TMP env var but running it remotely will not:

interactive:

ssh [email protected] -i .ssh/id_rsa-acceptance
-bash-4.3$ cmd /c set TMP
TMP=C:\cygwin64\tmp

vs non-interactive:

ssh [email protected] -i .ssh/id_rsa-acceptance cmd /c set TMP
Environment variable TMP not defined

Adding the variable in .ssh/environment would probably fix this, but we still need to enter the value manually.

I agree, it's not right to hardcode the username but since we are running on the vmpooler VMs, we can assume it is running under Administrator.

Facter.add(:docker_user_temp_path) do
confine :osfamily => :windows
setcode do
ENV['TEMP']

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the Puppet service this is probably OK. For end users with Unicode characters in their usernames running puppet apply, there's a chance that this corrupts the directory name due to Ruby bugs.

If you must expose the TEMP directory as a fact (which I would generally advise against if you can help it), you'll want to use the built-in Puppet helpers Puppet::Util.get_env - see https://github.com/puppetlabs/puppet/blob/24ead48f617cd3912491fe419ac7b67cda53a320/lib/puppet/util.rb#L40-L52

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the tip. I'll update the code with Puppet::Util.get_env.
Is there a reason why we should not use expose the TEMP directory as a custom fact and is there a better alternative to getting to the temporary directory from within a manifest?

@florindragos florindragos force-pushed the defaults branch 2 times, most recently from a7531ab to 49d485b Compare November 26, 2018 15:28
@davejrt davejrt merged commit 1896eca into puppetlabs:master Dec 5, 2018
@Ramesh7 Ramesh7 added the bugfix label Jul 5, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants