<commit_before>// Copyright 2014 Hajime Hoshi
//
// 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.

// +build ignore

package main

import (
	"fmt"
	"html/template"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strings"
	"time"
)

var license = ""

func init() {
	b, err := ioutil.ReadFile("../license.txt")
	if err != nil {
		panic(err)
	}
	license = strings.TrimSpace(string(b))

	// TODO: Year check
}

var copyright = ""

func init() {
	copyright = fmt.Sprintf("© %d Hajime Hoshi", time.Now().Year())
}

var stableVersion = ""

var devVersion = ""

func init() {
	b, err := ioutil.ReadFile("../version.txt")
	if err != nil {
		panic(err)
	}
	stableVersion = strings.TrimSpace(string(b))
}

func init() {
	b, err := exec.Command("git", "show", "master:version.txt").Output()
	if err != nil {
		panic(err)
	}
	devVersion = strings.TrimSpace(string(b))
}

func comment(text string) template.HTML {
	// TODO: text should be escaped
	return template.HTML("<!--\n" + text + "\n-->")
}

func safeHTML(text string) template.HTML {
	return template.HTML(text)
}

type example struct {
	Name string
}

func (e *example) ThumbWidth() int {
	if e.Name == "blocks" {
		return 256
	}
	return 320
}

func (e *example) ThumbHeight() int {
	if e.Name == "blocks" {
		return 240
	}
	return 240
}

func (e *example) Width() int {
	return e.ThumbWidth() * 2
}

func (e *example) Height() int {
	return e.ThumbHeight() * 2
}

func (e *example) Source() string {
	if e.Name == "blocks" {
		return "// Please read example/blocks/main.go and example/blocks/blocks/*.go"
	}

	path := filepath.Join(os.Getenv("GOPATH"), "src", "github.com", "hajimehoshi", "ebiten", "example", e.Name, "main.go")
	b, err := ioutil.ReadFile(path)
	if err != nil {
		panic(err)
	}
	str := regexp.MustCompile("(?s)^.*?\n\n").ReplaceAllString(string(b), "")
	str = strings.Replace(str, "\t", "    ", -1)
	return str
}

func versions() string {
	return fmt.Sprintf("v%s (dev: v%s)", stableVersion, devVersion)
}

var examples = []example{
	{Name: "hue"},
	{Name: "keyboard"},
	{Name: "mosaic"},
	{Name: "perspective"},
	{Name: "rotate"},
	{Name: "blocks"},
}

func clear() error {
	if err := filepath.Walk("public", func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		// Remove auto-generated html files.
		m, err := regexp.MatchString(".html$", path)
		if err != nil {
			return err
		}
		if !m {
			return nil
		}
		// Remove example resources that are copied.
		m, err = regexp.MatchString("public/example/images", path)
		if err != nil {
			return err
		}
		if !m {
			return nil
		}
		return os.Remove(path)
	}); err != nil {
		return err
	}
	return nil
}

func outputMain() error {
	f, err := os.Create("public/index.html")
	if err != nil {
		return err
	}
	defer f.Close()

	funcs := template.FuncMap{
		"comment":  comment,
		"safeHTML": safeHTML,
	}
	const templatePath = "index.tmpl.html"
	name := filepath.Base(templatePath)
	t, err := template.New(name).Funcs(funcs).ParseFiles(templatePath)
	if err != nil {
		return err
	}

	data := map[string]interface{}{
		"License":       license,
		"Copyright":     copyright,
		"StableVersion": stableVersion,
		"DevVersion":    devVersion,
		"Examples":      examples,
	}
	return t.Funcs(funcs).Execute(f, data)
}

func outputExampleContent(e *example) error {
	const dir = "public/example"
	if err := os.MkdirAll(dir, 0755); err != nil {
		return err
	}
	f, err := os.Create(filepath.Join(dir, e.Name+".content.html"))
	if err != nil {
		return err
	}
	defer f.Close()

	funcs := template.FuncMap{
		"comment":  comment,
		"safeHTML": safeHTML,
	}
	const templatePath = "examplecontent.tmpl.html"
	name := filepath.Base(templatePath)
	t, err := template.New(name).Funcs(funcs).ParseFiles(templatePath)
	if err != nil {
		return err
	}

	data := map[string]interface{}{
		"License":   license,
		"Copyright": copyright,
		"Example":   e,
	}
	if err := t.Funcs(funcs).Execute(f, data); err != nil {
		return err
	}

	out := filepath.Join(dir, e.Name+".js")
	path := "github.com/hajimehoshi/ebiten/example/" + e.Name
	if err := exec.Command("gopherjs", "build", "-m", "-o", out, path).Run(); err != nil {
		return err
	}

	return nil
}

func outputExample(e *example) error {
	const dir = "public/example"
	if err := os.MkdirAll(dir, 0755); err != nil {
		return err
	}
	f, err := os.Create(filepath.Join(dir, e.Name+".html"))
	if err != nil {
		return err
	}
	defer f.Close()

	funcs := template.FuncMap{
		"comment":  comment,
		"safeHTML": safeHTML,
	}
	const templatePath = "example.tmpl.html"
	name := filepath.Base(templatePath)
	t, err := template.New(name).Funcs(funcs).ParseFiles(templatePath)
	if err != nil {
		return err
	}

	data := map[string]interface{}{
		"License":   license,
		"Copyright": copyright,
		"Example":   e,
	}
	return t.Funcs(funcs).Execute(f, data)
}

func main() {
	if err := clear(); err != nil {
		log.Fatal(err)
	}
	if err := outputMain(); err != nil {
		log.Fatal(err)
	}
	for _, e := range examples {
		if err := outputExampleContent(&e); err != nil {
			log.Fatal(err)
		}
		if err := outputExample(&e); err != nil {
			log.Fatal(err)
		}
	}
}
<commit_msg>docs: Check license year<commit_after>// Copyright 2014 Hajime Hoshi
//
// 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.

// +build ignore

package main

import (
	"fmt"
	"html/template"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
	"time"
)

var license = ""

func init() {
	b, err := ioutil.ReadFile("../license.txt")
	if err != nil {
		panic(err)
	}
	license = strings.TrimSpace(string(b))

	year, err := strconv.Atoi(regexp.MustCompile(`^Copyright (\d+)`).FindStringSubmatch(license)[1])
	if err != nil {
		panic(err)
	}
	if year != time.Now().Year() {
		panic("the license's year is not this year")
	}
}

var copyright = ""

func init() {
	copyright = fmt.Sprintf("© %d Hajime Hoshi", time.Now().Year())
}

var stableVersion = ""

var devVersion = ""

func init() {
	b, err := ioutil.ReadFile("../version.txt")
	if err != nil {
		panic(err)
	}
	stableVersion = strings.TrimSpace(string(b))
}

func init() {
	b, err := exec.Command("git", "show", "master:version.txt").Output()
	if err != nil {
		panic(err)
	}
	devVersion = strings.TrimSpace(string(b))
}

func comment(text string) template.HTML {
	// TODO: text should be escaped
	return template.HTML("<!--\n" + text + "\n-->")
}

func safeHTML(text string) template.HTML {
	return template.HTML(text)
}

type example struct {
	Name string
}

func (e *example) ThumbWidth() int {
	if e.Name == "blocks" {
		return 256
	}
	return 320
}

func (e *example) ThumbHeight() int {
	if e.Name == "blocks" {
		return 240
	}
	return 240
}

func (e *example) Width() int {
	return e.ThumbWidth() * 2
}

func (e *example) Height() int {
	return e.ThumbHeight() * 2
}

func (e *example) Source() string {
	if e.Name == "blocks" {
		return "// Please read example/blocks/main.go and example/blocks/blocks/*.go"
	}

	path := filepath.Join(os.Getenv("GOPATH"), "src", "github.com", "hajimehoshi", "ebiten", "example", e.Name, "main.go")
	b, err := ioutil.ReadFile(path)
	if err != nil {
		panic(err)
	}
	str := regexp.MustCompile("(?s)^.*?\n\n").ReplaceAllString(string(b), "")
	str = strings.Replace(str, "\t", "    ", -1)
	return str
}

func versions() string {
	return fmt.Sprintf("v%s (dev: v%s)", stableVersion, devVersion)
}

var examples = []example{
	{Name: "hue"},
	{Name: "keyboard"},
	{Name: "mosaic"},
	{Name: "perspective"},
	{Name: "rotate"},
	{Name: "blocks"},
}

func clear() error {
	if err := filepath.Walk("public", func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		// Remove auto-generated html files.
		m, err := regexp.MatchString(".html$", path)
		if err != nil {
			return err
		}
		if !m {
			return nil
		}
		// Remove example resources that are copied.
		m, err = regexp.MatchString("public/example/images", path)
		if err != nil {
			return err
		}
		if !m {
			return nil
		}
		return os.Remove(path)
	}); err != nil {
		return err
	}
	return nil
}

func outputMain() error {
	f, err := os.Create("public/index.html")
	if err != nil {
		return err
	}
	defer f.Close()

	funcs := template.FuncMap{
		"comment":  comment,
		"safeHTML": safeHTML,
	}
	const templatePath = "index.tmpl.html"
	name := filepath.Base(templatePath)
	t, err := template.New(name).Funcs(funcs).ParseFiles(templatePath)
	if err != nil {
		return err
	}

	data := map[string]interface{}{
		"License":       license,
		"Copyright":     copyright,
		"StableVersion": stableVersion,
		"DevVersion":    devVersion,
		"Examples":      examples,
	}
	return t.Funcs(funcs).Execute(f, data)
}

func outputExampleContent(e *example) error {
	const dir = "public/example"
	if err := os.MkdirAll(dir, 0755); err != nil {
		return err
	}
	f, err := os.Create(filepath.Join(dir, e.Name+".content.html"))
	if err != nil {
		return err
	}
	defer f.Close()

	funcs := template.FuncMap{
		"comment":  comment,
		"safeHTML": safeHTML,
	}
	const templatePath = "examplecontent.tmpl.html"
	name := filepath.Base(templatePath)
	t, err := template.New(name).Funcs(funcs).ParseFiles(templatePath)
	if err != nil {
		return err
	}

	data := map[string]interface{}{
		"License":   license,
		"Copyright": copyright,
		"Example":   e,
	}
	if err := t.Funcs(funcs).Execute(f, data); err != nil {
		return err
	}

	out := filepath.Join(dir, e.Name+".js")
	path := "github.com/hajimehoshi/ebiten/example/" + e.Name
	if err := exec.Command("gopherjs", "build", "-m", "-o", out, path).Run(); err != nil {
		return err
	}

	return nil
}

func outputExample(e *example) error {
	const dir = "public/example"
	if err := os.MkdirAll(dir, 0755); err != nil {
		return err
	}
	f, err := os.Create(filepath.Join(dir, e.Name+".html"))
	if err != nil {
		return err
	}
	defer f.Close()

	funcs := template.FuncMap{
		"comment":  comment,
		"safeHTML": safeHTML,
	}
	const templatePath = "example.tmpl.html"
	name := filepath.Base(templatePath)
	t, err := template.New(name).Funcs(funcs).ParseFiles(templatePath)
	if err != nil {
		return err
	}

	data := map[string]interface{}{
		"License":   license,
		"Copyright": copyright,
		"Example":   e,
	}
	return t.Funcs(funcs).Execute(f, data)
}

func main() {
	if err := clear(); err != nil {
		log.Fatal(err)
	}
	if err := outputMain(); err != nil {
		log.Fatal(err)
	}
	for _, e := range examples {
		if err := outputExampleContent(&e); err != nil {
			log.Fatal(err)
		}
		if err := outputExample(&e); err != nil {
			log.Fatal(err)
		}
	}
}
<|endoftext|>


<commit_before>package search

import "github.com/algolia/algoliasearch-client-go/v3/algolia/opt"

type RuleConsequence struct {
	Params         *RuleParams               `json:"params,omitempty"`
	Promote        []PromotedObject          `json:"promote,omitempty"`
	FilterPromotes *opt.FilterPromotesOption `json:"filterPromotes,omitempty"`
	Hide           []HiddenObject            `json:"hide,omitempty"`
	UserData       interface{}               `json:"userData,omitempty"`
}

type PromotedObject struct {
	ObjectID string `json:"objectID"`
	Position int    `json:"position"`
}

type HiddenObject struct {
	ObjectID string `json:"objectID"`
}
<commit_msg>feat(rule): expose Rule.RuleConsequence.Promote.ObjectIDs string slice<commit_after>package search

import "github.com/algolia/algoliasearch-client-go/v3/algolia/opt"

type RuleConsequence struct {
	Params         *RuleParams               `json:"params,omitempty"`
	Promote        []PromotedObject          `json:"promote,omitempty"`
	FilterPromotes *opt.FilterPromotesOption `json:"filterPromotes,omitempty"`
	Hide           []HiddenObject            `json:"hide,omitempty"`
	UserData       interface{}               `json:"userData,omitempty"`
}

type PromotedObject struct {
	ObjectID  string   `json:"objectID,omitempty"`
	ObjectIDs []string `json:"objectIDs,omitempty"`
	Position  int      `json:"position"`
}

type HiddenObject struct {
	ObjectID string `json:"objectID"`
}
<|endoftext|>


<commit_before>// Copyright 2009 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 draw provides basic graphics and drawing primitives,
// in the style of the Plan 9 graphics library
// (see http://plan9.bell-labs.com/magic/man2html/2/draw)
// and the X Render extension.
package draw

