<commit_before>// Copyright 2014 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 (
	"fmt"
	"io"
	"log"
	"os"
	"strconv"
	"strings"
	"time"
	"unicode/utf8"
)

/*
The archive format is:

First, on a line by itself
	!<arch>

Then zero or more file records. Each file record has a fixed-size one-line header
followed by data bytes followed by an optional padding byte. The header is:

	%-16s%-12d%-6d%-6d%-8o%-10d`
	name mtime uid gid mode size

(note the trailing backquote). The %-16s here means at most 16 *bytes* of
the name, and if shorter, space padded on the right.
*/

const usageMessage = `Usage: pack op file.a [name....]
Where op is one of prtx optionally followed by v for verbose output.
For more information, run
	godoc cmd/pack`

func usage() {
	fmt.Fprintln(os.Stderr, usageMessage)
	os.Exit(2)
}

func main() {
	log.SetFlags(0)
	// need "pack op archive" at least.
	if len(os.Args) < 3 {
		usage()
	}
	setOp(os.Args[1])
	var ar *Archive
	switch op {
	case 'p':
		ar = archive(os.Args[2], os.O_RDONLY, os.Args[3:])
		ar.scan(ar.printContents)
	case 'r':
		ar = archive(os.Args[2], os.O_RDWR, os.Args[3:])
		ar.scan(ar.skipContents)
		ar.addFiles()
	case 't':
		ar = archive(os.Args[2], os.O_RDONLY, os.Args[3:])
		ar.scan(ar.tableOfContents)
	case 'x':
		ar = archive(os.Args[2], os.O_RDONLY, os.Args[3:])
		ar.scan(ar.extractContents)
	default:
		usage()
	}
	if len(ar.files) > 0 {
		log.Fatalf("pack: file %q not in archive", ar.files[0])
	}
}

// The unusual ancestry means the arguments are not Go-standard.
// These variables hold the decoded operation specified by the first argument.
// op holds the operation we are doing (prtx).
// verbose tells whether the 'v' option was specified.
var (
	op      rune
	verbose bool
)

// setOp parses the operation string (first argument).
func setOp(arg string) {
	for _, r := range arg {
		switch r {
		case 'p', 'r', 't', 'x':
			if op != 0 {
				// At most one can be set.
				usage()
			}
			op = r
		case 'v':
			if verbose {
				// Can be set only once.
				usage()
			}
			verbose = true
		default:
			usage()
		}
	}
}

const (
	arHeader    = "!<arch>\n"
	entryHeader = "%s%-12d%-6d%-6d%-8o%-10d`\n"
	// In entryHeader the first entry, the name, is always printed as 16 bytes right-padded.
	entryLen   = 16 + 12 + 6 + 6 + 8 + 10 + 1 + 1
	timeFormat = "Jan _2 15:04 2006"
)

// An Archive represents an open archive file. It is always scanned sequentially
// from start to end, without backing up.
type Archive struct {
	fd    *os.File // Open file descriptor.
	files []string // Explicit list of files to be processed.
}

// archive opens (or if necessary creates) the named archive.
func archive(name string, mode int, files []string) *Archive {
	fd, err := os.OpenFile(name, mode, 0)
	if err != nil && mode == os.O_RDWR && os.IsNotExist(err) {
		fd, err = create(name)
	}
	if err != nil {
		log.Fatal("pack: ", err)
	}
	mustBeArchive(fd)
	return &Archive{
		fd:    fd,
		files: files,
	}
}

// create creates and initializes an archive that does not exist.
func create(name string) (*os.File, error) {
	fd, err := os.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
	if err != nil {
		return nil, err
	}
	fmt.Fprint(fd, arHeader)
	fd.Seek(0, 0)
	return fd, nil
}

// mustBeArchive verifies the header of the file. It assumes the file offset
// is 0 coming in, and leaves it positioned immediately after the header.
func mustBeArchive(fd *os.File) {
	buf := make([]byte, len(arHeader))
	_, err := io.ReadFull(fd, buf)
	if err != nil || string(buf) != arHeader {
		log.Fatal("pack: file is not an archive: bad header")
	}
}

// An Entry is the internal representation of the per-file header information of one entry in the archive.
type Entry struct {
	name  string
	mtime int64
	uid   int
	gid   int
	mode  os.FileMode
	size  int64
}

func (e *Entry) String() string {
	return fmt.Sprintf("%s %6d/%-6d %12d %s %s",
		(e.mode & 0777).String(),
		e.uid,
		e.gid,
		e.size,
		time.Unix(e.mtime, 0).Format(timeFormat),
		e.name)
}

// readMetadata reads and parses the metadata for the next entry in the archive.
func (ar *Archive) readMetadata() *Entry {
	buf := make([]byte, entryLen)
	_, err := io.ReadFull(ar.fd, buf)
	if err == io.EOF {
		// No entries left.
		return nil
	}
	if err != nil || buf[entryLen-2] != '`' || buf[entryLen-1] != '\n' {
		log.Fatal("pack: file is not an archive: bad entry")
	}
	entry := new(Entry)
	entry.name = strings.TrimRight(string(buf[:16]), " ")
	if len(entry.name) == 0 {
		log.Fatal("pack: file is not an archive: bad name")
	}
	buf = buf[16:]
	str := string(buf)
	get := func(width, base, bitsize int) int64 {
		v, err := strconv.ParseInt(strings.TrimRight(str[:width], " "), base, bitsize)
		if err != nil {
			log.Fatal("pack: file is not an archive: bad number in entry: ", err)
		}
		str = str[width:]
		return v
	}
	// %-16s%-12d%-6d%-6d%-8o%-10d`
	entry.mtime = get(12, 10, 64)
	entry.uid = int(get(6, 10, 32))
	entry.gid = int(get(6, 10, 32))
	entry.mode = os.FileMode(get(8, 8, 32))
	entry.size = get(10, 10, 64)
	return entry
}

// scan scans the archive and executes the specified action on each entry.
// When action returns, the file offset is at the start of the next entry.
func (ar *Archive) scan(action func(*Entry)) {
	for {
		entry := ar.readMetadata()
		if entry == nil {
			break
		}
		action(entry)
	}
}

// listEntry prints to standard output a line describing the entry.
func listEntry(ar *Archive, entry *Entry, verbose bool) {
	if verbose {
		fmt.Fprintf(stdout, "%s\n", entry)
	} else {
		fmt.Fprintf(stdout, "%s\n", entry.name)
	}
}

// output copies the entry to the specified writer.
func (ar *Archive) output(entry *Entry, w io.Writer) {
	n, err := io.Copy(w, io.LimitReader(ar.fd, entry.size))
	if err != nil {
		log.Fatal("pack: ", err)
	}
	if n != entry.size {
		log.Fatal("pack: short file")
	}
	if entry.size&1 == 1 {
		_, err := ar.fd.Seek(1, 1)
		if err != nil {
			log.Fatal("pack: ", err)
		}
	}
}

// skip skips the entry without reading it.
func (ar *Archive) skip(entry *Entry) {
	size := entry.size
	if size&1 == 1 {
		size++
	}
	_, err := ar.fd.Seek(size, 1)
	if err != nil {
		log.Fatal("pack: ", err)
	}
}

// match reports whether the entry matches the argument list.
// If it does, it also drops the file from the to-be-processed list.
func (ar *Archive) match(entry *Entry) bool {
	if len(ar.files) == 0 {
		return true
	}
	for i, name := range ar.files {
		if entry.name == name {
			copy(ar.files[i:], ar.files[i+1:])
			ar.files = ar.files[:len(ar.files)-1]
			return true
		}
	}
	return false
}

// addFiles adds files to the archive. The archive is known to be
// sane and we are positioned at the end. No attempt is made
// to check for existing files.
func (ar *Archive) addFiles() {
	if len(ar.files) == 0 {
		usage()
	}
	for _, file := range ar.files {
		if verbose {
			fmt.Printf("%s\n", file)
		}
		fd, err := os.Open(file)
		if err != nil {
			log.Fatal("pack: ", err)
		}
		ar.addFile(fd)
	}
	ar.files = nil
}

// FileLike abstracts the few methods we need, so we can test without needing real files.
type FileLike interface {
	Name() string
	Stat() (os.FileInfo, error)
	Read([]byte) (int, error)
	Close() error
}

// addFile adds a single file to the archive
func (ar *Archive) addFile(fd FileLike) {
	defer fd.Close()
	// Format the entry.
	// First, get its info.
	info, err := fd.Stat()
	if err != nil {
		log.Fatal("pack: ", err)
	}
	// mtime, uid, gid are all zero so repeated builds produce identical output.
	mtime := int64(0)
	uid := 0
	gid := 0
	n, err := fmt.Fprintf(ar.fd, entryHeader, exactly16Bytes(info.Name()), mtime, uid, gid, info.Mode(), info.Size())
	if err != nil || n != entryLen {
		log.Fatal("pack: writing entry header: ", err)
	}
	n64, err := io.Copy(ar.fd, fd)
	if err != nil {
		log.Fatal("pack: writing file: ", err)
	}
	if n64 != info.Size() {
		log.Fatal("pack: writing file: wrote %d bytes; file is size %d", n64, info.Size())
	}
	if info.Size()&1 == 1 {
		_, err = ar.fd.Write([]byte{0})
		if err != nil {
			log.Fatal("pack: writing archive: ", err)
		}
	}
}

// exactly16Bytes truncates the string if necessary so it is at most 16 bytes long,
// then pads the result with spaces to be exactly 16 bytes.
// Fmt uses runes for its width calculation, but we need bytes in the entry header.
func exactly16Bytes(s string) string {
	for len(s) > 16 {
		_, wid := utf8.DecodeLastRuneInString(s)
		s = s[:len(s)-wid]
	}
	const sixteenSpaces = "                "
	s += sixteenSpaces[:16-len(s)]
	return s
}

// Finally, the actual commands. Each is an action.

// can be modified for testing.
var stdout io.Writer = os.Stdout

// printContents implements the 'p' command.
func (ar *Archive) printContents(entry *Entry) {
	if ar.match(entry) {
		if verbose {
			listEntry(ar, entry, false)
		}
		ar.output(entry, stdout)
	} else {
		ar.skip(entry)
	}
}

// skipContents implements the first part of the 'r' command.
// It just scans the archive to make sure it's intact.
func (ar *Archive) skipContents(entry *Entry) {
	ar.skip(entry)
}

// tableOfContents implements the 't' command.
func (ar *Archive) tableOfContents(entry *Entry) {
	if ar.match(entry) {
		listEntry(ar, entry, verbose)
	}
	ar.skip(entry)
}

// extractContents implements the 'x' command.
func (ar *Archive) extractContents(entry *Entry) {
	if ar.match(entry) {
		if verbose {
			listEntry(ar, entry, false)
		}
		fd, err := os.OpenFile(entry.name, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, entry.mode)
		if err != nil {
			log.Fatal("pack: ", err)
		}
		ar.output(entry, fd)
		fd.Close()
	} else {
		ar.skip(entry)
	}
}
<commit_msg>cmd/pack: use log.SetPrefix to make log calls more compact and consistent Taking my own advice from a review of addr2line.<commit_after>// Copyright 2014 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 (
	"fmt"
	"io"
	"log"
	"os"
	"strconv"
	"strings"
	"time"
	"unicode/utf8"
)

/*
The archive format is:

First, on a line by itself
	!<arch>

Then zero or more file records. Each file record has a fixed-size one-line header
followed by data bytes followed by an optional padding byte. The header is:

	%-16s%-12d%-6d%-6d%-8o%-10d`
	name mtime uid gid mode size

(note the trailing backquote). The %-16s here means at most 16 *bytes* of
the name, and if shorter, space padded on the right.
*/

const usageMessage = `Usage: pack op file.a [name....]
Where op is one of prtx optionally followed by v for verbose output.
For more information, run
	godoc cmd/pack`

func usage() {
	fmt.Fprintln(os.Stderr, usageMessage)
	os.Exit(2)
}

func main() {
	log.SetFlags(0)
	log.SetPrefix("pack: ")
	// need "pack op archive" at least.
	if len(os.Args) < 3 {
		usage()
	}
	setOp(os.Args[1])
	var ar *Archive
	switch op {
	case 'p':
		ar = archive(os.Args[2], os.O_RDONLY, os.Args[3:])
		ar.scan(ar.printContents)
	case 'r':
		ar = archive(os.Args[2], os.O_RDWR, os.Args[3:])
		ar.scan(ar.skipContents)
		ar.addFiles()
	case 't':
		ar = archive(os.Args[2], os.O_RDONLY, os.Args[3:])
		ar.scan(ar.tableOfContents)
	case 'x':
		ar = archive(os.Args[2], os.O_RDONLY, os.Args[3:])
		ar.scan(ar.extractContents)
	default:
		usage()
	}
	if len(ar.files) > 0 {
		log.Fatalf("file %q not in archive", ar.files[0])
	}
}

// The unusual ancestry means the arguments are not Go-standard.
// These variables hold the decoded operation specified by the first argument.
// op holds the operation we are doing (prtx).
// verbose tells whether the 'v' option was specified.
var (
	op      rune
	verbose bool
)

// setOp parses the operation string (first argument).
func setOp(arg string) {
	for _, r := range arg {
		switch r {
		case 'p', 'r', 't', 'x':
			if op != 0 {
				// At most one can be set.
				usage()
			}
			op = r
		case 'v':
			if verbose {
				// Can be set only once.
				usage()
			}
			verbose = true
		default:
			usage()
		}
	}
}

const (
	arHeader    = "!<arch>\n"
	entryHeader = "%s%-12d%-6d%-6d%-8o%-10d`\n"
	// In entryHeader the first entry, the name, is always printed as 16 bytes right-padded.
	entryLen   = 16 + 12 + 6 + 6 + 8 + 10 + 1 + 1
	timeFormat = "Jan _2 15:04 2006"
)

// An Archive represents an open archive file. It is always scanned sequentially
// from start to end, without backing up.
type Archive struct {
	fd    *os.File // Open file descriptor.
	files []string // Explicit list of files to be processed.
}

// archive opens (or if necessary creates) the named archive.
func archive(name string, mode int, files []string) *Archive {
	fd, err := os.OpenFile(name, mode, 0)
	if err != nil && mode == os.O_RDWR && os.IsNotExist(err) {
		fd, err = create(name)
	}
	if err != nil {
		log.Fatal(err)
	}
	mustBeArchive(fd)
	return &Archive{
		fd:    fd,
		files: files,
	}
}

// create creates and initializes an archive that does not exist.
func create(name string) (*os.File, error) {
	fd, err := os.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
	if err != nil {
		return nil, err
	}
	fmt.Fprint(fd, arHeader)
	fd.Seek(0, 0)
	return fd, nil
}

// mustBeArchive verifies the header of the file. It assumes the file offset
// is 0 coming in, and leaves it positioned immediately after the header.
func mustBeArchive(fd *os.File) {
	buf := make([]byte, len(arHeader))
	_, err := io.ReadFull(fd, buf)
	if err != nil || string(buf) != arHeader {
		log.Fatal("file is not an archive: bad header")
	}
}

// An Entry is the internal representation of the per-file header information of one entry in the archive.
type Entry struct {
	name  string
	mtime int64
	uid   int
	gid   int
	mode  os.FileMode
	size  int64
}

func (e *Entry) String() string {
	return fmt.Sprintf("%s %6d/%-6d %12d %s %s",
		(e.mode & 0777).String(),
		e.uid,
		e.gid,
		e.size,
		time.Unix(e.mtime, 0).Format(timeFormat),
		e.name)
}

// readMetadata reads and parses the metadata for the next entry in the archive.
func (ar *Archive) readMetadata() *Entry {
	buf := make([]byte, entryLen)
	_, err := io.ReadFull(ar.fd, buf)
	if err == io.EOF {
		// No entries left.
		return nil
	}
	if err != nil || buf[entryLen-2] != '`' || buf[entryLen-1] != '\n' {
		log.Fatal("file is not an archive: bad entry")
	}
	entry := new(Entry)
	entry.name = strings.TrimRight(string(buf[:16]), " ")
	if len(entry.name) == 0 {
		log.Fatal("file is not an archive: bad name")
	}
	buf = buf[16:]
	str := string(buf)
	get := func(width, base, bitsize int) int64 {
		v, err := strconv.ParseInt(strings.TrimRight(str[:width], " "), base, bitsize)
		if err != nil {
			log.Fatal("file is not an archive: bad number in entry: ", err)
		}
		str = str[width:]
		return v
	}
	// %-16s%-12d%-6d%-6d%-8o%-10d`
	entry.mtime = get(12, 10, 64)
	entry.uid = int(get(6, 10, 32))
	entry.gid = int(get(6, 10, 32))
	entry.mode = os.FileMode(get(8, 8, 32))
	entry.size = get(10, 10, 64)
	return entry
}

// scan scans the archive and executes the specified action on each entry.
// When action returns, the file offset is at the start of the next entry.
func (ar *Archive) scan(action func(*Entry)) {
	for {
		entry := ar.readMetadata()
		if entry == nil {
			break
		}
		action(entry)
	}
}

// listEntry prints to standard output a line describing the entry.
func listEntry(ar *Archive, entry *Entry, verbose bool) {
	if verbose {
		fmt.Fprintf(stdout, "%s\n", entry)
	} else {
		fmt.Fprintf(stdout, "%s\n", entry.name)
	}
}

// output copies the entry to the specified writer.
func (ar *Archive) output(entry *Entry, w io.Writer) {
	n, err := io.Copy(w, io.LimitReader(ar.fd, entry.size))
	if err != nil {
		log.Fatal(err)
	}
	if n != entry.size {
		log.Fatal("short file")
	}
	if entry.size&1 == 1 {
		_, err := ar.fd.Seek(1, 1)
		if err != nil {
			log.Fatal(err)
		}
	}
}

// skip skips the entry without reading it.
func (ar *Archive) skip(entry *Entry) {
	size := entry.size
	if size&1 == 1 {
		size++
	}
	_, err := ar.fd.Seek(size, 1)
	if err != nil {
		log.Fatal(err)
	}
}

// match reports whether the entry matches the argument list.
// If it does, it also drops the file from the to-be-processed list.
func (ar *Archive) match(entry *Entry) bool {
	if len(ar.files) == 0 {
		return true
	}
	for i, name := range ar.files {
		if entry.name == name {
			copy(ar.files[i:], ar.files[i+1:])
			ar.files = ar.files[:len(ar.files)-1]
			return true
		}
	}
	return false
}

// addFiles adds files to the archive. The archive is known to be
// sane and we are positioned at the end. No attempt is made
// to check for existing files.
func (ar *Archive) addFiles() {
	if len(ar.files) == 0 {
		usage()
	}
	for _, file := range ar.files {
		if verbose {
			fmt.Printf("%s\n", file)
		}
		fd, err := os.Open(file)
		if err != nil {
			log.Fatal(err)
		}
		ar.addFile(fd)
	}
	ar.files = nil
}

// FileLike abstracts the few methods we need, so we can test without needing real files.
type FileLike interface {
	Name() string
	Stat() (os.FileInfo, error)
	Read([]byte) (int, error)
	Close() error
}

// addFile adds a single file to the archive
func (ar *Archive) addFile(fd FileLike) {
	defer fd.Close()
	// Format the entry.
	// First, get its info.
	info, err := fd.Stat()
	if err != nil {
		log.Fatal(err)
	}
	// mtime, uid, gid are all zero so repeated builds produce identical output.
	mtime := int64(0)
	uid := 0
	gid := 0
	n, err := fmt.Fprintf(ar.fd, entryHeader, exactly16Bytes(info.Name()), mtime, uid, gid, info.Mode(), info.Size())
	if err != nil || n != entryLen {
		log.Fatal("writing entry header: ", err)
	}
	n64, err := io.Copy(ar.fd, fd)
	if err != nil {
		log.Fatal("writing file: ", err)
	}
	if n64 != info.Size() {
		log.Fatal("writing file: wrote %d bytes; file is size %d", n64, info.Size())
	}
	if info.Size()&1 == 1 {
		_, err = ar.fd.Write([]byte{0})
		if err != nil {
			log.Fatal("writing archive: ", err)
		}
	}
}

// exactly16Bytes truncates the string if necessary so it is at most 16 bytes long,
// then pads the result with spaces to be exactly 16 bytes.
// Fmt uses runes for its width calculation, but we need bytes in the entry header.
func exactly16Bytes(s string) string {
	for len(s) > 16 {
		_, wid := utf8.DecodeLastRuneInString(s)
		s = s[:len(s)-wid]
	}
	const sixteenSpaces = "                "
	s += sixteenSpaces[:16-len(s)]
	return s
}

// Finally, the actual commands. Each is an action.

// can be modified for testing.
var stdout io.Writer = os.Stdout

// printContents implements the 'p' command.
func (ar *Archive) printContents(entry *Entry) {
	if ar.match(entry) {
		if verbose {
			listEntry(ar, entry, false)
		}
		ar.output(entry, stdout)
	} else {
		ar.skip(entry)
	}
}

// skipContents implements the first part of the 'r' command.
// It just scans the archive to make sure it's intact.
func (ar *Archive) skipContents(entry *Entry) {
	ar.skip(entry)
}

// tableOfContents implements the 't' command.
func (ar *Archive) tableOfContents(entry *Entry) {
	if ar.match(entry) {
		listEntry(ar, entry, verbose)
	}
	ar.skip(entry)
}

// extractContents implements the 'x' command.
func (ar *Archive) extractContents(entry *Entry) {
	if ar.match(entry) {
		if verbose {
			listEntry(ar, entry, false)
		}
		fd, err := os.OpenFile(entry.name, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, entry.mode)
		if err != nil {
			log.Fatal(err)
		}
		ar.output(entry, fd)
		fd.Close()
	} else {
		ar.skip(entry)
	}
}
<|endoftext|>


<commit_before>//
// Copyright (c) 2016 Intel Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package main

import (
	"bufio"
	"flag"
	"fmt"
	"os"
	"strconv"

	"github.com/golang/glog"
	"github.com/urfave/cli"

	agentApi "github.com/clearcontainers/agent/api"
	"github.com/containers/virtcontainers/pkg/hyperstart"
)

const unixSocketType = "unix"

const (
	commandInputMsg  = "Command: "
	sequenceInputMsg = "Sequence: "
	payloadInputMsg  = "Payload: "
)

const (
	sendTty = "tty"
	exit    = "exit"
)

var cmdList = map[int]string{
	1:   agentApi.CmdToString(agentApi.StartPodCmd),
	2:   agentApi.CmdToString(agentApi.DestroyPodCmd),
	3:   agentApi.CmdToString(agentApi.ExecCmd),
	4:   agentApi.CmdToString(agentApi.ReadyCmd),
	5:   agentApi.CmdToString(agentApi.AckCmd),
	6:   agentApi.CmdToString(agentApi.ErrorCmd),
	7:   agentApi.CmdToString(agentApi.WinsizeCmd),
	8:   agentApi.CmdToString(agentApi.PingCmd),
	9:   agentApi.CmdToString(agentApi.NewContainerCmd),
	10:  agentApi.CmdToString(agentApi.KillContainerCmd),
	11:  agentApi.CmdToString(agentApi.RemoveContainerCmd),
	50:  sendTty,
	100: exit,
}

var waitingInput = false
var waitingInputText = ""

func magicLog(format string, args ...interface{}) {
	waitInput, waitInputText := getWaitingInputs()

	if waitInput == true {
		fmt.Println("")
	}

	fmt.Printf(format, args...)

	if waitInput == true {
		fmt.Println("")
		fmt.Printf(waitInputText)
	}
}

func setWaitingInputs(input bool, inputText string) {
	waitingInput = input
	waitingInputText = inputText
}

func getWaitingInputs() (bool, string) {
	return waitingInput, waitingInputText
}

func dumpSupportedCommands() {
	magicLog("== Supported commands ==\n")
	magicLog("  1 - STARTPOD\n")
	magicLog("  2 - DESTROYPOD\n")
	magicLog("  3 - EXECCMD\n")
	magicLog("  4 - READY\n")
	magicLog("  5 - ACK\n")
	magicLog("  6 - ERROR\n")
	magicLog("  7 - WINSIZE\n")
	magicLog("  8 - PING\n")
	magicLog("  9 - NEWCONTAINER\n")
	magicLog(" 10 - KILLCONTAINER\n")
	magicLog(" 11 - REMOVECONTAINER\n")
	magicLog(" 50 - TTY SEQUENCE\n")
	magicLog("100 - EXIT\n\n")
}

func dumpFrame(msg interface{}) {
	switch m := msg.(type) {
	case hyperstart.DecodedMessage:
		magicLog("DecodedMessage {\n\tCode: %x\n\tMessage: %s\n}\n", m.Code, m.Message)
	case hyperstart.TtyMessage:
		magicLog("TtyMessage {\n\tSession: %x\n\tMessage: %s\n}\n", m.Session, m.Message)
	}
}

func readStringNoDelimiter(reader *bufio.Reader, delim byte) (string, error) {
	input, err := reader.ReadBytes('\n')
	if err != nil {
		return "", err
	}

	strInput := string(input[:len(input)-1])

	return strInput, nil
}

func convertInputToCmd(input string) (string, error) {
	intInput, err := strconv.Atoi(input)
	if err != nil {
		return "", err
	}

	_, ok := cmdList[intInput]
	if ok == false {
		return "", fmt.Errorf("%d is not a valid command", intInput)
	}

	return cmdList[intInput], nil
}

func sendMessage(h *hyperstart.Hyperstart, ctlType bool, cmd string, payload string) error {
	payloadSlice, err := hyperstart.FormatMessage(payload)
	if err != nil {
		return err
	}

	if ctlType == true {
		msg, err := h.SendCtlMessage(cmd, payloadSlice)
		if err != nil {
			return err
		}

		if msg != nil {
			dumpFrame(*msg)
		}
	} else {
		seq, err := strconv.ParseUint(cmd, 10, 64)
		if err != nil {
			return err
		}

		ttyMsg := &hyperstart.TtyMessage{
			Session: seq,
			Message: payloadSlice,
		}

		err = h.SendIoMessage(ttyMsg)
		if err != nil {
			return err
		}
	}

	return nil
}

