<commit_before>// Copyright 2011 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

var helpImportpath = &Command{
	UsageLine: "importpath",
	Short:     "description of import paths",
	Long: `
Many commands apply to a set of packages named by import paths:

	go action [importpath...]

An import path that is a rooted path or that begins with
a . or .. element is interpreted as a file system path and
denotes the package in that directory.

Otherwise, the import path P denotes the package found in
the directory DIR/src/P for some DIR listed in the GOPATH
environment variable (see 'go help gopath').

If no import paths are given, the action applies to the
package in the current directory.

The special import path "all" expands to all package directories
found in all the GOPATH trees.  For example, 'go list all'
lists all the packages on the local system.

The special import path "std" is like all but expands to just the
packages in the standard Go library.

An import path can also name a package to be downloaded from
a remote repository.  Run 'go help remote' for details.

Every package in a program must have a unique import path.
By convention, this is arranged by starting each path with a
unique prefix that belongs to you.  For example, paths used
internally at Google all begin with 'google', and paths
denoting remote repositories begin with the path to the code,
such as 'project.googlecode.com/'.
	`,
}

var helpRemote = &Command{
	UsageLine: "remote",
	Short:     "remote import path syntax",
	Long: `

An import path (see 'go help importpath') denotes a package
stored in the local file system.  Certain import paths also
describe how to obtain the source code for the package using
a revision control system.

A few common code hosting sites have special syntax:

	BitBucket (Mercurial)

		import "bitbucket.org/user/project"
		import "bitbucket.org/user/project/sub/directory"

	GitHub (Git)

		import "github.com/user/project"
		import "github.com/user/project/sub/directory"

	Google Code Project Hosting (Git, Mercurial, Subversion)

		import "project.googlecode.com/git"
		import "project.googlecode.com/git/sub/directory"

		import "project.googlecode.com/hg"
		import "project.googlecode.com/hg/sub/directory"

		import "project.googlecode.com/svn/trunk"
		import "project.googlecode.com/svn/trunk/sub/directory"

	Launchpad (Bazaar)

		import "launchpad.net/project"
		import "launchpad.net/project/series"
		import "launchpad.net/project/series/sub/directory"

		import "launchpad.net/~user/project/branch"
		import "launchpad.net/~user/project/branch/sub/directory"

For code hosted on other servers, an import path of the form

	repository.vcs/path

specifies the given repository, with or without the .vcs suffix,
using the named version control system, and then the path inside
that repository.  The supported version control systems are:

	Bazaar      .bzr
	Git         .git
	Mercurial   .hg
	Subversion  .svn

For example,

	import "example.org/user/foo.hg"

denotes the root directory of the Mercurial repository at
example.org/user/foo or foo.hg, and

	import "example.org/repo.git/foo/bar"

denotes the foo/bar directory of the Git repository at
example.com/repo or repo.git.

When a version control system supports multiple protocols,
each is tried in turn when downloading.  For example, a Git
download tries git://, then https://, then http://.

New downloaded packages are written to the first directory
listed in the GOPATH environment variable (see 'go help gopath').

The go command attempts to download the version of the
package appropriate for the Go release being used.
Run 'go help install' for more.
	`,
}

var helpGopath = &Command{
	UsageLine: "gopath",
	Short:     "GOPATH environment variable",
	Long: `
The GOPATH environment variable lists places to look for Go code.
On Unix, the value is a colon-separated string.
On Windows, the value is a semicolon-separated string.
On Plan 9, the value is a list.

GOPATH must be set to build and install packages outside the
standard Go tree.

Each directory listed in GOPATH must have a prescribed structure:

The src/ directory holds source code.  The path below 'src'
determines the import path or executable name.

The pkg/ directory holds installed package objects.
As in the Go tree, each target operating system and
architecture pair has its own subdirectory of pkg
(pkg/GOOS_GOARCH).

If DIR is a directory listed in the GOPATH, a package with
source in DIR/src/foo/bar can be imported as "foo/bar" and
has its compiled form installed to "DIR/pkg/GOOS_GOARCH/foo/bar.a".

The bin/ directory holds compiled commands.
Each command is named for its source directory, but only
the final element, not the entire path.  That is, the
command with source in DIR/src/foo/quux is installed into
DIR/bin/quux, not DIR/bin/foo/quux.  The foo/ is stripped
so that you can add DIR/bin to your PATH to get at the
installed commands.

Here's an example directory layout:

    GOPATH=/home/user/gocode

    /home/user/gocode/
        src/
            foo/
                bar/               (go code in package bar)
                    x.go
                quux/              (go code in package main)
                    y.go
        bin/
            quux                   (installed command)
		pkg/
		    linux_amd64/
		        foo/
		            bar.a          (installed package object)

Go searches each directory listed in GOPATH to find source code,
but new packages are always downloaded into the first directory
in the list.
	`,
}
<commit_msg>cmd/go: use spaces consistently in help message<commit_after>// Copyright 2011 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

var helpImportpath = &Command{
	UsageLine: "importpath",
	Short:     "description of import paths",
	Long: `
Many commands apply to a set of packages named by import paths:

	go action [importpath...]

An import path that is a rooted path or that begins with
a . or .. element is interpreted as a file system path and
denotes the package in that directory.

Otherwise, the import path P denotes the package found in
the directory DIR/src/P for some DIR listed in the GOPATH
environment variable (see 'go help gopath').

If no import paths are given, the action applies to the
package in the current directory.

The special import path "all" expands to all package directories
found in all the GOPATH trees.  For example, 'go list all'
lists all the packages on the local system.

The special import path "std" is like all but expands to just the
packages in the standard Go library.

An import path can also name a package to be downloaded from
a remote repository.  Run 'go help remote' for details.

Every package in a program must have a unique import path.
By convention, this is arranged by starting each path with a
unique prefix that belongs to you.  For example, paths used
internally at Google all begin with 'google', and paths
denoting remote repositories begin with the path to the code,
such as 'project.googlecode.com/'.
	`,
}

var helpRemote = &Command{
	UsageLine: "remote",
	Short:     "remote import path syntax",
	Long: `

An import path (see 'go help importpath') denotes a package
stored in the local file system.  Certain import paths also
describe how to obtain the source code for the package using
a revision control system.

A few common code hosting sites have special syntax:

	BitBucket (Mercurial)

		import "bitbucket.org/user/project"
		import "bitbucket.org/user/project/sub/directory"

	GitHub (Git)

		import "github.com/user/project"
		import "github.com/user/project/sub/directory"

	Google Code Project Hosting (Git, Mercurial, Subversion)

		import "project.googlecode.com/git"
		import "project.googlecode.com/git/sub/directory"

		import "project.googlecode.com/hg"
		import "project.googlecode.com/hg/sub/directory"

		import "project.googlecode.com/svn/trunk"
		import "project.googlecode.com/svn/trunk/sub/directory"

	Launchpad (Bazaar)

		import "launchpad.net/project"
		import "launchpad.net/project/series"
		import "launchpad.net/project/series/sub/directory"

		import "launchpad.net/~user/project/branch"
		import "launchpad.net/~user/project/branch/sub/directory"

For code hosted on other servers, an import path of the form

	repository.vcs/path

specifies the given repository, with or without the .vcs suffix,
using the named version control system, and then the path inside
that repository.  The supported version control systems are:

	Bazaar      .bzr
	Git         .git
	Mercurial   .hg
	Subversion  .svn

For example,

	import "example.org/user/foo.hg"

denotes the root directory of the Mercurial repository at
example.org/user/foo or foo.hg, and

	import "example.org/repo.git/foo/bar"

denotes the foo/bar directory of the Git repository at
example.com/repo or repo.git.

When a version control system supports multiple protocols,
each is tried in turn when downloading.  For example, a Git
download tries git://, then https://, then http://.

New downloaded packages are written to the first directory
listed in the GOPATH environment variable (see 'go help gopath').

The go command attempts to download the version of the
package appropriate for the Go release being used.
Run 'go help install' for more.
	`,
}

var helpGopath = &Command{
	UsageLine: "gopath",
	Short:     "GOPATH environment variable",
	Long: `
The GOPATH environment variable lists places to look for Go code.
On Unix, the value is a colon-separated string.
On Windows, the value is a semicolon-separated string.
On Plan 9, the value is a list.

GOPATH must be set to build and install packages outside the
standard Go tree.

Each directory listed in GOPATH must have a prescribed structure:

The src/ directory holds source code.  The path below 'src'
determines the import path or executable name.

The pkg/ directory holds installed package objects.
As in the Go tree, each target operating system and
architecture pair has its own subdirectory of pkg
(pkg/GOOS_GOARCH).

If DIR is a directory listed in the GOPATH, a package with
source in DIR/src/foo/bar can be imported as "foo/bar" and
has its compiled form installed to "DIR/pkg/GOOS_GOARCH/foo/bar.a".

The bin/ directory holds compiled commands.
Each command is named for its source directory, but only
the final element, not the entire path.  That is, the
command with source in DIR/src/foo/quux is installed into
DIR/bin/quux, not DIR/bin/foo/quux.  The foo/ is stripped
so that you can add DIR/bin to your PATH to get at the
installed commands.

Here's an example directory layout:

    GOPATH=/home/user/gocode

    /home/user/gocode/
        src/
            foo/
                bar/               (go code in package bar)
                    x.go
                quux/              (go code in package main)
                    y.go
        bin/
            quux                   (installed command)
        pkg/
            linux_amd64/
                foo/
                    bar.a          (installed package object)

Go searches each directory listed in GOPATH to find source code,
but new packages are always downloaded into the first directory
in the list.
	`,
}
<|endoftext|>


<commit_before>// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

/*

Vet examines Go source code and reports suspicious constructs, such as Printf
calls whose arguments do not align with the format string. Vet uses heuristics
that do not guarantee all reports are genuine problems, but it can find errors
not caught by the compilers.

It can be invoked three ways:

By package, from the go tool:
	go vet package/path/name
vets the package whose path is provided.

By files:
	go tool vet source/directory/*.go
vets the files named, all of which must be in the same package.

By directory:
	go tool vet source/directory
recursively descends the directory, vetting each package it finds.

Vet's exit code is 2 for erroneous invocation of the tool, 1 if a
problem was reported, and 0 otherwise. Note that the tool does not
check every possible problem and depends on unreliable heuristics
so it should be used as guidance only, not as a firm indicator of
program correctness.

By default the -all flag is set so all checks are performed.
If any flags are explicitly set to true, only those tests are run. Conversely, if
any flag is explicitly set to false, only those tests are disabled.  Thus -printf=true
runs the printf check, -printf=false runs all checks except the printf check.

Available checks:

Assembly declarations

Flag: -asmdecl

Mismatches between assembly files and Go function declarations.

Useless assignments

Flag: -assign

Check for useless assignments.

Atomic mistakes

Flag: -atomic

Common mistaken usages of the sync/atomic package.

Boolean conditions

Flag: -bool

Mistakes involving boolean operators.

Build tags

Flag: -buildtags

Badly formed or misplaced +build tags.

Invalid uses of cgo

Flag: -cgocall

Detect some violations of the cgo pointer passing rules.

Unkeyed composite literals

Flag: -composites

Composite struct literals that do not use the field-keyed syntax.

Copying locks

Flag: -copylocks

Locks that are erroneously passed by value.

Tests, benchmarks and documentation examples

Flag: -tests

Mistakes involving tests including functions with incorrect names or signatures
and example tests that document identifiers not in the package.

Failure to call the cancelation function returned by context.WithCancel.

Flag: -lostcancel

The cancelation function returned by context.WithCancel, WithTimeout,
and WithDeadline must be called or the new context will remain live
until its parent context is cancelled.
(The background context is never cancelled.)

Methods

Flag: -methods

Non-standard signatures for methods with familiar names, including:
	Format GobEncode GobDecode MarshalJSON MarshalXML
	Peek ReadByte ReadFrom ReadRune Scan Seek
	UnmarshalJSON UnreadByte UnreadRune WriteByte
	WriteTo

Nil function comparison

Flag: -nilfunc

Comparisons between functions and nil.

Printf family

Flag: -printf

Suspicious calls to functions in the Printf family, including any functions
with these names, disregarding case:
	Print Printf Println
	Fprint Fprintf Fprintln
	Sprint Sprintf Sprintln
	Error Errorf
	Fatal Fatalf
	Log Logf
	Panic Panicf Panicln
The -printfuncs flag can be used to redefine this list.
If the function name ends with an 'f', the function is assumed to take
a format descriptor string in the manner of fmt.Printf. If not, vet
complains about arguments that look like format descriptor strings.

It also checks for errors such as using a Writer as the first argument of
Printf.

Struct tags

Range loop variables

Flag: -rangeloops

Incorrect uses of range loop variables in closures.

Shadowed variables

Flag: -shadow=false (experimental; must be set explicitly)

Variables that may have been unintentionally shadowed.

Shifts

Flag: -shift

Shifts equal to or longer than the variable's length.

Flag: -structtags

Struct tags that do not follow the format understood by reflect.StructTag.Get.
Well-known encoding struct tags (json, xml) used with unexported fields.

Unreachable code

Flag: -unreachable

Unreachable code.

Misuse of unsafe Pointers

Flag: -unsafeptr

Likely incorrect uses of unsafe.Pointer to convert integers to pointers.
A conversion from uintptr to unsafe.Pointer is invalid if it implies that
there is a uintptr-typed word in memory that holds a pointer value,
because that word will be invisible to stack copying and to the garbage
collector.

Unused result of certain function calls

Flag: -unusedresult

Calls to well-known functions and methods that return a value that is
discarded.  By default, this includes functions like fmt.Errorf and
fmt.Sprintf and methods like String and Error. The flags -unusedfuncs
and -unusedstringmethods control the set.

Other flags

These flags configure the behavior of vet:

	-all (default true)
		Enable all non-experimental checks.
	-v
		Verbose mode
	-printfuncs
		A comma-separated list of print-like function names
		to supplement the standard list.
		For more information, see the discussion of the -printf flag.
	-shadowstrict
		Whether to be strict about shadowing; can be noisy.
*/
package main
<commit_msg>cmd/vet: fix documentation for -structtags<commit_after>// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

