Exemple #1
0
// MergeToYaml converts a Config object and a yaml.File to a single yaml.File.
//
// Params:
//	- conf (*Config): The configuration to merge.
//	- overwriteImports (bool, default true): If this is true, old config will
//		overwritten. If false, we attempt to merge the old and new config, with
//		preference to the old.
//
// Returns:
//	- The root yaml.Node of the modified config.
//
// Uses:
//	- cxt.Get("yaml.File") as the source for the YAML file.
func MergeToYaml(c cookoo.Context, p *cookoo.Params) (interface{}, cookoo.Interrupt) {
	root := c.Get("yaml.File", nil).(*yaml.File).Root
	cfg := p.Get("conf", nil).(*Config)
	overwrite := p.Get("overwriteImports", true).(bool)

	rootMap, ok := root.(yaml.Map)
	if !ok {
		return nil, fmt.Errorf("Expected root node to be a map.")
	}

	if len(cfg.Name) > 0 {
		rootMap["package"] = yaml.Scalar(cfg.Name)
	}
	if cfg.InCommand != "" {
		rootMap["incmd"] = yaml.Scalar(cfg.InCommand)
	}

	if overwrite {
		// Imports
		imports := make([]yaml.Node, len(cfg.Imports))
		for i, imp := range cfg.Imports {
			imports[i] = imp.ToYaml()
		}
		rootMap["import"] = yaml.List(imports)
	} else {
		var err error
		rootMap, err = mergeImports(rootMap, cfg)
		if err != nil {
			Warn("Problem merging imports: %s\n", err)
		}
	}

	return root, nil
}
Exemple #2
0
// ParallelBuild runs multiple docker builds at the same time.
//
// Params:
//	-images ([]BuildImg): Images to build
// 	-alwaysFetch (bool): Default false. If set to true, this will always fetch
// 		the Docker image even if it already exists in the registry.
//
// Returns:
//
// 	- Waiter: A *sync.WaitGroup that is waiting for the docker downloads to finish.
//
// Context:
//
// This puts 'ParallelBuild.failN" (int) into the context to indicate how many failures
// occurred during fetches.
func ParallelBuild(c cookoo.Context, p *cookoo.Params) (interface{}, cookoo.Interrupt) {
	images := p.Get("images", []BuildImg{}).([]BuildImg)

	var wg sync.WaitGroup
	var m sync.Mutex
	var fails int

	for _, img := range images {
		img := img

		// HACK: ensure "docker build" is serialized by allowing only one entry in
		// the WaitGroup. This works around the "simultaneous docker pull" bug.
		wg.Wait()
		wg.Add(1)
		safely.GoDo(c, func() {
			log.Infof(c, "Starting build for %s (tag: %s)", img.Path, img.Tag)
			if _, err := buildImg(c, img.Path, img.Tag); err != nil {
				log.Errf(c, "Failed to build docker image: %s", err)
				m.Lock()
				fails++
				m.Unlock()
			}
			wg.Done()
		})

	}

	// Number of failures.
	c.Put("ParallelBuild.failN", fails)

	return &wg, nil
}
Exemple #3
0
// BufferPost buffers the body of the POST request into the context.
//
// Params:
//
// Returns:
//	- []byte with the content of the request.
func BufferPost(c cookoo.Context, p *cookoo.Params) (interface{}, cookoo.Interrupt) {
	req := c.Get("http.Request", nil).(*http.Request)
	var b bytes.Buffer
	_, err := io.Copy(&b, req.Body)
	c.Logf("info", "Received POST: %s", b.Bytes())
	return b.Bytes(), err
}
Exemple #4
0
// iam injects info into the environment about a host's self.
//
// Sets the following environment variables. (Values represent the data format.
// Instances will get its own values.)
//
//	MY_NODEIP=10.245.1.3
//	MY_SERVICE_IP=10.22.1.4
// 	MY_PORT_PEER=2380
// 	MY_PORT_CLIENT=2379
//	MY_NAMESPACE=default
//	MY_SELFLINK=/api/v1/namespaces/default/pods/deis-etcd-1-336jp
//	MY_UID=62a3b54a-6956-11e5-b8ab-0800279dd272
//	MY_APISERVER=https://10.247.0.1:443
//	MY_NAME=deis-etcd-1-336jp
//	MY_IP=10.246.44.7
//	MY_LABEL_NAME=deis-etcd-1   # One entry per label in the JSON
// 	MY_ANNOTATION_NAME=deis-etcd-1  # One entry per annitation in the JSON
//	MY_PORT_CLIENT=4100
//	MY_PORT_PEER=2380
func iam(c cookoo.Context, p *cookoo.Params) (interface{}, cookoo.Interrupt) {
	me, err := aboutme.FromEnv()
	if err != nil {
		log.Errf(c, "Failed aboutme.FromEnv: %s", err)
	} else {

		if strings.TrimSpace(me.IP) == "" {
			log.Warn(c, "No IP found by API query.")
			ip, err := aboutme.MyIP()
			if err != nil || ip == "" {
				// Force pod death.
				log.Errf(c, "Failed to get an IP address: %s", err)
				os.Exit(5)
			}
		}

		me.ShuntEnv()
		os.Setenv("ETCD_NAME", me.Name)
		c.Put("ETCD_NAME", me.Name)
	}

	passEnv("MY_PORT_CLIENT", "$DEIS_ETCD_1_SERVICE_PORT_CLIENT")
	passEnv("MY_PORT_PEER", "$DEIS_ETCD_1_SERVICE_PORT_PEER")
	return nil, nil
}
Exemple #5
0
// Subscribe allows an request to subscribe to topic updates.
//
// Params:
// 	- topic (string): The topic to subscribe to.
// 	-
//
// Returns:
//
func Subscribe(c cookoo.Context, p *cookoo.Params) (interface{}, cookoo.Interrupt) {
	medium, err := getMedium(c)
	if err != nil {
		return nil, &cookoo.FatalError{"No medium."}
	}
	topic := p.Get("topic", "").(string)
	if len(topic) == 0 {
		return nil, errors.New("No topic is set.")
	}

	rw := c.Get("http.ResponseWriter", nil).(ResponseWriterFlusher)
	clientGone := rw.(http.CloseNotifier).CloseNotify()

	sub := NewSubscription(rw)
	t := fetchOrCreateTopic(medium, topic, true, DefaultMaxHistory)
	t.Subscribe(sub)

	defer func() {
		t.Unsubscribe(sub)
		sub.Close()
	}()

	sub.Listen(clientGone)

	return nil, nil
}
Exemple #6
0
// ParseHostKeys parses the host key files.
//
// By default it looks in /etc/ssh for host keys of the patterh ssh_host_{{TYPE}}_key.
//
// Params:
// 	- keytypes ([]string): Key types to parse. Defaults to []string{rsa, dsa, ecdsa}
// 	- enableV1 (bool): Allow V1 keys. By default this is disabled.
// 	- path (string): Override the lookup pattern. If %s, it will be replaced with the keytype.
//
// Returns:
// 	[]ssh.Signer
func ParseHostKeys(c cookoo.Context, p *cookoo.Params) (interface{}, cookoo.Interrupt) {
	log.Debugf(c, "Parsing ssh host keys")
	hostKeyTypes := p.Get("keytypes", []string{"rsa", "dsa", "ecdsa"}).([]string)
	pathTpl := p.Get("path", "/etc/ssh/ssh_host_%s_key").(string)
	hostKeys := make([]ssh.Signer, 0, len(hostKeyTypes))
	for _, t := range hostKeyTypes {
		path := fmt.Sprintf(pathTpl, t)

		if key, err := ioutil.ReadFile(path); err == nil {
			if hk, err := ssh.ParsePrivateKey(key); err == nil {
				log.Infof(c, "Parsed host key %s.", path)
				hostKeys = append(hostKeys, hk)
			} else {
				log.Errf(c, "Failed to parse host key %s (skipping): %s", path, err)
			}
		}
	}
	if c.Get("enableV1", false).(bool) {
		path := "/etc/ssh/ssh_host_key"
		if key, err := ioutil.ReadFile(path); err != nil {
			log.Errf(c, "Failed to read ssh_host_key")
		} else if hk, err := ssh.ParsePrivateKey(key); err == nil {
			log.Infof(c, "Parsed host key %s.", path)
			hostKeys = append(hostKeys, hk)
		} else {
			log.Errf(c, "Failed to parse host key %s: %s", path, err)
		}
	}
	return hostKeys, nil
}
Exemple #7
0
/**
 * Perform authentication.
 *
 * Params:
 * 	- realm (string): The name of the realm. (Default: "web")
 * 	- datasource (string): The name of the datasource that should be used to authenticate.
 * 	  This datasource must be an `auth.UserDatasource`. (Default: "auth.UserDatasource")
 *
 * Context:
 * 	- http.Request (*http.Request): The HTTP request. This is usually placed into the
 * 	context for you.
 * 	- http.ResponseWriter (http.ResponseWriter): The response. This is usually placed
 * 	 into the context for you.
 *
 * Datasource:
 * 	- An auth.UserDatasource. By default, this will look for a datasource named
 * 	  "auth.UserDatasource". This can be overridden by the `datasource` param.
 *
 * Returns:
 * 	- True if the user authenticated. If not, this will send a 401 and then stop
 * 	  the current chain.
 */