func monitorStdInLoop(h *hyperstart.Hyperstart, done chan<- bool) error {
	dumpSupportedCommands()
	reader := bufio.NewReader(os.Stdin)

	for {
		magicLog(commandInputMsg)
		setWaitingInputs(true, commandInputMsg)
		input, err := readStringNoDelimiter(reader, '\n')
		if err != nil {
			setWaitingInputs(false, "")
			magicLog("%s\n", err)
			break
		}
		setWaitingInputs(false, "")

		if input == "" {
			continue
		}

		cmd, err := convertInputToCmd(input)
		if err != nil {
			magicLog("%s\n", err)
			continue
		}

		if cmd == exit {
			break
		}

		ctlType := true

		if cmd == sendTty {
			ctlType = false
			magicLog(sequenceInputMsg)
			setWaitingInputs(true, sequenceInputMsg)
			cmd, err = readStringNoDelimiter(reader, '\n')
			if err != nil {
				setWaitingInputs(false, "")
				magicLog("%s\n", err)
				break
			}
			setWaitingInputs(false, "")
		}

		magicLog(payloadInputMsg)
		setWaitingInputs(true, payloadInputMsg)
		payload, err := readStringNoDelimiter(reader, '\n')
		if err != nil {
			setWaitingInputs(false, "")
			magicLog("%s\n", err)
			break
		}
		setWaitingInputs(false, "")

		err = sendMessage(h, ctlType, cmd, payload)
		if err != nil {
			magicLog("%s\n", err)
			continue
		}
	}

	done <- true
	return nil
}

func monitorTtyOutLoop(h *hyperstart.Hyperstart, done chan<- bool) error {
	for {
		msgCh := make(chan *hyperstart.TtyMessage, 1)
		errorCh := make(chan bool, 1)

		go func() {
			msg, err := h.ReadIoMessage()
			if err != nil {
				magicLog("%s\n", err)
				errorCh <- true
				return
			}

			msgCh <- msg
		}()

		select {
		case msg := <-msgCh:
			dumpFrame(*msg)
		case <-errorCh:
			done <- true
			break
		}
	}
}

func mainLoop(c *cli.Context) error {
	ctlSockPath := c.String("ctl")
	ttySockPath := c.String("tty")

	if ctlSockPath == "" || ttySockPath == "" {
		return fmt.Errorf("Missing socket path. Please provide CTL and TTY socket paths.")
	}

	h := hyperstart.NewHyperstart(ctlSockPath, ttySockPath, unixSocketType)

	if err := h.OpenSockets(); err != nil {
		return err
	}
	defer h.CloseSockets()

	if err := h.WaitForReady(); err != nil {
		return err
	}

	done := make(chan bool, 1)

	go monitorStdInLoop(h, done)
	go monitorTtyOutLoop(h, done)

	<-done

	return nil
}

func main() {
	flag.Parse()

	agentCli := cli.NewApp()
	agentCli.Name = "Agent CLI"
	agentCli.Version = "1.0.0"
	agentCli.Commands = []cli.Command{
		{
			Name:  "run",
			Usage: "send/receive on hyperstart sockets",
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "ctl",
					Value: "",
					Usage: "the CTL socket path",
				},
				cli.StringFlag{
					Name:  "tty",
					Value: "",
					Usage: "the TTY socket path",
				},
			},
			Action: func(context *cli.Context) error {
				return mainLoop(context)
			},
		},
	}

	err := agentCli.Run(os.Args)
	if err != nil {
		glog.Fatal(err)
	}
}
<commit_msg>hack: agent-cli: Fix golint issue<commit_after>//
// Copyright (c) 2016 Intel Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package main

import (
	"bufio"
	"flag"
	"fmt"
	"os"
	"strconv"

	"github.com/golang/glog"
	"github.com/urfave/cli"

	agentApi "github.com/clearcontainers/agent/api"
	"github.com/containers/virtcontainers/pkg/hyperstart"
)

const unixSocketType = "unix"

const (
	commandInputMsg  = "Command: "
	sequenceInputMsg = "Sequence: "
	payloadInputMsg  = "Payload: "
)

const (
	sendTty = "tty"
	exit    = "exit"
)

var cmdList = map[int]string{
	1:   agentApi.CmdToString(agentApi.StartPodCmd),
	2:   agentApi.CmdToString(agentApi.DestroyPodCmd),
	3:   agentApi.CmdToString(agentApi.ExecCmd),
	4:   agentApi.CmdToString(agentApi.ReadyCmd),
	5:   agentApi.CmdToString(agentApi.AckCmd),
	6:   agentApi.CmdToString(agentApi.ErrorCmd),
	7:   agentApi.CmdToString(agentApi.WinsizeCmd),
	8:   agentApi.CmdToString(agentApi.PingCmd),
	9:   agentApi.CmdToString(agentApi.NewContainerCmd),
	10:  agentApi.CmdToString(agentApi.KillContainerCmd),
	11:  agentApi.CmdToString(agentApi.RemoveContainerCmd),
	50:  sendTty,
	100: exit,
}

var waitingInput = false
var waitingInputText = ""

func magicLog(format string, args ...interface{}) {
	waitInput, waitInputText := getWaitingInputs()

	if waitInput == true {
		fmt.Println("")
	}

	fmt.Printf(format, args...)

	if waitInput == true {
		fmt.Println("")
		fmt.Printf(waitInputText)
	}
}

func setWaitingInputs(input bool, inputText string) {
	waitingInput = input
	waitingInputText = inputText
}

func getWaitingInputs() (bool, string) {
	return waitingInput, waitingInputText
}

func dumpSupportedCommands() {
	magicLog("== Supported commands ==\n")
	magicLog("  1 - STARTPOD\n")
	magicLog("  2 - DESTROYPOD\n")
	magicLog("  3 - EXECCMD\n")
	magicLog("  4 - READY\n")
	magicLog("  5 - ACK\n")
	magicLog("  6 - ERROR\n")
	magicLog("  7 - WINSIZE\n")
	magicLog("  8 - PING\n")
	magicLog("  9 - NEWCONTAINER\n")
	magicLog(" 10 - KILLCONTAINER\n")
	magicLog(" 11 - REMOVECONTAINER\n")
	magicLog(" 50 - TTY SEQUENCE\n")
	magicLog("100 - EXIT\n\n")
}

func dumpFrame(msg interface{}) {
	switch m := msg.(type) {
	case hyperstart.DecodedMessage:
		magicLog("DecodedMessage {\n\tCode: %x\n\tMessage: %s\n}\n", m.Code, m.Message)
	case hyperstart.TtyMessage:
		magicLog("TtyMessage {\n\tSession: %x\n\tMessage: %s\n}\n", m.Session, m.Message)
	}
}

func readStringNoDelimiter(reader *bufio.Reader, delim byte) (string, error) {
	input, err := reader.ReadBytes('\n')
	if err != nil {
		return "", err
	}

	strInput := string(input[:len(input)-1])

	return strInput, nil
}

func convertInputToCmd(input string) (string, error) {
	intInput, err := strconv.Atoi(input)
	if err != nil {
		return "", err
	}

	_, ok := cmdList[intInput]
	if ok == false {
		return "", fmt.Errorf("%d is not a valid command", intInput)
	}

	return cmdList[intInput], nil
}

func sendMessage(h *hyperstart.Hyperstart, ctlType bool, cmd string, payload string) error {
	payloadSlice, err := hyperstart.FormatMessage(payload)
	if err != nil {
		return err
	}

	if ctlType == true {
		msg, err := h.SendCtlMessage(cmd, payloadSlice)
		if err != nil {
			return err
		}

		if msg != nil {
			dumpFrame(*msg)
		}
	} else {
		seq, err := strconv.ParseUint(cmd, 10, 64)
		if err != nil {
			return err
		}

		ttyMsg := &hyperstart.TtyMessage{
			Session: seq,
			Message: payloadSlice,
		}

		err = h.SendIoMessage(ttyMsg)
		if err != nil {
			return err
		}
	}

	return nil
}

func monitorStdInLoop(h *hyperstart.Hyperstart, done chan<- bool) error {
	dumpSupportedCommands()
	reader := bufio.NewReader(os.Stdin)

	for {
		magicLog(commandInputMsg)
		setWaitingInputs(true, commandInputMsg)
		input, err := readStringNoDelimiter(reader, '\n')
		if err != nil {
			setWaitingInputs(false, "")
			magicLog("%s\n", err)
			break
		}
		setWaitingInputs(false, "")

		if input == "" {
			continue
		}

		cmd, err := convertInputToCmd(input)
		if err != nil {
			magicLog("%s\n", err)
			continue
		}

		if cmd == exit {
			break
		}

		ctlType := true

		if cmd == sendTty {
			ctlType = false
			magicLog(sequenceInputMsg)
			setWaitingInputs(true, sequenceInputMsg)
			cmd, err = readStringNoDelimiter(reader, '\n')
			if err != nil {
				setWaitingInputs(false, "")
				magicLog("%s\n", err)
				break
			}
			setWaitingInputs(false, "")
		}

		magicLog(payloadInputMsg)
		setWaitingInputs(true, payloadInputMsg)
		payload, err := readStringNoDelimiter(reader, '\n')
		if err != nil {
			setWaitingInputs(false, "")
			magicLog("%s\n", err)
			break
		}
		setWaitingInputs(false, "")

		err = sendMessage(h, ctlType, cmd, payload)
		if err != nil {
			magicLog("%s\n", err)
			continue
		}
	}

	done <- true
	return nil
}

func monitorTtyOutLoop(h *hyperstart.Hyperstart, done chan<- bool) error {
	for {
		msgCh := make(chan *hyperstart.TtyMessage, 1)
		errorCh := make(chan bool, 1)

		go func() {
			msg, err := h.ReadIoMessage()
			if err != nil {
				magicLog("%s\n", err)
				errorCh <- true
				return
			}

			msgCh <- msg
		}()

		select {
		case msg := <-msgCh:
			dumpFrame(*msg)
		case <-errorCh:
			done <- true
			break
		}
	}
}

func mainLoop(c *cli.Context) error {
	ctlSockPath := c.String("ctl")
	ttySockPath := c.String("tty")

	if ctlSockPath == "" || ttySockPath == "" {
		return fmt.Errorf("Missing socket path: please provide CTL and TTY socket paths")
	}

	h := hyperstart.NewHyperstart(ctlSockPath, ttySockPath, unixSocketType)

	if err := h.OpenSockets(); err != nil {
		return err
	}
	defer h.CloseSockets()

	if err := h.WaitForReady(); err != nil {
		return err
	}

	done := make(chan bool, 1)

	go monitorStdInLoop(h, done)
	go monitorTtyOutLoop(h, done)

	<-done

	return nil
}

func main() {
	flag.Parse()

	agentCli := cli.NewApp()
	agentCli.Name = "Agent CLI"
	agentCli.Version = "1.0.0"
	agentCli.Commands = []cli.Command{
		{
			Name:  "run",
			Usage: "send/receive on hyperstart sockets",
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "ctl",
					Value: "",
					Usage: "the CTL socket path",
				},
				cli.StringFlag{
					Name:  "tty",
					Value: "",
					Usage: "the TTY socket path",
				},
			},
			Action: func(context *cli.Context) error {
				return mainLoop(context)
			},
		},
	}

	err := agentCli.Run(os.Args)
	if err != nil {
		glog.Fatal(err)
	}
}
<|endoftext|>


<commit_before>//  This Source Code Form is subject to the terms of the Mozilla Public
//  License, v. 2.0. If a copy of the MPL was not distributed with this
//  file, You can obtain one at http://mozilla.org/MPL/2.0/.

package main

import (
	"math"
)

// A three component vector
type Vector []float64

// Create new zero vector
func NewZeroVector() Vector {

	return Vector(make([]float64, 3))
}

// Create new vector
func NewVector(x, y, z float64) Vector {

	v := NewZeroVector()

	v[0], v[1], v[2] = x, y, z

	return v
}

// Add two vectors
func (a Vector) Plus(b Vector) (c Vector) {

	c = NewZeroVector()

	for i, _ := range a {

		c[i] = a[i] + b[i]
	}

	return c
}

// Invert a vector's direction
func (a Vector) Negate() (minusA Vector) {

	minusA = NewZeroVector()

	for i, aComponent := range a {

		minusA[i] = -aComponent
	}

	return minusA
}

// Subtract two vectors
func (a Vector) Minus(b Vector) Vector {

	return a.Plus(b.Negate())
}

// Take the dot product of two vectors
func (a Vector) Dot(b Vector) float64 {

	sum := 0.0

	for i, _ := range a {

		sum += a[i] * b[i]
	}

	return sum
}

// Calculate the norm of a vector
func (a Vector) Norm() float64 {

	return math.Sqrt(a.Dot(a))
}
<commit_msg>Add dacart.Vector.Cross<commit_after>//  This Source Code Form is subject to the terms of the Mozilla Public
//  License, v. 2.0. If a copy of the MPL was not distributed with this
//  file, You can obtain one at http://mozilla.org/MPL/2.0/.

package main

import (
	"math"
)

// A three component cartesian vector
type Vector []float64

// Create new zero vector
func NewZeroVector() Vector {

	return Vector(make([]float64, 3))
}

// Create new vector
func NewVector(x, y, z float64) Vector {

	v := NewZeroVector()

	v[0], v[1], v[2] = x, y, z

	return v
}

// Add two vectors
func (a Vector) Plus(b Vector) (c Vector) {

	c = NewZeroVector()

	for i, _ := range a {

		c[i] = a[i] + b[i]
	}

	return c
}

// Invert a vector's direction
func (a Vector) Negate() (minusA Vector) {

	minusA = NewZeroVector()

	for i, aComponent := range a {

		minusA[i] = -aComponent
	}

	return minusA
}

// Subtract two vectors
func (a Vector) Minus(b Vector) Vector {

	return a.Plus(b.Negate())
}

// Take the dot product of two vectors
func (a Vector) Dot(b Vector) float64 {

	sum := 0.0

	for i, _ := range a {

		sum += a[i] * b[i]
	}

	return sum
}

// Calculate the norm of a vector
func (a Vector) Norm() float64 {

	return math.Sqrt(a.Dot(a))
}

// Take the cross product of two vectors
func (a Vector) Cross(b Vector) (c Vector) {

	c = NewZeroVector()

	for i, _ := range a {

		c[i] += a[(i+1)%3] * b[(i+2)%3]

		c[i] -= a[(i+2)%3] * b[(i+1)%3]
	}

	return c
}
<|endoftext|>


<commit_before>package bridge

import (
	"errors"
	"log"
	"net"
	"net/url"
	"os"
	"path"
	"strconv"
	"strings"
	"sync"

	dockerapi "github.com/fsouza/go-dockerclient"
)

type Bridge struct {
	sync.Mutex
	registry       RegistryAdapter
	docker         *dockerapi.Client
	services       map[string][]*Service
	deadContainers map[string]*DeadContainer
	config         Config
}

func New(docker *dockerapi.Client, adapterUri string, config Config) (*Bridge, error) {
	uri, err := url.Parse(adapterUri)
	if err != nil {
		return nil, errors.New("bad adapter uri: " + adapterUri)
	}
	factory, found := AdapterFactories.Lookup(uri.Scheme)
	if !found {
		return nil, errors.New("unrecognized adapter: " + adapterUri)
	}

	log.Println("Using", uri.Scheme, "adapter:", uri)
	return &Bridge{
		docker:         docker,
		config:         config,
		registry:       factory.New(uri),
		services:       make(map[string][]*Service),
		deadContainers: make(map[string]*DeadContainer),
	}, nil
}

func (b *Bridge) Ping() error {
	return b.registry.Ping()
}

func (b *Bridge) Add(containerId string) {
	b.Lock()
	defer b.Unlock()
	b.add(containerId, false)
}

func (b *Bridge) Remove(containerId string) {
	b.remove(containerId, true)
}

func (b *Bridge) RemoveOnExit(containerId string) {
	b.remove(containerId, b.config.DeregisterCheck == "always" || b.didExitCleanly(containerId))
}

func (b *Bridge) Refresh() {
	b.Lock()
	defer b.Unlock()

	for containerId, deadContainer := range b.deadContainers {
		deadContainer.TTL -= b.config.RefreshInterval
		if deadContainer.TTL <= 0 {
			delete(b.deadContainers, containerId)
		}
	}

	for containerId, services := range b.services {
		for _, service := range services {
			err := b.registry.Refresh(service)
			if err != nil {
				log.Println("refresh failed:", service.ID, err)
				continue
			}
			log.Println("refreshed:", containerId[:12], service.ID)
		}
	}
}

func (b *Bridge) Sync(quiet bool) {
	b.Lock()
	defer b.Unlock()

	containers, err := b.docker.ListContainers(dockerapi.ListContainersOptions{})
	if err != nil && quiet {
		log.Println("error listing containers, skipping sync")
		return
	} else if err != nil && !quiet {
		log.Fatal(err)
	}

	log.Printf("Syncing services on %d containers", len(containers))

	// NOTE: This assumes reregistering will do the right thing, i.e. nothing.
	// NOTE: This will NOT remove services.
	for _, listing := range containers {
		services := b.services[listing.ID]
		if services == nil {
			b.add(listing.ID, quiet)
		} else {
			for _, service := range services {
				err := b.registry.Register(service)
				if err != nil {
					log.Println("sync register failed:", service, err)
				}
			}
		}
	}
}

func (b *Bridge) add(containerId string, quiet bool) {
	if d := b.deadContainers[containerId]; d != nil {
		b.services[containerId] = d.Services
		delete(b.deadContainers, containerId)
	}

	if b.services[containerId] != nil {
		log.Println("container, ", containerId[:12], ", already exists, ignoring")
		// Alternatively, remove and readd or resubmit.
		return
	}

	container, err := b.docker.InspectContainer(containerId)
	if err != nil {
		log.Println("unable to inspect container:", containerId[:12], err)
		return
	}

	ports := make(map[string]ServicePort)

	// Extract configured host port mappings, relevant when using --net=host
	for port, published := range container.HostConfig.PortBindings {
		ports[string(port)] = servicePort(container, port, published)
	}

	// Extract runtime port mappings, relevant when using --net=bridge
	for port, published := range container.NetworkSettings.Ports {
		ports[string(port)] = servicePort(container, port, published)
	}

	if len(ports) == 0 && !quiet {
		log.Println("ignored:", container.ID[:12], "no published ports")
		return
	}

	for _, port := range ports {
		if b.config.Internal != true && port.HostPort == "" {
			if !quiet {
				log.Println("ignored:", container.ID[:12], "port", port.ExposedPort, "not published on host")
			}
			continue
		}
		service := b.newService(port, len(ports) > 1)
		if service == nil {
			if !quiet {
				log.Println("ignored:", container.ID[:12], "service on port", port.ExposedPort)
			}
			continue
		}
		err := b.registry.Register(service)
		if err != nil {
			log.Println("register failed:", service, err)
			continue
		}
		b.services[container.ID] = append(b.services[container.ID], service)
		log.Println("added:", container.ID[:12], service.ID)
	}
}

func (b *Bridge) newService(port ServicePort, isgroup bool) *Service {
	container := port.container
	defaultName := strings.Split(path.Base(container.Config.Image), ":")[0]

	// not sure about this logic. kind of want to remove it.
	hostname, err := os.Hostname()
	if err != nil {
		hostname = port.HostIP
	} else {
		if port.HostIP == "0.0.0.0" {
			ip, err := net.ResolveIPAddr("ip", hostname)
			if err == nil {
				port.HostIP = ip.String()
			}
		}
	}

	if b.config.HostIp != "" {
		port.HostIP = b.config.HostIp
	}

	metadata := serviceMetaData(container.Config, port.ExposedPort)

	ignore := mapDefault(metadata, "ignore", "")
	if ignore != "" {
		return nil
	}

	service := new(Service)
	service.Origin = port
	service.ID = hostname + ":" + container.Name[1:] + ":" + port.ExposedPort
	service.Name = mapDefault(metadata, "name", defaultName)
	if isgroup {
		 service.Name += "-" + port.ExposedPort
	}
	var p int
	if b.config.Internal == true {
		service.IP = port.ExposedIP
		p, _ = strconv.Atoi(port.ExposedPort)
	} else {
		service.IP = port.HostIP
		p, _ = strconv.Atoi(port.HostPort)
	}
	service.Port = p

	if port.PortType == "udp" {
		service.Tags = combineTags(
			mapDefault(metadata, "tags", ""), b.config.ForceTags, "udp")
		service.ID = service.ID + ":udp"
	} else {
		service.Tags = combineTags(
			mapDefault(metadata, "tags", ""), b.config.ForceTags)
	}

	id := mapDefault(metadata, "id", "")
	if id != "" {
		service.ID = id
	}

	delete(metadata, "id")
	delete(metadata, "tags")
	delete(metadata, "name")
	service.Attrs = metadata
	service.TTL = b.config.RefreshTtl

	return service
}

func (b *Bridge) remove(containerId string, deregister bool) {
	b.Lock()
	defer b.Unlock()

	if deregister {
		deregisterAll := func(services []*Service) {
			for _, service := range services {
				err := b.registry.Deregister(service)
				if err != nil {
					log.Println("deregister failed:", service.ID, err)
					continue
				}
				log.Println("removed:", containerId[:12], service.ID)
			}
		}
		deregisterAll(b.services[containerId])
		if d := b.deadContainers[containerId]; d != nil {
			deregisterAll(d.Services)
			delete(b.deadContainers, containerId)
		}
	} else if b.config.RefreshTtl != 0 && b.services[containerId] != nil {
		// need to stop the refreshing, but can't delete it yet
		b.deadContainers[containerId] = &DeadContainer{b.config.RefreshTtl, b.services[containerId]}
	}
	delete(b.services, containerId)
}

func (b *Bridge) didExitCleanly(containerId string) bool {
	container, err := b.docker.InspectContainer(containerId)
	if _, ok := err.(*dockerapi.NoSuchContainer); ok {
		// the container has already been removed from Docker
		// e.g. probabably run with "--rm" to remove immediately
		// so its exit code is not accessible
		log.Printf("registrator: container %v was removed, could not fetch exit code", containerId[:12])
		return true
	} else if err != nil {
		log.Printf("registrator: error fetching status for container %v on \"die\" event: %v\n", containerId[:12], err)
		return false
	}
	return !container.State.Running && container.State.ExitCode == 0
}
<commit_msg>Skip skydns registration of services with empty IP addrs<commit_after>package bridge

import (
	"errors"
	"log"
	"net"
	"net/url"
	"os"
	"path"
	"strconv"
	"strings"
	"sync"

	dockerapi "github.com/fsouza/go-dockerclient"
)

type Bridge struct {
	sync.Mutex
	registry       RegistryAdapter
	docker         *dockerapi.Client
	services       map[string][]*Service
	deadContainers map[string]*DeadContainer
	config         Config
}

func New(docker *dockerapi.Client, adapterUri string, config Config) (*Bridge, error) {
	uri, err := url.Parse(adapterUri)
	if err != nil {
		return nil, errors.New("bad adapter uri: " + adapterUri)
	}
	factory, found := AdapterFactories.Lookup(uri.Scheme)
	if !found {
		return nil, errors.New("unrecognized adapter: " + adapterUri)
	}

	log.Println("Using", uri.Scheme, "adapter:", uri)
	return &Bridge{
		docker:         docker,
		config:         config,
		registry:       factory.New(uri),
		services:       make(map[string][]*Service),
		deadContainers: make(map[string]*DeadContainer),
	}, nil
}

func (b *Bridge) Ping() error {
	return b.registry.Ping()
}

func (b *Bridge) Add(containerId string) {
	b.Lock()
	defer b.Unlock()
	b.add(containerId, false)
}

func (b *Bridge) Remove(containerId string) {
	b.remove(containerId, true)
}

func (b *Bridge) RemoveOnExit(containerId string) {
	b.remove(containerId, b.config.DeregisterCheck == "always" || b.didExitCleanly(containerId))
}

func (b *Bridge) Refresh() {
	b.Lock()
	defer b.Unlock()

	for containerId, deadContainer := range b.deadContainers {
		deadContainer.TTL -= b.config.RefreshInterval
		if deadContainer.TTL <= 0 {
			delete(b.deadContainers, containerId)
		}
	}

	for containerId, services := range b.services {
		for _, service := range services {
			err := b.registry.Refresh(service)
			if err != nil {
				log.Println("refresh failed:", service.ID, err)
				continue
			}
			log.Println("refreshed:", containerId[:12], service.ID)
		}
	}
}

func (b *Bridge) Sync(quiet bool) {
	b.Lock()
	defer b.Unlock()

	containers, err := b.docker.ListContainers(dockerapi.ListContainersOptions{})
	if err != nil && quiet {
		log.Println("error listing containers, skipping sync")
		return
	} else if err != nil && !quiet {
		log.Fatal(err)
	}

	log.Printf("Syncing services on %d containers", len(containers))

	// NOTE: This assumes reregistering will do the right thing, i.e. nothing.
	// NOTE: This will NOT remove services.
	for _, listing := range containers {
		services := b.services[listing.ID]
		if services == nil {
			b.add(listing.ID, quiet)
		} else {
			for _, service := range services {
				err := b.registry.Register(service)
				if err != nil {
					log.Println("sync register failed:", service, err)
				}
			}
		}
	}
}

func (b *Bridge) add(containerId string, quiet bool) {
	if d := b.deadContainers[containerId]; d != nil {
		b.services[containerId] = d.Services
		delete(b.deadContainers, containerId)
	}

	if b.services[containerId] != nil {
		log.Println("container, ", containerId[:12], ", already exists, ignoring")
		// Alternatively, remove and readd or resubmit.
		return
	}

	container, err := b.docker.InspectContainer(containerId)
	if err != nil {
		log.Println("unable to inspect container:", containerId[:12], err)
		return
	}

	ports := make(map[string]ServicePort)

	// Extract configured host port mappings, relevant when using --net=host
	for port, published := range container.HostConfig.PortBindings {
		ports[string(port)] = servicePort(container, port, published)
	}

	// Extract runtime port mappings, relevant when using --net=bridge
	for port, published := range container.NetworkSettings.Ports {
		ports[string(port)] = servicePort(container, port, published)
	}

	if len(ports) == 0 && !quiet {
		log.Println("ignored:", container.ID[:12], "no published ports")
		return
	}

	for _, port := range ports {
		if b.config.Internal != true && port.HostPort == "" {
			if !quiet {
				log.Println("ignored:", container.ID[:12], "port", port.ExposedPort, "not published on host")
			}
			continue
		}
		service := b.newService(port, len(ports) > 1)
		if service == nil {
			if !quiet {
				log.Println("ignored:", container.ID[:12], "service on port", port.ExposedPort)
			}
			continue
		}
		err := b.registry.Register(service)
		if err != nil {
			log.Println("register failed:", service, err)
			continue
		}
		b.services[container.ID] = append(b.services[container.ID], service)
		log.Println("added:", container.ID[:12], service.ID, service.IP)
	}
}

func (b *Bridge) newService(port ServicePort, isgroup bool) *Service {
	container := port.container
	defaultName := strings.Split(path.Base(container.Config.Image), ":")[0]

	// not sure about this logic. kind of want to remove it.
	hostname, err := os.Hostname()
	if err != nil {
		hostname = port.HostIP
	} else {
		if port.HostIP == "0.0.0.0" {
			ip, err := net.ResolveIPAddr("ip", hostname)
			if err == nil {
				port.HostIP = ip.String()
			}
		}
	}

	if b.config.HostIp != "" {
		port.HostIP = b.config.HostIp
	}

	metadata := serviceMetaData(container.Config, port.ExposedPort)

	ignore := mapDefault(metadata, "ignore", "")
	if ignore != "" {
		return nil
	}

	service := new(Service)
	service.Origin = port
	service.ID = hostname + ":" + container.Name[1:] + ":" + port.ExposedPort
	service.Name = mapDefault(metadata, "name", defaultName)
	if isgroup {
		service.Name += "-" + port.ExposedPort
	}
	var p int
	if b.config.Internal == true {
		service.IP = port.ExposedIP
		p, _ = strconv.Atoi(port.ExposedPort)
	} else {
		service.IP = port.HostIP
		p, _ = strconv.Atoi(port.HostPort)
	}
	service.Port = p

	// We want to skip a registration of a service with an empty IP
	if len(service.IP) == 0 {
		log.Println("ignored:", service.ID, "due to an empty IP record")
		return nil
	}

	if port.PortType == "udp" {
		service.Tags = combineTags(
			mapDefault(metadata, "tags", ""), b.config.ForceTags, "udp")
		service.ID = service.ID + ":udp"
	} else {
		service.Tags = combineTags(
			mapDefault(metadata, "tags", ""), b.config.ForceTags)
	}

	id := mapDefault(metadata, "id", "")
	if id != "" {
		service.ID = id
	}

	delete(metadata, "id")
	delete(metadata, "tags")
	delete(metadata, "name")
	service.Attrs = metadata
	service.TTL = b.config.RefreshTtl

	return service
}

func (b *Bridge) remove(containerId string, deregister bool) {
	b.Lock()
	defer b.Unlock()

	if deregister {
		deregisterAll := func(services []*Service) {
			for _, service := range services {
				err := b.registry.Deregister(service)
				if err != nil {
					log.Println("deregister failed:", service.ID, err)
					continue
				}
				log.Println("removed:", containerId[:12], service.ID)
			}
		}
		deregisterAll(b.services[containerId])
		if d := b.deadContainers[containerId]; d != nil {
			deregisterAll(d.Services)
			delete(b.deadContainers, containerId)
		}
	} else if b.config.RefreshTtl != 0 && b.services[containerId] != nil {
		// need to stop the refreshing, but can't delete it yet
		b.deadContainers[containerId] = &DeadContainer{b.config.RefreshTtl, b.services[containerId]}
	}
	delete(b.services, containerId)
}

func (b *Bridge) didExitCleanly(containerId string) bool {
	container, err := b.docker.InspectContainer(containerId)
	if _, ok := err.(*dockerapi.NoSuchContainer); ok {
		// the container has already been removed from Docker
		// e.g. probabably run with "--rm" to remove immediately
		// so its exit code is not accessible
		log.Printf("registrator: container %v was removed, could not fetch exit code", containerId[:12])
		return true
	} else if err != nil {
		log.Printf("registrator: error fetching status for container %v on \"die\" event: %v\n", containerId[:12], err)
		return false
	}
	return !container.State.Running && container.State.ExitCode == 0
}
<|endoftext|>


<commit_before>package data

import (
	"crypto/sha256"
	"crypto/sha512"
	"crypto/subtle"
	"encoding/hex"
	"fmt"
	"hash"
	"io"
	"io/ioutil"
	"path"
	"strings"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/docker/go/canonical/json"
	"github.com/docker/notary"
)

// GUN type for specifying gun
type GUN string

func (g GUN) String() string {
	return string(g)
}

// RoleName type for specifying role
type RoleName string

func (r RoleName) String() string {
	return string(r)
}

// Parent provides the parent path role from the provided child role
func (r RoleName) Parent() RoleName {
	return RoleName(path.Dir(r.String()))
}

// MetadataRoleMapToStringMap generates a map string of bytes from a map RoleName of bytes
func MetadataRoleMapToStringMap(roles map[RoleName][]byte) map[string][]byte {
	metadata := make(map[string][]byte)
	for k, v := range roles {
		metadata[k.String()] = v
	}
	return metadata
}

// NewRoleList generates an array of RoleName objects from a slice of strings
func NewRoleList(roles []string) []RoleName {
	var roleNames []RoleName
	for _, role := range roles {
		roleNames = append(roleNames, RoleName(role))
	}
	return roleNames
}

// RoleListToStringList generates an array of string objects from a slice of roles
func RolesListToStringList(roles []RoleName) []string {
	var roleNames []string
	for _, role := range roles {
		roleNames = append(roleNames, role.String())
	}
	return roleNames
}

// SigAlgorithm for types of signatures
type SigAlgorithm string

func (k SigAlgorithm) String() string {
	return string(k)
}

const defaultHashAlgorithm = "sha256"

// NotaryDefaultExpiries is the construct used to configure the default expiry times of
// the various role files.
var NotaryDefaultExpiries = map[RoleName]time.Duration{
	CanonicalRootRole:      notary.NotaryRootExpiry,
	CanonicalTargetsRole:   notary.NotaryTargetsExpiry,
	CanonicalSnapshotRole:  notary.NotarySnapshotExpiry,
	CanonicalTimestampRole: notary.NotaryTimestampExpiry,
}

// Signature types
const (
	EDDSASignature       SigAlgorithm = "eddsa"
	RSAPSSSignature      SigAlgorithm = "rsapss"
	RSAPKCS1v15Signature SigAlgorithm = "rsapkcs1v15"
	ECDSASignature       SigAlgorithm = "ecdsa"
	PyCryptoSignature    SigAlgorithm = "pycrypto-pkcs#1 pss"
)

// Key types
const (
	ED25519Key   = "ed25519"
	RSAKey       = "rsa"
	RSAx509Key   = "rsa-x509"
	ECDSAKey     = "ecdsa"
	ECDSAx509Key = "ecdsa-x509"
)

// TUFTypes is the set of metadata types
var TUFTypes = map[RoleName]string{
	CanonicalRootRole:      "Root",
	CanonicalTargetsRole:   "Targets",
	CanonicalSnapshotRole:  "Snapshot",
	CanonicalTimestampRole: "Timestamp",
}

// ValidTUFType checks if the given type is valid for the role
func ValidTUFType(typ string, role RoleName) bool {
	if ValidRole(role) {
		// All targets delegation roles must have
		// the valid type is for targets.
		if role == "" {
			// role is unknown and does not map to
			// a type
			return false
		}
		if strings.HasPrefix(role.String(), CanonicalTargetsRole.String()+"/") {
			role = CanonicalTargetsRole
		}
	}
	// most people will just use the defaults so have this optimal check
	// first. Do comparison just in case there is some unknown vulnerability
	// if a key and value in the map differ.
	if v, ok := TUFTypes[role]; ok {
		return typ == v
	}
	return false
}

// Signed is the high level, partially deserialized metadata object
// used to verify signatures before fully unpacking, or to add signatures
// before fully packing
type Signed struct {
	Signed     *json.RawMessage `json:"signed"`
	Signatures []Signature      `json:"signatures"`
}

// SignedCommon contains the fields common to the Signed component of all
// TUF metadata files
type SignedCommon struct {
	Type    string    `json:"_type"`
	Expires time.Time `json:"expires"`
	Version int       `json:"version"`
}

// SignedMeta is used in server validation where we only need signatures
// and common fields
type SignedMeta struct {
	Signed     SignedCommon `json:"signed"`
	Signatures []Signature  `json:"signatures"`
}

// Signature is a signature on a piece of metadata
type Signature struct {
	KeyID     string       `json:"keyid"`
	Method    SigAlgorithm `json:"method"`
	Signature []byte       `json:"sig"`
	IsValid   bool         `json:"-"`
}

// Files is the map of paths to file meta container in targets and delegations
// metadata files
type Files map[string]FileMeta

// Hashes is the map of hash type to digest created for each metadata
// and target file
type Hashes map[string][]byte

// NotaryDefaultHashes contains the default supported hash algorithms.
var NotaryDefaultHashes = []string{notary.SHA256, notary.SHA512}

// FileMeta contains the size and hashes for a metadata or target file. Custom
// data can be optionally added.
type FileMeta struct {
	Length int64            `json:"length"`
	Hashes Hashes           `json:"hashes"`
	Custom *json.RawMessage `json:"custom,omitempty"`
}

// CheckHashes verifies all the checksums specified by the "hashes" of the payload.
func CheckHashes(payload []byte, name string, hashes Hashes) error {
	cnt := 0

	// k, v indicate the hash algorithm and the corresponding value
	for k, v := range hashes {
		switch k {
		case notary.SHA256:
			checksum := sha256.Sum256(payload)
			if subtle.ConstantTimeCompare(checksum[:], v) == 0 {
				return ErrMismatchedChecksum{alg: notary.SHA256, name: name, expected: hex.EncodeToString(v)}
			}
			cnt++
		case notary.SHA512:
			checksum := sha512.Sum512(payload)
			if subtle.ConstantTimeCompare(checksum[:], v) == 0 {
				return ErrMismatchedChecksum{alg: notary.SHA512, name: name, expected: hex.EncodeToString(v)}
			}
			cnt++
		}
	}

	if cnt == 0 {
		return ErrMissingMeta{Role: name}
	}

	return nil
}

// CompareMultiHashes verifies that the two Hashes passed in can represent the same data.
// This means that both maps must have at least one key defined for which they map, and no conflicts.
// Note that we check the intersection of map keys, which adds support for non-default hash algorithms in notary
func CompareMultiHashes(hashes1, hashes2 Hashes) error {
	// First check if the two hash structures are valid
	if err := CheckValidHashStructures(hashes1); err != nil {
		return err
	}
	if err := CheckValidHashStructures(hashes2); err != nil {
		return err
	}
	// Check if they have at least one matching hash, and no conflicts
	cnt := 0
	for hashAlg, hash1 := range hashes1 {

		hash2, ok := hashes2[hashAlg]
		if !ok {
			continue
		}

		if subtle.ConstantTimeCompare(hash1[:], hash2[:]) == 0 {
			return fmt.Errorf("mismatched %s checksum", hashAlg)
		}
		// If we reached here, we had a match
		cnt++
	}

	if cnt == 0 {
		return fmt.Errorf("at least one matching hash needed")
	}

	return nil
}

// CheckValidHashStructures returns an error, or nil, depending on whether
// the content of the hashes is valid or not.
func CheckValidHashStructures(hashes Hashes) error {
	cnt := 0

	for k, v := range hashes {
		switch k {
		case notary.SHA256:
			if len(v) != sha256.Size {
				return ErrInvalidChecksum{alg: notary.SHA256}
			}
			cnt++
		case notary.SHA512:
			if len(v) != sha512.Size {
				return ErrInvalidChecksum{alg: notary.SHA512}
			}
			cnt++
		}
	}

	if cnt == 0 {
		return fmt.Errorf("at least one supported hash needed")
	}

	return nil
}

// NewFileMeta generates a FileMeta object from the reader, using the
// hash algorithms provided
func NewFileMeta(r io.Reader, hashAlgorithms ...string) (FileMeta, error) {
	if len(hashAlgorithms) == 0 {
		hashAlgorithms = []string{defaultHashAlgorithm}
	}
	hashes := make(map[string]hash.Hash, len(hashAlgorithms))
	for _, hashAlgorithm := range hashAlgorithms {
		var h hash.Hash
		switch hashAlgorithm {
		case notary.SHA256:
			h = sha256.New()
		case notary.SHA512:
			h = sha512.New()
		default:
			return FileMeta{}, fmt.Errorf("Unknown hash algorithm: %s", hashAlgorithm)
		}
		hashes[hashAlgorithm] = h
		r = io.TeeReader(r, h)
	}
	n, err := io.Copy(ioutil.Discard, r)
	if err != nil {
		return FileMeta{}, err
	}
	m := FileMeta{Length: n, Hashes: make(Hashes, len(hashes))}
	for hashAlgorithm, h := range hashes {
		m.Hashes[hashAlgorithm] = h.Sum(nil)
	}
	return m, nil
}

// Delegations holds a tier of targets delegations
type Delegations struct {
	Keys  Keys    `json:"keys"`
	Roles []*Role `json:"roles"`
}

// NewDelegations initializes an empty Delegations object
func NewDelegations() *Delegations {
	return &Delegations{
		Keys:  make(map[string]PublicKey),
		Roles: make([]*Role, 0),
	}
}

// These values are recommended TUF expiry times.
var defaultExpiryTimes = map[RoleName]time.Duration{
	CanonicalRootRole:      notary.Year,
	CanonicalTargetsRole:   90 * notary.Day,
	CanonicalSnapshotRole:  7 * notary.Day,
	CanonicalTimestampRole: notary.Day,
}

// SetDefaultExpiryTimes allows one to change the default expiries.
func SetDefaultExpiryTimes(times map[RoleName]time.Duration) {
	for key, value := range times {
		if _, ok := defaultExpiryTimes[key]; !ok {
			logrus.Errorf("Attempted to set default expiry for an unknown role: %s", key.String())
			continue
		}
		defaultExpiryTimes[key] = value
	}
}

// DefaultExpires gets the default expiry time for the given role
func DefaultExpires(role RoleName) time.Time {
	if d, ok := defaultExpiryTimes[role]; ok {
		return time.Now().Add(d)
	}
	var t time.Time
	return t.UTC().Round(time.Second)
}

type unmarshalledSignature Signature

// UnmarshalJSON does a custom unmarshalling of the signature JSON
func (s *Signature) UnmarshalJSON(data []byte) error {
	uSignature := unmarshalledSignature{}
	err := json.Unmarshal(data, &uSignature)
	if err != nil {
		return err
	}
	uSignature.Method = SigAlgorithm(strings.ToLower(string(uSignature.Method)))
	*s = Signature(uSignature)
	return nil
}
<commit_msg>fix godoc comment<commit_after>package data

import (
	"crypto/sha256"
	"crypto/sha512"
	"crypto/subtle"
	"encoding/hex"
	"fmt"
	"hash"
	"io"
	"io/ioutil"
	"path"
	"strings"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/docker/go/canonical/json"
	"github.com/docker/notary"
)

// GUN type for specifying gun
type GUN string

func (g GUN) String() string {
	return string(g)
}

// RoleName type for specifying role
type RoleName string

func (r RoleName) String() string {
	return string(r)
}

// Parent provides the parent path role from the provided child role
func (r RoleName) Parent() RoleName {
	return RoleName(path.Dir(r.String()))
}

// MetadataRoleMapToStringMap generates a map string of bytes from a map RoleName of bytes
func MetadataRoleMapToStringMap(roles map[RoleName][]byte) map[string][]byte {
	metadata := make(map[string][]byte)
	for k, v := range roles {
		metadata[k.String()] = v
	}
	return metadata
}

// NewRoleList generates an array of RoleName objects from a slice of strings
func NewRoleList(roles []string) []RoleName {
	var roleNames []RoleName
	for _, role := range roles {
		roleNames = append(roleNames, RoleName(role))
	}
	return roleNames
}

// RolesListToStringList generates an array of string objects from a slice of roles
func RolesListToStringList(roles []RoleName) []string {
	var roleNames []string
	for _, role := range roles {
		roleNames = append(roleNames, role.String())
	}
	return roleNames
}

// SigAlgorithm for types of signatures
type SigAlgorithm string

func (k SigAlgorithm) String() string {
	return string(k)
}

const defaultHashAlgorithm = "sha256"

// NotaryDefaultExpiries is the construct used to configure the default expiry times of
// the various role files.
var NotaryDefaultExpiries = map[RoleName]time.Duration{
	CanonicalRootRole:      notary.NotaryRootExpiry,
	CanonicalTargetsRole:   notary.NotaryTargetsExpiry,
	CanonicalSnapshotRole:  notary.NotarySnapshotExpiry,
	CanonicalTimestampRole: notary.NotaryTimestampExpiry,
}

// Signature types
const (
	EDDSASignature       SigAlgorithm = "eddsa"
	RSAPSSSignature      SigAlgorithm = "rsapss"
	RSAPKCS1v15Signature SigAlgorithm = "rsapkcs1v15"
	ECDSASignature       SigAlgorithm = "ecdsa"
	PyCryptoSignature    SigAlgorithm = "pycrypto-pkcs#1 pss"
)

// Key types
const (
	ED25519Key   = "ed25519"
	RSAKey       = "rsa"
	RSAx509Key   = "rsa-x509"
	ECDSAKey     = "ecdsa"
	ECDSAx509Key = "ecdsa-x509"
)

// TUFTypes is the set of metadata types
var TUFTypes = map[RoleName]string{
	CanonicalRootRole:      "Root",
	CanonicalTargetsRole:   "Targets",
	CanonicalSnapshotRole:  "Snapshot",
	CanonicalTimestampRole: "Timestamp",
}

// ValidTUFType checks if the given type is valid for the role
func ValidTUFType(typ string, role RoleName) bool {
	if ValidRole(role) {
		// All targets delegation roles must have
		// the valid type is for targets.
		if role == "" {
			// role is unknown and does not map to
			// a type
			return false
		}
		if strings.HasPrefix(role.String(), CanonicalTargetsRole.String()+"/") {
			role = CanonicalTargetsRole
		}
	}
	// most people will just use the defaults so have this optimal check
	// first. Do comparison just in case there is some unknown vulnerability
	// if a key and value in the map differ.
	if v, ok := TUFTypes[role]; ok {
		return typ == v
	}
	return false
}

// Signed is the high level, partially deserialized metadata object
// used to verify signatures before fully unpacking, or to add signatures
// before fully packing
type Signed struct {
	Signed     *json.RawMessage `json:"signed"`
	Signatures []Signature      `json:"signatures"`
}

// SignedCommon contains the fields common to the Signed component of all
// TUF metadata files
type SignedCommon struct {
	Type    string    `json:"_type"`
	Expires time.Time `json:"expires"`
	Version int       `json:"version"`
}

// SignedMeta is used in server validation where we only need signatures
// and common fields
type SignedMeta struct {
	Signed     SignedCommon `json:"signed"`
	Signatures []Signature  `json:"signatures"`
}

// Signature is a signature on a piece of metadata
type Signature struct {
	KeyID     string       `json:"keyid"`
	Method    SigAlgorithm `json:"method"`
	Signature []byte       `json:"sig"`
	IsValid   bool         `json:"-"`
}

// Files is the map of paths to file meta container in targets and delegations
// metadata files
type Files map[string]FileMeta

// Hashes is the map of hash type to digest created for each metadata
// and target file
type Hashes map[string][]byte

// NotaryDefaultHashes contains the default supported hash algorithms.
var NotaryDefaultHashes = []string{notary.SHA256, notary.SHA512}

// FileMeta contains the size and hashes for a metadata or target file. Custom
// data can be optionally added.
type FileMeta struct {
	Length int64            `json:"length"`
	Hashes Hashes           `json:"hashes"`
	Custom *json.RawMessage `json:"custom,omitempty"`
}

// CheckHashes verifies all the checksums specified by the "hashes" of the payload.
func CheckHashes(payload []byte, name string, hashes Hashes) error {
	cnt := 0

	// k, v indicate the hash algorithm and the corresponding value
	for k, v := range hashes {
		switch k {
		case notary.SHA256:
			checksum := sha256.Sum256(payload)
			if subtle.ConstantTimeCompare(checksum[:], v) == 0 {
				return ErrMismatchedChecksum{alg: notary.SHA256, name: name, expected: hex.EncodeToString(v)}
			}
			cnt++
		case notary.SHA512:
			checksum := sha512.Sum512(payload)
			if subtle.ConstantTimeCompare(checksum[:], v) == 0 {
				return ErrMismatchedChecksum{alg: notary.SHA512, name: name, expected: hex.EncodeToString(v)}
			}
			cnt++
		}
	}

	if cnt == 0 {
		return ErrMissingMeta{Role: name}
	}

	return nil
}

// CompareMultiHashes verifies that the two Hashes passed in can represent the same data.
// This means that both maps must have at least one key defined for which they map, and no conflicts.
// Note that we check the intersection of map keys, which adds support for non-default hash algorithms in notary
func CompareMultiHashes(hashes1, hashes2 Hashes) error {
	// First check if the two hash structures are valid
	if err := CheckValidHashStructures(hashes1); err != nil {
		return err
	}
	if err := CheckValidHashStructures(hashes2); err != nil {
		return err
	}
	// Check if they have at least one matching hash, and no conflicts
	cnt := 0
	for hashAlg, hash1 := range hashes1 {

		hash2, ok := hashes2[hashAlg]
		if !ok {
			continue
		}

		if subtle.ConstantTimeCompare(hash1[:], hash2[:]) == 0 {
			return fmt.Errorf("mismatched %s checksum", hashAlg)
		}
		// If we reached here, we had a match
		cnt++
	}

	if cnt == 0 {
		return fmt.Errorf("at least one matching hash needed")
	}

	return nil
}

// CheckValidHashStructures returns an error, or nil, depending on whether
// the content of the hashes is valid or not.
func CheckValidHashStructures(hashes Hashes) error {
	cnt := 0

	for k, v := range hashes {
		switch k {
		case notary.SHA256:
			if len(v) != sha256.Size {
				return ErrInvalidChecksum{alg: notary.SHA256}
			}
			cnt++
		case notary.SHA512:
			if len(v) != sha512.Size {
				return ErrInvalidChecksum{alg: notary.SHA512}
			}
			cnt++
		}
	}

	if cnt == 0 {
		return fmt.Errorf("at least one supported hash needed")
	}

	return nil
}

// NewFileMeta generates a FileMeta object from the reader, using the
// hash algorithms provided
func NewFileMeta(r io.Reader, hashAlgorithms ...string) (FileMeta, error) {
	if len(hashAlgorithms) == 0 {
		hashAlgorithms = []string{defaultHashAlgorithm}
	}
	hashes := make(map[string]hash.Hash, len(hashAlgorithms))
	for _, hashAlgorithm := range hashAlgorithms {
		var h hash.Hash
		switch hashAlgorithm {
		case notary.SHA256:
			h = sha256.New()
		case notary.SHA512:
			h = sha512.New()
		default:
			return FileMeta{}, fmt.Errorf("Unknown hash algorithm: %s", hashAlgorithm)
		}
		hashes[hashAlgorithm] = h
		r = io.TeeReader(r, h)
	}
	n, err := io.Copy(ioutil.Discard, r)
	if err != nil {
		return FileMeta{}, err
	}
	m := FileMeta{Length: n, Hashes: make(Hashes, len(hashes))}
	for hashAlgorithm, h := range hashes {
		m.Hashes[hashAlgorithm] = h.Sum(nil)
	}
	return m, nil
}

// Delegations holds a tier of targets delegations
type Delegations struct {
	Keys  Keys    `json:"keys"`
	Roles []*Role `json:"roles"`
}

// NewDelegations initializes an empty Delegations object
func NewDelegations() *Delegations {
	return &Delegations{
		Keys:  make(map[string]PublicKey),
		Roles: make([]*Role, 0),
	}
}

// These values are recommended TUF expiry times.
var defaultExpiryTimes = map[RoleName]time.Duration{
	CanonicalRootRole:      notary.Year,
	CanonicalTargetsRole:   90 * notary.Day,
	CanonicalSnapshotRole:  7 * notary.Day,
	CanonicalTimestampRole: notary.Day,
}

// SetDefaultExpiryTimes allows one to change the default expiries.
func SetDefaultExpiryTimes(times map[RoleName]time.Duration) {
	for key, value := range times {
		if _, ok := defaultExpiryTimes[key]; !ok {
			logrus.Errorf("Attempted to set default expiry for an unknown role: %s", key.String())
			continue
		}
		defaultExpiryTimes[key] = value
	}
}

// DefaultExpires gets the default expiry time for the given role
func DefaultExpires(role RoleName) time.Time {
	if d, ok := defaultExpiryTimes[role]; ok {
		return time.Now().Add(d)
	}
	var t time.Time
	return t.UTC().Round(time.Second)
}

type unmarshalledSignature Signature

// UnmarshalJSON does a custom unmarshalling of the signature JSON
func (s *Signature) UnmarshalJSON(data []byte) error {
	uSignature := unmarshalledSignature{}
	err := json.Unmarshal(data, &uSignature)
	if err != nil {
		return err
	}
	uSignature.Method = SigAlgorithm(strings.ToLower(string(uSignature.Method)))
	*s = Signature(uSignature)
	return nil
}
<|endoftext|>


<commit_before>package pelicantun

import (
	"fmt"
	"net/http"
	"testing"

	cv "github.com/glycerine/goconvey/convey"
)