// BUG(rsc): This is a toy library and not ready for production use.

import "image"

// A Porter-Duff compositing operator.
type Op int

const (
	// Over specifies ``(src in mask) over dst''.
	Over Op = iota
	// Src specifies ``src in mask''.
	Src
)

var zeroColor image.Color = image.AlphaColor{0}

// A draw.Image is an image.Image with a Set method to change a single pixel.
type Image interface {
	image.Image
	Set(x, y int, c image.Color)
}

// Draw calls DrawMask with a nil mask and an Over op.
func Draw(dst Image, r Rectangle, src image.Image, sp Point) {
	DrawMask(dst, r, src, sp, nil, ZP, Over)
}

// DrawMask aligns r.Min in dst with sp in src and mp in mask and then replaces the rectangle r
// in dst with the result of a Porter-Duff composition. A nil mask is treated as opaque.
// The implementation is simple and slow.
// TODO(nigeltao): Optimize this.
func DrawMask(dst Image, r Rectangle, src image.Image, sp Point, mask image.Image, mp Point, op Op) {
	dx, dy := src.Width()-sp.X, src.Height()-sp.Y
	if mask != nil {
		if dx > mask.Width()-mp.X {
			dx = mask.Width() - mp.X
		}
		if dy > mask.Height()-mp.Y {
			dy = mask.Height() - mp.Y
		}
	}
	if r.Dx() > dx {
		r.Max.X = r.Min.X + dx
	}
	if r.Dy() > dy {
		r.Max.Y = r.Min.Y + dy
	}

	// TODO(nigeltao): Clip r to dst's bounding box, and handle the case when sp or mp has negative X or Y.
	// TODO(nigeltao): Ensure that r is well formed, i.e. r.Max.X >= r.Min.X and likewise for Y.

	// Fast paths for special cases. If none of them apply, then we fall back to a general but slow implementation.
	if dst0, ok := dst.(*image.RGBA); ok {
		if op == Over {
			// TODO(nigeltao): Implement a fast path for font glyphs (i.e. when mask is an image.Alpha).
		} else {
			if mask == nil {
				if src0, ok := src.(image.ColorImage); ok {
					drawFill(dst0, r, src0)
					return
				}
				if src0, ok := src.(*image.RGBA); ok {
					if dst0 == src0 && r.Overlaps(r.Add(sp.Sub(r.Min))) {
						// TODO(nigeltao): Implement a fast path for the overlapping case.
					} else {
						drawCopy(dst0, r, src0, sp)
						return
					}
				}
			}
		}
	}

	x0, x1, dx := r.Min.X, r.Max.X, 1
	y0, y1, dy := r.Min.Y, r.Max.Y, 1
	if image.Image(dst) == src && r.Overlaps(r.Add(sp.Sub(r.Min))) {
		// Rectangles overlap: process backward?
		if sp.Y < r.Min.Y || sp.Y == r.Min.Y && sp.X < r.Min.X {
			x0, x1, dx = x1-1, x0-1, -1
			y0, y1, dy = y1-1, y0-1, -1
		}
	}

	var out *image.RGBA64Color
	sy := sp.Y + y0 - r.Min.Y
	my := mp.Y + y0 - r.Min.Y
	for y := y0; y != y1; y, sy, my = y+dy, sy+dy, my+dy {
		sx := sp.X + x0 - r.Min.X
		mx := mp.X + x0 - r.Min.X
		for x := x0; x != x1; x, sx, mx = x+dx, sx+dx, mx+dx {
			// A nil mask is equivalent to a fully opaque, infinitely large mask.
			// We work in 16-bit color, so that multiplying two values does not overflow a uint32.
			const M = 1<<16 - 1
			ma := uint32(M)
			if mask != nil {
				_, _, _, ma = mask.At(mx, my).RGBA()
				ma >>= 16
			}
			switch {
			case ma == 0:
				if op == Over {
					// No-op.
				} else {
					dst.Set(x, y, zeroColor)
				}
			case ma == M && op == Src:
				dst.Set(x, y, src.At(sx, sy))
			default:
				sr, sg, sb, sa := src.At(sx, sy).RGBA()
				sr >>= 16
				sg >>= 16
				sb >>= 16
				sa >>= 16
				if out == nil {
					out = new(image.RGBA64Color)
				}
				if op == Over {
					dr, dg, db, da := dst.At(x, y).RGBA()
					dr >>= 16
					dg >>= 16
					db >>= 16
					da >>= 16
					a := M - (sa * ma / M)
					out.R = uint16((dr*a + sr*ma) / M)
					out.G = uint16((dg*a + sg*ma) / M)
					out.B = uint16((db*a + sb*ma) / M)
					out.A = uint16((da*a + sa*ma) / M)
				} else {
					out.R = uint16(sr * ma / M)
					out.G = uint16(sg * ma / M)
					out.B = uint16(sb * ma / M)
					out.A = uint16(sa * ma / M)
				}
				dst.Set(x, y, out)
			}
		}
	}
}

func drawFill(dst *image.RGBA, r Rectangle, src image.ColorImage) {
	if r.Dy() < 1 {
		return
	}
	cr, cg, cb, ca := src.RGBA()
	color := image.RGBAColor{uint8(cr >> 24), uint8(cg >> 24), uint8(cb >> 24), uint8(ca >> 24)}
	// The built-in copy function is faster than a straightforward for loop to fill the destination with
	// the color, but copy requires a slice source. We therefore use a for loop to fill the first row, and
	// then use the first row as the slice source for the remaining rows.
	dx0, dx1 := r.Min.X, r.Max.X
	dy0, dy1 := r.Min.Y, r.Max.Y
	firstRow := dst.Pixel[dy0]
	for x := dx0; x < dx1; x++ {
		firstRow[x] = color
	}
	copySrc := firstRow[dx0:dx1]
	for y := dy0 + 1; y < dy1; y++ {
		copy(dst.Pixel[y][dx0:dx1], copySrc)
	}
}

func drawCopy(dst *image.RGBA, r Rectangle, src *image.RGBA, sp Point) {
	dx0, dx1 := r.Min.X, r.Max.X
	dy0, dy1 := r.Min.Y, r.Max.Y
	sx0, sx1 := sp.X, sp.X+dx1-dx0
	for y, sy := dy0, sp.Y; y < dy1; y, sy = y+1, sy+1 {
		copy(dst.Pixel[y][dx0:dx1], src.Pixel[sy][sx0:sx1])
	}
}

// Border aligns r.Min in dst with sp in src and then replaces pixels
// in a w-pixel border around r in dst with the result of the Porter-Duff compositing
// operation ``src over dst.''  If w is positive, the border extends w pixels inside r.
// If w is negative, the border extends w pixels outside r.
func Border(dst Image, r Rectangle, w int, src image.Image, sp Point) {
	i := w
	if i > 0 {
		// inside r
		Draw(dst, Rect(r.Min.X, r.Min.Y, r.Max.X, r.Min.Y+i), src, sp)                          // top
		Draw(dst, Rect(r.Min.X, r.Min.Y+i, r.Min.X+i, r.Max.Y-i), src, sp.Add(Pt(0, i)))        // left
		Draw(dst, Rect(r.Max.X-i, r.Min.Y+i, r.Max.X, r.Max.Y-i), src, sp.Add(Pt(r.Dx()-i, i))) // right
		Draw(dst, Rect(r.Min.X, r.Max.Y-i, r.Max.X, r.Max.Y), src, sp.Add(Pt(0, r.Dy()-i)))     // bottom
		return
	}

	// outside r;
	i = -i
	Draw(dst, Rect(r.Min.X-i, r.Min.Y-i, r.Max.X+i, r.Min.Y), src, sp.Add(Pt(-i, -i))) // top
	Draw(dst, Rect(r.Min.X-i, r.Min.Y, r.Min.X, r.Max.Y), src, sp.Add(Pt(-i, 0)))      // left
	Draw(dst, Rect(r.Max.X, r.Min.Y, r.Max.X+i, r.Max.Y), src, sp.Add(Pt(r.Dx(), 0)))  // right
	Draw(dst, Rect(r.Min.X-i, r.Max.Y, r.Max.X+i, r.Max.Y+i), src, sp.Add(Pt(-i, 0)))  // bottom
}
<commit_msg>exp/draw fast path for glyph images.<commit_after>// Copyright 2009 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 draw provides basic graphics and drawing primitives,
// in the style of the Plan 9 graphics library
// (see http://plan9.bell-labs.com/magic/man2html/2/draw)
// and the X Render extension.
package draw

// BUG(rsc): This is a toy library and not ready for production use.

import "image"

// A Porter-Duff compositing operator.
type Op int

const (
	// Over specifies ``(src in mask) over dst''.
	Over Op = iota
	// Src specifies ``src in mask''.
	Src
)

var zeroColor image.Color = image.AlphaColor{0}

// A draw.Image is an image.Image with a Set method to change a single pixel.
type Image interface {
	image.Image
	Set(x, y int, c image.Color)
}

// Draw calls DrawMask with a nil mask and an Over op.
func Draw(dst Image, r Rectangle, src image.Image, sp Point) {
	DrawMask(dst, r, src, sp, nil, ZP, Over)
}

// DrawMask aligns r.Min in dst with sp in src and mp in mask and then replaces the rectangle r
// in dst with the result of a Porter-Duff composition. A nil mask is treated as opaque.
// The implementation is simple and slow.
// TODO(nigeltao): Optimize this.
func DrawMask(dst Image, r Rectangle, src image.Image, sp Point, mask image.Image, mp Point, op Op) {
	dx, dy := src.Width()-sp.X, src.Height()-sp.Y
	if mask != nil {
		if dx > mask.Width()-mp.X {
			dx = mask.Width() - mp.X
		}
		if dy > mask.Height()-mp.Y {
			dy = mask.Height() - mp.Y
		}
	}
	if r.Dx() > dx {
		r.Max.X = r.Min.X + dx
	}
	if r.Dy() > dy {
		r.Max.Y = r.Min.Y + dy
	}

	// TODO(nigeltao): Clip r to dst's bounding box, and handle the case when sp or mp has negative X or Y.
	// TODO(nigeltao): Ensure that r is well formed, i.e. r.Max.X >= r.Min.X and likewise for Y.

	// Fast paths for special cases. If none of them apply, then we fall back to a general but slow implementation.
	if dst0, ok := dst.(*image.RGBA); ok {
		if op == Over {
			if mask0, ok := mask.(*image.Alpha); ok {
				if src0, ok := src.(image.ColorImage); ok {
					drawGlyphOver(dst0, r, src0, mask0, mp)
					return
				}
			}
		} else {
			if mask == nil {
				if src0, ok := src.(image.ColorImage); ok {
					drawFill(dst0, r, src0)
					return
				}
				if src0, ok := src.(*image.RGBA); ok {
					if dst0 == src0 && r.Overlaps(r.Add(sp.Sub(r.Min))) {
						// TODO(nigeltao): Implement a fast path for the overlapping case.
					} else {
						drawCopy(dst0, r, src0, sp)
						return
					}
				}
			}
		}
	}

	x0, x1, dx := r.Min.X, r.Max.X, 1
	y0, y1, dy := r.Min.Y, r.Max.Y, 1
	if image.Image(dst) == src && r.Overlaps(r.Add(sp.Sub(r.Min))) {
		// Rectangles overlap: process backward?
		if sp.Y < r.Min.Y || sp.Y == r.Min.Y && sp.X < r.Min.X {
			x0, x1, dx = x1-1, x0-1, -1
			y0, y1, dy = y1-1, y0-1, -1
		}
	}

	var out *image.RGBA64Color
	sy := sp.Y + y0 - r.Min.Y
	my := mp.Y + y0 - r.Min.Y
	for y := y0; y != y1; y, sy, my = y+dy, sy+dy, my+dy {
		sx := sp.X + x0 - r.Min.X
		mx := mp.X + x0 - r.Min.X
		for x := x0; x != x1; x, sx, mx = x+dx, sx+dx, mx+dx {
			// A nil mask is equivalent to a fully opaque, infinitely large mask.
			// We work in 16-bit color, so that multiplying two values does not overflow a uint32.
			const M = 1<<16 - 1
			ma := uint32(M)
			if mask != nil {
				_, _, _, ma = mask.At(mx, my).RGBA()
				ma >>= 16
			}
			switch {
			case ma == 0:
				if op == Over {
					// No-op.
				} else {
					dst.Set(x, y, zeroColor)
				}
			case ma == M && op == Src:
				dst.Set(x, y, src.At(sx, sy))
			default:
				sr, sg, sb, sa := src.At(sx, sy).RGBA()
				sr >>= 16
				sg >>= 16
				sb >>= 16
				sa >>= 16
				if out == nil {
					out = new(image.RGBA64Color)
				}
				if op == Over {
					dr, dg, db, da := dst.At(x, y).RGBA()
					dr >>= 16
					dg >>= 16
					db >>= 16
					da >>= 16
					a := M - (sa * ma / M)
					out.R = uint16((dr*a + sr*ma) / M)
					out.G = uint16((dg*a + sg*ma) / M)
					out.B = uint16((db*a + sb*ma) / M)
					out.A = uint16((da*a + sa*ma) / M)
				} else {
					out.R = uint16(sr * ma / M)
					out.G = uint16(sg * ma / M)
					out.B = uint16(sb * ma / M)
					out.A = uint16(sa * ma / M)
				}
				dst.Set(x, y, out)
			}
		}
	}
}

