This isn't terrible earth shaking but since I had to futz with this a little, I thought I would write it down.

Goal: We moved to a new subversion server, and while we were at it, wanted to stop using joke passwords in a file called passwd, start using our windows credentials, and also install trac, on a windows 2003 x64 server.

Ingredients:

First, we installed subversion and restored a backup of our repository...and waited...and waited.

The next day, we installed apache. We set apache to run on port 8080 and verified that it worked (http://localhost:8080) We then copied mod_authz_svn.so, mod_dav_svn.so, libdb44.dll, and mod_auth_sspi.so to the apache modules folder. We enabled these things:

LoadModule asis_module modules/mod_asis.so
LoadModule sspi_auth_module modules/mod_auth_sspi.so
LoadModule auth_basic_module modules/mod_auth_basic.so

and at the end of the LoadModules section:

#LoadModule ssl_module dav_module modules/mod_ssldav.so
LoadModule status_module dav_svn_module modules/mod_statusdav_svn.so
#LoadModule substitute_module authz_svn_module modules/mod_substitutemod_authz_svn.so

finally, at the bottom of the file, we added

<IfModule ssl_module>
SSLRandomSeed startup builtin
SSLRandomSeed connect builtin
</IfModule>


<Location /svn>
    DAV svn
    SVNPath E:\svn
</Location>

We verified that this worked then added the authorization bits to <Location /svn>:

AuthName "svn" 
AuthzSVNAccessFile E:\svn\conf\authz 
AuthType SSPI 
SSPIAuth On 
SSPIAuthoritative Off 
SSPIDomain our.domain.name.com 
SSPIOfferBasic On 
SSPIOmitDomain On 
SSPIUsernameCase lower 
AuthType Basic 
AuthUserFile E:\svn\conf\http-passwd 
AuthBasicAuthoritative Off 
Require valid-user

We needed both sspi authentication (for normal people) and basic (for our build user, a service running on another machine as local system. Side Note: I thought I'd be able to grant DOMAIN\MachineName$ read access and use mod_sspi to authenticate them, but couldn't get this to work - apparently the subversion client needs someone to actually type in the password of the account you are authenticating.)

Note that to use both forms of authentication, they have to be *Authoritative Off. The subversionary document I link to below has the setting name for AuthBasicAuthoritative as AuthAuthoritative. When in doubt, you can look up the correct settings at apache.org.

Note that mod_sspi has some nice options - SSPIOmitDomain and SSPIUsernameCase - to help keep us from running into problems when users type in different case versions of their logins or domains. Using these values, they just always use their lowercase user name.

Subversion will still use %REPOSITORYROOT%/conf/authz for determining what resources different users are allowed access to and the level of access. We defined some groups for our different scrum teams (using their lower case logins), then mapped those to different parts of the repository.

We verified this works, then got our builds working again off this new repository. Pretty uneventful. Mental Note: Remember to copy over old builds and ccserver state files when switching to new working copy on the build server, so you don't start over on version 1.0.0.0 with no history.

Next, it was time to install Trac. I didn't set up Python on the subversion server myself - Jeff Olson did, but I think it was pretty uneventful. Python 2.5. mod_python. There is some subversion stuff, which I think you need to install separately, but I think the trac documentation describes how to do this.

I had to grab the latest trac from their subversion repository. There is a utility (easy_install) to do this, but I saw svn co and I was there. Ran through the normal trac install steps. Didn't realize at first that trac wanted the path to the _physical_svn_ repository files. Eventually figured this out...but I didn't want one big trac site for the whole repository. No problem: you can append the path inside the repo. So, given svn repo physically at E:\svn, and I want trac to be looking at /trunk/MyProject, I just specify E:\svn\trunk\MyProject.

I tested my trac project with the stand-alone tracd. Worked.

Next I wanted it running under mod_python, to hopefully improve performance, and also using mod_sspi, to keep me out of the business of maintaing more passwd files. I added:

LoadModule unique_id_module python_module modules/mod_unique_idmod_python.so

To the modules section, then:

<Location /MyProjectName>
  SetHandler mod_python
  PythonDebug on
  PythonInterpreter main_interpreter
  PythonHandler trac.web.modpython_frontend
  PythonOption TracEnv E:\trac\MyProjectName
  PythonOption TracUriRoot /MyProjectName
  AuthName "My Trac Project"
  AuthType SSPI
  SSPIAuth On
  SSPIAuthoritative On
  SSPIDomain our.domain.name.com
  SSPIOfferBasic On
  SSPIOmitDomain On
  SSPIUsernameCase lower
  Require valid-user
</Location>

To the bottom of the file, copying my mod_sspi settings from subversion above. I tested this out, and ran into a version conflict between the APR (apache runtime) libraries used by python's libsvn and the APR apache was using. I futzed around for a while trying to get this to work and just ended up installing the 2.2 APR -> Subversion 1.4.6 python bindings again and it worked. I guess I had installed APR 2.0 at some point and just forgotten.

Now all I have to do is decide whose and admin and setup other projects.

References:


 
Categories:

March 5, 2008
@ 05:27 AM

image On our CI Factory Enhanced Build server, we use the CSDiff package to create diff files of our checkins. This is kind of cool, because it saves me a trip to TortoiseSVN to dig around, find the commit, and do a diff there.

Our Cruise Control service runs as local system, and unfortunately CSDiff needs to be run interactively at least once to be used. I didn't want to run under a real person's account, because we have a policy to change passwords every 45 days, and I have ....ehemm...had issues with account lockouts due to this.

So, to get past the CSDiff needs to run once problem, I just saved my CS Diff registry settings, then changed the user part to the SID of local system. This fixed the problem.

Hopefully I can find this post next time I need to set up a build server.

csdiff.reg

-------------

Windows Registry Editor Version 5.00

[HKEY_USERS\S-1-5-18\Software\ComponentSoftware\CSDiff]
"CSDiffPath"="C:\\projects\\SelectHoldings\\Current\\Build\\Packages\\CSDiff\\bin"
"CSDiffOpt"=dword:47c6ea88
"CSDiffFlg"=dword:00000001

 

Categories:

March 3, 2008
@ 05:29 PM
image

Due to various other things I have been doing, I haven't had a chance to play around with F# much over the past month. I was finally able to find time this last weekend. I'm really glad, because it was fun.

I did problem 5 from Project Euler:

2520 is the smallest number that can be divided by each of the numbers from 1 to 10 without any remainder.

What is the smallest number that is evenly divisible by all of the numbers from 1 to 20?

Even though this is allegedly one of the simplest problems on Project Euler, I had to spend some time re-learning some fundamental math concepts to do this one. I also had to push my F# abilities a little to get it to work. It paid off because I am finally getting a feel for the cryptic error messages I get from F# when I do something stupid.

So, to solve this problem, I knew I needed to find the smallest number factorable by all the numbers in this range. After reading wikipedia and reviewing my favorite math book, I decided on my strategy: I would calculate the prime factors for [1 .. 20], find the highest power for each prime, and multiply these together - the "Alternative Method" mentioned in the wikipedia article.

I needed a good prime number function. I had spent some time reading on this last month and came up with the following slightly optimized (compared to my previous prime number generators) function:

    let rec primes n =
        seq { 2I .. n } |> Seq.filter(fun x ->
            match x with
            | _ when x <= 3I -> true
            | _ when x % 2I = 0I -> false
            | _ when x % 3I = 0I -> false
            | _ -> match smallestPrimeFactor x with
                    | Some(_) -> false
                    | None -> true)
    and smallestPrimeFactor = 
        Util.memoize(fun x -> 
            Util.ceilSqrt x |> primes |> Seq.tryfind (fun y -> x % y = 0I))

This lets you get all primes <= n, like "primes 1000000I" to get all primes <= 1 million. Memoizing the smallestPrimeFactor part helps a lot when the primes get larger and larger.

A few utility functions are mentioned. Here is their code:

    let memoize (f:'a->'b) =
        let t = new Dictionary<'a, 'b>()
        fun n -> 
            if t.ContainsKey(n) then t.[n]
            else
                let res = f n
                t.Add(n, res)
                res        

    let ceilSqrt (x:bigint) =
        BigInt.to_float x |> sqrt |> ceil |> Float.to_string |> BigInt.of_string

Memoize is straight out of Expert F#, and ceilSqrt is because I am tired of converting to strings and floats because bigint doesn't have a sqrt method, and I usually want the ceiling for this problem.

I've got the primes I want, now to compute the prime factors:

    let rec primeFactors x =
        match x with
        | _ when x <= 3I -> [ x ]
        | _ -> match smallestPrimeFactor(x) with
                | Some(y) -> y :: primeFactors (x / y)
                | None -> [ x ]

 

I can use my smallest prime factor function again to pull out each prime factor then cons it to the list of factors. This yields a list that looks like [2; 2; 2; 5] for 40. To find the highest powers for each prime, I will need to group each prime in each sequence and count the number of occurrences:

    let leastCommonMultiple (n:seq<bigint>) =
        let highestPowers = new Dictionary<bigint,int>()

        n |> Seq.iter(fun x ->
            primeFactors x
            |> Seq.countBy (fun y -> y)
            |> Seq.iter (fun (y, p) -> 
                if highestPowers.ContainsKey(y) then
                    if highestPowers.[y] < p then
                        highestPowers.[y] <- p
                else
                    highestPowers.Add(y, p)))

        highestPowers 
        |> Seq.map (fun x -> BigInt.pow x.Key (BigInt.of_int(x.Value)))
        |> Seq.fold1 (fun x a -> BigInt.mul x a)

 

This is the function I need to get the answer:

    let Problem5 () =
        let answer = Primes.leastCommonMultiple (seq {1I .. 20I})
        printfn "Answer: %O" answer

 

It's more than fast enough on my computer. I was kind of happy to actually get the answer on the first try. Then, of course, my vandal-nature took over and I had to try and break it:

    let Problem5 () =
        //let answer = Primes.leastCommonMultiple (seq {1I .. 20I})
        let start = DateTime.Now
        let answer = Primes.leastCommonMultiple (seq {1I .. (BigInt.pow 10I 5I)})
        printfn "Answer: %O" answer
        let howFast = DateTime.Now - start
        printfn "It took me %f seconds...not that it matters how fast it is..."
            howFast.TotalSeconds

 

Which takes 31.n seconds on my workstation, and is how I ended up with the screen shot above. By the way, that number is 44,234 digits long by my reckoning. That's right, the smallest number factorable by all numbers from 1 to 100,000 is 44,000+ digits long, which makes it technically a "big-ass number."

I tried 10 ** 6 but ran out of e-mail to read while waiting for it, so killed it.

This was pretty fun and I can't wait to tackle the next one. In the meantime, I'd love any critic/feedback of my F#. I don't really personally know anyone else doing F#, so I'll take whatever I can get.


 
Categories: