<commit_before>package main

import (
	neural "./neural"
	problems "./problems"
	"fmt"
	sdl "github.com/veandco/go-sdl2/sdl"
	"math"
	"runtime"
	"sort"
	"time"
)

type FBird struct {
	bird  *problems.Bird
	brain *neural.Net
	bestX float64
	dead  bool
}

type Flock []FBird

func (birds Flock) Len() int {
	return len(birds)
}

func (birds Flock) Less(c, r int) bool {
	return birds[c].bestX > birds[r].bestX
}

func (birds Flock) Swap(c, r int) {
	birds[c], birds[r] = birds[r], birds[c]
}

// will check if going from pos to next will collide
func checkFlock(birds Flock, lvl *problems.Level) {

	collide := func(aX, bX, cX float64) bool {
		// c.X == d.X
		return aX-1 <= cX && bX+1 >= cX
	}

	hSize := float64(problems.PylonHole / 2)

	for c := range birds {
		if birds[c].bird.Pos.Y >= lvl.GetSize().Y || birds[c].bird.Pos.Y < 1 {
			// hit ceeling or floor
			birds[c].dead = true
			continue
		}

		pylon := lvl.ClosestPylon(&birds[c].bird.Pos)
		if birds[c].bird.Pos.Y >= pylon.Y-hSize && birds[c].bird.Pos.Y <= pylon.Y+hSize {
			// in the pylon hole
			continue
		}

		if birds[c].bird.Pos.Y > pylon.Y {
			// bottom pylon segment
			birds[c].dead = collide(birds[c].bird.Pos.X, birds[c].bird.NextPos.X, pylon.X)
		} else {
			// top pylon segment
			birds[c].dead = collide(birds[c].bird.Pos.X, birds[c].bird.NextPos.X, pylon.X)
		}
	}

}

func thnikFlock(birds Flock, lvl *problems.Level) {
	wg := make(chan struct{}, len(birds))

	thinkBird := func(c int) {
		next := lvl.FirstPylonAfter(&birds[c].bird.Pos)
		diffY := next.Y - birds[c].bird.Pos.Y
		diffX := next.X - birds[c].bird.Pos.X

		birds[c].brain.Stimulate(0, diffY)
		birds[c].brain.Stimulate(1, diffX)
		birds[c].brain.Stimulate(2, birds[c].bird.Vel.Y)

		birds[c].brain.Step()
		if birds[c].brain.ValueOf(7) > 0.75 {
			birds[c].bird.Vel.Y = -0.4
		}

		birds[c].brain.Clear()
		wg <- struct{}{}
	}

	for c := 0; c < len(birds); c++ {
		go thinkBird(c)
	}

	for c := 0; c < len(birds); c++ {
		<-wg
	}
}

func mutateFlock(birds Flock, lvl *problems.Level) {
	sort.Sort(birds)

	randNet := func() *neural.Net {
		return birds[int(neural.RandMax(float64(len(birds))))].brain
	}

	best := birds[0].brain

	for c := range birds {
		if birds[c].dead {
			birds[c].dead = false
			birds[c].bird.Pos = *lvl.NewBirdPos()
			birds[c].bird.Vel = *problems.NewVector(0.1, 0)

			birds[c].brain = neural.Cross(best, randNet())

			if neural.Chance(0.1) {
				// penalize best achievement due to mutation
				birds[c].bestX *= 0.99
				birds[c].brain.Mutate(0.33)
			}
		} else {
			birds[c].bird.Pos = birds[c].bird.NextPos
			birds[c].bestX = math.Max(birds[c].bird.Pos.X, birds[c].bestX)
		}
	}

}

func main() {
	runtime.GOMAXPROCS(runtime.NumCPU())
	doDraw := true

	W := 1500
	H := 800
	LVL_W := W * 50
	fmt.Println(W, H)
	var FPS float64 = 60.0

	lvl := problems.NewLevel(LVL_W, H)

	sdl.Init(sdl.INIT_EVERYTHING)

	window, err := sdl.CreateWindow("test", sdl.WINDOWPOS_UNDEFINED, sdl.WINDOWPOS_UNDEFINED,
		int(W), int(H), sdl.WINDOW_SHOWN)
	if err != nil {
		panic(err)
	}
	defer window.Destroy()

	surface, err := window.GetSurface()
	if err != nil {
		panic(err)
	}

	rect := sdl.Rect{0, 0, 5, 5}
	clearRect := sdl.Rect{0, 0, int32(W), int32(H)}
	surface.FillRect(&clearRect, 0xffffffff)

	bcount := 10000

	nets := make([]*neural.Net, bcount, bcount)
	for c := range nets {
		nets[c] = neural.NewNet(8)

		// diffY- to hidden
		*nets[c].Synapse(0, 3) = 0.0
		*nets[c].Synapse(0, 4) = 0.0
		*nets[c].Synapse(0, 5) = 0.0
		*nets[c].Synapse(0, 6) = 0.0

		// diffX- to hidden
		*nets[c].Synapse(1, 3) = 0.0
		*nets[c].Synapse(1, 4) = 0.0
		*nets[c].Synapse(1, 5) = 0.0
		*nets[c].Synapse(1, 6) = 0.0

		// velY - to hidden
		*nets[c].Synapse(2, 3) = 0.0
		*nets[c].Synapse(2, 4) = 0.0
		*nets[c].Synapse(2, 5) = 0.0
		*nets[c].Synapse(2, 6) = 0.0

		// hidden to output
		*nets[c].Synapse(3, 7) = 0.0
		*nets[c].Synapse(4, 7) = 0.0
		*nets[c].Synapse(5, 7) = 0.0
		*nets[c].Synapse(6, 7) = 0.0

		nets[c].Randomize()
	}

	lvl.AddBirds(bcount)
	flock := make(Flock, bcount)
	for c := 0; c < bcount; c++ {
		flock[c].bird = (*lvl.GetBirds())[c]
		flock[c].brain = nets[c]
		flock[c].bestX = 0
	}

	offset := 0
	step := 65

	frame := 0
	var frameTime float64 = 1000 / FPS
	start := time.Now()
	for {

		frame++
		thnikFlock(flock, lvl)

		visible := func(x float64) bool {
			return x >= float64(offset) && x < float64(offset+W)
		}

		toScreen := func(x float64) float64 {
			return float64(x - float64(offset))
		}

		brds := *lvl.GetBirds()
		for _, brd := range brds {
			if !visible(brd.Pos.X) {
				continue
			}

			rect.X = int32(toScreen(brd.Pos.X))
			rect.Y = int32(brd.Pos.Y)
			rect.W = 5
			rect.H = 5
			if doDraw {
				surface.FillRect(&rect, 0xffff0000)
			}
		}

		hSize := float64(problems.PylonHole) / 2.0
		for _, pylon := range lvl.GetPylons() {
			if !visible(pylon.X) {
				continue
			}

			rect.X = int32(toScreen(pylon.X))
			rect.Y = int32(0)
			rect.W = 5

			// top part
			rect.H = int32(pylon.Y - hSize)
			if doDraw {
				surface.FillRect(&rect, 0xff00ff00)
			}

			// bottom part
			rect.Y = int32(pylon.Y + hSize)
			rect.H = int32(float64(H) - (pylon.Y + hSize))
			if doDraw {
				surface.FillRect(&rect, 0xff00ff00)
			}

			rect.Y = int32(pylon.Y)
			rect.W = 3
			rect.H = 3
			if doDraw {
				surface.FillRect(&rect, 0xff0000ff)
			}
		}

		elapsed := time.Since(start)

		if doDraw && frameTime < 1000.0/FPS {
			time.Sleep(time.Millisecond * time.Duration(1000.0/FPS))
		}

		start = time.Now()

		frameTime = frameTime*0.9 + float64(elapsed.Nanoseconds())*0.1

		if frame > 60 {
			frame = 0
			fmt.Printf("fps last: %s\tfps average %f\tcompletion %f%%\n", elapsed, frameTime/1000000.0, flock[0].bestX/float64(LVL_W)*100.0)
		}

		window.UpdateSurface()
		lvl.Step(float64(elapsed.Nanoseconds()) / 1000000.0)
		checkFlock(flock, lvl)
		// lvl.DoStep()

		if doDraw {
			surface.FillRect(&clearRect, 0xffffffff)
		}

		stop := false
		for event := sdl.PollEvent(); event != nil; event = sdl.PollEvent() {
			switch t := event.(type) {
			case *sdl.QuitEvent:
				stop = true
			case *sdl.KeyDownEvent:
				switch t.Keysym.Sym {
				case sdl.K_LEFT:
					offset = int(math.Max(0, float64(offset-step)))
				case sdl.K_RIGHT:
					offset = int(math.Min(float64(LVL_W-W), float64(offset+step)))
				case sdl.K_SPACE:
					doDraw = !doDraw
					surface.FillRect(&clearRect, 0xffaaaaaa)
				}
			}
		}

		if stop {
			break
		}

		mutateFlock(flock, lvl)
	}

	sdl.Quit()
}
<commit_msg>Sleep acording to frame time<commit_after>package main

import (
	neural "./neural"
	problems "./problems"
	"fmt"
	sdl "github.com/veandco/go-sdl2/sdl"
	"math"
	"runtime"
	"sort"
	"time"
)

type FBird struct {
	bird  *problems.Bird
	brain *neural.Net
	bestX float64
	dead  bool
}

type Flock []FBird

func (birds Flock) Len() int {
	return len(birds)
}

func (birds Flock) Less(c, r int) bool {
	return birds[c].bestX > birds[r].bestX
}

func (birds Flock) Swap(c, r int) {
	birds[c], birds[r] = birds[r], birds[c]
}

// will check if going from pos to next will collide
func checkFlock(birds Flock, lvl *problems.Level) {

	collide := func(aX, bX, cX float64) bool {
		// c.X == d.X
		return aX-1 <= cX && bX+1 >= cX
	}

	hSize := float64(problems.PylonHole / 2)

	for c := range birds {
		if birds[c].bird.Pos.Y >= lvl.GetSize().Y || birds[c].bird.Pos.Y < 1 {
			// hit ceeling or floor
			birds[c].dead = true
			continue
		}

		pylon := lvl.ClosestPylon(&birds[c].bird.Pos)
		if birds[c].bird.Pos.Y >= pylon.Y-hSize && birds[c].bird.Pos.Y <= pylon.Y+hSize {
			// in the pylon hole
			continue
		}

		if birds[c].bird.Pos.Y > pylon.Y {
			// bottom pylon segment
			birds[c].dead = collide(birds[c].bird.Pos.X, birds[c].bird.NextPos.X, pylon.X)
		} else {
			// top pylon segment
			birds[c].dead = collide(birds[c].bird.Pos.X, birds[c].bird.NextPos.X, pylon.X)
		}
	}

}

func thnikFlock(birds Flock, lvl *problems.Level) {
	wg := make(chan struct{}, len(birds))

	thinkBird := func(c int) {
		next := lvl.FirstPylonAfter(&birds[c].bird.Pos)
		diffY := next.Y - birds[c].bird.Pos.Y
		diffX := next.X - birds[c].bird.Pos.X

		birds[c].brain.Stimulate(0, diffY)
		birds[c].brain.Stimulate(1, diffX)
		birds[c].brain.Stimulate(2, birds[c].bird.Vel.Y)

		birds[c].brain.Step()
		if birds[c].brain.ValueOf(7) > 0.75 {
			birds[c].bird.Vel.Y = -0.4
		}

		birds[c].brain.Clear()
		wg <- struct{}{}
	}

	for c := 0; c < len(birds); c++ {
		go thinkBird(c)
	}

	for c := 0; c < len(birds); c++ {
		<-wg
	}
}

func mutateFlock(birds Flock, lvl *problems.Level) {
	sort.Sort(birds)

	randNet := func() *neural.Net {
		return birds[int(neural.RandMax(float64(len(birds))))].brain
	}

	best := birds[0].brain

	for c := range birds {
		if birds[c].dead {
			birds[c].dead = false
			birds[c].bird.Pos = *lvl.NewBirdPos()
			birds[c].bird.Vel = *problems.NewVector(0.1, 0)

			birds[c].brain = neural.Cross(best, randNet())

			if neural.Chance(0.1) {
				// penalize best achievement due to mutation
				birds[c].bestX *= 0.99
				birds[c].brain.Mutate(0.33)
			}
		} else {
			birds[c].bird.Pos = birds[c].bird.NextPos
			birds[c].bestX = math.Max(birds[c].bird.Pos.X, birds[c].bestX)
		}
	}

}