func drawGlyphOver(dst *image.RGBA, r Rectangle, src image.ColorImage, mask *image.Alpha, mp Point) {
	x0, x1 := r.Min.X, r.Max.X
	y0, y1 := r.Min.Y, r.Max.Y
	cr, cg, cb, ca := src.RGBA()
	cr >>= 16
	cg >>= 16
	cb >>= 16
	ca >>= 16
	for y, my := y0, mp.Y; y != y1; y, my = y+1, my+1 {
		for x, mx := x0, mp.X; x != x1; x, mx = x+1, mx+1 {
			ma := uint32(mask.Pixel[my][mx].A)
			if ma == 0 {
				continue
			}
			ma |= ma << 8
			dr := uint32(dst.Pixel[y][x].R)
			dg := uint32(dst.Pixel[y][x].G)
			db := uint32(dst.Pixel[y][x].B)
			da := uint32(dst.Pixel[y][x].A)
			// dr, dg, db and da are all 8-bit color at the moment, ranging in [0,255].
			// We work in 16-bit color, and so would normally do:
			// dr |= dr << 8
			// and similarly for dg, db and da, but instead we multiply a
			// (which is a 16-bit color, ranging in [0,65535]) by 0x101.
			// This yields the same result, but is fewer arithmetic operations.
			const M = 1<<16 - 1
			a := M - (ca * ma / M)
			a *= 0x101
			dr = (dr*a + cr*ma) / M
			dg = (dg*a + cg*ma) / M
			db = (db*a + cb*ma) / M
			da = (da*a + ca*ma) / M
			dst.Pixel[y][x] = image.RGBAColor{uint8(dr >> 8), uint8(dg >> 8), uint8(db >> 8), uint8(da >> 8)}
		}
	}
}

func drawFill(dst *image.RGBA, r Rectangle, src image.ColorImage) {
	if r.Dy() < 1 {
		return
	}
	cr, cg, cb, ca := src.RGBA()
	color := image.RGBAColor{uint8(cr >> 24), uint8(cg >> 24), uint8(cb >> 24), uint8(ca >> 24)}
	// The built-in copy function is faster than a straightforward for loop to fill the destination with
	// the color, but copy requires a slice source. We therefore use a for loop to fill the first row, and
	// then use the first row as the slice source for the remaining rows.
	dx0, dx1 := r.Min.X, r.Max.X
	dy0, dy1 := r.Min.Y, r.Max.Y
	firstRow := dst.Pixel[dy0]
	for x := dx0; x < dx1; x++ {
		firstRow[x] = color
	}
	copySrc := firstRow[dx0:dx1]
	for y := dy0 + 1; y < dy1; y++ {
		copy(dst.Pixel[y][dx0:dx1], copySrc)
	}
}

func drawCopy(dst *image.RGBA, r Rectangle, src *image.RGBA, sp Point) {
	dx0, dx1 := r.Min.X, r.Max.X
	dy0, dy1 := r.Min.Y, r.Max.Y
	sx0, sx1 := sp.X, sp.X+dx1-dx0
	for y, sy := dy0, sp.Y; y < dy1; y, sy = y+1, sy+1 {
		copy(dst.Pixel[y][dx0:dx1], src.Pixel[sy][sx0:sx1])
	}
}

// Border aligns r.Min in dst with sp in src and then replaces pixels
// in a w-pixel border around r in dst with the result of the Porter-Duff compositing
// operation ``src over dst.''  If w is positive, the border extends w pixels inside r.
// If w is negative, the border extends w pixels outside r.
func Border(dst Image, r Rectangle, w int, src image.Image, sp Point) {
	i := w
	if i > 0 {
		// inside r
		Draw(dst, Rect(r.Min.X, r.Min.Y, r.Max.X, r.Min.Y+i), src, sp)                          // top
		Draw(dst, Rect(r.Min.X, r.Min.Y+i, r.Min.X+i, r.Max.Y-i), src, sp.Add(Pt(0, i)))        // left
		Draw(dst, Rect(r.Max.X-i, r.Min.Y+i, r.Max.X, r.Max.Y-i), src, sp.Add(Pt(r.Dx()-i, i))) // right
		Draw(dst, Rect(r.Min.X, r.Max.Y-i, r.Max.X, r.Max.Y), src, sp.Add(Pt(0, r.Dy()-i)))     // bottom
		return
	}

	// outside r;
	i = -i
	Draw(dst, Rect(r.Min.X-i, r.Min.Y-i, r.Max.X+i, r.Min.Y), src, sp.Add(Pt(-i, -i))) // top
	Draw(dst, Rect(r.Min.X-i, r.Min.Y, r.Min.X, r.Max.Y), src, sp.Add(Pt(-i, 0)))      // left
	Draw(dst, Rect(r.Max.X, r.Min.Y, r.Max.X+i, r.Max.Y), src, sp.Add(Pt(r.Dx(), 0)))  // right
	Draw(dst, Rect(r.Min.X-i, r.Max.Y, r.Max.X+i, r.Max.Y+i), src, sp.Add(Pt(-i, 0)))  // bottom
}
<|endoftext|>


<commit_before>// Copyright (C) 2014 The Protocol Authors.

package protocol

import (
	"encoding/binary"
	"encoding/hex"
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"reflect"
	"strings"
	"testing"
	"testing/quick"
	"time"

	"github.com/calmh/xdr"
)

var (
	c0ID     = NewDeviceID([]byte{1})
	c1ID     = NewDeviceID([]byte{2})
	quickCfg = &quick.Config{}
)

func TestMain(m *testing.M) {
	flag.Parse()
	if flag.Lookup("test.short").Value.String() != "false" {
		quickCfg.MaxCount = 10
	}
	os.Exit(m.Run())
}

func TestHeaderEncodeDecode(t *testing.T) {
	f := func(ver, id, typ int) bool {
		ver = int(uint(ver) % 16)
		id = int(uint(id) % 4096)
		typ = int(uint(typ) % 256)
		h0 := header{version: ver, msgID: id, msgType: typ}
		h1 := decodeHeader(encodeHeader(h0))
		return h0 == h1
	}
	if err := quick.Check(f, nil); err != nil {
		t.Error(err)
	}
}

func TestHeaderMarshalUnmarshal(t *testing.T) {
	f := func(ver, id, typ int) bool {
		ver = int(uint(ver) % 16)
		id = int(uint(id) % 4096)
		typ = int(uint(typ) % 256)
		buf := make([]byte, 4)

		h0 := header{version: ver, msgID: id, msgType: typ}
		h0.MarshalXDRInto(&xdr.Marshaller{Data: buf})

		var h1 header
		h1.UnmarshalXDRFrom(&xdr.Unmarshaller{Data: buf})
		return h0 == h1
	}
	if err := quick.Check(f, nil); err != nil {
		t.Error(err)
	}
}

func TestHeaderLayout(t *testing.T) {
	var e, a uint32

	// Version are the first four bits
	e = 0xf0000000
	a = encodeHeader(header{version: 0xf})
	if a != e {
		t.Errorf("Header layout incorrect; %08x != %08x", a, e)
	}

	// Message ID are the following 12 bits
	e = 0x0fff0000
	a = encodeHeader(header{msgID: 0xfff})
	if a != e {
		t.Errorf("Header layout incorrect; %08x != %08x", a, e)
	}

	// Type are the last 8 bits before reserved
	e = 0x0000ff00
	a = encodeHeader(header{msgType: 0xff})
	if a != e {
		t.Errorf("Header layout incorrect; %08x != %08x", a, e)
	}
}

func TestPing(t *testing.T) {
	ar, aw := io.Pipe()
	br, bw := io.Pipe()

	c0 := NewConnection(c0ID, ar, bw, newTestModel(), "name", CompressAlways).(wireFormatConnection).Connection.(*rawConnection)
	c0.Start()
	c1 := NewConnection(c1ID, br, aw, newTestModel(), "name", CompressAlways).(wireFormatConnection).Connection.(*rawConnection)
	c1.Start()
	c0.ClusterConfig(ClusterConfigMessage{})
	c1.ClusterConfig(ClusterConfigMessage{})

	if ok := c0.ping(); !ok {
		t.Error("c0 ping failed")
	}
	if ok := c1.ping(); !ok {
		t.Error("c1 ping failed")
	}
}

func TestVersionErr(t *testing.T) {
	m0 := newTestModel()
	m1 := newTestModel()

	ar, aw := io.Pipe()
	br, bw := io.Pipe()

	c0 := NewConnection(c0ID, ar, bw, m0, "name", CompressAlways).(wireFormatConnection).Connection.(*rawConnection)
	c0.Start()
	c1 := NewConnection(c1ID, br, aw, m1, "name", CompressAlways)
	c1.Start()
	c0.ClusterConfig(ClusterConfigMessage{})
	c1.ClusterConfig(ClusterConfigMessage{})

	timeoutWriteHeader(c0.cw, header{
		version: 2, // higher than supported
		msgID:   0,
		msgType: messageTypeIndex,
	})

	if err := m1.closedError(); err == nil || !strings.Contains(err.Error(), "unknown protocol version") {
		t.Error("Connection should close due to unknown version, not", err)
	}
}

func TestTypeErr(t *testing.T) {
	m0 := newTestModel()
	m1 := newTestModel()

	ar, aw := io.Pipe()
	br, bw := io.Pipe()

	c0 := NewConnection(c0ID, ar, bw, m0, "name", CompressAlways).(wireFormatConnection).Connection.(*rawConnection)
	c0.Start()
	c1 := NewConnection(c1ID, br, aw, m1, "name", CompressAlways)
	c1.Start()
	c0.ClusterConfig(ClusterConfigMessage{})
	c1.ClusterConfig(ClusterConfigMessage{})

	timeoutWriteHeader(c0.cw, header{
		version: 0,
		msgID:   0,
		msgType: 42, // unknown type
	})

	if err := m1.closedError(); err == nil || !strings.Contains(err.Error(), "unknown message type") {
		t.Error("Connection should close due to unknown message type, not", err)
	}
}

func TestClose(t *testing.T) {
	m0 := newTestModel()
	m1 := newTestModel()

	ar, aw := io.Pipe()
	br, bw := io.Pipe()

	c0 := NewConnection(c0ID, ar, bw, m0, "name", CompressAlways).(wireFormatConnection).Connection.(*rawConnection)
	c0.Start()
	c1 := NewConnection(c1ID, br, aw, m1, "name", CompressAlways)
	c1.Start()
	c0.ClusterConfig(ClusterConfigMessage{})
	c1.ClusterConfig(ClusterConfigMessage{})

	c0.close(errors.New("manual close"))

	<-c0.closed
	if err := m0.closedError(); err == nil || !strings.Contains(err.Error(), "manual close") {
		t.Fatal("Connection should be closed")
	}

	// None of these should panic, some should return an error

	if c0.ping() {
		t.Error("Ping should not return true")
	}

	c0.Index("default", nil, 0, nil)
	c0.Index("default", nil, 0, nil)

	if _, err := c0.Request("default", "foo", 0, 0, nil, 0, nil); err == nil {
		t.Error("Request should return an error")
	}
}

func TestElementSizeExceededNested(t *testing.T) {
	m := ClusterConfigMessage{
		ClientName: "longstringlongstringlongstringinglongstringlongstringlonlongstringlongstringlon",
	}
	_, err := m.MarshalXDR()
	if err == nil {
		t.Errorf("ID length %d > max 64, but no error", len(m.Folders[0].ID))
	}
}

func TestMarshalIndexMessage(t *testing.T) {
	f := func(m1 IndexMessage) bool {
		if len(m1.Options) == 0 {
			m1.Options = nil
		}
		for i, f := range m1.Files {
			m1.Files[i].CachedSize = 0
			if len(f.Blocks) == 0 {
				m1.Files[i].Blocks = nil
			} else {
				for j := range f.Blocks {
					f.Blocks[j].Offset = 0
					if len(f.Blocks[j].Hash) == 0 {
						f.Blocks[j].Hash = nil
					}
				}
			}
		}

		return testMarshal(t, "index", &m1, &IndexMessage{})
	}

	if err := quick.Check(f, quickCfg); err != nil {
		t.Error(err)
	}
}

func TestMarshalRequestMessage(t *testing.T) {
	f := func(m1 RequestMessage) bool {
		if len(m1.Options) == 0 {
			m1.Options = nil
		}
		return testMarshal(t, "request", &m1, &RequestMessage{})
	}

	if err := quick.Check(f, quickCfg); err != nil {
		t.Error(err)
	}
}

func TestMarshalResponseMessage(t *testing.T) {
	f := func(m1 ResponseMessage) bool {
		if len(m1.Data) == 0 {
			m1.Data = nil
		}
		return testMarshal(t, "response", &m1, &ResponseMessage{})
	}

	if err := quick.Check(f, quickCfg); err != nil {
		t.Error(err)
	}
}

func TestMarshalClusterConfigMessage(t *testing.T) {
	f := func(m1 ClusterConfigMessage) bool {
		if len(m1.Options) == 0 {
			m1.Options = nil
		}
		return testMarshal(t, "clusterconfig", &m1, &ClusterConfigMessage{})
	}

	if err := quick.Check(f, quickCfg); err != nil {
		t.Error(err)
	}
}

func TestMarshalCloseMessage(t *testing.T) {
	f := func(m1 CloseMessage) bool {
		return testMarshal(t, "close", &m1, &CloseMessage{})
	}

	if err := quick.Check(f, quickCfg); err != nil {
		t.Error(err)
	}
}