func TestFullRoundtripSocksProxyTalksToReverseProxy002(t *testing.T) {

	// setup a mock web server that replies to ping with pong.
	mux := http.NewServeMux()

	// ping allows our test machinery to function
	mux.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) {
		r.Body.Close()
		fmt.Fprintf(w, "pong")
	})

	web := NewWebServer(WebServerConfig{}, mux)
	web.Start()
	defer web.Stop()

	if !PortIsBound(web.Cfg.Listen.IpPort) {
		panic("web server did not come up")
	}

	// start a reverse proxy
	rev := NewReverseProxy(ReverseProxyConfig{Dest: web.Cfg.Listen})
	rev.Start()
	defer rev.Stop()

	if !PortIsBound(rev.Cfg.Listen.IpPort) {
		panic("rev proxy not up")
	}

	// start the forward proxy, talks to the reverse proxy.
	fwd := NewPelicanSocksProxy(PelicanSocksProxyConfig{
		Dest: rev.Cfg.Listen,
	})
	fwd.Start()
	defer fwd.Stop()
	if !PortIsBound(fwd.Cfg.Listen.IpPort) {
		panic("fwd proxy not up")
	}

	cv.Convey("Given a ForwardProxy and a ReverseProxy, they should communicate over http", t, func() {

		po("\n fetching url from %v\n", fwd.Cfg.Listen.IpPort)

		by, err := FetchUrl("http://" + fwd.Cfg.Listen.IpPort + "/ping")
		cv.So(err, cv.ShouldEqual, nil)
		//fmt.Printf("by:'%s'\n", string(by))
		cv.So(string(by), cv.ShouldEqual, "pong")
	})
	fmt.Printf("\n done with TestSocksProxyTalksToReverseProxy002()\n")
}
<commit_msg>ft: 002 hangs for 60 seconds, then goes green.<commit_after>package pelicantun

import (
	"fmt"
	"net/http"
	"testing"

	cv "github.com/glycerine/goconvey/convey"
)

func TestFullRoundtripSocksProxyTalksToReverseProxy002(t *testing.T) {

	// setup a mock web server that replies to ping with pong.
	mux := http.NewServeMux()

	// ping allows our test machinery to function
	mux.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) {
		r.Body.Close()
		fmt.Fprintf(w, "pong")
	})

	web := NewWebServer(WebServerConfig{}, mux)
	web.Start()
	defer web.Stop()

	if !PortIsBound(web.Cfg.Listen.IpPort) {
		panic("web server did not come up")
	}

	// start a reverse proxy
	rev := NewReverseProxy(ReverseProxyConfig{Dest: web.Cfg.Listen})
	rev.Start()
	defer rev.Stop()

	if !PortIsBound(rev.Cfg.Listen.IpPort) {
		panic("rev proxy not up")
	}

	// start the forward proxy, talks to the reverse proxy.
	fwd := NewPelicanSocksProxy(PelicanSocksProxyConfig{
		Dest: rev.Cfg.Listen,
	})
	fwd.Start()
	defer fwd.Stop()
	if !PortIsBound(fwd.Cfg.Listen.IpPort) {
		panic("fwd proxy not up")
	}

	cv.Convey("Given a ForwardProxy and a ReverseProxy, they should communicate over http", t, func() {

		po("\n fetching url from %v\n", fwd.Cfg.Listen.IpPort)

		by, err := FetchUrl("http://" + fwd.Cfg.Listen.IpPort + "/ping")
		cv.So(err, cv.ShouldEqual, nil)
		//fmt.Printf("by:'%s'\n", string(by))
		cv.So(string(by), cv.ShouldEqual, "pong")
	})
	fmt.Printf("\n done with TestSocksProxyTalksToReverseProxy002()\n")

	// hangs for 60 seconds, then finishes???
}
<|endoftext|>


<commit_before>package ui

import (
	"errors"
	"fmt"
	"sort"
	"sync"

	"github.com/draganm/go-reactor"
	"github.com/netice9/apparatchik/core"
)

type Application struct {
	sync.Mutex
	ctx       reactor.ScreenContext
	app       *core.Application
	alert     error
	status    core.ApplicationStatus
	showModal bool
}

func ApplicationFactory(ctx reactor.ScreenContext) reactor.Screen {
	appName := ctx.Params["application"]

	app, err := core.ApparatchikInstance.GetApplicationByName(appName)

	if err != nil {
		return reactor.DefaultNotFoundScreenFactory(ctx)
	}

	return &Application{
		ctx: ctx,
		app: app,
	}
}

func (a *Application) Mount() {
	a.app.On("update", a.onUpdate)
	a.app.On("terminated", a.onTerminated)
	a.onUpdate(a.app.Status())
}

func (a *Application) OnUserEvent(evt *reactor.UserEvent) {
	a.Lock()
	defer a.Unlock()

	switch evt.ElementID {
	case "deleteButton":
		a.showModal = true
	case "deleteCancelButton":
		a.showModal = false
	case "deleteConfirmButton":
		a.showModal = false
		a.alert = errors.New("Deleting application.")

		err := core.ApparatchikInstance.TerminateApplication(a.app.Name)
		if err != nil {
			a.alert = err
		}

	}
	a.render()

}

func (a *Application) onUpdate(status core.ApplicationStatus) {
	a.Lock()
	defer a.Unlock()
	a.status = status
	a.render()
}

func (a *Application) onTerminated() {
	a.ctx.UpdateScreen(&reactor.DisplayUpdate{
		Location: "#/",
	})
}

func (a *Application) render() {
	view := applicationUI.DeepCopy()
	view.SetElementAttribute("app_panel", "header", a.status.Name)
	view.SetElementText("main_goal", a.app.MainGoal)

	goalNames := []string{}

	for goalName := range a.status.Goals {
		goalNames = append(goalNames, goalName)
	}

	sort.Strings(goalNames)

	for _, name := range goalNames {
		goal := a.status.Goals[name]
		row := goalRowUI.DeepCopy()
		row.SetElementText("goal_name", name)
		row.SetElementAttribute("goal_name", "href", fmt.Sprintf("#/apps/%s/%s", a.app.Name, goal.Name))
		row.SetElementAttribute("goal_term_link", "href", fmt.Sprintf("#/apps/%s/%s/xterm", a.app.Name, goal.Name))
		row.SetElementText("goal_state", goal.Status)
		view.AppendChild("goal_table_body", row)
	}

	if a.alert != nil {
		view.SetElementText("alert", a.alert.Error())
	} else {
		view.DeleteChild("alert")
	}

	view.SetElementAttribute("delete_confirm_modal", "show", a.showModal)
	view.SetElementText("application_name", a.app.Name)

	a.ctx.UpdateScreen(&reactor.DisplayUpdate{
		Model: WithNavigation(view, [][]string{{"Applications", "#/"}, {a.app.Name, fmt.Sprintf("#/apps/%s", a.app.Name)}}),
	})
}

func (a *Application) Unmount() {
	a.app.RemoveListener("update", a.onUpdate)
	a.app.RemoveListener("terminated", a.onTerminated)
}

var goalRowUI = reactor.MustParseDisplayModel(`

			XTerm

`)
var applicationUI = reactor.MustParseDisplayModel(`

    <bs.Panel id="app_panel" header="">
      <bs.Alert id="alert" bsStyle="danger"/>

Main Goal

Goals

          <bs.Table bool:striped="true" bool:bordered="true" bool:condensed="true" bool:hover="true">

                Name
                State
                Actions

          </bs.Table>

      <bs.Button id="deleteButton" bsStyle="danger" reportEvents="click">Delete!</bs.Button>
			<bs.Modal id="delete_confirm_modal" bool:show="false" reportEvents="hide">
					<bs.Modal.Header>
						<bs.Modal.Title>Confirm Deleting Application</bs.Modal.Title>
					</bs.Modal.Header>

					<bs.Modal.Body>
						You are about to delete application "". Are you sure?
					</bs.Modal.Body>

					<bs.Modal.Footer>
						<bs.Button id="deleteConfirmButton" bsStyle="danger" reportEvents="click">Delete</bs.Button>
						<bs.Button id="deleteCancelButton" bsStyle="primary" reportEvents="click">Cancel</bs.Button>
					</bs.Modal.Footer>
			</bs.Modal>

    </bs.Panel>

`)
<commit_msg>disable XTerm buttons for non-running containers<commit_after>package ui

import (
	"errors"
	"fmt"
	"sort"
	"sync"

	"github.com/draganm/go-reactor"
	"github.com/netice9/apparatchik/core"
)

type Application struct {
	sync.Mutex
	ctx       reactor.ScreenContext
	app       *core.Application
	alert     error
	status    core.ApplicationStatus
	showModal bool
}

func ApplicationFactory(ctx reactor.ScreenContext) reactor.Screen {
	appName := ctx.Params["application"]

	app, err := core.ApparatchikInstance.GetApplicationByName(appName)

	if err != nil {
		return reactor.DefaultNotFoundScreenFactory(ctx)
	}

	return &Application{
		ctx: ctx,
		app: app,
	}
}

func (a *Application) Mount() {
	a.app.On("update", a.onUpdate)
	a.app.On("terminated", a.onTerminated)
	a.onUpdate(a.app.Status())
}

func (a *Application) OnUserEvent(evt *reactor.UserEvent) {
	a.Lock()
	defer a.Unlock()

	switch evt.ElementID {
	case "deleteButton":
		a.showModal = true
	case "deleteCancelButton":
		a.showModal = false
	case "deleteConfirmButton":
		a.showModal = false
		a.alert = errors.New("Deleting application.")

		err := core.ApparatchikInstance.TerminateApplication(a.app.Name)
		if err != nil {
			a.alert = err
		}

	}
	a.render()

}

func (a *Application) onUpdate(status core.ApplicationStatus) {
	a.Lock()
	defer a.Unlock()
	a.status = status
	a.render()
}

func (a *Application) onTerminated() {
	a.ctx.UpdateScreen(&reactor.DisplayUpdate{
		Location: "#/",
	})
}

func (a *Application) render() {
	view := applicationUI.DeepCopy()
	view.SetElementAttribute("app_panel", "header", a.status.Name)
	view.SetElementText("main_goal", a.app.MainGoal)

	goalNames := []string{}

	for goalName := range a.status.Goals {
		goalNames = append(goalNames, goalName)
	}

	sort.Strings(goalNames)

	for _, name := range goalNames {
		goal := a.status.Goals[name]
		row := goalRowUI.DeepCopy()
		row.SetElementText("goal_name", name)
		row.SetElementAttribute("goal_name", "href", fmt.Sprintf("#/apps/%s/%s", a.app.Name, goal.Name))
		if goal.Status == "running" {
			row.SetElementAttribute("goal_term_link", "href", fmt.Sprintf("#/apps/%s/%s/xterm", a.app.Name, goal.Name))
		} else {
			row.SetElementAttribute("goal_term_link", "disabled", true)
		}
		row.SetElementText("goal_state", goal.Status)
		view.AppendChild("goal_table_body", row)
	}

	if a.alert != nil {
		view.SetElementText("alert", a.alert.Error())
	} else {
		view.DeleteChild("alert")
	}

	view.SetElementAttribute("delete_confirm_modal", "show", a.showModal)
	view.SetElementText("application_name", a.app.Name)

	a.ctx.UpdateScreen(&reactor.DisplayUpdate{
		Model: WithNavigation(view, [][]string{{"Applications", "#/"}, {a.app.Name, fmt.Sprintf("#/apps/%s", a.app.Name)}}),
	})
}

func (a *Application) Unmount() {
	a.app.RemoveListener("update", a.onUpdate)
	a.app.RemoveListener("terminated", a.onTerminated)
}

var goalRowUI = reactor.MustParseDisplayModel(`

			<bs.ButtonToolbar>
				<bs.Button id="goal_term_link">XTerm</bs.Button>
			</bs.ButtonToolbar>

`)
var applicationUI = reactor.MustParseDisplayModel(`

    <bs.Panel id="app_panel" header="">
      <bs.Alert id="alert" bsStyle="danger"/>

Main Goal

Goals

          <bs.Table bool:striped="true" bool:bordered="true" bool:condensed="true" bool:hover="true">

                Name
                State
                Actions

          </bs.Table>

      <bs.Button id="deleteButton" bsStyle="danger" reportEvents="click">Delete!</bs.Button>
			<bs.Modal id="delete_confirm_modal" bool:show="false" reportEvents="hide">
					<bs.Modal.Header>
						<bs.Modal.Title>Confirm Deleting Application</bs.Modal.Title>
					</bs.Modal.Header>

					<bs.Modal.Body>
						You are about to delete application "". Are you sure?
					</bs.Modal.Body>

					<bs.Modal.Footer>
						<bs.Button id="deleteConfirmButton" bsStyle="danger" reportEvents="click">Delete</bs.Button>
						<bs.Button id="deleteCancelButton" bsStyle="primary" reportEvents="click">Cancel</bs.Button>
					</bs.Modal.Footer>
			</bs.Modal>

    </bs.Panel>

`)
<|endoftext|>


<commit_before>package upkeep

import (
	d "github.com/jeffjen/docker-monitor/driver"
	disc "github.com/jeffjen/go-discovery"
	"github.com/jeffjen/go-libkv/libkv"
	timer "github.com/jeffjen/go-libkv/timer"

	log "github.com/Sirupsen/logrus"
	etcd "github.com/coreos/etcd/client"
	docker "github.com/fsouza/go-dockerclient"
	ctx "golang.org/x/net/context"

	"encoding/gob"
	"fmt"
	"path"
	"strings"
	"time"
)

const (
	DEFAULT_HEARTBEAT = 2 * time.Minute
	DEFAULT_TTL       = 2*time.Minute + 5*time.Second

	DEFAULT_SYNC_PATH  = "/tmp"
	DEFAULT_SYNC_CYCLE = 2 * time.Minute
)

var (
	Sched *timer.Timer

	rec *libkv.Store

	AllocDriver func(string) (d.Driver, error) = nil
)