/*

Vet examines Go source code and reports suspicious constructs, such as Printf
calls whose arguments do not align with the format string. Vet uses heuristics
that do not guarantee all reports are genuine problems, but it can find errors
not caught by the compilers.

It can be invoked three ways:

By package, from the go tool:
	go vet package/path/name
vets the package whose path is provided.

By files:
	go tool vet source/directory/*.go
vets the files named, all of which must be in the same package.

By directory:
	go tool vet source/directory
recursively descends the directory, vetting each package it finds.

Vet's exit code is 2 for erroneous invocation of the tool, 1 if a
problem was reported, and 0 otherwise. Note that the tool does not
check every possible problem and depends on unreliable heuristics
so it should be used as guidance only, not as a firm indicator of
program correctness.

By default the -all flag is set so all checks are performed.
If any flags are explicitly set to true, only those tests are run. Conversely, if
any flag is explicitly set to false, only those tests are disabled.  Thus -printf=true
runs the printf check, -printf=false runs all checks except the printf check.

Available checks:

Assembly declarations

Flag: -asmdecl

Mismatches between assembly files and Go function declarations.

Useless assignments

Flag: -assign

Check for useless assignments.

Atomic mistakes

Flag: -atomic

Common mistaken usages of the sync/atomic package.

Boolean conditions

Flag: -bool

Mistakes involving boolean operators.

Build tags

Flag: -buildtags

Badly formed or misplaced +build tags.

Invalid uses of cgo

Flag: -cgocall

Detect some violations of the cgo pointer passing rules.

Unkeyed composite literals

Flag: -composites

Composite struct literals that do not use the field-keyed syntax.

Copying locks

Flag: -copylocks

Locks that are erroneously passed by value.

Tests, benchmarks and documentation examples

Flag: -tests

Mistakes involving tests including functions with incorrect names or signatures
and example tests that document identifiers not in the package.

Failure to call the cancelation function returned by context.WithCancel.

Flag: -lostcancel

The cancelation function returned by context.WithCancel, WithTimeout,
and WithDeadline must be called or the new context will remain live
until its parent context is cancelled.
(The background context is never cancelled.)

Methods

Flag: -methods

Non-standard signatures for methods with familiar names, including:
	Format GobEncode GobDecode MarshalJSON MarshalXML
	Peek ReadByte ReadFrom ReadRune Scan Seek
	UnmarshalJSON UnreadByte UnreadRune WriteByte
	WriteTo

Nil function comparison

Flag: -nilfunc

Comparisons between functions and nil.

Printf family

Flag: -printf

Suspicious calls to functions in the Printf family, including any functions
with these names, disregarding case:
	Print Printf Println
	Fprint Fprintf Fprintln
	Sprint Sprintf Sprintln
	Error Errorf
	Fatal Fatalf
	Log Logf
	Panic Panicf Panicln
The -printfuncs flag can be used to redefine this list.
If the function name ends with an 'f', the function is assumed to take
a format descriptor string in the manner of fmt.Printf. If not, vet
complains about arguments that look like format descriptor strings.

It also checks for errors such as using a Writer as the first argument of
Printf.

Struct tags

Range loop variables

Flag: -rangeloops

Incorrect uses of range loop variables in closures.

Shadowed variables

Flag: -shadow=false (experimental; must be set explicitly)

Variables that may have been unintentionally shadowed.

Shifts

Flag: -shift

Shifts equal to or longer than the variable's length.

Struct tags

Flag: -structtags

Struct tags that do not follow the format understood by reflect.StructTag.Get.
Well-known encoding struct tags (json, xml) used with unexported fields.

Unreachable code

Flag: -unreachable

Unreachable code.

Misuse of unsafe Pointers

Flag: -unsafeptr

Likely incorrect uses of unsafe.Pointer to convert integers to pointers.
A conversion from uintptr to unsafe.Pointer is invalid if it implies that
there is a uintptr-typed word in memory that holds a pointer value,
because that word will be invisible to stack copying and to the garbage
collector.

Unused result of certain function calls

Flag: -unusedresult

Calls to well-known functions and methods that return a value that is
discarded.  By default, this includes functions like fmt.Errorf and
fmt.Sprintf and methods like String and Error. The flags -unusedfuncs
and -unusedstringmethods control the set.

Other flags

These flags configure the behavior of vet:

	-all (default true)
		Enable all non-experimental checks.
	-v
		Verbose mode
	-printfuncs
		A comma-separated list of print-like function names
		to supplement the standard list.
		For more information, see the discussion of the -printf flag.
	-shadowstrict
		Whether to be strict about shadowing; can be noisy.
*/
package main
<|endoftext|>


<commit_before>package isogrids

import (
	"image/color"
	"net/http"

	"github.com/ajstarks/svgo"
	"github.com/taironas/tinygraphs/draw"
)

// Hexa builds an image with lines x lines grids of half diagonals in the form of an hexagon
func Hexa16(w http.ResponseWriter, key string, colors []color.RGBA, size, lines int) {
	canvas := svg.New(w)
	canvas.Start(size, size)

	fringeSize := size / lines
	distance := distanceTo3rdPoint(fringeSize)
	lines = size / fringeSize
	offset := ((fringeSize - distance) * lines) / 2

	fillTriangle := triangleColors(0, key, colors, lines)

	for xL := 0; xL < lines/2; xL++ {
		for yL := 0; yL < lines; yL++ {

			if isOutsideHexagon(xL, yL, lines) {
				continue
			}

			fill1 := fillTransparent()
			fill2 := fillTransparent()

			var x1, x2, y1, y2, y3 int
			if (xL % 2) == 0 {
				x1, y1, x2, y2, _, y3 = right1stTriangle(xL, yL, fringeSize, distance)
			} else {
				x1, y1, x2, y2, _, y3 = left1stTriangle(xL, yL, fringeSize, distance)
			}

			xs := []int{x2 + offset, x1 + offset, x2 + offset}
			ys := []int{y1, y2, y3}

			tpL := newTrianglePosition(xL, yL, left)
			tpR := newTrianglePosition(xL, yL, right)

			if (xL%2) == 0 && tpL.isInTriangle() {
				rid := tpL.rotationId()
				canvas.Polygon(xs, ys, fillTriangle[rid])
			} else if (xL%2) != 0 && tpR.isInTriangle() {
				rid := tpR.rotationId()
				canvas.Polygon(xs, ys, fillTriangle[rid])
			} else {
				canvas.Polygon(xs, ys, fill1)
			}

			xsMirror := mirrorCoordinates(xs, lines, distance, offset*2)
			xLMirror := lines - xL - 1
			yLMirror := yL

			tpLMirror := newTrianglePosition(xLMirror, yLMirror, left)
			tpRMirror := newTrianglePosition(xLMirror, yLMirror, right)

			if (xLMirror%2) == 0 && tpLMirror.isInTriangle() {
				rid := tpLMirror.rotationId()
				canvas.Polygon(xsMirror, ys, fillTriangle[rid])
			} else if (xLMirror%2) != 0 && tpRMirror.isInTriangle() {
				rid := tpRMirror.rotationId()
				canvas.Polygon(xsMirror, ys, fillTriangle[rid])
			} else {
				canvas.Polygon(xsMirror, ys, fill1)
			}

			var x11, x12, y11, y12, y13 int
			if (xL % 2) == 0 {
				x11, y11, x12, y12, _, y13 = left2ndTriangle(xL, yL, fringeSize, distance)

				// in order to have a perfect hexagon,
				// we make sure that the previous triangle and this one touch each other in this point.
				y12 = y3
			} else {
				x11, y11, x12, y12, _, y13 = right2ndTriangle(xL, yL, fringeSize, distance)

				// in order to have a perfect hexagon,
				// we make sure that the previous triangle and this one touch each other in this point.
				y12 = y1 + fringeSize
			}

			xs1 := []int{x12 + offset, x11 + offset, x12 + offset}
			ys1 := []int{y11, y12, y13}
			// triangles that go to the right
			tpL = newTrianglePosition(xL, yL, left)
			tpR = newTrianglePosition(xL, yL, right)

			if (xL%2) != 0 && tpL.isInTriangle() {
				rid := tpL.rotationId()
				canvas.Polygon(xs1, ys1, fillTriangle[rid])
			} else if (xL%2) == 0 && tpR.isInTriangle() {
				rid := tpR.rotationId()
				canvas.Polygon(xs1, ys1, fillTriangle[rid])
			} else {
				canvas.Polygon(xs1, ys1, fill2)
			}

			xs1 = mirrorCoordinates(xs1, lines, distance, offset*2)
			tpLMirror = newTrianglePosition(xLMirror, yLMirror, left)
			tpRMirror = newTrianglePosition(xLMirror, yLMirror, right)

			if (xL%2) == 0 && tpLMirror.isInTriangle() {
				rid := tpLMirror.rotationId()
				canvas.Polygon(xs1, ys1, fillTriangle[rid])
			} else if (xL%2) != 0 && tpRMirror.isInTriangle() {
				rid := tpRMirror.rotationId()
				canvas.Polygon(xs1, ys1, fillTriangle[rid])
			} else {
				canvas.Polygon(xs1, ys1, fill2)
			}
		}
	}
	canvas.End()
}

// triangleColors returns an array of strings, one for each sub triangle.
// Each string corresponds to an svg color.
// Colors are selected from the array of colors passed as parameter and the key.
func triangleColors(id int, key string, colors []color.RGBA, lines int) (tColors []string) {

	for _, t := range triangles[id] {
		x := t.x
		y := t.y
		tColors = append(tColors, draw.FillFromRGBA(draw.PickColor(key, colors, (x+3*y+lines)%15)))
	}
	return
}
<commit_msg>add canFill function.<commit_after>package isogrids

import (
	"errors"
	"image/color"
	"net/http"

	"github.com/ajstarks/svgo"
	"github.com/taironas/tinygraphs/draw"
)

