Use Win32 API atomic replace in `replace_file`

The changes to enable Windows support in `replace_file` were not actually
complete, and it didn't work when the file didn't exist - because of
limitations of the emulation done on our side, rather than anything else.

Windows has a bunch of quirks, and Ruby doesn't actually abstract over the
underlying platform a great deal.  We can use the Windows API ReplaceFile, and
MoveFileEx, to achieve the desired behaviour though.

This adds even more conditional code inside the `replace_file` method to
handle multiple platforms - but it really isn't very clean.  Better to get
this working now, then refactor, though.

Signed-off-by: Daniel Pittman <daniel@puppetlabs.com>
Make `binwrite` emulation work on Windows.

So, it turns out that Ruby on Windows is pretty damn awesome, and that to
successfully sysopen a file, then use it as an IO, you need to write this:

    IO.new(IO::sysopen(filename, mode), mode) {|io| ... }

...yes, indeed, you need to repeat the mode, and pass the sysopen mode - which
is completely incompatible with the normal open.  It works when you pass a
file descriptor from sysopen, though, and gets Ruby convinced to use it right.

Signed-off-by: Daniel Pittman <daniel@puppetlabs.com>
Fix Rails schema setup on Windows.

There were several problems with the StoreConfig Rails schema building code on
Windows, starting with the use of `/dev/null` to discard migration output.

Unfortunately, this was hard to debug - it broke tests silently because of the
hacking on $stdout, and didn't report anything about failures.

Now, instead of discarding the output we capture it in memory, and report any
exception we encounter along with the complete textual output at the time of
failure.

This improves the ability to debug errors in this code, along with making it
work transparently on any platform.

Signed-off-by: Daniel Pittman <daniel@puppetlabs.com>
Forcibly remove temporary files in tests.

We have some test helpers, and they allow us to create scratch files and
directories that will be politely destroyed at the end of the run.

We also have some tests that result in permissions on files being changed; we
still want those destroyed, and turning on "force" mode for the delete
operation gives us what we need with no real cost - we have plenty of other
safety factors in place.

Signed-off-by: Daniel Pittman <daniel@puppetlabs.com>
Recover better when `maxgroups` is not implemented.

Puppet tries to bump up the maximum number of supplementary groups for the
process, which works only on Unix-like platform, in C-like implementations of
Ruby.

Previously there was a blacklist of platforms where this didn't work, which
means that as new platforms come along we failed in new and exciting ways.

Instead we can just rescue any failure from invoking the method and carry on,
which works for any failure mode including "not implemented" nicely.

Signed-off-by: Daniel Pittman <daniel@puppetlabs.com>
Store Rails logs on physical disk when testing.

On Unix, we stored the Rails log from our StoreConfigs scratch database in
`/dev/null`, which is reasonable enough - we don't generally care for them,
and that works.

On Windows this is harder because their NUL device is magic, and it is hard to
configure the Rails logs to actually go there.  The system loves to interpret
it as a random file relative to the current directory and do the wrong thing
in various test scenarios.

Ultimately, it doesn't matter if we write the logs to physical disk, and we
already had a scratch directory for the physical database - so we can throw
the logs there too.

Just another reason to use a tmpfs to back /tmp, I guess.

Signed-off-by: Daniel Pittman <daniel@puppetlabs.com>
Make a path absolute on Windows as well as Unix.

When this test was changed the path became relative on Windows, because it
doesn't have a drive letter.  This fixes that with `make_absolute`.

Signed-off-by: Daniel Pittman <daniel@puppetlabs.com>
Accept paths with `~` for `generate` on Windows.

The `generate` function has a fairly strict definition of what is and isn't
legal as a command name, in an effort to keep it reasonably sanitary.

On Windows this included `.`, `-`, and any alpha-numeric characters, but not
the `~` character.  This blew up when faced with an "8.3" generated path -
something that we legitimately should accept since it can reduce problems
around spaces in path names.

This changes generate to accept that, and adds a specific test to verify that
it continues to work as expected.

Signed-off-by: Daniel Pittman <daniel@puppetlabs.com>
Make the base service provider spec work on Windows.

The base service provider pushes the status command into an array, and Windows
has a little heartburn about that vs quoting.  While it isn't clear who is
right, it is easy to just use an array when creating the test service and have
things work.

Signed-off-by: Daniel Pittman <daniel@puppetlabs.com>
Use platform path separator in tests.

When we test path modification, we should use the platform path separator.
Makes the test pass on Windows.

Signed-off-by: Daniel Pittman <daniel@puppetlabs.com>
Use a platform temporary file instead of a Unix one.

The log test used a Unix path, which didn't work on Windows.

Signed-off-by: Daniel Pittman <daniel@puppetlabs.com>
Manually load Win32{API,OLE} for Ruby 1.9.

In Ruby 1.8.7 on Windows the Win32API and Win32OLE objects were automatically
loaded; in Ruby 1.9 there is a general move to get rid of them, and presumably
one step along the path is that they no longer automatically load.

Explicitly loading them along with other gems we depend on helps solve that
problem for us automatically.

Signed-off-by: Daniel Pittman <daniel@puppetlabs.com>
Use conditional pending to block out "fails_on_windows" tests.

A whole bunch of tests scattered through the system fail on Windows, around
features that are not supported on that platform.  (They are things that only
the master does, which an agent-only platform doesn't need to support.)

These were tagged `fails_on_windows` to allow filtering them from rspec runs,
which is great, but doesn't actually communicate nearly as much useful
information as it would if we used the "conditionally pending" facilities that
rspec has supported since 2.3.

That gives us two key things: one, it works automatically based on our
knowledge of the platform, which means you can't forget to turn off failing
tests.

Two, it means that if the test starts unexpectedly passing we also get a
failure, since we should respond to "works when it shouldn't" as seriously as
"fails when it shouldn't".

Signed-off-by: Daniel Pittman <daniel@puppetlabs.com>