func main() {
	runtime.GOMAXPROCS(runtime.NumCPU())
	doDraw := true

	W := 1500
	H := 800
	LVL_W := W * 50
	fmt.Println(W, H)
	var FPS float64 = 60.0

	lvl := problems.NewLevel(LVL_W, H)

	sdl.Init(sdl.INIT_EVERYTHING)

	window, err := sdl.CreateWindow("test", sdl.WINDOWPOS_UNDEFINED, sdl.WINDOWPOS_UNDEFINED,
		int(W), int(H), sdl.WINDOW_SHOWN)
	if err != nil {
		panic(err)
	}
	defer window.Destroy()

	surface, err := window.GetSurface()
	if err != nil {
		panic(err)
	}

	rect := sdl.Rect{0, 0, 5, 5}
	clearRect := sdl.Rect{0, 0, int32(W), int32(H)}
	surface.FillRect(&clearRect, 0xffffffff)

	bcount := 10000

	nets := make([]*neural.Net, bcount, bcount)
	for c := range nets {
		nets[c] = neural.NewNet(8)

		// diffY- to hidden
		*nets[c].Synapse(0, 3) = 0.0
		*nets[c].Synapse(0, 4) = 0.0
		*nets[c].Synapse(0, 5) = 0.0
		*nets[c].Synapse(0, 6) = 0.0

		// diffX- to hidden
		*nets[c].Synapse(1, 3) = 0.0
		*nets[c].Synapse(1, 4) = 0.0
		*nets[c].Synapse(1, 5) = 0.0
		*nets[c].Synapse(1, 6) = 0.0

		// velY - to hidden
		*nets[c].Synapse(2, 3) = 0.0
		*nets[c].Synapse(2, 4) = 0.0
		*nets[c].Synapse(2, 5) = 0.0
		*nets[c].Synapse(2, 6) = 0.0

		// hidden to output
		*nets[c].Synapse(3, 7) = 0.0
		*nets[c].Synapse(4, 7) = 0.0
		*nets[c].Synapse(5, 7) = 0.0
		*nets[c].Synapse(6, 7) = 0.0

		nets[c].Randomize()
	}

	lvl.AddBirds(bcount)
	flock := make(Flock, bcount)
	for c := 0; c < bcount; c++ {
		flock[c].bird = (*lvl.GetBirds())[c]
		flock[c].brain = nets[c]
		flock[c].bestX = 0
	}

	offset := 0
	step := 65

	frame := 0
	var frameTime float64 = 1000 / FPS
	start := time.Now()
	for {

		frame++
		thnikFlock(flock, lvl)

		visible := func(x float64) bool {
			return x >= float64(offset) && x < float64(offset+W)
		}

		toScreen := func(x float64) float64 {
			return float64(x - float64(offset))
		}

		brds := *lvl.GetBirds()
		for _, brd := range brds {
			if !visible(brd.Pos.X) {
				continue
			}

			rect.X = int32(toScreen(brd.Pos.X))
			rect.Y = int32(brd.Pos.Y)
			rect.W = 5
			rect.H = 5
			if doDraw {
				surface.FillRect(&rect, 0xffff0000)
			}
		}

		hSize := float64(problems.PylonHole) / 2.0
		for _, pylon := range lvl.GetPylons() {
			if !visible(pylon.X) {
				continue
			}

			rect.X = int32(toScreen(pylon.X))
			rect.Y = int32(0)
			rect.W = 5

			// top part
			rect.H = int32(pylon.Y - hSize)
			if doDraw {
				surface.FillRect(&rect, 0xff00ff00)
			}

			// bottom part
			rect.Y = int32(pylon.Y + hSize)
			rect.H = int32(float64(H) - (pylon.Y + hSize))
			if doDraw {
				surface.FillRect(&rect, 0xff00ff00)
			}

			rect.Y = int32(pylon.Y)
			rect.W = 3
			rect.H = 3
			if doDraw {
				surface.FillRect(&rect, 0xff0000ff)
			}
		}

		elapsed := time.Since(start)

		if doDraw && frameTime < 1000.0/FPS {
			time.Sleep(time.Millisecond * time.Duration(1000.0/FPS-frameTime))
		}

		start = time.Now()

		frameTime = frameTime*0.9 + float64(elapsed.Nanoseconds())*0.1

		if frame > 60 {
			frame = 0
			fmt.Printf("fps last: %s\tfps average %f\tcompletion %f%%\n", elapsed, frameTime/1000000.0, flock[0].bestX/float64(LVL_W)*100.0)
		}

		window.UpdateSurface()
		lvl.Step(float64(elapsed.Nanoseconds()) / 1000000.0)
		checkFlock(flock, lvl)

		if doDraw {
			surface.FillRect(&clearRect, 0xffffffff)
		}

		stop := false
		for event := sdl.PollEvent(); event != nil; event = sdl.PollEvent() {
			switch t := event.(type) {
			case *sdl.QuitEvent:
				stop = true
			case *sdl.KeyDownEvent:
				switch t.Keysym.Sym {
				case sdl.K_LEFT:
					offset = int(math.Max(0, float64(offset-step)))
				case sdl.K_RIGHT:
					offset = int(math.Min(float64(LVL_W-W), float64(offset+step)))
				case sdl.K_SPACE:
					doDraw = !doDraw
					surface.FillRect(&clearRect, 0xffaaaaaa)
				}
			}
		}

		if stop {
			break
		}

		mutateFlock(flock, lvl)
	}

	sdl.Quit()
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"fmt"
	"io/ioutil"
	"net/url"
	"os"
	"path/filepath"
	"strings"

	"github.com/google/go-github/github"
	"golang.org/x/oauth2"
)

type (
	Repo struct {
		Owner string
		Name  string
	}

	Build struct {
		Event string
	}

	Commit struct {
		Ref string
	}

	Config struct {
		APIKey          string
		Files           []string
		FileExists      string
		Checksum        []string
		ChecksumFile    string
		ChecksumFlatten bool
		Draft           bool
		Prerelease      bool
		BaseURL         string
		UploadURL       string
		Title           string
		Note            string
		Overwrite		bool
	}

	Plugin struct {
		Repo   Repo
		Build  Build
		Commit Commit
		Config Config
	}
)

func (p Plugin) Exec() error {
	var (
		files []string
	)

	if p.Build.Event != "tag" {
		return fmt.Errorf("The GitHub Release plugin is only available for tags")
	}

	if p.Config.APIKey == "" {
		return fmt.Errorf("You must provide an API key")
	}

	if !fileExistsValues[p.Config.FileExists] {
		return fmt.Errorf("Invalid value for file_exists")
	}

	if !strings.HasSuffix(p.Config.BaseURL, "/") {
		p.Config.BaseURL = p.Config.BaseURL + "/"
	}

	if !strings.HasSuffix(p.Config.UploadURL, "/") {
		p.Config.UploadURL = p.Config.UploadURL + "/"
	}

	var err error
	if p.Config.Note != "" {
		if p.Config.Note, err = readStringOrFile(p.Config.Note); err != nil {
			return fmt.Errorf("error while reading %s: %v", p.Config.Note, err)
		}
	}

	if p.Config.Title != "" {
		if p.Config.Title, err = readStringOrFile(p.Config.Title); err != nil {
			return fmt.Errorf("error while reading %s: %v", p.Config.Note, err)
		}
	}

	for _, glob := range p.Config.Files {
		globed, err := filepath.Glob(glob)

		if err != nil {
			return fmt.Errorf("Failed to glob %s. %s", glob, err)
		}

		if globed != nil {
			files = append(files, globed...)
		}
	}

	if len(p.Config.Checksum) > 0 {
		var (
			err error
		)

		files, err = writeChecksums(files, p.Config.Checksum, p.Config.ChecksumFile, p.Config.ChecksumFlatten)

		if err != nil {
			return fmt.Errorf("Failed to write checksums. %s", err)
		}
	}

	baseURL, err := url.Parse(p.Config.BaseURL)

	if err != nil {
		return fmt.Errorf("Failed to parse base URL. %s", err)
	}

	uploadURL, err := url.Parse(p.Config.UploadURL)

	if err != nil {
		return fmt.Errorf("Failed to parse upload URL. %s", err)
	}

	ctx := context.Background()
	ts := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: p.Config.APIKey})
	tc := oauth2.NewClient(ctx, ts)

	client := github.NewClient(tc)

	client.BaseURL = baseURL
	client.UploadURL = uploadURL

	rc := releaseClient{
		Client:     client,
		Context:    ctx,
		Owner:      p.Repo.Owner,
		Repo:       p.Repo.Name,
		Tag:        strings.TrimPrefix(p.Commit.Ref, "refs/tags/"),
		Draft:      p.Config.Draft,
		Prerelease: p.Config.Prerelease,
		FileExists: p.Config.FileExists,
		Title:      p.Config.Title,
		Note:       p.Config.Note,
		Overwrite:  p.Config.Overwrite,
	}

	release, err := rc.buildRelease()

	if err != nil {
		return fmt.Errorf("Failed to create the release. %s", err)
	}

	if err := rc.uploadFiles(*release.ID, files); err != nil {
		return fmt.Errorf("Failed to upload the files. %s", err)
	}

	return nil
}

func readStringOrFile(input string) (string, error) {
	// Check if input is a file path
	if _, err := os.Stat(input); err != nil && os.IsNotExist(err) {
		// No file found => use input as result
		return input, nil
	} else if err != nil {
		return "", err
	}
	result, err := ioutil.ReadFile(input)
	if err != nil {
		return "", err
	}
	return string(result), nil
}
<commit_msg>fix wrong indentation<commit_after>package main

import (
	"context"
	"fmt"
	"io/ioutil"
	"net/url"
	"os"
	"path/filepath"
	"strings"

	"github.com/google/go-github/github"
	"golang.org/x/oauth2"
)

type (
	Repo struct {
		Owner string
		Name  string
	}

	Build struct {
		Event string
	}

	Commit struct {
		Ref string
	}

	Config struct {
		APIKey          string
		Files           []string
		FileExists      string
		Checksum        []string
		ChecksumFile    string
		ChecksumFlatten bool
		Draft           bool
		Prerelease      bool
		BaseURL         string
		UploadURL       string
		Title           string
		Note            string
		Overwrite       bool
	}

	Plugin struct {
		Repo   Repo
		Build  Build
		Commit Commit
		Config Config
	}
)

func (p Plugin) Exec() error {
	var (
		files []string
	)

	if p.Build.Event != "tag" {
		return fmt.Errorf("The GitHub Release plugin is only available for tags")
	}

	if p.Config.APIKey == "" {
		return fmt.Errorf("You must provide an API key")
	}

	if !fileExistsValues[p.Config.FileExists] {
		return fmt.Errorf("Invalid value for file_exists")
	}

	if !strings.HasSuffix(p.Config.BaseURL, "/") {
		p.Config.BaseURL = p.Config.BaseURL + "/"
	}

	if !strings.HasSuffix(p.Config.UploadURL, "/") {
		p.Config.UploadURL = p.Config.UploadURL + "/"
	}

	var err error
	if p.Config.Note != "" {
		if p.Config.Note, err = readStringOrFile(p.Config.Note); err != nil {
			return fmt.Errorf("error while reading %s: %v", p.Config.Note, err)
		}
	}

	if p.Config.Title != "" {
		if p.Config.Title, err = readStringOrFile(p.Config.Title); err != nil {
			return fmt.Errorf("error while reading %s: %v", p.Config.Note, err)
		}
	}

	for _, glob := range p.Config.Files {
		globed, err := filepath.Glob(glob)

		if err != nil {
			return fmt.Errorf("Failed to glob %s. %s", glob, err)
		}

		if globed != nil {
			files = append(files, globed...)
		}
	}

	if len(p.Config.Checksum) > 0 {
		var (
			err error
		)

		files, err = writeChecksums(files, p.Config.Checksum, p.Config.ChecksumFile, p.Config.ChecksumFlatten)

		if err != nil {
			return fmt.Errorf("Failed to write checksums. %s", err)
		}
	}

	baseURL, err := url.Parse(p.Config.BaseURL)

	if err != nil {
		return fmt.Errorf("Failed to parse base URL. %s", err)
	}

	uploadURL, err := url.Parse(p.Config.UploadURL)

	if err != nil {
		return fmt.Errorf("Failed to parse upload URL. %s", err)
	}

	ctx := context.Background()
	ts := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: p.Config.APIKey})
	tc := oauth2.NewClient(ctx, ts)

	client := github.NewClient(tc)

	client.BaseURL = baseURL
	client.UploadURL = uploadURL

	rc := releaseClient{
		Client:     client,
		Context:    ctx,
		Owner:      p.Repo.Owner,
		Repo:       p.Repo.Name,
		Tag:        strings.TrimPrefix(p.Commit.Ref, "refs/tags/"),
		Draft:      p.Config.Draft,
		Prerelease: p.Config.Prerelease,
		FileExists: p.Config.FileExists,
		Title:      p.Config.Title,
		Note:       p.Config.Note,
		Overwrite:  p.Config.Overwrite,
	}

	release, err := rc.buildRelease()

	if err != nil {
		return fmt.Errorf("Failed to create the release. %s", err)
	}

	if err := rc.uploadFiles(*release.ID, files); err != nil {
		return fmt.Errorf("Failed to upload the files. %s", err)
	}

	return nil
}