// Hexa builds an image with lines x lines grids of half diagonals in the form of an hexagon
func Hexa16(w http.ResponseWriter, key string, colors []color.RGBA, size, lines int) {
	canvas := svg.New(w)
	canvas.Start(size, size)

	fringeSize := size / lines
	distance := distanceTo3rdPoint(fringeSize)
	lines = size / fringeSize
	offset := ((fringeSize - distance) * lines) / 2

	fillTriangle := triangleColors(0, key, colors, lines)

	for xL := 0; xL < lines/2; xL++ {
		for yL := 0; yL < lines; yL++ {

			if isOutsideHexagon(xL, yL, lines) {
				continue
			}

			fill1 := fillTransparent()
			fill2 := fillTransparent()

			var x1, x2, y1, y2, y3 int
			if (xL % 2) == 0 {
				x1, y1, x2, y2, _, y3 = right1stTriangle(xL, yL, fringeSize, distance)
			} else {
				x1, y1, x2, y2, _, y3 = left1stTriangle(xL, yL, fringeSize, distance)
			}

			xs := []int{x2 + offset, x1 + offset, x2 + offset}
			ys := []int{y1, y2, y3}

			isLeft := func(v int) bool { return (v % 2) == 0 }
			isRight := func(v int) bool { return (v % 2) != 0 }

			if fill, err := canFill(xL, yL, fillTriangle, isLeft, isRight); err != nil {
				canvas.Polygon(xs, ys, fill1)
			} else {
				canvas.Polygon(xs, ys, fill)
			}

			xsMirror := mirrorCoordinates(xs, lines, distance, offset*2)
			xLMirror := lines - xL - 1
			yLMirror := yL

			if fill, err := canFill(xLMirror, yLMirror, fillTriangle, isLeft, isRight); err != nil {
				canvas.Polygon(xsMirror, ys, fill1)
			} else {
				canvas.Polygon(xsMirror, ys, fill)
			}

			var x11, x12, y11, y12, y13 int
			if (xL % 2) == 0 {
				x11, y11, x12, y12, _, y13 = left2ndTriangle(xL, yL, fringeSize, distance)

				// in order to have a perfect hexagon,
				// we make sure that the previous triangle and this one touch each other in this point.
				y12 = y3
			} else {
				x11, y11, x12, y12, _, y13 = right2ndTriangle(xL, yL, fringeSize, distance)

				// in order to have a perfect hexagon,
				// we make sure that the previous triangle and this one touch each other in this point.
				y12 = y1 + fringeSize
			}

			xs1 := []int{x12 + offset, x11 + offset, x12 + offset}
			ys1 := []int{y11, y12, y13}

			// triangles that go to the right

			if fill, err := canFill(xL, yL, fillTriangle, isRight, isLeft); err != nil {
				canvas.Polygon(xs1, ys1, fill2)
			} else {
				canvas.Polygon(xs1, ys1, fill)
			}

			xs1 = mirrorCoordinates(xs1, lines, distance, offset*2)

			if fill, err := canFill(xLMirror, yLMirror, fillTriangle, isRight, isLeft); err != nil {
				canvas.Polygon(xs1, ys1, fill2)
			} else {
				canvas.Polygon(xs1, ys1, fill)
			}
		}
	}
	canvas.End()
}

// triangleColors returns an array of strings, one for each sub triangle.
// Each string corresponds to an svg color.
// Colors are selected from the array of colors passed as parameter and the key.
func triangleColors(id int, key string, colors []color.RGBA, lines int) (tColors []string) {

	for _, t := range triangles[id] {
		x := t.x
		y := t.y
		tColors = append(tColors, draw.FillFromRGBA(draw.PickColor(key, colors, (x+3*y+lines)%15)))
	}
	return
}

func canFill(x, y int, fills []string, isLeft func(x int) bool, isRight func(x int) bool) (string, error) {
	l := newTrianglePosition(x, y, left)
	r := newTrianglePosition(x, y, right)

	if isLeft(x) && l.isInTriangle() {
		rid := l.rotationId()
		return fills[rid], nil
	} else if isRight(x) && r.isInTriangle() {
		rid := r.rotationId()
		return fills[rid], nil
	}
	return "", errors.New("cannot find svg fill for given position.")
}
<|endoftext|>


<commit_before>package hostqueue

import (
  "appengine"
  "appengine/datastore"
  "encoding/json"
  "github.com/go-martini/martini"
  "net/http"
  "time"
  )

type Host struct {
	Id int64 `json:"id" datastore:"-"`
	HostName string `json:"hostName"`
	Emails []string `json:"emails"`
	TimesHosted int64 `json:"timesHosted"`
	LastHosted time.Time `json:"lastHosted"`
}

type Group struct {
    Id   int64  `json: "id" datastore:"-"`
    GroupName string `json:"groupName"`
    GroupEmail string `json:"groupEmail"`
    Hosts []Host `json:"hosts"`
    Next Host `json:"next"`
}

// Add creates a new quote given the fields in AddRequest, stores it in the
// datastore, and returns it.
func  Add(c martini.Context, r *http.Request) (*Group, error) {
  // We set the same parent key on every Quote entity to ensure each Quote
  // is in the same entity group. Queries across the single entity group
  // will be consistent.
  ctx := appengine.NewContext(r)

  var group *Group

  err := json.NewDecoder(r.Body).Decode(&group)
  if err != nil {
    panic(err)
  }
  k := g.key(ctx)

  //TODO: Oh my, trusing input from a user!!
  k, err := datastore.Put(ctx, k, g)
  if err != nil {
    return nil, err
  }
  g.Id = k.IntID()
  return g, nil
}

//Datastore methods from:  http://stevenlu.com/posts/2015/03/23/google-datastore-with-golang/
func (group *Group) key(c appengine.Context) *datastore.Key {
  // if there is no Id, we want to generate an "incomplete"
  // one and let datastore determine the key/Id for us
  //if group.Id == 0 {
  //  return datastore.NewIncompleteKey(c, "Group", nil)
  //}

  // if Id is already set, we'll just build the Key based
  // on the one provided.
  //return datastore.NewKey(c, "Group", "", group.Id, nil)

  return datastore.NewKey(c, "Group", "default_group", 0, nil)
}

func (group *Group) save(ctx appengine.Context) error {
  // reference the key function and generate it
  // accordingly basically its isNew true/false
  k, err := datastore.Put(ctx, group.key(ctx), group)
  if err != nil {
    return err
  }

  // The Id on the model is not prepopulated so we'll have
  // to append manually
  group.Id = k.IntID()
  return nil
}

func GetGroups(ctx appengine.Context) ([]Group, error) {
  q := datastore.NewQuery("Group")

  var groups []Group
  keys, err := q.GetAll(ctx, &groups)
  if err != nil {
    return nil, err
  }

  for i := 0; i < len(groups); i++ {
    groups[i].Id = keys[i].IntID()
  }

  return groups, nil
}

func  SendEmail(c martini.Context, w http.ResponseWriter, r *http.Request) {
  ctx := appengine.NewContext(r)
  w.Header().Set("Content-Type", "text/plain; charset=utf-8")
  g, err := GetGroups(ctx)

  if err != nil {
  	panic("Error getting groups: ", err)
  }

  for _, element := range g {
  	sendReminder(element, r)
  }
}

func init() {
   m := martini.Classic()

   m.Get("/_ah/mail/", IncomingMail)
   m.Post("/group/add", Add)
   m.Get("/group/action/email", SendEmail)

   m.Get("/", func() string {
     return "hostq"
   })

   http.Handle("/", m)
}
<commit_msg>changed g to group, fixed compiler errors.<commit_after>package hostqueue

import (
  "appengine"
  "appengine/datastore"
  "encoding/json"
  "github.com/go-martini/martini"
  "net/http"
  "time"
  )

type Host struct {
	Id int64 `json:"id" datastore:"-"`
	HostName string `json:"hostName"`
	Emails []string `json:"emails"`
	TimesHosted int64 `json:"timesHosted"`
	LastHosted time.Time `json:"lastHosted"`
}

type Group struct {
    Id   int64  `json: "id" datastore:"-"`
    GroupName string `json:"groupName"`
    GroupEmail string `json:"groupEmail"`
    Hosts []Host `json:"hosts"`
    Next Host `json:"next"`
}

// Add creates a new quote given the fields in AddRequest, stores it in the
// datastore, and returns it.
func  Add(c martini.Context, r *http.Request) (*Group, error) {
  // We set the same parent key on every Quote entity to ensure each Quote
  // is in the same entity group. Queries across the single entity group
  // will be consistent.
  ctx := appengine.NewContext(r)

  var group *Group

  err := json.NewDecoder(r.Body).Decode(&group)
  if err != nil {
    panic(err)
  }
  k := group.key(ctx)

  //TODO: Oh my, trusing input from a user!!
  k, err = datastore.Put(ctx, k, group)
  if err != nil {
    return nil, err
  }
  group.Id = k.IntID()
  return group, nil
}

//Datastore methods from:  http://stevenlu.com/posts/2015/03/23/google-datastore-with-golang/
func (group *Group) key(c appengine.Context) *datastore.Key {
  // if there is no Id, we want to generate an "incomplete"
  // one and let datastore determine the key/Id for us
  //if group.Id == 0 {
  //  return datastore.NewIncompleteKey(c, "Group", nil)
  //}

  // if Id is already set, we'll just build the Key based
  // on the one provided.
  //return datastore.NewKey(c, "Group", "", group.Id, nil)

  return datastore.NewKey(c, "Group", "default_group", 0, nil)
}

func (group *Group) save(ctx appengine.Context) error {
  // reference the key function and generate it
  // accordingly basically its isNew true/false
  k, err := datastore.Put(ctx, group.key(ctx), group)
  if err != nil {
    return err
  }

  // The Id on the model is not prepopulated so we'll have
  // to append manually
  group.Id = k.IntID()
  return nil
}

func GetGroups(ctx appengine.Context) ([]Group, error) {
  q := datastore.NewQuery("Group")

  var groups []Group
  keys, err := q.GetAll(ctx, &groups)
  if err != nil {
    return nil, err
  }

  for i := 0; i < len(groups); i++ {
    groups[i].Id = keys[i].IntID()
  }

  return groups, nil
}

func  SendEmail(c martini.Context, w http.ResponseWriter, r *http.Request) {
  ctx := appengine.NewContext(r)
  w.Header().Set("Content-Type", "text/plain; charset=utf-8")
  g, err := GetGroups(ctx)

  if err != nil {
  	panic(err)
  }

  for _, element := range g {
  	sendReminder(element, r)
  }
}

func init() {
   m := martini.Classic()

   m.Get("/_ah/mail/", IncomingMail)
   m.Post("/group/add", Add)
   m.Get("/group/action/email", SendEmail)

   m.Get("/", func() string {
     return "hostq"
   })

   http.Handle("/", m)
}
<|endoftext|>


<commit_before>// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"bytes"
	"exec"
	"io"
	"log"
	"os"
	"runtime"
	"strings"
)

// run is a simple wrapper for exec.Run/Close
func run(envv []string, dir string, argv ...string) os.Error {
	if *verbose {
		log.Println("run", argv)
	}
	if runtime.GOOS == "windows" && isBash(argv[0]) {
		// shell script cannot be executed directly on Windows.
		argv = append([]string{"bash", "-c"}, argv...)
	}
	bin, err := lookPath(argv[0])
	if err != nil {
		return err
	}
	p, err := exec.Run(bin, argv, envv, dir,
		exec.DevNull, exec.DevNull, exec.PassThrough)
	if err != nil {
		return err
	}
	return p.Close()
}

// runLog runs a process and returns the combined stdout/stderr,
// as well as writing it to logfile (if specified).
func runLog(envv []string, logfile, dir string, argv ...string) (output string, exitStatus int, err os.Error) {
	if *verbose {
		log.Println("runLog", argv)
	}
	if runtime.GOOS == "windows" && isBash(argv[0]) {
		// shell script cannot be executed directly on Windows.
		argv = append([]string{"bash", "-c"}, argv...)
	}
	bin, err := lookPath(argv[0])
	if err != nil {
		return
	}
	p, err := exec.Run(bin, argv, envv, dir,
		exec.DevNull, exec.Pipe, exec.MergeWithStdout)
	if err != nil {
		return
	}
	defer p.Close()
	b := new(bytes.Buffer)
	var w io.Writer = b
	if logfile != "" {
		f, err := os.OpenFile(logfile, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
		if err != nil {
			return
		}
		defer f.Close()
		w = io.MultiWriter(f, b)
	}
	_, err = io.Copy(w, p.Stdout)
	if err != nil {
		return
	}
	wait, err := p.Wait(0)
	if err != nil {
		return
	}
	return b.String(), wait.WaitStatus.ExitStatus(), nil
}

// lookPath looks for cmd in $PATH if cmd does not begin with / or ./ or ../.
func lookPath(cmd string) (string, os.Error) {
	if strings.HasPrefix(cmd, "/") || strings.HasPrefix(cmd, "./") || strings.HasPrefix(cmd, "../") {
		return cmd, nil
	}
	return exec.LookPath(cmd)
}

// isBash determines if name refers to a shell script.
func isBash(name string) bool {
	// TODO(brainman): perhaps it is too simple and needs better check.
	return strings.HasSuffix(name, ".bash")
}
<commit_msg>gobuilder: remove some windows-specificity<commit_after>// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"bytes"
	"exec"
	"io"
	"log"
	"os"
	"strings"
)

// run is a simple wrapper for exec.Run/Close
func run(envv []string, dir string, argv ...string) os.Error {
	if *verbose {
		log.Println("run", argv)
	}
	argv = useBash(argv)
	bin, err := lookPath(argv[0])
	if err != nil {
		return err
	}
	p, err := exec.Run(bin, argv, envv, dir,
		exec.DevNull, exec.DevNull, exec.PassThrough)
	if err != nil {
		return err
	}
	return p.Close()
}

