Support GCE Windows.
1. Allow updating cookie files.
2. Error out with warning when --nofork is missing. os.fork() is not supported on Windows.
3. Error out with warning when env var HOMEPATH is not set.
Change-Id: I155774afed8910d5cd38780a47e321fac6651953
diff --git a/README.md b/README.md
index 92af485..9e04de6 100644
--- a/README.md
+++ b/README.md
@@ -17,7 +17,10 @@
...
```
-## Installation
+To add a scope to an existing GCE instance see this
+[gcloud beta feature](https://cloud.google.com/sdk/gcloud/reference/beta/compute/instances/set-scopes).
+
+## Installation on Linux
Install the daemon within the VM image and start it running:
@@ -29,3 +32,63 @@
The daemon launches itself into the background and continues
to keep the OAuth2 access token fresh.
+
+## Installation on Windows
+
+1. Install [Python](https://www.python.org/downloads/windows/) and
+ [Git](https://git-scm.com/download) for Windows.
+1. Run `git-cookie-authdaemon` in the same environment under the same user
+ git commands will be run, for example in either `Command Prompt`
+ or `Cygwin bash shell` under user `builder`.
+```
+python git-cookie-authdaemon --nofork
+```
+
+### Launch at Windows boot
+
+It may be desired in automation to launch `git-cookie-authdaemon` at
+Windows boot. It can be done as a scheduled task. The following is an
+example on a Jenkins node. The setup is:
+
+1. The VM is created from GCE Windows Server 2012R2 image.
+1. Gygwin with SSHD is installed.
+1. The Jenkins master connects to the VM through SSH as `builder` account.
+
+How to create a scheduled task.
+
+1. Launch `Task Scheduler` from an Administrator account.
+1. Click `Create Task` in the right pane.
+1. In `General` tab:
+ 1. Change user to the one running Jenkins node if it is different. You may
+ want to run Jenkins node as a non-privileged user, `builder` in this
+ example.
+ 1. Select `Run whether user is logged on or not`
+1. In `Trigger` tab. Add a trigger
+ 1. Set `Begin the task` as `At startup`.
+ 1. Uncheck `Stop task if it runs longer than`.
+ 1. Check `Enabled`.
+1. In `Actions` tab. Add `Start a program`.
+ 1. Set `Program/script` as `C:\cygwin64\bin\bash.ext`,
+ 1. Set `Add arguments` as
+ `--login -c /home/builder/git-cookie-authdaemon_wrapper.sh` (see note
+ below)
+1. Click `Ok` to save it.
+1. Optional: click `Enable All Tasks History` in `Task Scheduler`'s right pane.
+1. Add `builder` account to `Administrative Tools -> Local Security Policy ->
+ Local Policies -> User Rights Assignment -> Log On As Batch Job`
+
+Note: /home/builder/git-cookie-authdaemon_wrapper.sh` below does
+
+1. Set HOMEPATH if it is not.
+2. Capture git-cookie-autodaemon.log stdout and stderr for debugging.
+
+```
+#!/bin/bash
+exe=gcompute-tools/git-cookie-authdaemon
+log=/cygdrive/c/build/git-cookie-autodaemon.log
+
+# HOMEPATH is not set in task scheduled at machine boot.
+export HOMEPATH=${HOMEPATH:-'\Users\builder'}
+
+/cygdrive/c/Python27/python $exe --nofork >> $log 2>&1 # option --debug is also available.
+```
diff --git a/git-cookie-authdaemon b/git-cookie-authdaemon
index e18b279..536e011 100755
--- a/git-cookie-authdaemon
+++ b/git-cookie-authdaemon
@@ -18,7 +18,8 @@
Tokens are written to ~/.git-credential-cache/cookie
Git config variable http.cookiefile is updated.
-Runs only on Google Compute Engine.
+Runs only on Google Compute Engine (GCE). On GCE Windows '--nofork' option
+is needed. '--debug' option is available.
"""
import atexit
@@ -26,6 +27,7 @@
import cookielib
import json
import os
+import platform
import subprocess
import sys
import time
@@ -42,6 +44,7 @@
'https://www.googleapis.com/auth/source.read_only',
]
COOKIE_JAR = None
+IS_WINDOWS = platform.system() == 'Windows'
def read_meta(part):
r = urllib2.Request(META_URL + part)
@@ -62,8 +65,26 @@
def configure_git():
global COOKIE_JAR
- dir = os.path.join(os.environ['HOME'], '.git-credential-cache')
+ if IS_WINDOWS:
+ # Git for Windows reads %HOMEPATH%/.gitconfig in Command Prompt,
+ # but $HOME/.gitconfig in Cygwin. The two paths can be different.
+ # Cygwin sets env var $HOMEPATH accordingly to %HOMEPATH%.
+ # Set cookie file as %HOMEPATH%/.git-credential-cache/cookie,
+ # so it can be used in both cases.
+ if 'HOMEPATH' in os.environ:
+ homepath = os.environ['HOMEPATH']
+ else:
+ # When launched as a scheduled task at machine startup
+ # HOMEPATH may not be set.
+ sys.stderr.write('HOMEPATH is not set.\n')
+ sys.exit(1)
+ else:
+ homepath = os.environ['HOME']
+ dir = os.path.join(homepath, '.git-credential-cache')
+
COOKIE_JAR = os.path.join(dir, 'cookie')
+ if '--debug' in sys.argv:
+ print 'Cookie file: %s' % COOKIE_JAR
if os.path.exists(dir):
os.chmod(dir, 0700)
@@ -88,7 +109,7 @@
now = int(time.time())
token = acquire_token(scope, retry)
access_token = token['access_token']
- expires = now + int(token['expires_in'])
+ expires = now + int(token['expires_in']) # Epoch in sec
tmp_jar = COOKIE_JAR + '.lock'
cj = cookielib.MozillaCookieJar(tmp_jar)
@@ -113,6 +134,16 @@
rest = None))
cj.save()
+ if '--debug' in sys.argv:
+ print 'Updating %s.' % COOKIE_JAR
+ print 'Expires: %d, %s, in %d seconds'% (
+ expires, time.ctime(expires), expires - now)
+ sys.stdout.flush()
+ if IS_WINDOWS:
+ # os.rename() below on Windows will raise OSError when dst exists.
+ # See https://docs.python.org/2/library/os.html#os.rename
+ if os.path.isfile(COOKIE_JAR):
+ os.remove(COOKIE_JAR)
os.rename(tmp_jar, COOKIE_JAR)
return expires
@@ -136,9 +167,15 @@
def main():
scope = select_scope()
configure_git()
+
expires = update_cookie(scope, retry=False)
if '--nofork' not in sys.argv:
+ if IS_WINDOWS:
+ # os.fork() is not supported on Windows.
+ sys.stderr.write('Add \'--nofork\' on Windows\n')
+ sys.exit(1)
+
if os.fork() > 0:
sys.exit(0)