func readStringOrFile(input string) (string, error) {
	// Check if input is a file path
	if _, err := os.Stat(input); err != nil && os.IsNotExist(err) {
		// No file found => use input as result
		return input, nil
	} else if err != nil {
		return "", err
	}
	result, err := ioutil.ReadFile(input)
	if err != nil {
		return "", err
	}
	return string(result), nil
}
<|endoftext|>


<commit_before>package learn

import (
	"fmt"
	"github.com/PuerkitoBio/goquery"
	"github.com/tsinghua-io/api-server/model"
	"github.com/tsinghua-io/api-server/util"
	"net/http"
	"net/url"
	"regexp"
	"strings"
)

func AnnouncementURL(courseId, id string) string {
	return fmt.Sprintf("%s/MultiLanguage/public/bbs/note_reply.jsp?bbs_type=课程公告&course_id=%s&id=%s", BaseURL, courseId, id)
}

func ParseAnnouncementURL(rawurl string) (courseId, id string) {
	if parsed, err := url.Parse(rawurl); err == nil {
		values := parsed.Query()
		courseId = values.Get("course_id")
		id = values.Get("id")
	}
	return
}

func AnnouncementListURL(courseId string) string {
	return fmt.Sprintf("%s/MultiLanguage/public/bbs/getnoteid_student.jsp?course_id=%s", BaseURL, courseId)
}

func (ada *Adapter) Announcement(courseId, id string) (title, body, email string, status int, errMsg error) {
	status = http.StatusOK

	url := AnnouncementURL(courseId, id)
	doc, err := ada.GetDocument(url)
	if err != nil {
		status = http.StatusBadGateway
		errMsg = err
		return
	}

	// Be careful here, as the body can contain anything.
	if content := doc.Find("#table_box>tbody>tr>td:last-child"); content.Size() != 2 {
		errMsg = fmt.Errorf("Expect 2 content blocks, got %d.", content.Size())
	} else if bodyHTML, err := content.Eq(1).Html(); err != nil {
		errMsg = fmt.Errorf("Failed to rebuild body HTML: %s", err)
	} else {
		title = strings.TrimSpace(content.Eq(0).Text())
		body = strings.TrimSpace(bodyHTML)
		if sendEmail := doc.Find("#table_box>tbody>tr:last-child>td>input[name=sendmail]"); sendEmail.Size() != 0 {
			// Try our best to parse it, but don't panic if we cannot.
			regex, _ := regexp.Compile("/MultiLanguage/public/mail/student/sendmail.jsp\\?usersToSend=(.*?)&")
			if subs := regex.FindStringSubmatch(sendEmail.AttrOr("onclick", "")); len(subs) > 1 {
				email = subs[1]
			}
		}
	}

	if errMsg != nil {
		status = http.StatusInternalServerError
		errMsg = fmt.Errorf("Failed to parse %s: %s", url, errMsg)
		return
	}

	return
}

func (ada *Adapter) AnnouncementList(courseId string) (announcements []*model.Announcement, status int, errMsg error) {
	status = http.StatusOK

	url := AnnouncementListURL(courseId)
	doc, err := ada.GetDocument(url)
	if err != nil {
		status = http.StatusBadGateway
		errMsg = err
		return
	}

	rows := doc.Find("#table_box tr~tr")
	announcements = make([]*model.Announcement, rows.Size())

	rows.EachWithBreak(func(i int, s *goquery.Selection) bool {
		if cols := s.Children(); cols.Size() != 5 {
			errMsg = fmt.Errorf("Expect 5 columns, got %d.", cols.Size())
		} else if link := cols.Eq(1).Find("a"); link.Size() == 0 {
			errMsg = fmt.Errorf("Failed to find link in column 1.")
		} else if _, id := ParseAnnouncementURL(link.AttrOr("href", "")); id == "" {
			errMsg = fmt.Errorf("Failed to find announcement id from href (%s).", link.AttrOr("href", ""))
		} else {
			texts := util.TrimmedTexts(cols)
			annc := &model.Announcement{
				Id:        id,
				CourseId:  courseId,
				Owner:     &model.User{Name: strings.TrimSuffix(texts[2], "老师")},
				CreatedAt: texts[3],
				Title:     texts[1],
			}
			if red := link.Find("font[color=red]"); red.Size() > 0 {
				annc.Priority = 1
			}

			announcements[i] = annc
		}

		return errMsg == nil
	})

	if errMsg != nil {
		status = http.StatusInternalServerError
		errMsg = fmt.Errorf("Failed to parse %s: %s", url, errMsg)
		return
	}

	return
}

func (ada *Adapter) Announcements(courseId string) (announcements []*model.Announcement, status int, errMsg error) {
	if announcements, status, errMsg = ada.AnnouncementList(courseId); errMsg != nil {
		return
	}

	sg := util.NewStatusGroup()

	for _, annc := range announcements {
		annc := annc
		sg.Go(func(status *int, err *error) {
			_, annc.Body, annc.Owner.Email, *status, *err = ada.Announcement(courseId, annc.Id)
		})
	}

	status, errMsg = sg.Wait()
	return
}
<commit_msg>Fix a wrong CSS selector.<commit_after>package learn

import (
	"fmt"
	"github.com/PuerkitoBio/goquery"
	"github.com/tsinghua-io/api-server/model"
	"github.com/tsinghua-io/api-server/util"
	"net/http"
	"net/url"
	"regexp"
	"strings"
)

func AnnouncementURL(courseId, id string) string {
	return fmt.Sprintf("%s/MultiLanguage/public/bbs/note_reply.jsp?bbs_type=课程公告&course_id=%s&id=%s", BaseURL, courseId, id)
}

func ParseAnnouncementURL(rawurl string) (courseId, id string) {
	if parsed, err := url.Parse(rawurl); err == nil {
		values := parsed.Query()
		courseId = values.Get("course_id")
		id = values.Get("id")
	}
	return
}

func AnnouncementListURL(courseId string) string {
	return fmt.Sprintf("%s/MultiLanguage/public/bbs/getnoteid_student.jsp?course_id=%s", BaseURL, courseId)
}

func (ada *Adapter) Announcement(courseId, id string) (title, body, email string, status int, errMsg error) {
	status = http.StatusOK

	url := AnnouncementURL(courseId, id)
	doc, err := ada.GetDocument(url)
	if err != nil {
		status = http.StatusBadGateway
		errMsg = err
		return
	}

	// Be careful here, as the body can contain anything.
	if content := doc.Find("#table_box>tbody>tr>td:nth-child(2)"); content.Size() != 2 {
		errMsg = fmt.Errorf("Expect 2 content blocks, got %d.", content.Size())
	} else if bodyHTML, err := content.Eq(1).Html(); err != nil {
		errMsg = fmt.Errorf("Failed to rebuild body HTML: %s", err)
	} else {
		title = strings.TrimSpace(content.Eq(0).Text())
		body = strings.TrimSpace(bodyHTML)
		if sendEmail := doc.Find("#table_box>tbody>tr:last-child>td>input[name=sendmail]"); sendEmail.Size() != 0 {
			// Try our best to parse it, but don't panic if we cannot.
			regex, _ := regexp.Compile("/MultiLanguage/public/mail/student/sendmail.jsp\\?usersToSend=(.*?)&")
			if subs := regex.FindStringSubmatch(sendEmail.AttrOr("onclick", "")); len(subs) > 1 {
				email = subs[1]
			}
		}
	}

	if errMsg != nil {
		status = http.StatusInternalServerError
		errMsg = fmt.Errorf("Failed to parse %s: %s", url, errMsg)
		return
	}

	return
}

func (ada *Adapter) AnnouncementList(courseId string) (announcements []*model.Announcement, status int, errMsg error) {
	status = http.StatusOK

	url := AnnouncementListURL(courseId)
	doc, err := ada.GetDocument(url)
	if err != nil {
		status = http.StatusBadGateway
		errMsg = err
		return
	}

	rows := doc.Find("#table_box tr~tr")
	announcements = make([]*model.Announcement, rows.Size())

	rows.EachWithBreak(func(i int, s *goquery.Selection) bool {
		if cols := s.Children(); cols.Size() != 5 {
			errMsg = fmt.Errorf("Expect 5 columns, got %d.", cols.Size())
		} else if link := cols.Eq(1).Find("a"); link.Size() == 0 {
			errMsg = fmt.Errorf("Failed to find link in column 1.")
		} else if _, id := ParseAnnouncementURL(link.AttrOr("href", "")); id == "" {
			errMsg = fmt.Errorf("Failed to find announcement id from href (%s).", link.AttrOr("href", ""))
		} else {
			texts := util.TrimmedTexts(cols)
			annc := &model.Announcement{
				Id:        id,
				CourseId:  courseId,
				Owner:     &model.User{Name: strings.TrimSuffix(texts[2], "老师")},
				CreatedAt: texts[3],
				Title:     texts[1],
			}
			if red := link.Find("font[color=red]"); red.Size() > 0 {
				annc.Priority = 1
			}

			announcements[i] = annc
		}

		return errMsg == nil
	})

	if errMsg != nil {
		status = http.StatusInternalServerError
		errMsg = fmt.Errorf("Failed to parse %s: %s", url, errMsg)
		return
	}

	return
}

func (ada *Adapter) Announcements(courseId string) (announcements []*model.Announcement, status int, errMsg error) {
	if announcements, status, errMsg = ada.AnnouncementList(courseId); errMsg != nil {
		return
	}

	sg := util.NewStatusGroup()

	for _, annc := range announcements {
		annc := annc
		sg.Go(func(status *int, err *error) {
			_, annc.Body, annc.Owner.Email, *status, *err = ada.Announcement(courseId, annc.Id)
		})
	}

	status, errMsg = sg.Wait()
	return
}
<|endoftext|>


<commit_before>package app_files_test