// runLog runs a process and returns the combined stdout/stderr,
// as well as writing it to logfile (if specified).
func runLog(envv []string, logfile, dir string, argv ...string) (output string, exitStatus int, err os.Error) {
	if *verbose {
		log.Println("runLog", argv)
	}
	argv = useBash(argv)
	bin, err := lookPath(argv[0])
	if err != nil {
		return
	}
	p, err := exec.Run(bin, argv, envv, dir,
		exec.DevNull, exec.Pipe, exec.MergeWithStdout)
	if err != nil {
		return
	}
	defer p.Close()
	b := new(bytes.Buffer)
	var w io.Writer = b
	if logfile != "" {
		f, err := os.OpenFile(logfile, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
		if err != nil {
			return
		}
		defer f.Close()
		w = io.MultiWriter(f, b)
	}
	_, err = io.Copy(w, p.Stdout)
	if err != nil {
		return
	}
	wait, err := p.Wait(0)
	if err != nil {
		return
	}
	return b.String(), wait.WaitStatus.ExitStatus(), nil
}

// lookPath looks for cmd in $PATH if cmd does not begin with / or ./ or ../.
func lookPath(cmd string) (string, os.Error) {
	if strings.HasPrefix(cmd, "/") || strings.HasPrefix(cmd, "./") || strings.HasPrefix(cmd, "../") {
		return cmd, nil
	}
	return exec.LookPath(cmd)
}

// useBash prefixes a list of args with 'bash' if the first argument
// is a bash script.
func useBash(argv []string) []string {
	// TODO(brainman): choose a more reliable heuristic here.
	if strings.HasSuffix(argv[0], ".bash") {
		argv = append([]string{"bash"}, argv...)
	}
	return argv
}
<|endoftext|>


<commit_before>package pop

// SQLite is currently not supported due to cgo issues

import (
	"fmt"
	"log"
	"os"
	"path/filepath"
	"sync"
	"time"

	"github.com/markbates/going/defaults"
	. "github.com/markbates/pop/columns"
	"github.com/markbates/pop/fizz"
	"github.com/markbates/pop/fizz/translators"
)

type SQLite struct {
	gil               *sync.Mutex
	ConnectionDetails *ConnectionDetails
}

func (m *SQLite) Details() *ConnectionDetails {
	return m.ConnectionDetails
}

func (m *SQLite) URL() string {
	return m.ConnectionDetails.Database + "?cache=shared&mode=rwc"
}

func (m *SQLite) MigrationURL() string {
	return m.ConnectionDetails.URL
}

func (m *SQLite) Create(store Store, model *Model, cols Columns) error {
	return m.Lock(func() error {
		return genericCreate(store, model, cols)
	})
}

func (m *SQLite) Update(store Store, model *Model, cols Columns) error {
	return m.Lock(func() error {
		return genericUpdate(store, model, cols)
	})
}

func (m *SQLite) Destroy(store Store, model *Model) error {
	return m.Lock(func() error {
		return genericDestroy(store, model)
	})
}

func (m *SQLite) SelectOne(store Store, model *Model, query Query) error {
	return m.Lock(func() error {
		return genericSelectOne(store, model, query)
	})
}

func (m *SQLite) SelectMany(store Store, models *Model, query Query) error {
	return m.Lock(func() error {
		return genericSelectMany(store, models, query)
	})
}

func (m *SQLite) Lock(fn func() error) error {
	if defaults.String(m.Details().Options["lock"], "true") == "true" {
		defer m.gil.Unlock()
		m.gil.Lock()
	}
	err := fn()
	attempts := 0
	for err != nil && err.Error() == "database is locked" && attempts <= m.Details().RetryLimit() {
		time.Sleep(m.Details().RetrySleep())
		err = fn()
		attempts++
	}
	return err
}

func (m *SQLite) CreateDB() error {
	d := filepath.Dir(m.ConnectionDetails.Database)
	err := os.MkdirAll(d, 0766)
	if err != nil {
		log.Println(err)
	}
	return err
}

func (m *SQLite) DropDB() error {
	return os.Remove(m.ConnectionDetails.Database)
}

func (m *SQLite) TranslateSQL(sql string) string {
	return sql
}

func (m *SQLite) FizzTranslator() fizz.Translator {
	return translators.NewSQLite(m.Details().Database)
}

func NewSQLite(deets *ConnectionDetails) Dialect {
	deets.URL = fmt.Sprintf("sqlite3://%s", deets.Database)
	cd := &SQLite{
		gil:               &sync.Mutex{},
		ConnectionDetails: deets,
	}

	return cd
}
<commit_msg>revert the last commit<commit_after>package pop

// SQLite is currently not supported due to cgo issues

import (
	"fmt"
	"log"
	"os"
	"path/filepath"
	"sync"
	"time"

	"github.com/markbates/going/defaults"
	. "github.com/markbates/pop/columns"
	"github.com/markbates/pop/fizz"
	"github.com/markbates/pop/fizz/translators"
)

type SQLite struct {
	gil               *sync.Mutex
	ConnectionDetails *ConnectionDetails
}

func (m *SQLite) Details() *ConnectionDetails {
	return m.ConnectionDetails
}

func (m *SQLite) URL() string {
	return m.ConnectionDetails.Database + "?cache=shared&mode=rwc"
}

func (m *SQLite) MigrationURL() string {
	return m.ConnectionDetails.URL
}

func (m *SQLite) Create(store Store, model *Model, cols Columns) error {
	return genericCreate(store, model, cols)

}

func (m *SQLite) Update(store Store, model *Model, cols Columns) error {
	return genericUpdate(store, model, cols)

}

func (m *SQLite) Destroy(store Store, model *Model) error {
	return genericDestroy(store, model)

}

func (m *SQLite) SelectOne(store Store, model *Model, query Query) error {
	return genericSelectOne(store, model, query)
}

func (m *SQLite) SelectMany(store Store, models *Model, query Query) error {
	return genericSelectMany(store, models, query)
}

func (m *SQLite) Lock(fn func() error) error {
	if defaults.String(m.Details().Options["lock"], "true") == "true" {
		defer m.gil.Unlock()
		m.gil.Lock()
	}
	err := fn()
	attempts := 0
	for err != nil && err.Error() == "database is locked" && attempts <= m.Details().RetryLimit() {
		time.Sleep(m.Details().RetrySleep())
		err = fn()
		attempts++
	}
	return err
}

func (m *SQLite) CreateDB() error {
	d := filepath.Dir(m.ConnectionDetails.Database)
	err := os.MkdirAll(d, 0766)
	if err != nil {
		log.Println(err)
	}
	return err
}

func (m *SQLite) DropDB() error {
	return os.Remove(m.ConnectionDetails.Database)
}

func (m *SQLite) TranslateSQL(sql string) string {
	return sql
}

func (m *SQLite) FizzTranslator() fizz.Translator {
	return translators.NewSQLite(m.Details().Database)
}

func NewSQLite(deets *ConnectionDetails) Dialect {
	deets.URL = fmt.Sprintf("sqlite3://%s", deets.Database)
	cd := &SQLite{
		gil:               &sync.Mutex{},
		ConnectionDetails: deets,
	}

	return cd
}
<|endoftext|>


<commit_before>package render

import (
	"regexp"
	"strings"

	"github.com/weaveworks/scope/probe/docker"
	"github.com/weaveworks/scope/report"
)

// Constants are used in the tests.
const (
	UncontainedID    = "uncontained"
	UncontainedMajor = "Uncontained"

	// Topology for IPs so we can differentiate them at the end
	IP = "IP"
)

// UncontainedIDPrefix is the prefix of uncontained pseudo nodes
var UncontainedIDPrefix = MakePseudoNodeID(UncontainedID)

// ContainerRenderer is a Renderer which produces a renderable container
// graph by merging the process graph and the container topology.
// NB We only want processes in container _or_ processes with network connections
// but we need to be careful to ensure we only include each edge once, by only
// including the ProcessRenderer once.
var ContainerRenderer = MakeFilter(
	func(n report.Node) bool {
		// Drop deleted containers
		state, ok := n.Latest.Lookup(docker.ContainerState)
		return !ok || state != docker.StateDeleted
	},
	MakeReduce(
		MakeMap(
			MapProcess2Container,
			ColorConnectedProcessRenderer,
		),
		ConnectionJoin(MapContainer2IP, SelectContainer),
	),
)

var mapEndpoint2IP = MakeMap(endpoint2IP, SelectEndpoint)

const originalNodeID = "original_node_id"

// ConnectionJoin joins the given renderer with connections from the
// endpoints topology, using the toIPs function to extract IPs from
// the nodes.
func ConnectionJoin(toIPs func(report.Node) []string, r Renderer) Renderer {
	nodeToIP := func(n report.Node, _ report.Networks) report.Nodes {
		result := report.Nodes{}
		for _, ip := range toIPs(n) {
			result[ip] = report.MakeNode(ip).
				WithTopology(IP).
				WithLatests(map[string]string{
					originalNodeID: n.ID,
				}).
				WithCounters(map[string]int{IP: 1})
		}
		return result
	}

	return MakeReduce(
		MakeMap(
			ipToNode,
			MakeReduce(
				MakeMap(nodeToIP, r),
				mapEndpoint2IP,
			),
		),
		r,
	)
}

func ipToNode(n report.Node, _ report.Networks) report.Nodes {
	// If an IP is shared between multiple nodes, we can't reliably
	// attribute an connection based on its IP
	if count, _ := n.Counters.Lookup(IP); count > 1 {
		return report.Nodes{}
	}

	// Propagate the internet and service pseudo nodes
	if strings.HasSuffix(n.ID, TheInternetID) || strings.HasPrefix(n.ID, ServiceNodeIDPrefix) {
		return report.Nodes{n.ID: n}
	}

	// If this node is not of the original type, exclude it.  This
	// excludes all the nodes we've dragged in from endpoint that we
	// failed to join to a node.
	id, ok := n.Latest.Lookup(originalNodeID)
	if !ok {
		return report.Nodes{}
	}

	return report.Nodes{id: NewDerivedNode(id, n)}
}

// endpoint2IP maps endpoint nodes to their IP address, for joining
// with container nodes.
func endpoint2IP(m report.Node, local report.Networks) report.Nodes {
	scope, addr, port, ok := report.ParseEndpointNodeID(m.ID)
	if !ok {
		return report.Nodes{}
	}

	// Nodes without a hostid may be pseudo nodes
	if _, ok := m.Latest.Lookup(report.HostNodeID); !ok {
		if externalNode, ok := NewDerivedExternalNode(m, addr, local); ok {
			return report.Nodes{externalNode.ID: externalNode}
		}
	}

	// We also allow for joining on ip:port pairs.  This is useful for
	// connections to the host IPs which have been port mapped to a
	// container can only be unambiguously identified with the port.
	// So we need to emit two nodes, for two different cases.
	id := report.MakeScopedEndpointNodeID(scope, addr, "")
	idWithPort := report.MakeScopedEndpointNodeID(scope, addr, port)
	return report.Nodes{
		id:         NewDerivedNode(id, m).WithTopology(IP),
		idWithPort: NewDerivedNode(idWithPort, m).WithTopology(IP),
	}
}

// FilterEmpty is a Renderer which filters out nodes which have no children
// from the specified topology.
func FilterEmpty(topology string, r Renderer) Renderer {
	return MakeFilter(HasChildren(topology), r)
}

// HasChildren returns true if the node has no children from the specified
// topology.
func HasChildren(topology string) FilterFunc {
	return func(n report.Node) bool {
		count := 0
		n.Children.ForEach(func(child report.Node) {
			if child.Topology == topology {
				count++
			}
		})
		return count > 0
	}
}

type containerWithImageNameRenderer struct {
	Renderer
}

// Render produces a container graph where the the latest metadata contains the
// container image name, if found.
func (r containerWithImageNameRenderer) Render(rpt report.Report, dct Decorator) report.Nodes {
	containers := r.Renderer.Render(rpt, dct)
	images := SelectContainerImage.Render(rpt, dct)

	outputs := report.Nodes{}
	for id, c := range containers {
		outputs[id] = c
		imageID, ok := c.Latest.Lookup(docker.ImageID)
		if !ok {
			continue
		}
		image, ok := images[report.MakeContainerImageNodeID(imageID)]
		if !ok {
			continue
		}
		imageName, ok := image.Latest.Lookup(docker.ImageName)
		if !ok {
			continue
		}
		imageNameWithoutVersion := docker.ImageNameWithoutVersion(imageName)
		imageNodeID := report.MakeContainerImageNodeID(imageNameWithoutVersion)

		c = propagateLatest(docker.ImageName, image, c)
		c = propagateLatest(docker.ImageSize, image, c)
		c = propagateLatest(docker.ImageVirtualSize, image, c)
		c = propagateLatest(docker.ImageLabelPrefix+"works.weave.role", image, c)
		c.Parents = c.Parents.
			Delete(report.ContainerImage).
			Add(report.ContainerImage, report.MakeStringSet(imageNodeID))
		outputs[id] = c
	}
	return outputs
}

// ContainerWithImageNameRenderer is a Renderer which produces a container
// graph where the ranks are the image names, not their IDs
var ContainerWithImageNameRenderer = containerWithImageNameRenderer{ContainerRenderer}

// ContainerImageRenderer is a Renderer which produces a renderable container
// image graph by merging the container graph and the container image topology.
var ContainerImageRenderer = FilterEmpty(report.Container,
	MakeMap(
		MapContainerImage2Name,
		MakeReduce(
			MakeMap(
				MapContainer2ContainerImage,
				ContainerWithImageNameRenderer,
			),
			SelectContainerImage,
		),
	),
)

// ContainerHostnameRenderer is a Renderer which produces a renderable container
// by hostname graph..
var ContainerHostnameRenderer = FilterEmpty(report.Container,
	MakeReduce(
		MakeMap(
			MapContainer2Hostname,
			ContainerWithImageNameRenderer,
		),
		// Grab *all* the hostnames, so we can count the number which were empty
		// for accurate stats.
		MakeMap(
			MapToEmpty,
			MakeMap(
				MapContainer2Hostname,
				ContainerRenderer,
			),
		),
	),
)

var portMappingMatch = regexp.MustCompile(`([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}):([0-9]+)->([0-9]+)/tcp`)

// MapContainer2IP maps container nodes to their IP addresses (outputs
// multiple nodes).  This allows container to be joined directly with
// the endpoint topology.
func MapContainer2IP(m report.Node) []string {
	// if this container doesn't make connections, we can ignore it
	_, doesntMakeConnections := m.Latest.Lookup(report.DoesNotMakeConnections)
	// if this container belongs to the host's networking namespace
	// we cannot use its IP to attribute connections
	// (they could come from any other process on the host or DNAT-ed IPs)
	_, isInHostNetwork := m.Latest.Lookup(docker.IsInHostNetwork)
	if doesntMakeConnections || isInHostNetwork {
		return nil
	}

	result := []string{}
	if addrs, ok := m.Sets.Lookup(docker.ContainerIPsWithScopes); ok {
		for _, addr := range addrs {
			scope, addr, ok := report.ParseAddressNodeID(addr)
			if !ok {
				continue
			}
			// loopback addresses are shared among all namespaces
			// so we can't use them to attribute connections to a container
			if report.IsLoopback(addr) {
				continue
			}
			id := report.MakeScopedEndpointNodeID(scope, addr, "")
			result = append(result, id)
		}
	}

	// Also output all the host:port port mappings (see above comment).
	// In this case we assume this doesn't need a scope, as they are for host IPs.
	ports, _ := m.Sets.Lookup(docker.ContainerPorts)
	for _, portMapping := range ports {
		if mapping := portMappingMatch.FindStringSubmatch(portMapping); mapping != nil {
			ip, port := mapping[1], mapping[2]
			id := report.MakeScopedEndpointNodeID("", ip, port)
			result = append(result, id)
		}
	}

	return result
}

// MapProcess2Container maps process Nodes to container
// Nodes.
//
// If this function is given a node without a docker_container_id
// (including other pseudo nodes), it will produce an "Uncontained"
// pseudo node.
//
// Otherwise, this function will produce a node with the correct ID
// format for a container, but without any Major or Minor labels.
// It does not have enough info to do that, and the resulting graph
// must be merged with a container graph to get that info.
func MapProcess2Container(n report.Node, _ report.Networks) report.Nodes {
	// Propagate pseudo nodes
	if n.Topology == Pseudo {
		return report.Nodes{n.ID: n}
	}

	// Otherwise, if the process is not in a container, group it
	// into an per-host "Uncontained" node.  If for whatever reason
	// this node doesn't have a host id in their nodemetadata, it'll
	// all get grouped into a single uncontained node.
	var (
		id   string
		node report.Node
	)
	if containerID, ok := n.Latest.Lookup(docker.ContainerID); ok {
		id = report.MakeContainerNodeID(containerID)
		node = NewDerivedNode(id, n).WithTopology(report.Container)
	} else {
		id = MakePseudoNodeID(UncontainedID, report.ExtractHostID(n))
		node = NewDerivedPseudoNode(id, n)
		node = propagateLatest(report.HostNodeID, n, node)
		node = propagateLatest(IsConnected, n, node)
	}
	return report.Nodes{id: node}
}

// MapContainer2ContainerImage maps container Nodes to container
// image Nodes.
//
// If this function is given a node without a docker_image_id
// (including other pseudo nodes), it will produce an "Uncontained"
// pseudo node.
//
// Otherwise, this function will produce a node with the correct ID
// format for a container, but without any Major or Minor labels.
// It does not have enough info to do that, and the resulting graph
// must be merged with a container graph to get that info.
func MapContainer2ContainerImage(n report.Node, _ report.Networks) report.Nodes {
	// Propagate all pseudo nodes
	if n.Topology == Pseudo {
		return report.Nodes{n.ID: n}
	}

	// Otherwise, if some some reason the container doesn't have a image_id
	// (maybe slightly out of sync reports), just drop it
	imageID, timestamp, ok := n.Latest.LookupEntry(docker.ImageID)
	if !ok {
		return report.Nodes{}
	}

	// Add container id key to the counters, which will later be counted to produce the minor label
	id := report.MakeContainerImageNodeID(imageID)
	result := NewDerivedNode(id, n).WithTopology(report.ContainerImage)
	result.Latest = result.Latest.Set(docker.ImageID, timestamp, imageID)
	result.Counters = result.Counters.Add(n.Topology, 1)
	return report.Nodes{id: result}
}

// MapContainerImage2Name ignores image versions
func MapContainerImage2Name(n report.Node, _ report.Networks) report.Nodes {
	// Propagate all pseudo nodes
	if n.Topology == Pseudo {
		return report.Nodes{n.ID: n}
	}

	imageName, ok := n.Latest.Lookup(docker.ImageName)
	if !ok {
		return report.Nodes{}
	}

	imageNameWithoutVersion := docker.ImageNameWithoutVersion(imageName)
	n.ID = report.MakeContainerImageNodeID(imageNameWithoutVersion)

	if imageID, ok := report.ParseContainerImageNodeID(n.ID); ok {
		n.Sets = n.Sets.Add(docker.ImageID, report.MakeStringSet(imageID))
	}

	return report.Nodes{n.ID: n}
}

// MapContainer2Hostname maps container Nodes to 'hostname' renderabled nodes..
func MapContainer2Hostname(n report.Node, _ report.Networks) report.Nodes {
	// Propagate all pseudo nodes
	if n.Topology == Pseudo {
		return report.Nodes{n.ID: n}
	}

	// Otherwise, if some some reason the container doesn't have a hostname
	// (maybe slightly out of sync reports), just drop it
	id, timestamp, ok := n.Latest.LookupEntry(docker.ContainerHostname)
	if !ok {
		return report.Nodes{}
	}

	node := NewDerivedNode(id, n).WithTopology(MakeGroupNodeTopology(n.Topology, docker.ContainerHostname))
	node.Latest = node.Latest.Set(docker.ContainerHostname, timestamp, id)
	node.Counters = node.Counters.Add(n.Topology, 1)
	return report.Nodes{id: node}
}

// MapToEmpty removes all the attributes, children, etc, of a node. Useful when
// we just want to count the presence of nodes.
func MapToEmpty(n report.Node, _ report.Networks) report.Nodes {
	return report.Nodes{n.ID: report.MakeNode(n.ID).WithTopology(n.Topology)}
}
<commit_msg>simplify node propagation in ipToNode<commit_after>package render

import (
	"regexp"

	"github.com/weaveworks/scope/probe/docker"
	"github.com/weaveworks/scope/report"
)

// Constants are used in the tests.
const (
	UncontainedID    = "uncontained"
	UncontainedMajor = "Uncontained"

	// Topology for IPs so we can differentiate them at the end
	IP = "IP"
)

// UncontainedIDPrefix is the prefix of uncontained pseudo nodes
var UncontainedIDPrefix = MakePseudoNodeID(UncontainedID)

// ContainerRenderer is a Renderer which produces a renderable container
// graph by merging the process graph and the container topology.
// NB We only want processes in container _or_ processes with network connections
// but we need to be careful to ensure we only include each edge once, by only
// including the ProcessRenderer once.
var ContainerRenderer = MakeFilter(
	func(n report.Node) bool {
		// Drop deleted containers
		state, ok := n.Latest.Lookup(docker.ContainerState)
		return !ok || state != docker.StateDeleted
	},
	MakeReduce(
		MakeMap(
			MapProcess2Container,
			ColorConnectedProcessRenderer,
		),
		ConnectionJoin(MapContainer2IP, SelectContainer),
	),
)

var mapEndpoint2IP = MakeMap(endpoint2IP, SelectEndpoint)

const originalNodeID = "original_node_id"

// ConnectionJoin joins the given renderer with connections from the
// endpoints topology, using the toIPs function to extract IPs from
// the nodes.
func ConnectionJoin(toIPs func(report.Node) []string, r Renderer) Renderer {
	nodeToIP := func(n report.Node, _ report.Networks) report.Nodes {
		result := report.Nodes{}
		for _, ip := range toIPs(n) {
			result[ip] = report.MakeNode(ip).
				WithTopology(IP).
				WithLatests(map[string]string{
					originalNodeID: n.ID,
				}).
				WithCounters(map[string]int{IP: 1})
		}
		return result
	}

	return MakeReduce(
		MakeMap(
			ipToNode,
			MakeReduce(
				MakeMap(nodeToIP, r),
				mapEndpoint2IP,
			),
		),
		r,
	)
}

func ipToNode(n report.Node, _ report.Networks) report.Nodes {
	// propagate non-IP nodes
	if n.Topology != IP {
		return report.Nodes{n.ID: n}
	}
	// If an IP is shared between multiple nodes, we can't reliably
	// attribute an connection based on its IP
	if count, _ := n.Counters.Lookup(IP); count > 1 {
		return report.Nodes{}
	}
	// If this node is not of the original type, exclude it.  This
	// excludes all the nodes we've dragged in from endpoint that we
	// failed to join to a node.
	id, ok := n.Latest.Lookup(originalNodeID)
	if !ok {
		return report.Nodes{}
	}

	return report.Nodes{id: NewDerivedNode(id, n)}
}

// endpoint2IP maps endpoint nodes to their IP address, for joining
// with container nodes.
func endpoint2IP(m report.Node, local report.Networks) report.Nodes {
	scope, addr, port, ok := report.ParseEndpointNodeID(m.ID)
	if !ok {
		return report.Nodes{}
	}

	// Nodes without a hostid may be pseudo nodes
	if _, ok := m.Latest.Lookup(report.HostNodeID); !ok {
		if externalNode, ok := NewDerivedExternalNode(m, addr, local); ok {
			return report.Nodes{externalNode.ID: externalNode}
		}
	}

	// We also allow for joining on ip:port pairs.  This is useful for
	// connections to the host IPs which have been port mapped to a
	// container can only be unambiguously identified with the port.
	// So we need to emit two nodes, for two different cases.
	id := report.MakeScopedEndpointNodeID(scope, addr, "")
	idWithPort := report.MakeScopedEndpointNodeID(scope, addr, port)
	return report.Nodes{
		id:         NewDerivedNode(id, m).WithTopology(IP),
		idWithPort: NewDerivedNode(idWithPort, m).WithTopology(IP),
	}
}

// FilterEmpty is a Renderer which filters out nodes which have no children
// from the specified topology.
func FilterEmpty(topology string, r Renderer) Renderer {
	return MakeFilter(HasChildren(topology), r)
}

// HasChildren returns true if the node has no children from the specified
// topology.
func HasChildren(topology string) FilterFunc {
	return func(n report.Node) bool {
		count := 0
		n.Children.ForEach(func(child report.Node) {
			if child.Topology == topology {
				count++
			}
		})
		return count > 0
	}
}

type containerWithImageNameRenderer struct {
	Renderer
}

// Render produces a container graph where the the latest metadata contains the
// container image name, if found.
func (r containerWithImageNameRenderer) Render(rpt report.Report, dct Decorator) report.Nodes {
	containers := r.Renderer.Render(rpt, dct)
	images := SelectContainerImage.Render(rpt, dct)

	outputs := report.Nodes{}
	for id, c := range containers {
		outputs[id] = c
		imageID, ok := c.Latest.Lookup(docker.ImageID)
		if !ok {
			continue
		}
		image, ok := images[report.MakeContainerImageNodeID(imageID)]
		if !ok {
			continue
		}
		imageName, ok := image.Latest.Lookup(docker.ImageName)
		if !ok {
			continue
		}
		imageNameWithoutVersion := docker.ImageNameWithoutVersion(imageName)
		imageNodeID := report.MakeContainerImageNodeID(imageNameWithoutVersion)

		c = propagateLatest(docker.ImageName, image, c)
		c = propagateLatest(docker.ImageSize, image, c)
		c = propagateLatest(docker.ImageVirtualSize, image, c)
		c = propagateLatest(docker.ImageLabelPrefix+"works.weave.role", image, c)
		c.Parents = c.Parents.
			Delete(report.ContainerImage).
			Add(report.ContainerImage, report.MakeStringSet(imageNodeID))
		outputs[id] = c
	}
	return outputs
}

// ContainerWithImageNameRenderer is a Renderer which produces a container
// graph where the ranks are the image names, not their IDs
var ContainerWithImageNameRenderer = containerWithImageNameRenderer{ContainerRenderer}

// ContainerImageRenderer is a Renderer which produces a renderable container
// image graph by merging the container graph and the container image topology.
var ContainerImageRenderer = FilterEmpty(report.Container,
	MakeMap(
		MapContainerImage2Name,
		MakeReduce(
			MakeMap(
				MapContainer2ContainerImage,
				ContainerWithImageNameRenderer,
			),
			SelectContainerImage,
		),
	),
)

// ContainerHostnameRenderer is a Renderer which produces a renderable container
// by hostname graph..
var ContainerHostnameRenderer = FilterEmpty(report.Container,
	MakeReduce(
		MakeMap(
			MapContainer2Hostname,
			ContainerWithImageNameRenderer,
		),
		// Grab *all* the hostnames, so we can count the number which were empty
		// for accurate stats.
		MakeMap(
			MapToEmpty,
			MakeMap(
				MapContainer2Hostname,
				ContainerRenderer,
			),
		),
	),
)

var portMappingMatch = regexp.MustCompile(`([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}):([0-9]+)->([0-9]+)/tcp`)

// MapContainer2IP maps container nodes to their IP addresses (outputs
// multiple nodes).  This allows container to be joined directly with
// the endpoint topology.
func MapContainer2IP(m report.Node) []string {
	// if this container doesn't make connections, we can ignore it
	_, doesntMakeConnections := m.Latest.Lookup(report.DoesNotMakeConnections)
	// if this container belongs to the host's networking namespace
	// we cannot use its IP to attribute connections
	// (they could come from any other process on the host or DNAT-ed IPs)
	_, isInHostNetwork := m.Latest.Lookup(docker.IsInHostNetwork)
	if doesntMakeConnections || isInHostNetwork {
		return nil
	}

	result := []string{}
	if addrs, ok := m.Sets.Lookup(docker.ContainerIPsWithScopes); ok {
		for _, addr := range addrs {
			scope, addr, ok := report.ParseAddressNodeID(addr)
			if !ok {
				continue
			}
			// loopback addresses are shared among all namespaces
			// so we can't use them to attribute connections to a container
			if report.IsLoopback(addr) {
				continue
			}
			id := report.MakeScopedEndpointNodeID(scope, addr, "")
			result = append(result, id)
		}
	}

	// Also output all the host:port port mappings (see above comment).
	// In this case we assume this doesn't need a scope, as they are for host IPs.
	ports, _ := m.Sets.Lookup(docker.ContainerPorts)
	for _, portMapping := range ports {
		if mapping := portMappingMatch.FindStringSubmatch(portMapping); mapping != nil {
			ip, port := mapping[1], mapping[2]
			id := report.MakeScopedEndpointNodeID("", ip, port)
			result = append(result, id)
		}
	}

	return result
}

// MapProcess2Container maps process Nodes to container
// Nodes.
//
// If this function is given a node without a docker_container_id
// (including other pseudo nodes), it will produce an "Uncontained"
// pseudo node.
//
// Otherwise, this function will produce a node with the correct ID
// format for a container, but without any Major or Minor labels.
// It does not have enough info to do that, and the resulting graph
// must be merged with a container graph to get that info.
func MapProcess2Container(n report.Node, _ report.Networks) report.Nodes {
	// Propagate pseudo nodes
	if n.Topology == Pseudo {
		return report.Nodes{n.ID: n}
	}

	// Otherwise, if the process is not in a container, group it
	// into an per-host "Uncontained" node.  If for whatever reason
	// this node doesn't have a host id in their nodemetadata, it'll
	// all get grouped into a single uncontained node.
	var (
		id   string
		node report.Node
	)
	if containerID, ok := n.Latest.Lookup(docker.ContainerID); ok {
		id = report.MakeContainerNodeID(containerID)
		node = NewDerivedNode(id, n).WithTopology(report.Container)
	} else {
		id = MakePseudoNodeID(UncontainedID, report.ExtractHostID(n))
		node = NewDerivedPseudoNode(id, n)
		node = propagateLatest(report.HostNodeID, n, node)
		node = propagateLatest(IsConnected, n, node)
	}
	return report.Nodes{id: node}
}

// MapContainer2ContainerImage maps container Nodes to container
// image Nodes.
//
// If this function is given a node without a docker_image_id
// (including other pseudo nodes), it will produce an "Uncontained"
// pseudo node.
//
// Otherwise, this function will produce a node with the correct ID
// format for a container, but without any Major or Minor labels.
// It does not have enough info to do that, and the resulting graph
// must be merged with a container graph to get that info.
func MapContainer2ContainerImage(n report.Node, _ report.Networks) report.Nodes {
	// Propagate all pseudo nodes
	if n.Topology == Pseudo {
		return report.Nodes{n.ID: n}
	}

	// Otherwise, if some some reason the container doesn't have a image_id
	// (maybe slightly out of sync reports), just drop it
	imageID, timestamp, ok := n.Latest.LookupEntry(docker.ImageID)
	if !ok {
		return report.Nodes{}
	}

	// Add container id key to the counters, which will later be counted to produce the minor label
	id := report.MakeContainerImageNodeID(imageID)
	result := NewDerivedNode(id, n).WithTopology(report.ContainerImage)
	result.Latest = result.Latest.Set(docker.ImageID, timestamp, imageID)
	result.Counters = result.Counters.Add(n.Topology, 1)
	return report.Nodes{id: result}
}

// MapContainerImage2Name ignores image versions
func MapContainerImage2Name(n report.Node, _ report.Networks) report.Nodes {
	// Propagate all pseudo nodes
	if n.Topology == Pseudo {
		return report.Nodes{n.ID: n}
	}

	imageName, ok := n.Latest.Lookup(docker.ImageName)
	if !ok {
		return report.Nodes{}
	}

	imageNameWithoutVersion := docker.ImageNameWithoutVersion(imageName)
	n.ID = report.MakeContainerImageNodeID(imageNameWithoutVersion)

	if imageID, ok := report.ParseContainerImageNodeID(n.ID); ok {
		n.Sets = n.Sets.Add(docker.ImageID, report.MakeStringSet(imageID))
	}

	return report.Nodes{n.ID: n}
}

// MapContainer2Hostname maps container Nodes to 'hostname' renderabled nodes..
func MapContainer2Hostname(n report.Node, _ report.Networks) report.Nodes {
	// Propagate all pseudo nodes
	if n.Topology == Pseudo {
		return report.Nodes{n.ID: n}
	}

	// Otherwise, if some some reason the container doesn't have a hostname
	// (maybe slightly out of sync reports), just drop it
	id, timestamp, ok := n.Latest.LookupEntry(docker.ContainerHostname)
	if !ok {
		return report.Nodes{}
	}

	node := NewDerivedNode(id, n).WithTopology(MakeGroupNodeTopology(n.Topology, docker.ContainerHostname))
	node.Latest = node.Latest.Set(docker.ContainerHostname, timestamp, id)
	node.Counters = node.Counters.Add(n.Topology, 1)
	return report.Nodes{id: node}
}

// MapToEmpty removes all the attributes, children, etc, of a node. Useful when
// we just want to count the presence of nodes.
func MapToEmpty(n report.Node, _ report.Networks) report.Nodes {
	return report.Nodes{n.ID: report.MakeNode(n.ID).WithTopology(n.Topology)}
}
<|endoftext|>


<commit_before>package pxlocal

import (
	"bufio"
	"errors"
	"fmt"
	"io"
	"net"
	"net/http"
	"net/http/httputil"
	"sync"

	"github.com/gorilla/websocket"
	"github.com/qiniu/log"
)

// :thinking
// start server HTTP service
// start agent
//  - agent connect server with websocket
//  - agent convert http request to conn
// need ref: revproxy

const (
	TYPE_NEWCONN = iota + 1
	TYPE_MESSAGE
)

var (
	upgrader = websocket.Upgrader{
		ReadBufferSize:  1024,
		WriteBufferSize: 1024,
	}
	namedConnection = make(map[string]chan net.Conn, 10)
	proxyStats      = &ProxyStats{}
)

type Msg struct {
	Type int
	Name string
	Body string
}

type Tunnel struct {
	wsconn *websocket.Conn
	sync.Mutex
	index int64
}

func (t *Tunnel) uniqName() string {
	t.Lock()
	defer t.Unlock()
	t.index += 1
	return fmt.Sprintf("%d", t.index)
}

func (t *Tunnel) RequestNewConn(remoteAddr string) (net.Conn, error) {
	connCh := make(chan net.Conn)
	namedConnection[remoteAddr] = connCh
	defer delete(namedConnection, remoteAddr)

	// request a reverse connection
	var msg = Msg{Type: TYPE_NEWCONN, Name: remoteAddr}
	t.wsconn.WriteJSON(msg)
	lconn := <-connCh
	if lconn == nil {
		return nil, errors.New("maybe hijack not supported, failed")
	}
	return lconn, nil
}

// used for httputil reverse proxy
func (t *Tunnel) generateTransportDial() func(network, addr string) (net.Conn, error) {
	return func(network, addr string) (net.Conn, error) {
		log.Println("transport", network, addr)
		return t.RequestNewConn(t.uniqName())
	}
}

// Listen and forward connections
func NewTcpProxyListener(tunnel *Tunnel, listenAddress string) (lis net.Listener, err error) {
	laddr, err := net.ResolveTCPAddr("tcp", listenAddress)
	if err != nil {
		return nil, err
	}
	listener, err := net.ListenTCP("tcp", laddr)
	if err != nil {
		return nil, err
	}
	go func() {
		for {
			rconn, err := listener.AcceptTCP()
			if err != nil {
				log.Println(err)
				break
			}
			// find proxy to where
			log.Println(laddr, "Receive new connections from", rconn.RemoteAddr())
			lconn, err := tunnel.RequestNewConn(rconn.RemoteAddr().String())
			if err != nil {
				log.Println("request new conn err:", err)
				rconn.Close()
				continue
			}

			log.Println("request new conn:", lconn, err)
			pc := &ProxyConn{
				lconn: lconn,
				rconn: rconn,
				stats: proxyStats,
			}
			go pc.start()
		}
	}()
	return listener, nil
}

func FigureListenAddress(r *http.Request) (protocal, subdomain string, port int) {
	protocal = r.FormValue("protocal")
	if protocal == "" {
		protocal = "http"
	}
	reqPort := r.FormValue("port")
	if reqPort == "" {
		port = 12345
	} else {
		fmt.Sscanf(reqPort, "%d", &port)
	}
	subdomain = r.FormValue("subdomain")
	return
}

type HijactRW struct {
	*net.TCPConn
	bufrw *bufio.ReadWriter
}

func (this *HijactRW) Write(data []byte) (int, error) {
	nn, err := this.bufrw.Write(data)
	this.bufrw.Flush()
	return nn, err
}

func (this *HijactRW) Read(p []byte) (int, error) {
	return this.bufrw.Read(p)
}

func NewHijackReadWriteCloser(conn *net.TCPConn, bufrw *bufio.ReadWriter) net.Conn {
	return &HijactRW{
		bufrw:   bufrw,
		TCPConn: conn,
	}
}

func proxyHandler(w http.ResponseWriter, r *http.Request) {
	hj, ok := w.(http.Hijacker)
	if !ok {
		http.Error(w, "webserver don't support hijacking", http.StatusInternalServerError)
		return
	}

	var proxyFor = r.Header.Get("X-Proxy-For")
	log.Println("proxy name:", proxyFor)

	connCh, ok := namedConnection[proxyFor]
	if !ok {
		http.Error(w, "inside error: proxy not ready to receive conn", http.StatusInternalServerError)
		return
	}
	hjconn, bufrw, err := hj.Hijack()
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		connCh <- nil
		return
	}
	if _, ok := hjconn.(*net.TCPConn); ok {
		log.Println("Hijack is tcp conn")
	}

	conn := NewHijackReadWriteCloser(hjconn.(*net.TCPConn), bufrw)
	connCh <- conn
}