type message interface {
	MarshalXDR() ([]byte, error)
	UnmarshalXDR([]byte) error
}

func testMarshal(t *testing.T, prefix string, m1, m2 message) bool {
	failed := func(bc []byte) {
		bs, _ := json.MarshalIndent(m1, "", "  ")
		ioutil.WriteFile(prefix+"-1.txt", bs, 0644)
		bs, _ = json.MarshalIndent(m2, "", "  ")
		ioutil.WriteFile(prefix+"-2.txt", bs, 0644)
		if len(bc) > 0 {
			f, _ := os.Create(prefix + "-data.txt")
			fmt.Fprint(f, hex.Dump(bc))
			f.Close()
		}
	}

	buf, err := m1.MarshalXDR()
	if err != nil && strings.Contains(err.Error(), "exceeds size") {
		return true
	}
	if err != nil {
		failed(nil)
		t.Fatal(err)
	}

	err = m2.UnmarshalXDR(buf)
	if err != nil {
		failed(buf)
		t.Fatal(err)
	}

	ok := reflect.DeepEqual(m1, m2)
	if !ok {
		failed(buf)
	}
	return ok
}

func timeoutWriteHeader(w io.Writer, hdr header) {
	// This tries to write a message header to w, but times out after a while.
	// This is useful because in testing, with a PipeWriter, it will block
	// forever if the other side isn't reading any more. On the other hand we
	// can't just "go" it into the background, because if the other side is
	// still there we should wait for the write to complete. Yay.

	var buf [8]byte // header and message length
	binary.BigEndian.PutUint32(buf[:], encodeHeader(hdr))
	binary.BigEndian.PutUint32(buf[4:], 0) // zero message length, explicitly

	done := make(chan struct{})
	go func() {
		w.Write(buf[:])
		close(done)
	}()
	select {
	case <-done:
	case <-time.After(250 * time.Millisecond):
	}
}

func TestFileInfoSize(t *testing.T) {
	fi := FileInfo{
		Blocks: []BlockInfo{
			{Size: 42},
			{Offset: 42, Size: 23},
			{Offset: 42 + 23, Size: 34},
		},
	}

	size := fi.Size()
	want := int64(42 + 23 + 34)
	if size != want {
		t.Errorf("Incorrect size reported, got %d, want %d", size, want)
	}

	size = fi.Size() // Cached, this time
	if size != want {
		t.Errorf("Incorrect cached size reported, got %d, want %d", size, want)
	}

	fi.CachedSize = 8
	want = 8
	size = fi.Size() // Ensure it came from the cache
	if size != want {
		t.Errorf("Incorrect cached size reported, got %d, want %d", size, want)
	}

	fi.CachedSize = 0
	fi.Flags = FlagDirectory
	want = 128
	size = fi.Size() // Directories are 128 bytes large
	if size != want {
		t.Errorf("Incorrect cached size reported, got %d, want %d", size, want)
	}

	fi.CachedSize = 0
	fi.Flags = FlagDeleted
	want = 128
	size = fi.Size() // Also deleted files
	if size != want {
		t.Errorf("Incorrect cached size reported, got %d, want %d", size, want)
	}
}
<commit_msg>Fix marshalling tests for Go 1.6<commit_after>// Copyright (C) 2014 The Protocol Authors.

package protocol

import (
	"encoding/binary"
	"encoding/hex"
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"reflect"
	"strings"
	"testing"
	"testing/quick"
	"time"

	"github.com/calmh/xdr"
)

var (
	c0ID     = NewDeviceID([]byte{1})
	c1ID     = NewDeviceID([]byte{2})
	quickCfg = &quick.Config{}
)

func TestMain(m *testing.M) {
	flag.Parse()
	if flag.Lookup("test.short").Value.String() != "false" {
		quickCfg.MaxCount = 10
	}
	os.Exit(m.Run())
}

func TestHeaderEncodeDecode(t *testing.T) {
	f := func(ver, id, typ int) bool {
		ver = int(uint(ver) % 16)
		id = int(uint(id) % 4096)
		typ = int(uint(typ) % 256)
		h0 := header{version: ver, msgID: id, msgType: typ}
		h1 := decodeHeader(encodeHeader(h0))
		return h0 == h1
	}
	if err := quick.Check(f, nil); err != nil {
		t.Error(err)
	}
}

func TestHeaderMarshalUnmarshal(t *testing.T) {
	f := func(ver, id, typ int) bool {
		ver = int(uint(ver) % 16)
		id = int(uint(id) % 4096)
		typ = int(uint(typ) % 256)
		buf := make([]byte, 4)

		h0 := header{version: ver, msgID: id, msgType: typ}
		h0.MarshalXDRInto(&xdr.Marshaller{Data: buf})

		var h1 header
		h1.UnmarshalXDRFrom(&xdr.Unmarshaller{Data: buf})
		return h0 == h1
	}
	if err := quick.Check(f, nil); err != nil {
		t.Error(err)
	}
}

func TestHeaderLayout(t *testing.T) {
	var e, a uint32

	// Version are the first four bits
	e = 0xf0000000
	a = encodeHeader(header{version: 0xf})
	if a != e {
		t.Errorf("Header layout incorrect; %08x != %08x", a, e)
	}

	// Message ID are the following 12 bits
	e = 0x0fff0000
	a = encodeHeader(header{msgID: 0xfff})
	if a != e {
		t.Errorf("Header layout incorrect; %08x != %08x", a, e)
	}

	// Type are the last 8 bits before reserved
	e = 0x0000ff00
	a = encodeHeader(header{msgType: 0xff})
	if a != e {
		t.Errorf("Header layout incorrect; %08x != %08x", a, e)
	}
}

func TestPing(t *testing.T) {
	ar, aw := io.Pipe()
	br, bw := io.Pipe()

	c0 := NewConnection(c0ID, ar, bw, newTestModel(), "name", CompressAlways).(wireFormatConnection).Connection.(*rawConnection)
	c0.Start()
	c1 := NewConnection(c1ID, br, aw, newTestModel(), "name", CompressAlways).(wireFormatConnection).Connection.(*rawConnection)
	c1.Start()
	c0.ClusterConfig(ClusterConfigMessage{})
	c1.ClusterConfig(ClusterConfigMessage{})

	if ok := c0.ping(); !ok {
		t.Error("c0 ping failed")
	}
	if ok := c1.ping(); !ok {
		t.Error("c1 ping failed")
	}
}

func TestVersionErr(t *testing.T) {
	m0 := newTestModel()
	m1 := newTestModel()

	ar, aw := io.Pipe()
	br, bw := io.Pipe()

	c0 := NewConnection(c0ID, ar, bw, m0, "name", CompressAlways).(wireFormatConnection).Connection.(*rawConnection)
	c0.Start()
	c1 := NewConnection(c1ID, br, aw, m1, "name", CompressAlways)
	c1.Start()
	c0.ClusterConfig(ClusterConfigMessage{})
	c1.ClusterConfig(ClusterConfigMessage{})

	timeoutWriteHeader(c0.cw, header{
		version: 2, // higher than supported
		msgID:   0,
		msgType: messageTypeIndex,
	})

	if err := m1.closedError(); err == nil || !strings.Contains(err.Error(), "unknown protocol version") {
		t.Error("Connection should close due to unknown version, not", err)
	}
}

func TestTypeErr(t *testing.T) {
	m0 := newTestModel()
	m1 := newTestModel()

	ar, aw := io.Pipe()
	br, bw := io.Pipe()

	c0 := NewConnection(c0ID, ar, bw, m0, "name", CompressAlways).(wireFormatConnection).Connection.(*rawConnection)
	c0.Start()
	c1 := NewConnection(c1ID, br, aw, m1, "name", CompressAlways)
	c1.Start()
	c0.ClusterConfig(ClusterConfigMessage{})
	c1.ClusterConfig(ClusterConfigMessage{})

	timeoutWriteHeader(c0.cw, header{
		version: 0,
		msgID:   0,
		msgType: 42, // unknown type
	})

	if err := m1.closedError(); err == nil || !strings.Contains(err.Error(), "unknown message type") {
		t.Error("Connection should close due to unknown message type, not", err)
	}
}

func TestClose(t *testing.T) {
	m0 := newTestModel()
	m1 := newTestModel()

	ar, aw := io.Pipe()
	br, bw := io.Pipe()

	c0 := NewConnection(c0ID, ar, bw, m0, "name", CompressAlways).(wireFormatConnection).Connection.(*rawConnection)
	c0.Start()
	c1 := NewConnection(c1ID, br, aw, m1, "name", CompressAlways)
	c1.Start()
	c0.ClusterConfig(ClusterConfigMessage{})
	c1.ClusterConfig(ClusterConfigMessage{})

	c0.close(errors.New("manual close"))

	<-c0.closed
	if err := m0.closedError(); err == nil || !strings.Contains(err.Error(), "manual close") {
		t.Fatal("Connection should be closed")
	}

	// None of these should panic, some should return an error

	if c0.ping() {
		t.Error("Ping should not return true")
	}

	c0.Index("default", nil, 0, nil)
	c0.Index("default", nil, 0, nil)

	if _, err := c0.Request("default", "foo", 0, 0, nil, 0, nil); err == nil {
		t.Error("Request should return an error")
	}
}

func TestElementSizeExceededNested(t *testing.T) {
	m := ClusterConfigMessage{
		ClientName: "longstringlongstringlongstringinglongstringlongstringlonlongstringlongstringlon",
	}
	_, err := m.MarshalXDR()
	if err == nil {
		t.Errorf("ID length %d > max 64, but no error", len(m.Folders[0].ID))
	}
}

func TestMarshalIndexMessage(t *testing.T) {
	f := func(m1 IndexMessage) bool {
		if len(m1.Options) == 0 {
			m1.Options = nil
		}
		if len(m1.Files) == 0 {
			m1.Files = nil
		}
		for i, f := range m1.Files {
			m1.Files[i].CachedSize = 0
			if len(f.Blocks) == 0 {
				m1.Files[i].Blocks = nil
			} else {
				for j := range f.Blocks {
					f.Blocks[j].Offset = 0
					if len(f.Blocks[j].Hash) == 0 {
						f.Blocks[j].Hash = nil
					}
				}
			}
		}

		return testMarshal(t, "index", &m1, &IndexMessage{})
	}

	if err := quick.Check(f, quickCfg); err != nil {
		t.Error(err)
	}
}

func TestMarshalRequestMessage(t *testing.T) {
	f := func(m1 RequestMessage) bool {
		if len(m1.Options) == 0 {
			m1.Options = nil
		}
		if len(m1.Hash) == 0 {
			m1.Hash = nil
		}
		return testMarshal(t, "request", &m1, &RequestMessage{})
	}

	if err := quick.Check(f, quickCfg); err != nil {
		t.Error(err)
	}
}

func TestMarshalResponseMessage(t *testing.T) {
	f := func(m1 ResponseMessage) bool {
		if len(m1.Data) == 0 {
			m1.Data = nil
		}
		return testMarshal(t, "response", &m1, &ResponseMessage{})
	}

	if err := quick.Check(f, quickCfg); err != nil {
		t.Error(err)
	}
}

func TestMarshalClusterConfigMessage(t *testing.T) {
	f := func(m1 ClusterConfigMessage) bool {
		if len(m1.Options) == 0 {
			m1.Options = nil
		}
		if len(m1.Folders) == 0 {
			m1.Folders = nil
		}
		for i := range m1.Folders {
			if len(m1.Folders[i].Devices) == 0 {
				m1.Folders[i].Devices = nil
			}
			if len(m1.Folders[i].Options) == 0 {
				m1.Folders[i].Options = nil
			}
		}
		return testMarshal(t, "clusterconfig", &m1, &ClusterConfigMessage{})
	}

	if err := quick.Check(f, quickCfg); err != nil {
		t.Error(err)
	}
}

func TestMarshalCloseMessage(t *testing.T) {
	f := func(m1 CloseMessage) bool {
		return testMarshal(t, "close", &m1, &CloseMessage{})
	}

	if err := quick.Check(f, quickCfg); err != nil {
		t.Error(err)
	}
}

type message interface {
	MarshalXDR() ([]byte, error)
	UnmarshalXDR([]byte) error
}

func testMarshal(t *testing.T, prefix string, m1, m2 message) bool {
	failed := func(bc []byte) {
		bs, _ := json.MarshalIndent(m1, "", "  ")
		ioutil.WriteFile(prefix+"-1.txt", bs, 0644)
		bs, _ = json.MarshalIndent(m2, "", "  ")
		ioutil.WriteFile(prefix+"-2.txt", bs, 0644)
		if len(bc) > 0 {
			f, _ := os.Create(prefix + "-data.txt")
			fmt.Fprint(f, hex.Dump(bc))
			f.Close()
		}
	}

	buf, err := m1.MarshalXDR()
	if err != nil && strings.Contains(err.Error(), "exceeds size") {
		return true
	}
	if err != nil {
		failed(nil)
		t.Fatal(err)
	}

	err = m2.UnmarshalXDR(buf)
	if err != nil {
		failed(buf)
		t.Fatal(err)
	}

	ok := reflect.DeepEqual(m1, m2)
	if !ok {
		failed(buf)
	}
	return ok
}