import (
	"io/ioutil"
	"os"
	"path/filepath"
	"runtime"
	"strings"

	"github.com/cloudfoundry/cli/cf/app_files"

	"github.com/cloudfoundry/cli/cf/models"
	"github.com/cloudfoundry/gofileutils/fileutils"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

type WalkAppFileArgs struct {
	relativePath string
	absolutePath string
}

func (a WalkAppFileArgs) RelativePath() string {
	return filepath.Join(strings.Split(a.relativePath, "/")...)
}

func (a WalkAppFileArgs) AbsolutePath() string {
	return filepath.Join(strings.Split(a.relativePath, "/")...)
}

func (a WalkAppFileArgs) Equal(other WalkAppFileArgs) bool {
	return a.RelativePath() == other.RelativePath() &&
		a.AbsolutePath() == other.AbsolutePath()
}

var _ = Describe("AppFiles", func() {
	var appFiles app_files.ApplicationFiles
	var fixturePath string

	BeforeEach(func() {
		appFiles = app_files.ApplicationFiles{}
		fixturePath = filepath.Join("..", "..", "fixtures", "applications")
	})

	Describe("AppFilesInDir", func() {
		It("all files have '/' path separators", func() {
			files, err := appFiles.AppFilesInDir(fixturePath)
			Expect(err).ShouldNot(HaveOccurred())

			for _, afile := range files {
				Expect(afile.Path).Should(Equal(filepath.ToSlash(afile.Path)))
			}
		})

		It("excludes files based on the .cfignore file", func() {
			appPath := filepath.Join(fixturePath, "app-with-cfignore")
			files, err := appFiles.AppFilesInDir(appPath)
			Expect(err).ShouldNot(HaveOccurred())

			paths := []string{}
			for _, file := range files {
				paths = append(paths, file.Path)
			}

			Expect(paths).To(Equal([]string{
				"dir1",
				"dir1/child-dir",
				"dir1/child-dir/file3.txt",
				"dir1/file1.txt",
				"dir2",

				// TODO: this should be excluded.
				// .cfignore doesn't handle ** patterns right now
				"dir2/child-dir2",
			}))
		})

		// NB: on windows, you can never rely on the size of a directory being zero
		// see: http://msdn.microsoft.com/en-us/library/windows/desktop/aa364946(v=vs.85).aspx
		// and: https://www.pivotaltracker.com/story/show/70470232
		It("always sets the size of directories to zero bytes", func() {
			fileutils.TempDir("something", func(tempdir string, err error) {
				Expect(err).ToNot(HaveOccurred())

				err = os.Mkdir(filepath.Join(tempdir, "nothing"), 0600)
				Expect(err).ToNot(HaveOccurred())

				files, err := appFiles.AppFilesInDir(tempdir)
				Expect(err).ToNot(HaveOccurred())

				sizes := []int64{}
				for _, file := range files {
					sizes = append(sizes, file.Size)
				}

				Expect(sizes).To(Equal([]int64{0}))
			})
		})
	})

	Describe("CopyFiles", func() {
		It("copies only the files specified", func() {
			copyDir := filepath.Join(fixturePath, "app-copy-test")

			filesToCopy := []models.AppFileFields{
				{Path: filepath.Join("dir1")},
				{Path: filepath.Join("dir1", "child-dir", "file2.txt")},
			}

			files := []string{}

			fileutils.TempDir("copyToDir", func(tmpDir string, err error) {
				copyErr := appFiles.CopyFiles(filesToCopy, copyDir, tmpDir)
				Expect(copyErr).ToNot(HaveOccurred())

				filepath.Walk(tmpDir, func(path string, fileInfo os.FileInfo, err error) error {
					Expect(err).ToNot(HaveOccurred())

					if !fileInfo.IsDir() {
						files = append(files, fileInfo.Name())
					}
					return nil
				})
			})

			// file2.txt is in lowest subtree, thus is walked first.
			Expect(files).To(Equal([]string{
				"file2.txt",
			}))
		})
	})

	Describe("WalkAppFiles", func() {
		var cb func(string, string) error
		var actualWalkAppFileArgs []WalkAppFileArgs

		BeforeEach(func() {
			actualWalkAppFileArgs = []WalkAppFileArgs{}
			cb = func(fileRelativePath, fullPath string) error {
				actualWalkAppFileArgs = append(actualWalkAppFileArgs, WalkAppFileArgs{
					relativePath: fileRelativePath,
					absolutePath: fullPath,
				})
				return nil
			}
		})

		It("calls the callback with the relative and absolute path for each file within the given dir", func() {
			err := appFiles.WalkAppFiles(filepath.Join(fixturePath, "app-copy-test"), cb)
			Expect(err).NotTo(HaveOccurred())
			expectedArgs := []WalkAppFileArgs{
				{
					relativePath: "dir1",
					absolutePath: "../../fixtures/applications/app-copy-test/dir1",
				},
				{
					relativePath: "dir1/child-dir",
					absolutePath: "../../fixtures/applications/app-copy-test/dir1/child-dir",
				},
				{
					relativePath: "dir1/child-dir/file2.txt",
					absolutePath: "../../fixtures/applications/app-copy-test/dir1/child-dir/file2.txt",
				},
				{
					relativePath: "dir1/child-dir/file3.txt",
					absolutePath: "../../fixtures/applications/app-copy-test/dir1/child-dir/file3.txt",
				},
				{
					relativePath: "dir1/file1.txt",
					absolutePath: "../../fixtures/applications/app-copy-test/dir1/file1.txt",
				},
				{
					relativePath: "dir2",
					absolutePath: "../../fixtures/applications/app-copy-test/dir2",
				},
				{
					relativePath: "dir2/child-dir2",
					absolutePath: "../../fixtures/applications/app-copy-test/dir2/child-dir2",
				},
				{
					relativePath: "dir2/child-dir2/grandchild-dir2",
					absolutePath: "../../fixtures/applications/app-copy-test/dir2/child-dir2/grandchild-dir2",
				},
				{
					relativePath: "dir2/child-dir2/grandchild-dir2/file4.txt",
					absolutePath: "../../fixtures/applications/app-copy-test/dir2/child-dir2/grandchild-dir2/file4.txt",
				},
			}

			for i, actual := range actualWalkAppFileArgs {
				Expect(actual.Equal(expectedArgs[i])).To(BeTrue())
			}
		})

		Context("when the given dir contains an untraversable dir", func() {
			var nonTraversableDirPath string

			BeforeEach(func() {
				if runtime.GOOS == "windows" {
					Skip("This test is only for non-Windows platforms")
				}

				nonTraversableDirPath = filepath.Join(fixturePath, "app-copy-test", "non-traversable-dir")

				err := os.Mkdir(nonTraversableDirPath, os.ModeDir) // non-traversable without os.ModePerm
				Expect(err).NotTo(HaveOccurred())
			})

			AfterEach(func() {
				err := os.Remove(nonTraversableDirPath)
				Expect(err).NotTo(HaveOccurred())
			})

			It("returns an error", func() {
				err := appFiles.WalkAppFiles(filepath.Join(fixturePath, "app-copy-test"), cb)
				Expect(err).To(HaveOccurred())
			})

			Context("when the untraversable dir is .cfignored", func() {
				var cfIgnorePath string

				BeforeEach(func() {
					cfIgnorePath = filepath.Join(fixturePath, "app-copy-test", ".cfignore")
					err := ioutil.WriteFile(cfIgnorePath, []byte("non-traversable-dir\n"), os.ModePerm)
					Expect(err).NotTo(HaveOccurred())
				})

				AfterEach(func() {
					err := os.Remove(cfIgnorePath)
					Expect(err).NotTo(HaveOccurred())
				})

				It("does not return an error", func() {
					err := appFiles.WalkAppFiles(filepath.Join(fixturePath, "app-copy-test"), cb)
					Expect(err).NotTo(HaveOccurred())
				})

				It("does not call the callback with the untraversable dir", func() {
					appFiles.WalkAppFiles(filepath.Join(fixturePath, "app-copy-test"), cb)
					expectedArgs := []WalkAppFileArgs{
						{
							relativePath: "dir1",
							absolutePath: "../../fixtures/applications/app-copy-test/dir1",
						},
						{
							relativePath: "dir1/child-dir",
							absolutePath: "../../fixtures/applications/app-copy-test/dir1/child-dir",
						},
						{
							relativePath: "dir1/child-dir/file2.txt",
							absolutePath: "../../fixtures/applications/app-copy-test/dir1/child-dir/file2.txt",
						},
						{
							relativePath: "dir1/child-dir/file3.txt",
							absolutePath: "../../fixtures/applications/app-copy-test/dir1/child-dir/file3.txt",
						},
						{
							relativePath: "dir1/file1.txt",
							absolutePath: "../../fixtures/applications/app-copy-test/dir1/file1.txt",
						},
						{
							relativePath: "dir2",
							absolutePath: "../../fixtures/applications/app-copy-test/dir2",
						},
						{
							relativePath: "dir2/child-dir2",
							absolutePath: "../../fixtures/applications/app-copy-test/dir2/child-dir2",
						},
						{
							relativePath: "dir2/child-dir2/grandchild-dir2",
							absolutePath: "../../fixtures/applications/app-copy-test/dir2/child-dir2/grandchild-dir2",
						},
						{
							relativePath: "dir2/child-dir2/grandchild-dir2/file4.txt",
							absolutePath: "../../fixtures/applications/app-copy-test/dir2/child-dir2/grandchild-dir2/file4.txt",
						},
					}

					for i, actual := range actualWalkAppFileArgs {
						Expect(actual.Equal(expectedArgs[i])).To(BeTrue())
					}
				})
			})
		})
	})
})
<commit_msg>Skip WalkAppFiles test that expects an error<commit_after>package app_files_test

import (
	"io/ioutil"
	"os"
	"path/filepath"
	"runtime"
	"strings"

	"github.com/cloudfoundry/cli/cf/app_files"

	"github.com/cloudfoundry/cli/cf/models"
	"github.com/cloudfoundry/gofileutils/fileutils"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

type WalkAppFileArgs struct {
	relativePath string
	absolutePath string
}

func (a WalkAppFileArgs) RelativePath() string {
	return filepath.Join(strings.Split(a.relativePath, "/")...)
}

func (a WalkAppFileArgs) AbsolutePath() string {
	return filepath.Join(strings.Split(a.relativePath, "/")...)
}

func (a WalkAppFileArgs) Equal(other WalkAppFileArgs) bool {
	return a.RelativePath() == other.RelativePath() &&
		a.AbsolutePath() == other.AbsolutePath()
}

var _ = Describe("AppFiles", func() {
	var appFiles app_files.ApplicationFiles
	var fixturePath string

	BeforeEach(func() {
		appFiles = app_files.ApplicationFiles{}
		fixturePath = filepath.Join("..", "..", "fixtures", "applications")
	})

	Describe("AppFilesInDir", func() {
		It("all files have '/' path separators", func() {
			files, err := appFiles.AppFilesInDir(fixturePath)
			Expect(err).ShouldNot(HaveOccurred())

			for _, afile := range files {
				Expect(afile.Path).Should(Equal(filepath.ToSlash(afile.Path)))
			}
		})

		It("excludes files based on the .cfignore file", func() {
			appPath := filepath.Join(fixturePath, "app-with-cfignore")
			files, err := appFiles.AppFilesInDir(appPath)
			Expect(err).ShouldNot(HaveOccurred())

			paths := []string{}
			for _, file := range files {
				paths = append(paths, file.Path)
			}

			Expect(paths).To(Equal([]string{
				"dir1",
				"dir1/child-dir",
				"dir1/child-dir/file3.txt",
				"dir1/file1.txt",
				"dir2",

				// TODO: this should be excluded.
				// .cfignore doesn't handle ** patterns right now
				"dir2/child-dir2",
			}))
		})

		// NB: on windows, you can never rely on the size of a directory being zero
		// see: http://msdn.microsoft.com/en-us/library/windows/desktop/aa364946(v=vs.85).aspx
		// and: https://www.pivotaltracker.com/story/show/70470232
		It("always sets the size of directories to zero bytes", func() {
			fileutils.TempDir("something", func(tempdir string, err error) {
				Expect(err).ToNot(HaveOccurred())

				err = os.Mkdir(filepath.Join(tempdir, "nothing"), 0600)
				Expect(err).ToNot(HaveOccurred())

				files, err := appFiles.AppFilesInDir(tempdir)
				Expect(err).ToNot(HaveOccurred())

				sizes := []int64{}
				for _, file := range files {
					sizes = append(sizes, file.Size)
				}

				Expect(sizes).To(Equal([]int64{0}))
			})
		})
	})

	Describe("CopyFiles", func() {
		It("copies only the files specified", func() {
			copyDir := filepath.Join(fixturePath, "app-copy-test")

			filesToCopy := []models.AppFileFields{
				{Path: filepath.Join("dir1")},
				{Path: filepath.Join("dir1", "child-dir", "file2.txt")},
			}

			files := []string{}

			fileutils.TempDir("copyToDir", func(tmpDir string, err error) {
				copyErr := appFiles.CopyFiles(filesToCopy, copyDir, tmpDir)
				Expect(copyErr).ToNot(HaveOccurred())

				filepath.Walk(tmpDir, func(path string, fileInfo os.FileInfo, err error) error {
					Expect(err).ToNot(HaveOccurred())

					if !fileInfo.IsDir() {
						files = append(files, fileInfo.Name())
					}
					return nil
				})
			})

			// file2.txt is in lowest subtree, thus is walked first.
			Expect(files).To(Equal([]string{
				"file2.txt",
			}))
		})
	})

	Describe("WalkAppFiles", func() {
		var cb func(string, string) error
		var actualWalkAppFileArgs []WalkAppFileArgs

		BeforeEach(func() {
			actualWalkAppFileArgs = []WalkAppFileArgs{}
			cb = func(fileRelativePath, fullPath string) error {
				actualWalkAppFileArgs = append(actualWalkAppFileArgs, WalkAppFileArgs{
					relativePath: fileRelativePath,
					absolutePath: fullPath,
				})
				return nil
			}
		})

		It("calls the callback with the relative and absolute path for each file within the given dir", func() {
			err := appFiles.WalkAppFiles(filepath.Join(fixturePath, "app-copy-test"), cb)
			Expect(err).NotTo(HaveOccurred())
			expectedArgs := []WalkAppFileArgs{
				{
					relativePath: "dir1",
					absolutePath: "../../fixtures/applications/app-copy-test/dir1",
				},
				{
					relativePath: "dir1/child-dir",
					absolutePath: "../../fixtures/applications/app-copy-test/dir1/child-dir",
				},
				{
					relativePath: "dir1/child-dir/file2.txt",
					absolutePath: "../../fixtures/applications/app-copy-test/dir1/child-dir/file2.txt",
				},
				{
					relativePath: "dir1/child-dir/file3.txt",
					absolutePath: "../../fixtures/applications/app-copy-test/dir1/child-dir/file3.txt",
				},
				{
					relativePath: "dir1/file1.txt",
					absolutePath: "../../fixtures/applications/app-copy-test/dir1/file1.txt",
				},
				{
					relativePath: "dir2",
					absolutePath: "../../fixtures/applications/app-copy-test/dir2",
				},
				{
					relativePath: "dir2/child-dir2",
					absolutePath: "../../fixtures/applications/app-copy-test/dir2/child-dir2",
				},
				{
					relativePath: "dir2/child-dir2/grandchild-dir2",
					absolutePath: "../../fixtures/applications/app-copy-test/dir2/child-dir2/grandchild-dir2",
				},
				{
					relativePath: "dir2/child-dir2/grandchild-dir2/file4.txt",
					absolutePath: "../../fixtures/applications/app-copy-test/dir2/child-dir2/grandchild-dir2/file4.txt",
				},
			}

			for i, actual := range actualWalkAppFileArgs {
				Expect(actual.Equal(expectedArgs[i])).To(BeTrue())
			}
		})

		Context("when the given dir contains an untraversable dir", func() {
			var nonTraversableDirPath string

			BeforeEach(func() {
				if runtime.GOOS == "windows" {
					Skip("This test is only for non-Windows platforms")
				}

				nonTraversableDirPath = filepath.Join(fixturePath, "app-copy-test", "non-traversable-dir")

				err := os.Mkdir(nonTraversableDirPath, os.ModeDir) // non-traversable without os.ModePerm
				Expect(err).NotTo(HaveOccurred())
			})

			AfterEach(func() {
				err := os.Remove(nonTraversableDirPath)
				Expect(err).NotTo(HaveOccurred())
			})

			It("returns an error", func() {
				Skip("This isn't a reliable test as root can still traverse a non-executable directory")
				err := appFiles.WalkAppFiles(filepath.Join(fixturePath, "app-copy-test"), cb)
				Expect(err).To(HaveOccurred())
			})

			Context("when the untraversable dir is .cfignored", func() {
				var cfIgnorePath string

				BeforeEach(func() {
					cfIgnorePath = filepath.Join(fixturePath, "app-copy-test", ".cfignore")
					err := ioutil.WriteFile(cfIgnorePath, []byte("non-traversable-dir\n"), os.ModePerm)
					Expect(err).NotTo(HaveOccurred())
				})

				AfterEach(func() {
					err := os.Remove(cfIgnorePath)
					Expect(err).NotTo(HaveOccurred())
				})

				It("does not return an error", func() {
					err := appFiles.WalkAppFiles(filepath.Join(fixturePath, "app-copy-test"), cb)
					Expect(err).NotTo(HaveOccurred())
				})

				It("does not call the callback with the untraversable dir", func() {
					appFiles.WalkAppFiles(filepath.Join(fixturePath, "app-copy-test"), cb)
					expectedArgs := []WalkAppFileArgs{
						{
							relativePath: "dir1",
							absolutePath: "../../fixtures/applications/app-copy-test/dir1",
						},
						{
							relativePath: "dir1/child-dir",
							absolutePath: "../../fixtures/applications/app-copy-test/dir1/child-dir",
						},
						{
							relativePath: "dir1/child-dir/file2.txt",
							absolutePath: "../../fixtures/applications/app-copy-test/dir1/child-dir/file2.txt",
						},
						{
							relativePath: "dir1/child-dir/file3.txt",
							absolutePath: "../../fixtures/applications/app-copy-test/dir1/child-dir/file3.txt",
						},
						{
							relativePath: "dir1/file1.txt",
							absolutePath: "../../fixtures/applications/app-copy-test/dir1/file1.txt",
						},
						{
							relativePath: "dir2",
							absolutePath: "../../fixtures/applications/app-copy-test/dir2",
						},
						{
							relativePath: "dir2/child-dir2",
							absolutePath: "../../fixtures/applications/app-copy-test/dir2/child-dir2",
						},
						{
							relativePath: "dir2/child-dir2/grandchild-dir2",
							absolutePath: "../../fixtures/applications/app-copy-test/dir2/child-dir2/grandchild-dir2",
						},
						{
							relativePath: "dir2/child-dir2/grandchild-dir2/file4.txt",
							absolutePath: "../../fixtures/applications/app-copy-test/dir2/child-dir2/grandchild-dir2/file4.txt",
						},
					}

					for i, actual := range actualWalkAppFileArgs {
						Expect(actual.Equal(expectedArgs[i])).To(BeTrue())
					}
				})
			})
		})
	})
})
<|endoftext|>


<commit_before>package http

import (
	"bytes"
	"context"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"strings"
	"sync"

	"github.com/cloudevents/sdk-go/pkg/cloudevents"
	cecontext "github.com/cloudevents/sdk-go/pkg/cloudevents/context"
	"github.com/cloudevents/sdk-go/pkg/cloudevents/transport"
)

type EncodingSelector func(e cloudevents.Event) Encoding

// type check that this transport message impl matches the contract
var _ transport.Transport = (*Transport)(nil)

// Transport acts as both a http client and a http handler.
type Transport struct {
	Encoding                   Encoding
	DefaultEncodingSelectionFn EncodingSelector

	// Sending
	Client *http.Client
	Req    *http.Request

	// Receiving
	Receiver transport.Receiver
	Port     *int   // if nil, default 8080
	Path     string // if "", default "/"
	Handler  *http.ServeMux

	realPort          int
	server            *http.Server
	handlerRegistered bool
	codec             transport.Codec
	// Create Mutex
	crMu sync.Mutex
	// Receive Mutex
	reMu sync.Mutex
}

func New(opts ...Option) (*Transport, error) {
	t := &Transport{
		Req: &http.Request{
			Method: http.MethodPost,
		},
	}
	if err := t.applyOptions(opts...); err != nil {
		return nil, err
	}
	return t, nil
}

func (t *Transport) applyOptions(opts ...Option) error {
	for _, fn := range opts {
		if err := fn(t); err != nil {
			return err
		}
	}
	return nil
}

func (t *Transport) loadCodec() bool {
	if t.codec == nil {
		t.crMu.Lock()
		if t.DefaultEncodingSelectionFn != nil && t.Encoding != Default {
			log.Printf("[warn] Transport has a DefaultEncodingSelectionFn set but Encoding is not Default. DefaultEncodingSelectionFn will be ignored.")
		}
		t.codec = &Codec{
			Encoding:                   t.Encoding,
			DefaultEncodingSelectionFn: t.DefaultEncodingSelectionFn,
		}
		t.crMu.Unlock()
	}
	return true
}

func (t *Transport) Send(ctx context.Context, event cloudevents.Event) (*cloudevents.Event, error) {
	if t.Client == nil {
		t.crMu.Lock()
		t.Client = &http.Client{}
		t.crMu.Unlock()
	}

	var req http.Request
	if t.Req != nil {
		req.Method = t.Req.Method
		req.URL = t.Req.URL
	}

	// Override the default request with target from context.
	if target := cecontext.TargetFrom(ctx); target != nil {
		req.URL = target
	}

	if ok := t.loadCodec(); !ok {
		return nil, fmt.Errorf("unknown encoding set on transport: %d", t.Encoding)
	}

	msg, err := t.codec.Encode(event)
	if err != nil {
		return nil, err
	}

	// TODO: merge the incoming request with msg, for now just replace.
	if m, ok := msg.(*Message); ok {
		req.Header = m.Header
		req.Body = ioutil.NopCloser(bytes.NewBuffer(m.Body))
		req.ContentLength = int64(len(m.Body))
		return httpDo(ctx, &req, func(resp *http.Response, err error) (*cloudevents.Event, error) {
			if err != nil {
				return nil, err
			}
			defer resp.Body.Close()

			body, _ := ioutil.ReadAll(resp.Body)
			msg := &Message{
				Header: resp.Header,
				Body:   body,
			}

			var respEvent *cloudevents.Event
			if msg.CloudEventsVersion() != "" {
				if ok := t.loadCodec(); !ok {
					err := fmt.Errorf("unknown encoding set on transport: %d", t.Encoding)
					log.Printf("failed to load codec: %s", err)
				}
				if respEvent, err = t.codec.Decode(msg); err != nil {
					log.Printf("failed to decode message: %s %v", err, resp)
				}
			}

			if accepted(resp) {
				return respEvent, nil
			}
			return respEvent, fmt.Errorf("error sending cloudevent: %s", status(resp))
		})
	}
	return nil, fmt.Errorf("failed to encode Event into a Message")
}

func (t *Transport) SetReceiver(r transport.Receiver) {
	t.Receiver = r
}

func (t *Transport) StartReceiver(ctx context.Context) error {
	t.reMu.Lock()
	defer t.reMu.Unlock()

	if t.server == nil {
		if t.Handler == nil {
			t.Handler = http.NewServeMux()
		}
		if !t.handlerRegistered {
			// handler.Handle might panic if the user tries to use the same path as the sdk.
			t.Handler.Handle(t.GetPath(), t)
			t.handlerRegistered = true
		}

		addr := fmt.Sprintf(":%d", t.GetPort())
		t.server = &http.Server{
			Addr:    addr,
			Handler: t.Handler,
		}

		listener, err := net.Listen("tcp", addr)
		if err != nil {
			return err
		}
		t.realPort = listener.Addr().(*net.TCPAddr).Port

		// Shutdown
		defer func() {
			t.realPort = 0
			t.server.Close()
			t.server = nil
		}()

		errChan := make(chan error, 1)
		go func() {
			errChan <- t.server.Serve(listener)
		}()

		// wait for the server to return or ctx.Done().
		select {
		case <-ctx.Done():
			return nil
		case err := <-errChan:
			return err
		}

	}
	return fmt.Errorf("http server already started")
}

type eventError struct {
	event *cloudevents.Event
	err   error
}

func httpDo(ctx context.Context, req *http.Request, fn func(*http.Response, error) (*cloudevents.Event, error)) (*cloudevents.Event, error) {
	// Run the HTTP request in a goroutine and pass the response to fn.
	c := make(chan eventError, 1)
	req = req.WithContext(ctx)
	go func() {
		event, err := fn(http.DefaultClient.Do(req))
		c <- eventError{event: event, err: err}
	}()
	select {
	case <-ctx.Done():
		return nil, ctx.Err()
	case ee := <-c:
		return ee.event, ee.err
	}
}

// accepted is a helper method to understand if the response from the target
// accepted the CloudEvent.
func accepted(resp *http.Response) bool {
	if resp.StatusCode >= 200 && resp.StatusCode < 300 {
		return true
	}
	return false
}

// status is a helper method to read the response of the target.
func status(resp *http.Response) string {
	status := resp.Status
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return fmt.Sprintf("Status[%s] error reading response body: %v", status, err)
	}
	return fmt.Sprintf("Status[%s] %s", status, body)
}

func (t *Transport) invokeReceiver(ctx context.Context, event cloudevents.Event) (*Response, error) {
	if t.Receiver != nil {
		// Note: http does not use eventResp.Reason
		eventResp := cloudevents.EventResponse{}
		resp := Response{}

		err := t.Receiver.Receive(ctx, event, &eventResp)
		if err != nil {
			log.Printf("got an error from receiver fn: %s", err.Error())
			resp.StatusCode = http.StatusInternalServerError
			return &resp, err
		}

		if eventResp.Event != nil {
			if t.loadCodec() {
				if m, err := t.codec.Encode(*eventResp.Event); err != nil {
					log.Printf("failed to encode response from receiver fn: %s", err.Error())
				} else if msg, ok := m.(*Message); ok {
					resp.Header = msg.Header
					resp.Body = msg.Body
				}
			} else {
				log.Printf("failed to load codec")
				resp.StatusCode = http.StatusInternalServerError
				return &resp, err
			}
		}

		if eventResp.Status != 0 {
			resp.StatusCode = eventResp.Status
		} else {
			resp.StatusCode = http.StatusAccepted // default is 202 - Accepted
		}
		return &resp, err
	}
	return nil, nil
}

// ServeHTTP implements http.Handler
func (t *Transport) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	body, err := ioutil.ReadAll(req.Body)
	if err != nil {
		log.Printf("failed to handle request: %s %v", err, req)
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte(`{"error":"Invalid request"}`))
		return
	}
	msg := &Message{
		Header: req.Header,
		Body:   body,
	}

	if ok := t.loadCodec(); !ok {
		err := fmt.Errorf("unknown encoding set on transport: %d", t.Encoding)
		log.Printf("failed to load codec: %s", err)
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte(fmt.Sprintf(`{"error":%q}`, err.Error())))
		return
	}
	event, err := t.codec.Decode(msg)
	if err != nil {
		log.Printf("failed to decode message: %s %v", err, req)
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte(fmt.Sprintf(`{"error":%q}`, err.Error())))
		return
	}

	ctx := req.Context()

	if req != nil {
		ctx = WithTransportContext(ctx, TransportContext{
			URI:    req.RequestURI,
			Host:   req.Host,
			Method: req.Method,
		})
	}

	resp, err := t.invokeReceiver(ctx, *event)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte(fmt.Sprintf(`{"error":%q}`, err.Error())))
		return
	}
	if resp != nil {
		if len(resp.Header) > 0 {
			for k, vs := range resp.Header {
				for _, v := range vs {
					w.Header().Set(k, v)
				}
			}
		}
		if resp.StatusCode >= 200 && resp.StatusCode < 600 {
			w.WriteHeader(resp.StatusCode)
		}
		if len(resp.Body) > 0 {
			w.Write(resp.Body)
		}
		return
	}

	w.WriteHeader(http.StatusNoContent)
}