type ProxyServer struct {
	domain string
	*http.ServeMux
	revProxies map[string]*httputil.ReverseProxy
	sync.RWMutex
}

func wsSendMessage(conn *websocket.Conn, message string) error {
	return conn.WriteJSON(&Msg{Type: TYPE_MESSAGE, Body: message})
}

func (ps *ProxyServer) newHomepageHandler() func(w http.ResponseWriter, r *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		w.Header().Add("Content-Type", "text/html")
		io.WriteString(w, fmt.Sprintf("TCP: recvBytes: %d, sendBytes: %d
",
			proxyStats.receivedBytes, proxyStats.sentBytes))
		io.WriteString(w, "
")
		for pname, _ := range ps.revProxies {
			io.WriteString(w, fmt.Sprintf("http proxy: %s
", pname))
		}
	}
}

func (ps *ProxyServer) newControlHandler() func(w http.ResponseWriter, r *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		// read listen port from request
		protocal, subdomain, port := FigureListenAddress(r)
		log.Println("proxy listen addr:", protocal, subdomain, port)

		// create websocket connection
		conn, err := upgrader.Upgrade(w, r, nil)
		if err != nil {
			http.Error(w, err.Error(), 502)
			return
		}
		defer conn.Close()
		log.Println(conn.RemoteAddr())

		tunnel := &Tunnel{wsconn: conn}
		// TCP: create new port to listen
		switch protocal {
		case "tcp":
			proxyAddr := fmt.Sprintf("0.0.0.0:%d", port)
			listener, err := NewTcpProxyListener(tunnel, proxyAddr)
			if err != nil {
				http.Error(w, err.Error(), 501)
				return
			}
			defer listener.Close()
		case "http":
			log.Println("start http proxy")
			tr := &http.Transport{
				Dial: tunnel.generateTransportDial(),
			}
			revProxy := &httputil.ReverseProxy{
				Director: func(req *http.Request) {
					log.Println("director:", req.RequestURI)
				},
				Transport: tr,
			}
			// should hook here
			// hook(HOOK_CREATE_HTTP_SUBDOMAIN, subdomain)
			// generate a uniq domain
			if subdomain == "" {
				subdomain = uniqName(5)
			}
			pxDomain := subdomain + "." + ps.domain
			log.Println("http px use domain:", pxDomain)
			if _, exists := ps.revProxies[pxDomain]; exists {
				wsSendMessage(conn, fmt.Sprintf("subdomain [%s] has already been taken", pxDomain))
				return
			}
			ps.Lock()
			ps.revProxies[pxDomain] = revProxy
			ps.Unlock()
			wsSendMessage(conn, fmt.Sprintf("visit http://%s", pxDomain))
		default:
			log.Println("unknown protocal:", protocal)
			return
		}
		// HTTP: use httputil.ReverseProxy
		for {
			var msg Msg
			if err := conn.ReadJSON(&msg); err != nil {
				log.Println(err)
				break
			}
			log.Println("recv json:", msg)
		}
	}
}

func (p *ProxyServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	log.Println("request info:", r.Method, r.Host, r.RequestURI)
	//host, _, _ := net.SplitHostPort(r.Host)
	// http://stackoverflow.com/questions/6899069/why-are-request-url-host-and-scheme-blank-in-the-development-server
	r.URL.Scheme = "http" // ??
	r.URL.Host = r.Host   // ??
	log.Println("URL path:", r.URL.Path)
	log.Printf("pxies: %v", p.revProxies)
	if rpx, ok := p.revProxies[r.Host]; ok {
		log.Println("server http rev proxy")
		rpx.ServeHTTP(w, r)
		return
	}

	//if p.domain != host {
	//	http.Error(w, fmt.Sprintf("%s not ready", host), 504)
	//	return
	//}

	h, _ := p.Handler(r)
	h.ServeHTTP(w, r)
}

// domain, ex shengxiang.me
// dns should set *.shengxiang.me
func NewProxyServer(domain string) *ProxyServer {
	if domain == "" {
		domain = "localhost"
	}
	p := &ProxyServer{
		domain:     domain,
		ServeMux:   http.NewServeMux(),
		revProxies: make(map[string]*httputil.ReverseProxy),
	}
	p.HandleFunc("/", p.newHomepageHandler())
	p.HandleFunc("/ws", p.newControlHandler())
	p.HandleFunc("/proxyhijack", proxyHandler)

	return p
}
<commit_msg>remove closed proxy<commit_after>package pxlocal

import (
	"bufio"
	"errors"
	"fmt"
	"io"
	"net"
	"net/http"
	"net/http/httputil"
	"sync"

	"github.com/gorilla/websocket"
	"github.com/qiniu/log"
)

// :thinking
// start server HTTP service
// start agent
//  - agent connect server with websocket
//  - agent convert http request to conn
// need ref: revproxy

const (
	TYPE_NEWCONN = iota + 1
	TYPE_MESSAGE
)

var (
	upgrader = websocket.Upgrader{
		ReadBufferSize:  1024,
		WriteBufferSize: 1024,
	}
	namedConnection = make(map[string]chan net.Conn, 10)
	proxyStats      = &ProxyStats{}
)

type Msg struct {
	Type int
	Name string
	Body string
}

type Tunnel struct {
	wsconn *websocket.Conn
	sync.Mutex
	index int64
}

func (t *Tunnel) uniqName() string {
	t.Lock()
	defer t.Unlock()
	t.index += 1
	return fmt.Sprintf("%d", t.index)
}

func (t *Tunnel) RequestNewConn(remoteAddr string) (net.Conn, error) {
	connCh := make(chan net.Conn)
	namedConnection[remoteAddr] = connCh
	defer delete(namedConnection, remoteAddr)

	// request a reverse connection
	var msg = Msg{Type: TYPE_NEWCONN, Name: remoteAddr}
	t.wsconn.WriteJSON(msg)
	lconn := <-connCh
	if lconn == nil {
		return nil, errors.New("maybe hijack not supported, failed")
	}
	return lconn, nil
}

// used for httputil reverse proxy
func (t *Tunnel) generateTransportDial() func(network, addr string) (net.Conn, error) {
	return func(network, addr string) (net.Conn, error) {
		log.Println("transport", network, addr)
		return t.RequestNewConn(t.uniqName())
	}
}

// Listen and forward connections
func NewTcpProxyListener(tunnel *Tunnel, listenAddress string) (lis net.Listener, err error) {
	laddr, err := net.ResolveTCPAddr("tcp", listenAddress)
	if err != nil {
		return nil, err
	}
	listener, err := net.ListenTCP("tcp", laddr)
	if err != nil {
		return nil, err
	}
	go func() {
		for {
			rconn, err := listener.AcceptTCP()
			if err != nil {
				log.Println(err)
				break
			}
			// find proxy to where
			log.Println(laddr, "Receive new connections from", rconn.RemoteAddr())
			lconn, err := tunnel.RequestNewConn(rconn.RemoteAddr().String())
			if err != nil {
				log.Println("request new conn err:", err)
				rconn.Close()
				continue
			}

			log.Println("request new conn:", lconn, err)
			pc := &ProxyConn{
				lconn: lconn,
				rconn: rconn,
				stats: proxyStats,
			}
			go pc.start()
		}
	}()
	return listener, nil
}

func FigureListenAddress(r *http.Request) (protocal, subdomain string, port int) {
	protocal = r.FormValue("protocal")
	if protocal == "" {
		protocal = "http"
	}
	reqPort := r.FormValue("port")
	if reqPort == "" {
		port = 12345
	} else {
		fmt.Sscanf(reqPort, "%d", &port)
	}
	subdomain = r.FormValue("subdomain")
	return
}

type HijactRW struct {
	*net.TCPConn
	bufrw *bufio.ReadWriter
}

func (this *HijactRW) Write(data []byte) (int, error) {
	nn, err := this.bufrw.Write(data)
	this.bufrw.Flush()
	return nn, err
}

func (this *HijactRW) Read(p []byte) (int, error) {
	return this.bufrw.Read(p)
}

func NewHijackReadWriteCloser(conn *net.TCPConn, bufrw *bufio.ReadWriter) net.Conn {
	return &HijactRW{
		bufrw:   bufrw,
		TCPConn: conn,
	}
}

func proxyHandler(w http.ResponseWriter, r *http.Request) {
	hj, ok := w.(http.Hijacker)
	if !ok {
		http.Error(w, "webserver don't support hijacking", http.StatusInternalServerError)
		return
	}

	var proxyFor = r.Header.Get("X-Proxy-For")
	log.Println("proxy name:", proxyFor)

	connCh, ok := namedConnection[proxyFor]
	if !ok {
		http.Error(w, "inside error: proxy not ready to receive conn", http.StatusInternalServerError)
		return
	}
	hjconn, bufrw, err := hj.Hijack()
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		connCh <- nil
		return
	}
	if _, ok := hjconn.(*net.TCPConn); ok {
		log.Println("Hijack is tcp conn")
	}

	conn := NewHijackReadWriteCloser(hjconn.(*net.TCPConn), bufrw)
	connCh <- conn
}