func timeoutWriteHeader(w io.Writer, hdr header) {
	// This tries to write a message header to w, but times out after a while.
	// This is useful because in testing, with a PipeWriter, it will block
	// forever if the other side isn't reading any more. On the other hand we
	// can't just "go" it into the background, because if the other side is
	// still there we should wait for the write to complete. Yay.

	var buf [8]byte // header and message length
	binary.BigEndian.PutUint32(buf[:], encodeHeader(hdr))
	binary.BigEndian.PutUint32(buf[4:], 0) // zero message length, explicitly

	done := make(chan struct{})
	go func() {
		w.Write(buf[:])
		close(done)
	}()
	select {
	case <-done:
	case <-time.After(250 * time.Millisecond):
	}
}

func TestFileInfoSize(t *testing.T) {
	fi := FileInfo{
		Blocks: []BlockInfo{
			{Size: 42},
			{Offset: 42, Size: 23},
			{Offset: 42 + 23, Size: 34},
		},
	}

	size := fi.Size()
	want := int64(42 + 23 + 34)
	if size != want {
		t.Errorf("Incorrect size reported, got %d, want %d", size, want)
	}

	size = fi.Size() // Cached, this time
	if size != want {
		t.Errorf("Incorrect cached size reported, got %d, want %d", size, want)
	}

	fi.CachedSize = 8
	want = 8
	size = fi.Size() // Ensure it came from the cache
	if size != want {
		t.Errorf("Incorrect cached size reported, got %d, want %d", size, want)
	}

	fi.CachedSize = 0
	fi.Flags = FlagDirectory
	want = 128
	size = fi.Size() // Directories are 128 bytes large
	if size != want {
		t.Errorf("Incorrect cached size reported, got %d, want %d", size, want)
	}

	fi.CachedSize = 0
	fi.Flags = FlagDeleted
	want = 128
	size = fi.Size() // Also deleted files
	if size != want {
		t.Errorf("Incorrect cached size reported, got %d, want %d", size, want)
	}
}
<|endoftext|>


<commit_before>// Copyright 2009 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.

// The os package provides a platform-independent interface to operating
// system functionality.  The design is Unix-like.
package os

import (
	"syscall"
)

func epipecheck(file *File, e int) {
	if e == syscall.EPIPE {
		file.nepipe++
		if file.nepipe >= 10 {
			Exit(syscall.EPIPE)
		}
	} else {
		file.nepipe = 0
	}
}

// Pipe returns a connected pair of Files; reads from r return bytes written to w.
// It returns the files and an Error, if any.
func Pipe() (r *File, w *File, err Error) {
	var p [2]int

	// See ../syscall/exec.go for description of lock.
	syscall.ForkLock.RLock()
	e := syscall.Pipe(p[0:])
	if iserror(e) {
		syscall.ForkLock.RUnlock()
		return nil, nil, NewSyscallError("pipe", e)
	}
	syscall.CloseOnExec(p[0])
	syscall.CloseOnExec(p[1])
	syscall.ForkLock.RUnlock()

	return NewFile(p[0], "|0"), NewFile(p[1], "|1"), nil
}

// Stat returns a FileInfo structure describing the named file and an error, if any.
// If name names a valid symbolic link, the returned FileInfo describes
// the file pointed at by the link and has fi.FollowedSymlink set to true.
// If name names an invalid symbolic link, the returned FileInfo describes
// the link itself and has fi.FollowedSymlink set to false.
func Stat(name string) (fi *FileInfo, err Error) {
	var lstat, stat syscall.Stat_t
	e := syscall.Lstat(name, &lstat)
	if iserror(e) {
		return nil, &PathError{"stat", name, Errno(e)}
	}
	statp := &lstat
	if lstat.Mode&syscall.S_IFMT == syscall.S_IFLNK {
		e := syscall.Stat(name, &stat)
		if !iserror(e) {
			statp = &stat
		}
	}
	return fileInfoFromStat(name, new(FileInfo), &lstat, statp), nil
}

// Lstat returns the FileInfo structure describing the named file and an
// error, if any.  If the file is a symbolic link, the returned FileInfo
// describes the symbolic link.  Lstat makes no attempt to follow the link.
func Lstat(name string) (fi *FileInfo, err Error) {
	var stat syscall.Stat_t
	e := syscall.Lstat(name, &stat)
	if iserror(e) {
		return nil, &PathError{"lstat", name, Errno(e)}
	}
	return fileInfoFromStat(name, new(FileInfo), &stat, &stat), nil
}

// Remove removes the named file or directory.
func Remove(name string) Error {
	// System call interface forces us to know
	// whether name is a file or directory.
	// Try both: it is cheaper on average than
	// doing a Stat plus the right one.
	e := syscall.Unlink(name)
	if !iserror(e) {
		return nil
	}
	e1 := syscall.Rmdir(name)
	if !iserror(e1) {
		return nil
	}

	// Both failed: figure out which error to return.
	// OS X and Linux differ on whether unlink(dir)
	// returns EISDIR, so can't use that.  However,
	// both agree that rmdir(file) returns ENOTDIR,
	// so we can use that to decide which error is real.
	// Rmdir might also return ENOTDIR if given a bad
	// file path, like /etc/passwd/foo, but in that case,
	// both errors will be ENOTDIR, so it's okay to
	// use the error from unlink.
	// For windows syscall.ENOTDIR is set
	// to syscall.ERROR_DIRECTORY, hopefully it should
	// do the trick.
	if e1 != syscall.ENOTDIR {
		e = e1
	}
	return &PathError{"remove", name, Errno(e)}
}

// LinkError records an error during a link or symlink or rename
// system call and the paths that caused it.
type LinkError struct {
	Op    string
	Old   string
	New   string
	Error Error
}

func (e *LinkError) String() string {
	return e.Op + " " + e.Old + " " + e.New + ": " + e.Error.String()
}

// Link creates a hard link.
func Link(oldname, newname string) Error {
	e := syscall.Link(oldname, newname)
	if iserror(e) {
		return &LinkError{"link", oldname, newname, Errno(e)}
	}
	return nil
}

// Symlink creates a symbolic link.
func Symlink(oldname, newname string) Error {
	e := syscall.Symlink(oldname, newname)
	if iserror(e) {
		return &LinkError{"symlink", oldname, newname, Errno(e)}
	}
	return nil
}

// Readlink reads the contents of a symbolic link: the destination of
// the link.  It returns the contents and an Error, if any.
func Readlink(name string) (string, Error) {
	for len := 128; ; len *= 2 {
		b := make([]byte, len)
		n, e := syscall.Readlink(name, b)
		if iserror(e) {
			return "", &PathError{"readlink", name, Errno(e)}
		}
		if n < len {
			return string(b[0:n]), nil
		}
	}
	// Silence 6g.
	return "", nil
}

// Rename renames a file.
func Rename(oldname, newname string) Error {
	e := syscall.Rename(oldname, newname)
	if iserror(e) {
		return &LinkError{"rename", oldname, newname, Errno(e)}
	}
	return nil
}

// Chmod changes the mode of the named file to mode.
// If the file is a symbolic link, it changes the mode of the link's target.
func Chmod(name string, mode uint32) Error {
	if e := syscall.Chmod(name, mode); iserror(e) {
		return &PathError{"chmod", name, Errno(e)}
	}
	return nil
}

// Chmod changes the mode of the file to mode.
func (f *File) Chmod(mode uint32) Error {
	if e := syscall.Fchmod(f.fd, mode); iserror(e) {
		return &PathError{"chmod", f.name, Errno(e)}
	}
	return nil
}

// Chown changes the numeric uid and gid of the named file.
// If the file is a symbolic link, it changes the uid and gid of the link's target.
func Chown(name string, uid, gid int) Error {
	if e := syscall.Chown(name, uid, gid); iserror(e) {
		return &PathError{"chown", name, Errno(e)}
	}
	return nil
}

// Lchown changes the numeric uid and gid of the named file.
// If the file is a symbolic link, it changes the uid and gid of the link itself.
func Lchown(name string, uid, gid int) Error {
	if e := syscall.Lchown(name, uid, gid); iserror(e) {
		return &PathError{"lchown", name, Errno(e)}
	}
	return nil
}

// Chown changes the numeric uid and gid of the named file.
func (f *File) Chown(uid, gid int) Error {
	if e := syscall.Fchown(f.fd, uid, gid); iserror(e) {
		return &PathError{"chown", f.name, Errno(e)}
	}
	return nil
}

// Truncate changes the size of the file.
// It does not change the I/O offset.
func (f *File) Truncate(size int64) Error {
	if e := syscall.Ftruncate(f.fd, size); iserror(e) {
		return &PathError{"truncate", f.name, Errno(e)}
	}
	return nil
}

// Sync commits the current contents of the file to stable storage.
// Typically, this means flushing the file system's in-memory copy
// of recently written data to disk.
func (file *File) Sync() (err Error) {
	if file == nil {
		return EINVAL
	}
	if e := syscall.Fsync(file.fd); iserror(e) {
		return NewSyscallError("fsync", e)
	}
	return nil
}

// Chtimes changes the access and modification times of the named
// file, similar to the Unix utime() or utimes() functions.
//
// The argument times are in nanoseconds, although the underlying
// filesystem may truncate or round the values to a more
// coarse time unit.
func Chtimes(name string, atime_ns int64, mtime_ns int64) Error {
	var utimes [2]syscall.Timeval
	utimes[0] = syscall.NsecToTimeval(atime_ns)
	utimes[1] = syscall.NsecToTimeval(mtime_ns)
	if e := syscall.Utimes(name, utimes[0:]); iserror(e) {
		return &PathError{"chtimes", name, Errno(e)}
	}
	return nil
}
<commit_msg>os: turn EPIPE exit into panic<commit_after>// Copyright 2009 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.

// The os package provides a platform-independent interface to operating
// system functionality.  The design is Unix-like.
package os

import (
	"syscall"
)

func epipecheck(file *File, e int) {
	if e == syscall.EPIPE {
		file.nepipe++
		if file.nepipe >= 10 {
			panic("os.File Write: caller keeps writing after too many EPIPE errors")
		}
	} else {
		file.nepipe = 0
	}
}

// Pipe returns a connected pair of Files; reads from r return bytes written to w.
// It returns the files and an Error, if any.
func Pipe() (r *File, w *File, err Error) {
	var p [2]int

	// See ../syscall/exec.go for description of lock.
	syscall.ForkLock.RLock()
	e := syscall.Pipe(p[0:])
	if iserror(e) {
		syscall.ForkLock.RUnlock()
		return nil, nil, NewSyscallError("pipe", e)
	}
	syscall.CloseOnExec(p[0])
	syscall.CloseOnExec(p[1])
	syscall.ForkLock.RUnlock()

	return NewFile(p[0], "|0"), NewFile(p[1], "|1"), nil
}

// Stat returns a FileInfo structure describing the named file and an error, if any.
// If name names a valid symbolic link, the returned FileInfo describes
// the file pointed at by the link and has fi.FollowedSymlink set to true.
// If name names an invalid symbolic link, the returned FileInfo describes
// the link itself and has fi.FollowedSymlink set to false.
func Stat(name string) (fi *FileInfo, err Error) {
	var lstat, stat syscall.Stat_t
	e := syscall.Lstat(name, &lstat)
	if iserror(e) {
		return nil, &PathError{"stat", name, Errno(e)}
	}
	statp := &lstat
	if lstat.Mode&syscall.S_IFMT == syscall.S_IFLNK {
		e := syscall.Stat(name, &stat)
		if !iserror(e) {
			statp = &stat
		}
	}
	return fileInfoFromStat(name, new(FileInfo), &lstat, statp), nil
}

// Lstat returns the FileInfo structure describing the named file and an
// error, if any.  If the file is a symbolic link, the returned FileInfo
// describes the symbolic link.  Lstat makes no attempt to follow the link.
func Lstat(name string) (fi *FileInfo, err Error) {
	var stat syscall.Stat_t
	e := syscall.Lstat(name, &stat)
	if iserror(e) {
		return nil, &PathError{"lstat", name, Errno(e)}
	}
	return fileInfoFromStat(name, new(FileInfo), &stat, &stat), nil
}

// Remove removes the named file or directory.
func Remove(name string) Error {
	// System call interface forces us to know
	// whether name is a file or directory.
	// Try both: it is cheaper on average than
	// doing a Stat plus the right one.
	e := syscall.Unlink(name)
	if !iserror(e) {
		return nil
	}
	e1 := syscall.Rmdir(name)
	if !iserror(e1) {
		return nil
	}

	// Both failed: figure out which error to return.
	// OS X and Linux differ on whether unlink(dir)
	// returns EISDIR, so can't use that.  However,
	// both agree that rmdir(file) returns ENOTDIR,
	// so we can use that to decide which error is real.
	// Rmdir might also return ENOTDIR if given a bad
	// file path, like /etc/passwd/foo, but in that case,
	// both errors will be ENOTDIR, so it's okay to
	// use the error from unlink.
	// For windows syscall.ENOTDIR is set
	// to syscall.ERROR_DIRECTORY, hopefully it should
	// do the trick.
	if e1 != syscall.ENOTDIR {
		e = e1
	}
	return &PathError{"remove", name, Errno(e)}
}

// LinkError records an error during a link or symlink or rename
// system call and the paths that caused it.
type LinkError struct {
	Op    string
	Old   string
	New   string
	Error Error
}

func (e *LinkError) String() string {
	return e.Op + " " + e.Old + " " + e.New + ": " + e.Error.String()
}

// Link creates a hard link.
func Link(oldname, newname string) Error {
	e := syscall.Link(oldname, newname)
	if iserror(e) {
		return &LinkError{"link", oldname, newname, Errno(e)}
	}
	return nil
}