func (t *Transport) GetPort() int {
	if t.Port != nil && *t.Port == 0 && t.realPort != 0 {
		return t.realPort
	}

	if t.Port != nil && *t.Port >= 0 { // 0 means next open port
		return *t.Port
	}
	return 8080 // default
}

func (t *Transport) GetPath() string {
	path := strings.TrimSpace(t.Path)
	if len(path) > 0 {
		return path
	}
	return "/" // default
}
<commit_msg>gracefully shutdown the http server.<commit_after>package http

import (
	"bytes"
	"context"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"strings"
	"sync"

	"github.com/cloudevents/sdk-go/pkg/cloudevents"
	cecontext "github.com/cloudevents/sdk-go/pkg/cloudevents/context"
	"github.com/cloudevents/sdk-go/pkg/cloudevents/transport"
)

type EncodingSelector func(e cloudevents.Event) Encoding

// type check that this transport message impl matches the contract
var _ transport.Transport = (*Transport)(nil)

// Transport acts as both a http client and a http handler.
type Transport struct {
	Encoding                   Encoding
	DefaultEncodingSelectionFn EncodingSelector

	// Sending
	Client *http.Client
	Req    *http.Request

	// Receiving
	Receiver transport.Receiver
	Port     *int   // if nil, default 8080
	Path     string // if "", default "/"
	Handler  *http.ServeMux

	realPort          int
	server            *http.Server
	handlerRegistered bool
	codec             transport.Codec
	// Create Mutex
	crMu sync.Mutex
	// Receive Mutex
	reMu sync.Mutex
}