func sync(jobId int64) {
	if err := rec.Save(DEFAULT_SYNC_PATH); err != nil {
		log.Warningf("unable to persist: %v", err)
	} else {
		log.Infof("persist: %v", DEFAULT_SYNC_PATH)
	}
}

func noop(string) (d.Driver, error) {
	return &d.Noop{}, nil
}

func Init(persist bool) {
	var err error

	if AllocDriver == nil {
		AllocDriver = noop // default safety net for driver maker
	}

	Sched = timer.NewTimer()

	Sched.Tic() // start the scheduler, don't ever stop

	if persist {
		if rec, err = libkv.Load(DEFAULT_SYNC_PATH); err != nil {
			log.Warningf("unable to load: %v", err)
		}
		for _, k := range rec.Key() {
			MakeService(rec.Get(k).(*Service))
		}
		Sched.RepeatFunc(DEFAULT_SYNC_CYCLE, 1, sync)
	} else {
		rec = libkv.NewStore()
	}
}

func ParseDuration(s string, df time.Duration) time.Duration {
	if d, err := time.ParseDuration(s); err != nil {
		return df
	} else {
		return d
	}
}

func Validate(iden, srv, port string, network []docker.APIPort) bool {
	if srv == "" {
		log.WithFields(log.Fields{"ID": iden}).Warning("not tracking: service")
		return false
	}
	if port == "" {
		if len(network) == 0 {
			log.WithFields(log.Fields{"ID": iden}).Warning("not tracking: port")
			return false
		}
		open := 0
		for _, net := range network {
			if net.PublicPort != 0 {
				open += 1
			}
		}
		if open == 0 {
			log.WithFields(log.Fields{"ID": iden}).Warning("not tracking: port")
			return false
		}
	}
	return true
}

func MakeService(s *Service) {
	s.f = log.Fields{"ID": s.Id[:12], "srv": s.Srv, "heartbeat": s.Hb, "ttl": s.TTL}

	if s.Port != "" {
		s.key = path.Join(s.Srv, fmt.Sprintf("%s:%s", disc.Advertise, s.Port))
	} else if len(s.Net) > 0 {
		key := make([]string, 0)
		for _, p := range s.Net {
			if p.PublicPort != 0 && p.IP == "0.0.0.0" {
				key = append(key, fmt.Sprintf("%s:%d", disc.Advertise, p.PublicPort))
			}
		}
		if len(key) == 1 {
			s.key = path.Join(s.Srv, strings.Join(key, ","))
		} else {
			log.WithFields(log.Fields{"ID": s.Id[:12], "Net": s.Net}).Warning("refuse; 0 or too many port")
			return
		}
	}

	s.opts = &etcd.SetOptions{TTL: s.TTL}

	if s.Start() {
		rec.Set(s.Id, s)
	} else {
		log.WithFields(log.Fields{"ID": s.Id[:12]}).Error("not tracking: probe failed")
	}
}

func NewService(heartbeat, ttl time.Duration, iden, service, port string, net []docker.APIPort) (s *Service) {
	s = &Service{
		Hb:   heartbeat,
		TTL:  ttl,
		Id:   iden,
		Srv:  service,
		Port: port,
		Net:  net,
	}
	MakeService(s)
	return
}

func ServiceStop(iden string) (s *Service) {
	if s, _ := rec.Get(iden).(*Service); s != nil && s.Running() {
		s.Stop()
	}
	return
}

func ServiceDie(iden string) (s *Service) {
	if s, _ := rec.Get(iden).(*Service); s != nil {
		if s.Running() {
			s.Stop()
		}
		rec.Del(iden)
		log.WithFields(log.Fields{"ID": s.Id[:12], "srv": s.Srv}).Info("die")
	}
	return
}

func Get(iden string) (s *Service) {
	s, _ = rec.Get(iden).(*Service)
	return
}

type Service struct {
	Hb  time.Duration `json: "Heartbeat"`
	TTL time.Duration `json: "TTL"`

	Id   string           `json: "ContainerID"`
	Srv  string           `json: "Service"`
	Port string           `json: "Port"`
	Net  []docker.APIPort `json: "Net"`

	kAPI etcd.KeysAPI     `json:-`
	key  string           `json:-`
	opts *etcd.SetOptions `json:-`

	jobId int64 `json:-`

	f log.Fields `json:-`

	driver d.Driver `json:-`
	fail   int      `json:-`
}

func (s *Service) Done(jobId int64) {
	if s.Probe() != nil {
		s.fail += 1
		log.WithFields(s.f).Warning("probe missed")
		return
	}
	if s.fail > 3 {
		s.Stop()
		return
	} else {
		s.fail = 0
		s.Upkeep()
	}
}

func (s *Service) Probe() error {
	return s.driver.Probe()
}

func (s *Service) Upkeep() {
	if _, err := s.kAPI.Set(ctx.Background(), s.key, s.Id, s.opts); err != nil {
		log.WithFields(s.f).Warning(err)
	} else {
		log.WithFields(s.f).Info("up")
		s.opts.PrevExist = etcd.PrevExist
	}
}

func (s *Service) Update() {
	Sched.Cancel(s.jobId)
	log.WithFields(s.f).Info("update")
	MakeService(s)
}

func (s *Service) Start() bool {
	// FIXME: we probably need a allow specific port for probing
	if s.driver, _ = AllocDriver(path.Base(s.key)); s.driver == nil {
		return false
	}

	s.kAPI = etcd.NewKeysAPI(disc.NewDiscovery())

	s.Upkeep()
	if s.Probe() != nil {
		s.fail += 1
		log.WithFields(s.f).Warning("probe missed")
	}

	s.jobId = Sched.Repeat(s.Hb, 1, s)

	return true
}

func (s *Service) Stop() {
	Sched.Cancel(s.jobId)
	if s.driver != nil {
		s.driver.Close()
		s.driver = nil
	}
	s.kAPI = nil
	s.jobId = -1
	s.opts.PrevExist = etcd.PrevIgnore
	log.WithFields(s.f).Info("down")
}

func (s *Service) Running() bool {
	return s.jobId != -1
}

func init() {
	gob.Register(&Service{})
	gob.Register([]docker.APIPort{})
}
<commit_msg>TMP: FIX: set service record again if last heartbeat failed<commit_after>package upkeep

import (
	d "github.com/jeffjen/docker-monitor/driver"
	disc "github.com/jeffjen/go-discovery"
	"github.com/jeffjen/go-libkv/libkv"
	timer "github.com/jeffjen/go-libkv/timer"

	log "github.com/Sirupsen/logrus"
	etcd "github.com/coreos/etcd/client"
	docker "github.com/fsouza/go-dockerclient"
	ctx "golang.org/x/net/context"

	"encoding/gob"
	"fmt"
	"path"
	"strings"
	"time"
)

const (
	DEFAULT_HEARTBEAT = 2 * time.Minute
	DEFAULT_TTL       = 2*time.Minute + 5*time.Second

	DEFAULT_SYNC_PATH  = "/tmp"
	DEFAULT_SYNC_CYCLE = 2 * time.Minute
)

var (
	Sched *timer.Timer

	rec *libkv.Store

	AllocDriver func(string) (d.Driver, error) = nil
)

func sync(jobId int64) {
	if err := rec.Save(DEFAULT_SYNC_PATH); err != nil {
		log.Warningf("unable to persist: %v", err)
	} else {
		log.Infof("persist: %v", DEFAULT_SYNC_PATH)
	}
}

func noop(string) (d.Driver, error) {
	return &d.Noop{}, nil
}

func Init(persist bool) {
	var err error

	if AllocDriver == nil {
		AllocDriver = noop // default safety net for driver maker
	}

	Sched = timer.NewTimer()

	Sched.Tic() // start the scheduler, don't ever stop

	if persist {
		if rec, err = libkv.Load(DEFAULT_SYNC_PATH); err != nil {
			log.Warningf("unable to load: %v", err)
		}
		for _, k := range rec.Key() {
			MakeService(rec.Get(k).(*Service))
		}
		Sched.RepeatFunc(DEFAULT_SYNC_CYCLE, 1, sync)
	} else {
		rec = libkv.NewStore()
	}
}

func ParseDuration(s string, df time.Duration) time.Duration {
	if d, err := time.ParseDuration(s); err != nil {
		return df
	} else {
		return d
	}
}

func Validate(iden, srv, port string, network []docker.APIPort) bool {
	if srv == "" {
		log.WithFields(log.Fields{"ID": iden}).Warning("not tracking: service")
		return false
	}
	if port == "" {
		if len(network) == 0 {
			log.WithFields(log.Fields{"ID": iden}).Warning("not tracking: port")
			return false
		}
		open := 0
		for _, net := range network {
			if net.PublicPort != 0 {
				open += 1
			}
		}
		if open == 0 {
			log.WithFields(log.Fields{"ID": iden}).Warning("not tracking: port")
			return false
		}
	}
	return true
}

func MakeService(s *Service) {
	s.f = log.Fields{"ID": s.Id[:12], "srv": s.Srv, "heartbeat": s.Hb, "ttl": s.TTL}

	if s.Port != "" {
		s.key = path.Join(s.Srv, fmt.Sprintf("%s:%s", disc.Advertise, s.Port))
	} else if len(s.Net) > 0 {
		key := make([]string, 0)
		for _, p := range s.Net {
			if p.PublicPort != 0 && p.IP == "0.0.0.0" {
				key = append(key, fmt.Sprintf("%s:%d", disc.Advertise, p.PublicPort))
			}
		}
		if len(key) == 1 {
			s.key = path.Join(s.Srv, strings.Join(key, ","))
		} else {
			log.WithFields(log.Fields{"ID": s.Id[:12], "Net": s.Net}).Warning("refuse; 0 or too many port")
			return
		}
	}

	s.opts = &etcd.SetOptions{TTL: s.TTL}

	if s.Start() {
		rec.Set(s.Id, s)
	} else {
		log.WithFields(log.Fields{"ID": s.Id[:12]}).Error("not tracking: probe failed")
	}
}

func NewService(heartbeat, ttl time.Duration, iden, service, port string, net []docker.APIPort) (s *Service) {
	s = &Service{
		Hb:   heartbeat,
		TTL:  ttl,
		Id:   iden,
		Srv:  service,
		Port: port,
		Net:  net,
	}
	MakeService(s)
	return
}

func ServiceStop(iden string) (s *Service) {
	if s, _ := rec.Get(iden).(*Service); s != nil && s.Running() {
		s.Stop()
	}
	return
}

func ServiceDie(iden string) (s *Service) {
	if s, _ := rec.Get(iden).(*Service); s != nil {
		if s.Running() {
			s.Stop()
		}
		rec.Del(iden)
		log.WithFields(log.Fields{"ID": s.Id[:12], "srv": s.Srv}).Info("die")
	}
	return
}

func Get(iden string) (s *Service) {
	s, _ = rec.Get(iden).(*Service)
	return
}

type Service struct {
	Hb  time.Duration `json: "Heartbeat"`
	TTL time.Duration `json: "TTL"`

	Id   string           `json: "ContainerID"`
	Srv  string           `json: "Service"`
	Port string           `json: "Port"`
	Net  []docker.APIPort `json: "Net"`

	kAPI etcd.KeysAPI     `json:-`
	key  string           `json:-`
	opts *etcd.SetOptions `json:-`

	jobId int64 `json:-`

	f log.Fields `json:-`

	driver d.Driver `json:-`
	fail   int      `json:-`
}

func (s *Service) Done(jobId int64) {
	if s.Probe() != nil {
		s.fail += 1
		log.WithFields(s.f).Warning("probe missed")
		return
	}
	if s.fail > 3 {
		s.Stop()
		return
	} else {
		s.fail = 0
		s.Upkeep()
	}
}

func (s *Service) Probe() error {
	return s.driver.Probe()
}

func (s *Service) Upkeep() {
	if _, err := s.kAPI.Set(ctx.Background(), s.key, s.Id, s.opts); err != nil {
		log.WithFields(s.f).Warning(err)
		s.opts.PrevExist = etcd.PrevIgnore
	} else {
		log.WithFields(s.f).Info("up")
		s.opts.PrevExist = etcd.PrevExist
	}
}

func (s *Service) Update() {
	Sched.Cancel(s.jobId)
	log.WithFields(s.f).Info("update")
	MakeService(s)
}

func (s *Service) Start() bool {
	// FIXME: we probably need a allow specific port for probing
	if s.driver, _ = AllocDriver(path.Base(s.key)); s.driver == nil {
		return false
	}

	s.kAPI = etcd.NewKeysAPI(disc.NewDiscovery())

	s.Upkeep()
	if s.Probe() != nil {
		s.fail += 1
		log.WithFields(s.f).Warning("probe missed")
	}

	s.jobId = Sched.Repeat(s.Hb, 1, s)

	return true
}

func (s *Service) Stop() {
	Sched.Cancel(s.jobId)
	if s.driver != nil {
		s.driver.Close()
		s.driver = nil
	}
	s.kAPI = nil
	s.jobId = -1
	s.opts.PrevExist = etcd.PrevIgnore
	log.WithFields(s.f).Info("down")
}

func (s *Service) Running() bool {
	return s.jobId != -1
}

func init() {
	gob.Register(&Service{})
	gob.Register([]docker.APIPort{})
}
<|endoftext|>