// Symlink creates a symbolic link.
func Symlink(oldname, newname string) Error {
	e := syscall.Symlink(oldname, newname)
	if iserror(e) {
		return &LinkError{"symlink", oldname, newname, Errno(e)}
	}
	return nil
}

// Readlink reads the contents of a symbolic link: the destination of
// the link.  It returns the contents and an Error, if any.
func Readlink(name string) (string, Error) {
	for len := 128; ; len *= 2 {
		b := make([]byte, len)
		n, e := syscall.Readlink(name, b)
		if iserror(e) {
			return "", &PathError{"readlink", name, Errno(e)}
		}
		if n < len {
			return string(b[0:n]), nil
		}
	}
	// Silence 6g.
	return "", nil
}

// Rename renames a file.
func Rename(oldname, newname string) Error {
	e := syscall.Rename(oldname, newname)
	if iserror(e) {
		return &LinkError{"rename", oldname, newname, Errno(e)}
	}
	return nil
}

// Chmod changes the mode of the named file to mode.
// If the file is a symbolic link, it changes the mode of the link's target.
func Chmod(name string, mode uint32) Error {
	if e := syscall.Chmod(name, mode); iserror(e) {
		return &PathError{"chmod", name, Errno(e)}
	}
	return nil
}

// Chmod changes the mode of the file to mode.
func (f *File) Chmod(mode uint32) Error {
	if e := syscall.Fchmod(f.fd, mode); iserror(e) {
		return &PathError{"chmod", f.name, Errno(e)}
	}
	return nil
}

// Chown changes the numeric uid and gid of the named file.
// If the file is a symbolic link, it changes the uid and gid of the link's target.
func Chown(name string, uid, gid int) Error {
	if e := syscall.Chown(name, uid, gid); iserror(e) {
		return &PathError{"chown", name, Errno(e)}
	}
	return nil
}

// Lchown changes the numeric uid and gid of the named file.
// If the file is a symbolic link, it changes the uid and gid of the link itself.
func Lchown(name string, uid, gid int) Error {
	if e := syscall.Lchown(name, uid, gid); iserror(e) {
		return &PathError{"lchown", name, Errno(e)}
	}
	return nil
}

// Chown changes the numeric uid and gid of the named file.
func (f *File) Chown(uid, gid int) Error {
	if e := syscall.Fchown(f.fd, uid, gid); iserror(e) {
		return &PathError{"chown", f.name, Errno(e)}
	}
	return nil
}

// Truncate changes the size of the file.
// It does not change the I/O offset.
func (f *File) Truncate(size int64) Error {
	if e := syscall.Ftruncate(f.fd, size); iserror(e) {
		return &PathError{"truncate", f.name, Errno(e)}
	}
	return nil
}

// Sync commits the current contents of the file to stable storage.
// Typically, this means flushing the file system's in-memory copy
// of recently written data to disk.
func (file *File) Sync() (err Error) {
	if file == nil {
		return EINVAL
	}
	if e := syscall.Fsync(file.fd); iserror(e) {
		return NewSyscallError("fsync", e)
	}
	return nil
}

// Chtimes changes the access and modification times of the named
// file, similar to the Unix utime() or utimes() functions.
//
// The argument times are in nanoseconds, although the underlying
// filesystem may truncate or round the values to a more
// coarse time unit.
func Chtimes(name string, atime_ns int64, mtime_ns int64) Error {
	var utimes [2]syscall.Timeval
	utimes[0] = syscall.NsecToTimeval(atime_ns)
	utimes[1] = syscall.NsecToTimeval(mtime_ns)
	if e := syscall.Utimes(name, utimes[0:]); iserror(e) {
		return &PathError{"chtimes", name, Errno(e)}
	}
	return nil
}
<|endoftext|>