func New(opts ...Option) (*Transport, error) {
	t := &Transport{
		Req: &http.Request{
			Method: http.MethodPost,
		},
	}
	if err := t.applyOptions(opts...); err != nil {
		return nil, err
	}
	return t, nil
}

func (t *Transport) applyOptions(opts ...Option) error {
	for _, fn := range opts {
		if err := fn(t); err != nil {
			return err
		}
	}
	return nil
}

func (t *Transport) loadCodec() bool {
	if t.codec == nil {
		t.crMu.Lock()
		if t.DefaultEncodingSelectionFn != nil && t.Encoding != Default {
			log.Printf("[warn] Transport has a DefaultEncodingSelectionFn set but Encoding is not Default. DefaultEncodingSelectionFn will be ignored.")
		}
		t.codec = &Codec{
			Encoding:                   t.Encoding,
			DefaultEncodingSelectionFn: t.DefaultEncodingSelectionFn,
		}
		t.crMu.Unlock()
	}
	return true
}

func (t *Transport) Send(ctx context.Context, event cloudevents.Event) (*cloudevents.Event, error) {
	if t.Client == nil {
		t.crMu.Lock()
		t.Client = &http.Client{}
		t.crMu.Unlock()
	}

	var req http.Request
	if t.Req != nil {
		req.Method = t.Req.Method
		req.URL = t.Req.URL
	}

	// Override the default request with target from context.
	if target := cecontext.TargetFrom(ctx); target != nil {
		req.URL = target
	}

	if ok := t.loadCodec(); !ok {
		return nil, fmt.Errorf("unknown encoding set on transport: %d", t.Encoding)
	}

	msg, err := t.codec.Encode(event)
	if err != nil {
		return nil, err
	}

	// TODO: merge the incoming request with msg, for now just replace.
	if m, ok := msg.(*Message); ok {
		req.Header = m.Header
		req.Body = ioutil.NopCloser(bytes.NewBuffer(m.Body))
		req.ContentLength = int64(len(m.Body))
		return httpDo(ctx, &req, func(resp *http.Response, err error) (*cloudevents.Event, error) {
			if err != nil {
				return nil, err
			}
			defer resp.Body.Close()

			body, _ := ioutil.ReadAll(resp.Body)
			msg := &Message{
				Header: resp.Header,
				Body:   body,
			}

			var respEvent *cloudevents.Event
			if msg.CloudEventsVersion() != "" {
				if ok := t.loadCodec(); !ok {
					err := fmt.Errorf("unknown encoding set on transport: %d", t.Encoding)
					log.Printf("failed to load codec: %s", err)
				}
				if respEvent, err = t.codec.Decode(msg); err != nil {
					log.Printf("failed to decode message: %s %v", err, resp)
				}
			}

			if accepted(resp) {
				return respEvent, nil
			}
			return respEvent, fmt.Errorf("error sending cloudevent: %s", status(resp))
		})
	}
	return nil, fmt.Errorf("failed to encode Event into a Message")
}

func (t *Transport) SetReceiver(r transport.Receiver) {
	t.Receiver = r
}

func (t *Transport) StartReceiver(ctx context.Context) error {
	t.reMu.Lock()
	defer t.reMu.Unlock()

	if t.Handler == nil {
		t.Handler = http.NewServeMux()
	}
	if !t.handlerRegistered {
		// handler.Handle might panic if the user tries to use the same path as the sdk.
		t.Handler.Handle(t.GetPath(), t)
		t.handlerRegistered = true
	}

	addr := fmt.Sprintf(":%d", t.GetPort())
	t.server = &http.Server{
		Addr:    addr,
		Handler: t.Handler,
	}

	listener, err := net.Listen("tcp", addr)
	if err != nil {
		return err
	}
	t.realPort = listener.Addr().(*net.TCPAddr).Port

	// Shutdown
	defer func() {
		t.realPort = 0
		t.server.Close()
		t.server = nil
	}()

	errChan := make(chan error, 1)
	go func() {
		errChan <- t.server.Serve(listener)
	}()

	// wait for the server to return or ctx.Done().
	select {
	case <-ctx.Done():
		// Try a gracefully shutdown.
		return t.server.Shutdown(context.Background())
	case err := <-errChan:
		return err
	}
}

type eventError struct {
	event *cloudevents.Event
	err   error
}

func httpDo(ctx context.Context, req *http.Request, fn func(*http.Response, error) (*cloudevents.Event, error)) (*cloudevents.Event, error) {
	// Run the HTTP request in a goroutine and pass the response to fn.
	c := make(chan eventError, 1)
	req = req.WithContext(ctx)
	go func() {
		event, err := fn(http.DefaultClient.Do(req))
		c <- eventError{event: event, err: err}
	}()
	select {
	case <-ctx.Done():
		return nil, ctx.Err()
	case ee := <-c:
		return ee.event, ee.err
	}
}

// accepted is a helper method to understand if the response from the target
// accepted the CloudEvent.
func accepted(resp *http.Response) bool {
	if resp.StatusCode >= 200 && resp.StatusCode < 300 {
		return true
	}
	return false
}

// status is a helper method to read the response of the target.
func status(resp *http.Response) string {
	status := resp.Status
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return fmt.Sprintf("Status[%s] error reading response body: %v", status, err)
	}
	return fmt.Sprintf("Status[%s] %s", status, body)
}

func (t *Transport) invokeReceiver(ctx context.Context, event cloudevents.Event) (*Response, error) {
	if t.Receiver != nil {
		// Note: http does not use eventResp.Reason
		eventResp := cloudevents.EventResponse{}
		resp := Response{}

		err := t.Receiver.Receive(ctx, event, &eventResp)
		if err != nil {
			log.Printf("got an error from receiver fn: %s", err.Error())
			resp.StatusCode = http.StatusInternalServerError
			return &resp, err
		}

		if eventResp.Event != nil {
			if t.loadCodec() {
				if m, err := t.codec.Encode(*eventResp.Event); err != nil {
					log.Printf("failed to encode response from receiver fn: %s", err.Error())
				} else if msg, ok := m.(*Message); ok {
					resp.Header = msg.Header
					resp.Body = msg.Body
				}
			} else {
				log.Printf("failed to load codec")
				resp.StatusCode = http.StatusInternalServerError
				return &resp, err
			}
		}

		if eventResp.Status != 0 {
			resp.StatusCode = eventResp.Status
		} else {
			resp.StatusCode = http.StatusAccepted // default is 202 - Accepted
		}
		return &resp, err
	}
	return nil, nil
}

// ServeHTTP implements http.Handler
func (t *Transport) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	body, err := ioutil.ReadAll(req.Body)
	if err != nil {
		log.Printf("failed to handle request: %s %v", err, req)
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte(`{"error":"Invalid request"}`))
		return
	}
	msg := &Message{
		Header: req.Header,
		Body:   body,
	}

	if ok := t.loadCodec(); !ok {
		err := fmt.Errorf("unknown encoding set on transport: %d", t.Encoding)
		log.Printf("failed to load codec: %s", err)
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte(fmt.Sprintf(`{"error":%q}`, err.Error())))
		return
	}
	event, err := t.codec.Decode(msg)
	if err != nil {
		log.Printf("failed to decode message: %s %v", err, req)
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte(fmt.Sprintf(`{"error":%q}`, err.Error())))
		return
	}

	ctx := req.Context()

	if req != nil {
		ctx = WithTransportContext(ctx, TransportContext{
			URI:    req.RequestURI,
			Host:   req.Host,
			Method: req.Method,
		})
	}

	resp, err := t.invokeReceiver(ctx, *event)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte(fmt.Sprintf(`{"error":%q}`, err.Error())))
		return
	}
	if resp != nil {
		if len(resp.Header) > 0 {
			for k, vs := range resp.Header {
				for _, v := range vs {
					w.Header().Set(k, v)
				}
			}
		}
		if resp.StatusCode >= 200 && resp.StatusCode < 600 {
			w.WriteHeader(resp.StatusCode)
		}
		if len(resp.Body) > 0 {
			w.Write(resp.Body)
		}
		return
	}

	w.WriteHeader(http.StatusNoContent)
}

func (t *Transport) GetPort() int {
	if t.Port != nil && *t.Port == 0 && t.realPort != 0 {
		return t.realPort
	}

	if t.Port != nil && *t.Port >= 0 { // 0 means next open port
		return *t.Port
	}
	return 8080 // default
}

func (t *Transport) GetPath() string {
	path := strings.TrimSpace(t.Path)
	if len(path) > 0 {
		return path
	}
	return "/" // default
}
<|endoftext|>


<commit_before>// Copyright (c) 2019 The OpenSDS 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.

/*
This module implements a entry into the OpenSDS metrics controller service.

*/

package metrics

import (
	"encoding/json"
	log "github.com/golang/glog"
	"github.com/opensds/opensds/pkg/dock/client"
	"github.com/opensds/opensds/pkg/model"
	pb "github.com/opensds/opensds/pkg/model/proto"
	"io/ioutil"
	"net/http"
	"strconv"
)

// Controller is an interface for exposing some operations of metric controllers.
type Controller interface {
	GetLatestMetrics(opt *pb.GetMetricsOpts) ([]*model.MetricSpec, error)
	GetInstantMetrics(opt *pb.GetMetricsOpts) ([]*model.MetricSpec, error)
	GetRangeMetrics(opt *pb.GetMetricsOpts) ([]*model.MetricSpec, error)
	SetDock(dockInfo *model.DockSpec)
}

// NewController method creates a controller structure and expose its pointer.
func NewController() Controller {
	return &controller{
		Client: client.NewClient(),
	}
}

type controller struct {
	client.Client
	DockInfo *model.DockSpec
}

// latest+instant metrics structs begin
type InstantMetricReponseFromPrometheus struct {
	Status string `json:"status"`
	Data   Data   `json:"data"`
}
type Metric struct {
	Name       string `json:"__name__"`
	Device     string `json:"device"`
	InstanceID string `json:"instanceID"`
	Job        string `json:"job"`
}
type Result struct {
	Metric Metric        `json:"metric"`
	Value  []interface{} `json:"value"`
}
type Data struct {
	ResultType string   `json:"resultType"`
	Result     []Result `json:"result"`
}

// latest+instant metrics structs end

// latest+range metrics structs begin
type RangeMetricReponseFromPrometheus struct {
	Status string    `json:"status"`
	Data   RangeData `json:"data"`
}
type RangeMetric struct {
	Name     string `json:"__name__"`
	Device   string `json:"device"`
	Instance string `json:"instance"`
	Job      string `json:"job"`
}
type RangeResult struct {
	Metric RangeMetric     `json:"metric"`
	Values [][]interface{} `json:"values"`
}
type RangeData struct {
	ResultType string        `json:"resultType"`
	Result     []RangeResult `json:"result"`
}

// latest+range metrics structs end

