github.com/technosophos/deis@v1.7.1-0.20150915173815-f9005256004b/builder/routes.go (about)

     1  package builder
     2  
     3  import (
     4  	"time"
     5  
     6  	"github.com/Masterminds/cookoo"
     7  	"github.com/Masterminds/cookoo/fmt"
     8  	"github.com/deis/deis/builder/confd"
     9  	"github.com/deis/deis/builder/docker"
    10  	"github.com/deis/deis/builder/env"
    11  	"github.com/deis/deis/builder/etcd"
    12  	"github.com/deis/deis/builder/git"
    13  	"github.com/deis/deis/builder/sshd"
    14  )
    15  
    16  // routes builds the Cookoo registry.
    17  //
    18  // Esssentially this is a list of all of the things that Builder can do, broken
    19  // down into a step-by-step list.
    20  func routes(reg *cookoo.Registry) {
    21  
    22  	// The "boot" route starts up the builder as a daemon process. Along the
    23  	// way, it starts and configures multiple services, including etcd, confd,
    24  	// and sshd.
    25  	reg.AddRoute(cookoo.Route{
    26  		Name: "boot",
    27  		Help: "Boot the builder",
    28  		Does: []cookoo.Task{
    29  
    30  			// ENV: Make sure the environment is correct.
    31  			cookoo.Cmd{
    32  				Name: "vars",
    33  				Fn:   env.Get,
    34  				Using: []cookoo.Param{
    35  					{Name: "HOST", DefaultValue: "127.0.0.1"},
    36  					{Name: "ETCD_PORT", DefaultValue: "4001"},
    37  					{Name: "ETCD_PATH", DefaultValue: "/deis/builder"},
    38  					{Name: "ETCD_TTL", DefaultValue: "20"},
    39  				},
    40  			},
    41  			cookoo.Cmd{ // This depends on others being processed first.
    42  				Name: "vars2",
    43  				Fn:   env.Get,
    44  				Using: []cookoo.Param{
    45  					{Name: "ETCD", DefaultValue: "$HOST:$ETCD_PORT"},
    46  				},
    47  			},
    48  
    49  			// DOCKER: start up Docker and make sure it's running.
    50  			// Then let it download the images while we keep going.
    51  			cookoo.Cmd{
    52  				Name: "docker",
    53  				Fn:   docker.CreateClient,
    54  				Using: []cookoo.Param{
    55  					{Name: "url", DefaultValue: "unix:///var/run/docker.sock"},
    56  				},
    57  			},
    58  			cookoo.Cmd{
    59  				Name: "dockerclean",
    60  				Fn:   docker.Cleanup,
    61  			},
    62  			cookoo.Cmd{
    63  				Name: "dockerstart",
    64  				Fn:   docker.Start,
    65  			},
    66  			cookoo.Cmd{
    67  				Name: "waitfordocker",
    68  				Fn:   docker.WaitForStart,
    69  				Using: []cookoo.Param{
    70  					{Name: "client", From: "cxt:docker"},
    71  				},
    72  			},
    73  
    74  			cookoo.Cmd{
    75  				Name: "buildImages",
    76  				Fn:   docker.ParallelBuild,
    77  				Using: []cookoo.Param{
    78  					{Name: "client", From: "cxt:docker"},
    79  					{
    80  						Name: "images",
    81  						DefaultValue: []docker.BuildImg{
    82  							{Path: "/usr/local/src/slugbuilder/", Tag: "deis/slugbuilder"},
    83  							{Path: "/usr/local/src/slugrunner/", Tag: "deis/slugrunner"},
    84  						},
    85  					},
    86  				},
    87  			},
    88  
    89  			// ETCD: Make sure Etcd is running, and do the initial population.
    90  			cookoo.Cmd{
    91  				Name:  "client",
    92  				Fn:    etcd.CreateClient,
    93  				Using: []cookoo.Param{{Name: "url", DefaultValue: "http://127.0.0.1:4001", From: "cxt:ETCD"}},
    94  			},
    95  			cookoo.Cmd{
    96  				Name: "etcdup",
    97  				Fn:   etcd.IsRunning,
    98  				Using: []cookoo.Param{
    99  					{Name: "client", From: "cxt:client"},
   100  					{Name: "count", DefaultValue: 20},
   101  				},
   102  			},
   103  			cookoo.Cmd{
   104  				Name: "-",
   105  				Fn:   Sleep,
   106  				Using: []cookoo.Param{
   107  					{Name: "duration", DefaultValue: 21 * time.Second},
   108  					{Name: "message", DefaultValue: "Sleeping while etcd expires keys."},
   109  				},
   110  			},
   111  			cookoo.Cmd{
   112  				Name: "newdir",
   113  				Fn:   fmt.Sprintf,
   114  				Using: []cookoo.Param{
   115  					{Name: "format", DefaultValue: "%s/users"},
   116  					{Name: "0", From: "cxt:ETCD_PATH"},
   117  				},
   118  			},
   119  			cookoo.Cmd{
   120  				Name: "mkdir",
   121  				Fn:   etcd.MakeDir,
   122  				Using: []cookoo.Param{
   123  					{Name: "path", From: "cxt:newdir"},
   124  					{Name: "client", From: "cxt:client"},
   125  				},
   126  			},
   127  
   128  			// SSHD: Create and configure host keys.
   129  			cookoo.Cmd{
   130  				Name: "installSshHostKeys",
   131  				Fn:   etcd.StoreHostKeys,
   132  				Using: []cookoo.Param{
   133  					{Name: "client", From: "cxt:client"},
   134  					{Name: "basepath", From: "cxt:ETCD_PATH"},
   135  				},
   136  			},
   137  			cookoo.Cmd{
   138  				Name: sshd.HostKeys,
   139  				Fn:   sshd.ParseHostKeys,
   140  			},
   141  			cookoo.Cmd{
   142  				Name: sshd.ServerConfig,
   143  				Fn:   sshd.Configure,
   144  			},
   145  
   146  			// CONFD: Build out the templates, then start the Confd server.
   147  			cookoo.Cmd{
   148  				Name:  "once",
   149  				Fn:    confd.RunOnce,
   150  				Using: []cookoo.Param{{Name: "node", From: "cxt:ETCD"}},
   151  			},
   152  			cookoo.Cmd{
   153  				Name:  "confd",
   154  				Fn:    confd.Run,
   155  				Using: []cookoo.Param{{Name: "node", From: "cxt:ETCD"}},
   156  			},
   157  
   158  			// Now we wait for Docker to finish downloading.
   159  			cookoo.Cmd{
   160  				Name: "dowloadImages",
   161  				Fn:   docker.Wait,
   162  				Using: []cookoo.Param{
   163  					{Name: "wg", From: "cxt:buildImages"},
   164  					{Name: "msg", DefaultValue: "Images downloaded"},
   165  					{Name: "waiting", DefaultValue: "Downloading Docker images. This may take a long time. https://xkcd.com/303/"},
   166  					{Name: "failures", From: "cxt:ParallelBuild.failN"},
   167  				},
   168  			},
   169  			cookoo.Cmd{
   170  				Name: "pushImages",
   171  				Fn:   docker.Push,
   172  				Using: []cookoo.Param{
   173  					{Name: "tag", DefaultValue: "deis/slugrunner:latest"},
   174  					{Name: "client", From: "cxt:client"},
   175  				},
   176  			},
   177  
   178  			// ETDCD: Now watch for events on etcd, and trigger a git check-repos for
   179  			// each. For the most part, this runs in the background.
   180  			cookoo.Cmd{
   181  				Name: "Cleanup",
   182  				Fn:   etcd.Watch,
   183  				Using: []cookoo.Param{
   184  					{Name: "client", From: "cxt:client"},
   185  				},
   186  			},
   187  			// If there's an EXTERNAL_PORT, we publish info to etcd.
   188  			cookoo.Cmd{
   189  				Name: "externalport",
   190  				Fn:   env.Get,
   191  				Using: []cookoo.Param{
   192  					{Name: "EXTERNAL_PORT", DefaultValue: ""},
   193  				},
   194  			},
   195  			cookoo.Cmd{
   196  				Name: "etcdupdate",
   197  				Fn:   etcd.UpdateHostPort,
   198  				Using: []cookoo.Param{
   199  					{Name: "base", From: "cxt:ETCD_PATH"},
   200  					{Name: "host", From: "cxt:HOST"},
   201  					{Name: "port", From: "cxt:EXTERNAL_PORT"},
   202  					{Name: "client", From: "cxt:client"},
   203  					{Name: "sshdPid", From: "cxt:sshd"},
   204  				},
   205  			},
   206  
   207  			// DAEMON: Finally, we wait around for a signal, and then cleanup.
   208  			cookoo.Cmd{
   209  				Name: "listen",
   210  				Fn:   KillOnExit,
   211  				Using: []cookoo.Param{
   212  					{Name: "docker", From: "cxt:dockerstart"},
   213  					{Name: "sshd", From: "cxt:sshdstart"},
   214  				},
   215  			},
   216  		},
   217  	})
   218  
   219  	// This route is called during a user authentication for SSH.
   220  	// The rough pattern is that we parse the local authorized keys file, and
   221  	// then validate that the supplied user key matches an authorized key.
   222  	//
   223  	// This grants access to running git-receive, but does not grant access
   224  	// to writing to the repo. That's handled by the sshReceive.
   225  	reg.AddRoute(cookoo.Route{
   226  		Name: "pubkeyAuth",
   227  		Does: []cookoo.Task{
   228  			// Parse the authorized keys file.
   229  			// We do this every time because confd is constantly regenerating
   230  			// the auth keys file.
   231  			cookoo.Cmd{
   232  				Name: "authorizedKeys",
   233  				Fn:   sshd.ParseAuthorizedKeys,
   234  				Using: []cookoo.Param{
   235  					{Name: "path", DefaultValue: "/home/git/.ssh/authorized_keys"},
   236  				},
   237  			},
   238  
   239  			// Auth against the keys
   240  			cookoo.Cmd{
   241  				Name: "authN",
   242  				Fn:   sshd.AuthKey,
   243  				Using: []cookoo.Param{
   244  					{Name: "metadata", From: "cxt:metadata"},
   245  					{Name: "key", From: "cxt:key"},
   246  					{Name: "authorizedKeys", From: "cxt:authorizedKeys"},
   247  				},
   248  			},
   249  		},
   250  	})
   251  
   252  	// This provides a very basic SSH ping.
   253  	// Called by the sshd.Server
   254  	reg.AddRoute(cookoo.Route{
   255  		Name: "sshPing",
   256  		Help: "Handles an ssh exec ping.",
   257  		Does: []cookoo.Task{
   258  			cookoo.Cmd{
   259  				Name: "ping",
   260  				Fn:   sshd.Ping,
   261  				Using: []cookoo.Param{
   262  					{Name: "request", From: "cxt:request"},
   263  					{Name: "channel", From: "cxt:channel"},
   264  				},
   265  			},
   266  		},
   267  	})
   268  
   269  	// This proxies a client session into a git receive.
   270  	//
   271  	// Called by the sshd.Server
   272  	reg.AddRoute(cookoo.Route{
   273  		Name: "sshGitReceive",
   274  		Help: "Handle a git receive over an SSH connection.",
   275  		Does: []cookoo.Task{
   276  			// The Git receive handler needs the username. So we provide
   277  			// it by looking up the name based on the key. When the
   278  			// controller no longer requires username for SSH auth, we can
   279  			// ditch this.
   280  			cookoo.Cmd{
   281  				Name: "fingerprint",
   282  				Fn:   sshd.FingerprintKey,
   283  				Using: []cookoo.Param{
   284  					{Name: "key", From: "cxt:key"},
   285  				},
   286  			},
   287  			cookoo.Cmd{
   288  				Name: "username",
   289  				Fn:   etcd.FindSSHUser,
   290  				Using: []cookoo.Param{
   291  					{Name: "client", From: "cxt:client"},
   292  					{Name: "fingerprint", From: "cxt:fingerprint"},
   293  				},
   294  			},
   295  			cookoo.Cmd{
   296  				Name: "receive",
   297  				Fn:   git.Receive,
   298  				Using: []cookoo.Param{
   299  					{Name: "request", From: "cxt:request"},
   300  					{Name: "channel", From: "cxt:channel"},
   301  					{Name: "operation", From: "cxt:operation"},
   302  					{Name: "repoName", From: "cxt:repository"},
   303  					{Name: "fingerprint", From: "cxt:fingerprint"},
   304  					{Name: "permissions", From: "cxt:authN"},
   305  					{Name: "user", From: "cxt:username"},
   306  				},
   307  			},
   308  		},
   309  	})
   310  }