<commit_before>/*
Copyright 2015 The Kubernetes Authors All rights reserved.

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 pulls

import (
	"time"

	"k8s.io/contrib/mungegithub/opts"
	github_util "k8s.io/contrib/submit-queue/github"

	"github.com/golang/glog"
	"github.com/google/go-github/github"
)

// PingCIMunger looks for situations CI (Travis | Shippable) has flaked for some
// reason and we want to re-run them.  Achieves this by closing and re-opening the pr
type PingCIMunger struct{}

func init() {
	RegisterMungerOrDie(PingCIMunger{})
}

func (PingCIMunger) Name() string { return "ping-ci" }

func (PingCIMunger) MungePullRequest(client *github.Client, pr *github.PullRequest, issue *github.Issue, commits []github.RepositoryCommit, events []github.IssueEvent, opts opts.MungeOptions) {
	if !HasLabel(issue.Labels, "lgtm") {
		return
	}
	status, err := github_util.GetStatus(client, opts.Org, opts.Project, *pr.Number, []string{"Shippable", "continuous-integration/travis-ci/pr"})
	if err != nil {
		glog.Errorf("unexpected error getting status: %v", err)
		return
	}
	if status == "incomplete" {
		if opts.Dryrun {
			glog.Infof("would have pinged CI for %d", pr.Number)
			return
		}
		glog.V(2).Infof("status is incomplete, closing and re-opening")
		msg := "Continuous integration appears to have missed, closing and re-opening to trigger it"
		if _, _, err := client.Issues.CreateComment(opts.Org, opts.Project, *pr.Number, &github.IssueComment{Body: &msg}); err != nil {
			glog.Errorf("failed to create comment: %v", err)
		}
		state := "closed"
		pr.State = &state
		if _, _, err := client.PullRequests.Edit(opts.Org, opts.Project, *pr.Number, pr); err != nil {
			glog.Errorf("Failed to close pr %d: %v", *pr.Number, err)
			return
		}
		time.Sleep(5 * time.Second)
		state = "open"
		pr.State = &state
		// Try pretty hard to re-open, since it's pretty bad if we accidentally leave a PR closed
		for tries := 0; tries < 10; tries++ {
			if _, _, err := client.PullRequests.Edit(opts.Org, opts.Project, *pr.Number, pr); err == nil {
				break
			} else {
				glog.Errorf("failed to re-open pr %d: %v", *pr.Number, err)
			}
			time.Sleep(5 * time.Second)
		}
	}
}
<commit_msg>Skip CI check when a PR is not mergeable<commit_after>/*
Copyright 2015 The Kubernetes Authors All rights reserved.

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 pulls

import (
	"time"

	"k8s.io/contrib/mungegithub/opts"
	github_util "k8s.io/contrib/submit-queue/github"

	"github.com/golang/glog"
	"github.com/google/go-github/github"
)

// PingCIMunger looks for situations CI (Travis | Shippable) has flaked for some
// reason and we want to re-run them.  Achieves this by closing and re-opening the pr
type PingCIMunger struct{}

func init() {
	RegisterMungerOrDie(PingCIMunger{})
}

func (PingCIMunger) Name() string { return "ping-ci" }

func (PingCIMunger) MungePullRequest(client *github.Client, pr *github.PullRequest, issue *github.Issue, commits []github.RepositoryCommit, events []github.IssueEvent, opts opts.MungeOptions) {
	if !HasLabel(issue.Labels, "lgtm") {
		return
	}
	if pr.Mergeable == nil || !*pr.Mergeable {
		glog.Infof("skipping CI check for %d since mergeable is nil or false", *pr.Number)
		return
	}
	status, err := github_util.GetStatus(client, opts.Org, opts.Project, *pr.Number, []string{"Shippable", "continuous-integration/travis-ci/pr"})
	if err != nil {
		glog.Errorf("unexpected error getting status: %v", err)
		return
	}
	if status == "incomplete" {
		if opts.Dryrun {
			glog.Infof("would have pinged CI for %d", pr.Number)
			return
		}
		glog.V(2).Infof("status is incomplete, closing and re-opening")
		msg := "Continuous integration appears to have missed, closing and re-opening to trigger it"
		if _, _, err := client.Issues.CreateComment(opts.Org, opts.Project, *pr.Number, &github.IssueComment{Body: &msg}); err != nil {
			glog.Errorf("failed to create comment: %v", err)
		}
		state := "closed"
		pr.State = &state
		if _, _, err := client.PullRequests.Edit(opts.Org, opts.Project, *pr.Number, pr); err != nil {
			glog.Errorf("Failed to close pr %d: %v", *pr.Number, err)
			return
		}
		time.Sleep(5 * time.Second)
		state = "open"
		pr.State = &state
		// Try pretty hard to re-open, since it's pretty bad if we accidentally leave a PR closed
		for tries := 0; tries < 10; tries++ {
			if _, _, err := client.PullRequests.Edit(opts.Org, opts.Project, *pr.Number, pr); err == nil {
				break
			} else {
				glog.Errorf("failed to re-open pr %d: %v", *pr.Number, err)
			}
			time.Sleep(5 * time.Second)
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package service

import (
	"fmt"
	"os"
	"path/filepath"

	"github.com/juju/cmd"
	"github.com/juju/errors"
	"github.com/juju/names"
	"gopkg.in/juju/charm.v6-unstable"
	charmresource "gopkg.in/juju/charm.v6-unstable/resource"
	"gopkg.in/juju/charmrepo.v2-unstable"
	"launchpad.net/gnuflag"

	"github.com/juju/juju/api"
	apiservice "github.com/juju/juju/api/service"
	"github.com/juju/juju/cmd/juju/block"
	"github.com/juju/juju/cmd/modelcmd"
	"github.com/juju/juju/resource"
	"github.com/juju/juju/resource/resourceadapters"
)

// NewUpgradeCharmCommand returns a command which upgrades service's charm.
func NewUpgradeCharmCommand() cmd.Command {
	return modelcmd.Wrap(&upgradeCharmCommand{})
}

// UpgradeCharm is responsible for upgrading a service's charm.
type upgradeCharmCommand struct {
	modelcmd.ModelCommandBase
	ServiceName string
	ForceUnits  bool
	ForceSeries bool
	RepoPath    string // defaults to JUJU_REPOSITORY
	SwitchURL   string
	CharmPath   string
	Revision    int // defaults to -1 (latest)
	// Resources is a map of resource name to filename to be uploaded on upgrade.
	Resources map[string]string
}

const upgradeCharmDoc = `
When no flags are set, the service's charm will be upgraded to the latest
revision available in the repository from which it was originally deployed. An
explicit revision can be chosen with the --revision flag.

If the charm was not originally deployed from a repository, but from a path,
then a path will need to be supplied to allow an updated copy of the charm
to be located.

If the charm came from a local repository, its path will be assumed to be
$JUJU_REPOSITORY unless overridden by --repository. Note that deploying from
a local repository is deprecated in favour of deploying from a path.

Deploying from a path or local repository is intended to suit the workflow of a charm
author working on a single client machine; use of this deployment method from
multiple clients is not supported and may lead to confusing behaviour. Each
local charm gets uploaded with the revision specified in the charm, if possible,
otherwise it gets a unique revision (highest in state + 1).

When deploying from a path, the --path flag is used to specify the location from
which to load the updated charm. Note that the directory containing the charm must
match what was originally used to deploy the charm as a superficial check that the
updated charm is compatible.

Resources may be uploaded at upgrade time by specifying the --resource flag.
Following the resource flag should be name=filepath pair.  This flag may be
repeated more than once to upload more than one resource.

  juju upgrade-charm foo --resource bar=/some/file.tgz --resource baz=./docs/cfg.xml

Where bar and baz are resources named in the metadata for the foo charm.

If the new version of a charm does not explicitly support the service's series, the
upgrade is disallowed unless the --force-series flag is used. This option should be
used with caution since using a charm on a machine running an unsupported series may
cause unexpected behavior.

When using a local repository, the --switch flag allows you to replace the charm
with an entirely different one. The new charm's URL and revision are inferred as
they would be when running a deploy command.

Please note that --switch is dangerous, because juju only has limited
information with which to determine compatibility; the operation will succeed,
regardless of potential havoc, so long as the following conditions hold:

- The new charm must declare all relations that the service is currently
participating in.
- All config settings shared by the old and new charms must
have the same types.

The new charm may add new relations and configuration settings.

--switch and --path are mutually exclusive.

--path and --revision are mutually exclusive. The revision of the updated charm
is determined by the contents of the charm at the specified path.

--switch and --revision are mutually exclusive. To specify a given revision
number with --switch, give it in the charm URL, for instance "cs:wordpress-5"
would specify revision number 5 of the wordpress charm.

Use of the --force-units flag is not generally recommended; units upgraded while in an
error state will not have upgrade-charm hooks executed, and may cause unexpected
behavior.
`

func (c *upgradeCharmCommand) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "upgrade-charm",
		Args:    "<service>",
		Purpose: "upgrade a service's charm",
		Doc:     upgradeCharmDoc,
	}
}

func (c *upgradeCharmCommand) SetFlags(f *gnuflag.FlagSet) {
	f.BoolVar(&c.ForceUnits, "force-units", false, "upgrade all units immediately, even if in error state")
	f.BoolVar(&c.ForceSeries, "force-series", false, "upgrade even if series of deployed services are not supported by the new charm")
	f.StringVar(&c.RepoPath, "repository", os.Getenv("JUJU_REPOSITORY"), "local charm repository path")
	f.StringVar(&c.SwitchURL, "switch", "", "crossgrade to a different charm")
	f.StringVar(&c.CharmPath, "path", "", "upgrade to a charm located at path")
	f.IntVar(&c.Revision, "revision", -1, "explicit revision of current charm")
	f.Var(stringMap{&c.Resources}, "resource", "resource to be uploaded to the controller")
}

func (c *upgradeCharmCommand) Init(args []string) error {
	switch len(args) {
	case 1:
		if !names.IsValidService(args[0]) {
			return fmt.Errorf("invalid service name %q", args[0])
		}
		c.ServiceName = args[0]
	case 0:
		return fmt.Errorf("no service specified")
	default:
		return cmd.CheckEmpty(args[1:])
	}
	if c.SwitchURL != "" && c.Revision != -1 {
		return fmt.Errorf("--switch and --revision are mutually exclusive")
	}
	if c.CharmPath != "" && c.Revision != -1 {
		return fmt.Errorf("--path and --revision are mutually exclusive")
	}
	if c.SwitchURL != "" && c.CharmPath != "" {
		return fmt.Errorf("--switch and --path are mutually exclusive")
	}
	return nil
}

func (c *upgradeCharmCommand) newServiceAPIClient() (*apiservice.Client, error) {
	root, err := c.NewAPIRoot()
	if err != nil {
		return nil, errors.Trace(err)
	}
	return apiservice.NewClient(root), nil
}

// Run connects to the specified environment and starts the charm
// upgrade process.
func (c *upgradeCharmCommand) Run(ctx *cmd.Context) error {
	client, err := c.NewAPIClient()
	if err != nil {
		return err
	}
	defer client.Close()

	serviceClient, err := c.newServiceAPIClient()
	if err != nil {
		return err
	}

	oldURL, err := serviceClient.GetCharmURL(c.ServiceName)
	if err != nil {
		return err
	}

	newRef := c.SwitchURL
	if newRef == "" {
		newRef = c.CharmPath
	}
	if c.SwitchURL == "" && c.CharmPath == "" {
		// No new URL specified, but revision might have been.
		newRef = oldURL.WithRevision(c.Revision).String()
	}

	httpClient, err := c.HTTPClient()
	if err != nil {
		return errors.Trace(err)
	}
	csClient := newCharmStoreClient(httpClient)

	addedURL, err := c.addCharm(oldURL, newRef, ctx, client, csClient)
	if err != nil {
		return block.ProcessBlockedError(err, block.BlockChange)
	}

	ids, err := c.upgradeResources(client, addedURL)
	if err != nil {
		return errors.Trace(err)
	}

	cfg := apiservice.SetCharmConfig{
		ServiceName: c.ServiceName,
		CharmUrl:    addedURL.String(),
		ForceSeries: c.ForceSeries,
		ForceUnits:  c.ForceUnits,
		ResourceIDs: ids,
	}

	return block.ProcessBlockedError(serviceClient.SetCharm(cfg), block.BlockChange)
}

// upgradeResources pushes metadata up to the server for each resource defined
// in the new charm's metadata and returns a map of resource names to pending
// IDs to include in the upgrage-charm call.
func (c *upgradeCharmCommand) upgradeResources(client *api.Client, cURL *charm.URL) (map[string]string, error) {
	// this gets the charm info that was added to the controller using addcharm.
	charmInfo, err := client.CharmInfo(cURL.String())
	if err != nil {
		return nil, err
	}
	if len(charmInfo.Meta.Resources) == 0 {
		return nil, nil
	}
	resclient, err := resourceadapters.NewAPIClient(c.NewAPIRoot)
	if err != nil {
		return nil, errors.Trace(err)
	}
	svcs, err := resclient.ListResources([]string{c.ServiceName})
	if err != nil {
		return nil, errors.Trace(err)
	}
	// ListResources guarantees a number of values returned == number of
	// services passed in.
	currentResources := svcs[0].Resources
	current := make(map[string]resource.Resource, len(currentResources))
	for _, res := range currentResources {
		current[res.Name] = res
	}

	metaRes := make(map[string]charmresource.Meta, len(charmInfo.Meta.Resources))
	for name, res := range charmInfo.Meta.Resources {
		if shouldUploadMeta(res, c.Resources, current) {
			metaRes[name] = res
		}
	}

	// Note: the validity of user-supplied resources to be uploaded will be
	// checked further down the stack.
	return handleResources(c, c.Resources, c.ServiceName, metaRes)
}

// shouldUploadMeta reports whether we should upload the metadata for the given
// resource.  This is always true for resources we're adding with the --resource
// flag. For resources we're not adding with --resource, we only upload metadata
// for charmstore resources.  Previously uploaded resources stay pinned to the
// data the user uploaded.
func shouldUploadMeta(res charmresource.Meta, uploads map[string]string, current map[string]resource.Resource) bool {
	// Always upload metadata for resources the user is uploading during
	// upgrade-charm.
	if _, ok := uploads[res.Name]; ok {
		return true
	}
	cur, ok := current[res.Name]
	if !ok {
		// If there's no information on the server, there should be.
		return true
	}
	// Never override existing resources a user has already uploaded.
	if cur.Origin == charmresource.OriginUpload {
		return false
	}
	return true
}

// addCharm interprets the new charmRef and adds the specified charm if the new charm is different
// to what's already deployed as specified by oldURL.
func (c *upgradeCharmCommand) addCharm(oldURL *charm.URL, charmRef string, ctx *cmd.Context,
	client *api.Client, csClient *csClient,
) (*charm.URL, error) {
	// Charm may have been supplied via a path reference.
	ch, newURL, err := charmrepo.NewCharmAtPathForceSeries(charmRef, oldURL.Series, c.ForceSeries)
	if err == nil {
		_, newName := filepath.Split(charmRef)
		if newName != oldURL.Name {
			return nil, fmt.Errorf("cannot upgrade %q to %q", oldURL.Name, newName)
		}
		return client.AddLocalCharm(newURL, ch)
	}
	if _, ok := err.(*charmrepo.NotFoundError); ok {
		return nil, errors.Errorf("no charm found at %q", charmRef)
	}
	// If we get a "not exists" or invalid path error then we attempt to interpret
	// the supplied charm reference as a URL below, otherwise we return the error.
	if err != os.ErrNotExist && !charmrepo.IsInvalidPathError(err) {
		return nil, err
	}

	// Charm has been supplied as a URL so we resolve and deploy using the store.
	conf, err := getClientConfig(client)
	if err != nil {
		return nil, err
	}

	newURL, supportedSeries, repo, err := resolveCharmStoreEntityURL(resolveCharmStoreEntityParams{
		urlStr:          charmRef,
		requestedSeries: oldURL.Series,
		forceSeries:     c.ForceSeries,
		csParams:        csClient.params,
		repoPath:        ctx.AbsPath(c.RepoPath),
		conf:            conf,
	})
	if err != nil {
		return nil, errors.Trace(err)
	}
	if !c.ForceSeries && oldURL.Series != "" && newURL.Series == "" && !isSeriesSupported(oldURL.Series, supportedSeries) {
		series := []string{"no series"}
		if len(supportedSeries) > 0 {
			series = supportedSeries
		}
		return nil, errors.Errorf(
			"cannot upgrade from single series %q charm to a charm supporting %q. Use --force-series to override.",
			oldURL.Series, series,
		)
	}
	// If no explicit revision was set with either SwitchURL
	// or Revision flags, discover the latest.
	if *newURL == *oldURL {
		newRef, _ := charm.ParseURL(charmRef)
		if newRef.Revision != -1 {
			return nil, fmt.Errorf("already running specified charm %q", newURL)
		}
		if newURL.Schema == "cs" {
			// No point in trying to upgrade a charm store charm when
			// we just determined that's the latest revision
			// available.
			return nil, fmt.Errorf("already running latest charm %q", newURL)
		}
	}

	addedURL, err := addCharmFromURL(client, newURL, repo, csClient)
	if err != nil {
		return nil, err
	}
	ctx.Infof("Added charm %q to the model.", addedURL)
	return addedURL, nil
}
<commit_msg>Clean up upgradeResources() a bit.<commit_after>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package service

import (
	"fmt"
	"os"
	"path/filepath"

	"github.com/juju/cmd"
	"github.com/juju/errors"
	"github.com/juju/names"
	"gopkg.in/juju/charm.v6-unstable"
	charmresource "gopkg.in/juju/charm.v6-unstable/resource"
	"gopkg.in/juju/charmrepo.v2-unstable"
	"launchpad.net/gnuflag"

	"github.com/juju/juju/api"
	apiservice "github.com/juju/juju/api/service"
	"github.com/juju/juju/cmd/juju/block"
	"github.com/juju/juju/cmd/modelcmd"
	"github.com/juju/juju/resource"
	"github.com/juju/juju/resource/resourceadapters"
)

// NewUpgradeCharmCommand returns a command which upgrades service's charm.
func NewUpgradeCharmCommand() cmd.Command {
	return modelcmd.Wrap(&upgradeCharmCommand{})
}

// UpgradeCharm is responsible for upgrading a service's charm.
type upgradeCharmCommand struct {
	modelcmd.ModelCommandBase
	ServiceName string
	ForceUnits  bool
	ForceSeries bool
	RepoPath    string // defaults to JUJU_REPOSITORY
	SwitchURL   string
	CharmPath   string
	Revision    int // defaults to -1 (latest)
	// Resources is a map of resource name to filename to be uploaded on upgrade.
	Resources map[string]string
}

const upgradeCharmDoc = `
When no flags are set, the service's charm will be upgraded to the latest
revision available in the repository from which it was originally deployed. An
explicit revision can be chosen with the --revision flag.

If the charm was not originally deployed from a repository, but from a path,
then a path will need to be supplied to allow an updated copy of the charm
to be located.

If the charm came from a local repository, its path will be assumed to be
$JUJU_REPOSITORY unless overridden by --repository. Note that deploying from
a local repository is deprecated in favour of deploying from a path.

Deploying from a path or local repository is intended to suit the workflow of a charm
author working on a single client machine; use of this deployment method from
multiple clients is not supported and may lead to confusing behaviour. Each
local charm gets uploaded with the revision specified in the charm, if possible,
otherwise it gets a unique revision (highest in state + 1).

When deploying from a path, the --path flag is used to specify the location from
which to load the updated charm. Note that the directory containing the charm must
match what was originally used to deploy the charm as a superficial check that the
updated charm is compatible.

Resources may be uploaded at upgrade time by specifying the --resource flag.
Following the resource flag should be name=filepath pair.  This flag may be
repeated more than once to upload more than one resource.

  juju upgrade-charm foo --resource bar=/some/file.tgz --resource baz=./docs/cfg.xml

Where bar and baz are resources named in the metadata for the foo charm.

If the new version of a charm does not explicitly support the service's series, the
upgrade is disallowed unless the --force-series flag is used. This option should be
used with caution since using a charm on a machine running an unsupported series may
cause unexpected behavior.

When using a local repository, the --switch flag allows you to replace the charm
with an entirely different one. The new charm's URL and revision are inferred as
they would be when running a deploy command.

Please note that --switch is dangerous, because juju only has limited
information with which to determine compatibility; the operation will succeed,
regardless of potential havoc, so long as the following conditions hold:

- The new charm must declare all relations that the service is currently
participating in.
- All config settings shared by the old and new charms must
have the same types.

The new charm may add new relations and configuration settings.

--switch and --path are mutually exclusive.

--path and --revision are mutually exclusive. The revision of the updated charm
is determined by the contents of the charm at the specified path.

--switch and --revision are mutually exclusive. To specify a given revision
number with --switch, give it in the charm URL, for instance "cs:wordpress-5"
would specify revision number 5 of the wordpress charm.

Use of the --force-units flag is not generally recommended; units upgraded while in an
error state will not have upgrade-charm hooks executed, and may cause unexpected
behavior.
`

func (c *upgradeCharmCommand) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "upgrade-charm",
		Args:    "<service>",
		Purpose: "upgrade a service's charm",
		Doc:     upgradeCharmDoc,
	}
}

func (c *upgradeCharmCommand) SetFlags(f *gnuflag.FlagSet) {
	f.BoolVar(&c.ForceUnits, "force-units", false, "upgrade all units immediately, even if in error state")
	f.BoolVar(&c.ForceSeries, "force-series", false, "upgrade even if series of deployed services are not supported by the new charm")
	f.StringVar(&c.RepoPath, "repository", os.Getenv("JUJU_REPOSITORY"), "local charm repository path")
	f.StringVar(&c.SwitchURL, "switch", "", "crossgrade to a different charm")
	f.StringVar(&c.CharmPath, "path", "", "upgrade to a charm located at path")
	f.IntVar(&c.Revision, "revision", -1, "explicit revision of current charm")
	f.Var(stringMap{&c.Resources}, "resource", "resource to be uploaded to the controller")
}

func (c *upgradeCharmCommand) Init(args []string) error {
	switch len(args) {
	case 1:
		if !names.IsValidService(args[0]) {
			return fmt.Errorf("invalid service name %q", args[0])
		}
		c.ServiceName = args[0]
	case 0:
		return fmt.Errorf("no service specified")
	default:
		return cmd.CheckEmpty(args[1:])
	}
	if c.SwitchURL != "" && c.Revision != -1 {
		return fmt.Errorf("--switch and --revision are mutually exclusive")
	}
	if c.CharmPath != "" && c.Revision != -1 {
		return fmt.Errorf("--path and --revision are mutually exclusive")
	}
	if c.SwitchURL != "" && c.CharmPath != "" {
		return fmt.Errorf("--switch and --path are mutually exclusive")
	}
	return nil
}

func (c *upgradeCharmCommand) newServiceAPIClient() (*apiservice.Client, error) {
	root, err := c.NewAPIRoot()
	if err != nil {
		return nil, errors.Trace(err)
	}
	return apiservice.NewClient(root), nil
}

// Run connects to the specified environment and starts the charm
// upgrade process.
func (c *upgradeCharmCommand) Run(ctx *cmd.Context) error {
	client, err := c.NewAPIClient()
	if err != nil {
		return err
	}
	defer client.Close()

	serviceClient, err := c.newServiceAPIClient()
	if err != nil {
		return err
	}

	oldURL, err := serviceClient.GetCharmURL(c.ServiceName)
	if err != nil {
		return err
	}

	newRef := c.SwitchURL
	if newRef == "" {
		newRef = c.CharmPath
	}
	if c.SwitchURL == "" && c.CharmPath == "" {
		// No new URL specified, but revision might have been.
		newRef = oldURL.WithRevision(c.Revision).String()
	}

	httpClient, err := c.HTTPClient()
	if err != nil {
		return errors.Trace(err)
	}
	csClient := newCharmStoreClient(httpClient)

	addedURL, err := c.addCharm(oldURL, newRef, ctx, client, csClient)
	if err != nil {
		return block.ProcessBlockedError(err, block.BlockChange)
	}

	ids, err := c.upgradeResources(client, addedURL)
	if err != nil {
		return errors.Trace(err)
	}

	cfg := apiservice.SetCharmConfig{
		ServiceName: c.ServiceName,
		CharmUrl:    addedURL.String(),
		ForceSeries: c.ForceSeries,
		ForceUnits:  c.ForceUnits,
		ResourceIDs: ids,
	}

	return block.ProcessBlockedError(serviceClient.SetCharm(cfg), block.BlockChange)
}

// upgradeResources pushes metadata up to the server for each resource defined
// in the new charm's metadata and returns a map of resource names to pending
// IDs to include in the upgrage-charm call.
func (c *upgradeCharmCommand) upgradeResources(client *api.Client, cURL *charm.URL) (map[string]string, error) {
	meta, err := getMetaResources(cURL, client)
	if err != nil {
		return nil, errors.Trace(err)
	}
	if len(meta) == 0 {
		return nil, nil
	}

	current, err := getResources(c.ServiceName, c.NewAPIRoot)
	if err != nil {
		return nil, errors.Trace(err)
	}
	filtered := filterResources(meta, current, c.Resources)

	// Note: the validity of user-supplied resources to be uploaded will be
	// checked further down the stack.
	return handleResources(c, c.Resources, c.ServiceName, filtered)
}

// TODO(ericsnow) Move these helpers into handleResources()?

func getMetaResources(cURL *charm.URL, client *api.Client) (map[string]charmresource.Meta, error) {
	// this gets the charm info that was added to the controller using addcharm.
	charmInfo, err := client.CharmInfo(cURL.String())
	if err != nil {
		return nil, errors.Trace(err)
	}
	return charmInfo.Meta.Resources, nil
}

func getResources(serviceID string, newAPIRoot func() (api.Connection, error)) (map[string]resource.Resource, error) {
	resclient, err := resourceadapters.NewAPIClient(newAPIRoot)
	if err != nil {
		return nil, errors.Trace(err)
	}
	svcs, err := resclient.ListResources([]string{serviceID})
	if err != nil {
		return nil, errors.Trace(err)
	}
	// ListResources guarantees a number of values returned == number of
	// services passed in.
	return svcs[0].AsMap(), nil
}

// TODO(ericsnow) Move filterResources() and shouldUploadMeta()
// somewhere more general under the "resource" package?

func filterResources(meta map[string]charmresource.Meta, current map[string]resource.Resource, uploads map[string]string) map[string]charmresource.Meta {
	filtered := make(map[string]charmresource.Meta)
	for name, res := range meta {
		if shouldUpgradeResource(res, uploads, current) {
			filtered[name] = res
		}
	}
	return filtered
}

// shouldUpgradeResource reports whether we should upload the metadata for the given
// resource.  This is always true for resources we're adding with the --resource
// flag. For resources we're not adding with --resource, we only upload metadata
// for charmstore resources.  Previously uploaded resources stay pinned to the
// data the user uploaded.
func shouldUpgradeResource(res charmresource.Meta, uploads map[string]string, current map[string]resource.Resource) bool {
	// Always upload metadata for resources the user is uploading during
	// upgrade-charm.
	if _, ok := uploads[res.Name]; ok {
		return true
	}
	cur, ok := current[res.Name]
	if !ok {
		// If there's no information on the server, there should be.
		return true
	}
	// Never override existing resources a user has already uploaded.
	if cur.Origin == charmresource.OriginUpload {
		return false
	}
	return true
}

// addCharm interprets the new charmRef and adds the specified charm if the new charm is different
// to what's already deployed as specified by oldURL.
func (c *upgradeCharmCommand) addCharm(oldURL *charm.URL, charmRef string, ctx *cmd.Context,
	client *api.Client, csClient *csClient,
) (*charm.URL, error) {
	// Charm may have been supplied via a path reference.
	ch, newURL, err := charmrepo.NewCharmAtPathForceSeries(charmRef, oldURL.Series, c.ForceSeries)
	if err == nil {
		_, newName := filepath.Split(charmRef)
		if newName != oldURL.Name {
			return nil, fmt.Errorf("cannot upgrade %q to %q", oldURL.Name, newName)
		}
		return client.AddLocalCharm(newURL, ch)
	}
	if _, ok := err.(*charmrepo.NotFoundError); ok {
		return nil, errors.Errorf("no charm found at %q", charmRef)
	}
	// If we get a "not exists" or invalid path error then we attempt to interpret
	// the supplied charm reference as a URL below, otherwise we return the error.
	if err != os.ErrNotExist && !charmrepo.IsInvalidPathError(err) {
		return nil, err
	}

	// Charm has been supplied as a URL so we resolve and deploy using the store.
	conf, err := getClientConfig(client)
	if err != nil {
		return nil, err
	}

	newURL, supportedSeries, repo, err := resolveCharmStoreEntityURL(resolveCharmStoreEntityParams{
		urlStr:          charmRef,
		requestedSeries: oldURL.Series,
		forceSeries:     c.ForceSeries,
		csParams:        csClient.params,
		repoPath:        ctx.AbsPath(c.RepoPath),
		conf:            conf,
	})
	if err != nil {
		return nil, errors.Trace(err)
	}
	if !c.ForceSeries && oldURL.Series != "" && newURL.Series == "" && !isSeriesSupported(oldURL.Series, supportedSeries) {
		series := []string{"no series"}
		if len(supportedSeries) > 0 {
			series = supportedSeries
		}
		return nil, errors.Errorf(
			"cannot upgrade from single series %q charm to a charm supporting %q. Use --force-series to override.",
			oldURL.Series, series,
		)
	}
	// If no explicit revision was set with either SwitchURL
	// or Revision flags, discover the latest.
	if *newURL == *oldURL {
		newRef, _ := charm.ParseURL(charmRef)
		if newRef.Revision != -1 {
			return nil, fmt.Errorf("already running specified charm %q", newURL)
		}
		if newURL.Schema == "cs" {
			// No point in trying to upgrade a charm store charm when
			// we just determined that's the latest revision
			// available.
			return nil, fmt.Errorf("already running latest charm %q", newURL)
		}
	}

	addedURL, err := addCharmFromURL(client, newURL, repo, csClient)
	if err != nil {
		return nil, err
	}
	ctx.Infof("Added charm %q to the model.", addedURL)
	return addedURL, nil
}
<|endoftext|>


<commit_before>package prologue_analysis

import (
	"bytes"
	"github.com/Sirupsen/logrus"
	AS "github.com/williballenthin/Lancelot/address_space"
	W "github.com/williballenthin/Lancelot/workspace"
)

func check(e error) {
	if e != nil {
		panic(e)
	}
}

type PrologueAnalysis struct {
	// referenced:
	ws *W.Workspace

	// owned:
}

func New(ws *W.Workspace) (*PrologueAnalysis, error) {
	return &PrologueAnalysis{
		ws: ws,
	}, nil
}

func (a *PrologueAnalysis) Close() error {
	return nil
}

// findAll locates all instances of the given separator in
//  the given byteslice and returns the RVAs relative to the
//  start of the slice.
func findAll(d []byte, sep []byte) ([]AS.RVA, error) {
	var offset uint64
	ret := make([]AS.RVA, 0, 100)
	for {
		i := bytes.Index(d, sep)
		if i == -1 {
			break
		}

		ret = append(ret, AS.RVA(uint64(i)+offset))

		if i+len(sep) > len(d) {
			break
		}
		d = d[i+len(sep):]
		offset += uint64(i + len(sep))
	}
	return ret, nil
}

// findPrologues locates all instances of common x86 function
//   prologues in the given byteslice.
func findPrologues(d []byte) ([]AS.RVA, error) {
	ret := make([]AS.RVA, 0, 100)
	bare := make(map[AS.RVA]bool)

	// first, find prologues with hotpatch region
	hits, e := findAll(d, []byte{0x8B, 0xFF, 0x55, 0x8B, 0xEC}) // mov edi, edi; push ebp; mov ebp, esp
	check(e)

	// index the "bare" prologue start for future overlap query
	ret = append(ret, hits...)
	for _, hit := range hits {
		bare[AS.RVA(uint64(hit)+0x2)] = true
	}

	// now, find prologues without hotpatch region
	hits, e = findAll(d, []byte{0x55, 0x8B, 0xEC}) // push ebp; mov ebp, esp
	check(e)

	// and ensure they don't overlap with the hotpatchable prologues
	for _, hit := range hits {
		if _, ok := bare[hit]; ok {
			continue
		}
		ret = append(ret, hit)
	}

	return ret, nil
}

/** PrologueAnalysis implements FileAnalysis interface **/
func (a *PrologueAnalysis) AnalyzeAll() error {

	// search for prologues in each memory region, queue them
	//  up as functions to analyze
	mmaps, e := a.ws.GetMaps()
	check(e)
	for _, mmap := range mmaps {
		d, e := a.ws.MemRead(mmap.Address, mmap.Length)
		check(e)

		fns, e := findPrologues(d)
		check(e)

		for _, fn := range fns {
			fva := fn.VA(mmap.Address)
			a.ws.MakeFunction(fva)
			logrus.Debug("function prologue analysis: found function: %s", fva)
		}
	}
	return nil
}