func (c *controller) GetLatestMetrics(opt *pb.GetMetricsOpts) ([]*model.MetricSpec, error) {

	// make a call to Prometheus, convert the response to our format, return
	response, err := http.Get("http://localhost:9090/api/v1/query?query=" + opt.MetricName)
	if err != nil {
		log.Infof("The HTTP query request failed with error %s\n", err)
	} else {
		data, _ := ioutil.ReadAll(response.Body)
		log.Infof("response data is %s", string(data))
		// unmarshal the JSON response into a struct (generated using the JSON, using this https://mholt.github.io/json-to-go/
		var fv InstantMetricReponseFromPrometheus
		err0 := json.Unmarshal(data, &fv)
		if err0 != nil {
			log.Infof("unmarshell operation failed %s\n", err0)
		}
		var metrics []*model.MetricSpec
		// now convert to our repsonse struct, so we can marshal it and send out the JSON
		for _, res := range fv.Data.Result {

			metricValues := make([]*model.Metric, 0)
			metricValue := &model.Metric{}
			for _, v := range res.Value {

				switch v.(type) {
				case string:
					metricValue.Value, err = strconv.ParseFloat(v.(string), 64)
				case float64:
					secs := int64(v.(float64))
					metricValue.Timestamp = secs
				default:
					log.Info(v, "is of a type I don't know how to handle")
				}

			}
			metricValues = append(metricValues, metricValue)
			metric := &model.MetricSpec{}
			metric.InstanceID = res.Metric.InstanceID
			metric.Name = res.Metric.Name
			metric.InstanceName = res.Metric.Device
			metric.MetricValues = metricValues
			metrics = append(metrics, metric)
		}

		bArr, _ := json.Marshal(metrics)
		log.Infof("metrics response json is %s", string(bArr))

		if err != nil {
			log.Error(err)
		}
		return metrics, err

	}
	return nil, err
}

func (c *controller) GetInstantMetrics(opt *pb.GetMetricsOpts) ([]*model.MetricSpec, error) {

	// make a call to Prometheus, convert the response to our format, return
	response, err := http.Get("http://localhost:9090/api/v1/query?query=" + opt.MetricName + "&time=" + opt.StartTime)
	if err != nil {
		log.Infof("The HTTP query request failed with error %s\n", err)
	} else {
		data, _ := ioutil.ReadAll(response.Body)
		log.Infof("response data is %s", string(data))
		// unmarshal the JSON response into a struct (generated using the JSON, using this https://mholt.github.io/json-to-go/
		var fv InstantMetricReponseFromPrometheus
		err0 := json.Unmarshal(data, &fv)
		if err0 != nil {
			log.Infof("unmarshell operation failed %s\n", err0)
		}
		var metrics []*model.MetricSpec
		// now convert to our repsonse struct, so we can marshal it and send out the JSON
		for _, res := range fv.Data.Result {

			metricValues := make([]*model.Metric, 0)
			metricValue := &model.Metric{}
			for _, v := range res.Value {

				switch v.(type) {
				case string:
					metricValue.Value, err = strconv.ParseFloat(v.(string), 64)
				case float64:
					secs := int64(v.(float64))
					metricValue.Timestamp = secs
				default:
					log.Info(v, "is of a type I don't know how to handle")
				}

			}
			metricValues = append(metricValues, metricValue)
			metric := &model.MetricSpec{}
			metric.InstanceID = res.Metric.InstanceID
			metric.Name = res.Metric.Name
			metric.InstanceName = res.Metric.Device
			metric.MetricValues = metricValues
			metrics = append(metrics, metric)
		}

		bArr, _ := json.Marshal(metrics)
		log.Infof("metrics response json is %s", string(bArr))

		if err != nil {
			log.Error(err)
		}
		return metrics, err

	}
	return nil, err
}

func (c *controller) GetRangeMetrics(opt *pb.GetMetricsOpts) ([]*model.MetricSpec, error) {

	// make a call to Prometheus, convert the response to our format, return
	response, err := http.Get("http://localhost:9090/api/v1/query_range?query=" + opt.MetricName + "&start=" + opt.StartTime + "&end=" + opt.EndTime + "&step=30")
	if err != nil {
		log.Infof("The HTTP query request failed with error %s\n", err)
	} else {
		data, _ := ioutil.ReadAll(response.Body)
		log.Info(string(data))

		// unmarshal the JSON response into a struct (generated using the JSON, using this https://mholt.github.io/json-to-go/
		var fv RangeMetricReponseFromPrometheus
		err0 := json.Unmarshal(data, &fv)
		if err0 != nil {
			log.Infof("unmarshell operation failed %s\n", err0)
		}
		var metrics []*model.MetricSpec
		// now convert to our repsonse struct, so we can marshal it and send out the JSON
		for _, res := range fv.Data.Result {

			metricValues := make([]*model.Metric, 0)
			metricValue := &model.Metric{}
			for j := 0; j < len(res.Values); j++ {
				for _, v := range res.Values[j] {
					switch v.(type) {
					case string:
						metricValue.Value, _ = strconv.ParseFloat(v.(string), 64)
					case float64:
						secs := int64(v.(float64))
						metricValue.Timestamp = secs
					default:
						log.Infof("%s is of a type I don't know how to handle", v)
					}

				}
				metricValues = append(metricValues, metricValue)
				metric := &model.MetricSpec{}
				metric.InstanceID = res.Metric.Instance
				metric.Name = res.Metric.Name
				metric.InstanceName = res.Metric.Device
				metric.MetricValues = metricValues
				metrics = append(metrics, metric)
			}
		}

		bArr, _ := json.Marshal(metrics)
		log.Infof("metrics response json is %s", string(bArr))

		if err != nil {
			log.Error(err)
		}
		return metrics, err

	}
	return nil, err
}

func (c *controller) SetDock(dockInfo *model.DockSpec) {
	c.DockInfo = dockInfo
}
<commit_msg>review comments addressed<commit_after>// Copyright (c) 2019 The OpenSDS Authors.
//
// 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.

/*
This module implements a entry into the OpenSDS metrics controller service.

*/

package metrics

import (
	"encoding/json"
	"io/ioutil"
	"net/http"
	"strconv"

	log "github.com/golang/glog"
	"github.com/opensds/opensds/pkg/dock/client"
	"github.com/opensds/opensds/pkg/model"
	pb "github.com/opensds/opensds/pkg/model/proto"
)

// Controller is an interface for exposing some operations of metric controllers.
type Controller interface {
	GetLatestMetrics(opt *pb.GetMetricsOpts) ([]*model.MetricSpec, error)
	GetInstantMetrics(opt *pb.GetMetricsOpts) ([]*model.MetricSpec, error)
	GetRangeMetrics(opt *pb.GetMetricsOpts) ([]*model.MetricSpec, error)
	SetDock(dockInfo *model.DockSpec)
}

// NewController method creates a controller structure and expose its pointer.
func NewController() Controller {
	return &controller{
		Client: client.NewClient(),
	}
}

type controller struct {
	client.Client
	DockInfo *model.DockSpec
}

// latest+instant metrics structs begin
type InstantMetricReponseFromPrometheus struct {
	Status string `json:"status"`
	Data   Data   `json:"data"`
}
type Metric struct {
	Name       string `json:"__name__"`
	Device     string `json:"device"`
	InstanceID string `json:"instanceID"`
	Job        string `json:"job"`
}
type Result struct {
	Metric Metric        `json:"metric"`
	Value  []interface{} `json:"value"`
}
type Data struct {
	ResultType string   `json:"resultType"`
	Result     []Result `json:"result"`
}

// latest+instant metrics structs end

// latest+range metrics structs begin
type RangeMetricReponseFromPrometheus struct {
	Status string    `json:"status"`
	Data   RangeData `json:"data"`
}
type RangeMetric struct {
	Name     string `json:"__name__"`
	Device   string `json:"device"`
	Instance string `json:"instance"`
	Job      string `json:"job"`
}
type RangeResult struct {
	Metric RangeMetric     `json:"metric"`
	Values [][]interface{} `json:"values"`
}
type RangeData struct {
	ResultType string        `json:"resultType"`
	Result     []RangeResult `json:"result"`
}

// latest+range metrics structs end

func (c *controller) GetLatestMetrics(opt *pb.GetMetricsOpts) ([]*model.MetricSpec, error) {

	// make a call to Prometheus, convert the response to our format, return
	response, err := http.Get("http://localhost:9090/api/v1/query?query=" + opt.MetricName)
	if err != nil {
		log.Errorf("The HTTP query request failed with error %s\n", err)
	} else {
		data, _ := ioutil.ReadAll(response.Body)
		log.Infof("response data is %s", string(data))
		// unmarshal the JSON response into a struct (generated using the JSON, using this https://mholt.github.io/json-to-go/
		var fv InstantMetricReponseFromPrometheus
		err0 := json.Unmarshal(data, &fv)
		if err0 != nil {
			log.Errorf("unmarshell operation failed %s\n", err0)
		}
		var metrics []*model.MetricSpec
		// now convert to our repsonse struct, so we can marshal it and send out the JSON
		for _, res := range fv.Data.Result {

			metricValues := make([]*model.Metric, 0)
			metricValue := &model.Metric{}
			for _, v := range res.Value {

				switch v.(type) {
				case string:
					metricValue.Value, err = strconv.ParseFloat(v.(string), 64)
				case float64:
					secs := int64(v.(float64))
					metricValue.Timestamp = secs
				default:
					log.Info(v, "is of a type I don't know how to handle")
				}

			}
			metricValues = append(metricValues, metricValue)
			metric := &model.MetricSpec{}
			metric.InstanceID = res.Metric.InstanceID
			metric.Name = res.Metric.Name
			metric.InstanceName = res.Metric.Device
			metric.MetricValues = metricValues
			metrics = append(metrics, metric)
		}

		bArr, _ := json.Marshal(metrics)
		log.Infof("metrics response json is %s", string(bArr))

		if err != nil {
			log.Error(err)
		}
		return metrics, err

	}
	return nil, err
}

func (c *controller) GetInstantMetrics(opt *pb.GetMetricsOpts) ([]*model.MetricSpec, error) {

	// make a call to Prometheus, convert the response to our format, return
	response, err := http.Get("http://localhost:9090/api/v1/query?query=" + opt.MetricName + "&time=" + opt.StartTime)
	if err != nil {
		log.Errorf("The HTTP query request failed with error %s\n", err)
	} else {
		data, _ := ioutil.ReadAll(response.Body)
		log.Infof("response data is %s", string(data))
		// unmarshal the JSON response into a struct (generated using the JSON, using this https://mholt.github.io/json-to-go/
		var fv InstantMetricReponseFromPrometheus
		err0 := json.Unmarshal(data, &fv)
		if err0 != nil {
			log.Errorf("unmarshell operation failed %s\n", err0)
		}
		var metrics []*model.MetricSpec
		// now convert to our repsonse struct, so we can marshal it and send out the JSON
		for _, res := range fv.Data.Result {

			metricValues := make([]*model.Metric, 0)
			metricValue := &model.Metric{}
			for _, v := range res.Value {

				switch v.(type) {
				case string:
					metricValue.Value, err = strconv.ParseFloat(v.(string), 64)
				case float64:
					secs := int64(v.(float64))
					metricValue.Timestamp = secs
				default:
					log.Info(v, "is of a type I don't know how to handle")
				}

			}
			metricValues = append(metricValues, metricValue)
			metric := &model.MetricSpec{}
			metric.InstanceID = res.Metric.InstanceID
			metric.Name = res.Metric.Name
			metric.InstanceName = res.Metric.Device
			metric.MetricValues = metricValues
			metrics = append(metrics, metric)
		}

		bArr, _ := json.Marshal(metrics)
		log.Infof("metrics response json is %s", string(bArr))

		if err != nil {
			log.Error(err)
		}
		return metrics, err

	}
	return nil, err
}