type ProxyServer struct {
	domain string
	*http.ServeMux
	revProxies map[string]*httputil.ReverseProxy
	sync.RWMutex
}

func wsSendMessage(conn *websocket.Conn, message string) error {
	return conn.WriteJSON(&Msg{Type: TYPE_MESSAGE, Body: message})
}

func (ps *ProxyServer) newHomepageHandler() func(w http.ResponseWriter, r *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		w.Header().Add("Content-Type", "text/html")
		io.WriteString(w, fmt.Sprintf("TCP: recvBytes: %d, sendBytes: %d
",
			proxyStats.receivedBytes, proxyStats.sentBytes))
		io.WriteString(w, "
")
		for pname, _ := range ps.revProxies {
			io.WriteString(w, fmt.Sprintf("http proxy: %s
", pname))
		}
	}
}

func (ps *ProxyServer) newControlHandler() func(w http.ResponseWriter, r *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		// read listen port from request
		protocal, subdomain, port := FigureListenAddress(r)
		log.Println("proxy listen addr:", protocal, subdomain, port)

		// create websocket connection
		conn, err := upgrader.Upgrade(w, r, nil)
		if err != nil {
			http.Error(w, err.Error(), 502)
			return
		}
		defer conn.Close()
		log.Println(conn.RemoteAddr())

		tunnel := &Tunnel{wsconn: conn}
		// TCP: create new port to listen
		switch protocal {
		case "tcp":
			proxyAddr := fmt.Sprintf("0.0.0.0:%d", port)
			listener, err := NewTcpProxyListener(tunnel, proxyAddr)
			if err != nil {
				http.Error(w, err.Error(), 501)
				return
			}
			defer listener.Close()
		case "http":
			log.Println("start http proxy")
			tr := &http.Transport{
				Dial: tunnel.generateTransportDial(),
			}
			revProxy := &httputil.ReverseProxy{
				Director: func(req *http.Request) {
					log.Println("director:", req.RequestURI)
				},
				Transport: tr,
			}
			// should hook here
			// hook(HOOK_CREATE_HTTP_SUBDOMAIN, subdomain)
			// generate a uniq domain
			if subdomain == "" {
				subdomain = uniqName(5)
			}
			pxDomain := subdomain + "." + ps.domain
			log.Println("http px use domain:", pxDomain)
			if _, exists := ps.revProxies[pxDomain]; exists {
				wsSendMessage(conn, fmt.Sprintf("subdomain [%s] has already been taken", pxDomain))
				return
			}
			ps.Lock()
			ps.revProxies[pxDomain] = revProxy
			ps.Unlock()
			wsSendMessage(conn, fmt.Sprintf("visit http://%s", pxDomain))

			defer func() {
				ps.Lock()
				delete(ps.revProxies, pxDomain)
				ps.Unlock()
			}()
		default:
			log.Println("unknown protocal:", protocal)
			return
		}
		// HTTP: use httputil.ReverseProxy
		for {
			var msg Msg
			if err := conn.ReadJSON(&msg); err != nil {
				log.Println(err)
				break
			}
			log.Println("recv json:", msg)
		}
	}
}

func (p *ProxyServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	log.Println("request info:", r.Method, r.Host, r.RequestURI)
	//host, _, _ := net.SplitHostPort(r.Host)
	// http://stackoverflow.com/questions/6899069/why-are-request-url-host-and-scheme-blank-in-the-development-server
	r.URL.Scheme = "http" // ??
	r.URL.Host = r.Host   // ??
	log.Println("URL path:", r.URL.Path)
	log.Printf("pxies: %v", p.revProxies)
	if rpx, ok := p.revProxies[r.Host]; ok {
		log.Println("server http rev proxy")
		rpx.ServeHTTP(w, r)
		return
	}

	//if p.domain != host {
	//	http.Error(w, fmt.Sprintf("%s not ready", host), 504)
	//	return
	//}

	h, _ := p.Handler(r)
	h.ServeHTTP(w, r)
}

// domain, ex shengxiang.me
// dns should set *.shengxiang.me
func NewProxyServer(domain string) *ProxyServer {
	if domain == "" {
		domain = "localhost"
	}
	p := &ProxyServer{
		domain:     domain,
		ServeMux:   http.NewServeMux(),
		revProxies: make(map[string]*httputil.ReverseProxy),
	}
	p.HandleFunc("/", p.newHomepageHandler())
	p.HandleFunc("/ws", p.newControlHandler())
	p.HandleFunc("/proxyhijack", proxyHandler)

	return p
}
<|endoftext|>


<commit_before>package registry

import (
	"encoding/json"
	"errors"
	"net/http"
	"regexp"
)

var (
	ErrNoMorePages = errors.New("No more pages")
)

func (registry *Registry) getJson(url string, response interface{}) error {
	resp, err := registry.Client.Get(url)
	if resp != nil {
		defer resp.Body.Close()
	}
	if err != nil {
		return err
	}

	decoder := json.NewDecoder(resp.Body)
	err = decoder.Decode(response)
	if err != nil {
		return err
	}

	return nil
}

// getPaginatedJson accepts a string and a pointer, and returns the
// next page URL while updating pointed-to variable with a parsed JSON
// value. When there are no more pages it returns `ErrNoMorePages`.
func (registry *Registry) getPaginatedJson(url string, response interface{}) (string, error) {
	resp, err := registry.Client.Get(url)
	if resp != nil {
		defer resp.Body.Close()
	}
	if err != nil {
		return "", err
	}

	decoder := json.NewDecoder(resp.Body)
	err = decoder.Decode(response)
	if err != nil {
		return "", err
	}
	return getNextLink(resp)
}

var linkRE *regexp.Regexp = regexp.MustCompile(`^ *<?([^;>]+)>?(?:; *([a-z]+)="?([^";]*)"?)*$`)

func getNextLink(resp *http.Response) (string, error) {
	for _, link := range resp.Header[http.CanonicalHeaderKey("Link")] {
		parts := linkRE.FindStringSubmatch(link)
		if len(parts) < 4 {
			continue
		}
		// We have a structure like []string{(whole match), URL, key, value, ...}
		for i := 2; i < len(parts); i += 2 {
			if parts[i] == "rel" && parts[i+1] == "next" {
				return parts[1], nil
			}
		}
	}
	return "", ErrNoMorePages
}
<commit_msg>Review feedback<commit_after>package registry

import (
	"encoding/json"
	"errors"
	"net/http"
	"regexp"
)

var (
	ErrNoMorePages = errors.New("No more pages")
)

func (registry *Registry) getJson(url string, response interface{}) error {
	resp, err := registry.Client.Get(url)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	decoder := json.NewDecoder(resp.Body)
	err = decoder.Decode(response)
	if err != nil {
		return err
	}

	return nil
}

// getPaginatedJson accepts a string and a pointer, and returns the
// next page URL while updating pointed-to variable with a parsed JSON
// value. When there are no more pages it returns `ErrNoMorePages`.
func (registry *Registry) getPaginatedJson(url string, response interface{}) (string, error) {
	resp, err := registry.Client.Get(url)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()

	decoder := json.NewDecoder(resp.Body)
	err = decoder.Decode(response)
	if err != nil {
		return "", err
	}
	return getNextLink(resp)
}

// Matches an RFC 5988 (https://tools.ietf.org/html/rfc5988#section-5)
// Link header. For example,
//
//    <http://registry.example.com/v2/_catalog?n=5&last=tag5>; type="application/json"; rel="next"
//
// The URL is _supposed_ to be wrapped by angle brackets `< ... >`,
// but e.g., quay.io does not include them. Similarly, params like
// `rel="next"` may not have quoted values in the wild.
var nextLinkRE = regexp.MustCompile(`^ *<?([^;>]+)>? *(?:;[^;]*)*; *rel="?next"?(?:;.*)?`)

func getNextLink(resp *http.Response) (string, error) {
	for _, link := range resp.Header[http.CanonicalHeaderKey("Link")] {
		parts := nextLinkRE.FindStringSubmatch(link)
		if parts != nil {
			return parts[1], nil
		}
	}
	return "", ErrNoMorePages
}
<|endoftext|>


<commit_before>package registry

import (
	"encoding/json"
	"errors"
	"fmt"
	"path"

	etcdErr "github.com/coreos/fleet/third_party/github.com/coreos/etcd/error"
	"github.com/coreos/fleet/third_party/github.com/coreos/go-etcd/etcd"
	log "github.com/coreos/fleet/third_party/github.com/golang/glog"

	"github.com/coreos/fleet/unit"
)

const (
	unitPrefix = "/unit/"
	// Legacy versions of fleet stored payloads instead of units
	payloadPrefix = "/payload/"
)

func (r *EtcdRegistry) storeOrGetUnit(u unit.Unit) (err error) {
	key := r.hashedUnitPath(u.Hash())
	json, err := marshal(u)
	if err != nil {
		return err
	}

	log.V(3).Infof("Storing Unit(%s) in Registry: %s", u.Hash(), json)
	_, err = r.etcd.Create(key, json, 0)
	// unit is already stored
	if err != nil && err.(*etcd.EtcdError).ErrorCode == etcdErr.EcodeNodeExist {
		log.V(2).Infof("Unit(%s) already exists in Registry", u.Hash())
		// TODO(jonboulle): verify more here?
		err = nil
	}
	return
}

// getUnitFromLegacyPayload tries to extract a Unit from a legacy JobPayload of the given name
func (r *EtcdRegistry) getUnitFromLegacyPayload(name string) (*unit.Unit, error) {
	key := path.Join(r.keyPrefix, payloadPrefix, name)
	resp, err := r.etcd.Get(key, true, true)

	if err != nil {
		return nil, err
	}

	var ljp LegacyJobPayload
	if err := unmarshal(resp.Node.Value, &ljp); err != nil {
		return nil, errors.New(fmt.Sprintf("Error unmarshaling LegacyJobPayload(%s): %v", name, err))
	}
	if ljp.Name != name {
		return nil, errors.New(fmt.Sprintf("Payload name in Registry (%s) does not match expected name (%s)", ljp.Name, name))
	}
	// After the unmarshaling, the LegacyPayload should contain a fully hydrated Unit
	return &ljp.Unit, nil
}

// getUnitByHash retrieves from the Registry the Unit associated with the given Hash
func (r *EtcdRegistry) getUnitByHash(hash unit.Hash) *unit.Unit {
	key := r.hashedUnitPath(hash)
	resp, err := r.etcd.Get(key, false, true)
	if err != nil {
		return nil
	}
	var u unit.Unit
	if err := unmarshal(resp.Node.Value, &u); err != nil {
		log.Errorf("Error unmarshaling Unit(%s): %v", hash, err)
		return nil
	}
	return &u
}

func (r *EtcdRegistry) hashedUnitPath(hash unit.Hash) string {
	return path.Join(r.keyPrefix, unitPrefix, hash.String())
}

// LegacyJobPayload deals with the legacy concept of a "JobPayload" (deprecated by Units).
// The associated marshaling/unmarshaling methods deal with Payloads encoded in this legacy format.
type LegacyJobPayload struct {
	Name string
	Unit unit.Unit
}

func (ljp *LegacyJobPayload) UnmarshalJSON(data []byte) error {
	var ljpm legacyJobPayloadModel
	err := json.Unmarshal(data, &ljpm)
	if err != nil {
		return errors.New(fmt.Sprintf("Unable to JSON-deserialize object: %s", err))
	}

	if len(ljpm.Unit.Raw) > 0 {
		ljp.Unit = *unit.NewUnit(ljpm.Unit.Raw)
	} else {
		ljp.Unit = *unit.NewUnitFromLegacyContents(ljpm.Unit.Contents)
	}
	ljp.Name = ljpm.Name

	return nil
}

// legacyJobPayloadModel is an abstraction to deal with serialized LegacyJobPayloads
type legacyJobPayloadModel struct {
	Name string
	Unit unitFileModel
}

// unitFileModel is an abstraction to deal with serialized LegacyJobPayloads
type unitFileModel struct {
	Contents map[string]map[string]string
	Raw      string
}
<commit_msg>fix(registry): Ignore KeyNotFound in getUnitFromLegacyPayload<commit_after>package registry

import (
	"encoding/json"
	"errors"
	"fmt"
	"path"

	etcdErr "github.com/coreos/fleet/third_party/github.com/coreos/etcd/error"
	"github.com/coreos/fleet/third_party/github.com/coreos/go-etcd/etcd"
	log "github.com/coreos/fleet/third_party/github.com/golang/glog"

	"github.com/coreos/fleet/unit"
)

const (
	unitPrefix = "/unit/"
	// Legacy versions of fleet stored payloads instead of units
	payloadPrefix = "/payload/"
)

func (r *EtcdRegistry) storeOrGetUnit(u unit.Unit) (err error) {
	key := r.hashedUnitPath(u.Hash())
	json, err := marshal(u)
	if err != nil {
		return err
	}

	log.V(3).Infof("Storing Unit(%s) in Registry: %s", u.Hash(), json)
	_, err = r.etcd.Create(key, json, 0)
	// unit is already stored
	if err != nil && err.(*etcd.EtcdError).ErrorCode == etcdErr.EcodeNodeExist {
		log.V(2).Infof("Unit(%s) already exists in Registry", u.Hash())
		// TODO(jonboulle): verify more here?
		err = nil
	}
	return
}

// getUnitFromLegacyPayload tries to extract a Unit from a legacy JobPayload of the given name
func (r *EtcdRegistry) getUnitFromLegacyPayload(name string) (*unit.Unit, error) {
	key := path.Join(r.keyPrefix, payloadPrefix, name)
	resp, err := r.etcd.Get(key, true, true)
	if err != nil {
		if e, ok := err.(*etcd.EtcdError); ok && e.ErrorCode == etcdErr.EcodeKeyNotFound {
			err = nil
		}
		return nil, err
	}

	var ljp LegacyJobPayload
	if err := unmarshal(resp.Node.Value, &ljp); err != nil {
		return nil, errors.New(fmt.Sprintf("Error unmarshaling LegacyJobPayload(%s): %v", name, err))
	}
	if ljp.Name != name {
		return nil, errors.New(fmt.Sprintf("Payload name in Registry (%s) does not match expected name (%s)", ljp.Name, name))
	}
	// After the unmarshaling, the LegacyPayload should contain a fully hydrated Unit
	return &ljp.Unit, nil
}

// getUnitByHash retrieves from the Registry the Unit associated with the given Hash
func (r *EtcdRegistry) getUnitByHash(hash unit.Hash) *unit.Unit {
	key := r.hashedUnitPath(hash)
	resp, err := r.etcd.Get(key, false, true)
	if err != nil {
		return nil
	}
	var u unit.Unit
	if err := unmarshal(resp.Node.Value, &u); err != nil {
		log.Errorf("Error unmarshaling Unit(%s): %v", hash, err)
		return nil
	}
	return &u
}

func (r *EtcdRegistry) hashedUnitPath(hash unit.Hash) string {
	return path.Join(r.keyPrefix, unitPrefix, hash.String())
}

// LegacyJobPayload deals with the legacy concept of a "JobPayload" (deprecated by Units).
// The associated marshaling/unmarshaling methods deal with Payloads encoded in this legacy format.
type LegacyJobPayload struct {
	Name string
	Unit unit.Unit
}

func (ljp *LegacyJobPayload) UnmarshalJSON(data []byte) error {
	var ljpm legacyJobPayloadModel
	err := json.Unmarshal(data, &ljpm)
	if err != nil {
		return errors.New(fmt.Sprintf("Unable to JSON-deserialize object: %s", err))
	}

	if len(ljpm.Unit.Raw) > 0 {
		ljp.Unit = *unit.NewUnit(ljpm.Unit.Raw)
	} else {
		ljp.Unit = *unit.NewUnitFromLegacyContents(ljpm.Unit.Contents)
	}
	ljp.Name = ljpm.Name

	return nil
}

// legacyJobPayloadModel is an abstraction to deal with serialized LegacyJobPayloads
type legacyJobPayloadModel struct {
	Name string
	Unit unitFileModel
}

// unitFileModel is an abstraction to deal with serialized LegacyJobPayloads
type unitFileModel struct {
	Contents map[string]map[string]string
	Raw      string
}
<|endoftext|>