func Basic(c cookoo.Context, p *cookoo.Params) (interface{}, cookoo.Interrupt) {
	realm := p.Get("realm", "web").(string)
	dsName := p.Get("datasource", "auth.UserDatasource").(string)

	req := c.Get("http.Request", nil).(*http.Request)
	res := c.Get("http.ResponseWriter", nil).(http.ResponseWriter)

	ds := c.Datasource(dsName).(UserDatasource)

	authz := strings.TrimSpace(req.Header.Get("Authorization"))
	if len(authz) == 0 || !strings.Contains(authz, "Basic ") {
		return sendUnauthorized(realm, res)
	}

	user, pass, err := parseBasicString(authz)
	if err != nil {
		c.Logf("info", "Basic authentication parsing failed: %s", err)
		return sendUnauthorized(realm, res)
	}

	ok, err := ds.AuthUser(user, pass)
	if !ok {
		if err != nil {
			c.Logf("info", "Basic authentication caused an error: %s", err)
		}
		return sendUnauthorized(realm, res)
	}

	return ok, err
}
Exemple #8
0
// ParallelBuild runs multiple docker builds at the same time.
//
// Params:
//	-images ([]BuildImg): Images to build
// 	-alwaysFetch (bool): Default false. If set to true, this will always fetch
// 		the Docker image even if it already exists in the registry.
//
// Returns:
//
// 	- Waiter: A *sync.WaitGroup that is waiting for the docker downloads to finish.
//
// Context:
//
// This puts 'ParallelBuild.failN" (int) into the context to indicate how many failures
// occurred during fetches.
func ParallelBuild(c cookoo.Context, p *cookoo.Params) (interface{}, cookoo.Interrupt) {
	images := p.Get("images", []BuildImg{}).([]BuildImg)

	var wg sync.WaitGroup
	var m sync.Mutex
	var fails int

	for _, img := range images {
		img := img
		wg.Add(1)
		safely.GoDo(c, func() {
			log.Infof(c, "Starting build for %s (tag: %s)", img.Path, img.Tag)
			if _, err := buildImg(c, img.Path, img.Tag); err != nil {
				log.Errf(c, "Failed to build docker image: %s", err)
				m.Lock()
				fails++
				m.Unlock()
			}
			wg.Done()
		})

	}

	// Number of failures.
	c.Put("ParallelBuild.failN", fails)

	return &wg, nil
}
Exemple #9
0
// Utility function to get the database from a datasource.
func GetDb(cxt cookoo.Context, dbname string) (*sql.DB, error) {
	dbO, ok := cxt.HasDatasource(dbname)
	if !ok {
		return nil, &cookoo.FatalError{fmt.Sprintf("No DB datasource named '%s' found.", dbname)}
	}
	return dbO.(*sql.DB), nil
}
Exemple #10
0
// Template is a template-based text formatter.
//
// This uses the core `text/template` to process a given string template.
//
// Params
// 	- template (string): A template string.
// 	- template.Context (bool): If true, the context will be placed into the
// 		template renderer as 'Cxt', and can be used as `{{.Cxt.Foo}}`. False
// 		by default.
// 	- ... (interface{}): Values passed into the template.
//
// Conventionally, template variables should start with an initial capital.
//
// Returns a formatted string.
func Template(c cookoo.Context, p *cookoo.Params) (interface{}, cookoo.Interrupt) {
	format := cookoo.GetString("template", "", p)
	withCxt := cookoo.GetBool("template.Context", false, p)

	name := fmt.Sprintf("%x", md5.Sum([]byte(format)))

	//c.Logf("debug", "Template %s is '%s'\n", name, format)

	tpl, err := template.New(name).Parse(format)
	if err != nil {
		return "", err
	}

	data := p.AsMap()
	if withCxt {
		//c.Logf("debug", "Adding context.")
		data["Cxt"] = c.AsMap()
	}

	var out bytes.Buffer
	if err := tpl.Execute(&out, data); err != nil {
		return "", err
	}

	return out.String(), nil
}
Exemple #11
0
func (r *URIPathResolver) subtreeMatch(c cookoo.Context, pathName, pattern string) bool {

	if pattern == "**" {
		return true
	}

	// Find out how many slashes we have.
	countSlash := strings.Count(pattern, "/")

	// '**' matches anything.
	if countSlash == 0 {
		c.Logf("warn", "Illegal pattern: %s", pattern)
		return false
	}

	// Add 2 for verb plus trailer.
	parts := strings.SplitN(pathName, "/", countSlash+1)
	prefix := strings.Join(parts[0:countSlash], "/")

	subpattern := strings.Replace(pattern, "/**", "", -1)
	if ok, err := path.Match(subpattern, prefix); ok && err == nil {
		return true
	} else if err != nil {
		c.Logf("warn", "Parsing path `%s` gave error: %s", err)
	}
	return false
}
Exemple #12
0
// Show help.
// This command is useful for placing at the front of a CLI "subcommand" to have it output
// help information. It will only trigger when "show" is set to true, so another command
// can, for example, check for a "-h" or "-help" flag and set "show" based on that.
//
// Params:
// 	- show (bool): If `true`, show help.
// 	- summary (string): A one-line summary of the command.
// 	- description (string): A short description of what the command does.
// 	- usage (string): usage information.
// 	- flags (FlagSet): Flags that are supported. The FlagSet will be converted to help text.
// 	- writer (Writer): The location that this will write to. Default is os.Stdout
// 	- subcommands ([]string): A list of subcommands. This will be formatted as help text.
func ShowHelp(cxt cookoo.Context, params *cookoo.Params) (interface{}, cookoo.Interrupt) {
	showHelp := false
	showHelpO := params.Get("show", false)
	switch showHelpO.(type) {
	case string:
		showHelp = strings.ToLower(showHelpO.(string)) == "true"
	case bool:
		showHelp = showHelpO.(bool)
	}

	writer := params.Get("writer", os.Stdout).(io.Writer)

	pmap := params.AsMap()

	// Last resort: If no summary, pull it from the route description.
	if summary, ok := pmap["summary"]; !ok || len(summary.(string)) == 0 {
		pmap["summary"] = cxt.Get("route.Description", "").(string)
	}

	sections := []string{"summary", "description", "usage"}
	if _, ok := params.Has("subcommands"); ok {
		sections = append(sections, "subcommands")
	}

	if showHelp {
		displayHelp(sections, pmap, writer)
		return true, new(cookoo.Stop)
	}

	return false, nil
}
Exemple #13
0
// Serve creates a new Cookoo web server.
//
// Important details:
//
// 	- A URIPathResolver is used for resolving request names.
// 	- The following datasources are added to the Context:
// 	  * url: A URLDatasource (Provides access to parts of the URL)
// 	  * path: A PathDatasource (Provides access to parts of a path. E.g. "/foo/bar")
// 	  * query: A QueryParameterDatasource (Provides access to URL query parameters.)
// 	  * post: A FormValuesDatasource (Provides access to form data or the body of a request.)
// 	- The following context variables are set:
// 	  * http.Request: A pointer to the http.Request object
// 	  * http.ResponseWriter: The response writer.
// 	  * server.Address: The server's address and port (NOT ALWAYS PRESENT)
// 	- The handler includes logic to redirect "not found" errors to a path named "@404" if present.
//
// Context Params:
//
// 	- server.Address: If this key exists in the context, it will be used to determine the host/port the
//   server runes on. EXPERIMENTAL. Default is ":8080".
//
// Example:
//
//    package main
//
//    import (
//      //This is the path to Cookoo
//      "github.com/Masterminds/cookoo"
//      "github.com/Masterminds/cookoo/web"
//      "fmt"
//    )
//
//    func main() {
//      // Build a new Cookoo app.
//      registry, router, context := cookoo.Cookoo()
//
//      // Fill the registry.
//      registry.Route("GET /", "The index").Does(web.Flush, "example").
//        Using("content").WithDefault("Hello World")
//
//      // Create a server
//      web.Serve(reg, router, cookoo.SyncContext(cxt))
//    }
//
// Note that we synchronize the context before passing it into Serve(). This
// is optional because each handler gets its own copy of the context already.
// However, if commands pass the context to goroutines, the context ought to be
// synchronized to avoid race conditions.
//
// Note that copies of the context are not synchronized with each other.
// So by declaring the context synchronized here, you
// are not therefore synchronizing across handlers.
func Serve(reg *cookoo.Registry, router *cookoo.Router, cxt cookoo.Context) {
	addr := cxt.Get("server.Address", ":8080").(string)

	handler := NewCookooHandler(reg, router, cxt)

	// MPB: I dont think there's any real point in having a multiplexer in
	// this particular case. The Cookoo handler is mux enough.
	//
	// Note that we can always use Cookoo with the built-in multiplexer. It
	// just doesn't make sense if Cookoo's the only handler on the app.
	//http.Handle("/", handler)

	server := &http.Server{Addr: addr}

	// Instead of mux, set a single default handler.
	// What we might be losing:
	// - Handling of non-conforming paths.
	server.Handler = handler

	go handleSignals(router, cxt, server)
	err := server.ListenAndServe()
	//err := http.ListenAndServe(addr, nil)
	if err != nil {
		cxt.Logf("error", "Caught error while serving: %s", err)
		if router.HasRoute("@crash") {
			router.HandleRequest("@crash", cxt, false)
		}
	}
}
Exemple #14
0
// Receive receives a Git repo.
// This will only work for git-receive-pack.
//
// Params:
// 	- operation (string): e.g. git-receive-pack
// 	- repoName (string): The repository name, in the form '/REPO.git'.
// 	- channel (ssh.Channel): The channel.
// 	- request (*ssh.Request): The channel.
// 	- gitHome (string): Defaults to /home/git.
// 	- fingerprint (string): The fingerprint of the user's SSH key.
// 	- user (string): The name of the Deis user.
//
// Returns:
// 	- nothing
func Receive(c cookoo.Context, p *cookoo.Params) (interface{}, cookoo.Interrupt) {
	if ok, z := p.Requires("channel", "request", "fingerprint", "permissions"); !ok {
		return nil, fmt.Errorf("Missing requirements %q", z)
	}
	repoName := p.Get("repoName", "").(string)
	operation := p.Get("operation", "").(string)
	channel := p.Get("channel", nil).(ssh.Channel)
	gitHome := p.Get("gitHome", "/home/git").(string)
	fingerprint := p.Get("fingerprint", nil).(string)
	user := p.Get("user", "").(string)

	repo, err := cleanRepoName(repoName)
	if err != nil {
		log.Warnf(c, "Illegal repo name: %s.", err)
		channel.Stderr().Write([]byte("No repo given"))
		return nil, err
	}
	repo += ".git"

	if _, err := createRepo(c, filepath.Join(gitHome, repo), gitHome); err != nil {
		log.Infof(c, "Did not create new repo: %s", err)
	}
	cmd := exec.Command("git-shell", "-c", fmt.Sprintf("%s '%s'", operation, repo))
	log.Infof(c, strings.Join(cmd.Args, " "))

	var errbuff bytes.Buffer

	cmd.Dir = gitHome
	cmd.Env = []string{
		fmt.Sprintf("RECEIVE_USER=%s", user),
		fmt.Sprintf("RECEIVE_REPO=%s", repo),
		fmt.Sprintf("RECEIVE_FINGERPRINT=%s", fingerprint),
		fmt.Sprintf("SSH_ORIGINAL_COMMAND=%s '%s'", operation, repo),
		fmt.Sprintf("SSH_CONNECTION=%s", c.Get("SSH_CONNECTION", "0 0 0 0").(string)),
	}
	cmd.Env = append(cmd.Env, os.Environ()...)

	done := plumbCommand(cmd, channel, &errbuff)

	if err := cmd.Start(); err != nil {
		log.Warnf(c, "Failed git receive immediately: %s %s", err, errbuff.Bytes())
		return nil, err
	}
	fmt.Printf("Waiting for git-receive to run.\n")
	done.Wait()
	fmt.Printf("Waiting for deploy.\n")
	if err := cmd.Wait(); err != nil {
		log.Errf(c, "Error on command: %s %s", err, errbuff.Bytes())
		return nil, err
	}
	if errbuff.Len() > 0 {
		log.Warnf(c, "Unreported error: %s", errbuff.Bytes())
	}
	log.Infof(c, "Deploy complete.\n")

	return nil, nil
}
Exemple #15
0
// Flush sends content to output.
//
// If no writer is specified, this will attempt to write to whatever is in the
// Context with the key "http.ResponseWriter". If no suitable writer is found, it will
// not write to anything at all.
//
// Params:
// 	- writer: A Writer of some sort. This will try to write to the HTTP response if no writer
// 	is specified.
// 	- content: The content to write as a body. If this is a byte[], it is sent unchanged. Otherwise.
// 	we first try to convert to a string, then pass it into a writer.
// 	- contentType: The content type header (e.g. text/html). Default is text/plain
// 	- responseCode: Integer HTTP Response Code: Default is `http.StatusOK`.
// 	- headers: a map[string]string of HTTP headers. The keys will be run through
// 	 http.CannonicalHeaderKey()
//
// Note that this is optimized for writing from strings or arrays, not Readers. For larger
// objects, you may find it more efficient to use a different command.
//
// Context:
// - If this finds `web.ContentEncoding`, it will set a content-encoding header.
//
// Returns
//
// 	- boolean true
func Flush(cxt cookoo.Context, params *cookoo.Params) (interface{}, cookoo.Interrupt) {

	// Make sure we have a place to write this stuff.
	writer, ok := params.Has("writer")
	if writer == nil {
		writer, ok = cxt.Has("http.ResponseWriter")
		if !ok {
			return false, nil
		}
	}
	out := writer.(http.ResponseWriter)

	// Get the rest of the info.
	code := params.Get("responseCode", http.StatusOK).(int)
	header := out.Header()
	contentType := params.Get("contentType", "text/plain; charset=utf-8").(string)

	// Prepare the content.
	var content []byte
	rawContent, ok := params.Has("content")
	if !ok {
		// No content. Send nothing in the body.
		content = []byte("")
	} else if byteContent, ok := rawContent.([]byte); ok {
		// Got a byte[]; add it as is.
		content = byteContent
	} else {
		// Use the formatter to convert to a string, and then
		// cast it to bytes.
		content = []byte(fmt.Sprintf("%v", rawContent))
	}

	// Add headers:
	header.Set(http.CanonicalHeaderKey("content-type"), contentType)

	te := cxt.Get(ContentEncoding, "").(string)
	if len(te) > 0 {
		header.Set(http.CanonicalHeaderKey("transfer-encoding"), te)
	}

	headerO, ok := params.Has("headers")
	if ok {
		headers := headerO.(map[string]string)
		for k, v := range headers {
			header.Add(http.CanonicalHeaderKey(k), v)
		}
	}

	// Send the headers.
	out.WriteHeader(code)

	//io.WriteString(out, content)
	out.Write(content)

	return true, nil
}
Exemple #16
0
// ServerInfo gets the server info for this request.
//
// This assumes that `http.Request` and `http.ResponseWriter` are in the context, which
// they are by default.
//
// Returns:
// 	- boolean true
func ServerInfo(cxt cookoo.Context, params *cookoo.Params) (interface{}, cookoo.Interrupt) {
	req := cxt.Get("http.Request", nil).(*http.Request)
	out := cxt.Get("http.ResponseWriter", nil).(http.ResponseWriter)

	out.Header().Add("X-Foo", "Bar")
	out.Header().Add("Content-type", "text/plain; charset=utf-8")

	fmt.Fprintf(out, "Request:\n %+v\n", req)
	fmt.Fprintf(out, "\n\n\nResponse:\n%+v\n", out)
	return true, nil
}
Exemple #17
0
// DropToShell executes a glide plugin. A command that's implemented by
// another application is executed in a similar manner to the way git commands
// work. For example, 'glide foo' would try to execute the application glide-foo.
// Params:
//   - command: the name of the command to attempt executing.
func DropToShell(c cookoo.Context, p *cookoo.Params) (interface{}, cookoo.Interrupt) {
	args := c.Get("os.Args", nil).([]string)
	command := p.Get("command", "").(string)

	if len(args) == 0 {
		return nil, fmt.Errorf("Could not get os.Args.")
	}

	cwd, err := os.Getwd()
	if err != nil {
		return nil, err
	}

	projpath := cwd
	if tmp := os.Getenv("GLIDE_PROJECT"); len(tmp) != 0 {
		projpath = tmp
	}

	cmd := "glide-" + command
	var fullcmd string
	if fullcmd, err = exec.LookPath(cmd); err != nil {
		fullcmd = projpath + "/" + cmd
		if _, err := os.Stat(fullcmd); err != nil {
			return nil, fmt.Errorf("Command %s does not exist.", cmd)
		}
	}

	// Turning os.Args first argument from `glide` to `glide-command`
	args[0] = cmd
	// Removing the first argument (command)
	removed := false
	for i, v := range args {
		if removed == false && v == command {
			args = append(args[:i], args[i+1:]...)
			removed = true
		}
	}
	pa := os.ProcAttr{
		Files: []*os.File{os.Stdin, os.Stdout, os.Stderr},
		Dir:   cwd,
	}

	fmt.Printf("Delegating to plugin %s (%v)\n", fullcmd, args)

	proc, err := os.StartProcess(fullcmd, args, &pa)
	if err != nil {
		return nil, err
	}

	if _, err := proc.Wait(); err != nil {
		return nil, err
	}
	return nil, nil
}
Exemple #18
0
// ParseYaml parses the glide.yaml format and returns a Configuration object.
//
// Params:
//	- filename (string): YAML filename as a string
//
// Context:
//	- yaml.File: This puts the parsed YAML file into the context.
//
// Returns:
//	- *Config: The configuration.
func ParseYaml(c cookoo.Context, p *cookoo.Params) (interface{}, cookoo.Interrupt) {
	fname := p.Get("filename", "glide.yaml").(string)
	//conf := new(Config)
	f, err := yaml.ReadFile(fname)
	if err != nil {
		return nil, err
	}

	c.Put("yaml.File", f)
	return FromYaml(f.Root)
}
Exemple #19
0
func setupHandler(c *cli.Context, route string, cxt cookoo.Context, router *cookoo.Router) {
	cxt.Put("q", c.GlobalBool("quiet"))
	cxt.Put("debug", c.GlobalBool("debug"))
	cxt.Put("no-color", c.GlobalBool("no-color"))
	cxt.Put("yaml", c.GlobalString("yaml"))
	cxt.Put("home", c.GlobalString("home"))
	cxt.Put("cliArgs", c.Args())
	if err := router.HandleRequest(route, cxt, false); err != nil {
		fmt.Printf("Oops! %s\n", err)
		os.Exit(1)
	}
}
Exemple #20
0
// handleSignals traps kill and interrupt signals and runs shutdown().
func handleSignals(router *cookoo.Router, cxt cookoo.Context, server *http.Server) {
	sig := make(chan os.Signal, 1)
	signal.Notify(sig, os.Kill, os.Interrupt)

	s := <-sig
	cxt.Logf("info", "Received signal %s. Shutting down.", s)
	// Not particularly useful on its own.
	// server.SetKeepAlivesEnabled(false)
	// TODO: Implement graceful shutdowns.
	shutdown(router, cxt)
	os.Exit(0)

}
Exemple #21
0
// iam injects info into the environment about a host's self.
//
// Sets the following environment variables. (Values represent the data format.
// Instances will get its own values.)
//
//	MY_NODEIP=10.245.1.3
//	MY_SERVICE_IP=10.22.1.4
// 	MY_PORT_PEER=2380
// 	MY_PORT_CLIENT=2379
//	MY_NAMESPACE=default
//	MY_SELFLINK=/api/v1/namespaces/default/pods/deis-etcd-1-336jp
//	MY_UID=62a3b54a-6956-11e5-b8ab-0800279dd272
//	MY_APISERVER=https://10.247.0.1:443
//	MY_NAME=deis-etcd-1-336jp
//	MY_IP=10.246.44.7
//	MY_LABEL_NAME=deis-etcd-1   # One entry per label in the JSON
// 	MY_ANNOTATION_NAME=deis-etcd-1  # One entry per annitation in the JSON
//	MY_PORT_CLIENT=4100
//	MY_PORT_PEER=2380
func iam(c cookoo.Context, p *cookoo.Params) (interface{}, cookoo.Interrupt) {
	me, err := aboutme.FromEnv()
	if err != nil {
		log.Errf(c, "Failed aboutme.FromEnv: %s", err)
	} else {
		me.ShuntEnv()
		os.Setenv("ETCD_NAME", me.Name)
		c.Put("ETCD_NAME", me.Name)
	}

	passEnv("MY_PORT_CLIENT", "$DEIS_ETCD_1_SERVICE_PORT_CLIENT")
	passEnv("MY_PORT_PEER", "$DEIS_ETCD_1_SERVICE_PORT_PEER")
	return nil, nil
}
Exemple #22
0
// Serve starts a native SSH server.
//
// The general design of the server is that it acts as a main server for
// a Cookoo app. It assumes that certain things have been configured for it,
// like an ssh.ServerConfig. Once it runs, it will block until the main
// process terminates. If you want to stop it prior to that, you can grab
// the closer ("sshd.Closer") out of the context and send it a signal.
//
// Currently, the service is not generic. It only runs git hooks.
//
// This expects the following Context variables.
// 	- ssh.Hostkeys ([]ssh.Signer): Host key, as an unparsed byte slice.
// 	- ssh.Address (string): Address/port
// 	- ssh.ServerConfig (*ssh.ServerConfig): The server config to use.
//
// This puts the following variables into the context:
// 	- ssh.Closer (chan interface{}): Send a message to this to shutdown the server.
func Serve(reg *cookoo.Registry, router *cookoo.Router, c cookoo.Context) cookoo.Interrupt {
	hostkeys := c.Get(HostKeys, []ssh.Signer{}).([]ssh.Signer)
	addr := c.Get(Address, "0.0.0.0:2223").(string)
	cfg := c.Get(ServerConfig, &ssh.ServerConfig{}).(*ssh.ServerConfig)

	for _, hk := range hostkeys {
		cfg.AddHostKey(hk)
		log.Infof(c, "Added hostkey.")
	}

	listener, err := net.Listen("tcp", addr)
	if err != nil {
		return err
	}

	srv := &server{
		c:       c,
		gitHome: "/home/git",
	}

	closer := make(chan interface{}, 1)
	c.Put("sshd.Closer", closer)

	log.Infof(c, "Listening on %s", addr)
	srv.listen(listener, cfg, closer)

	return nil
}
Exemple #23
0
// sendHistory sends the accumulated history to the writer.
func sendHistory(c cookoo.Context, writer ResponseWriterFlusher, data [][]byte) (int, error) {
	c.Logf("info", "Sending history.")
	var i int
	var d []byte
	for i, d = range data {
		_, err := writer.Write(d)
		if err != nil {
			c.Logf("warn", "Failed to write history message: %s", err)
			return i + 1, nil
		}
		writer.Flush()
	}
	return i + 1, nil
}
Exemple #24
0
// ServeTLS does the same as Serve, but with SSL support.
//
// If `server.Address` is not found in the context, the default address is
// `:4433`.
//
// Neither certFile nor keyFile are stored in the context. These values are
// considered to be security sensitive.
func ServeTLS(reg *cookoo.Registry, router *cookoo.Router, cxt cookoo.Context, certFile, keyFile string) {
	addr := cxt.Get("server.Address", ":4433").(string)

	server := &http.Server{Addr: addr}
	server.Handler = NewCookooHandler(reg, router, cxt)

	go handleSignals(router, cxt, server)
	err := server.ListenAndServeTLS(certFile, keyFile)
	if err != nil {
		cxt.Logf("error", "Caught error while serving: %s", err)
		if router.HasRoute("@crash") {
			router.HandleRequest("@crash", cxt, false)
		}
	}
}
Exemple #25
0
// Configure creates a new SSH configuration object.
//
// Config sets a PublicKeyCallback handler that forwards public key auth
// requests to the route named "pubkeyAuth".
//
// This assumes certain details about our environment, like the location of the
// host keys. It also provides only key-based authentication.
// ConfigureServerSshConfig
//
// Returns:
//  An *ssh.ServerConfig
func Configure(c cookoo.Context, p *cookoo.Params) (interface{}, cookoo.Interrupt) {
	router := c.Get("cookoo.Router", nil).(*cookoo.Router)

	cfg := &ssh.ServerConfig{
		PublicKeyCallback: func(m ssh.ConnMetadata, k ssh.PublicKey) (*ssh.Permissions, error) {
			c.Put("metadata", m)
			c.Put("key", k)

			pubkeyAuth := c.Get("route.sshd.pubkeyAuth", "pubkeyAuth").(string)
			err := router.HandleRequest(pubkeyAuth, c, true)
			return c.Get("pubkeyAuth", &ssh.Permissions{}).(*ssh.Permissions), err
		},
	}

	return cfg, nil
}
Exemple #26
0
// Get gets one or more environment variables and puts them into the context.
//
// Parameters passed in are of the form varname => defaultValue.
//
// 	r.Route("foo", "example").Does(envvar.Get).Using("HOME").WithDefault(".")
//
// As with all environment variables, the default value must be a string.
//
// WARNING: Since parameters are a map, order of processing is not
// guaranteed. If order is important, you'll need to call this command
// multiple times.
//
// For each parameter (`Using` clause), this command will look into the
// environment for a matching variable. If it finds one, it will add that
// variable to the context. If it does not find one, it will expand the
// default value (so you can set a default to something like "$HOST:$PORT")
// and also put the (unexpanded) default value back into the context in case
// any subsequent call to `os.Getenv` occurs.
func Get(c cookoo.Context, params *cookoo.Params) (interface{}, cookoo.Interrupt) {
	for name, def := range params.AsMap() {
		var val string
		if val = os.Getenv(name); len(val) == 0 {
			def := def.(string)
			val = os.ExpandEnv(def)
			// We want to make sure that any subsequent calls to Getenv
			// return the same default.
			os.Setenv(name, val)

		}
		c.Put(name, val)
		log.Debugf(c, "Name: %s, Val: %s", name, val)
	}
	return true, nil
}
Exemple #27
0
// Set takes the given names and values and puts them into both the context
// and the environment.
//
// Unlike Get, it does not try to retrieve the values from the environment
// first.
//
// Values are passed through os.ExpandEnv()
//
// There is no guarantee of insertion order. If multiple name/value pairs
// are given, they will be put into the context in whatever order they
// are retrieved from the underlying map.
//
// Params:
//   accessed as map[string]string
// Returns:
//   nothing, but inserts all name/value pairs into the context and the
//   environment.
func Set(c cookoo.Context, p *cookoo.Params) (interface{}, cookoo.Interrupt) {
	for name, def := range p.AsMap() {
		// Assume Nil means unset the value.
		if def == nil {
			def = ""
		}

		val := fmt.Sprintf("%v", def)
		val = os.ExpandEnv(val)
		log.Debugf(c, "Name: %s, Val: %s", name, val)

		os.Setenv(name, val)
		c.Put(name, val)
	}
	return true, nil

}
Exemple #28
0
// Publish sends a new message to a topic.
//
// Params:
// 	- topic (string): The topic to send to.
// 	- message ([]byte): The message to send.
// 	- withHistory (bool): Turn on history. Default is true. This only takes
// 		effect when the channel is created.
//
// Datasources:
// 	- This uses the 'drift.Medium' datasource.
//
// Returns:
//
func Publish(c cookoo.Context, p *cookoo.Params) (interface{}, cookoo.Interrupt) {
	hist := p.Get("withHistory", true).(bool)
	topic := p.Get("topic", "").(string)
	if len(topic) == 0 {
		return nil, errors.New("No topic supplied.")
	}

	medium, _ := getMedium(c)

	// Is there any reason to disallow empty messages?
	msg := p.Get("message", []byte{}).([]byte)
	c.Logf("info", "Msg: %s", msg)

	t := fetchOrCreateTopic(medium, topic, hist, DefaultMaxHistory)
	return nil, t.Publish(msg)

}
Exemple #29
0
func addFlagsToContext(flagset *flag.FlagSet, cxt cookoo.Context) {
	store := func(f *flag.Flag) {
		// fmt.Printf("Storing %s in context with value %s.\n", f.Name, f.Value.String())

		// Basically, we can tell the difference between booleans and strings, and that's it.
		// Other types are a loss.
		/*
			if f.IsBoolFlag != nil {
				cxt.Put(f.Name, f.Value.String() == "true")
			} else {
				cxt.Put(f.Name, f.Value.String())
			}
		*/
		cxt.Put(f.Name, f.Value.String())
	}

	flagset.VisitAll(store)
}
Exemple #30
0
// Return the path to the vendor directory.
func VendorPath(c cookoo.Context) (string, error) {
	vendor := c.Get("VendorDir", "vendor").(string)
	filename := c.Get("yaml", "glide.yaml").(string)
	cwd, err := os.Getwd()
	if err != nil {
		return "", err
	}

	// Find the directory that contains glide.yaml
	yamldir, err := glideWD(cwd, filename)
	if err != nil {
		return cwd, err
	}

	gopath := filepath.Join(yamldir, vendor)

	return gopath, nil
}