func (c *controller) GetRangeMetrics(opt *pb.GetMetricsOpts) ([]*model.MetricSpec, error) {

	// make a call to Prometheus, convert the response to our format, return
	response, err := http.Get("http://localhost:9090/api/v1/query_range?query=" + opt.MetricName + "&start=" + opt.StartTime + "&end=" + opt.EndTime + "&step=30")
	if err != nil {
		log.Errorf("The HTTP query request failed with error %s\n", err)
	} else {
		data, _ := ioutil.ReadAll(response.Body)
		log.Info(string(data))

		// unmarshal the JSON response into a struct (generated using the JSON, using this https://mholt.github.io/json-to-go/
		var fv RangeMetricReponseFromPrometheus
		err0 := json.Unmarshal(data, &fv)
		if err0 != nil {
			log.Infof("unmarshell operation failed %s\n", err0)
		}
		var metrics []*model.MetricSpec
		// now convert to our repsonse struct, so we can marshal it and send out the JSON
		for _, res := range fv.Data.Result {

			metricValues := make([]*model.Metric, 0)
			metricValue := &model.Metric{}
			for j := 0; j < len(res.Values); j++ {
				for _, v := range res.Values[j] {
					switch v.(type) {
					case string:
						metricValue.Value, _ = strconv.ParseFloat(v.(string), 64)
					case float64:
						secs := int64(v.(float64))
						metricValue.Timestamp = secs
					default:
						log.Infof("%s is of a type I don't know how to handle", v)
					}

				}
				metricValues = append(metricValues, metricValue)
				metric := &model.MetricSpec{}
				metric.InstanceID = res.Metric.Instance
				metric.Name = res.Metric.Name
				metric.InstanceName = res.Metric.Device
				metric.MetricValues = metricValues
				metrics = append(metrics, metric)
			}
		}

		bArr, _ := json.Marshal(metrics)
		log.Infof("metrics response json is %s", string(bArr))

		if err != nil {
			log.Error(err)
		}
		return metrics, err

	}
	return nil, err
}

func (c *controller) SetDock(dockInfo *model.DockSpec) {
	c.DockInfo = dockInfo
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The HAProxy Ingress Controller Authors.

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 types

// Host Annotations
const (
	HostAppRoot                = "app-root"
	HostAuthTLSErrorPage       = "auth-tls-error-page"
	HostAuthTLSSecret          = "auth-tls-secret"
	HostAuthTLSStrict          = "auth-tls-strict"
	HostAuthTLSVerifyClient    = "auth-tls-verify-client"
	HostCertSigner             = "cert-signer"
	HostServerAlias            = "server-alias"
	HostServerAliasRegex       = "server-alias-regex"
	HostSSLPassthrough         = "ssl-passthrough"
	HostSSLPassthroughHTTPPort = "ssl-passthrough-http-port"
	HostVarNamespace           = "var-namespace"
)

var (
	// AnnHost ...
	AnnHost = map[string]struct{}{
		HostAppRoot:                {},
		HostAuthTLSErrorPage:       {},
		HostAuthTLSSecret:          {},
		HostAuthTLSStrict:          {},
		HostAuthTLSVerifyClient:    {},
		HostCertSigner:             {},
		HostServerAlias:            {},
		HostServerAliasRegex:       {},
		HostSSLPassthrough:         {},
		HostSSLPassthroughHTTPPort: {},
		HostVarNamespace:           {},
	}
)

// Backend Annotations
const (
	BackAffinity               = "affinity"
	BackAgentCheckAddr         = "agent-check-addr"
	BackAgentCheckInterval     = "agent-check-interval"
	BackAgentCheckPort         = "agent-check-port"
	BackAgentCheckSend         = "agent-check-send"
	BackAuthRealm              = "auth-realm"
	BackAuthSecret             = "auth-secret"
	BackAuthTLSCertHeader      = "auth-tls-cert-header"
	BackAuthType               = "auth-type"
	BackBackendCheckInterval   = "backend-check-interval"
	BackBackendProtocol        = "backend-protocol"
	BackBackendServerNaming    = "backend-server-naming"
	BackBackendServerSlotsInc  = "backend-server-slots-increment"
	BackBalanceAlgorithm       = "balance-algorithm"
	BackBlueGreenBalance       = "blue-green-balance"
	BackBlueGreenCookie        = "blue-green-cookie"
	BackBlueGreenDeploy        = "blue-green-deploy"
	BackBlueGreenHeader        = "blue-green-header"
	BackBlueGreenMode          = "blue-green-mode"
	BackConfigBackend          = "config-backend"
	BackCorsAllowCredentials   = "cors-allow-credentials"
	BackCorsAllowHeaders       = "cors-allow-headers"
	BackCorsAllowMethods       = "cors-allow-methods"
	BackCorsAllowOrigin        = "cors-allow-origin"
	BackCorsEnable             = "cors-enable"
	BackCorsExposeHeaders      = "cors-expose-headers"
	BackCorsMaxAge             = "cors-max-age"
	BackDynamicScaling         = "dynamic-scaling"
	BackHeaders                = "headers"
	BackHealthCheckAddr        = "health-check-addr"
	BackHealthCheckFallCount   = "health-check-fall-count"
	BackHealthCheckInterval    = "health-check-interval"
	BackHealthCheckPort        = "health-check-port"
	BackHealthCheckRiseCount   = "health-check-rise-count"
	BackHealthCheckURI         = "health-check-uri"
	BackHSTS                   = "hsts"
	BackHSTSIncludeSubdomains  = "hsts-include-subdomains"
	BackHSTSMaxAge             = "hsts-max-age"
	BackHSTSPreload            = "hsts-preload"
	BackInitialWeight          = "initial-weight"
	BackLimitConnections       = "limit-connections"
	BackLimitRPS               = "limit-rps"
	BackLimitWhitelist         = "limit-whitelist"
	BackMaxconnServer          = "maxconn-server"
	BackMaxQueueServer         = "maxqueue-server"
	BackOAuth                  = "oauth"
	BackOAuthHeaders           = "oauth-headers"
	BackOAuthURIPrefix         = "oauth-uri-prefix"
	BackProxyBodySize          = "proxy-body-size"
	BackProxyProtocol          = "proxy-protocol"
	BackRewriteTarget          = "rewrite-target"
	BackSlotsMinFree           = "slots-min-free"
	BackSecureBackends         = "secure-backends"
	BackSecureCrtSecret        = "secure-crt-secret"
	BackSecureVerifyCASecret   = "secure-verify-ca-secret"
	BackServiceUpstream        = "service-upstream"
	BackSessionCookieDynamic   = "session-cookie-dynamic"
	BackSessionCookieName      = "session-cookie-name"
	BackSessionCookieShared    = "session-cookie-shared"
	BackSessionCookieStrategy  = "session-cookie-strategy"
	BackSessionCookieKeywords  = "session-cookie-keywords"
	BackSSLCipherSuitesBackend = "ssl-cipher-suites-backend"
	BackSSLCiphersBackend      = "ssl-ciphers-backend"
	BackSSLFingerprintLower    = "ssl-fingerprint-lower"
	BackSSLOptionsBackend      = "ssl-options-backend"
	BackSSLRedirect            = "ssl-redirect"
	BackTimeoutConnect         = "timeout-connect"
	BackTimeoutHTTPRequest     = "timeout-http-request"
	BackTimeoutKeepAlive       = "timeout-keep-alive"
	BackTimeoutQueue           = "timeout-queue"
	BackTimeoutServer          = "timeout-server"
	BackTimeoutServerFin       = "timeout-server-fin"
	BackTimeoutTunnel          = "timeout-tunnel"
	BackUseResolver            = "use-resolver"
	BackWAF                    = "waf"
	BackWAFMode                = "waf-mode"
	BackWhitelistSourceRange   = "whitelist-source-range"
)

// Extra Annotations
const (
	ExtraTLSAcme = "kubernetes.io/tls-acme"
)
<commit_msg>Sort backend annotations alphabetically<commit_after>/*
Copyright 2019 The HAProxy Ingress Controller Authors.

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 types

// Host Annotations
const (
	HostAppRoot                = "app-root"
	HostAuthTLSErrorPage       = "auth-tls-error-page"
	HostAuthTLSSecret          = "auth-tls-secret"
	HostAuthTLSStrict          = "auth-tls-strict"
	HostAuthTLSVerifyClient    = "auth-tls-verify-client"
	HostCertSigner             = "cert-signer"
	HostServerAlias            = "server-alias"
	HostServerAliasRegex       = "server-alias-regex"
	HostSSLPassthrough         = "ssl-passthrough"
	HostSSLPassthroughHTTPPort = "ssl-passthrough-http-port"
	HostVarNamespace           = "var-namespace"
)

var (
	// AnnHost ...
	AnnHost = map[string]struct{}{
		HostAppRoot:                {},
		HostAuthTLSErrorPage:       {},
		HostAuthTLSSecret:          {},
		HostAuthTLSStrict:          {},
		HostAuthTLSVerifyClient:    {},
		HostCertSigner:             {},
		HostServerAlias:            {},
		HostServerAliasRegex:       {},
		HostSSLPassthrough:         {},
		HostSSLPassthroughHTTPPort: {},
		HostVarNamespace:           {},
	}
)

// Backend Annotations
const (
	BackAffinity               = "affinity"
	BackAgentCheckAddr         = "agent-check-addr"
	BackAgentCheckInterval     = "agent-check-interval"
	BackAgentCheckPort         = "agent-check-port"
	BackAgentCheckSend         = "agent-check-send"
	BackAuthRealm              = "auth-realm"
	BackAuthSecret             = "auth-secret"
	BackAuthTLSCertHeader      = "auth-tls-cert-header"
	BackAuthType               = "auth-type"
	BackBackendCheckInterval   = "backend-check-interval"
	BackBackendProtocol        = "backend-protocol"
	BackBackendServerNaming    = "backend-server-naming"
	BackBackendServerSlotsInc  = "backend-server-slots-increment"
	BackBalanceAlgorithm       = "balance-algorithm"
	BackBlueGreenBalance       = "blue-green-balance"
	BackBlueGreenCookie        = "blue-green-cookie"
	BackBlueGreenDeploy        = "blue-green-deploy"
	BackBlueGreenHeader        = "blue-green-header"
	BackBlueGreenMode          = "blue-green-mode"
	BackConfigBackend          = "config-backend"
	BackCorsAllowCredentials   = "cors-allow-credentials"
	BackCorsAllowHeaders       = "cors-allow-headers"
	BackCorsAllowMethods       = "cors-allow-methods"
	BackCorsAllowOrigin        = "cors-allow-origin"
	BackCorsEnable             = "cors-enable"
	BackCorsExposeHeaders      = "cors-expose-headers"
	BackCorsMaxAge             = "cors-max-age"
	BackDynamicScaling         = "dynamic-scaling"
	BackHeaders                = "headers"
	BackHealthCheckAddr        = "health-check-addr"
	BackHealthCheckFallCount   = "health-check-fall-count"
	BackHealthCheckInterval    = "health-check-interval"
	BackHealthCheckPort        = "health-check-port"
	BackHealthCheckRiseCount   = "health-check-rise-count"
	BackHealthCheckURI         = "health-check-uri"
	BackHSTS                   = "hsts"
	BackHSTSIncludeSubdomains  = "hsts-include-subdomains"
	BackHSTSMaxAge             = "hsts-max-age"
	BackHSTSPreload            = "hsts-preload"
	BackInitialWeight          = "initial-weight"
	BackLimitConnections       = "limit-connections"
	BackLimitRPS               = "limit-rps"
	BackLimitWhitelist         = "limit-whitelist"
	BackMaxconnServer          = "maxconn-server"
	BackMaxQueueServer         = "maxqueue-server"
	BackOAuth                  = "oauth"
	BackOAuthHeaders           = "oauth-headers"
	BackOAuthURIPrefix         = "oauth-uri-prefix"
	BackProxyBodySize          = "proxy-body-size"
	BackProxyProtocol          = "proxy-protocol"
	BackRewriteTarget          = "rewrite-target"
	BackSlotsMinFree           = "slots-min-free"
	BackSecureBackends         = "secure-backends"
	BackSecureCrtSecret        = "secure-crt-secret"
	BackSecureVerifyCASecret   = "secure-verify-ca-secret"
	BackServiceUpstream        = "service-upstream"
	BackSessionCookieDynamic   = "session-cookie-dynamic"
	BackSessionCookieKeywords  = "session-cookie-keywords"
	BackSessionCookieName      = "session-cookie-name"
	BackSessionCookieShared    = "session-cookie-shared"
	BackSessionCookieStrategy  = "session-cookie-strategy"
	BackSSLCipherSuitesBackend = "ssl-cipher-suites-backend"
	BackSSLCiphersBackend      = "ssl-ciphers-backend"
	BackSSLFingerprintLower    = "ssl-fingerprint-lower"
	BackSSLOptionsBackend      = "ssl-options-backend"
	BackSSLRedirect            = "ssl-redirect"
	BackTimeoutConnect         = "timeout-connect"
	BackTimeoutHTTPRequest     = "timeout-http-request"
	BackTimeoutKeepAlive       = "timeout-keep-alive"
	BackTimeoutQueue           = "timeout-queue"
	BackTimeoutServer          = "timeout-server"
	BackTimeoutServerFin       = "timeout-server-fin"
	BackTimeoutTunnel          = "timeout-tunnel"
	BackUseResolver            = "use-resolver"
	BackWAF                    = "waf"
	BackWAFMode                = "waf-mode"
	BackWhitelistSourceRange   = "whitelist-source-range"
)

// Extra Annotations
const (
	ExtraTLSAcme = "kubernetes.io/tls-acme"
)
<|endoftext|>