func (a *PrologueAnalysis) Priority() uint {
	return 75
}
<commit_msg>prologue analysis: fix debug formatting<commit_after>package prologue_analysis

import (
	"bytes"
	"github.com/Sirupsen/logrus"
	AS "github.com/williballenthin/Lancelot/address_space"
	W "github.com/williballenthin/Lancelot/workspace"
)

func check(e error) {
	if e != nil {
		panic(e)
	}
}

type PrologueAnalysis struct {
	// referenced:
	ws *W.Workspace

	// owned:
}

func New(ws *W.Workspace) (*PrologueAnalysis, error) {
	return &PrologueAnalysis{
		ws: ws,
	}, nil
}

func (a *PrologueAnalysis) Close() error {
	return nil
}

// findAll locates all instances of the given separator in
//  the given byteslice and returns the RVAs relative to the
//  start of the slice.
func findAll(d []byte, sep []byte) ([]AS.RVA, error) {
	var offset uint64
	ret := make([]AS.RVA, 0, 100)
	for {
		i := bytes.Index(d, sep)
		if i == -1 {
			break
		}

		ret = append(ret, AS.RVA(uint64(i)+offset))

		if i+len(sep) > len(d) {
			break
		}
		d = d[i+len(sep):]
		offset += uint64(i + len(sep))
	}
	return ret, nil
}

// findPrologues locates all instances of common x86 function
//   prologues in the given byteslice.
func findPrologues(d []byte) ([]AS.RVA, error) {
	ret := make([]AS.RVA, 0, 100)
	bare := make(map[AS.RVA]bool)

	// first, find prologues with hotpatch region
	hits, e := findAll(d, []byte{0x8B, 0xFF, 0x55, 0x8B, 0xEC}) // mov edi, edi; push ebp; mov ebp, esp
	check(e)

	// index the "bare" prologue start for future overlap query
	ret = append(ret, hits...)
	for _, hit := range hits {
		bare[AS.RVA(uint64(hit)+0x2)] = true
	}

	// now, find prologues without hotpatch region
	hits, e = findAll(d, []byte{0x55, 0x8B, 0xEC}) // push ebp; mov ebp, esp
	check(e)

	// and ensure they don't overlap with the hotpatchable prologues
	for _, hit := range hits {
		if _, ok := bare[hit]; ok {
			continue
		}
		ret = append(ret, hit)
	}

	return ret, nil
}

/** PrologueAnalysis implements FileAnalysis interface **/
func (a *PrologueAnalysis) AnalyzeAll() error {

	// search for prologues in each memory region, queue them
	//  up as functions to analyze
	mmaps, e := a.ws.GetMaps()
	check(e)
	for _, mmap := range mmaps {
		d, e := a.ws.MemRead(mmap.Address, mmap.Length)
		check(e)

		fns, e := findPrologues(d)
		check(e)

		for _, fn := range fns {
			fva := fn.VA(mmap.Address)
			a.ws.MakeFunction(fva)
			logrus.Debugf("function prologue analysis: found function: %s", fva)
		}
	}
	return nil
}

func (a *PrologueAnalysis) Priority() uint {
	return 75
}
<|endoftext|>